aboutsummaryrefslogtreecommitdiff
path: root/unix/boot/mkpkg/tok.c
diff options
context:
space:
mode:
authorJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
committerJoe Hunkeler <jhunkeler@gmail.com>2015-08-11 16:51:37 -0400
commit40e5a5811c6ffce9b0974e93cdd927cbcf60c157 (patch)
tree4464880c571602d54f6ae114729bf62a89518057 /unix/boot/mkpkg/tok.c
downloadiraf-osx-40e5a5811c6ffce9b0974e93cdd927cbcf60c157.tar.gz
Repatch (from linux) of OSX IRAF
Diffstat (limited to 'unix/boot/mkpkg/tok.c')
-rw-r--r--unix/boot/mkpkg/tok.c1457
1 files changed, 1457 insertions, 0 deletions
diff --git a/unix/boot/mkpkg/tok.c b/unix/boot/mkpkg/tok.c
new file mode 100644
index 00000000..41bdf626
--- /dev/null
+++ b/unix/boot/mkpkg/tok.c
@@ -0,0 +1,1457 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define import_spp
+#define import_error
+#include <iraf.h>
+
+#include "mkpkg.h"
+#include "extern.h"
+#include "../bootProto.h"
+
+
+
+
+/*
+ * TOK.C -- Preprocessor functions.
+ */
+
+/* GETTOK -- Get the next token from the make file currently being scanned.
+ * Conditional interpretation is provided via the $IFxxx directives.
+ */
+int
+gettok (
+ register struct context *cx, /* current context */
+ char *outstr, /* receives token */
+ int maxch
+)
+{
+ register int ch;
+ register char *op;
+ char tokbuf[SZ_COMMAND+1];
+ int token, delim;
+
+ if (debug > 1) {
+ printf ("gettok:\n");
+ fflush (stdout);
+ }
+
+again:
+ /* Skip whitespace */
+ for (ch = m_getc(cx); ch == ' '; ch = m_getc(cx))
+ ;
+ if (ch == EOF) {
+ outstr[0] = EOS;
+ return (TOK_END);
+ }
+ outstr[0] = ch;
+ outstr[1] = EOS;
+
+ /* First nonwhite character identifies each token.
+ */
+ switch (ch) {
+ case COMMENT:
+ /* Ignore a comment.
+ */
+ while ((ch = m_rawgetc(cx)) != '\n' && ch != EOF)
+ ;
+ m_ungetc ('\n', cx);
+ goto again;
+
+ case PREPROCESSOR:
+ /* Preprocessor directive.
+ */
+ for (op=tokbuf, *op++ = ch; (ch = m_getc(cx)) != EOF; )
+ if (islower (ch))
+ *op++ = ch;
+ else if (isupper (ch))
+ *op++ = tolower (ch);
+ else {
+ m_ungetc (ch, cx);
+ break;
+ }
+
+ *op = EOS;
+ if (strncmp (tokbuf, "$exit", 5) == 0)
+ return (TOK_END);
+
+ do_ppdir (cx, tokbuf);
+ goto again;
+
+ case SYSCMD:
+ /* Send a command to host system.
+ */
+ do_osescape (cx);
+ goto again;
+
+ case BEGIN_CHAR:
+ /* Start of program.
+ */
+ token = TOK_BEGIN;
+ break;
+
+ case END_CHAR:
+ /* End of program.
+ */
+ token = TOK_END;
+ break;
+
+ case '\n':
+ token = TOK_NEWLINE;
+ break;
+
+ case SYSFILE_BEGIN:
+ /* Replace '<' by system library pathname, concatentate
+ * filename and exit.
+ */
+ getstr (cx, tokbuf, SZ_COMMAND, SYSFILE_END);
+ if (m_sysfile (tokbuf, outstr, maxch) <= 0)
+ sprintf (outstr, "<%s>", tokbuf);
+
+ if (debug) {
+ /* Don't print diagnostic if the file was found to be
+ * in the usual place, i.e., the system library lib$.
+ */
+ if (strncmp (outstr, "iraf$lib/", 9) != 0) {
+ printf ("<%s> matched with `%s'\n", tokbuf, outstr);
+ fflush (stdout);
+ }
+ }
+
+ token = TOK_FNAME;
+ break;
+
+ case '\'':
+ case '"':
+ /* Quoted strings are treated as fname tokens, permitting
+ * optional quoting of filenames in module lists.
+ */
+ getstr (cx, outstr, maxch, delim = ch);
+ token = TOK_FNAME;
+ break;
+
+ case '\\':
+ if ((ch = m_getc(cx)) == '\n')
+ goto again;
+ /* fall through */
+
+ default:
+ /* Unquoted filename token.
+ */
+ m_ungetc (ch, cx);
+ getstr (cx, outstr, maxch, delim = ' ');
+ token = TOK_FNAME;
+ break;
+ }
+
+ /* Discard token? */
+ if (ifstate[iflev] == STOP)
+ goto again;
+
+ if (debug > 1) {
+ if (outstr[0] <= 040)
+ printf ("token = char 0%o\n", outstr[0]);
+ else
+ printf ("token = `%s'\n", outstr);
+ fflush (stdout);
+ }
+
+ return (token);
+}
+
+
+/* DO_OSESCAPE -- Send a command to host system. If the first char after
+ * the ! is a left paren or quote then the matching char is taken to terminate
+ * the command, otherwise an (unescaped) newline terminates the command.
+ * The parenthesized form permits additional directives on the same line.
+ */
+void
+do_osescape (register struct context *cx)
+{
+ register int ch;
+ char cmd[SZ_CMD+1];
+
+ if (debug > 1) {
+ printf ("do_osescape:\n");
+ fflush (stdout);
+ }
+
+ ch = m_getc (cx);
+ if (ch == '(' || ch == '\'' || ch == '"') {
+ getstr (cx, cmd, SZ_CMD, (ch == '(' ? ')' : ch));
+ } else if (ch == '\n') {
+ return;
+
+ } else {
+ char *op, *otop;
+
+ op = cmd;
+ *op++ = ch;
+ otop = &cmd[SZ_CMD];
+
+ while (op < otop && (ch = m_getc(cx)) != EOF)
+ if (ch == ESCAPE) {
+ ch = m_getc (cx);
+ if (ch != '\n') {
+ *op++ = ESCAPE;
+ *op++ = ch;
+ }
+ } else if (ch == '\n') {
+ break;
+ } else
+ *op++ = ch;
+
+ *op = EOS;
+ }
+
+ if (ifstate[iflev] == STOP)
+ return;
+ if (verbose) {
+ printf ("!%s\n", cmd);
+ fflush (stdout);
+ }
+
+ if (execute)
+ exit_status = os_cmd (cmd);
+ if (exit_status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", cx->library);
+}
+
+
+/* DO_PPDIR -- Execute a preprocessor directive. A hash table would be more
+ * efficient, but the complexity is not warranted since this is only called
+ * when a $ prefixed preprocessor directive has already been recognized in
+ * the input.
+ */
+void
+do_ppdir (
+ struct context *cx, /* current context */
+ char *token /* directive to be executed */
+)
+{
+ int islib;
+
+ if (debug > 1) {
+ printf ("do_ppdir: %s\n", token);
+ fflush (stdout);
+ }
+
+ if ( strncmp (token, "$if", 3) == 0)
+ do_if (cx, token);
+ else if (strncmp (token, "$else", 5) == 0)
+ do_else (cx);
+ else if (strncmp (token, "$endif", 6) == 0)
+ do_endif (cx);
+ else if (strncmp (token, "$end", 4) == 0)
+ do_end (cx);
+
+ else if (strncmp (token, "$call", 5) == 0)
+ do_call (cx, getargs(cx), islib=NO);
+ else if (strncmp (token, "$echo", 5) == 0)
+ do_echo (cx, getargs(cx));
+ else if (strncmp (token, "$goto", 5) == 0)
+ do_goto (cx, getargs(cx));
+ else if (strncmp (token, "$include", 8) == 0)
+ do_include (cx, getargs(cx));
+ else if (strncmp (token, "$set", 4) == 0)
+ do_set (cx);
+ else if (strncmp (token, "$special", 8) == 0)
+ sf_scanlist (cx);
+ else if (strncmp (token, "$update", 7) == 0)
+ do_call (cx, getargs(cx), islib=YES);
+
+ else if (strncmp (token, "$checkin", 8) == 0)
+ do_incheck (cx);
+ else if (strncmp (token, "$checkout", 9) == 0)
+ do_outcheck (cx);
+ else if (strncmp (token, "$copy", 5) == 0)
+ do_copyfile (cx);
+ else if (strncmp (token, "$delete", 7) == 0)
+ do_delete (cx);
+ else if (strncmp (token, "$generic", 8) == 0)
+ do_generic (cx);
+ else if (strncmp (token, "$link", 5) == 0)
+ do_link (cx);
+ else if (strncmp (token, "$move", 5) == 0)
+ do_movefile (cx);
+ else if (strncmp (token, "$omake", 6) == 0)
+ do_omake (cx, getargs(cx));
+ else if (strncmp (token, "$purge", 6) == 0)
+ do_purge (cx, getargs(cx));
+ else if (strncmp (token, "$xc", 3) == 0)
+ do_xc (cx);
+
+ else if (strncmp (token, "$debug", 6) == 0) {
+ if ((debug = (strcmp (getargs(cx), "off")) != 0))
+ verbose++; }
+ else if (strncmp (token, "$verbose", 8) == 0)
+ verbose = (strcmp (getargs(cx), "off") != 0);
+
+ else
+ warns ("illegal preprocessor directive `%s'", token);
+}
+
+
+/* DO_IF -- Called when a "$if.." token is seen in the input stream. Read in
+ * the predicate and set the state of the ifcode accordingly.
+ */
+void
+do_if (struct context *cx, char *keyword)
+{
+ register int ch;
+ register char *op;
+ char tokbuf[SZ_COMMAND+1];
+ char buf[SZ_PREDBUF], *argv[MAX_ARGS];
+ long fdate, altdate, os_fdate();
+ int argc, negate, bval, i;
+ char *key;
+
+ if (debug > 1) {
+ printf ("do_if: %s\n", keyword);
+ fflush (stdout);
+ }
+
+ /* Set the negate flag for the "$ifn" form of the if. Leave key
+ * pointing to the first char of whatever follows. Watch out for
+ * "$ifnewer".
+ */
+ key = &keyword[3]; /* "$if^" */
+ negate = (*key == 'n' && strncmp(key,"newer",5) != 0);
+ if (negate)
+ key++;
+
+ /* Extract the paren delimited list of predicate strings. This may
+ * extend over multiple lines if the newlines are escaped.
+ */
+ while ((ch = m_getc(cx)) != '(')
+ if (ch == '\n')
+ warns ("illegal `%s' predicate", keyword);
+ else if (ch == EOF)
+ warns ("unexpected EOF in `%s'", keyword);
+
+ argv[0] = buf;
+ op = buf;
+ argc = 0;
+
+ while ((ch = m_getc(cx)) != ')') {
+ if (ch == ESCAPE) {
+ ch = m_getc (cx);
+ if (ch == '\n')
+ continue;
+ else
+ *op++ = ch;
+ } else if (ch == '\n') {
+ warns ("missing right paren in `%s'", keyword);
+ } else if (ch == EOF) {
+ warns ("unexpected EOF in `%s'", keyword);
+ } else if (ch == ' ') {
+ continue;
+ } else if (ch == SYSFILE_BEGIN && op == argv[argc]) {
+ getstr (cx, tokbuf, SZ_COMMAND, SYSFILE_END);
+ if (m_sysfile (tokbuf, op, SZ_PREDBUF+buf-op) <= 0)
+ sprintf (op, "<%s>", tokbuf);
+ while (*op)
+ op++;
+ continue;
+ } else if (ch == ':' || ch == ',') {
+ *op++ = EOS;
+ if (op - buf >= SZ_PREDBUF)
+ warns ("predicate too large in `%s'", keyword);
+ if (++argc >= MAX_ARGS)
+ warns ("too many arguments in `%s' predicate", keyword);
+ argv[argc] = op;
+ } else
+ *op++ = ch;
+ }
+
+ *op = EOS;
+ argc++;
+
+ if (++iflev > SZ_IFSTACK)
+ warns ("$IFs nested too deeply (%s)", keyword);
+
+ /* If the $IF is encountered while scanning the tokens in a false-IF
+ * clause, do not "execute" the $IF. We still have to push the IF
+ * onto the control stack, because the matching $ENDIF is going to
+ * pop the stack.
+ */
+ if (ifstate[iflev-1] == STOP) {
+ ifstate[iflev] = STOP;
+ return;
+ }
+
+ /* Execute the $IF statement.
+ */
+ bval = 0;
+ if (strcmp (key, "def") == 0) {
+ /* $IFDEF. If the named symbol exists execute the true clause,
+ * else go to the else clause.
+ */
+ if (argc > 0) {
+ bval = (getsym (argv[0]) != NULL);
+ if (!bval)
+ bval = (os_getenv (argv[0]) != NULL);
+ }
+
+ } else if (strcmp (key, "eq") == 0) {
+ /* $IFEQ. Test if the named environment variable has one of the
+ * indicated values.
+ */
+ char *valstr;
+
+ if (argc > 0) {
+ if ((valstr = getsym (argv[0])) == NULL &&
+ (valstr = os_getenv (argv[0])) == NULL) {
+
+ warns ("symbol `%s' not found", argv[0]);
+ bval = 0;
+
+ } else {
+ if (argc == 1)
+ bval = 1;
+ else {
+ for (i=1; i < argc; i++)
+ if (strcmp (valstr, argv[i]) == 0) {
+ bval = 1;
+ break;
+ }
+ }
+ }
+ }
+
+ } else if (strcmp (key, "file") == 0) {
+ /* $IFFILE. Check for the existence of any of the named files.
+ */
+ for (i=0; i < argc; i++)
+ if (os_access (argv[i], 0,0) == YES) {
+ bval = 1;
+ break;
+ }
+
+ } else if (strcmp (key, "older") == 0) {
+ /* $IFOLDER. Check if the named file is older than any of the
+ * listed files. If the named file does not exist the result
+ * is true. If any of the listed files do not exist a warning
+ * is printed and they are ignored.
+ */
+ if (os_access (argv[1], 0,0) == NO) {
+ warns ("file `%s' not found", argv[1]);
+ bval = 1;
+ } else if ((fdate = os_fdate(argv[0])) <= 0) {
+ warns ("file `%s' not found", argv[0]);
+ bval = 1;
+ } else {
+ for (i=1; i < argc; i++) {
+ altdate = m_fdate (argv[i]);
+ if (altdate <= 0) {
+ warns ("file `%s' not found", argv[i]);
+ bval = 1;
+ break;
+ } else if (fdate < altdate) {
+ bval = 1;
+ break;
+ }
+ }
+ }
+
+ } else if (strcmp (key, "newer") == 0) {
+ /* $IFNEWER. Check if the named file is newer than any of the
+ * listed files. If the named file does not exist the result
+ * is false. If any of the listed files do not exist a warning
+ * is printed and they are ignored.
+ */
+ if (os_access (argv[1], 0,0) == NO) {
+ warns ("file `%s' not found", argv[1]);
+ bval = 1;
+ } else if ((fdate = os_fdate(argv[0])) <= 0) {
+ warns ("file `%s' not found", argv[0]);
+ bval = 1;
+ } else {
+ for (i=1; i < argc; i++) {
+ altdate = m_fdate (argv[i]);
+ if (altdate <= 0)
+ warns ("file `%s' not found", argv[i]);
+ else if (fdate > altdate) {
+ bval = 1;
+ break;
+ }
+ }
+ }
+
+ } else if (strcmp (key, "err") == 0) {
+ /* $IFERR. Test the exit status of the last command executed.
+ */
+ bval = (exit_status != OK);
+
+ } else
+ warns ("unrecognized $if statement `%s'", keyword);
+
+ if (negate)
+ bval = !bval;
+ ifstate[iflev] = bval;
+
+ if (debug) {
+ printf ("%s (", keyword);
+ if (argc > 0)
+ printf ("%s", argv[0]);
+ for (i=1; i < argc; i++)
+ printf (", %s", argv[i]);
+ printf (") -> %s\n", bval ? "YES" : "NO");
+ fflush (stdout);
+ }
+}
+
+
+/* DO_ELSE -- Called when the token "$else" is seen in the input stream.
+ * Toggle the if state. Do nothing if the state one level down in STOP,
+ * indicating that this $ELSE is nested inside the false clause of an
+ * outer $IF.
+ */
+void
+do_else (struct context *cx)
+{
+ if (debug > 1) {
+ printf ("do_else:\n");
+ fflush (stdout);
+ }
+
+ if (iflev < 1)
+ warns ("%s with no matching $if", "$else");
+ else if (iflev > 1 && ifstate[iflev-1] == STOP)
+ return;
+ else
+ ifstate[iflev] = (ifstate[iflev] == PASS) ? STOP : PASS;
+}
+
+
+/* DO_ENDIF -- Called when the token "$endif" is seen in the input stream.
+ * Pop the if stack.
+ */
+void
+do_endif (struct context *cx)
+{
+ if (debug > 1) {
+ printf ("do_endif:\n");
+ fflush (stdout);
+ }
+
+ if (--iflev < 0)
+ warns ("unmatched %s", "$endif");
+}
+
+
+/* DO_END -- Called when the token "$end" is seen in the input stream.
+ * Clear the if stack and reenable pass-token.
+ */
+void
+do_end (struct context *cx)
+{
+ if (debug > 1) {
+ printf ("do_end:\n");
+ fflush (stdout);
+ }
+
+ if (cx->prev && cx->prev->old_iflev >= 0)
+ iflev = cx->prev->old_iflev;
+ else
+ iflev = 0;
+}
+
+
+/* DO_CALL -- Call a "subroutine", i.e., named entry in a mkpkg file. The call
+ * may include definitions for any temporary symbols (arguments) to be passed
+ * to the subroutine. The subroutine is assumed to be in the current mkpkg
+ * file unless otherwise indicated.
+ *
+ * Syntax:
+ * $call module
+ * $call module (sym1=value, sym2=value, ...)
+ * $call module@subdir/file
+ * $call module@subdir/file (sym1=value, ...)
+ * (etc.)
+ *
+ * Note that the statements are interpreted (as is everything in mkpkg), hence
+ * mkpkg subroutines should not be used for trivial things.
+ */
+void
+do_call (
+ struct context *cx, /* current context */
+ char *program, /* module to be called */
+ int islib /* module list for a library */
+)
+{
+ struct context *ncx;
+ char module[SZ_FNAME+1], subdir[SZ_FNAME+1], fname[SZ_FNAME+1];
+ char symbol[SZ_FNAME+1], value[SZ_COMMAND+1];
+ char modspec[SZ_FNAME+1];
+ char *old_cp;
+ int old_nsymbols;
+
+ strcpy (modspec, program);
+ if (debug && ifstate[iflev] == PASS) {
+ printf ("$call %s\n", modspec);
+ fflush (stdout);
+ }
+
+ old_cp = cp;
+ old_nsymbols = nsymbols;
+
+ /* Process the argument list, if any, into the symbol table.
+ */
+ while (getkwvpair (cx, symbol, value) != ERR)
+ if (ifstate[iflev] == PASS)
+ putsym (symbol, value);
+
+ if (ifstate[iflev] == STOP)
+ return;
+
+ /* Parse the module name, push a new context, and execute the
+ * subroutine.
+ */
+ parse_modname (modspec, module, subdir, fname);
+ if ((ncx = push_context (cx, module, subdir, fname)) == NULL)
+ exit_status = ERR;
+ else {
+ exit_status = do_mkpkg (ncx, islib);
+ cx = pop_context (ncx);
+ }
+
+ /* Restore the old context and discard the argument temporaries.
+ */
+ if (exit_status != OK)
+ warns ("module `%s' not found or returned error", modspec);
+
+ cp = old_cp;
+ nsymbols = old_nsymbols;
+}
+
+
+/* DO_ECHO -- Print a message on the standard output.
+ */
+void
+do_echo (struct context *cx, char *msg)
+{
+ if (ifstate[iflev] == PASS) {
+ printf ("%s\n", msg);
+ fflush (stdout);
+ }
+}
+
+
+/* DO_GOTO -- Advance the file pointer to the named symbol in the current
+ * file, without changing the current context.
+ */
+int
+do_goto (struct context *cx, char *symbol)
+{
+ register char *ip;
+ char match[SZ_FNAME+1];
+ char lbuf[SZ_LINE+1];
+ int len_matchstr;
+ long fpos;
+
+ if (ifstate[iflev] == STOP)
+ return (OK);
+
+ if (debug) {
+ printf ("goto %s\n", symbol);
+ fflush (stdout);
+ }
+
+ sprintf (match, "%s:", symbol);
+ len_matchstr = strlen (match);
+
+ fpos = k_ftell (cx);
+ if (cx->fp != stdin)
+ k_fseek (cx, 0L, 0);
+
+ while (k_fgets (lbuf, SZ_LINE, cx) != NULL) {
+ cx->lineno++;
+ for (ip=lbuf; isspace (*ip); ip++)
+ ;
+ if (strncmp (ip, match, len_matchstr) == 0) {
+ /* GOTO clears the IF stack back to where it whatever it was
+ * upon entry to the module.
+ */
+ if (cx->prev && cx->prev->old_iflev >= 0)
+ iflev = cx->prev->old_iflev;
+ return (OK);
+ }
+ }
+
+ warns ("could not find mkpkg module or label `%s'", symbol);
+ if (cx->fp != stdin)
+ k_fseek (cx, fpos, 0);
+
+ return (ERR);
+}
+
+
+/* DO_INCLUDE -- Open a file and execute any preprocessor directives therein.
+ * Macros defined in an include are retained after the context of the include
+ * is popped.
+ */
+int
+do_include (
+ struct context *cx, /* current context */
+ char *fname /* include file name */
+)
+{
+ struct context *ncx;
+ int islib;
+
+ if (ifstate[iflev] == STOP)
+ return (OK);
+
+ if (debug > 1) {
+ printf ("do_include: %s\n", fname);
+ fflush (stdout);
+ }
+
+ ncx = push_context (cx, "BOF", "", fname);
+ do_mkpkg (ncx, islib=NO);
+ cx->old_cp = cp; /* keep symbols */
+ cx->old_nsymbols = nsymbols;
+ cx = pop_context (ncx);
+
+ return (OK);
+}
+
+
+/* DO_OMAKE -- Generate the object module for the named source module, if
+ * the object does not exist or is older than the source module.
+ */
+void
+do_omake (
+ struct context *cx,
+ char *fname
+)
+{
+ char cmd[SZ_COMMAND+1];
+ char xflags[SZ_LINE+1];
+ char *dflist[MAX_DEPFILES+1];
+ char *s_xflags, *dfile;
+ long sourcedate, objdate, date;
+ int recompile, i;
+
+
+ if (ifstate[iflev] == STOP)
+ return;
+
+ if (debug) {
+ printf ("omake %s\n", fname);
+ fflush (stdout);
+ }
+
+ if ((sourcedate = os_fdate (fname)) <= 0) {
+ warns ("file `%s' not found", fname);
+ exit_status = ERR;
+ return;
+
+ } else {
+ get_dependency_list (cx, fname, dflist, MAX_DEPFILES);
+ objdate = os_fdate (makeobj (fname));
+ recompile = 0;
+
+ if (sourcedate > objdate)
+ recompile++;
+ else {
+ for (i=0; (dfile = dflist[i]) != NULL; i++)
+ if ((date = m_fdate (dfile)) == 0)
+ warns ("dependency file `%s' not found", dfile);
+ else if (date > objdate) {
+ recompile++;
+ break;
+ }
+ }
+ }
+
+ if (recompile) {
+ /* Get XFLAGS. */
+ s_xflags = getsym (XFLAGS);
+ xflags[0] = EOS;
+ if (debug)
+ strcat (xflags, "-d ");
+ if (dbgout)
+ strcat (xflags, "-x ");
+ strcat (xflags, s_xflags);
+
+ if (irafdir[0])
+ sprintf (cmd, "%s %s -r %s %s", XC, xflags, irafdir, fname);
+ else
+ sprintf (cmd, "%s %s %s", XC, xflags, fname);
+
+ if (verbose) {
+ printf ("%s\n", cmd);
+ fflush (stdout);
+ }
+
+ if (execute)
+ exit_status = h_xc (cmd);
+ if (exit_status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", cx->library);
+
+ } else if (verbose) {
+ printf ("Object %s is up to date\n", makeobj(fname));
+ fflush (stdout);
+ }
+}
+
+
+/* DO_XC -- Call XC. Note that the current default xflags are not
+ * automatically included in the generated command.
+ */
+int
+do_xc (struct context *cx)
+{
+ char cmd[SZ_CMD+1];
+
+
+ if (debug > 1) {
+ printf ("do_xc:\n");
+ fflush (stdout);
+ }
+
+ if (irafdir[0])
+ sprintf (cmd, "%s -r %s", XC, irafdir);
+ else
+ sprintf (cmd, "%s", XC);
+
+ if (debug)
+ strcat (cmd, " -d");
+ if (dbgout)
+ strcat (cmd, " -x");
+
+ getcmd (cx, cmd, cmd, SZ_CMD);
+
+ if (ifstate[iflev] == STOP)
+ return 0;
+
+ if (verbose) {
+ printf ("%s\n", cmd);
+ fflush (stdout);
+ }
+
+ if (execute)
+ exit_status = h_xc (cmd);
+ if (exit_status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", cx->library);
+
+ return (exit_status);
+}
+
+
+/* DO_LINK -- Call XC to link a list of objects and/or libraries. This is
+ * equivalent to $XC, except that the LFLAGS are used instead of the XFLAGS.
+ */
+int
+do_link (struct context *cx)
+{
+ register struct sfile *sflist, *sfp=NULL;
+ static int skip_sf = 0;
+ char *ip, token[SZ_FNAME+1];
+ char linkline[SZ_CMD+1];
+ char cmdbuf[SZ_CMD+1];
+ char *cmd = cmdbuf;
+ int lflags_set = 0;
+ char *lflags;
+
+
+ if (debug > 1) {
+ printf ("do_link:\n");
+ fflush (stdout);
+ }
+
+ /* Get the link command from the input stream. */
+ getcmd (cx, "", linkline, SZ_CMD);
+
+ /* Check whether the executable being generated is on the special
+ * file list.
+ */
+ if (!skip_sf && (sflist = sf_dirsearch (cx->dirpath))) {
+ for (ip=linkline; getword(&ip,token,SZ_FNAME); )
+ if (strcmp (token, "-o") == 0)
+ if (getword (&ip, token, SZ_FNAME))
+ if ((sfp = sf_filesearch (sflist, token)))
+ break;
+ }
+
+ /* Check if LFLAGS is being substituted for this file. */
+ if (sfp && strncmp (sfp->sf_mkobj, "LFLAGS", 6) == 0) {
+ for (ip=sfp->sf_mkobj; *ip && *ip != '='; ip++)
+ ;
+ lflags = (*ip == '=') ? ip + 1 : ip;
+ lflags_set++;
+ } else
+ lflags = getsym (LFLAGS);
+
+ if (irafdir[0])
+ sprintf (cmd, "%s %s -r %s", XC, lflags, irafdir);
+ else
+ sprintf (cmd, "%s %s", XC, lflags);
+
+ if (debug)
+ strcat (cmd, " -d");
+ if (dbgout)
+ strcat (cmd, " -x");
+
+ strcat (cmd, linkline);
+
+ if (ifstate[iflev] == STOP)
+ return 0;
+
+ /* Check whether a special $link command or other build command
+ * should be executed.
+ */
+ if (sfp && !lflags_set) {
+ /* Push back the special link command. */
+ m_pushstr (cx, "\n");
+ m_pushstr (cx, sfp->sf_mkobj);
+
+ /* Avoid recursion if $link is pushed back. */
+ if (strncmp (sfp->sf_mkobj, "$link", 5) == 0)
+ skip_sf++;
+ return (OK);
+ }
+
+ if (verbose) {
+ printf ("%s\n", cmd);
+ fflush (stdout);
+ }
+
+ if (execute)
+ exit_status = h_xc (cmd);
+ if (exit_status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", cx->library);
+
+ skip_sf = 0;
+ return (exit_status);
+}
+
+
+/* DO_GENERIC -- Call the generic preprocessor.
+ */
+int
+do_generic (struct context *cx)
+{
+ char cmd[SZ_CMD+1];
+
+ if (debug > 1) {
+ printf ("do_generic:\n");
+ fflush (stdout);
+ }
+
+ getcmd (cx, GENERIC, cmd, SZ_CMD);
+
+ if (ifstate[iflev] == STOP)
+ return 0;
+
+ if (verbose) {
+ printf ("%s\n", cmd);
+ fflush (stdout);
+ }
+
+ if (execute)
+ exit_status = os_cmd (cmd);
+ if (exit_status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", cx->library);
+
+ return (exit_status);
+}
+
+
+/* DO_SET -- Enter the name and value of a symbol (macro) into the symbol
+ * table.
+ */
+void
+do_set (struct context *cx)
+{
+ char symbol[SZ_FNAME+1];
+ char value[SZ_PBBUF+1];
+
+ if (debug > 1) {
+ printf ("do_set:\n");
+ fflush (stdout);
+ }
+
+ if (getkwvpair (cx, symbol, value) != ERR) {
+ if (ifstate[iflev] == STOP)
+ return;
+
+ if (debug) {
+ printf ("set %s = `%s'\n", symbol, value);
+ fflush (stdout);
+ }
+ putsym (symbol, value);
+ }
+}
+
+
+/* DO_INCHECK -- Check a file (e.g, library) back into the named directory.
+ * (the "in" is first to make the external function name unique on systems
+ * which truncate external names).
+ */
+int
+do_incheck (struct context *cx)
+{
+ char fname[SZ_FNAME+1];
+ char dname[SZ_FNAME+1];
+
+ if (debug > 1) {
+ printf ("do_checkin:\n");
+ fflush (stdout);
+ }
+
+ strcpy (fname, getargs (cx));
+ strcpy (dname, getargs (cx));
+
+ exit_status = h_incheck (fname, dname);
+ if (exit_status != OK)
+ warns ("error during checkin of %s", fname);
+
+ return (exit_status);
+}
+
+
+/* DO_OUTCHECK -- Check a file (e.g, library) out of the named directory.
+ */
+int
+do_outcheck (struct context *cx)
+{
+ char fname[SZ_FNAME+1];
+ char dname[SZ_FNAME+1];
+ int clobber;
+
+ if (debug > 1) {
+ printf ("do_checkout:\n");
+ fflush (stdout);
+ }
+
+ strcpy (fname, getargs (cx));
+ strcpy (dname, getargs (cx));
+
+ exit_status = h_outcheck (fname, dname, clobber=YES);
+ if (exit_status != OK)
+ warns ("error during checkout of %s", fname);
+
+ return (exit_status);
+}
+
+
+/* DO_COPYFILE -- Copy a file.
+ */
+int
+do_copyfile (struct context *cx)
+{
+ char old[SZ_FNAME+1];
+ char new[SZ_FNAME+1];
+
+ if (debug > 1) {
+ printf ("do_copyfile:\n");
+ fflush (stdout);
+ }
+
+ strcpy (old, getargs (cx));
+ strcpy (new, getargs (cx));
+
+ if (ifstate[iflev] == STOP)
+ return 0;
+
+ if (verbose) {
+ printf ("copy `%s' to `%s'\n", old, new);
+ fflush (stdout);
+ }
+
+ exit_status = h_copyfile (old, new);
+ if (exit_status != OK)
+ warns ("error making copy of %s", old);
+
+ return (exit_status);
+}
+
+
+/* DO_MOVEFILE -- Move a file to another directory, or rename the file in the
+ * current directory.
+ */
+int
+do_movefile (struct context *cx)
+{
+ register char *ip, *op;
+ char old[SZ_FNAME+1];
+ char new[SZ_PATHNAME+1];
+
+ if (debug > 1) {
+ printf ("do_movefile:\n");
+ fflush (stdout);
+ }
+
+ strcpy (old, getargs (cx));
+ strcpy (new, getargs (cx));
+
+ if (ifstate[iflev] == STOP)
+ return 0;
+
+ /* If NEW is a directory, concatenate the filename. Always pass a
+ * filename to h_movefile.
+ */
+ for (op=new; *op; op++)
+ ;
+ if (*(op-1) == '$' || *(op-1) == '/')
+ for (ip=old; (*op++ = *ip++); )
+ ;
+
+ if (verbose) {
+ printf ("move `%s' to `%s'\n", old, new);
+ fflush (stdout);
+ }
+
+ exit_status = h_movefile (old, new);
+ if (exit_status != OK)
+ warns ("error moving file %s", old);
+
+ return (exit_status);
+}
+
+
+/* DO_DELETE -- Delete a file or list of files.
+ */
+void
+do_delete (struct context *cx)
+{
+ char fname[SZ_PATHNAME+1];
+
+
+ if (debug > 1) {
+ printf ("do_delete:\n");
+ fflush (stdout);
+ }
+
+ for (;;) {
+ strcpy (fname, getargs (cx));
+ if (fname[0] == EOS)
+ return;
+
+ if (ifstate[iflev] == STOP)
+ return;
+
+ if (execute) {
+ if (verbose) {
+ printf ("delete file %s\n", vfn2osfn(fname,0));
+ fflush (stdout);
+ }
+
+ exit_status = os_delete (fname);
+ if (exit_status != OK)
+ warns ("cannot delete file %s", fname);
+ }
+ }
+}
+
+
+/* DO_PURGE -- Purge all files in a directory. This is a no-op on systems
+ * that do not support multiple file versions.
+ */
+void
+do_purge (
+ struct context *cx, /* not used */
+ char *dname /* logical directory name */
+)
+{
+ if (debug > 1) {
+ printf ("do_purge: %s\n", dname);
+ fflush (stdout);
+ }
+
+ if (ifstate[iflev] == STOP)
+ return;
+
+ exit_status = h_purge (dname);
+ if (exit_status != OK)
+ warns ("error during purge of %s", dname);
+}
+
+
+/* GETCMD -- Extract a possibly multiline command from the input stream
+ * into a buffer, with macro replacement in the process.
+ */
+int
+getcmd (
+ register struct context *cx,
+ char *prefix, /* first part of command */
+ char *cmd, /* receives the command */
+ int maxch
+)
+{
+ register char *op, *otop;
+ register int ch;
+
+
+ otop = &cmd[maxch];
+ strcpy (cmd, prefix);
+ for (op=cmd; *op; op++)
+ ;
+
+ while (op < otop && (ch = m_getc(cx)) != EOF)
+ if (ch == ESCAPE) {
+ ch = m_getc (cx);
+ if (ch != '\n') {
+ *op++ = ESCAPE;
+ *op++ = ch;
+ }
+ } else if (ch == '\n') {
+ *op = EOS;
+ break;
+ } else if (ch == PREPROCESSOR && *(op-1) == ' ') {
+ /* $ is only recognized as a command delimiter if it occurs
+ * at the start of a new token.
+ */
+ m_ungetc (ch, cx);
+ *op = EOS;
+ break;
+ } else
+ *op++ = ch;
+
+ return (op - cmd);
+}
+
+
+/* GETARGS -- Accumulate the argument list of a preprocessor macro.
+ * The argument list may optionally be enclosed in parens or quotes,
+ * otherwise we look for whitespace or newline as the delimiter.
+ */
+char *
+getargs (
+ register struct context *cx /* current context */
+)
+{
+ register int ch;
+ static char args[SZ_PBBUF+1];
+ char tokbuf[SZ_COMMAND+1];
+ int delim;
+
+
+ while ((ch = m_getc(cx)) == ' ')
+ ;
+
+ if (ch == '(')
+ delim = ')';
+ else if (ch == '"' || ch == '\'')
+ delim = ch;
+ else if (ch == SYSFILE_BEGIN)
+ delim = SYSFILE_END;
+ else {
+ delim = ' ';
+ m_ungetc (ch, cx);
+ }
+
+ getstr (cx, tokbuf, SZ_COMMAND, delim);
+ strcpy (args, tokbuf);
+
+ if (delim == SYSFILE_END)
+ if (m_sysfile (tokbuf, args, SZ_PBBUF) <= 0)
+ sprintf (args, "<%s>", tokbuf);
+
+ return (args);
+}
+
+
+/* GETSTR -- Accumulate a string from the input stream, stopping when the
+ * specified delimiter character is seen. Note that macros are expanded
+ * even within quoted strings, as in MAKE (macros are defined at the character
+ * level, rather than at the token level).
+ */
+int
+getstr (
+ register struct context *cx, /* current context */
+ char *outstr, /* receives string */
+ int maxch, /* max chars out */
+ int delim /* delimiter character */
+)
+{
+ register char *op;
+ register int ch, n;
+
+ for (op=outstr, n=maxch; --n >= 0 && (ch = m_getc(cx)) != delim; )
+ if (ch == '\\') {
+ ch = m_getc(cx);
+ if (ch == '\n')
+ ;
+ else if (ch == delim)
+ *op++ = ch;
+ else {
+ *op++ = '\\';
+ *op++ = ch;
+ }
+ } else if (ch == '\n' || ch == EOF) {
+ *op = EOS;
+ if (delim != ' ')
+ warns ("missing closing quote in string `%s'", outstr);
+ m_ungetc ('\n', cx);
+ break;
+ } else
+ *op++ = ch;
+
+ *op = EOS;
+ return (op - outstr);
+}
+
+
+/* GETKWVPAIR -- Extract the keyword and value fields from a "keyword=value"
+ * construct in the input stream.
+ */
+int
+getkwvpair (
+ register struct context *cx, /* current context */
+ char *symbol, /* receives name of symbol */
+ char *value /* receives value of symbol */
+)
+{
+ register char *op;
+ register int ch;
+
+ while ((ch = m_getc(cx)) == ' ')
+ ;
+ if (!isalpha(ch)) {
+ m_ungetc (ch, cx);
+ return (ERR);
+ }
+
+ /* Extract module name */
+ for (op=symbol, *op++ = ch; (ch = m_getc(cx)) != '='; ) {
+ if (ch == ' ') {
+ continue;
+ } else if (ch == '\n') {
+ warns ("missing `=' in $set statement `%s'", symbol);
+ m_ungetc ('\n', cx);
+ return (ERR);
+ } else
+ *op++ = ch;
+ }
+ *op = EOS;
+
+ /* Extract symbol value */
+ strcpy (value, getargs(cx));
+ return (OK);
+}
+
+
+/* GETWORD -- Extract a whitespace delimited substring from a string.
+ * The input pointer is left pointing to the first character following
+ * the extracted string.
+ */
+int
+getword (
+ char **str,
+ char *outstr,
+ int maxch
+)
+{
+ register char *ip=(*str), *op=outstr;
+ register char *otop = outstr + maxch;
+ register int ch;
+
+ while (*ip && isspace (*ip))
+ ip++;
+
+ while (op < otop && (ch = *ip++))
+ if (isspace (ch))
+ break;
+ else
+ *op++ = ch;
+
+ *op = EOS;
+ *str = ip;
+
+ return (op - outstr);
+}
+
+
+/* PUTSYM -- Add a symbol (macro definition) to the symbol table. Symbol
+ * storage is in the string buffer, with all symbols defined local to a
+ * module being discarded when the module exits. All symbols are globally
+ * accessible, with local symbols possibly redefining (temporarily) existing
+ * external symbols (e.g., the value of "xflags" might be reset locally,
+ * but should not affect outer level code once the module has exited).
+ * Symbol names are treated in a case insensitive fashion to simplify use
+ * on systems that do not preserve case, e.g., in the MKPKG argument list.
+ */
+void
+putsym (
+ char *name, /* symbol name */
+ char *value /* symbol value */
+)
+{
+ char *symbol;
+
+ if (debug) {
+ printf ("put symbol %s = `%s'\n", name, value);
+ fflush (stdout);
+ }
+
+ symbol = mklower (name);
+ symtab[nsymbols].s_name = putstr (symbol);
+ symtab[nsymbols].s_value = putstr (value);
+
+ if (++nsymbols >= MAX_SYMBOLS)
+ fatals ("too many symbols (`%s')", name);
+}
+
+
+/* GETSYM -- Lookup a symbol in the symbol table. Return the symbol value
+ * as the function value if the symbol is found, else return NULL. The symbol
+ * table is searched most-recently-defined symbols first, permitting symbols
+ * to be redefined locally. Note that the full table is searched, hence the
+ * outer symbols are globally accessible. The number of symbols tends to be
+ * quite small and symbol lookup only occurs when a macro is explicitly
+ * referenced as $(NAME), hence a simple linear search is best.
+ */
+char *
+getsym (
+ char *name /* symbol name */
+)
+{
+ register struct symbol *sp, *stop;
+ register int ch;
+ char *symbol;
+
+ symbol = mklower (name);
+ stop = &symtab[0];
+ sp = &symtab[nsymbols];
+ ch = symbol[0];
+
+ /* Search the symbol table.
+ */
+ while (--sp >= stop)
+ if (sp->s_name[0] == ch)
+ if (strcmp (sp->s_name, symbol) == 0)
+ return (sp->s_value);
+
+ return (NULL);
+}
+
+
+/* MKLOWER -- Convert a small string to lower case and return a pointer to
+ * a local copy of the new string.
+ */
+char *
+mklower (char *s)
+{
+ register char *ip, *op;
+ register int n, ch;
+ static char lstr[SZ_FNAME+1];
+
+ for (ip=s, op=lstr, n=SZ_FNAME; --n >= 0 && (ch = *ip++); )
+ if (isupper (ch))
+ *op++ = tolower (ch);
+ else
+ *op++ = ch;
+ *op = EOS;
+
+ return (lstr);
+}