/**************************************************************************/
/* GENERR - program for error messages generation                         */
/* (c) Petr Peringer 1991-1997                                            */
/**************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>

/**************************************************************************/

#define  MAXERR      1000
#define  MAXIDLEN    32
#define  MAXLINELEN  255

enum Boolean { FALSE, TRUE };

char tittle[] =
"GENERR - error messages generator                     (c)PerPet 1991-1997 \n";
char help[] =
"\n"
" usage:   GENERR InputFileName \n"
"\n"
" Input file format:\n"
"\n"
"   ErrorFunctionName(ErrorEnumName)\n"
"   ErrorName0      Text of error message 0\n"
"   ErrorName1      Text of error message 1\n"
"    ...\n"
"\n"
"   Comment line starts with nonletter character\n"
"   Name must be an C-identifier!\n"
"\n"
"   Output files are:\n"
"     InputFileName.h\n"
"     InputFileName.cc\n"
;

struct tabitem {
  char *ErrId;
  char *ErrMsg;
};

typedef struct {
  struct tabitem Table[MAXERR];
  unsigned NumErr;
} TABLE;

TABLE *tab;
char funcname[MAXIDLEN+1];
char enumname[MAXIDLEN+1];

FILE *inf;
char fname[512];                /* module file name */
char modfname[80];              /* module file name */
char headfname[80];             /* header file name */
char line[MAXLINELEN+2];
char *sp = line;
int lineno=0;

/**************************************************************************/
/**************************************************************************/
/* Generated by program GENERR */

enum ErrEnum {
/* 0 */ NoError,
/* 1 */ ErrBadArguments,
/* 2 */ ErrBadFile,
/* 3 */ ErrBadHeader,
/* 4 */ ErrBadIdentifier,
/* 5 */ ErrCantOpenIn,
/* 6 */ ErrCantOpenOut,
/* 7 */ ErrDuplicateId,
/* 8 */ ErrIdTooLong,
/* 9 */ ErrLineTooLong,
/* 10 */ ErrNoIdentifier,
/* 11 */ ErrNoMemory,
/* 12 */ ErrTooManyMsg,
};

static char _Errors[] = {
/* 0 */ "\0"
/* 1 */ "Bad arguments\0"
/* 2 */ "File have not a header\0"
/* 3 */ "Error in head line\0"
/* 4 */ "Bad identifier\0"
/* 5 */ "Can't open input file\0"
/* 6 */ "Can't open output file\0"
/* 7 */ "Duplicate identifier\0"
/* 8 */ "Identifier too long\0"
/* 9 */ "Line too long\0"
/* 10 */ "Identifier expected\0"
/* 11 */ "Out of memory\0"
/* 12 */ "Too many messages in input file\0"
};

char *ErrMsg(enum ErrEnum N)
{
  char *p = _Errors;
  while( N-- > 0 )
    while( *p++ != '\0' );
  return p;
};

/**************************************************************************/
/**************************************************************************/
void error( enum ErrEnum N )
{
  printf("Error#%d: %s\n",N,ErrMsg(N));
  if (N==ErrBadArguments) printf(help);
  exit(N);
}

/**************************************************************************/
void InitTable(TABLE **t)
{
  (*t) = malloc(sizeof(TABLE));
  if( *t == NULL ) 
    error(ErrNoMemory);
  (*t)->NumErr = 0;
}


FILE *OpenInputFile(char *name)
{
  FILE *inf;
  char *s;
  strncpy(fname,name,500); /* save name for future use */
  inf = fopen(name,"rt");
  if (inf==NULL)  
    error(ErrCantOpenIn);
  strcpy(modfname,name);
  for(s=modfname; *s!='\0'&& *s!='.'; s++);
  strcpy(s,".cc");
  strcpy(headfname,name);
  for(s=headfname; *s!='\0'&& *s!='.'; s++);
  strcpy(s,".h");
  return inf;
}

FILE *OpenOutputFile(char *name)
{
  FILE *outf;
  outf = fopen(name,"wt");
  if (outf==NULL) 
    error(ErrCantOpenOut);
  return outf;
}


/**************************************************************************/
int GetLine(FILE *inf)                   /* read one input line to buffer */
{
  int l;
  do{
    if (fgets(line,MAXLINELEN,inf) == NULL) return FALSE;
    ++lineno;
#ifdef PRINT_LINE_NUMBERS
    if(lineno==1) printf("Line start ");
    printf("\b\b\b\b\b\b%-5d ",lineno);
#endif
    l = strlen(line) - 1;
    if (line[l] != '\n')
    {
       if(!feof(inf)) error(ErrLineTooLong); //###
    }
    line[l] = '\0';
  } while ( !(isalpha(line[0])||line[0]=='_') || line[0]=='\0');
  sp = line;
  return TRUE;
}

/**************************************************************************/
void SkipBlanks(void)
{
  while( *sp==' ' || *sp=='\t' ) sp++;
}

int GetChar(void)                        /***** get character *****/
{
  return (*sp=='\0')? *sp : *sp++ ;
}

void GetIdent(char *sptr)                /***** get an identifier *****/
{ int i=0;
  if (!(isalpha(*sp)||*sp=='_'))  error(ErrBadIdentifier);
  while(i<=MAXIDLEN && (isalnum(*sp)||*sp=='_'))  sptr[i++] = *sp++;
  if (i>MAXIDLEN)  error(ErrIdTooLong);
  if (i==0)  error(ErrBadIdentifier);
  sptr[i] = '\0';
}

char *MyStrDup(char *sp)
{
  char *s = strdup(sp);
  if (s==NULL) error(ErrNoMemory); /* with test */
  return s;
}

char *GetErrId(void)
{
  char s[MAXIDLEN+1];
  GetIdent(s);
  return MyStrDup(s);
}

char *GetErrMsg(void)
{
  SkipBlanks();
  return MyStrDup(sp);
}

/**************************************************************************/
/*                             G E T                                      */
/**************************************************************************/
void GetHeader(FILE *inf,char*funcname, char*enumname)
{
  if (!GetLine(inf)) error(ErrBadFile);
  GetIdent(funcname);
  SkipBlanks();
  if (GetChar()!='(') error(ErrBadHeader);
  SkipBlanks();
  GetIdent(enumname);
  SkipBlanks();
  if (GetChar()!=')') error(ErrBadHeader);
}

int CheckUniq(TABLE *t)
{
  int i;
  char *s = t->Table[t->NumErr].ErrId;
  for( i=0; i<t->NumErr; i++ )
    if (strcmp(t->Table[i].ErrId,s)==0) return FALSE;
  return TRUE; /* unique identifier */
}

void GetTable(FILE *inf, TABLE *t)
{
  while (GetLine(inf))
    {
      t->Table[t->NumErr].ErrId  = GetErrId();
      t->Table[t->NumErr].ErrMsg = GetErrMsg();
      if (!CheckUniq(t)) error(ErrDuplicateId);
      if (++t->NumErr>MAXERR) error(ErrTooManyMsg);
    }
}

/**************************************************************************/
/*                             G E N E R                                  */
/**************************************************************************/
void GenEnum(FILE *outf, TABLE *t)
{
  int i;
  fprintf(outf,"enum %s {\n",enumname);
  for( i=0 ; i<(*t).NumErr ; i++ )
    fprintf(outf,"/* %d */ %s,\n",i,(*t).Table[i].ErrId);
  fprintf(outf,"};\n\n");
}

void GenMessages(FILE *outf, TABLE *t)
{
  int i;
  fprintf(outf,"static char _Errors[] = {\n");
  for( i=0 ; i<(*t).NumErr ; i++ )
    fprintf(outf,"/* %d */ \"%s\\0\" \n",i,(*t).Table[i].ErrMsg);
  fprintf(outf,"};\n\n");
}

void GenErrFun(FILE *outf)
{
  fprintf(outf,"char *%s(enum %s N)\n",funcname,enumname);
  fprintf(outf,"{\n");
  fprintf(outf,"  char *p = _Errors;\n");
  fprintf(outf,"  int i = N;\n");
  fprintf(outf,"  while( i-- > 0 )\n");
  fprintf(outf,"    while( *p++ != '\\0' );\n");
  fprintf(outf,"  return p;\n");
  fprintf(outf,"};\n");
}

/**************************************************************************/
void GenerModule(TABLE *t)
{
  FILE *outf = OpenOutputFile(modfname);
  fprintf(outf,"//\n// %s\n", modfname);
  fprintf(outf,"//\n//\n//\n//\n");
  fprintf(outf,"/* Generated from file '%s' by program GENERR */\n\n", fname);
  fprintf(outf,"#include \"%s\"\n\n",headfname);
  GenMessages(outf,t);
  GenErrFun(outf);
  fclose(outf);
}

/**************************************************************************/
void GenerHeader(TABLE *t)
{
  FILE *outf = OpenOutputFile(headfname);
  fprintf(outf,"//\n// %s\n", headfname);
  fprintf(outf,"//\n//\n//\n//\n");
  fprintf(outf,"/* Generated from file '%s' by program GENERR */\n\n", fname);
  GenEnum(outf,t);
  fprintf(outf,"extern char *%s(enum %s N);\n",funcname,enumname);
  fprintf(outf,"\n");
  fclose(outf);
}

/**************************************************************************/
/*                                M A I N                                 */
/**************************************************************************/
int main( int argc, char *argv[])
{
  printf(tittle);
  if (argc!=2)  error(ErrBadArguments);
  inf = OpenInputFile(argv[1]);
  InitTable(&tab);
  GetHeader(inf,funcname,enumname);    /* cte 1 radek - hlavicku          */
  GetTable(inf,tab);                   /* cte chybova hlaseni do tabulky  */
  GenerModule(tab);                    /* generuje vyst. soubor .C        */
  GenerHeader(tab);                    /* generuje vyst. soubor .H        */
  printf("\nNumber of messages = %d \n",tab->NumErr);
  return(0);                           /* OK                              */
}
/**************************************************************************/
