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