1 /*
2 * Copyright (c) 1996-2003, Darren Hiebert
3 *
4 * This source code is released into the public domain.
5 *
6 * This module contains functions for reading tag files.
7 */
8
9 #include "readtags.h"
10 #include "printtags.h"
11 #include <string.h> /* strerror */
12 #include <stdlib.h> /* exit */
13 #include <stdio.h> /* stderr */
14
15 static const char *TagFileName = "tags";
16 static const char *ProgramName;
17 static int extensionFields;
18 static int SortOverride;
19 static sortType SortMethod;
20 static int allowPrintLineNumber;
21 static int debugMode;
22 static int escaping;
23 #ifdef READTAGS_DSL
24 #include "dsl/qualifier.h"
25 static QCode *Qualifier;
26 #include "dsl/sorter.h"
27 static SCode *Sorter;
28 #endif
29
tagsStrerror(int err)30 static const char* tagsStrerror (int err)
31 {
32 if (err > 0)
33 return strerror (err);
34 else if (err < 0)
35 {
36 switch (err)
37 {
38 case TagErrnoUnexpectedSortedMethod:
39 return "Unexpected sorted method";
40 case TagErrnoUnexpectedFormat:
41 return "Unexpected format number";
42 case TagErrnoUnexpectedLineno:
43 return "Unexpected value for line: field";
44 case TagErrnoInvalidArgument:
45 return "Unexpected argument passed to the API function";
46 default:
47 return "Unknown error";
48 }
49 }
50 else
51 return "no error";
52 }
53
printTag(const tagEntry * entry)54 static void printTag (const tagEntry *entry)
55 {
56 tagPrintOptions opts = {
57 .extensionFields = extensionFields,
58 .lineNumber = allowPrintLineNumber,
59 .escaping = escaping,
60 };
61 tagsPrint (entry, &opts, NULL, stdout);
62 }
63
printPseudoTag(const tagEntry * entry)64 static void printPseudoTag (const tagEntry *entry)
65 {
66 tagPrintOptions opts = {
67 .extensionFields = extensionFields,
68 .lineNumber = allowPrintLineNumber,
69 .escaping = escaping,
70 };
71 tagsPrintPseudoTag (entry, &opts, NULL, stdout);
72 }
73
74 #ifdef READTAGS_DSL
freeCopiedTag(tagEntry * e)75 static void freeCopiedTag (tagEntry *e)
76 {
77 free ((void *)e->name);
78 free ((void *)e->file);
79 if (e->address.pattern)
80 free ((void *)e->address.pattern);
81 if (e->kind)
82 free ((void *)e->kind);
83 for (unsigned short c = 0; c < e->fields.count; c++)
84 {
85 free ((void *)e->fields.list[c].key);
86 free ((void *)e->fields.list[c].value);
87 }
88 if (e->fields.count)
89 free ((void *)e->fields.list);
90 free ((void *)e);
91 }
92
copyTag(tagEntry * o)93 static tagEntry *copyTag (tagEntry *o)
94 {
95 tagEntry *n;
96
97 n = calloc (1, sizeof (*o));
98 if (!n)
99 perror (__FUNCTION__);
100
101 n->name = strdup (o->name);
102
103 if (!n->name)
104 perror (__FUNCTION__);
105
106 if (o->file)
107 n->file = strdup (o->file);
108 if (o->file && !n->file)
109 perror (__FUNCTION__);
110
111 if (o->address.pattern)
112 {
113 n->address.pattern = strdup (o->address.pattern);
114 if (!n->address.pattern)
115 perror (__FUNCTION__);
116 }
117
118 n->address.lineNumber = o->address.lineNumber;
119
120 if (o->kind)
121 {
122 n->kind = strdup (o->kind);
123 if (!n->kind)
124 perror (__FUNCTION__);
125 }
126
127 n->fileScope = o->fileScope;
128 n->fields.count = o->fields.count;
129
130 if (o->fields.count == 0)
131 return n;
132
133 n->fields.list = malloc (o->fields.count *sizeof (*o->fields.list));
134 if (!n->fields.list)
135 perror (__FUNCTION__);
136
137 for (unsigned short c = 0; c < o->fields.count; c++)
138 {
139 n->fields.list[c].key = strdup (o->fields.list[c].key);
140 if (!n->fields.list[c].key)
141 perror (__FUNCTION__);
142
143 n->fields.list[c].value = strdup (o->fields.list[c].value);
144 if (!n->fields.list[c].value)
145 perror (__FUNCTION__);
146 }
147
148 return n;
149 }
150
151 struct tagEntryHolder {
152 tagEntry *e;
153 };
154 struct tagEntryArray {
155 int count;
156 int length;
157 struct tagEntryHolder *a;
158 };
159
tagEntryArrayNew(void)160 struct tagEntryArray *tagEntryArrayNew (void)
161 {
162 struct tagEntryArray * a = malloc (sizeof (struct tagEntryArray));
163 if (!a)
164 perror(__FUNCTION__);
165
166 a->count = 0;
167 a->length = 1024;
168 a->a = malloc(a->length * sizeof (a->a[0]));
169 if (!a->a)
170 perror(__FUNCTION__);
171
172 return a;
173 }
174
tagEntryArrayPush(struct tagEntryArray * a,tagEntry * e)175 void tagEntryArrayPush (struct tagEntryArray *a, tagEntry *e)
176 {
177 if (a->count + 1 == a->length)
178 {
179 if (a->length * 2 < a->length)
180 perror("Too large array allocation");
181
182 struct tagEntryHolder *tmp = realloc (a->a, sizeof (a->a[0]) * (a->length * 2));
183 if (!tmp)
184 perror(__FUNCTION__);
185
186 a->a = tmp;
187 a->length *= 2;
188 }
189
190 a->a[a->count++].e = e;
191 }
192
tagEntryArrayFree(struct tagEntryArray * a,int freeTags)193 void tagEntryArrayFree (struct tagEntryArray *a, int freeTags)
194 {
195 if (freeTags)
196 {
197 for (int i = 0; i < a->count; i++)
198 freeCopiedTag (a->a[i].e);
199 }
200 free (a->a);
201 free (a);
202 }
203
compareTagEntry(const void * a,const void * b)204 static int compareTagEntry (const void *a, const void *b)
205 {
206 return s_compare (((struct tagEntryHolder *)a)->e, ((struct tagEntryHolder *)b)->e, Sorter);
207 }
208
walkTags(tagFile * const file,tagEntry * first_entry,tagResult (* nextfn)(tagFile * const,tagEntry *),void (* actionfn)(const tagEntry *))209 static void walkTags (tagFile *const file, tagEntry *first_entry,
210 tagResult (* nextfn) (tagFile *const, tagEntry *),
211 void (* actionfn) (const tagEntry *))
212 {
213 struct tagEntryArray *a = NULL;
214
215 if (Sorter)
216 a = tagEntryArrayNew ();
217
218 do
219 {
220 if (Qualifier)
221 {
222 int i = q_is_acceptable (Qualifier, first_entry);
223 switch (i)
224 {
225 case Q_REJECT:
226 continue;
227 case Q_ERROR:
228 exit (1);
229 }
230 }
231
232 if (a)
233 {
234 tagEntry *e = copyTag (first_entry);
235 tagEntryArrayPush (a, e);
236 }
237 else
238 (* actionfn) (first_entry);
239 } while ( (*nextfn) (file, first_entry) == TagSuccess);
240
241 int err = tagsGetErrno (file);
242 if (err != 0)
243 {
244 fprintf (stderr, "%s: error in walktTags(): %s\n",
245 ProgramName,
246 tagsStrerror (err));
247 exit (1);
248 }
249
250 if (a)
251 {
252 qsort (a->a, a->count, sizeof (a->a[0]), compareTagEntry);
253 for (int i = 0; i < a->count; i++)
254 (* actionfn) (a->a[i].e);
255 tagEntryArrayFree (a, 1);
256 }
257 }
258 #else
walkTags(tagFile * const file,tagEntry * first_entry,tagResult (* nextfn)(tagFile * const,tagEntry *),void (* actionfn)(const tagEntry *))259 static void walkTags (tagFile *const file, tagEntry *first_entry,
260 tagResult (* nextfn) (tagFile *const, tagEntry *),
261 void (* actionfn) (const tagEntry *))
262 {
263 do
264 (* actionfn) (first_entry);
265 while ( (*nextfn) (file, first_entry) == TagSuccess);
266
267 int err = tagsGetErrno (file);
268 if (err != 0)
269 {
270 fprintf (stderr, "%s: error in walktTags(): %s\n",
271 ProgramName,
272 tagsStrerror (err));
273 exit (1);
274 }
275 }
276 #endif
277
findTag(const char * const name,const int options)278 static void findTag (const char *const name, const int options)
279 {
280 tagFileInfo info;
281 tagEntry entry;
282 tagFile *const file = tagsOpen (TagFileName, &info);
283 if (file == NULL || !info.status.opened)
284 {
285 fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
286 ProgramName, tagsStrerror (info.status.error_number), TagFileName);
287 if (file)
288 tagsClose (file);
289 exit (1);
290 }
291 else
292 {
293 int err = 0;
294 if (SortOverride)
295 {
296 if (tagsSetSortType (file, SortMethod) != TagSuccess)
297 {
298 err = tagsGetErrno (file);
299 fprintf (stderr, "%s: cannot set sort type to %d: %s\n",
300 ProgramName,
301 SortMethod,
302 tagsStrerror (err));
303 exit (1);
304 }
305 }
306 if (debugMode)
307 fprintf (stderr, "%s: searching for \"%s\" in \"%s\"\n",
308 ProgramName, name, TagFileName);
309 if (tagsFind (file, &entry, name, options) == TagSuccess)
310 walkTags (file, &entry, tagsFindNext, printTag);
311 else if ((err = tagsGetErrno (file)) != 0)
312 {
313 fprintf (stderr, "%s: error in tagsFind(): %s\n",
314 ProgramName,
315 tagsStrerror (err));
316 exit (1);
317 }
318 tagsClose (file);
319 }
320 }
321
listTags(int pseudoTags)322 static void listTags (int pseudoTags)
323 {
324 tagFileInfo info;
325 tagEntry entry;
326 tagFile *const file = tagsOpen (TagFileName, &info);
327 if (file == NULL || !info.status.opened)
328 {
329 fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
330 ProgramName,
331 tagsStrerror (info.status.error_number),
332 TagFileName);
333 if (file)
334 tagsClose (file);
335 exit (1);
336 }
337 else if (pseudoTags)
338 {
339 int err = 0;
340 if (tagsFirstPseudoTag (file, &entry) == TagSuccess)
341 walkTags (file, &entry, tagsNextPseudoTag, printPseudoTag);
342 else if ((err = tagsGetErrno (file)) != 0)
343 {
344 fprintf (stderr, "%s: error in tagsFirstPseudoTag(): %s\n",
345 ProgramName,
346 tagsStrerror (err));
347 exit (1);
348 }
349 tagsClose (file);
350 }
351 else
352 {
353 int err = 0;
354 if (tagsFirst (file, &entry) == TagSuccess)
355 walkTags (file, &entry, tagsNext, printTag);
356 else if ((err = tagsGetErrno (file)) != 0)
357 {
358 fprintf (stderr, "%s: error in tagsFirst(): %s\n",
359 ProgramName,
360 tagsStrerror (err));
361 exit (1);
362 }
363 tagsClose (file);
364 }
365 }
366
367 static const char *const Usage =
368 "Find tag file entries matching specified names.\n\n"
369 "Usage: \n"
370 " %s -h | --help\n"
371 " Print this help message.\n"
372 #ifdef READTAGS_DSL
373 " %s -H POSTPROCESSOR | --help-expression POSTPROCESSOR\n"
374 " Print available terms that can be used in POSTPROCESSOR expression.\n"
375 " POSTPROCESSOR: filter sorter\n"
376 #endif
377 " %s [OPTIONS] ACTION\n"
378 " Do the specified action.\n"
379 "Actions:\n"
380 " -l | --list\n"
381 " List regular tags.\n"
382 " [-] NAME...\n"
383 " List regular tags matching NAME(s).\n"
384 " \"-\" indicates arguments after this as NAME(s) even if they start with -.\n"
385 " -D | --list-pseudo-tags\n"
386 " List pseudo tags.\n"
387 "Options:\n"
388 " -d | --debug\n"
389 " Turn on debugging output.\n"
390 " -E | --escape-output\n"
391 " Escape characters like tabs in output as described in tags(5).\n"
392 " -e | --extension-fields\n"
393 " Include extension fields in output.\n"
394 " -i | --icase-match\n"
395 " Perform case-insensitive matching in the NAME action.\n"
396 " -n | --line-number\n"
397 " Also include the line number field when -e option is given.\n"
398 " -p | --prefix-match\n"
399 " Perform prefix matching in the NAME action.\n"
400 " -t TAGFILE | --tag-file TAGFILE\n"
401 " Use specified tag file (default: \"tags\").\n"
402 " -s[0|1|2] | --override-sort-detection METHOD\n"
403 " Override sort detection of tag file.\n"
404 " METHOD: unsorted|sorted|foldcase\n"
405 #ifdef READTAGS_DSL
406 " -Q EXP | --filter EXP\n"
407 " Filter the tags listed by ACTION with EXP before printing.\n"
408 " -S EXP | --sorter EXP\n"
409 " Sort the tags listed by ACTION with EXP before printing.\n"
410 #endif
411 ;
412
printUsage(FILE * stream,int exitCode)413 static void printUsage(FILE* stream, int exitCode)
414 {
415 fprintf (stream, Usage, ProgramName,
416 #ifdef READTAGS_DSL
417 ProgramName,
418 #endif
419 ProgramName);
420 exit (exitCode);
421 }
422
423 #ifdef READTAGS_DSL
printFilterExpression(FILE * stream,int exitCode)424 static void printFilterExpression (FILE *stream, int exitCode)
425 {
426 fprintf (stream, "Filter expression: \n");
427 q_help (stream);
428 exit (exitCode);
429 }
430
printSorterExpression(FILE * stream,int exitCode)431 static void printSorterExpression (FILE *stream, int exitCode)
432 {
433 fprintf (stream, "Sorter expression: \n");
434 s_help (stream);
435 exit (exitCode);
436 }
437
compileExpression(const char * exp,void * (* compiler)(EsObject *),const char * compiler_name)438 static void *compileExpression(const char* exp, void * (*compiler) (EsObject *),
439 const char *compiler_name)
440 {
441 EsObject *sexp = es_read_from_string (exp, NULL);
442 void *code;
443
444 if (es_error_p (sexp))
445 {
446 fprintf (stderr,
447 "Failed to read the expression for %s: %s\n", compiler_name, exp);
448 fprintf (stderr,
449 "Reason: %s\n", es_error_name (sexp));
450 exit (1);
451 }
452
453 code = compiler (sexp);
454 if (code == NULL)
455 {
456 fprintf (stderr,
457 "Failed to compile the expression of %s: %s\n", compiler_name, exp);
458 exit (1);
459 }
460 es_object_unref (sexp);
461 return code;
462 }
463 #endif
464
main(int argc,char ** argv)465 extern int main (int argc, char **argv)
466 {
467 int options = 0;
468 int actionSupplied = 0;
469 int i;
470 int ignore_prefix = 0;
471
472 ProgramName = argv [0];
473 if (argc == 1)
474 printUsage(stderr, 1);
475 for (i = 1 ; i < argc ; ++i)
476 {
477 const char *const arg = argv [i];
478 if (ignore_prefix || arg [0] != '-')
479 {
480 findTag (arg, options);
481 actionSupplied = 1;
482 }
483 else if (arg [0] == '-' && arg [1] == '\0')
484 ignore_prefix = 1;
485 else if (arg [0] == '-' && arg [1] == '-')
486 {
487 const char *optname = arg + 2;
488 if (strcmp (optname, "debug") == 0)
489 debugMode++;
490 else if (strcmp (optname, "list-pseudo-tags") == 0)
491 {
492 listTags (1);
493 actionSupplied = 1;
494 }
495 else if (strcmp (optname, "help") == 0)
496 printUsage (stdout, 0);
497 #ifdef READTAGS_DSL
498 else if (strcmp (optname, "help-expression") == 0)
499 {
500 if (i + 1 < argc)
501 {
502 const char *exp_klass = argv [++i];
503 if (strcmp (exp_klass, "filter") == 0)
504 printFilterExpression (stdout, 0);
505 if (strcmp (exp_klass, "sorter") == 0)
506 printSorterExpression (stdout, 0);
507 else
508 {
509 fprintf (stderr, "%s: unknown expression class for --%s option\n",
510 ProgramName, optname);
511 exit (1);
512
513 }
514 }
515 else
516 {
517 fprintf (stderr, "%s: missing expression class for --%s option\n",
518 ProgramName, optname);
519 exit (1);
520 }
521 }
522 #endif
523 else if (strcmp (optname, "escape-output") == 0)
524 escaping = 1;
525 else if (strcmp (optname, "extension-fields") == 0)
526 extensionFields = 1;
527 else if (strcmp (optname, "icase-match") == 0)
528 options |= TAG_IGNORECASE;
529 else if (strcmp (optname, "prefix-match") == 0)
530 options |= TAG_PARTIALMATCH;
531 else if (strcmp (optname, "list") == 0)
532 {
533 listTags (0);
534 actionSupplied = 1;
535 }
536 else if (strcmp (optname, "line-number") == 0)
537 allowPrintLineNumber = 1;
538 else if (strcmp (optname, "tag-file") == 0)
539 {
540 if (i + 1 < argc)
541 TagFileName = argv [++i];
542 else
543 printUsage (stderr, 1);
544 }
545 else if (strcmp (optname, "override-sort-detection") == 0)
546 {
547 if (i + 1 < argc)
548 {
549 const char *sort_spec = argv [++i];
550 if (strcmp (sort_spec, "0") == 0
551 || strcmp (sort_spec, "unsorted") == 0)
552 SortMethod = 0;
553 else if (strcmp (sort_spec, "1") == 0
554 || strcmp (sort_spec, "sorted") == 0)
555 SortMethod = 1;
556 else if (strcmp (sort_spec, "2") == 0
557 || strcmp (sort_spec, "foldcase") == 0)
558 SortMethod = 2;
559 else
560 {
561 fprintf (stderr, "%s: unknown sort method for --%s option\n",
562 ProgramName, optname);
563 exit (1);
564 }
565 }
566 else
567 {
568 fprintf (stderr, "%s: missing sort method for --%s option\n",
569 ProgramName, optname);
570 exit (1);
571 }
572 }
573 #ifdef READTAGS_DSL
574 else if (strcmp (optname, "filter") == 0)
575 {
576 if (i + 1 < argc)
577 Qualifier = compileExpression (argv[++i],
578 (void * (*)(EsObject *))q_compile,
579 optname);
580 else
581 {
582 fprintf (stderr, "%s: missing filter expression for --%s option\n",
583 ProgramName, optname);
584 exit (1);
585 }
586 }
587 else if (strcmp (optname, "sorter") == 0)
588 {
589 if (i + 1 < argc)
590 Sorter = compileExpression (argv[++i],
591 (void * (*)(EsObject *))s_compile,
592 optname);
593 else
594 {
595 fprintf (stderr, "%s: missing sorter expression for --%s option\n",
596 ProgramName, optname);
597 exit (1);
598 }
599 }
600 #endif
601 else
602 {
603 fprintf (stderr, "%s: unknown long options: --%s\n",
604 ProgramName, optname);
605 exit (1);
606 break;
607 }
608 }
609 else
610 {
611 size_t j;
612 for (j = 1 ; arg [j] != '\0' ; ++j)
613 {
614 switch (arg [j])
615 {
616 case 'd': debugMode++; break;
617 case 'D': listTags (1); actionSupplied = 1; break;
618 case 'h': printUsage (stdout, 0); break;
619 #ifdef READTAGS_DSL
620 case 'H':
621 if (i + 1 < argc)
622 {
623 const char *exp_klass = argv [++i];
624 if (strcmp (exp_klass, "filter") == 0)
625 printFilterExpression (stdout, 0);
626 else if (strcmp (exp_klass, "sorter") == 0)
627 printSorterExpression (stdout, 0);
628 else
629 printUsage(stderr, 1);
630 }
631 else
632 printUsage(stderr, 1);
633 #endif
634 case 'E': escaping = 1; break;
635 case 'e': extensionFields = 1; break;
636 case 'i': options |= TAG_IGNORECASE; break;
637 case 'p': options |= TAG_PARTIALMATCH; break;
638 case 'l': listTags (0); actionSupplied = 1; break;
639 case 'n': allowPrintLineNumber = 1; break;
640 case 't':
641 if (arg [j+1] != '\0')
642 {
643 TagFileName = arg + j + 1;
644 j += strlen (TagFileName);
645 }
646 else if (i + 1 < argc)
647 TagFileName = argv [++i];
648 else
649 printUsage(stderr, 1);
650 break;
651 case 's':
652 SortOverride = 1;
653 ++j;
654 if (arg [j] == '\0')
655 SortMethod = TAG_SORTED;
656 else if (strchr ("012", arg[j]) != NULL)
657 SortMethod = (sortType) (arg[j] - '0');
658 else
659 printUsage(stderr, 1);
660 break;
661 #ifdef READTAGS_DSL
662 case 'Q':
663 if (i + 1 == argc)
664 printUsage(stderr, 1);
665 Qualifier = compileExpression (argv[++i],
666 (void * (*)(EsObject *))q_compile,
667 "filter");
668 break;
669 case 'S':
670 if (i + 1 == argc)
671 printUsage(stderr, 1);
672 Sorter = compileExpression (argv[++i],
673 (void * (*)(EsObject *))s_compile,
674 "sorter");
675 break;
676 #endif
677 default:
678 fprintf (stderr, "%s: unknown option: %c\n",
679 ProgramName, arg[j]);
680 exit (1);
681 break;
682 }
683 }
684 }
685 }
686 if (! actionSupplied)
687 {
688 fprintf (stderr,
689 "%s: no action specified: specify one of NAME, -l or -D\n",
690 ProgramName);
691 exit (1);
692 }
693 #ifdef READTAGS_DSL
694 if (Qualifier)
695 q_destroy (Qualifier);
696 if (Sorter)
697 s_destroy (Sorter);
698 #endif
699 return 0;
700 }
701