1/*
2*
3* Copyright (c) 1996-2003, Darren Hiebert <dhiebert at users dot sourceforge dot net>
4*
5* This source code is released into the public domain.
6*
7* This module contains functions for reading tag files.
8*
9*/
10
11/*
12* INCLUDE FILES
13*/
14#include <stdlib.h>
15#include <string.h>
16#include <ctype.h>
17#include <stdio.h>
18#include <errno.h>
19#include <sys/types.h> /* to declare off_t */
20
21#include "readtags.h"
22
23/*
24* MACROS
25*/
26#define TAB '\t'
27
28
29/*
30* DATA DECLARATIONS
31*/
32typedef struct {
33 size_t size;
34 char *buffer;
35} vstring;
36
37/* Information about current tag file */
38struct sTagFile {
39 /* has the file been opened and this structure initialized? */
40 short initialized;
41 /* format of tag file */
42 short format;
43 /* how is the tag file sorted? */
44 sortType sortMethod;
45 /* pointer to file structure */
46 FILE* fp;
47 /* file position of first character of `line' */
48 off_t pos;
49 /* size of tag file in seekable positions */
50 off_t size;
51 /* last line read */
52 vstring line;
53 /* name of tag in last line read */
54 vstring name;
55 /* defines tag search state */
56 struct {
57 /* file position of last match for tag */
58 off_t pos;
59 /* name of tag last searched for */
60 const char *name;
61 /* length of name for partial matches */
62 size_t nameLength;
63 /* peforming partial match */
64 short partial;
65 /* ignoring case */
66 short ignorecase;
67 } search;
68 /* miscellaneous extension fields */
69 struct {
70 /* number of entries in `list' */
71 unsigned short max;
72 /* list of key value pairs */
73 tagExtensionField *list;
74 } fields;
75 /* buffers to be freed at close */
76 struct {
77 /* name of program author */
78 char *author;
79 /* name of program */
80 char *name;
81 /* URL of distribution */
82 char *url;
83 /* program version */
84 char *version;
85 } program;
86};
87
88/*
89* DATA DEFINITIONS
90*/
91const char *const EmptyString = "";
92const char *const PseudoTagPrefix = "!_";
93
94/*
95* FUNCTION DEFINITIONS
96*/
97
98/*
99 * Compare two strings, ignoring case.
100 * Return 0 for match, < 0 for smaller, > 0 for bigger
101 * Make sure case is folded to uppercase in comparison (like for 'sort -f')
102 * This makes a difference when one of the chars lies between upper and lower
103 * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !)
104 */
105static int struppercmp (const char *s1, const char *s2)
106{
107 int result;
108 do
109 {
110 result = toupper ((int) *s1) - toupper ((int) *s2);
111 } while (result == 0 && *s1++ != '\0' && *s2++ != '\0');
112 return result;
113}
114
115static int strnuppercmp (const char *s1, const char *s2, size_t n)
116{
117 int result;
118 do
119 {
120 result = toupper ((int) *s1) - toupper ((int) *s2);
121 } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0');
122 return result;
123}
124
125static int growString (vstring *s)
126{
127 int result = 0;
128 size_t newLength;
129 char *newLine;
130 if (s->size == 0)
131 {
132 newLength = 128;
133 newLine = (char*) malloc (newLength);
134 *newLine = '\0';
135 }
136 else
137 {
138 newLength = 2 * s->size;
139 newLine = (char*) realloc (s->buffer, newLength);
140 }
141 if (newLine == NULL)
142 perror ("string too large");
143 else
144 {
145 s->buffer = newLine;
146 s->size = newLength;
147 result = 1;
148 }
149 return result;
150}
151
152/* Copy name of tag out of tag line */
153static void copyName (tagFile *const file)
154{
155 size_t length;
156 const char *end = strchr (file->line.buffer, '\t');
157 if (end == NULL)
158 {
159 end = strchr (file->line.buffer, '\n');
160 if (end == NULL)
161 end = strchr (file->line.buffer, '\r');
162 }
163 if (end != NULL)
164 length = end - file->line.buffer;
165 else
166 length = strlen (file->line.buffer);
167 while (length >= file->name.size)
168 growString (&file->name);
169 strncpy (file->name.buffer, file->line.buffer, length);
170 file->name.buffer [length] = '\0';
171}
172
173static int readTagLineRaw (tagFile *const file)
174{
175 int result = 1;
176 int reReadLine;
177
178 /* If reading the line places any character other than a null or a
179 * newline at the last character position in the buffer (one less than
180 * the buffer size), then we must resize the buffer and reattempt to read
181 * the line.
182 */
183 do
184 {
185 char *const pLastChar = file->line.buffer + file->line.size - 2;
186 char *line;
187
188 file->pos = ftell (file->fp);
189 reReadLine = 0;
190 *pLastChar = '\0';
191 line = fgets (file->line.buffer, (int) file->line.size, file->fp);
192 if (line == NULL)
193 {
194 /* read error */
195 if (! feof (file->fp))
196 perror ("readTagLine");
197 result = 0;
198 }
199 else if (*pLastChar != '\0' &&
200 *pLastChar != '\n' && *pLastChar != '\r')
201 {
202 /* buffer overflow */
203 growString (&file->line);
204 fseek (file->fp, file->pos, SEEK_SET);
205 reReadLine = 1;
206 }
207 else
208 {
209 size_t i = strlen (file->line.buffer);
210 while (i > 0 &&
211 (file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r'))
212 {
213 file->line.buffer [i - 1] = '\0';
214 --i;
215 }
216 }
217 } while (reReadLine && result);
218 if (result)
219 copyName (file);
220 return result;
221}
222
223static int readTagLine (tagFile *const file)
224{
225 int result;
226 do
227 {
228 result = readTagLineRaw (file);
229 } while (result && *file->name.buffer == '\0');
230 return result;
231}
232
233static tagResult growFields (tagFile *const file)
234{
235 tagResult result = TagFailure;
236 unsigned short newCount = 2 * file->fields.max;
237 tagExtensionField *newFields = (tagExtensionField*)
238 realloc (file->fields.list, newCount * sizeof (tagExtensionField));
239 if (newFields == NULL)
240 perror ("too many extension fields");
241 else
242 {
243 file->fields.list = newFields;
244 file->fields.max = newCount;
245 result = TagSuccess;
246 }
247 return result;
248}
249
250static void parseExtensionFields (tagFile *const file, tagEntry *const entry,
251 char *const string)
252{
253 char *p = string;
254 while (p != NULL && *p != '\0')
255 {
256 while (*p == TAB)
257 *p++ = '\0';
258 if (*p != '\0')
259 {
260 char *colon;
261 char *field = p;
262 p = strchr (p, TAB);
263 if (p != NULL)
264 *p++ = '\0';
265 colon = strchr (field, ':');
266 if (colon == NULL)
267 entry->kind = field;
268 else
269 {
270 const char *key = field;
271 const char *value = colon + 1;
272 *colon = '\0';
273 if (strcmp (key, "kind") == 0)
274 entry->kind = value;
275 else if (strcmp (key, "file") == 0)
276 entry->fileScope = 1;
277 else if (strcmp (key, "line") == 0)
278 entry->address.lineNumber = atol (value);
279 else
280 {
281 if (entry->fields.count == file->fields.max)
282 growFields (file);
283 file->fields.list [entry->fields.count].key = key;
284 file->fields.list [entry->fields.count].value = value;
285 ++entry->fields.count;
286 }
287 }
288 }
289 }
290}
291
292static void parseTagLine (tagFile *file, tagEntry *const entry)
293{
294 int i;
295 char *p = file->line.buffer;
296 char *tab = strchr (p, TAB);
297 int fieldsPresent = 0;
298
299 entry->fields.list = NULL;
300 entry->fields.count = 0;
301 entry->kind = NULL;
302 entry->fileScope = 0;
303
304 entry->name = p;
305 if (tab != NULL)
306 {
307 *tab = '\0';
308 p = tab + 1;
309 entry->file = p;
310 tab = strchr (p, TAB);
311 if (tab != NULL)
312 {
313 *tab = '\0';
314 p = tab + 1;
315 if (*p == '/' || *p == '?')
316 {
317 /* parse pattern */
318 int delimiter = *(unsigned char*) p;
319 entry->address.lineNumber = 0;
320 entry->address.pattern = p;
321 do
322 {
323 p = strchr (p + 1, delimiter);
324 } while (p != NULL && *(p - 1) == '\\');
325 if (p == NULL)
326 {
327 /* invalid pattern */
328 }
329 else
330 ++p;
331 }
332 else if (isdigit ((int) *(unsigned char*) p))
333 {
334 /* parse line number */
335 entry->address.pattern = p;
336 entry->address.lineNumber = atol (p);
337 while (isdigit ((int) *(unsigned char*) p))
338 ++p;
339 }
340 else
341 {
342 /* invalid pattern */
343 }
344 if ( p != NULL )
345 {
346 fieldsPresent = (strncmp (p, ";\"", 2) == 0);
347 *p = '\0';
348 if (fieldsPresent)
349 parseExtensionFields (file, entry, p + 2);
350 }
351 }
352 }
353 if (entry->fields.count > 0)
354 entry->fields.list = file->fields.list;
355 for (i = entry->fields.count ; i < file->fields.max ; ++i)
356 {
357 file->fields.list [i].key = NULL;
358 file->fields.list [i].value = NULL;
359 }
360}
361
362static char *duplicate (const char *str)
363{
364 char *result = NULL;
365 if (str != NULL)
366 {
367 result = (char*) malloc (strlen (str) + 1);
368 if (result == NULL)
369 perror (NULL);
370 else
371 strcpy (result, str);
372 }
373 return result;
374}
375
376static void readPseudoTags (tagFile *const file, tagFileInfo *const info)
377{
378 fpos_t startOfLine;
379 const size_t prefixLength = strlen (PseudoTagPrefix);
380 if (info != NULL)
381 {
382 info->file.format = 1;
383 info->file.sort = TAG_UNSORTED;
384 info->program.author = NULL;
385 info->program.name = NULL;
386 info->program.url = NULL;
387 info->program.version = NULL;
388 }
389 while (1)
390 {
391 fgetpos (file->fp, &startOfLine);
392 if (! readTagLine (file))
393 break;
394 if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
395 break;
396 else
397 {
398 tagEntry entry;
399 const char *key, *value;
400 parseTagLine (file, &entry);
401 key = entry.name + prefixLength;
402 value = entry.file;
403 if (strcmp (key, "TAG_FILE_SORTED") == 0)
404 file->sortMethod = (sortType) atoi (value);
405 else if (strcmp (key, "TAG_FILE_FORMAT") == 0)
406 file->format = atoi (value);
407 else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0)
408 file->program.author = duplicate (value);
409 else if (strcmp (key, "TAG_PROGRAM_NAME") == 0)
410 file->program.name = duplicate (value);
411 else if (strcmp (key, "TAG_PROGRAM_URL") == 0)
412 file->program.url = duplicate (value);
413 else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0)
414 file->program.version = duplicate (value);
415 if (info != NULL)
416 {
417 info->file.format = file->format;
418 info->file.sort = file->sortMethod;
419 info->program.author = file->program.author;
420 info->program.name = file->program.name;
421 info->program.url = file->program.url;
422 info->program.version = file->program.version;
423 }
424 }
425 }
426 fsetpos (file->fp, &startOfLine);
427}
428
429static void gotoFirstLogicalTag (tagFile *const file)
430{
431 fpos_t startOfLine;
432 const size_t prefixLength = strlen (PseudoTagPrefix);
433 rewind (file->fp);
434 while (1)
435 {
436 fgetpos (file->fp, &startOfLine);
437 if (! readTagLine (file))
438 break;
439 if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
440 break;
441 }
442 fsetpos (file->fp, &startOfLine);
443}
444
445static tagFile *initialize (const char *const filePath, tagFileInfo *const info)
446{
447 tagFile *result = (tagFile*) malloc (sizeof (tagFile));
448 if (result != NULL)
449 {
450 memset (result, 0, sizeof (tagFile));
451 growString (&result->line);
452 growString (&result->name);
453 result->fields.max = 20;
454 result->fields.list = (tagExtensionField*) malloc (
455 result->fields.max * sizeof (tagExtensionField));
456 result->fp = fopen (filePath, "r");
457 if (result->fp == NULL)
458 {
459 free (result);
460 result = NULL;
461 info->status.error_number = errno;
462 }
463 else
464 {
465 fseek (result->fp, 0, SEEK_END);
466 result->size = ftell (result->fp);
467 rewind (result->fp);
468 readPseudoTags (result, info);
469 info->status.opened = 1;
470 result->initialized = 1;
471 }
472 }
473 return result;
474}
475
476static void terminate (tagFile *const file)
477{
478 fclose (file->fp);
479
480 free (file->line.buffer);
481 free (file->name.buffer);
482 free (file->fields.list);
483
484 if (file->program.author != NULL)
485 free (file->program.author);
486 if (file->program.name != NULL)
487 free (file->program.name);
488 if (file->program.url != NULL)
489 free (file->program.url);
490 if (file->program.version != NULL)
491 free (file->program.version);
492
493 memset (file, 0, sizeof (tagFile));
494
495 free (file);
496}
497
498static tagResult readNext (tagFile *const file, tagEntry *const entry)
499{
500 tagResult result = TagFailure;
501 if (file == NULL || ! file->initialized)
502 result = TagFailure;
503 else if (! readTagLine (file))
504 result = TagFailure;
505 else
506 {
507 if (entry != NULL)
508 parseTagLine (file, entry);
509 result = TagSuccess;
510 }
511 return result;
512}
513
514static const char *readFieldValue (
515 const tagEntry *const entry, const char *const key)
516{
517 const char *result = NULL;
518 int i;
519 if (strcmp (key, "kind") == 0)
520 result = entry->kind;
521 else if (strcmp (key, "file") == 0)
522 result = EmptyString;
523 else for (i = 0 ; i < entry->fields.count && result == NULL ; ++i)
524 if (strcmp (entry->fields.list [i].key, key) == 0)
525 result = entry->fields.list [i].value;
526 return result;
527}
528
529static int readTagLineSeek (tagFile *const file, const off_t pos)
530{
531 int result = 0;
532 if (fseek (file->fp, pos, SEEK_SET) == 0)
533 {
534 result = readTagLine (file); /* read probable partial line */
535 if (pos > 0 && result)
536 result = readTagLine (file); /* read complete line */
537 }
538 return result;
539}
540
541static int nameComparison (tagFile *const file)
542{
543 int result;
544 if (file->search.ignorecase)
545 {
546 if (file->search.partial)
547 result = strnuppercmp (file->search.name, file->name.buffer,
548 file->search.nameLength);
549 else
550 result = struppercmp (file->search.name, file->name.buffer);
551 }
552 else
553 {
554 if (file->search.partial)
555 result = strncmp (file->search.name, file->name.buffer,
556 file->search.nameLength);
557 else
558 result = strcmp (file->search.name, file->name.buffer);
559 }
560 return result;
561}
562
563static void findFirstNonMatchBefore (tagFile *const file)
564{
565#define JUMP_BACK 512
566 int more_lines;
567 int comp;
568 off_t start = file->pos;
569 off_t pos = start;
570 do
571 {
572 if (pos < (off_t) JUMP_BACK)
573 pos = 0;
574 else
575 pos = pos - JUMP_BACK;
576 more_lines = readTagLineSeek (file, pos);
577 comp = nameComparison (file);
578 } while (more_lines && comp == 0 && pos > 0 && pos < start);
579}
580
581static tagResult findFirstMatchBefore (tagFile *const file)
582{
583 tagResult result = TagFailure;
584 int more_lines;
585 off_t start = file->pos;
586 findFirstNonMatchBefore (file);
587 do
588 {
589 more_lines = readTagLine (file);
590 if (nameComparison (file) == 0)
591 result = TagSuccess;
592 } while (more_lines && result != TagSuccess && file->pos < start);
593 return result;
594}
595
596static tagResult findBinary (tagFile *const file)
597{
598 tagResult result = TagFailure;
599 off_t lower_limit = 0;
600 off_t upper_limit = file->size;
601 off_t last_pos = 0;
602 off_t pos = upper_limit / 2;
603 while (result != TagSuccess)
604 {
605 if (! readTagLineSeek (file, pos))
606 {
607 /* in case we fell off end of file */
608 result = findFirstMatchBefore (file);
609 break;
610 }
611 else if (pos == last_pos)
612 {
613 /* prevent infinite loop if we backed up to beginning of file */
614 break;
615 }
616 else
617 {
618 const int comp = nameComparison (file);
619 last_pos = pos;
620 if (comp < 0)
621 {
622 upper_limit = pos;
623 pos = lower_limit + ((upper_limit - lower_limit) / 2);
624 }
625 else if (comp > 0)
626 {
627 lower_limit = pos;
628 pos = lower_limit + ((upper_limit - lower_limit) / 2);
629 }
630 else if (pos == 0)
631 result = TagSuccess;
632 else
633 result = findFirstMatchBefore (file);
634 }
635 }
636 return result;
637}
638
639static tagResult findSequential (tagFile *const file)
640{
641 tagResult result = TagFailure;
642 if (file->initialized)
643 {
644 while (result == TagFailure && readTagLine (file))
645 {
646 if (nameComparison (file) == 0)
647 result = TagSuccess;
648 }
649 }
650 return result;
651}
652
653static tagResult find (tagFile *const file, tagEntry *const entry,
654 const char *const name, const int options)
655{
656 tagResult result = TagFailure;
657 file->search.name = name;
658 file->search.nameLength = strlen (name);
659 file->search.partial = (options & TAG_PARTIALMATCH) != 0;
660 file->search.ignorecase = (options & TAG_IGNORECASE) != 0;
661 fseek (file->fp, 0, SEEK_END);
662 file->size = ftell (file->fp);
663 rewind (file->fp);
664 if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
665 (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
666 {
667#ifdef DEBUG
668 printf ("<performing binary search>\n");
669#endif
670 result = findBinary (file);
671 }
672 else
673 {
674#ifdef DEBUG
675 printf ("<performing sequential search>\n");
676#endif
677 result = findSequential (file);
678 }
679
680 if (result != TagSuccess)
681 file->search.pos = file->size;
682 else
683 {
684 file->search.pos = file->pos;
685 if (entry != NULL)
686 parseTagLine (file, entry);
687 }
688 return result;
689}
690
691static tagResult findNext (tagFile *const file, tagEntry *const entry)
692{
693 tagResult result = TagFailure;
694 if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
695 (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
696 {
697 result = tagsNext (file, entry);
698 if (result == TagSuccess && nameComparison (file) != 0)
699 result = TagFailure;
700 }
701 else
702 {
703 result = findSequential (file);
704 if (result == TagSuccess && entry != NULL)
705 parseTagLine (file, entry);
706 }
707 return result;
708}
709
710/*
711* EXTERNAL INTERFACE
712*/
713
714extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info)
715{
716 return initialize (filePath, info);
717}
718
719extern tagResult tagsSetSortType (tagFile *const file, const sortType type)
720{
721 tagResult result = TagFailure;
722 if (file != NULL && file->initialized)
723 {
724 file->sortMethod = type;
725 result = TagSuccess;
726 }
727 return result;
728}
729
730extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry)
731{
732 tagResult result = TagFailure;
733 if (file != NULL && file->initialized)
734 {
735 gotoFirstLogicalTag (file);
736 result = readNext (file, entry);
737 }
738 return result;
739}
740
741extern tagResult tagsNext (tagFile *const file, tagEntry *const entry)
742{
743 tagResult result = TagFailure;
744 if (file != NULL && file->initialized)
745 result = readNext (file, entry);
746 return result;
747}
748
749extern const char *tagsField (const tagEntry *const entry, const char *const key)
750{
751 const char *result = NULL;
752 if (entry != NULL)
753 result = readFieldValue (entry, key);
754 return result;
755}
756
757extern tagResult tagsFind (tagFile *const file, tagEntry *const entry,
758 const char *const name, const int options)
759{
760 tagResult result = TagFailure;
761 if (file != NULL && file->initialized)
762 result = find (file, entry, name, options);
763 return result;
764}
765
766extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry)
767{
768 tagResult result = TagFailure;
769 if (file != NULL && file->initialized)
770 result = findNext (file, entry);
771 return result;
772}
773
774extern tagResult tagsClose (tagFile *const file)
775{
776 tagResult result = TagFailure;
777 if (file != NULL && file->initialized)
778 {
779 terminate (file);
780 result = TagSuccess;
781 }
782 return result;
783}
784
785/*
786* TEST FRAMEWORK
787*/
788
789#ifdef READTAGS_MAIN
790
791static const char *TagFileName = "tags";
792static const char *ProgramName;
793static int extensionFields;
794static int SortOverride;
795static sortType SortMethod;
796
797static void printTag (const tagEntry *entry)
798{
799 int i;
800 int first = 1;
801 const char* separator = ";\"";
802 const char* const empty = "";
803/* "sep" returns a value only the first time it is evaluated */
804#define sep (first ? (first = 0, separator) : empty)
805 printf ("%s\t%s\t%s",
806 entry->name, entry->file, entry->address.pattern);
807 if (extensionFields)
808 {
809 if (entry->kind != NULL && entry->kind [0] != '\0')
810 printf ("%s\tkind:%s", sep, entry->kind);
811 if (entry->fileScope)
812 printf ("%s\tfile:", sep);
813#if 0
814 if (entry->address.lineNumber > 0)
815 printf ("%s\tline:%lu", sep, entry->address.lineNumber);
816#endif
817 for (i = 0 ; i < entry->fields.count ; ++i)
818 printf ("%s\t%s:%s", sep, entry->fields.list [i].key,
819 entry->fields.list [i].value);
820 }
821 putchar ('\n');
822#undef sep
823}
824
825static void findTag (const char *const name, const int options)
826{
827 tagFileInfo info;
828 tagEntry entry;
829 tagFile *const file = tagsOpen (TagFileName, &info);
830 if (file == NULL)
831 {
832 fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
833 ProgramName, strerror (info.status.error_number), name);
834 exit (1);
835 }
836 else
837 {
838 if (SortOverride)
839 tagsSetSortType (file, SortMethod);
840 if (tagsFind (file, &entry, name, options) == TagSuccess)
841 {
842 do
843 {
844 printTag (&entry);
845 } while (tagsFindNext (file, &entry) == TagSuccess);
846 }
847 tagsClose (file);
848 }
849}
850
851static void listTags (void)
852{
853 tagFileInfo info;
854 tagEntry entry;
855 tagFile *const file = tagsOpen (TagFileName, &info);
856 if (file == NULL)
857 {
858 fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
859 ProgramName, strerror (info.status.error_number), TagFileName);
860 exit (1);
861 }
862 else
863 {
864 while (tagsNext (file, &entry) == TagSuccess)
865 printTag (&entry);
866 tagsClose (file);
867 }
868}
869
870const char *const Usage =
871 "Find tag file entries matching specified names.\n\n"
872 "Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n"
873 "Options:\n"
874 " -e Include extension fields in output.\n"
875 " -i Perform case-insensitive matching.\n"
876 " -l List all tags.\n"
877 " -p Perform partial matching.\n"
878 " -s[0|1|2] Override sort detection of tag file.\n"
879 " -t file Use specified tag file (default: \"tags\").\n"
880 "Note that options are acted upon as encountered, so order is significant.\n";
881
882extern int main (int argc, char **argv)
883{
884 int options = 0;
885 int actionSupplied = 0;
886 int i;
887 ProgramName = argv [0];
888 if (argc == 1)
889 {
890 fprintf (stderr, Usage, ProgramName);
891 exit (1);
892 }
893 for (i = 1 ; i < argc ; ++i)
894 {
895 const char *const arg = argv [i];
896 if (arg [0] != '-')
897 {
898 findTag (arg, options);
899 actionSupplied = 1;
900 }
901 else
902 {
903 size_t j;
904 for (j = 1 ; arg [j] != '\0' ; ++j)
905 {
906 switch (arg [j])
907 {
908 case 'e': extensionFields = 1; break;
909 case 'i': options |= TAG_IGNORECASE; break;
910 case 'p': options |= TAG_PARTIALMATCH; break;
911 case 'l': listTags (); actionSupplied = 1; break;
912
913 case 't':
914 if (arg [j+1] != '\0')
915 {
916 TagFileName = arg + j + 1;
917 j += strlen (TagFileName);
918 }
919 else if (i + 1 < argc)
920 TagFileName = argv [++i];
921 else
922 {
923 fprintf (stderr, Usage, ProgramName);
924 exit (1);
925 }
926 break;
927 case 's':
928 SortOverride = 1;
929 ++j;
930 if (arg [j] == '\0')
931 SortMethod = TAG_SORTED;
932 else if (strchr ("012", arg[j]) != NULL)
933 SortMethod = (sortType) (arg[j] - '0');
934 else
935 {
936 fprintf (stderr, Usage, ProgramName);
937 exit (1);
938 }
939 break;
940 default:
941 fprintf (stderr, "%s: unknown option: %c\n",
942 ProgramName, arg[j]);
943 exit (1);
944 break;
945 }
946 }
947 }
948 }
949 if (! actionSupplied)
950 {
951 fprintf (stderr,
952 "%s: no action specified: specify tag name(s) or -l option\n",
953 ProgramName);
954 exit (1);
955 }
956 return 0;
957}
958
959#endif
960
961/* vi:set tabstop=8 shiftwidth=4: */
962