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