/* -*- c -*- */

/*
  GlossTeX, a tool for the automatic preparation of glossaries.
  Copyright (C) 1997 Volkan Yavuz

  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 program 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; if not, write to the Free Software
  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

  Volkan Yavuz, [email protected]
*/

/* $Id: database.c,v 1.45 1997/12/13 16:06:54 volkan Exp $ */

#include "glosstex.h"
#include "database.h"
#include "error.h"
#include "list.h"
#include "labels.h"
#include <assert.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>

enum e_pass {
 PASS_1 = 0, PASS_2 = 1
};

static unsigned int lineno = 0;

static void process_file (enum e_pass pass, FILE * infile, char *inname);
static void process_line (enum e_pass pass, char *inname, char *line);
static void write_line (FILE * outfile,
                       char* inname,
                       char *list, char *label, char *item, char *longform,
                       char *line, char *list_mode, char *pageref_mode,
                       char *page);
static int glo_parse_item (char *line, char *label,
                          char *item, char *longform, int *ptr);

void
read_databases (void)
{
 FILE *dbfile;
 enum e_pass pass = PASS_1;
 s_node *filename = databases.root;

 for (pass = PASS_1; pass <= PASS_2; pass++) {
   while (filename != 0) {
     if ((dbfile = fopen (filename->ptr, "r")) == NULL) {
       error ("database %s", (char *) filename->ptr);
     } else {
       printlog (PROGRESS, STDOUT, "(%s ", (char *) filename->ptr);
       process_file (pass, dbfile, (char *) filename->ptr);
       printlog (PROGRESS, STDOUT, ")");
     }
     filename = filename->next;
   }
   filename = databases.root;
 }
}

static void
process_file (enum e_pass pass, FILE * dbfile, char *inname)
{
 enum e_state {
   HEADER, BODY
 };

 int status;
 char buf[LINESIZE];
 enum e_state state = HEADER;
 char *line = 0;

 while (fgets (buf, LINESIZE, dbfile) != 0) {
   if (buf[strlen (buf) - 1] == '\n')
     lineno++;

   status = strncmp (buf, "@entry{", 7);
   if ((state == HEADER) && (status != 0)) {
     ;                         /* ignore heading garbage */
   } else {
     state = BODY;
     if (status == 0) {        /* begin new entry */

       /* process current entry before starting new entry */
       if (line != 0) {
         process_line (pass, inname, line);
         free (line);
       }
       line = (char *) malloc (strlen (buf) + 1);
       assert (line != 0);
       strcpy (line, buf);

       if (line[strlen (line) - 1] == '\n')
         line[strlen (line) - 1] = ' ';

     } else if (strncmp (buf, "%", 1) == 0) {
       ;                       /* skip comments */
     } else {                  /* add lines to current entry */
       size_t len = strlen (line);     /* LINT: null is ok here */
       line = (char *) realloc (line, len + strlen (buf) + 1);
       assert (line != 0);
       strcpy (&line[len], buf);
       if (line[strlen (line) - 1] == '\n')
         line[strlen (line) - 1] = ' ';
     }
   }
 }

 /* process last pending line in file */
 if (line != 0) {
   process_line (pass, inname, line);
   free (line);
 }
}

static void
process_line (enum e_pass pass, char *inname, char *line)
{
 char label[LINESIZE];
 char item[LINESIZE];
 char longform[LINESIZE];
 size_t index;
 int ptr = 0;
 s_list_iterator iter;
 s_list_iterator iter2;
 s_label *node;
 s_label *node2;

 iter.root = labels.root;
 iter.current = labels.root;
 iter2.root = labels.root;
 iter2.current = labels.root;

 if (glo_parse_item (line, label, item, longform, &ptr) != 0) {
   printlog (PROGRESS, STDOUT, "x");
   printlog (WARNING, LOGFILE, "\n%s:%u parse error: %s",
             inname, lineno, line);
   count_gdf_parsing++;
   return;
 }
 /* remove all trailing spaces */
 index = strlen (&line[ptr]);
 index--;

 while (line[ptr + index] == ' ') {
   line[ptr + index] = '\0';
   index--;
 }

 switch (pass) {
 case PASS_1:
   /* is there a reference to <label> in any list? */
   node = find_label (&iter, FIND_FIRST, label, 0);

   if (node != 0) {
     /* explicit reference to <label> found
        process references to <label> in each <list> */
     while (node != 0) {
       switch ((enum e_label_flag) node->flag) {
       case UNRESOLVED:
         write_line (outfile, inname,
                     node->list, label, item, longform, &line[ptr],
                     node->list_mode, node->pageref_mode, node->page);
         node->flag = RESOLVED;
         break;

       case RESOLVED:
         printlog (PROGRESS, STDOUT, "i");
         printlog (INFORMATION, LOGFILE,
                   "\n%s:%u %s already resolved",
                   inname, lineno, label);
         count_gdf_defined++;
         break;
       }
       node = find_label (&iter, FIND_NEXT, label, 0);
     }
   }
   break;
 case PASS_2:
   /* now look for wildcard in any list */
   node = find_label (&iter, FIND_FIRST, "*", 0);

   if (node == 0) {
     printlog (PROGRESS, STDOUT, ".");
     printlog (DEBUG, LOGFILE, "\n%s:%u %s@%s(%s) not needed",
               inname, lineno, label, item, longform);
   } else {
     /* check for wildcard in every list */
     while (node != 0) {
       /* wurde <label> in <node->list> schon explicit angefordert? */
       if ((node2 =
            find_label (&iter2, FIND_FIRST, label, node->list)) == 0) {
         write_line (outfile, inname,
                     node->list, label, item, longform, &line[ptr],
                     node->list_mode, node->pageref_mode, node->page);
         node->flag = RESOLVED;
       }
       node = find_label (&iter, FIND_NEXT, "*", 0);
     }
   }
   break;
 }
}

static void
write_line (FILE * outfile,
           char* inname,
           char *list, char *label, char *item, char *longform,
           char *line, char *list_mode, char *pageref_mode, char *page)
{
 fprintf (outfile,
          "\\GlossTeXEntry{%s%s@{%s}{%s}{%s}{%s}{%s}{%s}"
          "{\\GlossTeXPage{%s}{%s}}|GlossTeXNull}{0}\n",
          list, label, label, item, longform, line,
          list, list_mode, pageref_mode, page);
 printlog (PROGRESS, STDOUT, "o");
 printlog (VERBOSE, LOGFILE, "\n%s:%u %s@%s(%s) used *",
           inname, lineno, label, item, longform);
 count_gdf_success++;
}

/* #module    GloScan    "2-001"
  ***********************************************************************
  *                                                                     *
  * The software was developed at the Monsanto Company and is provided  *
  * "as-is".  Monsanto Company and the auther disclaim all warranties   *
  * on the software, including without limitation, all implied warran-  *
  * ties of merchantabilitiy and fitness.                               *
  *                                                                     *
  * This software does not contain any technical data or information    *
  * that is proprietary in nature.  It may be copied, modified, and     *
  * distributed on a non-profit basis and with the inclusion of this    *
  * notice.                                                             *
  *                                                                     *
  ***********************************************************************
*/

/* this has been slightly modified by volkan yavuz 1996/12/18 */

static int
glo_parse_item (char *line, char *label, char *item, char *longform, int *ptr)
{
 int i, brace;
 char x;

 /* Copy the label to the output string. */
 i = 0;
 brace = 1;
 *ptr = 7;
 while (1) {
   x = line[(*ptr)++];
   if (x == '\0')
     return 1;
   if (x == '{')
     if (line[*ptr - 2] != '\\')
       brace++;
   if (x == '}') {
     if (line[*ptr - 2] != '\\')
       brace--;
     if (brace <= 0)
       break;
   }
   if (x == ',')
     break;
   label[i++] = x;
 }
 label[i] = '\0';

 /* Find the beginning of the item string. */
 while (isspace (line[*ptr]) != 0)
   (*ptr)++;

 /* Copy the item to the output string. */
 i = 0;
 while (brace > 0) {
   x = line[(*ptr)++];
   if (x == '\0')
     return 1;
   if (x == '{')
     if (line[*ptr - 2] != '\\')
       brace++;
   if (x == '}') {
     if (line[*ptr - 2] != '\\')
       brace--;
     if (brace <= 0)
       break;
   }
   if (x == ',')
     break;
   item[i++] = x;
 }
 item[i] = '\0';

 /* Check to see if the item is missing. If it is, default to the label. */
 if (i == 0)
   (void) strcpy (item, label);        /* FIXME: lint code error */

 while (isspace (line[*ptr]) != 0)
   (*ptr)++;

 /* Copy the long-form to the output string. */
 i = 0;
 while (brace > 0) {
   x = line[(*ptr)++];
   if (x == '\0')
     return 1;
   if (x == '{')
     if (line[*ptr - 2] != '\\')
       brace++;
   if (x == '}') {
     if (line[*ptr - 2] != '\\')
       brace--;
     if (brace <= 0)
       break;
   }
   longform[i++] = x;
 }
 longform[i] = '\0';

 return 0;                     /* it's all ok */
}