aboutsummaryrefslogtreecommitdiff
path: root/unix/boot/mkpkg
diff options
context:
space:
mode:
Diffstat (limited to 'unix/boot/mkpkg')
-rw-r--r--unix/boot/mkpkg/README54
-rw-r--r--unix/boot/mkpkg/char.c478
-rw-r--r--unix/boot/mkpkg/extern.h18
-rw-r--r--unix/boot/mkpkg/fdcache.c190
-rw-r--r--unix/boot/mkpkg/fncache.c228
-rw-r--r--unix/boot/mkpkg/host.c917
-rw-r--r--unix/boot/mkpkg/main.c347
-rw-r--r--unix/boot/mkpkg/mkpkg33
-rw-r--r--unix/boot/mkpkg/mkpkg.h254
-rw-r--r--unix/boot/mkpkg/mkpkg.hlp626
-rw-r--r--unix/boot/mkpkg/mkpkg.sh9
-rw-r--r--unix/boot/mkpkg/pkg.c902
-rw-r--r--unix/boot/mkpkg/scanlib.c355
-rw-r--r--unix/boot/mkpkg/sflist.c321
-rw-r--r--unix/boot/mkpkg/tok.c1457
15 files changed, 6189 insertions, 0 deletions
diff --git a/unix/boot/mkpkg/README b/unix/boot/mkpkg/README
new file mode 100644
index 00000000..999d154c
--- /dev/null
+++ b/unix/boot/mkpkg/README
@@ -0,0 +1,54 @@
+MKPKG -- Package maintenance utility.
+
+ The MKPKG utility is used to maintain the IRAF system libraries as well
+as the system executables and the applications packages. The file "mkpkg.csh"
+in this directory will make and install the initial mkpkg.e executable.
+The libraries lib$libboot.a and lib$libos.a must have been made first.
+Once MKPKG is up it can be used to remake itself.
+
+
+NOTES
+
+ The MKPKG utility is used to keep libraries and/or packages up to date.
+The dates of the library modules are compared to the corresponding SOURCE
+(not object) files in the directories contributing to the library.
+Any source files newer than their corresponding library modules are
+compiled and the library is updated. Note that the sources contributing
+to the library may reside in multiple subdirectories as well as in the
+current directory. Each source file may depend on zero or more other files.
+If any of these files are newer than the source file, the source file is
+recompiled and replaced in the library.
+
+MKPKG is built upon a preprocessor front end providing macro replacement
+and conditional interpretation facilities. These facilities, in combination
+with the OS escape mechanism used to send commands to the host system,
+make it possible to use MKPKG for more than just updating libraries.
+
+As far as possible, the system dependent functions required by MKPKG have
+been isolated and placed in separate small files. The bulk of the code is
+machine independent. Additional system dependent functions are provided
+by the BOOTLIB library (LIBBOOT) and by the IRAF kernel (LIBOS). The MKPKG
+specific functions required are the following:
+
+ [1] Given the NAME of a source file, return the date of the
+ corresponding object module in a library.
+ [2] Replace (or add) a series of object modules in a library,
+ creating the library if it does not already exist.
+ [3] "Rebuild" the library after all updates are complete.
+
+The library functions are normally implemented by formatting a command
+for the host librarian utility and sending it to the host with the ZOSCMD
+utility. Note that an entire command script can be built in a temporary
+file if the ZOSCMD interface is too inefficient for multiple small calls
+on your system.
+
+All filenames in the portable code (and in the Makelib files) are in the
+IRAF format, which is very similar to UNIX format. Do not change the high
+level code to manipulate host system filenames directly. All filename
+mapping should be performed in the host interface code; the VFN2OSFN
+function is convenient to use for this purpose.
+
+For simplicity, most buffers are fixed in size. Dynamically allocating
+everything is less efficient and is not warranted since the memory
+requirements of this program are modest. If a buffer overflows simply
+increase the allocation below and remake mkpkg.
diff --git a/unix/boot/mkpkg/char.c b/unix/boot/mkpkg/char.c
new file mode 100644
index 00000000..9532dfd6
--- /dev/null
+++ b/unix/boot/mkpkg/char.c
@@ -0,0 +1,478 @@
+/* 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"
+
+/*
+ * CHAR.C -- Character functions, character i/o.
+ */
+
+/* M_GETC -- Get a (possibly pushed back) character from the mkpkgfile
+ * associated with the given context. If the sequence $( is encountered
+ * in the input, fetch the value of the named macro and push it back into
+ * the input stream and continue scanning. Implementing recursive macro
+ * expansion at this low level permits the use of macros in any part of
+ * the input except comments.
+ */
+int
+m_getc (register struct context *cx)
+{
+ register int ch, nch;
+ register char *op;
+ char name[SZ_FNAME+1], *val;
+ char lbuf[SZ_CMD+1];
+
+ while ((ch = m_rawgetc (cx)) == '$') {
+ /* Check for the escape sequence "$$" and return the literal $
+ * if this is seen. Also return if $ is seen but the next char
+ * is not left paren ("$(..)" is a macro reference).
+ */
+ nch = m_rawgetc (cx);
+ if (nch == '$')
+ return (nch);
+ else if (nch != '(') {
+ m_ungetc (nch, cx);
+ break;
+ }
+
+ /* Extract the name of the macro from the input stream.
+ */
+ for (op=name; (*op = m_rawgetc(cx)) != ')'; op++)
+ if (*op == '\n' || *op == EOF) {
+ *op = EOS;
+ warns ("missing right paren in $(M) macro reference: `%s'",
+ name);
+ *op++ = '\n';
+ *op = EOS;
+ val = name;
+ goto push;
+ break;
+ }
+ *op = EOS;
+
+ /* If the symbol name is prefixed by a question mark, e.g., $(?sym),
+ * query for the symbol and read the value from the standard input.
+ * If the syntax is "$(@file)" return the contents of the named
+ * file as the value of the macro reference. Otherwise look in
+ * the symbol table and then in the environment for the named
+ * symbol. If the symbol cannot be found in either place push
+ * its name and hope for the best.
+ */
+ if (name[0] == '?') {
+ /* Interactive query. */
+ if ((cx->fp == stdin)) {
+ warns ("`$(%s)': cannot query in -f stdin mode", name);
+ val = &name[1];
+ } else {
+ printf ("%s: ", &name[1]);
+ fflush (stdout);
+ if (fgets (lbuf, SZ_CMD, stdin) == NULL)
+ strcpy (lbuf, name);
+ if ((val = index (lbuf, '\n')))
+ *val = EOS;
+ val = lbuf;
+ }
+ } else if (name[0] == '@') {
+ /* Return contents of a file. */
+ FILE *fp;
+ int ch, n;
+
+ if ((fp = fopen (&name[1], "r")) == NULL) {
+ warns ("`$(%s)': cannot open file", name);
+ val = &name[1];
+ } else {
+ for (n=SZ_CMD,op=lbuf; --n >= 0 && (ch=getc(fp)) != EOF; )
+ *op++ = isspace(ch) ? ' ' : ch;
+ while (op > lbuf) {
+ ch = *(op-1);
+ if (isspace (ch))
+ --op;
+ else
+ break;
+ }
+ *op = EOS;
+ val = lbuf;
+ fclose (fp);
+ }
+
+ } else if ((val = getsym (name)) == NULL) {
+ if ((val = os_getenv (name)) == NULL) {
+ warns ("macro `%s' not found", name);
+ val = name;
+ }
+ }
+push:
+ if (debug > 1) {
+ printf ("pushback macro `%s' = `%s'\n", name, val);
+ fflush (stdout);
+ }
+
+ m_pushstr (cx, val);
+ }
+
+ /* Get rid of the tabs once and for all.
+ */
+ return ((ch == '\t') ? ' ' : ch);
+}
+
+
+
+/* M_RAWGETC -- Get a (possibly pushed back) character from the mkpkgfile
+ * associated with the given context.
+ */
+int
+m_rawgetc (register struct context *cx)
+{
+ register struct pushback *pb;
+ register int ch;
+
+ for (;;) {
+ /* Check for single character pushback first. This type of pushback
+ * occurs at the end of every token.
+ */
+ if ((ch = cx->pbchar)) {
+ if (debug > 3) {
+ if (ch <= 040)
+ printf ("return pushback character 0%o\n", ch);
+ else
+ printf ("return pushback character `%c'\n", ch);
+ fflush (stdout);
+ }
+ cx->pbchar = 0;
+ break;
+ }
+
+ /* Check for string type pushback; return character directly from
+ * file if no pushback.
+ */
+ if (!cx->pushback) {
+ ch = k_getc (cx);
+ break;
+ }
+
+ /* Get pushed back character from pushback buffer.
+ */
+ pb = cx->pb;
+ if ((ch = *(pb->ip)++) != EOS) {
+ if (debug > 3) {
+ if (ch <= 040)
+ printf ("return pbbuf character 0%o\n", ch);
+ else
+ printf ("return pbbuf character `%c'\n", ch);
+ fflush (stdout);
+ }
+ break;
+ }
+
+ /* End of pushed back string; pop pushback stack.
+ */
+ if (debug > 3) {
+ printf ("pop pushback stack at level=%d\n", pb->npb);
+ fflush (stdout);
+ }
+
+ pb->op = pb->pbstk[--(pb->npb)];
+ pb->ip = pb->pbstk[--(pb->npb)];
+
+ if (pb->npb <= 0)
+ cx->pushback = 0;
+ }
+
+ if (ch == '\n')
+ cx->lineno++;
+
+ return (ch);
+}
+
+
+/* M_UNGETC -- Pushback a single character, last in first out. Only a single
+ * character of this type of pushback is normally allowed, however by using
+ * PUSHSTR we can provide additional pushback at additional expense (no
+ * problem provided it is not used a lot).
+ */
+void
+m_ungetc (
+ int ch,
+ struct context *cx
+)
+{
+ static char ps[2] = "\0";
+
+ if (ch == '\n')
+ --cx->lineno;
+
+ if ((ps[0] = cx->pbchar))
+ m_pushstr (cx, ps);
+
+ cx->pbchar = ch;
+
+ if (debug > 3) {
+ if (ch <= 040)
+ printf ("ungetc 0%o\n", ch);
+ else
+ printf ("ungetc `%c'\n", ch);
+ fflush (stdout);
+ }
+}
+
+
+/* M_PUSHSTR -- Pushback a string. Pushed strings are read back LIFO, although
+ * of course the individual characters are returned FIFO.
+ */
+void
+m_pushstr (
+ struct context *cx,
+ char *str
+)
+{
+ register struct pushback *pb;
+ register char *ip, *op, *otop, ch;
+
+ if (debug > 2) {
+ if (str[0] <= 040)
+ printf ("pushback punct char 0x%lx\n", (long) str);
+ else
+ printf ("pushback string `%s'\n", str);
+ fflush (stdout);
+ }
+
+ cx->pushback++;
+ while ((pb = cx->pb) == NULL)
+ mk_pbbuf (cx);
+
+ pb->pbstk[(pb->npb)++] = pb->ip;
+ pb->pbstk[(pb->npb)++] = pb->op;
+ otop = pb->otop;
+
+ for (ip=str, op=pb->op; (*op++ = ch = *ip++); ) {
+ if (ch == '\n')
+ --cx->lineno;
+ if (op >= otop)
+ break;
+ }
+
+ pb->ip = pb->op;
+ pb->op = op;
+
+ if (debug > 2) {
+ printf ("pb status: ");
+ printf ("level=%d(%d) nleft=%ld ip=%ld op=%ld bp=%ld otop=%ld\n",
+ pb->npb, SZ_PBSTK,
+ (long) (otop-op),
+ (long) pb->ip,
+ (long) pb->op,
+ (long) pb->pbbuf,
+ (long) otop);
+ fflush (stdout);
+ }
+
+ if (pb->npb + 2 >= SZ_PBSTK || pb->op >= pb->otop)
+ fatals ("excessive pushback in `%s'; macro recursion?",
+ cx->mkpkgfile);
+}
+
+
+/* MK_PBBUF -- Allocate and initialize the pushback descriptor.
+ */
+void
+mk_pbbuf (register struct context *cx)
+{
+ register struct pushback *pb;
+
+ pb = cx->pb = (struct pushback *) malloc (sizeof (struct pushback));
+ if (pb == NULL)
+ fatals ("out of memory in `%s'", cx->mkpkgfile);
+
+ pb->npb = 0;
+ pb->ip = pb->pbbuf;
+ pb->op = pb->pbbuf;
+ pb->otop = &pb->pbbuf[SZ_PBBUF];
+}
+
+
+/* PB_CANCEL -- Cancel any pushback.
+ */
+void
+pb_cancel (register struct context *cx)
+{
+ register struct pushback *pb;
+
+ cx->pushback = 0;
+ cx->pbchar = 0;
+
+ if ((pb = cx->pb) != NULL) {
+ pb->npb = 0;
+ pb->ip = pb->pbbuf;
+ pb->op = pb->pbbuf;
+ pb->otop = &pb->pbbuf[SZ_PBBUF];
+ }
+}
+
+
+/* PUTSTR -- Add a string to end of the string buffer. It is a fatal error
+ * if the string buffer overflows.
+ */
+char *
+putstr (char *s)
+{
+ register char *ip, *op, *otop;
+ char *start;
+
+ start = cp;
+ otop = ctop;
+
+ for (ip=s, op=cp; (*op++ = *ip++); )
+ if (op >= otop)
+ fatals ("string buffer overflow at `%s'", s);
+
+ cp = op;
+
+ if (debug > 2) {
+ printf ("putstr `%s': nleft=%ld\n", s, (long)(otop-op));
+ fflush (stdout);
+ }
+
+ return (start);
+}
+
+
+/*
+ * OS Character I/O. This set of routines are provided as a workaround in
+ * the event that the host system cannot execute FTELL/FSEEK reliably (VMS/C
+ * could not). The idea here is to keep track of the character offset from
+ * the beginning of the file. K_FTELL returns the character offset. K_FSEEK
+ * rewinds the file and reads characters forward to the indicated offset.
+ * K_GETC keeps a count of the file position. (the k_ stands for kludge).
+ */
+
+#ifdef vms
+
+int
+k_getc (register struct context *cx)
+{
+ register int ch;
+
+ cx->fpos++;
+ if (debug > 3) {
+ if ((ch = getc (cx->fp)) > 0)
+ printf ("%5d %03o %c\n", cx->fpos, ch, ch > 040 ? ch : 040);
+ return (ch);
+ } else
+ return (getc (cx->fp));
+}
+
+char *
+k_fgets (
+ char *obuf,
+ int maxch,
+ register struct context *cx
+)
+{
+ register int ch, n;
+ register char *op;
+
+ for (op=obuf, n=maxch; --n >= 0; )
+ if ((ch = k_getc(cx)) < 0)
+ return (NULL);
+ else {
+ *op++ = ch;
+ if (ch == '\n')
+ break;
+ }
+
+ return (obuf);
+}
+
+int
+k_fseek (
+ register struct context *cx,
+ long offset,
+ int type
+)
+{
+ register FILE *fp = cx->fp;
+ register int ch;
+
+ if (debug > 1)
+ printf ("seek (%s, %ld, %d)\n", cx->mkpkgfile, offset, type);
+
+ if (type == 0) {
+ fseek (fp, 0L, 0);
+ cx->fpos = 0;
+
+ while (cx->fpos < offset && (ch = getc(fp)) != EOF) {
+ if (debug > 1)
+ fputc (ch, stdout);
+ cx->fpos++;
+ }
+
+ if (debug > 1)
+ printf ("[]\n");
+
+ return (0);
+ }
+
+ if (fseek (fp, offset, type) == ERR)
+ return (ERR);
+ else {
+ cx->fpos = ftell (fp);
+ return (0);
+ }
+}
+
+long
+k_ftell (register struct context *cx)
+{
+ if (debug > 1) {
+ printf ("ftell returns %d\n", cx->fpos);
+ fflush (stdout);
+ }
+ return (cx->fpos);
+}
+
+#else
+
+int
+k_getc (struct context *cx)
+{
+ return (getc (cx->fp));
+}
+
+char *
+k_fgets (
+ char *op,
+ int maxch,
+ register struct context *cx
+)
+{
+ return (fgets (op, maxch, cx->fp));
+}
+
+int
+k_fseek (
+ struct context *cx,
+ long offset,
+ int type
+)
+{
+ return (fseek (cx->fp, offset, type));
+}
+
+long
+k_ftell (struct context *cx)
+{
+ return (ftell (cx->fp));
+}
+
+#endif
diff --git a/unix/boot/mkpkg/extern.h b/unix/boot/mkpkg/extern.h
new file mode 100644
index 00000000..6ade9584
--- /dev/null
+++ b/unix/boot/mkpkg/extern.h
@@ -0,0 +1,18 @@
+/* EXTERN.H -- External static variables.
+ */
+extern char sbuf[]; /* string buffer */
+extern struct symbol symtab[]; /* symbol table (macros) */
+extern struct context *topcx; /* currently active context */
+extern char *cp; /* pointer into sbuf */
+extern char *ctop; /* top of sbuf */
+extern char irafdir[]; /* iraf root directory */
+extern int nsymbols; /* number of defined symbols */
+extern int ifstate[]; /* $IF stack */
+extern int iflev; /* $IF stack pointer */
+extern int debug; /* print debug messages */
+extern int dbgout; /* compile for debugging */
+extern int verbose; /* print informative messages */
+extern int ignore; /* ignore warns */
+extern int execute; /* think but don't act? */
+extern int exit_status; /* exit status of last syscall */
+extern int forceupdate; /* foribly update libmod dates */
diff --git a/unix/boot/mkpkg/fdcache.c b/unix/boot/mkpkg/fdcache.c
new file mode 100644
index 00000000..7dfca1a3
--- /dev/null
+++ b/unix/boot/mkpkg/fdcache.c
@@ -0,0 +1,190 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+/*
+ * FDCACHE -- Maintain a cache of filenames and their associated modification
+ * dates. This can greatly reduce the amount of time required to determine
+ * which, if any, of the modules in a library need updating because an include
+ * file they depend upon has been modified.
+ *
+ * External entry points:
+ *
+ * l = m_fdate (fname) # return file (modification) date
+ * m_fdinit (debug) # initialize cache
+ */
+
+#define MAX_FILES 20 /* size of the cache */
+#define SZ_NAME 32 /* size of filename slot */
+#define EOS '\0'
+
+struct _fdate { /* cache list element structure */
+ struct _fdate *uplnk;
+ struct _fdate *dnlnk;
+ int nrefs; /* number of references */
+ int chksum; /* speeds searches */
+ long fdate; /* file modification date */
+ char fname[SZ_NAME+1]; /* file name */
+};
+
+struct _fdate fdcache[MAX_FILES]; /* the cache */
+struct _fdate *fd_head; /* doubly linked list */
+struct _fdate *fd_tail;
+int fd_hits, fd_misses;
+
+struct _fdate *fd_unlink();
+struct _fdate *fd_tohead();
+struct _fdate *fd_totail();
+
+long m_fdate (char *fname);
+void m_fdinit (int debug);
+int fd_chksum (char *s);
+
+extern long os_fdate (char *fname);
+
+
+/* M_FDATE -- Get file modification date. This is functionally equivalent to
+ * os_fdate().
+ */
+long
+m_fdate (char *fname)
+{
+ register struct _fdate *fd;
+ register int chksum;
+
+ /* Look in the cache first.
+ */
+ chksum = fd_chksum (fname);
+ for (fd=fd_head; fd != NULL; fd=fd->dnlnk)
+ if (fd->chksum == chksum && strcmp (fname, fd->fname) == 0) {
+ fd_tohead (fd_unlink (fd));
+ fd->nrefs++;
+ fd_hits++;
+ return (fd->fdate);
+ }
+
+ /* Cache miss. Don't put in cache it name is too long.
+ */
+ fd_misses++;
+ if (strlen (fname) > SZ_NAME)
+ return (os_fdate (fname));
+
+ /* Put fname in the cache. Reuse slot at tail of list.
+ */
+ fd = fd_tohead (fd_unlink (fd_tail));
+ strncpy (fd->fname, fname, SZ_NAME);
+ fd->chksum = fd_chksum (fname);
+ fd->fdate = os_fdate (fname);
+ fd->nrefs = 1;
+
+ return (fd->fdate);
+}
+
+
+/* M_FDINIT -- Initialize (clear) the fdate cache.
+ */
+void
+m_fdinit (int debug)
+{
+ register struct _fdate *fd;
+ register int i;
+ int total;
+
+ if (debug) {
+ total = fd_hits + fd_misses;
+ printf ("file date cache: %d hits, %d misses, %d%% of %d\n",
+ fd_hits, fd_misses, (total ? fd_hits * 100 / total : 0), total);
+
+ for (fd=fd_head; fd != NULL; fd=fd->dnlnk)
+ if (fd->fname[0])
+ printf ("%3d %10ld (%05d) %s\n",
+ fd->nrefs, fd->fdate, fd->chksum, fd->fname);
+
+ fd_hits = 0;
+ fd_misses = 0;
+
+ fflush (stdout);
+ }
+
+ fd = fd_head = fd_tail = &fdcache[0];
+ fd->uplnk = NULL;
+ fd->dnlnk = NULL;
+ fd->nrefs = 0;
+ fd->chksum = -1;
+ fd->fname[0] = EOS;
+
+ for (i=1; i < MAX_FILES; i++) {
+ fd = fd_tohead (&fdcache[i]);
+ fd->fname[0] = EOS;
+ fd->chksum = -1;
+ fd->nrefs = 0;
+ }
+}
+
+
+/* FD_TOHEAD -- Link a fdate struct at the head of the list.
+ */
+struct _fdate *
+fd_tohead (register struct _fdate *fd)
+{
+ if (fd != fd_head) {
+ fd->uplnk = NULL;
+ fd->dnlnk = fd_head;
+ fd_head->uplnk = fd;
+ fd_head = fd;
+ }
+
+ return (fd);
+}
+
+
+/* FD_TOTAIL -- Link a fdate struct at the tail of the list.
+ */
+struct _fdate *
+fd_totail (register struct _fdate *fd)
+{
+ if (fd != fd_tail) {
+ fd->uplnk = fd_tail;
+ fd->dnlnk = NULL;
+ fd_tail->dnlnk = fd;
+ fd_tail = fd;
+ }
+
+ return (fd);
+}
+
+
+/* FD_UNLINK -- Unlink an fdate struct.
+ */
+struct _fdate *
+fd_unlink (register struct _fdate *fd)
+{
+ if (fd == fd_head)
+ fd_head = fd->dnlnk;
+ if (fd == fd_tail)
+ fd_tail = fd->uplnk;
+
+ if (fd->uplnk)
+ fd->uplnk->dnlnk = fd->dnlnk;
+ if (fd->dnlnk)
+ fd->dnlnk->uplnk = fd->uplnk;
+
+ return (fd);
+}
+
+
+/* FD_CHKSUM -- Compute the checksum of a character string.
+ */
+int
+fd_chksum (char *s)
+{
+ register int sum=0;
+
+ while (*s)
+ sum += *s++;
+
+ return (sum);
+}
diff --git a/unix/boot/mkpkg/fncache.c b/unix/boot/mkpkg/fncache.c
new file mode 100644
index 00000000..2053f2fe
--- /dev/null
+++ b/unix/boot/mkpkg/fncache.c
@@ -0,0 +1,228 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+//#include "../bootProto.h"
+
+
+/*
+ * FNCACHE -- Maintain a cache of system logical filenames (e.g., <config.h>)
+ * and their associated virtual filenames (e.g., "host$hlib/config.h").
+ * This can greatly reduce the amount of time required to resolve references
+ * to system include files in dependency file lists.
+ *
+ * External entry points:
+ *
+ * nc = m_sysfile (lname, fname, maxch) # return file name
+ * m_fninit (debug) # initialize cache
+ */
+
+#define MAX_FILES 20 /* size of the cache */
+#define SZ_LNAME 32 /* size of logical name */
+#define SZ_FNAME 32 /* size of virtual file name */
+#define EOS '\0'
+
+struct _sysfile { /* cache list element structure */
+ struct _sysfile *uplnk;
+ struct _sysfile *dnlnk;
+ int nrefs; /* number of references */
+ int chksum; /* speeds searches */
+ char lname[SZ_LNAME+1]; /* logical name */
+ char fname[SZ_FNAME+1]; /* file name */
+};
+
+struct _sysfile fncache[MAX_FILES]; /* the cache */
+struct _sysfile *fn_head; /* doubly linked list */
+struct _sysfile *fn_tail;
+int fn_hits, fn_misses;
+
+struct _sysfile *fn_unlink();
+struct _sysfile *fn_tohead();
+struct _sysfile *fn_totail();
+
+
+extern int os_sysfile (char *sysfile, char *fname, int maxch);
+
+int m_sysfile (char *lname, char *fname, int maxch);
+void m_fninit (int debug);
+int fn_chksum (char *s);
+int fn_strncpy (char *out, char *in, int maxch);
+
+
+
+/* M_SYSFILE -- Search for the named system file and return the virtual file
+ * name in the output string if the system file is found. This is functionally
+ * equivalent to os_sysfile().
+ */
+int
+m_sysfile (
+ char *lname, /* logical name of system file */
+ char *fname, /* receives virtual file name */
+ int maxch
+)
+{
+ register struct _sysfile *fn;
+ register int chksum;
+ int fnlen;
+
+ /* Look in the cache first. For a small cache a linear search is
+ * plenty fast enough.
+ */
+ chksum = fn_chksum (lname);
+ for (fn=fn_head; fn != NULL; fn=fn->dnlnk)
+ if (fn->chksum == chksum && strcmp (lname, fn->lname) == 0) {
+ fn_tohead (fn_unlink (fn));
+ fn->nrefs++;
+ fn_hits++;
+ return (fn_strncpy (fname, fn->fname, maxch));
+ }
+
+ /* Cache miss. Don't put in cache it name is too long.
+ */
+ fn_misses++;
+ fnlen = os_sysfile (lname, fname, maxch);
+ if (fnlen > SZ_FNAME || strlen(lname) > SZ_LNAME)
+ return (fnlen);
+
+ /* Put fname in the cache. Reuse slot at tail of list.
+ */
+ fn = fn_tohead (fn_unlink (fn_tail));
+ strcpy (fn->lname, lname);
+ strcpy (fn->fname, fname);
+ fn->chksum = fn_chksum (lname);
+ fn->nrefs = 1;
+
+ return (fnlen);
+}
+
+
+/* M_FNINIT -- Initialize (clear) the sysfile cache.
+ */
+void
+m_fninit (int debug)
+{
+ register struct _sysfile *fn;
+ register int i;
+ int total;
+
+ if (debug) {
+ char lname[SZ_FNAME+1];
+
+ total = fn_hits + fn_misses;
+ printf ("file name cache: %d hits, %d misses, %d%% of %d\n",
+ fn_hits, fn_misses, (total ? fn_hits * 100 / total : 0), total);
+
+ for (fn=fn_head; fn != NULL; fn=fn->dnlnk)
+ if (fn->lname[0]) {
+ sprintf (lname, "<%s>", fn->lname);
+ printf ("%3d (%05d) %-20s => %s\n",
+ fn->nrefs, fn->chksum, lname, fn->fname);
+ }
+
+ fn_hits = 0;
+ fn_misses = 0;
+
+ fflush (stdout);
+ }
+
+ fn = fn_head = fn_tail = &fncache[0];
+ fn->uplnk = NULL;
+ fn->dnlnk = NULL;
+ fn->nrefs = 0;
+ fn->chksum = -1;
+ fn->lname[0] = EOS;
+
+ for (i=1; i < MAX_FILES; i++) {
+ fn = fn_tohead (&fncache[i]);
+ fn->lname[0] = EOS;
+ fn->chksum = -1;
+ fn->nrefs = 0;
+ }
+}
+
+
+/* FN_TOHEAD -- Link a sysfile struct at the head of the list.
+ */
+struct _sysfile *
+fn_tohead (register struct _sysfile *fn)
+{
+ if (fn != fn_head) {
+ fn->uplnk = NULL;
+ fn->dnlnk = fn_head;
+ fn_head->uplnk = fn;
+ fn_head = fn;
+ }
+
+ return (fn);
+}
+
+
+/* FN_TOTAIL -- Link a sysfile struct at the tail of the list.
+ */
+struct _sysfile *
+fn_totail (register struct _sysfile *fn)
+{
+ if (fn != fn_tail) {
+ fn->uplnk = fn_tail;
+ fn->dnlnk = NULL;
+ fn_tail->dnlnk = fn;
+ fn_tail = fn;
+ }
+
+ return (fn);
+}
+
+
+/* FN_UNLINK -- Unlink an sysfile struct.
+ */
+struct _sysfile *
+fn_unlink (register struct _sysfile *fn)
+{
+ if (fn == fn_head)
+ fn_head = fn->dnlnk;
+ if (fn == fn_tail)
+ fn_tail = fn->uplnk;
+
+ if (fn->uplnk)
+ fn->uplnk->dnlnk = fn->dnlnk;
+ if (fn->dnlnk)
+ fn->dnlnk->uplnk = fn->uplnk;
+
+ return (fn);
+}
+
+
+/* FN_CHKSUM -- Compute the checksum of a character string.
+ */
+int
+fn_chksum (char *s)
+{
+ register int sum=0;
+
+ while (*s)
+ sum += *s++;
+
+ return (sum);
+}
+
+
+/* FN_STRNCPY -- Copy up to maxch characters from a string and return the
+ * number of characters copied as the function value.
+ */
+int
+fn_strncpy (
+ char *out,
+ char *in,
+ int maxch
+)
+{
+ register char *ip, *op;
+ register int n;
+
+ for (ip=in, op=out, n=maxch; --n >= 0 && (*op++ = *ip++); )
+ ;
+ return (op-1 - out);
+}
diff --git a/unix/boot/mkpkg/host.c b/unix/boot/mkpkg/host.c
new file mode 100644
index 00000000..2f7c140b
--- /dev/null
+++ b/unix/boot/mkpkg/host.c
@@ -0,0 +1,917 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#define import_spp
+#define import_error
+#include <iraf.h>
+#include "mkpkg.h"
+#include "extern.h"
+#include "../bootProto.h"
+
+#ifdef LINUX
+# undef SYSV
+# undef i386
+# define GNUAR
+#else
+# ifdef BSD
+# undef SYSV
+# endif
+#endif
+
+/*
+ * HOST.C -- [MACHDEP] Special host interface routines required by the MKPKG
+ * utility.
+ */
+
+#define SZ_COPYBUF 4096
+#ifndef SZ_CMD
+#define SZ_CMD 2048 /* max size OS command, see mkpkg.h */
+#endif
+#define SZ_LIBPATH 512 /* path to library */
+#define LIBRARIAN "ar"
+#define LIBTOOL "libtool"
+#define LIBFLAGS "r"
+#define REBUILD "ranlib"
+#define XC "xc"
+#define INTERRUPT SYS_XINT
+
+extern char *makeobj();
+extern char *vfn2osfn();
+extern char *getenv();
+
+extern void fatals (char *fmt, char *arg);
+
+char *resolvefname();
+char *mkpath();
+
+int h_updatelibrary (char *library, char *flist[], int totfiles,
+ char *xflags, char *irafdir);
+int h_rebuildlibrary (char *library);
+int h_incheck (char *file, char *dir);
+int h_outcheck (char *file, char *dir, int clobber);
+void h_getlibname (char *file, char *fname);
+int h_xc (char *cmd);
+int h_purge (char *dir);
+int h_copyfile (char *oldfile, char *newfile);
+
+int u_fcopy (char *old, char *new);
+int h_movefile (char *old, char *new);
+int u_fmove (char *old, char *new );
+int add_sources (char *cmd, int maxch, char *flist[],
+ int totfiles, int hostnames, int *nsources);
+int add_objects (char *cmd, int maxch, char *flist[],
+ int totfiles, int hostnames);
+
+char *makeobj (char *fname);
+char *mkpath (char *module, char *directory, char *outstr);
+char *resolvefname (char *fname);
+int h_direq (char *dir1, char *dir2);
+
+
+
+/* H_UPDATELIBRARY -- Compile a list of source files and replace them in the
+ * host library. This is done by formatting a command for the XC compiler
+ * and passing it to the host system. Since XC is pretty much the same on
+ * all systems, this should be close to portable. Note that when we are
+ * called we are not necessarily in the same directory as the library, but
+ * we are always in the same directory as the files in the file list.
+ * Note also that the file list may contain object files which cannot be
+ * compiled, but which must be replaced in the library.
+ */
+int
+h_updatelibrary (
+ char *library, /* pathname of library */
+ char *flist[], /* pointers to filename strings */
+ int totfiles, /* number of files in list */
+ char *xflags, /* XC compiler flags */
+ char *irafdir /* iraf root directory */
+)
+{
+ char cmd[SZ_CMD+1], *args;
+ int exit_status, baderr, npass;
+ int nsources, nfiles, ndone, nleft;
+ int hostnames, status;
+ char libfname[SZ_PATHNAME+1];
+ char *lname = NULL;
+
+ /* Get the library file name. */
+ h_getlibname (library, libfname);
+ lname = resolvefname(libfname);
+
+ /*
+ * Compile the files.
+ * -------------------
+ */
+ if (irafdir[0])
+ sprintf (cmd, "%s -r %s %s", XC, irafdir, xflags);
+ else
+ sprintf (cmd, "%s %s", XC, xflags);
+
+ if (debug)
+ strcat (cmd, " -d");
+ if (dbgout)
+ strcat (cmd, " -x");
+
+ /* Compute offset to the file list and initialize loop variables.
+ * Since the maximum command length is limited, only a few files
+ * are typically processed in each iteration.
+ */
+ exit_status = OK;
+ baderr = NO;
+ args = &cmd[strlen(cmd)];
+ nleft = totfiles;
+ ndone = 0;
+
+ while (nleft > 0) {
+ /* Add as many filenames as will fit on the command line.
+ */
+ nfiles = add_sources (cmd, SZ_CMD, &flist[ndone], nleft,
+ hostnames=NO, &nsources);
+
+ /* This should not happen.
+ */
+ if (nfiles <= 0) {
+ printf ("OS command overflow; cannot compile files\n");
+ fflush (stdout);
+ exit_status = ERR;
+ return 0;
+ }
+
+ if (verbose) {
+ if (nsources > 0)
+ printf ("%s\n", cmd);
+ else
+ printf ("file list contains only object files\n");
+ fflush (stdout);
+ }
+
+ if (execute && nsources > 0)
+ if ((status = os_cmd (cmd)) != OK) {
+ if (status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", library);
+ if (!ignore)
+ baderr++;
+ exit_status += status;
+ }
+
+ /* Truncate command and repeat with the next few files.
+ */
+ (*args) = EOS;
+
+ ndone += nfiles;
+ nleft -= nfiles;
+ }
+
+ /* Do not update object modules in library if a compilation error
+ * occurred. The object files will be left on disk and the user
+ * will rerun us after fixing the problem; the next time around we
+ * will see that the objects exist and are up to date, hence will
+ * not recompile them. When all have been successfully compiled
+ * the library will be updated.
+ */
+ if (baderr)
+ return 0;
+
+ /*
+ * Update the library.
+ * ---------------------
+ */
+#if defined(LINUX) || defined(BSD) || defined(MACOSX)
+#if defined(MACOSX) && !defined(MACH64)
+ /* For FAT libraries we need to use libtool to update.
+ */
+ if (access (lname, F_OK) == 0)
+ sprintf (cmd, "%s %s %s %s", LIBTOOL, "-a -T -o", lname, lname);
+ else
+ sprintf (cmd, "%s %s %s ", LIBTOOL, "-a -T -o", lname);
+#else
+ sprintf (cmd, "%s %s %s", LIBRARIAN, LIBFLAGS, resolvefname(libfname));
+#endif
+#else
+ sprintf (cmd, "%s %s %s", LIBRARIAN, LIBFLAGS, libfname);
+#endif
+
+ /* Compute offset to the file list and initialize loop variables.
+ * Since the maximum command length is limited, only a few files
+ * are typically processed in each iteration.
+ */
+ args = &cmd[strlen(cmd)];
+ nleft = totfiles;
+ ndone = 0;
+
+ for (npass=0; nleft > 0; npass++) {
+
+#if defined(MACOSX) && !defined(MACH64)
+ if (npass > 0) {
+ /* For FAT libraries we need to use libtool to update.
+ */
+ if (access (lname, F_OK) == 0)
+ sprintf (cmd, "%s %s %s %s", LIBTOOL, "-a -T -o",
+ lname, lname);
+ else
+ sprintf (cmd, "%s %s %s ", LIBTOOL, "-a -T -o", lname);
+ }
+#endif
+
+ /* Add as many filenames as will fit on the command line. */
+ nfiles = add_objects (cmd, SZ_CMD, &flist[ndone], nleft,
+ hostnames=NO);
+
+ /* This should not happen. */
+ if (nfiles <= 0) {
+ printf ("OS command overflow; cannot update library `%s'\n",
+ libfname);
+ fflush (stdout);
+ exit_status = ERR;
+ return 0;
+ }
+
+ if (verbose) {
+ printf ("%s\n", cmd);
+ fflush (stdout);
+ }
+
+ if (execute) {
+ if ((exit_status = os_cmd (cmd)) == OK) {
+ /* Delete the object files.
+ */
+ int i;
+
+ for (i=0; i < nfiles; i++)
+ os_delete (makeobj (flist[ndone+i]));
+ } else if (exit_status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", library);
+ }
+
+ /* Truncate command and repeat with the next few files.
+ */
+ (*args) = EOS;
+
+ ndone += nfiles;
+ nleft -= nfiles;
+
+#if defined(MACOSX) && !defined(MACH64)
+ h_rebuildlibrary (lname);
+#endif
+ }
+
+ return (exit_status);
+}
+
+
+/* H_REBUILDLIBRARY -- Called after all recently recompiled modules have been
+ * replaced in the library. When we are called we are in the same directory
+ * as the library.
+ */
+int
+h_rebuildlibrary (
+ char *library /* filename of library */
+)
+{
+#ifdef SYSV
+ /* Skip the library rebuild if COFF format library. */
+ return (OK);
+#else
+ char cmd[SZ_LINE+1];
+ char libfname[SZ_PATHNAME+1];
+ char *libpath;
+
+ /* Get the library file name. */
+ h_getlibname (library, libfname);
+ libpath = resolvefname (vfn2osfn(libfname,0));
+
+ sprintf (cmd, "%s %s", REBUILD, libpath);
+ if (verbose) {
+ printf ("%s\n", cmd);
+ fflush (stdout);
+ }
+
+ if (execute)
+ return (os_cmd (cmd));
+ else
+ return (OK);
+#endif
+}
+
+
+/* H_INCHECK -- Check a file, e.g., a library, back into the directory it
+ * was originally checked out from. If the directory name pointer is NULL
+ * merely delete the checked out copy of the file. On a UNIX system the
+ * checked out file is a symbolic link, so all we do is delete the link.
+ * On a VMS system the checked out file is a copy, and we have to physically
+ * copy the new file back, creating a new version of the original file.
+ */
+int
+h_incheck (
+ char *file, /* file to be checked in */
+ char *dir /* where to put the file */
+)
+{
+ char backup[SZ_PATHNAME+1];
+ char path[SZ_PATHNAME+1];
+ char fname[SZ_PATHNAME+1];
+ char *osfn, *ip;
+ struct stat fi;
+ int status;
+
+ /* Get the library file name. */
+ h_getlibname (file, fname);
+ osfn = vfn2osfn (fname, 0);
+
+ if (verbose) {
+ printf ("check file `%s' into `%s'\n", fname, dir ? dir : "");
+ fflush (stdout);
+ }
+
+ if (stat (osfn, &fi) == ERR) {
+ printf ("$checkin: file `%s' not found\n", osfn);
+ fflush (stdout);
+ return (ERR);
+ }
+
+ /* If the file is not a symbolic link to an existing remote file it
+ * is probably a new library, so move it to the destination directory,
+ * otherwise just delete the link. If the named file exists in
+ * IRAFULIB update that version of the file instead of the standard one.
+ */
+ if (dir != NULL && !(fi.st_mode & S_IFLNK)) {
+ path[0] = EOS;
+ if ((ip = getenv("IRAFULIB")))
+ if (access (mkpath(fname,ip,path), 0) < 0)
+ path[0] = EOS;
+
+ if (path[0] == EOS)
+ status = h_movefile (osfn, mkpath(fname,dir,path));
+ else
+ status = h_movefile (osfn, path);
+
+ } else
+ status = unlink (osfn);
+
+ /* If there was a local copy of the file it will have been renamed
+ * with a .cko extension when the file was checked out, and should be
+ * restored.
+ */
+ sprintf (backup, "%s.cko", fname);
+ if (access (backup, 0) == 0) {
+ if (debug) {
+ printf ("h_incheck: rename %s -> %s\n", backup, fname);
+ fflush (stdout);
+ }
+ if (rename (backup, fname) == -1)
+ printf ("cannot rename %s -> %s\n", backup, fname);
+ }
+
+ return (status);
+}
+
+
+/* H_OUTCHECK -- Check out a file, e.g., gain access to a library in the
+ * current directory so that it can be updated. If the file has already
+ * been checked out do not check it out again. In principle we should also
+ * place some sort of a lock on the file while it is checked out, but...
+ */
+int
+h_outcheck (
+ char *file, /* file to be checked out */
+ char *dir, /* where to get the file */
+ int clobber /* clobber existing copy of file? */
+)
+{
+ register char *ip, *op;
+ char path[SZ_PATHNAME+1];
+ char fname[SZ_PATHNAME+1];
+
+ /* Get the library file name. */
+ h_getlibname (file, fname);
+
+ /* Make the UNIX pathname of the destination file. [MACHDEP]
+ * Use the IRAFULIB version of the file if there is one.
+ */
+ path[0] = EOS;
+ if ((ip = getenv("IRAFULIB")))
+ if (access (mkpath(fname,ip,path), 0) < 0)
+ path[0] = EOS;
+
+ if (path[0] == EOS) {
+ for (ip=vfn2osfn(dir,0), op=path; (*op = *ip++); op++)
+ ;
+ if (*(op-1) != '/')
+ *op++ = '/';
+ for (ip=vfn2osfn(fname,0); (*op = *ip++); op++)
+ ;
+ *op = EOS;
+ }
+
+ if (verbose) {
+ printf ("check out file `%s = %s'\n", fname, path);
+ fflush (stdout);
+ }
+
+ /* If the file already exists and clobber is enabled, delete it.
+ * If the file is a symbolic link (a pathname), and IRAF has been
+ * moved since the link was created, then the symlink will be
+ * pointing off into never never land and must be redone. If clobber
+ * is NOT enabled, then probably the remote copy of the file is an
+ * alternate source for the local file, which must be preserved.
+ */
+ if (access (fname, 0) != -1) {
+ char backup[SZ_PATHNAME+1];
+
+ if (clobber) {
+ if (debug) {
+ printf ("h_outcheck: deleting %s\n", fname);
+ fflush (stdout);
+ }
+ unlink (fname);
+ } else {
+ /* Do not rename the file twice; if the .cko file already
+ * exists, the second time would clobber it. Note that if a
+ * mkpkg run is aborted, the checked out file and renamed
+ * local file will remain, but a subsequent successful mkpkg
+ * will restore everything.
+ */
+ sprintf (backup, "%s.cko", fname);
+ if (access (backup, 0) == -1) {
+ if (debug) {
+ printf ("h_outcheck: rename %s -> %s\n", fname, backup);
+ fflush (stdout);
+ }
+ if (rename (fname, backup) == -1)
+ printf ("cannot rename %s -> %s\n", fname, backup);
+ }
+ }
+ }
+
+ return (symlink (path, fname));
+}
+
+
+/* H_GETLIBNAME -- Get a library filename. If debug output is enabled (-g
+ * or -x), and we are checking out a library file (.a), update the debug
+ * version of the library (XX_p.a).
+ */
+void
+h_getlibname (
+ char *file,
+ char *fname
+)
+{
+ register char *ip;
+
+ strcpy (fname, file);
+ if (dbgout) {
+ for (ip=fname; *ip; ip++)
+ ;
+ if (*(ip-2) == '.' && *(ip-1) == 'a' &&
+ !(*(ip-4) == '_' && *(ip-3) == 'p')) {
+ *(ip-2) = '_';
+ *(ip-1) = 'p';
+ *(ip-0) = '.';
+ *(ip+1) = 'a';
+ *(ip+2) = '\0';
+ }
+ }
+}
+
+
+/* H_XC -- Host interface to the XC compiler. On UNIX all we do is use the
+ * oscmd facility to pass the XC command line on to UNIX.
+ */
+int
+h_xc (char *cmd)
+{
+ return (os_cmd (cmd));
+}
+
+
+/* H_PURGE -- Purge all old versions of all files in the named directory.
+ * This is a no-op on UNIX since multiple file versions are not supported.
+ */
+int
+h_purge (
+ char *dir /* LOGICAL directory name */
+)
+{
+ if (verbose) {
+ printf ("purge directory `%s'\n", dir);
+ fflush (stdout);
+ }
+
+ /*
+ * format command "purge [dir]*.*;*"
+ * if (verbose)
+ * echo command to stdout
+ * if (execute)
+ * call os_cmd to execute purge command
+ */
+
+ return (OK);
+}
+
+
+/* H_COPYFILE -- Copy a file. If the new file already exists it is
+ * clobbered (updated).
+ */
+int
+h_copyfile (
+ char *oldfile, /* existing file to be copied */
+ char *newfile /* new file, not a directory name */
+)
+{
+ char old[SZ_PATHNAME+1];
+ char new[SZ_PATHNAME+1];
+
+ strcpy (old, vfn2osfn (oldfile, 0));
+ strcpy (new, vfn2osfn (newfile, 1));
+
+ if (verbose) {
+ printf ("copy %s to %s\n", old, new);
+ fflush (stdout);
+ }
+
+ if (execute) {
+ if (os_access (old, 0,0) == NO) {
+ printf ("$copy: file `%s' not found\n", oldfile);
+ fflush (stdout);
+ return (ERR);
+ } else
+ return (u_fcopy (old, new));
+ }
+
+ return (OK);
+}
+
+
+/* U_FCOPY -- Copy a file, UNIX.
+ */
+int
+u_fcopy (
+ char *old,
+ char *new
+)
+{
+ char buf[SZ_COPYBUF], *ip;
+ int in, out, nbytes;
+ struct stat fi;
+ long totbytes;
+
+ /* Open the old file and create the new one with the same mode bits
+ * as the original.
+ */
+ if ((in = open(old,0)) == ERR || fstat(in,&fi) == ERR) {
+ printf ("$copy: cannot open input file `%s'\n", old);
+ fflush (stdout);
+ return (ERR);
+ } if ((out = creat(new,0644)) == ERR || fchmod(out,fi.st_mode) == ERR) {
+ printf ("$copy: cannot create output file `%s'\n", new);
+ fflush (stdout);
+ close (in);
+ return (ERR);
+ }
+
+ /* Copy the file.
+ */
+ totbytes = 0;
+ while ((nbytes = read (in, buf, SZ_COPYBUF)) > 0)
+ if (write (out, buf, nbytes) == ERR) {
+ close (in); close (out);
+ printf ("$copy: file write error on `%s'\n", new);
+ fflush (stdout);
+ return (ERR);
+ } else
+ totbytes += nbytes;
+
+ close (in);
+ close (out);
+
+ /* Check for premature termination of the copy.
+ */
+ if (totbytes != fi.st_size) {
+ printf ("$copy: file changed size `%s' oldsize=%d, newsize=%d\n",
+ old, (int)fi.st_size, (int)totbytes);
+ fflush (stdout);
+ return (ERR);
+ }
+
+ /* If file is a library (".a" extension in UNIX), preserve the
+ * modify date else UNIX will think the library symbol table is
+ * out of date.
+ */
+ for (ip=old; *ip; ip++)
+ ;
+ ip -= 2;
+ if (ip > old && strcmp (ip, ".a") == 0) {
+ struct timeval tv[2];
+
+ tv[0].tv_sec = fi.st_atime;
+ tv[1].tv_sec = fi.st_mtime;
+ utimes (new, tv);
+ }
+
+ return (OK);
+}
+
+
+/* H_MOVEFILE -- Move a file from the current directory to another directory,
+ * or rename the file within the current directory. If the destination file
+ * already exists it is clobbered.
+ */
+int
+h_movefile (
+ char *old, /* file to be moved */
+ char *new /* new pathname of file */
+)
+{
+ char old_osfn[SZ_PATHNAME+1];
+ char new_osfn[SZ_PATHNAME+1];
+
+ strcpy (old_osfn, vfn2osfn (old, 0));
+ strcpy (new_osfn, vfn2osfn (new, 0));
+
+ if (debug) {
+ printf ("move %s to %s\n", old_osfn, new_osfn);
+ fflush (stdout);
+ }
+
+ if (execute) {
+ if (os_access (old_osfn, 0,0) == NO) {
+ printf ("$move: file `%s' not found\n", old);
+ fflush (stdout);
+ return (ERR);
+ } else
+ return (u_fmove (old_osfn, new_osfn));
+ }
+
+ return (OK);
+}
+
+
+/* U_FMOVE -- Unix procedure to move or rename a file. Will move file to a
+ * different device (via a file copy) if necessary.
+ */
+int
+u_fmove (
+ char *old,
+ char *new
+)
+{
+ unlink (new);
+ if (link (old, new) == ERR)
+ if (u_fcopy (old, new) == ERR) {
+ printf ("$move: cannot create `%s'\n", new);
+ fflush (stdout);
+ return (ERR);
+ }
+
+ if (unlink (old) == ERR) {
+ printf ("$move: cannot unlink `%s'\n", old);
+ fflush (stdout);
+ return (ERR);
+ }
+
+ return (OK);
+}
+
+
+/* ADD_SOURCES -- Append source files from the file list to the command
+ * buffer. Omit object files. Return a count of the number of files to
+ * be compiled. This code is machine dependent since Unix permits arbitrarily
+ * long command lines, but most systems do not, in which case something
+ * else must be done (e.g., write a command file and have the host system
+ * process that).
+ */
+int
+add_sources (
+ char *cmd, /* concatenate to this */
+ int maxch, /* max chars out */
+ char *flist[], /* pointers to filename strings */
+ int totfiles, /* number of files in list */
+ int hostnames, /* return host filenames? */
+ int *nsources /* receives number of src files */
+)
+{
+ register char *ip, *op, *otop;
+ register int i;
+ int nfiles;
+
+ *nsources = 0;
+ nfiles = 0;
+
+ otop = &cmd[maxch];
+ for (op=cmd; *op; op++)
+ ;
+
+ for (i=0; i < totfiles; i++) {
+ /* Skip over object files.
+ */
+ for (ip=flist[i]; *ip; ip++)
+ ;
+ if (strcmp (ip-2, ".o") == 0) {
+ nfiles++;
+ continue;
+ }
+
+ if (op + strlen (flist[i]) + 1 >= otop)
+ break;
+
+ nfiles++;
+ (*nsources)++;
+ *op++ = ' ';
+
+ if (hostnames)
+ ip = vfn2osfn (flist[i], 0);
+ else
+ ip = flist[i];
+
+ for (; (*op = *ip++); op++)
+ ;
+ }
+
+ return (nfiles);
+}
+
+
+/* ADD_OBJECTS -- Append the ".o" equivalent of each file name to the
+ * output command buffer. Return the number of file names appended.
+ */
+int
+add_objects (
+ char *cmd, /* concatenate to this */
+ int maxch, /* max chars out */
+ char *flist[], /* pointers to filename strings */
+ int totfiles, /* number of files in list */
+ int hostnames /* return host filenames? */
+)
+{
+ register char *ip, *op, *otop;
+ register int i;
+ int nfiles;
+
+ otop = &cmd[maxch];
+ for (op=cmd; *op; op++)
+ ;
+
+ for (i=0, nfiles=0; i < totfiles; i++) {
+ if (op + strlen (flist[i]) + 1 >= otop)
+ break;
+
+ nfiles++;
+ *op++ = ' ';
+
+ ip = makeobj (flist[i]);
+ if (hostnames)
+ ip = vfn2osfn (ip,0);
+
+ for (; (*op = *ip++); op++)
+ ;
+ }
+
+ return (nfiles);
+}
+
+
+/* MAKEOBJ -- Return a pointer to the ".o" equivalent of the input file
+ * name. The last period in the input filename is assumed to delimit the
+ * filename extension.
+ */
+char *
+makeobj (char *fname)
+{
+ register char *ip, *op;
+ static char objfile[SZ_FNAME+1];
+ char *lastdot;
+
+ for (ip=fname, op=objfile, lastdot=NULL; (*op = *ip++); op++)
+ if (*op == '.')
+ lastdot = op;
+
+ if (lastdot != NULL)
+ op = lastdot;
+ strcpy (op, ".o");
+
+ return (objfile);
+}
+
+
+/* MKPATH -- Given a module name and a directory name, return the pathname of
+ * the module in the output string. Do not use the directory pathname if the
+ * module name is already a pathname.
+ */
+char *
+mkpath (
+ char *module,
+ char *directory,
+ char *outstr
+)
+{
+ register char *ip, *op;
+
+ if (directory && module[0] != '/') {
+ for (ip=directory, op=outstr; (*op = *ip++); op++)
+ ;
+ if (op > outstr && *(op-1) != '/') {
+ *op++ = '/';
+ *op = EOS;
+ }
+ for (ip=module; (*op = *ip++); op++)
+ ;
+ } else
+ strcpy (outstr, module);
+
+ return (outstr);
+}
+
+
+/* RESOLVEFNAME -- If a filename reference is a symbolic link resolve it to
+ * the pathname of an actual file by tracing back through all symbolic links
+ * to the fully resolved file or path.
+ *
+ * Example:
+ *
+ * ./libsys.a -> /iraf/iraf/lib/libsys.a
+ * /iraf/iraf/lib/libsys.a -> ../bin/libsys.a
+ * -> /iraf/iraf/bin/libsys.a
+ *
+ * Note that the "fully resolved" filename may still contain unresolved links
+ * for directory elements - it is only the filename which is fully resolved
+ * in the output pathname.
+ */
+char *
+resolvefname (char *fname)
+{
+ static char pathname[SZ_LIBPATH];
+ char relpath[SZ_LIBPATH];
+ extern char *strrchr();
+
+ strcpy (pathname, fname);
+ while (os_symlink (pathname, relpath, SZ_LIBPATH)) {
+ if (relpath[0] == '/') {
+ /* Link to an absolute pathname, just use new path. */
+ strcpy (pathname, relpath);
+ } else {
+ /* Relative path. This includes upwards references such
+ * as ../foo. Replace the filename by the relative path.
+ * Let unix resolve any upwards references later, when the
+ * file is accessed.
+ */
+ char *str = strrchr(pathname,'/');
+ strcpy ((str ? (str+1) : pathname), relpath);
+ }
+ }
+
+ return (pathname);
+}
+
+
+/* H_DIREQ -- Compare two directory pathnames for equality. This is easy
+ * in most cases, but the comparison can fail when it shouldn't due to aliases
+ * for directory names, e.g., a directory may be referred to by a symbolic
+ * name, but get-cwd will return a different path, causing the comparison to
+ * fail.
+ */
+int
+h_direq (char *dir1, char *dir2)
+{
+ register char *ip1, *ip2;
+
+ /* If the pathname contains a directory named "irafXXX" (where the
+ * XXX are optional characters in the directory name) everything to
+ * the left for the purposes of this comparision. This allows the
+ * iraf root directory to be specified with a path such as
+ *
+ * /<whatever>/iraf/iraf.version/
+ *
+ * and the directory name comparision will take place using only
+ * the portion of the path following this prefix.
+ */
+ for (ip1=dir1; *ip1; ip1++)
+ if (*ip1 == '/' && *(ip1+1) == 'i')
+ if (strncmp (ip1+1, "iraf", 4) == 0) {
+ for (ip1++; *ip1 && *ip1 != '/'; ip1++)
+ ;
+ if (*ip1 == '/')
+ dir1 = ip1 + 1;
+ --ip1;
+ }
+ for (ip2=dir2; *ip2; ip2++)
+ if (*ip2 == '/' && *(ip2+1) == 'i')
+ if (strncmp (ip2+1, "iraf", 4) == 0) {
+ for (ip2++; *ip2 && *ip2 != '/'; ip2++)
+ ;
+ if (*ip2 == '/')
+ dir2 = ip2 + 1;
+ --ip2;
+ }
+
+ return (strcmp (dir1, dir2) == 0);
+}
diff --git a/unix/boot/mkpkg/main.c b/unix/boot/mkpkg/main.c
new file mode 100644
index 00000000..eb2cb5c3
--- /dev/null
+++ b/unix/boot/mkpkg/main.c
@@ -0,0 +1,347 @@
+/* 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_knames
+#define import_error
+
+#include <iraf.h>
+
+#include "mkpkg.h"
+#include "../bootProto.h"
+
+/*
+ * MKPKG -- Make a package or library, following the instructions given in
+ * the mkpkg file in the current directory.
+ *
+ * mkpkg [-flags] [module] [sym=val ...]
+ *
+ * -dddd output debug info; up to 4 levels
+ * -i ignore errors (cannot ignore interrupt)
+ * -f fname set mkpkg filename; default "mkpkg"
+ * -n no execute, just go through the motions
+ * -p pkg load environment for the named package
+ * -u forcibly update library module dates
+ * -v verbose: show actions (implied by -n)
+ *
+ * The switch "-f stdin" causes MKPKG to read its commands from the standard
+ * input, e.g., the terminal. If a module name is given execution will start
+ * at the mkpkg entry for the module, else execution starts at the beginning
+ * of file. See the manual page, etc. for additional documentation.
+ */
+
+char sbuf[SZ_SBUF]; /* string buffer */
+struct symbol symtab[MAX_SYMBOLS]; /* symbol table (macros) */
+struct context *topcx; /* currently active context */
+char *cp = sbuf; /* pointer into sbuf */
+char *ctop = &sbuf[SZ_SBUF]; /* top of sbuf */
+int npkg = 0; /* number of packages */
+char *pkgenv[MAX_PKGENV]; /* package environments */
+char v_pkgenv[SZ_PKGENV+1]; /* buffer for pkgenv names */
+char irafdir[SZ_PATHNAME+1]; /* iraf root directory */
+int nsymbols = 0; /* number of defined symbols */
+int ifstate[SZ_IFSTACK]; /* $IF stack */
+int iflev; /* $IF stack pointer */
+int debug = 0; /* print debug messages */
+int dbgout = 0; /* compile for debugging */
+int verbose = NO; /* print informative messages */
+int ignore = YES; /* ignore warns */
+int execute = YES; /* think but don't act? */
+int exit_status; /* exit status of last syscall */
+int forceupdate = NO; /* forcibly update libmod dates */
+extern char *os_getenv();
+
+
+void warns (char *fmt, char *arg);
+void fatals (char *fmt, char *arg);
+
+extern int ZZSTRT (void);
+extern int ZZSTOP (void);
+
+extern int do_mkpkg (struct context *cx, int islib);
+
+
+
+void zzpause () { printf ("ready ...."); (void) getc(stdin); }
+
+
+/* MAIN -- Entry point of mkpkg.e
+ */
+int
+main (int argc, char *argv[])
+{
+ struct context *cx;
+ char flags[SZ_LINE+1];
+ char *symargs[MAX_ARGS], *modules[MAX_ARGS];
+ int islib, nsymargs=0, nmodules=0, i;
+ char **argp, *ip, *op;
+
+ ZZSTRT();
+
+ /* Initialize the MKPKG context.
+ */
+ irafdir[0] = EOS;
+ topcx = cx = (struct context *) calloc (1, sizeof (struct context));
+ if (cx == NULL)
+ fatals ("out of memory (%s)", "mkpkg.e");
+
+ strcpy (cx->mkpkgfile, MKPKGFILE);
+ os_fpathname ("", cx->dirpath, SZ_PATHNAME);
+ m_fninit (0);
+ m_fdinit (0);
+
+ exit_status = OK;
+ ifstate[0] = PASS;
+ iflev = 0;
+ flags[0] = EOS;
+ islib = YES;
+ npkg = 0;
+
+ /* Process the command line.
+ */
+ for (argp = &argv[1]; *argp; ) {
+ if (**argp == '-') {
+ /* A Mkpkg switch, or a flag to be passed on to XC.
+ */
+ for (ip = *argp++ + 1; *ip; ip++) {
+ switch (*ip) {
+ case 'f':
+ if (*argp == NULL)
+ warns ("missing argument to switch `-f'", NULL);
+ else
+ strcpy (cx->mkpkgfile, *argp++);
+ break;
+ case 'i':
+ ignore = YES;
+ break;
+ case 'd':
+ /* There are multiple levels of "debug"; each
+ * -d in the arg list adds a level.
+ */
+ debug++;
+ verbose = YES;
+ break;
+ case 'x':
+ case 'g':
+ dbgout++;
+ goto addflag;
+ case 'n':
+ execute = NO;
+ verbose = YES;
+ break;
+ case 'p':
+ if (*argp == NULL)
+ warns ("missing argument to switch `-p'", NULL);
+ else {
+ pkgenv[npkg] = *argp++;
+ loadpkgenv (pkgenv[npkg]);
+ if (npkg++ >= MAX_PKGENV)
+ fatals ("too many -p package arguments", NULL);
+ }
+ break;
+ case 'u':
+ forceupdate = YES;
+ break;
+ case 'v':
+ verbose = YES;
+ break;
+ case 'w':
+ zzpause();
+ break;
+ case 'r':
+ if (*argp == NULL)
+ warns ("missing argument to switch `-r'", NULL);
+ else
+ strcpy (irafdir, *argp++);
+ break;
+ default:
+addflag: for (op=flags; *op; op++)
+ ;
+ *op++ = ' ';
+ *op++ = '-';
+ *op++ = *ip;
+ *op++ = EOS;
+ break;
+ }
+ }
+
+ } else if (index (*argp, '=') != NULL) {
+ /* Mark the position of a symbol definition argument. Wait
+ * to enter this into the symbol table until after the command
+ * line has been processed and the mkpkg global include file
+ * has been read in, but go ahead and update the environment
+ * in case a logical name is affected which is referenced while
+ * processing the rest of the argument list.
+ */
+ char symbol[SZ_FNAME+1];
+ char *ip, *op;
+
+ ip = symargs[nsymargs++] = *argp++;
+ for (op=symbol; (*op = *ip++) != '='; op++)
+ ;
+ *op = EOS;
+ os_putenv (symbol, ip);
+
+ } else {
+ /* The name of a module to be processed.
+ */
+ modules[nmodules++] = *argp++;
+ }
+ }
+
+ if (debug) {
+ printf ("mkpkg");
+ for (argp = &argv[1]; *argp; argp++)
+ printf (" %s", *argp);
+ printf ("\n");
+ fflush (stdout);
+ }
+
+ /* Initialize the package environment. This has already been done
+ * if any -p pkgname arguments were given on the command line,
+ * otherwise look for the name PKGENV in the user's environment.
+ */
+ if (npkg <= 0)
+ if ((pkgenv[0] = os_getenv (PKGENV))) {
+ char *ip;
+
+ strcpy (v_pkgenv, pkgenv[0]);
+ for (ip=v_pkgenv; *ip; ) {
+ while (isspace (*ip))
+ ip++;
+ pkgenv[npkg] = ip;
+ while (*ip && !isspace (*ip))
+ ip++;
+ *ip++ = EOS;
+ loadpkgenv (pkgenv[npkg]);
+ if (npkg++ >= MAX_PKGENV)
+ fatals ("too many -p package arguments", NULL);
+ }
+ }
+
+ /* Initialize the symbol table from the system dependent global
+ * MKPKG include file.
+ */
+ do_include (cx, MKPKGINC);
+
+ /* Likewise load the package global mkpkg.inc files for each
+ * reference package.
+ */
+ if (npkg > 0) {
+ char fname[SZ_PATHNAME+1];
+ int i;
+
+ for (i=0; i < npkg; i++) {
+ sprintf (fname, "%s$lib/mkpkg.inc", pkgenv[i]);
+ do_include (cx, fname);
+ }
+ }
+
+ /* Append any flags given on the command line to XFLAGS.
+ */
+ if (flags[0]) {
+ char new_xflags[SZ_LINE+1];
+ sprintf (new_xflags, "%s %s", getsym(XFLAGS), flags);
+ putsym (XFLAGS, new_xflags);
+ }
+
+ /* Append any flags given on the command line to XVFLAGS.
+ */
+ if (flags[0]) {
+ char new_xvflags[SZ_LINE+1];
+ sprintf (new_xvflags, "%s %s", getsym(XVFLAGS), flags);
+ putsym (XVFLAGS, new_xvflags);
+ }
+
+ /* Append any flags given on the command line to LFLAGS.
+ */
+ if (flags[0]) {
+ char new_lflags[SZ_LINE+1];
+ sprintf (new_lflags, "%s %s", getsym(LFLAGS), flags);
+ putsym (LFLAGS, new_lflags);
+ }
+
+ /* Define the symbol "DEBUG" if building for debugging (-x).
+ */
+ if (dbgout)
+ putsym (DEBUGSYM, "1");
+
+ /* Enter any symbols or macros defined on the command line into the
+ * symbol table and environment. Must be given without embedded
+ * whitespace, e.g., "symbol=value".
+ */
+ for (i=0; i < nsymargs; i++) {
+ char symbol[SZ_FNAME+1];
+ char *ip, *op, *value;
+
+ for (ip = symargs[i], op=symbol; (*op = *ip++) != '='; op++)
+ ;
+ *op = EOS;
+ value = ip;
+ putsym (symbol, value);
+ os_putenv (symbol, value);
+ }
+
+ /* Process the named modules (or the first module in the mkpkg file
+ * if no modules were named.
+ */
+ if (nmodules == 0) {
+ cx->library[0] = EOS;
+ exit_status = do_mkpkg (cx, islib = 0);
+ } else {
+ for (i=0; i < nmodules; i++) {
+ /* If the module is a library specification, the module name,
+ * which is the filename of the library, must end in ".a".
+ */
+ char *ip, *op;
+ for (ip = modules[i], op=cx->library; (*op = *ip++); op++)
+ ;
+ islib = (strcmp (op - 2, ".a") == 0);
+ exit_status += do_mkpkg (cx, islib);
+ }
+ }
+
+ free (cx);
+ m_fninit (debug);
+ m_fdinit (debug);
+
+ ZZSTOP();
+ exit (exit_status == OK ? OSOK : exit_status);
+}
+
+
+/* WARNS -- Print error message with one string argument but do not terminate
+ * program execution.
+ */
+void
+warns (char *fmt, char *arg)
+{
+ char errmsg[SZ_LINE+1];
+
+ sprintf (errmsg, fmt, arg);
+ printf ("Warning, %s line %d: %s\n", topcx->mkpkgfile, topcx->lineno,
+ errmsg);
+ fflush (stdout);
+}
+
+
+/* FATALS -- Print error message with one string argument and terminate
+ * program execution.
+ */
+void
+fatals (char *fmt, char *arg)
+{
+ char errmsg[SZ_LINE+1];
+
+ sprintf (errmsg, fmt, arg);
+ printf ("Fatal error, %s line %d: %s\n", topcx->mkpkgfile,
+ topcx->lineno, errmsg);
+ fflush (stdout);
+ exit (OSOK+1);
+}
diff --git a/unix/boot/mkpkg/mkpkg b/unix/boot/mkpkg/mkpkg
new file mode 100644
index 00000000..d842357d
--- /dev/null
+++ b/unix/boot/mkpkg/mkpkg
@@ -0,0 +1,33 @@
+# Make the MKPKG utility [MACHDEP].
+
+$call relink
+$exit
+
+update:
+ $call relink
+ $call install
+ ;
+
+relink:
+ $set LIBS = "$(HSI_LIBS)"
+ $set XFLAGS = "-c $(HSI_XF)"
+
+ $update libpkg.a
+ $omake main.c mkpkg.h <libc/error.h>
+ !$(CC) $(HSI_LF) main.o libpkg.a $(LIBS) $(HSI_OSLIBS) -o mkpkg.e
+ ;
+
+install:
+ $move mkpkg.e $(hlib)
+ ;
+
+libpkg.a:
+ char.c extern.h mkpkg.h <libc/error.h> <libc/spp.h>
+ fdcache.c
+ fncache.c
+ host.c <libc/error.h> <libc/spp.h> <libc/knames.h> <libc/spp.h>
+ pkg.c extern.h mkpkg.h <libc/error.h> <libc/spp.h>
+ scanlib.c <libc/spp.h>
+ sflist.c <libc/error.h> <libc/spp.h> mkpkg.h extern.h
+ tok.c extern.h mkpkg.h <libc/error.h> <libc/spp.h>
+ ;
diff --git a/unix/boot/mkpkg/mkpkg.h b/unix/boot/mkpkg/mkpkg.h
new file mode 100644
index 00000000..9b8073d7
--- /dev/null
+++ b/unix/boot/mkpkg/mkpkg.h
@@ -0,0 +1,254 @@
+/* MKPKG.H -- Global definitions for MKPKG.
+ */
+
+#define SZ_SBUF 10240 /* string buffer size (fixed) */
+#define SZ_PBSTK 50 /* push back stack */
+#define SZ_PBBUF 2048 /* push back buffer */
+#define SZ_CMD 2048 /* buf for os escape */
+#define SZ_IFSTACK 50 /* max $IF nesting */
+#define SZ_PREDBUF 1024 /* largest $IF predicate */
+#define SZ_PKGENV 256 /* pkgenv package list buffer */
+#define MAX_ARGS 50 /* max args to a $IF */
+#define MAX_FILES 512 /* max files in a module list */
+#define MAX_LIBFILES 8192 /* max files in a library index */
+#define MAX_DEPFILES 100 /* max dependency files */
+#define MAX_SYMBOLS 256 /* max macros */
+#define MAX_SFDIRS 128 /* max dirs containing special files */
+#define MAX_SFFILES 1024 /* max special files */
+#define MAX_PKGENV 20 /* max package environments */
+
+#define INTERRUPT SYS_XINT
+#define MKPKGFILE "mkpkg"
+#define MKPKGINC "hlib$mkpkg.inc"
+#define PKGENV "PKGENV"
+#define LFLAGS "lflags"
+#define XFLAGS "xflags"
+#define XVFLAGS "xvflags"
+#define DEBUGSYM "debug"
+#define XC "xc"
+#define GENERIC "generic"
+#define GFLAGS "gflags"
+#define BACK ".."
+
+#define BEGIN_CHAR ':'
+#define END_CHAR ';'
+#define SUBDIR_CHAR '@'
+#define COMMENT '#'
+#define PREPROCESSOR '$'
+#define SYSCMD '!'
+#define SYSFILE_BEGIN '<'
+#define SYSFILE_END '>'
+#define ESCAPE '\\'
+
+#define PASS 1
+#define STOP 0
+#define TOK_FNAME 1
+#define TOK_NEWLINE 2
+#define TOK_BEGIN 3
+#define TOK_END 4
+#define TOK_WHITESPACE 5
+
+/* Pushback structure, used to implement macro expansion.
+ */
+struct pushback {
+ char *ip; /* next char to return */
+ char *op; /* next avail char in buffer */
+ char *otop; /* top of buffer */
+ int npb; /* number of pushed ips */
+ char *pbstk[SZ_PBSTK]; /* save pushed ips */
+ char pbbuf[SZ_PBBUF+1]; /* push back buffer */
+};
+
+/* Mkpkg context descriptor.
+ */
+struct context {
+ FILE *fp; /* mkpkg file descriptor */
+ long fpos; /* saved file pointer */
+ struct pushback *pb; /* pushback descriptor */
+ int pbchar; /* single char pushback */
+ int pushback; /* flag that is pushback */
+ struct context *prev; /* previous mkpkg context */
+ int totfiles; /* total library files updated */
+ int nfiles; /* nfiles last updated */
+ int nrfiles; /* nrfiles last updated */
+ int lineno; /* lineno in mkpkg file */
+ int level; /* subdirectory level */
+ int sublib; /* called from lib module list */
+ char *old_cp; /* old cp when pushing new ctx */
+ int old_nsymbols; /* old nsymbols */
+ int old_iflev; /* old IF stack pointer */
+ char *flist[MAX_FILES]; /* file list */
+ char *rflist[MAX_FILES]; /* remote file list */
+ char curdir[SZ_PATHNAME+1]; /* cwd for printed output */
+ char dirpath[SZ_PATHNAME+1]; /* os path of cwd */
+ char library[SZ_PATHNAME+1]; /* library being updated */
+ char libpath[SZ_PATHNAME+1]; /* pathname of library */
+ char mkpkgfile[SZ_FNAME+1]; /* mkpkg file being scanned */
+};
+
+/* Macros.
+ */
+struct symbol {
+ char *s_name; /* symbol name */
+ char *s_value; /* symbol value */
+};
+
+/* Special file list.
+ */
+struct sfile {
+ char *sf_stname; /* standard filename */
+ char *sf_sfname; /* special filename */
+ char *sf_mkobj; /* MKPKG command to make object */
+ struct sfile *sf_next; /* next file in directory */
+};
+
+
+/* External functions.
+ */
+struct sfile *sf_dirsearch(), *sf_filesearch();
+struct context *push_context();
+struct context *pop_context();
+char *vfn2osfn();
+char *os_getenv();
+char *mklower();
+char *getargs();
+char *makeobj();
+char *getsym();
+char *putstr();
+/*
+char *malloc();
+char *calloc();
+*/
+long os_fdate();
+long m_fdate();
+char *index();
+char *k_fgets();
+
+
+/*****************************************************************************/
+
+/* main.c */
+void warns (char *fmt, char *arg);
+void fatals (char *fmt, char *arg);
+
+
+/* char.c */
+int m_getc (register struct context *cx);
+int m_rawgetc (register struct context *cx);
+void m_ungetc (int ch, struct context *cx);
+void m_pushstr (struct context *cx, char *str);
+void mk_pbbuf (register struct context *cx);
+void pb_cancel (register struct context *cx);
+char *putstr (char *s);
+
+int k_getc (register struct context *cx);
+char *k_fgets (char *obuf, int maxch, register struct context *cx);
+int k_fseek (register struct context *cx, long offset, int type);
+long k_ftell (register struct context *cx);
+
+
+/* fdcache.c */
+long m_fdate (char *fname);
+void m_fdinit (int debug);
+int fd_chksum (char *s);
+
+
+/* fncache.c */
+int m_sysfile (char *lname, char *fname, int maxch);
+void m_fninit (int debug);
+int fn_chksum (char *s);
+int fn_strncpy (char *out, char *in, int maxch);
+
+
+/* host.c */
+int h_updatelibrary (char *library, char *flist[], int totfiles,
+ char *xflags, char *irafdir);
+int h_rebuildlibrary (char *library);
+int h_incheck (char *file, char *dir);
+int h_outcheck (char *file, char *dir, int clobber);
+void h_getlibname (char *file, char *fname);
+int h_xc (char *cmd);
+int h_purge (char *dir);
+int h_copyfile (char *oldfile, char *newfile);
+
+int u_fcopy (char *old, char *new);
+int h_movefile (char *old, char *new);
+int u_fmove (char *old, char *new );
+
+int add_sources (char *cmd, int maxch, char *flist[],
+ int totfiles, int hostnames, int *nsources);
+int add_objects (char *cmd, int maxch, char *flist[],
+ int totfiles, int hostnames);
+
+char *makeobj (char *fname);
+char *mkpath (char *module, char *directory, char *outstr);
+char *resolvefname (char *fname);
+int h_direq (char *dir1, char *dir2);
+
+
+/* pkg.c */
+int do_mkpkg (struct context *cx, int islib);
+int scan_modlist (struct context *cx, int islib);
+void parse_modname (char *modname, char *module, char *subdir, char *fname);
+void parse_fname (char *path, char *dname, char *fname);
+struct context *push_context (register struct context *cx, char *module,
+ char *newdir, char *fname);
+struct context *pop_context (register struct context *cx);
+void get_dependency_list (struct context *cx, char *module,
+ char *dflist[], int maxfiles);
+int up_to_date (struct context *cx, char *module, char *lname,
+ char *dflist[], int *useobj);
+int open_mkpkgfile (register struct context *cx);
+void close_mkpkgfile (register struct context *cx);
+struct context *find_mkpkgfile ( struct context *head_cx,
+ char *mkpkgfile, int level);
+int search_mkpkgfile (register struct context *cx);
+
+
+/* tok.c */
+int gettok (register struct context *cx, char *outstr, int maxch );
+
+void do_osescape (register struct context *cx);
+void do_ppdir (struct context *cx, char *token);
+void do_if (struct context *cx, char *keyword);
+void do_else (struct context *cx);
+void do_endif (struct context *cx);
+void do_end (struct context *cx);
+void do_call (struct context *cx, char *program, int islib);
+void do_echo (struct context *cx, char *msg);
+int do_goto (struct context *cx, char *symbol);
+int do_include (struct context *cx, char *fname);
+void do_omake (struct context *cx, char *fname);
+int do_xc (struct context *cx);
+int do_link (struct context *cx);
+int do_generic (struct context *cx);
+void do_set (struct context *cx);
+int do_incheck (struct context *cx);
+int do_outcheck (struct context *cx);
+int do_copyfile (struct context *cx);
+int do_movefile (struct context *cx);
+void do_delete (struct context *cx);
+void do_purge (struct context *cx, char *dname);
+
+int getcmd (register struct context *cx, char *prefix, char *cmd, int maxch);
+char *getargs (register struct context *cx);
+int getstr (register struct context *cx, char *outstr, int maxch, int delim);
+int getkwvpair (register struct context *cx, char *symbol, char *value);
+int getword (char **str, char *outstr, int maxch);
+void putsym (char *name, char *value);
+char *getsym (char *name);
+char *mklower (char *s);
+
+
+/* sflist.c */
+int sf_scanlist (struct context *cx);
+struct sfile *sf_dirsearch (char *dirname);
+struct sfile *sf_filesearch (struct sfile *sflist, char *stname);
+void sf_prune (register char *cp);
+
+
+/* scanlib.c */
+int h_scanlibrary (char *library);
+long h_ardate (char *fname);
+int mlb_setdate (char *modname, long fdate);
+long mlb_getdate (char *modname);
diff --git a/unix/boot/mkpkg/mkpkg.hlp b/unix/boot/mkpkg/mkpkg.hlp
new file mode 100644
index 00000000..39dd1163
--- /dev/null
+++ b/unix/boot/mkpkg/mkpkg.hlp
@@ -0,0 +1,626 @@
+.help mkpkg Mar90 "softools"
+.ih
+NAME
+mkpkg - make or update a package or library
+.ih
+USAGE
+mkpkg [switches] [module ...] [name=value ...]
+.ih
+ARGUMENTS
+.ls 10 \fB-d[ddd]\fR
+Debug mode. Print detailed messages describing what \fImkpkg\fR is doing.
+There are four levels of debug messages, selected by repeating the "d"
+character in the switch, e.g., "-d" is level one, "-dd" is level two, and
+so on. The debug messages get progressively more detailed as the debug level
+increases. Debug mode automatically enables the verbose mode messages.
+.le
+.ls 10 \fB-f file\fR
+Set the name of the file to be interpreted (default: "mkpkg").
+The special value "stdin" (lower case) allows commands to be entered
+interactively from the standard input, e.g., for debugging \fImkpkg\fR.
+.le
+.ls 10 \fB-i\fR
+Ignore errors. Execution continues even if an error occurs. In most cases
+it does anyhow, so this switch has little effect at present.
+.le
+.ls 10 \fB-n\fR
+No execute. Go through the motions, but do not touch any files.
+No execute mode automatically enables verbose mode (flag "-v").
+This switch should be used to verify new mkpkg files before execution.
+.le
+.ls 10 \fB-p \fIpkgname\fR
+Load the package environment for the named external package, e.g.,
+"mkpkg -p noao update". If the same package is always specified
+the environment variable or logical name PKGENV may be defined at the
+host level to accomplish the same thing. The package name \fImust\fR
+be specified when doing software development in an external or layered
+package.
+.le
+.ls 10 \fB-u\fR [AOSVS/IRAF only]
+Forcibly update the dates of improperly dated library modules. This option
+is used when a binary archive is restored on a machine which cannot restore
+the file modify dates. In this case, all source file dates would appear to
+have been modified since the libraries were updated, causing all sources to
+be recompiled. By running \fImkpkg\fR with the \fI-u\fR flag, one can update
+the library module dates without recompiling the associated files. This is
+done by setting the date of each library module to be no older than the
+file \fIhlib$iraf.h\fR, which should be "touched" after the system has fully
+been restored to disk to mark the installation time. Note that files which
+have been modified \fIsince\fR the system was restored to disk will still
+cause the affected library modules to be updated, even when the \fI-u\fR flag
+is specfied.
+.le
+.ls 10 \fB-v\fR
+Verbose mode. A message is printed whenever a file is touched.
+Recommended when running large mkpkg jobs in batch mode.
+.le
+.ls 10 \fBmodule\fR
+The names of the module or modules (named entries in the "mkpkg" file) to be
+executed. If no module is named the first module encountered is executed,
+unless a \fImkpkg\fR macro preprocessor directive at the beginning of the file
+specifies a different default action.
+.le
+.ls 10 \fBname=value [name=value...]\fR
+Enter the named symbol/value pair into the symbol table of the \fImkpkg\fR
+macro preprocessor. The symbols \fIXFLAGS\fR (for the XC compiler) and
+\fILFLAGS\fR (for the linker) are predefined but may be redefined on the
+command line. Case is ignored in symbol names for portability reasons.
+.le
+.ih
+DESCRIPTION
+The \fImkpkg\fR utility is used to make or update IRAF packages or libraries.
+\fIMkpkg\fR is used to bootstrap the IRAF system hence is implemented as
+a foreign task, callable either from within the IRAF environment or from the
+host system. Usage is identical in either case (except that the details of
+when a particular argument may need to be quoted will vary depending on the
+command language used). \fIMkpkg\fR is upwards compatible with the old
+\fImklib\fR utility.
+
+
+.tp 4
+1. \fBIntroduction\fR
+
+ \fIMkpkg\fR provides two major facilities: a library update capability and
+a macro preprocessor. The macro preprocessor provides symbol definition and
+replacement, conditional execution, and a number of builtin commands.
+The usefulness of these facilities is enhanced by the ability of \fImkpkg\fR
+to update entire directory trees, or to enter the hierarchy of \fImkpkg\fR
+descriptors at any level. For example, typing "mkpkg" in the root directory
+of IRAF will make or update the entire system, whereas in the "iraf$sys"
+directory \fImkpkg\fR will update only the system libraries, and in the
+"iraf$sys/fio" directory \fImkpkg\fR will update only the FIO portion of the
+system library "libsys.a".
+
+The \fImkpkg\fR utility is quite simple to use to maintain small packages
+or libraries, despite the complexity of the discussion which follows.
+The reader is encouraged to study several examples of working mkpkg-files
+before reading further; examples will be found throughout the IRAF system.
+The mkpkg files for applications packages tend to be very similar to one
+another, and it is quite possible to successfully copy and modify the
+mkpkg-file from another package without studying the reference information
+given here.
+
+
+.tp 4
+2. \fBLexical Conventions\fR
+
+ The lexical conventions employed in \fImkpkg\fR are those used throughout
+IRAF. Comments may occur anywhere, begin with the character #, and extend
+to the end of the current line. Blank lines are ignored virtually everywhere.
+Newline may be escaped with backslash to continue on the next line.
+All filenames are IRAF virtual filenames with the following extensions.
+
+
+.ks
+.nf
+ .a object library
+ .c C source
+ .e executable (e.g., "x_package.e")
+ .f Fortran source
+ .gc generic C source
+ .gx generic SPP source
+ .h C or SPP header file
+ .inc include file
+ .l Lex source
+ .o object file
+ .r Ratfor source
+ .s assembler source
+ .y Yacc source
+.fi
+.ke
+
+
+Since \fImkpkg\fR is an IRAF utility it recognizes the major IRAF logical
+directories; these are summarized in the list below. The IRAF (or UNIX)
+pathname convention is used to specify pathnames rooted in the current
+directory or a logical directory.
+
+
+.ks
+.nf
+ as$ where .s files go host$as/
+ bin$ installed executables iraf$bin/
+ dev$ device tables iraf$dev/
+ hlib$ machdep header files host$hlib/
+ host$ host system interface [MACHDEP]
+ iraf$ the root directory of IRAF [MACHDEP]
+ lib$ system library iraf$lib/
+ math$ math sources iraf$math/
+ pkg$ applications packages iraf$pkg/
+ sys$ the VOS, system libraries iraf$sys/
+ tmp$ where temporary files go [MACHDEP]
+.fi
+.ke
+
+
+All other directories should be referenced by giving the path from either the
+current directory or from one of the system logical directories shown above.
+For example, "pkg$system/" is the root directory of the SYSTEM package,
+and ".." is the directory one level up from the current directory.
+
+
+.tp 4
+3. \fBMaintaining Libraries with MKPKG\fR
+
+ Libraries are described by a \fBmember list\fR module in the "mkpkg" file.
+The syntax of a library member list module is shown below. Note that the
+\fBmkpkg\fR module name for a library member list module is the same as the
+name of the actual library, hence must end with the extension ".a".
+
+
+.ks
+.nf
+ libname.a:
+ member1 dep1 dep2 ... depN
+ member2 dep1 dep2 ... depN
+ ...
+ memberN dep1 dep2 ... depN
+ ;
+.fi
+.ke
+
+
+Here, "libname.a" is the IRAF virtual filename of the library (regardless of
+what directory it resides in), "memberN" is the name of a source file which
+may contain any number of actual library object modules, and "depN" is the
+name of a file upon which the named member depends. If any of the named
+dependency files is newer than the corresponding member source file, or if
+the member source file is newer than the compiled library object module,
+the source file is recompiled and replaced in the library. Both source
+files and dependency files may reside in remote directories. The names of
+dependency files in system libraries should be enclosed in <> delimiters,
+e.g., "<fset.h>". Each member must be described on a separate line.
+
+If the library being updated does not reside in the current directory
+(directory from which the "mkpkg" command was entered) then the library must
+be "checked out" of the remote directory before it can be updated, and checked
+back in when updating is complete. These operations are performed by macro
+preprocessor directives, e.g.:
+
+
+.ks
+.nf
+ $checkout libsys.a lib$
+ $update libsys.a
+ $checkin libsys.a lib$
+ $exit
+
+ libsys.a:
+ @symtab # update libsys.a in ./symtab
+ brktime.x <time.h>
+ environ.x environ.com environ.h <ctype.h>\
+ <fset.h> <knet.h>
+ main.x <clset.h> <config.h> <ctype.h>\
+ <error.h> <fset.h> <knet.h>\
+ <printf.h> <xwhen.h>
+ onentry.x <clset.h> <fset.h> <knet.h>
+ spline.x <math.h> <math/interp.h>
+ ;
+.fi
+.ke
+
+
+Note that the checkout operation is required only in the directory from which
+the "mkpkg" command was entered, since the library has already been checked
+out when the mkpkg-file in a subdirectory is called to update its portion
+of the library (as in the "@symtab" in the example above). The checkout
+commands should however be included in each mkpkg-file in a hierarchy in such
+a way that the library will be automatically checked out and back in if
+\fImkpkg\fR is run from that directory. The checkout commands are ignored
+if the mkpkg-file is entered when updating the library from a higher level,
+because in that case \fImkpkg\fR will search for the named entry for the
+library being updated, ignoring the remainder of the mkpkg-file.
+
+Sometimes it is necessary or desirable to break the library member list up
+into separate modules within the same mkpkg-file, e.g., to temporarily
+change the value of the symbol XFLAGS when compiling certain modules.
+To do this use the "@" indirection operator in the primary module list to
+reference a named sublist, as in the example below. Normal indirection
+cannot be used unless the sublist resides in a subdirectory or in a different
+file in the current directory, e.g., "@./mki2", since a single mkpkg-file
+cannot contain two modules with the same name. The same restrictions apply
+to the \fI$update\fR operator.
+
+
+.ks
+.nf
+ libpkg.a:
+ @(i2)
+ alpha.x
+ beta.x
+ zeta.f
+ ;
+ i2:
+ $set XFLAGS = "-cO -i2"
+ gamma.f
+ delta.f
+ ;
+.fi
+.ke
+
+
+In the example above five object modules are to be updated in the library
+"libpkg.a". The files listed in module "i2", if out of date, will be compiled
+with the nonstandard XFLAGS (compiler flags) specified by the \fI$set\fR
+statement shown.
+
+
+.tp 4
+4. \fBThe MKPKG Macro Preprocessor\fR
+
+ The \fImkpkg\fR macro preprocessor provides a simple recursive symbol
+definition and replacement facility, an include file facility, conditional
+execution facilities, an OS escape facility, and a number of builtin directives.
+The names of the preprocessor directives always begin with a dollar sign;
+whitespace is not permitted between the dollar sign and the remainder of the
+name. Several preprocessor directives may be given on one line if desired.
+Preprocessor directives are executed as they are encountered, and may appear
+anywhere, even in the member list for a library.
+
+
+.tp 4
+4.1 Symbol Replacement
+
+ Symbol substitution in the \fImkpkg\fR macro preprocessor is carried out
+at the character level rather than at the token level, allowing macro expansion
+within tokens, quoted strings, or OS escape commands. Macros are recursively
+expanded but may not have arguments.
+
+Macros may be defined on the \fBmkpkg\fR command line, in the argument list
+to a \fB$call\fR or \fB$update\fR directive (see below), in an include file
+referenced with the \fB$include\fR directive, or in a \fB$set\fR directive.
+All symbols are global and hence available to all lower level modules,
+but symbols are automatically discarded whenever a module exits, hence cannot
+affect higher level modules. A local symbol may redefine a previously
+defined symbol. The IRAF and host system environment is treated as an
+extension of the \fBmkpkg\fR symbol table, i.e., a logical directory such
+as "iraf" may be referenced like a locally defined symbol.
+
+Macro replacement occurs only when explicitly indicated in the input text,
+as in the following example, which prints the pathname of the
+\fBdev$graphcap\fR file on the \fBmkpkg\fR standard output. The sequence
+"$(" triggers macro substitution. The value of a symbol may be obtained
+interactively from the standard input by adding a question mark after the
+left parenthesis, i.e., "$(?terminal)" (this does not work with the -f stdin
+flag). The contents of a file may be included using the notation
+"$(@\fIfile\fR)". Note that case is ignored in macro names; by convention,
+logical directories are normally given in lower case, and locally defined
+symbols in upper case.
+
+
+.ks
+.nf
+ $echo $(dev)graphcap
+ !xc $(XFLAGS) filea.x fileb.x
+.fi
+.ke
+
+
+Symbols are most commonly defined locally with the \fB$set\fR directive.
+The \fB$include\fR directive is useful for sharing symbols amongst different
+modules, or for isolating any machine dependent definitions in a separate
+file. The IRAF \fBmkpkg\fR system include file \fBhlib$mkpkg.inc\fR is
+automatically included whenever \fImkpkg\fR is run.
+.ls 4
+.ls \fB$set\fR symbol = value
+Enter the named symbol into the symbol table with the given string value.
+Any existing symbol will be silently redefined. Symbols defined within a
+module are discarded when the module exits.
+.le
+.ls \fB$include\fR filename
+Read commands (e.g., \fB$set\fR directives) from the named include file.
+The include filename may be any legal virtual filename, but only the
+major logical directories are recognized, e.g., "iraf$", "host$", "hlib$",
+"lib$", "pkg$", and so on.
+.le
+.le
+
+
+The use of the \fB$set\fR directive is illustrated in the example below.
+Note the doubling of the preprocessor meta-character to avoid macro expansion
+when entering the value of the GEN macro into the symbol table. The sequence
+"$$" is replaced by a single "$" whenever it is encountered in the input
+stream.
+
+
+.ks
+.nf
+ $set GFLAGS = "-k -t silrdx -p ak/"
+ $set GEN = "$generic $$(GFLAGS)"
+
+ ifolder (amulr.x, amul.x) $(GEN) amul.x $endif
+.fi
+.ke
+
+
+.tp 4
+4.2 Conditional Execution
+
+ Conditional control flow is implemented by the \fB$if\fR directives
+introduced in the last example and described below. The character "n" may
+be inserted after the "$if" prefix of any directive to negate the sense of
+the test, e.g., "$ifndef" tests whether the named symbol does not exist.
+Nesting is permitted.
+.ls 4
+.ls \fB$ifdef\fR (symbol [, symbol, ...])
+.sp
+Test for the existence of one of the named symbols.
+.le
+.ls \fB$ifeq\fR (symbol, value [, value,...])
+.sp
+Test if the value of the named symbol matches one of the listed value strings.
+.le
+.ls \fB$iferr\fR
+.sp
+Test for an error return from the last directive executed which touched
+a file.
+.le
+.ls \fB$iffile\fR (file [, file,...])
+.sp
+Test for the existence of any of the named files.
+.le
+.ls \fB$ifnewer\fR (file, filea)
+.in -4
+\fB$ifnewer\fR (file: filea [, fileb, ...])
+.in 4
+.sp
+Test if the named file is newer (has been modified more recently) than
+any of the named files to the right. The colon syntax may be used for
+clarity when comparing one file to many, but a comma will do.
+.le
+.ls \fB$ifolder\fR (file, filea)
+.in -4
+\fB$ifolder\fR (file: filea [, fileb, ...])
+.in 4
+.sp
+Test if the named file is older than any of the named files.
+.le
+.ls \fB$else\fR
+.sp
+Marks the \fIelse\fR clause of an \fIif\fR statement. The \fIelse-if\fR
+construct is implemented as "$else $if", i.e., as a combination of the two
+more primitive constructs.
+.le
+.ls \fB$endif\fR
+.sp
+Terminates a $if or $if-$else statement.
+.le
+.ls \fB$end\fR
+.sp
+Terminates an arbitrary number of $if or $if-$else statements. This is most
+useful for terminating a long list of $if-$else clauses, where the alternative
+would be a long string of $endif directives.
+.le
+.ls \fB$exit\fR
+Terminate the current program; equivalent to a semicolon, but the latter
+is normally used only at the end of the program to match the colon at the
+beginning, whereas \fB$exit\fR is used in conditionals.
+.le
+.le
+
+
+.tp 4
+4.3 Calling Modules
+
+ The following preprocessor directives are available for calling \fImkpkg\fR
+modules or altering the normal flow of control.
+.ls
+.ls \fB$call\fR module[@subdir[/file]] [name=value] [name=value...]
+.sp
+Call the named mkpkg-file module as a subroutine. In most cases the called
+module will be in the current mkpkg-file, but the full module name syntax
+permits the module to be in any file of any subdirectory ("./file" references
+a different file in the current directory). Arguments may be passed to
+the called module using the symbol definition facility; any symbols
+defined in this fashion are available to any modules called in turn by
+the called module, but the symbols are discarded when the called module returns.
+.le
+.ls \fB$update\fR module[@subdir[/file]] [name=value] [name=value...]
+.sp
+Identical to \fB$call\fR except that the named module is understood to
+be a library member list. The current value of the symbol XFLAGS is used
+if XC is called to compile any files. If the named library does not exist
+one will be created (a warning message is issued).
+.le
+.ls \fB$goto\fR label
+.sp
+Causes execution to resume at the line following the indicated label.
+The syntax of a goto label is identical to that of a mkpkg-file module name,
+i.e., a line starting with the given name followed by a colon.
+The \fI$goto\fR statement automatically cancels any \fI$if\fR nesting.
+.le
+.le
+
+
+.tp 4
+4.4 Preprocessor Directives
+
+ The remaining preprocessor directives are described below in alphabetical
+order. Additional capability is available via OS escapes, provided the
+resultant machine dependence is acceptable.
+.ls
+.ls \fB$echo\fR message
+.sp
+Print the given message string on the standard output. The string must be
+quoted if it contains any spaces.
+.le
+.ls \fB$checkout\fR file directory
+.sp
+Check the named file out of the indicated directory. The checkout operation
+makes the file accessible as if it were in the current directory; checkout
+is implemented either as a symbolic link or as a physical file copy depending
+upon the host system. The referenced directory may be a logical directory,
+e.g., "lib$", or a path, e.g, "pkg$images/". Checkout is not disabled by
+the "-n" flag.
+.le
+.ls \fB$checkin\fR file directory
+.sp
+Check the named file back into the indicated directory. The checkin operation
+is implemented either as a remove link or copy and delete depending upon the
+host system. Checkin is not disabled by the "-n" flag.
+.le
+.ls \fB$copy\fR filea fileb
+.sp
+Make a copy \fIfileb\fR of the existing file \fIfilea\fR. On a UNIX host
+the copy operation will preserve the file modify date if the file is a library
+(to avoid the "symbol table out of date" syndrome).
+.le
+.ls \fB$delete\fR file [file ...]
+.sp
+Delete the named file or files.
+.le
+.ls \fB$generic\fR [-k] [-p prefix] [-t types] [-o root] files
+.sp
+Run the generic preprocessor on the named files. The generic preprocessor
+is an IRAF bootstrap utility and may not be available on non-UNIX hosts.
+.le
+.ls \fB$link\fR [switches] file1 file2 ... fileN [-o file.e]
+.sp
+Call XC with the given argument list to link the indicated files and libraries.
+The value of the symbol LFLAGS (default value the null string) is automatically
+inserted at the beginning of the command line. This is equivalent to
+"!xc $(LFLAGS) ...".
+.le
+.ls \fB$move\fR file destination
+.sp
+Move the named file to the indicated directory, or rename the file in the
+current directory.
+.le
+.ls \fB$omake\fR file [dep1] [dep2 ...]
+.sp
+Compile the named source file if it does not have a corresponding object file
+in the current directory, if the object file is older, or if any of the
+listed dependency files are newer (or not found). The current value of the
+symbol XFLAGS is used if XC is called to compile the file.
+.le
+.ls \fB$purge\fR directory
+.sp
+Delete all old versions of all files in the named directory. Nothing is done
+if the system does not support multiple file versions.
+.le
+.ls \fB$special\fR directory : filelist ;
+.sp
+Add one or more files to the special file list for the host system. This is
+a system facility, not intended for use in applications \fImkpkg\fR files.
+The special file list is a list of all source files needing special processing
+for the local host system. Examples of special files are files which are
+optimized in assembler (or some other nonstandard language), or files which
+must be compiled in a special way to get around bugs in a host compiler.
+The special file list makes it possible to flag arbitrary files for special
+processing, without having to modify the standard software distribution.
+In the IRAF system, the special file list is defined in the file
+"hlib$mkpkg.sf" which is included automatically by "hlib$mkpkg.inc" whenever
+\fImkpkg\fR is run.
+
+The syntax of a \fIfilelist\fR entry is as follows:
+
+ modname source_file mkobj_command
+
+where \fImodname\fR is the filename of a library module as it appears in a
+library module list for the named directory, \fIsource_file\fR is the virtual
+pathname of the source file to be used in lieu of the standard portable
+source file \fImodname\fR, and \fImkobj_command\fR is the \fImkpkg\fR command
+(e.g., $xc or an OS escape) to be executed to compile the named module.
+The character "&" appearing in either the source file name or mkobj command
+is replaced by \fImodname\fR. If the \fImkobj_command\fR is omitted the
+specified source file will be compiled with $XC using the current value of
+XFLAGS.
+.le
+.ls \fB$xc\fR [switches] file1 file2 ... fileN
+.sp
+Call the XC compiler to compile the named files. Note that the value of
+the symbol XFLAGS is \fInot\fR used when XC is explicitly called in this
+fashion (XFLAGS is used by \fB$update\fR and \fB$omake\fR).
+.le
+.ls \fB$debug\fR [on|off]
+.sp
+Turn debug mode on or off. If no argument is supplied debug mode is turned
+on. Turning on debug mode automatically enables verbose mode.
+.le
+.ls \fB$verbose\fR [on|off]
+.sp
+Turn verbose mode on or off. If no argument is supplied verbose mode is turned
+on.
+.le
+.le
+
+
+.tp 4
+5. Error Recovery
+
+ \fBMkpkg\fR is implemented in such a way that it is restartable. If a mkpkg
+operation terminates prematurely for some reason, e.g., because of a compile
+error, execution error (such as cannot find the mkpkgfile in a subdirectory),
+interrupt, etc., then the mkpkg command can be repeated after correcting
+the error, without repeating the operations already completed. If \fBmkpkg\fR
+is interrupted it may leave checked out files, objects compiled but not yet
+updated in a library, etc. lying about, but this is harmless and the
+intermediate files will be cleaned up when the errors have been corrected
+and the run successfully completes.
+
+.ih
+EXAMPLES
+Update the current package.
+
+ cl> mkpkg
+
+Update the package library but do not relink.
+
+ cl> mkpkg libpkg.a
+
+Make a listing of the package.
+
+ cl> mkpkg listing
+
+
+.ks
+.nf
+Sample mkpkg-file for the above commands:
+
+
+ # Make my package.
+
+ $call relink
+ $exit
+
+ relink:
+ $update libpkg.a
+ $omake x_mypkg.x
+ $link x_mypkg.o -lxtools
+ ;
+
+ libpkg.a:
+ task1.x pkg.h
+ task2.x
+ filea.x pkg.com pkg.h <fset.h>
+ fileb.x pkg.com
+ ;
+
+ listing:
+ !pr task1.x task2.x file[ab].x | vpr -Pvup
+ ;
+.fi
+.ke
+.ih
+SEE ALSO
+xc, generic, softools package
diff --git a/unix/boot/mkpkg/mkpkg.sh b/unix/boot/mkpkg/mkpkg.sh
new file mode 100644
index 00000000..a565cd70
--- /dev/null
+++ b/unix/boot/mkpkg/mkpkg.sh
@@ -0,0 +1,9 @@
+# Bootstrap MKPKG.
+
+$CC -c $HSI_CF char.c fdcache.c fncache.c host.c main.c pkg.c scanlib.c\
+ sflist.c tok.c
+$CC $HSI_LF main.o char.o fdcache.o fncache.o host.o pkg.o scanlib.o\
+ sflist.o tok.o $HSI_LIBS -o mkpkg.e
+
+mv -f mkpkg.e ../../hlib
+rm *.o
diff --git a/unix/boot/mkpkg/pkg.c b/unix/boot/mkpkg/pkg.c
new file mode 100644
index 00000000..a8875bc3
--- /dev/null
+++ b/unix/boot/mkpkg/pkg.c
@@ -0,0 +1,902 @@
+/* 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"
+
+
+/* DO_MKPKG -- Open the mkpkg file and scan it for the named program. A program
+ * may be either a sequence of preprocessor directives or the module list for
+ * a library, as indicated by the ISLIB flag. In the case of a library build
+ * up a list of library modules needing updating, and replace these modules
+ * in the library.
+ */
+int
+do_mkpkg (
+ struct context *cx, /* current context */
+ int islib /* update a library? */
+)
+{
+ if (cx->mkpkgfile[0] == EOS)
+ strcpy (cx->mkpkgfile, MKPKGFILE);
+
+ if (debug) {
+ printf ("do_mkpkg (file=%s, library=%s, islib=%d)\n",
+ cx->mkpkgfile, cx->library, islib);
+ fflush (stdout);
+ }
+
+ if (open_mkpkgfile (cx) == ERR) {
+ char fname[SZ_PATHNAME+1];
+ struct context *save_cx;
+
+ save_cx = topcx;
+ if (cx->prev)
+ topcx = cx->prev;
+
+ sprintf (fname, "%s%s", cx->curdir, cx->mkpkgfile);
+ warns ("cannot open `%s'", fname);
+
+ topcx = save_cx;
+ return (ERR);
+ }
+
+ /* Search the mkpkg file for the module list for the named library,
+ * or the first module list encountered if no library is named.
+ * Any number of preprocessor directives may be executed while
+ * searching; in particular, $EXIT will terminate the search,
+ * causing ERR to be returned by the search procedure to indicate
+ * that no module list was found.
+ */
+ if (search_mkpkgfile (cx) == ERR) {
+ if (cx->library[0] != EOS) {
+ warns ("no entry in mkpkg file for `%s'", cx->library);
+ return (ERR);
+ } else {
+ /* Presumably we just executed a bunch of preprocessor
+ * commands and there is no library to update, or it was
+ * already updated by the commands just executed.
+ */
+ return (OK);
+ }
+ }
+
+ /* The mkpkg file is open and positioned to the entry for a library
+ * (or any other sequence of commands with the given name). Update
+ * the named library, close the mkpkgfile, and exit.
+ */
+ exit_status = scan_modlist (cx, islib);
+ close_mkpkgfile (cx);
+
+ return (exit_status);
+}
+
+
+/* SCAN_MODLIST -- Called when positioned to the module list for a library.
+ * Scan the module list and compare file and library module dates, building
+ * up a list of files to be updated. If any files were found which need
+ * updating recompile them and replace them in the library. Call the rebuild
+ * procedure when done to perform any library rebuild or cleanup operations
+ * necessary on the local system.
+ */
+int
+scan_modlist (
+ struct context *cx, /* current mkpkg context */
+ int islib
+)
+{
+ char token[SZ_FNAME+1];
+ char *dflist[MAX_DEPFILES+1];
+ struct sfile *sflist;
+ int root_modlist;
+ int tok;
+
+ /* This is for the case "@(module)" in a library member list, indicating
+ * that the named module is a library member list for the current
+ * library, even though the module name is not the same as the library
+ * name. For searching purposes the cl->library field contains the
+ * module name until we get here, and now we must overwrite this with
+ * the name of the library being updated.
+ */
+ if (islib && cx->sublib)
+ strcpy (cx->library, cx->prev->library);
+
+ if (debug) {
+ printf ("scan_modlist (file=%s, line=%d, library=%s, islib=%d)\n",
+ cx->mkpkgfile, cx->lineno, cx->library, islib);
+ fflush (stdout);
+ }
+
+ /* Check if this directory contains any files needing special
+ * processing.
+ */
+ sflist = sf_dirsearch (cx->dirpath);
+
+ if (cx->prev)
+ root_modlist = (strcmp (cx->library, cx->prev->library) != 0);
+ else
+ root_modlist = 1;
+
+ if (islib && root_modlist) {
+ /* Save the pathname of the library in the context descriptor.
+ * We may be changing the current directory later, so a pathname
+ * is required.
+ */
+ os_fpathname (cx->library, cx->libpath, SZ_PATHNAME);
+ if (debug) {
+ printf ("pathname of `%s' is `%s'\n", cx->library,
+ cx->libpath);
+ fflush (stdout);
+ }
+
+ /* Scan the library and build up a list of modules and their dates.
+ * This will create a new library if necessary. If there are any
+ * fatal warns the scan library routine prints its own error
+ * messages and we return, since no further processing of the
+ * library is possible.
+ */
+ if ((exit_status = h_scanlibrary (cx->library)) != OK) {
+ warns ("error reading library file `%s'", cx->library);
+ return (ERR);
+ }
+ }
+
+ /* Scan the module list in the mkpkg file. An "@subdir" reference
+ * causes us to push a new context and continue scanning the entry
+ * for the same library in a subdirectory. Any number of preprocessor
+ * directives may be executed while we are scanning the module list.
+ * For each module in the list, test the file dates and add the name
+ * to the file list if the module has to be updated.
+ */
+ for (;;) {
+next_: tok = gettok (cx, token, SZ_FNAME);
+
+ if (tok == TOK_NEWLINE) {
+ ; /* ignore blank lines */
+
+ } else if (islib && tok == TOK_FNAME && token[0] != SUBDIR_CHAR) {
+ /* Check if the named module is up to date, and if not,
+ * add to the file list for the library. The useobj flag
+ * is set if the module is not up to date, but the object
+ * file has already been compiled and should be replaced
+ * in the library.
+ */
+ char srcname[SZ_PATHNAME+1], modname[SZ_PATHNAME+1];
+ char dname[SZ_FNAME+1], fname[SZ_FNAME+1];
+ struct sfile *sfp;
+ int useobj;
+
+ strcpy (modname, token);
+
+ /* If this directory has any files needing special processing,
+ * determine if this is such a file, and if so obtain the name
+ * of the actual source file to be used.
+ */
+ sfp = sf_filesearch (sflist, modname);
+ strcpy (srcname, sfp ? sfp->sf_sfname : modname);
+ if (sfp && debug) {
+ printf ("module %s on special file list: ", modname);
+ if (sfp->sf_mkobj[0])
+ printf ("mkobj=`%s'\n", sfp->sf_mkobj);
+ else
+ printf ("src=%s\n", srcname);
+ fflush (stdout);
+ }
+
+ /* Check that the regular, standard source file has not been
+ * modified more recently than the special file, if any.
+ */
+ if (sfp && debug && os_fdate(modname) > os_fdate(srcname))
+ warns ("special file for %s is out of date", modname);
+
+ /* Break filename into the logical directory and local
+ * filenames; if file is remote a local copy will be
+ * created temporarily (see below). Get list of files
+ * upon which the module is dependent, if any.
+ */
+ parse_fname (srcname, dname, fname);
+ get_dependency_list (cx, modname, dflist, MAX_DEPFILES);
+
+ if (!up_to_date (cx, srcname, fname, dflist, &useobj)) {
+
+ /* If file is remote add its name to the remote file list
+ * and "checkout" the file, making it accessible in the
+ * current directory. The file will be checked back in
+ * after the library is updated. It may not be necessary
+ * to compile the file locally, but it is too risky to
+ * predict what the host system will do when asked to
+ * compile a file resident in a remote directory.
+ */
+ if (dname[0]) {
+ int clobber, i;
+
+ for (i=0; i < cx->nrfiles; i++)
+ if (strcmp (fname, cx->rflist[i]) == 0) {
+ /* Multiple modules map to the same remote
+ * source file, which has already been checked
+ * out. Skip duplicate references to the same
+ * source file.
+ */
+ goto next_;
+ }
+ cx->rflist[cx->nrfiles++] = putstr (fname);
+ h_outcheck (fname, dname, clobber=NO);
+ }
+
+ /* If the module needs special processing and a mkobj
+ * command string was given, but the source file has not
+ * yet been compiled, push the command back into the input
+ * stream to compile the source, and set the useobj flag
+ * to defeat recompilation of this module.
+ */
+ if (sfp && sfp->sf_mkobj[0]) {
+ if (useobj) {
+ warns ("module %s has already been compiled",
+ modname);
+ } else {
+ m_pushstr (cx, "\n");
+ m_pushstr (cx, sfp->sf_mkobj);
+ useobj++;
+ }
+ }
+
+ /* Add the local filename to the list of files to be
+ * updated.
+ */
+ cx->flist[cx->nfiles++] =
+ putstr (useobj ? makeobj(fname) : fname);
+
+ if (debug) {
+ printf ("add %s to file list for %s\n",
+ cx->flist[cx->nfiles-1], cx->library);
+ fflush (stdout);
+ }
+
+ if (cx->nfiles > MAX_FILES)
+ fatals ("too many modules listed for library `%s'",
+ cx->library);
+ }
+
+ } else if (tok == TOK_FNAME && token[0] == SUBDIR_CHAR) {
+ /* Push a new context, open mkpkg file and continue scanning
+ * in the new subdirectory.
+ */
+ struct context *ncx;
+ char module[SZ_FNAME+1];
+ char subdir[SZ_FNAME+1];
+ char fname[SZ_FNAME+1];
+
+ /* Parse the "module@subdir/fname" string. */
+ parse_modname (token, module, subdir, fname);
+
+ /* Push a new context and start over; recursive call. May
+ * "reopen" (soft) the current mkpkg file or the mkpkg in a
+ * subdirectory.
+ */
+ if ((ncx = push_context (cx, module, subdir, fname)) == NULL)
+ exit_status = ERR;
+ else {
+ exit_status = do_mkpkg (ncx, islib);
+ cx = pop_context (ncx);
+ }
+
+ if (exit_status != OK && !ignore)
+ return (exit_status);
+
+ } else if (tok == TOK_END || tok == 0) {
+ /* We have reached the end of the current module list (;),
+ * executed a $EXIT, or seen EOF on the mkpkg file. If the
+ * file list is nonempty update the current library, restore
+ * the previous context, and return (from the do_mkpkg, above).
+ */
+
+ /* The file list now contains the names of all the files that
+ * need to be updated. Compile and update the archive.
+ */
+ if (islib && cx->nfiles == 0) {
+ /* No modules were found that need updating.
+ */
+ if (cx->prev != NULL && cx->level > cx->prev->level) {
+ char dirname[SZ_FNAME+1];
+ char *ip, *op;
+
+ /* Prettify the directory name.
+ */
+ for (ip=cx->curdir, op=dirname; (*op = *ip++); op++)
+ ;
+ if (*(op-1) == '/')
+ *(op-1) = EOS;
+
+ printf ("Subdirectory %s is up to date\n", dirname);
+ fflush (stdout);
+ }
+ } else if (islib) {
+ char dname[SZ_FNAME+1], fname[SZ_FNAME+1];
+ int i;
+
+ /* Compile the modules and update the library.
+ */
+ exit_status = h_updatelibrary (cx->libpath,
+ cx->flist, cx->nfiles, getsym(XFLAGS), irafdir);
+ if (exit_status == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", cx->library);
+ cx->totfiles += cx->nfiles;
+
+ /* Delete any local copies of (or links to) files that were
+ * checked out of a remote directory.
+ */
+ for (i=0; i < cx->nrfiles; i++) {
+ parse_fname (cx->rflist[i], dname, fname);
+ h_incheck (fname, NULL);
+ }
+ }
+
+ /* If the module list just terminated was a partial list,
+ * return immediately to continue processing the next higher
+ * level module list for the same library.
+ */
+ if (root_modlist && islib)
+ break;
+ else {
+ if (debug) {
+ printf ("not root library; return to higher level\n");
+ fflush (stdout);
+ }
+ return (exit_status);
+ }
+
+ } else if (islib)
+ warns ("bad token `%s' in library module list", token);
+ }
+
+ /* We get here when the end of the root module list for a library has
+ * been reached (but only if the module being processed is a library
+ * list).
+ */
+ if (cx->totfiles == 0 && !forceupdate) {
+ printf ("Library %s is up to date\n", cx->library);
+ fflush (stdout);
+ } else if (exit_status == OK || ignore) {
+ /* Run the system dependent library rebuild operator.
+ */
+ if ((exit_status = h_rebuildlibrary (cx->library)) == INTERRUPT)
+ fatals ("<ctrl/c> interrupt %s", cx->library);
+ printf ("Updated %d files in %s\n", cx->totfiles, cx->library);
+ fflush (stdout);
+ }
+
+ return (exit_status);
+}
+
+
+/* PARSE_MODNAME -- Parse a module reference into its component parts.
+ *
+ * Syntax: module@subdir/fname
+ * or @(module)subdir/fname
+ */
+void
+parse_modname (
+ char *modname, /* "module@subdir/fname" */
+ char *module, /* receives module */
+ char *subdir, /* receives subdir */
+ char *fname /* receives fname */
+)
+{
+ register char *ip, *op;
+ register int ch;
+ char *path;
+
+ for (ip=modname; isspace (*ip); ip++)
+ ;
+
+ /* Module name XXX@ */
+ op = module;
+ for (; (*op = *ip) && *op != '@'; op++, ip++)
+ ;
+ *op = EOS;
+
+ /* Module name @(XXX) */
+ if (op == module && *ip == '@' && *(ip+1) == '(') {
+ for (ip++; (*op = *ip) && *op != ')'; op++, ip++)
+ ;
+ *(op+1) = EOS;
+ if (*ip == ')')
+ ip++;
+ }
+
+ if (*ip == '@')
+ ip++;
+
+ /* Get subdirectory and mkpkg file names. If a simple identifier is
+ * given it is taken to be the name of the subdirectory, otherwise
+ * ($ or / found) the given pathname is parsed.
+ */
+ fname[0] = EOS;
+ for (op=subdir, path=ip; (ch = *op = *ip++); op++)
+ if (ch == '$' || ch == '/') {
+ if (*(op-1) == '\\')
+ *--op = ch;
+ else {
+ parse_fname (path, subdir, fname);
+ break;
+ }
+ }
+}
+
+
+/* PARSE_FNAME -- Return logical directory and filename fields of a filename.
+ */
+void
+parse_fname (
+ char *path, /* input filename */
+ char *dname, /* receives directory name */
+ char *fname /* receives file name */
+)
+{
+ register char *ip, *op;
+ register char *delim;
+
+ delim = NULL;
+ for (ip=path, op=fname; (*op = *ip); op++, ip++)
+ if (*ip == '$' || *ip == '/') {
+ if (*(ip-1) == '\\')
+ *(--op) = *ip;
+ else
+ delim = ip;
+ }
+
+ if (delim == NULL) {
+ dname[0] = EOS;
+ return; /* no directory name */
+ }
+
+ for (ip=path, op=dname; ip <= delim; )
+ *op++ = *ip++;
+ *op = EOS;
+
+ for (op=fname; (*op++ = *ip++); )
+ ;
+}
+
+
+/* PUSH_CONTEXT -- Push a new context, i.e., save the current context in the
+ * current context descriptor, allocate and initialize a new context
+ * descriptor. Set up the new context, including the current directory,
+ * but do not open the new mkpkgfile.
+ */
+struct context *
+push_context (
+ register struct context *cx, /* current context */
+ char *module, /* new module (library) */
+ char *newdir, /* new directory */
+ char *fname /* mkpkgfile name */
+)
+{
+ register struct context *ncx;
+
+ if (debug) {
+ printf ("push_context (module=%s, newdir=%s, fname=%s)\n",
+ module, newdir, fname);
+ fflush (stdout);
+ }
+
+ /* Update old context.
+ */
+ cx->old_nsymbols = nsymbols;
+ cx->old_iflev = iflev;
+ cx->old_cp = cp;
+
+ if (cx->fp && cx->fp != stdin)
+ cx->fpos = k_ftell (cx);
+
+ /* Initialize new context.
+ */
+ ncx = (struct context *) malloc (sizeof (struct context));
+ if (ncx == NULL)
+ fatals ("out of memory in `%s'", fname);
+
+ *ncx = *cx; /* copy old struct to new */
+
+ ncx->pb = NULL;
+ ncx->prev = cx;
+ ncx->totfiles = 0;
+ ncx->nfiles = 0;
+ ncx->nrfiles = 0;
+ ncx->pbchar = 0;
+ ncx->pushback = 0;
+ ncx->sublib = 0;
+
+ /* In the case of a (XXX) module name reference to a module containing
+ * a sub-member list of the current library, strip the () and set the
+ * sublib flag for scanlibrary().
+ */
+ if (module[0]) {
+ if (strcmp (module, "BOF") == 0) {
+ ncx->library[0] = EOS;
+ } else if (module[0] == '(') {
+ char *ip, *op;
+
+ for (ip=module+1, op=ncx->library; (*op = *ip++); op++)
+ if (*op == ')')
+ break;
+ *op = EOS;
+ ncx->sublib = YES;
+ } else
+ strcpy (ncx->library, module);
+ }
+
+ if (newdir[0] && strcmp(newdir,".") != 0 && strcmp(newdir,"./") != 0) {
+ /* Record the directory path for printed output. Note that this
+ * will be a conventional pathname only if each "newdir" reference
+ * is to a subdirectory.
+ */
+ strcat (ncx->curdir, newdir);
+ strcat (ncx->curdir, "/");
+
+ if (debug) {
+ printf ("change directory to `%s'\n", newdir);
+ fflush (stdout);
+ }
+
+ if (os_chdir (newdir) == ERR) {
+ warns ("cannot access subdirectory `%s'", newdir);
+ free (ncx);
+ return (NULL);
+ } else {
+ os_fpathname ("", ncx->dirpath, SZ_PATHNAME);
+ ncx->level++;
+ }
+
+ /* Initialize the file date cache, since the filenames therein
+ * often reference the current directory.
+ */
+ m_fdinit (debug);
+ }
+
+ if (fname[0])
+ strcpy (ncx->mkpkgfile, fname);
+
+ return (topcx = ncx);
+}
+
+
+/* POP_CONTEXT -- Restore the previous context, including the current
+ * directory.
+ */
+struct context *
+pop_context (
+ register struct context *cx /* current context */
+)
+{
+ register struct context *pcx;
+ int root_modlist;
+ int level;
+
+ if (debug) {
+ printf ("pop_context (library=%s)\n", cx->library);
+ fflush (stdout);
+ }
+
+ /* Pop the previous context.
+ */
+ if (cx->prev != NULL) {
+ level = cx->level;
+ pcx = cx->prev;
+
+ root_modlist = (strcmp (cx->library, pcx->library) != 0);
+ if (!root_modlist)
+ pcx->totfiles += cx->totfiles;
+
+ free (cx);
+ topcx = cx = pcx;
+
+ if (cx->fp && cx->fp != stdin)
+ k_fseek (cx, cx->fpos, 0);
+
+ sf_prune (cp = cx->old_cp);
+ nsymbols = cx->old_nsymbols;
+ iflev = cx->old_iflev;
+
+ if (level > pcx->level) {
+ if (debug) {
+ printf ("chdir ..\n");
+ fflush (stdout);
+ }
+
+ if (os_chdir (pcx->dirpath) == ERR)
+ fatals ("cannot return from subdirectory", cx->curdir);
+
+ /* Initialize the file date cache, since the filenames therein
+ * often reference the current directory.
+ */
+ m_fdinit (debug);
+ }
+ }
+
+ return (cx);
+}
+
+
+/* GET_DEPENDENCY_LIST -- Each file name in a library membership list occurs
+ * on a separate line in the Makelib file. This file name may be followed by
+ * the names of zero or more other files, upon which the primary file is
+ * dependent. The following procedure extracts the names of these files into
+ * the string buffer, returning a list of pointers to the filenames to the
+ * caller. Note that the string buffer space is only "borrowed" and the
+ * filenames should be used promptly, before the string buffer space is reused.
+ */
+void
+get_dependency_list (
+ struct context *cx, /* current library context */
+ char *module, /* module list is for */
+ char *dflist[], /* receives filename pointers */
+ int maxfiles /* maxfiles out */
+)
+{
+ char fname[SZ_FNAME+1];
+ int token, nfiles=0;
+ char *save_cp;
+ int i;
+
+ save_cp = cp;
+
+ while ((token = gettok (cx, fname, SZ_FNAME)) != 0) {
+ switch (token) {
+ case TOK_NEWLINE:
+ goto done;
+ case TOK_FNAME:
+ if (nfiles >= MAX_DEPFILES)
+ warns ("too many dependency files for module `%s'", module);
+ dflist[nfiles++] = putstr (fname);
+ break;
+ case TOK_END:
+ warns ("unexpected EOF in dependency list for `%s'", module);
+ default:
+ warns ("bad token `%s' in dependency list", fname);
+ }
+ }
+
+done:
+ /* A null string pointer marks the end of the list.
+ */
+ dflist[nfiles] = NULL;
+
+ if (debug) {
+ printf ("%s:", module);
+ for (i=0; i < nfiles; i++)
+ printf (" %s", dflist[i]);
+ printf ("\n");
+ fflush (stdout);
+ }
+
+ cp = save_cp;
+}
+
+
+/* UP_TO_DATE -- Determine if the named module is up to date. A module is up
+ * to date if:
+ *
+ * (1) The lib module is newer than the source file, and
+ * (2) The source file is newer than any of its dependents.
+ *
+ * If the module is out of date, and an object file exists which is current
+ * (newer than the source, which is in turn newer than any dependents),
+ * set the USEOBJ flag to tell our caller to use the .o file, rather than
+ * recompile the module.
+ */
+int
+up_to_date (
+ struct context *cx, /* current library context */
+ char *module, /* module to compare dates for */
+ char *lname, /* local name of module */
+ char *dflist[], /* list of dependent files */
+ int *useobj /* obj exists and is usable */
+)
+{
+ long armod_date, newest_date, date;
+ long h_ardate();
+ char *fname;
+ int old, i;
+
+ armod_date = h_ardate (lname);
+ newest_date = armod_date;
+ (*useobj) = NO;
+
+ /* Compare lib module date and source file date.
+ */
+ date = os_fdate (module);
+ if (date == 0) {
+ warns ("module source file `%s' not found", module);
+ return (YES);
+ } else if (armod_date < date) {
+ if (debug > 1) {
+ printf ("(%s) ar: %ld fil: %ld\n", module, armod_date, date);
+ fflush (stdout);
+ }
+ old = YES;
+ newest_date = date;
+ } else
+ old = NO;
+
+ /* Compare dates of archive file and any dependent files.
+ */
+ for (i=0; (fname = dflist[i]) != NULL; i++) {
+ date = m_fdate (fname);
+ if (date == 0) {
+ warns ("dependency file `%s' not found", fname);
+ } else if (armod_date < date) {
+ old = YES;
+ if (date > newest_date)
+ newest_date = date;
+ }
+ }
+
+ if (old == NO) {
+ /* Module is up to date.
+ */
+ return (YES);
+ } else {
+ /* Library module is not up to date. Check if an object file
+ * exists which can be used w/o recompilation.
+ */
+ if (newest_date <= os_fdate (makeobj (module)))
+ (*useobj) = YES;
+ return (NO);
+ }
+}
+
+
+/* OPEN_MKPKGFILE -- Open the mkpkgfile for the current library context.
+ * If the same file is already physically open by this process, this is
+ * a "soft" open.
+ */
+int
+open_mkpkgfile (register struct context *cx)
+{
+ register char *fname = cx->mkpkgfile;
+ struct context *find_mkpkgfile();
+ struct context *ax;
+
+ if (strcmp (fname, "stdin") == 0 || strcmp (fname, "STDIN") == 0) {
+ cx->fp = stdin;
+ } else if ((ax = find_mkpkgfile (cx->prev, fname, cx->level)) == NULL) {
+ cx->fp = fopen (vfn2osfn(fname,0), "r");
+ if (cx->fp)
+ k_fseek (cx, 0L, 0);
+ } else {
+ cx->fp = ax->fp;
+ if (cx->fp && cx->fp != stdin)
+ k_fseek (cx, 0L, 0);
+ }
+
+ cx->lineno = 1;
+ return (cx->fp == NULL ? ERR : OK);
+}
+
+
+/* CLOSE_MKPKGFILE -- Close a mkpkgfile. If the file is multiply open (in
+ * software) wait until the last context closes the file to physically close
+ * the file.
+ */
+void
+close_mkpkgfile (register struct context *cx)
+{
+ struct context *find_mkpkgfile();
+
+ if (cx->fp != stdin)
+ if (find_mkpkgfile (cx->prev, cx->mkpkgfile, cx->level) == NULL)
+ fclose (cx->fp);
+}
+
+
+/* FIND_MKPKGFILE -- Search the list of open library contexts for an entry
+ * which already has the named mkpkgfile open.
+ */
+struct context *
+find_mkpkgfile (
+ struct context *head_cx, /* head of context list */
+ char *mkpkgfile, /* file to search for */
+ int level /* subdirectory level */
+)
+{
+ register struct context *cx;
+
+ for (cx=head_cx; cx != NULL; cx=cx->prev)
+ if (cx->level == level && strcmp (cx->mkpkgfile, mkpkgfile) == 0)
+ return (cx);
+
+ return (NULL);
+}
+
+
+/* SEARCH_MKPKGFILE -- Search the mkpkgfile for the named entry. A mkpkg
+ * entry consists of a TOK_FNAME (identifier) followed by TOK_BEGIN (colon),
+ * e.g., "entry:". If a specific module is named, go directly there without
+ * processing any preprocessor directives. If no module is named, search
+ * for the first entry, executing any preprocessor directives encountered
+ * while searching.
+ */
+int
+search_mkpkgfile (register struct context *cx)
+{
+ char word1[SZ_FNAME+1], word2[SZ_FNAME+1];
+ char *prev, *curr, *temp;
+ int tok, gettok();
+
+ if (debug) {
+ printf ("search_mkpkgfile (file=%s, library=%s)\n",
+ cx->mkpkgfile, cx->library);
+ fflush (stdout);
+ }
+
+ /* If a specific module is desired and we are not in search mode,
+ * go directly to the named module without executing any preprocessor
+ * directives.
+ */
+ if (cx->library[0])
+ return (do_goto (cx, cx->library));
+
+ /* Search Makelib file until an entry for the named library is found.
+ * Execute any preprocessor directives encountered while searching.
+ */
+ prev = word1;
+ curr = word2;
+
+ /* Advance to the next entry. If an @subdir reference is
+ * encountered, go process the subdirectory in search mode
+ * and then continue locally.
+ */
+ while ((tok = gettok (cx, curr, SZ_FNAME)) != TOK_BEGIN) {
+ if (tok == 0 || tok == TOK_END) {
+ /* Exit; no entry found.
+ */
+ return (ERR);
+
+ } else if (tok == TOK_FNAME && curr[0] == SUBDIR_CHAR) {
+ /* Continue the search in the context of a subdirectory.
+ */
+ struct context *ncx;
+ char module[SZ_FNAME+1];
+ char subdir[SZ_FNAME+1];
+ char fname[SZ_FNAME+1];
+ int islib;
+
+ /* Push a new context and start over; recursive call.
+ * May "reopen" (soft) the current mkpkg file or the mkpkg
+ * in a subdirectory.
+ */
+ parse_modname (curr, module, subdir, fname);
+ if ((ncx = push_context (cx, module,subdir,fname)) == NULL)
+ exit_status = ERR;
+ else {
+ exit_status = do_mkpkg (ncx, islib=NO);
+ cx = pop_context (ncx);
+ }
+
+ if (exit_status != OK && !ignore)
+ return (exit_status);
+
+ } else {
+ /* Save the old token; pointer swapping rather than copy
+ * used for efficiency.
+ */
+ temp = curr;
+ curr = prev;
+ prev = temp;
+ }
+ }
+
+ strcpy (cx->library, prev); /* return module name */
+ return (OK);
+}
diff --git a/unix/boot/mkpkg/scanlib.c b/unix/boot/mkpkg/scanlib.c
new file mode 100644
index 00000000..cb70efd5
--- /dev/null
+++ b/unix/boot/mkpkg/scanlib.c
@@ -0,0 +1,355 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <ctype.h>
+
+#include <ar.h>
+#ifdef MACOSX
+#include <ranlib.h>
+#include <mach-o/fat.h>
+#endif
+
+#define import_spp
+#include <iraf.h>
+#include "mkpkg.h"
+#include "extern.h"
+
+#ifdef OLD_MACOSX
+#define AR_EFMT1 1
+#endif
+
+
+/*
+ * SCANLIB.C -- Routines to scan a 4.2BSD UNIX archive file and create a
+ * symbol table naming the files in the archive and their dates.
+ *
+ * External entry points:
+ *
+ * h_scanlibrary (libname) extract list of modules and their dates
+ * h_ardate (modname) return long integer module date
+ */
+
+#define SZ_KEY 128 /* arbitrary */
+extern int forceupdate; /* NOT IMPLEMENTED for UNIX */
+
+char mlb_sbuf[SZ_SBUF]; /* string buffer */
+int mlb_op = 0; /* index into string buffer */
+int mlb_index[MAX_LIBFILES]; /* sbuf indices for each symbol */
+long mlb_fdate[MAX_LIBFILES]; /* file date of each module */
+int mlb_modified; /* modified flag */
+char *mlb_filename();
+
+struct dbentry { /* module entry on disk */
+ long fdate;
+ int keylen;
+ /* key chars */
+};
+
+
+/**
+ * Local procedure declarations.
+ */
+int mlb_setdate (char *modname, long fdate);
+
+
+
+/* SCANLIBRARY -- Scan the archive file, extract module names and dates,
+ * building the "ar" module list.
+ */
+int
+h_scanlibrary (char *library)
+{
+ register char *ip, *op;
+ register int i, is_fat = 0;
+ char libfname[SZ_PATHNAME+1];
+ char modname[SZ_KEY+1];
+ char lbuf[SZ_LINE];
+ struct ar_hdr arf;
+ long length, fdate;
+ int len=0, len_arfmag, nmodules;
+ FILE *fp;
+
+ /* Get the library file name. */
+ h_getlibname (library, libfname);
+
+ /* Clear the symbol table.
+ */
+ mlb_modified = NO;
+ mlb_op = 1;
+ nmodules = 0;
+
+ len = 0;
+ for (i=0; i < MAX_LIBFILES; i++)
+ mlb_index[i] = 0;
+
+ /* Open the UNIX archive file.
+ */
+ if ((fp = fopen (libfname, "r")) == NULL) {
+ printf ("warning: library `%s' not found\n", libfname);
+ fflush (stdout);
+ return (0);
+ }
+
+ if (debug) {
+ printf ("scan unix archive %s:\n", libfname);
+ fflush (stdout);
+ }
+
+ /* Verify that file is indeed an archive file.
+ */
+ memset (lbuf, 0, SZ_LINE);
+ fread (lbuf, 1, SARMAG, fp);
+ if (strncmp (lbuf, ARMAG, SARMAG) != 0) {
+#ifndef MACOSX
+ printf ("file `%s' is not a library\n", libfname);
+ goto err;
+#else
+ /* See if it's a FAT archive file.
+ */
+ struct fat_header fh;
+ struct fat_arch fa;
+ char *ip;
+
+ rewind (fp);
+ memset (&fh, 0, sizeof(struct fat_header));
+ fread (&fh, 1, sizeof(struct fat_header), fp); /* read header */
+ if (fh.magic == FAT_MAGIC || fh.magic == FAT_CIGAM) {
+ int narch = 0;
+
+ is_fat++;
+
+ /* The following is a cheat to avoid byte swapping the
+ * nfat_arch field in Intel systems. Assumes we'll never
+ * see more that 8-bits worth of architectures. 8-)
+ */
+ ip = (char *) &fh, ip += 7;
+ memmove (&narch, ip, 1);
+ for (i=0; i < narch; i++) { /* skip headers */
+ memset (&fa, 0, sizeof(struct fat_arch));
+ fread (&fa, 1, sizeof(struct fat_arch), fp);
+ }
+
+ /* Read the AR header.
+ */
+ memset (lbuf, 0, SZ_LINE);
+ fread (lbuf, 1, SARMAG, fp);
+ if (strncmp (lbuf, ARMAG, SARMAG) != 0) {
+ printf ("file `%s' is not a library\n", libfname);
+ goto err;
+ }
+ } else {
+ printf ("file `%s' is not a library\n", libfname);
+ goto err;
+ }
+#endif
+ }
+
+ len_arfmag = strlen (ARFMAG);
+ memset (&arf, 0, sizeof(arf));
+ while ((int)(fread (&arf, 1, sizeof(arf), fp)) > 0) {
+
+ /* Don't scan past the first architecture for FAT libs.
+ */
+ if (is_fat && strncmp (arf.ar_name, ARMAG, SARMAG) == 0)
+ break;
+
+ if (strncmp (arf.ar_fmag, ARFMAG, len_arfmag) != 0) {
+ printf ("cannot decode library `%s'\n", libfname);
+ goto err;
+ }
+
+ if (debug > 1) {
+ char name[17], date[13];
+ strncpy (name, arf.ar_name, 16); name[16] = '\0';
+ strncpy (date, arf.ar_date, 12); date[12] = '\0';
+ printf ("objname='%s', date='%s'\n", name, date);
+ }
+
+ /* Extract module name. */
+ for (ip=arf.ar_name; *ip == ' '; ip++) ;
+ for (op=modname; (*op = *ip++) != ' ' && *op != '/'; op++) ;
+ *op++ = EOS;
+
+ /* Skip dummy entry with null modname (COFF format) as well
+ * as the __SYMDEF from ranlib.
+ */
+#ifdef MACOSX
+ if (strncmp (modname, RANLIBMAG, 9) || modname[0] != EOS) {
+#else
+ if (modname[0] != EOS) {
+#endif
+#if defined(AR_EFMT1) && !defined(__CYGWIN__)
+ /*
+ * BSD 4.4 extended AR format: #1/<namelen>, with name as the
+ * first <namelen> bytes of the file
+ */
+ if ((arf.ar_name[0] == '#') &&
+ (arf.ar_name[1] == '1') &&
+ (arf.ar_name[2] == '/') && (isdigit(arf.ar_name[3]))) {
+
+ char p[SZ_PATHNAME];
+
+ len = atoi(&arf.ar_name[3]);
+ bzero (p, SZ_PATHNAME);
+ if (fread(p, len, 1, fp) != 1) {
+ fprintf (stderr, "%s: premature EOF", libfname);
+ }
+ bzero (modname, SZ_KEY+1);
+ sprintf (modname, "%s", p);
+ } else
+ len = 0;
+#endif
+ /* Get module date. */
+ sscanf (arf.ar_date, "%ld", &fdate);
+
+ /* Insert entry into symbol table. */
+ mlb_setdate (modname, fdate);
+ }
+
+ /* Advance to the next entry.
+ */
+ if (sscanf (arf.ar_size, "%ld", &length) == 1) {
+ if (length & 1) /* must be even */
+ length++;
+#if defined(AR_EFMT1) && !defined(__CYGWIN__)
+ fseek (fp, length-len, 1);
+#else
+ fseek (fp, length, 1);
+#endif
+ } else {
+ printf ("could not decode length `%s' of library module\n",
+ arf.ar_size);
+ goto err;
+ }
+
+ memset (&arf, 0, sizeof(arf));
+ }
+
+ fclose (fp);
+ return (nmodules);
+
+err:
+ fflush (stdout);
+ fclose (fp);
+ return (ERR);
+}
+
+
+/* H_ARDATE -- Look up file in archive. If found, return date of archive
+ * version, otherwise return zero. This is the entry point called by MKLIB
+ * to get the update date of a library module.
+ */
+long
+h_ardate (char *fname)
+{
+ extern char *makeobj();
+ long mlb_getdate();
+
+ return (mlb_getdate (makeobj (fname)));
+}
+
+
+/* MLB_SETDATE -- Enter the given module and file date into the symbol table,
+ * or update the file date if the module is already present in the table.
+ */
+int
+mlb_setdate (
+ char *modname, /* module name */
+ long fdate /* object file date */
+)
+{
+ register int hashval, keylen, i;
+ register char *ip;
+ int start;
+
+
+ if (*modname == EOS || fdate <= 0) {
+ printf ("warning, mlb_setdate: attempted illegal entry for %s\n",
+ modname);
+ fflush (stdout);
+ return (ERR);
+ }
+
+ /* Hash the key.
+ */
+ for (hashval=0, keylen=0, ip=modname; *ip; ip++, keylen++)
+ hashval += hashval + *ip;
+ start = hashval % MAX_LIBFILES;
+
+ mlb_modified = YES;
+
+ /* Update the entry if the module is already in the table, else find
+ * an empty slot, checking for table overflow in the process.
+ */
+ for (i=start; mlb_index[i]; ) {
+ ip = &mlb_sbuf[mlb_index[i]];
+ if (*ip == *modname)
+ if (strncmp (modname, ip, keylen) == 0) {
+ mlb_fdate[i] = fdate;
+ return (OK);
+ }
+ if (++i >= MAX_LIBFILES)
+ i = 0;
+ if (i == start) {
+ printf ("error: library module list overflow\n");
+ fflush (stdout);
+ return (ERR);
+ }
+ }
+
+ if (mlb_op + keylen + 1 >= SZ_SBUF) {
+ printf ("error: library module list string buffer overflow\n");
+ fflush (stdout);
+ return (ERR);
+ }
+
+ /* Enter the module into the symbol table.
+ */
+ mlb_index[i] = mlb_op;
+ mlb_fdate[i] = fdate;
+
+ strcpy (&mlb_sbuf[mlb_op], modname);
+ mlb_op += (keylen + 1);
+
+ return (OK);
+}
+
+
+/* MLB_GETDATE -- Lookup a module in the symbol table and return its date.
+ * Return zero if the module is not found.
+ */
+long
+mlb_getdate (char *modname)
+{
+ register int hashval, keylen, i;
+ register char *ip;
+ int start;
+
+ if (*modname == EOS)
+ return (0L);
+
+ /* Hash the key.
+ */
+ for (hashval=0, keylen=0, ip=modname; *ip; ip++, keylen++)
+ hashval += hashval + *ip;
+ start = hashval % MAX_LIBFILES;
+
+ /* Search the symbol table for the named module.
+ */
+ for (i=start; mlb_index[i]; ) {
+ ip = &mlb_sbuf[mlb_index[i]];
+ if (*ip == *modname)
+ if (strncmp (modname, ip, keylen) == 0)
+ return (mlb_fdate[i]);
+ if (++i >= MAX_LIBFILES)
+ i = 0;
+ if (i == start)
+ return (0L);
+ }
+
+ return (0L);
+}
diff --git a/unix/boot/mkpkg/sflist.c b/unix/boot/mkpkg/sflist.c
new file mode 100644
index 00000000..e487df77
--- /dev/null
+++ b/unix/boot/mkpkg/sflist.c
@@ -0,0 +1,321 @@
+/* Copyright(c) 1986 Association of Universities for Research in Astronomy Inc.
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+
+#define import_spp
+#define import_error
+#include <iraf.h>
+
+#include "mkpkg.h"
+#include "extern.h"
+#include "../bootProto.h"
+
+
+/*
+ * SFLIST.C -- Special file list package. The special file list is a list of
+ * library module list source files which need special processing on a given
+ * host system. Examples of such files are files which have been optimized in
+ * a machine dependent way, e.g., in assembler or C, or files which must be
+ * compiled in a nonstandard way due to host compiler bugs. The special file
+ * list makes this special processing possible without having to modify the
+ * mkpkg files in the portable system in a host dependent way, concentrating
+ * all knowledge of those parts of the system which have been tailored for the
+ * local host into a single, easily modifiable table file stored in HLIB.
+ *
+ * External functions:
+ *
+ * sf_scanlist (cx) # parse $special file list
+ * sflist = sf_dirsearch (dirname) # lookup directory in sflist
+ * sfp = sf_filesearch (sflist, filename) # lookup file in dir file list
+ * sf_prune (cp) # free space in string buffer
+ *
+ * where
+ *
+ * struct context *cx;
+ * struct sfile *sflist, *sfp;
+ * char *filename, *dirname;
+ *
+ * The special file list is organized by source directory to speed searches
+ * (most directories will not contain any files needing special processing,
+ * eliminating the need to lookup the files in module lists in that directory)
+ * and to reduce storage requirements for the list. The special file database
+ * thus consists of a list of directories containing special files, and for
+ * each directory, a pointer to a linked list of special file entries, one
+ * for each special file in the directory. Since the organization by directory
+ * tends to produce a database consisting of very short file lists, we use a
+ * linked list rather than a hash table for the file lists.
+ *
+ * For each special file we record the standard file name, the pathname of
+ * the special file to be used, and a command to be pushed back into the MKPKG
+ * command input stream to generate the object file for the module.
+ * The special file name may be the same as the standard file name, e.g, if
+ * the standard file only needs to be compiled in a nonstandard way. If the
+ * mkobj string is null the special file name will simply be returned in the
+ * module list, and compiled with XC using the default compile flags.
+ */
+
+static int sf_ndirs = 0; /* no. of directories */
+static int sf_nfiles = 0; /* no. of special files */
+static char *sf_dirs[MAX_SFDIRS]; /* source directories */
+static struct sfile *sf_flist[MAX_SFDIRS]; /* directory file lists */
+static struct sfile sf_files[MAX_SFFILES]; /* special file list */
+static char nullstr[] = "";
+
+
+/* SF_SCANLIST -- Called when the $special macro preprocessor directive is
+ * encountered to parse a special file list, entering each file listed into
+ * the special file list database. The syntax of a $special special file
+ * list directive is as follows:
+ *
+ * $special dirname:
+ * stname1 sfname1 mkobj_command1
+ * stname2 sfname2 mkobj_command2
+ * ...
+ * stnameN sfnameN mkobj_commandN
+ * ;
+ *
+ * where any string value may optionally be quoted, and the mkobj command
+ * strings are optional. The token "&" in <sfname> or <mkobj_command> is
+ * replaced by <stname>.
+ */
+int
+sf_scanlist (
+ struct context *cx /* current mkpkg context */
+)
+{
+ register struct sfile *sfp;
+ register char *ip, *op, *tp;
+
+ char dirname[SZ_PATHNAME+1];
+ char stname[SZ_PATHNAME+1];
+ char sfname[SZ_PATHNAME+1];
+ char mkobj[SZ_CMD+SZ_PATHNAME+1];
+ char token[SZ_CMD+1];
+ struct sfile *head, *tail;
+ int tok, nfiles, eol=0;
+ char *old_cp;
+
+ old_cp = cp; /* mark position in sbuf */
+ nfiles = 0;
+
+ /* Get the directory name. */
+ if (gettok (cx, token, SZ_LINE) != TOK_FNAME) {
+ warns ("missing directory name in special file list", "");
+ goto err;
+ } else
+ os_fpathname (token, dirname, SZ_PATHNAME);
+
+ if (debug) {
+ printf ("scan special file list for directory %s\n",
+ debug > 1 ? dirname : token);
+ fflush (stdout);
+ }
+
+ /* Advance to the start of the module list. */
+ while ((tok = gettok (cx, token, SZ_LINE)) != TOK_BEGIN)
+ if (tok == EOF || tok == TOK_END)
+ goto err;
+
+ /* Get a pointer to the last element in the special file list for
+ * the named directory. If this is the first entry for the named
+ * directory, enter the name in the symbol table and set the sflist
+ * pointer to NULL.
+ */
+ if ((head = sf_dirsearch (dirname)) == NULL) {
+ sf_dirs[sf_ndirs++] = putstr (dirname);
+ if (sf_ndirs >= MAX_SFDIRS)
+ fatals ("too many special file list directories: %s", dirname);
+ tail = NULL;
+ } else {
+ for (tail=sfp=head; sfp; sfp=sfp->sf_next)
+ tail = sfp;
+ }
+
+ /* Read successive entries from the special file list for the named
+ * directory, entering each file at the tail of the list.
+ */
+ while (!eol && (tok = gettok (cx, token, SZ_LINE)) != TOK_END) {
+ if (tok == EOF || tok == TOK_END)
+ break;
+
+ /* Get standard file name (module name). */
+ if (tok == TOK_NEWLINE)
+ continue; /* blank line */
+ else if (tok != TOK_FNAME)
+ goto badline;
+ else
+ strcpy (stname, token);
+
+ /* Get the special file name. */
+ if ((tok = gettok (cx, sfname, SZ_PATHNAME)) == TOK_END)
+ eol++;
+ if (tok != TOK_FNAME)
+ goto badline;
+
+ /* Get the mkobj command string, if any. */
+ if ((tok = gettok (cx, token, SZ_LINE)) == TOK_NEWLINE) {
+ mkobj[0] = EOS;
+ } else if (tok == TOK_END) {
+ mkobj[0] = EOS;
+ eol++;
+ } else if (tok != TOK_FNAME) {
+ goto badline;
+ } else {
+ /* Extract the command string, expanding any "&" filename
+ * references therein.
+ */
+ for (ip=token, op=mkobj; (*op = *ip++); op++)
+ if (*op == '&') {
+ for (tp=stname; (*op = *tp++); op++)
+ ;
+ --op;
+ }
+ }
+
+ if (debug)
+ printf ("file %s -> %s, mkobj = `%s'\n",
+ stname, (sfname[0] == '&') ? stname : sfname, mkobj);
+
+ /* Add the file to the tail of the file list. */
+ nfiles++;
+ sfp = &sf_files[sf_nfiles++];
+ if (sf_nfiles >= MAX_SFFILES)
+ fatals ("too many special files: %s", stname);
+
+ sfp->sf_stname = putstr (stname);
+ sfp->sf_sfname = (sfname[0]=='&') ? sfp->sf_stname : putstr(sfname);
+ sfp->sf_mkobj = mkobj[0] ? putstr(mkobj) : nullstr;
+ sfp->sf_next = NULL;
+
+ if (tail) {
+ tail->sf_next = sfp;
+ tail = sfp;
+ } else
+ sf_flist[sf_ndirs-1] = head = tail = sfp;
+
+ continue;
+badline:
+ /* Print message and discard rest of line, but do not quit. */
+ warns ("bad token `%s' in special file list", token);
+ while (!eol && (tok = gettok (cx, token, SZ_LINE)) != TOK_NEWLINE)
+ if (tok == TOK_END)
+ break;
+ else if (tok == EOF)
+ goto err;
+ }
+
+ if (debug) {
+ printf ("%d special files added; total ndirs=%d, nfiles=%d\n",
+ nfiles, sf_ndirs, sf_nfiles);
+ fflush (stdout);
+ }
+
+ if (nfiles == 0) {
+ warns ("empty special file list for %s", dirname);
+ sf_prune (cp = old_cp);
+ return (ERR);
+ } else
+ return (OK);
+
+err:
+ /* Discard rest of directive. */
+ while (!eol && (tok = gettok (cx, token, SZ_LINE)) != TOK_END)
+ if (tok == EOF || tok == TOK_END)
+ break;
+
+ /* Return memory and sfile database space. */
+ sf_prune ((cp = old_cp));
+
+ return (ERR);
+}
+
+
+/* SF_DIRSEARCH -- Search the special file database for the named directory,
+ * returning a pointer to the special file list for that directory if the
+ * directory is found, else NULL. Note that directory names are stored as
+ * host system pathnames (so that any equivalent form of reference may be used
+ * in the mkpkg files), and we assume that we are called with the directory
+ * pathname already resolved.
+ */
+struct sfile *
+sf_dirsearch (
+ char *dirname /* host pathname of directory */
+)
+{
+ register int i;
+
+ if (debug) {
+ printf ("search sflist for directory %s\n", dirname);
+ fflush (stdout);
+ }
+
+ for (i=0; i < sf_ndirs; i++)
+ if (h_direq (sf_dirs[i], dirname))
+ return (sf_flist[i]);
+
+ return (NULL);
+}
+
+
+/* SF_FILESEARCH -- Search the special file list for a directory for the named
+ * file. File names are stored in the list by the name given in the library
+ * module list in the mkpkg file. If the named file is found a pointer to the
+ * special file descriptor for that file is returned, otherwise NULL is
+ * returned. Note that "file*" is a prefix match, whereas "file" requires an
+ * exact match.
+ */
+struct sfile *
+sf_filesearch (
+ struct sfile *sflist, /* special file list */
+ char *stname /* standard file name */
+)
+{
+ register struct sfile *sfp;
+ register char *p1, *p2;
+
+ for (sfp=sflist; sfp; sfp=sfp->sf_next) {
+ for (p1=sfp->sf_stname, p2=stname; *p1 && *p1 == *p2; p1++, p2++)
+ ;
+ if ((*p1 == EOS && *p2 == EOS) || *p1 == '*')
+ return (sfp);
+ }
+
+ return (NULL);
+}
+
+
+/* SF_PRUNE -- Prune the special file database back to the given point in the
+ * string buffer.
+ */
+void
+sf_prune (
+ register char *cp /* lop off everything here and above */
+)
+{
+ register struct sfile *sfp, *sf_top;
+ register int i;
+
+ /* Prune the directory list. */
+ for (i=0; i < sf_ndirs; i++)
+ if (sf_dirs[i] >= cp || sf_flist[i]->sf_stname >= cp) {
+ sf_ndirs = i;
+ break;
+ }
+
+ /* Prune the global file list. */
+ for (i=0; i < sf_nfiles; i++)
+ if (sf_files[i].sf_stname >= cp) {
+ sf_nfiles = i;
+ break;
+ }
+
+ /* Prune the individual directory file lists. */
+ for (i=0, sf_top = &sf_files[sf_nfiles]; i < sf_nfiles; i++) {
+ sfp = &sf_files[i];
+ if (sfp->sf_next >= sf_top)
+ sfp->sf_next = NULL;
+ }
+}
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);
+}