/* genmess. Generation of messages.
   Copyright 1994 Tristan Gingold
		  Written January 1994 by Tristan Gingold
		  
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of the
License, or (at your option) any later version.

This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
General Public License for more details.

You should have received a copy of the GNU General Public License 
along with this program; see the file COPYING.  If not, write to
the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

   The author may be reached (Email) at the address marc@david.saclay.cea.fr,
   or (US/French mail) as Tristan Gingold 
   			  8 rue Parmentier
   			  F91120 PALAISEAU
   			  FRANCE 
*/
/* genmess allow you to generate easily messages in differents languages.
   From the source file, it generates an header file (.h) and a message 
   file (.c). The header file is design to be included by program.
   
   The input file can have 5 sortes of lines:
   $header "............."
   $format "............"
   ....... ".........."
   #...........
   
   The last one is a blank line.
   Each line must begin with a '$', a '#', a letter or nothing.
   If a line begins with a '#' or with nothing, it is treated as a comment.
   If a line begins with $header, $declare or $format a string, enclosed in "
    must follow. 
    For $header lines, this string, without the " is directly copied in the 
    message file and ={ is appended. This is used to declare the array of
    messages.
    For $format lines, this string will be used to build the header file. This
    line is optional, and initialized by default, after each $header lines to
    "%d".
    For $declare lines, the string is copied in the header file, with a \n.
   If a line begins with characters ([a-zA-Z_0-9]), the first word is called a
    symbol, and the next string a message. The message is written to the
    message file with a comma. The symbol is appended to the header file:
    #define symbol format
    format is defined by $format and is used for the format of printf. If 
    format contains %s, the argument is the message and the message is not
    written to the message file. If format contains %d, the argument is the
    number of the message, starting from 0.
   You can't used symbol lines before an $header line.
   Spaces are ' ', '\t' and musr separate two fields.
   
   At the end of the message file, }; is appended.
Exemple:
$header "const char *mess[]"
FIRST "first"
SECOND "second"
   
   Usage: genmess [options] headerfile messagefile inputfile
     options are:
      -a --append: append after the files
    inputfile can be - to use stdin.
   If headerfile or messagefile doesn't exist, they are created. Otherwise,
    genmess appends if -a option is used
   See the source for other details.
*/   

#define _POSIX_SOURCES
#include <limits.h>
#include <stdio.h>
#include <strings.h>

FILE *inputfile;
FILE *headerfile;
FILE *message;

char buffer[_POSIX2_LINE_MAX];
char header[_POSIX2_LINE_MAX];
char format[_POSIX2_LINE_MAX];

int line = 1;			/* line number */
char *inputname = NULL;		/* input file name */
char *progname;

void
fatal(char *m)
{
  if (inputname == NULL)
    fprintf(stderr, "%s", m);
  else
    fprintf(stderr, "%s:%d: %s", inputname, line, m);  
  exit(1);
}

/* Return the offset of the first '"' in a buffer (buf) starting from 'start'
 * and put an \0 after the second '"'. \" are accepted.
 */
int
get_string(char *buf, int start)
{
 int i;
 /* skip the blanks */
 for (; buf[start] == ' ' || buf[start] == '\t'; start++)
   ;
 /* check the fisrt '"' */
 if (buf[start] != '\"' )
   return 0;
 i = start + 1;
 while (1)
   {
     if (buf[i] == '\\')
       if (buf[i+1] != '\0' || buf[i+1] != '\n')
         i++;
       else
         return 0;
     if (buf[i] == '\"')
       break;
     if (buf[i] == '\n' || buf[i] == '\0')
       return 0;
     i++;
   }
 buf[++i] = '\0';
 return start;
}

/* Return 1 if c is [A-Za-z0-9_] */
int
is_c_char(char c)
{
  if ( c >= 'A' && c <= 'Z')
    return 1;
  if ( c >= 'a' && c <= 'z')
    return 1;
  if ( c >= '0' && c <= '9')
    return 1;
  if ( c == '_')
    return 1;
  return 0;
}

int
format_type(char *f)
{
 while (*f != '\0')
   {
     if (*f == '%')
       switch (f[1])
         {
           case '%':
           	f++;
           	break;
           case 'd':
           	return 0;
           case 's':
           	return 1;
           default:
           	fatal("Bad format\n");
         }
     f++;
   }
 fatal("Bad format\n");
 return 0;	/* unuseful */
} 

void
usage()
{
  fprintf(stderr, "Usage: %s [options] headerfile messagefile inputfile\n", progname);
  fprintf(stderr, "Options are:\n");
  fprintf(stderr, "-a --append\n");
  exit(2);
}
           	
void main(int argc, char *argv[])
{
 int i,j;
 int number = 0;
 int ftype = 0;
 int noheader = 1;
 int append = 0;
 
 progname = argv[0];
 
 if (argc < 4)
   usage();
   
 i = 1;	/* first argument */
 while(argv[i][0] == '-')
   {
     if ((argv[i][1] == 'a' && argv[i][2] == '\0') 
          || strcmp(argv[i],"--append") == 0)
       {
         append = 1;
         i++;
         continue;
       }
     usage();
   }
     
 if ( (headerfile = fopen(argv[i], append ? "aw" : "w")) == NULL)
   {
     perror("Can't open header file");
     exit(1);
   }
 if (append == 0 || ftell(headerfile) == 0)
   fprintf(headerfile, "/* Automaticaly generated by genmess. Do not edit! */\n");
 if ( (message = fopen(argv[i+1], append ? "aw" : "w")) == NULL)
   {
     perror("Can't open message file");
     exit(1);
   }
 if (append == 0 || ftell(message) == 0)
   fprintf(message, "/* Automaticaly generated by genmess. Do not edit! */\n");
 if ( argv[i+2][0] == '-' && argv[i+2][1] == '\0')
   {
     inputfile = stdin;
     inputname = "(standard input)";
   }
 else
   {
     if ( (inputfile=fopen(argv[i+2], "r")) == NULL)
       {
         perror("Can't open input file");
         exit(1);
       }
     inputname = argv[i+2];
   }
     
 /* Main loop: read a line and treat it */
 while(1)
   {
     line++;
     if (fgets(buffer, _POSIX2_LINE_MAX, inputfile) == NULL)
       {
         if (feof(inputfile))
           {
             fclose(headerfile);
             if (noheader == 0)
               fprintf(message,"/* %d */ \"\"\n};\n", number );
             fclose(message);
             fclose(inputfile);
             exit(0);
           }
         fatal("Unexpected EOF\n");
       }
     /* Skip nul lines */
     if (strlen(buffer) == 0)
       continue;
     /* Skip comments */
     if (buffer[0] == '#')
       continue;
     /* Skip blank lines */
     for(i=0; buffer[i] != '\0' ;i++)
       if (buffer[i] != ' ' && buffer[i] != '\n' && buffer[i] != '\t')
         break;
     if (buffer[i] == '\0')
       continue;
     if (buffer[0] == '$')
       {
         if (strncmp(&buffer[1], "header", 6) == 0)
           {
             j = get_string(buffer, 7);
             if (j == 0)
               fatal("$header must be followed by a string (\"xxx\")\n");
             buffer[j++] = '\0';
             /* replace the last '"' by a '\0' */
             buffer[j + strlen(buffer + j) - 1] = '\0';
             if (noheader == 0)
               fprintf(message,"/* %d */ \"\"\n};\n", number );
             strcpy(format,"%d");	/* default format */
             ftype = 0;
             noheader = 0;
             fprintf(message,"\n%s={\n", buffer + j);
             continue;
           }
         if (strncmp(&buffer[1], "format", 6) == 0)
           {
             j = get_string(buffer, 7);
             if (j == 0)
               fatal("$format must be followed by a string (\"xxx\")\n");
             buffer[j++] = '\0';
             /* Replace the second '"' by '\0' */
             buffer[j + strlen(buffer + j) - 1] = '\0';
             if (noheader == 1)
               fatal("Can't defined a format before an header\n");
             strcpy(format, buffer + j);
             ftype = format_type(format);	/* type of format */
             continue;
           }
         if (strncmp(&buffer[1], "declare", 7) == 0)
           {
             j = get_string(buffer, 8);
             if (j == 0)
               fatal("$declare must be followed by a string (\"xxx\")\n");
             buffer[j++] = '\0';
             /* Replace the second '"' by '\0' */
             buffer[j + strlen(buffer + j) - 1] = '\0';
             fprintf(headerfile, "%s\n", buffer + j);
             continue;
           }
         fatal("Unknown order\n");
       }
     if (noheader == 1)
       fatal("Get a message before an header\n");
     /* get symbol */
     for (i = 0; is_c_char(buffer[i]); i++)
       ;
     if (i<2 || buffer[i] == '\0')
       {
         fprintf(stderr,"%s:%d: syntax error\n", argv[1], line);
         exit(1);
       }
     buffer[i++] = '\0';
     
     /* Get the message */
     j = get_string(buffer, i);
     if (j == 0)
       fatal("Syntax error\n");
       
     fprintf(headerfile,"#define %s\t", buffer);
     if (ftype == 0)
       {
         fprintf(headerfile,format,number);
         fprintf(message,"/* %d */ %s,\n", number, &(buffer[j]));
       }
     else
       fprintf(headerfile,format,&(buffer[j]));
     fputc('\n', headerfile);

     number++;
   }
}     
