/***************************************************************************
 *                                                                         *
 *   UPDUTL.C                                                              *
 *                                                                         *
 *   Copyright (c) 1989-1997 Galacticomm, Inc.  All Rights Reserved        *
 *                                                                         *
 *   These are the utility routines used by INSTALL.EXE and WGSMUP.EXE     *
 *   to update .MSG files by merging new messages into old message files.  *
 *                                                                         *
 *   The merge1() routine will update the destination directory with any   *
 *   .MSG files now already present, also merging .MSG's present in both   *
 *   directories, so that the resulting destination directory files have   *
 *   the structure and message order of the original source directory      *
 *   files, but the message-contents of the original destination directory *
 *   files.  The original destination directory .MSG files are renamed to  *
 *   another filetype in the process (if files of this type are already    *
 *   present when merge1() is invoked, they are used in place of the       *
 *   .MSG's).                                                              *
 *                                                                         *
 *   The merge1() routine's "lang2ow" parameter tells UPDUTL to copy all   *
 *   text blocks for any one language from the new MSG file to the output  *
 *   one.  Passing "English/RIP" as the "lang2ow" parameter will cause all *
 *   English/RIP text blocks in the new MSG file to go into the output     *
 *   one, ignoring any changes the Sysop may have made to existing         *
 *   messages.  Pass "" or NULL as the "lang2ow" to treat all languages in *
 *   the default manner.                                                   *
 *                                                                         *
 *   If the keyword "END_UPD" is found immediately before a LEVEL## {}     *
 *   directive, merge1() will copy the remaining contents of the *old*     *
 *   message file into the output file.                                    *
 *                                                                         *
 *   Usage:                                                                *
 *                                                                         *
 *        merge1(oldnam,newnam,outnam);                                    *
 *                                                                         *
 *                                            - T. Stryker 2/7/89          *
 *                                              (walhf SJB)                *
 *                                            - C. Robert 1/31/93          *
 *                                                                         *
 ***************************************************************************/

#ifdef GCWINNT
#include <windows.h>
#include <direct.h>
#endif
#include "gcomm.h"
#include "updutl.h"

#define FILREV "$Revision: 14 $"

#define MNMSIZ 9                   /* maximum size of each msg name (ditto)*/

#define MNMPFX "Module name:"      /* module name prefix in .MDF file      */
#define DFLPFX "Dynamic Btrieve files:" /* dynamic btrieve files prefix    */

INT nlingo=MAXLANG;                /* lingo.c stub of nlingo (fake-out)    */

CHAR oldnam[GCMAXPTH],             /* old message file name                */
     newnam[GCMAXPTH],             /* new message file name                */
     outnam[GCMAXPTH],             /* output message file name             */
     owlang[LNGSIZ];               /* language to overwrite messages for   */

FILE *oldfp,                       /* old message file block pointer       */
     *newfp,                       /* new message file block pointer       */
     *outfp;                       /* output message file block pointer    */

INT msgcnt,                        /* messages counted so far              */
    numold,                        /* number of messages in old msg file   */
    noldln,                        /* number of lingos in old msg file     */
    totlng;                        /* total number of languages in output  */

struct oldmsgs {                   /* old message structure definition     */
     CHAR name[MNMSIZ];            /*   name of message                    */
     LONG offset;                  /*   offset to message in .MSG file     */
} *oldmsg=NULL;                    /* array of MAXOPTS old message structs */

INT newsal;                        /* saved salingo value for new .MSG file*/
struct altlng *newlnm;             /* saved altlnm array for new .MSG file */

CHAR *lnglst[MAXLANG];             /* array of languages for output file   */
INT newmap[MAXLANG];               /* mapping of new languages to lnglst   */

GBOOL cataok;                      /* should we catastro() errors?         */

CHAR *upderr=NULL;                 /* error text of last error             */

INT maktbl(VOID);
INT oldmat(VOID);
INT isinold(INT lngnum);
INT useold(INT lngnum,INT ncma);
INT isinnew(INT lngnum);
INT usenew(INT lngnum,INT ncma);
INT addnew(CHAR *nam,INT num);
VOID freeln(VOID);
INT chkstf(VOID);
INT hdlerr(CHAR *errstg,...);

VOID
merge1(                            /* merge newnam and oldnam into outnam  */
CHAR *oldn,                             /* old message file path and name  */
CHAR *newn,                             /* new message file path and name  */
CHAR *outn,                             /* output msg file path and name   */
CHAR *lang2ow)                          /* language to overwrite msgs for  */
{
     ll_merge1(TRUE,oldn,newn,outn,lang2ow);
}

INT
ll_merge1(             /* low-level merge - newnam and oldnam into outnam  */
GBOOL cataonerr,                        /* catastro on errors?             */
CHAR *oldn,                             /* old message file path and name  */
CHAR *newn,                             /* new message file path and name  */
CHAR *outn,                             /* output msg file path and name   */
CHAR *lang2ow)                          /* language to overwrite msgs for  */
{
     LONG fpos;
     INT i,map,ncma;

     cataok=cataonerr;
     if (oldmsg == NULL) {
          oldmsg=(struct oldmsgs *)alcmem(MAXOPTS*sizeof(struct oldmsgs));
          inimsgrdr(OPTSIZE);
          newlnm=(struct altlng *)alcmem(MAXLANG*sizeof(struct altlng));
     }
     stzcpy(oldnam,oldn,sizeof(oldnam));
     stzcpy(newnam,newn,sizeof(newnam));
     stzcpy(outnam,outn,sizeof(outnam));
     if (lang2ow == NULL) {
          owlang[0]='\0';
     }
     else {
          stzcpy(owlang,lang2ow,LNGSIZ);
     }
     if ((newfp=fopen(newnam,FOPRB)) == NULL) {
          return(hdlerr("ll_merge1: Can't open %s for input!",newnam));
     }
     if ((oldfp=fopen(oldnam,FOPRB)) == NULL) {
          if (!movefile(outnam,oldnam)) {
               return(hdlerr("ll_merge1: Error in renaming %s to %s!",
                             outnam,oldnam));
          }
          if ((oldfp=fopen(oldnam,FOPRB)) == NULL) {
               return(hdlerr("ll_merge1: Can't open %s for input!",oldnam));
          }
     }
     if ((outfp=fopen(outnam,FOPWB)) == NULL) {
          return(hdlerr("ll_merge1: Can't open %s for output!",outnam));
     }
     if (maktbl() == UPDFAILURE) {
          return(UPDFAILURE);
     }
     msgcnt=0;
     rdfp=newfp;
     while (rdmsg()) {
          if (chkstf() == UPDFAILURE) {
               return(UPDFAILURE);
          }
          scanalt();
          litopts();
          if (msgcnt == 0) {
               if (sameas(msgnam,"LANGUAGE")) {
                    for (i=0 ; i < salingo ; i++) {
                         if (altlnm[i].value.fsk == 0L) {
                              return(hdlerr("ll_merge1: Language name #%d "
                                            "omitted in %s",i,newnam));
                         }
                         if (i != 0) {
                              loadtv(rdfp,&altlnm[i]);
                         }
                         if (addnew(txtbuf,i) == UPDFAILURE) {
                              return(UPDFAILURE);
                         }
                    }
               }
               else {
                    newmap[0]=0;
               }
               if (sameas(msgnam,"LANGUAGE")
                || sameas(oldmsg[0].name,"LANGUAGE")) {
                    fprintf(outfp,STR_EOL"LANGUAGE {");
                    for (i=0 ; i < totlng ; i++) {
                         if (i != 0) {
                              fprintf(outfp,",{");
                         }
                         fprintf(outfp,"%s}",lnglst[i]);
                    }
                    fprintf(outfp,STR_EOL STR_EOL);
               }
          }
          if (sameas(msgnam,"LANGUAGE")) {
               if (msgcnt != 0) {
                    return(hdlerr("ll_merge1: In %s, LANGUAGE{} must be the "
                                  "very first option",newnam));
               }
          }
          else {
               fprintf(outfp,"%s"STR_EOL"%s {",hlpbuf,msgnam);
               if (samein("UNUSED*",lobuf) || !oldmat()) {
                    ncma=0;
                    fpos=ftell(newfp);
                    for (i=0 ; i < totlng ; i++) {
                         if ((map=newmap[i]) == -1 || map >= salingo
                           || altlnm[map].value.fsk == 0L) {
                              if (i == 0) {
                                   fprintf(outfp,"}");
                              }
                              else {
                                   ncma++;
                              }
                         }
                         else {
                              while (ncma != 0) {
                                   fprintf(outfp,",");
                                   ncma--;
                              }
                              if (i != 0) {
                                   fprintf(outfp,",{");
                              }
                              loadtv(newfp,&altlnm[map]);
                              putval(outfp);
                              fprintf(outfp,"}");
                         }
                    }
                    fseek(newfp,fpos,SEEK_SET);
                    fprintf(outfp,"%s"STR_EOL STR_EOL,lobuf);
               }
          }
          msgcnt++;
     }
     fclose(oldfp);
     fclose(newfp);
     fclose(outfp);
     freeln();
     return(UPDSUCCESS);
}

INT
maktbl(VOID)                       /* fill in the oldmsg array with info   */
{
     INT i;

     setmem(newmap,sizeof(newmap),0xFF);
     setmem(oldmsg,MAXOPTS*sizeof(struct oldmsgs),0);
     msgcnt=0;
     rdfp=oldfp;
     while (rdmsg()) {
          scanalt();
          if (chkstf() == UPDFAILURE) {
               return(UPDFAILURE);
          }
          if (sameas(msgnam,"LANGUAGE")) {
               if (msgcnt != 0) {
                    return(hdlerr("maktbl: In %s, LANGUAGE{} must be the very "
                                  "first option",oldnam));
               }
               if ((noldln=salingo) > MAXLANG) {
                    return(hdlerr("maktbl: Too many languages in %s!",oldnam));
               }
               for (i=0 ; i < noldln ; i++) {
                    if (altlnm[i].value.fsk == 0L) {
                         return(hdlerr("maktbl: Language name #%d omitted "
                                       "in %s",i,oldnam));
                    }
                    if (i != 0) {
                         loadtv(oldfp,&altlnm[i]);
                    }
                    lnglst[i]=alcdup(txtbuf);
               }
          }
          else if (msgcnt == 0) {
               noldln=1;
               lnglst[0]=alcdup(DFTLNG);
          }
          strcpy(oldmsg[msgcnt].name,msgnam);
          oldmsg[msgcnt].offset=altlnm[0].value.fsk;
          msgcnt++;
     }
     numold=msgcnt;
     totlng=noldln;
     return(UPDSUCCESS);
}

INT
oldmat(VOID)                       /* old match for msg?  take care of it  */
{
     LONG fpos;
     INT i,j,ncma,new1st;
     CHAR buf[25];

     for (i=0 ; i < numold ; i++) {
          if (sameas(oldmsg[i].name,msgnam)) {
               stzcpy(buf,skpwht(hlpbuf),sizeof(buf));
               depad(buf);
               if (sameto("LEVEL",msgnam) && alldgs(&msgnam[5]) &&
                   sameas(buf,"END_UPD")) {
                    fseek(newfp,0L,SEEK_END);
                    fseek(oldfp,oldmsg[i].offset,SEEK_SET);
                    xfrfil(oldfp,outfp,GCMAXLONG);
               }
               else {
                    newsal=salingo;
                    movmem(altlnm,newlnm,newsal*sizeof(struct altlng));
                    rdfp=oldfp;
                    fseek(oldfp,oldmsg[i].offset,SEEK_SET);
                    getval(oldfp);
                    scanalt();
                    fpos=ftell(newfp);
                    ncma=0;
                    for (j=0 ; j < totlng ; j++) {
                         new1st=sameas(lnglst[j],owlang)
                             || (sameto("LEVEL",msgnam) && alldgs(&msgnam[5]));
                         if (!new1st && isinold(j)) {
                              ncma=useold(j,ncma);
                         }
                         else if (isinnew(j)) {
                              ncma=usenew(j,ncma);
                         }
                         else if (new1st && isinold(j)) {
                              ncma=useold(j,ncma);
                         }
                         else {
                              if (j == 0) {
                                   fprintf(outfp,"}");
                              }
                              else {
                                   ncma++;
                              }
                         }
                    }
                    fprintf(outfp,"%s"STR_EOL STR_EOL,lobuf);
                    fseek(newfp,fpos,SEEK_SET);
                    rdfp=newfp;
               }
               return(1);
          }
     }
     return(0);
}

INT
isinold(                           /* is this language number in old file? */
INT lngnum)
{
     return(lngnum < salingo && altlnm[lngnum].value.fsk != 0L);
}

INT
useold(                            /* bring over the msg from the old file */
INT lngnum,
INT ncma)
{
     while (ncma != 0) {
          fprintf(outfp,",");
          ncma--;
     }
     if (lngnum != 0) {
          fprintf(outfp,",{");
          loadtv(oldfp,&altlnm[lngnum]);
     }
     putval(outfp);
     fprintf(outfp,"}");
     return(ncma);
}

INT
isinnew(                           /* is this language number in new file? */
INT lngnum)
{
     INT map;

     map=newmap[lngnum];
     return(map != -1 && map < newsal && newlnm[map].value.fsk != 0L);
}

INT
usenew(                            /* bring over the msg from the new file */
INT lngnum,
INT ncma)
{
     while (ncma != 0) {
          fprintf(outfp,",");
          ncma--;
     }
     if (lngnum != 0) {
          fprintf(outfp,",{");
     }
     loadtv(newfp,&newlnm[newmap[lngnum]]);
     putval(outfp);
     fprintf(outfp,"}");
     return(ncma);
}

INT
addnew(                            /* possibly add new language to list    */
CHAR *nam,
INT num)
{
     INT i;

     for (i=0 ; i < noldln ; i++) {
          if (sameas(lnglst[i],nam)) {
               newmap[i]=num;
               return(UPDSUCCESS);
          }
     }
     if (totlng == MAXLANG-1) {
          return(hdlerr("addnew: Too many languages to import from %s!",
                         newnam));
     }
     lnglst[totlng]=alcdup(nam);
     newmap[totlng]=num;
     totlng++;
     return(UPDSUCCESS);
}

VOID
freeln(VOID)                       /* free memory used for languages       */
{
     INT i;

     for (i=0 ; i < totlng ; i++) {
          free(lnglst[i]);
     }
}

INT
chkstf(VOID)                       /* check to make sure everything's ok   */
{
     if (msgcnt == MAXOPTS-1) {
          return(hdlerr("chkstf: Maximum msg count exceeded at %s!",msgnam));
     }
     return(UPDSUCCESS);
}


INT
updmdf(                            /* update .MDF/.DMD file (preserve some)*/
GBOOL cataonerr,                        /* catastro if error?              */
CHAR *oldmdf,                           /* name of temporary old .MDF file */
CHAR *newmdf,                           /* name of temporary new .MDF file */
CHAR *finmdf)                           /* name for the final .MDF/DMD file*/
{
     CHAR *nptr;
     FILE *infp,*outfp;
     INT olddfl=-1;
     static CHAR oldnam[50],mdfbuf[100];

     cataok=cataonerr;
     oldnam[0]='\0';
     if ((infp=fopen(oldmdf,FOPRA)) == NULL) {
          return(hdlerr("updmdf: Can't read from \"%s\"!",oldmdf));
     }
     while (fgets(mdfbuf,sizeof(mdfbuf),infp) != NULL) {
          if (sameto(MNMPFX,mdfbuf)) {
               nptr=skpwht(&mdfbuf[strlen(MNMPFX)]);
               stzcpy(oldnam,nptr,sizeof(oldnam));
          }
          else if (sameto(DFLPFX,mdfbuf)) {
               olddfl=atoi(skpwht(&mdfbuf[strlen(DFLPFX)]));
          }
     }
     fclose(infp);
     if ((infp=fopen(newmdf,FOPRA)) == NULL) {
          return(hdlerr("updmdf: Can't read from \"%s\"!",newmdf));
     }
     if ((outfp=fopen(finmdf,FOPWA)) == NULL) {
          return(hdlerr("updmdf: Can't write to \"%s\"!",finmdf));
     }
     while (fgets(mdfbuf,sizeof(mdfbuf),infp) != NULL) {
          if (sameto(MNMPFX,mdfbuf) && oldnam[0] != '\0') {
               sprintf(mdfbuf,"%s %s\n",MNMPFX,oldnam);
          }
          else if (sameto(DFLPFX,mdfbuf) && olddfl != -1) {
               sprintf(mdfbuf,"%s %d\n",DFLPFX,olddfl);
          }
          fputs(mdfbuf,outfp);
     }
     fclose(infp);
     fclose(outfp);
     unlink(oldmdf);
     unlink(newmdf);
     return(UPDSUCCESS);
}

VOID
freeupdm(VOID)                     /* free memory allocated by utilities   */
{
     if (oldmsg != NULL) {
          free(oldmsg);
          oldmsg=NULL;
          clsmsgrdr();
          free(newlnm);
          newlnm=NULL;
     }
     if (upderr != NULL) {
          free(upderr);
          upderr=NULL;
     }
}

INT                                /* returns UPDFAILURE if not catastro() */
hdlerr(                            /* handle error condition               */
CHAR *errstg,                      /*   format string for error            */
...)                               /*   variable parameters                */
{
     va_list errargs;

     va_start(errargs,errstg);
     if (upderr == NULL) {
          upderr=alczer(1024);
     }
     vsprintf(upderr,errstg,errargs);
     va_end(errargs);
     if (cataok) {
          catastro(upderr);
     }
     return(UPDFAILURE);
}
