/*
* pstex:
*
*      This is a BiBTeX-like program which
*      performs figure inclusions in conjunction
*      with a suitable set of macros.
*
* Neil Hunt (Neil%[email protected]).
*
* Copyright (c) 1989 Teleos Research, Inc 1989.
*
* Anyone can use this software in any manner they choose,
* including modification and redistribution, provided they make
* no charge for it, and these conditions remain unchanged.
*
* This program is distributed as is, with all faults (if any), and
* without any wrranty.  No author or distributor accepts responsibility
* to anyone for the consequences of using it, or for whether it serves any
* particular purpose at all, or any other reason.
*
* $Log:        pstex.c,v $
* Revision 1.8  89/07/28  10:25:53  neil
* Added copyright and conditions notice.
*
* Revision 1.7  89/07/28  09:51:03  neil
* Cleaned up, and removed some references to spar in pathnames.
*
* Revision 1.6  89/02/10  18:40:48  neil
* Removed dependencies on newlib.
* Now uses ./std.h for standard definitions.
*
* Revision 1.5  89/01/09  13:05:05  hunt
* Updated to use a single .psz file per job rather than a .tps file
* for each included figure.
*
* Revision 1.4  88/12/20  11:52:58  hunt
* General cleanup.
*
* Revision 1.3  88/09/27  07:04:33  hunt
* Added a `%' comment char to end of postscriptbox output lines, to avoid
* additional space which is otherwise left within the psbox area.
*
* Revision 1.2  88/09/20  13:11:26  hunt
* Writes `file' instead of `file.ps' into .psz file.
*
* Revision 1.1  88/08/04  12:34:17  hunt
* Initial revision
*/



#include <stdio.h>
#include <string.h>
#include <math.h>
#include <varargs.h>
#include "std.h"
#include "args.h"

extern FILE *           fopenp();
extern double           atof();
#ifdef MSDOS
#include <process.h>
#include <stdlib.h>
#else
extern char *           getenv();
#endif

#define STRSIZE 256
#ifdef MSDOS
#define DEFPSPATH       ".;a:\\tex\\pslib"
#define DEFTEXINPUTS    ".;a:\\tex\\inputs"
#else
#define DEFPSPATH       ".:/usr/local/lib/tex/font/ps"
#define DEFTEXINPUTS    ".:/usr/local/lib/tex/inputs"
#endif
char                    line[1000];
char *                  ps_path;
char *                  texinputs;
bool                    framemode = FALSE;
bool                    verbose = FALSE;
FILE *                  pszfile;
FILE *                  logfile;

forward bool            parse();
forward bool            process();
forward char *          getarg();
forward bool            makepszentry();
forward bool            readpsfile();
forward void            printerr();
forward void            printlog();

int
main(argc, argv)
int argc;
char *argv[];
{
       char opt;
       int f;
       static char auxname[STRSIZE];
       static char pszname[STRSIZE];
       static char logname[STRSIZE];

       /*
        * Read paths from environment.
        */
       if((ps_path = getenv("DVIPSPATH")) == NULL)
               ps_path = DEFPSPATH;
       if((texinputs = getenv("TEXINPUTS")) == NULL)
               texinputs = DEFTEXINPUTS;
       if(getenv("FMHOME"))
               framemode = TRUE;

       /*
        * Parse args.
        */
       for(f = 0; (opt = a_next(argc, argv)) != A_END; )
       {
               switch(opt)
               {
               default:
                       fprintf(stderr, "%s: unrecognised option -%c\n",
                         a_prog_name, opt);
                       /* FALLTHROUGH */

               case 'H':
usage:;
                       fprintf(stderr, "usage: %s [-z DVIPSPATH] texfile\n",
                         a_prog_name);
                       exit(1);

               case 'z':
                       ps_path = a_arg(argc, argv);
                       break;

               case 'f':
               case 'F':
                       framemode = TRUE;
                       break;

               case 'v':
                       verbose = TRUE;
                       break;

               case A_ARG:
                       switch(f++)
                       {
                       case 0:
                               strcpy(auxname, a_arg(argc, argv));
                               strcpy(pszname, auxname);
                               strcpy(logname, auxname);
                               strcat(auxname, ".aux");
                               strcat(pszname, ".psz");
                               strcat(logname, ".plg");
                               break;

                       default:
                               fprintf(stderr, "%s: too many filenames: %s\n",
                                 a_prog_name, a_arg(argc, argv));
                               goto usage;
                       }
                       break;
               }
       }
       if(f != 1)
               goto usage;

       if((logfile = fopen(logname, "w")) == NULL)
       {
               fprintf(stderr, "Cant open log file %s\n", logname);
               exit(-1);
       }

       if((pszfile = fopen(pszname, "w")) == NULL)
       {
               fprintf(stderr, "Cant open psz file %s\n", pszname);
               exit(-1);
       }

       if(! parse(auxname))
               exit(1);

       fclose(logfile);

       exit(0);
}

/*
* Recurses over \jobname.aux files.
*/

bool
parse(filename)
char *filename;
{
       static int level = 0;
       FILE *fp;
       static char fullname[STRSIZE];
       static char args[STRSIZE];
       bool status = TRUE;

       if((fp = fopenp(texinputs, filename, fullname, "rt")) == NULL)
       {
               printerr("WARNING: Cant open aux file %s\n", filename);
               return FALSE;
       }
       else
               printlog("Level %d aux file: %s\n", level++, filename);

       while(fgets(line, 1000, fp))
       {
               if(sscanf(line, " \\@input { %[^{}] } ", args) == 1)
               {
                       if(! parse(args))
                               status = FALSE;
                       continue;
               }
               if(sscanf(line, " \\psboxaux %[^\n] ", args) == 1)
               {
                       if(! process(args))
                               status = FALSE;
                       continue;
               }
       }

       fclose(fp);

       --level;

       return status;
}


/*
* Processes lines in the .aux files of the form:
*      \psboxaux{option-string}{filanem}
* args is the rest of the line when psboxaux is recognised.
*/

bool
process(args)
char *args;
{
       char *p, *a;
       static char width[STRSIZE];
       static char height[STRSIZE];
       static char aspect[STRSIZE];
       static char scale[STRSIZE];
       static char name[STRSIZE];

       width[0] = '\0';
       height[0] = '\0';
       aspect[0] = '\0';
       scale[0] = '\0';
       name[0] = '\0';

       a = args;

       if(*a++ != '{')
               goto abort;
       while(*a == ' ' || *a == '\t')
               a++;
       while(*a)
       {
               switch(*a)
               {
               case 'w':
                       a = getarg(a, width);
                       break;
               case 'h':
                       a = getarg(a, height);
                       break;
               case 'a':
                       a = getarg(a, aspect);
                       break;
               case 's':
                       a = getarg(a, scale);
                       break;
               case '}':
                       break;
               default:
                       goto abort;
               }
               if(a == NULL)
                       goto abort;
               if(*a++ == '}')
                       break;
       }
       while(*a == ' ' || *a == '\t')
               a++;
       if(*a++ != '{')
               goto abort;
       while(*a == ' ' || *a == '\t')
               a++;
       p = name;
       while(*a && *a != '}')
               *p++ = *a++;
       *p = '\0';

       return makepszentry(name, width, height, aspect, scale);

abort:
       printerr("WARNING: Bad arguments: \\psboxaux%s\n", args);
       return FALSE;
}

/*
* Returns pointer to char after end of arg.
* Arg ends with ',' ';' unmatched '}' or end of string.
* Arg is terminated with '\0'.
*/

char *
getarg(p, arg)
char *p;
char *arg;
{
       int nesting;

       /*
        * Skip rest of `xxx = '
        */
       while((*p >= 'a' && *p <= 'z') ||
         (*p >= 'A' && *p <= 'Z'))
               p++;
       while(*p == ' ' || *p == '\t')
               p++;
       if(*p++ != '=')
       {
               printerr("WARNING: Bad psbox format syntax (no '=' found).\n");
               return NULL;
       }
       while(*p == ' ' || *p == '\t')
               p++;

       /*
        * Copy arg.
        */
       for(nesting = 0;
         *p && (nesting > 0 || (*p != ',' && *p != ';' &&*p != '}')); p++)
       {
               if(*p == '{')
                       nesting++;
               else if(*p == '}')
                       --nesting;
               *arg++ = *p;
       }
       *arg = '\0';

       /*
        * Check.
        */
       if(nesting != 0)
       {
               printerr("WARNING: Unmatched braces in \\psboxaux command\n");
               return NULL;
       }

       return p;
}

/*
* Creates a psz file entry for a ps figure.
* The option string has been parsed into width, height, aspect, and scale.
*/

bool
makepszentry(name, width, height, aspect, scale)
char name[];
char width[];
char height[];
char aspect[];
char scale[];
{
       FILE *psfp;
       static char psname[STRSIZE];
       static char fullname[STRSIZE];
       static char wu[STRSIZE], hu[STRSIZE];
       double w, h, a, s;
       double bbw, bbh;

       strcpy(psname, name);
       strcat(psname, ".ps");
       if((psfp = fopenp(ps_path, psname, fullname, "rt")) == NULL)
       {
               printerr("WARNING: No file %s\n", psname);
               return FALSE;
       }

       if(*width)
       {
               if(sscanf(width, "%lg%s", &w, wu) != 2)
               {
                       w = 1.0;
                       strcpy(wu, width);
               }
       }
       if(*height)
       {
               if(sscanf(height, "%lg%s", &h, hu) != 2)
               {
                       h = 1.0;
                       strcpy(hu, height);
               }
       }

       if(*width && *height)
       {
               if(*scale)
                       s = atof(scale);
               else
                       s = 1.0;
               fprintf(pszfile, "\\pssize{%g%s}{%g%s}{%s}%%\n",
                 s*w, wu, s*h, hu, name);
               printlog("  \\pssize{%.4g%s}{%.4g%s}{%s}\n",
                 s*w, wu, s*h, hu, name);
               if(*aspect)
                       printlog("  (aspect ignored)\n");
       }
       else
       {
               if(! readpsfile(psfp, &bbw, &bbh))
               {
                       printerr(
 "WARNING: Postscript file %s contains no %%%%BoundingBox\n",
                         psname);
                       goto abort;
               }

               if(*width)
               {
                       if(*aspect)
                               a = atof(aspect);
                       else
                               a = bbw / bbh;
                       if(*scale)
                               s = atof(scale);
                       else
                               s = 1.0;
                       fprintf(pszfile, "\\pssize{%g%s}{%g%s}{%s}%%\n",
                         s*w, wu, s*w/a, wu, name);
                       printlog("  \\pssize{%.4g%s}{%.4g%s}{%s}\n",
                         s*w, wu, s*w/a, wu, name);
               }
               else if(*height)
               {
                       if(*aspect)
                               a = atof(aspect);
                       else
                               a = bbw / bbh;
                       if(*scale)
                               s = atof(scale);
                       else
                               s = 1.0;
                       fprintf(pszfile, "\\pssize{%g%s}{%g%s}{%s}%%\n",
                         s*h*a, hu, s*h, hu, name);
                       printlog("  \\pssize{%.4g%s}{%.4g%s}{%s}\n",
                         s*h*a, hu, s*h, hu, name);
               }
               else
               {
                       if(*scale)
                               s = atof(scale);
                       else
                               s = 1.0;
                       fprintf(pszfile, "\\pssize{%gin}{%gin}{%s}%%\n",
                          s * bbw/72.0, s * bbh/72.0, name);
                       printlog("  \\pssize{%.4gin}{%.4gin}{%s}\n",
                         s*bbw/72.0, s*bbh/72.0, name);
                       if(*aspect)
                               printlog("  (aspect ignored)\n");
               }
       }

       fclose(psfp);

       return TRUE;

abort:
       fclose(psfp);

       return FALSE;
}

/*
* Reads the .ps file
* to obtain the bounding box data.
*/

bool
readpsfile(psfp, wp, hp)
FILE *psfp;
double *wp;
double *hp;
{
       char line[1000];
       double x1, y1, x2, y2;
       char dummy[100];

       while(fgets(line, 1000, psfp))
       {
               if(sscanf(line, " %%%% BoundingBox : %lf %lf %lf %lf",
                 &x1, &y1, &x2, &y2) == 4)
               {
                       *wp = Abs(x2 - x1);
                       *hp = Abs(y2 - y1);
                       return TRUE;
               }
               if(framemode && sscanf(line, " %lf %lf %lf %lf %[C] ",
                 &x1, &y1, &x2, &y2, &dummy[0]) == 5)
               {
                       *wp = Abs(x2 - x1);
                       *hp = Abs(y2 - y1);
                       return TRUE;
               }
       }

       return FALSE;
}

void
printerr(fmt, va_alist)
char *fmt;
va_dcl
{
       va_list ap;

       va_start(ap);
       {
               vfprintf(logfile, fmt, ap);
               vfprintf(stderr, fmt, ap);
       }
       va_end(ap);
}

void
printlog(fmt, va_alist)
char *fmt;
va_dcl
{
       va_list ap;

       va_start(ap);
       {
               vfprintf(logfile, fmt, ap);
               if(verbose)
                       vfprintf(stdout, fmt, ap);
       }
       va_end(ap);
}