|
|
1.1 root 1: /*
2: * language.c - Foreign language translation for PGP
3: * Finds foreign language "subtitles" for English phrases
4: * in external foriegn language text file.
5: */
6:
7: #include <stdio.h>
8: #include <stdlib.h>
9: #include <string.h>
10: #include <ctype.h>
11: #include "usuals.h"
12: #include "fileio.h"
13: #include "language.h"
14: #include "pgp.h"
15:
16: #define SUBTITLES_FILE "language.txt"
17: #define LANG_INDEXFILE "language.idx"
18:
19: #define STRBUFSIZE 2048
20:
21: char language[16] = "en"; /* The language code, defaults to English */
22: static char *strbuf;
23: static char lang[16]; /* readstr sets this to the language id of the msg it last read */
24: static int subtitles_available = 0;
25: static int line = 0;
26: /* subtitles_available is used to determine if we know whether the special
27: subtitles_file exists. subtitles_available has the following values:
28: 0 = first time thru, we don't yet know if subtitles_file exists.
29: 1 = we have already determined that subtitles_file exists.
30: -1 = we have already determined that subtitles_file does not exist.
31: */
32:
33: #define NEWLINE 0
34: #define COMMENT 1
35: #define INSTRING 2
36: #define ESCAPE 3
37: #define IDENT 4
38: #define DONE 5
39: #define ERROR 6
40: #define ERR1 7
41:
42: /* Look for and return a quoted string from the file.
43: * If nlabort is true, return failure if we find a blank line
44: * before we find the opening quote.
45: */
46: static char *
47: readstr (FILE *f, char *buf, int nlabort)
48: { int c, d;
49: char *p = buf;
50: int state = NEWLINE;
51: int i = 0;
52:
53: while ((c = getc(f)) != EOF)
54: {
55: if (c == '\r')
56: continue;
57: /* line numbers are only incremented when creating index file */
58: if (line && c == '\n')
59: ++line;
60: switch (state)
61: {
62: case NEWLINE:
63: switch(c)
64: {
65: case '#': state = COMMENT; break;
66: case '"': state = INSTRING; break;
67: case '\n':
68: if (nlabort)
69: { *buf = '\0';
70: return(buf);
71: }
72: default:
73: if (i == 0 && isalnum(c))
74: {
75: state = IDENT;
76: lang[i++] = c;
77: break;
78: }
79: if (!isspace(c))
80: {
81: fprintf(stderr, "language.txt:%d: syntax error\n", line);
82: state = ERROR;
83: }
84: }
85: break;
86: case COMMENT:
87: if (c == '\n')
88: state = NEWLINE;
89: break;
90: case INSTRING:
91: switch(c)
92: {
93: case '\\': state = ESCAPE; break;
94: case '"': state = DONE; break;
95: default: *p++ = c;
96: }
97: break;
98: case ESCAPE:
99: switch (c)
100: {
101: case 'n': *p++ = '\n'; break;
102: case 'r': *p++ = '\r'; break;
103: case 't': *p++ = '\t'; break;
104: case 'e': *p++ = '\033'; break;
105: case 'a': *p++ = '\007'; break;
106: case '#':
107: case '"':
108: case '\\': *p++ = c; break;
109: case '\n': break;
110: case '0':
111: d = 0;
112: while ((c = fgetc(f)) >= '0' && c <= '7')
113: d = 8 * d + c - '0';
114: *p++ = d;
115: ungetc(c, f);
116: break;
117: default:
118: fprintf(stderr, "language.txt:%d: illegal escape sequence: '\\%c'\n", line, c);
119: break;
120: }
121: state = INSTRING;
122: break;
123: case IDENT: /* language identifier */
124: if (c == ':') {
125: state = NEWLINE;
126: break;
127: }
128: if (c == '\n' && strncmp(lang, "No translation", 14) == 0)
129: {
130: i = 0;
131: state = NEWLINE;
132: break;
133: }
134: lang[i++] = c;
135: if (i == 15 || !isalnum(c) && !isspace(c))
136: {
137: lang[i] = '\0';
138: fprintf(stderr, "language.txt:%d: bad language identifier: '%s'\n", line, lang);
139: state = ERROR;
140: i = 0;
141: }
142: break;
143: case DONE:
144: if (c == '\n')
145: {
146: lang[i] = '\0';
147: *p = '\0';
148: return(buf);
149: }
150: if (!isspace(c))
151: {
152: fprintf(stderr, "language.txt:%d: extra characters after '\"'\n", line);
153: state = ERROR;
154: }
155: break;
156: case ERROR:
157: if (c == '\n')
158: state = ERR1;
159: break;
160: case ERR1:
161: state = (c == '\n' ? NEWLINE : ERROR);
162: break;
163: }
164: }
165: if (state != NEWLINE)
166: fprintf(stderr, "language.txt: unexpected EOF\n");
167: return(NULL);
168: }
169:
170: #ifdef TEST
171: main()
172: {
173: char buf[2048];
174:
175: line = 1;
176: while (readstr(stdin, buf, 0)) {
177: printf("\nen: <%s>\n", buf);
178: while (readstr(stdin, buf, 1) && *buf != '\0')
179: printf("%s: <%s>\n", lang, buf);
180: }
181: exit(0);
182: }
183: #else
184:
185: static struct indx_ent {
186: word32 crc;
187: long offset;
188: } *indx_tbl = NULL;
189:
190: static int max_msgs = 0;
191: static int nmsg = 0;
192:
193: static FILE *langf;
194:
195: static void init_lang();
196: static int make_indexfile(char *);
197: word32 crcupdate(byte, word32);
198: void init_crc();
199:
200: /*
201: * uses 24-bit CRC function from armor.c
202: */
203: static word32
204: message_crc(char *s)
205: {
206: word32 crc = 0;
207:
208: while (*s)
209: crc = crcupdate(*s++, crc);
210: return(crc);
211: }
212:
213: /*
214: * lookup file offset in indx_tbl
215: */
216: static long
217: lookup_offset(word32 crc)
218: {
219: int i;
220:
221: for (i = 0; i < nmsg; ++i)
222: if (indx_tbl[i].crc == crc)
223: return(indx_tbl[i].offset);
224: return(-1);
225: }
226:
227:
228: /*
229: * return foreign translation of s
230: */
231: char *
232: PSTR (char *s)
233: {
234: long filepos;
235:
236: if (subtitles_available == 0)
237: init_lang();
238: if (subtitles_available < 0)
239: return(s);
240:
241: filepos = lookup_offset(message_crc(s));
242: if (filepos == -1)
243: return(s);
244: else
245: {
246: fseek(langf, filepos, SEEK_SET);
247: readstr(langf, strbuf, 1);
248: }
249:
250: if (strbuf[0] == '\0')
251: return(s);
252:
253: for (s = strbuf; *s; ++s)
254: *s = EXT_C(*s);
255: return(strbuf);
256: }
257:
258:
259: static struct {
260: long lang_fsize; /* size of language.txt */
261: char lang[16]; /* language identifier */
262: int nmsg; /* number of messages */
263: } indx_hdr;
264:
265:
266: /*
267: * initialize the index table: read it from language.idx or create
268: * a new one and write it to the index file. A new index file is
269: * created if the language set in config.pgp doesn't match the one
270: * in language.idx or if the size of language.txt has changed.
271: */
272: static void
273: init_lang()
274: {
275: char indexfile[MAX_PATH];
276: char subtitles_file[MAX_PATH];
277: FILE *indexf;
278:
279: if (strcmp(language, "en") == 0)
280: { subtitles_available = -1;
281: return; /* use default messages */
282: }
283:
284: buildfilename (subtitles_file, SUBTITLES_FILE);
285: if ((langf = fopenbin(subtitles_file, "r")) == NULL)
286: {
287: subtitles_available = -1;
288: return;
289: }
290: init_crc();
291: if ((strbuf = (char *) malloc(STRBUFSIZE)) == NULL)
292: {
293: fprintf(stderr, "Not enough memory for foreign subtitles\n");
294: fclose(langf);
295: subtitles_available = -1;
296: return;
297: }
298: buildfilename(indexfile, LANG_INDEXFILE);
299: if ((indexf = fopenbin(indexfile, "r")) != NULL)
300: {
301: if (fread(&indx_hdr, 1, sizeof(indx_hdr), indexf) == sizeof(indx_hdr) &&
302: indx_hdr.lang_fsize == fsize(langf) &&
303: strcmp(indx_hdr.lang, language) == 0)
304: {
305: nmsg = indx_hdr.nmsg;
306: indx_tbl = (struct indx_ent *) malloc(nmsg * sizeof(struct indx_ent));
307: if (indx_tbl == NULL)
308: {
309: fprintf(stderr, "Not enough memory for foreign subtitles\n");
310: fclose(indexf);
311: fclose(langf);
312: subtitles_available = -1;
313: return;
314: }
315: if (fread(indx_tbl, sizeof(struct indx_ent), nmsg, indexf) != nmsg)
316: {
317: free(indx_tbl); /* create a new one */
318: indx_tbl = NULL;
319: }
320: }
321: fclose(indexf);
322: }
323: if (indx_tbl == NULL && make_indexfile(indexfile) < 0)
324: {
325: fclose(langf);
326: subtitles_available = -1;
327: }
328: else
329: subtitles_available = 1;
330: }
331:
332:
333: static int
334: make_indexfile(char *indexfile)
335: {
336: FILE *indexf;
337: long filepos;
338: int total_msgs = 0;
339: char *res;
340:
341: if (verbose) /* must be set in config.pgp */
342: fprintf(stderr, "Creating language index file '%s' for language \"%s\"\n",
343: indexfile, language);
344: rewind(langf);
345: indx_hdr.lang_fsize = fsize(langf);
346: strncpy(indx_hdr.lang, language, 15);
347: init_crc();
348: line = 1;
349: nmsg = 0;
350: while (readstr(langf, strbuf, 0))
351: {
352: if (nmsg == max_msgs)
353: {
354: if (max_msgs)
355: { max_msgs *= 2;
356: indx_tbl = (struct indx_ent *) realloc(indx_tbl, max_msgs *
357: sizeof(struct indx_ent));
358: }
359: else
360: { max_msgs = 400;
361: indx_tbl = (struct indx_ent *) malloc(max_msgs *
362: sizeof(struct indx_ent));
363: }
364: if (indx_tbl == NULL)
365: {
366: fprintf(stderr, "Not enough memory for foreign subtitles\n");
367: return(-1);
368: }
369: }
370: ++total_msgs;
371: indx_tbl[nmsg].crc = message_crc(strbuf);
372: if (lookup_offset(indx_tbl[nmsg].crc) != -1)
373: fprintf(stderr, "language.txt:%d: Message CRC not unique: \"%s\"\n",
374: line, strbuf);
375: do
376: {
377: filepos = ftell(langf);
378: res = readstr (langf, strbuf, 1); /* Abort if find newline first */
379: } while (res && strbuf[0] != '\0' && strcmp(language, lang) != 0);
380:
381: if (res == NULL)
382: break;
383: if (strbuf[0] == '\0') /* No translation */
384: continue;
385:
386: indx_tbl[nmsg].offset = filepos;
387: ++nmsg;
388: do
389: res = readstr (langf, strbuf, 1); /* Abort if find newline first */
390: while (res && strbuf[0] != '\0');
391: }
392: line = 0;
393: indx_hdr.nmsg = nmsg;
394: if (nmsg == 0)
395: { fprintf(stderr, "No translations available for language \"%s\"\n\n",
396: language);
397: return(-1);
398: }
399: if (verbose || total_msgs != nmsg)
400: fprintf(stderr, "%d messages, %d translations\n\n", total_msgs, nmsg);
401:
402: if ((indexf = fopenbin(indexfile, "w")) == NULL)
403: fprintf(stderr, "Cannot create %s\n", indexfile);
404: else
405: {
406: fwrite(&indx_hdr, 1, sizeof(indx_hdr), indexf);
407: fwrite(indx_tbl, sizeof(struct indx_ent), nmsg, indexf);
408: if (ferror(indexf) || fclose(indexf))
409: fprintf(stderr, "error writing %s\n", indexfile);
410: }
411: return(0);
412: }
413: #endif /* TEST */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.