/***************************************************************************
 *                                                                         *
 *   MCVAPI.C                                                              *
 *                                                                         *
 *   Copyright (c) 1987-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   This file contains a library of routines for dealing with .MCV files  *
 *   (output from WGSMSX).  These routines allow the converted messages    *
 *   to be retrieved by message number, which will generally be done thru  *
 *   the symbolic names "#defined" in WGSMSX's output header file.         *
 *                                                                         *
 *                                            - T. Stryker 6/10/86         *
 *                                            - R. Stein 1/31/88           *
 *                                            - T. Stryker 2/9/92          *
 *                                            - R. Stein 1/9/93            *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"

#define FILREV "$Revision: 4 $"

#define MCVEND 16               /* bytes included in lenlist,loclist,msgcnt*/
#define WHREND lnglist             /* where that information goes in msgblk*/

UINT mxmssz;                       /* maximum message size                 */
CHAR *msgbuf=NULL;                 /* message text buffer                  */
INT starit;                        /* flag set by msgseek() if msg too long*/
INT rlingo;                        /* actual language read by msgseek()    */
INT mslidx;                        /* set by msgseek()                     */
INT badlang=0;                     /* to flag undefined languages in .MCVs */
CHAR mcvpath[80];                  /* alternate path for mcvs              */
HMCVFILE curmbk;                   /* setmbk() sets, lclmbk is local alias */

struct msgblk *mbstk[DFSTSZ];      /* setmbk()/rstmbk() stack              */

VOID
inimsg(                            /* init msgutl with buffer size         */
UINT maxsiz)                       /*   maximum size of message buffer     */
{
     if (mxmssz < maxsiz) {
          if (mxmssz == 0) {
               msgbuf=alcmem(maxsiz);
          }
          else {
               msgbuf=alcrsz(msgbuf,mxmssz,maxsiz);
          }
          mxmssz=maxsiz;
     }
}

HMCVFILE                           /*   returns handle to open mcv file    */
opnmsg(                            /* open a message file                  */
CHAR *mcvfil)                      /*   mcv file to open                   */
{
     INT i,lgo;
     LONG lsize2d;
     UINT size2d;
     CHAR lngbuf[LNGSIZ];
     CHAR actfil[100],*cp;
     static INT set=0;

     if (!set) {
          set=1;
          if ((cp=getenv("MCVPATH")) != NULL && cp[0] != 0) {
               stzcpy(mcvpath,cp,sizeof(mcvpath)-1);
               if (mcvpath[strlen(mcvpath)-1] != SL
             && mcvpath[strlen(mcvpath)-1] != ':') {
                    strcat(mcvpath,SLS);
               }
          }
          else {
               setmem(mcvpath,sizeof(mcvpath),0);
          }
     }
     if (mcvpath[0] != '\0' && mcvfil[1] != ':' && !strchr(mcvfil,SL)) {
          strcpy(actfil,mcvpath);
          strcat(actfil,mcvfil);
          mcvfil=actfil;
     }
     setmbk((HMCVFILE)alczer(sizeof(struct msgblk)));
     if ((lclmbk->msgfp=fopen(mcvfil,FOPRB)) == NULL) {
          catastro("CANNOT FIND \"%s\"",mcvfil);
     }
     if (fseek(lclmbk->msgfp,0L-MCVEND,SEEK_END) != 0
      || fread(&lclmbk->WHREND,1,MCVEND,lclmbk->msgfp) != MCVEND
      || fseek(lclmbk->msgfp,lclmbk->lnglist,SEEK_SET) != 0) {
          catastro("CANNOT READ \"%s\" VARIABLES",mcvfil);
     }
     if (languages != NULL) {
          lclmbk->mslidx=(INT *)alczer(nlingo*sizeof(INT));
          for (i=0 ; i < lclmbk->lngcnt ; i++) {
               if (!fgetstg(lngbuf,LNGSIZ,lclmbk->msgfp)
             || (lgo=lngfnd(lngbuf)) == -1
             || (i == 0) != (lgo == 0)) {
                    badlang=1;
               }
               else {
                    lclmbk->mslidx[lgo]=i;
               }
          }
     }
     lclmbk->msgloc=(LONG *)alcmem((lclmbk->msgcnt+1)*sizeof(LONG));
     if (fseek(lclmbk->msgfp,lclmbk->loclist,SEEK_SET) != 0
      || fread(lclmbk->msgloc,sizeof(LONG),lclmbk->msgcnt+1,
            lclmbk->msgfp) != (size_t)lclmbk->msgcnt+1) {
          catastro("CANNOT READ \"%s\" POINTER ARRAY",mcvfil);
     }
     if (lclmbk->lngcnt > 1) {
          lsize2d=((LONG)lclmbk->msgcnt)*((LONG)lclmbk->lngcnt);
          if (lsize2d >= 32768L) {
               catastro("TOO MANY LANGUAGES (%d) & MESSAGES (%d) IN \"%s\"",
                        lclmbk->lngcnt,lclmbk->msgcnt,mcvfil);
          }
          size2d=(UINT)lsize2d;
          lclmbk->lengths=(USHORT *)alcmem(size2d*sizeof(USHORT));
          if (fseek(lclmbk->msgfp,lclmbk->lenlist,SEEK_SET) != 0
           || fread(lclmbk->lengths,sizeof(USHORT),size2d,
                 lclmbk->msgfp) != size2d) {
               catastro("CANNOT READ \"%s\" LENGTH TABLE",mcvfil);
          }
     }
     lclmbk->filnam=alcdup(mcvfil);
     return(curmbk);
}

UINT                               /*   returns msgnum's size              */
msgseek(                           /* seek to msg and calculate size       */
INT msgnum)                        /*   msgnum to read                     */
{
     LONG msgbgn;
     UINT msgsiz,lnoff;
     INT i;

     if (msgbuf == NULL) {
          catastro("INIMSG NOT CALLED");
     }
     if (msgnum < 0 || msgnum >= lclmbk->msgcnt) {
          catastro("MESSAGE NUMBER %d OUT OF RANGE IN %s",msgnum,
             lclmbk->filnam);
     }
     msgbgn=lclmbk->msgloc[msgnum];
     if (lclmbk->lngcnt <= 1) {
          mslidx=rlingo=0;
          msgsiz=(UINT)(lclmbk->msgloc[msgnum+1]-msgbgn);
     }
     else if (lclmbk->mslidx == NULL) {
          mslidx=0;
          msgsiz=lclmbk->lengths[msgnum*lclmbk->lngcnt];
     }
     else {
          lnoff=msgnum*lclmbk->lngcnt;
          for (alg1st(rlingo=clingo) ; 1 ; rlingo=algnxt()) {
               mslidx=lclmbk->mslidx[rlingo];
               msgsiz=lclmbk->lengths[lnoff+mslidx];
               if (rlingo == 0 || (mslidx != 0 && msgsiz != 0)) {
                    break;
               }
          }
          for (i=0 ; i < mslidx ; i++) {
               msgbgn+=lclmbk->lengths[lnoff+i];
          }
     }
     if (fseek(lclmbk->msgfp,msgbgn,SEEK_SET) != 0) {
          catastro("CANNOT SEEK MESSAGE NUMBER %d (LANGUAGE %d) IN %s\n",
             msgnum,mslidx,lclmbk->filnam);
     }
     if (msgsiz > mxmssz) {
          msgsiz=mxmssz;
          starit=1;
     }
     else {
          starit=0;
     }
     return(msgsiz);
}

CHAR *                             /*   returns ptr to message             */
rawmsg(                            /* get a raw message (by number)        */
INT msgnum)                        /*   msgnum to get                      */
{
     if (fread(msgbuf,msgseek(msgnum),1,lclmbk->msgfp) != 1) {
          catastro("CANNOT READ MESSAGE NUMBER %d (LANGUAGE %d) IN %s",
                   msgnum,mslidx,lclmbk->filnam);
     }
     if (starit) {
          strcpy(msgbuf+mxmssz-2,"*");
     }
     lclmbk->lstmsg=msgnum;
     return(msgbuf);
}

VOID
clsmsg(                            /* close message file                   */
HMCVFILE mbptr)                    /*   message file to close              */
{
     struct msgblk *mb;
     CHAR *filnam;

     mb=(struct msgblk *)mbptr;
     if (mb != NULL && mb->filnam != NULL) {
          filnam=mb->filnam;
          mb->filnam=NULL;
          fclose(mb->msgfp);
          if (mb->mslidx != NULL) {
               free(mb->mslidx);
          }
          free(mb->msgloc);
          if (mb->lengths != NULL) {
               free(mb->lengths);
          }
          free(filnam);
          setmem(mb,sizeof(struct msgblk),0);
          free(mb);
     }
}

VOID
setmbk(                            /* set message block                    */
HMCVFILE mb)                       /*   file to point to                   */
{
     movmem(mbstk,mbstk+1,sizeof(struct msgblk *)*(DFSTSZ-1));
     curmbk=mb;
     mbstk[0]=lclmbk;
     lclmbk->lstmsg=-1;
}

VOID                               /*   restore msg file ptr from before   */
rstmbk(VOID)                       /*   the last setmbk() or opnmsg()      */
{
     movmem(mbstk+1,mbstk,sizeof(struct msgblk *)*(DFSTSZ-1));
     lclmbk=mbstk[0];
}

LONG                               /*   returns a long value               */
lngopt(                            /* get numeric option from msg file     */
INT msgnum,                        /*   message number to read             */
LONG floor,                        /*   lower allowable limit              */
LONG ceiling)                      /*   upper allowable limit              */
{
     LONG ln;

     if (sscanf(lastwd(rawmsg(msgnum)),"%ld",&ln) == 0) {
          catastro("ERROR IN NUMERIC OPT %d IN %s",msgnum,lclmbk->filnam);
     }
     if (ln < floor || (ln > ceiling && ceiling != -1)) {
          catastro("NUMERIC OPT %d IN %s OUT OF RANGE",msgnum,lclmbk->filnam);
     }
     return(ln);
}

UINT                               /*   returns a hex value                */
hexopt(                            /* get hexadecimal option from msg file */
INT msgnum,                        /*   message number to read             */
UINT floor,                        /*   lower allowable limit              */
UINT ceiling)                      /*   upper allowable limit              */
{
     LONG ln;

     if (sscanf(lastwd(rawmsg(msgnum)),"%lx",&ln) == 0) {
          catastro("ERROR IN HEX OPT %d IN %s",msgnum,lclmbk->filnam);
     }
     if (ln < (LONG)floor || ln > (LONG)ceiling) {
          catastro("HEX OPT %d IN %s OUT OF RANGE",msgnum,lclmbk->filnam);
     }
     return((USHORT)ln);
}

INT                                /*   returns a numeric value            */
numopt(                            /* get numeric option from msg file     */
INT msgnum,                        /*   message number to read             */
INT floor,                         /*   lower allowable limit              */
INT ceiling)                       /*   upper allowable limit              */
{
     return((INT)lngopt(msgnum,(LONG)floor,(LONG)ceiling));
}

GBOOL                              /*   returns TRUE=yes FALSE=no          */
ynopt(                             /* get yes/no option from msg file      */
INT msgnum)                        /*   message number to read             */
{
     GBOOL rc;

     switch (toupper(*lastwd(rawmsg(msgnum)))) {
     case 'Y':
          rc=TRUE;
          break;
     case 'N':
          rc=FALSE;
          break;
     default:
          catastro("ERROR IN YES/NO OPTION %d IN %s",msgnum,lclmbk->filnam);
     }
     return(rc);
}

INT                                /*   returns character                  */
chropt(                            /* get single-character option from msg */
INT msgnum)                        /*   message number to read             */
{
     return(*lastwd(rawmsg(msgnum)));
}

CHAR *                             /*   returns ptr to newly allocated stg */
stgopt(                            /* get a string from a message file     */
INT msgnum)                        /*   message number to read             */
{
     CHAR *cp,*mp;

     cp=alcmem(strlen(mp=rawmsg(msgnum))+1);
     strcpy(cp,mp);
     return(cp);
}

                                   /*   returns ptr to newly allocated path*/
CHAR *                             /*   includes trailing slash            */
pthopt(                            /* get a path from a message file       */
INT msgnum)                        /*   message number to read             */
{
     CHAR *cp,*retval;

     if (strlen(cp=rawmsg(msgnum)) == 0) {
          retval=strdup(cp);
     }
     else {
          if (isvalds(cp[strlen(cp)-1])) {
               retval=strdup(cp);
          }
          else {
               retval=alcmem(strlen(cp)+2);
               strcpy(retval,cp);
            strcat(retval,SLS);
          }
     }
     return(retval);
}

INT                                /*   returns index to string from list  */
tokopt(                            /* find token in a list                 */
INT msgnum,                        /*   message number to read             */
...)                               /*   list to search                     */
{
     va_list ap;
     CHAR *token;
     CHAR *ppt;
     INT i;

     va_start(ap,msgnum);
     token=lastwd(rawmsg(msgnum));
     ppt=va_arg(ap,CHAR *);
     for(i=1 ; ppt != NULL ; i++) {
       if (strcmp(token,ppt) == 0) {
            va_end(ap);
            return(i);
       }
       ppt=va_arg(ap,CHAR *);
     }
     va_end(ap);
     return(0);
}

CHAR *
catfix2(VOID)                      /* catfix2() for catamsg()              */
{
     static CHAR mfbuff[30]={""};

     if (goodblk(lclmbk,sizeof(struct msgblk)) == sizeof(struct msgblk)) {
          goodstg(lclmbk->filnam);
          sprintf(mfbuff,"MSG:%s/%d",pinstd,lclmbk->lstmsg);
     }
     return(mfbuff);
}
