📄 mref.c
字号:
/* mref - MINIX cross referencer Author: Andy Tanenbaum *//* Mref is intended for producing listings and cross reference maps of * MINIX. The default is to produce three outputs: * * on stdout: numbered listing of all the files given as args * on symbol.out: list of all "global" symbols and their definition line * on xref.out: cross reference map of all references to global symbols * * For this program, a global symbol is one that is defined as PUBLIC, PRIVATE, * EXTERN, #define, or SYMBOL. The latter is provided to allow users to force * symbols x, y, and z to "global" status but simply making up a file with * * SYMBOL x * SYMBOL y * SYMBOL z * * The numbering is consecutive across files, i.e., the second file begins * where the first one left off, except that each file begins at the top of * a new page, and the line number is rounded upward to the next page. * The three types of output can each be suppressed, using the -l, -d, and -x * flags, respectively. Line numbering between procedures may be optionally * suppressed with -s, as in the MINIX book. Page length and numbering can be * user controlled with flags. * * Normally output is designed for a dumb line printer, but the -t flag causes * the output to be enhanced with macros and font control so it can be fed to * troff (or nroff). If troff form, the symbol table is printed in three * columns, the first page being shorter to allow for the chapter opening. * * Flags: * -# Number of lines to print per page, default = 50 * -d Don't produce definition file (symbol table) * -l Don't produce listing * -m Multiple reference on one line only are cited once * -p n Set initial page number to n * -t Generate troff macro call before each page * -s Suppress line numbering between procedures * -x Don't produce the cross reference map * * Examples: * mref -60 *.c # print 60 lines per page (default is 50) * mref -t -p 50 *.c # first page is 50; generate .Ep and .Op macros * mref -l -d *.h *.c # no listing or symbols, just cross ref map */#include <sys/types.h>#include <ctype.h>#include <fcntl.h>#include <stdlib.h>#include <string.h>#include <unistd.h>#include <sys/wait.h>#include <stdio.h>#define LINE_SIZE 512#define PHYS_PAGE 66 /* physical page length */#define DEF_LEN 50 /* default # lines/page */#define SYM_SIZE 13 /* symbols are truncated to this size */#define HDR_SIZE 5 /* number of lines in the default header */#define NSYMS 2000 /* size of the symbol table */#define NTOKENS 100 /* maximum number of tokens per line */#define MAX_VALUES 100 /* maximum times a name can be declared */#define MAX_PER_LINE 9 /* number of cross refs to print per line */#define NCOLS 3 /* number of columns for troffed symbol.out */#define SYM_FILE "symbol.out" /* place to write the symbol table */#define XREF_FILE "xref.out" /* place to write cross ref map */#define TMP_FILE "xref.tmp" /* temporary xref file for gathering refs */#define SORTED_FILE "xref.sort" /* temporary xref file for sorting */#define DEF 1 /* this line contains #define */#define EXT 2 /* this line contains EXTERN */#define PRIV 3 /* this line contains PRIVATE */#define PUB 4 /* this line contains PUBLIC */#define SYM 5 /* this line contains SYMBOL */int listing = 1; /* 1 = output listing; 0: don't */int xref = 1; /* 1 = output cross ref map; 0: don't */int definitions = 1; /* 1 = output definitions; 0: don't */int sflag = 0; /* 1 = number all lines; 0: most */int tflag = 0; /* 1 = troff output; 0: flat */int mflag = 0; /* 1 = max one ref/line 0: many */int page_len = DEF_LEN; /* # lines to print per page */int phys_page = PHYS_PAGE; /* total page size: top hdr+text+bottom hdr */int cur_line; /* current line number */int cur_page = 1; /* current page number */int nsymbols; /* number of symbols in symbol table */int ntokens; /* number of tokens in the token[] array */int suppressing; /* don't print numbers between procedures */int hunting; /* set when hunting for end of declaration */int comment; /* set when scanning a comment */int xcount; /* number of references so far to this sym */int prev_ref; /* previous reference printed */int stride; /* length of first symbol table page */int stride2; /* length of second symbol table page */char *prog_name; /* argv[0] */char line_buf[LINE_SIZE]; /* buffer for 1 line */char b[LINE_SIZE]; /* processed line goes here */char *token[NTOKENS]; /* array of pointers to tokens *//* Symbol table. If a symbol is defined multiple times, it will get as many * entries in the symbol table as there are definitions. */struct symtab { char sym_name[SYM_SIZE + 1]; /* symbol, followed by at least one '\0' */ char sym_type; /* PUB, PRIV, EXT, SYM, or DEF */ int sym_val; /* line number on which symbol occurs */} symtab[NSYMS];char *ppmap[] = {"", "#define", "EXTERN ", "PRIVATE", "PUBLIC ", "SYMBOL "};char *ppmap2[] = {"", "#def", "EXTN", "PRIV", "PUBL", "SYMB"};char spaces[] = " ";char *xref_text[] = { ".nf\n", ".tr _\\(ru\n", ".ta 1.25i 2iR 2.45iR 2.9iR 3.35iR 3.8iR 4.25iR 4.7iR 5.15iR 5.6iR 6.05iR\n", (char *) 0};char *list_text[] = { ".po .5i\n", ".lg 0\n", ".nf\n", ".ec `\n", ".ps 10\n", ".vs 12p\n", "`f(lb\n", ".nr T `w'0'\n", ".ta 8u*`nTu 16u*`nTu 24u*`nTu 32u*`nTu 40u*`nTu 48u*`nTu 56u*`nTu 64u*`nTu 72u*`nTu 80u*`nTu\n", ".de Op \n", ".bp ``$1\n", ".sp 0.5i\n", ".tl '``fR``s10MINIX SOURCE CODE``s0'``s11File: ``$2``s0``fP'``fB``s12``n%%``s0``fP'\n", ".sp 1\n", "``f(lb\n", "..\n", ".de Ep \n", ".bp ``$1\n", ".sp 0.5i\n", ".tl '``fB``s12``n%%``s0``fP``fR'``s11File: ``$2'``s0``s10MINIX SOURCE CODE``s0``fP'\n", ".sp 1\n", "``f(lb\n", "..\n", (char *) 0};char *sym_text[] = { ".nf\n", ".tr _\\(ru\n", ".ta 1.6iR 2.0i 3.6iR 4.0i 5.6iR\n", (char *) 0};FILE *sym; /* for symbol table file */FILE *xr; /* for cross reference file */FILE *tmp; /* for collecting cross references */FILE *sortf; /* for sorting cross refernces */_PROTOTYPE(int main, (int argc, char **argv));_PROTOTYPE(int eat_flag, (char *argv [], int i));_PROTOTYPE(void make_files, (void));_PROTOTYPE(void process, (char *file));_PROTOTYPE(void list, (char *file));_PROTOTYPE(void get_sym, (void));_PROTOTYPE(void fill_page, (void));_PROTOTYPE(void eject, (void));_PROTOTYPE(void new_page, (char *file));_PROTOTYPE(void squash, (void));_PROTOTYPE(void strip, (char *string, char *s, int n));_PROTOTYPE(char *backup, (char *q));_PROTOTYPE(void enter_sym, (char *p, int value, int type));_PROTOTYPE(int find_slot, (char *p));_PROTOTYPE(int lookup, (char *p));_PROTOTYPE(unsigned int hash, (char *p));_PROTOTYPE(void print_sym, (void));_PROTOTYPE(int compact, (void));_PROTOTYPE(void swap, (struct symtab *ap, struct symtab *bp));_PROTOTYPE(void sort, (int n));_PROTOTYPE(void gen_xref, (char *file));_PROTOTYPE(char *skip_comment, (char *p));_PROTOTYPE(void collect_xref, (void));_PROTOTYPE(int new_name, (char *cur, int used, char *pname, int values []));_PROTOTYPE(void sort_xref, (void));_PROTOTYPE(void output_macros, (FILE *f, char *text []));_PROTOTYPE(void sym_macros, (void));_PROTOTYPE(void xref_macros, (void));_PROTOTYPE(void panic, (char *s));_PROTOTYPE(void usage, (void));int main(argc, argv)int argc;char *argv[];{ int i, j; char *p; prog_name = argv[0]; /* Process the command line. */ if (argc < 2) usage(); for (i = 1; i < argc; i++) { p = argv[i]; if (*p != '-') break; p++; i += eat_flag(argv, i); } stride = page_len / 2; /* allow for chapter opening */ stride2 = page_len; /* Create the output files. */ make_files(); /* Read all the files. Make the listing and extract the symbols. */ for (j = i; j < argc; j++) process(argv[j]); if (xref) { /* Read all the files again and generate the cross * refererence data. */ cur_line = 0; for (j = i; j < argc; j++) gen_xref(argv[j]); /* Go get the cross reference data and print it. */ collect_xref(); } /* Print the symbol table. */ if (definitions) print_sym(); return(0);}int eat_flag(argv, i)char *argv[]; /* argument array */int i; /* process argv[i] */{ char *p; int r = 0; p = argv[i]; p++; if (*p >= '0' && *p <= '9') { page_len = atoi(p); /* e.g. numb -60 file */ return(r); } while (1) { /* Flag may be something like -dls. */ switch (*p) { case 'd': definitions = 0; break; case 'l': listing = 0; break; case 'x': xref = 0; break; case 'm': mflag = 1; break; case 's': sflag = 1; break; case 't': tflag = 1; break; case 'p': cur_page = atoi(argv[i + 1]); i++; r = 1; break; default: usage(); } p++; if (*p == 0) return(r); }}void make_files(){/* Create output files. */ int fd; if (tflag && listing) output_macros(stdout, list_text); if (definitions) { if ((fd = creat(SYM_FILE, 0666)) < 0) { fprintf(stderr, "%s: cannot create %s\n", prog_name, SYM_FILE); exit(2); } close(fd); sym = fopen(SYM_FILE, "w"); if (sym == NULL) { fprintf(stderr, "%s: cannot open %s for output\n", prog_name, SYM_FILE); exit(2); } if (tflag) output_macros(sym, sym_text); } if (xref) { if ((fd = creat(XREF_FILE, 0666)) < 0) { fprintf(stderr,"%s: cannot create %s\n", prog_name, XREF_FILE); exit(2); } close(fd); xr = fopen(XREF_FILE, "w"); if (xr == NULL) { fprintf(stderr, "%s: cannot open %s for output\n", prog_name, XREF_FILE); exit(2); } if (tflag) output_macros(xr, xref_text); if ((fd = creat(TMP_FILE, 0666)) < 0) { fprintf(stderr, "%s: cannot create %s\n", prog_name, TMP_FILE); exit(2); } close(fd); tmp = fopen(TMP_FILE, "w"); if (tmp == NULL) { fprintf(stderr, "%s: cannot open %s for output\n", prog_name, TMP_FILE); exit(2); } }}void process(file)char *file;{/* Process one file. */ FILE *f; if ((f = fopen(file, "r")) == NULL) { fprintf(stderr, "%s: cannot open %s\n", prog_name, file); exit(1); } while (1) { if (fgets(line_buf, LINE_SIZE, f) == NULL) { /* End of file hit. */ fclose(f); if (listing && cur_line % page_len != 0) { fill_page(); /* fill out the e.g. 50 lines */ eject();/* space to top of next sheet */ } return; } if (listing) list(file); else cur_line++; if (definitions || xref) get_sym(); }}void list(file)char *file;{/* We are printing a listing. */ if (cur_line % page_len == 0) new_page(file); if (suppressing == 1 && strlen(line_buf) == 1) printf("\n"); else printf("%5d %s", cur_line, line_buf); cur_line++; if (cur_line % page_len == 0) eject(); if (strlen(line_buf) > 1) suppressing = 0; if (strcmp(line_buf, "}\n") == 0 && sflag) suppressing = 1;}void get_sym(){/* Look for lines starting with PRIVATE, PUBLIC, EXTERN, SYMBOL or #define and * make a symbol table entry for them. Life is slightly complicated by * constructions like: * * 1000 PRIVATE struct foobar { * 1001 int i; * 1002 char c; * 1003 } name[MAX]; * * in which the symbol 'name' is triggered on line 1000, but its value is 1003. */ int type; register char c; char *p, *q; /* If we are hunting for the end of a declaration, the rules are * different. */ if (hunting) { q = line_buf; while (*q == ' ' || *q == '\t') q++; if (*q != '}') return; q++; while (*q == ' ' || *q == '\t') q++; p = q; while (isalnum(*p) || *p == '_') p++; *p = 0; enter_sym(q, cur_line - 1, hunting); hunting = 0; return; } /* For efficiency, make a quick check to see if this line is interesting. */ c = line_buf[0]; if (c != 'P' && c != 'E' && c != 'S' && c != '#') return; squash(); /* squeeze out white space */ /* Real check to see if this line is interesting. */ type = 0; if (strncmp(b, "#define", (size_t)7) == 0) type = DEF; if (strncmp(b, "PRIVATE", (size_t)7) == 0) type = PRIV; if (strncmp(b, "PUBLIC", (size_t)6) == 0) type = PUB; if (strncmp(b, "EXTERN", (size_t)6) == 0) type = EXT; if (strncmp(b, "SYMBOL", (size_t)6) == 0) type = SYM; if (type == 0) return; /* Process #define */ if (type == DEF) { q = &b[8]; while (*q != ' ') q++; *q = 0; enter_sym(&b[8], cur_line - 1, type); return; } q = b + strlen(b) - 1; /* q points to last character */ if (*q == '!' || *q == '+') { *q = ' '; while (*q == ' ') q--; } /* If last char is paren, }, or comma, this is a function call. */ if (*q == ')' || *q == ',' || *q == '}') { /* It is a paren. */ q = b; while (*q != '(') q++; if (*(q + 1) == '*') *q = ' '; /* worry about: int (*foo[N])() */ strip(b, "(", 1); strip(b, "[", 1); q = b + strlen(b) - 1; q = backup(q); enter_sym(q, cur_line - 1, type); return; } /* If last char is semicolon, '[', or letter, this is a declaration. */ if (*q == ';' || *q == ']' || *q == '_' || isalnum(*q)) { /* It is a declaration. */ if (*q == ';') q--; if (*q == ' ') q--; *(q + 1) = 0; if (*q == ']') strip(b, "[", 1); /* e.g. PRIVATE int foo[N]; */ q = b + strlen(b) - 1; q = backup(q); enter_sym(q, cur_line - 1, type); return; } /* If last char is curly bracket, this is the start of a struct array. */ if (*q == '{') { hunting = type; /* Start hunting for the '}' */ return; } panic("getsym got unknown line type");}void fill_page(){/* Fill out current page with line feeds. This routine is only called when * an end of file is encountered. It fills out the current page to 50 * lines (or whatever). For -t it does nothing. */ int k; k = cur_line % page_len; if (k > 0) k = page_len - k; cur_line += k; if (tflag == 0) while (k--) printf("\n");}void eject(){/* Finish off the last 11 or so blank lines on a page (not for -t. */ int i; if (tflag) return; i = phys_page - page_len - HDR_SIZE; while (i--) printf("\n");}void new_page(file)char *file;{/* Top of page processing. */ if (tflag) { /* Generate a troff macro that does the heading (odd/even). */ if (cur_page & 1) printf(".Op %d %s\n", cur_page, file); /* odd page */ else printf(".Ep %d %s\n", cur_page, file); /* even page */ } else { /* Standard header */ printf("\n\n\nPage: %d ", cur_page); printf("File: %s\n\n", file); } cur_page++;}void squash(){/* Copy line_buf to b, squeezing out white space, comments, and initializers. */ register char *p, *q; char c; p = line_buf; q = b; c = *p; *q++ = *p++; /* copy first character */ if (c == '#') { while (*p == ' ' || *p == '\t') p++; /* skip spaces after '#' */ } while (*p != '\0') { if (*p == '\n' || *p == '=') break; if (*p == '/' && *(p + 1) == '*') break; if (*p == ' ' || *p == '\t') { while (*p == ' ' || *p == '\t') p++; *q++ = ' '; /* copy one space */ } else { *q++ = *p++; } } if (*(q - 1) == ' ') q--; *q = 0;}void strip(string, s, n)char *string;char *s;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -