/*  gurmukhi.c
   Preprocessor for TeX for text containing Gurmukhi characters.

   Author:   Amarjit Singh
   E-mail:   [email protected]

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 1, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/

#include <stdio.h>
#include <ctype.h>
#define TRUE 1
#define FALSE 0
#define ILL_CHAR  29
#define DUMMY 30
#define END_OF_FILE 30
#define END_OF_LINE 31
#define N_NOLIGS 40
/* ch_typ values */
#define ILLEGAL 0
#define CMR 1
#define CONTROL 2
#define GM 3
#define NUMERAL 4
/* ch_subtyp values */
#define LO_VOWEL 0
#define HI_VOWEL 1
#define CONSONANT 2
#define SPECIAL 3

#define LBRACE 273
#define RBRACE 264
#define RE 263
#define RN 265
#define RS 256

#define INBUF_LEN 133

/*  GLOBAL VARIABLES */
FILE *f_in,*f_out,*fopen();

short buf_length;
short syll[30]; /* offset of syll is in chr_ptr or cons_ptr */
short cons_ptr, chr_ptr;
short cons_code;

char *strchr();
char wrong[10];
char *p_in,*p_out,*s_ptr,*o_ptr;
char infil[80], outfil[80];
char inbuf[INBUF_LEN], outbuf[133];
char word[500];

unsigned char gm_mode, dollar_mode, cmr_mode;
unsigned char d_found, no_gm, buf_ptr, wait_syl, lin_obey;
unsigned char error, cons_seen, vow_seen;

struct  char_def {
       short ch_typ, ch_subtyp, ch_code, ch_subcode;
};

struct char_def table[127] = {
   {ILLEGAL,SPECIAL,0,0},                        /* 1 not used */
   {GM,CONSONANT,'\126',1},                      /* 2 .t */
   {GM,CONSONANT,'\130',2},                      /* 3 .d */
   {GM,SPECIAL,'\72',0},                         /* 4 .o */
   {GM,CONSONANT,'\132',3},                      /* 5 .n */
   {GM,CONSONANT,'\13',4},                       /* 6 .g */
   {GM,SPECIAL,'\24',0},                         /* 7 .. */
   {GM,CONSONANT,'\14',5},                       /* 8 .K */
   {GM,CONSONANT,'\127',6},                      /* 9 .T */
   {GM,CONSONANT,'\131',7},                      /* 10 .D */
   {GM,SPECIAL,'\54',0},                         /* 11 .m */
   {ILLEGAL,SPECIAL,0,0},                        /* 12 not used */
   {ILLEGAL,SPECIAL,0,0},                        /* 13 not used */
   {ILLEGAL,SPECIAL,0,0},                        /* 14 not used */
   {ILLEGAL,SPECIAL,0,0},                        /* 15 not used */
   {ILLEGAL,SPECIAL,0,0},                        /* 16 not used */
   {ILLEGAL,SPECIAL,0,0},                        /* 17 not used */
   {GM,CONSONANT,'\122',8},                      /* 18 "n */
   {GM,CONSONANT,'\146',9},                      /* 19 "s */
   {ILLEGAL,SPECIAL,0,0},                        /* 20 not used */
   {GM,CONSONANT,'\32', 10},                     /* 21 ~n */
   {GM,SPECIAL,'\137',0},                        /* 22 ~a */
   {ILLEGAL,SPECIAL,0,0},                        /* 23 not used */
   {ILLEGAL,SPECIAL,0,0},                        /* 24 not used */
   {GM,HI_VOWEL,275,'\106'},                     /* 25 ii */
   {GM,LO_VOWEL,274,1},                          /* 26 uu */
   {GM,HI_VOWEL,260,'\117'},                     /* 27 oo */
   {ILLEGAL,SPECIAL,0,0},                        /* 28 not used */
   {ILLEGAL,SPECIAL,0,0},                        /* 29 not used */
   {CONTROL,SPECIAL,0,0},                        /* 30 DUMMY */
   {CONTROL,SPECIAL,0,0},                        /* 31 END_OF_LINE */
   {CONTROL,SPECIAL,0,0},                        /* 32 space */
   {CMR,SPECIAL,0,0},                            /* ! */
   {ILLEGAL,SPECIAL,0,0},                        /* " */
   {ILLEGAL,SPECIAL,0,0},                        /* # */
   {ILLEGAL,SPECIAL,0,0},                        /* $ */
   {ILLEGAL,SPECIAL,0,0},                        /* % */
   {ILLEGAL,SPECIAL,0,0},                        /* & */
   {CMR,SPECIAL,0,0},                            /* ' to - */
   {CMR,SPECIAL,0,0},                            /* ' to - */
   {CMR,SPECIAL,0,0},                            /* ' to - */
   {CMR,SPECIAL,0,0},                            /* ' to - */
   {CMR,SPECIAL,0,0},                            /* ' to - */
   {CMR,SPECIAL,0,0},                            /* ' to - */
   {CMR,SPECIAL,0,0},                            /* ' to - */
   {ILLEGAL,SPECIAL,0,0},                        /* . */
   {GM,SPECIAL,'\40',0},                         /* / */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {NUMERAL,SPECIAL,0,0},                        /* 0 to 9 */
   {CMR,SPECIAL,0,0},                            /* : and ; */
   {CMR,SPECIAL,0,0},                            /* : and ; */
   {ILLEGAL,SPECIAL,0,0},                        /* < */
   {CMR,SPECIAL,0,0},                            /* = */
   {ILLEGAL,SPECIAL,0,0},                        /* > */
   {CMR,SPECIAL,0,0},                            /* ? */
   {GM,SPECIAL,'\177',0},                        /* @ */
   {GM,HI_VOWEL,258,'\101'},                     /* A */
   {GM,CONSONANT,'\102',11},                     /* B */
   {GM,CONSONANT,'\103',12},                     /* C */
   {GM,CONSONANT,'\104',13},                     /* D */
   {GM,HI_VOWEL,259,'\173'},                     /* E */
   {ILLEGAL,SPECIAL,0,0},                        /* F */
   {GM,CONSONANT,'\107',14},                     /* G */
   {GM,SPECIAL,'\54',0},                         /* H */
   {GM,HI_VOWEL,267,'\106'},                     /* I */
   {GM,CONSONANT,'\112',15},                     /* J */
   {GM,CONSONANT,'\113',16},                     /* K */
   {ILLEGAL,SPECIAL,0,0},                        /* L not used */
   {GM,SPECIAL,'\54',0},                         /* M */
   {GM,SPECIAL,'\134',0},                        /* N */
   {GM,HI_VOWEL,261,'\117'},                     /* O */
   {GM,CONSONANT,'\120',17},                     /* P */
   {ILLEGAL,SPECIAL,0,0},                        /* Q */
   {GM,CONSONANT,'\167',18},                     /* R */
   {GM,CONSONANT,'\146',9},                      /* S */
   {GM,CONSONANT,'\124',19},                     /* T */
   {GM,LO_VOWEL,276,1},                          /* U */
   {ILLEGAL,SPECIAL,0,0},                        /* V to Z */
   {ILLEGAL,SPECIAL,0,0},                        /* V to Z */
   {ILLEGAL,SPECIAL,0,0},                        /* V to Z */
   {ILLEGAL,SPECIAL,0,0},                        /* V to Z */
   {ILLEGAL,SPECIAL,0,0},                        /* V to Z */
   {CMR,SPECIAL,0,0},                            /* [ */
   {CONTROL,SPECIAL,0,0},                        /* \ */
   {CMR,SPECIAL,0,0},                            /* ] */
   {ILLEGAL,SPECIAL,0,0},                        /* ^ */
   {ILLEGAL,SPECIAL,0,0},                        /* _ */
   {CMR,SPECIAL,0,0},                            /* ` */
   {GM,HI_VOWEL,'\141',257},                     /* a */
   {GM,CONSONANT,'\142',20},                     /* b */
   {GM,CONSONANT,'\143',21},                     /* c */
   {GM,CONSONANT,'\144',22},                     /* d */
   {GM,HI_VOWEL,262,3},                          /* e */
   {GM,CONSONANT,'\47',23},                      /* f */
   {GM,CONSONANT,'\147',24},                     /* g */
   {GM,CONSONANT,'\150',25},                     /* h */
   {GM,HI_VOWEL,'\151','\105'},                  /* i */
   {GM,CONSONANT,'\152',26},                     /* j */
   {GM,CONSONANT,'\153',27},                     /* k */
   {GM,CONSONANT,'\154',28},                     /* l */
   {GM,CONSONANT,'\155',29},                     /* m */
   {GM,CONSONANT,'\156',30},                     /* n */
   {GM,HI_VOWEL,'\33','\157'},                   /* o */
   {GM,CONSONANT,'\160',31},                     /* p */
   {ILLEGAL,SPECIAL,0,0},                        /* not used */
   {GM,CONSONANT,'\162',32},                     /* r */
   {GM,CONSONANT,'\163',33},                     /* s */
   {GM,CONSONANT,'\164',34},                     /* t */
   {GM,LO_VOWEL,'\165',0},                       /* u */
   {GM,CONSONANT,'\166',35},                     /* v */
   {ILLEGAL,SPECIAL,0,0},                        /* not used */
   {ILLEGAL,SPECIAL,0,0},                        /* not used */
   {GM,CONSONANT,'\171',36},                     /* y */
   {GM,CONSONANT,'\51',37},                      /* z */
   {CONTROL,SPECIAL,0,0},                        /* left brace */
   {GM,SPECIAL,'\56',0},                         /* | */
   {CONTROL,SPECIAL,0,0},                        /* right brace */
   {ILLEGAL,SPECIAL,0,0},                        /* ~ */
   {ILLEGAL,SPECIAL,0,0}                         /* del */
};

char chset1[12]= {'k','t','d','o','n','g','.','K','T','D','m',' '};/* . */
char chset4[10]= {'k','g','c','j','t','d','p','b','s',' '};        /* h */
char chset3[3] = {'n','s',' '};                                    /* " */
char chset6[3] = {'n','a',' '};                                    /* ~ */
char chset2[3] = {'a','i',' '};                                    /* a */
char chset5[2] = {'A','E'};                      /* result of aa and ai */

char out_string[277][7] = {
     "\\7{","\\8{","\\9{","\\?","\\<","^^E","^^F",
      "^^G","^^H","^^I","^^J","^^K","^^L","\\0",
      "\\qx{","^^O","^^P","^^Q","^^R",
      "^^S","^^T","^^U","^^V","^^W","^^X",
      "^^Y","^^Z","^^[","^^\\","^^]",
      "^^^","\\qy{","\\1","!","\"","\\#","\\$",
      "\\%","\\&","\'","(",")","*","+",",","-",".","/","0","1","2",
      "3","4","5","6","7","8","9",":",";","<","=",">","?","@",
      "A","B","C","D","E","F","G","H","I","J","K","L","M","N",
      "O","P","Q","R","S","T","U","V","W","X","Y","Z","[","\\2",
      "]","\\qq{","\\35Fw","`","a","b","c","d","e","f","g","h","i","j",
      "k","l","m","n","o","p","q","r","s","t","u","v","w","x",
      "y","z","\\4","\\qz{","\\5","\\6{","^^?",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","","","","",
      "","","{\\rs ","","aA","a\\4",
      "ao","aO","a\\?","\\re}","}","\\rn{",
      "{\\rdt}","iF","{\\qva}","{\\qvb}","{\\qvc}",
      "\\qa{","\\qb{","{", "\\7{u}","Ei", "\\8{u}"
};

/* strstr : to make it portable */
char *st_find(s,q)
char *s, *q;
{
   short i,j,k,l;
   j = strlen(s);
   k = strlen(q);
   for (i=0; i <= j-k; i++) {
     if (s[i]==q[0]) {
       for (l = 1; (s[i + l] == q[l]) && (q[l] != '\0'); l++) ;
       if (q[l] == '\0') return(&s[i]);
     }
   }
   return(NULL);
}

/* strchr : to make it portable */
char *ch_find(s,q)
char *s;
char q;
{
   short i,j;
   j = strlen(s);
   for (i=0; i < j; i++)
     if (s[i]==q) return(&s[i]);
   return(NULL);
}

err_ill(str)
char *str;
{
   printf("Error: illegal character(s): %s\n",str);
   puts(inbuf);
   getchar();
   error = TRUE;
}

/* Find {\gm in inbuf, copy everything till {\gm to outbuf */
/* and modify rest of it back to inbuf                     */
/* RETURN : TRUE if inbuf is valid                         */
/*          FALSE otherwise                                */
/* other ouputs : no_gm                                    */
/*                d_found                                  */
char find_gm()
{
   char *d_ptr;
   char *gm_ptr;
   char *svbuf;
   char again;

   d_found = FALSE;
   svbuf = inbuf;
   do {
     again = FALSE;
     gm_ptr = st_find(svbuf,"{\\gm");
     if (gm_ptr != NULL) {
       again = isalpha(gm_ptr[4]);
       svbuf = gm_ptr + 4;
     }
   } while(again);

   if (dollar_mode) {
     d_ptr = ch_find(inbuf,'$');
     if ((d_ptr != NULL) && ((gm_ptr == NULL) || (d_ptr < gm_ptr))) {
       d_found = TRUE;
       gm_ptr = d_ptr;
     }
   }

   if (gm_ptr == NULL)
     return(FALSE);
   strncat(outbuf,inbuf,gm_ptr-inbuf);
   no_gm = FALSE;
   if (!d_found) {
     if (gm_ptr[4] == '#') {
       no_gm = TRUE;
       gm_ptr += 1;
     } else {
       strcat(outbuf,"{\\gm");
     }
     gm_ptr += 4;
   } else {
     switch(dollar_mode) {
       case 1: strcat(outbuf,"{\\gm ");
               break;
       case 2: strcat(outbuf,"\\pgm ");
       case 3: no_gm = TRUE;
     }
     gm_ptr += 1;
   }
   strcpy(inbuf,gm_ptr);
   return(TRUE);
}

/* Returns one char from "inbuf" at "buf_ptr"               */
/* if inbuf is at END_OF_LINE then refills it from "f_in"   */
/* other inputs : p_in                                      */
/* other outputs: p_in, buf_length                          */
char inp_ch()
{
   char ch, ch_out;
   ch = inbuf[buf_ptr++];
   if (ch == '\n') {
     if (p_in == 0)
       ch_out = END_OF_FILE;
     else {
       p_in = fgets(inbuf,INBUF_LEN,f_in);
       buf_ptr = 0;
       buf_length = strlen(inbuf);
       ch_out = END_OF_LINE;
     }
   } else {
     if (ch < 32)
       ch_out = ILL_CHAR;
     else
       ch_out = ch;
   }
   return(ch_out);
}

put_macro(macro)
short macro;
{
   char tmp[5];
   int  l, i;
   if (syll[chr_ptr-1] == '\175') {
     syll[chr_ptr+1] = '\175';
     syll[chr_ptr] = RBRACE;
     syll[chr_ptr-1] = syll[chr_ptr-2];
     syll[chr_ptr-2] = macro;
     chr_ptr += 2;
   } else if (syll[chr_ptr-1]==RBRACE) {
     syll[chr_ptr-3] = 271;
     syll[chr_ptr] = LBRACE;
     sprintf(tmp,"%d",macro);
     l = strlen(tmp);
     chr_ptr += 1;
     for (i = 0; i < l; i++)
       syll[chr_ptr+i] = tmp[i];
     chr_ptr += l;
     syll[chr_ptr] = RBRACE;
     chr_ptr += 1;
   } else {
     syll[chr_ptr] = syll[chr_ptr-1];
     syll[chr_ptr-1] = macro;
     syll[chr_ptr+1] = RBRACE;
     chr_ptr = chr_ptr +2;
   }
}

/* find out string corresponding to chars in syll */
/* and then move to word.                         */
/* Reset related flags                            */
put_syll()
{
   short i;
   for (i = 0; i <= (chr_ptr-1); i++)
     strcat(word,&out_string[syll[i]][0]);
   chr_ptr = 0;
   cons_seen =FALSE;
   vow_seen =FALSE;
   wait_syl = FALSE;
}

/* puts out string for one code ( or char ) in word */
put_sym(code)
short code;
{
   strcat(word,&out_string[code][0]);
}

/* move word to outbuf */
/* if not lin_obey then
    put outnuf in f_out
    keep moving word to outbuf to f_out till
      len of word is bigger than 80
  At end of this routine outbuf may have a valid string
*/
put_word()
{
   short lw,lb;
   lw = strlen(word);
   lb = strlen(outbuf);
   if (((lb + lw) > 80) && !lin_obey)
   {
     if (lb > 1)
     {
       if (outbuf[lb-1] != ' ')
         strcat(outbuf,"%\n");
       else
         outbuf[lb-1] = '\n';
       fputs(outbuf,f_out);
     }
     while (lw > 80)
     {
       strncpy(outbuf,word,79);
       outbuf[79] = '\0';
       strcat(outbuf,"%\n");
       fputs(outbuf,f_out);
       strcpy(word,&word[79]);
       lw = strlen(word);
     }
     strcpy(outbuf,word);
   }
   else
     strcat(outbuf,word);
   strcpy(word,"");
}

/* copies c to word  and                    */
/* if c is space then outputs a word        */
sendchar(c)
char c;
{
   char cstr[2];
   cstr[0] = c;
   cstr[1] = '\0';
   strcat(word,cstr);
   if (isspace(c))
     put_word();
}

put_ch(code)
short code;
{
   struct char_def *c_ptr;
   short i;

   c_ptr = &table[code-1];
   switch(c_ptr->ch_typ) {
   case GM:
     if (cmr_mode) {
       cmr_mode = FALSE;
       put_sym(RE);
     }
     switch(c_ptr->ch_subtyp) {
     case HI_VOWEL:
       if (wait_syl)
         put_syll();
       if (cons_seen) {
         if (code == 'i') {
           for (i = chr_ptr; i>= (cons_ptr+1); i--)
             syll[i] = syll[i-1];
           syll[cons_ptr] = c_ptr->ch_subcode;
         } else {
           syll[chr_ptr] = c_ptr->ch_subcode;
           if ((code != 'A') && (code != 'a'))
             vow_seen = TRUE;
         }
       } else
           syll[chr_ptr] = c_ptr->ch_code;
       chr_ptr = chr_ptr + 1;
       wait_syl = TRUE;
       cons_seen = FALSE;
       break;
     case LO_VOWEL:
       if (wait_syl)
         put_syll();
       if (cons_seen)
       {
         put_macro(c_ptr->ch_subcode);
         chr_ptr = chr_ptr - 1;
       }
       else
         syll[chr_ptr] = c_ptr->ch_code;
       chr_ptr = chr_ptr + 1;
       wait_syl = TRUE;
       cons_seen = FALSE;
       break;
     case CONSONANT:
       if (wait_syl)
         put_syll();
       if (!cons_seen) {
         cons_seen = TRUE;
         cons_ptr = chr_ptr;
         cons_code = c_ptr->ch_subcode;
         syll[chr_ptr] = c_ptr->ch_code;
         chr_ptr += 1;
       } else {
         if (code == 'r') {
           syll[chr_ptr] = '\175';
           chr_ptr = chr_ptr + 1;
           cons_code = 0;
         } else if (code == 'h') {
           syll[chr_ptr] = '\110';
           chr_ptr = chr_ptr + 1;
           cons_code = 0;
         } else if (code == 'v') {
           syll[chr_ptr] = '\46';
           chr_ptr = chr_ptr + 1;
           cons_code = 0;
         } else {
           cons_code = c_ptr->ch_subcode;
           syll[chr_ptr] = c_ptr->ch_code;
           chr_ptr += 1;
         }
       }
       break;

     case SPECIAL:
       put_syll();
       put_sym(c_ptr->ch_code);
     }
     break;

   case ILLEGAL:
     wrong[0] = code; wrong[1] = '\0';
     err_ill(wrong);
     break;

   case CONTROL:
     if (cmr_mode) {
       cmr_mode = FALSE;
       put_sym(RE);
     } else {
         put_syll();
     }
     if (code == END_OF_LINE) {
       put_word();
       strcat(outbuf,"\n");
       fputs(outbuf,f_out);
       strcpy(outbuf,"");
       if (!lin_obey)
         fputs("\n",f_out);
     } else if (code != DUMMY)
       sendchar(code);
     break;

   case CMR:
     if (cmr_mode)
       sendchar(code);
     else {
       cmr_mode = TRUE;
         put_syll();
       put_sym(RS);
       sendchar(code);
     }
     break;

   case NUMERAL:
       sendchar(code);
     break;
   } /* end of switch on ch_typ */
}

/* This routine resolves the combination of charaters        */
/* used to symbolize font characters                         */
/* i.e. it cooks set of input chars to one offset in "table" */
gmproc(symbol)
char symbol;
{
   unsigned char saved,gmready;
   short brace_lev;
   char savchr;
   short i;

   brace_lev = 1;
   saved = FALSE;
   gmready = FALSE;
   do {
     switch(symbol) {
     case '.':
       savchr = inp_ch();
       i = 0;
       do {
         i++;
       } while ((i != 12) && (chset1[i-1] != savchr));
       if (i == 12) {
         wrong[0] = '.';
         wrong[1] = savchr;
         wrong[2] = '\0';
         err_ill(wrong);
       } else if (i < 4) {
           savchr = inp_ch();
           if (savchr == 'h')
             i = i+7;
           else {
             saved = TRUE;
             if (i == 1)
               err_ill(".k");
           }
       }
       if (!error) {
         if (i == 11)
           put_ch('M');
         else
           put_ch(i);
       }
       break;
     case 'i':
       savchr = inp_ch();
       if (savchr == 'i')
         put_ch(25);
       else {
         put_ch(symbol);
         saved = TRUE;
       }
       break;
     case 'u':
       savchr = inp_ch();
       if (savchr == 'u')
         put_ch(26);
       else {
         put_ch(symbol);
         saved = TRUE;
       }
       break;
     case 'o':
       savchr = inp_ch();
       if (savchr == 'o')
         put_ch(27);
       else {
         put_ch(symbol);
         saved = TRUE;
       }
       break;
     case 'a':
       savchr = inp_ch();
       i = 0;
       do {
         i++;
       } while ((i != 3) && (chset2[i-1] != savchr));
       if (i == 3)
       {
         put_ch(symbol);
         saved = TRUE;
       } else
         put_ch(chset5[i-1]);
       break;

     case '\"':
       savchr = inp_ch();
       i = 0;
       do {
         i++;
       } while ((i != 3) && (chset3[i-1] != savchr));
       if (i == 3) {
         wrong[0] = '~';
         wrong[1] = savchr;
         wrong[2] = '\0';
         err_ill(wrong);
       } else
         put_ch(i+17);
       break;

     case '~':
       savchr = inp_ch();
       i = 0;
       do {
         i++;
       } while ((i != 3) && (chset6[i-1] != savchr));
       if (i == 3) {
         wrong[0] = '~';
         wrong[1] = savchr;
         wrong[2] = '\0';
         err_ill(wrong);
       } else
         put_ch(i+20);
       break;

     case END_OF_LINE:
       if (lin_obey) {
         put_ch(END_OF_LINE);
         put_ch(END_OF_LINE);
       } else {
         do {
           savchr = inp_ch();
         } while (savchr == ' ');
         if (savchr == END_OF_LINE)
           put_ch(END_OF_LINE);
         else {
           put_ch(' ');
           saved = TRUE;
         }
       }
       break;

     case '$':
       if (!dollar_mode)
         put_ch(symbol);
       else {
         if (no_gm)
           put_ch(DUMMY);
         else
           put_ch('}');
         gmready= TRUE;
         put_word();
         strcpy(inbuf,&inbuf[buf_ptr]);
       }
       break;

     case '}':
       brace_lev = brace_lev-1;
       if ((brace_lev == 0) && !d_found) {
         if (no_gm)
           put_ch(DUMMY);
         else
           put_ch(symbol);
         gmready= TRUE;
         put_word();
         strcpy(inbuf,&inbuf[buf_ptr]);
       } else
         put_ch(symbol);
       break;

     case '{':
       put_ch(symbol);
       brace_lev = brace_lev+1;
       break;

     case '%':  do
       symbol = inp_ch();
       while(symbol != END_OF_LINE);
       break;

     case '\\':
       put_ch(symbol);
       symbol = inp_ch();
       if (symbol == END_OF_LINE) {
           put_word();
           strcat(outbuf,"\n");
           fputs(outbuf,f_out);
           strcpy(outbuf,"");
       } else {
         if (!isalnum(symbol))
           sendchar(symbol);
         else {
           do {
             sendchar(symbol);
             symbol = inp_ch();
           } while (isalnum(symbol));
           savchr = symbol;
           saved = TRUE;
         }
       }
       break;

     case ILL_CHAR: err_ill('\0');
       break;

     case END_OF_FILE:
       puts("error: missing }");
       error = TRUE;
       break;

     default:
       i = 0;
       do {
         i++;
       } while ((i != 10) && (chset4[i-1] != symbol));
       if (i == 10)
         put_ch(symbol);
       else {
         savchr = inp_ch();
         if (savchr == 'h') {
             put_ch(symbol-32);
         } else {
           put_ch(symbol);
           saved = TRUE;
         }
       }
       break;
     } /* end of switch */
     if (saved) {
       symbol = savchr;
       saved = FALSE;
     } else if ((!error) && (!gmready)) {
         symbol = inp_ch();
     }
   } while (!error && !gmready);
}

main(argc,argv)
int argc;
char *argv[];
{
   unsigned char gm_yes;
   /* get file specifications */

   if (argc == 3) {
     strcpy(infil,argv[1]);
     strcpy(outfil,argv[2]);
   } else if (argc == 2) {
       strcpy(infil,argv[1]);
       strcpy(outfil,"");
   } else {
     do {
       printf("input file: ");
       gets(infil);
     } while (strlen(infil) == 0);
     printf("output file: ");
     gets(outfil);
   }
   s_ptr = strchr(infil,'.');
   if (strlen(outfil) == 0) {
     strcpy(outfil,infil);
     o_ptr = strchr(outfil,'.');
     if (o_ptr != 0)
       *o_ptr = '\0';
   }
   if (s_ptr == 0)
     strcat(infil,".gm");
   o_ptr = strchr(outfil,'.');
   if (o_ptr == 0)
     strcat(outfil,".tex");

   /* open files */

   if ((f_in = fopen(infil,"r")) == NULL) {
     printf("cannot open file %s\n",infil);
     exit(1);
   }
   if ((f_out = fopen(outfil,"w")) == NULL) {
     printf("cannot open file %s\n",infil);
     exit(1);
   }

   /* initialization */

   error = FALSE;
   cons_seen = FALSE;
   vow_seen = FALSE;
   wait_syl = FALSE;

   cmr_mode = FALSE;
   gm_mode = TRUE;
   lin_obey = FALSE;
   dollar_mode = 0;

   chr_ptr = 0;
   strcpy(word,"");
   strcpy(outbuf,"");

   /* read preprocessor commands */

   while (TRUE) {
     strcpy(inbuf,"");
     p_in = fgets(inbuf,INBUF_LEN,f_in);
     if (inbuf[0] != '@')
       break;
     while (TRUE) {
       if (st_find(inbuf,"dollars") == &inbuf[1]) {
         dollar_mode = 1;
         break;
       }
       if (st_find(inbuf,"punjabi") == &inbuf[1]) {
         /* reserved for hindi & pujabi merger */
         break;
       }
       if (st_find(inbuf,"obeylines") == &inbuf[1]) {
         lin_obey = TRUE;
         break;
       }
       if (st_find(inbuf,"dolmode1") == &inbuf[1]) {
         dollar_mode = 1;
         break;
       }
       if (st_find(inbuf,"dolmode2") == &inbuf[1]) {
         dollar_mode = 2;
         break;
       }
       if (st_find(inbuf,"dolmode3") == &inbuf[1]) {
         dollar_mode = 3;
         break;
       }
       printf("Error: illegal preprocessor command\n");
       puts(inbuf);
       getchar();
       break;
     }
   }

   /* main loop */

   do {
     if (!find_gm())
       fputs(inbuf,f_out);
     else {
       do {
         buf_length = strlen(inbuf);
         buf_ptr = 0;
         gmproc(inp_ch());
         if (!error)
           gm_yes = find_gm();
         if (!gm_yes) {
           strcat(outbuf,inbuf);
           fputs(outbuf,f_out);
         }
       } while (gm_yes && !error);
     }
     strcpy(inbuf,"");
     p_in = fgets(inbuf,INBUF_LEN,f_in);
     strcpy(outbuf,"");
   } while ((p_in != NULL) && !error);
   fclose(f_in);
   fclose(f_out);
}