140395Sbostic /* 240395Sbostic * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. 340395Sbostic * Copyright (c) 1988, 1989 by Adam de Boor 440395Sbostic * Copyright (c) 1989 by Berkeley Softworks 540395Sbostic * All rights reserved. 640395Sbostic * 740395Sbostic * This code is derived from software contributed to Berkeley by 840395Sbostic * Adam de Boor. 940395Sbostic * 1042752Sbostic * %sccs.include.redist.c% 1140395Sbostic */ 1240395Sbostic 1340395Sbostic #ifndef lint 14*44402Sbostic static char sccsid[] = "@(#)parse.c 5.15 (Berkeley) 06/28/90"; 1540395Sbostic #endif /* not lint */ 1640395Sbostic 1740393Sbostic /*- 1840393Sbostic * parse.c -- 1940393Sbostic * Functions to parse a makefile. 2040393Sbostic * 2140393Sbostic * One function, Parse_Init, must be called before any functions 2240393Sbostic * in this module are used. After that, the function Parse_File is the 2340393Sbostic * main entry point and controls most of the other functions in this 2440393Sbostic * module. 2540393Sbostic * 2640393Sbostic * Most important structures are kept in Lsts. Directories for 2740393Sbostic * the #include "..." function are kept in the 'parseIncPath' Lst, while 2840393Sbostic * those for the #include <...> are kept in the 'sysIncPath' Lst. The 2940393Sbostic * targets currently being defined are kept in the 'targets' Lst. 3040393Sbostic * 3140393Sbostic * The variables 'fname' and 'lineno' are used to track the name 3240393Sbostic * of the current file and the line number in that file so that error 3340393Sbostic * messages can be more meaningful. 3440393Sbostic * 3540393Sbostic * Interface: 3640393Sbostic * Parse_Init Initialization function which must be 3740393Sbostic * called before anything else in this module 3840393Sbostic * is used. 3940393Sbostic * 4040393Sbostic * Parse_File Function used to parse a makefile. It must 4140393Sbostic * be given the name of the file, which should 4240393Sbostic * already have been opened, and a function 4340393Sbostic * to call to read a character from the file. 4440393Sbostic * 4540393Sbostic * Parse_IsVar Returns TRUE if the given line is a 4640393Sbostic * variable assignment. Used by MainParseArgs 4740393Sbostic * to determine if an argument is a target 4840393Sbostic * or a variable assignment. Used internally 4940393Sbostic * for pretty much the same thing... 5040393Sbostic * 5140393Sbostic * Parse_Error Function called when an error occurs in 5240393Sbostic * parsing. Used by the variable and 5340393Sbostic * conditional modules. 5440393Sbostic * Parse_MainName Returns a Lst of the main target to create. 5540393Sbostic */ 5640393Sbostic 5740444Sbostic #include <varargs.h> 5840444Sbostic #include <stdio.h> 5940444Sbostic #include <ctype.h> 6040444Sbostic #include "make.h" 6140444Sbostic #include "buf.h" 6240515Sbostic #include "pathnames.h" 6340393Sbostic 6440393Sbostic /* 6540393Sbostic * These values are returned by ParseEOF to tell Parse_File whether to 6640393Sbostic * CONTINUE parsing, i.e. it had only reached the end of an include file, 6740393Sbostic * or if it's DONE. 6840393Sbostic */ 6940393Sbostic #define CONTINUE 1 7040393Sbostic #define DONE 0 7140393Sbostic static int ParseEOF(); 7240393Sbostic 7340393Sbostic static Lst targets; /* targets we're working on */ 7440393Sbostic static Boolean inLine; /* true if currently in a dependency 7540393Sbostic * line or its commands */ 7640393Sbostic 7740393Sbostic static char *fname; /* name of current file (for errors) */ 7840393Sbostic static int lineno; /* line number in current file */ 7940393Sbostic static FILE *curFILE; /* current makefile */ 8040393Sbostic 8140393Sbostic static int fatals = 0; 8240393Sbostic 8340393Sbostic static GNode *mainNode; /* The main target to create. This is the 8440393Sbostic * first target on the first dependency 8540393Sbostic * line in the first makefile */ 8640393Sbostic /* 8740393Sbostic * Definitions for handling #include specifications 8840393Sbostic */ 8940393Sbostic typedef struct IFile { 9040393Sbostic char *fname; /* name of previous file */ 9140393Sbostic int lineno; /* saved line number */ 9240393Sbostic FILE * F; /* the open stream */ 9340393Sbostic } IFile; 9440393Sbostic 9540393Sbostic static Lst includes; /* stack of IFiles generated by 9640393Sbostic * #includes */ 9740393Sbostic Lst parseIncPath; /* list of directories for "..." includes */ 9840393Sbostic Lst sysIncPath; /* list of directories for <...> includes */ 9940393Sbostic 10040393Sbostic /*- 10140393Sbostic * specType contains the SPECial TYPE of the current target. It is 10240393Sbostic * Not if the target is unspecial. If it *is* special, however, the children 10340393Sbostic * are linked as children of the parent but not vice versa. This variable is 10440393Sbostic * set in ParseDoDependency 10540393Sbostic */ 10640393Sbostic typedef enum { 10740393Sbostic Begin, /* .BEGIN */ 10840393Sbostic Default, /* .DEFAULT */ 10940393Sbostic End, /* .END */ 11040393Sbostic Ignore, /* .IGNORE */ 11140393Sbostic Includes, /* .INCLUDES */ 11240393Sbostic Interrupt, /* .INTERRUPT */ 11340393Sbostic Libs, /* .LIBS */ 11440393Sbostic MFlags, /* .MFLAGS or .MAKEFLAGS */ 11540393Sbostic Main, /* .MAIN and we don't have anything user-specified to 11640393Sbostic * make */ 11740393Sbostic Not, /* Not special */ 11840393Sbostic NotParallel, /* .NOTPARALELL */ 11940393Sbostic Null, /* .NULL */ 12040393Sbostic Order, /* .ORDER */ 12140393Sbostic Path, /* .PATH */ 12240393Sbostic Precious, /* .PRECIOUS */ 12340393Sbostic Shell, /* .SHELL */ 12440393Sbostic Silent, /* .SILENT */ 12540393Sbostic SingleShell, /* .SINGLESHELL */ 12640393Sbostic Suffixes, /* .SUFFIXES */ 12740393Sbostic Attribute, /* Generic attribute */ 12840393Sbostic } ParseSpecial; 12940393Sbostic 13040393Sbostic ParseSpecial specType; 13140393Sbostic 13240393Sbostic /* 13340393Sbostic * Predecessor node for handling .ORDER. Initialized to NILGNODE when .ORDER 13440393Sbostic * seen, then set to each successive source on the line. 13540393Sbostic */ 13640393Sbostic static GNode *predecessor; 13740393Sbostic 13840393Sbostic /* 13940393Sbostic * The parseKeywords table is searched using binary search when deciding 14040393Sbostic * if a target or source is special. The 'spec' field is the ParseSpecial 14140393Sbostic * type of the keyword ("Not" if the keyword isn't special as a target) while 14240393Sbostic * the 'op' field is the operator to apply to the list of targets if the 14340393Sbostic * keyword is used as a source ("0" if the keyword isn't special as a source) 14440393Sbostic */ 14540393Sbostic static struct { 14640393Sbostic char *name; /* Name of keyword */ 14740393Sbostic ParseSpecial spec; /* Type when used as a target */ 14840393Sbostic int op; /* Operator when used as a source */ 14940393Sbostic } parseKeywords[] = { 15040393Sbostic { ".BEGIN", Begin, 0 }, 15140393Sbostic { ".DEFAULT", Default, 0 }, 15240546Sbostic { ".OPTIONAL", Attribute, OP_OPTIONAL }, 15340393Sbostic { ".END", End, 0 }, 15440393Sbostic { ".EXEC", Attribute, OP_EXEC }, 15540393Sbostic { ".IGNORE", Ignore, OP_IGNORE }, 15640393Sbostic { ".INCLUDES", Includes, 0 }, 15740393Sbostic { ".INTERRUPT", Interrupt, 0 }, 15840393Sbostic { ".INVISIBLE", Attribute, OP_INVISIBLE }, 15940393Sbostic { ".JOIN", Attribute, OP_JOIN }, 16040393Sbostic { ".LIBS", Libs, 0 }, 16140393Sbostic { ".MAIN", Main, 0 }, 16240393Sbostic { ".MAKE", Attribute, OP_MAKE }, 16340393Sbostic { ".MAKEFLAGS", MFlags, 0 }, 16440393Sbostic { ".MFLAGS", MFlags, 0 }, 16540393Sbostic { ".NOTMAIN", Attribute, OP_NOTMAIN }, 16640393Sbostic { ".NOTPARALLEL", NotParallel, 0 }, 16740393Sbostic { ".NULL", Null, 0 }, 16840393Sbostic { ".ORDER", Order, 0 }, 16940393Sbostic { ".PATH", Path, 0 }, 17040393Sbostic { ".PRECIOUS", Precious, OP_PRECIOUS }, 17140393Sbostic { ".RECURSIVE", Attribute, OP_MAKE }, 17240393Sbostic { ".SHELL", Shell, 0 }, 17340393Sbostic { ".SILENT", Silent, OP_SILENT }, 17440393Sbostic { ".SINGLESHELL", SingleShell, 0 }, 17540393Sbostic { ".SUFFIXES", Suffixes, 0 }, 17640393Sbostic { ".USE", Attribute, OP_USE }, 17740393Sbostic }; 17840424Sbostic 17940393Sbostic /*- 18040393Sbostic *---------------------------------------------------------------------- 18140393Sbostic * ParseFindKeyword -- 18240393Sbostic * Look in the table of keywords for one matching the given string. 18340393Sbostic * 18440393Sbostic * Results: 18540393Sbostic * The index of the keyword, or -1 if it isn't there. 18640393Sbostic * 18740393Sbostic * Side Effects: 18840393Sbostic * None 18940393Sbostic *---------------------------------------------------------------------- 19040393Sbostic */ 19140393Sbostic static int 19240393Sbostic ParseFindKeyword (str) 19340393Sbostic char *str; /* String to find */ 19440393Sbostic { 19540393Sbostic register int start, 19640393Sbostic end, 19740393Sbostic cur; 19840393Sbostic register int diff; 19940393Sbostic 20040393Sbostic start = 0; 20140393Sbostic end = (sizeof(parseKeywords)/sizeof(parseKeywords[0])) - 1; 20240393Sbostic 20340393Sbostic do { 20440393Sbostic cur = start + ((end - start) / 2); 20540393Sbostic diff = strcmp (str, parseKeywords[cur].name); 20640393Sbostic 20740393Sbostic if (diff == 0) { 20840393Sbostic return (cur); 20940393Sbostic } else if (diff < 0) { 21040393Sbostic end = cur - 1; 21140393Sbostic } else { 21240393Sbostic start = cur + 1; 21340393Sbostic } 21440393Sbostic } while (start <= end); 21540393Sbostic return (-1); 21640393Sbostic } 21740424Sbostic 21840393Sbostic /*- 21940393Sbostic * Parse_Error -- 22040393Sbostic * Error message abort function for parsing. Prints out the context 22140393Sbostic * of the error (line number and file) as well as the message with 22240393Sbostic * two optional arguments. 22340393Sbostic * 22440393Sbostic * Results: 22540393Sbostic * None 22640393Sbostic * 22740393Sbostic * Side Effects: 22840393Sbostic * "fatals" is incremented if the level is PARSE_FATAL. 22940393Sbostic */ 23040444Sbostic /* VARARGS */ 23140393Sbostic void 23240444Sbostic Parse_Error(type, va_alist) 23340444Sbostic int type; /* Error type (PARSE_WARNING, PARSE_FATAL) */ 23440444Sbostic va_dcl 23540393Sbostic { 23640444Sbostic va_list ap; 23740444Sbostic char *fmt; 23840393Sbostic 23940444Sbostic (void)fprintf(stderr, "\"%s\", line %d: ", fname, lineno); 24040444Sbostic if (type == PARSE_WARNING) 24140540Sbostic (void)fprintf(stderr, "warning: "); 24240476Sbostic va_start(ap); 24340444Sbostic fmt = va_arg(ap, char *); 24440444Sbostic (void)vfprintf(stderr, fmt, ap); 24540444Sbostic va_end(ap); 24640444Sbostic (void)fprintf(stderr, "\n"); 24740444Sbostic (void)fflush(stderr); 24840444Sbostic if (type == PARSE_FATAL) 24940444Sbostic fatals += 1; 25040393Sbostic } 25140424Sbostic 25240393Sbostic /*- 25340393Sbostic *--------------------------------------------------------------------- 25440393Sbostic * ParseLinkSrc -- 25540393Sbostic * Link the parent node to its new child. Used in a Lst_ForEach by 25640393Sbostic * ParseDoDependency. If the specType isn't 'Not', the parent 25740393Sbostic * isn't linked as a parent of the child. 25840393Sbostic * 25940393Sbostic * Results: 26040393Sbostic * Always = 0 26140393Sbostic * 26240393Sbostic * Side Effects: 26340393Sbostic * New elements are added to the parents list of cgn and the 26440393Sbostic * children list of cgn. the unmade field of pgn is updated 26540393Sbostic * to reflect the additional child. 26640393Sbostic *--------------------------------------------------------------------- 26740393Sbostic */ 26840393Sbostic static int 26940393Sbostic ParseLinkSrc (pgn, cgn) 27040393Sbostic GNode *pgn; /* The parent node */ 27140393Sbostic GNode *cgn; /* The child node */ 27240393Sbostic { 27340393Sbostic if (Lst_Member (pgn->children, (ClientData)cgn) == NILLNODE) { 27440393Sbostic (void)Lst_AtEnd (pgn->children, (ClientData)cgn); 27540393Sbostic if (specType == Not) { 27640393Sbostic (void)Lst_AtEnd (cgn->parents, (ClientData)pgn); 27740393Sbostic } 27840393Sbostic pgn->unmade += 1; 27940393Sbostic } 28040393Sbostic return (0); 28140393Sbostic } 28240424Sbostic 28340393Sbostic /*- 28440393Sbostic *--------------------------------------------------------------------- 28540393Sbostic * ParseDoOp -- 28640393Sbostic * Apply the parsed operator to the given target node. Used in a 28740393Sbostic * Lst_ForEach call by ParseDoDependency once all targets have 28840393Sbostic * been found and their operator parsed. If the previous and new 28940393Sbostic * operators are incompatible, a major error is taken. 29040393Sbostic * 29140393Sbostic * Results: 29240393Sbostic * Always 0 29340393Sbostic * 29440393Sbostic * Side Effects: 29540393Sbostic * The type field of the node is altered to reflect any new bits in 29640393Sbostic * the op. 29740393Sbostic *--------------------------------------------------------------------- 29840393Sbostic */ 29940393Sbostic static int 30040393Sbostic ParseDoOp (gn, op) 30140393Sbostic GNode *gn; /* The node to which the operator is to be 30240393Sbostic * applied */ 30340393Sbostic int op; /* The operator to apply */ 30440393Sbostic { 30540393Sbostic /* 30640393Sbostic * If the dependency mask of the operator and the node don't match and 30740393Sbostic * the node has actually had an operator applied to it before, and 30840393Sbostic * the operator actually has some dependency information in it, complain. 30940393Sbostic */ 31040393Sbostic if (((op & OP_OPMASK) != (gn->type & OP_OPMASK)) && 31140393Sbostic !OP_NOP(gn->type) && !OP_NOP(op)) 31240393Sbostic { 31340393Sbostic Parse_Error (PARSE_FATAL, "Inconsistent operator for %s", gn->name); 31440393Sbostic return (1); 31540393Sbostic } 31640393Sbostic 31740393Sbostic if ((op == OP_DOUBLEDEP) && ((gn->type & OP_OPMASK) == OP_DOUBLEDEP)) { 31840393Sbostic /* 31940393Sbostic * If the node was the object of a :: operator, we need to create a 32040393Sbostic * new instance of it for the children and commands on this dependency 32140393Sbostic * line. The new instance is placed on the 'cohorts' list of the 32240393Sbostic * initial one (note the initial one is not on its own cohorts list) 32340393Sbostic * and the new instance is linked to all parents of the initial 32440393Sbostic * instance. 32540393Sbostic */ 32640393Sbostic register GNode *cohort; 32740393Sbostic LstNode ln; 32840393Sbostic 32940393Sbostic cohort = Targ_NewGN(gn->name); 33040393Sbostic /* 33140393Sbostic * Duplicate links to parents so graph traversal is simple. Perhaps 33240393Sbostic * some type bits should be duplicated? 33340393Sbostic * 33440393Sbostic * Make the cohort invisible as well to avoid duplicating it into 33540393Sbostic * other variables. True, parents of this target won't tend to do 33640393Sbostic * anything with their local variables, but better safe than 33740393Sbostic * sorry. 33840393Sbostic */ 33940393Sbostic Lst_ForEach(gn->parents, ParseLinkSrc, (ClientData)cohort); 34040393Sbostic cohort->type = OP_DOUBLEDEP|OP_INVISIBLE; 34140393Sbostic (void)Lst_AtEnd(gn->cohorts, (ClientData)cohort); 34240393Sbostic 34340393Sbostic /* 34440393Sbostic * Replace the node in the targets list with the new copy 34540393Sbostic */ 34640393Sbostic ln = Lst_Member(targets, (ClientData)gn); 34740393Sbostic Lst_Replace(ln, (ClientData)cohort); 34840393Sbostic gn = cohort; 34940393Sbostic } 35040393Sbostic /* 35140393Sbostic * We don't want to nuke any previous flags (whatever they were) so we 35240393Sbostic * just OR the new operator into the old 35340393Sbostic */ 35440393Sbostic gn->type |= op; 35540393Sbostic 35640393Sbostic return (0); 35740393Sbostic } 35840424Sbostic 35940393Sbostic /*- 36040393Sbostic *--------------------------------------------------------------------- 36140393Sbostic * ParseDoSrc -- 36240393Sbostic * Given the name of a source, figure out if it is an attribute 36340393Sbostic * and apply it to the targets if it is. Else decide if there is 36440393Sbostic * some attribute which should be applied *to* the source because 36540393Sbostic * of some special target and apply it if so. Otherwise, make the 36640393Sbostic * source be a child of the targets in the list 'targets' 36740393Sbostic * 36840393Sbostic * Results: 36940393Sbostic * None 37040393Sbostic * 37140393Sbostic * Side Effects: 37240393Sbostic * Operator bits may be added to the list of targets or to the source. 37340393Sbostic * The targets may have a new source added to their lists of children. 37440393Sbostic *--------------------------------------------------------------------- 37540393Sbostic */ 37640393Sbostic static void 37740393Sbostic ParseDoSrc (tOp, src) 37840393Sbostic int tOp; /* operator (if any) from special targets */ 37940393Sbostic char *src; /* name of the source to handle */ 38040393Sbostic { 38140393Sbostic int op; /* operator (if any) from special source */ 38240393Sbostic GNode *gn; 38340393Sbostic 38440393Sbostic op = 0; 38540393Sbostic if (*src == '.' && isupper (src[1])) { 38640393Sbostic int keywd = ParseFindKeyword(src); 38740393Sbostic if (keywd != -1) { 38840393Sbostic op = parseKeywords[keywd].op; 38940393Sbostic } 39040393Sbostic } 39140393Sbostic if (op != 0) { 39240393Sbostic Lst_ForEach (targets, ParseDoOp, (ClientData)op); 39340393Sbostic } else if (specType == Main) { 39440393Sbostic /* 39540393Sbostic * If we have noted the existence of a .MAIN, it means we need 39640393Sbostic * to add the sources of said target to the list of things 39740393Sbostic * to create. The string 'src' is likely to be free, so we 39840393Sbostic * must make a new copy of it. Note that this will only be 39940393Sbostic * invoked if the user didn't specify a target on the command 40040393Sbostic * line. This is to allow #ifmake's to succeed, or something... 40140393Sbostic */ 40240424Sbostic (void) Lst_AtEnd (create, (ClientData)strdup(src)); 40340393Sbostic /* 40440393Sbostic * Add the name to the .TARGETS variable as well, so the user cna 40540393Sbostic * employ that, if desired. 40640393Sbostic */ 40740393Sbostic Var_Append(".TARGETS", src, VAR_GLOBAL); 40840393Sbostic } else if (specType == Order) { 40940393Sbostic /* 41040393Sbostic * Create proper predecessor/successor links between the previous 41140393Sbostic * source and the current one. 41240393Sbostic */ 41340393Sbostic gn = Targ_FindNode(src, TARG_CREATE); 41440393Sbostic if (predecessor != NILGNODE) { 41540393Sbostic (void)Lst_AtEnd(predecessor->successors, (ClientData)gn); 41640393Sbostic (void)Lst_AtEnd(gn->preds, (ClientData)predecessor); 41740393Sbostic } 41840393Sbostic /* 41940393Sbostic * The current source now becomes the predecessor for the next one. 42040393Sbostic */ 42140393Sbostic predecessor = gn; 42240393Sbostic } else { 42340393Sbostic /* 42440393Sbostic * If the source is not an attribute, we need to find/create 42540393Sbostic * a node for it. After that we can apply any operator to it 42640393Sbostic * from a special target or link it to its parents, as 42740393Sbostic * appropriate. 42840393Sbostic * 42940393Sbostic * In the case of a source that was the object of a :: operator, 43040393Sbostic * the attribute is applied to all of its instances (as kept in 43140393Sbostic * the 'cohorts' list of the node) or all the cohorts are linked 43240393Sbostic * to all the targets. 43340393Sbostic */ 43440393Sbostic gn = Targ_FindNode (src, TARG_CREATE); 43540393Sbostic if (tOp) { 43640393Sbostic gn->type |= tOp; 43740393Sbostic } else { 43840393Sbostic Lst_ForEach (targets, ParseLinkSrc, (ClientData)gn); 43940393Sbostic } 44040393Sbostic if ((gn->type & OP_OPMASK) == OP_DOUBLEDEP) { 44140393Sbostic register GNode *cohort; 44240393Sbostic register LstNode ln; 44340393Sbostic 44440393Sbostic for (ln=Lst_First(gn->cohorts); ln != NILLNODE; ln = Lst_Succ(ln)){ 44540393Sbostic cohort = (GNode *)Lst_Datum(ln); 44640393Sbostic if (tOp) { 44740393Sbostic cohort->type |= tOp; 44840393Sbostic } else { 44940393Sbostic Lst_ForEach(targets, ParseLinkSrc, (ClientData)cohort); 45040393Sbostic } 45140393Sbostic } 45240393Sbostic } 45340393Sbostic } 45440393Sbostic } 45540424Sbostic 45640393Sbostic /*- 45740393Sbostic *----------------------------------------------------------------------- 45840393Sbostic * ParseFindMain -- 45940393Sbostic * Find a real target in the list and set it to be the main one. 46040393Sbostic * Called by ParseDoDependency when a main target hasn't been found 46140393Sbostic * yet. 46240393Sbostic * 46340393Sbostic * Results: 46440393Sbostic * 0 if main not found yet, 1 if it is. 46540393Sbostic * 46640393Sbostic * Side Effects: 46740393Sbostic * mainNode is changed and Targ_SetMain is called. 46840393Sbostic * 46940393Sbostic *----------------------------------------------------------------------- 47040393Sbostic */ 47140393Sbostic static int 47240393Sbostic ParseFindMain(gn) 47340393Sbostic GNode *gn; /* Node to examine */ 47440393Sbostic { 47540393Sbostic if ((gn->type & (OP_NOTMAIN|OP_USE|OP_EXEC|OP_TRANSFORM)) == 0) { 47640393Sbostic mainNode = gn; 47740393Sbostic Targ_SetMain(gn); 47840393Sbostic return (1); 47940393Sbostic } else { 48040393Sbostic return (0); 48140393Sbostic } 48240393Sbostic } 48340424Sbostic 48440393Sbostic /*- 48540393Sbostic *----------------------------------------------------------------------- 48640393Sbostic * ParseAddDir -- 48740393Sbostic * Front-end for Dir_AddDir to make sure Lst_ForEach keeps going 48840393Sbostic * 48940393Sbostic * Results: 49040393Sbostic * === 0 49140393Sbostic * 49240393Sbostic * Side Effects: 49340393Sbostic * See Dir_AddDir. 49440393Sbostic * 49540393Sbostic *----------------------------------------------------------------------- 49640393Sbostic */ 49740393Sbostic static int 49840393Sbostic ParseAddDir(path, name) 49940393Sbostic Lst path; 50040393Sbostic char *name; 50140393Sbostic { 50240393Sbostic Dir_AddDir(path, name); 50340393Sbostic return(0); 50440393Sbostic } 50540424Sbostic 50640393Sbostic /*- 50740393Sbostic *----------------------------------------------------------------------- 50840393Sbostic * ParseClearPath -- 50940393Sbostic * Front-end for Dir_ClearPath to make sure Lst_ForEach keeps going 51040393Sbostic * 51140393Sbostic * Results: 51240393Sbostic * === 0 51340393Sbostic * 51440393Sbostic * Side Effects: 51540393Sbostic * See Dir_ClearPath 51640393Sbostic * 51740393Sbostic *----------------------------------------------------------------------- 51840393Sbostic */ 51940393Sbostic static int 52040393Sbostic ParseClearPath(path) 52140393Sbostic Lst path; 52240393Sbostic { 52340393Sbostic Dir_ClearPath(path); 52440393Sbostic return(0); 52540393Sbostic } 52640424Sbostic 52740393Sbostic /*- 52840393Sbostic *--------------------------------------------------------------------- 52940393Sbostic * ParseDoDependency -- 53040393Sbostic * Parse the dependency line in line. 53140393Sbostic * 53240393Sbostic * Results: 53340393Sbostic * None 53440393Sbostic * 53540393Sbostic * Side Effects: 53640393Sbostic * The nodes of the sources are linked as children to the nodes of the 53740393Sbostic * targets. Some nodes may be created. 53840393Sbostic * 53940393Sbostic * We parse a dependency line by first extracting words from the line and 54040393Sbostic * finding nodes in the list of all targets with that name. This is done 54140393Sbostic * until a character is encountered which is an operator character. Currently 54240393Sbostic * these are only ! and :. At this point the operator is parsed and the 54340393Sbostic * pointer into the line advanced until the first source is encountered. 54440393Sbostic * The parsed operator is applied to each node in the 'targets' list, 54540393Sbostic * which is where the nodes found for the targets are kept, by means of 54640393Sbostic * the ParseDoOp function. 54740393Sbostic * The sources are read in much the same way as the targets were except 54840393Sbostic * that now they are expanded using the wildcarding scheme of the C-Shell 54940393Sbostic * and all instances of the resulting words in the list of all targets 55040393Sbostic * are found. Each of the resulting nodes is then linked to each of the 55140393Sbostic * targets as one of its children. 55240393Sbostic * Certain targets are handled specially. These are the ones detailed 55340393Sbostic * by the specType variable. 55440393Sbostic * The storing of transformation rules is also taken care of here. 55540393Sbostic * A target is recognized as a transformation rule by calling 55640393Sbostic * Suff_IsTransform. If it is a transformation rule, its node is gotten 55740393Sbostic * from the suffix module via Suff_AddTransform rather than the standard 55840393Sbostic * Targ_FindNode in the target module. 55940393Sbostic *--------------------------------------------------------------------- 56040393Sbostic */ 56140393Sbostic static void 56240393Sbostic ParseDoDependency (line) 56340393Sbostic char *line; /* the line to parse */ 56440393Sbostic { 56540393Sbostic register char *cp; /* our current position */ 56640393Sbostic register GNode *gn; /* a general purpose temporary node */ 56740393Sbostic register int op; /* the operator on the line */ 56840393Sbostic char savec; /* a place to save a character */ 56940393Sbostic Lst paths; /* List of search paths to alter when parsing 57040393Sbostic * a list of .PATH targets */ 57140393Sbostic int tOp; /* operator from special target */ 57240393Sbostic Lst sources; /* list of source names after expansion */ 57340393Sbostic Lst curTargs; /* list of target names to be found and added 57440393Sbostic * to the targets list */ 57540393Sbostic 57640393Sbostic tOp = 0; 57740393Sbostic 57840393Sbostic specType = Not; 57940393Sbostic paths = (Lst)NULL; 58040393Sbostic 58140393Sbostic curTargs = Lst_Init(FALSE); 58240393Sbostic 58340393Sbostic do { 58440393Sbostic for (cp = line; 58540393Sbostic *cp && !isspace (*cp) && 58640393Sbostic (*cp != '!') && (*cp != ':') && (*cp != '('); 58740393Sbostic cp++) 58840393Sbostic { 58940393Sbostic if (*cp == '$') { 59040393Sbostic /* 59140393Sbostic * Must be a dynamic source (would have been expanded 59240393Sbostic * otherwise), so call the Var module to parse the puppy 59340393Sbostic * so we can safely advance beyond it...There should be 59440393Sbostic * no errors in this, as they would have been discovered 59540393Sbostic * in the initial Var_Subst and we wouldn't be here. 59640393Sbostic */ 59740393Sbostic int length; 59840393Sbostic Boolean freeIt; 59940393Sbostic char *result; 60040393Sbostic 60140393Sbostic result=Var_Parse(cp, VAR_CMD, TRUE, &length, &freeIt); 60240393Sbostic 60340393Sbostic if (freeIt) { 60440393Sbostic free(result); 60540393Sbostic } 60640393Sbostic cp += length-1; 60740393Sbostic } 60840393Sbostic continue; 60940393Sbostic } 61040393Sbostic if (*cp == '(') { 61140393Sbostic /* 61240393Sbostic * Archives must be handled specially to make sure the OP_ARCHV 61340393Sbostic * flag is set in their 'type' field, for one thing, and because 61440393Sbostic * things like "archive(file1.o file2.o file3.o)" are permissible. 61540393Sbostic * Arch_ParseArchive will set 'line' to be the first non-blank 61640393Sbostic * after the archive-spec. It creates/finds nodes for the members 61740393Sbostic * and places them on the given list, returning SUCCESS if all 61840393Sbostic * went well and FAILURE if there was an error in the 61940393Sbostic * specification. On error, line should remain untouched. 62040393Sbostic */ 62140393Sbostic if (Arch_ParseArchive (&line, targets, VAR_CMD) != SUCCESS) { 62240393Sbostic Parse_Error (PARSE_FATAL, 62340393Sbostic "Error in archive specification: \"%s\"", line); 62440393Sbostic return; 62540393Sbostic } else { 62640393Sbostic continue; 62740393Sbostic } 62840393Sbostic } 62940393Sbostic savec = *cp; 63040393Sbostic 63140393Sbostic if (!*cp) { 63240393Sbostic /* 63340393Sbostic * Ending a dependency line without an operator is a Bozo 63440393Sbostic * no-no 63540393Sbostic */ 63640393Sbostic Parse_Error (PARSE_FATAL, "Need an operator"); 63740393Sbostic return; 63840393Sbostic } 63940393Sbostic *cp = '\0'; 64040393Sbostic /* 64140393Sbostic * Have a word in line. See if it's a special target and set 64240393Sbostic * specType to match it. 64340393Sbostic */ 64440393Sbostic if (*line == '.' && isupper (line[1])) { 64540393Sbostic /* 64640393Sbostic * See if the target is a special target that must have it 64740393Sbostic * or its sources handled specially. 64840393Sbostic */ 64940393Sbostic int keywd = ParseFindKeyword(line); 65040393Sbostic if (keywd != -1) { 65140393Sbostic if (specType == Path && parseKeywords[keywd].spec != Path) { 65240393Sbostic Parse_Error(PARSE_FATAL, "Mismatched special targets"); 65340393Sbostic return; 65440393Sbostic } 65540393Sbostic 65640393Sbostic specType = parseKeywords[keywd].spec; 65740393Sbostic tOp = parseKeywords[keywd].op; 65840393Sbostic 65940393Sbostic /* 66040393Sbostic * Certain special targets have special semantics: 66140393Sbostic * .PATH Have to set the dirSearchPath 66240393Sbostic * variable too 66340393Sbostic * .MAIN Its sources are only used if 66440393Sbostic * nothing has been specified to 66540393Sbostic * create. 66640393Sbostic * .DEFAULT Need to create a node to hang 66740393Sbostic * commands on, but we don't want 66840393Sbostic * it in the graph, nor do we want 66940393Sbostic * it to be the Main Target, so we 67040393Sbostic * create it, set OP_NOTMAIN and 67140393Sbostic * add it to the list, setting 67240393Sbostic * DEFAULT to the new node for 67340393Sbostic * later use. We claim the node is 67440393Sbostic * A transformation rule to make 67540393Sbostic * life easier later, when we'll 67640393Sbostic * use Make_HandleUse to actually 67740393Sbostic * apply the .DEFAULT commands. 67840393Sbostic * .BEGIN 67940393Sbostic * .END 68040393Sbostic * .INTERRUPT Are not to be considered the 68140393Sbostic * main target. 68240393Sbostic * .NOTPARALLEL Make only one target at a time. 68340393Sbostic * .SINGLESHELL Create a shell for each command. 68440393Sbostic * .ORDER Must set initial predecessor to NIL 68540393Sbostic */ 68640393Sbostic switch (specType) { 68740393Sbostic case Path: 68840393Sbostic if (paths == NULL) { 68940393Sbostic paths = Lst_Init(FALSE); 69040393Sbostic } 69140393Sbostic (void)Lst_AtEnd(paths, (ClientData)dirSearchPath); 69240393Sbostic break; 69340393Sbostic case Main: 69440393Sbostic if (!Lst_IsEmpty(create)) { 69540393Sbostic specType = Not; 69640393Sbostic } 69740393Sbostic break; 69840393Sbostic case Begin: 69940393Sbostic case End: 70040393Sbostic case Interrupt: 70140393Sbostic gn = Targ_FindNode(line, TARG_CREATE); 70240393Sbostic gn->type |= OP_NOTMAIN; 70340393Sbostic (void)Lst_AtEnd(targets, (ClientData)gn); 70440393Sbostic break; 70540393Sbostic case Default: 70640393Sbostic gn = Targ_NewGN(".DEFAULT"); 70740393Sbostic gn->type |= (OP_NOTMAIN|OP_TRANSFORM); 70840393Sbostic (void)Lst_AtEnd(targets, (ClientData)gn); 70940393Sbostic DEFAULT = gn; 71040393Sbostic break; 71140393Sbostic case NotParallel: 71240393Sbostic { 71340393Sbostic extern int maxJobs; 71440393Sbostic 71540393Sbostic maxJobs = 1; 71640393Sbostic break; 71740393Sbostic } 71840393Sbostic case SingleShell: 71940444Sbostic /* backwards = 1; */ 72040393Sbostic break; 72140393Sbostic case Order: 72240393Sbostic predecessor = NILGNODE; 72340393Sbostic break; 72440393Sbostic } 72540393Sbostic } else if (strncmp (line, ".PATH", 5) == 0) { 72640393Sbostic /* 72740393Sbostic * .PATH<suffix> has to be handled specially. 72840393Sbostic * Call on the suffix module to give us a path to 72940393Sbostic * modify. 73040393Sbostic */ 73140393Sbostic Lst path; 73240393Sbostic 73340393Sbostic specType = Path; 73440393Sbostic path = Suff_GetPath (&line[5]); 73540393Sbostic if (path == NILLST) { 73640393Sbostic Parse_Error (PARSE_FATAL, 73740393Sbostic "Suffix '%s' not defined (yet)", 73840393Sbostic &line[5]); 73940393Sbostic return; 74040393Sbostic } else { 74140393Sbostic if (paths == (Lst)NULL) { 74240393Sbostic paths = Lst_Init(FALSE); 74340393Sbostic } 74440393Sbostic (void)Lst_AtEnd(paths, (ClientData)path); 74540393Sbostic } 74640393Sbostic } 74740393Sbostic } 74840393Sbostic 74940393Sbostic /* 75040393Sbostic * Have word in line. Get or create its node and stick it at 75140393Sbostic * the end of the targets list 75240393Sbostic */ 75340393Sbostic if ((specType == Not) && (*line != '\0')) { 75440393Sbostic if (Dir_HasWildcards(line)) { 75540393Sbostic /* 75640393Sbostic * Targets are to be sought only in the current directory, 75740393Sbostic * so create an empty path for the thing. Note we need to 75840393Sbostic * use Dir_Destroy in the destruction of the path as the 75940393Sbostic * Dir module could have added a directory to the path... 76040393Sbostic */ 76140393Sbostic Lst emptyPath = Lst_Init(FALSE); 76240393Sbostic 76340393Sbostic Dir_Expand(line, emptyPath, curTargs); 76440393Sbostic 76540393Sbostic Lst_Destroy(emptyPath, Dir_Destroy); 76640393Sbostic } else { 76740393Sbostic /* 76840393Sbostic * No wildcards, but we want to avoid code duplication, 76940393Sbostic * so create a list with the word on it. 77040393Sbostic */ 77140393Sbostic (void)Lst_AtEnd(curTargs, (ClientData)line); 77240393Sbostic } 77340393Sbostic 77440393Sbostic while(!Lst_IsEmpty(curTargs)) { 77540393Sbostic char *targName = (char *)Lst_DeQueue(curTargs); 77640393Sbostic 77740393Sbostic if (!Suff_IsTransform (targName)) { 77840393Sbostic gn = Targ_FindNode (targName, TARG_CREATE); 77940393Sbostic } else { 78040393Sbostic gn = Suff_AddTransform (targName); 78140393Sbostic } 78240393Sbostic 78340393Sbostic (void)Lst_AtEnd (targets, (ClientData)gn); 78440393Sbostic } 78540393Sbostic } else if (specType == Path && *line != '.' && *line != '\0') { 78640393Sbostic Parse_Error(PARSE_WARNING, "Extra target (%s) ignored", line); 78740393Sbostic } 78840393Sbostic 78940393Sbostic *cp = savec; 79040393Sbostic /* 79140393Sbostic * If it is a special type and not .PATH, it's the only target we 79240393Sbostic * allow on this line... 79340393Sbostic */ 79440393Sbostic if (specType != Not && specType != Path) { 79540393Sbostic Boolean warn = FALSE; 79640393Sbostic 79740393Sbostic while ((*cp != '!') && (*cp != ':') && *cp) { 79840393Sbostic if (*cp != ' ' && *cp != '\t') { 79940393Sbostic warn = TRUE; 80040393Sbostic } 80140393Sbostic cp++; 80240393Sbostic } 80340393Sbostic if (warn) { 80440393Sbostic Parse_Error(PARSE_WARNING, "Extra target ignored"); 80540393Sbostic } 80640393Sbostic } else { 80740393Sbostic while (*cp && isspace (*cp)) { 80840393Sbostic cp++; 80940393Sbostic } 81040393Sbostic } 81140393Sbostic line = cp; 81240393Sbostic } while ((*line != '!') && (*line != ':') && *line); 81340393Sbostic 81440393Sbostic /* 81540393Sbostic * Don't need the list of target names anymore... 81640393Sbostic */ 81740393Sbostic Lst_Destroy(curTargs, NOFREE); 81840393Sbostic 81940393Sbostic if (!Lst_IsEmpty(targets)) { 82040393Sbostic switch(specType) { 82140393Sbostic default: 82240393Sbostic Parse_Error(PARSE_WARNING, "Special and mundane targets don't mix. Mundane ones ignored"); 82340393Sbostic break; 82440393Sbostic case Default: 82540393Sbostic case Begin: 82640393Sbostic case End: 82740393Sbostic case Interrupt: 82840393Sbostic /* 82940393Sbostic * These four create nodes on which to hang commands, so 83040393Sbostic * targets shouldn't be empty... 83140393Sbostic */ 83240393Sbostic case Not: 83340393Sbostic /* 83440393Sbostic * Nothing special here -- targets can be empty if it wants. 83540393Sbostic */ 83640393Sbostic break; 83740393Sbostic } 83840393Sbostic } 83940393Sbostic 84040393Sbostic /* 84140393Sbostic * Have now parsed all the target names. Must parse the operator next. The 84240393Sbostic * result is left in op . 84340393Sbostic */ 84440393Sbostic if (*cp == '!') { 84540393Sbostic op = OP_FORCE; 84640393Sbostic } else if (*cp == ':') { 84740393Sbostic if (cp[1] == ':') { 84840393Sbostic op = OP_DOUBLEDEP; 84940393Sbostic cp++; 85040393Sbostic } else { 85140393Sbostic op = OP_DEPENDS; 85240393Sbostic } 85340393Sbostic } else { 85440393Sbostic Parse_Error (PARSE_FATAL, "Missing dependency operator"); 85540393Sbostic return; 85640393Sbostic } 85740393Sbostic 85840393Sbostic cp++; /* Advance beyond operator */ 85940393Sbostic 86040393Sbostic Lst_ForEach (targets, ParseDoOp, (ClientData)op); 86140393Sbostic 86240393Sbostic /* 86340393Sbostic * Get to the first source 86440393Sbostic */ 86540393Sbostic while (*cp && isspace (*cp)) { 86640393Sbostic cp++; 86740393Sbostic } 86840393Sbostic line = cp; 86940393Sbostic 87040393Sbostic /* 87140393Sbostic * Several special targets take different actions if present with no 87240393Sbostic * sources: 87340393Sbostic * a .SUFFIXES line with no sources clears out all old suffixes 87440393Sbostic * a .PRECIOUS line makes all targets precious 87540393Sbostic * a .IGNORE line ignores errors for all targets 87640393Sbostic * a .SILENT line creates silence when making all targets 87740393Sbostic * a .PATH removes all directories from the search path(s). 87840393Sbostic */ 87940393Sbostic if (!*line) { 88040393Sbostic switch (specType) { 88140393Sbostic case Suffixes: 88240393Sbostic Suff_ClearSuffixes (); 88340393Sbostic break; 88440393Sbostic case Precious: 88540393Sbostic allPrecious = TRUE; 88640393Sbostic break; 88740393Sbostic case Ignore: 88840393Sbostic ignoreErrors = TRUE; 88940393Sbostic break; 89040393Sbostic case Silent: 89140393Sbostic beSilent = TRUE; 89240393Sbostic break; 89340393Sbostic case Path: 89440393Sbostic Lst_ForEach(paths, ParseClearPath, (ClientData)NULL); 89540393Sbostic break; 89640393Sbostic } 89740393Sbostic } else if (specType == MFlags) { 89840393Sbostic /* 89940393Sbostic * Call on functions in main.c to deal with these arguments and 90040393Sbostic * set the initial character to a null-character so the loop to 90140393Sbostic * get sources won't get anything 90240393Sbostic */ 90340393Sbostic Main_ParseArgLine (line); 90440393Sbostic *line = '\0'; 90540393Sbostic } else if (specType == Shell) { 90640393Sbostic if (Job_ParseShell (line) != SUCCESS) { 90740393Sbostic Parse_Error (PARSE_FATAL, "improper shell specification"); 90840393Sbostic return; 90940393Sbostic } 91040393Sbostic *line = '\0'; 91140393Sbostic } else if ((specType == NotParallel) || (specType == SingleShell)) { 91240393Sbostic *line = '\0'; 91340393Sbostic } 91440393Sbostic 91540393Sbostic /* 91640393Sbostic * NOW GO FOR THE SOURCES 91740393Sbostic */ 91840393Sbostic if ((specType == Suffixes) || (specType == Path) || 91940393Sbostic (specType == Includes) || (specType == Libs) || 92040439Sbostic (specType == Null)) 92140393Sbostic { 92240393Sbostic while (*line) { 92340393Sbostic /* 92440393Sbostic * If the target was one that doesn't take files as its sources 92540393Sbostic * but takes something like suffixes, we take each 92640393Sbostic * space-separated word on the line as a something and deal 92740393Sbostic * with it accordingly. 92840393Sbostic * 92940393Sbostic * If the target was .SUFFIXES, we take each source as a 93040393Sbostic * suffix and add it to the list of suffixes maintained by the 93140393Sbostic * Suff module. 93240393Sbostic * 93340393Sbostic * If the target was a .PATH, we add the source as a directory 93440393Sbostic * to search on the search path. 93540393Sbostic * 93640393Sbostic * If it was .INCLUDES, the source is taken to be the suffix of 93740393Sbostic * files which will be #included and whose search path should 93840393Sbostic * be present in the .INCLUDES variable. 93940393Sbostic * 94040393Sbostic * If it was .LIBS, the source is taken to be the suffix of 94140393Sbostic * files which are considered libraries and whose search path 94240393Sbostic * should be present in the .LIBS variable. 94340393Sbostic * 94440393Sbostic * If it was .NULL, the source is the suffix to use when a file 94540393Sbostic * has no valid suffix. 94640393Sbostic */ 94740393Sbostic char savec; 94840393Sbostic while (*cp && !isspace (*cp)) { 94940393Sbostic cp++; 95040393Sbostic } 95140393Sbostic savec = *cp; 95240393Sbostic *cp = '\0'; 95340393Sbostic switch (specType) { 95440393Sbostic case Suffixes: 95540393Sbostic Suff_AddSuffix (line); 95640393Sbostic break; 95740393Sbostic case Path: 95840393Sbostic Lst_ForEach(paths, ParseAddDir, (ClientData)line); 95940393Sbostic break; 96040393Sbostic case Includes: 96140393Sbostic Suff_AddInclude (line); 96240393Sbostic break; 96340393Sbostic case Libs: 96440393Sbostic Suff_AddLib (line); 96540393Sbostic break; 96640393Sbostic case Null: 96740393Sbostic Suff_SetNull (line); 96840393Sbostic break; 96940393Sbostic } 97040393Sbostic *cp = savec; 97140393Sbostic if (savec != '\0') { 97240393Sbostic cp++; 97340393Sbostic } 97440393Sbostic while (*cp && isspace (*cp)) { 97540393Sbostic cp++; 97640393Sbostic } 97740393Sbostic line = cp; 97840393Sbostic } 97940393Sbostic if (paths) { 98040393Sbostic Lst_Destroy(paths, NOFREE); 98140393Sbostic } 98240393Sbostic } else { 98340393Sbostic while (*line) { 98440393Sbostic /* 98540393Sbostic * The targets take real sources, so we must beware of archive 98640393Sbostic * specifications (i.e. things with left parentheses in them) 98740393Sbostic * and handle them accordingly. 98840393Sbostic */ 98940393Sbostic while (*cp && !isspace (*cp)) { 99040393Sbostic if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) { 99140393Sbostic /* 99240393Sbostic * Only stop for a left parenthesis if it isn't at the 99340393Sbostic * start of a word (that'll be for variable changes 99440393Sbostic * later) and isn't preceded by a dollar sign (a dynamic 99540393Sbostic * source). 99640393Sbostic */ 99740393Sbostic break; 99840393Sbostic } else { 99940393Sbostic cp++; 100040393Sbostic } 100140393Sbostic } 100240393Sbostic 100340393Sbostic if (*cp == '(') { 100440393Sbostic GNode *gn; 100540393Sbostic 100640393Sbostic sources = Lst_Init (FALSE); 100740393Sbostic if (Arch_ParseArchive (&line, sources, VAR_CMD) != SUCCESS) { 100840393Sbostic Parse_Error (PARSE_FATAL, 100940393Sbostic "Error in source archive spec \"%s\"", line); 101040393Sbostic return; 101140393Sbostic } 101240393Sbostic 101340393Sbostic while (!Lst_IsEmpty (sources)) { 101440393Sbostic gn = (GNode *) Lst_DeQueue (sources); 101540393Sbostic ParseDoSrc (tOp, gn->name); 101640393Sbostic } 101740393Sbostic Lst_Destroy (sources, NOFREE); 101840393Sbostic cp = line; 101940393Sbostic } else { 102040393Sbostic if (*cp) { 102140393Sbostic *cp = '\0'; 102240393Sbostic cp += 1; 102340393Sbostic } 102440393Sbostic 102540393Sbostic ParseDoSrc (tOp, line); 102640393Sbostic } 102740393Sbostic while (*cp && isspace (*cp)) { 102840393Sbostic cp++; 102940393Sbostic } 103040393Sbostic line = cp; 103140393Sbostic } 103240393Sbostic } 103340393Sbostic 103440393Sbostic if (mainNode == NILGNODE) { 103540393Sbostic /* 103640393Sbostic * If we have yet to decide on a main target to make, in the 103740393Sbostic * absence of any user input, we want the first target on 103840393Sbostic * the first dependency line that is actually a real target 103940393Sbostic * (i.e. isn't a .USE or .EXEC rule) to be made. 104040393Sbostic */ 104140393Sbostic Lst_ForEach (targets, ParseFindMain, (ClientData)0); 104240393Sbostic } 104340393Sbostic 104440393Sbostic } 104540424Sbostic 104640393Sbostic /*- 104740393Sbostic *--------------------------------------------------------------------- 104840393Sbostic * Parse_IsVar -- 104940393Sbostic * Return TRUE if the passed line is a variable assignment. A variable 105040393Sbostic * assignment consists of a single word followed by optional whitespace 105140393Sbostic * followed by either a += or an = operator. 105240393Sbostic * This function is used both by the Parse_File function and main when 105340393Sbostic * parsing the command-line arguments. 105440393Sbostic * 105540393Sbostic * Results: 105640393Sbostic * TRUE if it is. FALSE if it ain't 105740393Sbostic * 105840393Sbostic * Side Effects: 105940393Sbostic * none 106040393Sbostic *--------------------------------------------------------------------- 106140393Sbostic */ 106240393Sbostic Boolean 106340393Sbostic Parse_IsVar (line) 106440393Sbostic register char *line; /* the line to check */ 106540393Sbostic { 106640393Sbostic register Boolean wasSpace = FALSE; /* set TRUE if found a space */ 106740393Sbostic register Boolean haveName = FALSE; /* Set TRUE if have a variable name */ 106840393Sbostic 106940393Sbostic /* 107040393Sbostic * Skip to variable name 107140393Sbostic */ 107240393Sbostic while ((*line == ' ') || (*line == '\t')) { 107340393Sbostic line++; 107440393Sbostic } 107540393Sbostic 107640393Sbostic while (*line != '=') { 107740393Sbostic if (*line == '\0') { 107840393Sbostic /* 107940393Sbostic * end-of-line -- can't be a variable assignment. 108040393Sbostic */ 108140393Sbostic return (FALSE); 108240393Sbostic } else if ((*line == ' ') || (*line == '\t')) { 108340393Sbostic /* 108440393Sbostic * there can be as much white space as desired so long as there is 108540393Sbostic * only one word before the operator 108640393Sbostic */ 108740393Sbostic wasSpace = TRUE; 108840393Sbostic } else if (wasSpace && haveName) { 108940393Sbostic /* 109040393Sbostic * Stop when an = operator is found. 109140393Sbostic */ 109240393Sbostic if ((*line == '+') || (*line == ':') || (*line == '?') || 109340393Sbostic (*line == '!')) { 109440393Sbostic break; 109540393Sbostic } 109640393Sbostic 109740393Sbostic /* 109840393Sbostic * This is the start of another word, so not assignment. 109940393Sbostic */ 110040393Sbostic return (FALSE); 110140393Sbostic } else { 110240393Sbostic haveName = TRUE; 110340393Sbostic wasSpace = FALSE; 110440393Sbostic } 110540393Sbostic line++; 110640393Sbostic } 110740393Sbostic 110840393Sbostic /* 110940393Sbostic * A final check: if we stopped on a +, ?, ! or :, the next character must 111040393Sbostic * be an = or it ain't a valid assignment 111140393Sbostic */ 111240393Sbostic if (((*line == '+') || 111340393Sbostic (*line == '?') || 111440393Sbostic (*line == ':') || 111540393Sbostic (*line == '!')) && 111640393Sbostic (line[1] != '=')) 111740393Sbostic { 111840393Sbostic return (FALSE); 111940393Sbostic } else { 112040393Sbostic return (haveName); 112140393Sbostic } 112240393Sbostic } 112340424Sbostic 112440393Sbostic /*- 112540393Sbostic *--------------------------------------------------------------------- 112640393Sbostic * Parse_DoVar -- 112740393Sbostic * Take the variable assignment in the passed line and do it in the 112840393Sbostic * global context. 112940393Sbostic * 113040393Sbostic * Note: There is a lexical ambiguity with assignment modifier characters 113140393Sbostic * in variable names. This routine interprets the character before the = 113240393Sbostic * as a modifier. Therefore, an assignment like 113340393Sbostic * C++=/usr/bin/CC 113440393Sbostic * is interpreted as "C+ +=" instead of "C++ =". 113540393Sbostic * 113640393Sbostic * Results: 113740393Sbostic * none 113840393Sbostic * 113940393Sbostic * Side Effects: 114040393Sbostic * the variable structure of the given variable name is altered in the 114140393Sbostic * global context. 114240393Sbostic *--------------------------------------------------------------------- 114340393Sbostic */ 114440393Sbostic void 114540393Sbostic Parse_DoVar (line, ctxt) 114640393Sbostic char *line; /* a line guaranteed to be a variable 114740393Sbostic * assignment. This reduces error checks */ 114840393Sbostic GNode *ctxt; /* Context in which to do the assignment */ 114940393Sbostic { 115040393Sbostic register char *cp; /* pointer into line */ 115140393Sbostic enum { 115240393Sbostic VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL 115340393Sbostic } type; /* Type of assignment */ 115440393Sbostic char *opc; /* ptr to operator character to 115540393Sbostic * null-terminate the variable name */ 115640393Sbostic 115740393Sbostic /* 115840393Sbostic * Skip to variable name 115940393Sbostic */ 116040393Sbostic while ((*line == ' ') || (*line == '\t')) { 116140393Sbostic line++; 116240393Sbostic } 116340393Sbostic 116440393Sbostic /* 116540393Sbostic * Skip to operator character, nulling out whitespace as we go 116640393Sbostic */ 116740393Sbostic for (cp = line + 1; *cp != '='; cp++) { 116840393Sbostic if (isspace (*cp)) { 116940393Sbostic *cp = '\0'; 117040393Sbostic } 117140393Sbostic } 117240393Sbostic opc = cp-1; /* operator is the previous character */ 117340393Sbostic *cp++ = '\0'; /* nuke the = */ 117440393Sbostic 117540393Sbostic /* 117640393Sbostic * Check operator type 117740393Sbostic */ 117840393Sbostic switch (*opc) { 117940393Sbostic case '+': 118040393Sbostic type = VAR_APPEND; 118140393Sbostic *opc = '\0'; 118240393Sbostic break; 118340393Sbostic 118440393Sbostic case '?': 118540393Sbostic /* 118640393Sbostic * If the variable already has a value, we don't do anything. 118740393Sbostic */ 118840393Sbostic *opc = '\0'; 118940393Sbostic if (Var_Exists(line, ctxt)) { 119040393Sbostic return; 119140393Sbostic } else { 119240393Sbostic type = VAR_NORMAL; 119340393Sbostic } 119440393Sbostic break; 119540393Sbostic 119640393Sbostic case ':': 119740393Sbostic type = VAR_SUBST; 119840393Sbostic *opc = '\0'; 119940393Sbostic break; 120040393Sbostic 120140393Sbostic case '!': 120240393Sbostic type = VAR_SHELL; 120340393Sbostic *opc = '\0'; 120440393Sbostic break; 120540393Sbostic 120640393Sbostic default: 120740393Sbostic type = VAR_NORMAL; 120840393Sbostic break; 120940393Sbostic } 121040393Sbostic 121140393Sbostic while (isspace (*cp)) { 121240393Sbostic cp++; 121340393Sbostic } 121440393Sbostic 121540393Sbostic if (type == VAR_APPEND) { 121640393Sbostic Var_Append (line, cp, ctxt); 121740393Sbostic } else if (type == VAR_SUBST) { 121840393Sbostic /* 121940393Sbostic * Allow variables in the old value to be undefined, but leave their 122040393Sbostic * invocation alone -- this is done by forcing oldVars to be false. 122140393Sbostic * XXX: This can cause recursive variables, but that's not hard to do, 122240393Sbostic * and this allows someone to do something like 122340393Sbostic * 122440393Sbostic * CFLAGS = $(.INCLUDES) 122540393Sbostic * CFLAGS := -I.. $(CFLAGS) 122640393Sbostic * 122740393Sbostic * And not get an error. 122840393Sbostic */ 122940393Sbostic Boolean oldOldVars = oldVars; 123040393Sbostic 123140393Sbostic oldVars = FALSE; 123240393Sbostic cp = Var_Subst(cp, ctxt, FALSE); 123340393Sbostic oldVars = oldOldVars; 123440393Sbostic 123540393Sbostic Var_Set(line, cp, ctxt); 123640393Sbostic free(cp); 123740393Sbostic } else if (type == VAR_SHELL) { 123840393Sbostic char result[BUFSIZ]; /* Result of command */ 123940393Sbostic char *args[4]; /* Args for invoking the shell */ 124040393Sbostic int fds[2]; /* Pipe streams */ 124140393Sbostic int cpid; /* Child PID */ 124240393Sbostic int pid; /* PID from wait() */ 124340393Sbostic Boolean freeCmd; /* TRUE if the command needs to be freed, i.e. 124440393Sbostic * if any variable expansion was performed */ 124540393Sbostic 124640393Sbostic /* 124740393Sbostic * Set up arguments for shell 124840393Sbostic */ 124940393Sbostic args[0] = "sh"; 125040393Sbostic args[1] = "-c"; 125140393Sbostic if (index(cp, '$') != (char *)NULL) { 125240393Sbostic /* 125340393Sbostic * There's a dollar sign in the command, so perform variable 125440393Sbostic * expansion on the whole thing. The resulting string will need 125540393Sbostic * freeing when we're done, so set freeCmd to TRUE. 125640393Sbostic */ 125740393Sbostic args[2] = Var_Subst(cp, VAR_CMD, TRUE); 125840393Sbostic freeCmd = TRUE; 125940393Sbostic } else { 126040393Sbostic args[2] = cp; 126140393Sbostic freeCmd = FALSE; 126240393Sbostic } 126340393Sbostic args[3] = (char *)NULL; 126440393Sbostic 126540393Sbostic /* 126640393Sbostic * Open a pipe for fetching its output 126740393Sbostic */ 126840393Sbostic pipe(fds); 126940393Sbostic 127040393Sbostic /* 127140393Sbostic * Fork 127240393Sbostic */ 127340393Sbostic cpid = vfork(); 127440393Sbostic if (cpid == 0) { 127540393Sbostic /* 127640393Sbostic * Close input side of pipe 127740393Sbostic */ 127840393Sbostic close(fds[0]); 127940393Sbostic 128040393Sbostic /* 128140393Sbostic * Duplicate the output stream to the shell's output, then 128240393Sbostic * shut the extra thing down. Note we don't fetch the error 128340393Sbostic * stream...why not? Why? 128440393Sbostic */ 128540393Sbostic dup2(fds[1], 1); 128640393Sbostic close(fds[1]); 128740393Sbostic 128840393Sbostic execv("/bin/sh", args); 128940393Sbostic _exit(1); 129040393Sbostic } else if (cpid < 0) { 129140393Sbostic /* 129240393Sbostic * Couldn't fork -- tell the user and make the variable null 129340393Sbostic */ 129440393Sbostic Parse_Error(PARSE_WARNING, "Couldn't exec \"%s\"", cp); 129540393Sbostic Var_Set(line, "", ctxt); 129640393Sbostic } else { 129740393Sbostic int status; 129840393Sbostic int cc; 129940393Sbostic 130040393Sbostic /* 130140393Sbostic * No need for the writing half 130240393Sbostic */ 130340393Sbostic close(fds[1]); 130440393Sbostic 130540393Sbostic /* 130640393Sbostic * Wait for the process to exit. 130740393Sbostic * 130840393Sbostic * XXX: If the child writes more than a pipe's worth, we will 130940393Sbostic * deadlock. 131040393Sbostic */ 131140393Sbostic while(((pid = wait(&status)) != cpid) && (pid >= 0)) { 131240393Sbostic ; 131340393Sbostic } 131440393Sbostic 131540393Sbostic /* 131640393Sbostic * Read all the characters the child wrote. 131740393Sbostic */ 131840393Sbostic cc = read(fds[0], result, sizeof(result)); 131940393Sbostic 132040393Sbostic if (cc < 0) { 132140393Sbostic /* 132240393Sbostic * Couldn't read the child's output -- tell the user and 132340393Sbostic * set the variable to null 132440393Sbostic */ 132540393Sbostic Parse_Error(PARSE_WARNING, "Couldn't read shell's output"); 132640393Sbostic cc = 0; 132740393Sbostic } 132840393Sbostic 132940393Sbostic if (status) { 133040393Sbostic /* 133140393Sbostic * Child returned an error -- tell the user but still use 133240393Sbostic * the result. 133340393Sbostic */ 133440393Sbostic Parse_Error(PARSE_WARNING, "\"%s\" returned non-zero", cp); 133540393Sbostic } 133640393Sbostic /* 133740393Sbostic * Null-terminate the result, convert newlines to spaces and 133840393Sbostic * install it in the variable. 133940393Sbostic */ 134040393Sbostic result[cc] = '\0'; 134140393Sbostic cp = &result[cc] - 1; 134240393Sbostic 134340393Sbostic if (*cp == '\n') { 134440393Sbostic /* 134540393Sbostic * A final newline is just stripped 134640393Sbostic */ 134740393Sbostic *cp-- = '\0'; 134840393Sbostic } 134940393Sbostic while (cp >= result) { 135040393Sbostic if (*cp == '\n') { 135140393Sbostic *cp = ' '; 135240393Sbostic } 135340393Sbostic cp--; 135440393Sbostic } 135540393Sbostic Var_Set(line, result, ctxt); 135640393Sbostic 135740393Sbostic /* 135840393Sbostic * Close the input side of the pipe. 135940393Sbostic */ 136040393Sbostic close(fds[0]); 136140393Sbostic } 136240393Sbostic if (freeCmd) { 136340393Sbostic free(args[2]); 136440393Sbostic } 136540393Sbostic } else { 136640393Sbostic /* 136740393Sbostic * Normal assignment -- just do it. 136840393Sbostic */ 136940393Sbostic Var_Set (line, cp, ctxt); 137040393Sbostic } 137140393Sbostic } 137240424Sbostic 137340393Sbostic /*- 137440393Sbostic * ParseAddCmd -- 137540393Sbostic * Lst_ForEach function to add a command line to all targets 137640393Sbostic * 137740393Sbostic * Results: 137840393Sbostic * Always 0 137940393Sbostic * 138040393Sbostic * Side Effects: 138140393Sbostic * A new element is added to the commands list of the node. 138240393Sbostic */ 138340476Sbostic static 138440476Sbostic ParseAddCmd(gn, cmd) 138540476Sbostic GNode *gn; /* the node to which the command is to be added */ 138640476Sbostic char *cmd; /* the command to add */ 138740393Sbostic { 138840476Sbostic /* if target already supplied, ignore commands */ 138940476Sbostic if (!(gn->type & OP_HAS_COMMANDS)) 139040476Sbostic (void)Lst_AtEnd(gn->commands, (ClientData)cmd); 139140476Sbostic return(0); 139240393Sbostic } 139340424Sbostic 139440393Sbostic /*- 139540393Sbostic *----------------------------------------------------------------------- 139640393Sbostic * ParseHasCommands -- 139740393Sbostic * Callback procedure for Parse_File when destroying the list of 139840393Sbostic * targets on the last dependency line. Marks a target as already 139940393Sbostic * having commands if it does, to keep from having shell commands 140040393Sbostic * on multiple dependency lines. 140140393Sbostic * 140240393Sbostic * Results: 140340393Sbostic * Always 0. 140440393Sbostic * 140540393Sbostic * Side Effects: 140640393Sbostic * OP_HAS_COMMANDS may be set for the target. 140740393Sbostic * 140840393Sbostic *----------------------------------------------------------------------- 140940393Sbostic */ 141040393Sbostic static int 141140393Sbostic ParseHasCommands(gn) 141240393Sbostic GNode *gn; /* Node to examine */ 141340393Sbostic { 141440393Sbostic if (!Lst_IsEmpty(gn->commands)) { 141540393Sbostic gn->type |= OP_HAS_COMMANDS; 141640393Sbostic } 141740393Sbostic return(0); 141840393Sbostic } 141940424Sbostic 142040393Sbostic /*- 142140393Sbostic *----------------------------------------------------------------------- 142240393Sbostic * Parse_AddIncludeDir -- 142340393Sbostic * Add a directory to the path searched for included makefiles 142440393Sbostic * bracketed by double-quotes. Used by functions in main.c 142540393Sbostic * 142640393Sbostic * Results: 142740393Sbostic * None. 142840393Sbostic * 142940393Sbostic * Side Effects: 143040393Sbostic * The directory is appended to the list. 143140393Sbostic * 143240393Sbostic *----------------------------------------------------------------------- 143340393Sbostic */ 143440393Sbostic void 143540393Sbostic Parse_AddIncludeDir (dir) 143640393Sbostic char *dir; /* The name of the directory to add */ 143740393Sbostic { 143840393Sbostic Dir_AddDir (parseIncPath, dir); 143940393Sbostic } 144040424Sbostic 144140393Sbostic /*- 144240393Sbostic *--------------------------------------------------------------------- 144340393Sbostic * ParseDoInclude -- 144440393Sbostic * Push to another file. 144540393Sbostic * 144640393Sbostic * The input is the line minus the #include. A file spec is a string 144740393Sbostic * enclosed in <> or "". The former is looked for only in sysIncPath. 144840393Sbostic * The latter in . and the directories specified by -I command line 144940393Sbostic * options 145040393Sbostic * 145140393Sbostic * Results: 145240393Sbostic * None 145340393Sbostic * 145440393Sbostic * Side Effects: 145540393Sbostic * A structure is added to the includes Lst and readProc, lineno, 145640393Sbostic * fname and curFILE are altered for the new file 145740393Sbostic *--------------------------------------------------------------------- 145840393Sbostic */ 145940393Sbostic static void 146040393Sbostic ParseDoInclude (file) 146140393Sbostic char *file; /* file specification */ 146240393Sbostic { 146340393Sbostic char *fullname; /* full pathname of file */ 146440393Sbostic IFile *oldFile; /* state associated with current file */ 146540393Sbostic Lst path; /* the path to use to find the file */ 146640393Sbostic char endc; /* the character which ends the file spec */ 146740393Sbostic char *cp; /* current position in file spec */ 146840393Sbostic Boolean isSystem; /* TRUE if makefile is a system makefile */ 146940393Sbostic 147040393Sbostic /* 147140393Sbostic * Skip to delimiter character so we know where to look 147240393Sbostic */ 147340393Sbostic while ((*file == ' ') || (*file == '\t')) { 147440393Sbostic file++; 147540393Sbostic } 147640393Sbostic 147740393Sbostic if ((*file != '"') && (*file != '<')) { 147842443Sbostic Parse_Error (PARSE_FATAL, 147942443Sbostic ".include filename must be delimited by '\"' or '<'"); 148040393Sbostic return; 148140393Sbostic } 148240393Sbostic 148340393Sbostic /* 148440393Sbostic * Set the search path on which to find the include file based on the 148540393Sbostic * characters which bracket its name. Angle-brackets imply it's 148640393Sbostic * a system Makefile while double-quotes imply it's a user makefile 148740393Sbostic */ 148840393Sbostic if (*file == '<') { 148940393Sbostic isSystem = TRUE; 149040393Sbostic endc = '>'; 149140393Sbostic } else { 149240393Sbostic isSystem = FALSE; 149340393Sbostic endc = '"'; 149440393Sbostic } 149540393Sbostic 149640393Sbostic /* 149740393Sbostic * Skip to matching delimiter 149840393Sbostic */ 149940393Sbostic for (cp = ++file; *cp && *cp != endc; cp++) { 150040393Sbostic continue; 150140393Sbostic } 150240393Sbostic 150340393Sbostic if (*cp != endc) { 150440393Sbostic Parse_Error (PARSE_FATAL, 150540435Sbostic "Unclosed .include filename. '%c' expected", endc); 150640393Sbostic return; 150740393Sbostic } 150840393Sbostic *cp = '\0'; 150940393Sbostic 151040393Sbostic /* 151140393Sbostic * Substitute for any variables in the file name before trying to 151240393Sbostic * find the thing. 151340393Sbostic */ 151440393Sbostic file = Var_Subst (file, VAR_CMD, FALSE); 151540393Sbostic 151640393Sbostic /* 151740393Sbostic * Now we know the file's name and its search path, we attempt to 151840393Sbostic * find the durn thing. A return of NULL indicates the file don't 151940393Sbostic * exist. 152040393Sbostic */ 152140393Sbostic if (!isSystem) { 152240393Sbostic /* 152340393Sbostic * Include files contained in double-quotes are first searched for 152440393Sbostic * relative to the including file's location. We don't want to 152540393Sbostic * cd there, of course, so we just tack on the old file's 152640393Sbostic * leading path components and call Dir_FindFile to see if 152740393Sbostic * we can locate the beast. 152840393Sbostic */ 152940393Sbostic char *prefEnd; 153040393Sbostic 153140393Sbostic prefEnd = rindex (fname, '/'); 153240393Sbostic if (prefEnd != (char *)NULL) { 153340393Sbostic char *newName; 153440393Sbostic 153540393Sbostic *prefEnd = '\0'; 153640534Sbostic newName = str_concat (fname, file, STR_ADDSLASH); 153740393Sbostic fullname = Dir_FindFile (newName, parseIncPath); 153840393Sbostic if (fullname == (char *)NULL) { 153940393Sbostic fullname = Dir_FindFile(newName, dirSearchPath); 154040393Sbostic } 154140393Sbostic free (newName); 154240393Sbostic *prefEnd = '/'; 154340393Sbostic } else { 154440393Sbostic fullname = (char *)NULL; 154540393Sbostic } 154640393Sbostic } else { 154740393Sbostic fullname = (char *)NULL; 154840393Sbostic } 154940393Sbostic 155040393Sbostic if (fullname == (char *)NULL) { 155140393Sbostic /* 155240393Sbostic * System makefile or makefile wasn't found in same directory as 155340393Sbostic * included makefile. Search for it first on the -I search path, 155440393Sbostic * then on the .PATH search path, if not found in a -I directory. 155540393Sbostic * XXX: Suffix specific? 155640393Sbostic */ 155740393Sbostic fullname = Dir_FindFile (file, parseIncPath); 155840393Sbostic if (fullname == (char *)NULL) { 155940393Sbostic fullname = Dir_FindFile(file, dirSearchPath); 156040393Sbostic } 156140393Sbostic } 156240393Sbostic 156340393Sbostic if (fullname == (char *)NULL) { 156440393Sbostic /* 156540393Sbostic * Still haven't found the makefile. Look for it on the system 156640393Sbostic * path as a last resort. 156740393Sbostic */ 156840393Sbostic fullname = Dir_FindFile(file, sysIncPath); 156940393Sbostic } 157040393Sbostic 157140393Sbostic if (fullname == (char *) NULL) { 157240393Sbostic *cp = endc; 157340393Sbostic Parse_Error (PARSE_FATAL, "Could not find %s", file); 157440393Sbostic return; 157540393Sbostic } 157640393Sbostic 157740393Sbostic /* 157840393Sbostic * Once we find the absolute path to the file, we get to save all the 157940393Sbostic * state from the current file before we can start reading this 158040393Sbostic * include file. The state is stored in an IFile structure which 158140393Sbostic * is placed on a list with other IFile structures. The list makes 158240393Sbostic * a very nice stack to track how we got here... 158340393Sbostic */ 158440534Sbostic oldFile = (IFile *) emalloc (sizeof (IFile)); 158540393Sbostic oldFile->fname = fname; 158640393Sbostic 158740393Sbostic oldFile->F = curFILE; 158840393Sbostic oldFile->lineno = lineno; 158940393Sbostic 159040393Sbostic (void) Lst_AtFront (includes, (ClientData)oldFile); 159140393Sbostic 159240393Sbostic /* 159340393Sbostic * Once the previous state has been saved, we can get down to reading 159440393Sbostic * the new file. We set up the name of the file to be the absolute 159540393Sbostic * name of the include file so error messages refer to the right 159640393Sbostic * place. Naturally enough, we start reading at line number 0. 159740393Sbostic */ 159840393Sbostic fname = fullname; 159940393Sbostic lineno = 0; 160040393Sbostic 160140393Sbostic curFILE = fopen (fullname, "r"); 160240393Sbostic if (curFILE == (FILE * ) NULL) { 160340393Sbostic Parse_Error (PARSE_FATAL, "Cannot open %s", fullname); 160440393Sbostic /* 160540393Sbostic * Pop to previous file 160640393Sbostic */ 160740393Sbostic (void) ParseEOF(); 160840393Sbostic } 160940393Sbostic } 161040424Sbostic 161140393Sbostic /*- 161240393Sbostic *--------------------------------------------------------------------- 161340393Sbostic * ParseEOF -- 161440393Sbostic * Called when EOF is reached in the current file. If we were reading 161540393Sbostic * an include file, the includes stack is popped and things set up 161640393Sbostic * to go back to reading the previous file at the previous location. 161740393Sbostic * 161840393Sbostic * Results: 161940393Sbostic * CONTINUE if there's more to do. DONE if not. 162040393Sbostic * 162140393Sbostic * Side Effects: 162240393Sbostic * The old curFILE, is closed. The includes list is shortened. 162340393Sbostic * lineno, curFILE, and fname are changed if CONTINUE is returned. 162440393Sbostic *--------------------------------------------------------------------- 162540393Sbostic */ 162640393Sbostic static int 162740393Sbostic ParseEOF () 162840393Sbostic { 162940393Sbostic IFile *ifile; /* the state on the top of the includes stack */ 163040393Sbostic 163140393Sbostic if (Lst_IsEmpty (includes)) { 163240393Sbostic return (DONE); 163340393Sbostic } 163440393Sbostic 163540393Sbostic ifile = (IFile *) Lst_DeQueue (includes); 163640393Sbostic free (fname); 163740393Sbostic fname = ifile->fname; 163840393Sbostic lineno = ifile->lineno; 163940393Sbostic fclose (curFILE); 164040393Sbostic curFILE = ifile->F; 164140393Sbostic free ((Address)ifile); 164240393Sbostic return (CONTINUE); 164340393Sbostic } 164440424Sbostic 164540393Sbostic /*- 164640393Sbostic *--------------------------------------------------------------------- 164740393Sbostic * ParseReadc -- 164840393Sbostic * Read a character from the current file and update the line number 164940393Sbostic * counter as necessary 165040393Sbostic * 165140393Sbostic * Results: 165240393Sbostic * The character that was read 165340393Sbostic * 165440393Sbostic * Side Effects: 165540393Sbostic * The lineno counter is incremented if the character is a newline 165640393Sbostic *--------------------------------------------------------------------- 165740393Sbostic */ 165840393Sbostic #ifdef notdef 165940393Sbostic static int parseReadChar; 166040393Sbostic 166140393Sbostic #define ParseReadc() (((parseReadChar = getc(curFILE)) == '\n') ? \ 166240393Sbostic (lineno++, '\n') : parseReadChar) 166340393Sbostic #else 166440393Sbostic #define ParseReadc() (getc(curFILE)) 166540393Sbostic #endif /* notdef */ 166640393Sbostic 166740424Sbostic 166840393Sbostic /*- 166940393Sbostic *--------------------------------------------------------------------- 167040393Sbostic * ParseReadLine -- 167140393Sbostic * Read an entire line from the input file. Called only by Parse_File. 167240393Sbostic * To facilitate escaped newlines and what have you, a character is 167340393Sbostic * buffered in 'lastc', which is '\0' when no characters have been 167440393Sbostic * read. When we break out of the loop, c holds the terminating 167540393Sbostic * character and lastc holds a character that should be added to 167640393Sbostic * the line (unless we don't read anything but a terminator). 167740393Sbostic * 167840393Sbostic * Results: 167940393Sbostic * A line w/o its newline 168040393Sbostic * 168140393Sbostic * Side Effects: 168240393Sbostic * Only those associated with reading a character 168340393Sbostic *--------------------------------------------------------------------- 168440393Sbostic */ 168540393Sbostic static char * 168640393Sbostic ParseReadLine () 168740393Sbostic { 168840393Sbostic Buffer buf; /* Buffer for current line */ 168940393Sbostic register int c; /* the current character */ 169040393Sbostic register int lastc; /* The most-recent character */ 169140393Sbostic Boolean semiNL; /* treat semi-colons as newlines */ 169240393Sbostic Boolean ignDepOp; /* TRUE if should ignore dependency operators 169340393Sbostic * for the purposes of setting semiNL */ 169440393Sbostic Boolean ignComment; /* TRUE if should ignore comments (in a 169540393Sbostic * shell command */ 169640393Sbostic char *line; /* Result */ 169740393Sbostic int lineLength; /* Length of result */ 169840393Sbostic 169940393Sbostic semiNL = FALSE; 170040393Sbostic ignDepOp = FALSE; 170140393Sbostic ignComment = FALSE; 170240393Sbostic 170340393Sbostic /* 170440393Sbostic * Handle special-characters at the beginning of the line. Either a 170540393Sbostic * leading tab (shell command) or pound-sign (possible conditional) 170640393Sbostic * forces us to ignore comments and dependency operators and treat 170740393Sbostic * semi-colons as semi-colons (by leaving semiNL FALSE). This also 170840393Sbostic * discards completely blank lines. 170940393Sbostic */ 171040393Sbostic while(1) { 171140393Sbostic c = ParseReadc(); 171240393Sbostic 171340435Sbostic if ((c == '\t') || (c == '.')) { 1714*44402Sbostic ignComment = TRUE; 171540393Sbostic break; 171640393Sbostic } else if (c == '\n') { 171740393Sbostic lineno++; 171841001Sbostic } else if (c == '#') { 171941001Sbostic ungetc(c, curFILE); 172041001Sbostic break; 172140393Sbostic } else { 172240393Sbostic /* 172340393Sbostic * Anything else breaks out without doing anything 172440393Sbostic */ 172540393Sbostic break; 172640393Sbostic } 172740393Sbostic } 172840393Sbostic 172940393Sbostic if (c != EOF) { 173040393Sbostic lastc = c; 173140393Sbostic buf = Buf_Init(BSIZE); 173240393Sbostic 173340393Sbostic while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) && 173440393Sbostic (c != EOF)) 173540393Sbostic { 173640393Sbostic test_char: 173740393Sbostic switch(c) { 173840393Sbostic case '\n': 173940393Sbostic /* 174040393Sbostic * Escaped newline: read characters until a non-space or an 174140393Sbostic * unescaped newline and replace them all by a single space. 174240393Sbostic * This is done by storing the space over the backslash and 174340393Sbostic * dropping through with the next nonspace. If it is a 174440393Sbostic * semi-colon and semiNL is TRUE, it will be recognized as a 174540393Sbostic * newline in the code below this... 174640393Sbostic */ 174740393Sbostic lineno++; 174840393Sbostic lastc = ' '; 174940393Sbostic while ((c = ParseReadc ()) == ' ' || c == '\t') { 175040393Sbostic continue; 175140393Sbostic } 175240393Sbostic if (c == EOF || c == '\n') { 175340393Sbostic goto line_read; 175440393Sbostic } else { 175540393Sbostic /* 175640393Sbostic * Check for comments, semiNL's, etc. -- easier than 175740393Sbostic * ungetc(c, curFILE); continue; 175840393Sbostic */ 175940393Sbostic goto test_char; 176040393Sbostic } 176140393Sbostic break; 176240393Sbostic case ';': 176340393Sbostic /* 176440393Sbostic * Semi-colon: Need to see if it should be interpreted as a 176540393Sbostic * newline 176640393Sbostic */ 176740393Sbostic if (semiNL) { 176840393Sbostic /* 176940393Sbostic * To make sure the command that may be following this 177040393Sbostic * semi-colon begins with a tab, we push one back into the 177140393Sbostic * input stream. This will overwrite the semi-colon in the 177240393Sbostic * buffer. If there is no command following, this does no 177340393Sbostic * harm, since the newline remains in the buffer and the 177440393Sbostic * whole line is ignored. 177540393Sbostic */ 177640393Sbostic ungetc('\t', curFILE); 177740393Sbostic goto line_read; 177840393Sbostic } 177940393Sbostic break; 178040393Sbostic case '=': 178140393Sbostic if (!semiNL) { 178240393Sbostic /* 178340393Sbostic * Haven't seen a dependency operator before this, so this 178440393Sbostic * must be a variable assignment -- don't pay attention to 178540393Sbostic * dependency operators after this. 178640393Sbostic */ 178740393Sbostic ignDepOp = TRUE; 178840393Sbostic } else if (lastc == ':' || lastc == '!') { 178940393Sbostic /* 179040393Sbostic * Well, we've seen a dependency operator already, but it 179140393Sbostic * was the previous character, so this is really just an 179240393Sbostic * expanded variable assignment. Revert semi-colons to 179340393Sbostic * being just semi-colons again and ignore any more 179440393Sbostic * dependency operators. 179540393Sbostic * 179640393Sbostic * XXX: Note that a line like "foo : a:=b" will blow up, 179740393Sbostic * but who'd write a line like that anyway? 179840393Sbostic */ 179940393Sbostic ignDepOp = TRUE; semiNL = FALSE; 180040393Sbostic } 180140393Sbostic break; 180240393Sbostic case '#': 180340393Sbostic if (!ignComment) { 180440393Sbostic /* 180540393Sbostic * If the character is a hash mark and it isn't escaped 180640393Sbostic * (or we're being compatible), the thing is a comment. 180740393Sbostic * Skip to the end of the line. 180840393Sbostic */ 180940393Sbostic do { 181040393Sbostic c = ParseReadc(); 181140393Sbostic } while ((c != '\n') && (c != EOF)); 181240393Sbostic goto line_read; 181340393Sbostic } 181440393Sbostic break; 181540393Sbostic case ':': 181640393Sbostic case '!': 181740393Sbostic if (!ignDepOp && (c == ':' || c == '!')) { 181840393Sbostic /* 181940393Sbostic * A semi-colon is recognized as a newline only on 182040393Sbostic * dependency lines. Dependency lines are lines with a 182140393Sbostic * colon or an exclamation point. Ergo... 182240393Sbostic */ 182340393Sbostic semiNL = TRUE; 182440393Sbostic } 182540393Sbostic break; 182640393Sbostic } 182740393Sbostic /* 182840393Sbostic * Copy in the previous character and save this one in lastc. 182940393Sbostic */ 183040393Sbostic Buf_AddByte (buf, (Byte)lastc); 183140393Sbostic lastc = c; 183240393Sbostic 183340393Sbostic } 183440393Sbostic line_read: 183540393Sbostic lineno++; 183640393Sbostic 183740393Sbostic if (lastc != '\0') { 183840393Sbostic Buf_AddByte (buf, (Byte)lastc); 183940393Sbostic } 184040393Sbostic Buf_AddByte (buf, (Byte)'\0'); 184140393Sbostic line = (char *)Buf_GetAll (buf, &lineLength); 184240393Sbostic Buf_Destroy (buf, FALSE); 184340393Sbostic 184440435Sbostic if (line[0] == '.') { 184540393Sbostic /* 184640393Sbostic * The line might be a conditional. Ask the conditional module 184740393Sbostic * about it and act accordingly 184840393Sbostic */ 184940393Sbostic switch (Cond_Eval (line)) { 185040393Sbostic case COND_SKIP: 185140393Sbostic do { 185240393Sbostic /* 185340393Sbostic * Skip to next conditional that evaluates to COND_PARSE. 185440393Sbostic */ 185540393Sbostic free (line); 185640393Sbostic c = ParseReadc(); 185740393Sbostic /* 185840393Sbostic * Skip lines until get to one that begins with a 185940393Sbostic * special char. 186040393Sbostic */ 186140435Sbostic while ((c != '.') && (c != EOF)) { 186240393Sbostic while (((c != '\n') || (lastc == '\\')) && 186340393Sbostic (c != EOF)) 186440393Sbostic { 186540393Sbostic /* 186640393Sbostic * Advance to next unescaped newline 186740393Sbostic */ 186840393Sbostic if ((lastc = c) == '\n') { 186940393Sbostic lineno++; 187040393Sbostic } 187140393Sbostic c = ParseReadc(); 187240393Sbostic } 187340393Sbostic lineno++; 187440393Sbostic 187540393Sbostic lastc = c; 187640393Sbostic c = ParseReadc (); 187740393Sbostic } 187840393Sbostic 187940393Sbostic if (c == EOF) { 188040393Sbostic Parse_Error (PARSE_FATAL, "Unclosed conditional"); 188140393Sbostic return ((char *)NULL); 188240393Sbostic } 188340393Sbostic 188440393Sbostic /* 188540393Sbostic * Read the entire line into buf 188640393Sbostic */ 188740393Sbostic buf = Buf_Init (BSIZE); 188840393Sbostic do { 188940393Sbostic Buf_AddByte (buf, (Byte)c); 189040393Sbostic c = ParseReadc(); 189140393Sbostic } while ((c != '\n') && (c != EOF)); 189240393Sbostic lineno++; 189340393Sbostic 189440393Sbostic Buf_AddByte (buf, (Byte)'\0'); 189540393Sbostic line = (char *)Buf_GetAll (buf, &lineLength); 189640393Sbostic Buf_Destroy (buf, FALSE); 189740393Sbostic } while (Cond_Eval(line) != COND_PARSE); 189840393Sbostic /*FALLTHRU*/ 189940393Sbostic case COND_PARSE: 190040393Sbostic free (line); 190140393Sbostic line = ParseReadLine(); 190240393Sbostic break; 190340393Sbostic } 190440393Sbostic } 190540393Sbostic 190640393Sbostic return (line); 190740393Sbostic } else { 190840393Sbostic /* 190940393Sbostic * Hit end-of-file, so return a NULL line to indicate this. 191040393Sbostic */ 191140393Sbostic return((char *)NULL); 191240393Sbostic } 191340393Sbostic } 191440424Sbostic 191540393Sbostic /*- 191640393Sbostic *----------------------------------------------------------------------- 191740393Sbostic * ParseFinishLine -- 191840393Sbostic * Handle the end of a dependency group. 191940393Sbostic * 192040393Sbostic * Results: 192140393Sbostic * Nothing. 192240393Sbostic * 192340393Sbostic * Side Effects: 192440393Sbostic * inLine set FALSE. 'targets' list destroyed. 192540393Sbostic * 192640393Sbostic *----------------------------------------------------------------------- 192740393Sbostic */ 192840393Sbostic static void 192940393Sbostic ParseFinishLine() 193040393Sbostic { 193140393Sbostic extern int Suff_EndTransform(); 193240393Sbostic 193340393Sbostic if (inLine) { 193440393Sbostic Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL); 193540393Sbostic Lst_Destroy (targets, ParseHasCommands); 193640393Sbostic inLine = FALSE; 193740393Sbostic } 193840393Sbostic } 193940393Sbostic 194040424Sbostic 194140393Sbostic /*- 194240393Sbostic *--------------------------------------------------------------------- 194340393Sbostic * Parse_File -- 194440393Sbostic * Parse a file into its component parts, incorporating it into the 194540393Sbostic * current dependency graph. This is the main function and controls 194640393Sbostic * almost every other function in this module 194740393Sbostic * 194840393Sbostic * Results: 194940393Sbostic * None 195040393Sbostic * 195140393Sbostic * Side Effects: 195240393Sbostic * Loads. Nodes are added to the list of all targets, nodes and links 195340393Sbostic * are added to the dependency graph. etc. etc. etc. 195440393Sbostic *--------------------------------------------------------------------- 195540393Sbostic */ 195640393Sbostic void 195740393Sbostic Parse_File(name, stream) 195840393Sbostic char *name; /* the name of the file being read */ 195940393Sbostic FILE * stream; /* Stream open to makefile to parse */ 196040393Sbostic { 196140393Sbostic register char *cp, /* pointer into the line */ 196240393Sbostic *line; /* the line we're working on */ 196340393Sbostic 196440393Sbostic inLine = FALSE; 196540393Sbostic fname = name; 196640393Sbostic curFILE = stream; 196740393Sbostic lineno = 0; 196840393Sbostic fatals = 0; 196940393Sbostic 197040393Sbostic do { 197140393Sbostic while (line = ParseReadLine ()) { 197240435Sbostic if (*line == '.') { 197340393Sbostic /* 197440393Sbostic * Lines that begin with the special character are either 197540393Sbostic * include or undef directives. 197640393Sbostic */ 197740393Sbostic for (cp = line + 1; isspace (*cp); cp++) { 197840393Sbostic continue; 197940393Sbostic } 198040393Sbostic if (strncmp (cp, "include", 7) == 0) { 198140393Sbostic ParseDoInclude (cp + 7); 198240393Sbostic goto nextLine; 198340393Sbostic } else if (strncmp(cp, "undef", 5) == 0) { 198440393Sbostic char *cp2; 198540393Sbostic for (cp += 5; isspace(*cp); cp++) { 198640393Sbostic continue; 198740393Sbostic } 198840393Sbostic 198940393Sbostic for (cp2 = cp; !isspace(*cp2) && (*cp2 != '\0'); cp2++) { 199040393Sbostic continue; 199140393Sbostic } 199240393Sbostic 199340393Sbostic *cp2 = '\0'; 199440393Sbostic 199540393Sbostic Var_Delete(cp, VAR_GLOBAL); 199640393Sbostic goto nextLine; 199740393Sbostic } 199840393Sbostic } 199940393Sbostic if (*line == '#') { 200040435Sbostic /* If we're this far, the line must be a comment. */ 200140393Sbostic goto nextLine; 200240393Sbostic } 200340393Sbostic 200440393Sbostic if (*line == '\t' 200540393Sbostic #ifdef POSIX 200640393Sbostic || *line == ' ' 200740393Sbostic #endif 200840393Sbostic ) 200940393Sbostic { 201040393Sbostic /* 201140393Sbostic * If a line starts with a tab (or space in POSIX-land), it 201240393Sbostic * can only hope to be a creation command. 201340393Sbostic */ 201440393Sbostic shellCommand: 201540393Sbostic for (cp = line + 1; isspace (*cp); cp++) { 201640393Sbostic continue; 201740393Sbostic } 201840393Sbostic if (*cp) { 201940393Sbostic if (inLine) { 202040393Sbostic /* 202140393Sbostic * So long as it's not a blank line and we're actually 202240393Sbostic * in a dependency spec, add the command to the list of 202340393Sbostic * commands of all targets in the dependency spec 202440393Sbostic */ 202540393Sbostic Lst_ForEach (targets, ParseAddCmd, (ClientData)cp); 202640393Sbostic continue; 202740393Sbostic } else { 202840393Sbostic Parse_Error (PARSE_FATAL, 202940393Sbostic "Unassociated shell command \"%.20s\"", 203040393Sbostic cp); 203140393Sbostic } 203240393Sbostic } 203340393Sbostic } else if (Parse_IsVar (line)) { 203440393Sbostic ParseFinishLine(); 203540393Sbostic Parse_DoVar (line, VAR_GLOBAL); 203640393Sbostic } else { 203740393Sbostic /* 203840393Sbostic * We now know it's a dependency line so it needs to have all 203940393Sbostic * variables expanded before being parsed. Tell the variable 204040393Sbostic * module to complain if some variable is undefined... 204140393Sbostic * To make life easier on novices, if the line is indented we 204240393Sbostic * first make sure the line has a dependency operator in it. 204340393Sbostic * If it doesn't have an operator and we're in a dependency 204440393Sbostic * line's script, we assume it's actually a shell command 204540393Sbostic * and add it to the current list of targets. 204640393Sbostic * 204740393Sbostic * Note that POSIX declares all lines that start with 204840393Sbostic * whitespace are shell commands, so there's no need to check 204940393Sbostic * here... 205040393Sbostic */ 205140393Sbostic Boolean nonSpace = FALSE; 205240393Sbostic 205340393Sbostic cp = line; 205440393Sbostic #ifndef POSIX 205540393Sbostic if (line[0] == ' ') { 205640393Sbostic while ((*cp != ':') && (*cp != '!') && (*cp != '\0')) { 205740393Sbostic if (!isspace(*cp)) { 205840393Sbostic nonSpace = TRUE; 205940393Sbostic } 206040393Sbostic cp++; 206140393Sbostic } 206240393Sbostic } 206340393Sbostic 206440393Sbostic if (*cp == '\0') { 206540393Sbostic if (inLine) { 206640393Sbostic Parse_Error (PARSE_WARNING, 206740393Sbostic "Shell command needs a leading tab"); 206840393Sbostic goto shellCommand; 206940393Sbostic } else if (nonSpace) { 207040393Sbostic Parse_Error (PARSE_FATAL, "Missing operator"); 207140393Sbostic } 207240393Sbostic } else { 207340393Sbostic #endif 207440393Sbostic ParseFinishLine(); 207540393Sbostic 207640393Sbostic cp = Var_Subst (line, VAR_CMD, TRUE); 207740393Sbostic free (line); 207840393Sbostic line = cp; 207940393Sbostic 208040393Sbostic /* 208140393Sbostic * Need a non-circular list for the target nodes 208240393Sbostic */ 208340393Sbostic targets = Lst_Init (FALSE); 208440393Sbostic inLine = TRUE; 208540393Sbostic 208640393Sbostic ParseDoDependency (line); 208740393Sbostic #ifndef POSIX 208840393Sbostic } 208940393Sbostic #endif 209040393Sbostic } 209140393Sbostic 209240393Sbostic nextLine: 209340393Sbostic 209440393Sbostic free (line); 209540393Sbostic } 209640393Sbostic /* 209740393Sbostic * Reached EOF, but it may be just EOF of an include file... 209840393Sbostic */ 209940393Sbostic } while (ParseEOF() == CONTINUE); 210040393Sbostic 210140393Sbostic /* 210240393Sbostic * Make sure conditionals are clean 210340393Sbostic */ 210440393Sbostic Cond_End(); 210540393Sbostic 210640393Sbostic if (fatals) { 210740393Sbostic fprintf (stderr, "Fatal errors encountered -- cannot continue\n"); 210840393Sbostic exit (1); 210940393Sbostic } 211040393Sbostic } 211140424Sbostic 211240393Sbostic /*- 211340393Sbostic *--------------------------------------------------------------------- 211440393Sbostic * Parse_Init -- 211540393Sbostic * initialize the parsing module 211640393Sbostic * 211740393Sbostic * Results: 211840393Sbostic * none 211940393Sbostic * 212040393Sbostic * Side Effects: 212140393Sbostic * the parseIncPath list is initialized... 212240393Sbostic *--------------------------------------------------------------------- 212340393Sbostic */ 212440393Sbostic Parse_Init () 212540393Sbostic { 212640515Sbostic char *cp, *start; 212740515Sbostic /* avoid faults on read-only strings */ 212840515Sbostic static char syspath[] = _PATH_DEFSYSPATH; 212940393Sbostic 213040393Sbostic mainNode = NILGNODE; 213140393Sbostic parseIncPath = Lst_Init (FALSE); 213240393Sbostic sysIncPath = Lst_Init (FALSE); 213340393Sbostic includes = Lst_Init (FALSE); 213440393Sbostic 213540393Sbostic /* 213640393Sbostic * Add the directories from the DEFSYSPATH (more than one may be given 213740393Sbostic * as dir1:...:dirn) to the system include path. 213840393Sbostic */ 213940393Sbostic for (start = syspath; *start != '\0'; start = cp) { 214040393Sbostic for (cp = start; *cp != '\0' && *cp != ':'; cp++) { 214140393Sbostic ; 214240393Sbostic } 214340393Sbostic if (*cp == '\0') { 214440393Sbostic Dir_AddDir(sysIncPath, start); 214540393Sbostic } else { 214640393Sbostic *cp++ = '\0'; 214740393Sbostic Dir_AddDir(sysIncPath, start); 214840393Sbostic } 214940393Sbostic } 215040393Sbostic } 215140424Sbostic 215240393Sbostic /*- 215340393Sbostic *----------------------------------------------------------------------- 215440393Sbostic * Parse_MainName -- 215540393Sbostic * Return a Lst of the main target to create for main()'s sake. If 215640393Sbostic * no such target exists, we Punt with an obnoxious error message. 215740393Sbostic * 215840393Sbostic * Results: 215940393Sbostic * A Lst of the single node to create. 216040393Sbostic * 216140393Sbostic * Side Effects: 216240393Sbostic * None. 216340393Sbostic * 216440393Sbostic *----------------------------------------------------------------------- 216540393Sbostic */ 216640393Sbostic Lst 216740393Sbostic Parse_MainName() 216840393Sbostic { 216940393Sbostic Lst main; /* result list */ 217040393Sbostic 217140393Sbostic main = Lst_Init (FALSE); 217240393Sbostic 217340393Sbostic if (mainNode == NILGNODE) { 217440393Sbostic Punt ("I don't know what to DO!\n"); 217540393Sbostic /*NOTREACHED*/ 217640393Sbostic } else if (mainNode->type & OP_DOUBLEDEP) { 217740393Sbostic Lst_Concat(main, mainNode->cohorts, LST_CONCNEW); 217840393Sbostic } 217940393Sbostic (void) Lst_AtEnd (main, (ClientData)mainNode); 218040393Sbostic return (main); 218140393Sbostic } 2182