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