diff options
author | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
---|---|---|
committer | Norbert Preining <norbert@preining.info> | 2019-09-02 13:46:59 +0900 |
commit | e0c6872cf40896c7be36b11dcc744620f10adf1d (patch) | |
tree | 60335e10d2f4354b0674ec22d7b53f0f8abee672 /support/translit/translit.c |
Initial commit
Diffstat (limited to 'support/translit/translit.c')
-rw-r--r-- | support/translit/translit.c | 1752 |
1 files changed, 1752 insertions, 0 deletions
diff --git a/support/translit/translit.c b/support/translit/translit.c new file mode 100644 index 0000000000..bebf3b941a --- /dev/null +++ b/support/translit/translit.c @@ -0,0 +1,1752 @@ + +/* This is a program fom transliterating files from one character set to + another. + TRANSLIT --- Version 1.02, April 4, 1993. + Copyright (c) by Jan Labanowski, 1993 and JKL Enterprises, Inc. + Permission is given to disribute this program freely in accordance with + the rules and conditions spelled out in the program documentation. If you + got this program without the documentation, or if some files were missing, + somebody must have violated the rules. In this case, please delete the the + program and obtain the information on how to get the complete distribution + from the author. The rules require that the whole package is distributed + (i.e., the source code, the transliteration tables, and the documentation). + + 3/29/93 Jan Labanowski. Thanks to Leonid A. Broukhis (leo@ipmce.su) + I noticed that translit will not work correctly if SHIFT-IN and SHIFT-OUT + is the same sequence. The modification was made in which code detecting + SHIFT-IN and SHIFT-OUT exchanged places (i.e., program looks for SHIFT-IN + before it looks for SHIFT-OUT) + + Author: Jan Labanowski, P.O.Box 21821, Columbus, OH 43221-0821, USA + E-mail: jkl@osc.edu, JKL@OHSTPY.BITNET + */ + +#include "paths.h" /* for local definitions */ +#include "reg_exp.h" /* for regexp package */ + + +#define OPTIONS "i:o:t:d" /* allowed options on command line */ +#define MAXPAIRS 1000 /* maximum number of conversion pairs */ +#define MAXSETS 10 /* maximum number of shift in/out sets */ +#define MAXLEVEL 10 /* maximum set nesting level */ +#define MAXBUFF 1000 /* maximum size of the buffer */ +#define MAXMATCH 100 /* maximum length of match to regular exp. */ + + +/* define all local functions as static if compiler likes it */ +#if STATICFUN +#define STATIC static +#else +#define STATIC +#endif + +/* types to hold the translation maps for single chars. If inp_maps is of + * type IMAPP, then character of code c in set k will correspond to + * a string pointed by (*inp_maps[k])[c]; + */ + +/* ========== now include definitions for paths and regexp */ + + +typedef char* IMAP[256]; /* type IMAP is a 256 element array + of pointers to string */ +typedef IMAP* IMAPP[MAXSETS]; /* array of pointers to IMAP */ + +/* types to hold output set number for a single char. if out_sets is of type + * OSETP and c is a code, and k is the set of input character, then output + * set number is (*out_sets[k])[c]; */ +typedef int OSET[256]; +typedef OSET* OSETP[MAXSETS]; + + +typedef union { + char *seq; /* pointer to a string */ + reg_exp *re; /* pointer to a regular expression "program */ + } ADDR; + + +typedef struct { + int typ; /* type of pointer in ADDR union: + 0-string (seq), 1-input regexp (re), + 2-output regexp (seq) (output regexp + is a string !)*/ + int len; /* length of a string if present */ + int set; /* character set number for the string */ + ADDR ad; /* string or regexp program */ + } SDATA; + + +/* Some compiler represent character codes > 127 as negative numbers, i.e., + * character 255 is -1, char 254 is -2, etc. + * The flag SIGNED_CHAR_TYPE is set by the program, (program checks which + * convention is used. It is set to 1, if characters have sign (i.e., 255=-1) + * and is set to 0 if characters are unsigned (i.e., 255=255). Do not touch + * this declaration, unless you know what you are doing. + */ +int SIGNED_CHAR_TYPE; + + char tabline[MAXBUFF]; /* line of text from conversion table file */ + char last_tab_line[MAXBUFF]; + char *lineptr; /* pointer to the first unread character of tabline */ + int n_line_chars; /* number of characters in tabline buffer */ + int chars_left; /* no. of chars left in input buffer */ + + int memleft; /* tells how much memory is left in allocated area */ + char *memptr; /* pointer for memory allocation area */ + char regerrstr[100]; /* string to hold error message from regular exp */ + reg_exp *regauxptr; /* aux pointer for regular expresion structure */ + int debug_flg=0; /* if 1, then additional info sent to stderr */ + + FILE *inpf; /* input file pointer */ + FILE *outf; /* output file pointer */ + FILE *tabl; /* file with translation table */ + + int n_conv_seq; /* number of conversion sequences */ + SDATA inp_data[MAXPAIRS]; /* structure to hold types, lengths and pointers + for input sequences */ + SDATA out_data[MAXPAIRS]; /* structure to hold types lengths and pointers + for output sequences */ + SDATA inp_SO_data[MAXSETS]; /* structure with types, lens, ptrs for inp_SO*/ + SDATA inp_SO_subs[MAXSETS]; /* holds substitution string/regexp for inp_SO*/ + SDATA inp_SI_data[MAXSETS]; /* structure with types, lens, ptrs for inp_SI*/ + SDATA inp_SI_subs[MAXSETS]; /* holds substitution string/regexp for inp SI*/ + SDATA inp_nest_open[MAXSETS]; /* for sick transliteration cases, like TeX */ + SDATA inp_nest_close[MAXSETS]; /* where you need to count {} pairs */ + + SDATA *junky; + + IMAPP inp_maps; /* maps for single character sequences, + array of pointers. Element of the array is a pointer + to the array of pointers which point at strings */ + OSETP out_sets; /* output set numbers corresponding to inp_maps + array of pointers to integer pointers */ + int n_inp_sets; /* number of input sets */ + int n_out_sets; /* number of output sets */ + char *out_SI[MAXSETS]; /* pointer to output shift in sequences */ + char *out_SO[MAXSETS]; /* pointer to output shift out sequences */ + int out_SI_len[MAXSETS]; /* out SI sequence length */ + int out_SO_len[MAXSETS]; /* out SO sequence length */ + + char *begseq, /* sequence to be written at the beginning of output*/ + *endseq; /* sequence to be written at the end of output file */ + + int file_version; /* conversion table version number */ + int strstart, strend, /* codes delimiting strings */ + liststart, listend, /* codes delimiting lists */ + regexstart, regexend, /* codes delimiting expressions */ + curst1, curend1, + curst2, curend2; /* auxiliary */ + + char scr1[MAXBUFF], scr2[MAXBUFF], /* scratch space */ + scr1a[MAXBUFF], scr2a[MAXBUFF]; /* scratch space */ + char *scr1ptr, *scr2ptr, *scr1pt, *scr2pt, + *scrauxptr, *scrcurptr; /* aux string pointers */ + + int inp_seq_length; /* length of input sequence */ + int out_seq_length; /* length of output sequence */ + int out_set_number; /* number of output set */ + char *out_seq_ptr; /* pointer to output sequence */ + + reg_exp *reg_comp(); + int reg_try(); + void reg_sub(); + void reg_error(); + +/* fix if no strchr routine in the libarry */ +#if STRCHR +#else +#define strchr indexfun +#endif + +/* this is index which is equivalent to strchr */ +char *indexfun (s, c) +char *s; +int c; + { + while (*s) + if (c == *s) return (s); + else s++; + return (NULL); + } + + +/* ============================================================ */ + +/* include code for getopt() if not known to compiler */ + +#if GETOPT +#else + +/* + This is a some getopt I took from the net and do not remember + who actually wrote this +*/ + +#define ARGCH (int)':' +#define BADCH (int)'?' +#define EMSG "" +#define ENDARGS "--" + +/* + * get option letter from argument vector + */ +int opterr = 1, /* useless, never set or used */ + optind = 1, /* index into parent argv vector */ + optopt; /* character checked for validity */ +char *optarg; /* argument associated with option */ + +#define tell(s) fputs(*nargv,stderr);fputs(s,stderr); \ + fputc(optopt,stderr);fputc('\n',stderr);return(BADCH); + + +STATIC int getopt(nargc,nargv,ostr) +int nargc; +char **nargv, + *ostr; +{ + static char *place = EMSG; /* option letter processing */ + register char *oli; /* option letter list index */ + + if(!*place) { /* update scanning pointer */ + if(optind >= nargc || *(place = nargv[optind]) != '-' || + !*++place) return(EOF); + if (*place == '-') { /* found "--" */ + ++optind; + return(EOF); + } + } /* option letter okay? */ + if ((optopt = (int)*place++) == ARGCH || !(oli = strchr(ostr,optopt))) { + if(!*place) ++optind; + tell(": illegal option -- "); + } + if (*++oli != ARGCH) { /* don't need argument */ + optarg = NULL; + if (!*place) ++optind; + } + else { /* need an argument */ + if (*place) optarg = place; /* no white space */ + else if (nargc <= ++optind) { /* no arg */ + place = EMSG; + tell(": option requires an argument -- "); + } + else optarg = nargv[optind]; /* white space */ + place = EMSG; + ++optind; + } + return(optopt); /* dump back option letter */ +} + +#endif + + +/* ================= charcode ====================== + * returns the code of the character given its integer code. If global + * variable SIGNED_CHAR_TYPE flag is 1, then character code is negative + * for chars >= 128, otherwise they are passed through. + * ================================================== */ +STATIC char charcode (intcde) +int intcde; +{ + if(SIGNED_CHAR_TYPE == 1) { /* if signed chars used */ + if(intcde >= 128) { /* if integer code is larger than 128 */ + return((char)(intcde - 256)); /* make it negative complement */ + } + else { /* return the original code */ + return((char)intcde); + } + } + else { + return((char)intcde); + } +} +/* ================= intcode ===================== + * returns integer code of a character depending on the value of + * SIGNED_CHAR_TYPE flag + * =============================================== */ +int intcode(charcde) +char charcde; +{ + if(SIGNED_CHAR_TYPE == 1) { /* if signed chars used */ + if((int)charcde < (int)0) { /* if negative code */ + return((int)((int)charcde + 256)); /* convert to positive */ + } + else { /* return the original code */ + return((int)charcde); + } + } + else { + return((int)charcde); + } +} + +/* ================= tablerr ====================== + * terminates the program with a message to stderr and contents of + * the buffer. num - exit status, errmsg - message + * ================================================ */ +STATIC int tablerr(num, errmsg) +int num; +char *errmsg; +{ + fprintf(stderr,"%s\n", errmsg); + fprintf(stderr, + "Current contents of the input buffer for conversion table file:\n"); + fprintf(stderr,"%s\n", last_tab_line); + exit(num); + return(0); /* to keep compiler happy that there is return from function */ +} + +/* ================= getnblkline ==================== + * gets a nonblank line from tabl file and resets pointers if clearflg == 1, + * otherwise, appends the line to the current buffer. + * Line is stored in the global variable tabline. The global *lineptr + * is reset to line beginning. + * If EOF reached, or line too long, returns -1, else a number of chars in + * the line. If line starts with # or ! in first column, it is skipped. + * ================================================== */ +STATIC int getnblkline(fileptr, clearflg) +FILE *fileptr; +int clearflg; +{ + int l, maxc; + char *auxptr; + if(clearflg == 1) { + n_line_chars = 0; + lineptr = tabline; + } + maxc = MAXBUFF - n_line_chars -2; /* how much space in the buffer */ + while (fgets (lineptr, maxc, fileptr) != NULL) { + strcpy(last_tab_line, lineptr); /* save current line for error messages */ + if((*lineptr == '#') || (*lineptr == '!')) { /* skip comment lines */ + continue; + } + l = strlen(lineptr); /* how many chars we read ? */ + n_line_chars += l; /* how many chars in the buffer */ + + if(n_line_chars > MAXBUFF-5) { /* if line too long */ + return(-1); + } + if(clearflg == 1) { /* do it only if first line is fetched */ + auxptr = lineptr; + while ((isspace(*auxptr) != 0) && (*auxptr != '\0')) { + auxptr++; /* skip front spaces */ + } + + if(*auxptr == '\0') { /* if blank line */ + continue; + } + } + return(l); /* return length of line just read */ + } /* end while */ + return(-1); /* end of file found */ +} +/* ================= chknblk ========================== + * returns a code of the first nonblank character at the current position + * of tabline buffer. The lineptr is left at this char (NOT AT THE NEXT CHAR ! + * If no nonblank * character, returns -1. + * ==================================================== */ +STATIC int chknblk(fileptr) +FILE *fileptr; +{ + int ch; + + Fetch_next: + while (*lineptr != '\0') { + if(isspace(*lineptr) == 0) { + ch = intcode(*lineptr); + return(ch); + } + lineptr++; + } + if(getnblkline(fileptr, 0) > 0) { + goto Fetch_next; + } + else { + return(-1); + } +} + +/* ================= getnumber =========================== + * retrieves integer nonnegative decimal number from a the current + * line (tabline). + * Returns the number, or -9999 if no good number in the line + * only number < 1000 allowed + * ========================================================== */ +STATIC int getnumber(fileptr) +FILE *fileptr; +{ + int num, flg, sign; + num = 0; + flg = 0; + sign = 0; + +Next_line: + while (*lineptr != '\0') { + if(flg == 0) { /* only spaces found till now */ + if(isspace(*lineptr) != 0) { + lineptr++; + continue; + } + else { + flg = 1; /* the nonblank char found */ + } + } + + if(flg == 1) { /* the nonblank char was found */ + if(sign == 0) { /* sign may only be located before the number */ + if(*lineptr == '-') { + sign = -1; + lineptr++; + } + else if(*lineptr == '+') { + sign = 1; + lineptr++; + } + else { /* set it to +1, so it is checked only once */ + sign = 1; + } + } + if(isdigit(*lineptr) != 0) { + num = 10*num + *lineptr - '0'; + if(num > 1000) { + return(-9999); /* number too large */ + } + } + else if(isspace(*lineptr) != 0) { /* end of number */ + return(num*sign); + } + else { + return(-9999); /* some strange character */ + } + } + lineptr++; /* to next character */ + } + if(flg == 1) { /* if valid number collected before '\0' */ + return(num*sign); + } + else { + if(getnblkline(fileptr, 0) > 0) { + goto Next_line; + } + else { + return(-9999); /* if no number before end of file, error */ + } + } +} + + +/* ================= getstring ========================== + * returns the pointer to the string from the tabline. The pointer is + * volatile, and will point to garbage after next getnblkline call, so + * you need to copy it (or use it) immedaitely after the call. + * Returns a pointer to string if successful, and NULL pointer if not. + * startcode --- character code which starts the string (it is not + * included in the string. If startcode = '\0', the + * string is collected from the curent pointer to a buffer. + * endcode --- character which ends the string. It is not included in + * the string. If endcode = '\0', then string is collected + * until first blank or end of string found. + * If no startcode found or no endcode found, the NULL string is returned. + * ========================================================= */ + +STATIC char *getstring(startcode, endcode, fileptr) +int startcode, endcode; +FILE *fileptr; +{ + int flg; + char *startptr; + + flg = 0; +Read_next_line: + while (*lineptr != '\0') { + if(flg == 0) { /* if startcode not found yet */ + if(startcode != 0) { + if(charcode(startcode) == *lineptr) { + flg = 1; /* the startcode found */ + lineptr++; + startptr = lineptr; /* skip startcode */ + continue; + } + else { + lineptr++; + continue; + } + } + else { /* startcode is 0 */ + flg = 1; + startptr = lineptr; + lineptr++; + continue; + } + } /* end flg == 0 */ + + if(flg == 1) { /* the 1st char was found */ + if(endcode == 0) { /* if stop on blank requested */ + if(isspace(*lineptr) != 0) { /* if space found */ + *lineptr = '\0'; /* mark string end */ + lineptr++; /* advance pointer */ + return(startptr); + } + else { /* collect chars */ + lineptr++; + continue; + } + } + else if(charcode(*lineptr) == endcode) { /* if stop at endcode */ + *lineptr = '\0'; + lineptr++; + return(startptr); + } + else { /* if not endcode , collect next characters */ + lineptr++; + continue; + } + } /* end flg == 1 */ + } /* end while */ + + /* the buffer was exhausted */ + if(endcode == 0) { + return(startptr); + } + else { + if(getnblkline(fileptr, 0) > 0) { + goto Read_next_line; + } + else { + return((char *)NULL); + } + } +} + +/* ============================ convnum =================== + * returns a nonegative number based on str. Scans the string + * from position posbeg, and returns first invalid character position + * in posend. If error, returns -1 (less than 2 characters, num > 255). + * str - scanned string + * digits string of allowed ordered digits in lowercase + * posbeg - start + * ======================================================== */ + +STATIC int convnum(buff, digits, posbeg) +char *buff, *digits; +int posbeg; +{ + int num, i, l, d, base; + + base = strlen(digits); + num = 0; + i = posbeg; + + while (buff[i] != '\0') { + d = -1; + for(l = 0; l < base; l++) { + if(buff[i] == digits[l]) { + d = l; + break; + } + } + if(d >= 0) { + num = d + num*base; + if(num > 255) { /* if code too large */ + return(-1); + } + i++; + } + else { + break; + } + } + if((i - posbeg) < 2) { /* if less than two characters in a number */ + return(-1); + } + buff[i] = '\0'; + return(num); +} + + +/* ============================ str2code ================== + * str2code returns a code specified in buff. The valid numbers must have at + * least 2 digits. Here is a format of the code string + * (n represents valid digit for a given base). + * nnn (up to 3 decimal digits, first is not zero) + * 0nnn (up to 3 octal digits) + * 0xnn (up to 3 hex digits) + * 0onnn (up to 3 octal digits) + * 0dnnn (up to 3 decimal digits) + * The buff string will have '\0' put at the position after a valid number + * If no valid number can be parsed, or number is greater than 255, -1 + * is returned. + * ======================================================== */ +STATIC int str2code(buff) +char *buff; +{ + int i, l, num; + static char decdig[]="0123456789", + octdig[]="01234567", + hexdig[]="0123456789abcdef"; + + l = strlen(buff); /* get length */ + if(l < 2) { /* string too short */ + return(-1); + } + + for(i = 0; i < l; i++) { /* convert to lowercase */ + if(isalpha(buff[i]) != 0) { /* if letter */ + buff[i] = tolower(buff[i]); + } + } + + if(isdigit(buff[0]) == 0) { /* if first char not a digit */ + return(-1); + } + + if(buff[0] == '0') { /*if starting char is 0, then octal */ + if((num = convnum(buff, octdig, 0)) != -1) { /* check if no base */ + return(num); + } + } + else { /* this has to be a decimal number */ + if((num = convnum(buff, decdig, 0)) != -1) { + return(num); + } + else { /* error in decimal number */ + return(-1); + } + } + /* the base is specified at buff[1] */ + if(buff[1] == 'o') { + num = convnum(buff, octdig, 2); + } + else if(buff[1] == 'd') { + num = convnum(buff, decdig, 2); + } + else if(buff[1] == 'x') { + num = convnum(buff, hexdig, 2); + } + else { /* no base found */ + return(-1); + } + return(num); +} + +/* ================= convstr ======================== + * copies inp_string to out_strings, and when codes are given as \xxx + * converts them to characters. + * Returns: + * 0 if OK + * 1 if character zero (e.g., \00 or \0x0) is found (it ends the string + * processing, since it is string terminator). + * =================================================== */ +STATIC int convstr(inp_string, out_string) +char *inp_string, *out_string; +{ + int ch, ch1, i, l, num, n; + char buff[8]; + + n = 0; + while ((ch = *inp_string) != '\0') { + n++; /* count characters */ + if(ch == '\\') { + /* skip blank sequence */ + ch1 = *(inp_string + 1); /* charcode following "\" */ + if(isspace(ch1) != 0) { /* if "\" followed by blanks */ + inp_string++; /* skip over "\" */ + n++; + while( isspace((*inp_string)) != 0) { /* skip all spaces */ + n++; + inp_string++; + } + n--; /* it will be advanced at the top of loop */ + ch = *inp_string; + if(ch == '\0') { + *out_string = '\0'; + return(0); + } + continue; /* start new loop turn */ + } /* ch is space */ + + /* now check is \020, etc., i.e., codes */ + for(i = 1; i <= 6; i++) { /* copy possible number to a buffer */ + buff[i-1] = *(inp_string+i); + } + buff[6] = '\0'; /* terminate buff */ + if((num = str2code(buff)) >= 0) { + *out_string++ = charcode(num); + /* find how many characters have beed used ( number + \ ) */ + if(num == 0) { + *out_string = '\0'; + return(1); + } + l = strlen(buff) + 1; + inp_string += l; + continue; + } + } + *out_string++ = *inp_string; + inp_string++; + } /* end while */ + *out_string = '\0'; + return(0); +} + + +/* ====================== compstr ======================== + * returns 1 if str1 is located at the beginning of str2 and 0 otherwise + * ======================================================= */ +STATIC int compstr(str1, str2) +char str1[], str2[]; +{ + int i; + if(str1[0] == '\0') { /* empty sequence never matches */ + return(0); + } + for(i = 0; str1[i] != '\0'; i++) { + if(str1[i] != str2[i]) { + return(0); + } + } + return(1); +} + +/* ====================== chkseqs ============================ + * returns the sequence number if sequence is present at the beginning + * of buffer and -1 otherwise (first sequence has number 0); + * If regular expression, then SDATA.len is set to the length of + * the string which matches the regular expession. + * ============================================================= */ +STATIC int chkseqs(n_seq, seqstruc, buff) +int n_seq; +SDATA *seqstruc; +char *buff; +{ + int i, j, l; + char *sp, *ep, *str; + reg_exp *reaux; + + if(n_seq == 0) { + return(-1); + } + for (i = 0; i < n_seq; i++) { + if(seqstruc->typ == 0) { /* if plain string */ + str = (seqstruc->ad).seq; + if(*str != '\0') { + l = 1; + for(j = 0; *str != '\0'; j++) { + if(*(buff + j) != *str++) { + l = 0; + break; + } + } + if(l == 1) { + return(i); + } + } + } + else if(seqstruc->typ == 1) { /* regexp */ + if(reg_try((seqstruc->ad).re, buff) == 1) { /* if anchored match found */ + reaux = (seqstruc->ad).re; /* get address of search program */ + sp = reaux->startp[0]; /* address of 1st char of match */ + ep = reaux->endp[0]; /* next char after match */ + if(sp != buff) { /* matches are anchored, at the buff beginning ! */ + tablerr(10, "Internal error in regexp package\n"); + } + l = seqstruc->len = ep - sp; /* match length */ + if(l <= 0) { + fprintf(stderr,"Error when matching regular expression %d\n", i+1); + exit(10); + } + return(i); + } + } + seqstruc++; + } /* end for */ + return(-1); +} + +/* =================== rdelim ================== + * read delimiters from tabl file + * ============================================= */ +STATIC int rdelim(startd, endd) +int *startd, *endd; +{ + if(getnblkline(tabl, 1) < 0) { + tablerr(10, "Could not read left delimiter code"); + } + if((*startd = chknblk(tabl)) < 0) { + tablerr(10, "Could not read left delimiter code"); + } + lineptr++; /* point at next char */ + if(isspace(*lineptr) == 0) { + tablerr(10, + "(Left Delimiter):Delimiters should be single chars separated by spaces"); + } + if((*endd = chknblk(tabl)) < 0) { + tablerr(10, "Could not read right delimiter code"); + } + lineptr++; /* point at next char */ + if(isspace(*lineptr) == 0) { + tablerr(10, + "(Right Delimiter):Delimiters should be single chars separated by spaces"); + } + return(0); +} + +/* ================== beseq ============== + * read starting or ending sequence for output + * and return pointer + * ======================================= */ +STATIC char* beseq() +{ + char *scr1pt, *scr2pt; + int l; + + if((getnblkline(tabl, 1) < 0) || + ((scr1pt = getstring(strstart,strend,tabl)) == (char*)NULL)) { + tablerr(10, "Error when reading starting/ending sequence"); + } + l = strlen(scr1pt) + 1; + if((scr2pt = (char*)malloc(l*sizeof(char))) == NULL) { + tablerr(10, "Out of memory"); + } + convstr(scr1pt,scr2pt); + return(scr2pt); +} + +/* ================= allomaps =============== + * Allocate space for maps + * ============================================ */ +STATIC int allomaps(n) +int n; +{ + int i; + /* Allocate space for inp_maps and out_sets for input set 0 */ + if((inp_maps[n] = (IMAP*)malloc(256*sizeof(char*))) == NULL) { + /*if failed */ + tablerr(10, "Out of memory for storing sequences"); + } + + if((out_sets[n] = (OSET*)malloc(256*sizeof(int))) == NULL) { /* if failed */ + tablerr(10, "Out of memory for storing sequences"); + } + + for(i = 0; i < 256; i++) { /* zero allocated memory */ + (*inp_maps[n])[i] = (char*)NULL; + (*out_sets[n])[i] = 0; + } + return(0); +} + +/* =================== savestring ================= + * saves string in the allocated storage and returns pointer to it + * does all the housekeeping + * ================================================== */ +STATIC char *savestring(str) +char *str; +{ + int l; + char *retptr; + + l = strlen(str)+1; + if(memleft < l) { + memleft = 5*MAXPAIRS; + if((memptr = (char*)malloc(memleft*sizeof(char))) + == NULL) { + tablerr(10,"Out of memory for allocation"); + } + } + strcpy(memptr, str); + retptr = memptr; + memptr += l; + memleft -= l; + return(retptr); +} + +/* ================= splitlist ================= + * unfolds the list [] to a list of characters (i.e. [a-d] = [abcd]) + * =============================================== */ +STATIC int splitlist(inlist, unflist) +char *inlist, *unflist; +{ + int ch, ch1, ch2, i, len; + + convstr(inlist, inlist); /* convert codes */ + len = strlen(inlist); + if(len == 0) { + tablerr(10, "Empty list specified"); + } + + *unflist++ = *inlist++; /* save first character */ + while ( *inlist != '\0') { + ch = *inlist; + if((ch != '-') || (*(inlist+1) == '\0')) { + *unflist++ = ch; + } + else { /* the minus is inside */ + ch1 = intcode(*(inlist-1)); + ch2 = intcode(*(inlist+1)); + if(ch2 <= ch1) { + tablerr(10, "The limits in the range within the list are reversed"); + } + for(i = ch1+1; i < ch2; i++) { + *unflist++ = charcode(i); + } + } + inlist++; + } + *unflist = '\0'; + return(0); +} + +/* ======================== regerror ================== + * regerror --- routine called from within a regexp package. Aborts + * program with message + * ==================================================== */ +void reg_error(s) +char *s; +{ + strcat(regerrstr,s); + tablerr(11,regerrstr); +} + +/* ======================== rdinshift ================== + * reads in a shift sequence, assuming that the getnblkline was called + * Fills in structure SDATA. If typ = 1, it is assumed that it is data + * for matching, if typ = 2, this is data to be output + * If OK, returns 0, else dies + * ======================================================= */ +STATIC int rdinshift(sdstr, sttyp) +SDATA *sdstr; +int sttyp; +{ + int mode1; + ADDR ads; + curst1 = chknblk(tabl); /* check what type delimiter */ + if(curst1 == strstart) { + mode1 = 1; + curend1 = strend; + } + else if(curst1 == liststart) { + mode1 = 2; + tablerr(10, "Lists not allowed for input SHIFT sequences"); + } + else if(curst1 == regexstart) { + mode1 = 3; + curend1 = regexend; + } + else { + tablerr(10, "Error when reading SHIFT input sequences"); + } + + if((scr1pt = getstring(curst1, curend1, tabl)) == (char*)NULL) { + tablerr(10, "Error when reading input SHIFT sequences"); + } + + convstr(scr1pt, scr1); /* convert codes in the sequence */ + scr1pt = savestring(scr1); /* save sequence in memory */ + strcpy(regerrstr, "Error in regexp for input SHIFT sequences:"); + + if(mode1 == 1) { + sdstr->typ = 0; /* common string */ + sdstr->len = strlen(scr1pt); + ads.seq = scr1pt; /* save string address */ + sdstr->ad = ads; + } + else if(mode1 == 3) { + if(sttyp == 1) { + sdstr->typ = 1; + regauxptr = reg_comp(scr1pt); + if(regauxptr == (reg_exp *)NULL) { + tablerr(10, "Error in regular expression"); + } + ads.re = regauxptr; + sdstr->ad = ads; + } + else { + sdstr->typ = 2; + sdstr->len = strlen(scr1pt); + ads.seq = scr1pt; + sdstr->ad = ads; + } + } + return(0); +} + +/* ========================= match_subs ========================== + * match_subs matches the match_data sequence description to the + * current position of the input file string (scrcurptr) and if match + * is found, finds the replacement string and puts it in scr1 buffer. + * it sets the global variables out_seq_length, out_seq_ptr, inp_seq_length + * out_set_number. Returns 1 on success, and 0 if match was not found. + * ================================================================== */ +STATIC int match_subs(match_data, repl_data) +SDATA *match_data, *repl_data; +{ + if(chkseqs(1, match_data, scrcurptr) >= 0) { + inp_seq_length = match_data->len; /*chkseqs sets it for inp.typ 1 */ + out_set_number = repl_data->set; + if(repl_data->typ == 2) { /* if regexp substitution */ + /* find a substitution string */ + regauxptr = (match_data->ad).re; /* pointer to regexp prog */ + /* scr contains the substitution string */ + reg_sub(regauxptr, (repl_data->ad).seq, scr1); + out_seq_length = strlen(scr1); /*number of chars in substitute */ + out_seq_ptr = scr1; /* pointer to substitute string */ + } + else { /* if plain string (type = 0) */ + out_seq_length = repl_data->len; + out_seq_ptr = (repl_data->ad).seq; + } + if(out_seq_length > MAXMATCH) { + fprintf(stderr, + "The substitution string is too long (%d chararacters):\n%s\n", + out_seq_length, out_seq_ptr); + exit(1); + } + return(1); + } + else { + return(0); + } +} + +/* =================== repl_inp ============================= + * replaces matching portion of an input text with a substitute string. + * ========================================================== */ +int repl_inp() +{ + int k, l, i; + + if(out_seq_length > MAXMATCH) { + fprintf(stderr, + "The output substitution sequence is too long (%d characters):\n%s\n", + out_seq_length, out_seq_ptr); + exit(1); + } + if(inp_seq_length >= out_seq_length) { /* do not have to copy strings */ + k = inp_seq_length - out_seq_length; /* diff in lengths */ + scrcurptr += k; /* move forwarde by the diff */ + chars_left -= k; + for (i = 0; i < out_seq_length; i++) { /* copy chars */ + *(scrcurptr + i) = *(out_seq_ptr + i); + } + } + else { /* have to push remaining chars to the right to make space */ + k = out_seq_length - inp_seq_length; /* diff in lengths */ + l = strlen(scrcurptr); /* length of input text */ + /* memmove could be used, but it is not in all libaries */ + for (i = l; i >= 0; i--) { /* move to right, start with terminating '\0' */ + *(scrcurptr + i + k) = *(scrcurptr + i); + } + for (i = 0; i < out_seq_length; i++) { /* place the output string */ + *(scrcurptr + i) = *(out_seq_ptr + i); + } + chars_left += k; /* update chars_left, scrcurptr not changed */ + } + return(0); +} + +/*======================== main ================================== */ + +int main(argc, argv) +int argc; +char **argv; +{ + char *tabl_file; /* name of file with conversion table */ + static char deftablfile[200]= + DEFCONVNAME; /* default conversion file name */ + static char deftablpath[200]= + TPATH; /* default conversion file path */ + char table_name[300]; /* working array for conversion table */ + + int level; /* input set nesting level */ + int inp_cur_set[MAXLEVEL];/* set input number being processed */ + int inp_cur_nest[MAXLEVEL]; /* current nesting count for input set */ + int cur_inp_set; /* current input set, same as inp_cur_set[level] */ + int out_cur_set; /* output set level being processed */ + int buffer_size; /* size of input buffer string */ + int opt; /* option letter */ + int mode1, mode2; /* type of string (1=str, 2=list, 3=regex) */ + + int flg, ch, i, j, k, l, n; /* aux variables */ + + +#if GETOPT + extern char *optarg; /* option argument from getopt */ + extern int optind, opterr; /* needed for getopt */ +#endif + + static char usage[]= + "Usage: translit [-i inpfil] [-o outfil] [-t convtabfil] [convtabfil]\n"; + + + inpf = stdin; /* initialize input to standard input */ /*UNIX*/ + outf = stdout; /* initialize output to standard output */ /*UNIX*/ + + /* set SIGNED_CHAR_TYPE flag */ + scr1[0] = '\372'; + if((int)scr1[0] < 0) { + SIGNED_CHAR_TYPE = 1; + } + else { + SIGNED_CHAR_TYPE = 0; + } + +/* if environment is supported */ +#if GETENV + /* if TRANSPATH variable defined, take its contents */ + if((scr1pt = getenv(TRANSPATH)) != (char *)NULL) { + strcpy(deftablpath, scr1pt); + } + if((scr1pt = getenv(DEFNAME)) != (char *)NULL) { + strcpy(deftablfile, scr1pt); + } +#endif + + tabl_file = deftablfile; /* default table file name */ + + flg = 0; /* set to no conv table given as an argument */ + i = j = k = 0; /* flags, for files specified: i-inp, j-out, k-tabl */ + while ((opt = getopt(argc, argv, OPTIONS)) != EOF) { + switch (opt) { + case 'd': + debug_flg = 1; + break; + case 'i': + if(i != 0) { + fprintf(stderr, "You specified option -i twice\n"); + return(1); + } + if((inpf = fopen(optarg, "r")) == NULL) { + fprintf(stderr,"Error: Could not find input file: %s\n", optarg); + return(1); + } + i = 1; + break; + case 'o': + if(j != 0) { + fprintf(stderr, "You specified option -o twice\n"); + return(1); + } + if((outf = fopen(optarg, "r")) != NULL) { + fprintf(stderr, + "Error: Output file: %s already exists! Delete it first.\n", optarg); + exit(3); + } + if((outf = fopen(optarg, "w")) == NULL) { + fprintf(stderr,"Error: Could not open output file: %s\n", optarg); + exit(2); + } + j = 1; + break; + + case 't': + if(k != 0) { + fprintf(stderr, "You specified option -t twice\n"); + return(1); + } + tabl_file = optarg; + flg = 1; + k = 1; + break; + case '?': + fprintf(stderr,"Error: %s\n", usage); + exit(3); + } /* end switch */ + } /* end while */ + + if(optind < argc) { /* check if translation table given w/o option -t */ + if(flg == 1) { + fprintf (stderr,"Error: You specified conversion table file twice\n"); + exit(4); + } + tabl_file = argv[optind]; + if(argc > optind + 1) { + fprintf (stderr,"Error: %s\n", usage); + exit(5); + } + } + + if((tabl = fopen(tabl_file, "r")) == NULL) { /* try to open file with table */ + strcpy(table_name, deftablpath); /* copy path to scratch string */ + strcat(table_name, tabl_file); + if((tabl = fopen(table_name, "r")) == NULL) { /* try to open path/file */ + fprintf(stderr,"Could not find the conversion table file: %s\n", + tabl_file); + exit(6); + } + } + + /* read in file version number */ + if((getnblkline(tabl, 1) < 0) || ((file_version = getnumber(tabl)) < 0)) { + tablerr(7, "Could not read file format number"); + } + if(file_version != 1) { + tablerr(10, "This format of conversion file is not supported"); + } + + /* read in delimiters */ + + rdelim(&strstart, &strend); + rdelim(&liststart, &listend); + rdelim(®exstart, ®exend); + + /* read in starting and ending sequences */ + begseq = beseq(); + endseq = beseq(); + + /* reserve memory for sequences */ + chars_left = 5*MAXPAIRS; /* size of allocated block */ + if((scr1ptr = (char*)malloc(chars_left*sizeof(char))) + == NULL) { + tablerr(10, "Out of memory for storing sequences"); + } + + /* Allocate space for inp_maps and out_sets for input set 0 */ + + allomaps(0); + + /* Read number of input sets */ + if((getnblkline(tabl, 1) <= 0) || ((n_inp_sets = getnumber(tabl)) < 0)) { + tablerr(10, "Error when reading input set count"); + } + + if(n_inp_sets >= MAXSETS) { + tablerr(10, "Too many input shift sequences"); + } + + /* read input SI/SO sequences */ + for (i = 0; i < n_inp_sets; i++) { + /* Allocate space for inp_maps and out_sets for input set i+1 */ + allomaps(i+1); + + /* read in input SHIFTs seq */ + if(getnblkline(tabl, 1) <= 0) { + tablerr(10, "Error when reading output shift sequences"); + } + rdinshift(&inp_SO_data[i], 1); + rdinshift(&inp_SO_subs[i], 2); + if((inp_SO_subs[i].typ == 2) && (inp_SO_data[i].typ == 0)) { + tablerr(10, + "Plain string type for matching and substitution expression for output"); + } + rdinshift(&inp_nest_open[i], 1); + rdinshift(&inp_nest_close[i], 1); + rdinshift(&inp_SI_data[i], 1); + rdinshift(&inp_SI_subs[i], 2); + if((inp_SI_subs[i].typ == 2) && (inp_SI_data[i].typ == 0)) { + tablerr(10, + "Plain string type for matching and substitution expression for output"); + } + + if(debug_flg == 1) { + if(inp_SO_data[i].typ == 0) { + fprintf(stderr,"%2d) inp_SO =|%s| ", i, (inp_SO_data[i].ad).seq); + } + else { + fprintf(stderr,"%2d) inp_SO =%d ", i, inp_SO_data[i].typ); + } + if((inp_SO_subs[i].typ == 0) || (inp_SO_subs[i].typ == 2)) { + fprintf(stderr,"%2d) inp_SOsub =|%s| ", i, (inp_SO_subs[i].ad).seq); + } + else { + fprintf(stderr,"%2d) inp_SOsub =%d ", i, inp_SO_subs[i].typ); + } + if(inp_nest_open[i].typ == 0) { + fprintf(stderr,"nest_open =|%s| ", (inp_nest_open[i].ad).seq); + } + else { + fprintf(stderr,"nest_open =%d ", inp_nest_open[i].typ); + } + if(inp_nest_close[i].typ == 0) { + fprintf(stderr,"nest_close =|%s| ", (inp_nest_close[i].ad).seq); + } + else { + fprintf(stderr,"nest_close =%d ", inp_nest_close[i].typ); + } + if(inp_SI_data[i].typ == 0) { + fprintf(stderr,"inp_SI =|%s|\n", (inp_SI_data[i].ad).seq); + } + else { + fprintf(stderr,"inp_SI =%d\n", inp_SI_data[i].typ); + } + if((inp_SI_subs[i].typ == 0) || (inp_SI_subs[i].typ == 2)) { + fprintf(stderr,"%2d) inp_SIsub =|%s| ", i, (inp_SI_subs[i].ad).seq); + } + else { + fprintf(stderr,"%2d) inp_SIsub =%d ", i, inp_SI_subs[i].typ); + } + } /* end debug_flg */ + } + + if((getnblkline(tabl, 1) <= 0) || ((n_out_sets = getnumber(tabl)) < 0)) { + /* read in out SHIFTs count */ + tablerr(10, "Error when reading output set count"); + } + if(n_out_sets > MAXSETS) { + tablerr(10, "Too many output SHIFT sequences requested"); + } + + for (i = 0; i < n_out_sets; i++) { + /* read in out SHIFTs seq */ + if((getnblkline(tabl, 1) <= 0) || + ((scr1pt = getstring(strstart, strend, tabl)) == (char*)NULL) || + ((scr2pt = getstring(strstart, strend, tabl)) == (char*)NULL)) { + tablerr(10, "Error when reading output shift sequences"); + } + convstr(scr1pt, scr1a); + out_SO_len[i]= strlen(scr1a); + out_SO[i] = savestring(scr1a); + + convstr(scr2pt, scr2a); + out_SI_len[i] = strlen(scr2a); + out_SI[i] = savestring(scr2a); + if(debug_flg == 1) { + fprintf(stderr,"%2d) out_SO string=|%s| out_SI string=|%s|\n", + i, out_SO[i], out_SI[i]); + } + } /* end for */ + + i = 0; + while (getnblkline(tabl, 1) > 0) { + if((inp_data[i].set = getnumber(tabl)) < 0) { /* get inp set number */ + tablerr(10, "Set number for input sequences is wrong"); + } + if(((k = inp_data[i].set) > n_inp_sets) || (k < 0) ) { + tablerr(10,"Input set number for a sequence wrong"); + } + curst1 = chknblk(tabl); /* check what type of string follows */ + if(curst1 == strstart) { + mode1 = 1; + curend1 = strend; + } + else if(curst1 == liststart) { + mode1 = 2; + curend1 = listend; + } + else if(curst1 == regexstart) { + mode1 = 3; + curend1 = regexend; + } + else { + tablerr(10, "Delimiter wrong when reading input sequences"); + } + /* get input sequence */ + if((scr1pt = getstring(curst1, curend1, tabl)) == (char*)NULL) { + tablerr(10, "Error reading input sequence"); + } + scr1pt = savestring(scr1pt); /* Save the string */ + + if((out_data[i].set = getnumber(tabl)) < -3) { /* get inp set number */ + tablerr(10, "Wrong code for the output set number"); + } + + if(out_data[i].set > n_out_sets) { + tablerr(10, "Output set number for a sequence is wrong"); + } + + curst2 = chknblk(tabl); /* check what type of string follows */ + if(curst2 == strstart) { + mode2 = 1; + curend2 = strend; + } + else if(curst2 == liststart) { + mode2 = 2; + curend2 = listend; + } + else if(curst2 == regexstart) { + mode2 = 3; + curend2 = regexend; + } + else { + tablerr(10, "Delimiter wrong when reading sequences"); + } + + if((scr2pt = getstring(curst2, curend2, tabl)) == (char*)NULL) { + tablerr(10, "Error reading input sequence"); + } + scr2pt = savestring(scr2pt); + + /* check if acceptable types for sequences */ + if((mode2 == 3) && (mode1 != 3)) { /* no regular expressions for output */ + tablerr(10, + "Regular expression as output sequence and input not a regular expression"); + } + else if((mode1 == 1) && (mode2 == 2)) { /* inp string, out list */ + tablerr(10, "You specified list for output and string for input"); + } + else if((mode1 == 3) && (mode2 == 2)) { /* inp regex, out list */ + tablerr(10, "You specified string for input and list for output"); + } + else if((mode1 == 2) && (out_data[i].set < 0)) { + tablerr(10, + "Input LIST and output set code -1/-2/-3 is not supported at this moment"); + } + + if(mode1 == 2) { /* if list for input expression */ + /* split string at - sign */ + splitlist(scr1pt, scr1); + if(mode2 == 2) { + splitlist(scr2pt, scr2); + if(strlen(scr1) != strlen(scr2)) { + tablerr(10, + "The number of codes in the input and output list is different"); + } + } + } /* end mode 2 */ + else { /* for all other modes, convert the codes */ + convstr(scr1pt, scr1a); /* convert codes in input string */ + } + + if((mode1 == 1) && (strlen(scr1a) == 1)) { /* single inp char */ + /* it is like list with a single character, so cheat */ + if(out_data[i].set >= 0) { + mode1 = 2; + strcpy(scr1, scr1a); /* make it a list */ + } + else { + tablerr(10, + "One-character input strings and output codes -1/-2/-3 are not supported\n"); + } + } + + if(mode1 == 2) { /* fill the lists for mode 2 */ + if(mode2 == 1) { /* if normal string as output sequence */ + convstr(scr2pt, scr2); + scr2pt = savestring(scr2); + } + else { /* if mode2 = 2 */ + scr2pt = scr2; + } + /* now fill the maps */ + k = inp_data[i].set; + l = out_data[i].set; + scr1pt = scr1; /* points at input list */ + scr2ptr = scr2pt; /* points at output list or string */ + while (*scr1pt != '\0') { + if(mode2 == 2) { /* prepare string with code */ + scr1a[0] = *scr2ptr++; + scr1a[1] = '\0'; + scr2pt = savestring(scr1a); + } + ch = intcode(*scr1pt); + if((*inp_maps[k])[ch] != (char *)NULL) { + fprintf(stderr, +"You have entered the character |%c| with code \\0d%d for input set %d\n", + charcode(ch), ch, k); + tablerr(10, "Delete previous references if not needed"); + } + + (*inp_maps[k])[ch] = scr2pt; /* save output sequence */ + (*out_sets[k])[ch] = l; /* save output set number */ + scr1pt++; /* next code for output */ + } + i--; /* do not save this line in inp_str and out */ + } /* end if mode1 = 2*/ + else if(mode1 == 1) { /* if multicharacter input string */ + scr1pt = savestring(scr1a); + convstr(scr2pt, scr2a); + scr2pt = savestring(scr2a); + inp_data[i].typ = 0; + inp_data[i].len = strlen(scr1pt); + (inp_data[i].ad).seq = scr1pt; + out_data[i].typ = 0; + out_data[i].len = strlen(scr2pt); + (out_data[i].ad).seq = scr2pt; + } + else if(mode1 == 3) { /* if regular expression for input */ + inp_data[i].typ = 1; + l = strlen(scr1a); /* length of converted input expression */ + if(scr1a[0] == '^') { + tablerr(10, + "The ^ (beginning anchor) is not supported"); + } + if((scr1a[l-1] == '$') && (scr1a[l-1] != '\\')) { + tablerr(10, "The $ (end anchor) is not supported"); + } + + strcpy(regerrstr, "Error in input regular expression sequence: "); + + if((regauxptr = reg_comp(scr1a)) == NULL) { + tablerr(10, "Error in the input regular expression sequence"); + } + (inp_data[i].ad).re = regauxptr; + convstr(scr2pt, scr1a); /* convert codes in out string */ + scr2pt = savestring(scr1a); + if(mode2 == 3) { /* mark type of expression plan(0)/substit string(2) */ + out_data[i].typ = 2; + } + else { + out_data[i].typ = 0; + } + out_data[i].len = strlen(scr2pt); + (out_data[i].ad).seq = scr2pt; + } + + /* advance pointers */ + + n_conv_seq = ++i; + if(n_conv_seq >= (MAXPAIRS-1)) { + tablerr(10, + "Too many transliteration sequences. Recompile program with larger MAXPAIRS"); + } + } /* end while getnblkline */ + + if(debug_flg == 1) { + fprintf(stderr,"Multicharacter input sequences \n"); + for(i=0; i < n_conv_seq; i++) { + fprintf(stderr,"%2d) inp.type=%2d inp.set=%2d out.type=%2d out.set=%2d\n", + i, inp_data[i].typ, inp_data[i].set, out_data[i].typ, out_data[i].set); + if(inp_data[i].typ == 0) { + fprintf(stderr," Inp.str=|%s| ", (inp_data[i].ad).seq); + } + fprintf(stderr, "Out.str=|%s|\n", (out_data[i].ad).seq); + } + fprintf(stderr, + "input_set charcode input_character --> output_set output_string/\n"); + for(i = 0; i <= n_inp_sets; i++) { + for(k = 0; k < 256; k++) { + if((*inp_maps[i])[k] != (char *)NULL) { + fprintf(stderr," %2d \\%04o %c --> %2d %s\n", + i, k, charcode(k), (*out_sets[i])[k], (*inp_maps[i])[k]); + } + } + } + } + + + fprintf(outf,"%s",begseq); /* output starting sequence */ + + /* transliterate input file to output file */ + + level = 0; + if(n_inp_sets > 0) { + cur_inp_set = 1; + } + else { + cur_inp_set = 0; + } + inp_cur_set[level] = cur_inp_set; /* 1st input set is a default */ + inp_cur_nest[level] = 0; + scr1ptr = scr1a; + scr1ptr[0] = '\0'; + scr2ptr = scr2a; + scr2ptr[0] = '\0'; + scrcurptr = scr2ptr; + chars_left = 0; + buffer_size = MAXBUFF/2; /* will be set to 0 if EOF */ + out_cur_set = 1; /* no output set yet */ + + + while ( buffer_size > 0) { + /* swap input buffer pointers */ + scrauxptr = scr2ptr; + scr2ptr = scr1ptr; + scr1ptr = scrauxptr; + + scrauxptr = scrcurptr; /* old buffer last pointer */ + scrcurptr = scr1ptr; /* new buffer start */ + + /* copy remains of old buffer to new one */ + strcpy(scrcurptr, scrauxptr); + + l = chars_left; + for(i = 0; i < buffer_size; i++) { /* append input chars to scr1a */ + if((k = fgetc(inpf)) == EOF) { + buffer_size = 0; /* end of file */ + break; + } + else if(k == '\0') { /* skip zero characters */ + i--; + continue; + } + else { /* if normal character */ + *(scrcurptr + l++) = k; + } + } + *(scrcurptr + l) = '\0'; /* terminate buffer with 0 */ + chars_left = l; ; /* length of combined string */ + if(buffer_size == 0) { /* if EOF */ + chars_left += MAXMATCH+1; /* fool the program that there is more */ + } + + while (chars_left > MAXMATCH) { + /* check if end of scrcurptr --- it means end of input file, since only + then it can get to the end of the string, otherwise it stops + MAXMATCH before */ + if(*scrcurptr == '\0') { /* end of file */ + if(n_out_sets > 0) { /* if multiple output sets */ + l = out_SI_len[out_cur_set - 1]; + for(i = 0; i < l; i++) { + k = out_SI[out_cur_set-1][i]; + fputc(k, outf); + } + break; + } + } + +Backstep2: + + /* check if SHIFT IN sequence for current input set */ + if(n_inp_sets > 0) { + /* check SI sequence only when nesting count is 0 */ + if(inp_cur_nest[level] == 0) { + if(match_subs(&inp_SI_data[cur_inp_set-1], + &inp_SO_subs[cur_inp_set-1]) > 0) { /* is SI */ + repl_inp(); + level--; + if(level < 0) { + level = 0; + fprintf(stderr, +"More SHIFT_IN sequences than corresponding SHIFT_OUT sequences in text\n"); + } + cur_inp_set = inp_cur_set[level]; /* set previous inp set number */ + continue; + } + } + } + + /* check if new set of input chars started */ + l = -1; + for (i = 0; i < n_inp_sets; i++) { + if(match_subs(&inp_SO_data[i], &inp_SO_subs[i]) > 0) { + l = i; + break; + } + } + if(l >= 0) { /* is SO matched */ + repl_inp(); /* substitute SO_data with SO_seqs */ + if((inp_SI_data[l].len > 0) || (inp_SI_data[l].typ == 1)){ + /* increase level only is SHIFT IN present */ + level++; /* increase number of "opened" input sets */ + inp_cur_nest[level] = 0; /* It is new level,zero nesting sequences */ + if(level > MAXLEVEL) { + fprintf(stderr, + "Too many nested input character sets in input file\n"); + exit(39); + } + } + l++; /* sets in arrays are saved starting from 0, + i.e., set nr 1 corresponds to element [0], 2 --> [1], etc. */ + inp_cur_set[level] = l; /* save set number at current nesting level */ + cur_inp_set = l; + continue; + } + + /* Now check if the input sequence corresponding to cur_inp_set + matches the string */ + +Backstep1: /* if output set number is -2, start again */ + flg = -1; + for(i = 0; i < n_conv_seq; i++) { + k = inp_data[i].set; /*get set number for current transliteration seq */ + if((k == cur_inp_set) || (k == 0)) { /* if equal to current or 0 */ + if(match_subs(&inp_data[i], &out_data[i]) > 0) { + if(out_set_number < 0) { /* if backsteping */ + repl_inp(); /* replace */ + if (out_set_number == -1) { + flg = -1; + } + else if (out_set_number == -2) { + goto Backstep1; + } + else if (out_set_number == -3) { + goto Backstep2; + } + } + else { /* if set number >= 0 */ + flg = i; + break; + } + } + } + } + + if(flg < 0) { /* if no matching input multichar sequence found */ + ch = intcode(*scrcurptr); /* current input character */ + if((out_seq_ptr = (*inp_maps[cur_inp_set])[ch]) != NULL) { + /* if out_seq exists for current input set */ + out_set_number = (*out_sets[cur_inp_set])[ch]; + flg = 1; + } + else if((out_seq_ptr = (*inp_maps[0])[ch]) != NULL) { + /* if out_seq exitst for set number 0 */ + flg = 1; + out_set_number = (*out_sets[0])[ch]; + } + if(flg >= 0) { /* set other things */ + out_seq_length = strlen(out_seq_ptr); + inp_seq_length = 1; + } + } + + if(flg < 0) { /* if no match found, copy the input char to output */ + scr1[0] = *scrcurptr; + scr1[1] = '\0'; + if(*scrcurptr != '\0') { + out_seq_length = 1; + } + else { + out_seq_length = 0; + } + + inp_seq_length = 1; + out_set_number = 0; + out_seq_ptr = scr1; + } + + /* At this point all matches and substitutuions have been done */ + + /* check if nesting sequences found for a given set and increase or + decrease nesting if needed */ + if((n_inp_sets > 0) && (out_cur_set > 0)) { + for(i = 0; i < inp_seq_length; i++) { + if(chkseqs(1, &inp_nest_close[cur_inp_set-1], scrcurptr+i) >= 0) { + inp_cur_nest[level]--; + } + if(chkseqs(1, &inp_nest_open[cur_inp_set-1], scrcurptr+i) >= 0) { + inp_cur_nest[level]++; + } + } + } + + /* output the SI/SO sequences if output set changed */ + if((n_out_sets > 0) && + (out_set_number > 0)) { /* check if multiple output sets */ + if(out_cur_set != out_set_number) { /* if new set starts */ + if(out_cur_set > 0) { /* put SHIFT IN for a previous set */ + l = out_SI_len[out_cur_set-1]; /* old SHIFT IN seq length */ + for(i = 0; i < l; i++) { /* output old SHIFT IN */ + k = out_SI[out_cur_set-1][i]; + fputc(k, outf); + } + } + out_cur_set = out_set_number; /* make it current now */ + if(out_cur_set > 0) { + l = out_SO_len[out_cur_set-1]; /* length of SHIFT OUT sequence */ + for(i = 0; i < l; i++) { /* output SHIFT OUT seq for this set */ + k = out_SO[out_cur_set-1][i]; + fputc(k, outf); + } + } + } /* end out_set changes */ + } /* if multiple output sets specified */ + + /* now output the corresponding sequence */ + for(i = 0; i < out_seq_length; i++) { + k = *(out_seq_ptr+i); + fputc(k,outf); + } + + /* move past processed input text */ + scrcurptr += inp_seq_length; + chars_left -= inp_seq_length; + + + } /* while scanning input characters */ + + } /* end while reading input file */ + fprintf(outf,"%s",endseq); /* output ending sequence */ + fclose(inpf); + fclose(outf); + exit(0); +} + |