📄 parse.c
字号:
* Side Effects: *--------------------------------------------------------------------- */static intParseReadc(){ if (curFILE) return fgetc(curFILE); if (curPTR && *curPTR->ptr) return *curPTR->ptr++; return EOF;}/*- *--------------------------------------------------------------------- * ParseUnreadc -- * Put back a character to the current file * * Results: * None. * * Side Effects: *--------------------------------------------------------------------- */static voidParseUnreadc(c) int c;{ if (curFILE) { ungetc(c, curFILE); return; } if (curPTR) { *--(curPTR->ptr) = c; return; }}/* ParseSkipLine(): * Grab the next line */static char *ParseSkipLine(skip) int skip; /* Skip lines that don't start with . */{ char *line; int c, lastc = '\0', lineLength; Buffer buf; c = ParseReadc(); if (skip) { /* * Skip lines until get to one that begins with a * special char. */ while ((c != '.') && (c != EOF)) { while (((c != '\n') || (lastc == '\\')) && (c != EOF)) { /* * Advance to next unescaped newline */ if ((lastc = c) == '\n') { lineno++; } c = ParseReadc(); } lineno++; lastc = c; c = ParseReadc (); } } if (c == EOF) { Parse_Error (PARSE_FATAL, "Unclosed conditional/for loop"); return ((char *)NULL); } /* * Read the entire line into buf */ buf = Buf_Init (MAKE_BSIZE); if (c != '\n') { do { Buf_AddByte (buf, (Byte)c); c = ParseReadc(); } while ((c != '\n') && (c != EOF)); } lineno++; Buf_AddByte (buf, (Byte)'\0'); line = (char *)Buf_GetAll (buf, &lineLength); Buf_Destroy (buf, FALSE); return line;}/*- *--------------------------------------------------------------------- * ParseReadLine -- * Read an entire line from the input file. Called only by Parse_File. * To facilitate escaped newlines and what have you, a character is * buffered in 'lastc', which is '\0' when no characters have been * read. When we break out of the loop, c holds the terminating * character and lastc holds a character that should be added to * the line (unless we don't read anything but a terminator). * * Results: * A line w/o its newline * * Side Effects: * Only those associated with reading a character *--------------------------------------------------------------------- */static char *ParseReadLine (){ Buffer buf; /* Buffer for current line */ register int c; /* the current character */ register int lastc; /* The most-recent character */ Boolean semiNL; /* treat semi-colons as newlines */ Boolean ignDepOp; /* TRUE if should ignore dependency operators * for the purposes of setting semiNL */ Boolean ignComment; /* TRUE if should ignore comments (in a * shell command */ char *line; /* Result */ int lineLength; /* Length of result */ semiNL = FALSE; ignDepOp = FALSE; ignComment = FALSE; /* * Handle special-characters at the beginning of the line. Either a * leading tab (shell command) or pound-sign (possible conditional) * forces us to ignore comments and dependency operators and treat * semi-colons as semi-colons (by leaving semiNL FALSE). This also * discards completely blank lines. */ for (;;) { c = ParseReadc(); if (c == '\t') { ignComment = ignDepOp = TRUE; break; } else if (c == '\n') { lineno++; } else if (c == '#') { ParseUnreadc(c); break; } else { /* * Anything else breaks out without doing anything */ break; } } if (c != EOF) { lastc = c; buf = Buf_Init(MAKE_BSIZE); while (((c = ParseReadc ()) != '\n' || (lastc == '\\')) && (c != EOF)) {test_char: switch(c) { case '\n': /* * Escaped newline: read characters until a non-space or an * unescaped newline and replace them all by a single space. * This is done by storing the space over the backslash and * dropping through with the next nonspace. If it is a * semi-colon and semiNL is TRUE, it will be recognized as a * newline in the code below this... */ lineno++; lastc = ' '; while ((c = ParseReadc ()) == ' ' || c == '\t') { continue; } if (c == EOF || c == '\n') { goto line_read; } else { /* * Check for comments, semiNL's, etc. -- easier than * ParseUnreadc(c); continue; */ goto test_char; } /*NOTREACHED*/ break; case ';': /* * Semi-colon: Need to see if it should be interpreted as a * newline */ if (semiNL) { /* * To make sure the command that may be following this * semi-colon begins with a tab, we push one back into the * input stream. This will overwrite the semi-colon in the * buffer. If there is no command following, this does no * harm, since the newline remains in the buffer and the * whole line is ignored. */ ParseUnreadc('\t'); goto line_read; } break; case '=': if (!semiNL) { /* * Haven't seen a dependency operator before this, so this * must be a variable assignment -- don't pay attention to * dependency operators after this. */ ignDepOp = TRUE; } else if (lastc == ':' || lastc == '!') { /* * Well, we've seen a dependency operator already, but it * was the previous character, so this is really just an * expanded variable assignment. Revert semi-colons to * being just semi-colons again and ignore any more * dependency operators. * * XXX: Note that a line like "foo : a:=b" will blow up, * but who'd write a line like that anyway? */ ignDepOp = TRUE; semiNL = FALSE; } break; case '#': if (!ignComment) { if (compatMake || (lastc != '\\')) { /* * If the character is a hash mark and it isn't escaped * (or we're being compatible), the thing is a comment. * Skip to the end of the line. */ do { c = ParseReadc(); } while ((c != '\n') && (c != EOF)); goto line_read; } else { /* * Don't add the backslash. Just let the # get copied * over. */ lastc = c; continue; } } break; case ':': case '!': if (!ignDepOp && (c == ':' || c == '!')) { /* * A semi-colon is recognized as a newline only on * dependency lines. Dependency lines are lines with a * colon or an exclamation point. Ergo... */ semiNL = TRUE; } break; } /* * Copy in the previous character and save this one in lastc. */ Buf_AddByte (buf, (Byte)lastc); lastc = c; } line_read: lineno++; if (lastc != '\0') { Buf_AddByte (buf, (Byte)lastc); } Buf_AddByte (buf, (Byte)'\0'); line = (char *)Buf_GetAll (buf, &lineLength); Buf_Destroy (buf, FALSE); if (line[0] == '.') { /* * The line might be a conditional. Ask the conditional module * about it and act accordingly */ switch (Cond_Eval (line)) { case COND_SKIP: /* * Skip to next conditional that evaluates to COND_PARSE. */ do { free (line); line = ParseSkipLine(1); } while (line && Cond_Eval(line) != COND_PARSE); if (line == NULL) break; /*FALLTHRU*/ case COND_PARSE: free ((Address) line); line = ParseReadLine(); break; case COND_INVALID: if (For_Eval(line)) { int ok; free(line); do { /* * Skip after the matching end */ line = ParseSkipLine(0); if (line == NULL) { Parse_Error (PARSE_FATAL, "Unexpected end of file in for loop.\n"); break; } ok = For_Eval(line); free(line); } while (ok); if (line != NULL) For_Run(); line = ParseReadLine(); } break; } } return (line); } else { /* * Hit end-of-file, so return a NULL line to indicate this. */ return((char *)NULL); }}/*- *----------------------------------------------------------------------- * ParseFinishLine -- * Handle the end of a dependency group. * * Results: * Nothing. * * Side Effects: * inLine set FALSE. 'targets' list destroyed. * *----------------------------------------------------------------------- */static voidParseFinishLine(){ extern int Suff_EndTransform(); if (inLine) { Lst_ForEach(targets, Suff_EndTransform, (ClientData)NULL); Lst_Destroy (targets, ParseHasCommands); inLine = FALSE; }} /*- *--------------------------------------------------------------------- * Parse_File -- * Parse a file into its component parts, incorporating it into the * current dependency graph. This is the main function and controls * almost every other function in this module * * Results: * None * * Side Effects: * Loads. Nodes are added to the list of all targets, nodes and links * are added to the dependency graph. etc. etc. etc. *--------------------------------------------------------------------- */voidParse_File(name, stream) char *name; /* the name of the file being read */ FILE * stream; /* Stream open to makefile to parse */{ register char *cp, /* pointer into the line */ *line; /* the line we're working on */ inLine = FALSE; fname = name; curFILE = stream; lineno = 0; fatals = 0; do { while ((line = ParseReadLine ()) != NULL) { if (*line == '.') { /* * Lines that begin with the special character are either * include or undef directives. */ for (cp = line + 1; isspace (*cp); cp++) { continue; } if (strncmp (cp, "include", 7) == 0) { ParseDoInclude (cp + 7); goto nextLine; } else if (strncmp(cp, "undef", 5) == 0) { char *cp2; for (cp += 5; isspace(*cp); cp++) { continue; } for (cp2 = cp; !isspace(*cp2) && (*cp2 != '\0'); cp2++) { continue; } *cp2 = '\0'; Var_Delete(cp, VAR_GLOBAL); goto nextLine; } } if (*line == '#') { /* If we're this far, the line must be a comment. */ goto nextLine; } if (*line == '\t'#ifdef POSIX || *line == ' '#endif ) { /* * If a line starts with a tab (or space in POSIX-land), it * can only hope to be a creation command. */ shellCommand: for (cp = line + 1; isspace (*cp); cp++) { continue; } if (*cp) { if (inLine) { /* * So long as it's not a blank line and we're actually * in a dependency spec, add the command to the list of * commands of all targets in the dependency spec */ Lst_ForEach (targets, ParseAddCmd, (ClientData)cp); continue; } else { Parse_Error (PARSE_FATAL, "Unassociated shell command \"%.20s\"", cp); } }#ifdef SYSVINCLUDE } else if (strncmp (line, "include", 7) == 0 && strchr(line, ':') == NULL) { /* * It's an S3/S5-style "include". */ ParseTraditionalInclude (line + 7); goto nextLine;#endif } else if (Parse_IsVar (line)) { ParseFinishLine(); Parse_DoVar (line, VAR_GLOBAL); } else { /* * We now know it's a dependency line so it needs to have all * variables expanded before being parsed. Tell the variable * module to complain if some variable is undefined... * To make life easier on novices, if the line is indented we * first make sure the line has a dependency operator in it. * If it doesn't have an operator and we're in a dependency * line's script, we assume it's actually a shell command
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -