Skip to content

HTTPS clone URL

Subversion checkout URL

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