Category : Miscellaneous Language Source Code
Archive   : MAKE15.ZIP
Filename : PARSE.C

 
Output of file : PARSE.C contained in archive : MAKE15.ZIP
/*
* parse.c
*
* 88-10-01 v1.0 created by greg yachuk, placed in the public domain
* 88-10-06 v1.1 changed prerequisite list handling
* 88-11-11 v1.2 fixed some bugs and added environment variables
* 89-07-12 v1.3 stop appending shell commands, and flush output
* 89-08-01 v1.4 AB lots of new options and code
* 89-10-30 v1.5 greggy -f -S -q options, took some changes from v1.4
*
*/
#include
#include
#include
#ifdef MSDOS
#include
#endif

#include "make.h"
#include "tstring.h"
#include "decl.h"

/*
* parse - read (text) makefile, and parse
* - close file before returing
*
* lines have the following format:
* # with or without preceeding spaces/tabs (comment line)
* commands (shell line)
* name = stuff (macro)
* name += stuff (macro)
* targ [targ...] : [pre-req...] [; shell cmd ] (target line)
*/
parse(fd)
FILE *fd;
{
char *input;
char *ip;
char *colonp;
char schar;
int ntargs, npreqs, nshell;
int tmax, pmax, smax;
targptr *targs;
fileptr *preqs;
shellptr *shells;

if (fd == NULL)
return (0);

/* start off with a short list of targets */
targs = (targptr *) grow_list(NULL, &tmax);
preqs = (fileptr *) grow_list(NULL, &pmax);
shells = (shellptr *) grow_list(NULL, &smax);

ntargs = npreqs = nshell = 0;

/* maximize buffering */
setvbuf(fd, NULL, _IOFBF, 2048);

while ((input = tgets(fd)) != NULL)
{
/* punt on comments and blank lines */
for (ip = input; isspace(*ip); ++ip);
if (*ip == '#' || *ip == '\0')
continue;

/* process include files */
if (!strncmp(ip, "include", 7))
{
/* skip spaces AFTER "include" */
for (ip += 7; isspace(*ip); ++ip);
if (!parse(fopen(ip, "r")))
terror(1, tstrcat("cannot open ", ip));
continue; /* get next input line */
}

/* display the makefile line ? */
if (opts.display)
puts(input);

/* get rid of comments and preceeding spaces */
for (colonp = ip; *colonp && *colonp != '#'; ++colonp)
{
if (*colonp == '\'' || *colonp == '"')
colonp = tstrspan(colonp);
}

for (--colonp; colonp >= ip && isspace(*colonp); --colonp);

/* we *know* that some non-space is on this line, from above */
if (colonp >= ip)
*++colonp = '\0';

/* see if we have a shell command */
if (isspace(*input))
{
if (ntargs == 0)
terror(1, "rules must be after target");
got_shell:
if (nshell == smax)
{
shells = (shellptr *)
grow_list((char **) shells, &smax);
}
shells[nshell++] = add_shell(ip);
continue;
}

/* not a shell line, so must be a target or a macro */
if (ntargs != 0)
{
/* link previous preq's and shell's */
targs[ntargs] = NULL;
preqs[npreqs] = NULL;
shells[nshell] = NULL;
link_targs(targs, preqs, shells);
ntargs = npreqs = nshell = 0;
}

/* don't break out symbols until macro is invoked */
if (add_macro(ip, 0))
continue;

/* okay, we have a target line; break out macro symbols */
input = breakout(ip);

/* just look for tokens with standard isspace() separators */
ip = token(input, NULL, &schar);
while (ip)
{
colonp = strchr(ip, ':');
#ifdef MSDOS
/* need to allow c:/bin/make.exe as a target */
if (colonp && colonp - ip == 1)
colonp = strchr(colonp + 1, ':');
#endif
if (colonp)
{
/* got a separator */
*colonp = '\0';

/* if at front of token, target is done */
if (colonp == ip)
break;
}

if (ntargs == tmax)
targs = (targptr *) grow_list((char **) targs,
&tmax);
targs[ntargs] = add_target(ip);

/* make sure we don't save .INIT as our 1st target */
if (first_targ == NULL && *ip != '.')
first_targ = targs[ntargs];
++ntargs;

if (colonp)
break;
ip = token(NULL, NULL, &schar);
}

/* a target line without a colon? naughty, naughty! */
if (!colonp)
terror(-1, "Unexpected end of line seen");

/*
* taking care of four possible cases:
* 1) object : source
* 2) object: source
* 3) object :source
* 4) object:source
*/

if (colonp && *++colonp)
ip = colonp;
else
ip = token(NULL, NULL, &schar);

/* link the pre-req's */
while (ip)
{
if ((colonp = strchr(ip, ';')) != NULL)
{
ip[strlen(ip)] = schar;
*colonp = '\0';
}

if (*ip)
{
if (npreqs == pmax)
{
preqs = (fileptr *)
grow_list((char **) preqs,
&pmax);
}

preqs[npreqs++] = add_file(ip);
}

if (colonp)
{
ip = colonp + 1;
goto got_shell;
}

ip = token(NULL, NULL, &schar);
}

/* gotta free the line allocated by breakout() */
tfree(input);
}

/* link up any dangling dependants */
if (ntargs != 0)
{
targs[ntargs] = NULL;
preqs[npreqs] = NULL;
shells[nshell] = NULL;
link_targs(targs, preqs, shells);
}

/* clean up our mallocs */
tfree(targs);
tfree(preqs);
tfree(shells);

fclose(fd);
return (1);
}


/*
* link_targs - force a list of targs to point to same preq's and shell's
*/
link_targs(targs, preqs, shells)
REGISTER targptr *targs;
fileptr *preqs;
shellptr *shells;
{
while (targs && *targs)
{
/* process some special targets */
if ((*targs)->tfile->fname[0] == '.')
{
if (equal((*targs)->tfile->fname, ".SILENT"))
opts.silent = 1;
else if (equal((*targs)->tfile->fname, ".IGNORE"))
opts.ignore = 1;
else if (equal((*targs)->tfile->fname, ".SUFFIXES"))
/*
* set `suffix_targ' to speed up
* `default_rule'
*/
suffix_targ = *targs;

/* special rule has preq's reset */
/* normally, preq's are merely appended */
if (*preqs == NULL && (*targs)->tpreq != NULL)
{
tfree((*targs)->tpreq);
(*targs)->tpreq = NULL;
}

/* special rules have their shell commands replaced */
if ((*targs)->tshell != NULL && *shells != NULL)
{
shellptr *sp;

for (sp = (*targs)->tshell; *sp; ++sp)
tfree(*sp);
tfree((*targs)->tshell);
(*targs)->tshell = NULL;
}
}

/* each target in the list points to the preq's and shell's */
(*targs)->tpreq = append_preq((*targs)->tpreq, preqs);

/* we cannot expand the list of shell commands */
if ((*targs)->tshell != NULL && *shells != NULL)
{
terror(1, tstrcat("Too many rules defined for target ",
(*targs)->tfile->fname));
}
(*targs)->tshell = append_shell((*targs)->tshell, shells);
++targs;
}
}


/* macros must have the format: WORD = more stuff
* WORD= more stuff
* WORD =more stuff
* WORD=more stuff
* or: WORD += more stuff
* WORD +=more stuff
*
* it is assumed that there is no leading whitespace in `input'
*/
add_macro(input, scmd)
char *input;
int scmd;
{
REGISTER char *eqsign;
REGISTER char *value;
symptr symp;

/* gotta have an '=' to be a macro */
eqsign = strchr(input, '=');
if (eqsign == NULL)
return (0);

/* make sure we catch imbedded '='s (e.g. MACRO=STUFF) */
for (value = input; *value && !isspace(*value); ++value);
if (value > eqsign)
value = eqsign;

/* terminate the macro name */
*value = '\0';

/* find start of value */
for (value = eqsign + 1; isspace(*value); ++value);

/* look for concat character */
--eqsign;

if (eqsign < input || (eqsign == input && *eqsign == '+'))
terror(1, "Badly formed macro");

if (*eqsign == '+')
{
/* append to the current macro definition */
*eqsign = '\0';
symp = get_symbol(input, scmd);
if (symp->scmd && !scmd)
return (1);
if (symp->slevel < make_level)
symp = dup_symbol(symp, symp->svalue);
if (symp->svalue)
{
eqsign = tstrcat(symp->svalue, " ");
value = tstrcat(eqsign, value);
tfree(eqsign);
tfree(symp->svalue);
symp->svalue = value;
return (1);
}
}

add_symbol(input, value, scmd);
return (1);
}


/*
* add_symbol - add a pair to the symbol table
* - override existing symbol value
* - mark as either command-line macro or not
*/
add_symbol(name, value, scmd)
char *name;
char *value;
int scmd;
{
REGISTER symptr symp;

symp = get_symbol(name, scmd);
if (symp->scmd & !scmd)
return;
if (symp->slevel < make_level)
symp = dup_symbol(symp, NULL); /* don't dup the value */
if (symp->svalue)
tfree(symp->svalue);
symp->svalue = tstrcpy(value);
symp->scmd = scmd;
}


/*
* get_symbol - find a symbol in the symbol table
* - if non-extant, create
* - return created or found symbol node
*/
symptr
get_symbol(name, scmd)
char *name;
int scmd;
{
REGISTER symptr symp;
REGISTER t_mask mask;
char *np;

/* use `mask' to screen out most string comparisons */
mask = 0;
np = name;
while (*np)
mask += *np++;

/* linear search through symbol list */
for (symp = symbol_list; symp != NULL; symp = symp->snext)
{
if (mask != symp->smask)
continue;

if (equal(name, symp->sname))
return (symp);
}

symp = tnew(symnode); /* allocate symbol node */
symp->smask = mask; /* record mask for later */
symp->sname = tstrcpy(name); /* allocate string and copy name */
symp->scmd = scmd; /* command line macro? */
symp->slevel = make_level; /* current new_make() level */

/* get the value from the environment, if it is there */

if ((symp->svalue = getenv(name)) != NULL)
{
symp->svalue = tstrcpy(symp->svalue);

/*
* if `-e', let command line macros override, but not macro
* assignments in the makefile.
*/
if (opts.envirn)
symp->scmd = 1;
}

symp->snext = symbol_list; /* link to head of symbol list */
symbol_list = symp;

return (symp);
}


/*
* dup_sym - duplicate a symbol node, but at current new_make() level
*/
symptr
dup_symbol(sp, svalue)
symptr sp;
char *svalue;
{
symptr nsp;

nsp = tnew(symnode); /* allocate symbol node */
nsp->smask = sp->smask; /* record mask for later */
nsp->sname = tstrcpy(sp->sname); /* allocate string and copy
* name */
nsp->svalue = (svalue == NULL) ? NULL : tstrcpy(svalue);
nsp->scmd = sp->scmd; /* command line macro? */
nsp->slevel = make_level; /* current new_make() level */

nsp->snext = symbol_list; /* link to head of symbol list */
symbol_list = nsp;

return (nsp);
}


/*
* add_target - return extant target node, or create new one
*/
targptr
add_target(name)
char *name;
{
t_mask mask;
REGISTER targptr targp;
fileptr filep;

/* each target must have a file node */
filep = add_file(name);

/* see if target already exists */
targp = hash_target(name, &mask);
if (targp)
return (targp);

/* oh well, gotta create one */
targp = tnew(targnode); /* allocate a target node */
targp->tmask = mask; /* save mask for later */
targp->tfile = filep; /* save pointer to file node */
targp->tpreq = NULL; /* no pre-req's yet */
targp->tshell = NULL; /* no shell lines yet */

targp->tnext = target_list; /* link to front of target list */
target_list = targp;

return (targp);
}


/*
* hash_target - look up target (by name) in target list
* - return target node or NULL
* - if requested, also return the mask
*/
targptr
hash_target(name, maskp)
char *name;
t_mask *maskp;
{
REGISTER targptr targp;
REGISTER t_mask mask;
char *np;

/* use `mask' to screen out most string comparisons */
mask = 0;
np = name;
while (*np)
mask += *np++;

/* see if we gotta return it */
if (maskp != NULL)
*maskp = mask;

/* linear search through target list */
for (targp = target_list; targp != NULL; targp = targp->tnext)
{
if (mask != targp->tmask)
continue;

/* target name is ONLY stored in the file node */
if (equal(name, targp->tfile->fname))
return (targp);
}

/* nope, no target here */
return (NULL);
}


/*
* add_file - return a found or created file node
*/
fileptr
add_file(name)
char *name;
{
t_mask mask;
REGISTER fileptr filep;

/* see if file node already exists */
filep = hash_file(name, &mask);
if (filep)
return (filep);

filep = tnew(filenode); /* allocate new file node */
filep->fmask = mask; /* save mask for later */
filep->fname = tstrcpy(name); /* allocate string and copy name */
filep->ftime = MAXNEGTIME; /* init MODIFY time to long time ago */

filep->fnext = file_list; /* link to head of file list */
file_list = filep;

return (filep);
}


/*
* hash_file - look up file (by name) in file list
* - return file node or NULL
* - if requested, also return the mask
*/
fileptr
hash_file(name, maskp)
char *name;
t_mask *maskp;
{
REGISTER fileptr filep;
REGISTER t_mask mask;
char *np;

/* use `mask' to screen out most string comparisons */
mask = 0;
np = name;
while (*np)
mask += *np++;

/* see if we gotta return it */
if (maskp != NULL)
*maskp = mask;

/* linear search through file list */
for (filep = file_list; filep != NULL; filep = filep->fnext)
{
if (filep->fmask != mask)
continue;

if (equal(filep->fname, name))
return (filep);
}

/* nope, no file here */
return (NULL);
}


/*
* append_node - add a node to the end of an array of nodes
*/
char **
append_node(node, adds, size)
char **node;
char **adds;
int size;
{
REGISTER int addlen, len;

for (addlen = 0; adds[addlen] != NULL; ++addlen);
if (addlen++ == 0)
return (node);

len = 0;

if (node != NULL)
{
for (; node[len] != NULL; ++len);
node = (char **) trealloc((char *) node, (len + addlen) * size);
}
else
node = (char **) talloc(addlen * size);

memcpy(node + len, adds, addlen * size);
return (node);
}

/*
* add_shell - create a new shell node, and add to end of given list
*/
shellptr
add_shell(input)
char *input;
{
REGISTER shellptr snode;

snode = tnew(shellnode);/* allocate a new shell node */
snode->s_shell = snode->s_ignore = snode->s_silent = 0;

for (; isspace(*input); ++input); /* skip over leading spaces */
for (;; ++input)
{
if (*input == '+')
snode->s_shell = 1; /* must use command.com */
else if (*input == '-')
snode->s_ignore = 1; /* ignore return value */
else if (*input == '@')
snode->s_silent = 1; /* don't echo command */
else
break;
}

snode->scmd = tstrcpy(input); /* allocate string and copy command */

snode->slink = shell_list; /* attach to global list */
shell_list = snode;

return (snode);
}


/*
* breakout - replace macro names with values
* - apply recursively
* note: allocates (and returns) a string which must be freed
*/
char *
breakout(input)
REGISTER char *input;
{
char *dest, *dend;
REGISTER char *dp;
int dlen;
int tlen;
int state;
char symname[100];
char *sp;
symptr symp;
int slen;
char endch;

/* allocate a string twice as long as input string */

dlen = strlen(input) * 2;
dest = dp = talloc(dlen);
dend = dest + dlen;

/*
* state machine with 4 states
* 0) normal text -- just copy
* 1) starting macro -- define end char (e.g. ')', '}')
* 2) macro name -- copy to a buffer
* 3) end of macro -- look up value, and copy
*/
state = 0;

while (*input || state == 3)
{
/* if we don't have enough room, double size of string */
if (dp == dend)
{
dlen *= 2;
tlen = dp - dest;
dest = trealloc(dest, dlen);
dp = dest + tlen;
dend = dest + dlen;
}

switch (state)
{
case 0:
if (*input == '$')
state = 1; /* found a macro */
else
*dp++ = *input++;
break;

case 1:
state = 2; /* only in this state for 1 char */
sp = symname;
switch (*++input)
{
case '(':
endch = ')';
break;
case '{':
endch = '}';
break;
default:
/* single char; go to state 3 immediately */
*sp++ = *input;
state = 3;
break;
}
++input;/* skip bracket (or character) */
break;

case 2:
if (*input == endch)
state = 3;
else
*sp++ = *input;

if ((sp - symname) >= (sizeof symname / sizeof symname[0]))
{
sp[-1] = '\0';
terror(1,
tstrcat("Macro too long (limit 100 chars): ",
symname));
}

++input;/* make sure we skip end char */
break;

case 3:
*sp = '\0';
symp = get_symbol(symname, 0);
sp = symp->svalue;
slen = -1;
while (sp && *sp)
{
/*
* if value has a macro in it, we must
* process recursively
*/
if (*sp == '$')
{
sp = breakout(symp->svalue);
/* now guaranteed not to have a '$' */
slen = strlen(sp);
break;
}
++sp;
}

if (slen == -1)
{
/* value did NOT have a macro */
slen = (sp - symp->svalue);
sp = symp->svalue;
}

/* if we have not enough room, expand */
if (slen >= (dend - dp))
{
/* use slen to make sure that we can fit */
dlen = dlen * 2 + slen;
tlen = dp - dest;
dest = trealloc(dest, dlen);
dp = dest + tlen;
dend = dest + dlen;
}

/* if length is zero, don't bother to copy */
if (slen)
{
strcpy(dp, sp);
dp += slen;
}

if (sp != symp->svalue)
tfree(sp); /* must've called `breakout' */

state = 0; /* and we are back to text */
break;
}
}

if (state != 0)
terror(1, tstrcat("Improper macro.\n", dest));

*dp = '\0'; /* terminate the string */
return (dest); /* and return it */
}


  3 Responses to “Category : Miscellaneous Language Source Code
Archive   : MAKE15.ZIP
Filename : PARSE.C

  1. Very nice! Thank you for this wonderful archive. I wonder why I found it only now. Long live the BBS file archives!

  2. This is so awesome! 😀 I’d be cool if you could download an entire archive of this at once, though.

  3. But one thing that puzzles me is the “mtswslnkmcjklsdlsbdmMICROSOFT” string. There is an article about it here. It is definitely worth a read: http://www.os2museum.com/wp/mtswslnk/