xref: /Universal-ctags/extra-cmds/readtags-cmd.c (revision d453aacfb88c1c59e3850594e8437c4ed9b52ebf)
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