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.
22 #include "general.h" /* must always come first */
24 #include <glib/gstdio.h>
27 # include <stdlib.h> /* to declare malloc (), realloc () */
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 () */
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)
43 extern struct DosLibrary
*DOSBase
;
44 # include <pragmas/dos_pragmas.h>
52 /* To declare "struct stat" and stat ().
54 #if defined (HAVE_SYS_TYPES_H)
55 # include <sys/types.h>
57 # if defined (HAVE_TYPES_H)
61 #ifdef HAVE_SYS_STAT_H
62 # include <sys/stat.h>
69 /* To provide directory searching for recursion feature.
73 # define boolean BORLAND_boolean
79 # include <direct.h> /* to _getcwd */
82 # include <dos.h> /* to declare FA_DIREC */
85 # include <dir.h> /* to declare findfirst () and findnext () */
88 # include <io.h> /* to declare _finddata_t in MSVC++ 4.x */
91 /* To provide timings features if available.
99 # ifdef HAVE_SYS_TIMES_H
100 # include <sys/times.h>
113 #ifdef TRAP_MEMORY_CALLS
114 # include "safe_malloc.h"
122 * Miscellaneous macros
124 #define selected(var,feature) (((int)(var) & (int)(feature)) == (int)feature)
125 #define plural(value) (((unsigned long)(value) == 1L) ? "" : "s")
130 #ifndef PATH_SEPARATOR
131 # if defined (MSDOS_STYLE_PATH)
132 # define PATH_SEPARATOR '\\'
133 # elif defined (QDOS)
134 # define PATH_SEPARATOR '_'
136 # define PATH_SEPARATOR '/'
140 #if defined (MSDOS_STYLE_PATH) && defined (UNIX_PATH_SEPARATOR)
141 # define OUTPUT_PATH_SEPARATOR '/'
143 # define OUTPUT_PATH_SEPARATOR PATH_SEPARATOR
149 # if defined (S_IFREG) && ! defined (AMIGA)
150 # define S_ISREG(mode) ((mode) & S_IFREG)
152 # define S_ISREG(mode) TRUE /* assume regular file */
158 # define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK)
160 # define S_ISLNK(mode) FALSE /* assume no soft links */
166 # define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR)
168 # define S_ISDIR(mode) FALSE /* assume no soft links */
183 # define S_IRUSR 0400
186 # define S_IWUSR 0200
189 /* Hack for rediculous practice of Microsoft Visual C++.
192 # if defined (_MSC_VER)
194 # define getcwd _getcwd
195 # define PATH_MAX _MAX_PATH
196 # elif defined (__BORLANDC__)
197 # define PATH_MAX MAXPATH
202 # define PATH_MAX 256
208 #if defined (MSDOS_STYLE_PATH)
209 static const char PathDelimiters
[] = ":/\\";
211 static const char PathDelimiters
[] = ":]>";
215 # define TMPDIR "/tmp"
218 char *CurrentDirectory
= NULL
;
220 static const char *ExecutableProgram
= NULL
;
222 static const char *ExecutableName
= "geany";
223 static stringList
* Excluded
= NULL
;
225 static struct { long files
, lines
, bytes
; } Totals
= { 0, 0, 0 };
229 static const char *VERsion
= "$VER: "PROGRAM_NAME
" "PROGRAM_VERSION
" "
239 * FUNCTION PROTOTYPES
241 #ifdef NEED_PROTO_STAT
242 extern int stat (const char *, struct stat
*);
244 #ifdef NEED_PROTO_LSTAT
245 extern int lstat (const char *, struct stat
*);
249 static boolean
createTagsForEntry (const char *const entryName
);
253 * FUNCTION DEFINITIONS
256 extern const char *getExecutableName (void)
258 return ExecutableName
;
262 static void setCurrentDirectory (void)
265 char* const cwd
= eStrdup (".");
267 char* const cwd
= getcwd (NULL
, PATH_MAX
);
269 CurrentDirectory
= xMalloc (strlen (cwd
) + 2, char);
270 if (cwd
[strlen (cwd
) - (size_t) 1] == PATH_SEPARATOR
)
271 strcpy (CurrentDirectory
, cwd
);
273 sprintf (CurrentDirectory
, "%s%c", cwd
, OUTPUT_PATH_SEPARATOR
);
278 extern void error (const errorSelection selection
,
279 const char *const format
, ...)
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
);
291 if (selected (selection
, FATAL
))
296 extern int stricmp (const char *s1
, const char *s2
)
301 result
= toupper ((int) *s1
) - toupper ((int) *s2
);
302 } while (result
== 0 && *s1
++ != '\0' && *s2
++ != '\0');
307 #ifndef HAVE_STRNICMP
308 extern int strnicmp (const char *s1
, const char *s2
, size_t n
)
313 result
= toupper ((int) *s1
) - toupper ((int) *s2
);
314 } while (result
== 0 && --n
> 0 && *s1
++ != '\0' && *s2
++ != '\0');
320 extern char* strstr (const char *str
, const char *substr
)
322 const size_t length
= strlen (substr
);
323 const char *match
= NULL
;
326 for (p
= str
; *p
!= '\0' && match
== NULL
; ++p
)
327 if (strncmp (p
, substr
, length
) == 0)
329 return (char*) match
;
333 extern char* eStrdup (const char* str
)
335 char* result
= xMalloc (strlen (str
) + 1, char);
336 strcpy (result
, str
);
340 extern void *eMalloc (const size_t size
)
342 void *buffer
= g_malloc (size
);
345 error (FATAL
, "out of memory");
350 extern void *eCalloc (const size_t count
, const size_t size
)
352 void *buffer
= calloc (count
, size
);
355 error (FATAL
, "out of memory");
360 extern void *eRealloc (void *const ptr
, const size_t size
)
364 buffer
= eMalloc (size
);
367 buffer
= g_realloc (ptr
, size
);
369 error (FATAL
, "out of memory");
374 extern void eFree (void *const ptr
)
380 extern void toLowerString (char* str
)
384 *str
= tolower ((int) *str
);
389 extern void toUpperString (char* str
)
393 *str
= toupper ((int) *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);
405 result
[i
] = tolower ((int) str
[i
]);
406 while (str
[i
++] != '\0');
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);
417 result
[i
] = toupper ((int) str
[i
]);
418 while (str
[i
++] != '\0');
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
;
434 static boolean
isSymbolicLink (const char *const name
)
436 #if defined (MSDOS) || defined (WIN32) || defined (VMS) || defined (__EMX__) || defined (AMIGA)
439 struct stat fileStatus
;
440 boolean result
= FALSE
;
442 if (g_lstat (name
, &fileStatus
) == 0)
443 result
= (boolean
) (S_ISLNK (fileStatus
.st_mode
));
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
));
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);
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
);
486 static boolean
isSetUID (const char *const name
)
488 #if defined (VMS) || defined (MSDOS) || defined (WIN32) || defined (__EMX__) || defined (AMIGA)
491 struct stat fileStatus
;
492 boolean result
= FALSE
;
494 if (g_stat (name
, &fileStatus
) == 0)
495 result
= (boolean
) ((fileStatus
.st_mode
& S_ISUID
) != 0);
504 static boolean
isDirectory (const char *const name
)
506 boolean result
= FALSE
;
508 struct FileInfoBlock
*const fib
= xMalloc (1, struct FileInfoBlock
);
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
);
523 struct stat fileStatus
;
525 if (g_stat (name
, &fileStatus
) == 0)
526 result
= (boolean
) S_ISDIR (fileStatus
.st_mode
);
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 )
547 *pos = ftell (stream);
554 extern int fsetpos ( stream, pos )
558 return fseek (stream, *pos, SEEK_SET);
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
579 || strcmp (Option
.tagFileName
, "sys$output") == 0
581 || strcmp (Option
.tagFileName
, "/dev/stdout") == 0
588 extern FILE *tempFile (const char *const mode
, char **const pName
)
594 const char *const template = "tags.XXXXXX";
595 const char *tmpdir
= NULL
;
596 if (! isSetUID (ExecutableProgram
))
597 tmpdir
= getenv ("TMPDIR");
600 name
= xMalloc (strlen (tmpdir
) + 1 + strlen (template) + 1, char);
601 sprintf (name
, "%s%c%s", tmpdir
, OUTPUT_PATH_SEPARATOR
, template);
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
);
610 error (FATAL
| PERROR
, "cannot open temporary file");
611 fp
= fdopen (fd
, mode
);
613 error (FATAL
| PERROR
, "cannot open temporary file");
615 debugPrintf (DEBUG_STATUS
, "opened temporary file %s\n", name
); )
616 Assert (*pName
== NULL
);
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
;
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
]);
641 const char *tail
= strrchr (filePath
, PATH_SEPARATOR
);
646 ++tail
; /* step past last delimiter */
649 /* remove version number from filename */
650 char *p
= strrchr ((char *) 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
)
665 else if (isalpha (path
[0]) && path
[1] == ':')
667 if (strchr (PathDelimiters
, path
[2]) != NULL
)
670 /* We don't support non-absolute file names with a drive
671 * letter, like `d:NAME' (it's too much hassle).
674 "%s: relative file names with drive letters not supported",
678 result
= (boolean
) (strchr (path
, ':') != NULL
);
680 result
= (boolean
) (path
[0] == PATH_SEPARATOR
);
685 /* Return a newly-allocated string whose contents concatenate those of
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);
695 strcpy (result
+ len1
, s2
);
696 strcpy (result
+ len1
+ len2
, s3
);
697 result
[len1
+ len2
+ len3
] = '\0';
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
)
711 if (isAbsolutePath (file
))
712 res
= eStrdup (file
);
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'))
727 while (cp
>= res
&& ! isAbsolutePath (cp
));
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
735 else if (cp
[0] != '/')
738 memmove (cp
, slashp
+ 3, strlen (slashp
+ 3) + 1);
742 else if (slashp
[2] == '/' || slashp
[2] == '\0')
744 memmove (slashp
, slashp
+ 2, strlen (slashp
+ 2) + 1);
748 slashp
= strchr (slashp
+ 1, '/');
752 return eStrdup ("/");
755 #ifdef MSDOS_STYLE_PATH
756 /* Canonicalize drive letter case. */
757 if (res
[1] == ':' && islower (res
[0]))
758 res
[0] = toupper (res
[0]);
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
)
773 #ifdef MSDOS_STYLE_PATH
775 for (p
= file
; *p
!= '\0' ; p
++)
779 slashp
= strrchr (file
, '/');
781 res
= eStrdup (CurrentDirectory
);
786 res
= absoluteFilename (file
);
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
)
802 /* Find the common root of file and dir (with a trailing slash). */
803 absdir
= absoluteFilename (file
);
806 while (*fp
++ == *dp
++)
809 dp
--; /* back to the first differing char */
811 { /* look at the equal chars until '/' */
813 return absdir
; /* first char differs, give up */
816 } while (*fp
!= '/');
818 /* Build a sequence of "../" strings for the resulting relative file name.
821 while ((dp
= strchr (dp
+ 1, '/')) != NULL
)
823 res
= xMalloc (3 * i
+ strlen (fp
+ 1) + 1, char);
828 /* Add the file name relative to the common root of file and dir. */
829 strcat (res
, fp
+ 1);
835 extern vString
*combinePathAndFile (const char *const path
,
836 const char *const file
)
838 vString
*const filePath
= vStringNew ();
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
);
850 vStringNCatS (filePath
, file
, versionId
- file
);
851 vStringCopyToLower (filePath
, filePath
);
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
, ']');
864 vStringPut (filePath
, '>');
865 vStringTerminate (filePath
);
868 const int lastChar
= path
[strlen (path
) - 1];
869 # ifdef MSDOS_STYLE_PATH
870 boolean terminated
= (boolean
) (strchr (PathDelimiters
, lastChar
) != NULL
);
872 boolean terminated
= (boolean
) (lastChar
== PATH_SEPARATOR
);
875 vStringCopyS (filePath
, path
);
878 vStringPut (filePath
, OUTPUT_PATH_SEPARATOR
);
879 vStringTerminate (filePath
);
881 vStringCatS (filePath
, file
);
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
)
902 stringListCombine (Excluded
, new);
906 vString
*const item
= vStringNewInit (parameter
);
907 if (Excluded
== NULL
)
908 Excluded
= stringListNew ();
909 stringListAdd (Excluded
, item
);
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
);
925 /* not a good solution, but the only one which works often */
927 result
= (boolean
) (strcmp (name
, TagFile
.name
) == 0);
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
);
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
);
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);
980 vStringDelete (filePath
);
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
);
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.
1002 anchor
->ap_Flags
= APF_DODOT
| APF_DOWILD
;
1004 anchor
->ap_Flags
= APF_DoDot
| APF_DoWild
;
1007 result
= MatchFirst ((UBYTE
*) pattern
, anchor
);
1010 resize
|= createTagsForEntry ((char *) anchor
->ap_Buf
);
1011 result
= MatchNext (anchor
);
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
)
1032 else if (separator
== path
) /* backed up to root directory */
1033 *(separator
+ 1) = '\0';
1036 result
= isSameFile (path
, dirName
);
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
);
1051 #if defined (HAVE_OPENDIR)
1052 DIR *const dir
= opendir (dirName
);
1054 error (WARNING
| PERROR
, "cannot recurse into directory \"%s\"",
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)
1066 if (strcmp (dirName
, ".") == 0)
1067 filePath
= vStringNewInit (entry
->d_name
);
1069 filePath
= combinePathAndFile (dirName
, entry
->d_name
);
1070 resize
|= createTagsForEntry (vStringValue (filePath
));
1071 vStringDelete (filePath
);
1076 #elif defined (AMIGA) || defined (MSDOS) || defined (WIN32)
1077 vString
*const pattern
= vStringNew ();
1078 verbose ("RECURSING into directory \"%s\"\n", dirName
);
1080 if (*dirName
!= '\0' && strcmp (dirName
, ".") != 0)
1082 vStringCopyS (pattern
, dirName
);
1083 if (dirName
[strlen (dirName
) - 1] != '/')
1084 vStringPut (pattern
, '/');
1086 vStringCatS (pattern
, "#?");
1088 vStringCopyS (pattern
, dirName
);
1089 vStringPut (pattern
, OUTPUT_PATH_SEPARATOR
);
1090 vStringCatS (pattern
, "*.*");
1092 resize
= createTagsForMatchingEntries (vStringValue (pattern
));
1093 vStringDelete (pattern
);
1094 #endif /* HAVE_OPENDIR */
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
);
1115 resize
= parseFile (entryName
);
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
);
1146 resize
|= createTagsForEntry (arg
);
1149 parseOptions (args
);
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
;
1161 cookedArgs
* args
= cArgNewFromLineFile (fp
);
1162 parseOptions (args
);
1163 while (! cArgOff (args
))
1165 resize
|= createTagsForEntry (cArgItem (args
));
1168 if (Option
.filterTerminator
!= NULL
)
1169 fputs (Option
.filterTerminator
, stdout
);
1173 parseOptions (args
);
1180 /* Read from a named file a list of file names for which to generate tags.
1182 static boolean
createTagsFromListFile (const char* const fileName
)
1185 Assert (fileName
!= NULL
);
1186 if (strcmp (fileName
, "-") == 0)
1187 resize
= createTagsFromFileInput (stdin
, FALSE
);
1190 FILE* const fp
= g_fopen (fileName
, "r");
1192 error (FATAL
| PERROR
, "cannot open list file \"%s\"", fileName
);
1193 resize
= createTagsFromFileInput (fp
, FALSE
);
1199 #if defined (HAVE_CLOCK)
1200 # define CLOCK_AVAILABLE
1201 # ifndef CLOCKS_PER_SEC
1202 # define CLOCKS_PER_SEC 1000000
1204 #elif defined (HAVE_TIMES)
1205 # define CLOCK_AVAILABLE
1206 # define CLOCKS_PER_SEC 60
1207 static clock_t clock (void)
1212 return (buf
.tms_utime
+ buf
.tms_stime
);
1215 # define clock() (clock_t)0
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])) /
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);
1238 fputc ('\n', errout
);
1240 fprintf (errout
, "%lu tag%s added to tag file",
1241 TagFile
.numTags
.added
, plural (TagFile
.numTags
.added
));
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
);
1253 fputc ('\n', errout
);
1257 fprintf (errout
, "longest tag line = %lu\n",
1258 (unsigned long) TagFile
.max
.line
);
1262 static void makeTags (cookedArgs
* args
)
1264 clock_t timeStamps
[3];
1265 boolean resize
= FALSE
;
1266 boolean files
= (boolean
)(! cArgOff (args
) || Option
.fileList
!= NULL
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
)
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
);
1291 verbose ("Reading filter input\n");
1292 resize
= (boolean
) (createTagsFromFileInput (stdin
, TRUE
) || resize
);
1294 if (! files
&& Option
.recurse
)
1295 resize
= recurseIntoDirectory (".");
1299 if (! Option
.filter
)
1300 closeTagFile (resize
);
1304 if (Option
.printTotals
)
1305 printTotals (timeStamps
);
1313 static void setExecutableName (const char *const path
)
1315 ExecutableProgram
= path
;
1316 ExecutableName
= baseFilename (path
);
1319 /* remove filetype from executable name */
1320 char *p
= strrchr (ExecutableName
, '.');
1327 extern int ctags_main (int UNUSED argc
, char **argv
)
1331 extern int getredirection (int *ac
, char ***av
);
1333 /* do wildcard expansion and I/O redirection */
1334 getredirection (&argc
, &argv
);
1338 /* This program doesn't work when started from the Workbench */
1344 _wildcard (&argc
, &argv
); /* expand wildcards in argument list */
1347 #if defined (macintosh) && BUILD_MPW_TOOL == 0
1348 argc
= ccommand (&argv
);
1351 setCurrentDirectory ();
1352 setExecutableName (*argv
++);
1357 /* always excluded by default */
1358 processExcludeOption (NULL
, "EIFGEN");
1359 processExcludeOption (NULL
, "SCCS");
1361 args
= cArgNewFromArgv (argv
);
1362 previewFirstOption (args
);
1363 testEtagsInvocation ();
1364 initializeParsing ();
1366 readOptionConfiguration ();
1367 verbose ("Reading initial options from command line\n");
1368 parseOptions (args
);
1374 eFree (CurrentDirectory
);
1375 freeList (&Excluded
);
1377 freeKeywordTable ();
1378 freeSourceFileResources ();
1379 freeTagFileResources ();
1380 freeOptionResources ();
1381 freeParserResources ();
1383 freeRegexResources ();
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: */