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 <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 bool showKeys = false; 25 bool showSummary = false; 26 bool showWarnings = false; 27 const char *inputFile = NULL; 28 BString outputFile; 29 const char *catalogSig = NULL; 30 const char *catalogLang = "English"; 31 BString rxString("(be_catalog\\s*->\\s*GetString\\s*" 32 "|BCatalogAddOn\\s*::\\s*MarkForTranslation\\s*)"); 33 34 35 BString str, ctx, cmt; 36 bool haveID; 37 int32 id; 38 39 40 PlainTextCatalog *catalog = NULL; 41 42 43 void 44 usage() 45 { 46 fprintf(stderr, 47 "usage: collectcatkeys [-pvw] [-r <regex>] [-o <outfile>] [-l <catalogLanguage>]\n" 48 " -s <catalogSig> <prepCppFile>\n" 49 "options:\n" 50 " -l <catalogLang>\tlanguage of the target-catalog (default is English)\n" 51 " -o <outfile>\t\texplicitly specifies the name of the output-file\n" 52 " -p\t\t\tprint keys as they are found\n" 53 " -r <regex>\t\tchanges the regex used by the key-scanner to the one given,\n" 54 " \t\t\tthe default is: be_catalog\\s*->\\s*GetString\\s*\n" 55 " -s <catalogSig>\tsignature of the target-catalog\n" 56 " -v\t\t\tbe verbose, show summary\n" 57 " -w\t\t\tshow warnings about catalog-accesses that couldn't be resolved completely\n"); 58 exit(-1); 59 } 60 61 62 bool 63 fetchStr(const char *&in, BString &str, bool lookForID) 64 { 65 int parLevel = 0; 66 while (isspace(*in) || *in == '(') { 67 if (*in == '(') 68 parLevel++; 69 in++; 70 } 71 if (*in == '"') { 72 bool inString = true; 73 bool quoted = false; 74 in++; 75 while (parLevel >= 0 && inString) 76 { 77 // Collect string content until we find a quote marking end of 78 // string (skip escaped quotes) 79 while (*in != '"' || quoted) 80 { 81 str.Append(in, 1); 82 if (*in == '\\' && !quoted) 83 quoted = true ; 84 else 85 quoted = false ; 86 in++; 87 } 88 in++; 89 90 inString = false; 91 92 // Strip all whitespace until we find a closing parenthesis, or the 93 // beginning of another string 94 // TODO: ignore comments 95 while (isspace(*in) || *in == ')') { 96 if (*in == ')') { 97 if (parLevel == 0) 98 return true; 99 parLevel--; 100 } 101 102 in++; 103 } 104 105 if (*in == '"') { 106 inString = true; 107 in++; 108 } 109 } 110 } else { 111 if (!memcmp(in, "__null", 6)) { 112 // NULL is preprocessed into __null, which we parse as "" 113 in += 6; 114 } else if (lookForID && (isdigit(*in) || *in == '-' || *in == '+')) { 115 // try to parse an ID (a long): 116 errno = 0; 117 char *next; 118 id = strtol(in, &next, 10); 119 if (id != 0 || errno == 0) { 120 haveID = true; 121 in = next; 122 } 123 } else 124 return false; 125 126 while (isspace(*in) || *in == ')') { 127 if (*in == ')') { 128 if (!parLevel) 129 return true; 130 parLevel--; 131 } 132 in++; 133 } 134 } 135 return true; 136 } 137 138 139 bool 140 fetchKey(const char *&in) 141 { 142 str = ctx = cmt = ""; 143 haveID = false; 144 // fetch native string or id: 145 if (!fetchStr(in, str, true)) { 146 return false; 147 } 148 if (*in == ',') { 149 in++; 150 // fetch context: 151 if (!fetchStr(in, ctx, false)) { 152 fprintf(stderr,"Context parsing error\n"); 153 return false; 154 } 155 if (*in == ',') { 156 in++; 157 // fetch comment: 158 if (!fetchStr(in, cmt, false)) 159 { 160 fprintf(stderr,"Comment parsing error\n"); 161 return false; 162 } 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(%ld)\n", id); 187 res = catalog->SetString(id, ""); 188 if (res != B_OK) { 189 fprintf(stderr, "Collectcatkeys: couldn't add key %ld - " 190 "error: %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 if (!outputFile.Length() && inputFile) { 260 // generate default output-file from input-file by replacing 261 // the extension with '.catkeys': 262 outputFile = inputFile; 263 int32 dot = outputFile.FindLast('.'); 264 if (dot >= B_OK) 265 outputFile.Truncate(dot); 266 outputFile << ".catkeys"; 267 } 268 if (!inputFile || !catalogSig || !outputFile.Length() || !catalogLang) 269 usage(); 270 271 BFile inFile; 272 status_t res = inFile.SetTo(inputFile, B_READ_ONLY); 273 if (res != B_OK) { 274 fprintf(stderr, "unable to open inputfile %s - error: %s\n", inputFile, 275 strerror(res)); 276 exit(-1); 277 } 278 off_t sz; 279 inFile.GetSize(&sz); 280 if (sz > 0) { 281 BString inputStr; 282 char *buf = inputStr.LockBuffer(sz); 283 off_t rsz = inFile.Read(buf, sz); 284 if (rsz < sz) { 285 fprintf(stderr, "couldn't read %Ld bytes from %s (got only %Ld)\n", 286 sz, inputFile, rsz); 287 exit(-1); 288 } 289 inputStr.UnlockBuffer(rsz); 290 catalog = new PlainTextCatalog(inputFile, catalogSig, catalogLang); 291 collectAllCatalogKeys(inputStr); 292 res = catalog->WriteToFile(outputFile.String()); 293 if (res != B_OK) { 294 fprintf(stderr, "couldn't write catalog to %s - error: %s\n", 295 outputFile.String(), strerror(res)); 296 exit(-1); 297 } 298 if (showSummary) { 299 int32 count = catalog->CountItems(); 300 if (count) 301 fprintf(stderr, "%ld key%s found and written to %s\n", 302 count, (count==1 ? "": "s"), outputFile.String()); 303 else 304 fprintf(stderr, "no keys found\n"); 305 } 306 delete catalog; 307 } 308 309 // BEntry inEntry(inputFile); 310 // inEntry.Remove(); 311 312 return res; 313 } 314