bright@dataio.UUCP (Walter Bright) (08/09/85)
Well, I got the make posted by grayson@uiucuxc.Uiuc.ARPA, and it compiled and worked fine. However, the overhead incurred by the program was 96k! This meant that I couldn't use much of a ramdisk. The solution was to recompile it with the Lattice 2.14 C compiler, which doesn't hog 64k for its data segment when it isn't necessary. Also, the Lattice compiler dropped the size of the .EXE file from 28k (the Microsoft version) to 20k, making the total overhead of make only 24k bytes! Obviously, for those of us with less than 640k of memory, a large improvement. Converting to Lattice C required numerous changes to the source: o Lattice can't handle unsigned longs. o Convert the spawnlp() call to forklp() call. WARNING: the fork functions in Lattice 2.14 are screwed up, so the way I wrote it is a little funny. If you have 2.15, you may have to change the fork. o I had to write a couple of missing library routines, and kludge up a realloc() function. o The code had a couple of spots where storage was freed, but was later referenced. I fixed these. o I recompiled it with small model. Anyhow, here's the new source. It's not a shell script, someday I'll figger out how to write one. Files are makefile, mstring.h, make.c and mstring.c. -------------- makefile ------------------ make.exe: make.obj mstring.obj # link make+mstring/STACK:9000; link cs+make+mstring,make,make,lcs/map *.obj : *.c # msc /AL /Ota $*.c; lc1 $* -dLATTICE lc2 $* mstring.obj : mstring.h make.obj : mstring.h -------------- mstring.h ----------------- /* mstring.h */ typedef char * mstring; mstring mfgets(), mstrcat(), msubstr(), talloc(); -------------- make.c -------------------- /* * make.c * * An imitation of the Unix MAKE facility * * Copyright (C) 1984 by Larry Campbell, 73 Concord St., Maynard, Mass. * Rewritten w/modifications by Mike Hickey, University of DC * * This software may be freely copied and disseminated for noncommercial * purposes, if and only if this entire copyright statement and notice * is included intact. This software, and the information contained * herein, may not be used for commercial purposes without my prior * written permission. * * This program runs a new shell (COMMAND.COM) for each command specified * in the makefile. This, I recommend that you put a copy of the shell in * ramdisk (if you have one). Assuming your ramdisk is called, say, drive * F:, you would: * * * COPY A:\COMMAND.COM F: * SET COMSPEC=F:\COMMAND.COM * */ /* Certain portions of this software are Copyright (C) 1985 by Dan Grayson, 2409 S. Vine St, Urbana, IL 61801, namely all those lines which differ between versions 2.10 and 2.11. Qualitative descriptions of these changes are described in the edit history below. Provided this copyright notice is not changed, and VERS211 below is not changed, and VERS211 is still prints when 'usage()' runs, these portions may be freely copied and used for any purpose, with one exception, namely: those persons or corporations who claim a copyright on this program or a part of it may not use these portions for any commercial purpose whatsoever. In particular, they may not collect royalties on any version of this program which includes these portions. */ /* * Edit history: * * 2.12 Made it compilable by Lattice C version 2.14. Use '-dLATTICE' * on the command line for this. Default is Microsoft C. * 2.11 Fixed breakout_symbols, which tried to return a pointer to a local variable! Made symbol substitution occur in all lines, not just shell command lines. Fixed breakout_symbols, which blew up when a symbol was undefined. Allowed blank lines, which are ignored now. Change command line length to 1000. Fixed it so MAKE, when no targets are specified on the command line, will simply make the first target in the makefile. Remove the command line symbol definition option. Changed the line continuation character to \ (it was - ) Now symbol definition lines do NOT begin with $. Fixed numerous bugs dealing with character arrays and their lengths. Now a shell command line which begins with @ is not echoed. A shell command line beginning with + is executed through command.com ; this makes io redirection and pipes available, but the exit code of the program cannot be checked due to a misfeature of command.com. A shell command line beginning with - may return a nonzero exit code without halting 'make'. Fixed it so a target:prerequisite line followed by no how-to lines is interpreted not as an error, and not as sharing the how-to lines following the next target:prerequisite line, but is considered fulfilled by no action other than making all the prerequisites. Fixed the bug which meant the return code from the commmand was never dicovered. This resulted from using "system", which uses "command.com", which hides the return code of the program it runs. Resident commands can still be used, nevertheless. Error messages now include the line number of the makefile, if relevant. Made the return code of the command print out if nonzero. Now the copyright notice only prints when the usage appears. Convert to Microsoft vers 3.00, large memory model. - dan grayson * 2.10 Fix bug in abort routine, update copyright notice * 2.09 Set up for command line parsing of macros * 2.08 Remove edit 2.05; keep debug a compile-time option * 2.07 Finish macro parsing * 2.06 Add initial code for macro handling * 2.05 Add -d (debug) switch * 2.04 Add error message handling (doserror). * 2.03 Add -i (ignore errors) switch. * 2.02 Add -s (silent run) switch. * 2.01 Convert to Lattice 2.14. Clean up code for faster execution. * 1.11 Make default makefilename be MAKEFILE. * 1.10 Add -n (trace) switch. * 1.09 Allow multiple targets before colon. * 1.08 Cleanup and speedup of makefile parsing. * 1.07 Add continuation lines (hyphen as last char of line) * 1.06 Don't need to make dummy FCBs, zero seems to work OK. * 1.05 Fix bug finding COMSPEC when it's not first env variable * 1.04 Wordier usage text, including copyright notice. * Remove printf's (except when DEBUG on) to shrink EXE * 1.03 Don't uppercase shell command and fix datetime bug * 1.02 Random cleanup * 1.01 The beginning */ #define VERSION "MAKE ver. 2.10 Copyright (C) 1984 by Larry Campbell, Maynard Mass." #define VERS211 "MAKE ver. 2.12 Portions copyright (C) 1985 by Dan Grayson, Urbana IL." #ifdef LATTICE #define assert(a) #define perror error #undef LATTICE #include <dos.h> #include <stdio.h> #include <ctype.h> #include "mstring.h" extern char *strchr(); typedef long ulong; #else #define LINT_ARGS /* for Microsoft v. 3.00 */ #include <assert.h> #include <process.h> #include <dos.h> #include <stdio.h> #include <malloc.h> #include <ctype.h> #include "mstring.h" #include <string.h> typedef unsigned long ulong; #endif char *dos_commands[] = { "dir", "type", "rem", "pause", "date", "time", "ren", "rename", "ver", "vol", "break", "verify", "mkdir", "md", "exit", "ctty", "echo", "if", "cls", "chdir", "cd", "rmdir", "rd", "copy", "del", "erase", NULL }; #define PREREQ_MAGIC 123 #define FILE_MAGIC 543 #define SHELL_MAGIC 678 #define TARG_MAGIC 987 #define SYMBOL_MAGIC 653 #define MAXLIN 1000 #define SYMLEN 1000 #define MAXTARGETS 100 #ifndef TRUE #define TRUE 1 #define FALSE 0 #define EOS '\0' /* End Of String */ #endif #define LINESIZE 1000 /* max length of input line */ #define MAXCOMMON 8 /* max no. of targets with common prereqs */ #define tfree(x) if (x) free(x),x=NULL #ifdef LATTICE extern char _dos; #else extern unsigned char _osmajor; /* in MS ver 3, gives version of OS to left of point */ extern int _doserrno; #endif extern int linenumber; /* defined in mstring.c */ char *talloc(), *strperm(); ulong getdatetime (); static int dontworry=0; union REGS inregs, outregs; struct SREGS segregs; struct SREGS seg; struct { char reserved[21]; char attr; unsigned time, date, size_l, size_h; char pname[13]; } find_buf; /* * MAKE parses the make file and constructs a somewhat tangled * directed graph describing dependencies. There is a list of * TargNode structures, each of which points to a list of * prereq_node structures. These both point to FileNode structures, * which contain information about files such as date-time of write * and the filename. To keep things simple, MAKE insures that only * one FileNode exists for any given file. */ typedef struct FileNode { #ifndef NDEBUG int magic; #endif char *fname; struct FileNode *chain; } *fileptr; fileptr FileNodeList, NewFileNode (); typedef struct TargNode { #ifndef NDEBUG int magic; #endif struct TargNode *next; fileptr file; struct prereq_node *PreqList; struct shell_node *shell_list; } *targptr; targptr target_list, new_target (), lookup_target (); typedef struct prereq_node { #ifndef NDEBUG int magic; #endif struct prereq_node *next; fileptr file; } *preqptr; preqptr NewPreqNode (); typedef struct shell_node { #ifndef NDEBUG int magic; #endif struct shell_node *next; char *command; unsigned quiet : 1, ignore : 1, useshell : 1; } *shellptr; typedef struct symbol_node { #ifndef NDEBUG int magic; #endif char *token, *value; struct symbol_node *next; } *symbptr; symbptr SymbolList; static char *makefilename; static int status, tracing, quietly, ignore_errors; usage () { puts (VERS211); puts (VERSION); puts ("This program may be copied freely for noncommercial purposes. It may"); puts ("not be copied for commercial use without the author's written permission.\n"); puts ("This program is an imitation of the MAKE program supplied with Unix."); puts ("It works somewhat like Unix MAKE (and VAX/VMS MMS) except that it has"); puts ("no default rules.\n"); puts ("Usage: make [target ...] [options ...]"); puts ("Options:"); puts (" -f filename specify makefile, default is MAKEFILE"); puts (" -i ignore errors while processing"); puts (" -n trace and print, but don't execute commands"); puts (" -s suppress MAKE echoing commands"); exit (1); } main (argc, argv) int argc; char **argv; { int argi, targi, linlen; char *targname[MAXTARGETS]; #ifdef LATTICE if (_dos < 2) error ("Must have DOS 2.XX or higher"); #else if (_osmajor < 2) error ("Must have DOS 2.XX or higher"); #endif makefilename = "MAKEFILE"; tracing = quietly = ignore_errors = FALSE; target_list = 0; SymbolList = 0; FileNodeList = 0; targi = 0; for (argi = 1; argi < argc; argi++) { if (argv[argi][0] == '-') /* switch */ switch (argv[argi][1]) { /* switch character */ case 'f': if (argi < (argc - 1)) makefilename = argv[++argi]; else usage (); break; case 'i': ignore_errors = TRUE; break; case 'n': tracing = TRUE; break; case 's': quietly = TRUE; break; default: usage (); } else { /* target name */ if (targi == MAXTARGETS) error ("Too many target files"); targname[targi] = strperm (argv[argi]); targi++; } } if (tracing && quietly) quietly = 0; parse (makefilename); if (target_list == NULL) error ("No targets in makefile"); if (targi) for (argi = 0; argi < targi; argi++) make (targname[argi]); else { targptr p; for (p=target_list; p->next ; p = p->next ); assert (p->magic == TARG_MAGIC); make(p->file->fname); /* make first target in makefile i.e. the last one on the list */ } return 0; /* need good return code */ } parse (makefilename) char *makefilename; { FILE *fd; int targi=0, i; char c, *sp, *dp; mstring input=NULL; targptr targ[MAXTARGETS]; fd = fopen (makefilename, "r"); if (fd == NULL) error ("Can't open makefile"); while (1) { tfree(input); input = mfgets(fd); if (input==NULL) break; #if DEBUG printf ("Makefile Input: \"%s\"\n", input); #endif sp = input; passpace(&sp); if (*sp==0 || *sp=='!' || *sp=='#') continue; /* ignore comment lines and blank lines */ if (isspace(*input)) { /* if leading space, then this is a shell line */ if (targi == 0) error ("Target line must come before shell lines"); sp = input; passpace(&sp); for (i = 0; i < targi; i++) NewShellLine (targ[i], sp); continue; } { /* substitute for symbols - this will be done later for shell lines, to take special symbols like $* into account, which can only be known at run time */ breakout_symbols(&input); } { /*** check for the form 'name = value' ***/ char *endword; sp=input; password(&sp); endword = sp; passpace(&sp); if (*sp == '=') { targi=0; sp++; *endword = EOS; SetSymbol (input, sp) ; continue; } } /* end of macro parsing */ /**** now we know this is a 'targets : prerequisite' line ***/ targi=0; for ( dp = sp = input; 1 ; sp++) /*** collect the targets ***/ if (*sp == ':' || isspace (*sp)) { /* space or colon ends target name */ if (targi == MAXTARGETS) error ("Too many targets in one line"); c = *sp; *sp = EOS; targ[targi++] = new_target (dp); *sp = c; passpace(&sp); if (*sp == ':') break; dp = sp; } else if (*sp == EOS) error ("no colon"); sp++; if (targi == 0) error ("No target file before ':' "); while(1) { /*** collect the prerequisites ***/ passpace(&sp); if (*sp == EOS) break; /* end of line */ dp = sp; /* beginning of prereq word */ passnonsp(&sp); c = *sp; *sp = EOS ; /* end of prereq word */ for (i = 0; i < targi; i++) LinkPreq (targ[i], NewFileNode(dp) ); *sp = c; } } /* end while */ tfree(input); fclose (fd); linenumber = 0; } /* * new_target * * We think we have a new target; this routine scans the * target list and if we've already seen this name returns * the node we already built. Otherwise, a new node is * allocated and linked. */ targptr new_target (name) char *name; { targptr targ ; #if DEBUG printf ("new_target (\"%s\")\n", name); #endif for ( targ = target_list; targ ; targ = targ->next ) { assert (targ->magic == TARG_MAGIC); if (strcmp (targ->file->fname, name) == 0) return targ; } targ = (targptr) talloc (sizeof (struct TargNode)); #ifndef NDEBUG targ->magic = TARG_MAGIC; #endif targ->file = NewFileNode (name); targ->next = target_list; targ->shell_list = NULL; targ->PreqList = NULL; target_list = targ; return targ; } SetSymbol (name, value) char *name, *value; { symbptr sym; for (sym = SymbolList; sym; sym = sym->next) if (0==strcmp(sym->token,name)) { free(sym->value); sym->value = strperm(value) ; return; } sym = (symbptr) talloc (sizeof (struct symbol_node)); #ifndef NDEBUG sym->magic = SYMBOL_MAGIC; #endif sym->token = strperm (name); sym->value = strperm (value); sym->next = SymbolList; SymbolList = sym; } /* * NewShellLine * * Add a shell command to the list of commands needed to update * a target file */ NewShellLine (targ, line) targptr targ; char *line; { shellptr snode, new; #if DEBUG printf ("NewShellLine (%x, \"%s\")\n", targ, line); #endif new = (shellptr) talloc (sizeof (struct shell_node)); new->next = 0; #ifndef NDEBUG new->magic = SHELL_MAGIC; #endif new->useshell = new->ignore = 0; new -> quiet = quietly ; for ( ; 1 ; line++, passpace(&line) ) if (line[0] == '@') new->quiet = 1; else if (line[0] == '+') new->useshell = 1; else if (line[0] == '-') new->ignore = 1; else break; new->command = strperm (line); snode = targ->shell_list; if (snode) { assert (snode->magic == SHELL_MAGIC); while (snode->next) { snode = snode->next; assert (snode->magic == SHELL_MAGIC); } snode->next = new; } else targ->shell_list = new; } /* * LinkPreq * * Link a new prerequisite file onto prereq list for a target. */ LinkPreq (targ, fnode) targptr targ; fileptr fnode; { preqptr p; #if DEBUG printf ("LinkPreq (\"%s\")\n", fnode->fname ); #endif p = targ->PreqList; ( targ->PreqList = NewPreqNode(fnode) ) -> next = p; } /* * NewPreqNode * * Allocate and return a new prerequisite node */ preqptr NewPreqNode (fnode) fileptr fnode; { preqptr new; #if DEBUG printf ("NewPreqNode (struct FileNode *%x, \"%s\")\n",fnode,fnode->fname); #endif new = (preqptr) talloc (sizeof (struct prereq_node)); new->next = NULL; #ifndef NDEBUG new->magic = PREREQ_MAGIC; #endif new->file = fnode; return new; } /* * NewFileNode * * Return FileNode pointer for a file; returns pointer to * existing FileNode if this file already seen, else allocates * and inits a new node */ fileptr NewFileNode (name) char *name; { fileptr fnode; #if DEBUG printf ("NewFileNode (\"%s\")\n", name); #endif for ( fnode = FileNodeList; fnode; fnode = fnode->chain) { assert (fnode->magic == FILE_MAGIC); if (strcmp (name, fnode->fname) == 0) { #if DEBUG printf ("NewFileNode returning existing node\n"); #endif return fnode; } } fnode = (fileptr) talloc (sizeof (struct FileNode)); fnode->fname = strperm (name); fnode->chain = 0; #ifndef NDEBUG fnode->magic = FILE_MAGIC; #endif fnode -> chain = FileNodeList; FileNodeList = fnode; return fnode; } /* * getdatetime * * Return date-time of a file squished into a ulong so compares * are easy */ ulong getdatetime (name) char *name; { ulong datetime; int dma_off, dma_seg; #ifdef LATTICE struct SREGS sregs; segread(&sregs); #define FP_SEG(p) (sregs.ds) #define FP_OFF(p) ((int)(p)) #endif #ifdef DEBUG printf("getdatetime(\"%s\")\n",name); #endif inregs.x.ax = 0x2F00; /* get current DMA address */ intdosx (&inregs, &outregs, &segregs); /* .. */ dma_off = outregs.x.bx; /* and save for later restoration */ dma_seg = segregs.es; { char *p = (char *) & find_buf; segregs.ds = FP_SEG(p); inregs.x.dx = FP_OFF(p); /* set DMA to GNJFN block */ } inregs.x.ax = 0x1A00; intdosx (&inregs, &outregs, &segregs); segregs.ds = FP_SEG(name); inregs.x.dx = FP_OFF(name); /* pathname */ inregs.x.cx = 0; /* attributes */ inregs.x.ax = 0x4E00; /* GTJFN */ #ifdef LATTICE status = intdosx (&inregs, &outregs, &segregs); if (status & 1) { #else outregs.x.cflag = 0; status = intdosx (&inregs, &outregs, &segregs); if (outregs.x.cflag || (status & 1)) { #endif #if DEBUG printf ("File \"%s\" does not exist\n", name); #endif return 0L; } segregs.ds = dma_seg; /* restore DMA address */ inregs.x.dx = dma_off; inregs.x.ax = 0x1A00; intdosx (&inregs, &outregs, &segregs); #if DEBUG printf ("filespec = \"%s\", date = %4x, time = %4x, sizel = %d\n", find_buf.pname, find_buf.date, find_buf.time, find_buf.size_l); #endif datetime = (ulong) find_buf.date; datetime = datetime << 16; datetime = datetime + find_buf.time; printf("'%s' %ld\n",find_buf.pname,datetime); return datetime; } /* * make (name) * * This routine actually does the work. It scans the list of * targets parsed from the makefile, and checks the target's * prerequisites date/time values against the target's. If * the prerequisite is itself a target (present in target_list), * we call make recursively to check it. Then, if any of our * prerequisites are newer than we are, we execute all our shell * commands. If there are no prerequisites specified at all, then * also execute all our shell commands. */ int make (targname) /* use fnode instead of fname here */ char *targname; { targptr targ; preqptr prereq; ulong NewestPreq=0; #if DEBUG printf ("Making %s\n", targname); #endif if ((targ = lookup_target (targname)) == 0) return TryDefault( targname ); prereq = targ->PreqList; if (prereq) { for ( ; prereq; prereq = prereq->next) { ulong date; make (prereq->file->fname); /* recursively make */ date = getdatetime(prereq->file->fname); if (date > NewestPreq) NewestPreq = date; } #if DEBUG printf ("Target \"%s\" datetime is %08lx, newest prereq is %08lx\n", targ->file->fname, getdatetime(targ->file->fname), NewestPreq); #endif if (getdatetime(targ->file->fname) < NewestPreq) build (targ); } else build(targ); /* if no prerequisites were listed, do it ! */ if (targ->shell_list == NULL) { int i; dontworry ++; i = TryDefault( targname ); dontworry --; return i; } return 1; } TryDefault(targname) char *targname;{ targptr targ; char * ext = strchr (targname, '.'); dontworry ++; if (ext != NULL) for (targ = target_list ; targ ; targ = targ -> next ) if (targ->file->fname[0] == '*' && 0 == strcmp ( ext , targ->file->fname+1 ) ) { char * root = msubstr( targname , 0 , ext-targname ); char *cname; int worked; cname = mstrcat( root ,targ->PreqList->file->fname+1 ); worked = make ( cname ) ; SetSymbol ( "*" , root ) ; free(root); if (!worked) {free(cname); goto ret0;} if (getdatetime(cname) <= getdatetime(targname)) { free(cname); goto ret1; } free(cname); build ( targ ) ; goto ret1; } if (getdatetime(targname) > 0) goto ret1; ret0: /* unsuccessful return */ if (--dontworry) return 0; else error ("Don't know how to make %s",targname); ret1: /* successful return */ dontworry--; return 1; } /* * build * * Invoke shell commands to build a target file */ build (targ) targptr targ; { shellptr snode; char *cmd; int runsts = 0; #if DEBUG printf ("Building \"%s\"\n", targ->file->fname); #endif for ( snode = targ->shell_list; snode; snode = snode->next, free(cmd) ) { char *p, **q, *cmdname; assert (snode->magic == SHELL_MAGIC); cmd = strperm(snode->command); breakout_symbols(&cmd); /* notice that this may introduce a space at the beginning of the command line */ cmdname = cmd; passpace(&cmdname); if (!snode->quiet) fputs (cmdname, stdout); if (tracing) { puts (""); /* EXEC does newline, so must be faked */ continue; } p = cmdname ; passnonsp(&p); if (*p) *p++ = EOS ; /* terminate the name of the cmd */ /* find whether it is a dos command */ strlwr(cmdname); /* lower case for comparison */ for (q=dos_commands ; *q ; q++) if (0==strcmp(*q,cmdname)) break; if (*q || snode->useshell) /* must we use command.com ? */ #ifndef LATTICE if (0==strcmp(cmdname,"chdir") || 0==strcmp(cmdname,"cd")) if (passpace (&p) , *p) { /* chdir with arg */ char *q=p; passnonsp(&q); *q = EOS; runsts = chdir(p); } else { /* chdir without arg */ char name[200]; if (getcwd(name,200)) { if (!snode->quiet) putchar('\n'); fputs(name,stdout); } else error("path name too long"); } else #endif { /* resident command */ if (*p) *--p = ' '; /* splice command line */ if (strlen(cmdname) > 128) error("shell command line too long"); runsts = system(cmdname); } else { /* transient command */ if (strlen(p)+1 > 128) error("shell command line too long"); if (!snode->quiet) putchar ('\n'); #ifdef LATTICE runsts = forklp(cmdname,cmdname,"",p,NULL); if (runsts) /* error */ runsts = -1; else runsts = wait(); /* get return status */ #else runsts = spawnlp (P_WAIT, cmdname, "", p, NULL); #endif /* can't use 'system()' here, because command.com does not return the exit code of the program */ } putchar('\n'); /* some programs do not end with return */ if (runsts == -1) perror("program not found : "), exit(1); if (runsts > 0 && !snode->ignore && !ignore_errors) printf ( " --- return code %d ---\7", runsts), exit(runsts); } } targptr lookup_target (name) char *name; { targptr targ; for ( targ = target_list; targ ; targ = targ->next) if (strcmp (name, targ->file->fname) == 0) break; return targ; } breakout_symbols (cmdlinptr) char **cmdlinptr; { char *cmdlin = *cmdlinptr, *cmd = talloc(LINESIZE+100); symbptr sym; char symcmp[SYMLEN]; int i, paren, cmdptr; #if DEBUG printf("breakout_symbols (\"%s\")\n", cmdlin); #endif /* this routine doesn't check for overflow of the line ! */ strcpy ( cmd, ""); cmdptr = 0; while (1) { while (*cmdlin != '$' && *cmdlin != EOS) { if (cmdptr >= LINESIZE) error ("Line too long after symbol substitution"); cmd[cmdptr++] = *cmdlin++; } if (cmdptr >= LINESIZE) error ("Line too long after symbol substitution"); cmd[cmdptr] = EOS; if (0==*cmdlin) break; /* end of line */ cmdlin++; /* pass the $ */ /* now we know we are looking at a symbol */ if (*cmdlin == '(') paren = 1, cmdlin++; else paren=0; for (i = 0; i < SYMLEN-1 && (*cmdlin == '*' || isalnum (*cmdlin)); ) symcmp[i++] = *cmdlin++; symcmp[i] = EOS; if (paren) if (*cmdlin == ')') cmdlin++; else puts ("No closing paren on shell line macro"); for ( sym = SymbolList; 1 ; sym = sym->next) { if (sym==NULL) error ("Undefined symbol %s", symcmp ); assert (sym->magic == SYMBOL_MAGIC); if (strcmp (sym->token, symcmp) == 0) break; } strcpy ( cmd + cmdptr , sym->value ); cmdptr = strlen ( cmd ) ; } free(*cmdlinptr); *cmdlinptr = strperm(cmd); free(cmd); #if DEBUG printf ("breakout_symbols returning (\"%s\")\n", *cmdlinptr); #endif } #ifdef LATTICE strlwr(p) char *p; { while (*p) { *p = tolower(*p); p++; } } #endif -------------- mstring.c ----------------- /* mstring.c */ /* The purpose of this file is to provide subroutines for handling strings whose space is allocated with malloc - in this way we remove all limitations on length of strings */ #ifdef LATTICE #include "mstring.h" #include <ctype.h> #include <stdio.h> #else #include "mstring.h" #include <ctype.h> #include <malloc.h> #include <stdio.h> #endif #define DLEN 80 int linenumber = 0; #ifdef LATTICE char *realloc(p,newsize) char *p; int newsize; { char *pnew,*malloc(); pnew = malloc(newsize); if (pnew) strcpy(pnew,p); free(p); return pnew; } #endif char lastchar(p) char *p;{ char c=0; while (*p) c = *p++; return c; } char * endptr(p) char *p;{ while (*p) p++; return p; } mstring mfgets (stream) FILE *stream;{ mstring p; int plen; if (feof(stream)) return NULL; p = talloc(plen = DLEN); p[0] = '\0'; while (1) { if (strlen(p) + DLEN > plen) { p = realloc(p, plen += DLEN); if (p==NULL) puts("no more memory (mfgets)"), exit(1); } if (NULL == fgets(endptr(p),DLEN,stream)) if (*p) return p; else { free(p); return NULL; } if (lastchar(p) != '\n') continue; linenumber++; endptr(p)[-1] = 0; if (lastchar(p) != '\\') { p = realloc (p,strlen(p)+1) ; if (p==NULL) puts("no more memory (mfgets)"), exit(1); return p; } endptr(p)[-1] = 0; } } mstring msubstr(p,i,l) /* creates a string from p[i],p[i+1],...,p[i+l-1] */ mstring p;{ mstring q; q = talloc(l+1); strncpy(q,p+i,l); q[l] = '\0'; return q; } mstring mstrcat(p,q) mstring p,q;{ mstring r = talloc (strlen(p) + strlen(q) + 1); strcpy(r,p); strcat(r,q); return r; } mstring strperm(s) char *s;{ /* allocate space for s, return new pointer */ char *t = talloc(strlen(s)+1); strcpy(t,s); return t; } passpace(p) char **p;{ while (isspace (**p)) (*p)++; } passnonsp(p) char **p;{ while (**p && !isspace(**p)) (*p)++; } password(p) char **p;{ while (isalnum(**p)) (*p)++; } error (errmsg,a,b,c,d,e,f,g,h) char *errmsg;long a,b,c,d,e,f,g,h; { /* unfortunately, this assumes only one file is being used */ if (linenumber) fprintf(stderr,"at line %d : ",linenumber); fprintf(stderr,errmsg,a,b,c,d,e,f,g,h); exit (1); } mstring talloc(i) int i;{ char *p; char *malloc(); p = malloc(i); if (p==NULL) error ("no more memory"); return p; }