v13i019: PD make V1.6 (Part 2 of 2)
Greg Yachuk
greggy at zebra.UUCP
Sun Jun 3 08:21:36 AEST 1990
Posting-number: Volume 13, Issue 19
Submitted-by: greggy at 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 at 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 at 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
More information about the Comp.sources.misc
mailing list