This is language.c in view mode; [Download] [Up]
/* * language.c - Foreign language translation for PGP * Finds foreign language "subtitles" for English phrases * in external foriegn language text file. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #include "usuals.h" #include "fileio.h" #include "language.h" #include "pgp.h" #include "charset.h" #include "armor.h" #define SUBTITLES_FILE "language.txt" #define LANG_INDEXFILE "language.idx" #define STRBUFSIZE 2048 char language[16] = "en"; /* The language code, defaults to English */ static char *strbuf; static char lang[16]; /* readstr sets this to the language id of the msg it last read */ static int subtitles_available = 0; static int line = 0; /* subtitles_available is used to determine if we know whether the special subtitles_file exists. subtitles_available has the following values: 0 = first time thru, we don't yet know if subtitles_file exists. 1 = we have already determined that subtitles_file exists. -1 = we have already determined that subtitles_file does not exist. */ #define NEWLINE 0 #define COMMENT 1 #define INSTRING 2 #define ESCAPE 3 #define IDENT 4 #define DONE 5 #define ERROR 6 #define ERR1 7 /* Look for and return a quoted string from the file. * If nlabort is true, return failure if we find a blank line * before we find the opening quote. */ static char * readstr (FILE *f, char *buf, int nlabort) { int c, d; char *p = buf; int state = NEWLINE; int i = 0; while ((c = getc(f)) != EOF) { if (c == '\r') continue; /* line numbers are only incremented when creating index file */ if (line && c == '\n') ++line; switch (state) { case NEWLINE: switch(c) { case '#': state = COMMENT; break; case '"': state = INSTRING; break; case '\n': if (nlabort) { *buf = '\0'; return(buf); } default: if (i == 0 && isalnum(c)) { state = IDENT; lang[i++] = c; break; } if (!isspace(c)) { fprintf(stderr, "language.txt:%d: syntax error\n", line); state = ERROR; } } break; case COMMENT: if (c == '\n') state = NEWLINE; break; case INSTRING: switch(c) { case '\\': state = ESCAPE; break; case '"': state = DONE; break; default: *p++ = c; } break; case ESCAPE: switch (c) { case 'n': *p++ = '\n'; break; case 'r': *p++ = '\r'; break; case 't': *p++ = '\t'; break; case 'e': *p++ = '\033'; break; case 'a': *p++ = '\007'; break; case '#': case '"': case '\\': *p++ = c; break; case '\n': break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': d = c - '0'; while ((c = fgetc(f)) >= '0' && c <= '7') d = 8 * d + c - '0'; *p++ = d; ungetc(c, f); break; default: fprintf(stderr, "language.txt:%d: illegal escape sequence: '\\%c'\n", line, c); break; } state = INSTRING; break; case IDENT: /* language identifier */ if (c == ':') { state = NEWLINE; break; } if (c == '\n' && strncmp(lang, "No translation", 14) == 0) { i = 0; state = NEWLINE; break; } lang[i++] = c; if (i == 15 || !isalnum(c) && !isspace(c)) { lang[i] = '\0'; fprintf(stderr, "language.txt:%d: bad language identifier: '%s'\n", line, lang); state = ERROR; i = 0; } break; case DONE: if (c == '\n') { lang[i] = '\0'; *p = '\0'; return(buf); } if (!isspace(c)) { fprintf(stderr, "language.txt:%d: extra characters after '\"'\n", line); state = ERROR; } break; case ERROR: if (c == '\n') state = ERR1; break; case ERR1: state = (c == '\n' ? NEWLINE : ERROR); break; } } if (state != NEWLINE) fprintf(stderr, "language.txt: unexpected EOF\n"); return(NULL); } #ifdef TEST main() { char buf[2048]; line = 1; while (readstr(stdin, buf, 0)) { printf("\nen: <%s>\n", buf); while (readstr(stdin, buf, 1) && *buf != '\0') printf("%s: <%s>\n", lang, buf); } exit(0); } #else static struct indx_ent { word32 crc; long offset; } *indx_tbl = NULL; static int max_msgs = 0; static int nmsg = 0; static FILE *langf; static void init_lang(void); static int make_indexfile(char *); /* * uses 24-bit CRC function from armor.c */ static word32 message_crc(char *s) { return crcbytes((byte *)s, strlen(s), (word32)0); } /* * lookup file offset in indx_tbl */ static long lookup_offset(word32 crc) { int i; for (i = 0; i < nmsg; ++i) if (indx_tbl[i].crc == crc) return(indx_tbl[i].offset); return(-1); } /* * return foreign translation of s */ char * PSTR (char *s) { long filepos; if (subtitles_available == 0) init_lang(); if (subtitles_available < 0) return(s); filepos = lookup_offset(message_crc(s)); if (filepos == -1) return(s); else { fseek(langf, filepos, SEEK_SET); readstr(langf, strbuf, 1); } if (strbuf[0] == '\0') return(s); for (s = strbuf; *s; ++s) *s = EXT_C(*s); return(strbuf); } static struct { long lang_fsize; /* size of language.txt */ char lang[16]; /* language identifier */ int nmsg; /* number of messages */ } indx_hdr; /* * initialize the index table: read it from language.idx or create * a new one and write it to the index file. A new index file is * created if the language set in config.pgp doesn't match the one * in language.idx or if the size of language.txt has changed. */ static void init_lang() { char indexfile[MAX_PATH]; char subtitles_file[MAX_PATH]; FILE *indexf; if (strcmp(language, "en") == 0) { subtitles_available = -1; return; /* use default messages */ } buildfilename (subtitles_file, SUBTITLES_FILE); if ((langf = fopen(subtitles_file, FOPRTXT)) == NULL) { subtitles_available = -1; return; } init_crc(); if ((strbuf = (char *) malloc(STRBUFSIZE)) == NULL) { fprintf(stderr, "Not enough memory for foreign subtitles\n"); fclose(langf); subtitles_available = -1; return; } buildfilename(indexfile, LANG_INDEXFILE); if ((indexf = fopen(indexfile, FOPRBIN)) != NULL) { if (fread(&indx_hdr, 1, sizeof(indx_hdr), indexf) == sizeof(indx_hdr) && indx_hdr.lang_fsize == fsize(langf) && strcmp(indx_hdr.lang, language) == 0) { nmsg = indx_hdr.nmsg; indx_tbl = (struct indx_ent *) malloc(nmsg * sizeof(struct indx_ent)); if (indx_tbl == NULL) { fprintf(stderr, "Not enough memory for foreign subtitles\n"); fclose(indexf); fclose(langf); subtitles_available = -1; return; } if (fread(indx_tbl, sizeof(struct indx_ent), nmsg, indexf) != nmsg) { free(indx_tbl); /* create a new one */ indx_tbl = NULL; } } fclose(indexf); } if (indx_tbl == NULL && make_indexfile(indexfile) < 0) { fclose(langf); subtitles_available = -1; } else subtitles_available = 1; } static int make_indexfile(char *indexfile) { FILE *indexf; long filepos; int total_msgs = 0; char *res; if (verbose) /* must be set in config.pgp */ fprintf(stderr, "Creating language index file '%s' for language \"%s\"\n", indexfile, language); rewind(langf); indx_hdr.lang_fsize = fsize(langf); strncpy(indx_hdr.lang, language, 15); init_crc(); line = 1; nmsg = 0; while (readstr(langf, strbuf, 0)) { if (nmsg == max_msgs) { if (max_msgs) { max_msgs *= 2; indx_tbl = (struct indx_ent *) realloc(indx_tbl, max_msgs * sizeof(struct indx_ent)); } else { max_msgs = 400; indx_tbl = (struct indx_ent *) malloc(max_msgs * sizeof(struct indx_ent)); } if (indx_tbl == NULL) { fprintf(stderr, "Not enough memory for foreign subtitles\n"); return(-1); } } ++total_msgs; indx_tbl[nmsg].crc = message_crc(strbuf); if (lookup_offset(indx_tbl[nmsg].crc) != -1) fprintf(stderr, "language.txt:%d: Message CRC not unique: \"%s\"\n", line, strbuf); do { filepos = ftell(langf); res = readstr (langf, strbuf, 1); /* Abort if find newline first */ } while (res && strbuf[0] != '\0' && strcmp(language, lang) != 0); if (res == NULL) break; if (strbuf[0] == '\0') /* No translation */ continue; indx_tbl[nmsg].offset = filepos; ++nmsg; do res = readstr (langf, strbuf, 1); /* Abort if find newline first */ while (res && strbuf[0] != '\0'); } line = 0; indx_hdr.nmsg = nmsg; if (nmsg == 0) { fprintf(stderr, "No translations available for language \"%s\"\n\n", language); return(-1); } if (verbose || total_msgs != nmsg) fprintf(stderr, "%d messages, %d translations\n\n", total_msgs, nmsg); if ((indexf = fopen(indexfile, FOPWBIN)) == NULL) fprintf(stderr, "Cannot create %s\n", indexfile); else { fwrite(&indx_hdr, 1, sizeof(indx_hdr), indexf); fwrite(indx_tbl, sizeof(struct indx_ent), nmsg, indexf); if (ferror(indexf) || fclose(indexf)) fprintf(stderr, "error writing %s\n", indexfile); } return(0); } #endif /* TEST */
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.