/***************************************************************************
 *                                                                         *
 *   WGSMSX.C                                                              *
 *                                                                         *
 *   Copyright (c) 1989-1997 Galacticomm, Inc.  All Rights Reserved        *
 *                                                                         *
 *   This is the named-message-file and "#define" file maker utility.      *
 *                                                                         *
 *   Usage: WGSMSX               ...global out-of-date .MSG->.MCV,.H       *
 *          WGSMSX FN            ...equiv to WGSMSX FN.MSG FN.MCV FN.H     *
 *          WGSMSX -O<dest dir>  ...global but with nondefault destination *
 *                                    directory for the .H files           *
 *          WGSMSX @<cfg file> [-O<dest dir>]                              *
 *                               ...handle all MSG= files in <cfg file>    *
 *                                                                         *
 *          WGSMSX FN.MSG FN.MCV FN.H                                      *
 *              ...converts message file to runtime format, together       *
 *                 with header files for numeric message references        *
 *                                                                         *
 *                                            - T. Stryker 6/10/86,2/6/89  *
 *                                            - R. Stein   1/22/93         *
 *                                            - B. Elliot  3/27/93         *
 *                                                                         *
 ***************************************************************************/

#include <utime.h>
#include "gcomm.h"
#include "excphand.h"

#define FILREV "$Revision: 10 $"

#define BIGSCNY 8                  /* BIG multi-file screen starting Y     */
#define SMLSCNY 11                 /* SMALL single-file screen starting Y  */

#define MAXMEM (0x3FF0)            /* approximately 16K for each element   */

#define MAXNAM (MAXMEM/(MOPTLEN+1))
#define MAXLOC (MAXNAM)
#define MAXLEN (MAXMEM/sizeof(int))

#define OUBSIZ 16384               /* bytes in oubuff                      */

int nlingo=MAXLANG;                /* extern reference: max # of languages */

extern CHAR scntbl[][GVIDSCNSIZ];/* array of screens (c/o MAKESCNS)*/

static
struct msgent {                    /* message file linked-list entry       */
     struct msgent *link;          /*   forward ptr                        */
     CHAR name[13];                /*   file name including .msg & '\0'    */
     UINT time;                    /*   .MSG file time                     */
     UINT date;                    /*   .MSG file date                     */
     LONG size;                    /*   .MSG file size                     */
} mshead,*mstptr=&mshead;

static INT nmsg=0;                 /* number of .MSG files                 */
static CHAR *oubuff=NULL;          /* setvbuf() buffer for outputting      */

static
FILE *mcvfp,                       /* output (.mcv) file block pointer     */
     *mdffp;                       /* output header (.h) file block ptr    */
static INT cwidth;                 /* width of file's bargraph             */
static USHORT msgcnt;              /* count of messages found so far       */
static INT multif=0;               /* 1=multiple files 0=only one          */
static
LONG maxfsiz,                      /* maximum size of file(s)              */
     totfsiz,                      /* total size of all file(s)            */
     cntfsiz;                      /* overall bargraph                     */

static CHAR outdir[GCMAXPTH]="";   /* output directory for .MCV files      */

static
INT savx,                          /* saved cursor position for restore    */
    savy;

static CHAR *savscn=NULL;          /* saved screen image for restore       */

static
struct msgtag {                    /* message tag structure                */
     CHAR nam[MOPTLEN+1];          /*   tag name (8 letters + null)        */
     LONG loc;                     /*   file position                      */
     USHORT len[MAXLANG];          /*   beginning of array of lengths      */
} tag;
static INT lngcnt;                 /* number of languages in this msg      */

static CHAR (*nam)[MOPTLEN+1]=NULL;/* array of message names               */

static LONG *loc=NULL;             /* array of message locations           */

static USHORT *len=NULL;           /* array of arrays of msg lengths       */

static CHAR (*tmp)[MOPTLEN+1]=NULL;/* temporary array for caching          */

static
INT namcnt,                        /* which name # are we on?              */
    loccnt,                        /* which location # are we on?          */
    lencnt;                        /* which length array # are we on?      */

static INT namitr;                 /* pos # of curr. name(for the iterator)*/

static
GBOOL mcvsame=TRUE,                /* make MCV file same date as MSG file  */
      hsame=FALSE;                  /* make H file same date as MSG file    */

static VOID batchf(CHAR *bfname);
static VOID newmsg(struct ffblk *msfb);
static VOID domsgs(VOID);
static VOID ck1stp(VOID);
static VOID go4one(CHAR *spec);
static VOID doone(CHAR *inspec,CHAR *mcvspec,CHAR *mdfspec);
static VOID filbar(LONG num,LONG den);
static VOID initag(VOID);
static VOID addtag(VOID);
static VOID putloc(VOID);
static VOID putlen(VOID);
static CHAR *nxtnam(VOID);

INT
main(
INT argc,
CHAR *argv[])
{
TRY
     struct ffblk fb;

     nlingo=MAXLANG;
     initvid();
     if (setjmp(disaster)) {
          locate(0,17);
          clsvid();
          return(1);
     }
     monorcol();
     cvtscn(scntbl[0]);
     inimsgrdr(OPTSIZE);
     setatr(0x0E);
     tdremv=0;
     if (argc > 1 && sameto("-O",argv[argc-1])) {
          sprintf(outdir,"%.*s\\",GCMAXPTH-1,argv[argc-1]+2);
          argc--;
     }
     switch (argc) {
     case 2:
          if (argv[1][0] == '@') {
               multif=1;
               batchf(&(argv[1][1]));
               domsgs();
          }
          else {
               go4one(argv[1]);
          }
          break;
     case 1:
          multif=1;
          if (fnd1st(&fb,"*.msg",0)) {
               do {
                    newmsg(&fb);
               } while (fndnxt(&fb));
          }
          domsgs();
          break;
     case 4:
          doone(argv[1],argv[2],argv[3]);
          break;
     default:
          printf("\nWGSMSX command format:\n\n"
              "     wgsmsx [<infil> [<mcvfil> <hfil>]] [-o<H-directory>]\n"
              "     wgsmsx [@<batch file>] [-o<H-directory>]\n");
          break;
     }
     if (savscn != NULL) {
          mem2scn(savscn,0,GVIDSCNSIZ);
          locate(savx,savy);
          cursiz(GVIDLILCURS);
     }
     clsvid();
EXCEPT
     return(0);
}

static VOID
batchf(                            /* process MSG files in batch specified */
CHAR *bfname)                      /*   batch file name                    */
{
     struct ffblk fb;

     if (tfsopn(bfname) == 0) {
          catastro("Cannot find \"%s\"",bfname);
     }
     while (tfsrdl() != TFSDUN) {
          if (tfstate == TFSLIN) {
               if (tfspfx("MSG=")) {
                    if (fndfile(&fb,spr("%s.MSG",tfspst),0)) {
                         newmsg(&fb);
                    }
               }
          }
     }
}

static VOID
newmsg(                            /* process message file referenced      */
struct ffblk *msfb)                /*   reference to MSG file              */
{
     struct msgent *numsg;

     for (numsg=mshead.link ; numsg != NULL ; numsg=numsg->link) {
          if (sameas(numsg->name,msfb->ff_name)) {
               return;
          }
     }
     numsg=(struct msgent *)alcmem(sizeof(struct msgent));
     numsg->link=NULL;
     strcpy(numsg->name,msfb->ff_name);
     numsg->time=msfb->ff_ftime;
     numsg->date=msfb->ff_fdate;
     numsg->size=msfb->ff_fsize;
     mstptr->link=numsg;
     mstptr=numsg;
     nmsg++;
}

static VOID
domsgs(VOID)                       /* convert all MSG files processed      */
{
     struct msgent *numsg;
     CHAR *mcvnam,fptmp[GCMAXFNM];
     struct ffblk mcvfb;

     if (nmsg == 0) {
          catastro("There are no .MSG files to convert!");
     }
     totfsiz=0L;
     maxfsiz=0L;
     nmsg=0;
     for (numsg=mshead.link ; numsg != NULL ; numsg=numsg->link) {
          mcvnam=spr("%s.mcv",
                     fileparts(GCPART_FILE,numsg->name,fptmp,GCMAXFNM));
          if (!fndfile(&mcvfb,mcvnam,0)
           || mcvfb.ff_ftime != numsg->time
           || mcvfb.ff_fdate != numsg->date) {
               totfsiz=totfsiz+numsg->size;
               maxfsiz=max(maxfsiz,numsg->size);
               nmsg++;
          }
          else {
               numsg->name[0]='\0';
          }
     }
     if (nmsg == 1) {
          multif=0;
     }
     for (numsg=mshead.link ; numsg != NULL ; numsg=numsg->link) {
          if (numsg->name[0] != '\0') {
               go4one(numsg->name);
          }
     }
     cntfsiz=0L;
}

static VOID
ck1stp(VOID)                       /* set up screen, once                  */
{
     static GBOOL done=FALSE;

     if (!done) {
          done=TRUE;
          savx=curcurx();
          savy=curcury();
          cursiz(GVIDNOCURS);
          scn2mem(savscn=alcmem(GVIDSCNSIZ),0,GVIDSCNSIZ);
          if (multif) {
               explodeto(scntbl[0],2,2,76,12,2,BIGSCNY);
               setwin(NULL,2+2,BIGSCNY+2,76-1,BIGSCNY+3+4,1);
               setatr(0x1A);
               printf("\fConverting:");
          }
          else {
               explodeto(scntbl[0],2,15,76,19,2,SMLSCNY);
               setwin(NULL,2+2,SMLSCNY+3,76-1,SMLSCNY+3,0);
               setatr(0x1A);
               printf("\f");
          }
     }
}

static VOID
go4one(                            /* break out filespec and process file  */
CHAR *spec)                        /*   filespec to parse and chop up      */
{
     CHAR insbuf[GCMAXPTH],mcvsbuf[GCMAXPTH],mdfsbuf[GCMAXPTH],fptmp[GCMAXFNM];
     CHAR *cp;

     cp=fileparts(GCPART_FILE,spec,fptmp,GCMAXFNM);
     sprintf(insbuf,"%s.msg",cp);
     sprintf(mcvsbuf,"%s.mcv",cp);
     if (strlen(outdir)+strlen(cp)+3 < GCMAXPTH) {
          sprintf(mdfsbuf,"%s%s.h",outdir,cp);
     }
     else {
          catastro("WGSMSX: Outbound directory name is too long!");
     }
     doone(insbuf,mcvsbuf,mdfsbuf);
}

static VOID
doone(                             /* process specified file               */
CHAR *inspec,                      /*   MSG file to process                */
CHAR *mcvspec,                     /*   MCV destination file               */
CHAR *mdfspec)                     /*   H destination file                 */
{
     INT firstopt,i;
     LONG eomsgs,lngpos=0L,lenpos,locpos;
     struct ffblk infb;
     CHAR *mbfptr,nxc,*nxtm,*dinspec,*dmcvspec,*dmdfspec;
     USHORT ddate,dtime;
     LONG HACK;

     initag();
     fnmcse(dinspec=alcdup(inspec));
     fnmcse(dmcvspec=alcdup(mcvspec));
     fnmcse(dmdfspec=alcdup(mdfspec));
     if (!fndfile(&infb,inspec,0) || (rdfp=fopen(curfn=inspec,FOPRA)) == NULL) {
          catastro("WGSMSX: Cannot find \"%s\"",dinspec);
     }
     getFileTm(curfn,&ddate,&dtime);
     if ((mcvfp=fopen(mcvspec,FOPWB)) == NULL) {
          catastro("WGSMSX: Cannot create \"%s\"",dmcvspec);
     }
     if ((mdffp=fopen(mdfspec,FOPWA)) == NULL) {
          catastro("WGSMSX: Cannot create \"%s\"",dmdfspec);
     }
     ck1stp();
     if (oubuff == NULL) {
          oubuff=malloc(OUBSIZ);
     }
     if (oubuff != NULL) {    /* gracefully handle low memory condition    */
          if (setvbuf(mcvfp,oubuff,_IOFBF,OUBSIZ) != 0) {
               catastro("WGSMSX: Cannot set output buffer for %s",dmcvspec);
          }
     }
     setatr(0x1E);
     printf("\n%12s",dinspec);
     setatr(0x1A);
     printf(" Í ");
     setatr(0x1E);
     printf("%12s",dmcvspec);
     setatr(0x1A);
     printf(", ");
     setatr(0x1E);
     printf("%s",spr("%*s",10+min(strlen(outdir),6),dmdfspec));
     if (!multif) {
          maxfsiz=infb.ff_fsize;
     }
     cwidth=(INT)((18*infb.ff_fsize)/maxfsiz)+1;
     msgcnt=0;
     firstopt=1;
     if (sameto("wgsmajor.msg",inspec)) {
          fwrite("\003\000\016\000\000\000\00F\000\000\000"
                 "\025\000\000\000\000 AUTO",1,21,mcvfp);
     }
     while (rdmsg()) {
          scanalt();
          litopts();
          HACK=ftell(rdfp);
          if (sameas(msgnam,"LANGUAGE")) {
               if (!firstopt) {
                    catastro("In %s, LANGUAGE{} must be the very first option",
                             dinspec);
               }
               firstopt=0;
               lngcnt=salingo;
               lngpos=ftell(mcvfp);
               for (i=0 ; i < lngcnt ; i++) {
                    if (altlnm[i].value.fsk == 0L) {
                         catastro("Language name #%d omitted in %s",i,dinspec);
                    }
                    if (i != 0) {
                         loadtv(rdfp,&altlnm[i]);
                    }
                    fprintf(mcvfp,"%s%c",txtbuf,'\0');
               }
          }
          else {
               if (firstopt) {
                    firstopt=0;
                    lngcnt=1;
                    lngpos=ftell(mcvfp);
                    fprintf(mcvfp,"%s%c",DFTLNG,'\0');
               }
               tag.loc=ftell(mcvfp);
               stzcpy(tag.nam,msgnam,MOPTLEN+1);
               for (i=0 ; i < salingo ; i++) {
                    if (altlnm[i].value.fsk != 0L) {
                         if (i != 0) {
                              loadtv(rdfp,&altlnm[i]);
                         }
                         for (mbfptr=txtbuf ; *mbfptr != '\0' ; mbfptr++) {
                              if (*mbfptr == '\n') {
                                   nxc=*(mbfptr+1);
                                   if (!isalnum(nxc)
                                     && nxc != '%'
                                     && nxc != '('
                                     && nxc != '"') {
                                        *mbfptr='\r';
                                   }
                              }
                         }
                         if (lngcnt > 1) {
                              tag.len[i]=(USHORT)(strlen(txtbuf)+1);
                         }
                         fprintf(mcvfp,"%s%c",txtbuf,'\0');
                    }
               }
               addtag();
               if ((msgcnt&15) == 0) {
                    filbar(altlnm[0].value.fsk,infb.ff_fsize);
               }
               msgcnt++;
          }
          if (feof(mcvfp)) {
               catastro("Disk full!  (while writing to %s)",dmcvspec);
          }
          if (ftell(rdfp) != HACK) { /* this is due to RIP messages not     */
               fseek(rdfp,HACK,0);   /* being properly skipped by something */
          }                          /* investigate later.                  */
     }
     eomsgs=ftell(mcvfp);
     if (lngcnt > 1) {
          lenpos=ftell(mcvfp);
          putlen();
     }
     else {
          lenpos=0L;
     }
     locpos=ftell(mcvfp);
     putloc();
     fwrite(&eomsgs,sizeof(LONG),1,mcvfp);
     fwrite(&lngpos,sizeof(LONG),1,mcvfp);
     fwrite(&lenpos,sizeof(LONG),1,mcvfp);
     fwrite(&locpos,sizeof(LONG),1,mcvfp);
     fwrite(&lngcnt,sizeof(SHORT),1,mcvfp);
     if (fwrite(&msgcnt,sizeof(USHORT),1,mcvfp) != 1) {
          catastro("Disk full!  (while writing to %s)",dmcvspec);
     }
     fclose(mcvfp);
     fclose(rdfp);
     if (mcvsame) {
          setFileTm(mcvspec,ddate,dtime);
     }
     filbar(1L,1L);
     fprintf(mdffp,"/*  Message-number definitions derived from \"%s\"  */\n\n",
                    dinspec);
     setatr(color ? 0x7F : 0x0F);
     for (i=0 ; i < msgcnt ; i++) {
          nxtm=nxtnam();
          if (!sameto("LEVEL",nxtm) || atoi(&nxtm[strlen("LEVEL")]) == 0) {
               if (fprintf(mdffp,"#define %-9s %3d\n",nxtm,i) == EOF) {
                    catastro("Disk full!  (while writing to %s)",dmdfspec);
               }
          }
          if (((msgcnt-i)&15) == 1) {
               locate(56,curcury());
               printf("%s",bargph(cwidth,(LONG)i+1,(LONG)msgcnt));
          }
     }
     cntfsiz+=infb.ff_fsize;
     fclose(mdffp);
     if (hsame) {
          setFileTm(mdfspec,ddate,dtime);
     }
     locate(56,curcury());
     printf("%s",bargph(cwidth,1L,1L));
     free(dmdfspec);
     free(dmcvspec);
     free(dinspec);
}

static VOID
filbar(                            /* display progress bars                */
LONG num,                          /*   division's numerator               */
LONG den)                          /*   " denominator                      */
{
     CHAR *bp;
     INT savx,savy;

     bp=bargph(cwidth,num,den);
     locate(51,curcury());
     setatr(0x1E);
     printf("%3d%% ",bgperc);
     setatr(0x37);
     printf("%s",bp);
     if (multif) {
          savx=curcurx();
          savy=curcury();
          bp=bargph(58,cntfsiz+altlnm[0].value.fsk,totfsiz);
          locate(2+10,BIGSCNY+9);
          setatr(0x1E);
          printf("%3d%% ",bgperc);
          setatr(0x3F);
          printf("%s",bp);
          locate(savx,savy);
     }
}

static VOID
initag(VOID)                       /* initialize for new MSG file          */
{
     setmem(&tag,sizeof(tag),0);

     if (nam == NULL) {
          nam=(CHAR (*)[MOPTLEN+1])alcmem(MAXNAM*(MOPTLEN+1));
     }
     if (loc == NULL) {
          loc=(LONG *)alcmem(MAXLOC*sizeof(LONG));
     }
     if (len == NULL) {
          len=alcmem(MAXLEN*sizeof(USHORT));
     }
     if (tmp == NULL) {
          tmp=(CHAR (*)[MOPTLEN+1])alcmem(MAXMEM);
     }
     setmem(nam,MAXNAM*(MOPTLEN+1),0);  /* each time we init (for a new    */
     setmem(loc,MAXLOC*sizeof(LONG),0); /* MSG file) set the elements to 0 */
     setmem(len,MAXLEN*sizeof(USHORT),0);

     namcnt=0;      /* set the array offset counters to zero */
     loccnt=0;
     lencnt=0;

     namitr=-1;     /* set the name iterator counter to zero */
     unlink("wgsmsx.$$1");
     unlink("wgsmsx.$$2");
     unlink("wgsmsx.$$3");
}

static VOID
addtag(VOID)                       /* add a new tag struct to cache        */
{
     FILE* fp;

     /* copy the elements from the "tag" structure into the chunks         */
     memcpy(nam[namcnt],tag.nam,MOPTLEN+1);
     memcpy(len+lencnt,tag.len,lngcnt*sizeof(USHORT));
     loc[loccnt]=tag.loc;

     namcnt++;
     /* is the current names segment full? */
     if (namcnt >= MAXNAM) {
          if ((fp=fopen("wgsmsx.$$1",FOPAB)) == NULL) {
               catastro("WGSMSX: Cannot open cache file \"WGSMSX.$$1\"");
          }
          fwrite(nam,MOPTLEN+1,MAXNAM,fp);
          fclose(fp);
          setmem(nam,MAXNAM*(MOPTLEN+1),0);
          namcnt=0;
     }

     lencnt+=lngcnt;
     /* is the current lengths positions segment full?                     */
     if (lencnt >= MAXLEN-lngcnt) {
          if ((fp=fopen("wgsmsx.$$2",FOPAB)) == NULL) {
               catastro("WGSMSX: Cannot open cache file \"WGSMSX.$$2\"");
          }
          fwrite(len,sizeof(USHORT),lencnt,fp);
          fclose(fp);
          setmem(len,MAXLEN*sizeof(USHORT),0);
          lencnt=0;
     }

     loccnt++;
     /* is the current file positions segment full?                        */
     if (loccnt >= MAXLOC) {
          if ((fp=fopen("wgsmsx.$$3",FOPAB)) == NULL) {
               catastro("WGSMSX: Cannot open cache file \"WGSMSX.$$3\"");
          }
          fwrite(loc,sizeof(LONG),MAXLOC,fp);
          fclose(fp);
          setmem(loc,MAXLOC*sizeof(LONG),0);
          loccnt=0;
     }

     /* zero out the tag structure for the next message                    */
     setmem(&tag,sizeof(tag),0);
}

static VOID
putlen(VOID)                       /* put msg/lang lengths to file         */
{
     FILE *fp;
     INT nread;

     if ((fp=fopen("wgsmsx.$$2",FOPRB)) != NULL) {
          while ((nread=(INT)fread(tmp,sizeof(USHORT),MAXLEN,fp)) > 0) {
               fwrite(tmp,sizeof(USHORT),nread,mcvfp);
          }
          fclose(fp);
          unlink("wgsmsx.$$2");
     }
     fwrite(len,sizeof(USHORT),lencnt,mcvfp);
}

static VOID
putloc(VOID)                       /* put msg locations to file            */
{                                  /* (reading from cache if necessary)    */
     FILE *fp;

     fp=fopen("wgsmsx.$$3",FOPRB);
     if (fp != NULL) {
          for (;;) {
               if ((INT)fread(tmp,sizeof(LONG),MAXLOC,fp) <= 0) {
                    break;
               }
               fwrite(tmp,sizeof(LONG),MAXLOC,mcvfp);
          }
          fclose(fp);
          unlink("wgsmsx.$$3");
     }
     fwrite(loc,sizeof(LONG),loccnt,mcvfp);
}

static CHAR *
nxtnam(VOID)                       /* retrieve the next msg name           */
{                                  /* (reading from cache if necessary)    */
     static CHAR name[MOPTLEN+1];
     static FILE *fp=NULL;

     if (namitr == -1) {
          fp=fopen("wgsmsx.$$1",FOPRB);
          if (fp != NULL) {
               fread(tmp,MOPTLEN+1,MAXNAM,fp);
          }
          namitr=0;
     }

     if (fp != NULL) {
          memcpy(name,tmp[namitr],MOPTLEN+1);
     }
     else {
          memcpy(name,nam[namitr],MOPTLEN+1);
     }

     namitr++;

     if (namitr >= MAXNAM) {
          if (fp != NULL) {
               if ((INT)fread(tmp,MOPTLEN+1,MAXNAM,fp) <= 0) {
                    fclose(fp);
                    fp=NULL;
                    unlink("wgsmsx.$$1");
               }
          }
          namitr=0;
     }
     return(name);
}
