ctags.rb 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (138) hide show
  1. data/Gemfile +2 -0
  2. data/Rakefile +23 -0
  3. data/ctags.rb.gemspec +23 -0
  4. data/ext/.gitignore +3 -0
  5. data/ext/extconf.rb +15 -0
  6. data/ext/vendor/exuberant-ctags/.gitignore +6 -0
  7. data/ext/vendor/exuberant-ctags/.indent.pro +31 -0
  8. data/ext/vendor/exuberant-ctags/COPYING +340 -0
  9. data/ext/vendor/exuberant-ctags/EXTENDING.html +386 -0
  10. data/ext/vendor/exuberant-ctags/FAQ +371 -0
  11. data/ext/vendor/exuberant-ctags/INSTALL +215 -0
  12. data/ext/vendor/exuberant-ctags/INSTALL.oth +73 -0
  13. data/ext/vendor/exuberant-ctags/MAINTAINERS +88 -0
  14. data/ext/vendor/exuberant-ctags/Makefile.in +222 -0
  15. data/ext/vendor/exuberant-ctags/NEWS +871 -0
  16. data/ext/vendor/exuberant-ctags/README +73 -0
  17. data/ext/vendor/exuberant-ctags/ant.c +42 -0
  18. data/ext/vendor/exuberant-ctags/argproc.c +505 -0
  19. data/ext/vendor/exuberant-ctags/args.c +274 -0
  20. data/ext/vendor/exuberant-ctags/args.h +63 -0
  21. data/ext/vendor/exuberant-ctags/asm.c +387 -0
  22. data/ext/vendor/exuberant-ctags/asp.c +328 -0
  23. data/ext/vendor/exuberant-ctags/awk.c +81 -0
  24. data/ext/vendor/exuberant-ctags/basic.c +203 -0
  25. data/ext/vendor/exuberant-ctags/beta.c +321 -0
  26. data/ext/vendor/exuberant-ctags/c.c +2932 -0
  27. data/ext/vendor/exuberant-ctags/cobol.c +50 -0
  28. data/ext/vendor/exuberant-ctags/config.h.in +277 -0
  29. data/ext/vendor/exuberant-ctags/configure +7704 -0
  30. data/ext/vendor/exuberant-ctags/configure.ac +532 -0
  31. data/ext/vendor/exuberant-ctags/ctags.1 +1186 -0
  32. data/ext/vendor/exuberant-ctags/ctags.h +28 -0
  33. data/ext/vendor/exuberant-ctags/ctags.html +2087 -0
  34. data/ext/vendor/exuberant-ctags/ctags.spec +40 -0
  35. data/ext/vendor/exuberant-ctags/debug.c +113 -0
  36. data/ext/vendor/exuberant-ctags/debug.h +70 -0
  37. data/ext/vendor/exuberant-ctags/descrip.mms +68 -0
  38. data/ext/vendor/exuberant-ctags/dosbatch.c +42 -0
  39. data/ext/vendor/exuberant-ctags/e_amiga.h +24 -0
  40. data/ext/vendor/exuberant-ctags/e_djgpp.h +47 -0
  41. data/ext/vendor/exuberant-ctags/e_mac.h +143 -0
  42. data/ext/vendor/exuberant-ctags/e_msoft.h +76 -0
  43. data/ext/vendor/exuberant-ctags/e_os2.h +37 -0
  44. data/ext/vendor/exuberant-ctags/e_qdos.h +34 -0
  45. data/ext/vendor/exuberant-ctags/e_riscos.h +58 -0
  46. data/ext/vendor/exuberant-ctags/e_vms.h +31 -0
  47. data/ext/vendor/exuberant-ctags/eiffel.c +1352 -0
  48. data/ext/vendor/exuberant-ctags/entry.c +847 -0
  49. data/ext/vendor/exuberant-ctags/entry.h +103 -0
  50. data/ext/vendor/exuberant-ctags/erlang.c +189 -0
  51. data/ext/vendor/exuberant-ctags/flex.c +2243 -0
  52. data/ext/vendor/exuberant-ctags/fortran.c +2197 -0
  53. data/ext/vendor/exuberant-ctags/general.h +127 -0
  54. data/ext/vendor/exuberant-ctags/get.c +669 -0
  55. data/ext/vendor/exuberant-ctags/get.h +50 -0
  56. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/all-wcprops +47 -0
  57. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/entries +112 -0
  58. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/README.txt.svn-base +5 -0
  59. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regcomp.c.svn-base +3818 -0
  60. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.c.svn-base +74 -0
  61. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex.h.svn-base +575 -0
  62. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.c.svn-base +1713 -0
  63. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regex_internal.h.svn-base +773 -0
  64. data/ext/vendor/exuberant-ctags/gnu_regex/.svn/text-base/regexec.c.svn-base +4338 -0
  65. data/ext/vendor/exuberant-ctags/gnu_regex/README.txt +5 -0
  66. data/ext/vendor/exuberant-ctags/gnu_regex/regcomp.c +3818 -0
  67. data/ext/vendor/exuberant-ctags/gnu_regex/regex.c +74 -0
  68. data/ext/vendor/exuberant-ctags/gnu_regex/regex.h +575 -0
  69. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.c +1713 -0
  70. data/ext/vendor/exuberant-ctags/gnu_regex/regex_internal.h +773 -0
  71. data/ext/vendor/exuberant-ctags/gnu_regex/regexec.c +4338 -0
  72. data/ext/vendor/exuberant-ctags/html.c +49 -0
  73. data/ext/vendor/exuberant-ctags/jscript.c +1572 -0
  74. data/ext/vendor/exuberant-ctags/keyword.c +258 -0
  75. data/ext/vendor/exuberant-ctags/keyword.h +34 -0
  76. data/ext/vendor/exuberant-ctags/lisp.c +139 -0
  77. data/ext/vendor/exuberant-ctags/lregex.c +704 -0
  78. data/ext/vendor/exuberant-ctags/lua.c +133 -0
  79. data/ext/vendor/exuberant-ctags/mac.c +273 -0
  80. data/ext/vendor/exuberant-ctags/magic.diff +21 -0
  81. data/ext/vendor/exuberant-ctags/main.c +584 -0
  82. data/ext/vendor/exuberant-ctags/main.h +32 -0
  83. data/ext/vendor/exuberant-ctags/maintainer.mak +476 -0
  84. data/ext/vendor/exuberant-ctags/make.c +217 -0
  85. data/ext/vendor/exuberant-ctags/matlab.c +44 -0
  86. data/ext/vendor/exuberant-ctags/mk_bc3.mak +46 -0
  87. data/ext/vendor/exuberant-ctags/mk_bc5.mak +49 -0
  88. data/ext/vendor/exuberant-ctags/mk_djg.mak +18 -0
  89. data/ext/vendor/exuberant-ctags/mk_manx.mak +65 -0
  90. data/ext/vendor/exuberant-ctags/mk_mingw.mak +31 -0
  91. data/ext/vendor/exuberant-ctags/mk_mpw.mak +130 -0
  92. data/ext/vendor/exuberant-ctags/mk_mvc.mak +40 -0
  93. data/ext/vendor/exuberant-ctags/mk_os2.mak +104 -0
  94. data/ext/vendor/exuberant-ctags/mk_qdos.mak +100 -0
  95. data/ext/vendor/exuberant-ctags/mk_sas.mak +63 -0
  96. data/ext/vendor/exuberant-ctags/mkinstalldirs +40 -0
  97. data/ext/vendor/exuberant-ctags/ocaml.c +1842 -0
  98. data/ext/vendor/exuberant-ctags/options.c +1842 -0
  99. data/ext/vendor/exuberant-ctags/options.h +155 -0
  100. data/ext/vendor/exuberant-ctags/parse.c +677 -0
  101. data/ext/vendor/exuberant-ctags/parse.h +129 -0
  102. data/ext/vendor/exuberant-ctags/parsers.h +63 -0
  103. data/ext/vendor/exuberant-ctags/pascal.c +267 -0
  104. data/ext/vendor/exuberant-ctags/perl.c +382 -0
  105. data/ext/vendor/exuberant-ctags/php.c +237 -0
  106. data/ext/vendor/exuberant-ctags/python.c +771 -0
  107. data/ext/vendor/exuberant-ctags/qdos.c +106 -0
  108. data/ext/vendor/exuberant-ctags/read.c +569 -0
  109. data/ext/vendor/exuberant-ctags/read.h +116 -0
  110. data/ext/vendor/exuberant-ctags/readtags.c +959 -0
  111. data/ext/vendor/exuberant-ctags/readtags.h +252 -0
  112. data/ext/vendor/exuberant-ctags/rexx.c +39 -0
  113. data/ext/vendor/exuberant-ctags/routines.c +891 -0
  114. data/ext/vendor/exuberant-ctags/routines.h +134 -0
  115. data/ext/vendor/exuberant-ctags/ruby.c +408 -0
  116. data/ext/vendor/exuberant-ctags/scheme.c +111 -0
  117. data/ext/vendor/exuberant-ctags/sh.c +115 -0
  118. data/ext/vendor/exuberant-ctags/slang.c +41 -0
  119. data/ext/vendor/exuberant-ctags/sml.c +212 -0
  120. data/ext/vendor/exuberant-ctags/sort.c +230 -0
  121. data/ext/vendor/exuberant-ctags/sort.h +32 -0
  122. data/ext/vendor/exuberant-ctags/source.mak +122 -0
  123. data/ext/vendor/exuberant-ctags/sql.c +2112 -0
  124. data/ext/vendor/exuberant-ctags/strlist.c +281 -0
  125. data/ext/vendor/exuberant-ctags/strlist.h +54 -0
  126. data/ext/vendor/exuberant-ctags/tcl.c +116 -0
  127. data/ext/vendor/exuberant-ctags/tex.c +524 -0
  128. data/ext/vendor/exuberant-ctags/verilog.c +340 -0
  129. data/ext/vendor/exuberant-ctags/vhdl.c +835 -0
  130. data/ext/vendor/exuberant-ctags/vim.c +636 -0
  131. data/ext/vendor/exuberant-ctags/vstring.c +232 -0
  132. data/ext/vendor/exuberant-ctags/vstring.h +85 -0
  133. data/ext/vendor/exuberant-ctags/yacc.c +40 -0
  134. data/lib/ctags/exuberant.rb +45 -0
  135. data/lib/ctags/version.rb +3 -0
  136. data/lib/ctags.rb +6 -0
  137. data/test/test_ctags.rb +24 -0
  138. metadata +233 -0
@@ -0,0 +1,959 @@
1
+ /*
2
+ * $Id: readtags.c 592 2007-07-31 03:30:41Z dhiebert $
3
+ *
4
+ * Copyright (c) 1996-2003, Darren Hiebert
5
+ *
6
+ * This source code is released into the public domain.
7
+ *
8
+ * This module contains functions for reading tag files.
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
+ */
32
+ typedef struct {
33
+ size_t size;
34
+ char *buffer;
35
+ } vstring;
36
+
37
+ /* Information about current tag file */
38
+ struct 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
+ 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
+ */
91
+ const char *const EmptyString = "";
92
+ const 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
+ */
105
+ static 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
+
115
+ static 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
+
125
+ static 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 */
153
+ static 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
+
173
+ static 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
+
223
+ static 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
+
233
+ static tagResult growFields (tagFile *const file)
234
+ {
235
+ tagResult result = TagFailure;
236
+ unsigned short newCount = (unsigned short) 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
+
250
+ static 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
+
292
+ static void parseTagLine (tagFile *file, tagEntry *const entry)
293
+ {
294
+ int i;
295
+ char *p = file->line.buffer;
296
+ char *tab = strchr (p, TAB);
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
+ int fieldsPresent;
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
+ fieldsPresent = (strncmp (p, ";\"", 2) == 0);
345
+ *p = '\0';
346
+ if (fieldsPresent)
347
+ parseExtensionFields (file, entry, p + 2);
348
+ }
349
+ }
350
+ if (entry->fields.count > 0)
351
+ entry->fields.list = file->fields.list;
352
+ for (i = entry->fields.count ; i < file->fields.max ; ++i)
353
+ {
354
+ file->fields.list [i].key = NULL;
355
+ file->fields.list [i].value = NULL;
356
+ }
357
+ }
358
+
359
+ static char *duplicate (const char *str)
360
+ {
361
+ char *result = NULL;
362
+ if (str != NULL)
363
+ {
364
+ result = strdup (str);
365
+ if (result == NULL)
366
+ perror (NULL);
367
+ }
368
+ return result;
369
+ }
370
+
371
+ static void readPseudoTags (tagFile *const file, tagFileInfo *const info)
372
+ {
373
+ fpos_t startOfLine;
374
+ const size_t prefixLength = strlen (PseudoTagPrefix);
375
+ if (info != NULL)
376
+ {
377
+ info->file.format = 1;
378
+ info->file.sort = TAG_UNSORTED;
379
+ info->program.author = NULL;
380
+ info->program.name = NULL;
381
+ info->program.url = NULL;
382
+ info->program.version = NULL;
383
+ }
384
+ while (1)
385
+ {
386
+ fgetpos (file->fp, &startOfLine);
387
+ if (! readTagLine (file))
388
+ break;
389
+ if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
390
+ break;
391
+ else
392
+ {
393
+ tagEntry entry;
394
+ const char *key, *value;
395
+ parseTagLine (file, &entry);
396
+ key = entry.name + prefixLength;
397
+ value = entry.file;
398
+ if (strcmp (key, "TAG_FILE_SORTED") == 0)
399
+ file->sortMethod = (sortType) atoi (value);
400
+ else if (strcmp (key, "TAG_FILE_FORMAT") == 0)
401
+ file->format = (short) atoi (value);
402
+ else if (strcmp (key, "TAG_PROGRAM_AUTHOR") == 0)
403
+ file->program.author = duplicate (value);
404
+ else if (strcmp (key, "TAG_PROGRAM_NAME") == 0)
405
+ file->program.name = duplicate (value);
406
+ else if (strcmp (key, "TAG_PROGRAM_URL") == 0)
407
+ file->program.url = duplicate (value);
408
+ else if (strcmp (key, "TAG_PROGRAM_VERSION") == 0)
409
+ file->program.version = duplicate (value);
410
+ if (info != NULL)
411
+ {
412
+ info->file.format = file->format;
413
+ info->file.sort = file->sortMethod;
414
+ info->program.author = file->program.author;
415
+ info->program.name = file->program.name;
416
+ info->program.url = file->program.url;
417
+ info->program.version = file->program.version;
418
+ }
419
+ }
420
+ }
421
+ fsetpos (file->fp, &startOfLine);
422
+ }
423
+
424
+ static void gotoFirstLogicalTag (tagFile *const file)
425
+ {
426
+ fpos_t startOfLine;
427
+ const size_t prefixLength = strlen (PseudoTagPrefix);
428
+ rewind (file->fp);
429
+ while (1)
430
+ {
431
+ fgetpos (file->fp, &startOfLine);
432
+ if (! readTagLine (file))
433
+ break;
434
+ if (strncmp (file->line.buffer, PseudoTagPrefix, prefixLength) != 0)
435
+ break;
436
+ }
437
+ fsetpos (file->fp, &startOfLine);
438
+ }
439
+
440
+ static tagFile *initialize (const char *const filePath, tagFileInfo *const info)
441
+ {
442
+ tagFile *result = (tagFile*) calloc ((size_t) 1, sizeof (tagFile));
443
+ if (result != NULL)
444
+ {
445
+ growString (&result->line);
446
+ growString (&result->name);
447
+ result->fields.max = 20;
448
+ result->fields.list = (tagExtensionField*) calloc (
449
+ result->fields.max, sizeof (tagExtensionField));
450
+ result->fp = fopen (filePath, "r");
451
+ if (result->fp == NULL)
452
+ {
453
+ free (result);
454
+ result = NULL;
455
+ info->status.error_number = errno;
456
+ }
457
+ else
458
+ {
459
+ fseek (result->fp, 0, SEEK_END);
460
+ result->size = ftell (result->fp);
461
+ rewind (result->fp);
462
+ readPseudoTags (result, info);
463
+ info->status.opened = 1;
464
+ result->initialized = 1;
465
+ }
466
+ }
467
+ return result;
468
+ }
469
+
470
+ static void terminate (tagFile *const file)
471
+ {
472
+ fclose (file->fp);
473
+
474
+ free (file->line.buffer);
475
+ free (file->name.buffer);
476
+ free (file->fields.list);
477
+
478
+ if (file->program.author != NULL)
479
+ free (file->program.author);
480
+ if (file->program.name != NULL)
481
+ free (file->program.name);
482
+ if (file->program.url != NULL)
483
+ free (file->program.url);
484
+ if (file->program.version != NULL)
485
+ free (file->program.version);
486
+ if (file->search.name != NULL)
487
+ free (file->search.name);
488
+
489
+ memset (file, 0, sizeof (tagFile));
490
+
491
+ free (file);
492
+ }
493
+
494
+ static tagResult readNext (tagFile *const file, tagEntry *const entry)
495
+ {
496
+ tagResult result;
497
+ if (file == NULL || ! file->initialized)
498
+ result = TagFailure;
499
+ else if (! readTagLine (file))
500
+ result = TagFailure;
501
+ else
502
+ {
503
+ if (entry != NULL)
504
+ parseTagLine (file, entry);
505
+ result = TagSuccess;
506
+ }
507
+ return result;
508
+ }
509
+
510
+ static const char *readFieldValue (
511
+ const tagEntry *const entry, const char *const key)
512
+ {
513
+ const char *result = NULL;
514
+ int i;
515
+ if (strcmp (key, "kind") == 0)
516
+ result = entry->kind;
517
+ else if (strcmp (key, "file") == 0)
518
+ result = EmptyString;
519
+ else for (i = 0 ; i < entry->fields.count && result == NULL ; ++i)
520
+ if (strcmp (entry->fields.list [i].key, key) == 0)
521
+ result = entry->fields.list [i].value;
522
+ return result;
523
+ }
524
+
525
+ static int readTagLineSeek (tagFile *const file, const off_t pos)
526
+ {
527
+ int result = 0;
528
+ if (fseek (file->fp, pos, SEEK_SET) == 0)
529
+ {
530
+ result = readTagLine (file); /* read probable partial line */
531
+ if (pos > 0 && result)
532
+ result = readTagLine (file); /* read complete line */
533
+ }
534
+ return result;
535
+ }
536
+
537
+ static int nameComparison (tagFile *const file)
538
+ {
539
+ int result;
540
+ if (file->search.ignorecase)
541
+ {
542
+ if (file->search.partial)
543
+ result = strnuppercmp (file->search.name, file->name.buffer,
544
+ file->search.nameLength);
545
+ else
546
+ result = struppercmp (file->search.name, file->name.buffer);
547
+ }
548
+ else
549
+ {
550
+ if (file->search.partial)
551
+ result = strncmp (file->search.name, file->name.buffer,
552
+ file->search.nameLength);
553
+ else
554
+ result = strcmp (file->search.name, file->name.buffer);
555
+ }
556
+ return result;
557
+ }
558
+
559
+ static void findFirstNonMatchBefore (tagFile *const file)
560
+ {
561
+ #define JUMP_BACK 512
562
+ int more_lines;
563
+ int comp;
564
+ off_t start = file->pos;
565
+ off_t pos = start;
566
+ do
567
+ {
568
+ if (pos < (off_t) JUMP_BACK)
569
+ pos = 0;
570
+ else
571
+ pos = pos - JUMP_BACK;
572
+ more_lines = readTagLineSeek (file, pos);
573
+ comp = nameComparison (file);
574
+ } while (more_lines && comp == 0 && pos > 0 && pos < start);
575
+ }
576
+
577
+ static tagResult findFirstMatchBefore (tagFile *const file)
578
+ {
579
+ tagResult result = TagFailure;
580
+ int more_lines;
581
+ off_t start = file->pos;
582
+ findFirstNonMatchBefore (file);
583
+ do
584
+ {
585
+ more_lines = readTagLine (file);
586
+ if (nameComparison (file) == 0)
587
+ result = TagSuccess;
588
+ } while (more_lines && result != TagSuccess && file->pos < start);
589
+ return result;
590
+ }
591
+
592
+ static tagResult findBinary (tagFile *const file)
593
+ {
594
+ tagResult result = TagFailure;
595
+ off_t lower_limit = 0;
596
+ off_t upper_limit = file->size;
597
+ off_t last_pos = 0;
598
+ off_t pos = upper_limit / 2;
599
+ while (result != TagSuccess)
600
+ {
601
+ if (! readTagLineSeek (file, pos))
602
+ {
603
+ /* in case we fell off end of file */
604
+ result = findFirstMatchBefore (file);
605
+ break;
606
+ }
607
+ else if (pos == last_pos)
608
+ {
609
+ /* prevent infinite loop if we backed up to beginning of file */
610
+ break;
611
+ }
612
+ else
613
+ {
614
+ const int comp = nameComparison (file);
615
+ last_pos = pos;
616
+ if (comp < 0)
617
+ {
618
+ upper_limit = pos;
619
+ pos = lower_limit + ((upper_limit - lower_limit) / 2);
620
+ }
621
+ else if (comp > 0)
622
+ {
623
+ lower_limit = pos;
624
+ pos = lower_limit + ((upper_limit - lower_limit) / 2);
625
+ }
626
+ else if (pos == 0)
627
+ result = TagSuccess;
628
+ else
629
+ result = findFirstMatchBefore (file);
630
+ }
631
+ }
632
+ return result;
633
+ }
634
+
635
+ static tagResult findSequential (tagFile *const file)
636
+ {
637
+ tagResult result = TagFailure;
638
+ if (file->initialized)
639
+ {
640
+ while (result == TagFailure && readTagLine (file))
641
+ {
642
+ if (nameComparison (file) == 0)
643
+ result = TagSuccess;
644
+ }
645
+ }
646
+ return result;
647
+ }
648
+
649
+ static tagResult find (tagFile *const file, tagEntry *const entry,
650
+ const char *const name, const int options)
651
+ {
652
+ tagResult result;
653
+ if (file->search.name != NULL)
654
+ free (file->search.name);
655
+ file->search.name = duplicate (name);
656
+ file->search.nameLength = strlen (name);
657
+ file->search.partial = (options & TAG_PARTIALMATCH) != 0;
658
+ file->search.ignorecase = (options & TAG_IGNORECASE) != 0;
659
+ fseek (file->fp, 0, SEEK_END);
660
+ file->size = ftell (file->fp);
661
+ rewind (file->fp);
662
+ if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
663
+ (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
664
+ {
665
+ #ifdef DEBUG
666
+ printf ("<performing binary search>\n");
667
+ #endif
668
+ result = findBinary (file);
669
+ }
670
+ else
671
+ {
672
+ #ifdef DEBUG
673
+ printf ("<performing sequential search>\n");
674
+ #endif
675
+ result = findSequential (file);
676
+ }
677
+
678
+ if (result != TagSuccess)
679
+ file->search.pos = file->size;
680
+ else
681
+ {
682
+ file->search.pos = file->pos;
683
+ if (entry != NULL)
684
+ parseTagLine (file, entry);
685
+ }
686
+ return result;
687
+ }
688
+
689
+ static tagResult findNext (tagFile *const file, tagEntry *const entry)
690
+ {
691
+ tagResult result;
692
+ if ((file->sortMethod == TAG_SORTED && !file->search.ignorecase) ||
693
+ (file->sortMethod == TAG_FOLDSORTED && file->search.ignorecase))
694
+ {
695
+ result = tagsNext (file, entry);
696
+ if (result == TagSuccess && nameComparison (file) != 0)
697
+ result = TagFailure;
698
+ }
699
+ else
700
+ {
701
+ result = findSequential (file);
702
+ if (result == TagSuccess && entry != NULL)
703
+ parseTagLine (file, entry);
704
+ }
705
+ return result;
706
+ }
707
+
708
+ /*
709
+ * EXTERNAL INTERFACE
710
+ */
711
+
712
+ extern tagFile *tagsOpen (const char *const filePath, tagFileInfo *const info)
713
+ {
714
+ return initialize (filePath, info);
715
+ }
716
+
717
+ extern tagResult tagsSetSortType (tagFile *const file, const sortType type)
718
+ {
719
+ tagResult result = TagFailure;
720
+ if (file != NULL && file->initialized)
721
+ {
722
+ file->sortMethod = type;
723
+ result = TagSuccess;
724
+ }
725
+ return result;
726
+ }
727
+
728
+ extern tagResult tagsFirst (tagFile *const file, tagEntry *const entry)
729
+ {
730
+ tagResult result = TagFailure;
731
+ if (file != NULL && file->initialized)
732
+ {
733
+ gotoFirstLogicalTag (file);
734
+ result = readNext (file, entry);
735
+ }
736
+ return result;
737
+ }
738
+
739
+ extern tagResult tagsNext (tagFile *const file, tagEntry *const entry)
740
+ {
741
+ tagResult result = TagFailure;
742
+ if (file != NULL && file->initialized)
743
+ result = readNext (file, entry);
744
+ return result;
745
+ }
746
+
747
+ extern const char *tagsField (const tagEntry *const entry, const char *const key)
748
+ {
749
+ const char *result = NULL;
750
+ if (entry != NULL)
751
+ result = readFieldValue (entry, key);
752
+ return result;
753
+ }
754
+
755
+ extern tagResult tagsFind (tagFile *const file, tagEntry *const entry,
756
+ const char *const name, const int options)
757
+ {
758
+ tagResult result = TagFailure;
759
+ if (file != NULL && file->initialized)
760
+ result = find (file, entry, name, options);
761
+ return result;
762
+ }
763
+
764
+ extern tagResult tagsFindNext (tagFile *const file, tagEntry *const entry)
765
+ {
766
+ tagResult result = TagFailure;
767
+ if (file != NULL && file->initialized)
768
+ result = findNext (file, entry);
769
+ return result;
770
+ }
771
+
772
+ extern tagResult tagsClose (tagFile *const file)
773
+ {
774
+ tagResult result = TagFailure;
775
+ if (file != NULL && file->initialized)
776
+ {
777
+ terminate (file);
778
+ result = TagSuccess;
779
+ }
780
+ return result;
781
+ }
782
+
783
+ /*
784
+ * TEST FRAMEWORK
785
+ */
786
+
787
+ #ifdef READTAGS_MAIN
788
+
789
+ static const char *TagFileName = "tags";
790
+ static const char *ProgramName;
791
+ static int extensionFields;
792
+ static int SortOverride;
793
+ static sortType SortMethod;
794
+
795
+ static void printTag (const tagEntry *entry)
796
+ {
797
+ int i;
798
+ int first = 1;
799
+ const char* separator = ";\"";
800
+ const char* const empty = "";
801
+ /* "sep" returns a value only the first time it is evaluated */
802
+ #define sep (first ? (first = 0, separator) : empty)
803
+ printf ("%s\t%s\t%s",
804
+ entry->name, entry->file, entry->address.pattern);
805
+ if (extensionFields)
806
+ {
807
+ if (entry->kind != NULL && entry->kind [0] != '\0')
808
+ printf ("%s\tkind:%s", sep, entry->kind);
809
+ if (entry->fileScope)
810
+ printf ("%s\tfile:", sep);
811
+ #if 0
812
+ if (entry->address.lineNumber > 0)
813
+ printf ("%s\tline:%lu", sep, entry->address.lineNumber);
814
+ #endif
815
+ for (i = 0 ; i < entry->fields.count ; ++i)
816
+ printf ("%s\t%s:%s", sep, entry->fields.list [i].key,
817
+ entry->fields.list [i].value);
818
+ }
819
+ putchar ('\n');
820
+ #undef sep
821
+ }
822
+
823
+ static void findTag (const char *const name, const int options)
824
+ {
825
+ tagFileInfo info;
826
+ tagEntry entry;
827
+ tagFile *const file = tagsOpen (TagFileName, &info);
828
+ if (file == NULL)
829
+ {
830
+ fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
831
+ ProgramName, strerror (info.status.error_number), name);
832
+ exit (1);
833
+ }
834
+ else
835
+ {
836
+ if (SortOverride)
837
+ tagsSetSortType (file, SortMethod);
838
+ if (tagsFind (file, &entry, name, options) == TagSuccess)
839
+ {
840
+ do
841
+ {
842
+ printTag (&entry);
843
+ } while (tagsFindNext (file, &entry) == TagSuccess);
844
+ }
845
+ tagsClose (file);
846
+ }
847
+ }
848
+
849
+ static void listTags (void)
850
+ {
851
+ tagFileInfo info;
852
+ tagEntry entry;
853
+ tagFile *const file = tagsOpen (TagFileName, &info);
854
+ if (file == NULL)
855
+ {
856
+ fprintf (stderr, "%s: cannot open tag file: %s: %s\n",
857
+ ProgramName, strerror (info.status.error_number), TagFileName);
858
+ exit (1);
859
+ }
860
+ else
861
+ {
862
+ while (tagsNext (file, &entry) == TagSuccess)
863
+ printTag (&entry);
864
+ tagsClose (file);
865
+ }
866
+ }
867
+
868
+ const char *const Usage =
869
+ "Find tag file entries matching specified names.\n\n"
870
+ "Usage: %s [-ilp] [-s[0|1]] [-t file] [name(s)]\n\n"
871
+ "Options:\n"
872
+ " -e Include extension fields in output.\n"
873
+ " -i Perform case-insensitive matching.\n"
874
+ " -l List all tags.\n"
875
+ " -p Perform partial matching.\n"
876
+ " -s[0|1|2] Override sort detection of tag file.\n"
877
+ " -t file Use specified tag file (default: \"tags\").\n"
878
+ "Note that options are acted upon as encountered, so order is significant.\n";
879
+
880
+ extern int main (int argc, char **argv)
881
+ {
882
+ int options = 0;
883
+ int actionSupplied = 0;
884
+ int i;
885
+ ProgramName = argv [0];
886
+ if (argc == 1)
887
+ {
888
+ fprintf (stderr, Usage, ProgramName);
889
+ exit (1);
890
+ }
891
+ for (i = 1 ; i < argc ; ++i)
892
+ {
893
+ const char *const arg = argv [i];
894
+ if (arg [0] != '-')
895
+ {
896
+ findTag (arg, options);
897
+ actionSupplied = 1;
898
+ }
899
+ else
900
+ {
901
+ size_t j;
902
+ for (j = 1 ; arg [j] != '\0' ; ++j)
903
+ {
904
+ switch (arg [j])
905
+ {
906
+ case 'e': extensionFields = 1; break;
907
+ case 'i': options |= TAG_IGNORECASE; break;
908
+ case 'p': options |= TAG_PARTIALMATCH; break;
909
+ case 'l': listTags (); actionSupplied = 1; break;
910
+
911
+ case 't':
912
+ if (arg [j+1] != '\0')
913
+ {
914
+ TagFileName = arg + j + 1;
915
+ j += strlen (TagFileName);
916
+ }
917
+ else if (i + 1 < argc)
918
+ TagFileName = argv [++i];
919
+ else
920
+ {
921
+ fprintf (stderr, Usage, ProgramName);
922
+ exit (1);
923
+ }
924
+ break;
925
+ case 's':
926
+ SortOverride = 1;
927
+ ++j;
928
+ if (arg [j] == '\0')
929
+ SortMethod = TAG_SORTED;
930
+ else if (strchr ("012", arg[j]) != NULL)
931
+ SortMethod = (sortType) (arg[j] - '0');
932
+ else
933
+ {
934
+ fprintf (stderr, Usage, ProgramName);
935
+ exit (1);
936
+ }
937
+ break;
938
+ default:
939
+ fprintf (stderr, "%s: unknown option: %c\n",
940
+ ProgramName, arg[j]);
941
+ exit (1);
942
+ break;
943
+ }
944
+ }
945
+ }
946
+ }
947
+ if (! actionSupplied)
948
+ {
949
+ fprintf (stderr,
950
+ "%s: no action specified: specify tag name(s) or -l option\n",
951
+ ProgramName);
952
+ exit (1);
953
+ }
954
+ return 0;
955
+ }
956
+
957
+ #endif
958
+
959
+ /* vi:set tabstop=4 shiftwidth=4: */