📄 parse.c
字号:
} else if (specType == MFlags) { /* * Call on functions in main.c to deal with these arguments and * set the initial character to a null-character so the loop to * get sources won't get anything */ Main_ParseArgLine (line); *line = '\0'; } else if (specType == ExShell) { if (Job_ParseShell (line) != SUCCESS) { Parse_Error (PARSE_FATAL, "improper shell specification"); return; } *line = '\0'; } else if ((specType == NotParallel) || (specType == SingleShell)) { *line = '\0'; } /* * NOW GO FOR THE SOURCES */ if ((specType == Suffixes) || (specType == ExPath) || (specType == Includes) || (specType == Libs) || (specType == Null)) { while (*line) { /* * If the target was one that doesn't take files as its sources * but takes something like suffixes, we take each * space-separated word on the line as a something and deal * with it accordingly. * * If the target was .SUFFIXES, we take each source as a * suffix and add it to the list of suffixes maintained by the * Suff module. * * If the target was a .PATH, we add the source as a directory * to search on the search path. * * If it was .INCLUDES, the source is taken to be the suffix of * files which will be #included and whose search path should * be present in the .INCLUDES variable. * * If it was .LIBS, the source is taken to be the suffix of * files which are considered libraries and whose search path * should be present in the .LIBS variable. * * If it was .NULL, the source is the suffix to use when a file * has no valid suffix. */ char savec; while (*cp && !isspace (*cp)) { cp++; } savec = *cp; *cp = '\0'; switch (specType) { case Suffixes: Suff_AddSuffix (line); break; case ExPath: Lst_ForEach(paths, ParseAddDir, (ClientData)line); break; case Includes: Suff_AddInclude (line); break; case Libs: Suff_AddLib (line); break; case Null: Suff_SetNull (line); break; default: break; } *cp = savec; if (savec != '\0') { cp++; } while (*cp && isspace (*cp)) { cp++; } line = cp; } if (paths) { Lst_Destroy(paths, NOFREE); } } else { while (*line) { /* * The targets take real sources, so we must beware of archive * specifications (i.e. things with left parentheses in them) * and handle them accordingly. */ while (*cp && !isspace (*cp)) { if ((*cp == '(') && (cp > line) && (cp[-1] != '$')) { /* * Only stop for a left parenthesis if it isn't at the * start of a word (that'll be for variable changes * later) and isn't preceded by a dollar sign (a dynamic * source). */ break; } else { cp++; } } if (*cp == '(') { GNode *gn; sources = Lst_Init (FALSE); if (Arch_ParseArchive (&line, sources, VAR_CMD) != SUCCESS) { Parse_Error (PARSE_FATAL, "Error in source archive spec \"%s\"", line); return; } while (!Lst_IsEmpty (sources)) { gn = (GNode *) Lst_DeQueue (sources); ParseDoSrc (tOp, gn->name); } Lst_Destroy (sources, NOFREE); cp = line; } else { if (*cp) { *cp = '\0'; cp += 1; } ParseDoSrc (tOp, line); } while (*cp && isspace (*cp)) { cp++; } line = cp; } } if (mainNode == NILGNODE) { /* * If we have yet to decide on a main target to make, in the * absence of any user input, we want the first target on * the first dependency line that is actually a real target * (i.e. isn't a .USE or .EXEC rule) to be made. */ Lst_ForEach (targets, ParseFindMain, (ClientData)0); }}/*- *--------------------------------------------------------------------- * Parse_IsVar -- * Return TRUE if the passed line is a variable assignment. A variable * assignment consists of a single word followed by optional whitespace * followed by either a += or an = operator. * This function is used both by the Parse_File function and main when * parsing the command-line arguments. * * Results: * TRUE if it is. FALSE if it ain't * * Side Effects: * none *--------------------------------------------------------------------- */BooleanParse_IsVar (line) register char *line; /* the line to check */{ register Boolean wasSpace = FALSE; /* set TRUE if found a space */ register Boolean haveName = FALSE; /* Set TRUE if have a variable name */ /* * Skip to variable name */ while ((*line == ' ') || (*line == '\t')) { line++; } while (*line != '=') { if (*line == '\0') { /* * end-of-line -- can't be a variable assignment. */ return (FALSE); } else if ((*line == ' ') || (*line == '\t')) { /* * there can be as much white space as desired so long as there is * only one word before the operator */ wasSpace = TRUE; } else if (wasSpace && haveName) { /* * Stop when an = operator is found. */ if ((*line == '+') || (*line == ':') || (*line == '?') || (*line == '!')) { break; } /* * This is the start of another word, so not assignment. */ return (FALSE); } else { haveName = TRUE; wasSpace = FALSE; } line++; } /* * A final check: if we stopped on a +, ?, ! or :, the next character must * be an = or it ain't a valid assignment */ if (((*line == '+') || (*line == '?') || (*line == ':') || (*line == '!')) && (line[1] != '=')) { return (FALSE); } else { return (haveName); }}/*- *--------------------------------------------------------------------- * Parse_DoVar -- * Take the variable assignment in the passed line and do it in the * global context. * * Note: There is a lexical ambiguity with assignment modifier characters * in variable names. This routine interprets the character before the = * as a modifier. Therefore, an assignment like * C++=/usr/bin/CC * is interpreted as "C+ +=" instead of "C++ =". * * Results: * none * * Side Effects: * the variable structure of the given variable name is altered in the * global context. *--------------------------------------------------------------------- */voidParse_DoVar (line, ctxt) char *line; /* a line guaranteed to be a variable * assignment. This reduces error checks */ GNode *ctxt; /* Context in which to do the assignment */{ char *cp; /* pointer into line */ enum { VAR_SUBST, VAR_APPEND, VAR_SHELL, VAR_NORMAL } type; /* Type of assignment */ char *opc; /* ptr to operator character to * null-terminate the variable name */ /* * Avoid clobbered variable warnings by forcing the compiler * to ``unregister'' variables */ #if __GNUC__ (void) &cp; (void) &line; #endif /* * Skip to variable name */ while ((*line == ' ') || (*line == '\t')) { line++; } /* * Skip to operator character, nulling out whitespace as we go */ for (cp = line + 1; *cp != '='; cp++) { if (isspace (*cp)) { *cp = '\0'; } } opc = cp-1; /* operator is the previous character */ *cp++ = '\0'; /* nuke the = */ /* * Check operator type */ switch (*opc) { case '+': type = VAR_APPEND; *opc = '\0'; break; case '?': /* * If the variable already has a value, we don't do anything. */ *opc = '\0'; if (Var_Exists(line, ctxt)) { return; } else { type = VAR_NORMAL; } break; case ':': type = VAR_SUBST; *opc = '\0'; break; case '!': type = VAR_SHELL; *opc = '\0'; break; default: type = VAR_NORMAL; break; } while (isspace (*cp)) { cp++; } if (type == VAR_APPEND) { Var_Append (line, cp, ctxt); } else if (type == VAR_SUBST) { /* * Allow variables in the old value to be undefined, but leave their * invocation alone -- this is done by forcing oldVars to be false. * XXX: This can cause recursive variables, but that's not hard to do, * and this allows someone to do something like * * CFLAGS = $(.INCLUDES) * CFLAGS := -I.. $(CFLAGS) * * And not get an error. */ Boolean oldOldVars = oldVars; oldVars = FALSE; cp = Var_Subst(NULL, cp, ctxt, FALSE); oldVars = oldOldVars; Var_Set(line, cp, ctxt); free(cp); } else if (type == VAR_SHELL) { char *args[4]; /* Args for invoking the shell */ int fds[2]; /* Pipe streams */ int cpid; /* Child PID */ int pid; /* PID from wait() */ Boolean freeCmd; /* TRUE if the command needs to be freed, i.e. * if any variable expansion was performed */ /* * Avoid clobbered variable warnings by forcing the compiler * to ``unregister'' variables */ #if __GNUC__ (void) &freeCmd;#endif /* * Set up arguments for shell */ args[0] = "sh"; args[1] = "-c"; if (strchr(cp, '$') != (char *)NULL) { /* * There's a dollar sign in the command, so perform variable * expansion on the whole thing. The resulting string will need * freeing when we're done, so set freeCmd to TRUE. */ args[2] = Var_Subst(NULL, cp, VAR_CMD, TRUE); freeCmd = TRUE; } else { args[2] = cp; freeCmd = FALSE; } args[3] = (char *)NULL; /* * Open a pipe for fetching its output */ pipe(fds); /* * Fork */ cpid = vfork(); if (cpid == 0) { /* * Close input side of pipe */ close(fds[0]); /* * Duplicate the output stream to the shell's output, then * shut the extra thing down. Note we don't fetch the error * stream...why not? Why? */ dup2(fds[1], 1); close(fds[1]); execv("/bin/sh", args); _exit(1); } else if (cpid < 0) { /* * Couldn't fork -- tell the user and make the variable null */ Parse_Error(PARSE_WARNING, "Couldn't exec \"%s\"", cp); Var_Set(line, "", ctxt); } else { int status; int cc; Buffer buf; char *res; /* * No need for the writing half */ close(fds[1]); buf = Buf_Init (MAKE_BSIZE); do { char result[BUFSIZ]; cc = read(fds[0], result, sizeof(result)); if (cc > 0) Buf_AddBytes(buf, cc, (unsigned char *) result); } while (cc > 0 || (cc == -1 && errno == EINTR)); /* * Close the input side of the pipe. */ close(fds[0]); /* * Wait for the process to exit. */ while(((pid = wait(&status)) != cpid) && (pid >= 0)) continue; res = (char *)Buf_GetAll (buf, &cc); Buf_Destroy (buf, FALSE); if (cc == 0) { /* * Couldn't read the child's output -- tell the user and * set the variable to null */ Parse_Error(PARSE_WARNING, "Couldn't read shell's output"); } if (status) { /* * Child returned an error -- tell the user but still use * the result. */ Parse_Error(PARSE_WARNING, "\"%s\" returned non-zero", cp); } /* * Null-terminate the result, convert newlines to spaces and * install it in the variable. */ res[cc] = '\0'; cp = &res[cc] - 1; if (*cp == '\n') { /* * A final newline is just stripped */ *cp-- = '\0'; } while (cp >= res) { if (*cp == '\n') { *cp = ' '; } cp--; } Var_Set(line, res, ctxt);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -