r5098
[geany-mirror.git] / tagmanager / ctags.c
blob5a275137a3ccfa48965d3d4d550369d7ebab4371
1 /*
3 * Copyright (c) 1996-2001, Darren Hiebert
5 * Author: Darren Hiebert <darren@hiebert.com>, <darren@hiwaay.net>
6 * http://darren.hiebert.com
8 * This source code is released for free distribution under the terms of the
9 * GNU General Public License. It is provided on an as-is basis and no
10 * responsibility is accepted for its failure to perform as expected.
12 * This is a reimplementation of the ctags (1) program. It is an attempt to
13 * provide a fully featured ctags program which is free of the limitations
14 * which most (all?) others are subject to.
16 * This module contains top level start-up and portability functions.
20 * INCLUDE FILES
22 #include "general.h" /* must always come first */
23 #include <glib.h>
24 #include <glib/gstdio.h>
26 #ifdef HAVE_STDLIB_H
27 # include <stdlib.h> /* to declare malloc (), realloc () */
28 #endif
29 #include <string.h>
30 #include <errno.h>
31 #include <ctype.h>
33 #include <stdio.h> /* to declare SEEK_SET (hopefully) */
34 # include <fcntl.h> /* to declar O_RDWR, O_CREAT, O_EXCL */
35 # include <unistd.h> /* to declare mkstemp () */
37 #ifdef AMIGA
38 # include <dos/dosasl.h> /* for struct AnchorPath */
39 # include <clib/dos_protos.h> /* function prototypes */
40 # define ANCHOR_BUF_SIZE 512
41 # define ANCHOR_SIZE (sizeof (struct AnchorPath) + ANCHOR_BUF_SIZE)
42 # ifdef __SASC
43 extern struct DosLibrary *DOSBase;
44 # include <pragmas/dos_pragmas.h>
45 # endif
46 #endif
48 #include <stdarg.h>
52 /* To declare "struct stat" and stat ().
54 #if defined (HAVE_SYS_TYPES_H)
55 # include <sys/types.h>
56 #else
57 # if defined (HAVE_TYPES_H)
58 # include <types.h>
59 # endif
60 #endif
61 #ifdef HAVE_SYS_STAT_H
62 # include <sys/stat.h>
63 #else
64 # ifdef HAVE_STAT_H
65 # include <stat.h>
66 # endif
67 #endif
69 /* To provide directory searching for recursion feature.
71 #ifdef HAVE_DIRENT_H
72 # ifdef __BORLANDC__
73 # define boolean BORLAND_boolean
74 # endif
75 # include <dirent.h>
76 # undef boolean
77 #endif
78 #ifdef HAVE_DIRECT_H
79 # include <direct.h> /* to _getcwd */
80 #endif
81 #ifdef HAVE_DOS_H
82 # include <dos.h> /* to declare FA_DIREC */
83 #endif
84 #ifdef HAVE_DIR_H
85 # include <dir.h> /* to declare findfirst () and findnext () */
86 #endif
87 #ifdef HAVE_IO_H
88 # include <io.h> /* to declare _finddata_t in MSVC++ 4.x */
89 #endif
91 /* To provide timings features if available.
93 #ifdef HAVE_CLOCK
94 # ifdef HAVE_TIME_H
95 # include <time.h>
96 # endif
97 #else
98 # ifdef HAVE_TIMES
99 # ifdef HAVE_SYS_TIMES_H
100 # include <sys/times.h>
101 # endif
102 # endif
103 #endif
105 #include "entry.h"
106 #include "keyword.h"
107 #include "main.h"
108 #include "options.h"
109 #include "parse.h"
110 #include "read.h"
111 #include "vstring.h"
113 #ifdef TRAP_MEMORY_CALLS
114 # include "safe_malloc.h"
115 #endif
118 * MACROS
122 * Miscellaneous macros
124 #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
125 #define plural(value) (((unsigned long)(value) == 1L) ? "" : "s")
128 * Portability macros
130 #ifndef PATH_SEPARATOR
131 # if defined (MSDOS_STYLE_PATH)
132 # define PATH_SEPARATOR '\\'
133 # elif defined (QDOS)
134 # define PATH_SEPARATOR '_'
135 # else
136 # define PATH_SEPARATOR '/'
137 # endif
138 #endif
140 #if defined (MSDOS_STYLE_PATH) && defined (UNIX_PATH_SEPARATOR)
141 # define OUTPUT_PATH_SEPARATOR '/'
142 #else
143 # define OUTPUT_PATH_SEPARATOR PATH_SEPARATOR
144 #endif
146 /* File type tests.
148 #ifndef S_ISREG
149 # if defined (S_IFREG) && ! defined (AMIGA)
150 # define S_ISREG(mode) ((mode) & S_IFREG)
151 # else
152 # define S_ISREG(mode) TRUE /* assume regular file */
153 # endif
154 #endif
156 #ifndef S_ISLNK
157 # ifdef S_IFLNK
158 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
159 # else
160 # define S_ISLNK(mode) FALSE /* assume no soft links */
161 # endif
162 #endif
164 #ifndef S_ISDIR
165 # ifdef S_IFDIR
166 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
167 # else
168 # define S_ISDIR(mode) FALSE /* assume no soft links */
169 # endif
170 #endif
172 #ifndef S_IXUSR
173 # define S_IXUSR 0
174 #endif
175 #ifndef S_IXGRP
176 # define S_IXGRP 0
177 #endif
178 #ifndef S_IXOTH
179 # define S_IXOTH 0
180 #endif
182 #ifndef S_IRUSR
183 # define S_IRUSR 0400
184 #endif
185 #ifndef S_IWUSR
186 # define S_IWUSR 0200
187 #endif
189 /* Hack for rediculous practice of Microsoft Visual C++.
191 #if defined (WIN32)
192 # if defined (_MSC_VER)
193 # define stat _stat
194 # define getcwd _getcwd
195 # define PATH_MAX _MAX_PATH
196 # elif defined (__BORLANDC__)
197 # define PATH_MAX MAXPATH
198 # endif
199 #endif
201 #ifndef PATH_MAX
202 # define PATH_MAX 256
203 #endif
206 * DATA DEFINITIONS
208 #if defined (MSDOS_STYLE_PATH)
209 static const char PathDelimiters [] = ":/\\";
210 #elif defined (VMS)
211 static const char PathDelimiters [] = ":]>";
212 #endif
214 #ifndef TMPDIR
215 # define TMPDIR "/tmp"
216 #endif
218 char *CurrentDirectory = NULL;
219 #ifdef HAVE_MKSTEMP
220 static const char *ExecutableProgram = NULL;
221 #endif
222 static const char *ExecutableName = "geany";
223 static stringList* Excluded = NULL;
225 static struct { long files, lines, bytes; } Totals = { 0, 0, 0 };
227 #ifdef AMIGA
228 # include "ctags.h"
229 static const char *VERsion = "$VER: "PROGRAM_NAME" "PROGRAM_VERSION" "
230 # ifdef __SASC
231 __AMIGADATE__
232 # else
233 __DATE__
234 # endif
235 " "AUTHOR_NAME" $";
236 #endif
239 * FUNCTION PROTOTYPES
241 #ifdef NEED_PROTO_STAT
242 extern int stat (const char *, struct stat *);
243 #endif
244 #ifdef NEED_PROTO_LSTAT
245 extern int lstat (const char *, struct stat *);
246 #endif
248 #if 0
249 static boolean createTagsForEntry (const char *const entryName);
250 #endif
253 * FUNCTION DEFINITIONS
256 extern const char *getExecutableName (void)
258 return ExecutableName;
261 #if 0
262 static void setCurrentDirectory (void)
264 #ifdef AMIGA
265 char* const cwd = eStrdup (".");
266 #else
267 char* const cwd = getcwd (NULL, PATH_MAX);
268 #endif
269 CurrentDirectory = xMalloc (strlen (cwd) + 2, char);
270 if (cwd [strlen (cwd) - (size_t) 1] == PATH_SEPARATOR)
271 strcpy (CurrentDirectory, cwd);
272 else
273 sprintf (CurrentDirectory, "%s%c", cwd, OUTPUT_PATH_SEPARATOR);
274 free (cwd);
276 #endif
278 extern void error (const errorSelection selection,
279 const char *const format, ...)
281 va_list ap;
283 va_start (ap, format);
284 fprintf (errout, "%s: %s", getExecutableName (),
285 selected (selection, WARNING) ? "Warning: " : "");
286 vfprintf (errout, format, ap);
287 if (selected (selection, PERROR))
288 fprintf (errout, " : %s", g_strerror (errno));
289 fputs ("\n", errout);
290 va_end (ap);
291 if (selected (selection, FATAL))
292 exit (1);
295 #ifndef HAVE_STRICMP
296 extern int stricmp (const char *s1, const char *s2)
298 int result;
301 result = toupper ((int) *s1) - toupper ((int) *s2);
302 } while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
303 return result;
305 #endif
307 #ifndef HAVE_STRNICMP
308 extern int strnicmp (const char *s1, const char *s2, size_t n)
310 int result;
313 result = toupper ((int) *s1) - toupper ((int) *s2);
314 } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
315 return result;
317 #endif
319 #ifndef HAVE_STRSTR
320 extern char* strstr (const char *str, const char *substr)
322 const size_t length = strlen (substr);
323 const char *match = NULL;
324 const char *p;
326 for (p = str ; *p != '\0' && match == NULL ; ++p)
327 if (strncmp (p, substr, length) == 0)
328 match = p;
329 return (char*) match;
331 #endif
333 extern char* eStrdup (const char* str)
335 char* result = xMalloc (strlen (str) + 1, char);
336 strcpy (result, str);
337 return result;
340 extern void *eMalloc (const size_t size)
342 void *buffer = g_malloc (size);
344 if (buffer == NULL)
345 error (FATAL, "out of memory");
347 return buffer;
350 extern void *eCalloc (const size_t count, const size_t size)
352 void *buffer = calloc (count, size);
354 if (buffer == NULL)
355 error (FATAL, "out of memory");
357 return buffer;
360 extern void *eRealloc (void *const ptr, const size_t size)
362 void *buffer;
363 if (ptr == NULL)
364 buffer = eMalloc (size);
365 else
367 buffer = g_realloc (ptr, size);
368 if (buffer == NULL)
369 error (FATAL, "out of memory");
371 return buffer;
374 extern void eFree (void *const ptr)
376 if (ptr != NULL)
377 free (ptr);
380 extern void toLowerString (char* str)
382 while (*str != '\0')
384 *str = tolower ((int) *str);
385 ++str;
389 extern void toUpperString (char* str)
391 while (*str != '\0')
393 *str = toupper ((int) *str);
394 ++str;
398 /* Newly allocated string containing lower case conversion of a string.
400 extern char* newLowerString (const char* str)
402 char* const result = xMalloc (strlen (str) + 1, char);
403 int i = 0;
405 result [i] = tolower ((int) str [i]);
406 while (str [i++] != '\0');
407 return result;
410 /* Newly allocated string containing upper case conversion of a string.
412 extern char* newUpperString (const char* str)
414 char* const result = xMalloc (strlen (str) + 1, char);
415 int i = 0;
417 result [i] = toupper ((int) str [i]);
418 while (str [i++] != '\0');
419 return result;
422 extern long unsigned int getFileSize (const char *const name)
424 struct stat fileStatus;
425 unsigned long size = 0;
427 if (g_stat (name, &fileStatus) == 0)
428 size = fileStatus.st_size;
430 return size;
433 #if 0
434 static boolean isSymbolicLink (const char *const name)
436 #if defined (MSDOS) || defined (WIN32) || defined (VMS) || defined (__EMX__) || defined (AMIGA)
437 return FALSE;
438 #else
439 struct stat fileStatus;
440 boolean result = FALSE;
442 if (g_lstat (name, &fileStatus) == 0)
443 result = (boolean) (S_ISLNK (fileStatus.st_mode));
445 return result;
446 #endif
449 static boolean isNormalFile (const char *const name)
451 struct stat fileStatus;
452 boolean result = FALSE;
454 if (g_stat (name, &fileStatus) == 0)
455 result = (boolean) (S_ISREG (fileStatus.st_mode));
457 return result;
459 #endif
461 extern boolean isExecutable (const char *const name)
463 struct stat fileStatus;
464 boolean result = FALSE;
466 if (g_stat (name, &fileStatus) == 0)
467 result = (boolean) ((fileStatus.st_mode & (S_IXUSR|S_IXGRP|S_IXOTH)) != 0);
469 return result;
472 extern boolean isSameFile (const char *const name1, const char *const name2)
474 boolean result = FALSE;
475 #ifdef HAVE_STAT_ST_INO
476 struct stat stat1, stat2;
478 if (g_stat (name1, &stat1) == 0 && g_stat (name2, &stat2) == 0)
479 result = (boolean) (stat1.st_ino == stat2.st_ino);
480 #endif
481 return result;
484 #ifdef HAVE_MKSTEMP
486 static boolean isSetUID (const char *const name)
488 #if defined (VMS) || defined (MSDOS) || defined (WIN32) || defined (__EMX__) || defined (AMIGA)
489 return FALSE;
490 #else
491 struct stat fileStatus;
492 boolean result = FALSE;
494 if (g_stat (name, &fileStatus) == 0)
495 result = (boolean) ((fileStatus.st_mode & S_ISUID) != 0);
497 return result;
498 #endif
501 #endif
503 #if 0
504 static boolean isDirectory (const char *const name)
506 boolean result = FALSE;
507 #ifdef AMIGA
508 struct FileInfoBlock *const fib = xMalloc (1, struct FileInfoBlock);
510 if (fib != NULL)
512 const BPTR flock = Lock ((UBYTE *) name, (long) ACCESS_READ);
514 if (flock != (BPTR) NULL)
516 if (Examine (flock, fib))
517 result = ((fib->fib_DirEntryType >= 0) ? TRUE : FALSE);
518 UnLock (flock);
520 eFree (fib);
522 #else
523 struct stat fileStatus;
525 if (g_stat (name, &fileStatus) == 0)
526 result = (boolean) S_ISDIR (fileStatus.st_mode);
527 #endif
528 return result;
530 #endif
532 extern boolean doesFileExist (const char *const fileName)
534 struct stat fileStatus;
536 return (boolean) (g_stat (fileName, &fileStatus) == 0);
539 /*#ifndef HAVE_FGETPOS*/
541 extern int fgetpos ( stream, pos )
542 FILE *const stream;
543 fpos_t *const pos;
545 int result = 0;
547 *pos = ftell (stream);
548 if (*pos == -1L)
549 result = -1;
551 return result;
554 extern int fsetpos ( stream, pos )
555 FILE *const stream;
556 fpos_t *const pos;
558 return fseek (stream, *pos, SEEK_SET);
561 /*#endif*/
563 extern void addTotals (const unsigned int files,
564 const long unsigned int lines,
565 const long unsigned int bytes)
567 Totals.files += files;
568 Totals.lines += lines;
569 Totals.bytes += bytes;
572 extern boolean isDestinationStdout (void)
574 boolean toStdout = FALSE;
576 if (Option.xref || Option.filter ||
577 (Option.tagFileName != NULL && (strcmp (Option.tagFileName, "-") == 0
578 #if defined (VMS)
579 || strcmp (Option.tagFileName, "sys$output") == 0
580 #else
581 || strcmp (Option.tagFileName, "/dev/stdout") == 0
582 #endif
584 toStdout = TRUE;
585 return toStdout;
588 extern FILE *tempFile (const char *const mode, char **const pName)
590 char *name;
591 FILE *fp;
592 int fd;
593 #ifdef HAVE_MKSTEMP
594 const char *const template = "tags.XXXXXX";
595 const char *tmpdir = NULL;
596 if (! isSetUID (ExecutableProgram))
597 tmpdir = getenv ("TMPDIR");
598 if (tmpdir == NULL)
599 tmpdir = TMPDIR;
600 name = xMalloc (strlen (tmpdir) + 1 + strlen (template) + 1, char);
601 sprintf (name, "%s%c%s", tmpdir, OUTPUT_PATH_SEPARATOR, template);
602 #ifdef G_OS_WIN32
603 fd = mkstemps(name, 0);
604 #else
605 fd = mkstemp(name);
606 #endif
607 #else
608 name = xMalloc (L_tmpnam, char);
609 if (tmpnam (name) != name)
610 error (FATAL | PERROR, "cannot assign temporary file name");
611 fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
612 #endif
613 if (fd == -1)
614 error (FATAL | PERROR, "cannot open temporary file");
615 fp = fdopen (fd, mode);
616 if (fp == NULL)
617 error (FATAL | PERROR, "cannot open temporary file");
618 DebugStatement (
619 debugPrintf (DEBUG_STATUS, "opened temporary file %s\n", name); )
620 Assert (*pName == NULL);
621 *pName = name;
622 return fp;
626 * Pathname manipulation (O/S dependent!!!)
629 extern const char *baseFilename (const char *const filePath)
631 #if defined (MSDOS_STYLE_PATH) || defined (VMS)
632 const char *tail = NULL;
633 unsigned int i;
635 /* Find whichever of the path delimiters is last.
637 for (i = 0 ; i < strlen (PathDelimiters) ; ++i)
639 const char *sep = strrchr (filePath, PathDelimiters [i]);
641 if (sep > tail)
642 tail = sep;
644 #else
645 const char *tail = strrchr (filePath, PATH_SEPARATOR);
646 #endif
647 if (tail == NULL)
648 tail = filePath;
649 else
650 ++tail; /* step past last delimiter */
651 #ifdef VAXC
653 /* remove version number from filename */
654 char *p = strrchr ((char *) tail, ';');
655 if (p != NULL)
656 *p = '\0';
658 #endif
660 return tail;
663 extern boolean isAbsolutePath (const char *const path)
665 boolean result = FALSE;
666 #if defined (MSDOS_STYLE_PATH)
667 if (strchr (PathDelimiters, path [0]) != NULL)
668 result = TRUE;
669 else if (isalpha (path [0]) && path [1] == ':')
671 if (strchr (PathDelimiters, path [2]) != NULL)
672 result = TRUE;
673 else
674 /* We don't support non-absolute file names with a drive
675 * letter, like `d:NAME' (it's too much hassle).
677 error (FATAL,
678 "%s: relative file names with drive letters not supported",
679 path);
681 #elif defined (VMS)
682 result = (boolean) (strchr (path, ':') != NULL);
683 #else
684 result = (boolean) (path [0] == PATH_SEPARATOR);
685 #endif
686 return result;
689 /* Return a newly-allocated string whose contents concatenate those of
690 * s1, s2, s3.
691 * Routine adapted from Gnu etags.
693 static char* concat (const char *s1, const char *s2, const char *s3)
695 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
696 char *result = xMalloc (len1 + len2 + len3 + 1, char);
698 strcpy (result, s1);
699 strcpy (result + len1, s2);
700 strcpy (result + len1 + len2, s3);
701 result [len1 + len2 + len3] = '\0';
703 return result;
706 /* Return a newly allocated string containing the absolute file name of FILE
707 * given CWD (which should end with a slash).
708 * Routine adapted from Gnu etags.
710 static char* absoluteFilename (const char *file)
712 char *slashp, *cp;
713 char *res = NULL;
715 if (isAbsolutePath (file))
716 res = eStrdup (file);
717 else
718 res = concat (CurrentDirectory, file, "");
720 /* Delete the "/dirname/.." and "/." substrings. */
721 slashp = strchr (res, '/');
722 while (slashp != NULL && slashp [0] != '\0')
724 if (slashp[1] == '.')
726 if (slashp [2] == '.' && (slashp [3] == '/' || slashp [3] == '\0'))
728 cp = slashp;
730 cp--;
731 while (cp >= res && ! isAbsolutePath (cp));
732 if (cp < res)
733 cp = slashp;/* the absolute name begins with "/.." */
734 #ifdef MSDOS_STYLE_PATH
735 /* Under MSDOS and NT we get `d:/NAME' as absolute file name,
736 * so the luser could say `d:/../NAME'. We silently treat this
737 * as `d:/NAME'.
739 else if (cp [0] != '/')
740 cp = slashp;
741 #endif
742 strcpy (cp, slashp + 3);
743 slashp = cp;
744 continue;
746 else if (slashp [2] == '/' || slashp [2] == '\0')
748 strcpy (slashp, slashp + 2);
749 continue;
752 slashp = strchr (slashp + 1, '/');
755 if (res [0] == '\0')
756 return eStrdup ("/");
757 else
759 #ifdef MSDOS_STYLE_PATH
760 /* Canonicalize drive letter case. */
761 if (res [1] == ':' && islower (res [0]))
762 res [0] = toupper (res [0]);
763 #endif
765 return res;
769 /* Return a newly allocated string containing the absolute file name of dir
770 * where FILE resides given CWD (which should end with a slash).
771 * Routine adapted from Gnu etags.
773 extern char* absoluteDirname (char *file)
775 char *slashp, *res;
776 char save;
777 #ifdef MSDOS_STYLE_PATH
778 char *p;
779 for (p = file ; *p != '\0' ; p++)
780 if (*p == '\\')
781 *p = '/';
782 #endif
783 slashp = strrchr (file, '/');
784 if (slashp == NULL)
785 res = eStrdup (CurrentDirectory);
786 else
788 save = slashp [1];
789 slashp [1] = '\0';
790 res = absoluteFilename (file);
791 slashp [1] = save;
793 return res;
796 /* Return a newly allocated string containing the file name of FILE relative
797 * to the absolute directory DIR (which should end with a slash).
798 * Routine adapted from Gnu etags.
800 extern char* relativeFilename (const char *file, const char *dir)
802 const char *fp, *dp;
803 char *absdir, *res;
804 int i;
806 /* Find the common root of file and dir (with a trailing slash). */
807 absdir = absoluteFilename (file);
808 fp = absdir;
809 dp = dir;
810 while (*fp++ == *dp++)
811 continue;
812 fp--;
813 dp--; /* back to the first differing char */
815 { /* look at the equal chars until '/' */
816 if (fp == absdir)
817 return absdir; /* first char differs, give up */
818 fp--;
819 dp--;
820 } while (*fp != '/');
822 /* Build a sequence of "../" strings for the resulting relative file name.
824 i = 0;
825 while ((dp = strchr (dp + 1, '/')) != NULL)
826 i += 1;
827 res = xMalloc (3 * i + strlen (fp + 1) + 1, char);
828 res [0] = '\0';
829 while (i-- > 0)
830 strcat (res, "../");
832 /* Add the file name relative to the common root of file and dir. */
833 strcat (res, fp + 1);
834 free (absdir);
836 return res;
839 extern vString *combinePathAndFile (const char *const path,
840 const char *const file)
842 vString *const filePath = vStringNew ();
843 #ifdef VMS
844 const char *const directoryId = strstr (file, ".DIR;1");
846 if (directoryId == NULL)
848 const char *const versionId = strchr (file, ';');
850 vStringCopyS (filePath, path);
851 if (versionId == NULL)
852 vStringCatS (filePath, file);
853 else
854 vStringNCatS (filePath, file, versionId - file);
855 vStringCopyToLower (filePath, filePath);
857 else
859 /* File really is a directory; append it to the path.
860 * Gotcha: doesn't work with logical names.
862 vStringNCopyS (filePath, path, strlen (path) - 1);
863 vStringPut (filePath, '.');
864 vStringNCatS (filePath, file, directoryId - file);
865 if (strchr (path, '[') != NULL)
866 vStringPut (filePath, ']');
867 else
868 vStringPut (filePath, '>');
869 vStringTerminate (filePath);
871 #else
872 const int lastChar = path [strlen (path) - 1];
873 # ifdef MSDOS_STYLE_PATH
874 boolean terminated = (boolean) (strchr (PathDelimiters, lastChar) != NULL);
875 # else
876 boolean terminated = (boolean) (lastChar == PATH_SEPARATOR);
877 # endif
879 vStringCopyS (filePath, path);
880 if (! terminated)
882 vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
883 vStringTerminate (filePath);
885 vStringCatS (filePath, file);
886 #endif
888 return filePath;
892 * Create tags
895 extern void processExcludeOption (const char *const __unused__ option,
896 const char *const parameter)
898 if (parameter [0] == '\0')
899 freeList (&Excluded);
900 else if (parameter [0] == '@')
902 stringList* const new = stringListNewFromFile (parameter + 1);
903 if (Excluded == NULL)
904 Excluded = new;
905 else
906 stringListCombine (Excluded, new);
908 else
910 vString *const item = vStringNewInit (parameter);
911 if (Excluded == NULL)
912 Excluded = stringListNew ();
913 stringListAdd (Excluded, item);
917 #if 0
918 static boolean excludedFile (const char* const name)
920 const char* base = baseFilename (name);
921 boolean result = FALSE;
922 if (Excluded != NULL)
924 result = stringListFileMatched (Excluded, base);
925 if (! result && name != base)
926 result = stringListFileMatched (Excluded, name);
928 #ifdef AMIGA
929 /* not a good solution, but the only one which works often */
930 if (! result)
931 result = (boolean) (strcmp (name, TagFile.name) == 0);
932 #endif
933 return result;
936 # if defined (MSDOS) || defined (WIN32)
938 static boolean createTagsForMatchingEntries (char *const pattern)
940 boolean resize = FALSE;
941 const size_t dirLength = baseFilename (pattern) - (char *) pattern;
942 vString *const filePath = vStringNew ();
943 #if defined (HAVE_FINDFIRST)
944 struct ffblk fileInfo;
945 int result = findfirst (pattern, &fileInfo, FA_DIREC);
947 while (result == 0)
949 const char *const entryName = fileInfo.ff_name;
951 /* We must not recurse into the directories "." or "..".
953 if (strcmp (entryName, ".") != 0 && strcmp (entryName, "..") != 0)
955 vStringNCopyS (filePath, pattern, dirLength);
956 vStringCatS (filePath, entryName);
957 resize |= createTagsForEntry (vStringValue (filePath));
959 result = findnext (&fileInfo);
961 #elif defined (HAVE__FINDFIRST)
962 struct _finddata_t fileInfo;
963 long hFile = _findfirst (pattern, &fileInfo);
965 if (hFile != -1L)
969 const char *const entryName = fileInfo.name;
971 /* We must not recurse into the directories "." or "..".
973 if (strcmp (entryName, ".") != 0 && strcmp (entryName, "..") != 0)
975 vStringNCopyS (filePath, pattern, dirLength);
976 vStringCatS (filePath, entryName);
977 resize |= createTagsForEntry (vStringValue (filePath));
979 } while (_findnext (hFile, &fileInfo) == 0);
980 _findclose (hFile);
982 #endif
984 vStringDelete (filePath);
985 return resize;
988 #elif defined (AMIGA)
990 static boolean createTagsForMatchingEntries (char *const pattern)
992 boolean resize = FALSE;
993 struct AnchorPath *const anchor =
994 (struct AnchorPath *) eMalloc ((size_t) ANCHOR_SIZE);
996 if (anchor != NULL)
998 LONG result;
1000 memset (anchor, 0, (size_t) ANCHOR_SIZE);
1001 anchor->ap_Strlen = ANCHOR_BUF_SIZE; /* ap_Length no longer supported */
1003 /* Allow '.' for current directory.
1005 #ifdef APF_DODOT
1006 anchor->ap_Flags = APF_DODOT | APF_DOWILD;
1007 #else
1008 anchor->ap_Flags = APF_DoDot | APF_DoWild;
1009 #endif
1011 result = MatchFirst ((UBYTE *) pattern, anchor);
1012 while (result == 0)
1014 resize |= createTagsForEntry ((char *) anchor->ap_Buf);
1015 result = MatchNext (anchor);
1017 MatchEnd (anchor);
1018 eFree (anchor);
1020 return resize;
1023 #endif
1025 static boolean isRecursiveLink (const char* const dirName)
1027 boolean result = FALSE;
1028 char* const path = absoluteFilename (dirName);
1029 while (path [strlen (path) - 1] == PATH_SEPARATOR)
1030 path [strlen (path) - 1] = '\0';
1031 while (! result && strlen (path) > (size_t) 1)
1033 char *const separator = strrchr (path, PATH_SEPARATOR);
1034 if (separator == NULL)
1035 break;
1036 else if (separator == path) /* backed up to root directory */
1037 *(separator + 1) = '\0';
1038 else
1039 *separator = '\0';
1040 result = isSameFile (path, dirName);
1042 eFree (path);
1043 return result;
1046 static boolean recurseIntoDirectory (const char *const dirName)
1048 boolean resize = FALSE;
1049 if (isRecursiveLink (dirName))
1050 verbose ("ignoring \"%s\" (recursive link)\n", dirName);
1051 else if (! Option.recurse)
1052 verbose ("ignoring \"%s\" (directory)\n", dirName);
1053 else
1055 #if defined (HAVE_OPENDIR)
1056 DIR *const dir = opendir (dirName);
1057 if (dir == NULL)
1058 error (WARNING | PERROR, "cannot recurse into directory \"%s\"",
1059 dirName);
1060 else
1062 struct dirent *entry;
1063 verbose ("RECURSING into directory \"%s\"\n", dirName);
1064 while ((entry = readdir (dir)) != NULL)
1066 if (strcmp (entry->d_name, ".") != 0 &&
1067 strcmp (entry->d_name, "..") != 0)
1069 vString *filePath;
1070 if (strcmp (dirName, ".") == 0)
1071 filePath = vStringNewInit (entry->d_name);
1072 else
1073 filePath = combinePathAndFile (dirName, entry->d_name);
1074 resize |= createTagsForEntry (vStringValue (filePath));
1075 vStringDelete (filePath);
1078 closedir (dir);
1080 #elif defined (AMIGA) || defined (MSDOS) || defined (WIN32)
1081 vString *const pattern = vStringNew ();
1082 verbose ("RECURSING into directory \"%s\"\n", dirName);
1083 # ifdef AMIGA
1084 if (*dirName != '\0' && strcmp (dirName, ".") != 0)
1086 vStringCopyS (pattern, dirName);
1087 if (dirName [strlen (dirName) - 1] != '/')
1088 vStringPut (pattern, '/');
1090 vStringCatS (pattern, "#?");
1091 # else
1092 vStringCopyS (pattern, dirName);
1093 vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
1094 vStringCatS (pattern, "*.*");
1095 # endif
1096 resize = createTagsForMatchingEntries (vStringValue (pattern));
1097 vStringDelete (pattern);
1098 #endif /* HAVE_OPENDIR */
1100 return resize;
1103 static boolean createTagsForEntry (const char *const entryName)
1105 boolean resize = FALSE;
1107 Assert (entryName != NULL);
1108 if (excludedFile (entryName))
1109 verbose ("excluding \"%s\"\n", entryName);
1110 else if (isSymbolicLink (entryName) && ! Option.followLinks)
1111 verbose ("ignoring \"%s\" (symbolic link)\n", entryName);
1112 else if (! doesFileExist (entryName))
1113 error (WARNING | PERROR, "cannot open source file \"%s\"", entryName);
1114 else if (isDirectory (entryName))
1115 resize = recurseIntoDirectory (entryName);
1116 else if (! isNormalFile (entryName))
1117 verbose ("ignoring \"%s\" (special file)\n", entryName);
1118 else
1119 resize = parseFile (entryName);
1121 return resize;
1124 static boolean createTagsForArgs (cookedArgs* const args)
1126 boolean resize = FALSE;
1128 /* Generate tags for each argument on the command line.
1130 while (! cArgOff (args))
1132 const char *arg = cArgItem (args);
1134 #if defined (MSDOS) || defined (WIN32)
1135 vString *const pattern = vStringNewInit (arg);
1136 char *patternS = vStringValue (pattern);
1138 /* We must transform the "." and ".." forms into something that can
1139 * be expanded by the MSDOS/Windows functions.
1141 if (Option.recurse &&
1142 (strcmp (patternS, ".") == 0 || strcmp (patternS, "..") == 0))
1144 vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
1145 vStringCatS (pattern, "*.*");
1147 resize |= createTagsForMatchingEntries (patternS);
1148 vStringDelete (pattern);
1149 #else
1150 resize |= createTagsForEntry (arg);
1151 #endif
1152 cArgForth (args);
1153 parseOptions (args);
1155 return resize;
1158 /* Read from an opened file a list of file names for which to generate tags.
1160 static boolean createTagsFromFileInput (FILE* const fp, const boolean filter)
1162 boolean resize = FALSE;
1163 if (fp != NULL)
1165 cookedArgs* args = cArgNewFromLineFile (fp);
1166 parseOptions (args);
1167 while (! cArgOff (args))
1169 resize |= createTagsForEntry (cArgItem (args));
1170 if (filter)
1172 if (Option.filterTerminator != NULL)
1173 fputs (Option.filterTerminator, stdout);
1174 fflush (stdout);
1176 cArgForth (args);
1177 parseOptions (args);
1179 cArgDelete (args);
1181 return resize;
1184 /* Read from a named file a list of file names for which to generate tags.
1186 static boolean createTagsFromListFile (const char* const fileName)
1188 boolean resize;
1189 Assert (fileName != NULL);
1190 if (strcmp (fileName, "-") == 0)
1191 resize = createTagsFromFileInput (stdin, FALSE);
1192 else
1194 FILE* const fp = g_fopen (fileName, "r");
1195 if (fp == NULL)
1196 error (FATAL | PERROR, "cannot open list file \"%s\"", fileName);
1197 resize = createTagsFromFileInput (fp, FALSE);
1198 fclose (fp);
1200 return resize;
1203 #if defined (HAVE_CLOCK)
1204 # define CLOCK_AVAILABLE
1205 # ifndef CLOCKS_PER_SEC
1206 # define CLOCKS_PER_SEC 1000000
1207 # endif
1208 #elif defined (HAVE_TIMES)
1209 # define CLOCK_AVAILABLE
1210 # define CLOCKS_PER_SEC 60
1211 static clock_t clock (void)
1213 struct tms buf;
1215 times (&buf);
1216 return (buf.tms_utime + buf.tms_stime);
1218 #else
1219 # define clock() (clock_t)0
1220 #endif
1222 static void printTotals (const clock_t *const timeStamps)
1224 const unsigned long totalTags = TagFile.numTags.added +
1225 TagFile.numTags.prev;
1227 fprintf (errout, "%ld file%s, %ld line%s (%ld kB) scanned",
1228 Totals.files, plural (Totals.files),
1229 Totals.lines, plural (Totals.lines),
1230 Totals.bytes/1024L);
1231 #ifdef CLOCK_AVAILABLE
1233 const double interval = ((double) (timeStamps [1] - timeStamps [0])) /
1234 CLOCKS_PER_SEC;
1236 fprintf (errout, " in %.01f seconds", interval);
1237 if (interval != (double) 0.0)
1238 fprintf (errout, " (%lu kB/s)",
1239 (unsigned long) (Totals.bytes / interval) / 1024L);
1241 #endif
1242 fputc ('\n', errout);
1244 fprintf (errout, "%lu tag%s added to tag file",
1245 TagFile.numTags.added, plural (TagFile.numTags.added));
1246 if (Option.append)
1247 fprintf (errout, " (now %lu tags)", totalTags);
1248 fputc ('\n', errout);
1250 if (totalTags > 0 && Option.sorted)
1252 fprintf (errout, "%lu tag%s sorted", totalTags, plural (totalTags));
1253 #ifdef CLOCK_AVAILABLE
1254 fprintf (errout, " in %.02f seconds",
1255 ((double) (timeStamps [2] - timeStamps [1])) / CLOCKS_PER_SEC);
1256 #endif
1257 fputc ('\n', errout);
1260 #ifdef TM_DEBUG
1261 fprintf (errout, "longest tag line = %lu\n",
1262 (unsigned long) TagFile.max.line);
1263 #endif
1266 static void makeTags (cookedArgs* args)
1268 clock_t timeStamps [3];
1269 boolean resize = FALSE;
1270 boolean files = (boolean)(! cArgOff (args) || Option.fileList != NULL
1271 || Option.filter);
1273 if (! files && ! Option.recurse)
1274 error (FATAL, "No files specified. Try \"%s --help\".",
1275 getExecutableName ());
1277 #define timeStamp(n) timeStamps[(n)]=(Option.printTotals ? clock():(clock_t)0)
1278 if (! Option.filter)
1279 openTagFile ();
1281 timeStamp (0);
1283 if (! cArgOff (args))
1285 verbose ("Reading command line arguments\n");
1286 resize = createTagsForArgs (args);
1288 if (Option.fileList != NULL)
1290 verbose ("Reading list file\n");
1291 resize = (boolean) (createTagsFromListFile (Option.fileList) || resize);
1293 if (Option.filter)
1295 verbose ("Reading filter input\n");
1296 resize = (boolean) (createTagsFromFileInput (stdin, TRUE) || resize);
1298 if (! files && Option.recurse)
1299 resize = recurseIntoDirectory (".");
1301 timeStamp (1);
1303 if (! Option.filter)
1304 closeTagFile (resize);
1306 timeStamp (2);
1308 if (Option.printTotals)
1309 printTotals (timeStamps);
1310 #undef timeStamp
1314 * Start up code
1317 static void setExecutableName (const char *const path)
1319 ExecutableProgram = path;
1320 ExecutableName = baseFilename (path);
1321 #ifdef VAXC
1323 /* remove filetype from executable name */
1324 char *p = strrchr (ExecutableName, '.');
1325 if (p != NULL)
1326 *p = '\0';
1328 #endif
1331 extern int ctags_main (int __unused__ argc, char **argv)
1333 cookedArgs *args;
1334 #ifdef VMS
1335 extern int getredirection (int *ac, char ***av);
1337 /* do wildcard expansion and I/O redirection */
1338 getredirection (&argc, &argv);
1339 #endif
1341 #ifdef AMIGA
1342 /* This program doesn't work when started from the Workbench */
1343 if (argc == 0)
1344 exit (1);
1345 #endif
1347 #ifdef __EMX__
1348 _wildcard (&argc, &argv); /* expand wildcards in argument list */
1349 #endif
1351 #if defined (macintosh) && BUILD_MPW_TOOL == 0
1352 argc = ccommand (&argv);
1353 #endif
1355 setCurrentDirectory ();
1356 setExecutableName (*argv++);
1357 #ifdef HAVE_REGEX
1358 checkRegex ();
1359 #endif
1361 /* always excluded by default */
1362 processExcludeOption (NULL, "EIFGEN");
1363 processExcludeOption (NULL, "SCCS");
1365 args = cArgNewFromArgv (argv);
1366 previewFirstOption (args);
1367 testEtagsInvocation ();
1368 initializeParsing ();
1369 initOptions ();
1370 readOptionConfiguration ();
1371 verbose ("Reading initial options from command line\n");
1372 parseOptions (args);
1373 checkOptions ();
1374 makeTags (args);
1376 /* Clean up.
1378 eFree (CurrentDirectory);
1379 freeList (&Excluded);
1380 cArgDelete (args);
1381 freeKeywordTable ();
1382 freeSourceFileResources ();
1383 freeTagFileResources ();
1384 freeOptionResources ();
1385 freeParserResources ();
1386 #ifdef HAVE_REGEX
1387 freeRegexResources ();
1388 #endif
1390 exit (0);
1391 return 0;
1393 #endif
1395 /* wrap g_warning so we don't include glib.h for all parsers, to keep compat with CTags */
1396 void utils_warn(const char *msg)
1398 g_warning("%s", msg);
1401 /* vi:set tabstop=8 shiftwidth=4: */