File indexing completed on 2024-05-05 05:51:38
0001 /* 0002 * 0003 * SPDX-FileCopyrightText: 1996-2003 Darren Hiebert <dhiebert at users dot sourceforge dot net> 0004 * 0005 * This source code is released into the public domain. 0006 * 0007 * This module contains functions for reading tag files. 0008 * 0009 */ 0010 0011 /* 0012 * INCLUDE FILES 0013 */ 0014 #include "readtags.h" 0015 0016 #include <stdlib.h> 0017 #include <string.h> 0018 #include <ctype.h> 0019 #include <stdio.h> 0020 #include <errno.h> 0021 #include <sys/types.h> /* to declare off_t */ 0022 0023 /* 0024 * MACROS 0025 */ 0026 #define TAB '\t' 0027 0028 0029 /* 0030 * DATA DECLARATIONS 0031 */ 0032 typedef struct { 0033 size_t size; 0034 char *buffer; 0035 } vstring; 0036 0037 /* Information about current tag file */ 0038 struct sTagFile { 0039 /* has the file been opened and this structure initialized? */ 0040 short initialized; 0041 /* format of tag file */ 0042 short format; 0043 /* how is the tag file sorted? */ 0044 sortType sortMethod; 0045 /* pointer to file structure */ 0046 FILE* fp; 0047 /* file position of first character of `line' */ 0048 off_t pos; 0049 /* size of tag file in seekable positions */ 0050 off_t size; 0051 /* last line read */ 0052 vstring line; 0053 /* name of tag in last line read */ 0054 vstring name; 0055 /* defines tag search state */ 0056 struct { 0057 /* file position of last match for tag */ 0058 off_t pos; 0059 /* name of tag last searched for */ 0060 const char *name; 0061 /* length of name for partial matches */ 0062 size_t nameLength; 0063 /* peforming partial match */ 0064 short partial; 0065 /* ignoring case */ 0066 short ignorecase; 0067 } search; 0068 /* miscellaneous extension fields */ 0069 struct { 0070 /* number of entries in `list' */ 0071 unsigned short max; 0072 /* list of key value pairs */ 0073 tagExtensionField *list; 0074 } fields; 0075 /* buffers to be freed at close */ 0076 struct { 0077 /* name of program author */ 0078 char *author; 0079 /* name of program */ 0080 char *name; 0081 /* URL of distribution */ 0082 char *url; 0083 /* program version */ 0084 char *version; 0085 } program; 0086 }; 0087 0088 /* 0089 * DATA DEFINITIONS 0090 */ 0091 const char *const EmptyString = ""; 0092 const char *const PseudoTagPrefix = "!_"; 0093 0094 /* 0095 * FUNCTION DEFINITIONS 0096 */ 0097 0098 /* 0099 * Compare two strings, ignoring case. 0100 * Return 0 for match, < 0 for smaller, > 0 for bigger 0101 * Make sure case is folded to uppercase in comparison (like for 'sort -f') 0102 * This makes a difference when one of the chars lies between upper and lower 0103 * ie. one of the chars [ \ ] ^ _ ` for ascii. (The '_' in particular !) 0104 */ 0105 static int struppercmp (const char *s1, const char *s2) 0106 { 0107 int result; 0108 do 0109 { 0110 result = toupper ((int) *s1) - toupper ((int) *s2); 0111 } while (result == 0 && *s1++ != '\0' && *s2++ != '\0'); 0112 return result; 0113 } 0114 0115 static int strnuppercmp (const char *s1, const char *s2, size_t n) 0116 { 0117 int result; 0118 do 0119 { 0120 result = toupper ((int) *s1) - toupper ((int) *s2); 0121 } while (result == 0 && --n > 0 && *s1++ != '\0' && *s2++ != '\0'); 0122 return result; 0123 } 0124 0125 static int growString (vstring *s) 0126 { 0127 int result = 0; 0128 size_t newLength; 0129 char *newLine; 0130 if (s->size == 0) 0131 { 0132 newLength = 128; 0133 newLine = (char*) malloc (newLength); 0134 *newLine = '\0'; 0135 } 0136 else 0137 { 0138 newLength = 2 * s->size; 0139 newLine = (char*) realloc (s->buffer, newLength); 0140 } 0141 if (newLine == NULL) 0142 perror ("string too large"); 0143 else 0144 { 0145 s->buffer = newLine; 0146 s->size = newLength; 0147 result = 1; 0148 } 0149 return result; 0150 } 0151 0152 /* Copy name of tag out of tag line */ 0153 static void copyName (tagFile *const file) 0154 { 0155 size_t length; 0156 const char *end = strchr (file->line.buffer, '\t'); 0157 if (end == NULL) 0158 { 0159 end = strchr (file->line.buffer, '\n'); 0160 if (end == NULL) 0161 end = strchr (file->line.buffer, '\r'); 0162 } 0163 if (end != NULL) 0164 length = end - file->line.buffer; 0165 else 0166 length = strlen (file->line.buffer); 0167 while (length >= file->name.size) 0168 growString (&file->name); 0169 strncpy (file->name.buffer, file->line.buffer, length); 0170 file->name.buffer [length] = '\0'; 0171 } 0172 0173 static int readTagLineRaw (tagFile *const file) 0174 { 0175 int result = 1; 0176 int reReadLine; 0177 0178 /* If reading the line places any character other than a null or a 0179 * newline at the last character position in the buffer (one less than 0180 * the buffer size), then we must resize the buffer and reattempt to read 0181 * the line. 0182 */ 0183 do 0184 { 0185 char *const pLastChar = file->line.buffer + file->line.size - 2; 0186 char *line; 0187 0188 file->pos = ftell (file->fp); 0189 reReadLine = 0; 0190 *pLastChar = '\0'; 0191 line = fgets (file->line.buffer, (int) file->line.size, file->fp); 0192 if (line == NULL) 0193 { 0194 /* read error */ 0195 if (! feof (file->fp)) 0196 perror ("readTagLine"); 0197 result = 0; 0198 } 0199 else if (*pLastChar != '\0' && 0200 *pLastChar != '\n' && *pLastChar != '\r') 0201 { 0202 /* buffer overflow */ 0203 growString (&file->line); 0204 fseek (file->fp, file->pos, SEEK_SET); 0205 reReadLine = 1; 0206 } 0207 else 0208 { 0209 size_t i = strlen (file->line.buffer); 0210 while (i > 0 && 0211 (file->line.buffer [i - 1] == '\n' || file->line.buffer [i - 1] == '\r')) 0212 { 0213 file->line.buffer [i - 1] = '\0'; 0214 --i; 0215 } 0216 } 0217 } while (reReadLine && result); 0218 if (result) 0219 copyName (file); 0220 return result; 0221 } 0222 0223 static int readTagLine (tagFile *const file) 0224 { 0225 int result; 0226 do 0227 { 0228 result = readTagLineRaw (file); 0229 } while (result && *file->name.buffer == '\0'); 0230 return result; 0231 } 0232 0233 static tagResult growFields (tagFile *const file) 0234 { 0235 tagResult result = TagFailure; 0236 unsigned short newCount = 2 * file->fields.max; 0237 tagExtensionField *newFields = (tagExtensionField*) 0238 realloc (file->fields.list, newCount * sizeof (tagExtensionField)); 0239 if (newFields == NULL) 0240 perror ("too many extension fields"); 0241 else 0242 { 0243 file->fields.list = newFields; 0244 file->fields.max = newCount; 0245 result = TagSuccess; 0246 } 0247 return result; 0248 } 0249 0250 static void parseExtensionFields (tagFile *const file, tagEntry *const entry, 0251 char *const string) 0252 { 0253 char *p = string; 0254 while (p != NULL && *p != '\0') 0255 { 0256 while (*p == TAB) 0257 *p++ = '\0'; 0258 if (*p != '\0') 0259 { 0260 char *colon; 0261 char *field = p; 0262 p = strchr (p, TAB); 0263 if (p != NULL) 0264 *p++ = '\0'; 0265 colon = strchr (field, ':'); 0266 if (colon == NULL) 0267 entry->kind = field; 0268 else 0269 { 0270 const char *key = field; 0271 const char *value = colon + 1; 0272 *colon = '\0'; 0273 if (strcmp (key, "kind") == 0) 0274 entry->kind = value; 0275 else if (strcmp (key, "file") == 0) 0276 entry->fileScope = 1; 0277 else if (strcmp (key, "line") == 0) 0278 entry->address.lineNumber = atol (value); 0279 else 0280 { 0281 if (entry->fields.count == file->fields.max) 0282 growFields (file); 0283 file->fields.list [entry->fields.count].key = key; 0284 file->fields.list [entry->fields.count].value = value; 0285 ++entry->fields.count; 0286 } 0287 } 0288 } 0289 } 0290 } 0291 0292 static void parseTagLine (tagFile *file, tagEntry *const entry) 0293 { 0294 int i; 0295 char *p = file->line.buffer; 0296 char *tab = strchr (p, TAB); 0297 int fieldsPresent = 0; 0298 0299 entry->fields.list = NULL; 0300 entry->fields.count = 0; 0301 entry->kind = NULL; 0302 entry->fileScope = 0; 0303 0304 entry->name = p; 0305 if (tab != NULL) 0306 { 0307 *tab = '\0'; 0308 p = tab + 1; 0309 entry->file = p; 0310 tab = strchr (p, TAB); 0311 if (tab != NULL) 0312 { 0313 *tab = '\0'; 0314 p = tab + 1; 0315 if (*p == '/' || *p == '?') 0316 { 0317 /* parse pattern */ 0318 int delimiter = *(unsigned char*) p; 0319 entry->address.lineNumber = 0; 0320 entry->address.pattern = p; 0321 do 0322 { 0323 p = strchr (p + 1, delimiter); 0324 } while (p != NULL && *(p - 1) == '\\'); 0325 if (p == NULL) 0326 { 0327 /* invalid pattern */ 0328 } 0329 else 0330 ++p; 0331 } 0332 else if (isdigit ((int) *(unsigned char*) p)) 0333 { 0334 /* parse line number */ 0335 entry->address.pattern = p; 0336 entry->address.lineNumber = atol (p); 0337 while (isdigit ((int) *(unsigned char*) p)) 0338 ++p; 0339 } 0340 else 0341 { 0342 /* invalid pattern */ 0343 } 0344 if ( p != NULL ) 0345 { 0346 fieldsPresent = (strncmp (p, ";\"", 2) == 0); 0347 *p = '\0'; 0348 if (fieldsPresent) 0349 parseExtensionFields (file, entry, p + 2); 0350 } 0351 } 0352 } 0353 if (entry->fields.count > 0) 0354 entry->fields.list = file->fields.list; 0355 for (i = entry->fields.count ; i < file->fields.max ; ++i) 0356 { 0357 file->fields.list [i].key = NULL; 0358 file->fields.list [i].value = NULL; 0359 } 0360 } 0361 0362 static char *duplicate (const char *str) 0363 { 0364 char *result = NULL; 0365 if (str != NULL) 0366 { 0367 result = (char*) malloc (strlen (str) + 1); 0368 if (result == NULL) 0369 perror (NULL); 0370 else 0371 strcpy (result, str); 0372 } 0373 return result; 0374 } 0375 0376 static void readPseudoTags (tagFile *const file, tagFileInfo *const info) 0377 { 0378 fpos_t startOfLine; 0379 const size_t prefixLength = strlen (PseudoTagPrefix); 0380 if (info != NULL) 0381 { 0382 info->file.format = 1; 0383 info->file.sort = TAG_UNSORTED; 0384 info->program.author = NULL; 0385 info->program.name = NULL; 0386 info->program.url = NULL; 0387 info->program.version = NULL; 0388 } 0389 while (1) 0390 { 0391 fgetpos (file->fp, &startOfLine); 0392 if (! readTagLine (file)) 0393 break; 0394 if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0) 0395 break; 0396 else 0397 { 0398 tagEntry entry; 0399 const char *key, *value; 0400 parseTagLine (file, &entry); 0401 key = entry.name + prefixLength; 0402 value = entry.file; 0403 if (strcmp (key, "TAG_FILE_SORTED") == 0) 0404 file->sortMethod = (sortType) atoi (value); 0405 else if (strcmp (key, "TAG_FILE_FORMAT") == 0) 0406 file->format = atoi (value); 0407 else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0) 0408 file->program.author = duplicate (value); 0409 else if (strcmp (key, "TAG_PROGRAM_NAME") == 0) 0410 file->program.name = duplicate (value); 0411 else if (strcmp (key, "TAG_PROGRAM_URL") == 0) 0412 file->program.url = duplicate (value); 0413 else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0) 0414 file->program.version = duplicate (value); 0415 if (info != NULL) 0416 { 0417 info->file.format = file->format; 0418 info->file.sort = file->sortMethod; 0419 info->program.author = file->program.author; 0420 info->program.name = file->program.name; 0421 info->program.url = file->program.url; 0422 info->program.version = file->program.version; 0423 } 0424 } 0425 } 0426 fsetpos (file->fp, &startOfLine); 0427 } 0428 0429 static void gotoFirstLogicalTag (tagFile *const file) 0430 { 0431 fpos_t startOfLine; 0432 const size_t prefixLength = strlen (PseudoTagPrefix); 0433 rewind (file->fp); 0434 while (1) 0435 { 0436 fgetpos (file->fp, &startOfLine); 0437 if (! readTagLine (file)) 0438 break; 0439 if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0) 0440 break; 0441 } 0442 fsetpos (file->fp, &startOfLine); 0443 } 0444 0445 static tagFile *initialize (const char *const filePath, tagFileInfo *const info) 0446 { 0447 tagFile *result = (tagFile*) malloc (sizeof (tagFile)); 0448 if (result != NULL) 0449 { 0450 memset (result, 0, sizeof (tagFile)); 0451 growString (&result->line); 0452 growString (&result->name); 0453 result->fields.max = 20; 0454 result->fields.list = (tagExtensionField*) malloc ( 0455 result->fields.max * sizeof (tagExtensionField)); 0456 result->fp = fopen (filePath, "r"); 0457 if (result->fp == NULL) 0458 { 0459 free (result); 0460 result = NULL; 0461 info->status.error_number = errno; 0462 } 0463 else 0464 { 0465 fseek (result->fp, 0, SEEK_END); 0466 result->size = ftell (result->fp); 0467 rewind (result->fp); 0468 readPseudoTags (result, info); 0469 info->status.opened = 1; 0470 result->initialized = 1; 0471 } 0472 } 0473 return result; 0474 } 0475 0476 static void terminate (tagFile *const file) 0477 { 0478 fclose (file->fp); 0479 0480 free (file->line.buffer); 0481 free (file->name.buffer); 0482 free (file->fields.list); 0483 0484 if (file->program.author != NULL) 0485 free (file->program.author); 0486 if (file->program.name != NULL) 0487 free (file->program.name); 0488 if (file->program.url != NULL) 0489 free (file->program.url); 0490 if (file->program.version != NULL) 0491 free (file->program.version); 0492 0493 memset (file, 0, sizeof (tagFile)); 0494 0495 free (file); 0496 } 0497 0498 static tagResult readNext (tagFile *const file, tagEntry *const entry) 0499 { 0500 tagResult result = TagFailure; 0501 if (file == NULL || ! file->initialized) 0502 result = TagFailure; 0503 else if (! readTagLine (file)) 0504 result = TagFailure; 0505 else 0506 { 0507 if (entry != NULL) 0508 parseTagLine (file, entry); 0509 result = TagSuccess; 0510 } 0511 return result; 0512 } 0513 0514 static const char *readFieldValue ( 0515 const tagEntry *const entry, const char *const key) 0516 { 0517 const char *result = NULL; 0518 int i; 0519 if (strcmp (key, "kind") == 0) 0520 result = entry->kind; 0521 else if (strcmp (key, "file") == 0) 0522 result = EmptyString; 0523 else for (i = 0 ; i < entry->fields.count && result == NULL ; ++i) 0524 if (strcmp (entry->fields.list [i].key, key) == 0) 0525 result = entry->fields.list [i].value; 0526 return result; 0527 } 0528 0529 static int readTagLineSeek (tagFile *const file, const off_t pos) 0530 { 0531 int result = 0; 0532 if (fseek (file->fp, pos, SEEK_SET) == 0) 0533 { 0534 result = readTagLine (file); /* read probable partial line */ 0535 if (pos > 0 && result) 0536 result = readTagLine (file); /* read complete line */ 0537 } 0538 return result; 0539 } 0540 0541 static int nameComparison (tagFile *const file) 0542 { 0543 int result; 0544 if (file->search.ignorecase) 0545 { 0546 if (file->search.partial) 0547 result = strnuppercmp (file->search.name, file->name.buffer, 0548 file->search.nameLength); 0549 else 0550 result = struppercmp (file->search.name, file->name.buffer); 0551 } 0552 else 0553 { 0554 if (file->search.partial) 0555 result = strncmp (file->search.name, file->name.buffer, 0556 file->search.nameLength); 0557 else 0558 result = strcmp (file->search.name, file->name.buffer); 0559 } 0560 return result; 0561 } 0562 0563 static void findFirstNonMatchBefore (tagFile *const file) 0564 { 0565 #define JUMP_BACK 512 0566 int more_lines; 0567 int comp; 0568 off_t start = file->pos; 0569 off_t pos = start; 0570 do 0571 { 0572 if (pos < (off_t) JUMP_BACK) 0573 pos = 0; 0574 else 0575 pos = pos - JUMP_BACK; 0576 more_lines = readTagLineSeek (file, pos); 0577 comp = nameComparison (file); 0578 } while (more_lines && comp == 0 && pos > 0 && pos < start); 0579 } 0580 0581 static tagResult findFirstMatchBefore (tagFile *const file) 0582 { 0583 tagResult result = TagFailure; 0584 int more_lines; 0585 off_t start = file->pos; 0586 findFirstNonMatchBefore (file); 0587 do 0588 { 0589 more_lines = readTagLine (file); 0590 if (nameComparison (file) == 0) 0591 result = TagSuccess; 0592 } while (more_lines && result != TagSuccess && file->pos < start); 0593 return result; 0594 } 0595 0596 static tagResult findBinary (tagFile *const file) 0597 { 0598 tagResult result = TagFailure; 0599 off_t lower_limit = 0; 0600 off_t upper_limit = file->size; 0601 off_t last_pos = 0; 0602 off_t pos = upper_limit / 2; 0603 while (result != TagSuccess) 0604 { 0605 if (! readTagLineSeek (file, pos)) 0606 { 0607 /* in case we fell off end of file */ 0608 result = findFirstMatchBefore (file); 0609 break; 0610 } 0611 else if (pos == last_pos) 0612 { 0613 /* prevent infinite loop if we backed up to beginning of file */ 0614 break; 0615 } 0616 else 0617 { 0618 const int comp = nameComparison (file); 0619 last_pos = pos; 0620 if (comp < 0) 0621 { 0622 upper_limit = pos; 0623 pos = lower_limit + ((upper_limit - lower_limit) / 2); 0624 } 0625 else if (comp > 0) 0626 { 0627 lower_limit = pos; 0628 pos = lower_limit + ((upper_limit - lower_limit) / 2); 0629 } 0630 else if (pos == 0) 0631 result = TagSuccess; 0632 else 0633 result = findFirstMatchBefore (file); 0634 } 0635 } 0636 return result; 0637 } 0638 0639 static tagResult findSequential (tagFile *const file) 0640 { 0641 tagResult result = TagFailure; 0642 if (file->initialized) 0643 { 0644 while (result == TagFailure && readTagLine (file)) 0645 { 0646 if (nameComparison (file) == 0) 0647 result = TagSuccess; 0648 } 0649 } 0650 return result; 0651 } 0652 0653 static tagResult find (tagFile *const file, tagEntry *const entry, 0654 const char *const name, const int options) 0655 { 0656 tagResult result = TagFailure; 0657 file->search.name = name; 0658 file->search.nameLength = strlen (name); 0659 file->search.partial = (options & TAG_PARTIALMATCH) != 0; 0660 file->search.ignorecase = (options & TAG_IGNORECASE) != 0; 0661 fseek (file->fp, 0, SEEK_END); 0662 file->size = ftell (file->fp); 0663 rewind (file->fp); 0664 if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) || 0665 (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase)) 0666 { 0667 #ifdef DEBUG 0668 printf ("<performing binary search>\n"); 0669 #endif 0670 result = findBinary (file); 0671 } 0672 else 0673 { 0674 #ifdef DEBUG 0675 printf ("<performing sequential search>\n"); 0676 #endif 0677 result = findSequential (file); 0678 } 0679 0680 if (result != TagSuccess) 0681 file->search.pos = file->size; 0682 else 0683 { 0684 file->search.pos = file->pos; 0685 if (entry != NULL) 0686 parseTagLine (file, entry); 0687 } 0688 return result; 0689 } 0690 0691 static tagResult findNext (tagFile *const file, tagEntry *const entry) 0692 { 0693 tagResult result = TagFailure; 0694 if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) || 0695 (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase)) 0696 { 0697 result = tagsNext (file, entry); 0698 if (result == TagSuccess && nameComparison (file) != 0) 0699 result = TagFailure; 0700 } 0701 else 0702 { 0703 result = findSequential (file); 0704 if (result == TagSuccess && entry != NULL) 0705 parseTagLine (file, entry); 0706 } 0707 return result; 0708 } 0709 0710 /* 0711 * EXTERNAL INTERFACE 0712 */ 0713 0714 extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info) 0715 { 0716 return initialize (filePath, info); 0717 } 0718 0719 extern tagResult tagsSetSortType (tagFile *const file, const sortType type) 0720 { 0721 tagResult result = TagFailure; 0722 if (file != NULL && file->initialized) 0723 { 0724 file->sortMethod = type; 0725 result = TagSuccess; 0726 } 0727 return result; 0728 } 0729 0730 extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry) 0731 { 0732 tagResult result = TagFailure; 0733 if (file != NULL && file->initialized) 0734 { 0735 gotoFirstLogicalTag (file); 0736 result = readNext (file, entry); 0737 } 0738 return result; 0739 } 0740 0741 extern tagResult tagsNext (tagFile *const file, tagEntry *const entry) 0742 { 0743 tagResult result = TagFailure; 0744 if (file != NULL && file->initialized) 0745 result = readNext (file, entry); 0746 return result; 0747 } 0748 0749 extern const char *tagsField (const tagEntry *const entry, const char *const key) 0750 { 0751 const char *result = NULL; 0752 if (entry != NULL) 0753 result = readFieldValue (entry, key); 0754 return result; 0755 } 0756 0757 extern tagResult tagsFind (tagFile *const file, tagEntry *const entry, 0758 const char *const name, const int options) 0759 { 0760 tagResult result = TagFailure; 0761 if (file != NULL && file->initialized) 0762 result = find (file, entry, name, options); 0763 return result; 0764 } 0765 0766 extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry) 0767 { 0768 tagResult result = TagFailure; 0769 if (file != NULL && file->initialized) 0770 result = findNext (file, entry); 0771 return result; 0772 } 0773 0774 extern tagResult tagsClose (tagFile *const file) 0775 { 0776 tagResult result = TagFailure; 0777 if (file != NULL && file->initialized) 0778 { 0779 terminate (file); 0780 result = TagSuccess; 0781 } 0782 return result; 0783 } 0784 0785 /* 0786 * TEST FRAMEWORK 0787 */ 0788 0789 #ifdef READTAGS_MAIN 0790 0791 static const char *TagFileName = "tags"; 0792 static const char *ProgramName; 0793 static int extensionFields; 0794 static int SortOverride; 0795 static sortType SortMethod; 0796 0797 static void printTag (const tagEntry *entry) 0798 { 0799 int i; 0800 int first = 1; 0801 const char* separator = ";\""; 0802 const char* const empty = ""; 0803 /* "sep" returns a value only the first time it is evaluated */ 0804 #define sep (first ? (first = 0, separator) : empty) 0805 printf ("%s\t%s\t%s", 0806 entry->name, entry->file, entry->address.pattern); 0807 if (extensionFields) 0808 { 0809 if (entry->kind != NULL && entry->kind [0] != '\0') 0810 printf ("%s\tkind:%s", sep, entry->kind); 0811 if (entry->fileScope) 0812 printf ("%s\tfile:", sep); 0813 #if 0 0814 if (entry->address.lineNumber > 0) 0815 printf ("%s\tline:%lu", sep, entry->address.lineNumber); 0816 #endif 0817 for (i = 0 ; i < entry->fields.count ; ++i) 0818 printf ("%s\t%s:%s", sep, entry->fields.list [i].key, 0819 entry->fields.list [i].value); 0820 } 0821 putchar ('\n'); 0822 #undef sep 0823 } 0824 0825 static void findTag (const char *const name, const int options) 0826 { 0827 tagFileInfo info; 0828 tagEntry entry; 0829 tagFile *const file = tagsOpen (TagFileName, &info); 0830 if (file == NULL) 0831 { 0832 fprintf (stderr, "%s: cannot open tag file: %s: %s\n", 0833 ProgramName, strerror (info.status.error_number), name); 0834 exit (1); 0835 } 0836 else 0837 { 0838 if (SortOverride) 0839 tagsSetSortType (file, SortMethod); 0840 if (tagsFind (file, &entry, name, options) == TagSuccess) 0841 { 0842 do 0843 { 0844 printTag (&entry); 0845 } while (tagsFindNext (file, &entry) == TagSuccess); 0846 } 0847 tagsClose (file); 0848 } 0849 } 0850 0851 static void listTags (void) 0852 { 0853 tagFileInfo info; 0854 tagEntry entry; 0855 tagFile *const file = tagsOpen (TagFileName, &info); 0856 if (file == NULL) 0857 { 0858 fprintf (stderr, "%s: cannot open tag file: %s: %s\n", 0859 ProgramName, strerror (info.status.error_number), TagFileName); 0860 exit (1); 0861 } 0862 else 0863 { 0864 while (tagsNext (file, &entry) == TagSuccess) 0865 printTag (&entry); 0866 tagsClose (file); 0867 } 0868 } 0869 0870 const char *const Usage = 0871 "Find tag file entries matching specified names.\n\n" 0872 "Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n" 0873 "Options:\n" 0874 " -e Include extension fields in output.\n" 0875 " -i Perform case-insensitive matching.\n" 0876 " -l List all tags.\n" 0877 " -p Perform partial matching.\n" 0878 " -s[0|1|2] Override sort detection of tag file.\n" 0879 " -t file Use specified tag file (default: \"tags\").\n" 0880 "Note that options are acted upon as encountered, so order is significant.\n"; 0881 0882 extern int main (int argc, char **argv) 0883 { 0884 int options = 0; 0885 int actionSupplied = 0; 0886 int i; 0887 ProgramName = argv [0]; 0888 if (argc == 1) 0889 { 0890 fprintf (stderr, Usage, ProgramName); 0891 exit (1); 0892 } 0893 for (i = 1 ; i < argc ; ++i) 0894 { 0895 const char *const arg = argv [i]; 0896 if (arg [0] != '-') 0897 { 0898 findTag (arg, options); 0899 actionSupplied = 1; 0900 } 0901 else 0902 { 0903 size_t j; 0904 for (j = 1 ; arg [j] != '\0' ; ++j) 0905 { 0906 switch (arg [j]) 0907 { 0908 case 'e': extensionFields = 1; break; 0909 case 'i': options |= TAG_IGNORECASE; break; 0910 case 'p': options |= TAG_PARTIALMATCH; break; 0911 case 'l': listTags (); actionSupplied = 1; break; 0912 0913 case 't': 0914 if (arg [j+1] != '\0') 0915 { 0916 TagFileName = arg + j + 1; 0917 j += strlen (TagFileName); 0918 } 0919 else if (i + 1 < argc) 0920 TagFileName = argv [++i]; 0921 else 0922 { 0923 fprintf (stderr, Usage, ProgramName); 0924 exit (1); 0925 } 0926 break; 0927 case 's': 0928 SortOverride = 1; 0929 ++j; 0930 if (arg [j] == '\0') 0931 SortMethod = TAG_SORTED; 0932 else if (strchr ("012", arg[j]) != NULL) 0933 SortMethod = (sortType) (arg[j] - '0'); 0934 else 0935 { 0936 fprintf (stderr, Usage, ProgramName); 0937 exit (1); 0938 } 0939 break; 0940 default: 0941 fprintf (stderr, "%s: unknown option: %c\n", 0942 ProgramName, arg[j]); 0943 exit (1); 0944 break; 0945 } 0946 } 0947 } 0948 } 0949 if (! actionSupplied) 0950 { 0951 fprintf (stderr, 0952 "%s: no action specified: specify tag name(s) or -l option\n", 0953 ProgramName); 0954 exit (1); 0955 } 0956 return 0; 0957 } 0958 0959 #endif 0960 0961 /* vi:set tabstop=8 shiftwidth=4: */