/* @(#)parse.c 1.129 21/08/06 Copyright 1985-2021 J. Schilling */ #include #ifndef lint static UConst char sccsid[] = "@(#)parse.c 1.129 21/08/06 Copyright 1985-2021 J. Schilling"; #endif /* * Make program * Parsing routines * * Copyright (c) 1985-2021 by J. Schilling */ /* * The contents of this file are subject to the terms of the * Common Development and Distribution License, Version 1.0 only * (the "License"). You may not use this file except in compliance * with the License. * * See the file CDDL.Schily.txt in this distribution for details. * A copy of the CDDL is also available via the Internet at * http://www.opensource.org/licenses/cddl1.txt * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file CDDL.Schily.txt from this distribution. */ #include #include #include #include #include /* include sys/types.h for schily/schily.h */ #include #include #include #include "make.h" #ifdef pdp11 #define MAXOBJS 64 /* Max # of targets in target list. */ #else #define MAXOBJS 512 /* Max # of targets in target list. */ #endif EXPORT void parsefile __PR((void)); LOCAL void define_obj __PR((obj_t * obj, int n, int objcnt, int type, list_t * dep, cmd_t * cmd)); LOCAL obj_t *define_dcolon __PR((obj_t *obj)); LOCAL void listappend __PR((obj_t *obj, list_t *dep)); LOCAL void define_patrule __PR((obj_t * obj, list_t * dep, cmd_t * cmd, int type)); LOCAL void print_patrules __PR((FILE *f)); LOCAL obj_t *check_ssuffrule __PR((obj_t *obj, list_t *dep)); EXPORT char *get_var __PR((char *name)); EXPORT void define_var __PR((char *name, char *val)); LOCAL int getobjname __PR((void)); LOCAL obj_t *getobj __PR((void)); LOCAL int getname __PR((int type)); LOCAL obj_t *getnam __PR((int type)); LOCAL int getln __PR((void)); LOCAL list_t **listcat __PR((obj_t * obj, list_t ** tail)); LOCAL list_t **mklist __PR((char *line, list_t ** tail, BOOL doexpand)); LOCAL list_t *cplist __PR((list_t * l)); LOCAL list_t **exp_list __PR((obj_t * o, list_t ** tail)); LOCAL list_t *getlist __PR((int *typep)); LOCAL BOOL is_shvar __PR((obj_t ** op, int *typep, list_t *** tailp)); LOCAL list_t *getshvar __PR((int *typep)); LOCAL cmd_t *getcmd __PR((void)); LOCAL int exp_ovec __PR((obj_t ** ovec, int objcnt)); LOCAL int read_ovec __PR((obj_t ** ovec, int *typep)); EXPORT list_t *cvtvpath __PR((list_t *l)); EXPORT BOOL is_inlist __PR((char *objname, char *name)); LOCAL BOOL obj_in_list __PR((obj_t *obj, list_t *l)); EXPORT BOOL nowarn __PR((char *name)); LOCAL void warn __PR((char *, ...)) __printflike__(1, 2); LOCAL void exerror __PR((char *, ...)) __printflike__(1, 2); LOCAL obj_t *_objlook __PR((obj_t *table[], char *name, BOOL create)); EXPORT obj_t *objlook __PR((char *name, BOOL create)); EXPORT list_t *objlist __PR((char *name)); EXPORT obj_t *ssufflook __PR((char *name, BOOL create)); EXPORT BOOL check_ssufftab __PR((void)); LOCAL void clear_ssufftab __PR((void)); LOCAL BOOL is_suffix_rule __PR((obj_t *obj)); LOCAL void prvar __PR((obj_t * p)); LOCAL void ptree __PR((obj_t * p, int n)); EXPORT void printtree __PR((void)); EXPORT void probj __PR((FILE *f, obj_t * o, int type, int dosuff)); EXPORT void prtree __PR((void)); LOCAL char *typestr __PR((int type)); LOCAL void printobj __PR((FILE *f, obj_t ** ovec, int objcnt, int type, list_t * deplist, cmd_t * cmdlist)); /* * parse the dependency file * * { ...} : * { } * * = * * += * * :sh= */ EXPORT void parsefile() { int i, objcnt; int type = 0; /* init to make GCC quiet */ obj_t *ovec[MAXOBJS]; list_t *deplist; cmd_t *cmdlist; if (Do_Warn) error("Parsing file '%s'\n", mfname); if (Dmake > 0) error(">>>>>>>>>>>>>>>> Reading makefile '%s'\n", mfname); ovec[0] = 0; lineno = 1; col = 0; getch(); while (lastc != EOF) { if (lastc == '\t') { /* * Skip leading white space that follows a TAB. */ while (lastc != EOF && (lastc == ' ' || lastc == '\t')) getch(); /* * Abort if such a line is not empty or contains more * than only a comment. This has the side effect that * '^ include' will not work anymore. */ if (lastc != '\n') { exerror( "Unexpected character followed by nonblank '%c' <%o>", lastc, lastc); } getch(); /* Eat up new line character */ continue; /* Continue with next line */ } type = 0; if ((objcnt = read_ovec(ovec, &type)) == 0) { if (Do_Warn && (type & NWARN) == 0) { /* * This is when $(EMPTY) was expanded to nothing * in exp_ovec() inside read_ovec(). */ warn("Missing object name"); } type = ntype(type); if (type != COLON && type != DCOLON) continue; } type = ntype(type); if (objcnt < 0) { errmsgno(EX_BAD, "Objcount: %d Objects: ", -objcnt); for (i = 0; i < -objcnt; i++) error("'%s' ", ovec[i]->o_name); error("\n"); exerror("Too many target items"); } #ifdef XXX /* Begin new code for include */ printf("objcnt: %d ovec[0]: %s lastc: '%c'\n", objcnt, ovec[0]->o_name, lastc); for (i = 0; i < objcnt; i++) printf("name[%d] = %s ", i, ovec[i]->o_name); printf("\n"); #endif if (lastc == '\n') { if (streql(ovec[0]->o_name, "include") || streql(ovec[0]->o_name, "-include")) { for (i = 1; i < objcnt; i++) { doinclude(ovec[i]->o_name, ovec[0]->o_name[0] != '-'); } /* XXX freeobj ??? */ continue; } if (streql(ovec[0]->o_name, "export")) { for (i = 1; i < objcnt; i++) { doexport(ovec[i]->o_name); } /* XXX freeobj ??? */ continue; } if (streql(ovec[0]->o_name, "unexport")) { for (i = 1; i < objcnt; i++) { dounexport(ovec[i]->o_name); } /* XXX freeobj ??? */ continue; } if (streql(ovec[0]->o_name, "readonly")) { for (i = 1; i < objcnt; i++) { ovec[i]->o_flags |= F_READONLY; } /* XXX freeobj ??? */ continue; } /* * Any other word on the beginning of a line * that is not followed by a ':' or a '=' is an error. */ } /* end new code for include */ if (lastc != ':' && lastc != '=') { errmsgno(EX_BAD, "Objcount: %d Objects: ", objcnt); for (i = 0; i < objcnt; i++) error("'%s' ", ovec[i]->o_name); error("\n"); exerror("Missing : or =, got '%c' <%o>", lastc, lastc); } getch(); /* * If the macro type is ::= (GNU style assgignment), * the + operator expands variables on the right hand side * and we need to replace the operator + by +:= first. * ::= followed by + results in unpredictable behavior and for * this reason, it is recommended to have at least one * lowercase char in ::= types macro name to tell people about * the unusual behavior of the + operator. */ if (type == ADDMAC && ovec[0]->o_type == GNU_ASSIGN) type = ADD_VARMAC; in_parser = -1; /* In parser but no ::= expansion */ deplist = getlist(&type); in_parser = 0; if (type == SHVAR) deplist = getshvar(&type); if (lastc == ';') { lastc = '\t'; firstc = '\t'; } /* * We should test for type == COLON only but unfortunately, * the makefilesystem contained some simple pattern rule * definitions that are written as: tgt_suff = list. * We only allow what has been used in the makefilesystem. * This changed in late 1999, we should keep support * for it for at least 5 years. */ /* if (basetype(type) == COLON) {*/ if (basetype(type) == COLON || (basetype(type) == EQUAL && deplist && objcnt > 0 && ovec[0]->o_name[0] == '.')) { cmdlist = getcmd(); } else { cmdlist = NULL; } while (lastc == '\n') getch(); for (i = 0; i < objcnt; i++) define_obj(ovec[i], i, objcnt, type, deplist, cmdlist); if (Dmake > 0) printobj(stderr, ovec, objcnt, type, deplist, cmdlist); } if (Dmake > 0) error(">>>>>>>>>>>>>>>> End of makefile '%s'\n", mfname); } /* * Add dependency and command lists to a node in the object tree. * Allow definitions to be overridden if the new definition in in * a different file. */ LOCAL void define_obj(obj, n, objcnt, type, dep, cmd) register obj_t *obj; int n; int objcnt; int type; register list_t *dep; cmd_t *cmd; { /* * For a conditional macro assignment ?=, we do nothing if the * object already has a value list assigned. */ if (type == CONDEQUAL && obj->o_list) return; /* * First check for possible direct recursions to avoid to blow * up memory immediately. */ if (type != '=' && obj_in_list(obj, dep)) exerror("Recursion in dependencies for '%s'", obj->o_name); /* * If we have a list of targets with the same dependency list, * we copy the list structure to be able to separately * append new elements to each of the targets. */ if (n > 0) dep = cplist(dep); if (objcnt == 1) { if (basetype(type) == COLON && strchr(obj->o_name, '%') != NULL) { define_patrule(obj, dep, cmd, type); return; } /* * We should test for type == COLON too but unfortunately, * the makefilesystem contained some simple pattern rule * definitions that are written as: tgt_suff = list. * This changed in late 1999, we should keep support * for it for at least 5 years. */ /* if (type == COLON && && dep != NULL && cmd != NULL) {*/ if (dep != NULL && cmd != NULL) { obj = check_ssuffrule(obj, dep); } } else { obj->o_flags |= F_MULTITARGET; } if (obj->o_type == 0) obj->o_type = type; /* * Save first target that does not start with * a dot in case no arguments have been supplied * to make. */ if (n == 0 && !default_tgt && basetype(type) == COLON && (obj->o_name[0] != '.' || obj->o_name[1] == SLASH || (obj->o_name[1] == '.' && obj->o_name[2] == SLASH))) default_tgt = obj; if (n == 0 && basetype(type) == COLON && obj->o_name[0] == '.' && streql(obj->o_name, ".POSIX")) posixmode = TRUE; if (type == DCOLON) obj = define_dcolon(obj); /* * XXX Probleme gibt es mit der Gleichbehandlung von ':' und '=' typen. * XXX So definiert 'smake' MAKE_NAME= smake und später kommt evt. * XXX smake: $(OBJLIST) */ if (obj->o_flags & F_READONLY) { /* Check for "read only" */ if (!((obj->o_flags & F_IDXOVERWRT) && (obj->o_fileindex == Mfileindex))) return; } obj->o_flags |= Mflags; /* Add current global flags */ /* * Old: * Definition in new Makefile overrides existing definitions * * New: * Definitions in Makefiles from commandline override * existing definitions. Further definitions in cmdline Makefiles * append. */ if ((obj->o_fileindex < MF_IDX_MAKEFILE || basetype(obj->o_type) == EQUAL) && (type != ADDMAC && type != ADD_VARMAC) && !streql(obj->o_name, ".SUFFIXES")) { /* * We unfortunately also print this warning in case that "obj" * just has been created by the parser. */ if (Do_Warn && (obj->o_flags & F_NEWNODE) == 0) warn("'%s' RE-defined", obj->o_name); if (obj->o_fileindex == MF_IDX_ENVIRON) { obj->o_fileindex = Mfileindex; obj->o_type = type; } obj->o_list = dep; obj->o_cmd = cmd; obj->o_flags &= ~F_NEWNODE; return; } /* * Add new definitions to list. * Ignore definitions already in list. */ listappend(obj, dep); if (obj->o_cmd != (cmd_t *) NULL) { if (cmd != (cmd_t *) NULL) { warn("Multiple commands defined for '%s'", obj->o_name); /* XXX freelist()/freecmd() ??? */ } } else { obj->o_cmd = cmd; } } /* * Define an intermediate target for Double Colon :: Rules * * a:: b * cmd_b * a:: c * cmd_c * * results in: * a:: ::1@a ::2@a * and * ::1@a: b * cmd_b * ::2@a: c * cmd_c */ LOCAL obj_t * define_dcolon(obj) register obj_t *obj; { static int serial = 0; obj_t *o; list_t *lo; char _name[TYPICAL_NAMEMAX]; char *name = _name; size_t namelen = sizeof (_name); char *np = NULL; if (obj->o_namelen + 16 >= namelen) { namelen = obj->o_namelen + 16; name = np = ___realloc(np, namelen, "dcolon target"); } snprintf(name, namelen, "::%d@%s", ++serial, obj->o_name); o = objlook(name, TRUE); if (np) free(np); o->o_type = ':'; o->o_flags |= F_DCOLON; /* Mark as intermediate :: object */ lo = (list_t *) fastalloc(sizeof (list_t)); lo->l_next = NULL; lo->l_obj = o; listappend(obj, lo); /* Append this intermediate to list */ o->o_node = obj; /* Make this inter. obj aux to orig */ return (o); /* Return intermediate object */ } /* * Append new definitions to dependencies in o_list, but * ignore them if they are already in the list. */ LOCAL void listappend(obj, dep) register obj_t *obj; register list_t *dep; { if (dep == NULL && obj->o_name[0] == '.' && streql(obj->o_name, ".SSUFFIX_RULES")) clear_ssufftab(); if (obj->o_list != (list_t *) NULL) { register list_t *l = obj->o_list; if (dep == NULL && obj->o_name[0] == '.') { /* * Allow to clear special targets. */ if (streql(obj->o_name, ".SUFFIXES") || streql(obj->o_name, ".SSUFFIX_RULES") || streql(obj->o_name, ".DEFAULT") || streql(obj->o_name, ".NO_WARN") || streql(obj->o_name, ".SCCS_GET") || streql(obj->o_name, ".SPACE_IN_NAMES")) obj->o_list = NULL; obj->o_flags &= ~F_NEWNODE; return; } if (Do_Warn && (obj->o_flags & F_NEWNODE) == 0) { if (obj->o_type != ':' || Do_Warn > 1) warn("'%s' ADD-defined", obj->o_name); } /* * if not already head of list, try to append ... */ if (l != dep) { while (l->l_next && l->l_next != dep) l = l->l_next; if (l->l_next == (list_t *) NULL) l->l_next = dep; } } else { obj->o_list = dep; } obj->o_flags &= ~F_NEWNODE; } patr_t *Patrules; patr_t **pattail = &Patrules; /* * Parse a pattern rule definition. This is a special type of implicit rules. */ LOCAL void define_patrule(obj, dep, cmd, type) obj_t *obj; list_t *dep; cmd_t *cmd; int type; { patr_t *p; char *s; register list_t *l; p = (patr_t *) fastalloc(sizeof (*p)); /* larger than list_t */ p->p_name = obj; p->p_list = dep; p->p_cmd = cmd; s = strchr(obj->o_name, '%'); if (s != NULL) { *s = '\0'; p->p_tgt_prefix = strsave(obj->o_name); p->p_tgt_suffix = strsave(&s[1]); *s = '%'; } else { /* This can never happen! */ p->p_tgt_prefix = strsave(Nullstr); p->p_tgt_suffix = strsave(obj->o_name); } p->p_tgt_pfxlen = strlen(p->p_tgt_prefix); p->p_tgt_suflen = strlen(p->p_tgt_suffix); for (l = dep; l; l = l->l_next) { if (strchr(l->l_obj->o_name, '%')) l->l_obj->o_flags |= F_PERCENT; } if (dep == NULL) { p->p_src_prefix = 0; p->p_src_suffix = 0; p->p_src_pfxlen = 0; p->p_src_suflen = 0; } else { s = strchr(dep->l_obj->o_name, '%'); if (s != NULL) { *s = '\0'; p->p_src_prefix = strsave(dep->l_obj->o_name); p->p_src_suffix = strsave(&s[1]); *s = '%'; } else { p->p_src_prefix = strsave(Nullstr); p->p_src_suffix = strsave(dep->l_obj->o_name); } p->p_src_pfxlen = strlen(p->p_src_prefix); p->p_src_suflen = strlen(p->p_src_suffix); } p->p_flags = 0; if (type == DCOLON) p->p_flags |= PF_TERM; /* Make it a termiator rule */ *pattail = p; pattail = &p->p_next; p->p_next = 0; } /* * Print the complete list of pattern rules. */ LOCAL void print_patrules(f) FILE *f; { patr_t *p = Patrules; cmd_t *c; while (p) { register list_t *l; fprintf(f, "%s%%%s:%s", p->p_tgt_prefix, p->p_tgt_suffix, (p->p_flags & PF_TERM) ? ":":""); for (l = p->p_list; l; l = l->l_next) { fprintf(f, " %s", l->l_obj->o_name); } printf("\n"); for (c = p->p_cmd; c; c = c->c_next) fprintf(f, "\t%s\n", c->c_line); p = p->p_next; } } /* * Check for a simple suffix rule definition. * In case we found a simple suffix rule definition, return a new obj_t *. */ LOCAL obj_t * check_ssuffrule(obj, dep) obj_t *obj; list_t *dep; { register char *name = obj->o_name; register list_t *l; extern int xssrules; /* * Check if the first character of the target is a '.' or * if the target name equals "", but make sure this is * not a vanilla target that just starts with "./". */ if ((name[0] == '.' && strchr(name, SLASH) == NULL) || (name[0] == '"' && name[1] == '"' && name[2] == '\0')) { /* * All dependency names must start with a '.' * and may not contain a SLASH. */ for (l = dep; l; l = l->l_next) { if (l->l_obj->o_name[0] != '.') return (obj); if (strchr(l->l_obj->o_name, SLASH) != NULL) return (obj); } obj = ssufflook(obj->o_name, TRUE); xssrules++; } return (obj); } /* * Get the macro value as in the form macro=value. */ EXPORT char * get_var(name) char *name; { obj_t *o = objlook(name, FALSE); if (o == (obj_t *)NULL) return ((char *)NULL); if (basetype(o->o_type) == EQUAL && o->o_list != NULL) return (o->o_list->l_obj->o_name); return ((char *)NULL); } /* * Define a macro as in the form macro=value. * Value may only be one word. */ EXPORT void define_var(name, val) char *name; char *val; { obj_t *o; obj_t *ov; list_t *list; list_t **tail = &list; o = objlook(name, TRUE); if (o->o_flags & F_READONLY) { /* Check for "read only" */ if (!((o->o_flags & F_IDXOVERWRT) && (o->o_fileindex == Mfileindex))) return; } o->o_flags |= Mflags; /* Add current global flags */ if (val && *val) { /* Only if not empty */ ov = objlook(val, TRUE); tail = listcat(ov, tail); } *tail = (list_t *) NULL; o->o_list = list; o->o_type = EQUAL; o->o_flags &= ~F_NEWNODE; } #ifdef NEEDED /* * Define a macro as in the form macro=vallist. * Vallist may be a list of words that is white space separated in one string. */ EXPORT void define_lvar(name, vallist) char *name; char *vallist; { obj_t *o; obj_t *ov; list_t *list; list_t **tail = &list; o = objlook(name, TRUE); if (o->o_flags & F_READONLY) /* Check for "read only" */ return; o->o_flags |= Mflags; /* Add current global flags */ tail = mklist(vallist, tail, FALSE); *tail = (list_t *) NULL; o->o_list = list; o->o_type = EQUAL; o->o_flags &= ~F_NEWNODE; } /* * Return a list_t pointer to the nth word in a macro value list. */ list_t * varvaln(name, n) char *name; int n; { obj_t *o = objlook(name, FALSE); if (o) return (list_nth(o->o_list, n)); return ((list_t *)0); } #endif /* NEEDED */ #define white(c) (c == ' ' || c == '\t') /* * Read a space separated word from current Makefile. * Used for target list. Stop at space, ':' '=' and ','. * Returns the length of the word. */ LOCAL int getobjname() { register char *p = gbuf; register int beg = 0; register int end = 0; register int nestlevel = 0; while (white(lastc)) getch(); /* Skip white space. */ if (lastc == '$') { switch (beg = peekch()) { case '(': end = ')'; break; case '{': end = '}'; break; default: beg = end = -1; } } while (lastc != EOF && (' ' < lastc || nestlevel > 0)) { if (lastc == beg) nestlevel++; else if (lastc == end) nestlevel--; if (nestlevel <= 0) { if (lastc == ':' || lastc == '=' || lastc == ',') break; } #ifdef __CHECK_NAMEMAX_IN_GETOBJNAME__ if (n >= NAMEMAX - 2) exerror("Name more than %d chars long", NAMEMAX - 2); #endif /* __CHECK_NAMEMAX_IN_GETOBJNAME__ */ if (p >= gbufend) p = growgbuf(p); *p++ = lastc; if (istext(lastc)) /* Can we avoid getch()? */ p = gtext(p); /* Get usual text faster */ if (nestlevel <= 0 && lastc == '\\') { if (white(peekch()) && objlist(".SPACE_IN_NAMES")) { getch(); p--; *p++ = lastc; } } getch(); } if (p >= gbufend) p = growgbuf(p); *p = '\0'; /* Terminate with null char */ return (p - gbuf); } /* * Read a target file name. */ LOCAL obj_t *getobj() { return (getobjname() ? objlook(gbuf, TRUE) : (obj_t *) NULL); } /* * Read a space separated word from current Makefile. * General purpose version for dependency list. * Returns the length of the word. */ LOCAL int getname(type) int type; { register char *p = gbuf; register int beg = 0; register int end = 0; register int nestlevel = 0; if (type == ':') type = ';'; else type = '\0'; while (white(lastc)) getch(); /* Skip white space. */ if (lastc == '$') { switch (beg = peekch()) { case '(': end = ')'; break; case '{': end = '}'; break; default: beg = end = -1; } } while (lastc != EOF && (' ' < lastc || nestlevel > 0)) { if (lastc == beg) nestlevel++; else if (lastc == end) nestlevel--; if (nestlevel <= 0) { if (lastc == type) break; } #ifdef __CHECK_NAMEMAX_IN_GETNAME__ if (n >= NAMEMAX - 2) exerror("Name more than %d chars long", NAMEMAX - 2); #endif /* __CHECK_NAMEMAX_IN_GETNAME__ */ if (p >= gbufend) p = growgbuf(p); *p++ = lastc; if (istext(lastc)) /* Can we avoid getch()? */ p = gtext(p); /* Get usual text faster */ if (nestlevel <= 0 && lastc == '\\') { if (white(peekch()) && objlist(".SPACE_IN_NAMES")) { getch(); p--; *p++ = lastc; } } getch(); } if (p >= gbufend) p = growgbuf(p); *p = '\0'; /* Terminate with null char */ return (p - gbuf); } /* * Read a dependency file name. */ LOCAL obj_t * getnam(type) int type; { return (getname(type) ? objlook(gbuf, TRUE) : (obj_t *) NULL); } /* * Reads a line from current Makefile. * Returns the length of the string. */ LOCAL int getln() { register char *p = gbuf; while (white(lastc)) getch(); /* Skip white space. */ while (lastc != EOF && lastc != '\n') { #ifdef __CHECK_NAMEMAX_IN_GETLN__ if (n >= NAMEMAX - 2) { exerror("Line more than %d chars long", NAMEMAX - 2); } #endif /* __CHECK_NAMEMAX_IN_GETLN__ */ if (p >= gbufend) p = growgbuf(p); *p++ = lastc; p = gtext(p); /* Get usual text faster */ getch(); } if (p >= gbufend) p = growgbuf(p); *p = '\0'; /* Terminate with null char */ return (p - gbuf); } /* * Add one new object to the end of a list of objects. */ LOCAL list_t ** listcat(obj, tail) obj_t *obj; list_t **tail; { list_t *item; *tail = item = (list_t *) fastalloc(sizeof (list_t)); item->l_obj = obj; tail = &item->l_next; return (tail); } /* * Make a list of objects by parsing a line * and cutting it at whitespaces. */ LOCAL list_t ** mklist(line, tail, doexpand) register char *line; register list_t **tail; BOOL doexpand; { register obj_t *o; register char *p; /*printf("Line: '%s'\n", line);*/ for (p = line; *p; ) { while (*p && white(*p)) p++; line = p; while (*p) { if (white(*p)) { *p++ = '\0'; if (*line) break; } p++; } /*printf("line: '%s'\n", line);*/ /* * If the list ends with white space, we will see * an empty string at the end of the list unless * we apply this test. */ if (*line == '\0') break; o = objlook(line, TRUE); if (doexpand) tail = exp_list(o, tail); else tail = listcat(o, tail); } return (tail); } /* * Copy a list. * Generate new list pointers and re-use the old obj pointers. */ LOCAL list_t * cplist(l) list_t *l; { list_t *list; register list_t **tail = &list; while (l) { tail = listcat(l->l_obj, tail); l = l->l_next; } *tail = (list_t *) NULL; return (list); } EXPORT BOOL in_parser; EXPORT BOOL in_varassign; /* * Expand one object name using make macro definitions. * Add the new objects to the end of a list of objects. */ LOCAL list_t ** exp_list(o, tail) obj_t *o; list_t **tail; { char *name; char *xname; name = o->o_name; if (strchr(name, '$')) { if (in_parser == FALSE) in_parser = TRUE; xname = substitute(name, NullObj, 0, 0); if (in_parser == TRUE) in_parser = FALSE; if (streql(name, xname)) { printf("eql: %s\n", name); tail = listcat(o, tail); } else { /*printf("Sxname: %s <- %s\n", xname, name);*/ tail = mklist(xname, tail, FALSE); } } else { tail = listcat(o, tail); } *tail = (list_t *) NULL; return (tail); } #ifdef NEEDED /* * Expand a list of object names using make macro definitions. * Add the new objects to the end of a list of objects. */ LOCAL list_t * exp_olist(l) list_t *l; { list_t *list; list_t **tail = &list; while (l) { tail = exp_list(l->l_obj, tail); l = l->l_next; } *tail = (list_t *) NULL; return (list); } #endif /* NEEDED */ /* * Read a list of dependency file names. */ LOCAL list_t * getlist(typep) int *typep; { list_t *list; list_t **tail = &list; obj_t *o; int type = basetype(*typep); BOOL first = TRUE; if (type == '=') { int n; #ifdef nono char *p; #endif n = getln(); #ifdef nono /* Do not kill trailing whitespace !!! */ p = &gbuf[n-1]; while (white(*p)) p--; *++p = '\0'; #endif /* * Only add to list if right hand side is not completely white. */ if (n) { o = objlook(gbuf, TRUE); if (*typep == VAR_ASSIGN || *typep == GNU_ASSIGN || *typep == ADD_VARMAC) { if (*typep == VAR_ASSIGN || *typep == ADD_VARMAC) in_varassign++; /* Inhibit $$ expans. */ tail = exp_list(o, tail); if (*typep == VAR_ASSIGN || *typep == ADD_VARMAC) in_varassign--; } else tail = listcat(o, tail); } } else while ((o = getnam(type)) != (obj_t *) NULL) { if (type == '=') { tail = listcat(o, tail); } else { if (first) { first = FALSE; if (is_shvar(&o, typep, &tail)) break; } tail = exp_list(o, tail); } } *tail = (list_t *) NULL; return (list); } /* * Check if a definition for immediate shell expansion follows. */ LOCAL BOOL is_shvar(op, typep, tailp) obj_t **op; int *typep; list_t ***tailp; { obj_t *o = *op; if (o->o_name[0] != 's') return (FALSE); if (streql(o->o_name, "sh=")) { *typep = SHVAR; return (TRUE); } if (streql(o->o_name, "sh")) { if ((o = getnam(*typep)) == (obj_t *)NULL) { return (FALSE); } if (streql(o->o_name, "=")) { *typep = SHVAR; return (TRUE); } else { *tailp = exp_list(*op, *tailp); *op = o; } } return (FALSE); } /* * read a line and expand by shell */ LOCAL list_t * getshvar(typep) int *typep; { list_t *list; register list_t **tail = &list; char *p; *typep = '='; getln(); p = shout(gbuf); tail = mklist(p, tail, FALSE); *tail = (list_t *) NULL; return (list); } /* * Read a list of command lines that follow a dependency list. */ LOCAL cmd_t * getcmd() { cmd_t *list; register cmd_t *item, **tail = &list; register char *p; setincmd(TRUE); /* Switch reader to command mode */ if (lastc == '\n') /* Not a ';' command type */ getch(); while (lastc != EOF && (firstc == '\t' || firstc == '#')) { /* * We handle comment in command lines as in old UNIX 'make' and * not as required by POSIX. A command line that starts with * a '#' (the following code) is handled as usual. * A '#' inside a command line and escaped newlines are * forwarded to the shell. This is needed to allow something * like cc -# with SunPRO C. */ if (firstc == '#') { skipline(); /* Skip commented out line */ getch(); /* Skip newline character. */ continue; } while (white(lastc)) getch(); /* Skip white space. */ for (p = gbuf; lastc != EOF; getch()) { if (lastc == '\n' && (p == gbuf || p[-1] != '\\')) break; if (p >= gbufend) p = growgbuf(p); *p++ = lastc; } if (p >= gbufend) p = growgbuf(p); *p = '\0'; *tail = item = (cmd_t *) fastalloc(sizeof (cmd_t)); item->c_line = strsave(gbuf); tail = &item->c_next; if (lastc == '\n') /* Skip newline character. */ getch(); } /*printf("getcmd: lastc: %c %CX '%.20s'\n", lastc, lastc, readbufp);*/ setincmd(FALSE); *tail = (cmd_t *) NULL; return (list); } /* * Expand a list of target names using make macro definitions. */ LOCAL int exp_ovec(ovec, objcnt) obj_t *ovec[]; int objcnt; { list_t *list; list_t *l; register list_t **tail = &list; int i; if (objcnt == 0) return (0); /* * Catch easy case. */ if (objcnt == 1 && (strchr(ovec[0]->o_name, '$') == NULL)) return (objcnt); for (i = 0; i < objcnt; i++) { tail = exp_list(ovec[i], tail); *tail = (list_t *) NULL; } for (i = 0; list; i++) { if (i >= MAXOBJS) { warn("Too many expanded target items"); return (-i); } ovec[i] = list->l_obj; l = list; list = list->l_next; fastfree((char *)l, sizeof (*l)); } return (i); } /* * Read a list of target names. * Stop if ':', '=', ',' or space is found. */ LOCAL int read_ovec(ovec, typep) obj_t *ovec[]; int *typep; { obj_t *o; int objcnt; char *p; int c; BOOL didwarn = FALSE; /*printf("read_ovec\n");*/ for (objcnt = 0; lastc != EOF; ) { o = getobj(); c = lastc; while (white(lastc)) getch(); /*printf("lastc: '%c'", lastc); if (o) printf(" nameL %s\n", o->o_name);*/ if (o != (obj_t *) NULL) { p = o->o_name; if (p[0] == '+' && p[1] == '\0') { if (c == '=') { *typep = ADDMAC; break; } else if (c == ':' && peekch() == '=') { getch(); *typep = ADD_VARMAC; break; } } if (objcnt >= MAXOBJS) { warn("Too many target items"); return (-objcnt); } ovec[objcnt++] = o; } else { if (lastc == '\n') { getch(); continue; } if (lastc == EOF && objcnt == 0) break; /* * Empty variable name or target. * The POSIX standard says that this is always illegal. * There are makefiles that include $(EMPTY): something * and we like to support them, so allow empty targets * with ':' and '::', but abort with '=', '+=' or ':=' * * We come here only with a definitely empty list. An * empty list still can be created later if $(EMPTY) * gets expanded in exp_ovec() below. */ if (lastc != ':' || peekch() == '=') exerror("Missing object name"); if (!nowarn(":")) { warn("Missing object name"); didwarn = TRUE; } } /* * end of definition: * colon, equal or end of line */ if (lastc == '?' && peekch() == '=') { getch(); *typep = CONDEQUAL; break; } if (lastc == ':' && peekch() == ':') { getch(); if (lastc == ':' && peekch() == ':') { getch(); if (peekch() == '=') { /* :::= */ getch(); *typep = VAR_ASSIGN; break; } else { ungetch(':'); lastc = ':'; } } #ifdef GNU_ASSIGN_BY_DEFAULT if ( #else if (posixmode && #endif lastc == ':' && peekch() == '=') { getch(); *typep = GNU_ASSIGN; break; } *typep = DCOLON; break; } if (lastc == ':' && peekch() == '=') { getch(); *typep = VAR_ASSIGN; /* Use Sun CONDMACRO later */ if (!nowarn(":=")) { warn( "Nonportable ':=' assignment found for macro '%s'", o ? o->o_name : ""); } break; } if (lastc == ':' || lastc == '=' || lastc == '\n') { *typep = lastc; break; } if (lastc == ',') getch(); } /* * XXX Achtung: UNIX make expandiert alles was links und rechts von ':' * steht, sowie Variablennamen (links von '='). */ in_parser = -1; /* In parser but no ::= expansion */ objcnt = exp_ovec(ovec, objcnt); in_parser = 0; if (didwarn) *typep |= NWARN; return (objcnt); } /* * */ EXPORT list_t * cvtvpath(l) list_t *l; { list_t *lsave; list_t *list = (list_t *)0; list_t **tail = &list; char vpath[NAMEMAX]; char *vpe = &vpath[NAMEMAX-1]; char *p1, *p2; if (l != NULL) { for (p1 = l->l_obj->o_name, p2 = vpath; *p1; p2++) { if (p2 >= vpe) break; if ((*p2 = *p1++) == ':') *p2 = ' '; } *p2 = '\0'; tail = mklist(vpath, tail, TRUE); *tail = (list_t *) NULL; lsave = l = list; tail = &list; for (; l; l = l->l_next) { tail = listcat(l->l_obj, tail); tail = listcat(l->l_obj, tail); } *tail = (list_t *) NULL; freelist(lsave); } if (Do_Warn) error("VPATH but no .SEARCHLIST\n"); return (list); } EXPORT BOOL is_inlist(objname, name) char *objname; char *name; { list_t *l; for (l = objlist(objname); l != NULL; l = l->l_next) { if (streql(l->l_obj->o_name, name)) return (TRUE); } return (FALSE); } LOCAL BOOL obj_in_list(obj, l) register obj_t *obj; register list_t *l; { while (l) { if (obj == l->l_obj) return (TRUE); l = l->l_next; } return (FALSE); } EXPORT BOOL nowarn(name) char *name; { return (is_inlist(".NO_WARN", name)); } /* * NOTE: as long as warn() and exerror() use the global vars * lineno, col and Mfileindex, * we cannot use them at any other time than parse time. */ /* * Print a warning with text, line number, column and filename. */ /* VARARGS1 */ #ifdef PROTOTYPES LOCAL void warn(char *msg, ...) #else LOCAL void warn(msg, va_alist) char *msg; va_dcl #endif { va_list args; #ifdef PROTOTYPES va_start(args, msg); #else va_start(args); #endif if (!No_Warn) errmsgno(EX_BAD, "WARNING: %r in line %d col %d of '%s'\n", msg, args, lineno, col, mfname); va_end(args); } /* * Print an error message with text, line number, column and filename, then exit. */ /* VARARGS1 */ #ifdef PROTOTYPES LOCAL void exerror(char *msg, ...) #else LOCAL void exerror(msg, va_alist) char *msg; va_dcl #endif { va_list args; int len; #ifdef PROTOTYPES va_start(args, msg); #else va_start(args); #endif errmsgno(EX_BAD, "%r in line %d col %d of '%s'\n", msg, args, lineno, col, mfname); va_end(args); len = getrdbufsize(); if (len > 80) len = 80; if (Debug > 0) errmsgno(EX_BAD, "Current read buffer is: '%.*s'\n", len, getrdbuf()); comerrno(EX_BAD, "Bad syntax in '%s'.\n", mfname); } #define MyObjTabSize 128 /* # of Hash table entries (power of two) .*/ #define ObjHash(name, h) { \ register unsigned char *str = (unsigned char *)(name); \ register int sum = 0; \ register int i; \ register int c; \ \ for (i = 0; (c = *str++) != '\0'; i++) \ sum ^= (c << (i&7)); \ (h) = sum & (MyObjTabSize -1); \ } LOCAL obj_t *ObjTab[MyObjTabSize]; LOCAL obj_t *SuffTab[MyObjTabSize]; EXPORT obj_t *NullObj; /* * Look up name in 'table'. * First look up in hash table then do binary search. */ LOCAL obj_t * _objlook(table, name, create) obj_t *table[]; char *name; BOOL create; { register obj_t **pp; register obj_t *p; register char *new; register char *old; int hash; ObjHash(name, hash); for (pp = &table[hash]; (p = *pp) != NULL; ) { for (new = name, old = p->o_name; *new++ == *old; ) { if (*old++ == '\0') /* Found 'name' */ return (p); } if (*--new < *old) pp = &p->o_left; else pp = &p->o_right; } if (!create) return ((obj_t *) NULL); /* * Add new entry to ObjTab. */ *pp = p = (obj_t *) fastalloc(sizeof (obj_t)); /* larger than list_t */ /* insert into list */ p->o_left = p->o_right = (obj_t *) NULL; /* old 'p' was NULL */ p->o_cmd = (cmd_t *) NULL; p->o_list = (list_t *) NULL; p->o_date = NOTIME; p->o_level = MAXLEVEL; p->o_type = 0; p->o_flags = 0; p->o_flags = F_NEWNODE; p->o_fileindex = Mfileindex; p->o_name = strsave(name); p->o_namelen = strlen(name); p->o_node = NULL; return (p); } /* * Look up name in ObjTab. */ EXPORT obj_t * objlook(name, create) char *name; BOOL create; { return (_objlook(ObjTab, name, create)); } EXPORT list_t * objlist(name) char *name; { obj_t *o; if ((o = objlook(name, FALSE)) == NULL) return ((list_t *)NULL); return (o->o_list); } /* * Look up name in SuffTab. */ EXPORT obj_t * ssufflook(name, create) char *name; BOOL create; { return (_objlook(SuffTab, name, create)); } /* * Check is SuffTab contains any entry. */ EXPORT BOOL check_ssufftab() { int i; for (i = 0; i < MyObjTabSize; i++) if (SuffTab[i]) return (TRUE); return (FALSE); } /* * Clear all entries in SuffTab. */ LOCAL void clear_ssufftab() { int i; for (i = 0; i < MyObjTabSize; i++) SuffTab[i] = NULL; SSuffrules = FALSE; } /* * Check whether obj->o_name is a Suffix Rule. * Name must either be identical to one of the .SUFFIXES (Single Suffix Rule) * or a concatenation of two .SUFFIXES. */ LOCAL BOOL is_suffix_rule(obj) register obj_t *obj; { list_t *l; list_t *l2; char *suffix; char *rp; int rlen; if (Suffixes == NULL) return (FALSE); for (l = Suffixes; l; l = l->l_next) { suffix = l->l_obj->o_name; rlen = strlen(suffix); if (strncmp(obj->o_name, suffix, rlen) == 0) { rp = &obj->o_name[rlen]; if (*rp == '\0') /* Single Suffix Rule */ return (TRUE); for (l2 = Suffixes; l2; l2 = l2->l_next) { if (streql(rp, l2->l_obj->o_name)) return (TRUE); } } } return (FALSE); } /* * Used by ptree() to print one single object. */ LOCAL void prvar(p) register obj_t *p; { register list_t *q; register cmd_t *c; if (!p) return; error("%s:", p->o_name); for (q = p->o_list; q; q = q->l_next) error(" %s", q->l_obj->o_name); putc('\n', stderr); for (c = p->o_cmd; c; c = c->c_next) error("\t...%s\n", c->c_line); } /* * Currently only used by printtree() to implement the -probj option. */ LOCAL void ptree(p, n) register obj_t *p; int n; { register int i; for (; p; p = p->o_right) { ptree(p->o_left, ++n); for (i = 1; i < n; i++) putc(' ', stderr); prvar(p); } } /* * Used by -probj Flag */ EXPORT void printtree() { int i; print_patrules(stderr); for (i = 0; i < MyObjTabSize; i++) ptree(ObjTab[i], 0); } /* * Currently only used by prtree() to implement the -p option. */ #define SUFF_ONLY 1 /* Print only Suffix Rules */ #define SUFF_SPECIAL 2 /* Print only special targets */ #define SUFF_NONE 0 /* Print only plain target rules */ #define SUFF_ANY -1 /* Print without filtering */ EXPORT void probj(f, o, type, dosuff) FILE *f; obj_t *o; int type; int dosuff; { for (; o; o = o->o_right) { probj(f, o->o_left, type, dosuff); if (type >= 0 && type != o->o_type) continue; if (type < 0 && (o->o_type == ':' || o->o_type == '=')) continue; if (Debug <= 0 && o->o_type == 0) continue; /* Ommit target only strings */ if (dosuff >= 0) { if (dosuff == SUFF_ONLY && !is_suffix_rule(o)) continue; else if (dosuff == SUFF_NONE && (is_suffix_rule(o) || !(o->o_name[0] != '.' || o->o_name[1] == SLASH || (o->o_name[1] == '.' && o->o_name[2] == SLASH)))) continue; else if (dosuff == SUFF_SPECIAL && (is_suffix_rule(o) || (o->o_name[0] != '.' || o->o_name[1] == SLASH || (o->o_name[1] == '.' && o->o_name[2] == SLASH)))) continue; } printobj(f, &o, 1, o->o_type, o->o_list, o->o_cmd); } } /* * Used by -p Flag, called from main(). */ EXPORT void prtree() { int i; printf("# Implicit Pattern Rules:\n"); print_patrules(stdout); printf("# Implicit Suffix Rules:\n"); for (i = 0; i < MyObjTabSize; i++) { probj(stdout, ObjTab[i], ':', SUFF_ONLY); } printf("# Simple Suffix Rules:\n"); for (i = 0; i < MyObjTabSize; i++) { probj(stdout, SuffTab[i], ':', SUFF_ANY); } printf("# Special Targets:\n"); for (i = 0; i < MyObjTabSize; i++) { probj(stdout, ObjTab[i], ':', SUFF_SPECIAL); } printf("# Target Rules:\n"); for (i = 0; i < MyObjTabSize; i++) { probj(stdout, ObjTab[i], ':', SUFF_NONE); } printf("# Macro definitions:\n"); for (i = 0; i < MyObjTabSize; i++) { probj(stdout, ObjTab[i], '=', SUFF_ANY); } printf("# Various other definitions:\n"); for (i = 0; i < MyObjTabSize; i++) { probj(stdout, ObjTab[i], -1, SUFF_ANY); } } /* * Convert the object type into a human readable string. */ LOCAL char * typestr(type) int type; { if (type == 0) return ("(%)"); if (type == EQUAL) return ("="); if (type == COLON) return (":"); if (type == DCOLON) return ("::"); if (type == SEMI) return (";"); if (type == CONDEQUAL) return ("?="); if (type == ADDMAC) return ("+="); if (type == ADD_VARMAC) return ("+:="); if (type == GNU_ASSIGN) return ("::="); if (type == VAR_ASSIGN) return (":::="); if (type == CONDMACRO) return (":="); if (type == SHVAR) return (":sh="); return ("UNKNOWN TYPE"); } /* * This is the central routine to print an object. */ LOCAL void printobj(f, ovec, objcnt, type, deplist, cmdlist) FILE *f; obj_t *ovec[]; int objcnt; int type; list_t *deplist; cmd_t *cmdlist; { register list_t *l; register cmd_t *c; register int i; for (i = 0; i < objcnt; i++) fprintf(f, "%s ", ovec[i]->o_name); fprintf(f, "%2s\t", typestr(type)); for (l = deplist; l; l = l->l_next) fprintf(f, "%s ", l->l_obj->o_name); fprintf(f, "\n"); for (c = cmdlist; c; c = c->c_next) fprintf(f, "\t%s\n", c->c_line); fflush(f); }