greggy@zebra.UUCP (Greg Yachuk) (06/03/90)
Posting-number: Volume 13, Issue 19 Submitted-by: greggy@zebra.UUCP (Greg Yachuk) Archive-name: make1.6/part02 #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # If this archive is complete, you will see the following message at the end: # "End of archive 2 (of 2)." # Contents: build.c default.mk default.msc make.h makefile # makefile.msc parse.c tstring.c tstring.h # Wrapped by greggy@etude on Thu May 31 10:55:31 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'build.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'build.c'\" else echo shar: Extracting \"'build.c'\" \(7577 characters\) sed "s/^X//" >'build.c' <<'END_OF_FILE' X/* X * build.c An imitation of the Unix MAKE facility X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 -f -S -q options, took some changes from v1.4 X * 90-04-18 v1.6 -b -- -W options, emulate <<, non-BSD cleanup X */ X X#include <stdio.h> X#include <string.h> X#ifdef MSDOS X#include <stdlib.h> X#include <process.h> X#endif X#ifdef BSD X#include <sys/wait.h> X#endif X X#include "make.h" X#include "tstring.h" X#include "decl.h" X X#ifndef MSDOS Xchar **bsearch(); X#endif X Xchar *tmpfn = NULL; X#define shellunlink() if(tmpfn!=NULL)unlink(tmpfn),tfree(tmpfn),tmpfn=NULL X X/* X * shell_cmpr - comparison routine for "shell command" binary search. X */ Xshell_cmpr(key, list) Xchar *key; Xchar **list; X{ X return (strcmp(key, *list)); X} X X/* X * build - process the shell commands X */ Xbuild(shellp) Xshellptr *shellp; X{ X char **argv; /* argified version of scmd */ X int runst; /* exec return status */ X char *scmd; /* command with symbols broken out */ X char *tcmd; /* copy of scmd for `tokenize' */ X char *errnum; /* error number in ascii */ X char *errmsg; /* error message */ X int i; X char *sp; X char *tp; X char **shcp; /* pointer to shell command list */ X X if (shellp == NULL || opts.query) X return (0); X X /* process every shell line */ X X for (; *shellp != NULL; X tfree(scmd), tfree(tcmd), tfree(argv), ++shellp) X { X /* breakout runtime symbols (e.g. $* $@ $<) */ X scmd = breakout((*shellp)->scmd); X X /* make a copy because tokenize litters '\0's */ X tcmd = tstrcpy(scmd); X argv = tokenize(tcmd); X X for (tp = scmd, i = 0; argv[i]; i++) X { X /* pretend to handle inline input ("<<" operator) */ X while ((sp = strchr(argv[i], '<')) != NULL) X { X if (*++sp != '<') X continue; X X sp[-1] = '\0'; X X /* got "<<". collect commands into a file */ X if (*++sp != '\0') /* got "<<TAG" */ X sp = shellinput(&shellp, sp); X else X { X /* got "<< TAG" (note space before TAG) */ X sp = shellinput(&shellp, argv[i + 1]); X if (argv[i + 1]) X { X tfree(argv[i + 1]); X argv[i + 1] = tstrcpy(""); X } X } X X /* add the filename to the argument */ X sp = tstrcat(argv[i], sp); X tfree(argv[i]); X argv[i] = sp; X } X X /* now strip the quotes from the argument */ X strcpy(tp, tunquote(argv[i])); X while (*tp++); X tp[-1] = ' '; X } X X /* finally, terminate the command line (scmd) */ X tp[-1] = '\0'; X X if (!opts.silent && (opts.noexec || !(*shellp)->s_silent)) X { X puts(scmd); X fflush(stdout); X } X X /* look for $(MAKE) */ X if (equal(argv[0], opts.make)) X { X /* call ourselves recursively */ X new_make(argv); X continue; X } X X if (opts.noexec) X continue; X X /* any SHELL meta-characters MUST be handled by the shell */ X if (!(*shellp)->s_shell && strpbrk(scmd, SHELL_METAS)) X (*shellp)->s_shell = 1; X X if (shell_cmds && !(*shellp)->s_shell) X { X /* check for COMMAND.COM builtin commands */ X for (shcp = shell_cmds; *shcp; ++shcp); X shcp = bsearch(argv[0], shell_cmds, X shcp - shell_cmds - 1, X sizeof(char *), shell_cmpr); X (*shellp)->s_shell = (shcp != NULL); X } X X /* run without COMMAND.COM if possible, 'cause it uses RAM */ X if (!(*shellp)->s_shell) X runst = spawnvp(P_WAIT, argv[0], argv); X else X runst = system(scmd); X X shellunlink(); X X if (runst == 0) X continue; X X /* uh-oh, an error */ X if (runst == -1) X perror("make"); X X errnum = talloc(18); X#ifdef MSDOS X errnum = itoa(runst, errnum, 10); X#else X sprintf(errnum, "%d", runst); X#endif X errmsg = (opts.keepon) ? "\007*** Ignoring Error code " X : "\007*** Error code "; X errmsg = tstrcat(errmsg, errnum); X terror(0, errmsg); X tfree(errmsg); X tfree(errnum); X X if (opts.keepon) X return (1); X X if (!opts.ignore && !(*shellp)->s_ignore) X exit(1); X } X X return (0); X} X X X/* X * shellinput - write the list of commands into a temp file, and return name X */ Xchar *shellinput(shellp, eof) Xshellptr **shellp; Xchar *eof; X{ X int eoflen; X char *scmd; X FILE *tfp; X X /* get rid of obvious errors */ X if (shellp == NULL || *shellp == NULL) X return (NULL); X X eoflen = (eof) ? strlen(eof) : 0; X X /* find the name of a candidate temporary file */ X tmpfn = tempnam(NULL, "mk"); X X /* write contents to stdout when '-n' is specified */ X if (opts.noexec && !opts.silent && !(**shellp)->s_silent) X tfp = stdout; X else X tfp = fopen(tmpfn, "w"); X X while (*++*shellp) X { X /* break out the current shell command */ X scmd = breakout((**shellp)->scmd); X X /* propogate the shell command attributes */ X (**shellp)->s_silent = ((*shellp)[-1])->s_silent; X (**shellp)->s_ignore = ((*shellp)[-1])->s_ignore; X (**shellp)->s_shell = ((*shellp)[-1])->s_shell; X X /* see if we've reached the eof-word */ X if (eof && !strncmp(scmd, eof, eoflen)) X break; X X /* no there, so write out the command to the temp file */ X if (tfp) X { X fputs(scmd, tfp); X fputc('\n', tfp); X } X X /* free the string allocated by breakout() */ X tfree(scmd); X } X X if (tfp != stdout) X fclose(tfp); X X if (**shellp == NULL) X --* shellp; /* point at last shell command */ X else X tfree(scmd); /* free EOF-word */ X X return (tmpfn); X} X X X/* X * new_make - save current environment X * - call make() recursively (actually main()) X * - clean up new environment X * - restore environment X */ Xnew_make(argv) Xchar **argv; X{ X targptr thead, tnext, tsuffix; X fileptr fhead, fnext; X symptr shead, snext; X shellptr shhead, shnext; X char **shcmds; X char **ttlist; X long tnow; X optnode topts; X int i; X X /* save all the globals */ X tsuffix = suffix_targ; X thead = target_list; X fhead = file_list; X shead = symbol_list; X shhead = shell_list; X shcmds = shell_cmds; X ttlist = tlist; X tnow = now; X topts = opts; X X /* count the arguments */ X for (i = 0; argv[i]; ++i) X tunquote(argv[i]); X X /* call ourselves recursively; this inherits flags */ X ++make_level; X main(i, argv); X --make_level; X X /* we're back, so gotta clean up and dispose of a few things */ X while (target_list) X { X tnext = target_list->tnext; X if (target_list->tpreq) X tfree(target_list->tpreq); X if (target_list->tshell) X tfree(target_list->tshell); X tfree(target_list); X target_list = tnext; X } X X while (file_list) X { X fnext = file_list->fnext; X tfree(file_list->fname); X tfree(file_list); X file_list = fnext; X } X X /* don't drop all symbols, just the new ones */ X X while (symbol_list != shead) X { X snext = symbol_list->snext; X tfree(symbol_list->sname); X tfree(symbol_list->svalue); X tfree(symbol_list); X symbol_list = snext; X } X X while (shell_list) X { X shnext = shell_list->slink; X tfree(shell_list->scmd); X tfree(shell_list); X shell_list = shnext; X } X X /* restore our original globals */ X suffix_targ = tsuffix; X target_list = thead; X file_list = fhead; X symbol_list = shead; X shell_list = shhead; X shell_cmds = shcmds; X tlist = ttlist; X now = tnow; X opts = topts; X} X X X#ifndef MSDOS Xint spawnvp(mode, path, args) Xint mode; Xchar *path; Xchar **args; X{ X int pid = 0; X int retpid; X#ifdef BSD X union wait waitword; X#else X int waitword; X#endif X X if (mode != P_OVERLAY) X pid = fork(); X X if (pid == 0) X execvp(path, args); X X while (((retpid = wait(&waitword)) != pid) && (retpid > 0)) X ; X#ifdef BSD X return ((retpid == pid) ? waitword.w_retcode : (-1)); X#else X return ((retpid == pid) ? ((waitword >> 8) & 0x00ff) : (-1)); X#endif X} X#endif END_OF_FILE if test 7577 -ne `wc -c <'build.c'`; then echo shar: \"'build.c'\" unpacked with wrong size! fi # end of 'build.c' fi if test -f 'default.mk' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'default.mk'\" else echo shar: Extracting \"'default.mk'\" \(2487 characters\) sed "s/^X//" >'default.mk' <<'END_OF_FILE' X### this is the default makefile for Unix: default.mk X XSUFFIXES = .o .C .cxx .ec .c .y .l .s .sh X.SUFFIXES: $(SUFFIXES) X X# ASM section XAS = as XASFLAGS = XCOMPILE.s= $(AS) $(ASFLAGS) X.s: X $(AS) $(ASFLAGS) $< X.s.o: X $(AS) $(ASFLAGS) -o $@ $< X X# C section XCC = cc XCFLAGS = -O XCDEBUG = -g XCOMPILE.c= $(CC) $(CFLAGS) -c X.c: X $(LINK.c) -o $@ $< $(LDFLAGS) X X.c.o: X $(COMPILE.c) $< X X.c.i: X $(COMPILE.c) -P $< X X# CPP Section XCPP = CC XCPPFLAGS = -O XCPPDEBUG = -g XCOMPILE.cpp = $(CPP) $(CPPFLAGS) X.C .cxx: X $(CPP) $(CPPFLAGS) -o $@ $< $(LDFLAGS) X X.C.o .cxx.o: X $(COMPILE.cpp) $< X X.C.c .cxx.c: X $(COMPILE.cpp) -P $< X X# ESQL/C section XESQL = esql XEFLAGS = XCOMPILE.e= $(ESQL) $(EFLAGS) X.ec: X $(COMPILE.e) -e $< X $(LINK.c) -o $@ $*.c $(LDFLAGS) X X.ec.o: X $(COMPILE.e) -e $< X $(COMPILE.c) $*.c X X.ec.c: X $(COMPILE.e) -e $< X X# Lex section XLEX = lex XLFLAGS = XLEX.l = $(LEX) $(LFLAGS) -t X.l: X $(LEX.l) $< > $*.c X $(LINK.c) -o $@ $*.c $(LDFLAGS) -ll X X.l.o: X $(LEX.l) $< > $*.c X $(COMPILE.c) $*.c X X.l.c: X $(LEX.l) $< > $*.c X X# YACC section XYACC = yacc XYFLAGS = XYACC.y = $(YACC) $(YFLAGS) X.y: X $(YACC.y) $< X $(LINK.c) -o $@ y.tab.c $(LDFLAGS) -ly X $(RM) y.tab.c X X.y.o: X $(YACC.y) $< X $(COMPILE.c) -o $*.o y.tab.c X $(RM) y.tab.c X X.y.c: X $(YACC) $(YFLAGS) $< X $(MV) y.tab.c $@ X X.y.h: X $(YACC) $(YFLAGS) -d $< X $(RM) y.tab.c X $(MV) y.tab.h $@ X X# Shell script section X.sh: X cp $< $@ X X# BSD Unix Misc section XA = .a XAR = ar XARFLAGS = XBIN = /usr/local/bin XCP = cp XE = XEDITOR = /usr/bin/vi XGFLAGS = XGET = get XLDEBUG = XLDFLAGS = XLD = ld XLIBDIR = XLINK.c = $(CC) $(CFLAGS) XMAKE = make XMKDEPEND = makedepend XMODEL = XMV = mv XO = .o XRANLIB = ranlib XRM = rm -f XSHELL = /bin/csh XSHELLCMD = XSTACK = X X# DOS Misc section X#A = .lib X#AR = lib X#ARFLAGS = X#BIN = C:\bin X#CP = cp X#E = .exe X#EDITOR = $(BIN)\vi X#GFLAGS = X#GET = echo cannot get X#LDEBUG = /link /noe /noi /co /st:$(STACK) $(LIBS) X#LDFLAGS = /link /noe /noi /st:$(STACK) $(LIBS) X#LD = $(CC) $(CFLAGS) X#LIBDIR = c:\msc5.1\lib X#LINK.c = $(CC) $(CFLAGS) X#MAKE = make X#MKDEPEND = mkdepend X#MODEL = S X#MV = mv X#O = .o X#RANLIB = echo cannot ranlib X#RM = rm -f X#SHELL = command /c X#SHELLCMD = break call cd chcp chdir cls copy ctty date del dir \ X# echo erase exit for goto if md mkdir path pause prompt \ X# rd rem ren rename rmdir set shift time type ver verify vol X#STACK = 2000 END_OF_FILE if test 2487 -ne `wc -c <'default.mk'`; then echo shar: \"'default.mk'\" unpacked with wrong size! fi # end of 'default.mk' fi if test -f 'default.msc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'default.msc'\" else echo shar: Extracting \"'default.msc'\" \(3930 characters\) sed "s/^X//" >'default.msc' <<'END_OF_FILE' X### this is the default makefile for DOS: default.mk X XSUFFIXES = .cpp .c .asm .l .y .ec X.SUFFIXES: $(SUFFIXES) X X# ASM section XAS = masm XASFLAGS = /mx # don't convert to upper case XCOMPILE.s= $(AS) $(ASFLAGS) X.asm .asm.com: X $(COMPILE.s) $<; X $(LINK.c) -o $*.exe $*.obj $(LDFLAGS) X $(RM) $*.obj X exe2bin $*.exe $*.com X $(RM) $*.exe Xasm.exe: X $(COMPILE.s) $<; X $(LINK.c) -o $@ $*.obj $(LDFLAGS) X $(RM) $*.obj X.asm.obj: X $(COMPILE.s) $<; X.asm.o: X $(COMPILE.s) $<; X mv $*.obj $@ X X# C section XCC = cl -A$(MODEL) XCFLAGS = /Ox /G2 # full optimization, 80286 opcodes XCDEBUG = /Od /Zi # no optimization, Codeview debuggable XCOMPILE.c= $(CC) $(CFLAGS) -c X.c.com: X $(COMPILE.cpp) -mt $< $(LIBS) X $(RM) $*.obj X.c .c.exe: X $(LINK.c) -o $@ $< $(LDFLAGS) X $(RM) $*.obj X.c.o: X $(COMPILE.c) /Fo$*.o $< X.c.obj: X $(COMPILE.c) $< X X# D section (ANSI C declaration headers) X.c.d: X echo "#ifndef NOPROTOTYPES" > $*.tmp X -$(COMPILE.c) -DNOPROTOTYPES -Zg $< >> $*.tmp X echo "#endif" >> $*.tmp X +updexhdr $*.tmp $*.d X $(RM) $*.tmp X X# I section (preprocessed C files) X.c.i: X $(COMPILE.c) -P $< X X# CPP Section XCPPINCDIR= c:\zortech\include XCPP = ztc -DNO_EXT_KEYS -I$(CPPINCDIR) -B -m$(MODEL)i # integer only XCPPF = ztc -DNO_EXT_KEYS -I$(CPPINCDIR) -B -m$(MODEL) # floats also XCPPFLAGS = -o # full optimization XCPPDEBUG = -g -co -s # codeview and stack checking XCOMPILE.cpp=$(CPP) $(CPPFLAGS) X.cpp .cpp.com: X $(COMPILE.cpp) -mt $< X $(RM) $*.obj X.cpp.exe: X $(COMPILE.cpp) $< X $(RM) $*.obj X.cpp.obj: X $(COMPILE.cpp) -c $< X.cpp.o: X $(COMPILE.cpp) -c $< X $(MV) $*.obj $@ X.cpp.c: X $(COMPILE.cpp) -c -e -l$*.c $< X X# ESQL/C section XESQL = esql XEFLAGS = XCOMPILE.e= $(ESQL) $(EFLAGS) X.ec .ec.exe: X $(COMPILE.e) -e $< X $(LINK.c) -o $@ $*.c $(LDFLAGS) $(MODEL)libsql X $(RM) $*.obj $*.c X.ec.o: X $(COMPILE.e) -e $< X $(COMPILE.c) /Fo$*.o $*.c X $(RM) $*.c X.ec.obj: X $(COMPILE.e) -e $< X $(COMPILE.c) $*.c X $(RM) $*.c X.ec.c: X $(COMPILE.e) -e $< X X# Lex section XLEX = flex XLFLAGS = -I # interactive scanner XLEX.l = $(LEX) $(LFLAGS) -t X.l.com: X $(LEX.l) $< > $*.c X $(COMPILE.cpp) -mt $*.c X $(RM) $*.obj $*.c X.l .l.exe: X $(LEX.l) $< > $*.c X $(LINK.c) -o $@ $*.c $(LDFLAGS) X $(RM) $*.obj $*.c X.l.o: X $(LEX.l) $< > $*.c X $(COMPILE.c) /Fo$*.o $*.c X $(RM) $*.c X.l.obj: X $(LEX.l) $< > $*.c X $(COMPILE.c) $*.c X $(RM) $*.c X.l.c: X $(LEX.l) $< > $@ X X# YACC Section XYACC = yacc XYFLAGS = XYACC.y = $(YACC) $(YFLAGS) X.y.com: X $(YACC.y) $< X $(COMPILE.cpp) -mt ytab.c X $(RM) $*.obj ytab.c X.y .y.exe: X $(YACC.y) $< X $(LINK.c) -o $@ ytab.c $(LDFLAGS) X $(RM) $*.obj ytab.c X.y.o: X $(YACC.y) $< X $(COMPILE.c) /Fo$*.o ytab.c X $(RM) ytab.c X.y.obj: X $(YACC.y) $< X $(COMPILE.c) ytab.c X $(RM) ytab.c X.y.h: X $(YACC.y) -d $< X $(RM) ytab.c X $(MV) ytab.h $@ X.y.c: X $(YACC.y) $< X $(MV) ytab.c $@ X X# BSD Unix Misc section X#A = .a X#AR = ar X#ARFLAGS = X#BIN = /usr/local/bin X#CP = cp X#E = X#EDITOR = /usr/bin/vi X#GFLAGS = X#GET = get X#LDEBUG = X#LDFLAGS = X#LD = ld X#LIBDIR = X#LINK.c = $(CC) $(CFLAGS) X#MAKE = make X#MKDEPEND = makedepend X#MODEL = X#MV = mv X#O = .o X#RANLIB = ranlib X#RM = rm -f X#SHELL = /bin/csh X#SHELLCMD = X#STACK = X X# DOS Misc section XA = .lib XAR = lib XARFLAGS = XBIN = C:\bin XCP = cp XE = .exe XEDITOR = $(BIN)\vi XGFLAGS = XGET = echo cannot get XLDEBUG = /link /noe /noi /co /st:$(STACK) $(LIBS) XLDFLAGS = /link /noe /noi /st:$(STACK) $(LIBS) XLD = $(CC) $(CFLAGS) XLIBDIR = c:\msc5.1\lib XLINK.c = $(CC) $(CFLAGS) XMAKE = make XMKDEPEND = mkdepend XMODEL = S XMV = mv XO = .o XRANLIB = echo cannot ranlib XRM = rm -f XSHELL = command /c XSHELLCMD = break call cd chcp chdir cls copy ctty date del dir \ X echo erase exit for goto if md mkdir path pause prompt \ X rd rem ren rename rmdir set shift time type ver verify vol XSTACK = 2000 END_OF_FILE if test 3930 -ne `wc -c <'default.msc'`; then echo shar: \"'default.msc'\" unpacked with wrong size! fi # end of 'default.msc' fi if test -f 'make.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'make.h'\" else echo shar: Extracting \"'make.h'\" \(3424 characters\) sed "s/^X//" >'make.h' <<'END_OF_FILE' X/* X * make.h X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 -f -S -q options, took some changes from v1.4 X * 90-04-18 v1.6 -b -- -W options, emulate <<, non-BSD cleanup X */ X X#define MAKEINI "default.mk" X X#ifdef MSDOS X#define PATH_SEPARATOR ";" X#define FILE_SEPARATOR ":/\\" X#define SHELL_METAS "<|>" X#else X#define PATH_SEPARATOR ":" X#define FILE_SEPARATOR "/" X#define SHELL_METAS "<|>`*?()[];&$" X#endif X X#define MAXNEGTIME 0x80000000 X X#define equal(s,t) (!strcmp((s),(t))) X#define get_target(t) hash_target((t), NULL) X#define get_file(f) hash_file((f), NULL) X#define append_preq(t,p) (fileptr*)append_node((char**)(t),(char**)(p),sizeof(fileptr*)) X#define append_shell(t,s) (shellptr*)append_node((char**)(t),(char**)(s),sizeof(shellptr*)) X Xtypedef unsigned short t_mask; X Xtypedef struct targnode X{ X t_mask tmask; /* mask to avoid string compares */ X struct targnode *tnext; /* next target in global target list */ X struct filenode *tfile; /* file node for this target */ X struct filenode **tpreq;/* pre-req list for this target */ X struct shellnode **tshell; /* command list for this target */ X} targnode, *targptr; X Xtypedef struct filenode X{ X t_mask fmask; /* mask to avoid string compares */ X char *fname; /* name of this file (targ, preq...) */ X long ftime; /* last MODIFY time for this file */ X struct filenode *fnext; /* next file node in global file list */ X} filenode, *fileptr; X Xtypedef struct shellnode X{ X char *scmd; /* text of command */ X unsigned s_silent:1; /* don't echo before executing */ X unsigned s_ignore:1; /* ignore exit status */ X unsigned s_shell:1; /* force spawning of command.com */ X struct shellnode *slink;/* next shell node in global list */ X} shellnode, *shellptr; X Xtypedef struct symnode X{ X t_mask smask; /* mask to avoid string compares */ X char *sname; /* name of a symbol */ X char *svalue; /* value of a symbol */ X struct symnode *snext; /* next symbol node in global list */ X int scmd; /* command line macro? */ X int slevel; /* level of new_make() */ X} symnode, *symptr; X Xtypedef struct optnode X{ X unsigned depend; /* -d */ X unsigned display:1; /* -D */ X unsigned envirn:1; /* -e */ X unsigned ignore:1; /* -i */ X unsigned keepon:1; /* -k */ X unsigned noexec:1; /* -n */ X unsigned query:1; /* -q */ X unsigned silent:1; /* -s */ X unsigned touch:1; /* -t */ X char *make; /* current value of $(MAKE) */ X} optnode; X Xextern targptr target_list; /* global list of targets */ Xextern fileptr file_list; /* global list of file nodes */ Xextern symptr symbol_list; /* global list of symbol nodes */ Xextern shellptr shell_list; /* global list of shell nodes */ X Xextern char **shell_cmds; /* commands which force usage of SHELL */ X Xextern int make_level; /* level of new_make() */ X Xextern targptr first_targ; /* first target (if nothing named) */ Xextern targptr suffix_targ; /* target node of ".SUFFIXES" */ X Xextern optnode opts; /* various options */ Xextern char **tlist; /* list of command line targets */ Xextern long now; /* current time */ X X#ifndef MSDOS Xchar *getenv(); X X#define P_WAIT 1 X#define P_NOWAIT 2 X#define P_OVERLAY 3 X#endif /* MSDOS */ END_OF_FILE if test 3424 -ne `wc -c <'make.h'`; then echo shar: \"'make.h'\" unpacked with wrong size! fi # end of 'make.h' fi if test -f 'makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile'\" else echo shar: Extracting \"'makefile'\" \(1286 characters\) sed "s/^X//" >'makefile' <<'END_OF_FILE' X# name of this program XNAME = make X X# define the FLAGS as required for MSDOS or Unix XCFLAGS = -O XLDFLAGS = X XSRCS = make.c parse.c build.c tstring.c XOBJS = make.o parse.o build.o tstring.o XPROG = $(NAME)$E X X# define your installation directory XBIN = $(HOME)/bin X X# archive utility XARCADD = zoo a X X# files to archive XARCS = decl.h make.h tstring.h make.c parse.c build.c tstring.c \ X default.mk default.bsd makefile makefile.bsd make.doc \ X README $(PROG) X Xall: $(PROG) X X$(PROG): $(OBJS) X $(CC) $(CFLAGS) $(OBJS) -o $(PROG) $(LDFLAGS) X Xarc: $(NAME).zoo Xzoo: $(NAME).zoo X X$(NAME).zoo: $(ARCS) X $(RM) $(NAME).zoo X $(ARCADD) $(NAME).zoo $(ARCS) X Xinstall: $(BIN)/$(PROG) $(BIN)/default.mk X$(BIN)/$(PROG): $(PROG) X cp $(PROG) $(BIN)/$(PROG) X$(BIN)/default.mk: default.mk X cp default.mk $(BIN)/default.mk X Xclean: X -$(RM) *.o X -$(RM) *.bak X X# use this to check the size of the program (use chkdsk if necessary) Xsize: X pmap X Xdepend: X $(MKDEPEND) $(SRCS) > makefile.new X $(MV) makefile makefile.BAK X $(MV) makefile.new makefile X Xtest: $(OBJS) X link @<<END_OF_LINK X $(OBJS) X x$(NAME) X /cparmaxalloc:1 /noe /stack:2000 X $(LIBS) X END_OF_LINK X X# DO NOT DELETE THIS LINE X Xmake.o: make.h tstring.h decl.h Xparse.o: make.h tstring.h decl.h Xbuild.o: make.h tstring.h decl.h Xtstring.o: tstring.h END_OF_FILE if test 1286 -ne `wc -c <'makefile'`; then echo shar: \"'makefile'\" unpacked with wrong size! fi # end of 'makefile' fi if test -f 'makefile.msc' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'makefile.msc'\" else echo shar: Extracting \"'makefile.msc'\" \(1483 characters\) sed "s/^X//" >'makefile.msc' <<'END_OF_FILE' X# name of this program XNAME = make X X# define the FLAGS as required for MSDOS or Unix XCFLAGS = /Oals /Gs /G2 XLDFLAGS = /link /cparmaxalloc:1 /noe /stack:2000 $(LIBS) X XSRCS = make.c parse.c build.c tstring.c XOBJS = make.o parse.o build.o tstring.o XPROG = $(NAME)$E X X# define your installation directory XBIN = c:/bin X X# archive utility XARCADD = zoo a X X# files to archive XARC1 = decl.h make.h tstring.h make.c parse.c build.c tstring.c default.mk XARC2 = default.bsd makefile makefile.bsd make.doc README $(PROG) XARCS = $(ARC1) $(ARC2) X Xall: $(PROG) X X$(PROG): $(OBJS) X echo out of date dependents($@): $? X $(CC) $(CFLAGS) $(OBJS) -o $(PROG) $(LDFLAGS) X Xarc: $(NAME).zoo Xzoo: $(NAME).zoo X X$(NAME).zoo: $(ARCS) X $(RM) $(NAME).zoo X $(ARCADD) $(NAME).zoo $(ARC1) X $(ARCADD) $(NAME).zoo $(ARC2) X Xinstall: $(BIN)/$(PROG) $(BIN)/default.mk X$(BIN)/$(PROG): $(PROG) X cp $(PROG) $(BIN)/$(PROG) X$(BIN)/default.mk: default.mk X cp default.mk $(BIN)/default.mk X Xclean: X -$(RM) *.o X -$(RM) *.bak X X# use this to check the size of the program (use chkdsk if necessary) Xsize: X pmap X Xdepend: X $(MKDEPEND) $(SRCS) > makefile.new X $(MV) makefile makefile.BAK X $(MV) makefile.new makefile X Xtest: $(OBJS) X echo out of date dependents($@): $? X link @<<END_OF_LINK X $(OBJS) X x$(NAME) X /cparmaxalloc:1 /noe /stack:2000 X $(LIBS) X END_OF_LINK X echo End Of Link X X# DO NOT DELETE THIS LINE X Xmake.o: make.h tstring.h decl.h Xparse.o: make.h tstring.h decl.h Xbuild.o: make.h tstring.h decl.h Xtstring.o: tstring.h END_OF_FILE if test 1483 -ne `wc -c <'makefile.msc'`; then echo shar: \"'makefile.msc'\" unpacked with wrong size! fi # end of 'makefile.msc' fi if test -f 'parse.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'parse.c'\" else echo shar: Extracting \"'parse.c'\" \(17139 characters\) sed "s/^X//" >'parse.c' <<'END_OF_FILE' X/* X * parse.c X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 -f -S -q options, took some changes from v1.4 X * 90-04-18 v1.6 -b -- -W options, emulate <<, non-BSD cleanup X */ X#include <stdio.h> X#include <ctype.h> X#include <string.h> X#ifdef MSDOS X#include <stdlib.h> X#endif X X#include "make.h" X#include "tstring.h" X#include "decl.h" X X/* X * parse - read (text) makefile, and parse X * - close file before returing X * X * lines have the following format: X * # with or without preceeding spaces/tabs (comment line) X * <TAB> commands (shell line) X * name = stuff (macro) X * name += stuff (macro) X * targ [targ...] : [pre-req...] [; shell cmd ] (target line) X */ Xparse(fd) XFILE *fd; X{ X char *input; X char *ip; X char *colonp; X char schar; X int ntargs, npreqs, nshell; X int tmax, pmax, smax; X targptr *targs; X fileptr *preqs; X shellptr *shells; X X if (fd == NULL) X return (0); X X /* start off with a short list of targets */ X targs = (targptr *) grow_list(NULL, &tmax); X preqs = (fileptr *) grow_list(NULL, &pmax); X shells = (shellptr *) grow_list(NULL, &smax); X X ntargs = npreqs = nshell = 0; X X /* maximize buffering */ X setvbuf(fd, NULL, _IOFBF, 2048); X X while ((input = tgets(fd)) != NULL) X { X /* punt on comments and blank lines */ X for (ip = input; isspace(*ip); ++ip); X if (*ip == '#' || *ip == '\0') X continue; X X /* process include files */ X if (!strncmp(ip, "include", 7)) X { X /* skip spaces AFTER "include" */ X for (ip += 7; isspace(*ip); ++ip); X X /* process macros in the filename */ X ip = breakout(ip); X X /* parse the makefile */ X if (!parse(fopen(ip, "r"))) X terror(1, tstrcat("cannot open ", ip)); X X /* free up the broken-out string */ X tfree(ip); X continue; /* get next input line */ X } X X /* display the makefile line ? */ X if (opts.display) X puts(input); X X /* get rid of comments and preceeding spaces */ X for (colonp = ip; *colonp && *colonp != '#'; ++colonp) X { X if (*colonp == '\'' || *colonp == '"') X colonp = tstrspan(colonp); X } X X for (--colonp; colonp >= ip && isspace(*colonp); --colonp); X X /* we *know* that some non-space is on this line, from above */ X if (colonp >= ip) X *++colonp = '\0'; X X /* see if we have a shell command */ X if (isspace(*input)) X { X if (ntargs == 0) X terror(1, "rules must be after target"); X got_shell: X if (nshell == smax) X { X shells = (shellptr *) X grow_list((char **) shells, &smax); X } X shells[nshell++] = add_shell(ip); X continue; X } X X /* not a shell line, so must be a target or a macro */ X if (ntargs != 0) X { X /* link previous preq's and shell's */ X targs[ntargs] = NULL; X preqs[npreqs] = NULL; X shells[nshell] = NULL; X link_targs(targs, preqs, shells); X ntargs = npreqs = nshell = 0; X } X X /* don't break out symbols until macro is invoked */ X if (add_macro(ip, 0)) X continue; X X /* okay, we have a target line; break out macro symbols */ X input = breakout(ip); X X /* just look for tokens with standard isspace() separators */ X ip = token(input, NULL, &schar); X while (ip) X { X colonp = strchr(ip, ':'); X#ifdef MSDOS X /* need to allow c:/bin/make.exe as a target */ X if (colonp && colonp - ip == 1) X colonp = strchr(colonp + 1, ':'); X#endif X if (colonp) X { X /* got a separator */ X *colonp = '\0'; X X /* if at front of token, target is done */ X if (colonp == ip) X break; X } X X if (ntargs == tmax) X targs = (targptr *) grow_list((char **) targs, X &tmax); X targs[ntargs] = add_target(ip); X X /* make sure we don't save .INIT as our 1st target */ X if (first_targ == NULL && *ip != '.') X first_targ = targs[ntargs]; X ++ntargs; X X if (colonp) X break; X ip = token(NULL, NULL, &schar); X } X X /* a target line without a colon? naughty, naughty! */ X if (!colonp) X terror(-1, "Unexpected end of line seen"); X X/* X * taking care of four possible cases: X * 1) object : source X * 2) object: source X * 3) object :source X * 4) object:source X */ X X if (colonp && *++colonp) X ip = colonp; X else X ip = token(NULL, NULL, &schar); X X /* link the pre-req's */ X while (ip) X { X if ((colonp = strchr(ip, ';')) != NULL) X { X ip[strlen(ip)] = schar; X *colonp = '\0'; X } X X if (*ip) X { X if (npreqs == pmax) X { X preqs = (fileptr *) X grow_list((char **) preqs, X &pmax); X } X X preqs[npreqs++] = add_file(ip); X } X X if (colonp) X { X ip = colonp + 1; X goto got_shell; X } X X ip = token(NULL, NULL, &schar); X } X X /* gotta free the line allocated by breakout() */ X tfree(input); X } X X /* link up any dangling dependants */ X if (ntargs != 0) X { X targs[ntargs] = NULL; X preqs[npreqs] = NULL; X shells[nshell] = NULL; X link_targs(targs, preqs, shells); X } X X /* clean up our mallocs */ X tfree(targs); X tfree(preqs); X tfree(shells); X X fclose(fd); X return (1); X} X X X/* X * link_targs - force a list of targs to point to same preq's and shell's X */ Xlink_targs(targs, preqs, shells) Xtargptr *targs; Xfileptr *preqs; Xshellptr *shells; X{ X while (targs && *targs) X { X /* process some special targets */ X if ((*targs)->tfile->fname[0] == '.') X { X if (equal((*targs)->tfile->fname, ".SILENT")) X opts.silent = 1; X else X if (equal((*targs)->tfile->fname, ".IGNORE")) X opts.ignore = 1; X else X if (equal((*targs)->tfile->fname, ".SUFFIXES")) X /* X * set `suffix_targ' to speed up X * `default_rule' X */ X suffix_targ = *targs; X X /* special rule has preq's reset */ X /* normally, preq's are merely appended */ X if (*preqs == NULL && (*targs)->tpreq != NULL) X { X tfree((*targs)->tpreq); X (*targs)->tpreq = NULL; X } X X /* special rules have their shell commands replaced */ X if ((*targs)->tshell != NULL && *shells != NULL) X { X shellptr *sp; X X for (sp = (*targs)->tshell; *sp; ++sp) X tfree(*sp); X tfree((*targs)->tshell); X (*targs)->tshell = NULL; X } X } X X /* each target in the list points to the preq's and shell's */ X (*targs)->tpreq = append_preq((*targs)->tpreq, preqs); X X /* we cannot expand the list of shell commands */ X if ((*targs)->tshell != NULL && *shells != NULL) X { X terror(1, tstrcat("Too many rules defined for target ", X (*targs)->tfile->fname)); X } X (*targs)->tshell = append_shell((*targs)->tshell, shells); X ++targs; X } X} X X X/* macros must have the format: WORD = more stuff X * WORD= more stuff X * WORD =more stuff X * WORD=more stuff X * or: WORD += more stuff X * WORD +=more stuff X * X * it is assumed that there is no leading whitespace in `input' X */ Xadd_macro(input, scmd) Xchar *input; Xint scmd; X{ X char *eqsign; X char *value; X symptr symp; X X /* gotta have an '=' to be a macro */ X eqsign = strchr(input, '='); X if (eqsign == NULL) X return (0); X X /* make sure we catch imbedded '='s (e.g. MACRO=STUFF) */ X for (value = input; *value && !isspace(*value); ++value); X if (value > eqsign) X value = eqsign; X X /* terminate the macro name */ X *value = '\0'; X X /* find start of value */ X for (value = eqsign + 1; isspace(*value); ++value); X X /* look for concat character */ X --eqsign; X X if (eqsign < input || (eqsign == input && *eqsign == '+')) X terror(1, "Badly formed macro"); X X if (*eqsign == '+') X { X /* append to the current macro definition */ X *eqsign = '\0'; X symp = get_symbol(input, scmd); X if (symp->scmd && !scmd) X return (1); X if (symp->slevel < make_level) X symp = dup_symbol(symp, symp->svalue); X if (symp->svalue) X { X eqsign = tstrcat(symp->svalue, " "); X value = tstrcat(eqsign, value); X tfree(eqsign); X tfree(symp->svalue); X symp->svalue = value; X return (1); X } X } X X add_symbol(input, value, scmd); X return (1); X} X X X/* X * add_symbol - add a <name,value> pair to the symbol table X * - override existing symbol value X * - mark as either command-line macro or not X */ Xadd_symbol(name, value, scmd) Xchar *name; Xchar *value; Xint scmd; X{ X symptr symp; X X symp = get_symbol(name, scmd); X if (symp->scmd & !scmd) X return; X if (symp->slevel < make_level) X symp = dup_symbol(symp, NULL); /* don't dup the value */ X if (symp->svalue) X tfree(symp->svalue); X symp->svalue = tstrcpy(value); X symp->scmd = scmd; X} X X X/* X * get_symbol - find a symbol in the symbol table X * - if non-extant, create <name,NULL> X * - return created or found symbol node X */ Xsymptr get_symbol(name, scmd) Xchar *name; Xint scmd; X{ X symptr symp; X t_mask mask; X char *np; X X /* use `mask' to screen out most string comparisons */ X mask = 0; X np = name; X while (*np) X mask += *np++; X X /* linear search through symbol list */ X for (symp = symbol_list; symp != NULL; symp = symp->snext) X { X if (mask != symp->smask) X continue; X X if (equal(name, symp->sname)) X return (symp); X } X X symp = tnew(symnode); /* allocate symbol node */ X symp->smask = mask; /* record mask for later */ X symp->sname = tstrcpy(name); /* allocate string and copy name */ X symp->scmd = scmd; /* command line macro? */ X symp->slevel = make_level; /* current new_make() level */ X X /* get the value from the environment, if it is there */ X X if ((symp->svalue = getenv(name)) != NULL) X { X symp->svalue = tstrcpy(symp->svalue); X X /* X * if `-e', let command line macros override, but not macro X * assignments in the makefile. X */ X if (opts.envirn) X symp->scmd = 1; X } X X symp->snext = symbol_list; /* link to head of symbol list */ X symbol_list = symp; X X return (symp); X} X X X/* X * dup_sym - duplicate a symbol node, but at current new_make() level X */ Xsymptr dup_symbol(symp, value) Xsymptr symp; Xchar *value; X{ X symptr nsp; X X nsp = tnew(symnode); /* allocate symbol node */ X nsp->smask = symp->smask; /* record mask for later */ X nsp->sname = tstrcpy(symp->sname); /* allocate string and copy X * name */ X nsp->svalue = (value == NULL) ? NULL : tstrcpy(value); X nsp->scmd = symp->scmd; /* command line macro? */ X nsp->slevel = make_level; /* current new_make() level */ X X nsp->snext = symbol_list; /* link to head of symbol list */ X symbol_list = nsp; X X return (nsp); X} X X X/* X * add_target - return extant target node, or create new one X */ Xtargptr add_target(name) Xchar *name; X{ X t_mask mask; X targptr targp; X fileptr filep; X X /* each target must have a file node */ X filep = add_file(name); X X /* see if target already exists */ X targp = hash_target(name, &mask); X if (targp) X return (targp); X X /* oh well, gotta create one */ X targp = tnew(targnode); /* allocate a target node */ X targp->tmask = mask; /* save mask for later */ X targp->tfile = filep; /* save pointer to file node */ X targp->tpreq = NULL; /* no pre-req's yet */ X targp->tshell = NULL; /* no shell lines yet */ X X targp->tnext = target_list; /* link to front of target list */ X target_list = targp; X X return (targp); X} X X X/* X * hash_target - look up target (by name) in target list X * - return target node or NULL X * - if requested, also return the mask X */ Xtargptr hash_target(name, maskp) Xchar *name; Xt_mask *maskp; X{ X targptr targp; X t_mask mask; X char *np; X X /* use `mask' to screen out most string comparisons */ X mask = 0; X np = name; X while (*np) X mask += *np++; X X /* see if we gotta return it */ X if (maskp != NULL) X *maskp = mask; X X /* linear search through target list */ X for (targp = target_list; targp != NULL; targp = targp->tnext) X { X if (mask != targp->tmask) X continue; X X /* target name is ONLY stored in the file node */ X if (equal(name, targp->tfile->fname)) X return (targp); X } X X /* nope, no target here */ X return (NULL); X} X X X/* X * add_file - return a found or created file node X */ Xfileptr add_file(name) Xchar *name; X{ X t_mask mask; X fileptr filep; X X /* see if file node already exists */ X filep = hash_file(name, &mask); X if (filep) X return (filep); X X filep = tnew(filenode); /* allocate new file node */ X filep->fmask = mask; /* save mask for later */ X filep->fname = tstrcpy(name); /* allocate string and copy name */ X filep->ftime = MAXNEGTIME; /* init MODIFY time to long time ago */ X X filep->fnext = file_list; /* link to head of file list */ X file_list = filep; X X return (filep); X} X X X/* X * hash_file - look up file (by name) in file list X * - return file node or NULL X * - if requested, also return the mask X */ Xfileptr hash_file(name, maskp) Xchar *name; Xt_mask *maskp; X{ X fileptr filep; X t_mask mask; X char *np; X X /* use `mask' to screen out most string comparisons */ X mask = 0; X np = name; X while (*np) X mask += *np++; X X /* see if we gotta return it */ X if (maskp != NULL) X *maskp = mask; X X /* linear search through file list */ X for (filep = file_list; filep != NULL; filep = filep->fnext) X { X if (filep->fmask != mask) X continue; X X if (equal(filep->fname, name)) X return (filep); X } X X /* nope, no file here */ X return (NULL); X} X X X/* X * append_node - add a node to the end of an array of nodes X */ Xchar **append_node(node, adds, size) Xchar **node; Xchar **adds; Xint size; X{ X int addlen, len; X X for (addlen = 0; adds[addlen] != NULL; ++addlen); X if (addlen++ == 0) X return (node); X X len = 0; X X if (node != NULL) X { X for (; node[len] != NULL; ++len); X node = (char **) trealloc((char *) node, (len + addlen) * size); X } X else X node = (char **) talloc(addlen * size); X X memcpy(node + len, adds, addlen * size); X return (node); X} X X/* X * add_shell - create a new shell node, and add to end of given list X */ Xshellptr add_shell(input) Xchar *input; X{ X shellptr snode; X X snode = tnew(shellnode);/* allocate a new shell node */ X snode->s_shell = snode->s_ignore = snode->s_silent = 0; X X for (; isspace(*input); ++input); /* skip over leading spaces */ X for (;; ++input) X { X if (*input == '+') X snode->s_shell = 1; /* must use command.com */ X else X if (*input == '-') X snode->s_ignore = 1; /* ignore return value */ X else X if (*input == '@') X snode->s_silent = 1; /* don't echo command */ X else X break; X } X X snode->scmd = tstrcpy(input); /* allocate string and copy command */ X X snode->slink = shell_list; /* attach to global list */ X shell_list = snode; X X return (snode); X} X X X/* X * breakout - replace macro names with values X * - apply recursively X * note: allocates (and returns) a string which must be freed X */ Xchar *breakout(input) Xchar *input; X{ X char *dest, *dend; X char *dp; X int dlen; X int tlen; X int state; X char symname[100]; X char *sp; X symptr symp; X int slen; X char endch; X X /* allocate a string twice as long as input string */ X X dlen = strlen(input) * 2; X dest = dp = talloc(dlen); X dend = dest + dlen; X X/* X * state machine with 4 states X * 0) normal text -- just copy X * 1) starting macro -- define end char (e.g. ')', '}') X * 2) macro name -- copy to a buffer X * 3) end of macro -- look up value, and copy X */ X state = 0; X X while (*input || state == 3) X { X /* if we don't have enough room, double size of string */ X if (dp == dend) X { X dlen *= 2; X tlen = dp - dest; X dest = trealloc(dest, dlen); X dp = dest + tlen; X dend = dest + dlen; X } X X switch (state) X { X case 0: X if (*input == '$') X state = 1; /* found a macro */ X else X *dp++ = *input++; X break; X X case 1: X state = 2; /* only in this state for 1 char */ X sp = symname; X switch (*++input) X { X case '$': X *dp++ = '$'; X state = 0; X break; X case '(': X endch = ')'; X break; X case '{': X endch = '}'; X break; X default: X /* single char; go to state 3 immediately */ X *sp++ = *input; X state = 3; X break; X } X ++input;/* skip bracket (or character) */ X break; X X case 2: X if (*input == endch) X state = 3; X else X *sp++ = *input; X X if ((sp - symname) >= (sizeof symname / sizeof symname[0])) X { X sp[-1] = '\0'; X terror(1, X tstrcat("Macro too long (limit 100 chars): ", X symname)); X } X X ++input;/* make sure we skip end char */ X break; X X case 3: X *sp = '\0'; X symp = get_symbol(symname, 0); X sp = symp->svalue; X slen = -1; X while (sp && *sp) X { X /* X * if value has a macro in it, we must X * process recursively X */ X if (*sp == '$') X { X sp = breakout(symp->svalue); X /* now guaranteed not to have a '$' */ X slen = strlen(sp); X break; X } X ++sp; X } X X if (slen == -1) X { X /* value did NOT have a macro */ X slen = (sp - symp->svalue); X sp = symp->svalue; X } X X /* if we have not enough room, expand */ X if (slen >= (dend - dp)) X { X /* use slen to make sure that we can fit */ X dlen = dlen * 2 + slen; X tlen = dp - dest; X dest = trealloc(dest, dlen); X dp = dest + tlen; X dend = dest + dlen; X } X X /* if length is zero, don't bother to copy */ X if (slen) X { X strcpy(dp, sp); X dp += slen; X } X X if (sp != symp->svalue) X tfree(sp); /* must've called `breakout' */ X X state = 0; /* and we are back to text */ X break; X } X } X X if (state != 0) X terror(1, tstrcat("Improper macro.\n", dest)); X X *dp = '\0'; /* terminate the string */ X return (dest); /* and return it */ X} END_OF_FILE if test 17139 -ne `wc -c <'parse.c'`; then echo shar: \"'parse.c'\" unpacked with wrong size! fi # end of 'parse.c' fi if test -f 'tstring.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tstring.c'\" else echo shar: Extracting \"'tstring.c'\" \(5480 characters\) sed "s/^X//" >'tstring.c' <<'END_OF_FILE' X/* X * tstring.c X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 -f -S -q options, took some changes from v1.4 X * 90-04-18 v1.6 -b -- -W options, emulate <<, non-BSD cleanup X */ X#include <stdio.h> X#include <ctype.h> X#include <malloc.h> X#include <string.h> X X#include "tstring.h" X X Xchar *talloc(n) Xint n; X{ X char *s; X X s = malloc(n); X if (s == NULL) X terror(1, "no free memory"); X return (s); X} X X Xchar *trealloc(s, n) Xchar *s; Xint n; X{ X s = realloc(s, n); X if (s == NULL) X talloc(n); /* force an error */ X return (s); X} X X Xchar *tstrncpy(s, n) Xchar *s; Xint n; X{ X s = strncpy(talloc(n + 1), s, n); X s[n] = '\0'; X return (s); X} X X Xterror(n, s) Xint n; Xchar *s; X{ X fputs("Make: ", stderr); X fputs(s, stderr); X putc('\n', stderr); X if (n) X exit(n); X} X X X/* X * tstrspan - move to the end of a quoted string, ignoring escaped quotes X */ Xchar *tstrspan(str) Xchar *str; X{ X char quote; X X if (*str != '\'' && *str != '"') X return (str + 1); X X quote = *str++; X X while (*str && *str != quote) X { X /* check for escaped quote */ X if (*str == '\\' && str[1] == quote) X ++str; X ++str; X } X X return (str); X} X X X/* X * tunquote - remove quotes from a string X */ Xchar *tunquote(str) Xchar *str; X{ X char *s; X char *d; X X d = s = str; X X while (*s) X { X while (*s && *s == '"') X ++s; X X while (*s && *s != '"') X *d++ = *s++; X } X X *d = '\0'; X X return (str); X} X X X/* X * tsplit - split a string into two components, normally a directory X * path and a filename. If a pointer to a directory is X * supplied, a string is allocated to contain the directory. X * The filename is returned as a pointer into the supplied X * string. X */ Xchar *tsplit(s, seps, dp) Xchar *s; Xchar *seps; Xchar **dp; X{ X char *d; /* directory portion */ X char *f; /* file portion */ X X d = s; X X /* find the final separator */ X while ((f = strpbrk(d, seps)) != NULL) X d = f + 1; X X /* back up to final component */ X f = d; X X /* if we are still at the beginning, there was no Directory */ X if (d == s || dp == NULL) X d = NULL; X else X { X int len; X X /* X * by the time we get here, d points to the final separator X * char. we can substitute a NULL for this sep-char. Thus, X * we don't need to add 1 in the following length X * calculation. X */ X len = d - s; X X d = talloc(len); X d[--len] = '\0'; X while (--len >= 0) X d[len] = s[len]; X } X X if (dp != NULL) X *dp = d; X X return (f); X} X X X/* X * token - take an input string and return a token each call X * - default token delimiter characters are `isspace()' X * - separator chars are in addition to `isspace()' X * - text between quotes (" and ') is a single token X * - if requested, the separator char is returned X * X * called as s = token(string, seps, &schar); X * or s = token(string, NULL, NULL); X * X * followed by s = token(NULL, seps, NULL); X * or s = token(NULL, NULL, &schar); X * X * returns NULL when no more tokens are available X */ Xchar *token(s, sep, schar) Xchar *s; Xchar *sep; Xchar *schar; X{ X static char *olds = NULL; X X if (s) X olds = s; /* we are starting all over again */ X X if (schar) X *schar = '\0'; X X if (!olds || !*olds) X return (NULL); /* no tokens left */ X X while (isspace(*olds) || (sep && strchr(sep, *olds))) X ++olds; /* skip leading spaces and sep's */ X X if (*olds == NULL) X return (NULL); /* remainder is all separator's */ X X s = olds; X X while (*olds) X { X if (isspace(*olds) || (sep && strchr(sep, *olds))) X { X if (schar) X *schar = *olds; X *olds++ = '\0'; /* delimit the token */ X return (s); X } X else X if (*olds == '"' || *olds == '\'') X { X olds = tstrspan(olds); X if (*olds != '\0') X ++olds; /* didn't hit eos, so skip quote */ X } X else X ++olds; /* otherwise, pass over char */ X } X X olds = NULL; X return (s); /* return last token */ X} X X X/* X * tokenize - chop a string up into an array of (char *)'s X */ Xchar **tokenize(input) Xchar *input; X{ X char **argv; X int argc = 0; X int alen; X X alen = 20; /* good initial guess */ X argv = (char **) talloc((alen + 1) * sizeof(char *)); X X input = token(input, NULL, NULL); /* use default separators */ X while (input) X { X if (alen == argc) X argv = (char **) trealloc((char *) argv, X (alen <<= 1) * sizeof(char *)); X argv[argc++] = input; X input = token(NULL, NULL, NULL); X } X X argv[argc] = NULL; /* mark end of array */ X X return (argv); X} X X X/* X * tgets - read input, swallowing escaped newlines as necessary X */ Xchar *tgets(fd) XFILE *fd; X{ X static char *input = NULL; X static int inlen = 0; X char *ep; X int len; X X if (inlen == 0) X input = talloc(inlen = 162); X X input[inlen - 2] = '\n'; X ep = input - 1; X while ((fgets(input, inlen, fd)) != NULL) X { X for (;;) X { X while (input[inlen - 2] != '\n' && input[inlen - 2] != '\0') X { X len = inlen; X input = trealloc(input, inlen <<= 1); X ep = &input[len - 2]; X input[inlen - 2] = '\n'; X fgets(ep + 1, len + 1, fd); X } X X while (*++ep); X *--ep = '\0'; X do X { X --ep; X } while (ep >= input && isspace(*ep)); X X if (ep > input && *ep == '\\' && *--ep != '\\') X fgets(ep + 1, inlen - (ep - input) - 1, fd); X else X break; X } X X return (input); X } X X inlen = 0; X tfree(input); X input = NULL; X X return (NULL); X} END_OF_FILE if test 5480 -ne `wc -c <'tstring.c'`; then echo shar: \"'tstring.c'\" unpacked with wrong size! fi # end of 'tstring.c' fi if test -f 'tstring.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'tstring.h'\" else echo shar: Extracting \"'tstring.h'\" \(1310 characters\) sed "s/^X//" >'tstring.h' <<'END_OF_FILE' X/* X * tstring.h X * X * 88-10-01 v1.0 created by greg yachuk, placed in the public domain X * 88-10-06 v1.1 changed prerequisite list handling X * 88-11-11 v1.2 fixed some bugs and added environment variables X * 89-07-12 v1.3 stop appending shell commands, and flush output X * 89-08-01 v1.4 AB lots of new options and code X * 89-10-30 v1.5 -f -S -q options, took some changes from v1.4 X * 90-04-18 v1.6 -b -- -W options, emulate <<, non-BSD cleanup X */ X#define tnew(t) ((t *) talloc(sizeof(t))) X#define tfree(t) (free((char *) t)) X X#define tstrcat(s,p) (strcat(strcpy(talloc(strlen(s)+strlen(p)+1),(s)),(p))) X#define tstrcpy(s) (strcpy(talloc(strlen(s)+1), (s))) X X#ifdef __STDC__ Xextern char *talloc(int n); Xextern char *trealloc(char *s, int n); Xextern char *tstrncpy(char *s, int n); Xextern int terror(int n, char *s); Xextern char *tstrspan(char *str); Xextern char *tunquote(char *str); Xextern char *tsplit(char *s, char *seps, char **dp); Xextern char *token(char *s, char *sep, char *schar); Xextern char **tokenize(char *input); Xextern char *tgets(FILE * fd); X#else Xextern char *talloc(); Xextern char *trealloc(); Xextern char *tstrncpy(); Xextern int terror(); Xextern char *tstrspan(); Xextern char *tunquote(); Xextern char *tsplit(); Xextern char *token(); Xextern char **tokenize(); Xextern char *tgets(); X#endif END_OF_FILE if test 1310 -ne `wc -c <'tstring.h'`; then echo shar: \"'tstring.h'\" unpacked with wrong size! fi # end of 'tstring.h' fi echo shar: End of archive 2 \(of 2\). cp /dev/null ark2isdone MISSING="" for I in 1 2 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked both archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0