/* $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;
}







