ctags.c

Go to the documentation of this file.
00001 /* ctags.c */
00002 
00003 /* This is a reimplementation of the ctags(1) program.  It supports ANSI C,
00004  * and has heaps o' flags.  It is meant to be distributed with elvis.
00005  */
00006 
00007 #include <stdio.h>
00008 #include "config.h"
00009 #ifndef FALSE
00010 # define FALSE  0
00011 # define TRUE   1
00012 #endif
00013 #ifndef TAGS
00014 # define TAGS   "tags"
00015 #endif
00016 #ifndef REFS
00017 # define REFS   "refs"
00018 #endif
00019 #ifndef BLKSIZE
00020 # define BLKSIZE 1024
00021 #endif
00022 
00023 #include "ctype.c" /* yes, that really is the .c file, not the .h one. */
00024 
00025 /* -------------------------------------------------------------------------- */
00026 /* Some global variables */
00027 
00028 /* The following boolean variables are set according to command line flags */
00029 int     incl_static;    /* -s  include static tags */
00030 int     incl_types;     /* -t  include typedefs and structs */
00031 int     incl_vars;      /* -v  include variables */
00032 int     make_refs;      /* -r  generate a "refs" file */
00033 int     append_files;   /* -a  append to "tags" [and "refs"] files */
00034 
00035 /* The following are used for outputting to the "tags" and "refs" files */
00036 FILE    *tags;          /* used for writing to the "tags" file */
00037 FILE    *refs;          /* used for writing to the "refs" file */
00038 
00039 /* -------------------------------------------------------------------------- */
00040 /* These are used for reading a source file.  It keeps track of line numbers */
00041 char    *file_name;     /* name of the current file */
00042 FILE    *file_fp;       /* stream used for reading the file */
00043 long    file_lnum;      /* line number in the current file */
00044 long    file_seek;      /* fseek() offset to the start of current line */
00045 int     file_afternl;   /* boolean: was previous character a newline? */
00046 int     file_prevch;    /* a single character that was ungotten */
00047 int     file_header;    /* boolean: is the current file a header file? */
00048 
00049 /* This function opens a file, and resets the line counter.  If it fails, it
00050  * it will display an error message and leave the file_fp set to NULL.
00051  */
00052 void file_open(name)
00053         char    *name;  /* name of file to be opened */
00054 {
00055         /* if another file was already open, then close it */
00056         if (file_fp)
00057         {
00058                 fclose(file_fp);
00059         }
00060 
00061         /* try to open the file for reading.  The file must be opened in
00062          * "binary" mode because otherwise fseek() would misbehave under DOS.
00063          */
00064 #if MSDOS || TOS
00065         file_fp = fopen(name, "rb");
00066 #else
00067         file_fp = fopen(name, "r");
00068 #endif
00069         if (!file_fp)
00070         {
00071                 perror(name);
00072         }
00073 
00074         /* reset the name & line number */
00075         file_name = name;
00076         file_lnum = 0L;
00077         file_seek = 0L;
00078         file_afternl = TRUE;
00079 
00080         /* determine whether this is a header file */
00081         file_header = FALSE;
00082         name += strlen(name) - 2;
00083         if (name >= file_name && name[0] == '.' && (name[1] == 'h' || name[1] == 'H'))
00084         {
00085                 file_header = TRUE;
00086         }
00087 }
00088 
00089 /* This function reads a single character from the stream.  If the *previous*
00090  * character was a newline, then it also increments file_lnum and sets
00091  * file_offset.
00092  */
00093 int file_getc()
00094 {
00095         int     ch;
00096 
00097         /* if there is an ungotten character, then return it.  Don't do any
00098          * other processing on it, though, because we already did that the
00099          * first time it was read.
00100          */
00101         if (file_prevch)
00102         {
00103                 ch = file_prevch;
00104                 file_prevch = 0;
00105                 return ch;
00106         }
00107 
00108         /* if previous character was a newline, then we're starting a line */
00109         if (file_afternl)
00110         {
00111                 file_afternl = FALSE;
00112                 file_seek = ftell(file_fp);
00113                 file_lnum++;
00114         }
00115 
00116         /* Get a character.  If no file is open, then return EOF */
00117         ch = (file_fp ? getc(file_fp) : EOF);
00118 
00119         /* if it is a newline, then remember that fact */
00120         if (ch == '\n')
00121         {
00122                 file_afternl = TRUE;
00123         }
00124 
00125         /* return the character */
00126         return ch;
00127 }
00128 
00129 /* This function ungets a character from the current source file */
00130 void file_ungetc(ch)
00131         int     ch;     /* character to be ungotten */
00132 {
00133         file_prevch = ch;
00134 }
00135 
00136 /* This function copies the current line out some other fp.  It has no effect
00137  * on the file_getc() function.  During copying, any '\' characters are doubled
00138  * and a leading '^' or trailing '$' is also quoted.  The newline character is
00139  * not copied.
00140  *
00141  * This is meant to be used when generating a tag line.
00142  */
00143 void file_copyline(seek, fp)
00144         long    seek;   /* where the lines starts in the source file */
00145         FILE    *fp;    /* the output stream to copy it to */
00146 {
00147         long    oldseek;/* where the file's pointer was before we messed it up */
00148         char    ch;     /* a single character from the file */
00149         char    next;   /* the next character from this file */
00150 
00151         /* go to the start of the line */
00152         oldseek = ftell(file_fp);
00153         fseek(file_fp, seek, 0);
00154 
00155         /* if first character is '^', then emit \^ */
00156         ch = getc(file_fp);
00157         if (ch == '^')
00158         {
00159                 putc('\\', fp);
00160                 putc('^', fp);
00161                 ch = getc(file_fp);
00162         }
00163 
00164         /* write everything up to, but not including, the newline */
00165         while (ch != '\n')
00166         {
00167                 /* preread the next character from this file */
00168                 next = getc(file_fp);
00169 
00170                 /* if character is '\', or a terminal '$', then quote it */
00171                 if (ch == '\\' || (ch == '$' && next == '\n'))
00172                 {
00173                         putc('\\', fp);
00174                 }
00175                 putc(ch, fp);
00176 
00177                 /* next character... */
00178                 ch = next;
00179         }
00180 
00181         /* seek back to the old position */
00182         fseek(file_fp, oldseek, 0);
00183 }
00184 
00185 /* -------------------------------------------------------------------------- */
00186 /* This section handles preprocessor directives.  It strips out all of the
00187  * directives, and may emit a tag for #define directives.
00188  */
00189 
00190 int     cpp_afternl;    /* boolean: look for '#' character? */
00191 int     cpp_prevch;     /* an ungotten character, if any */
00192 int     cpp_refsok;     /* boolean: can we echo characters out to "refs"? */
00193 
00194 /* This function opens the file & resets variables */
00195 void cpp_open(name)
00196         char    *name;  /* name of source file to be opened */
00197 {
00198         /* use the lower-level file_open function to open the file */
00199         file_open(name);
00200 
00201         /* reset variables */
00202         cpp_afternl = TRUE;
00203         cpp_refsok = TRUE;
00204 }
00205 
00206 /* This function copies a character from the source file to the "refs" file */
00207 void cpp_echo(ch)
00208         int     ch; /* the character to copy */
00209 {
00210         static  wasnl;
00211 
00212         /* echo non-EOF chars, unless not making "ref", or echo turned off */
00213         if (ch != EOF && make_refs && cpp_refsok && !file_header)
00214         {
00215                 /* try to avoid blank lines */
00216                 if (ch == '\n')
00217                 {
00218                         if (wasnl)
00219                         {
00220                                 return;
00221                         }
00222                         wasnl = TRUE;
00223                 }
00224                 else
00225                 {
00226                         wasnl = FALSE;
00227                 }
00228 
00229                 /* add the character */
00230                 putc(ch, refs);
00231         }
00232 }
00233 
00234 /* This function returns the next character which isn't part of a directive */
00235 int cpp_getc()
00236 {
00237         static
00238         int     ch;     /* the next input character */
00239         char    *scan;
00240 
00241         /* if we have an ungotten character, then return it */
00242         if (cpp_prevch)
00243         {
00244                 ch = cpp_prevch;
00245                 cpp_prevch = 0;
00246                 return ch;
00247         }
00248 
00249         /* Get a character from the file.  Return it if not special '#' */
00250         ch = file_getc();
00251         if (ch == '\n')
00252         {
00253                 cpp_afternl = TRUE;
00254                 cpp_echo(ch);
00255                 return ch;
00256         }
00257         else if (ch != '#' || !cpp_afternl)
00258         {
00259                 /* normal character.  Any non-whitespace should turn off afternl */
00260                 if (ch != ' ' && ch != '\t')
00261                 {
00262                         cpp_afternl = FALSE;
00263                 }
00264                 cpp_echo(ch);
00265                 return ch;
00266         }
00267 
00268         /* Yikes!  We found a directive */
00269 
00270         /* see whether this is a #define line */
00271         scan = " define ";
00272         while (*scan)
00273         {
00274                 if (*scan == ' ')
00275                 {
00276                         /* space character matches any whitespace */
00277                         do
00278                         {
00279                                 ch = file_getc();
00280                         } while (ch == ' ' || ch == '\t');
00281                         file_ungetc(ch);
00282                 }
00283                 else
00284                 {
00285                         /* other characters should match exactly */
00286                         ch = file_getc();
00287                         if (ch != *scan)
00288                         {
00289                                 file_ungetc(ch);
00290                                 break;
00291                         }
00292                 }
00293                 scan++;
00294         }
00295 
00296         /* is this a #define line?  and should we generate a tag for it? */
00297         if (!*scan && (file_header || incl_static))
00298         {
00299                 /* if not a header, then this will be a static tag */
00300                 if (!file_header)
00301                 {
00302                         fputs(file_name, tags);
00303                         putc(':', tags);
00304                 }
00305 
00306                 /* output the tag name */
00307                 for (ch = file_getc(); isalnum(ch) || ch == '_'; ch = file_getc())
00308                 {
00309                         putc(ch, tags);
00310                 }
00311 
00312                 /* output a tab, the filename, another tab, and the line number */
00313                 fprintf(tags, "\t%s\t%ld\n", file_name, file_lnum);
00314         }
00315 
00316         /* skip to the end of the directive -- a newline that isn't preceded
00317          * by a '\' character.
00318          */
00319         while (ch != EOF && ch != '\n')
00320         {
00321                 if (ch == '\\')
00322                 {
00323                         ch = file_getc();
00324                 }
00325                 ch = file_getc();
00326         }
00327 
00328         /* return the newline that we found at the end of the directive */
00329         cpp_echo(ch);
00330         return ch;
00331 }
00332 
00333 /* This puts a character back into the input queue for the source file */
00334 cpp_ungetc(ch)
00335         int     ch;     /* a character to be ungotten */
00336 {
00337         cpp_prevch = ch;
00338 }
00339 
00340 
00341 /* -------------------------------------------------------------------------- */
00342 /* This is the lexical analyser.  It gets characters from the preprocessor,
00343  * and gives tokens to the parser.  Some special codes are...
00344  *   (deleted)  /*...* /        (comments)
00345  *   (deleted)  //...\n (comments)
00346  *   (deleted)  (*      (parens used in complex declaration)
00347  *   (deleted)  [...]   (array subscript, when ... contains no ])
00348  *   (deleted)  struct  (intro to structure declaration)
00349  *   BODY       {...}   ('{' can occur anywhere, '}' only at BOW if ... has '{')
00350  *   ARGS       (...{   (args of function, not extern or forward)
00351  *   ARGS       (...);  (args of an extern/forward function declaration)
00352  *   COMMA      ,       (separate declarations that have same scope)
00353  *   SEMICOLON  ;       (separate declarations that have different scope)
00354  *   SEMICOLON  =...;   (initializer)
00355  *   TYPEDEF    typedef (the "typedef" keyword)
00356  *   STATIC     static  (the "static" keyword)
00357  *   STATIC     private (the "static" keyword)
00358  *   STATIC     PRIVATE (the "static" keyword)
00359  *   NAME       [a-z]+  (really any valid name that isn't reserved word)
00360  */
00361 
00362 /* #define EOF -1 */
00363 #define DELETED   0
00364 #define BODY      1
00365 #define ARGS      2
00366 #define COMMA     3
00367 #define SEMICOLON 4
00368 #define TYPEDEF   5
00369 #define STATIC    6
00370 #define EXTERN    7
00371 #define NAME      8
00372 
00373 char    lex_name[BLKSIZE];      /* the name of a "NAME" token */
00374 long    lex_seek;               /* start of line that contains lex_name */
00375 
00376 lex_gettoken()
00377 {
00378         int     ch;             /* a character from the preprocessor */
00379         int     next;           /* the next character */
00380         int     token;          /* the token that we'll return */
00381         int     i;
00382 
00383         /* loop until we get a token that isn't "DELETED" */
00384         do
00385         {
00386                 /* get the next character */
00387                 ch = cpp_getc();
00388 
00389                 /* process the character */
00390                 switch (ch)
00391                 {
00392                   case ',':
00393                         token = COMMA;
00394                         break;
00395 
00396                   case ';':
00397                         token = SEMICOLON;
00398                         break;
00399 
00400                   case '/':
00401                         /* get the next character */
00402                         ch = cpp_getc();
00403                         switch (ch)
00404                         {
00405                           case '*':     /* start of C comment */
00406                                 ch = cpp_getc();
00407                                 next = cpp_getc();
00408                                 while (next != EOF && (ch != '*' || next != '/'))
00409                                 {
00410                                         ch = next;
00411                                         next = cpp_getc();
00412                                 }
00413                                 break;
00414 
00415                           case '/':     /* start of a C++ comment */
00416                                 do
00417                                 {
00418                                         ch = cpp_getc();
00419                                 } while (ch != '\n' && ch != EOF);
00420                                 break;
00421 
00422                           default:      /* some other slash */
00423                                 cpp_ungetc(ch);
00424                         }
00425                         token = DELETED;
00426                         break;
00427 
00428                   case '(':
00429                         ch = cpp_getc();
00430                         if (ch == '*')
00431                         {
00432                                 token = DELETED;
00433                         }
00434                         else
00435                         {
00436                                 next = cpp_getc();
00437                                 while (ch != '{' && ch != EOF && (ch != ')' || next != ';'))/*}*/
00438                                 {
00439                                         ch = next;
00440                                         next = cpp_getc();
00441                                 }
00442                                 if (ch == '{')/*}*/
00443                                 {
00444                                         cpp_ungetc(ch);
00445                                 }
00446                                 else if (next == ';')
00447                                 {
00448                                         cpp_ungetc(next);
00449                                 }
00450                                 token = ARGS;
00451                         }
00452                         break;
00453 
00454                   case '{':/*}*/
00455                         /* don't send the next characters to "refs" */
00456                         cpp_refsok = FALSE;
00457 
00458                         /* skip ahead to closing '}', or to embedded '{' */
00459                         do
00460                         {
00461                                 ch = cpp_getc();
00462                         } while (ch != '{' && ch != '}' && ch != EOF);
00463 
00464                         /* if has embedded '{', then skip to '}' in column 1 */
00465                         if (ch == '{') /*}*/
00466                         {
00467                                 ch = cpp_getc();
00468                                 next = cpp_getc();
00469                                 while (ch != EOF && (ch != '\n' || next != '}'))/*{*/
00470                                 {
00471                                         ch = next;
00472                                         next = cpp_getc();
00473                                 }
00474                         }
00475 
00476                         /* resume "refs" processing */
00477                         cpp_refsok = TRUE;
00478                         cpp_echo('}');
00479 
00480                         token = BODY;
00481                         break;
00482 
00483                   case '[':
00484                         /* skip to matching ']' */
00485                         do
00486                         {
00487                                 ch = cpp_getc();
00488                         } while (ch != ']' && ch != EOF);
00489                         token = DELETED;
00490                         break;
00491 
00492                   case '=':
00493                         /* skip to next ';' */
00494                         do
00495                         {
00496                                 ch = cpp_getc();
00497 
00498                                 /* leave array initializers out of "refs" */
00499                                 if (ch == '{')
00500                                 {
00501                                         cpp_refsok = FALSE;
00502                                 }
00503                         } while (ch != ';' && ch != EOF);
00504 
00505                         /* resume echoing to "refs" */
00506                         if (!cpp_refsok)
00507                         {
00508                                 cpp_refsok = TRUE;
00509                                 cpp_echo('}');
00510                                 cpp_echo(';');
00511                         }
00512                         token = SEMICOLON;
00513                         break;
00514 
00515                   case EOF:
00516                         token = EOF;
00517                         break;
00518 
00519                   default:
00520                         /* is this the start of a name/keyword? */
00521                         if (isalpha(ch) || ch == '_')
00522                         {
00523                                 /* collect the whole word */
00524                                 lex_name[0] = ch;
00525                                 for (i = 1, ch = cpp_getc();
00526                                      i < BLKSIZE - 1 && (isalnum(ch) || ch == '_');
00527                                      i++, ch = cpp_getc())
00528                                 {
00529                                         lex_name[i] = ch;
00530                                 }
00531                                 lex_name[i] = '\0';
00532                                 cpp_ungetc(ch);
00533 
00534                                 /* is it a reserved word? */
00535                                 if (!strcmp(lex_name, "typedef"))
00536                                 {
00537                                         token = TYPEDEF;
00538                                         lex_seek = -1L;
00539                                 }
00540                                 else if (!strcmp(lex_name, "static")
00541                                       || !strcmp(lex_name, "private")
00542                                       || !strcmp(lex_name, "PRIVATE"))
00543                                 {
00544                                         token = STATIC;
00545                                         lex_seek = -1L;
00546                                 }
00547                                 else if (!strcmp(lex_name, "extern")
00548                                       || !strcmp(lex_name, "EXTERN")
00549                                       || !strcmp(lex_name, "FORWARD"))
00550                                 {
00551                                         token = EXTERN;
00552                                         lex_seek = -1L;
00553                                 }
00554                                 else
00555                                 {
00556                                         token = NAME;
00557                                         lex_seek = file_seek;
00558                                 }
00559                         }
00560                         else /* not part of a name/keyword */
00561                         {
00562                                 token = DELETED;
00563                         }
00564 
00565                 } /* end switch(ch) */
00566 
00567         } while (token == DELETED);
00568 
00569         return token;
00570 }
00571 
00572 /* -------------------------------------------------------------------------- */
00573 /* This is the parser.  It locates tag candidates, and then decides whether to
00574  * generate a tag for them.
00575  */
00576 
00577 /* This function generates a tag for the object in lex_name, whose tag line is
00578  * located at a given seek offset.
00579  */
00580 void maketag(scope, seek)
00581         int     scope;  /* 0 if global, or STATIC if static */
00582         long    seek;   /* the seek offset of the line */
00583 {
00584         /* output the tagname and filename fields */
00585         if (scope == EXTERN)
00586         {
00587                 /* whoa!  we should *never* output a tag for "extern" decl */
00588                 return;
00589         }
00590         else if (scope == STATIC)
00591         {
00592                 fprintf(tags, "%s:%s\t%s\t", file_name, lex_name, file_name);
00593         }
00594         else
00595         {
00596                 fprintf(tags, "%s\t%s\t", lex_name, file_name);
00597         }
00598 
00599         /* output the target line */
00600         putc('/', tags);
00601         putc('^', tags);
00602         file_copyline(seek, tags);
00603         putc('$', tags);
00604         putc('/', tags);
00605         putc('\n', tags);
00606 }
00607 
00608 
00609 /* This function parses a source file, adding any tags that it finds */
00610 void ctags(name)
00611         char    *name;  /* the name of a source file to be checked */
00612 {
00613         int     prev;   /* the previous token from the source file */
00614         int     token;  /* the current token from the source file */
00615         int     scope;  /* normally 0, but could be a TYPEDEF or STATIC token */
00616         int     gotname;/* boolean: does lex_name contain a tag candidate? */
00617         long    tagseek;/* start of line that contains lex_name */
00618 
00619         /* open the file */
00620         cpp_open(name);
00621 
00622         /* reset */
00623         scope = 0;
00624         gotname = FALSE;
00625         token = SEMICOLON;
00626 
00627         /* parse until the end of the file */
00628         while (prev = token, (token = lex_gettoken()) != EOF)
00629         {
00630                 /* scope keyword? */
00631                 if (token == TYPEDEF || token == STATIC || token == EXTERN)
00632                 {
00633                         scope = token;
00634                         gotname = FALSE;
00635                         continue;
00636                 }
00637 
00638                 /* name of a possible tag candidate? */
00639                 if (token == NAME)
00640                 {
00641                         tagseek = file_seek;
00642                         gotname = TRUE;
00643                         continue;
00644                 }
00645 
00646                 /* if NAME BODY, without ARGS, then NAME is a struct tag */
00647                 if (gotname && token == BODY && prev != ARGS)
00648                 {
00649                         gotname = FALSE;
00650                         
00651                         /* ignore if in typedef -- better name is coming soon */
00652                         if (scope == TYPEDEF)
00653                         {
00654                                 continue;
00655                         }
00656 
00657                         /* generate a tag, if -t and maybe -s */
00658                         if (incl_types && (file_header || incl_static))
00659                         {
00660                                 maketag(file_header ? 0 : STATIC, tagseek);
00661                         }
00662                 }
00663 
00664                 /* If NAME ARGS BODY, then NAME is a function */
00665                 if (gotname && prev == ARGS && token == BODY)
00666                 {
00667                         gotname = FALSE;
00668                         
00669                         /* generate a tag, maybe checking -s */
00670                         if (scope != STATIC || incl_static)
00671                         {
00672                                 maketag(scope, tagseek);
00673                         }
00674                 }
00675 
00676                 /* If NAME SEMICOLON or NAME COMMA, then NAME is var/typedef */
00677                 if (gotname && (token == SEMICOLON || token == COMMA))
00678                 {
00679                         gotname = FALSE;
00680 
00681                         /* generate a tag, if -v/-t and maybe -s */
00682                         if (scope == TYPEDEF && incl_types && (file_header || incl_static)
00683                          || scope == STATIC && incl_vars && incl_static
00684                          || incl_vars)
00685                         {
00686                                 /* a TYPEDEF outside of a header is STATIC */
00687                                 if (scope == TYPEDEF && !file_header)
00688                                 {
00689                                         maketag(STATIC, tagseek);
00690                                 }
00691                                 else /* use whatever scope was declared */
00692                                 {
00693                                         maketag(scope, tagseek);
00694                                 }
00695                         }
00696                 }
00697 
00698                 /* reset after a semicolon or ARGS BODY pair */
00699                 if (token == SEMICOLON || (prev == ARGS && token == BODY))
00700                 {
00701                         scope = 0;
00702                         gotname = FALSE;
00703                 }
00704         }
00705 
00706         /* The source file will be automatically closed */
00707 }
00708 
00709 /* -------------------------------------------------------------------------- */
00710 
00711 void usage()
00712 {
00713         fprintf(stderr, "usage: ctags [flags] filenames...\n");
00714         fprintf(stderr, "\t-s  include static functions\n");
00715         fprintf(stderr, "\t-t  include typedefs\n");
00716         fprintf(stderr, "\t-v  include variable declarations\n");
00717         fprintf(stderr, "\t-r  generate a \"refs\" file, too\n");
00718         fprintf(stderr, "\t-a  append to \"tags\", instead of overwriting\n");
00719         exit(2);
00720 }
00721 
00722 
00723 
00724 #if AMIGA
00725 # include "amiwild.c"
00726 #endif
00727 
00728 #if VMS
00729 # include "vmswild.c"
00730 #endif
00731 
00732 main(argc, argv)
00733         int     argc;
00734         char    **argv;
00735 {
00736         int     i, j;
00737 
00738 #if MSDOS || TOS
00739         char    **wildexpand();
00740         argv = wildexpand(&argc, argv);
00741 #endif
00742 
00743         /* build the tables used by the ctype macros */
00744         _ct_init("");
00745 
00746         /* parse the option flags */
00747         for (i = 1; i < argc && argv[i][0] == '-'; i++)
00748         {
00749                 for (j = 1; argv[i][j]; j++)
00750                 {
00751                         switch (argv[i][j])
00752                         {
00753                           case 's':     incl_static = TRUE;     break;
00754                           case 't':     incl_types = TRUE;      break;
00755                           case 'v':     incl_vars = TRUE;       break;
00756                           case 'r':     make_refs = TRUE;       break;
00757                           case 'a':     append_files = TRUE;    break;
00758                           default:      usage();
00759                         }
00760                 }
00761         }
00762 
00763         /* There should always be at least one source file named in args */
00764         if (i == argc)
00765         {
00766                 usage();
00767         }
00768 
00769         /* open the "tags" and maybe "refs" files */
00770         tags = fopen(TAGS, append_files ? "a" : "w");
00771         if (!tags)
00772         {
00773                 perror(TAGS);
00774                 exit(3);
00775         }
00776         if (make_refs)
00777         {
00778                 refs = fopen(REFS, append_files ? "a" : "w");
00779                 if (!refs)
00780                 {
00781                         perror(REFS);
00782                         exit(4);
00783                 }
00784         }
00785 
00786         /* parse each source file */
00787         for (; i < argc; i++)
00788         {
00789                 ctags(argv[i]);
00790         }
00791 
00792         /* close "tags" and maybe "refs" */
00793         fclose(tags);
00794         if (make_refs)
00795         {
00796                 fclose(refs);
00797         }
00798 
00799 #ifdef SORT
00800                 /* This is a hack which will sort the tags list.   It should
00801                  * on UNIX and OS-9.  You may have trouble with csh.   Note
00802                  * that the tags list only has to be sorted if you intend to
00803                  * use it with the real vi;  elvis permits unsorted tags.
00804                  */
00805 # if OSK
00806                 system("qsort tags >-_tags; -nx; del tags; rename _tags tags");
00807 # else  
00808                 system("sort tags >_tags$$; mv _tags$$ tags");
00809 # endif
00810 #endif
00811 
00812         exit(0);
00813         /*NOTREACHED*/
00814 }
00815 
00816 #if MSDOS || TOS
00817 # define WILDCARD_NO_MAIN
00818 # include "wildcard.c"
00819 #endif

Generated on Fri Apr 14 22:56:52 2006 for minix by  doxygen 1.4.6