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