brownc@utah-cs.UUCP (Eric C. Brown) (05/08/85)
# This is a shell archive. Remove anything before this line, then # unpack it by saving it in a file and typing "sh file". (Files # unpacked will be owned by you and have default permissions.) # # This archive contains: # lar.c lar.h lwtol.c make.man readme makefile echo x - lar.c cat > "lar.c" << '//E*O*F lar.c//' /* * Lar - LAR format library file maintainer * by Stephen C. Hemminger * linus!sch or sch@Mitre-Bedford * * Heavily hacked for MS-DOS by Eric C. Brown * utah-cs!brownc or brownc@utah-cs * * Usage: lar key library [files] ... * * Key functions are: * u - Update, add files to library * t - Table of contents * e - Extract files from library * a - extract All files from library * p - Print files in library * d - Delete files in library * r - Reorganize library * Other keys: * v - Verbose * * This program is public domain software, no warranty intended or * implied. * * DESCRPTION * Lar is a MS-DOS program to manipulate program libraries. * The primary use of lar is to combine several files together * to reduce disk space used. * Lar maintains the date and time of files in the library, so * that Make can extract the date/time for rebuilding a file. * When files are restored, the file has the original date/time * rather than the date/time when the file was extracted. * The original CP/M library program LU is the product * of Gary P. Novosielski. * * PORTABILITY * The code is modeled after the Software tools archive program, * and is setup for Version 7 Unix. It does not make any assumptions * about byte ordering, explict and's and shift's are used. * If you have a dumber C compiler, you may have to recode new features * like structure assignment, typedef's and enumerated types. * * * Unix is a trademark of Bell Labs. * ** CP/M is a trademark of Digital Research. */ #include "lar.h" /* Globals */ char *fname[MAXFILES]; bool ftouched[MAXFILES]; struct ludir ldir[MAXFILES]; int errcnt, nfiles, nslots; bool verbose = false; char *getname(), *malloc(), *fgets(); long fcopy(), lseek(), rlwtol(); int update(), reorg(), table(), extract(), print(), delete(), getall(); typedef int (*PFI)(); main (argc, argv) int argc; char **argv; { register char *flagp; char *aname; /* name of library file */ PFI function = (PFI) NULL; /* function to do on library */ /* set the function to be performed, but detect conflicts */ #define setfunc(val) if(function != (PFI) NULL) conflict(); else function = val if (argc < 3) help (); aname = argv[2]; filenames (argc, argv); for(flagp = argv[1]; *flagp; flagp++) switch (*flagp) { case 'u': setfunc(update); break; case 't': setfunc(table); break; case 'e': setfunc(extract); break; case 'a': setfunc(getall); break; case 'p': setfunc(print); break; case 'd': setfunc(delete); break; case 'r': setfunc(reorg); break; case 'v': verbose = true; break; default: help (); } if(function == (PFI) NULL) { fputs("No function key letter specified\n", stderr); help(); } (*function)(aname); exit(0); } /* print error message and exit */ help () { fputs ("Usage: lar {uteapdr}[v] library [files] ...\n", stderr); fputs ("Functions are:\n\tu - Update, add files to library\n", stderr); fputs ("\tt - Table of contents\n", stderr); fputs ("\te - Extract files from library\n", stderr); fputs ("\ta - extract All files from library\n", stderr); fputs ("\tp - Print files in library\n", stderr); fputs ("\td - Delete files in library\n", stderr); fputs ("\tr - Reorganize library\n", stderr); fputs ("Flags are:\n\tv - Verbose\n", stderr); exit (1); } conflict() { fputs ("Conficting keys\n", stderr); help(); } /* * This ltoa doesn't call in the floating point library: * CI C86 does an sprintf! */ ltoa(val, buf) long val; register char *buf; { register int i; int j; char tbuf[20]; if (val == 0) { buf[0] = '0'; buf[1] = '\0'; return; } i = 19; while (val != 0) { tbuf[i--] = (val % 10) + '0'; val /= 10; } i++; for (j = 0; i <= 19; i++, j++) buf[j] = tbuf[i]; buf[j] = '\0'; } /* Get file names, check for dups, and initialize */ filenames (ac, av) int ac; char **av; { register int i, j=0; int k, loop; struct ffblk ff; bool iswild(); errcnt = 0; for (i = 0; i < ac - 3; i++) { if (iswild(av[i + 3])) { k = findfirst(av[i + 3], &ff, 0); while (k == 0) { fname[j] = malloc(strlen(ff.ff_name)+1); if (fname[j] == NULL) error("Out of core.."); strcpy(fname[j], ff.ff_name); for (loop = 0; ff.ff_name[loop] != 0; loop++) fname[j][loop] = tolower(ff.ff_name[loop]); ftouched[j] = false; if (j == MAXFILES) error ("Too many file names."); j++; k = findnext(&ff); } /* while */ } /* if-then */ else { /* not a wildcard */ fname[j] = av[i+3]; ftouched[j] = false; if (j == MAXFILES) error ("Too many file names."); j++; } /* else */ } /* for */ checkdups(j); } bool iswild(str) register char *str; { while (*str != '\0') { if (*str == '*' || *str == '?') return true; else str++; } return false; } checkdups(i) register int i; { register int j; fname[i] = NULL; nfiles = i; for (i = 0; i < nfiles; i++) for (j = i + 1; j < nfiles; j++) if (equal (fname[i], fname[j])) { fputs (fname[i], stderr); error (": duplicate file name"); } } table (lib) char *lib; { fildesc lfd; register int i; register struct ludir *lptr; long total, offset, size; int active = 0, unused = 0, deleted = 0, k; char *uname, buf[20]; if ((lfd = _open (lib, O_RDWR)) == ERROR) cant (lib); getdir (lfd); total = lwtol(ldir[0].l_len); if (verbose) { puts("Name Index Length"); fputs("Directory ", stdout); ltoa(total, buf); puts(buf); } for (i = 1, lptr = &ldir[1]; i < nslots; i++,lptr++) switch(lptr->l_stat) { case ACTIVE: active++; uname = getname(lptr->l_name, lptr->l_ext); if (filarg (uname)) if(verbose) { offset = lwtol(lptr->l_off); size = lwtol(lptr->l_len); fputs(uname, stdout); for (k = 1; k < 18 - strlen(uname); k++) fputc(' ', stdout); ltoa(offset, buf); fputs(buf, stdout); for (k = 1; k < 16 - strlen(buf); k++) fputc(' ', stdout); ltoa(size, buf); puts(buf, stdout); } else puts(uname); total += lwtol(lptr->l_len); break; case UNUSED: unused++; break; default: deleted++; } if(verbose) { puts("--------------------------------------"); fputs("Total bytes ", stdout); ltoa(total, buf); puts(buf); fputs("\nLibrary ", stdout); fputs(lib, stdout); fputs(" has ", stdout); itoa(nslots, buf); fputs(buf, stdout); fputs(" slots, ", stdout); itoa(deleted, buf); fputs(buf, stdout); fputs(" deleted ", stdout); itoa(active, buf); fputs(buf, stdout); fputs(" active, ", stdout); itoa(unused, buf); fputs(buf, stdout); puts(" unused"); } VOID _close (lfd); not_found (); } putdir (f) fildesc f; { lseek(f, 0L, 0); /* rewind f */ if (_write (f, (char *) ldir, DSIZE * nslots) != nslots * DSIZE) error ("Can't write directory - library may be botched"); } initdir (f) fildesc f; { register int i; long numbytes; char line[80]; static struct ludir blankentry = { UNUSED, { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' }, { ' ', ' ', ' ' }, }; static struct ludir nameentry = { ACTIVE, { 'l', 'a', 'r', 'f', 'o', 'r', 'm', 't' }, { 'a', 'r', 'c' }, }; for (;;) { fputs ("Number of slots to allocate (1-255): ", stdout); if (fgets (line, 80, stdin) == NULL) error ("Eof when reading input"); nslots = atoi (line); if (nslots < 1) puts ("Must have at least one!"); else if (nslots > MAXFILES) puts ("Too many slots"); else break; } numbytes = nslots * DSIZE; for (i = 1; i < nslots; i++) ldir[i] = blankentry; ldir[0] = nameentry; ltolw (ldir[0].l_len, numbytes); ltolw (ldir[0].l_datetime, -1L); /* make funny date field */ putdir (f); } putname (cpmname, unixname) char *cpmname, *unixname; { register char *p1, *p2; for (p1 = unixname, p2 = cpmname; *p1; p1++, p2++) { while (*p1 == '.') { p2 = cpmname + 8; p1++; } if (p2 - cpmname < 11) *p2 = islower(*p1) ? toupper(*p1) : *p1; else { fputs (unixname, stderr); fputs (": name truncated\n", stderr); break; } } while (p2 - cpmname < 11) *p2++ = ' '; } /* filarg - check if name matches argument list */ filarg (name) char *name; { register int i; if (nfiles <= 0) return 1; for (i = 0; i < nfiles; i++) if (equal (name, fname[i])) { ftouched[i] = true; return 1; } return 0; } not_found () { register int i; for (i = 0; i < nfiles; i++) if (!ftouched[i]) { fputs(fname[i], stderr); fputs(": not in library.\n", stderr); errcnt++; } } extract(name) char *name; { getfiles(name, false); } print(name) char *name; { getfiles(name, true); } getall (libname) char *libname; { fildesc lfd, ofd; register int i; register struct ludir *lptr; union timer timeunion; extern int errno; char *unixname; if ((lfd = _open (libname, O_RDWR)) == ERROR) cant (libname); getdir (lfd); for (i = 1, lptr = &ldir[1]; i < nslots; i++, lptr++) { if(lptr->l_stat != ACTIVE) continue; unixname = getname (lptr->l_name, lptr->l_ext); fputs(unixname, stderr); ofd = _creat(unixname, 0); if (ofd == ERROR) { fputs (" - can't create", stderr); errcnt++; } else { VOID lseek (lfd, (long) lwtol (lptr->l_off), 0); acopy (lfd, ofd, lwtol (lptr->l_len)); timeunion.realtime = lwtol(lptr->l_datetime); VOID setftime(ofd, &(timeunion.ftimep)); VOID _close (ofd); } putc('\n', stderr); } VOID _close (lfd); not_found (); } getfiles (name, pflag) char *name; bool pflag; { fildesc lfd, ofd; register int i; register struct ludir *lptr; union timer timeunion; extern int errno; char *unixname; if ((lfd = _open (name, O_RDWR)) == ERROR) cant (name); ofd = pflag ? fileno(stdout) : ERROR; getdir (lfd); for (i = 1, lptr = &ldir[1]; i < nslots; i++, lptr++) { if(lptr->l_stat != ACTIVE) continue; unixname = getname (lptr->l_name, lptr->l_ext); if (!filarg (unixname)) continue; fputs(unixname, stderr); if (ofd != fileno(stdout)) ofd = _creat(unixname, 0); if (ofd == ERROR) { fputs (" - can't create", stderr); errcnt++; } else { VOID lseek (lfd, (long) lwtol (lptr->l_off), 0); acopy (lfd, ofd, lwtol (lptr->l_len)); timeunion.realtime = lwtol(lptr->l_datetime); if (ofd != fileno(stdout)) { VOID setftime(ofd, &(timeunion.ftimep)); VOID _close (ofd); } } putc('\n', stderr); } VOID _close (lfd); not_found (); } acopy (fdi, fdo, nbytes) /* copy nbytes from fdi to fdo */ fildesc fdi, fdo; long nbytes; { register int btr, retval; char blockbuf[BLOCK]; for (btr = (nbytes > BLOCK) ? BLOCK : (int) nbytes; btr > 0; nbytes -= BLOCK, btr = (nbytes > BLOCK) ? BLOCK : (int) nbytes) { if ((retval = _read(fdi, blockbuf, btr)) != btr) { if( retval == 0 ) { error("Premature EOF\n"); } if( retval == ERROR) error ("Can't read"); } if ((retval = _write(fdo, blockbuf, btr)) != btr) { if( retval == ERROR ) error ("Write Error"); } } } update (name) char *name; { fildesc lfd; register int i; if ((lfd = _open(name, O_RDWR)) == ERROR) { if ((lfd = _creat (name, 0)) == ERROR) { cant (name); } else initdir (lfd); } else getdir (lfd); /* read old directory */ if(verbose) fputs ("Updating files:\n", stderr); for (i = 0; i < nfiles; i++) addfil (fname[i], lfd); if (errcnt == 0) putdir (lfd); else fputs("fatal errors - library not changed\n", stderr); VOID _close (lfd); } addfil (name, lfd) char *name; fildesc lfd; { fildesc ifd; register int i; register struct ludir *lptr; long byteoffs, numbytes; union timer timeunion; if ((ifd = _open(name, O_RDWR)) == ERROR) { fputs("LAR: can't find ", stderr); fputs(name, stderr); fputs(" to add\n", stderr); errcnt++; return; } if(verbose) { fputs(name, stderr); fputs("\n", stderr); } for (i = 1, lptr = ldir+1; i < nslots; i++, lptr++) { if (equal( getname (lptr->l_name, lptr->l_ext), name) ) /* update */ break; if (lptr->l_stat != ACTIVE) break; } if (i >= nslots) { fputs(name, stderr); fputs(": can't add library is full\n",stderr); errcnt++; return; } lptr->l_stat = ACTIVE; putname (lptr->l_name, name); byteoffs = lseek(lfd, 0L, 2); /* append to end; return byte offset */ ltolw (lptr->l_off, byteoffs); numbytes = fcopy (ifd, lfd); ltolw (lptr->l_len, numbytes); getftime(ifd, &(timeunion.ftimep)); ltolw (lptr->l_datetime, timeunion.realtime); VOID _close (ifd); } long fcopy (ifd, ofd) /* copy file ifd (file) to ofd (library) */ fildesc ifd, ofd; { long total = 0L; register int n; char blockbuf[BLOCK]; while ( (n = _read(ifd, blockbuf, BLOCK)) > 0) { if (_write(ofd, blockbuf, n) != n) error("write error"); total += (long) n; } return total; } delete (lname) char *lname; { fildesc f; register int i; register struct ludir *lptr; if ((f = _open(lname, O_RDWR)) == ERROR) cant (lname); if (nfiles <= 0) error("delete by name only"); getdir (f); for (i = 0, lptr = ldir; i < nslots; i++, lptr++) { if (!filarg ( getname (lptr->l_name, lptr->l_ext))) continue; lptr->l_stat = DELETED; } not_found(); if (errcnt > 0) fputs ("errors - library not updated\n", stderr); else putdir (f); VOID _close (f); } reorg (name) char *name; { fildesc olib, nlib; int oldsize, k; register struct ludir *optr; register int i, j; struct ludir odir[MAXFILES], *nptr; char tmpname[80], buf[10]; char *mktemp(); strcpy(tmpname, mktemp("libXXXXXX")); if( (olib = _open(name, O_RDWR)) == ERROR) cant(name); if( (nlib = _creat(tmpname, 0)) == ERROR) cant(tmpname); getdir(olib); fputs("Old library has ", stdout); itoa(oldsize = nslots, buf); fputs(buf, stdout); puts(" slots."); /* copy ldir into odir */ for(i = 0, optr = ldir, nptr = odir ; i < nslots ; i++, optr++, nptr++) movmem((char *) optr, (char *) nptr, sizeof(struct ludir)); /* reinit ldir */ initdir(nlib); errcnt = 0; /* copy odir's files into ldir */ for (i = j = 1, optr = odir+1; i < oldsize; i++, optr++) { if( optr->l_stat == ACTIVE ) { if(verbose) { fputs("Copying: ", stderr); for (k = 0; k < 8; k++) fputc(optr->l_name[k], stderr); fputc('.', stderr); for (k = 0; k < 3; k++) fputc(optr->l_ext[k], stderr); fputc('\n', stderr); } copyentry( optr, olib, &ldir[j], nlib); if (++j >= nslots) { errcnt++; fputs("Not enough room in new library\n", stderr); break; } } } VOID _close(olib); putdir(nlib); VOID _close (nlib); if(errcnt == 0) { if (unlink(name) < 0 || rename(tmpname, name) < 0) { cant(name); exit(1); } } else fputs("Errors, library not updated\n", stderr); VOID unlink(tmpname); } copyentry( old, of, new, nf ) struct ludir *old, *new; fildesc of, nf; { long byteoffs, numbytes; register int btr; char blockbuf[BLOCK]; new->l_stat = ACTIVE; movmem(old->l_name, new->l_name, 8); /* copy name */ movmem(old->l_ext, new->l_ext, 3); /* copy extension */ numbytes = lwtol(old->l_datetime); /* copy date & time */ ltolw(new->l_datetime, numbytes); VOID lseek(of, (long) lwtol(old->l_off), 0); byteoffs = lseek(nf, 0L, 2); /* append to end; return new pos. */ ltolw (new->l_off, byteoffs); numbytes = lwtol(old->l_len); ltolw (new->l_len, numbytes); for (btr = (numbytes > BLOCK) ? BLOCK : (int) numbytes; btr > 0; numbytes -= BLOCK, btr = (numbytes > BLOCK) ? BLOCK : (int) numbytes ) { if (_read(of, blockbuf, btr) != btr) { error ("Read Error in CopyEntry"); } if (_write(nf, blockbuf, btr) != btr) { error ("Write Error in CopyEntry"); } } } //E*O*F lar.c// echo x - lar.h cat > "lar.h" << '//E*O*F lar.h//' /* LAR Version 2.0 */ #include <stdio.h> #include <ctype.h> #include <fcntl.h> #include <findfirs.h> #include <bdos.h> #define ACTIVE 00 #define UNUSED 0xff #define DELETED 0xfe #define MAXFILES 256 #define BLOCK 8192 #define DSIZE ( sizeof(struct ludir) ) #define equal(s1, s2) ( strcmp(s1,s2) == 0 ) /* if you don't have void type just define as blank */ #define VOID (void) #define false 0 #define true (char) 0xff typedef unsigned char bool; typedef unsigned char byte; typedef struct { byte hibyte; byte mbyte2; byte mbyte1; byte lobyte; } lword; typedef struct { byte hibyte; byte lobyte; } word; /* convert word to int */ #define wtoi(w) ((w.hibyte<<8) + w.lobyte) /* convert int to word */ #define itow(dst, src) (dst.hibyte = ((src & 0xff00) >> 8), dst.lobyte = (src & 0x00ff)) /* convert lword to long */ /* works, but is much slower than stupid assembler routine #define lwtol(lw) ( ((long) lw.hibyte<<24) + ((long) lw.mbyte2<<16) + ((long) lw.mbyte1<<8) + (long) lw.lobyte) */ /* cheap hack */ long rlwtol(); #define lwtol(lw) rlwtol(&lw) #define ltolw(dst, src) dst.hibyte = ((src & 0xff000000L) >> 24);\ dst.mbyte2 = ((src & 0x00ff0000L) >> 16);\ dst.mbyte1 = ((src & 0x0000ff00L) >> 8);\ dst.lobyte = (src & 0x000000ffL); union timer { struct ftime ftimep; long realtime; }; typedef int fildesc; struct ludir { /* Internal library ldir structure */ byte l_stat; /* status of file */ byte l_name[8]; /* name */ byte l_ext[3]; /* extension */ lword l_off; /* offset in library */ lword l_len; /* length of file */ lword l_datetime; /* date and time of file. */ byte l_attrib; /* attributes of file */ byte l_startvol; /* starting disk number */ byte l_endvol; /* ending volume number */ word l_cksum; /* crc checksum */ char l_fill[3]; /* pad to 32 bytes */ }; #define ERROR -1 //E*O*F lar.h// echo x - lwtol.c cat > "lwtol.c" << '//E*O*F lwtol.c//' #include "lar.h" /* cheap hack */ long rlwtol(lwa) register lword *lwa; { asm mov dh, [lwa] /* hibyte */ asm mov dl, [lwa+1] /* mbyte1 */ asm mov ah, [lwa+2] /* mbyte2 */ asm mov al, [lwa+3] /* lobyte */ } //E*O*F lwtol.c// echo x - make.man cat > "make.man" << '//E*O*F make.man//' MAKE(I) 3/10/84 MAKE(I) NAME MAKE - maintain multiple source files (VAX/VMS and MSDOS 2.0) SYNOPSIS MAKE [-N] [-A] [-D] [-I] [-K] [-F makefile] [name ...] DESCRIPTION MAKE is a utility inspired by the Unix(tm) command of the same name. MAKE helps maintain programs that are constructed from many files. MAKE processes a "makefile", a file which describes how to build a program from its source files, and produces a script file containing the commands necessary to recompile the program. Be careful: this MAKE is NOT compatible with Unix(tm) MAKE! The 'N' option causes MAKE to print out the steps it would follow in order to rebuild the program. The 'A' option tells MAKE to assume that all files are obsolete, and that everything should be recompiled. The 'I' option tells MAKE to ignore any error statuses returned when MAKE executes a program. The 'K' option tells MAKE to continue compiling anything that does not depend on an entry that had an error during the compilation. The 'F' option, followed by a filename, can be used to specify a makefile other than the default one. If no names are specified in the commandline, the first dependency in the makefile is examined. Otherwise, the specified root names are brought up to date. The default makefiles are: for VAX/VMS: MAKEFILE [-]MAKEFILE SYS$LOGIN:MAKEFILE for MSDOS: MAKEFILE ..\MAKEFILE If the first makefile cannot be found, MAKE attempts to use the next one. If no makefile is ever found, MAKE prints a diagnostic and aborts. THE MAKEFILE Comments begin with '#' and extend to the end of the line. A '#' (or almost any other character) may be escaped with the escape character (backslash (\) on VMS, backquote (`) on MSDOS). An escape character may be typed by doubling it (\\ or ``). The standard Unix escape codes are recognized (\n, \r, \t, \b, \f, `n, `r, `t, `b and `f). A makefile is a list of dependencies and library definitions. A dependency consists of a root name, a colon, and zero or more names of dependent files. (The colon MUST be preceeded by whitespace.) For instance, in: make.exe : make.obj parsedir.obj file.obj macro.obj mk.h the file 'make.exe' depends on five other files. A library definition consists of a root name, a vertical bar (|), and zero or more names of library entry points. (The vertical bar MUST be preceded by whitespace.) For instance, in: ibm.lib | ibmtext ibmsound ibmkeys ibmser ibm_com the file 'ibm.lib' has five entry points. Library definitions are useful for updating libraries without maintaining copies of the object files for the source files. The library entry points have the same date and time as the library itself, and so if the entry depend on the proper source files, all modified source files will be recompiled whenever necessary. A dependent file may be a member of an ARCHIVE. An ARCHIVE is a collection of files with date and time stamps. An example of an archive definition is: make.obj : make.lar ( make.c make.h ) In this definition, make.c and make.h are members of the archive make.lar. If make.obj must be rebuilt and either make.c or make.h is not found in the current directory, they will be copied out of the archive, compiled, and deleted at the end of the make session. Archive definitions are meaningless in a library definition, and so should not be used. A root name with an empty dependency, as in: print : is assumed NEVER up to date, and will always be recompiled. The dependency list or library definition may be continued on successive lines: bigfile.exe : one.obj two.obj three.obj four.obj five.obj six.obj gronk.obj freeple.obj scuzzy.lnk frog.txt greeble.out OR msdos.lib | alloc char_io disk_io file_io exec filesize freedisk freemem insert mouse port_io randg syscall sysclock sysint textsf timer ttime ucsd ucsdhelp unix Any number of 'method' lines may follow a dependency. Method lines begin with an ascii tab. When a file is to be rebuilt, MAKE executes these method lines (minus the tab). For example, in: make.exe : make.obj parsedir.obj file.obj macro.obj mk.h $link make, parsedir, file, macro $write sys$output "Just another version of MAKE ..." $purge the three lines following the dependency make up the method for recompiling (or in this case, re-linking) the file 'make.exe'. Library definitions may have method lines. These methods will *always* be executed, since the entry points have the same date as the library. If the macro "~INIT" is defined, its text will be executed first. If the macro "~DEINIT" is defined, its text will be executed last. By defining these two macros, it is possible to configure the shell enviroment: ~INIT = $set term/nowrap\n$on error then goto err_handler ~DEINIT = $set term/wrap\n$exit\$err_handler:\n ~DEINIT = #(~DEINIT)$type err.log\n$exit will be executed as: $set term/nowrap $on error then goto err_handler . . $set term/wrap $exit $err_handler: $type err.log $exit When a root's method is defined, the value of the macro "~BEFORE" is prefixed to the method, and the value of the macro "~AFTER" is appended to it. Frequently one wants to maintain more than one program with a single makefile. In this case, a "master dependency" can appear first in the file: allOfMyToolsAndHorribleHacks : cat peek poke.exe grunge cat : cat.exe cat.exe : .... (stuff for CAT.EXE) peek : peek.exe peek.exe : (stuff for PEEK.EXE) poke.exe : (stuff for POKE.EXE) grunge : grunge.com grunge.com : (stuff for grung) In other words, make will bring everything up to date that is somehow connected to the first dependency (its assumed that the incredibly lengthy filename specified in this example won't actually exist). MACROS A macro is defined by a line of the form (the '=' MUST be surrounded by whitespace): <macro-name> = <macro-body> A macro may be deleted by assigning an empty value to it. Macros may be redefined, but old definitions stay around. If a macro is redefined, and the redefinition is later deleted, the first definition will take effect: MAC = first ! MAC = "first" MAC = second ! MAC = "second" MAC = #(MAC) third ! MAC = "second third" MAC = ! MAC = "second" MAC = ! MAC = "first" MAC = ! MAC has no definition A macro may be referenced in two ways: #<char> or #(macro-name) The first way only works if the macro's name is a single character. If the macro's name is longer than one character, it must be enclosed in parenthesis. ['#' may be escaped by doubling it ("##".)] For example, in: G = mk.h mk1.h OBJS = make.obj file.obj parsedir.obj macro.obj BOTH = #(OBJS) #G make.exe : #(OBJS) #G make.exe : #(BOTH) make.exe : mk.h mk1.h make.obj file.obj parsedir.obj macro.obj $write sys$output "This is a number sign --> ##" after macro expansion, the three dependencies will appear identical and the two '#'s in the last line will turn into one '#'. UNIX(tm) MAKE AND THIS ONE They are NOT the same. Do not expect Unix makefiles to work with this MAKE, even if you change the pathnames. There are some major differences between this version and the standard Unix(tm) MAKE: 1. The Unix(tm) comment character is '#', VAX/VMS's is '!'. 2. The Unix(tm) macro-expansion character is '$'. While this would have been easy to leave the same, the '$' character is used so often in VAX/VMS command-lines that I thought it best to change it to '#'. 3. Multiple root names are not allowed. Unix(tm) MAKE accepts lines of the form: name1 name2 : depend1 depend2 but this one doesn't. 4. There is no equivalent of double-colon ("::".) 5. There is no equivalent of .SUFFIXES, or the corresponding special macros. SAMPLE MAKEFILE ! ! VAX/VMS MAKE ! Landon Dyer ! H = make.h FILES = #H, make.c, macro.c, token.c, parsedir.c, file.c DOCUMENTATION = distr.mem make.man makefile. make.com make.exe : make.obj macro.obj token.obj parsedir.obj file.obj $link make.obj, macro, token, parsedir, file $purge make.obj : make.c #H $cc make.c macro.obj : macro.c #H $cc macro token.obj : token.c #H $cc token parsedir.obj : parsedir.c #H $cc parsedir file.obj : file.c $cc file ! ! Print files associated with MAKE ! print : $print make.man, #(FILES), make.com, makefile. ! ! Type out source to MAKE ! type : $type #(FILES), make.com, makefile. ! ! Make backup of source files. ! BACKUP = [.bak] backup : $copy #(FILES) #(BACKUP) $copy make.man, make.com, makefile. #(BACKUP) ! ! Collect MAKE into a distribution file. ! collect : $collect collect distr.mem make.man makefile make.com make.h - make.c macro.c token.c parsedir.c file.c AUTHOR Landon Dyer G.DYER@SU-SCORE.ARPA 175 Calvert Dr. #F-211 BASHFL::DYER (Atari Coinop) Cupertino, CA 95014 MODIFIED BY Eric C. Brown brownc@utah-cs.arpa 1302 Austin Hall ...!harpo!utah-cs!brownc University of Utah, SLC, UT 84112 //E*O*F make.man// echo x - readme cat > "readme" << '//E*O*F readme//' Last month I wrote a version of the Unix(tm) utility MAKE. It runs under VAX/VMS and MSDOS 2.0. I am placing it in the public domain, and it is yours for the asking. You may copy it, or give it away. You can make any changes you like to it. All I ask is that you DO NOT TRY TO SELL IT. Anyway, there is now a MAKE for MSDOS. It is free, and it works pretty well. I'm giving it away because it might do the world some good. Who knows? Caveat: this version of MAKE is NOT compatible with the Unix(tm) version. Some differences are explained in the documentation. Most of the problem stems from the fact that I've never had a chance to use the original version of MAKE, and the documentation I've seen on it has been poor. My idea of what a make program should do is almost certainly different from what you Unix(tm) hackers are used to. Well, hell -- the software is worth what you paid for it. Have fun. In order to get MAKE running on your system, you need to: 1. Read the documentation file MAKE.MAN. (Yes, read the directions.) 2. Edit the file MAKE.H to represent your system (VAX/VMS or MSDOS 2.0.) 3. Recompile the source code by following the script file CMAKE.COM (for VAX/VMS) or CMAKE.BAT (for MSDOS 2.0.) VAX/VMS requires the DEC C compiler; MSDOS 2.0 requires Lattice C (or another C compiler of comparable quality) and the Macro Assembler. 4. Test out MAKE by running it on itself. (Make a backup first!) Good luck, Landon Dyer (G.DYER @ SU-SCORE) //E*O*F readme// echo x - makefile cat > "makefile" << '//E*O*F makefile//' # # MSDOS Make utility # (compile with Wizard C version 2.0) # CLIB = c:\wcc\clib.lib COBJ = c:\wcc\c0.obj H = make.h FILES = $H make.c macro.c token.c parsedir.c file.c osdate.c execute.c OBJS = make.obj macro.obj token.obj parsedir.obj file.obj osdate.obj getdir.obj lwtol.obj execute.obj ONAME = make+macro+token+parsedir+file+osdate+getdir+lwtol+execute DOCUMENTATION = readme make.man makefile make : makemake.exe lar : lar.exe makemake.exe : $(OBJS) $(COBJ) $(CLIB) link $(COBJ)+$(ONAME),makemake,make/map,$(CLIB) make.obj : make.lar ( make.c $H ) cc -c make.c macro.obj : make.lar ( macro.c $H ) cc -c macro.c token.obj : make.lar ( token.c $H ) cc -c token.c parsedir.obj : make.lar ( parsedir.c $H ) cc -c parsedir.c file.obj : make.lar ( file.c $H ) cc -c file.c execute.obj : make.lar ( execute.c $H ) cc -c execute.c osdate.obj : make.lar ( osdate.c $H ) lar.lar ( lar.h ) cc -c osdate.c # # Makefile for lar. # lar.exe : lwtol.obj getdir.obj lar.obj $(COBJ) $(CLIB) link $(COBJ)+lar+lwtol+getdir,lar,lar/map,$(CLIB) lar.obj : lar.lar ( lar.c lar.h ) cc -c lar.c # # Files shared by lar and make. # getdir.obj : lar.lar ( getdir.c lar.h ) cc -c getdir.c lwtol.obj : lar.lar ( lwtol.c lar.h ) cc -A -c lwtol.c # # Print files associated with MAKE # print : print make.man $(FILES) makefile # # collect source and documentation files # collect : lar uv make.lar $(FILES) $(DOCUMENTATION) lar uv lar.lar lar.c lar.h lwtol.c getdir.c lar rv make.lar lar rv lar.lar # # copy to distribution disk (on A:) # distribution : copy readme a: copy make.man a: copy makefile a: copy make.c a: copy macro.c a: copy token.c a: copy parsedir.c a: copy file.c a: copy osdate.c a: copy execute.c a: copy lar.h a: copy lar.c a: copy getdir.c a: copy lwtol.c a: copy makemake.exe a: //E*O*F makefile// exit 0