/***************************************************************************
 *                                                                         *
 *   MSGRDR.C                                                              *
 *                                                                         *
 *   Copyright (c) 1993-1997 Galacticomm, Inc.    All rights reserved.     *
 *                                                                         *
 *   Utilities for reading and writing multilingual .MSG files.            *
 *                                                                         *
 *                                               - R. Stein  1/21/93       *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"

#define FILREV "$Revision: 7 $"
                                   /*--- rdmsg() inputs ---                */
const CHAR *curfn="the .MSG file";       /* filename of current MSG file         */

FILE *rdfp;                        /* file to rdmsg() or rdopt() from      */

                                   /*--- rdmsg() outputs ---               */
LONG cmtptr;                       /* pointer to comment preceding message */
INT cmtlen;                        /* length of that comment               */
CHAR msgnam[MOPTLEN+1];            /* message name                         */
CHAR *txtbuf=NULL;                 /* Text buffer for 'T' vbls             */
CHAR hlpbuf[HLPLEN+1];             /* buffer for option help message       */

struct altlng *altlnm=NULL;        /* dyn array of alternate language info */
                                   /* altlnm[0] is output by rdmsg()       */
                     /* altlnm[1]..altlnm[salingo-1] is output by scanalt()*/

INT maxlnm=0;                      /* max # langs currently initialized for*/

UINT bufsize=0;                    /* size of txtbuf, including NUL term   */
INT salingo;           /* scanalt() reports # of alternates (incl lingo 0) */
INT sachar;       /* last character (or EOF) read by scanalt() or litopts()*/
INT valsiz;         /* chars read by getvalbuf(),getval(),loadtv() (no NUL)*/
INT sincebl;                      /* Chars since last \n -- set by nxtink()*/
INT gvbust=0;            /* set to 1 by getval() to indicate option too big*/
INT tdremv=1;                /* should getvalbuf(), etc. remove '~' tildes?*/

CHAR lobuf[LOBMAX+1];                       /* buffer output for litopts() */

VOID
inimsgrdr(                         /* initialize msg reader/text buffers   */
UINT siz)                          /* size of text buffers                 */
{
     if (altlnm == NULL || nlingo > maxlnm) {
          maxlnm=max(1,nlingo);
          if (altlnm != NULL) {
               free(altlnm);
          }
          altlnm=(struct altlng *)alczer(maxlnm*sizeof(struct altlng));
     }
     if (txtbuf == NULL || siz > bufsize) {
          bufsize=siz;
          if (txtbuf != NULL) {
               free(txtbuf);
          }
          txtbuf=alcmem(bufsize);
     }
}

INT                                /*   returns 0 if EOF reached           */
rdmsg(VOID)                        /* reads msg from file rdfp             */
{
     INT c,lastc=' ';
     INT bsince;
     INT ni;
     LONG valoff;
     CHAR mnmlcl[MOPTLEN+1+1];

     if ((c=nxtink()) == EOF) {
          return(0);
     }
     setmem(hlpbuf,cmtlen=sincebl,' ');
     mnmlcl[ni=0]='\0';
     cmtptr=ftell(rdfp)-1-sincebl;
     do {
          hlpbuf[cmtlen++]=c;
          if (isalnum(c)) {
               if (ni < MOPTLEN+1) {
                    mnmlcl[ni++]=c;
                    mnmlcl[ni]='\0';
               }
          }
          else {
               ni=0;
          }
          if (isspace(lastc) && !isspace(c)) {
               bsince=1;
          }
          else {
               bsince++;
          }
          lastc=c;
     } while ((c=getc(rdfp)) != EOF && c != OPNMSG);
     if (c == EOF) {
          return(0);
     }
     if (strlen(mnmlcl) > MOPTLEN) {
          mnmlcl[MOPTLEN]='\0';
          catastro("Option name \"%s...\" is too long in %s",mnmlcl,curfn);
     }
     strcpy(msgnam,mnmlcl);
     valoff=ftell(rdfp);
     cmtlen=min(HLPLEN,(INT)(valoff-1-bsince-strlen(STR_EOL)-cmtptr));
     cmtlen=max(cmtlen,0);
     hlpbuf[cmtlen]='\0';
     altlnm[0].value.fsk=valoff;
     altlnm[0].altered=0;
     if (!getval(rdfp)) {
          catastro("After \"%s {\", we can't find a normal \"}\" in %s",
                   msgnam,curfn);
     }
     sachar='\0';
     return(1);
}

             /* salingo is number of alternates read, including language 0 */
                     /* salingo is never allowed to get larger than nlingo */
                    /* altlnm[1..salingo-1].value are the implicit outputs */
                            /* 1 extra character will be read and returned */
                                  /* implicit inputs:  rdfp, msgnam, curfn */
INT
scanalt(VOID)   /* scan alternate language version(s) of getval()'d option */
{
     INT needcomma;

     salingo=0;
     needcomma=1;
     while (1) {
          switch (sachar=getc(rdfp)) {
          case ',':
               if (++salingo >= nlingo) {
                    catastro("There are more than %d languages defined for "
                             "option\n%s in %s.",nlingo,msgnam,curfn);
               }
               altlnm[salingo].value.fsk=0L;
               needcomma=0;
               break;
          case OPNMSG:
               if (needcomma) {
                    catastro("The alternate language versions of %s in %s\n"
                             "need to be separated by commas",msgnam,curfn);
               }
               altlnm[salingo].value.fsk=ftell(rdfp);
               altlnm[salingo].altered=0;
               getvalbuf(rdfp,NULL);
               needcomma=1;
               break;
          default:
               salingo++;
               return(sachar);
          }
     }
}

VOID
litopts(VOID)                      /* read specs line into lobuf[] stg     */
{
     INT ln;

     for (ln=0 ; sachar != EOF && sachar != '\n' && ln < LOBMAX ; ) {
          if (sachar != '\r' && sachar != '\0') {
               lobuf[ln++]=sachar;
          }
          sachar=getc(rdfp);
     }
     lobuf[ln]='\0';
}

CHAR
lotype(VOID)                       /* opt type, per recent litopts() call  */
{
     CHAR *cp,*ap;

     if (*(cp=skpwht(lobuf)) == OPNHNG) {
          if ((ap=strchr(cp,CLSHNG)) != NULL) {
               cp=skpwht(ap+1);
          }
     }
     return(*cp);
}

VOID
loadtv(                            /* put 'T' option value in txtbuf       */
FILE *msgfp,                       /*   .MSG file                          */
struct altlng *alptr)              /*   ptr to info for appropriate lang   */
{
     if (alptr->altered) {
          strcpy(txtbuf,alptr->value.ptr);
     }
     else {
          fseek(msgfp,alptr->value.fsk,SEEK_SET);
          getval(msgfp);
     }
}

                                   /*   puts result in txtbuf              */
INT                                /*   returns 1=ok 0=EOF                 */
getval(                            /* reads in current 'T' option from file*/
FILE *msgfp)                       /*   file ptr to read from              */
{
      return(getvalbuf(msgfp,txtbuf));
}

INT                                /*   returns 1=ok 0=EOF                 */
getvalbuf(                         /* reads in current 'T' option from file*/
FILE *msgfp,                       /*   file ptr to read from              */
CHAR *buf)                         /*   where to put it (NULL=nowhere)     */
{
     INT c;
     INT eschar;

     eschar=0;
     for (valsiz=0 ; (c=getc(msgfp)) != EOF && (eschar || c != CLSMSG) ; ) {
          if (eschar) {
               if ((c == '~' || c == '}' || c == '|' || c == ']') && tdremv) {
                    valsiz--;
               }
               eschar=0;
          }
          else if (c == '~') {
               eschar=1;
          }
          if (c != '\r') {
               if ((UINT)valsiz < bufsize-1) {
                    if (buf != NULL) {
                         buf[valsiz]=c;
                    }
                    valsiz++;
               }
               else {
                    gvbust=1;
               }
          }
     }
     if (buf != NULL) {
          buf[valsiz]='\0';
     }
     return(c != EOF);
}

VOID                               /*   uses txtbuf as input               */
putval(                            /* writes the current 'T' opt to a file */
FILE *msgfp)                       /*   file to write to                   */
{
     CHAR c,*vp;

     for (vp=txtbuf ; (c=*vp) != '\0' ; vp++) {
          switch (c) {
          case '\n':
               if (putc('\r',msgfp) == EOF) {
                    catastro("Disk full!");
               }
               break;
          case '~':
          case '}':
               if (putc('~',msgfp) == EOF) {
                    catastro("Disk full!");
               }
               break;
          }
          if (putc(c,msgfp) == EOF) {
               catastro("Disk full!");
          }
     }
}

INT                                /*   returns next CHAR                  */
nxtink(VOID)                       /* fnd nxt non-white char from .MSG file*/
{
     INT c;

     sincebl=0;
     while ((c=getc(rdfp)) != EOF && isspace(c)) {
          if (c == '\n') {
               sincebl=0;
          }
          else {
               sincebl++;
          }
     }
     return(c);
}

VOID
clsmsgrdr(VOID)                    /* close down msg reader/text buffers   */
{
     if (altlnm != NULL) {
          free(altlnm);
          altlnm=NULL;
     }
     if (txtbuf != NULL) {
          free(txtbuf);
          txtbuf=NULL;
     }
}
