/* $Id: word2x.cc,v 1.12 1997/04/13 05:53:11 dps Exp $ */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#ifdef __GNUC__
#define alloca __builtin_alloca
#else
#if HAVE_ALLOCA_H
#include <alloca.h>
#else /* Do not have alloca.h */
#ifdef _AIX
#pragma alloca
#else /* not _AIX */
extern "C" char *alloca(int);
#endif /* _AIX */
#endif /* HAVE_ALLOCA_H */
#endif /* __GNUC__ */

#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_STRING_H
#include <string.h>
#endif /* HAVE_STRING_H */
#ifdef HAVE_STRINGS_H
#include <strings.h>
#endif /* HAVE_STRINGS_H */
#ifdef HAVE_TIME_H
#include <time.h>
#endif /* HAVE_TIME_H */
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */
#ifdef HAVE_CTYPE_H
#include <ctype.h>
#endif /* HAVE_CTYPE_H */
#include "getopt.h"
#include "interface.h"
#include "lib.h"
#include "strip.h"

#ifndef N
#define N(x) (sizeof(x)/sizeof(x[0]))
#endif

extern docfmt txtfmt, latexfmt, htmlfmt;

/* postfix test */
static int postfix(const char *s, const char *t)
{
   unsigned int n;

   n=strlen(t);
   if (strlen(s)<=n)
       return 0;
   return (strcasecmp(s+strlen(s)-n, t)==0) ? 1 : 0;
}

/*
* open file with .doc or .DOC tacked on the end if the filename alone
* does not exist
*/
static FILE *open_file(const char *f)
{
   char *s;
   FILE *r;

   if ((r=fopen(f, "r"))!=NULL)
       return r;

   if ((s=(char *) malloc(strlen(f)+4))==NULL)
   {
       fprintf(stderr,"word2x: skipping %s due to malloc failure\n", f);
       return NULL;
   }
   strcpy(s, f);
   strcat(s, ".doc");

   if ((r=fopen(s, "r"))==NULL)
   {
       free(s);
       return r;
   }

   strcpy(s+strlen(f), ".DOC");
   r=fopen(s, "r");
   free(s);

   return r;
}


/* Just read the files and pass the results on... */
static void convert(istream *f, FILE *out, const struct docfmt *fmt)
{
   const tok_seq::tok *d;
   int i;
   void *dptr;
   tok_seq rd(f);

   dptr=fmt->new_state();
   while ((d=rd.read_token())!=NULL)
   {
       i=d->tokval;

#ifndef C_ALLOCA
       alloca(0);
#endif

       if (i<NFUNCS && d->end==tok_seq::tok::TOK_START)
       {
           (fmt->f[i]).start(d, fmt, out, dptr);
       }
       else
       {
           (fmt->f[i]).end(d, fmt, out, dptr);
       }
   }
   fmt->free_state(dptr);
}

static const char *outname(const char *in, const char *ext)
{
   char *r, *s;
   int adj;

   if (postfix(in, ".doc"))
       adj=4;
   else
       adj=0;

   if ((r=(char *) malloc(strlen(in)+strlen(ext)-adj+1))==NULL)
       return NULL;
   strcpy(r, in);
   s=r+strlen(r)-adj;
   strcpy(s, ext);

   return r;
}

int main(int argc, const char **argv)
{
   static const struct
   {
       const char *name;
       char *(*fmt)(time_t);
   } dates[]=
   {
       { "uk", uk_date },
       { "british", uk_date },
       { "us", us_date },
       { "de", de_date },
       { "deHTML", deHTML_date },
       { "deL1", deL1_date },
   };
   static const struct
   {
       const char *name;
       const char *ext;
       const docfmt *fmt;
   } formats[]=
   {
       { "text", ".txt", &txtfmt },
       { "latex", ".tex", &latexfmt },
       { "html", ".html", &htmlfmt },
   };
#if !defined(NO_LONG_OPTS)
   static const struct option lopts[]=
   {
       { "version", 0, NULL, 'V' },
       { "help", 0, NULL, 'h' },
       { "format", 1, NULL, 'f' },
       { "width", 0, NULL,  'w' },
       { "dates", 0, NULL, 'd' },
       { "verbose", 0, NULL, 'v' },
       { "quiet", 0, NULL, 'q' },
       { "pagebreak", 0, NULL, 'p' },
   };
#endif

   char *(*dfmt)(time_t);
   FILE *in, *out;
   docfmt fmt;
   int c, i, opt_index, n, wd;
   const char *s,*t, *ext;
   int verbose=0, name_alloced, res;

   struct
   {
       unsigned new_pages:1;
   } flags={ 0 };
   res=0;                      // Return code 0
   fmt=*(formats[0].fmt);      // Set default format
   dfmt=dates[0].fmt;          // Set default date format
   wd=fmt.maxline;             // Set default width
   ext=formats[0].ext;         // Set default extension

   while ((c=getopt_long(argc, (char *const *) argv, "Vhpvqf:w:d:",
                         lopts, &opt_index))!=-1)
   {
       switch(c)
       {
       case 'V':                       // Print version and exit
           fputs("word2x 0.005\n", stderr);
           return 0;

       case 'p':
           flags.new_pages=1;
           break;

       case 'h':                       // Help
           fputs("Usage: word2x [-f <output format>] [--dates <date format>]"
                 " [-w <line length>]\n"
                 "              <infile> [<outfile>]\n"
                 "Supported date formats: ", stderr);
           for (i=0; i<(int) N(dates); i++)
           {
               if (i!=0)
                   fputs(", ", stderr);
               fputs(dates[i].name, stderr);
           }
           fputs("\nSupported output formats: ", stderr);
           for (i=0; i<(int) N(formats); i++)
           {
               if (i!=0)
                   fputs(", ", stderr);
               fputs(formats[i].name, stderr);
           }
           fputc('\n', stderr);
           return 0;

       case 'v':                       // Verbose mode
           verbose=1;
           break;

       case 'q':                       // Queit mode
           verbose=0;
           break;

       case 'w':                       // Width
           n=0;
           if (*optarg=='\0')
           {
               fputs("-w requires a number\n", stderr);
               break;
           }
           for (s=optarg; *s; s++)
           {
               if (!isdigit(*s))
               {
                   res=1;
                   fputs("-w requires a number\n", stderr);
                   break;
               }
               n=n*10+(*s-'0');
           }
           if (*s=='\0')
               wd=n;
           break;

       case 'd':                       // Date format
           for (n=-1, i=0; i<(int) N(dates); i++)
           {
               if (strcasecmp(dates[i].name, optarg)==0)
               {
                   n=i;
                   break;
               }
           }
           if (n==-1)
           {
               res=1;
               fprintf(stderr, "%s is not a known date format\n", optarg);
           }
           else
               dfmt=dates[i].fmt;
           break;

       case 'f':                       // Output format
           for (n=-1, i=0; i<(int) N(formats); i++)
           {
               if (strcasecmp(formats[i].name, optarg)==0)
               {
                   n=i;
                   break;
               }
           }
           if (n==-1)
           {
               res=1;
               fprintf(stderr, "%s is not a known output format\n", optarg);
           }
           else
           {
               fmt=*(formats[i].fmt);
               ext=formats[i].ext;
           }
           break;

       case '?':
           break;

       default:
           abort();
       }
   }

   /* Make sure we have a filename */
   if (optind==argc)
   {
       fputs("word2x: filename required (can not handle stdin)\n", stderr);
       return 1;
   }

   /* Stop if invalid switches */
   if (res!=0)
       return res;

   /* Set line width and date format */
   fmt.date=dfmt;
   fmt.maxline=wd;
   fmt.flags.new_pages=flags.new_pages;

   /* Loop through files */
   for (i=optind; i<argc; i++)
   {
       s=argv[i];
       if (i==argc-1 || postfix(argv[i+1],".doc"))
       {
           /* Generate output file name */
           if ((t=outname(argv[i], ext))==NULL)
           {
               fprintf(stderr,
                       "word2x: skipping %s due to allocation failure\n",
                       s);
               continue;
           }
           name_alloced=1;
       }
       else
       {
           t=argv[++i];
           name_alloced=0;
       }

       /* Open input file */
       if ((in=open_file(s))==NULL)
       {
           if (name_alloced)
               free((void *) t);
           fprintf(stderr, "Could not open %s\n", s);
           res=1;
           continue;
       }

       /* Open output file */
       if (strcmp(t,"-")==0)
       {
           out=stdout;
           t="standard output";
       }
       else
       {
           if ((out=fopen(t, "w"))==NULL)
           {
               fprintf(stderr, "Could not open %s\n", t);
               if (name_alloced)
                   free((void *) t);
               fclose(in);
               res=1;
               continue;
           }
       }

       fclose(in);
       word_junk_filter filt(s);
       istream in(&filt);

       /* Finally do something */
       if (verbose)
           fprintf(stderr, "Converting %s to %s\n", s, t);
       convert(&in, out, &fmt);

       /* Junk filter and istream go out of scope here */
   }

   return res;
}