ctags.rb 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: */