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