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