Merge pull request #482 from philippwiesemann/fix-typos-po-de
[geany-mirror.git] / tagmanager / ctags / ctags.c
blob56bf67fbab08d0eacccfa442fa1cf8ebeb602e9f
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 fd = mkstemp(name);
603 #else
604 name = xMalloc (L_tmpnam, char);
605 if (tmpnam (name) != name)
606 error (FATAL | PERROR, "cannot assign temporary file name");
607 fd = open (name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR);
608 #endif
609 if (fd == -1)
610 error (FATAL | PERROR, "cannot open temporary file");
611 fp = fdopen (fd, mode);
612 if (fp == NULL)
613 error (FATAL | PERROR, "cannot open temporary file");
614 DebugStatement (
615 debugPrintf (DEBUG_STATUS, "opened temporary file %s\n", name); )
616 Assert (*pName == NULL);
617 *pName = name;
618 return fp;
622 * Pathname manipulation (O/S dependent!!!)
625 extern const char *baseFilename (const char *const filePath)
627 #if defined (MSDOS_STYLE_PATH) || defined (VMS)
628 const char *tail = NULL;
629 unsigned int i;
631 /* Find whichever of the path delimiters is last.
633 for (i = 0 ; i < strlen (PathDelimiters) ; ++i)
635 const char *sep = strrchr (filePath, PathDelimiters [i]);
637 if (sep > tail)
638 tail = sep;
640 #else
641 const char *tail = strrchr (filePath, PATH_SEPARATOR);
642 #endif
643 if (tail == NULL)
644 tail = filePath;
645 else
646 ++tail; /* step past last delimiter */
647 #ifdef VAXC
649 /* remove version number from filename */
650 char *p = strrchr ((char *) tail, ';');
651 if (p != NULL)
652 *p = '\0';
654 #endif
656 return tail;
659 extern boolean isAbsolutePath (const char *const path)
661 boolean result = FALSE;
662 #if defined (MSDOS_STYLE_PATH)
663 if (strchr (PathDelimiters, path [0]) != NULL)
664 result = TRUE;
665 else if (isalpha (path [0]) && path [1] == ':')
667 if (strchr (PathDelimiters, path [2]) != NULL)
668 result = TRUE;
669 else
670 /* We don't support non-absolute file names with a drive
671 * letter, like `d:NAME' (it's too much hassle).
673 error (FATAL,
674 "%s: relative file names with drive letters not supported",
675 path);
677 #elif defined (VMS)
678 result = (boolean) (strchr (path, ':') != NULL);
679 #else
680 result = (boolean) (path [0] == PATH_SEPARATOR);
681 #endif
682 return result;
685 /* Return a newly-allocated string whose contents concatenate those of
686 * s1, s2, s3.
687 * Routine adapted from Gnu etags.
689 static char* concat (const char *s1, const char *s2, const char *s3)
691 int len1 = strlen (s1), len2 = strlen (s2), len3 = strlen (s3);
692 char *result = xMalloc (len1 + len2 + len3 + 1, char);
694 strcpy (result, s1);
695 strcpy (result + len1, s2);
696 strcpy (result + len1 + len2, s3);
697 result [len1 + len2 + len3] = '\0';
699 return result;
702 /* Return a newly allocated string containing the absolute file name of FILE
703 * given CWD (which should end with a slash).
704 * Routine adapted from Gnu etags.
706 static char* absoluteFilename (const char *file)
708 char *slashp, *cp;
709 char *res = NULL;
711 if (isAbsolutePath (file))
712 res = eStrdup (file);
713 else
714 res = concat (CurrentDirectory, file, "");
716 /* Delete the "/dirname/.." and "/." substrings. */
717 slashp = strchr (res, '/');
718 while (slashp != NULL && slashp [0] != '\0')
720 if (slashp[1] == '.')
722 if (slashp [2] == '.' && (slashp [3] == '/' || slashp [3] == '\0'))
724 cp = slashp;
726 cp--;
727 while (cp >= res && ! isAbsolutePath (cp));
728 if (cp < res)
729 cp = slashp;/* the absolute name begins with "/.." */
730 #ifdef MSDOS_STYLE_PATH
731 /* Under MSDOS and NT we get `d:/NAME' as absolute file name,
732 * so the luser could say `d:/../NAME'. We silently treat this
733 * as `d:/NAME'.
735 else if (cp [0] != '/')
736 cp = slashp;
737 #endif
738 memmove (cp, slashp + 3, strlen (slashp + 3) + 1);
739 slashp = cp;
740 continue;
742 else if (slashp [2] == '/' || slashp [2] == '\0')
744 memmove (slashp, slashp + 2, strlen (slashp + 2) + 1);
745 continue;
748 slashp = strchr (slashp + 1, '/');
751 if (res [0] == '\0')
752 return eStrdup ("/");
753 else
755 #ifdef MSDOS_STYLE_PATH
756 /* Canonicalize drive letter case. */
757 if (res [1] == ':' && islower (res [0]))
758 res [0] = toupper (res [0]);
759 #endif
761 return res;
765 /* Return a newly allocated string containing the absolute file name of dir
766 * where FILE resides given CWD (which should end with a slash).
767 * Routine adapted from Gnu etags.
769 extern char* absoluteDirname (char *file)
771 char *slashp, *res;
772 char save;
773 #ifdef MSDOS_STYLE_PATH
774 char *p;
775 for (p = file ; *p != '\0' ; p++)
776 if (*p == '\\')
777 *p = '/';
778 #endif
779 slashp = strrchr (file, '/');
780 if (slashp == NULL)
781 res = eStrdup (CurrentDirectory);
782 else
784 save = slashp [1];
785 slashp [1] = '\0';
786 res = absoluteFilename (file);
787 slashp [1] = save;
789 return res;
792 /* Return a newly allocated string containing the file name of FILE relative
793 * to the absolute directory DIR (which should end with a slash).
794 * Routine adapted from Gnu etags.
796 extern char* relativeFilename (const char *file, const char *dir)
798 const char *fp, *dp;
799 char *absdir, *res;
800 int i;
802 /* Find the common root of file and dir (with a trailing slash). */
803 absdir = absoluteFilename (file);
804 fp = absdir;
805 dp = dir;
806 while (*fp++ == *dp++)
807 continue;
808 fp--;
809 dp--; /* back to the first differing char */
811 { /* look at the equal chars until '/' */
812 if (fp == absdir)
813 return absdir; /* first char differs, give up */
814 fp--;
815 dp--;
816 } while (*fp != '/');
818 /* Build a sequence of "../" strings for the resulting relative file name.
820 i = 0;
821 while ((dp = strchr (dp + 1, '/')) != NULL)
822 i += 1;
823 res = xMalloc (3 * i + strlen (fp + 1) + 1, char);
824 res [0] = '\0';
825 while (i-- > 0)
826 strcat (res, "../");
828 /* Add the file name relative to the common root of file and dir. */
829 strcat (res, fp + 1);
830 free (absdir);
832 return res;
835 extern vString *combinePathAndFile (const char *const path,
836 const char *const file)
838 vString *const filePath = vStringNew ();
839 #ifdef VMS
840 const char *const directoryId = strstr (file, ".DIR;1");
842 if (directoryId == NULL)
844 const char *const versionId = strchr (file, ';');
846 vStringCopyS (filePath, path);
847 if (versionId == NULL)
848 vStringCatS (filePath, file);
849 else
850 vStringNCatS (filePath, file, versionId - file);
851 vStringCopyToLower (filePath, filePath);
853 else
855 /* File really is a directory; append it to the path.
856 * Gotcha: doesn't work with logical names.
858 vStringNCopyS (filePath, path, strlen (path) - 1);
859 vStringPut (filePath, '.');
860 vStringNCatS (filePath, file, directoryId - file);
861 if (strchr (path, '[') != NULL)
862 vStringPut (filePath, ']');
863 else
864 vStringPut (filePath, '>');
865 vStringTerminate (filePath);
867 #else
868 const int lastChar = path [strlen (path) - 1];
869 # ifdef MSDOS_STYLE_PATH
870 boolean terminated = (boolean) (strchr (PathDelimiters, lastChar) != NULL);
871 # else
872 boolean terminated = (boolean) (lastChar == PATH_SEPARATOR);
873 # endif
875 vStringCopyS (filePath, path);
876 if (! terminated)
878 vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
879 vStringTerminate (filePath);
881 vStringCatS (filePath, file);
882 #endif
884 return filePath;
888 * Create tags
891 extern void processExcludeOption (const char *const UNUSED option,
892 const char *const parameter)
894 if (parameter [0] == '\0')
895 freeList (&Excluded);
896 else if (parameter [0] == '@')
898 stringList* const new = stringListNewFromFile (parameter + 1);
899 if (Excluded == NULL)
900 Excluded = new;
901 else
902 stringListCombine (Excluded, new);
904 else
906 vString *const item = vStringNewInit (parameter);
907 if (Excluded == NULL)
908 Excluded = stringListNew ();
909 stringListAdd (Excluded, item);
913 #if 0
914 static boolean excludedFile (const char* const name)
916 const char* base = baseFilename (name);
917 boolean result = FALSE;
918 if (Excluded != NULL)
920 result = stringListFileMatched (Excluded, base);
921 if (! result && name != base)
922 result = stringListFileMatched (Excluded, name);
924 #ifdef AMIGA
925 /* not a good solution, but the only one which works often */
926 if (! result)
927 result = (boolean) (strcmp (name, TagFile.name) == 0);
928 #endif
929 return result;
932 # if defined (MSDOS) || defined (WIN32)
934 static boolean createTagsForMatchingEntries (char *const pattern)
936 boolean resize = FALSE;
937 const size_t dirLength = baseFilename (pattern) - (char *) pattern;
938 vString *const filePath = vStringNew ();
939 #if defined (HAVE_FINDFIRST)
940 struct ffblk fileInfo;
941 int result = findfirst (pattern, &fileInfo, FA_DIREC);
943 while (result == 0)
945 const char *const entryName = fileInfo.ff_name;
947 /* We must not recurse into the directories "." or "..".
949 if (strcmp (entryName, ".") != 0 && strcmp (entryName, "..") != 0)
951 vStringNCopyS (filePath, pattern, dirLength);
952 vStringCatS (filePath, entryName);
953 resize |= createTagsForEntry (vStringValue (filePath));
955 result = findnext (&fileInfo);
957 #elif defined (HAVE__FINDFIRST)
958 struct _finddata_t fileInfo;
959 long hFile = _findfirst (pattern, &fileInfo);
961 if (hFile != -1L)
965 const char *const entryName = fileInfo.name;
967 /* We must not recurse into the directories "." or "..".
969 if (strcmp (entryName, ".") != 0 && strcmp (entryName, "..") != 0)
971 vStringNCopyS (filePath, pattern, dirLength);
972 vStringCatS (filePath, entryName);
973 resize |= createTagsForEntry (vStringValue (filePath));
975 } while (_findnext (hFile, &fileInfo) == 0);
976 _findclose (hFile);
978 #endif
980 vStringDelete (filePath);
981 return resize;
984 #elif defined (AMIGA)
986 static boolean createTagsForMatchingEntries (char *const pattern)
988 boolean resize = FALSE;
989 struct AnchorPath *const anchor =
990 (struct AnchorPath *) eMalloc ((size_t) ANCHOR_SIZE);
992 if (anchor != NULL)
994 LONG result;
996 memset (anchor, 0, (size_t) ANCHOR_SIZE);
997 anchor->ap_Strlen = ANCHOR_BUF_SIZE; /* ap_Length no longer supported */
999 /* Allow '.' for current directory.
1001 #ifdef APF_DODOT
1002 anchor->ap_Flags = APF_DODOT | APF_DOWILD;
1003 #else
1004 anchor->ap_Flags = APF_DoDot | APF_DoWild;
1005 #endif
1007 result = MatchFirst ((UBYTE *) pattern, anchor);
1008 while (result == 0)
1010 resize |= createTagsForEntry ((char *) anchor->ap_Buf);
1011 result = MatchNext (anchor);
1013 MatchEnd (anchor);
1014 eFree (anchor);
1016 return resize;
1019 #endif
1021 static boolean isRecursiveLink (const char* const dirName)
1023 boolean result = FALSE;
1024 char* const path = absoluteFilename (dirName);
1025 while (path [strlen (path) - 1] == PATH_SEPARATOR)
1026 path [strlen (path) - 1] = '\0';
1027 while (! result && strlen (path) > (size_t) 1)
1029 char *const separator = strrchr (path, PATH_SEPARATOR);
1030 if (separator == NULL)
1031 break;
1032 else if (separator == path) /* backed up to root directory */
1033 *(separator + 1) = '\0';
1034 else
1035 *separator = '\0';
1036 result = isSameFile (path, dirName);
1038 eFree (path);
1039 return result;
1042 static boolean recurseIntoDirectory (const char *const dirName)
1044 boolean resize = FALSE;
1045 if (isRecursiveLink (dirName))
1046 verbose ("ignoring \"%s\" (recursive link)\n", dirName);
1047 else if (! Option.recurse)
1048 verbose ("ignoring \"%s\" (directory)\n", dirName);
1049 else
1051 #if defined (HAVE_OPENDIR)
1052 DIR *const dir = opendir (dirName);
1053 if (dir == NULL)
1054 error (WARNING | PERROR, "cannot recurse into directory \"%s\"",
1055 dirName);
1056 else
1058 struct dirent *entry;
1059 verbose ("RECURSING into directory \"%s\"\n", dirName);
1060 while ((entry = readdir (dir)) != NULL)
1062 if (strcmp (entry->d_name, ".") != 0 &&
1063 strcmp (entry->d_name, "..") != 0)
1065 vString *filePath;
1066 if (strcmp (dirName, ".") == 0)
1067 filePath = vStringNewInit (entry->d_name);
1068 else
1069 filePath = combinePathAndFile (dirName, entry->d_name);
1070 resize |= createTagsForEntry (vStringValue (filePath));
1071 vStringDelete (filePath);
1074 closedir (dir);
1076 #elif defined (AMIGA) || defined (MSDOS) || defined (WIN32)
1077 vString *const pattern = vStringNew ();
1078 verbose ("RECURSING into directory \"%s\"\n", dirName);
1079 # ifdef AMIGA
1080 if (*dirName != '\0' && strcmp (dirName, ".") != 0)
1082 vStringCopyS (pattern, dirName);
1083 if (dirName [strlen (dirName) - 1] != '/')
1084 vStringPut (pattern, '/');
1086 vStringCatS (pattern, "#?");
1087 # else
1088 vStringCopyS (pattern, dirName);
1089 vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
1090 vStringCatS (pattern, "*.*");
1091 # endif
1092 resize = createTagsForMatchingEntries (vStringValue (pattern));
1093 vStringDelete (pattern);
1094 #endif /* HAVE_OPENDIR */
1096 return resize;
1099 static boolean createTagsForEntry (const char *const entryName)
1101 boolean resize = FALSE;
1103 Assert (entryName != NULL);
1104 if (excludedFile (entryName))
1105 verbose ("excluding \"%s\"\n", entryName);
1106 else if (isSymbolicLink (entryName) && ! Option.followLinks)
1107 verbose ("ignoring \"%s\" (symbolic link)\n", entryName);
1108 else if (! doesFileExist (entryName))
1109 error (WARNING | PERROR, "cannot open source file \"%s\"", entryName);
1110 else if (isDirectory (entryName))
1111 resize = recurseIntoDirectory (entryName);
1112 else if (! isNormalFile (entryName))
1113 verbose ("ignoring \"%s\" (special file)\n", entryName);
1114 else
1115 resize = parseFile (entryName);
1117 return resize;
1120 static boolean createTagsForArgs (cookedArgs* const args)
1122 boolean resize = FALSE;
1124 /* Generate tags for each argument on the command line.
1126 while (! cArgOff (args))
1128 const char *arg = cArgItem (args);
1130 #if defined (MSDOS) || defined (WIN32)
1131 vString *const pattern = vStringNewInit (arg);
1132 char *patternS = vStringValue (pattern);
1134 /* We must transform the "." and ".." forms into something that can
1135 * be expanded by the MSDOS/Windows functions.
1137 if (Option.recurse &&
1138 (strcmp (patternS, ".") == 0 || strcmp (patternS, "..") == 0))
1140 vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
1141 vStringCatS (pattern, "*.*");
1143 resize |= createTagsForMatchingEntries (patternS);
1144 vStringDelete (pattern);
1145 #else
1146 resize |= createTagsForEntry (arg);
1147 #endif
1148 cArgForth (args);
1149 parseOptions (args);
1151 return resize;
1154 /* Read from an opened file a list of file names for which to generate tags.
1156 static boolean createTagsFromFileInput (FILE* const fp, const boolean filter)
1158 boolean resize = FALSE;
1159 if (fp != NULL)
1161 cookedArgs* args = cArgNewFromLineFile (fp);
1162 parseOptions (args);
1163 while (! cArgOff (args))
1165 resize |= createTagsForEntry (cArgItem (args));
1166 if (filter)
1168 if (Option.filterTerminator != NULL)
1169 fputs (Option.filterTerminator, stdout);
1170 fflush (stdout);
1172 cArgForth (args);
1173 parseOptions (args);
1175 cArgDelete (args);
1177 return resize;
1180 /* Read from a named file a list of file names for which to generate tags.
1182 static boolean createTagsFromListFile (const char* const fileName)
1184 boolean resize;
1185 Assert (fileName != NULL);
1186 if (strcmp (fileName, "-") == 0)
1187 resize = createTagsFromFileInput (stdin, FALSE);
1188 else
1190 FILE* const fp = g_fopen (fileName, "r");
1191 if (fp == NULL)
1192 error (FATAL | PERROR, "cannot open list file \"%s\"", fileName);
1193 resize = createTagsFromFileInput (fp, FALSE);
1194 fclose (fp);
1196 return resize;
1199 #if defined (HAVE_CLOCK)
1200 # define CLOCK_AVAILABLE
1201 # ifndef CLOCKS_PER_SEC
1202 # define CLOCKS_PER_SEC 1000000
1203 # endif
1204 #elif defined (HAVE_TIMES)
1205 # define CLOCK_AVAILABLE
1206 # define CLOCKS_PER_SEC 60
1207 static clock_t clock (void)
1209 struct tms buf;
1211 times (&buf);
1212 return (buf.tms_utime + buf.tms_stime);
1214 #else
1215 # define clock() (clock_t)0
1216 #endif
1218 static void printTotals (const clock_t *const timeStamps)
1220 const unsigned long totalTags = TagFile.numTags.added +
1221 TagFile.numTags.prev;
1223 fprintf (errout, "%ld file%s, %ld line%s (%ld kB) scanned",
1224 Totals.files, plural (Totals.files),
1225 Totals.lines, plural (Totals.lines),
1226 Totals.bytes/1024L);
1227 #ifdef CLOCK_AVAILABLE
1229 const double interval = ((double) (timeStamps [1] - timeStamps [0])) /
1230 CLOCKS_PER_SEC;
1232 fprintf (errout, " in %.01f seconds", interval);
1233 if (interval != (double) 0.0)
1234 fprintf (errout, " (%lu kB/s)",
1235 (unsigned long) (Totals.bytes / interval) / 1024L);
1237 #endif
1238 fputc ('\n', errout);
1240 fprintf (errout, "%lu tag%s added to tag file",
1241 TagFile.numTags.added, plural (TagFile.numTags.added));
1242 if (Option.append)
1243 fprintf (errout, " (now %lu tags)", totalTags);
1244 fputc ('\n', errout);
1246 if (totalTags > 0 && Option.sorted)
1248 fprintf (errout, "%lu tag%s sorted", totalTags, plural (totalTags));
1249 #ifdef CLOCK_AVAILABLE
1250 fprintf (errout, " in %.02f seconds",
1251 ((double) (timeStamps [2] - timeStamps [1])) / CLOCKS_PER_SEC);
1252 #endif
1253 fputc ('\n', errout);
1256 #ifdef TM_DEBUG
1257 fprintf (errout, "longest tag line = %lu\n",
1258 (unsigned long) TagFile.max.line);
1259 #endif
1262 static void makeTags (cookedArgs* args)
1264 clock_t timeStamps [3];
1265 boolean resize = FALSE;
1266 boolean files = (boolean)(! cArgOff (args) || Option.fileList != NULL
1267 || Option.filter);
1269 if (! files && ! Option.recurse)
1270 error (FATAL, "No files specified. Try \"%s --help\".",
1271 getExecutableName ());
1273 #define timeStamp(n) timeStamps[(n)]=(Option.printTotals ? clock():(clock_t)0)
1274 if (! Option.filter)
1275 openTagFile ();
1277 timeStamp (0);
1279 if (! cArgOff (args))
1281 verbose ("Reading command line arguments\n");
1282 resize = createTagsForArgs (args);
1284 if (Option.fileList != NULL)
1286 verbose ("Reading list file\n");
1287 resize = (boolean) (createTagsFromListFile (Option.fileList) || resize);
1289 if (Option.filter)
1291 verbose ("Reading filter input\n");
1292 resize = (boolean) (createTagsFromFileInput (stdin, TRUE) || resize);
1294 if (! files && Option.recurse)
1295 resize = recurseIntoDirectory (".");
1297 timeStamp (1);
1299 if (! Option.filter)
1300 closeTagFile (resize);
1302 timeStamp (2);
1304 if (Option.printTotals)
1305 printTotals (timeStamps);
1306 #undef timeStamp
1310 * Start up code
1313 static void setExecutableName (const char *const path)
1315 ExecutableProgram = path;
1316 ExecutableName = baseFilename (path);
1317 #ifdef VAXC
1319 /* remove filetype from executable name */
1320 char *p = strrchr (ExecutableName, '.');
1321 if (p != NULL)
1322 *p = '\0';
1324 #endif
1327 extern int ctags_main (int UNUSED argc, char **argv)
1329 cookedArgs *args;
1330 #ifdef VMS
1331 extern int getredirection (int *ac, char ***av);
1333 /* do wildcard expansion and I/O redirection */
1334 getredirection (&argc, &argv);
1335 #endif
1337 #ifdef AMIGA
1338 /* This program doesn't work when started from the Workbench */
1339 if (argc == 0)
1340 exit (1);
1341 #endif
1343 #ifdef __EMX__
1344 _wildcard (&argc, &argv); /* expand wildcards in argument list */
1345 #endif
1347 #if defined (macintosh) && BUILD_MPW_TOOL == 0
1348 argc = ccommand (&argv);
1349 #endif
1351 setCurrentDirectory ();
1352 setExecutableName (*argv++);
1353 #ifdef HAVE_REGEX
1354 checkRegex ();
1355 #endif
1357 /* always excluded by default */
1358 processExcludeOption (NULL, "EIFGEN");
1359 processExcludeOption (NULL, "SCCS");
1361 args = cArgNewFromArgv (argv);
1362 previewFirstOption (args);
1363 testEtagsInvocation ();
1364 initializeParsing ();
1365 initOptions ();
1366 readOptionConfiguration ();
1367 verbose ("Reading initial options from command line\n");
1368 parseOptions (args);
1369 checkOptions ();
1370 makeTags (args);
1372 /* Clean up.
1374 eFree (CurrentDirectory);
1375 freeList (&Excluded);
1376 cArgDelete (args);
1377 freeKeywordTable ();
1378 freeSourceFileResources ();
1379 freeTagFileResources ();
1380 freeOptionResources ();
1381 freeParserResources ();
1382 #ifdef HAVE_REGEX
1383 freeRegexResources ();
1384 #endif
1386 exit (0);
1387 return 0;
1389 #endif
1391 /* wrap g_warning so we don't include glib.h for all parsers, to keep compat with CTags */
1392 void utils_warn(const char *msg)
1394 g_warning("%s", msg);
1397 /* vi:set tabstop=8 shiftwidth=4: */