#ifndef lint
static char sccsid[] = "@(#)decode.c	3.1\t11/16/89";
#endif lint

#include <stdio.h>
#include <strings.h>
#include "site.h"
#include "struct.h"
#include "defs.h"


/*********************************************************************/
/************************* Forward Definitions ***********************/
/*********************************************************************/
void  DecodeArgs();
void  Environs();
void  GetDVIFilePtr();
void  Initialize();
void  NoDVIFile();
void  OpenLogFile();
void  Usage();
void  UserSetUp();
bool  Validate();


/*********************************************************************/
/************************* External Functions ************************/
/*********************************************************************/
extern char *getenv(), *sprintf();
extern FILE *BINARYOPEN();
extern void  AbortRun(), Fatal(), Warning();
extern void  AnalyzePageList(), SetFontDirectories();
#ifdef BUILTIN
extern void ReadPSMap();
#endif BUILTIN


/*********************************************************************/
/************************* External Variables ************************/
/*********************************************************************/
extern FILE *dvifp;		/* DVI file pointer			 */
extern FILE *G_logfp;		/* log file pointer			 */
extern char  G_Logname[];	/* name of log file			 */
extern bool  C_logcreate;	/* are warning messages going to file?	 */
extern bool  C_reverse;		/* process DVI pages in reverse order?	 */
extern bool  C_preload;		/* preload the font descriptions?	 */
#ifdef BUDGET
extern int budget;		/* estimated available virtual memory	*/
extern int startbudget;
#endif BUDGET
#ifdef CREOPT
extern bool  C_create;		/* create an output file ?	 	 */
#endif CREOPT
#ifdef STATS
extern bool  C_stats;		/* are we reporting stats?		 */
#endif STATS

/*********************************************************************/
/************************** Global Variables *************************/
/*********************************************************************/
char  G_progname[STRSIZE];	/* program name				*/
char  filename[STRSIZE];	/* DVI file name			*/
char  rootname[STRSIZE];        /* DVI filename without extension	*/
char *G_headerfile;		/* name of default header file		*/
bool  C_nowarn = FALSE;		/* don't print out warnings		*/
int   C_firstpage = FIRST_DEF;	/* first page to print (based on count0)*/
int   C_lastpage = LAST_DEF;	/* last page to print			*/
char  C_pageargs[STRSIZE];	/* list of page arguments for -x option */
bool  C_header = TRUE;		/* copy header file to output?		*/
bool  C_quiet = FALSE;		/* for quiet operation			*/
int   C_usermag = 0;		/* user specified magnification		*/
int   C_copies = 1;             /* number of copies to print		*/
char *PScmd[100];               /* PostScript commands to send		*/
int   nps = 0;			/* number of PS commands to send	*/
char *Ifile[16];		/* files to include			*/
int   nif = 0;			/* number of files to include		*/
char  setupoptions[20][STRSIZE]; /* optional arguments in set up file   */
char *C_fontpaths;		/* colon-separated list of font dirs	*/
char *G_tfmpaths;		/* colon-separated list of tfm dirs	*/
char *G_homepath;		/* home directory			*/
int   C_fontdensity = DEFAULTRESOLUTION;
int   hconvResolution = DEFAULThconvRES;
int   vconvResolution = DEFAULTvconvRES;
#ifdef DEBUG
bool  C_debug = FALSE;
#endif DEBUG
#ifdef SPOOLING
bool  C_spooler = FALSE;	/* are we running as an lpd spooler ?	*/
#endif SPOOLING


/**********************************************************************/
/****************************  Initialize  ****************************/
/**********************************************************************/
void
Initialize(argc, argv)
int argc;
char *argv[];
{
    Environs(argv[0]);
    UserSetUp();
    DecodeArgs(argc, argv);
    AnalyzePageList();
    SetFontDirectories();
#ifdef BUILTIN
    ReadPSMap();
#endif BUILTIN
}


/*********************************************************************/
/***************************** DecodeArgs ****************************/
/*********************************************************************/
void
DecodeArgs(argc, argv)
int   argc;
char *argv[];
{
#ifdef USEGETOPT
    extern int getopt();

    extern char *optarg;	/* from getopt */
    extern int optind;		/* ditto */
    int   option;		/* value of getopt */

    while ((option = getopt(argc, argv, "a:b:B:cd:DEf:hi:lm:n:o:OpqrsSt:wx:"))
      != EOF)
	if (Validate(option, optarg) == FALSE) {
	    Usage();
	    Fatal("Invalid command line argument.\n");
	}
    if (optind < argc - 1) Fatal("Multiple dvi files specified.\n");
    if (optind == argc) NoDVIFile();
    else GetDVIFilePtr(argv[optind]);
#else
    char *optarg;
    int   argind;	/* argument index for flags	 */
    char *tcp;		/* temporary character pointer	 */

    argind = 1;
    while (argind < argc) {
	tcp = argv[argind];
	if (*tcp == '-') {
	    switch (*++tcp) {
	    case 'a': case 'b': case 'B': case 'd': case 'f': 
	    case 'i': case 'm': case 'n': case 'o': case 't': case 'x':
		if (++argind >= argc)
		    Fatal("No argument following -%c\n", *tcp);
		optarg = argv[argind];
		break;
	    default:
		break:
	    }
	    if(Validate(*tcp, optarg) == FALSE) {
		Usage();
		Fatal("Invalid command line argument.\n");
	    }
	} else {   /* Not a command line option, so it must be the dvi file */
	    if (dvifp != NULL) Fatal("Multiple dvi files specified.\n");
	    GetDVIFilePtr(argv[argind]);
	}
	argind++;
    }
    if (dvifp == NULL) NoDVIFile();
#endif USEGETOPT

    OpenLogFile();
    return;
}


/*********************************************************************/
/****************************** Environs *****************************/
/*********************************************************************/
void
Environs(argv0)
char *argv0;
{
    char *tcp;		/* temporary character pointer	     */

/* set program name for error messages */
    if ((tcp = rindex(argv0, '/')) == 0) {
	(void) strcpy(G_progname, argv0);
    } else {
	(void) strcpy(G_progname, tcp + 1);
    }

    if ((G_logfp = BINARYOPEN(DEFAULTLOG, "w+")) == NULL) {
	G_logfp = stderr;
	(void) fprintf(stderr, "%s: Could not open %s\n", G_progname,
	  DEFAULTLOG);
    }

    if ((C_fontpaths = getenv(ENVDVIFONTS)) == NULL)
	C_fontpaths = FONTAREA;		/* default font area */

    if ((G_tfmpaths = getenv(ENVTEXFONTS)) == NULL)
	G_tfmpaths = TFMAREA;		/* default tfm area */

    if ((G_headerfile = getenv(ENVHEADER)) == NULL)
	G_headerfile = HDRFILE;		/* default header file */

    if ((G_homepath = getenv("HOME")) == NULL)
	Warning("Cannot find home directory.\n");
}


/**********************************************************************/
/****************************  GetDVIFile  ****************************/
/**********************************************************************/
void
GetDVIFilePtr(argname)
char *argname;
{
    char  curarea[STRSIZE];	/* current file area		     */
    char  curname[STRSIZE];	/* current file name		     */
    char *tcp, *tcp1;		/* temporary character pointers	     */

    tcp = rindex(argname, '/');	/* split into dir + file name */
    if (tcp == NULL) {
	curarea[0] = '\0';	/* no directory specified */
	tcp = argname;
    }
    else {
	(void) strcpy(curarea, argname);	/* store the directory */
	curarea[tcp - argname + 1] = '\0';
	tcp++;
    }

    (void) strcpy(curname, tcp);	/* store the file name */

    tcp1 = rindex(tcp, '.');	/* split into file name + extension */
    if (tcp1 == NULL) {
	(void) strcpy(rootname, curname);
	(void) strcat(curname, ".dvi");
    }
    else {
	*tcp1 = '\0';
	(void) strcpy(rootname, tcp);
	*tcp1 = '.';
    }

/* store the full path name */
    (void) strcpy(filename, curarea);
    (void) strcat(filename, curname);

    if ((dvifp = BINARYOPEN(filename, "r")) == NULL) {
	Fatal("%s: Can't find DVI file \"%s\"\n\n", G_progname, filename);
    }
    if (C_logcreate == TRUE) {
	(void) strcpy(G_Logname, rootname);
	(void) strcat(G_Logname, ".dlog");
    }
}


/**********************************************************************/
/*****************************  NoDVIFile  ****************************/
/**********************************************************************/
void
NoDVIFile()
{
#ifdef SPOOLING
    register FILE *in = stdin;
    register FILE *out;
    register int c;

    if (!C_spooler) {
#endif SPOOLING
	Usage();
	Fatal("%s: No dvi file specified\n", G_progname);
#ifdef SPOOLING
    } /* create a temporary file and copy stdin to it */
    (void) strcpy(filename, "<stdin>");
    if ((out = tmpfile()) == NULL)
	Fatal("%s : couldn't create tmpfile to store stdin.\n", G_progname);
    while ((c = getc(in)) != EOF)
	(void) putc(c, out);
    rewind(out);

    dvifp = out;
    if (C_logcreate == TRUE)
	(void) strcpy(G_Logname, DEFAULTLOG);
#endif SPOOLING
}


/**********************************************************************/
/****************************  OpenLogFile  ***************************/
/**********************************************************************/
void
OpenLogFile()
{
    register FILE *in = G_logfp;
    register FILE *out;
    register int c;

    if (C_logcreate != TRUE) {
	if (in != stderr) {
	    (void) fclose(in);
	    (void) unlink(DEFAULTLOG);
	}
	G_logfp = NULL;
	return;
    }
    if (strcmp(G_Logname, DEFAULTLOG) == 0) return;
    if ((out = BINARYOPEN(G_Logname, "w+")) == NULL)
	Fatal("%s: Cannot open logfile %s.\n", G_progname, G_Logname);
    if (in != stderr) {
	while ((c = getc(in)) != EOF)
	    (void) putc(c, out);
	(void) fclose(in);
	(void) unlink(DEFAULTLOG);
    }
    G_logfp = out;
}


/**********************************************************************/
/*******************************  Usage  ******************************/
/**********************************************************************/
void
Usage()
{
#ifdef SPOOLING
    if (C_spooler)
	return;
#endif SPOOLING

    (void) fprintf(stderr,
      "Usage: %s [-a fontpaths] %s%s%s[-f n] [-F n] [-h] [-i file] [-l] %s[-M] [-n num] [-o option] [-p] [-q] [-r] %s%s[-t n] [-w] [-x pages] dvifile\n",
      G_progname,

#ifdef CREOPT
      "[-c] ",
#else
      "",
#endif CREOPT
#ifdef DENSITY
      "[-d n] ",
#else
      "",
#endif DENSITY
#ifdef DEBUG
      "[-D] ",
#else
      "",
#endif DEBUG
#ifdef USEGLOBALMAG
      "[-m{0|h|1|2|3|4| mag}] ",
#else
      "",
#endif USEGLOBALMAG
#ifdef SPOOLING
      "[-s] ",
#else
      "",
#endif SPOOLING
#ifdef STATS
      "[-S] ");
#else
      "");
#endif STATS
}

/**********************************************************************/
/*****************************  UserSetUp  ****************************/
/**********************************************************************/
void
UserSetUp()
{
    FILE *startupfile;
    char  fullstartupname[STRSIZE];
    char  option;
    char *optarg;
    int   numopt;
    int   line;

    if ((startupfile = fopen(STARTUPFILE, "r")) == NULL) {
	if (G_homepath == NULL) return;
	(void) strcpy(fullstartupname, G_homepath);
	(void) strcat(fullstartupname, "/");
	(void) strcat(fullstartupname, STARTUPFILE);
	if ((startupfile = fopen(fullstartupname, "r")) == NULL)
	    return;
    }
    line = 1;
    numopt = 0;
    optarg = &setupoptions[0][0];
    while (fscanf(startupfile, "%c", &option) == 1) {
	if (getc(startupfile) != '\n') {
	    ++numopt;
	    optarg = &setupoptions[numopt][0];
	    if (fscanf(startupfile, "%s\n", optarg) != 1)
		Fatal("Syntax error in %s, line %d", STARTUPFILE, line);
	} else optarg = NULL;
    if (Validate(option, optarg) == FALSE)
	Fatal("Invalid option in %s", STARTUPFILE);
    ++line;
    }
    (void) fclose(startupfile);
    return;
}


/**********************************************************************/
/******************************  Validate  ****************************/
/**********************************************************************/
bool
Validate(option, optarg)
int   option;
char *optarg;
{
	bool isvalid = TRUE;

	switch (option) {
	case 'a':	/* a selects different font pathlist */
	    if (optarg == NULL) isvalid = FALSE;
	    C_fontpaths = optarg;
	    break;
	case 'b':	/* b selects different tfm font pathlist */
	    if (optarg == NULL) isvalid = FALSE;
	    G_tfmpaths = optarg;
	    break;
#ifdef BUDGET
	case 'B':	/* B selects different value for VM routines */
	    if (sscanf(optarg, "%d", &startbudget)  != 1)
		Fatal("Argument to -B is not a valid integer\n", 0);
	    if (startbudget <= 0) {
		Warning("Negative budget %d ignored.\n", C_fontdensity);
		startbudget = BUDGET;
	    }
	    budget = startbudget;
	    break;
#endif BUDGET
#ifdef CREOPT
	case 'c':	/* create an output file */
	    C_create = TRUE;
	    break;
#endif CREOPT
#ifdef DENSITY
	case 'd':	/* d selects font pixel density */
	    if (sscanf(optarg, "%d", &C_fontdensity) != 1)
		Fatal("Argument to -d is not a valid integer\n", 0);
	    if (C_fontdensity <= 0) {
		Warning("Negative resolution %d ignored.\n", C_fontdensity);
		C_fontdensity = DEFAULTRESOLUTION;
	    }
	    hconvResolution = C_fontdensity;
	    vconvResolution = C_fontdensity;
	    C_quiet = FALSE;
	    break;
#endif DENSITY
#ifdef DEBUG
	case 'D':	/* D selects deBug output */
	    C_debug = TRUE;
	    break;
#endif DEBUG
	case 'E':	/* print even number pages */
	    (void) strcpy(C_pageargs,"FIRST_DEF.2.LAST_DEF,");
	    break;

	case 'f':	/* next arg is starting pagenumber */
	    if (sscanf(optarg, "%d", &C_firstpage) != 1)
		Fatal("Argument to -f is not a valid integer\n", 0);
	    break;

	case 'h':	/* don't copy PostScript header file to output */
	    C_header = FALSE;
	    break;

	case 'i':	/* next arg is a PostScript file to copy */
	    Ifile[nif++] = optarg;
	    break;

	case 'l':	/* disable logging errors */
	    C_logcreate = FALSE;
	    break;
#ifdef USEGLOBALMAG
	case 'm':	/* specify magnification to use */
	    if (optarg[1]) {
		if (sscanf(optarg, "%d", &C_usermag) != 1)
		    Fatal("Argument to -m is not a valid integer\n", 0);
	    }
	    else
		switch (optarg[0]) {
		case '0': C_usermag = 1000; break;
		case 'h': case 'H':
		    C_usermag = 1095; break;
		case '1': C_usermag = 1200; break;
		case '2': C_usermag = 1440; break;
		case '3': C_usermag = 1728; break;
		case '4': C_usermag = 2074; break;
		case '5': C_usermag = 2488; break;
		default:  Fatal("%c is a bad mag step\n", optarg[0]);
		}
	    break;
#endif USEGLOBALMAG
	case 'n':	/* next arg is number of copies to print */
	    if (sscanf(optarg, "%d", &C_copies) != 1)
		Fatal("Argument to -n is not a valid integer\n", 0);
	    break;

	case 'o':	/* next arg is a PostScript command to send */
	    if (optarg == NULL) isvalid = FALSE;
	    PScmd[nps++] = optarg;
	    break;

	case 'O':	/* print even number pages */
	    (void) strcpy(C_pageargs,"FIRST_ODD.2.LAST_DEF,");
	    break;

	case 'p':	/* p prohibits pre-font loading */
	    C_preload = 0;
	    C_reverse = FALSE;	/* must process in forward order */
	    break;

	case 'q':	/* quiet operation */
	    C_quiet = TRUE;
	    break;

	case 'r':	/* don't process pages in reverse order */
	    C_reverse = FALSE;
	    break;
#ifdef SPOOLING
	case 's':	/* are we running as an lpd spooler ? */
	    C_spooler = TRUE;
	    break;
#endif SPOOLING
#ifdef STATS
	case 'S':	/* print some statistics */
	    C_stats = TRUE;
	    C_quiet = FALSE;
	    break;
#endif STATS
	case 't':	/* next arg is ending pagenumber */
	    if (sscanf(optarg, "%d", &C_lastpage) != 1)
		Fatal("Argument to -t is not a valid integer\n", 0);
	    break;

	case 'w':	/* don't print out warnings */
	    C_nowarn = TRUE;
	    break;

	case 'x':	/* next argument is page(s) to print */
	    if (optarg == NULL) isvalid = FALSE;
	    (void) strcat(C_pageargs, optarg);
	    (void) strcat(C_pageargs, ",");
	    break;

	case '?':
	default :
	    isvalid = FALSE;
	    break;
	}
	return(isvalid);
}
