main.c
上传用户:gddssl
上传日期:2007-01-06
资源大小:1003k
文件大小:36k
源码类别:

编辑器/阅读器

开发平台:

DOS

  1. /*****************************************************************************
  2. *   $Id: main.c,v 6.23 1998/08/18 04:26:50 darren Exp $
  3. *
  4. *   Copyright (c) 1996-1998, Darren Hiebert
  5. *
  6. *   Author: Darren Hiebert <darren@hiebert.com>, <darren@hiwaay.net>
  7. *           http://darren.hiebert.com
  8. *
  9. *   This source code is released for free distribution under the terms of the
  10. *   GNU General Public License. It is provided on an as-is basis and no
  11. *   responsibility is accepted for its failure to perform as expected.
  12. *
  13. *   This is a reimplementation of the ctags(1) program. It is an attempt to
  14. *   provide a fully featured ctags program which is free of the limitations
  15. *   which most (all?) others are subject to.
  16. *
  17. *   It is derived from and inspired by the ctags program by Steve Kirkendall
  18. *   (kirkenda@cs.pdx.edu) that comes with the Elvis vi clone (though almost
  19. *   none of the original code remains). This, too, was freely available.
  20. *
  21. *   This program provides the following features:
  22. *
  23. *   Support for both K&R style and new ANSI style function definitions.
  24. *
  25. *   Generates tags for the following objects:
  26. * - macro definitions
  27. * - enumeration values
  28. * - function definitions (and C++ methods)
  29. * - function declarations (optional)
  30. * - enum, struct and union tags and C++ class names
  31. * - typedefs
  32. * - variables
  33. *
  34. *   This module contains top level start-up and portability functions.
  35. *****************************************************************************/
  36. /*============================================================================
  37. =   Include files
  38. ============================================================================*/
  39. #ifdef HAVE_CONFIG_H
  40. # include <config.h>
  41. #endif
  42. #if defined(__STDC__) || defined(MSDOS) || defined(WIN32) || defined(OS2)
  43. # define ENABLE_STDARG
  44. #endif
  45. #if defined(MSDOS) || defined(WIN32)
  46. # define HAVE_DOS_H
  47. # define HAVE_IO_H
  48. # define HAVE_TIME_H
  49. # define HAVE_CLOCK
  50. # define HAVE_CHSIZE
  51. # define HAVE_STRERROR
  52. # define HAVE_FINDNEXT
  53. # ifdef __BORLANDC__
  54. #  define HAVE_DIR_H
  55. #  define HAVE_DIRENT_H
  56. #  define HAVE_FINDFIRST
  57. # else
  58. #  ifdef _MSC_VER
  59. #   define HAVE__FINDFIRST
  60. #  endif
  61. # endif
  62. #endif
  63. #ifdef DJGPP
  64. # define HAVE_DIR_H
  65. # define HAVE_UNISTD_H
  66. # define HAVE_FINDFIRST
  67. # define HAVE_TRUNCATE
  68. #endif
  69. #if defined(OS2)
  70. # define HAVE_DIRENT_H
  71. # define HAVE_TIME_H
  72. # define HAVE_IO_H
  73. # define HAVE_CLOCK
  74. # define HAVE_CHSIZE
  75. # define HAVE_OPENDIR
  76. # define HAVE_STRERROR
  77. #endif
  78. #ifdef AMIGA
  79. # define HAVE_TIME_H
  80. # define HAVE_SYS_STAT_H
  81. # define HAVE_CLOCK
  82. # define HAVE_STRERROR
  83. # define PATH_MAX 255
  84. # include <dos/dosasl.h> /* for struct AnchorPath */
  85. # include <clib/dos_protos.h> /* function prototypes */
  86. # define ANCHOR_BUF_SIZE (512)
  87. # define ANCHOR_SIZE (sizeof(struct AnchorPath) + ANCHOR_BUF_SIZE)
  88. #endif
  89. #if defined(HAVE_UNISTD_H)
  90. # include <unistd.h> /* to declare close(), ftruncate(), truncate() */
  91. #endif
  92. #include <errno.h>
  93. #ifdef ENABLE_STDARG
  94. # include <stdarg.h>
  95. #else
  96. # include <varargs.h>
  97. #endif
  98. /*  To declare "struct stat" and stat().
  99.  */
  100. #if defined(__MWERKS__) && defined(__MACINTOSH__)
  101. # include <stat.h> /* there is no sys directory on the Mac */
  102. #else
  103. # include <sys/types.h>
  104. # include <sys/stat.h>
  105. #endif
  106. #ifdef HAVE_DIRENT_H
  107. # ifdef __BORLANDC__
  108. #  define boolean BORLAND_boolean
  109. # endif
  110. # include <dirent.h>
  111. # undef boolean
  112. #endif
  113. /*  These header files provide for the functions necessary to do file
  114.  *  truncation.
  115.  */
  116. #include <fcntl.h>
  117. #ifdef HAVE_IO_H
  118. # include <io.h>
  119. #endif
  120. #ifdef HAVE_DOS_H
  121. # include <dos.h>
  122. #endif
  123. #ifdef HAVE_DIR_H
  124. # include <dir.h>
  125. #endif
  126. /*  To define the maximum path length
  127.  */
  128. #include <limits.h> /* to define PATH_MAX (hopefully) */
  129. #ifndef PATH_MAX
  130. # ifdef MAXNAMLEN
  131. #  define PATH_MAX MAXNAMLEN
  132. # endif
  133. #endif
  134. #if !defined(PATH_MAX) && defined(MAXPATH) /* found in Borland C <dir.h> */
  135. # define PATH_MAX MAXPATH
  136. #endif
  137. #ifndef PATH_MAX
  138. # define PATH_MAX 127 /* ultimate fall-back */
  139. #endif
  140. /*  To provide timings features if available.
  141.  */
  142. #ifdef HAVE_CLOCK
  143. # ifdef HAVE_TIME_H
  144. #  include <time.h>
  145. # endif
  146. #else
  147. # ifdef HAVE_TIMES
  148. #  ifdef HAVE_SYS_TIMES_H
  149. #   include <sys/times.h>
  150. #  endif
  151. # endif
  152. #endif
  153. #ifdef DEBUG
  154. # include <assert.h>
  155. #endif
  156. #include "ctags.h"
  157. /*============================================================================
  158. =   Defines
  159. ============================================================================*/
  160. /*----------------------------------------------------------------------------
  161.  *  Portability defines
  162.  *--------------------------------------------------------------------------*/
  163. #if !defined(HAVE_TRUNCATE) && !defined(HAVE_FTRUNCATE) && !defined(HAVE_CHSIZE)
  164. # define USE_REPLACEMENT_TRUNCATE
  165. #endif
  166. #if defined(MSDOS) || defined(WIN32) || defined(OS2)
  167. # define PATH_SEPARATOR '\'
  168. #else
  169. # define PATH_SEPARATOR '/'
  170. #endif
  171. /*  File type tests.
  172.  */
  173. #ifndef S_ISREG
  174. # if defined(S_IFREG) && !defined(AMIGA)
  175. #  define S_ISREG(mode)     ((mode) & S_IFREG)
  176. # else
  177. #  define S_ISREG(mode)     TRUE /* assume regular file */
  178. # endif
  179. #endif
  180. #ifndef S_ISLNK
  181. # ifdef S_IFLNK
  182. #  define S_ISLNK(mode)     (((mode) & S_IFMT) == S_IFLNK)
  183. # else
  184. #  define S_ISLNK(mode)     FALSE /* assume no soft links */
  185. # endif
  186. #endif
  187. #ifndef S_ISDIR
  188. # ifdef S_IFDIR
  189. #  define S_ISDIR(mode)     (((mode) & S_IFMT) == S_IFDIR)
  190. # else
  191. #  define S_ISDIR(mode)     FALSE /* assume no soft links */
  192. # endif
  193. #endif
  194. /*  Hack for rediculous practice of Microsoft Visual C++.
  195.  */
  196. #if defined(WIN32) && defined(_MSC_VER)
  197. # define chsize _chsize
  198. # define open _open
  199. # define close _close
  200. # define stat _stat
  201. # define O_RDWR  _O_RDWR
  202. #endif
  203. /*----------------------------------------------------------------------------
  204.  *  Miscellaneous defines
  205.  *--------------------------------------------------------------------------*/
  206. #ifndef ETAGS
  207. # define ETAGS "etags" /* name which causes default use of to -e */
  208. #endif
  209. #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
  210. #define plural(value) (((unsigned long)(value) == 1L) ? "" : "s")
  211. /*============================================================================
  212. =   Data definitions
  213. ============================================================================*/
  214. #if defined(MSDOS) || defined(WIN32) || defined(OS2)
  215. static const char DosPathDelimiters[] = ":/\";
  216. #endif
  217. static const char *ExecutableName = NULL;
  218. static struct { long files, lines, bytes; } Totals = { 0, 0, 0 };
  219. tagFile TagFile = {
  220.     NULL, /* file name */
  221.     NULL, /* file pointer */
  222.     { 0, 0 }, /* numTags */
  223.     { 0, 0, 0 }, /* max */
  224.     { "", NULL }, /* etags */
  225.     { 0, NULL } /* line */
  226. };
  227. memberInfo NoClass = { MEMBER_NONE, VIS_UNDEFINED, FALSE, "" };
  228. /*============================================================================
  229. =   Function prototypes
  230. ============================================================================*/
  231. #ifdef NEED_PROTO_STAT
  232. extern int stat __ARGS((const char *, struct stat *));
  233. #endif
  234. #ifdef NEED_PROTO_TRUNCATE
  235. extern int truncate __ARGS((const char *path, off_t length));
  236. #endif
  237. #ifdef NEED_PROTO_FTRUNCATE
  238. extern int ftruncate __ARGS((int fd, off_t length));
  239. #endif
  240. static const char *baseFilename __ARGS((const char *const path));
  241. static boolean isAbsolutePath __ARGS((const char *const path));
  242. static void combinePathAndFile __ARGS((char *const filePath, const char *const path, const char *const file));
  243. static const char *findExtension __ARGS((const char *const fileName));
  244. static boolean isFileHeader __ARGS((const char *const FileName));
  245. static boolean isExtensionInList __ARGS((const char *const extension, const char *const *const list));
  246. static langType getExtensionLanguage __ARGS((const char *const extension));
  247. static langType getFileLanguage __ARGS((const char *const FileName));
  248. static boolean isValidTagAddress __ARGS((const char *const tag));
  249. static boolean isCtagsLine __ARGS((const char *const tag));
  250. static boolean isEtagsLine __ARGS((const char *const tag));
  251. static boolean isTagFile __ARGS((const char *const filename));
  252. static void openTagFile __ARGS((const boolean toStdout));
  253. static void closeTagFile __ARGS((const boolean resize));
  254. static void beginEtagsFile __ARGS((void));
  255. static void endEtagsFile __ARGS((const char *const name));
  256. static boolean createTagsForFile __ARGS((const char *const filePath, const langType language));
  257. static const char *getNextListFile __ARGS((FILE *const fp));
  258. static const char *sourceFilePath __ARGS((const char *const file));
  259. static boolean createTagsWithFallback __ARGS((const char *const fileName, const langType language));
  260. static boolean createTagsForDirectory __ARGS((const char *const dirName));
  261. static boolean createTagsForEntry __ARGS((const char *const entryName));
  262. static boolean createTagsForList __ARGS((const char *const listFile));
  263. static boolean createTagsForArgs __ARGS((const char *const *const argList));
  264. static void printTotals __ARGS((const clock_t *const timeStamps));
  265. static void makeTags __ARGS((const char *const *const argList));
  266. static void setExecutableName __ARGS((const char *const path));
  267. static void setDefaultTagFileName __ARGS((void));
  268. static void setOptionDefaults __ARGS((void));
  269. static void testEtagsInvocation __ARGS((void));
  270. extern int main __ARGS((int argc, char **argv));
  271. /*============================================================================
  272. =   Function definitions
  273. ============================================================================*/
  274. #ifdef ENABLE_STDARG
  275. extern void error( const errorSelection selection,
  276.    const char *const format, ... )
  277. #else
  278. extern void error( va_alist )
  279.     va_dcl
  280. #endif
  281. {
  282.     va_list ap;
  283. #ifdef ENABLE_STDARG
  284.     va_start(ap, format);
  285. #else
  286.     const char *format;
  287.     errorSelection selection;
  288.     va_start(ap);
  289.     selection = va_arg(ap, errorSelection);
  290.     format = va_arg(ap, char *);
  291. #endif
  292.     fprintf(errout, "%s: %s", getExecutableName(),
  293.     selected(selection, WARNING) ? "warning: " : "");
  294.     vfprintf(errout, format, ap);
  295.     if (selected(selection, PERROR))
  296. #ifdef HAVE_STRERROR
  297. fprintf(errout, " : %s", strerror(errno));
  298. #else
  299. perror(" ");
  300. #endif
  301.     fputs("n", errout);
  302.     va_end(ap);
  303.     if (selected(selection, FATAL))
  304. exit(1);
  305. }
  306. extern unsigned long getFileSize( name )
  307.     const char *const __unused__ name;
  308. {
  309.     struct stat fileStatus;
  310.     unsigned long size = 0;
  311.     if (stat(name, &fileStatus) == 0)
  312. size = fileStatus.st_size;
  313.     return size;
  314. }
  315. extern boolean isNormalFile( name )
  316.     const char *const name;
  317. {
  318.     struct stat fileStatus;
  319.     boolean isNormal = FALSE;
  320.     if (stat(name, &fileStatus) == 0)
  321. isNormal = (boolean)(S_ISLNK(fileStatus.st_mode) ||
  322.      S_ISREG(fileStatus.st_mode)  );
  323.     return isNormal;
  324. }
  325. extern boolean isDirectory( name )
  326.     const char *const name;
  327. {
  328.     boolean isDir = FALSE;
  329. #ifdef AMIGA
  330.     struct FileInfoBlock *const fib = (struct FileInfoBlock *)
  331. malloc(sizeof(struct FileInfoBlock));
  332.     if (fib != NULL)
  333.     {
  334. const BPTR flock = Lock((UBYTE *)name, (long)ACCESS_READ);
  335. if (flock != (BPTR)NULL)
  336. {
  337.     if (Examine(flock, fib))
  338. isDir = ((fib->fib_DirEntryType >= 0) ? TRUE : FALSE);
  339.     UnLock(flock);
  340. }
  341. free(fib);
  342.     }
  343. #else
  344.     struct stat fileStatus;
  345.     if (stat(name, &fileStatus) == 0)
  346. isDir = (boolean)S_ISDIR(fileStatus.st_mode);
  347. #endif
  348.     return isDir;
  349. }
  350. extern boolean doesFileExist( fileName )
  351.     const char *const fileName;
  352. {
  353.     struct stat fileStatus;
  354.     return (boolean)(stat(fileName, &fileStatus) == 0);
  355. }
  356. extern void addTotals( files, lines, bytes )
  357.     const unsigned int files;
  358.     const unsigned long lines;
  359.     const unsigned long bytes;
  360. {
  361.     Totals.files += files;
  362.     Totals.lines += lines;
  363.     Totals.bytes += bytes;
  364. }
  365. /*----------------------------------------------------------------------------
  366.  *  Pathname manipulation (O/S dependent!!!)
  367.  *--------------------------------------------------------------------------*/
  368. static const char *baseFilename( filePath )
  369.     const char *const filePath;
  370. {
  371. #if defined(MSDOS) || defined(WIN32) || defined(OS2)
  372.     const char *tail = NULL;
  373.     unsigned int i;
  374.     /*  Find whichever of the path delimiters is last.
  375.      */
  376.     for (i = 0  ;  i < strlen(DosPathDelimiters)  ;  ++i)
  377.     {
  378. const char *sep = strrchr(filePath, DosPathDelimiters[i]);
  379. if (sep > tail)
  380.     tail = sep;
  381.     }
  382. #else
  383.     const char *tail = strrchr(filePath, PATH_SEPARATOR);
  384. #endif
  385.     if (tail == NULL)
  386. tail = filePath;
  387.     else
  388. ++tail; /* step past last delimiter */
  389.     return tail;
  390. }
  391. static boolean isAbsolutePath( path )
  392.     const char *const path;
  393. {
  394. #if defined(MSDOS) || defined(WIN32) || defined(OS2)
  395.     return (strchr(DosPathDelimiters, path[0]) != NULL);
  396. #else
  397.     return (boolean)(path[0] == PATH_SEPARATOR);
  398. #endif
  399. }
  400. static void combinePathAndFile( filePath, path, file )
  401.     char *const filePath;
  402.     const char *const path;
  403.     const char *const file;
  404. {
  405. #if defined(MSDOS) || defined(WIN32) || defined(OS2)
  406.     if (strchr(DosPathDelimiters, path[strlen(path)-1]) != NULL)
  407. sprintf(filePath, "%s%s", path, file);
  408.     else
  409. sprintf(filePath, "%s%c%s", path, PATH_SEPARATOR, file);
  410. #else
  411.     if ((path[strlen(path) - 1]) == PATH_SEPARATOR)
  412. sprintf(filePath, "%s%s", path, file);
  413.     else
  414. sprintf(filePath, "%s%c%s", path, PATH_SEPARATOR, file);
  415. #endif
  416. }
  417. /*----------------------------------------------------------------------------
  418.  *  File extension and language handling
  419.  *--------------------------------------------------------------------------*/
  420. static const char *findExtension( fileName )
  421.     const char *const fileName;
  422. {
  423.     const char *extension;
  424.     const char *const start = strrchr(fileName, '.'); /* find last '.' */
  425.     if (start == NULL)
  426. extension = "";
  427.     else
  428. extension = start + 1; /* skip to character after '.' */
  429.     return extension;
  430. }
  431. /*  Determines whether the specified file name is considered to be a header
  432.  *  file for the purposes of determining whether enclosed tags are global or
  433.  *  static.
  434.  */
  435. static boolean isFileHeader( fileName )
  436.     const char *const fileName;
  437. {
  438.     const char *const extension = findExtension(fileName);
  439.     boolean header = FALSE;     /* default unless match found */
  440.     int i;
  441.     for (i = 0 ; Option.headerExt[i] != NULL ; ++i)
  442.     {
  443. if (strcmp(Option.headerExt[i], extension) == 0)
  444. {
  445.     header = TRUE;     /* found in list */
  446.     break;
  447. }
  448.     }
  449.     return header;
  450. }
  451. static boolean isExtensionInList( extension, list )
  452.     const char *const extension;
  453.     const char *const *const list;
  454. {
  455.     boolean isKnown = FALSE;
  456.     if (list != NULL)
  457.     {
  458. const char *const *pExtension = list;
  459. while (! isKnown  &&  *pExtension != NULL)
  460. {
  461. #if defined(MSDOS) || defined(WIN32) || defined(OS2)
  462.     if (strequiv(extension, *pExtension))
  463. #else
  464.     if (strcmp(extension, *pExtension) == 0)
  465. #endif
  466. isKnown = TRUE;
  467.     ++pExtension;
  468. }
  469.     }
  470.     return isKnown;
  471. }
  472. static langType getExtensionLanguage( extension )
  473.     const char *const extension;
  474. {
  475.     unsigned int i;
  476.     langType language = LANG_IGNORE;
  477.     for (i = 0  ;  i < (int)LANG_COUNT  ;  ++i)
  478.     {
  479. if (isExtensionInList(extension, Option.langMap[i]))
  480. {
  481.     language = (langType)i;
  482.     break;
  483. }
  484.     }
  485.     return language;
  486. }
  487. static langType getFileLanguage( fileName )
  488.     const char *const fileName;
  489. {
  490.     const char *const extension = findExtension(fileName);
  491.     langType language;
  492.     if (Option.language != LANG_AUTO)
  493. language = Option.language;
  494.     else if (isFileHeader(fileName))
  495. language = LANG_CPP;
  496.     else if (extension[0] == '')
  497. language = LANG_IGNORE; /* ignore files with no extension */
  498.     else
  499. language = getExtensionLanguage(extension);
  500.     return language;
  501. }
  502. /*----------------------------------------------------------------------------
  503.  *  Tag file management
  504.  *--------------------------------------------------------------------------*/
  505. static boolean isValidTagAddress( excmd )
  506.     const char *const excmd;
  507. {
  508.     boolean isValid = FALSE;
  509.     if (strchr("/?", excmd[0]) != NULL)
  510. isValid = TRUE;
  511.     else
  512.     {
  513. char *address = (char *)malloc(strlen(excmd) + 1);
  514. if (address != NULL)
  515. {
  516.     if (sscanf(excmd, "%[^;n]", address) == 1  &&
  517. strspn(address,"0123456789") == strlen(address))
  518.     isValid = TRUE;
  519.     free(address);
  520. }
  521.     }
  522.     return isValid;
  523. }
  524. static boolean isCtagsLine( line )
  525.     const char *const line;
  526. {
  527.     enum fieldList { TAG, TAB1, SRC_FILE, TAB2, EXCMD, NUM_FIELDS };
  528.     boolean ok = FALSE; /* we assume not unless confirmed */
  529.     const size_t fieldLength = strlen(line) + 1;
  530.     char *const fields = (char *)malloc((size_t)NUM_FIELDS * fieldLength);
  531.     if (fields == NULL)
  532. error(FATAL, "Cannot analyze tag file");
  533.     else
  534.     {
  535. #define field(x) (fields + ((size_t)(x) * fieldLength))
  536. const int numFields = sscanf(line, "%[^t]%[t]%[^t]%[t]%[^n]",
  537.      field(TAG), field(TAB1), field(SRC_FILE),
  538.      field(TAB2), field(EXCMD));
  539. /*  There must be exactly five fields: two tab fields containing
  540.  *  exactly one tab each, the tag must not begin with "#", and the
  541.  *  file name should not end with ";", and the excmd must be
  542.  *  accceptable.
  543.  *
  544.  *  These conditions will reject tag-looking lines like:
  545.  *      int a; <C-comment>
  546.  *      #define LABEL <C-comment>
  547.  */
  548. if (numFields == NUM_FIELDS   &&
  549.     strlen(field(TAB1)) == 1  &&
  550.     strlen(field(TAB2)) == 1  &&
  551.     field(TAG)[0] != '#'      &&
  552.     field(SRC_FILE)[strlen(field(SRC_FILE)) - 1] != ';'  &&
  553.     isValidTagAddress(field(EXCMD)))
  554. ok = TRUE;
  555. free(fields);
  556.     }
  557.     return ok;
  558. }
  559. static boolean isEtagsLine( line )
  560.     const char *const line;
  561. {
  562.     const char *const magic = "fn";
  563.     return (boolean)(strncmp(line, magic, strlen(magic)) == 0);
  564. }
  565. static boolean isTagFile( filename )
  566.     const char *const filename;
  567. {
  568.     boolean ok = FALSE; /* we assume not unless confirmed */
  569.     FILE *const fp = fopen(filename, "r");
  570.     if (fp == NULL  &&  errno == ENOENT)
  571. ok = TRUE;
  572.     else if (fp != NULL)
  573.     {
  574. const char *line = readLine(&TagFile.line, fp);
  575. if (line == NULL)
  576.     ok = TRUE;
  577. else if (Option.etags) /* check for etags magic number */
  578.     ok = isEtagsLine(line);
  579. else
  580.     ok = isCtagsLine(line);
  581. fclose(fp);
  582.     }
  583.     return ok;
  584. }
  585. static void openTagFile( toStdout )
  586.     const boolean toStdout;
  587. {
  588.     static char tempName[L_tmpnam];
  589.     /*  Open the tags File.
  590.      */
  591.     if (toStdout)
  592.     {
  593. TagFile.name = tmpnam(tempName);
  594. TagFile.fp = fopen(TagFile.name, "w");
  595.     }
  596.     else
  597.     {
  598. const char *const fname  = Option.tagFileName;
  599. const boolean fileExists = doesFileExist(fname);
  600. TagFile.name = fname;
  601. if (fileExists  &&  ! isTagFile(fname))
  602.     error(FATAL,
  603.       ""%s" doesn't look like a tag file; I refuse to overwrite it.",
  604.   fname);
  605. if (Option.append  &&  fileExists)
  606. {
  607.     TagFile.fp = fopen(fname, "r+");
  608.     if (TagFile.fp != NULL)
  609.     {
  610. TagFile.numTags.prev = updatePseudoTags();
  611. fseek(TagFile.fp, 0L, SEEK_END);
  612.     }
  613. }
  614. else
  615. {
  616.     TagFile.fp = fopen(fname, "w");
  617.     if (TagFile.fp != NULL)
  618. addPseudoTags();
  619. }
  620.     }
  621.     if (TagFile.fp == NULL)
  622.     {
  623. error(FATAL | PERROR, "cannot open tag file");
  624. exit(1);
  625.     }
  626. }
  627. #ifdef USE_REPLACEMENT_TRUNCATE
  628. static int copyChars __ARGS((FILE *const fpFrom, FILE *const fpTo, const off_t size));
  629. static int copyFile __ARGS((const char *const from, const char *const to, const off_t size));
  630. static int replacementTruncate __ARGS((const char *const name, const off_t size));
  631. static int copyChars( fpFrom, fpTo, size )
  632.     FILE *const fpFrom;
  633.     FILE *const fpTo;
  634.     const off_t size;
  635. {
  636.     off_t count;
  637.     int result = -1;
  638.     for (count = 0  ;  count < size  ;  ++count)
  639.     {
  640. const int c = getc(fpFrom);
  641. if (c == EOF  ||  putc(c, fpTo) == EOF)
  642.     break;
  643.     }
  644.     if (count == size)
  645. result = 0;
  646.     return result;
  647. }
  648. static int copyFile( from, to, size )
  649.     const char *const from;
  650.     const char *const to;
  651.     const off_t size;
  652. {
  653.     int result = -1;
  654.     FILE *const fpFrom = fopen(from, "r");
  655.     if (fpFrom != NULL)
  656.     {
  657. FILE *const fpTo = fopen(to, "w");
  658. if (fpFrom != NULL)
  659. {
  660.     result = copyChars(fpFrom, fpTo, size);
  661.     if (result == 0)
  662. result = fclose(fpTo);
  663. }
  664. if (result == 0)
  665.     result = fclose(fpFrom);
  666.     }
  667.     return result;
  668. }
  669. /*  Replacement for missing library function.
  670.  */
  671. static int replacementTruncate( name, size )
  672.     const char *const name;
  673.     const off_t size;
  674. {
  675.     char tempName[L_tmpnam];
  676.     int result = -1;
  677.     if (tmpnam(tempName) != NULL)
  678.     {
  679. result = copyFile(name, tempName, size);
  680. if (result == 0)
  681.     result = copyFile(tempName, name, size);
  682. if (result == 0)
  683.     result = remove(tempName);
  684.     }
  685.     return result;
  686. }
  687. #endif
  688. static void closeTagFile( resize )
  689.     const boolean resize;
  690. {
  691.     const long __unused__ size = ftell(TagFile.fp);
  692.     fclose(TagFile.fp);
  693.     if (resize)
  694.     {
  695. int __unused__ result = 0;
  696. #ifdef USE_REPLACEMENT_TRUNCATE
  697. result = replacementTruncate(TagFile.name, (off_t)size);
  698. #else
  699. # ifdef HAVE_TRUNCATE
  700. result = truncate(TagFile.name, (off_t)size);
  701. # else
  702. const int fd = open(TagFile.name, O_RDWR);
  703. if (fd != -1)
  704. {
  705. #  ifdef HAVE_FTRUNCATE
  706.     result = ftruncate(fd, (off_t)size);
  707. #  else
  708. #   ifdef HAVE_CHSIZE
  709.     result = chsize(fd, size);
  710. #   endif
  711. #  endif
  712.     close(fd);
  713. }
  714. # endif
  715. #endif
  716. if (result == -1)
  717.     fprintf(errout, "Cannot shorten tag file: errno = %dn", errno);
  718.     }
  719. }
  720. /*----------------------------------------------------------------------------
  721.  * Create tags
  722.  *--------------------------------------------------------------------------*/
  723. static void beginEtagsFile()
  724. {
  725.     tmpnam(TagFile.etags.name);
  726.     TagFile.etags.fp = fopen(TagFile.etags.name, "w+b");
  727.     if (TagFile.etags.fp == NULL)
  728. error(FATAL | PERROR, "cannot open "%s"", TagFile.etags.name);
  729.     TagFile.etags.byteCount = 0;
  730. }
  731. static void endEtagsFile( name )
  732.     const char *const name;
  733. {
  734.     const char *line;
  735.     fprintf(TagFile.fp, "fn%s,%ldn", name, (long)TagFile.etags.byteCount);
  736.     if (TagFile.etags.fp != NULL)
  737.     {
  738. rewind(TagFile.etags.fp);
  739. while ((line = readLine(&TagFile.line, TagFile.etags.fp)) != NULL)
  740.     fputs(line, TagFile.fp);
  741. fclose(TagFile.etags.fp);
  742. remove(TagFile.etags.name);
  743.     }
  744.     TagFile.etags.name[0] = '';
  745. }
  746. static boolean createTagsForFile( filePath, language )
  747.     const char *const filePath;
  748.     const langType language;
  749. {
  750.     boolean ok;
  751.     if (filePath == NULL)
  752. ok = FALSE;
  753.     else
  754.     {
  755. const boolean isHeader = isFileHeader(filePath);
  756. ok = TRUE;
  757. if (Option.etags)
  758.     beginEtagsFile();
  759. if (cppOpen(filePath, language, isHeader))
  760. {
  761.     tagInfo tag;
  762.     DebugStatement( clearString(tag.name, MaxNameLength); )
  763.     strcpy(tag.name, baseFilename(filePath));
  764.     tag.location   = 0;
  765.     tag.lineNumber = 1;
  766.     makeTag(&tag, &NoClass, SCOPE_GLOBAL, TAG_SOURCE_FILE);
  767.     ok = createTags(0, NULL);
  768.     cppClose();
  769. }
  770. if (Option.etags)
  771.     endEtagsFile(filePath);
  772.     }
  773.     return ok;
  774. }
  775. static const char *getNextListFile( fp )
  776.     FILE *const fp;
  777. {
  778.     static char fileName[PATH_MAX + 1];
  779.     const char *const buf = fgets(fileName, PATH_MAX + 1, fp);
  780.     if (buf != NULL)
  781.     {
  782. char *const newline = strchr(fileName, 'n');
  783. if (newline == NULL)
  784.     fileName[PATH_MAX] = '';
  785. else
  786.     *newline = '';
  787.     }
  788.     return buf;
  789. }
  790. static const char *sourceFilePath( file )
  791.     const char *const file;
  792. {
  793.     const char *result = NULL;
  794.     if (Option.path == NULL  ||  isAbsolutePath(file))
  795. result = file;
  796.     else if (strlen(Option.path) + strlen(file) < (size_t)PATH_MAX)
  797.     {
  798. static char filePath[PATH_MAX + 1];
  799. combinePathAndFile(filePath, Option.path, file);
  800. result = filePath;
  801.     }
  802.     return result;
  803. }
  804. static boolean createTagsWithFallback( fileName, language )
  805.     const char *const fileName;
  806.     const langType language;
  807. {
  808.     const char *const filePath = sourceFilePath(fileName);
  809.     const unsigned long numTags = TagFile.numTags.added;
  810.     const long tagFilePosition = ftell(TagFile.fp);
  811.     boolean resize = FALSE;
  812.     if (! createTagsForFile(filePath, language))
  813.     {
  814. /*  Restore prior state of tag file.
  815.  */
  816. fseek(TagFile.fp, tagFilePosition, SEEK_SET);
  817. TagFile.numTags.added = numTags;
  818. DebugStatement( debugPrintf(DEBUG_STATUS,
  819. "%s: formatting error; retryingn", fileName); )
  820. Option.braceFormat = TRUE;
  821.     createTagsForFile(filePath, language);
  822. Option.braceFormat = FALSE;
  823. resize = TRUE;
  824.     }
  825.     return resize;
  826. }
  827. # if defined(MSDOS) || defined(WIN32)
  828. static boolean createTagsForMatchingEntries __ARGS((char *const pattern));
  829. static boolean createTagsForMatchingEntries( pattern )
  830.     char *const pattern;
  831. {
  832.     boolean resize = FALSE;
  833.     const size_t tailIndex = baseFilename(pattern) - (const char *)pattern;
  834.     char *const tail = pattern + tailIndex;
  835. #ifdef HAVE_FINDFIRST
  836.     struct ffblk fileInfo;
  837.     int result = findfirst(pattern, &fileInfo, FA_DIREC);
  838.     while (result == 0)
  839.     {
  840. const char *const entryName = fileInfo.ff_name;
  841. /*  We must not recurse into the directories "." or "..".
  842.  */
  843. if (strcmp(entryName, ".") != 0  &&  strcmp(entryName, "..") != 0)
  844. {
  845.     strcpy(tail, entryName);
  846.     resize |= createTagsForEntry(pattern);
  847. }
  848. result = findnext(&fileInfo);
  849.     }
  850. #else
  851. # ifdef HAVE__FINDFIRST
  852.     struct _finddata_t fileInfo;
  853.     long hFile = _findfirst(pattern, &fileInfo);
  854.     if (hFile != -1L)
  855.     {
  856. do
  857. {
  858.     const char *const entryName = fileInfo.name;
  859.     /*  We must not recurse into the directories "." or "..".
  860.      */
  861.     if (strcmp(entryName, ".") != 0  &&  strcmp(entryName, "..") != 0)
  862.     {
  863. strcpy(tail, entryName);
  864. resize |= createTagsForEntry(pattern);
  865.     }
  866. } while (_findnext(hFile, &fileInfo) == 0);
  867. _findclose(hFile);
  868.     }
  869. # endif /* HAVE__FINDFIRST */
  870. #endif /* HAVE_FINDFIRST */
  871.     return resize;
  872. }
  873. #else
  874. # ifdef AMIGA
  875. static boolean createTagsForMatchingEntries __ARGS((char *const pattern));
  876. static boolean createTagsForMatchingEntries( pattern )
  877.     char *const pattern;
  878. {
  879.     boolean resize = FALSE;
  880.     struct AnchorPath *const anchor = (struct AnchorPath *)
  881. calloc((size_t)1, (size_t)ANCHOR_SIZE);
  882.     if (anchor != NULL)
  883.     {
  884. LONG result;
  885. anchor->ap_Strlen = ANCHOR_BUF_SIZE; /* ap_Length no longer supported */
  886. /*  Allow '.' for current directory.
  887.  */
  888. #ifdef APF_DODOT
  889. anchor->ap_Flags = APF_DODOT | APF_DOWILD;
  890. #else
  891. anchor->ap_Flags = APF_DoDot | APF_DoWild;
  892. #endif
  893. result = MatchFirst((UBYTE *)pattern, anchor);
  894. while (result == 0)
  895. {
  896.     resize |= createTagsForEntry((char *)anchor->ap_Buf);
  897.     result = MatchNext(anchor);
  898. }
  899. MatchEnd(anchor);
  900. free(anchor);
  901.     }
  902.     return resize;
  903. }
  904. # endif /* AMIGA */
  905. #endif /* MSDOS || WIN32 */
  906. static boolean createTagsForDirectory( dirName )
  907.     const char *const dirName;
  908. {
  909. #ifdef HAVE_OPENDIR
  910.     boolean resize = FALSE;
  911.     DIR *const dir = opendir(dirName);
  912.     if (dir == NULL)
  913. error(WARNING, "");
  914.     else
  915.     {
  916. struct dirent *entry;
  917. DebugStatement( debugPrintf(DEBUG_STATUS, "RECURSING into %s%cn",
  918.     dirName, PATH_SEPARATOR); )
  919. while ((entry = readdir(dir)) != NULL)
  920. {
  921.     const char *const entryName = entry->d_name;
  922.     if (strcmp(entryName, ".")    != 0  &&
  923. strcmp(entryName, "..")   != 0  &&
  924. strcmp(entryName, "SCCS") != 0   )
  925.     {
  926. char filePath[PATH_MAX + 1];
  927. combinePathAndFile(filePath, dirName, entryName);
  928. resize |= createTagsForEntry(filePath);
  929.     }
  930. }
  931. closedir(dir);
  932.     }
  933.     return resize;
  934. #else
  935. # if defined(MSDOS) || defined(WIN32)
  936.     char pattern[PATH_MAX + 1];
  937.     boolean resize;
  938.     DebugStatement( debugPrintf(DEBUG_STATUS, "RECURSING into %s%cn",
  939. dirName, PATH_SEPARATOR); )
  940.     strcpy(pattern, dirName);
  941.     strcat(pattern, "\*.*");
  942.     resize = createTagsForMatchingEntries(pattern);
  943.     return resize;
  944. # else
  945. #  ifdef AMIGA
  946.     char pattern[PATH_MAX + 1];
  947.     boolean resize = FALSE;
  948.     DebugStatement( debugPrintf(DEBUG_STATUS, "RECURSING into %s%cn",
  949. dirName, PATH_SEPARATOR); )
  950.     strcpy(pattern, dirName);
  951.     if (pattern[0] != '')
  952. strcat(pattern, "/");     /* "/#?" searches one directory upwards */
  953.     strcat(pattern, "#?");
  954.     resize = createTagsForMatchingEntries(pattern);
  955.     return resize;
  956. #  else /* !AMIGA */
  957.     return FALSE;
  958. #  endif
  959. # endif
  960. #endif /* HAVE_OPENDIR */
  961. }
  962. static boolean createTagsForEntry( entryName )
  963.     const char *const entryName;
  964. {
  965.     boolean resize = FALSE;
  966.     langType language;
  967.     if (! doesFileExist(entryName))
  968. error(WARNING | PERROR, "cannot open "%s"", entryName);
  969.     else if (isDirectory(entryName))
  970.     {
  971. if (Option.recurse)
  972.     resize = createTagsForDirectory(entryName);
  973. else
  974. {
  975.     DebugStatement( debugPrintf(DEBUG_STATUS,"  no recurse into %s%cn",
  976. entryName, PATH_SEPARATOR) );
  977.     resize = FALSE;
  978. }
  979.     }
  980.     else if (! isNormalFile(entryName))
  981.     {
  982. DebugStatement( if (debug(DEBUG_STATUS))
  983. printf("  ignoring %s (special file)n", entryName) );
  984.     }
  985.     else if ((language = getFileLanguage(entryName)) == LANG_IGNORE)
  986. DebugStatement( debugOpen(entryName, FALSE, language) );
  987.     else
  988.     {
  989. resize = createTagsWithFallback(entryName, language);
  990. addTotals(1, 0L, 0L);
  991.     }
  992.     return resize;
  993. }
  994. static boolean createTagsForArgs( argList )
  995.     const char *const *const argList;
  996. {
  997.     boolean resize = FALSE;
  998.     const char *const *pArg;
  999.     /*  Generate tags for each argument on the command line.
  1000.      */
  1001.     for (pArg = argList  ;  *pArg != NULL  ;  ++pArg)
  1002.     {
  1003. const char *arg = *pArg;
  1004. #if defined(MSDOS) || defined(WIN32)
  1005. char pattern[PATH_MAX + 1];
  1006. strcpy(pattern, arg);
  1007. /*  We must transform the "." and ".." forms into something that can
  1008.  *  be expanded by the MSDOS/Windows functions.
  1009.  */
  1010. if (Option.recurse  &&
  1011.     (strcmp(pattern, ".") == 0  ||  strcmp(pattern, "..") == 0))
  1012. {
  1013.     strcat(pattern, "\*.*");
  1014. }
  1015. resize |= createTagsForMatchingEntries(pattern);
  1016. #else
  1017. resize |= createTagsForEntry(arg);
  1018. #endif
  1019.     }
  1020.     return resize;
  1021. }
  1022. /*  Create tags for the source files listed in the file specified by
  1023.  *  "listFile".
  1024.  */
  1025. static boolean createTagsForList( listFile )
  1026.     const char *const listFile;
  1027. {
  1028.     boolean resize = FALSE;
  1029.     FILE *const fp = (strcmp(listFile,"-") == 0) ? stdin : fopen(listFile, "r");
  1030.     if (fp == NULL)
  1031. error(FATAL | PERROR, "cannot open "%s"", listFile);
  1032.     else
  1033.     {
  1034. const char *fileName = getNextListFile(fp);
  1035. while (fileName != NULL  &&  fileName[0] != '')
  1036. {
  1037.     resize |= createTagsForEntry(fileName);
  1038.     fileName = getNextListFile(fp);
  1039. }
  1040. if (fp != stdin)
  1041.     fclose(fp);
  1042.     }
  1043.     return resize;
  1044. }
  1045. #ifdef HAVE_CLOCK
  1046. # define CLOCK_AVAILABLE
  1047. # ifndef CLOCKS_PER_SEC
  1048. #  define CLOCKS_PER_SEC 1000000
  1049. # endif
  1050. #else
  1051. # ifdef HAVE_TIMES
  1052. #  define CLOCK_AVAILABLE
  1053. #  define CLOCKS_PER_SEC 60
  1054. static clock_t clock __ARGS((void));
  1055. static clock_t clock()
  1056. {
  1057.     struct tms buf;
  1058.     times(&buf);
  1059.     return (buf.tms_utime + buf.tms_stime);
  1060. }
  1061. # else
  1062. #  define clock()  (clock_t)0
  1063. # endif
  1064. #endif
  1065. static void printTotals( timeStamps )
  1066.     const clock_t *const timeStamps;
  1067. {
  1068.     const unsigned long totalTags = TagFile.numTags.added +
  1069.     TagFile.numTags.prev;
  1070.     fprintf(errout, "%ld file%s, %ld line%s (%ld kB) scanned",
  1071.     Totals.files, plural(Totals.files),
  1072.     Totals.lines, plural(Totals.lines),
  1073.     Totals.bytes/1024L);
  1074. #ifdef CLOCK_AVAILABLE
  1075.     {
  1076. const double interval = ((double)(timeStamps[1] - timeStamps[0])) /
  1077. CLOCKS_PER_SEC;
  1078. fprintf(errout, " in %.01f seconds", interval);
  1079. if (interval != (double)0.0)
  1080.     fprintf(errout, " (%lu kB/s)",
  1081.     (unsigned long)(Totals.bytes / interval) / 1024L);
  1082.     }
  1083. #endif
  1084.     fputc('n', errout);
  1085.     fprintf(errout, "%lu tag%s added to tag file",
  1086.     TagFile.numTags.added, plural(TagFile.numTags.added));
  1087.     if (Option.append)
  1088. fprintf(errout, " (now %lu tags)", totalTags);
  1089.     fputc('n', errout);
  1090.     if (totalTags > 0  &&  Option.sorted)
  1091.     {
  1092. fprintf(errout, "%lu tag%s sorted", totalTags, plural(totalTags));
  1093. #ifdef CLOCK_AVAILABLE
  1094. fprintf(errout, " in %.02f seconds",
  1095. ((double)(timeStamps[3] - timeStamps[2])) / CLOCKS_PER_SEC);
  1096. #endif
  1097. fputc('n', errout);
  1098.     }
  1099. #ifdef DEBUG
  1100.     fprintf(errout, "longest tag line = %lun",
  1101.     (unsigned long)TagFile.max.line);
  1102. #endif
  1103. }
  1104. static void makeTags( argList )
  1105.     const char *const *const argList;
  1106. {
  1107.     boolean toStdout = FALSE;
  1108.     boolean resize = FALSE;
  1109.     clock_t timeStamps[4];
  1110.     if (Option.xref  ||  strcmp(Option.tagFileName, "-") == 0
  1111. #if !defined(MSDOS) && !defined(WIN32) && !defined(OS2)
  1112. || strcmp(Option.tagFileName, "/dev/stdout") == 0
  1113. #endif
  1114. )
  1115. toStdout = TRUE;
  1116.     openTagFile(toStdout);
  1117.     if (Option.printTotals) timeStamps[0] = clock();
  1118.     if (Option.fileList != NULL)
  1119. resize = createTagsForList(Option.fileList);
  1120.     resize = (boolean)(createTagsForArgs(argList) || resize);
  1121.     if (Option.printTotals) timeStamps[1] = clock();
  1122.     closeTagFile(resize);
  1123.     freeIgnoreList();
  1124.     if (TagFile.numTags.added + TagFile.numTags.prev > 0L)
  1125.     {
  1126. if (Option.sorted)
  1127. {
  1128.     if (Option.printTotals) timeStamps[2] = clock();
  1129. #ifdef EXTERNAL_SORT
  1130.     externalSortTags(toStdout);
  1131. #else
  1132.     internalSortTags(toStdout);
  1133. #endif
  1134.     if (Option.printTotals) timeStamps[3] = clock();
  1135. }
  1136. else if (toStdout)
  1137.     catFile(TagFile.name);
  1138.     }
  1139.     if (toStdout)
  1140. remove(TagFile.name); /* remove temporary file */
  1141.     if (Option.printTotals)
  1142. printTotals(timeStamps);
  1143. }
  1144. /*----------------------------------------------------------------------------
  1145.  * Start up code
  1146.  *--------------------------------------------------------------------------*/
  1147. static void setExecutableName( path )
  1148.     const char *const path;
  1149. {
  1150.     ExecutableName = baseFilename(path);
  1151. }
  1152. extern const char *getExecutableName()
  1153. {
  1154.     return ExecutableName;
  1155. }
  1156. static void setDefaultTagFileName()
  1157. {
  1158.     if (Option.tagFileName != NULL)
  1159. ; /* accept given name */
  1160.     else if (Option.etags)
  1161. Option.tagFileName = ETAGS_FILE;
  1162.     else
  1163. Option.tagFileName = CTAGS_FILE;
  1164. }
  1165. static void setOptionDefaults()
  1166. {
  1167.     if (Option.xref)
  1168. Option.include.sourceFiles = FALSE;
  1169.     setDefaultTagFileName();
  1170. }
  1171. static void testEtagsInvocation()
  1172. {
  1173.     if (strncmp(getExecutableName(), ETAGS, strlen(ETAGS)) == 0)
  1174.     {
  1175. Option.startedAsEtags = TRUE;
  1176. Option.etags = TRUE;
  1177. Option.sorted = FALSE;
  1178.     }
  1179. }
  1180. extern int main( argc, argv )
  1181.     int __unused__ argc;
  1182.     char **argv;
  1183. {
  1184.     char **envArgList;
  1185.     const char *const *fileList;
  1186. #ifdef __EMX__
  1187.     _wildcard(&argc, &argv); /* expand wildcards in argument list */
  1188. #endif
  1189.     setExecutableName(*argv++);
  1190.     testEtagsInvocation();
  1191.     /*  Parse options.
  1192.      */
  1193.     envArgList = parseEnvironmentOptions();
  1194.     fileList = (const char *const *)parseOptions(argv);
  1195.     setOptionDefaults();
  1196.     buildKeywordHash();
  1197.     /* Generate tags if there is at least one source file or a file list.
  1198.      */
  1199.     if (*fileList == NULL  &&  Option.fileList == NULL)
  1200.     {
  1201. if (! Option.recurse)
  1202.     error(FATAL, "No files specified. Try "%s --help".",
  1203. getExecutableName());
  1204. else
  1205. {
  1206.     static const char *defaultRecursionDir[] = { ".", NULL };
  1207.     fileList = defaultRecursionDir;
  1208. }
  1209.     }
  1210.     makeTags(fileList);
  1211.     /*  Post tag generation clean-up.
  1212.      */
  1213.     freeLineBuffer(&TagFile.line);
  1214.     if (envArgList != NULL)
  1215. free(envArgList);
  1216.     exit(0);
  1217.     return 0;
  1218. }
  1219. /* vi:set tabstop=8 shiftwidth=4: */