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 "RegExp.h" 19 #include <StorageDefs.h> 20 #include <String.h> 21 22 #include <EditableCatalog.h> 23 24 using BPrivate::EditableCatalog; 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 BString str, ctx, cmt; 37 bool haveID; 38 int32 id; 39 40 41 EditableCatalog *catalog = NULL; 42 43 44 void 45 usage() 46 { 47 fputs("usage: collectcatkeys [-pvw] [-r <regex>] [-o <outfile>] " 48 "[-l <catalogLanguage>]\n" 49 " -s <catalogSig> <prepCppFile>\n" 50 "options:\n" 51 " -l <catalogLang>\tlanguage of the target-catalog (default is " 52 "English)\n" 53 " -o <outfile>\t\texplicitly specifies the name of the output-file\n" 54 " -p\t\t\tprint keys as they are found\n" 55 " -r <regex>\t\tchanges the regex used by the key-scanner to the one " 56 "given,\n" 57 " \t\t\tthe default is: ", stderr); 58 fputs(rxString.String(), stderr); 59 fputs("\n -s <catalogSig>\tsignature of the target-catalog\n" 60 " -v\t\t\tbe verbose, show summary\n" 61 " -w\t\t\tshow warnings about catalog-accesses that couldn't be " 62 " resolved completely\n", stderr); 63 exit(-1); 64 } 65 66 67 bool 68 fetchStr(const char *&in, BString &str, bool lookForID) 69 { 70 int parLevel = 0; 71 72 while (isspace(*in) || *in == '(') { 73 if (*in == '(') 74 parLevel++; 75 in++; 76 } 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 if (*in == ',') { 154 in++; 155 // fetch context: 156 if (!fetchStr(in, ctx, false)) 157 return false; 158 if (*in == ',') { 159 in++; 160 // fetch comment: 161 if (!fetchStr(in, cmt, false)) 162 return false; 163 } 164 } 165 return true; 166 } 167 168 169 void 170 collectAllCatalogKeys(BString& inputStr) 171 { 172 RegExp rx; 173 struct regexp *rxprg = rx.Compile(rxString.String()); 174 if (rx.InitCheck() != B_OK) { 175 fprintf(stderr, "regex-compilation error %s\n", rx.ErrorString()); 176 return; 177 } 178 status_t res; 179 const char *in = inputStr.String(); 180 while (rx.RunMatcher(rxprg, in)) { 181 const char *start = rxprg->startp[0]; 182 in = rxprg->endp[0]; 183 if (fetchKey(in)) { 184 if (haveID) { 185 if (showKeys) 186 printf("CatKey(%" B_PRId32 ")\n", id); 187 res = catalog->SetString(id, ""); 188 if (res != B_OK) { 189 fprintf(stderr, "couldn't add key %" B_PRId32 " - error: " 190 "%s\n", id, strerror(res)); 191 exit(-1); 192 } 193 } else { 194 if (showKeys) { 195 printf("CatKey(\"%s\", \"%s\", \"%s\")\n", str.String(), 196 ctx.String(), cmt.String()); 197 } 198 res = catalog->SetString(str.String(), str.String(), 199 ctx.String(), cmt.String()); 200 if (res != B_OK) { 201 fprintf(stderr, "couldn't add key %s,%s,%s - error: %s\n", 202 str.String(), ctx.String(), cmt.String(), 203 strerror(res)); 204 exit(-1); 205 } 206 } 207 } else if (showWarnings) { 208 const char *end = strchr(in, ';'); 209 BString match; 210 if (end) 211 match.SetTo(start, end-start+1); 212 else { 213 // can't determine end of statement, we output next 40 chars 214 match.SetTo(start, 40); 215 } 216 fprintf(stderr, "Warning: couldn't resolve catalog-access:\n\t%s\n", 217 match.String()); 218 } 219 } 220 } 221 222 223 int 224 main(int argc, char **argv) 225 { 226 while ((++argv)[0]) { 227 if (argv[0][0] == '-' && argv[0][1] != '-') { 228 char *arg = argv[0] + 1; 229 char c; 230 while ((c = *arg++) != '\0') { 231 if (c == 'p') 232 showKeys = true; 233 else if (c == 'l') 234 catalogLang = (++argv)[0]; 235 else if (c == 's') 236 catalogSig = (++argv)[0]; 237 else if (c == 'v') 238 showSummary = true; 239 else if (c == 'w') 240 showWarnings = true; 241 else if (c == 'o') { 242 outputFile = (++argv)[0]; 243 break; 244 } 245 else if (c == 'r') { 246 rxString = (++argv)[0]; 247 break; 248 } 249 } 250 } else if (!strcmp(argv[0], "--help")) { 251 usage(); 252 } else { 253 if (!inputFile) 254 inputFile = argv[0]; 255 else 256 usage(); 257 } 258 } 259 260 if (!outputFile.Length() && inputFile) { 261 // generate default output-file from input-file by replacing 262 // the extension with '.catkeys': 263 outputFile = inputFile; 264 int32 dot = outputFile.FindLast('.'); 265 if (dot >= B_OK) 266 outputFile.Truncate(dot); 267 outputFile << ".catkeys"; 268 } 269 if (!inputFile || !catalogSig || !outputFile.Length() || !catalogLang) 270 usage(); 271 272 BFile inFile; 273 status_t res = inFile.SetTo(inputFile, B_READ_ONLY); 274 if (res != B_OK) { 275 fprintf(stderr, "unable to open inputfile %s - error: %s\n", inputFile, 276 strerror(res)); 277 exit(-1); 278 } 279 280 off_t sz; 281 inFile.GetSize(&sz); 282 if (sz > 0) { 283 BString inputStr; 284 char *buf = inputStr.LockBuffer(sz); 285 off_t rsz = inFile.Read(buf, sz); 286 if (rsz < sz) { 287 fprintf(stderr, "couldn't read %" B_PRId64 " bytes from %s (got " 288 "only %" B_PRId64 ")\n", sz, inputFile, rsz); 289 exit(-1); 290 } 291 inputStr.UnlockBuffer(rsz); 292 catalog = new EditableCatalog("plaintext", catalogSig, catalogLang); 293 collectAllCatalogKeys(inputStr); 294 res = catalog->WriteToFile(outputFile.String()); 295 if (res != B_OK) { 296 fprintf(stderr, "couldn't write catalog to %s - error: %s\n", 297 outputFile.String(), strerror(res)); 298 exit(-1); 299 } 300 if (showSummary) { 301 int32 count = catalog->CountItems(); 302 if (count) 303 fprintf(stderr, "%" B_PRId32 " key%s found and written to %s\n", 304 count, (count==1 ? "": "s"), outputFile.String()); 305 else 306 fprintf(stderr, "no keys found\n"); 307 } 308 delete catalog; 309 } 310 311 // BEntry inEntry(inputFile); 312 // inEntry.Remove(); 313 314 return res; 315 } 316