geany  1.38
About: Geany is a text editor (using GTK2) with basic features of an integrated development environment (syntax highlighting, code folding, symbol name auto-completion, ...). F: office T: editor programming GTK+ IDE
  Fossies Dox: geany-1.38.tar.bz2  ("unofficial" and yet experimental doxygen-generated source code documentation)  

main.c
Go to the documentation of this file.
1/*
2* Copyright (c) 1996-2003, Darren Hiebert
3*
4* Author: Darren Hiebert <dhiebert@users.sourceforge.net>
5* http://ctags.sourceforge.net
6*
7* This source code is released for free distribution under the terms of the
8* GNU General Public License version 2 or (at your option) any later version.
9* It is provided on an as-is basis and no responsibility is accepted for its
10* failure to perform as expected.
11*
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.
15*
16* This module contains the start-up code and routines to determine the list
17* of files to parsed for tags.
18*/
19
20/*
21* INCLUDE FILES
22*/
23#include "general.h" /* must always come first */
24
25#if HAVE_DECL___ENVIRON
26#include <unistd.h>
27#elif HAVE_DECL__NSGETENVIRON
28#include <crt_externs.h>
29#endif
30
31#include <stdlib.h>
32#include <string.h>
33#include <time.h>
34
35/* To provide directory searching for recursion feature.
36 */
37
38#ifdef HAVE_DIRENT_H
39# ifdef HAVE_SYS_TYPES_H
40# include <sys/types.h> /* required by dirent.h */
41# endif
42# include <dirent.h> /* to declare opendir() */
43#endif
44#ifdef HAVE_DIRECT_H
45# include <direct.h> /* to _getcwd() */
46#endif
47#ifdef HAVE_IO_H
48# include <io.h> /* to declare _findfirst() */
49#endif
50
51
52#include "ctags.h"
53#include "debug.h"
54#include "entry_p.h"
55#include "error_p.h"
56#include "field_p.h"
57#include "keyword_p.h"
58#include "main_p.h"
59#include "options_p.h"
60#include "parse_p.h"
61#include "read_p.h"
62#include "routines_p.h"
63#include "stats_p.h"
64#include "trace.h"
65#include "trashbox_p.h"
66#include "writer_p.h"
67#include "xtag_p.h"
68
69#ifdef HAVE_JANSSON
70#include "interactive_p.h"
71#include <jansson.h>
72#include <errno.h>
73#endif
74
75/*
76* DATA DEFINITIONS
77*/
79static void *mainData;
80
81/*
82* FUNCTION PROTOTYPES
83*/
84static bool createTagsForEntry (const char *const entryName);
85
86/*
87* FUNCTION DEFINITIONS
88*/
89
90#if defined (HAVE_OPENDIR) && (defined (HAVE_DIRENT_H) || defined (_MSC_VER))
91static bool recurseUsingOpendir (const char *const dirName)
92{
93 bool resize = false;
94 DIR *const dir = opendir (dirName);
95 if (dir == NULL)
96 error (WARNING | PERROR, "cannot recurse into directory \"%s\"", dirName);
97 else
98 {
99 struct dirent *entry;
100 while ((entry = readdir (dir)) != NULL)
101 {
102 if (strcmp (entry->d_name, ".") != 0 &&
103 strcmp (entry->d_name, "..") != 0)
104 {
105 char *filePath;
106 bool free_p = false;
107 if (strcmp (dirName, ".") == 0)
108 filePath = entry->d_name;
109 else
110 {
111 filePath = combinePathAndFile (dirName, entry->d_name);
112 free_p = true;
113 }
114 resize |= createTagsForEntry (filePath);
115 if (free_p)
116 eFree (filePath);
117 }
118 }
119 closedir (dir);
120 }
121 return resize;
122}
123
124#elif defined (HAVE__FINDFIRST)
125
126static bool createTagsForWildcardEntry (
127 const char *const pattern, const size_t dirLength,
128 const char *const entryName)
129{
130 bool resize = false;
131 /* we must not recurse into the directories "." or ".." */
132 if (strcmp (entryName, ".") != 0 && strcmp (entryName, "..") != 0)
133 {
134 vString *const filePath = vStringNew ();
135 vStringNCopyS (filePath, pattern, dirLength);
136 vStringCatS (filePath, entryName);
137 resize = createTagsForEntry (vStringValue (filePath));
138 vStringDelete (filePath);
139 }
140 return resize;
141}
142
143static bool createTagsForWildcardUsingFindfirst (const char *const pattern)
144{
145 bool resize = false;
146 const size_t dirLength = baseFilename (pattern) - pattern;
147#if defined (HAVE__FINDFIRST)
148 struct _finddata_t fileInfo;
149 findfirst_t hFile = _findfirst (pattern, &fileInfo);
150 if (hFile != -1L)
151 {
152 do
153 {
154 const char *const entry = (const char *) fileInfo.name;
155 resize |= createTagsForWildcardEntry (pattern, dirLength, entry);
156 } while (_findnext (hFile, &fileInfo) == 0);
157 _findclose (hFile);
158 }
159#endif
160 return resize;
161}
162
163#endif
164
165
166static bool recurseIntoDirectory (const char *const dirName)
167{
168 static unsigned int recursionDepth = 0;
169
170 recursionDepth++;
171
172 bool resize = false;
173 if (isRecursiveLink (dirName))
174 verbose ("ignoring \"%s\" (recursive link)\n", dirName);
175 else if (! Option.recurse)
176 verbose ("ignoring \"%s\" (directory)\n", dirName);
177 else if(recursionDepth > Option.maxRecursionDepth)
178 verbose ("not descending in directory \"%s\" (depth %u > %u)\n",
179 dirName, recursionDepth, Option.maxRecursionDepth);
180 else
181 {
182 verbose ("RECURSING into directory \"%s\"\n", dirName);
183#if defined (HAVE_OPENDIR) && (defined (HAVE_DIRENT_H) || defined (_MSC_VER))
184 resize = recurseUsingOpendir (dirName);
185#elif defined (HAVE__FINDFIRST)
186 {
187 vString *const pattern = vStringNew ();
188 vStringCopyS (pattern, dirName);
190 vStringCatS (pattern, "*.*");
191 resize = createTagsForWildcardUsingFindfirst (vStringValue (pattern));
192 vStringDelete (pattern);
193 }
194#endif
195 }
196
197 recursionDepth--;
198
199 return resize;
200}
201
202static bool createTagsForEntry (const char *const entryName)
203{
204 bool resize = false;
205 fileStatus *status = eStat (entryName);
206
207 Assert (entryName != NULL);
208 if (isExcludedFile (entryName, true))
209 verbose ("excluding \"%s\" (the early stage)\n", entryName);
210 else if (status->isSymbolicLink && ! Option.followLinks)
211 verbose ("ignoring \"%s\" (symbolic link)\n", entryName);
212 else if (! status->exists)
213 error (WARNING | PERROR, "cannot open input file \"%s\"", entryName);
214 else if (status->isDirectory)
215 resize = recurseIntoDirectory (entryName);
216 else if (! status->isNormalFile)
217 verbose ("ignoring \"%s\" (special file)\n", entryName);
218 else if (isExcludedFile (entryName, false))
219 verbose ("excluding \"%s\"\n", entryName);
220 else
221 resize = parseFile (entryName);
222
223 eStatFree (status);
224 return resize;
225}
226
227#ifdef MANUAL_GLOBBING
228
229static bool createTagsForWildcardArg (const char *const arg)
230{
231 bool resize = false;
232 vString *const pattern = vStringNewInit (arg);
233 char *patternS = vStringValue (pattern);
234
235#if defined (HAVE__FINDFIRST)
236 /* We must transform the "." and ".." forms into something that can
237 * be expanded by the _findfirst function.
238 */
239 if (Option.recurse &&
240 (strcmp (patternS, ".") == 0 || strcmp (patternS, "..") == 0))
241 {
243 vStringCatS (pattern, "*.*");
244 }
245 resize |= createTagsForWildcardUsingFindfirst (patternS);
246#endif
247 vStringDelete (pattern);
248 return resize;
249}
250
251#endif
252
253static bool createTagsForArgs (cookedArgs *const args)
254{
255 bool resize = false;
256
257 /* Generate tags for each argument on the command line.
258 */
259 while (! cArgOff (args))
260 {
261 const char *const arg = cArgItem (args);
262
263#ifdef MANUAL_GLOBBING
264 resize |= createTagsForWildcardArg (arg);
265#else
266 resize |= createTagsForEntry (arg);
267#endif
268 cArgForth (args);
269 parseCmdlineOptions (args);
270 }
271 return resize;
272}
273
274/* Read from an opened file a list of file names for which to generate tags.
275 */
276static bool createTagsFromFileInput (FILE *const fp, const bool filter)
277{
278 bool resize = false;
279 if (fp != NULL)
280 {
281 cookedArgs *args = cArgNewFromLineFile (fp);
282 parseCmdlineOptions (args);
283 while (! cArgOff (args))
284 {
285 resize |= createTagsForEntry (cArgItem (args));
286 if (filter)
287 {
289 fputs (Option.filterTerminator, stdout);
290 fflush (stdout);
291 }
292 cArgForth (args);
293 parseCmdlineOptions (args);
294 }
295 cArgDelete (args);
296 }
297 return resize;
298}
299
300/* Read from a named file a list of file names for which to generate tags.
301 */
302static bool createTagsFromListFile (const char *const fileName)
303{
304 bool resize;
305 Assert (fileName != NULL);
306 if (strcmp (fileName, "-") == 0)
307 resize = createTagsFromFileInput (stdin, false);
308 else
309 {
310 FILE *const fp = fopen (fileName, "r");
311 if (fp == NULL)
312 error (FATAL | PERROR, "cannot open list file \"%s\"", fileName);
313 resize = createTagsFromFileInput (fp, false);
314 fclose (fp);
315 }
316 return resize;
317}
318
319static bool etagsInclude (void)
320{
321 return (bool)(Option.etags && Option.etagsInclude != NULL);
322}
323
324extern void setMainLoop (mainLoopFunc func, void *data)
325{
326 mainLoop = func;
327 mainData = data;
328}
329
330static void runMainLoop (cookedArgs *args)
331{
332 (* mainLoop) (args, mainData);
333}
334
335static void batchMakeTags (cookedArgs *args, void *user CTAGS_ATTR_UNUSED)
336{
337 clock_t timeStamps [3];
338 bool resize = false;
339 bool files = (bool)(! cArgOff (args) || Option.fileList != NULL
340 || Option.filter);
341
342 if (! files)
343 {
344 if (filesRequired ())
345 error (FATAL, "No files specified. Try \"%s --help\".",
347 else if (! Option.recurse && ! etagsInclude ())
348 return;
349 }
350
351#define timeStamp(n) timeStamps[(n)]=(Option.printTotals ? clock():(clock_t)0)
352 if ((! Option.filter) && (! Option.printLanguage))
353 openTagFile ();
354
355 timeStamp (0);
356
357 if (! cArgOff (args))
358 {
359 verbose ("Reading command line arguments\n");
360 resize = createTagsForArgs (args);
361 }
362 if (Option.fileList != NULL)
363 {
364 verbose ("Reading list file\n");
365 resize = (bool) (createTagsFromListFile (Option.fileList) || resize);
366 }
367 if (Option.filter)
368 {
369 verbose ("Reading filter input\n");
370 resize = (bool) (createTagsFromFileInput (stdin, true) || resize);
371 }
372 if (! files && Option.recurse)
373 resize = recurseIntoDirectory (".");
374
375 timeStamp (1);
376
377 if ((! Option.filter) && (!Option.printLanguage))
378 closeTagFile (resize);
379
380 timeStamp (2);
381
383 {
384 printTotals (timeStamps, Option.append, Option.sorted);
385 if (Option.printTotals > 1)
386 for (unsigned int i = 0; i < countParsers(); i++)
388 }
389
390#undef timeStamp
391}
392
393#ifdef HAVE_JANSSON
394void interactiveLoop (cookedArgs *args CTAGS_ATTR_UNUSED, void *user)
395{
396 struct interactiveModeArgs *iargs = user;
397
398 if (iargs->sandbox) {
399 /* As of jansson 2.6, the object hashing is seeded off
400 of /dev/urandom, so trigger the hash seeding
401 before installing the syscall filter.
402 */
403 json_t * tmp = json_object ();
404 json_decref (tmp);
405
406 if (installSyscallFilter ()) {
407 error (FATAL, "install_syscall_filter failed");
408 /* The explicit exit call is needed because
409 "error (FATAL,..." just prints a message in
410 interactive mode. */
411 exit (1);
412 }
413 }
414
415 char buffer[1024];
416 json_t *request;
417
418 fputs ("{\"_type\": \"program\", \"name\": \"" PROGRAM_NAME "\", \"version\": \"" PROGRAM_VERSION "\"}\n", stdout);
419 fflush (stdout);
420
421 while (fgets (buffer, sizeof(buffer), stdin))
422 {
423 if (buffer[0] == '\n')
424 continue;
425
426 request = json_loads (buffer, JSON_DISABLE_EOF_CHECK, NULL);
427 if (! request)
428 {
429 error (FATAL, "invalid json");
430 goto next;
431 }
432
433 json_t *command = json_object_get (request, "command");
434 if (! command)
435 {
436 error (FATAL, "command name not found");
437 goto next;
438 }
439
440 if (!strcmp ("generate-tags", json_string_value (command)))
441 {
442 json_int_t size = -1;
443 const char *filename;
444
445 if (json_unpack (request, "{ss}", "filename", &filename) == -1)
446 {
447 error (FATAL, "invalid generate-tags request");
448 goto next;
449 }
450
451 json_unpack (request, "{sI}", "size", &size);
452
453 openTagFile ();
454 if (size == -1)
455 { /* read from disk */
456 if (iargs->sandbox) {
457 error (FATAL,
458 "invalid request in sandbox submode: reading file contents from a file is limited");
459 closeTagFile (false);
460 goto next;
461 }
462
464 }
465 else
466 { /* read nbytes from stream */
467 unsigned char *data = eMalloc (size);
468 size = fread (data, 1, size, stdin);
469 MIO *mio = mio_new_memory (data, size, eRealloc, eFreeNoNullCheck);
471 mio_unref (mio);
472 }
473
474 closeTagFile (false);
475 fputs ("{\"_type\": \"completed\", \"command\": \"generate-tags\"}\n", stdout);
476 fflush(stdout);
477 }
478 else
479 {
480 error (FATAL, "unknown command name");
481 goto next;
482 }
483
484 next:
485 json_decref (request);
486 }
487}
488#endif
489
490static bool isSafeVar (const char* var)
491{
492 const char *safe_vars[] = {
493 "BASH_FUNC_module()=",
494 "BASH_FUNC_scl()=",
495 NULL
496 };
497 const char *sv;
498
499 for (sv = safe_vars[0]; sv != NULL; sv++)
500 if (strncmp(var, sv, strlen (sv)) == 0)
501 return true;
502
503 return false;
504}
505
506static void sanitizeEnviron (void)
507{
508 char **e;
509 int i;
510
511#if HAVE_DECL___ENVIRON
512 e = __environ;
513#elif HAVE_DECL__NSGETENVIRON
514 {
515 char ***ep = _NSGetEnviron();
516 if (ep)
517 e = *ep;
518 else
519 e = NULL;
520 }
521#else
522 e = NULL;
523#endif
524
525 if (!e)
526 return;
527
528 for (i = 0; e [i]; i++)
529 {
530 char *value;
531
532 value = strchr (e [i], '=');
533 if (!value)
534 continue;
535
536 value++;
537 if (!strncmp (value, "() {", 4))
538 {
539 if (isSafeVar (e [i]))
540 continue;
541 error (WARNING, "reset environment: %s", e [i]);
542 value [0] = '\0';
543 }
544 }
545}
546
547/*
548 * Start up code
549 */
550
551extern int ctags_cli_main (int argc CTAGS_ATTR_UNUSED, char **argv)
552{
553 cookedArgs *args;
554
555#if defined(WIN32) && defined(HAVE_MKSTEMP)
556 /* MinGW-w64's mkstemp() uses rand() for generating temporary files. */
557 srand ((unsigned int) clock ());
558#endif
559
561
562 DEBUG_INIT();
563
567
569 setExecutableName (*argv++);
571 checkRegex ();
574
575 args = cArgNewFromArgv (argv);
576 previewFirstOption (args);
579 initOptions ();
581 verbose ("Reading initial options from command line\n");
582 parseCmdlineOptions (args);
583 checkOptions ();
584
585 runMainLoop (args);
586
587 /* Clean up.
588 */
589 cArgDelete (args);
597#ifdef HAVE_ICONV
598 freeEncodingResources ();
599#endif
600
602
604 return (Option.printLanguage == true)? 0: 1;
605
606 exit (0);
607 return 0;
608}
const gchar * command
Definition: build.c:2677
static bool isSafeVar(const char *var)
Definition: main.c:490
static bool etagsInclude(void)
Definition: main.c:319
static void runMainLoop(cookedArgs *args)
Definition: main.c:330
int ctags_cli_main(int argc, char **argv)
Definition: main.c:551
static bool createTagsForEntry(const char *const entryName)
Definition: main.c:202
static bool recurseIntoDirectory(const char *const dirName)
Definition: main.c:166
static void batchMakeTags(cookedArgs *args, void *user)
Definition: main.c:335
static bool createTagsFromListFile(const char *const fileName)
Definition: main.c:302
static mainLoopFunc mainLoop
Definition: main.c:78
static bool createTagsFromFileInput(FILE *const fp, const bool filter)
Definition: main.c:276
static void sanitizeEnviron(void)
Definition: main.c:506
#define timeStamp(n)
static void * mainData
Definition: main.c:79
void setMainLoop(mainLoopFunc func, void *data)
Definition: main.c:324
static bool createTagsForArgs(cookedArgs *const args)
Definition: main.c:253
#define PROGRAM_VERSION
Definition: ctags.h:20
#define PROGRAM_NAME
Definition: ctags.h:22
#define Assert(c)
Definition: debug.h:47
#define DEBUG_INIT()
Definition: debug.h:100
#define findfirst_t
Definition: e_msoft.h:27
void freeTagFileResources(void)
Definition: entry.c:142
void closeTagFile(const bool resize)
Definition: entry.c:605
void openTagFile(void)
Definition: entry.c:379
void error(const errorSelection selection, const char *const format,...)
Definition: error.c:53
void setErrorPrinter(errorPrintFunc printer, void *data)
Definition: error.c:27
bool stderrDefaultErrorPrinter(const errorSelection selection, const char *const format, va_list ap, void *data)
Definition: error.c:33
void initFieldObjects(void)
Definition: field.c:237
static gchar ** filter
Definition: filebrowser.c:89
#define CTAGS_ATTR_UNUSED
Definition: gcc-attr.h:22
void interactiveLoop(cookedArgs *args, void *user)
int installSyscallFilter(void)
Definition: seccomp.c:75
void freeKeywordTable(void)
Definition: keyword.c:170
void freeRegexResources(void)
Definition: lregex.c:2233
bool checkRegex(void)
Definition: lregex.c:2768
MIO * mio_new_memory(unsigned char *data, size_t size, MIOReallocFunc realloc_func, MIODestroyNotify free_func)
mio_new_memory: @data: Initial data (may be NULL) @size: Length of @data in bytes @realloc_func: A fu...
Definition: mio.c:316
int mio_unref(MIO *mio)
mio_unref: @mio: A MIO object
Definition: mio.c:480
cookedArgs * cArgNewFromArgv(char *const *const argv)
Definition: options.c:938
const char * cArgItem(cookedArgs *const current)
Definition: options.c:996
bool cArgOff(cookedArgs *const current)
Definition: options.c:984
void parseCmdlineOptions(cookedArgs *const args)
Definition: options.c:3464
void testEtagsInvocation(void)
Definition: options.c:817
bool filesRequired(void)
Definition: options.c:722
void previewFirstOption(cookedArgs *const args)
Definition: options.c:3507
cookedArgs * cArgNewFromLineFile(FILE *const fp)
Definition: options.c:956
void initOptions(void)
Definition: options.c:3804
void checkOptions(void)
Definition: options.c:730
void verbose(const char *const format,...)
Definition: options.c:655
void cArgDelete(cookedArgs *const current)
Definition: options.c:965
void cArgForth(cookedArgs *const current)
Definition: options.c:1002
void readOptionConfiguration(void)
Definition: options.c:3791
optionValues Option
Definition: options.c:137
bool isExcludedFile(const char *const name, bool falseIfExceptionsAreDefeind)
Definition: options.c:1184
void freeOptionResources(void)
Definition: options.c:3881
void(* mainLoopFunc)(cookedArgs *args, void *data)
Definition: options_p.h:136
void printParserStatisticsIfUsed(langType language)
Definition: parse.c:3798
unsigned int countParsers(void)
Definition: parse.c:177
void initializeParsing(void)
Definition: parse.c:1907
bool parseFile(const char *const fileName)
Definition: parse.c:4076
bool parseFileWithMio(const char *const fileName, MIO *mio, void *clientData)
Definition: parse.c:4108
void freeParserResources(void)
Definition: parse.c:1954
#define NULL
Definition: rbtree.h:150
void freeInputFileResources(void)
Definition: read.c:281
char * combinePathAndFile(const char *const path, const char *const file)
Definition: routines.c:713
void setExecutableName(const char *const path)
Definition: routines.c:186
void * eMalloc(const size_t size)
Definition: routines.c:218
void eFreeNoNullCheck(void *const ptr)
Definition: routines.c:258
bool isRecursiveLink(const char *const dirName)
Definition: routines.c:507
const char * getExecutableName(void)
Definition: routines.c:192
void * eRealloc(void *const ptr, const size_t size)
Definition: routines.c:238
void setCurrentDirectory(void)
Definition: routines.c:438
void eFree(void *const ptr)
Definition: routines.c:252
fileStatus * eStat(const char *const fileName)
Definition: routines.c:455
void eStatFree(fileStatus *status)
Definition: routines.c:486
const char * baseFilename(const char *const filePath)
Definition: routines.c:608
void freeRoutineResources(void)
Definition: routines.c:180
@ PERROR
Definition: routines.h:37
@ FATAL
Definition: routines.h:37
@ WARNING
Definition: routines.h:37
#define OUTPUT_PATH_SEPARATOR
Definition: routines_p.h:33
GtkWidget * entry
Definition: search.c:118
const gchar filename[]
Definition: stash-example.c:4
void printTotals(const clock_t *const timeStamps, bool append, sortType sorted)
Definition: stats.c:47
long files
Definition: stats.c:32
MIO:
Definition: mio.c:136
bool exists
Definition: routines_p.h:49
bool isNormalFile
Definition: routines_p.h:58
bool isSymbolicLink
Definition: routines_p.h:52
bool isDirectory
Definition: routines_p.h:55
char * filterTerminator
Definition: options_p.h:110
sortType sorted
Definition: options_p.h:95
bool followLinks
Definition: options_p.h:108
stringList * etagsInclude
Definition: options_p.h:101
bool printLanguage
Definition: options_p.h:114
char * fileList
Definition: options_p.h:98
unsigned int maxRecursionDepth
Definition: options_p.h:120
bool recurse
Definition: options_p.h:94
void finiDefaultTrashBox(void)
Definition: trashbox.c:174
void initDefaultTrashBox(void)
Definition: trashbox.c:169
void vStringCopyS(vString *const string, const char *const s)
Definition: vstring.c:213
vString * vStringNew(void)
Definition: vstring.c:70
void vStringDelete(vString *const string)
Definition: vstring.c:60
void vStringNCopyS(vString *const string, const char *const s, const size_t length)
Definition: vstring.c:226
void vStringCatS(vString *const string, const char *const s)
Definition: vstring.c:146
vString * vStringNewInit(const char *const s)
Definition: vstring.c:90
#define vStringValue(vs)
Definition: vstring.h:28
static void vStringPut(vString *const string, const int c)
Definition: vstring.h:101
void setTagWriter(writerType wtype, tagWriter *customWriter)
Definition: writer.c:34
@ WRITER_U_CTAGS
Definition: writer_p.h:24
void initXtagObjects(void)
Definition: xtag.c:308