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