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