/***************************************************************************
 *                                                                         *
 *   GALPFNC.C                                                             *
 *                                                                         *
 *   Copyright (c) 1996-1997 Galacticomm, Inc.    All Rights Reserved.     *
 *                                                                         *
 *   Profanity list compiler.  This takes an unsorted list of "profane"    *
 *   words and profanity levels and compiles them into a properly sorted/  *
 *   indexed binary data file for use by GCOMM.LIB function readpfn().     *
 *                                                                         *
 *                                           - J. Alvrus    2/29/96        *
 *                                                                         *
 ***************************************************************************/

#include "gcomm.h"
#include "excphand.h"

#define FILREV "$Revision: 7 $"

#define USAGE \
     "Galacticomm Profanity List Compiler\n"                               \
     "Copyright (c) 1996-1997, Galacticomm, Inc.  All rights reserved.\n\n"\
     "usage: GALPFNC <filename>\n"                                         \
     "compile <filename> into new profanity list\n\n"                      \
     "or:    GALPFNC -x <filename>\n"                                      \
     "export current list to <filename>\n"

#define TMPLISTFILE "galpfn.new"
#define MAXENTRY 31

struct word {
     INT lvl;
     CHAR wrd[MAXENTRY];
} **wordlist;
#define MAXNWORDS (PFN_MAXALC/sizeof(struct word *))

INT numwords;
CHAR inpbuf[256];

VOID export(const CHAR *filename);
VOID import(const CHAR *filename);
INT parseline(CHAR *inp);
INT _USERENTRY wordcomp(const VOID *ppwrd1,const VOID *ppwrd2);
INT savepfn(FILE *fp);
VOID errexit(const CHAR *errmsg,...);
LONG filesize(const CHAR *filename);

INT
main(                              /* main function                        */
INT argc,
CHAR *argv[])
{
TRY
#ifdef GCWINNT
     if (!canRunUtil()) {
          MessageBox(NULL,NOPROCEED,
                    argv[0],MB_ICONSTOP|MB_OK|MB_TASKMODAL|MB_SETFOREGROUND);
          return(1);
     }
#endif // GCWINNT
     initvid();
     if (argc < 2 || argc > 4) {
          printf(USAGE);
          return(1);
     }
     if (argc == 3 && strcmpi(argv[1],"-x") == 0) {
          export(argv[2]);
     }
     else {
          import(argv[1]);
     }
     return(0);
EXCEPT
#ifdef GCWINNT
     return(2);
#endif // GCWINNT
}

VOID
export(                            /* export profanity list to text file   */
const CHAR *filename)              /*   name of file to write to           */
{
     INT i,c;
     USHORT *offsets;
     CHAR *cp;
     LONG tmpsiz;
     FILE *fp;
     CHAR *pfnbuf;

     tmpsiz=filesize(PFNLISTFILE);
     if (tmpsiz > PFN_MAXALC) {
          errexit("INVALID INPUT FILE: %s",PFNLISTFILE);
     }
     else if (tmpsiz == 0L) {
          errexit("INPUT FILE NOT FOUND: %s",PFNLISTFILE);
     }
     switch (readpfn(PFNLISTFILE,&pfnbuf)) {
     case RDPFN_OK:
          break;
     case RDPFN_FOPEN:
          errexit("UNABLE TO OPEN %s FOR INPUT",PFNLISTFILE);
     case RDPFN_EOF,RDPFN_INVALID:
          errexit("INVALID INPUT FILE: %s",PFNLISTFILE);
     default:
          errexit("ERROR READING FILE: %s",PFNLISTFILE);
     }
     offsets=(USHORT *)pfnbuf;
     if ((fp=fopen(filename,FOPWA)) != NULL) {
          for (i=0 ; i < 26 ; ++i) {
               cp=((CHAR *)&offsets[26])+offsets[i];
               while (*cp != 0) {
                    fputc('a'+i,fp);
                    while (islower(c=*cp++)) {
                         fputc(c,fp);
                    }
                    fprintf(fp," %d\n",c);
               }
          }
          fclose(fp);
     }
     else {
          errexit("UNABLE TO OPEN %s FOR OUTPUT",filename);
     }
}

VOID
import(                            /* import a new profanity list          */
const CHAR *filename)              /*   name of file to read from          */
{
     INT i;
     FILE *ifp,*ofp;

     if ((wordlist=(struct word **)malloc(sizeof(struct word *)*MAXNWORDS)) == NULL) {
          errexit("OUT OF MEMORY");
     }
     numwords=0;
     if ((ifp=fopen(filename,FOPRA)) != NULL) {
          while (fgets(inpbuf,sizeof(inpbuf),ifp) != NULL) {
               if (numwords >= MAXNWORDS) {
                    errexit("TOO MANY WORDS");
               }
               if (inpbuf[i=strlen(inpbuf)-1] == '\n') {
                    inpbuf[i]='\0';
               }
               else if (!feof(ifp)) {
                    errexit("LINE TOO LONG");
               }
               if ((i=parseline(inpbuf)) != RDPFN_OK) {
                    switch (i) {
                    case RDPFN_EOF:
                         errexit("OUT OF MEMORY");
                    case RDPFN_OVERFLOW:
                         errexit("WORD TOO LONG: %s",inpbuf);
                    case RDPFN_INVALID:
                         errexit("INVALID INPUT FILE FORMAT");
                    }
               }
          }
          fclose(ifp);
     }
     else {
          errexit("UNABLE TO OPEN %s FOR INPUT",filename);
     }
     qsort(wordlist,numwords,sizeof(struct word *),wordcomp);
     for (i=0 ; i < numwords-1 ; ++i) {
          if (wordcomp(&wordlist[i],&wordlist[i+1]) == 0) {
               errexit("DUPLICATE WORD: %s",wordlist[i]->wrd);
          }
     }
     if ((ofp=fopen(TMPLISTFILE,FOPWB)) != NULL) {
          i=savepfn(ofp);
          fclose(ofp);
          if (i == RDPFN_OK) {
               unlink(PFNLISTFILE);
               if (rename(TMPLISTFILE,PFNLISTFILE) == 0) {
                    unlink(TMPLISTFILE);
               }
          }
          else {
               remove(TMPLISTFILE);
               switch (i) {
               case RDPFN_OVERFLOW:
                    errexit("TOO MANY WORDS");
               }
          }
     }
     else {
          errexit("UNABLE TO OPEN %s FOR OUTPUT",TMPLISTFILE);
     }
}

INT                                /*   error code                         */
parseline(                         /* parse a line of input                */
CHAR *inp)                         /*   input line                         */
{
     INT i;
     CHAR *cp;
     struct word *wp;

     cp=unpad(skpwht(inp));
     if (*cp == '\0') {
          return(RDPFN_OK);
     }
     if ((wordlist[numwords++]=wp=malloc(sizeof(struct word))) == NULL) {
          return(RDPFN_EOF);
     }
     i=0;
     while (i < MAXENTRY && isalpha(*cp)) {
          wp->wrd[i++]=tolower(*cp++);
     }
     if (i == MAXENTRY) {
          return(RDPFN_OVERFLOW);
     }
     if (i == 0 || !isspace(*cp)) {
          return(RDPFN_INVALID);
     }
     wp->wrd[i]='\0';
     while (isspace(*cp)) {
          ++cp;
     }
     if (*cp < '1' || *cp > '3' || cp[1] != '\0') {
          return(RDPFN_INVALID);
     }
     wp->lvl=*cp-'0';
     return(RDPFN_OK);
}

INT _USERENTRY                     /*   result of comparison < 0, == 0, > 0*/
wordcomp(                          /* compare two words                    */
const VOID *ppwrd1,                /*   pointer to pointer to first word   */
const VOID *ppwrd2)                /*   pointer to pointer to second word  */
{
     INT rc;
     struct word *wp1,*wp2;

     wp1=*(struct word **)ppwrd1;
     wp2=*(struct word **)ppwrd2;
     rc=strcmp(wp1->wrd,wp2->wrd);
     if (rc != 0) {
          if (sameto(wp1->wrd,wp2->wrd)) {        /* ass > asshole */
               rc=1;
          }
          else if (sameto(wp2->wrd,wp1->wrd)) {   /* asshole < ass */
               rc=-1;
          }
     }
     return(rc);
}

INT                                /*   error code                         */
savepfn(                           /* save profanity list                  */
FILE *fp)                          /*   file to save in                    */
{
     INT i,firstc;
     USHORT ofs;
     struct word *wp;

     ofs=0;
     for (i=0 ; i < 26 ; ++i) {
          fwrite(&ofs,1,sizeof(USHORT),fp);
     }
     i=0;
     for (firstc=0 ; firstc < 26 ; ++firstc) {
          if (ofs >= PFN_MAXALC) {
               return(RDPFN_OVERFLOW);
          }
          fseek(fp,firstc*sizeof(USHORT),SEEK_SET);
          fwrite(&ofs,1,sizeof(USHORT),fp);
          fseek(fp,0,SEEK_END);
          ++ofs;
          while (i < numwords && (wp=wordlist[i])->wrd[0] == 'a'+firstc) {
               if (ofs+strlen(wp->wrd) >= PFN_MAXALC) {
                    return(RDPFN_OVERFLOW);
               }
               ofs+=strlen(wp->wrd);
               fputs(&wp->wrd[1],fp);
               fputc(wp->lvl,fp);
               ++i;
          }
          fputc(0,fp);
     }
     return(RDPFN_OK);
}

LONG
filesize(                          /* get size of a file                   */
const CHAR *filename)              /*   name of file                       */
{
     struct ffblk fb;

     if (findfirst(filename,&fb,FA_RDONLY|FA_HIDDEN|FA_SYSTEM) == 0) {
          return(fb.ff_fsize);
     }
     return(0L);
}

VOID
errexit(                           /* display error message and exit       */
const CHAR *errmsg,...)            /*   error message to display           */
{
     va_list args;

     fputc('\n',stderr);
     va_start(args,errmsg);
     vfprintf(stderr,errmsg,args);
     va_end(args);
     fputc('\n',stderr);
     exit(1);
}
