~ubuntu-branches/debian/sid/kdevelop/sid

« back to all changes in this revision

Viewing changes to parts/ctags2/readtags.c

  • Committer: Bazaar Package Importer
  • Author(s): Jeremy Lainé
  • Date: 2010-05-05 07:21:55 UTC
  • mfrom: (1.2.3 upstream) (5.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20100505072155-h78lx19pu04sbhtn
Tags: 4:4.0.0-2
* Upload to unstable (Closes: #579947, #481832).
* Acknowledge obsolete NMU fixes (Closes: #562410, #546961).

Show diffs side-by-side

added added

removed removed

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