📄 cccp.c
字号:
switch (c) { case '\\': if (escaped == 0) goto randomchar; if (*ip->bufp != '\n') { escaped = 1; goto randomchar; } /* always merge lines ending with backslash-newline */ ++ip->bufp; ++ip->lineno; ++excess_newlines; --op->bufp; /* remove backslash from obuf */ continue; /* back to top of while loop */ case '#': /* # keyword: a # must be first nonblank char on the line */ for (bp = ip->bufp - 1; bp >= ip->buf; bp--) if (*bp == '\n') break; bp++; /* skip nl or move back into buffer */ SKIP_WHITE_SPACE (bp); if (*bp != '#') goto randomchar; ident_length = hash = 0; --op->bufp; /* don't copy the '#' */ if (handle_directive (ip, op, &excess_newlines) == NULL) { ++op->bufp; /* copy the '#' after all */ goto randomchar; } break; case '\"': /* skip quoted string */ case '\'': /* a single quoted string is treated like a double -- some programs (e.g., troff) are perverse this way */ if (escaped == 0) goto randomchar; /* false alarm-- it's escaped. */ /* skip ahead to a matching quote. */ bp = ip->bufp; while (bp < limit) { *op->bufp++ = *bp; switch (*bp++) { case '\n': ++ip->lineno; break; case '\\': if (bp >= limit) break; if (*bp == '\n') { /* backslash newline is replaced by nothing at all, but remember that the source line count is out of synch. */ --op->bufp; ++bp; ++excess_newlines; ++ip->lineno; } else *op->bufp++ = *bp++; break; case '\"': case '\'': if (bp[-1] == c) goto while2end; break; } } while2end: ip->bufp = bp; break; case '/': /* possible comment */ if (*ip->bufp != '*') goto randomchar; if (put_out_comments) { bp = ip->bufp; *op->bufp++ = *bp++; } else { bp = ip->bufp + 1; --op->bufp; /* don't leave initial slash in buffer */ } for (;;) { while (bp < limit) { if (put_out_comments) *op->bufp++ = *bp; switch (*bp++) { case '*': goto whileend; case '\n': /* copy the newline into the output buffer, in order to avoid the pain of a #line every time a multiline comment is seen. This means keywords with embedded comments that contain newlines (blucch!) will lose. By making sure that excess_newlines is not just a flag, but really an accurate count, it might be possible to get around this. */ if (!put_out_comments) *op->bufp++ = '\n'; ++ip->lineno; } } whileend: if (bp >= limit) { error ("unterminated comment"); break; /* causes eof condition */ } if (*bp == '/') break; } if (put_out_comments) *op->bufp++ = '/'; ip->bufp = bp + 1; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': /* if digit is not part of identifier, it is random */ if (ident_length == 0) goto randomchar; /* fall through */ case '_': case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h': case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o': case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v': case 'w': case 'x': case 'y': case 'z': case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L': case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S': case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z': ident_length++; /* compute step of hash function, to avoid a proc call on every token */ hash = HASHSTEP(hash, c); break; default:randomchar: if (ident_length > 0) { register HASHNODE *hp; for (hp = hashtab[MAKE_POS(hash) % HASHSIZE]; hp != NULL; hp = hp->next) { U_CHAR *save_ibufp; /* kludge, see below */ if (hp->length == ident_length) { register int i = ident_length; register U_CHAR *p = hp->name; register U_CHAR *q = op->bufp - i; if (c != (U_CHAR) -1) q--; do { /* all this to avoid a strncmp() */ if (*p++ != *q++) goto hashcollision; } while (--i); save_ibufp = ip->bufp; /* back up over identifier, then expand token */ op->bufp -= ident_length; if (c != (U_CHAR) -1) op->bufp--; macroexpand (hp, ip, op, &excess_newlines); check_expand(op, ip->length - (ip->bufp - ip->buf)); /* If we just processed an identifier at end of input, return right away. */ if (c == (U_CHAR) -1) return; /* if the expansion routine has not moved the input pointer, put back the char that ended the token. This is a kludge because there might be a different reason to put it back or not put it back. */ if (ip->bufp == save_ibufp) *op->bufp++ = c; break; /* out of for loop */ }hashcollision: ; } /* end for loop */ ident_length = hash = 0; /* stop collecting identifier */ } /* If we just processed an identifier at end of input, return right away. */ if (c == -1) return; /* count the newline, if it was one. The reason this is done down here instead of as a case in the switch is that some expansions might want to look at the line number, and if they happen right before the newline, we don't want them to get the wrong one. So the newline must be counted AFTER any expansions happen. */ if (c == '\n') { ++ip->lineno; if (excess_newlines > 0) { output_line_command (ip, op); check_expand(op, ip->length - (ip->bufp - ip->buf)); excess_newlines = 0; } } break; /* from switch */ } }}/* * Process a # directive. Expects ip->bufp to point to the '#', as in * "#define foo bar". Bumps *excess_newlines counter as necessary if * the command is several lines long (and also updates ip->lineno). * The main reason for this is that the comments could contain * newlines, which would be confusing. Passes to the command handler * (do_define, do_include, etc.): the addresses of the 1st and * last chars of the command (starting immediately after the # * keyword), plus op and the keyword table pointer. If the line * contains comments the command is copied into a temporary buffer * (sans comments) and the temporary buffer is passed to the command * handler instead. */struct keyword_table *handle_directive (ip, op, excess_newlines) FILE_BUF *ip, *op; int *excess_newlines;{ register U_CHAR *bp, *cp; register struct keyword_table *kt; register int ident_length; /* Nonzero means we must copy the entire command to get rid of comments or backslash-newlines. */ int copy_command = 0; bp = ip->bufp; SKIP_WHITE_SPACE(bp); cp = bp; while (is_idchar[*cp]) cp++; ident_length = cp - bp; /* * Decode the keyword and call the appropriate expansion * routine, after moving the input pointer up to the next line. * If the keyword is not a legitimate control word, return NULL. * Otherwise, return ptr to the keyword structure matched. */ for (kt = keyword_table; kt->length > 0; kt++) { if (kt->length == ident_length && !strncmp(kt->name, bp, ident_length)) { register U_CHAR *buf; register U_CHAR *limit = ip->buf + ip->length; U_CHAR *skip_to_end_of_comment(); buf = bp = bp + ident_length; while (bp < limit) { if (*bp == '\'' || *bp == '\"') { /* JF handle quotes right */ U_CHAR quotec; for (quotec = *bp++; bp < limit && *bp != quotec; bp++) { if (*bp == '\\') bp++; if (*bp == '\n') { if (bp[-1] == '\\') copy_command++; else { /* --bp; */ break; /* JF ugly, but might work */ } } } continue; } if (*bp == '/' && bp[1] == '*') { copy_command++; ip->bufp = bp + 2; skip_to_end_of_comment (ip, NULL); bp = ip->bufp; continue; } if (*bp++ == '\n') { if (*(bp-2) == '\\') copy_command++; else { --bp; /* point to the newline */ break; } } } if (copy_command) { /* need to copy entire command into temp buffer before dispatching */ cp = (U_CHAR *) alloca (bp - buf + 5); /* room for cmd plus some slop */ bp = buf; buf = cp; while (bp < limit) { if (*bp == '\'' || *bp == '\"') { /* JF handle quotes right */ U_CHAR quotec; *cp++ = *bp; for (quotec = *bp++; bp < limit && *bp != quotec; *cp++ = *bp++) { if (*bp == '\\') *cp++ = *bp++; if (*bp == '\n') { if (bp[-1] == '\\') { ++ip->lineno; ++*excess_newlines; } else break; /* JF ugly, but might work */ } } continue; } if (*bp == '/' && bp[1] == '*') { int newlines_found = 0; ip->bufp = bp + 2; skip_to_end_of_comment (ip, &newlines_found); *excess_newlines += newlines_found; ip->lineno += newlines_found; bp = ip->bufp; continue; } if (*bp == '\n') { if (bp[-1] == '\\') { ++ip->lineno; ++*excess_newlines; } else break; } *cp++ = *bp++; } } else cp = bp; ip->bufp = bp; /* skip to the end of the command */ /* call the appropriate command handler. Buf now points to either the appropriate place in the input buffer, or to the temp buffer if it was necessary to make one. Cp points to the first char after the contents of the (possibly copied) command, in either case. */ (*kt->func) (buf, cp, op, kt); check_expand (op, ip->length - (ip->bufp - ip->buf)); break; } } if (kt->length <= 0) kt = NULL; return kt;}static char *monthnames[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", };/* * expand things like __FILE__. Place the expansion into the output * buffer *without* rescanning. */expand_special_symbol (hp, ip, op) HASHNODE *hp; FILE_BUF *ip, *op;{ char *buf; int i, len; FILE_BUF *last_ip = NULL; static struct tm *timebuf = NULL; struct tm *localtime(); int paren = 0; /* for special `defined' keyword */ HASHNODE *lookup(); for (i = indepth - 1; i >= 0; i--) if (instack[i].fname != NULL) { last_ip = &instack[i]; break; } if (last_ip == NULL) { error("CCCP error: not in any file?!"); return; /* the show must go on */ } switch (hp->type) { case T_FILE: buf = (char *) alloca (3 + strlen(last_ip->fname)); sprintf (buf, "\"%s\"", last_ip->fname); break; case T_SPECLINE: buf = (char *) alloca (10); sprintf (buf, "%d", last_ip->lineno); break; case T_DATE: case T_TIME: if (timebuf == NULL) { i = time(0); timebuf = localtime(&i); } buf = (char *) alloca (20); if (hp->type == T_DATE) sprintf(buf, "\"%s %2d %4d\"", monthnames[timebuf->tm_mon - 1], timebuf->tm_mday, timebuf->tm_year + 1900); else sprintf(buf, "\"%02d:%02d:%02d\"", timebuf->tm_hour, timebuf->tm_min, timebuf->tm_sec); break; case T_SPEC_DEFINED: buf = " 0 "; /* assume symbol is not defined */ if (is_hor_space[*(ip->bufp-1)]) { SKIP_WHITE_SPACE(ip->bufp); if (*ip->bufp == '(') { paren++; ip->bufp++; /* skip over the paren */ } } else if (*(ip->bufp-1) == '(') paren++; if (!is_idstart[*ip->bufp]) goto oops; if (lookup(ip->bufp)) buf = " 1 "; while (is_idchar[*ip->bufp]) ++ip->bufp; SKIP_WHITE_SPACE (ip->bufp); if (paren) { if (*ip->bufp != ')') goto oops; ++ip->bufp; } break; oops: error ("`defined' must be followed by IDENT or (IDENT)"); break; default: error("CCCP error: illegal special hash type"); /* time for gdb */ abort (); } len = strlen(buf); check_expand(op, len); bcopy (buf, op->bufp, len); op->bufp += len; return;}/* routines to handle #directives *//* * process include file by reading it in and calling rescan. * expects to see "fname" or <fname> on the input. * add error checking and -I option later. */do_include (buf, limit, op, keyword) U_CHAR *buf, *limit; FILE_BUF *op; struct keyword_table *keyword;{ char *fname; /* dynamically allocated fname buffer */ U_CHAR *fbeg, *fend; /* beginning and end of fname */ U_CHAR term; /* terminator for fname */ int err = 0; /* some error has happened */ struct stat sbuf; /* to stat the include file */ FILE_BUF *fp; /* for input stack frame */ struct directory_stack *stackp; int flen; int save_indepth = indepth; /* in case of errors */ int f; /* file number */ char *other_dir; /* JF */ f= -1; /* JF we iz PARANOID! */ fbeg = buf; SKIP_WHITE_SPACE(fbeg); switch (*fbeg++) { case '\"':
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -