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);
603
fd = mkstemps(name, 0);
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);
614
error (FATAL | PERROR, "cannot open temporary file");
615
fp = fdopen (fd, mode);
617
error (FATAL | PERROR, "cannot open temporary file");
619
debugPrintf (DEBUG_STATUS, "opened temporary file %s\n", name); )
620
Assert (*pName == NULL);
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;
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]);
645
const char *tail = strrchr (filePath, PATH_SEPARATOR);
650
++tail; /* step past last delimiter */
653
/* remove version number from filename */
654
char *p = strrchr ((char *) 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)
669
else if (isalpha (path [0]) && path [1] == ':')
671
if (strchr (PathDelimiters, path [2]) != NULL)
674
/* We don't support non-absolute file names with a drive
675
* letter, like `d:NAME' (it's too much hassle).
678
"%s: relative file names with drive letters not supported",
682
result = (boolean) (strchr (path, ':') != NULL);
684
result = (boolean) (path [0] == PATH_SEPARATOR);
689
/* Return a newly-allocated string whose contents concatenate those of
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);
699
strcpy (result + len1, s2);
700
strcpy (result + len1 + len2, s3);
701
result [len1 + len2 + len3] = '\0';
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)
715
if (isAbsolutePath (file))
716
res = eStrdup (file);
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'))
731
while (cp >= res && ! isAbsolutePath (cp));
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
739
else if (cp [0] != '/')
742
strcpy (cp, slashp + 3);
746
else if (slashp [2] == '/' || slashp [2] == '\0')
748
strcpy (slashp, slashp + 2);
752
slashp = strchr (slashp + 1, '/');
756
return eStrdup ("/");
759
#ifdef MSDOS_STYLE_PATH
760
/* Canonicalize drive letter case. */
761
if (res [1] == ':' && islower (res [0]))
762
res [0] = toupper (res [0]);
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)
777
#ifdef MSDOS_STYLE_PATH
779
for (p = file ; *p != '\0' ; p++)
783
slashp = strrchr (file, '/');
785
res = eStrdup (CurrentDirectory);
790
res = absoluteFilename (file);
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)
806
/* Find the common root of file and dir (with a trailing slash). */
807
absdir = absoluteFilename (file);
810
while (*fp++ == *dp++)
813
dp--; /* back to the first differing char */
815
{ /* look at the equal chars until '/' */
817
return absdir; /* first char differs, give up */
820
} while (*fp != '/');
822
/* Build a sequence of "../" strings for the resulting relative file name.
825
while ((dp = strchr (dp + 1, '/')) != NULL)
827
res = xMalloc (3 * i + strlen (fp + 1) + 1, char);
832
/* Add the file name relative to the common root of file and dir. */
833
strcat (res, fp + 1);
839
extern vString *combinePathAndFile (const char *const path,
840
const char *const file)
842
vString *const filePath = vStringNew ();
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);
854
vStringNCatS (filePath, file, versionId - file);
855
vStringCopyToLower (filePath, filePath);
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, ']');
868
vStringPut (filePath, '>');
869
vStringTerminate (filePath);
872
const int lastChar = path [strlen (path) - 1];
873
# ifdef MSDOS_STYLE_PATH
874
boolean terminated = (boolean) (strchr (PathDelimiters, lastChar) != NULL);
876
boolean terminated = (boolean) (lastChar == PATH_SEPARATOR);
879
vStringCopyS (filePath, path);
882
vStringPut (filePath, OUTPUT_PATH_SEPARATOR);
883
vStringTerminate (filePath);
885
vStringCatS (filePath, file);
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)
906
stringListCombine (Excluded, new);
910
vString *const item = vStringNewInit (parameter);
911
if (Excluded == NULL)
912
Excluded = stringListNew ();
913
stringListAdd (Excluded, item);
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);
929
/* not a good solution, but the only one which works often */
931
result = (boolean) (strcmp (name, TagFile.name) == 0);
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);
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);
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);
984
vStringDelete (filePath);
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);
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.
1006
anchor->ap_Flags = APF_DODOT | APF_DOWILD;
1008
anchor->ap_Flags = APF_DoDot | APF_DoWild;
1011
result = MatchFirst ((UBYTE *) pattern, anchor);
1014
resize |= createTagsForEntry ((char *) anchor->ap_Buf);
1015
result = MatchNext (anchor);
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)
1036
else if (separator == path) /* backed up to root directory */
1037
*(separator + 1) = '\0';
1040
result = isSameFile (path, dirName);
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);
1055
#if defined (HAVE_OPENDIR)
1056
DIR *const dir = opendir (dirName);
1058
error (WARNING | PERROR, "cannot recurse into directory \"%s\"",
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)
1070
if (strcmp (dirName, ".") == 0)
1071
filePath = vStringNewInit (entry->d_name);
1073
filePath = combinePathAndFile (dirName, entry->d_name);
1074
resize |= createTagsForEntry (vStringValue (filePath));
1075
vStringDelete (filePath);
1080
#elif defined (AMIGA) || defined (MSDOS) || defined (WIN32)
1081
vString *const pattern = vStringNew ();
1082
verbose ("RECURSING into directory \"%s\"\n", dirName);
1084
if (*dirName != '\0' && strcmp (dirName, ".") != 0)
1086
vStringCopyS (pattern, dirName);
1087
if (dirName [strlen (dirName) - 1] != '/')
1088
vStringPut (pattern, '/');
1090
vStringCatS (pattern, "#?");
1092
vStringCopyS (pattern, dirName);
1093
vStringPut (pattern, OUTPUT_PATH_SEPARATOR);
1094
vStringCatS (pattern, "*.*");
1096
resize = createTagsForMatchingEntries (vStringValue (pattern));
1097
vStringDelete (pattern);
1098
#endif /* HAVE_OPENDIR */
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);
1119
resize = parseFile (entryName);
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);
1150
resize |= createTagsForEntry (arg);
1153
parseOptions (args);
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;
1165
cookedArgs* args = cArgNewFromLineFile (fp);
1166
parseOptions (args);
1167
while (! cArgOff (args))
1169
resize |= createTagsForEntry (cArgItem (args));
1172
if (Option.filterTerminator != NULL)
1173
fputs (Option.filterTerminator, stdout);
1177
parseOptions (args);
1184
/* Read from a named file a list of file names for which to generate tags.
1186
static boolean createTagsFromListFile (const char* const fileName)
1189
Assert (fileName != NULL);
1190
if (strcmp (fileName, "-") == 0)
1191
resize = createTagsFromFileInput (stdin, FALSE);
1194
FILE* const fp = g_fopen (fileName, "r");
1196
error (FATAL | PERROR, "cannot open list file \"%s\"", fileName);
1197
resize = createTagsFromFileInput (fp, FALSE);
1203
#if defined (HAVE_CLOCK)
1204
# define CLOCK_AVAILABLE
1205
# ifndef CLOCKS_PER_SEC
1206
# define CLOCKS_PER_SEC 1000000
1208
#elif defined (HAVE_TIMES)
1209
# define CLOCK_AVAILABLE
1210
# define CLOCKS_PER_SEC 60
1211
static clock_t clock (void)
1216
return (buf.tms_utime + buf.tms_stime);
1219
# define clock() (clock_t)0
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])) /
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);
1242
fputc ('\n', errout);
1244
fprintf (errout, "%lu tag%s added to tag file",
1245
TagFile.numTags.added, plural (TagFile.numTags.added));
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);
1257
fputc ('\n', errout);
1261
fprintf (errout, "longest tag line = %lu\n",
1262
(unsigned long) TagFile.max.line);
1266
static void makeTags (cookedArgs* args)
1268
clock_t timeStamps [3];
1269
boolean resize = FALSE;
1270
boolean files = (boolean)(! cArgOff (args) || Option.fileList != NULL
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)
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);
1295
verbose ("Reading filter input\n");
1296
resize = (boolean) (createTagsFromFileInput (stdin, TRUE) || resize);
1298
if (! files && Option.recurse)
1299
resize = recurseIntoDirectory (".");
1303
if (! Option.filter)
1304
closeTagFile (resize);
1308
if (Option.printTotals)
1309
printTotals (timeStamps);
1317
static void setExecutableName (const char *const path)
1319
ExecutableProgram = path;
1320
ExecutableName = baseFilename (path);
1323
/* remove filetype from executable name */
1324
char *p = strrchr (ExecutableName, '.');
1331
extern int ctags_main (int __unused__ argc, char **argv)
1335
extern int getredirection (int *ac, char ***av);
1337
/* do wildcard expansion and I/O redirection */
1338
getredirection (&argc, &argv);
1342
/* This program doesn't work when started from the Workbench */
1348
_wildcard (&argc, &argv); /* expand wildcards in argument list */
1351
#if defined (macintosh) && BUILD_MPW_TOOL == 0
1352
argc = ccommand (&argv);
1355
setCurrentDirectory ();
1356
setExecutableName (*argv++);
1361
/* always excluded by default */
1362
processExcludeOption (NULL, "EIFGEN");
1363
processExcludeOption (NULL, "SCCS");
1365
args = cArgNewFromArgv (argv);
1366
previewFirstOption (args);
1367
testEtagsInvocation ();
1368
initializeParsing ();
1370
readOptionConfiguration ();
1371
verbose ("Reading initial options from command line\n");
1372
parseOptions (args);
1378
eFree (CurrentDirectory);
1379
freeList (&Excluded);
1381
freeKeywordTable ();
1382
freeSourceFileResources ();
1383
freeTagFileResources ();
1384
freeOptionResources ();
1385
freeParserResources ();
1387
freeRegexResources ();
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: */