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

    Author:   Amarjit Singh
    E-mail:   asingh@evolving.com

    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);
}