📄 ctags.c
字号:
/* ctags.c *//* This is a reimplementation of the ctags(1) program. It supports ANSI C, * and has heaps o' flags. It is meant to be distributed with elvis. */#include <stdio.h>#include "config.h"#ifndef FALSE# define FALSE 0# define TRUE 1#endif#ifndef TAGS# define TAGS "tags"#endif#ifndef REFS# define REFS "refs"#endif#ifndef BLKSIZE# define BLKSIZE 1024#endif#include "ctype.c" /* yes, that really is the .c file, not the .h one. *//* -------------------------------------------------------------------------- *//* Some global variables *//* The following boolean variables are set according to command line flags */int incl_static; /* -s include static tags */int incl_types; /* -t include typedefs and structs */int incl_vars; /* -v include variables */int make_refs; /* -r generate a "refs" file */int append_files; /* -a append to "tags" [and "refs"] files *//* The following are used for outputting to the "tags" and "refs" files */FILE *tags; /* used for writing to the "tags" file */FILE *refs; /* used for writing to the "refs" file *//* -------------------------------------------------------------------------- *//* These are used for reading a source file. It keeps track of line numbers */char *file_name; /* name of the current file */FILE *file_fp; /* stream used for reading the file */long file_lnum; /* line number in the current file */long file_seek; /* fseek() offset to the start of current line */int file_afternl; /* boolean: was previous character a newline? */int file_prevch; /* a single character that was ungotten */int file_header; /* boolean: is the current file a header file? *//* This function opens a file, and resets the line counter. If it fails, it * it will display an error message and leave the file_fp set to NULL. */void file_open(name) char *name; /* name of file to be opened */{ /* if another file was already open, then close it */ if (file_fp) { fclose(file_fp); } /* try to open the file for reading. The file must be opened in * "binary" mode because otherwise fseek() would misbehave under DOS. */#if MSDOS || TOS file_fp = fopen(name, "rb");#else file_fp = fopen(name, "r");#endif if (!file_fp) { perror(name); } /* reset the name & line number */ file_name = name; file_lnum = 0L; file_seek = 0L; file_afternl = TRUE; /* determine whether this is a header file */ file_header = FALSE; name += strlen(name) - 2; if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H')) { file_header = TRUE; }}/* This function reads a single character from the stream. If the *previous* * character was a newline, then it also increments file_lnum and sets * file_offset. */int file_getc(){ int ch; /* if there is an ungotten character, then return it. Don't do any * other processing on it, though, because we already did that the * first time it was read. */ if (file_prevch) { ch = file_prevch; file_prevch = 0; return ch; } /* if previous character was a newline, then we're starting a line */ if (file_afternl) { file_afternl = FALSE; file_seek = ftell(file_fp); file_lnum++; } /* Get a character. If no file is open, then return EOF */ ch = (file_fp ? getc(file_fp) : EOF); /* if it is a newline, then remember that fact */ if (ch == '\n') { file_afternl = TRUE; } /* return the character */ return ch;}/* This function ungets a character from the current source file */void file_ungetc(ch) int ch; /* character to be ungotten */{ file_prevch = ch;}/* This function copies the current line out some other fp. It has no effect * on the file_getc() function. During copying, any '\' characters are doubled * and a leading '^' or trailing '$' is also quoted. The newline character is * not copied. * * This is meant to be used when generating a tag line. */void file_copyline(seek, fp) long seek; /* where the lines starts in the source file */ FILE *fp; /* the output stream to copy it to */{ long oldseek;/* where the file's pointer was before we messed it up */ char ch; /* a single character from the file */ char next; /* the next character from this file */ /* go to the start of the line */ oldseek = ftell(file_fp); fseek(file_fp, seek, 0); /* if first character is '^', then emit \^ */ ch = getc(file_fp); if (ch == '^') { putc('\\', fp); putc('^', fp); ch = getc(file_fp); } /* write everything up to, but not including, the newline */ while (ch != '\n') { /* preread the next character from this file */ next = getc(file_fp); /* if character is '\', or a terminal '$', then quote it */ if (ch == '\\' || (ch == '$' && next == '\n')) { putc('\\', fp); } putc(ch, fp); /* next character... */ ch = next; } /* seek back to the old position */ fseek(file_fp, oldseek, 0);}/* -------------------------------------------------------------------------- *//* This section handles preprocessor directives. It strips out all of the * directives, and may emit a tag for #define directives. */int cpp_afternl; /* boolean: look for '#' character? */int cpp_prevch; /* an ungotten character, if any */int cpp_refsok; /* boolean: can we echo characters out to "refs"? *//* This function opens the file & resets variables */void cpp_open(name) char *name; /* name of source file to be opened */{ /* use the lower-level file_open function to open the file */ file_open(name); /* reset variables */ cpp_afternl = TRUE; cpp_refsok = TRUE;}/* This function copies a character from the source file to the "refs" file */void cpp_echo(ch) int ch; /* the character to copy */{ static wasnl; /* echo non-EOF chars, unless not making "ref", or echo turned off */ if (ch != EOF && make_refs && cpp_refsok && !file_header) { /* try to avoid blank lines */ if (ch == '\n') { if (wasnl) { return; } wasnl = TRUE; } else { wasnl = FALSE; } /* add the character */ putc(ch, refs); }}/* This function returns the next character which isn't part of a directive */int cpp_getc(){ static int ch; /* the next input character */ char *scan; /* if we have an ungotten character, then return it */ if (cpp_prevch) { ch = cpp_prevch; cpp_prevch = 0; return ch; } /* Get a character from the file. Return it if not special '#' */ ch = file_getc(); if (ch == '\n') { cpp_afternl = TRUE; cpp_echo(ch); return ch; } else if (ch != '#' || !cpp_afternl) { /* normal character. Any non-whitespace should turn off afternl */ if (ch != ' ' && ch != '\t') { cpp_afternl = FALSE; } cpp_echo(ch); return ch; } /* Yikes! We found a directive */ /* see whether this is a #define line */ scan = " define "; while (*scan) { if (*scan == ' ') { /* space character matches any whitespace */ do { ch = file_getc(); } while (ch == ' ' || ch == '\t'); file_ungetc(ch); } else { /* other characters should match exactly */ ch = file_getc(); if (ch != *scan) { file_ungetc(ch); break; } } scan++; } /* is this a #define line? and should we generate a tag for it? */ if (!*scan && (file_header || incl_static)) { /* if not a header, then this will be a static tag */ if (!file_header) { fputs(file_name, tags); putc(':', tags); } /* output the tag name */ for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc()) { putc(ch, tags); } /* output a tab, the filename, another tab, and the line number */ fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum); } /* skip to the end of the directive -- a newline that isn't preceded * by a '\' character. */ while (ch != EOF && ch != '\n') { if (ch == '\\') { ch = file_getc(); } ch = file_getc(); } /* return the newline that we found at the end of the directive */ cpp_echo(ch); return ch;}/* This puts a character back into the input queue for the source file */cpp_ungetc(ch) int ch; /* a character to be ungotten */{ cpp_prevch = ch;}/* -------------------------------------------------------------------------- *//* This is the lexical analyser. It gets characters from the preprocessor, * and gives tokens to the parser. Some special codes are... * (deleted) /*...* / (comments) * (deleted) //...\n (comments) * (deleted) (* (parens used in complex declaration) * (deleted) [...] (array subscript, when ... contains no ]) * (deleted) struct (intro to structure declaration) * BODY {...} ('{' can occur anywhere, '}' only at BOW if ... has '{') * ARGS (...{ (args of function, not extern or forward) * ARGS (...); (args of an extern/forward function declaration) * COMMA , (separate declarations that have same scope) * SEMICOLON ; (separate declarations that have different scope) * SEMICOLON =...; (initializer) * TYPEDEF typedef (the "typedef" keyword) * STATIC static (the "static" keyword) * STATIC private (the "static" keyword) * STATIC PRIVATE (the "static" keyword) * NAME [a-z]+ (really any valid name that isn't reserved word) *//* #define EOF -1 */#define DELETED 0#define BODY 1#define ARGS 2#define COMMA 3#define SEMICOLON 4#define TYPEDEF 5#define STATIC 6#define EXTERN 7#define NAME 8char lex_name[BLKSIZE]; /* the name of a "NAME" token */long lex_seek; /* start of line that contains lex_name */lex_gettoken(){ int ch; /* a character from the preprocessor */ int next; /* the next character */ int token; /* the token that we'll return */ int i; /* loop until we get a token that isn't "DELETED" */ do { /* get the next character */ ch = cpp_getc(); /* process the character */ switch (ch) { case ',': token = COMMA; break; case ';': token = SEMICOLON; break; case '/': /* get the next character */ ch = cpp_getc(); switch (ch) { case '*': /* start of C comment */ ch = cpp_getc(); next = cpp_getc(); while (next != EOF && (ch != '*' || next != '/')) { ch = next;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -