Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Newer
Older
100644 392 lines (345 sloc) 9.005 kb
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
1 /* tag.c */
2
3 /* This file contains functions and variables which are used by all tags
4 * programs, including elvis and ctags.
5 */
6
7 #include "elvis.h"
8
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
9 #ifdef FEATURE_TAGS
10 # if USE_PROTOTYPES
11 static ELVBOOL tagbefore(TAG *t1, TAG *t2);
12 # endif
13
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
14 /* This array stores the (dynamically allocated) names of attributes. */
15 char *tagattrname[MAXATTR] = {"tagname", "tagfile", "tagaddress"};
16
17 /* This stores the search direction to be used with regular expressions */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
18 ELVBOOL tagforward;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
19
20 /* This variable stores a list of tags. Tags can be added to the list via
21 * the tagadd() function, and deleted via tagdelete()
22 */
23 TAG *taglist;
24
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
25 /* Tag comparison function, returns non-zero if t1 should be inserted before t2.
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
26 */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
27 static ELVBOOL tagbefore(t1, t2)
28 TAG *t1, *t2;
29 {
30 long cmp;
31 char tmptime[20];
32
33 /* First compare them by tag name. If that doesn't resolve it, then
34 * compare them by how well they match previously accepted tags. As a
35 * last resort, compare them by timestamp
36 */
37 cmp = strcmp(t1->TAGNAME, t2->TAGNAME);
38 if (cmp == 0)
39 {
40 cmp = t2->match - t1->match;
41 }
42 if (cmp == 0)
43 {
44 strcpy(tmptime, dirtime(t1->TAGFILE));
45 cmp = strcmp(dirtime(t2->TAGFILE), tmptime);
46 }
47
48 /* return the comparison result */
49 return (ELVBOOL)(cmp < 0);
50 }
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
51
52 /* This function frees any names in the tagattrname[] array. It should be
53 * called when switching to a different tag file, after the last TAG structure
54 * from the previous file has been freed.
55 */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
56 void tagnamereset()
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
57 {
58 int i;
59
60 for (i = 3; i < MAXATTR && tagattrname[i]; i++)
61 {
62 safefree(tagattrname[i]);
63 tagattrname[i] = NULL;
64 }
65 }
66
67 /* This function allocates a TAG structure, and fills in all fields with
68 * copies of the attributes from another tag. The string fields are also
69 * dynamically allocated.
70 */
71 TAG *tagdup(tag)
72 TAG *tag; /* a tag to be duplicated */
73 {
74 TAG *ret; /* tag to be returned */
75 int i;
76
77 ret = (TAG *)safealloc(1, sizeof(TAG));
78 memset(ret, 0, sizeof(TAG));
79 for (i = 0; i < MAXATTR; i++)
80 {
81 if (tag->attr[i])
82 {
83 ret->attr[i] = safedup(tag->attr[i]);
84 }
85 }
86 ret->match = tag->match;
87 return ret;
88 }
89
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
90 /* This function adds a named attribute to a tag. Returns ElvTrue if successful,
91 * or ElvFalse if the tag already has too many named attributes.
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
92 */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
93 ELVBOOL tagattr(tag, name, value)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
94 TAG *tag; /* the tag to receive the value */
95 char *name; /* name of the attribute */
96 char *value; /* value of the attribute */
97 {
98 int i;
99
100 /* search for the name in tagattrname[] */
101 for (i = 0; i < MAXATTR && tagattrname[i] && strcmp(name, tagattrname[i]); i++)
102 {
103 }
104 if (i >= MAXATTR)
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
105 return ElvFalse;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
106 if (!tagattrname[i])
107 tagattrname[i] = safedup(name);
108
109 /* store the value */
110 tag->attr[i] = value;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
111 return ElvTrue;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
112 }
113
114
115 /* This function frees a tag. Returns the "next" field from the deleted
116 * tag, which can be handy in a loop.
117 */
118 TAG *tagfree(tag)
119 TAG *tag; /* the tag to be freed */
120 {
121 TAG *next;
122 int i;
123
124 /* free this tag */
125 next = tag->next;
126 for (i = 0; i < MAXATTR; i++)
127 {
128 if (tag->attr[i]) safefree(tag->attr[i]);
129 }
130 safefree(tag);
131
132 /* return its "next" field */
133 return next;
134 }
135
136
137 /* This function deletes the first tag, or all tags, from the taglist.
138 * After the last tag has been deleted, the tag attribute names are reset.
139 */
140 void tagdelete(all)
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
141 ELVBOOL all; /* if ElvTrue, delete all tags (else just first tag) */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
142 {
143 /* delete the tag(s) */
144 if (taglist)
145 {
146 do
147 {
148 taglist = tagfree(taglist);
149 } while (all && taglist != NULL);
150 }
151
152 /* if taglist is empty, then we can discard the attribute names too */
153 if (!taglist)
154 {
155 tagnamereset();
156 }
157 }
158
159
160 /* This function inserts a tag into the tag list. The list is sorted primarily
161 * by name; within each name, they are sorted by the likelyhood factor (highest
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
162 * first). Returns ElvTrue of another tag with that name was previously in the
163 * list, or ElvFalse if the new tag's name is unique so far.
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
164 *
165 * NOTE: This uses a special type of list, which uses an extra pointer to boost
166 * its update effiency to be about the same as a binary tree. Consequently,
167 * it should be efficient, even for fairly large lists.
168 */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
169 void tagadd(tag)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
170 TAG *tag;
171 {
172 TAG *scan;
173
174 /* if empty list, this is easy */
175 if (!taglist)
176 {
177 taglist = tag;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
178 return;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
179 }
180
181 /* if before head, then insert as new head */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
182 if (tagbefore(tag, taglist))
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
183 {
184 tag->next = taglist;
185 tag->bighop = taglist->bighop;
186 taglist->bighop = NULL;
187 taglist = tag;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
188 return;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
189 }
190
191 /* search for the insertion point */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
192 for (scan = taglist; scan->next && !tagbefore(tag, scan->next); )
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
193 {
194 /* if there is a bighop value, try it */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
195 if (scan->bighop && !tagbefore(tag, scan->bighop))
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
196 {
197 scan = scan->bighop;
198 }
199 else
200 {
201 scan = scan->next;
202 }
203 }
204
205 /* insert the item after the "scan" item */
206 tag->next = scan->next;
207 scan->next = tag;
208 if (!scan->bighop)
209 scan->bighop = tag;
210 }
211
212
213 /* This function parses a line from a tag file, and returns the corresponding
214 * tag. Returns NULL if...
215 * + the tag name is 0 characters long, or
216 * + the file name is missing or 0 characters long, or
217 * + the third field is neither a number nor a regular expression, or
218 * If any tag attributes are malformed, or if there are too many of them, then
219 * the remainder of the line is ignored but the tag data up to that point is
220 * returned.
221 *
222 * THE LINE IS CLOBBERED! THE RETURNED TAG IS STATICALLY-ALLOCATED, AND
223 * WILL CONTAIN REFERENCES BACK TO THE LINE TEXT! You can use tagdup() to
224 * make a dynamically-allocated copy of the tag.
225 */
226 TAG *tagparse(line)
227 char *line; /* a text line from a tags file */
228 {
229 char *tagname; /* name of tag (first field) */
230 char *filename; /* name of file (second field) */
231 char *address; /* address of the tag, as a string */
232 char *name, *value; /* an attribute of the tag */
233 char *build;
234 char c;
3a9bb55 @mbert Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
235 int quoted; /* 0=not quoted, 1=quoted, 2=backslash */
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
236 static TAG tag; /* the tag to be returned */
237
238 /* clobber any old data in "tag" */
239 memset(&tag, 0, sizeof tag);
240
241 /* parse the tag name */
242 tagname = line;
243 while (*line != '\t')
244 {
245 if (!*line || *line == '\n')
246 return NULL;
247 line++;
248 }
249 *line++ = '\0';
250
251 /* parse the file name */
252 filename = line;
253 while (*line != '\t')
254 {
255 if (!*line || *line == '\n')
256 return NULL;
257 line++;
258 }
259 *line++ = '\0';
260
261 /* parse the line address */
262 address = line;
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
263 if (elvdigit(*address))
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
264 {
265 /* number -- use all digits */
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
266 for (; elvdigit(*line); line++)
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
267 {
268 }
269 }
270 else if (*address == '?' || *address == '/')
271 {
272 /* regexp -- use chars up to EOL or next unquoted delimiter */
273 c = *line;
3a9bb55 @mbert Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
274 quoted = 0;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
275 do
276 {
277 if (*line == '\0' || *line == '\n')
278 return NULL;
279 line++;
3a9bb55 @mbert Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
280 if (quoted > 0)
281 quoted--;
282 if (quoted == 0 && *line == '\\')
283 quoted = 2;
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
284 } while (*line != c
3a9bb55 @mbert Import Elvis 2.1_3 (written by Steve Kirkendall)
authored
285 || quoted != 0
8d1ac0c @mbert Import Elvis 2.1 (written by Steve Kirkendall)
authored
286 || (line[1] == ';' && (line[2] == '/' || line[2] == '?')));
287 line++;
288 }
289 else
290 {
291 /* bad line address */
292 return NULL;
293 }
294
295 /* allow an optional semicolon-quote after the address */
296 if (line[0] == ';' && line[1] == '"')
297 {
298 *line = '\0';
299 line += 2;
300 }
301
302 /* must be EOL or a tab after the address */
303 if (*line && *line != '\n' && *line != '\t')
304 return NULL;
305
306 /* stuff the standard attributes into the tag */
307 tag.attr[0] = tagname;
308 tag.attr[1] = filename;
309 tag.attr[2] = address;
310
311 /* If there are any extra attributes, stuff them into the tag */
312 for (c = *line; c == '\t'; )
313 {
314 /* mark the end of the previous field */
315 *line++ = '\0';
316
317 /* parse the name of the attribute */
318 name = line;
319 while (*line && *line != '\n' && *line != '\t' && *line != ':')
320 {
321 line++;
322 }
323 if (*line != ':')
324 {
325 /* use the "name" as the value of `kind' */
326 c = *line;
327 *line = '\0';
328 if (!tagattr(&tag, "kind", name))
329 {
330 break;
331 }
332 *line = c;
333 continue;
334 }
335 *line++ = '\0';
336
337 /* Parse the value of the attribute, and translate "\x" into
338 * the corresponding character.
339 */
340 build = value = line;
341 while (*line && *line != '\n' && *line != '\t')
342 {
343 if (line[0] == '\\' && line[1] == 't')
344 {
345 *build++ = '\t';
346 line += 2;
347 }
348 else if (line[0] == '\\' && line[1] == 'n')
349 {
350 *build++ = '\n';
351 line += 2;
352 }
353 else if (line[0] == '\\' && line[1] == '\\')
354 {
355 *build++ = '\\';
356 line += 2;
357 }
358 else
359 {
360 *build++ = *line++;
361 }
362 }
363
364 /* Mark the end of the value. Before doing that, though,
365 * remember the last character scanned so far. We must
366 * remember that character before marking the end of the
367 * value, because it is possible that the end of the value
368 * will be at the same location as the last scanned char,
369 * and we don't want to clobber it until after we have a
370 * copy elsewhere.
371 */
372 c = *line;
373 *build = '\0';
374
375 /* For "file" attribute with no value, assume filename */
376 if (!*value && !strcmp(name, "file"))
377 value = filename;
378
379 /* Store it. If too many hints, then skip the rest */
380 if (!tagattr(&tag, name, value))
381 {
382 break;
383 }
384 }
385
386 /* mark the end of the last field */
387 *line = '\0';
388
389 return &tag;
390 }
9f1c6f0 @mbert Import Elvis 2.2_0 (written by Steve Kirkendall)
authored
391 #endif /* FEATURE_TAGS */
Something went wrong with that request. Please try again.