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