1 /* 2 * Copyright 2003-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Oliver Tappe, zooey@hirschkaefer.de 7 * Adrien Destugues, pulkomandy@gmail.com 8 */ 9 10 11 #include <cctype> 12 #include <cerrno> 13 #include <cstdio> 14 #include <cstdlib> 15 16 #include <Entry.h> 17 #include <File.h> 18 #include <PlainTextCatalog.h> 19 #include "RegExp.h" 20 #include <StorageDefs.h> 21 #include <String.h> 22 23 24 using BPrivate::PlainTextCatalog; 25 26 27 bool showKeys = false; 28 bool showSummary = false; 29 bool showWarnings = false; 30 const char *inputFile = NULL; 31 BString outputFile; 32 const char *catalogSig = NULL; 33 const char *catalogLang = "English"; 34 BString rxString("B_CATKEY\\s*"); 35 36 37 BString str, ctx, cmt; 38 bool haveID; 39 int32 id; 40 41 42 PlainTextCatalog *catalog = NULL; 43 44 45 void 46 usage() 47 { 48 fprintf(stderr, 49 "usage: collectcatkeys [-pvw] [-r <regex>] [-o <outfile>] " 50 "[-l <catalogLanguage>]\n" 51 " -s <catalogSig> <prepCppFile>\n" 52 "options:\n" 53 " -l <catalogLang>\tlanguage of the target-catalog (default is " 54 "English)\n" 55 " -o <outfile>\t\texplicitly specifies the name of the output-file\n" 56 " -p\t\t\tprint keys as they are found\n" 57 " -r <regex>\t\tchanges the regex used by the key-scanner to the one " 58 "given,\n" 59 " \t\t\tthe default is: "); 60 fprintf(stderr, "%s", rxString.String()); 61 fprintf(stderr,"\n -s <catalogSig>\tsignature of the target-catalog\n" 62 " -v\t\t\tbe verbose, show summary\n" 63 " -w\t\t\tshow warnings about catalog-accesses that couldn't be " 64 " resolved completely\n"); 65 exit(-1); 66 } 67 68 69 bool 70 fetchStr(const char *&in, BString &str, bool lookForID) 71 { 72 int parLevel = 0; 73 while (isspace(*in) || *in == '(') { 74 if (*in == '(') 75 parLevel++; 76 in++; 77 } 78 if (*in == '"') { 79 bool inString = true; 80 bool quoted = false; 81 in++; 82 while (parLevel >= 0 && inString) 83 { 84 // Collect string content until we find a quote marking end of 85 // string (skip escaped quotes) 86 while (*in != '"' || quoted) 87 { 88 str.Append(in, 1); 89 if (*in == '\\' && !quoted) 90 quoted = true ; 91 else 92 quoted = false ; 93 in++; 94 } 95 in++; 96 97 inString = false; 98 99 // Strip all whitespace until we find a closing parenthesis, or the 100 // beginning of another string 101 while (isspace(*in) || *in == ')') { 102 if (*in == ')') { 103 if (parLevel == 0) 104 return true; 105 parLevel--; 106 } 107 108 in++; 109 } 110 111 if (*in == '"') { 112 inString = true; 113 in++; 114 } 115 } 116 } else { 117 if (!memcmp(in, "__null", 6)) { 118 // NULL is preprocessed into __null, which we parse as "" 119 in += 6; 120 } else if (lookForID && (isdigit(*in) || *in == '-' || *in == '+')) { 121 // try to parse an ID (a long): 122 errno = 0; 123 char *next; 124 id = strtol(in, &next, 10); 125 if (id != 0 || errno == 0) { 126 haveID = true; 127 in = next; 128 } 129 } else 130 return false; 131 132 while (isspace(*in) || *in == ')') { 133 if (*in == ')') { 134 if (!parLevel) 135 return true; 136 parLevel--; 137 } 138 in++; 139 } 140 } 141 return true; 142 } 143 144 145 bool 146 fetchKey(const char *&in) 147 { 148 str = ctx = cmt = ""; 149 haveID = false; 150 // fetch native string or id: 151 if (!fetchStr(in, str, true)) { 152 return false; 153 } 154 if (*in == ',') { 155 in++; 156 // fetch context: 157 if (!fetchStr(in, ctx, false)) { 158 fprintf(stderr,"Context parsing error\n"); 159 return false; 160 } 161 if (*in == ',') { 162 in++; 163 // fetch comment: 164 if (!fetchStr(in, cmt, false)) 165 { 166 fprintf(stderr,"Comment parsing error\n"); 167 return false; 168 } 169 } 170 } 171 return true; 172 } 173 174 175 void 176 collectAllCatalogKeys(BString& inputStr) 177 { 178 RegExp rx; 179 struct regexp *rxprg = rx.Compile(rxString.String()); 180 if (rx.InitCheck() != B_OK) { 181 fprintf(stderr, "regex-compilation error %s\n", rx.ErrorString()); 182 return; 183 } 184 status_t res; 185 const char *in = inputStr.String(); 186 while (rx.RunMatcher(rxprg, in)) { 187 const char *start = rxprg->startp[0]; 188 in = rxprg->endp[0]; 189 if (fetchKey(in)) { 190 if (haveID) { 191 if (showKeys) 192 printf("CatKey(%d)\n", id); 193 res = catalog->SetString(id, ""); 194 if (res != B_OK) { 195 fprintf(stderr, "Collectcatkeys: couldn't add key %d - " 196 "error: %s\n", id, strerror(res)); 197 exit(-1); 198 } 199 } else { 200 if (showKeys) { 201 printf("CatKey(\"%s\", \"%s\", \"%s\")\n", str.String(), 202 ctx.String(), cmt.String()); 203 } 204 res = catalog->SetString(str.String(), str.String(), 205 ctx.String(), cmt.String()); 206 if (res != B_OK) { 207 fprintf(stderr, "couldn't add key %s,%s,%s - error: %s\n", 208 str.String(), ctx.String(), cmt.String(), 209 strerror(res)); 210 exit(-1); 211 } 212 } 213 } else if (showWarnings) { 214 const char *end = strchr(in, ';'); 215 BString match; 216 if (end) 217 match.SetTo(start, end-start+1); 218 else { 219 // can't determine end of statement, we output next 40 chars 220 match.SetTo(start, 40); 221 } 222 fprintf(stderr, "Warning: couldn't resolve catalog-access:\n\t%s\n", 223 match.String()); 224 } 225 } 226 } 227 228 229 int 230 main(int argc, char **argv) 231 { 232 while ((++argv)[0]) { 233 if (argv[0][0] == '-' && argv[0][1] != '-') { 234 char *arg = argv[0] + 1; 235 char c; 236 while ((c = *arg++) != '\0') { 237 if (c == 'p') 238 showKeys = true; 239 else if (c == 'l') 240 catalogLang = (++argv)[0]; 241 else if (c == 's') 242 catalogSig = (++argv)[0]; 243 else if (c == 'v') 244 showSummary = true; 245 else if (c == 'w') 246 showWarnings = true; 247 else if (c == 'o') { 248 outputFile = (++argv)[0]; 249 break; 250 } else if (c == 'r') { 251 rxString = (++argv)[0]; 252 break; 253 } 254 } 255 } else if (!strcmp(argv[0], "--help")) { 256 usage(); 257 } else { 258 if (!inputFile) 259 inputFile = argv[0]; 260 else 261 usage(); 262 } 263 } 264 if (!outputFile.Length() && inputFile) { 265 // generate default output-file from input-file by replacing 266 // the extension with '.catkeys': 267 outputFile = inputFile; 268 int32 dot = outputFile.FindLast('.'); 269 if (dot >= B_OK) 270 outputFile.Truncate(dot); 271 outputFile << ".catkeys"; 272 } 273 if (!inputFile || !catalogSig || !outputFile.Length() || !catalogLang) 274 usage(); 275 276 BFile inFile; 277 status_t res = inFile.SetTo(inputFile, B_READ_ONLY); 278 if (res != B_OK) { 279 fprintf(stderr, "unable to open inputfile %s - error: %s\n", inputFile, 280 strerror(res)); 281 exit(-1); 282 } 283 off_t sz; 284 inFile.GetSize(&sz); 285 if (sz > 0) { 286 BString inputStr; 287 char *buf = inputStr.LockBuffer(sz); 288 off_t rsz = inFile.Read(buf, sz); 289 if (rsz < sz) { 290 fprintf(stderr, "couldn't read %" B_PRIdOFF " bytes from %s " 291 "(got only %" B_PRIdOFF ")\n", 292 sz, inputFile, rsz); 293 exit(-1); 294 } 295 inputStr.UnlockBuffer(rsz); 296 catalog = new PlainTextCatalog(inputFile, catalogSig, catalogLang); 297 collectAllCatalogKeys(inputStr); 298 res = catalog->WriteToFile(outputFile.String()); 299 if (res != B_OK) { 300 fprintf(stderr, "couldn't write catalog to %s - error: %s\n", 301 outputFile.String(), strerror(res)); 302 exit(-1); 303 } 304 if (showSummary) { 305 int32 count = catalog->CountItems(); 306 if (count) 307 fprintf(stderr, "%d key%s found and written to %s\n", 308 count, (count==1 ? "": "s"), outputFile.String()); 309 else 310 fprintf(stderr, "no keys found\n"); 311 } 312 delete catalog; 313 } 314 315 // BEntry inEntry(inputFile); 316 // inEntry.Remove(); 317 318 return res; 319 } 320