From 40e5a5811c6ffce9b0974e93cdd927cbcf60c157 Mon Sep 17 00:00:00 2001 From: Joe Hunkeler Date: Tue, 11 Aug 2015 16:51:37 -0400 Subject: Repatch (from linux) of OSX IRAF --- unix/boot/mkpkg/README | 54 ++ unix/boot/mkpkg/char.c | 478 +++++++++++++++ unix/boot/mkpkg/extern.h | 18 + unix/boot/mkpkg/fdcache.c | 190 ++++++ unix/boot/mkpkg/fncache.c | 228 +++++++ unix/boot/mkpkg/host.c | 917 ++++++++++++++++++++++++++++ unix/boot/mkpkg/main.c | 347 +++++++++++ unix/boot/mkpkg/mkpkg | 33 + unix/boot/mkpkg/mkpkg.h | 254 ++++++++ unix/boot/mkpkg/mkpkg.hlp | 626 +++++++++++++++++++ unix/boot/mkpkg/mkpkg.sh | 9 + unix/boot/mkpkg/pkg.c | 902 ++++++++++++++++++++++++++++ unix/boot/mkpkg/scanlib.c | 355 +++++++++++ unix/boot/mkpkg/sflist.c | 321 ++++++++++ unix/boot/mkpkg/tok.c | 1457 +++++++++++++++++++++++++++++++++++++++++++++ 15 files changed, 6189 insertions(+) create mode 100644 unix/boot/mkpkg/README create mode 100644 unix/boot/mkpkg/char.c create mode 100644 unix/boot/mkpkg/extern.h create mode 100644 unix/boot/mkpkg/fdcache.c create mode 100644 unix/boot/mkpkg/fncache.c create mode 100644 unix/boot/mkpkg/host.c create mode 100644 unix/boot/mkpkg/main.c create mode 100644 unix/boot/mkpkg/mkpkg create mode 100644 unix/boot/mkpkg/mkpkg.h create mode 100644 unix/boot/mkpkg/mkpkg.hlp create mode 100644 unix/boot/mkpkg/mkpkg.sh create mode 100644 unix/boot/mkpkg/pkg.c create mode 100644 unix/boot/mkpkg/scanlib.c create mode 100644 unix/boot/mkpkg/sflist.c create mode 100644 unix/boot/mkpkg/tok.c (limited to 'unix/boot/mkpkg') 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 +#include +#include +#include +#include + +#define import_spp +#define import_error +#include + +#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 +#include +#include + +/* + * 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 +#include +#include + +//#include "../bootProto.h" + + +/* + * FNCACHE -- Maintain a cache of system logical filenames (e.g., ) + * 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 +#include +#include +#include +#include +#include +#include +#include + +#define import_spp +#define import_error +#include +#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 (" 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 (" 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 + * + * //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 +#include +#include +#include +#include + +#define import_spp +#define import_knames +#define import_error + +#include + +#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 + !$(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 + fdcache.c + fncache.c + host.c + pkg.c extern.h mkpkg.h + scanlib.c + sflist.c mkpkg.h extern.h + tok.c extern.h mkpkg.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., "". 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 + environ.x environ.com environ.h \ + + main.x \ + \ + + onentry.x + spline.x + ; +.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 + 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 +#include +#include +#include +#include + +#define import_spp +#define import_error +#include + +#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 (" 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 (" 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 +#include +#include +#include +#include + +#include +#ifdef MACOSX +#include +#include +#endif + +#define import_spp +#include +#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/, with name as the + * first 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 +#include +#include + +#define import_spp +#define import_error +#include + +#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 or is + * replaced by . + */ +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 +#include +#include +#include +#include + +#define import_spp +#define import_error +#include + +#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 (" 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 (" 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 (" 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 (" 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 (" 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); +} -- cgit