/*
 *      cawf - a C version of Henry Spencer's awf(1), the Amazingly
 *             Workable (text) Formatter
 *
 *      V. Abell, Purdue University Computing Center
 *      Version 1.0, February, 1991
 */

/*
 *      Copyright (c) 1991 Purdue University Research Foundation,
 *      West Lafayette, Indiana 47907.  All rights reserved.
 *
 *      Written by Victor A. Abell <abe@mace.cc.purdue.edu>,  Purdue
 *      University Computing Center.  Not derived from licensed software;
 *      derived from awf(1) by Henry Spencer of the University of Toronto.
 *
 *      Permission is granted to anyone to use this software for any
 *      purpose on any computer system, and to alter it and redistribute
 *      it freely, subject to the following restrictions:
 *
 *      1. The author is not responsible for any consequences of use of
 *         this software, even if they arise from flaws in it.
 *
 *      2. The origin of this software must not be misrepresented, either
 *         by explicit claim or by omission.  Credits must appear in the
 *         documentation.
 *
 *      3. Altered versions must be plainly marked as such, and must not
 *         be misrepresented as being the original software.  Credits must
 *         appear in the documentation.
 *
 *      4. This notice may not be removed or altered.
 */

#include "cawf.h"

#ifdef  UNIX
#ifdef  USG
#include <string.h>
#else
#include <strings.h>
#endif
#include <sys/stat.h>
#else
#include <io.h>
#include <process.h>
#include <string.h>
#include <sys\types.h>
#include <sys\stat.h>
#endif


main(argc, argv)
        int     argc;
        char    *argv[];
{
        char buf[MAXLINE];              /* character buffer */
        char *ep;                       /* environment pointer */
        int f = 0;                      /* -f option count */
        char *files[MAXFILES];          /* file names */
        int i;                          /* temporary index */
        size_t l;                       /* length */
        char *lib = CAWFLIB;            /* library path */
        int libl;                       /* library path length */
        int mac = 0;                    /* macro specification status */
        int nf = 0;                     /* number of files */
        char *np;                       /* name pointer */
        int pc;                         /* prolog count */
        struct stat sbuf;               /* stat buffer */
/*
 * Save program name.
 */
        if ((Pname = strrchr(argv[0], '\\')) != NULL)
                Pname++;
        else if ((Pname = strrchr(argv[0], '/')) != NULL)
                Pname++;
        else
                Pname = argv[0];
/*
 * Set error file stream pointer.
 */
        Efs = stderr;
/*
 * Get library name.
 */
        if ((np = getenv("CAWFLIB")) != NULL)
                lib = np;
        libl = strlen(lib);
/*
 * Get device file name.
 */
        if ((ep = getenv("TERM")) == NULL || *ep == '\0')
                ep = "dumb";
        l = libl + 1 + strlen(ep) + strlen(".dev") + 1;
        if ((np = malloc(l)) == NULL)
                Error(FATAL, NOLINE, " no string space for device file name",
                        NULL);
        (void) sprintf(np, "%s/%s.dev", lib, ep);
        files[nf++] = np;
/*
 * Get common text file name.
 */
        l = libl + 1 + strlen("common") + 1;
        if ((np = malloc(l)) == NULL)
                Error(FATAL, NOLINE, " no string space for common file name",
                        NULL);
        (void) sprintf(np, "%s/common", lib);
        files[nf++] = np;
/*
 * Process options.
 */
        while ((i = getopt(argc, argv, "f:m:")) != EOF) {
                switch (i) {
        /*
         * -f<font_switch>: n = none
         *                  b = use backspaces and underlines (default)
         *                  e = use ESC<font_code>
         */
                case 'f':
                        switch(*optarg) {
                        case 'n':
                        case 'b':
                        case 'e':
                                Fontout = *optarg;
                                f++;
                                break;
                        default:
                                Error(WARN, NOLINE, "illegal -f option: ",
                                        optarg);
                        }
                        break;
        /*
         * -m<macro_file_name>
         */
                case 'm':
                        if (mac) {
                                Error(WARN, NOLINE,
                                        "multiple macro file declaration",
                                        NULL);
                                break;
                        }
                        l = libl + 2 + strlen(optarg) + strlen(".mac") + 1;
                        if ((np = malloc(l)) == NULL)
                                Error(FATAL, NOLINE, " no string space for ",
                                        argv[1]);
                        (void) sprintf(np, "%s/m%s.mac", lib, optarg);
                        files[nf++] = np;
                        if (strcmp(optarg, "an") == 0)
                                Marg = MANMACROS;
                        else if (strcmp(optarg, "s") == 0)
                                Marg = MSMACROS;
                        mac++;
                        break;
                case '?':
                        Err = 1;
                }
        }
        if (f > 1)
                Error(WARN, NOLINE, " conflicting font (-f) specifications");
        if (Err) {
                (void) fprintf(stderr,
                        "%s usage: -f[n|b|e] -m[macro_file] file . . .\n",
                        Pname);
                exit(1);
        }
        if (mac == 0) {

            /*
             * No macroes - enable Bold, Italic and Roman fonts.
             */
                for (i = 0; Fcode[i].nm; i++) {
                        switch (Fcode[i].nm) {
                        case 'B':
                        case 'I':
                        case 'R':
                                Fcode[i].status = '1';
                        }
                }
        }
/*
 * Add user-supplied file names.
 */
        pc = nf;
        if (optind >= argc) {
                files[nf++] = NULL;       /* STDIN */
        } else {
                while (optind < argc) {
                        if (nf >= MAXFILES)
                                Error(WARN, NOLINE, " too many files at ",
                                        argv[optind]);
                        files[nf++] = argv[optind++];
                }
        }
/*
 * Make sure all input files are accessible.
 */
        for (i = 0; i < nf; i++) {
                if (files[i] != NULL) {
                        if (stat(files[i], &sbuf) != 0)
                                Error(WARN, NOLINE, " can't find ", files[i]);
                }
        }
        if (Err)
                exit(1);
/*
 * Miscellaneous initialization.
 */

        for (i = 0; ; i++) {
                if (Pat[i].re == NULL)
                        break;
                if ((Pat[i].pat = regcomp(Pat[i].re, 0)) == NULL)
                        Error(WARN, NOLINE, Pat[i].re, " regcomp failure");
        }
        if ((i = Findscale('n', 0.0, 0)) < 0)
                Error(WARN, NOLINE, " can't find Scale['n']", NULL);
        Scalen = Scale[i].val;
        if ((i = Findscale('u', 0.0, 0)) < 0)
                Error(WARN, NOLINE, " can't find Scale['u']", NULL);
        Scaleu = Scale[i].val;
        if ((i = Findscale('v', 0.0, 0)) < 0)
                Error(WARN, NOLINE, " can't find Scale['v']", NULL);
        Scalev = Scale[i].val;
        (void) Findstr("CH", "= % -", 1);
        Cont = Newstr(" ");
        Contlen = 1;
        if ((Trtbl = (char *)malloc(256)) == NULL)
                Error(WARN, NOLINE, " can't allocate translate table space",
                        NULL);
        else {
                *Trtbl = ' ';
                for (i = 1; i < 256; i++)
                        Trtbl[i] = (char) i;
        }
        if (Err)
                exit(1);
/*
 * Here begins pass1 of awf - reading input lines and expanding macros.
 */

/*
 * Output prolog.
 */
        Macro(".^x");
        Macro(".^b");
        Macro(".^# 1 <prolog>");
/*
 * Read input files.
 */
        for (i = 0; i < nf; i++) {
                if (files[i] == NULL) {
                        np = "stdin";
                        Ifs = stdin;
                } else {
#ifdef  UNIX
                        if ((Ifs = fopen(files[i], "r")) == NULL)
#else
                        if ((Ifs = fopen(files[i], "rt")) == NULL)
#endif
                                Error(FATAL, NOLINE, " can't open ", files[i]);
                        np = files[i];
                }
                if (i >= pc) {
                        (void) sprintf(Line, ".^# 1 %s", np);
                        Macro(Line);
                        NR = 0;
                }
                Fsp = 0;
                do {
                        while (fgets(Line, MAXLINE, Ifs) != NULL) {
                                NR++;
                                if ((np = strrchr(Line, '\n')) != NULL)
                                        *np = '\0';
                                else
                                        Line[MAXLINE-1] = '\0';
                                Macro(Line);
                        }
                        if (i >= pc)
                                Macro(".^e");
                        if (Ifs != stdin)
                                (void) fclose(Ifs);
                        if (Fsp > 0) {
                                Free(&Inname);
                                Inname = Inn_stk[Fsp-1];
                                NR = NR_stk[Fsp-1];
                                Ifs = Ifs_stk[Fsp-1];
                        }
                } while (Fsp-- > 0);
        }
        Macro(NULL);
        exit(Err);
}


/*
 * Macro(inp) - process a possible macro statement
 *              pass non-macros and macros alike to pass 2
 */

void
Macro(inp)
        char *inp;               /* possible macro statement pointer */
{
        char c[2];                      /* characters */
        FILE *fs;                       /* temporary file stream */
        int i, j, k;                    /* temporary indexes */
        int mx;                         /* Macrotab[] index */
        char *s1, *s2;                  /* temporary string pointers */

        if (inp == NULL) {
                Pass2(NULL);
                return;
        }
/*
 * Check for file name designator.
 */
        if (strncmp(inp, ".^#", 3) == 0) {
                Free(&Inname);
                Inname = Field(3, inp, 1);
                F = NULL;
                Pass2(inp);
                return;
        }
/*
 * Check for source command - "^\.so".
 */
        if (strncmp(inp, ".so", 3) == 0) {
                if ((s1 = Field(2, inp, 1)) == NULL) {
                        Error(WARN, LINE, " no file specified", NULL);
                        return;
                }
                if ((fs = fopen(s1, "r")) == NULL) {
                        Error(WARN, LINE, " can't open", NULL);
                        return;
                }
                if (Fsp >= MAXFSTK) {
                        (void) fclose(fs);
                        Error(WARN, LINE, " nesting too deep", NULL);
                        return;
                }
                Ifs_stk[Fsp] = Ifs;
                Ifs = fs;
                Inn_stk[Fsp] = Inname;
                Inname = F;
                F = NULL;
                NR_stk[Fsp++] = NR;
                NR = 0;
                return;
        }
 /*
  * Check for start of macro definition.
  */
        if (strncmp(inp, ".de", 3) == 0) {
                if (inp[3] != ' ' || inp[4] == '\0') {
                        Error(WARN, LINE, " illegal macro definition", NULL);
                        return;
                }
                c[0] = inp[4];
                c[1] = inp[5];
                Curmx = Findmacro(c, 1);
                return;
        }
/*
 * Check for macro text.  Remove double backslashes.
 */
        if (Curmx >= 0 && strcmp(inp, "..") != 0) {
                if (Mtx >= MAXMTXT)
                        Error(FATAL, LINE, " out of macro text space", NULL);
                if ((s1 = strchr(inp, '\\')) == NULL)
                        Macrotxt[Mtx] = Newstr(inp);
                else {
                        for (s1 = Pass1ln, s2 = inp;; s1++) {
                                if ((*s1 = *s2++) == '\0')
                                        break;
                                if (*s1 == '\\' && *s2 == '\\')
                                        s2++;
                        }
                        Macrotxt[Mtx] = Newstr(Pass1ln);
                }
                if (Macrotab[Curmx].bx == -1)
                        Macrotab[Curmx].bx = Mtx;
                Mtx++;
                Macrotab[Curmx].ct++;
                return;
        }
/*
 * Check for end of macro.
 */
        if (Curmx >= 0 && strcmp(inp, "..") == 0) {
                Curmx = -1;
                (void) sprintf(Pass1ln, ".^# %d %s", NR, Inname);
                Pass2(Pass1ln);
                return;
        }
 /*
  * Check for conditionals and macro expansions.
  */
        if (*inp == '.'
        &&  (((mx = Findmacro(inp+1, 0)) != -1) || regexec(Pat[0].pat, inp))) {
                Expand(inp);
                return;
        }
/*
 * None of the above: forward the line.
 */
        Pass2(inp);
}
