1 /*
2  * Copyright (c) 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005
3  *	2008	Tama Communications Corporation
4  *
5  * This file is part of GNU GLOBAL.
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation, either version 3 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
19  */
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23 #include <stdio.h>
24 #ifdef STDC_HEADERS
25 #include <stdlib.h>
26 #endif
27 #ifdef HAVE_STRING_H
28 #include <string.h>
29 #else
30 #include <strings.h>
31 #endif
32 #ifdef HAVE_CTYPE_H
33 #include <ctype.h>
34 #endif
35 #include "global.h"
36 #include "anchor.h"
37 #include "htags.h"
38 #include "path2url.h"
39 
40 static XARGS *anchor_input[GTAGLIM];
41 static struct anchor *table;
42 static VARRAY *vb;
43 
44 static struct anchor *start;
45 static struct anchor *curp;
46 static struct anchor *end;
47 static struct anchor *CURRENT;
48 
49 /** compare routine for qsort(3) */
50 static int
cmp(const void * s1,const void * s2)51 cmp(const void *s1, const void *s2)
52 {
53 	return ((struct anchor *)s1)->lineno - ((struct anchor *)s2)->lineno;
54 }
55 /*
56  * Pointers (as lineno).
57  */
58 static int FIRST;
59 static int LAST;
60 static struct anchor *CURRENTDEF;
61 
62 /**
63  * anchor_prepare: setup input stream.
64  *
65  *	@param[in]	anchor_stream	file pointer of path list
66  */
67 void
anchor_prepare(FILE * anchor_stream)68 anchor_prepare(FILE *anchor_stream)
69 {
70 	/*
71 	 * Option table:
72 	 * We use blank string as null option instead of null string("")
73 	 * not to change the command length. This length influences
74 	 * the number of arguments in the xargs processing.
75 	 */
76 	static const char *const options[] = {NULL, " ", "r", "s"};
77 	char comline[MAXFILLEN];
78 	int db;
79 
80 	for (db = GTAGS; db < GTAGLIM; db++) {
81 		anchor_input[db] = NULL;
82 		/*
83 		 * Htags(1) should not use gtags-parser(1) directly;
84 		 * it should use global(1) with the -f option instead.
85 		 * Because gtags-parser is part of the implementation of
86 		 * gtags(1) and global(1), and htags is only an application
87 		 * program which uses global(1). If htags depends on
88 		 * gtags-parser, it will become difficult to change the
89 		 * implementation of gtags and global.
90 		 *
91 		 * Though the output of global -f is not necessarily sorted
92 		 * by the path, it is guaranteed that the records concerning
93 		 * the same file are consecutive.
94 		 */
95 		if (gtags_exist[db] == 1) {
96 			snprintf(comline, sizeof(comline), "%s -f%s --encode-path=\" \t\" --result=ctags-xid --nofilter=path", quote_shell(global_path), options[db]);
97 			anchor_input[db] = xargs_open_with_file(comline, 0, anchor_stream);
98 		}
99 	}
100 }
101 /**
102  * anchor_load: load anchor table
103  *
104  *	@param[in]	path	path name
105  */
106 void
anchor_load(const char * path)107 anchor_load(const char *path)
108 {
109 	int db, current_fid;
110 
111 	/* Get fid of the path */
112 	{
113 		const char *p = path2fid(path);
114 		if (p == NULL)
115 			die("anchor_load: internal error. file '%s' not found in GPATH.", path);
116 		current_fid = atoi(p);
117 	}
118 	FIRST = LAST = 0;
119 	end = CURRENT = NULL;
120 
121 	if (vb == NULL)
122 		vb = varray_open(sizeof(struct anchor), 1000);
123 	else
124 		varray_reset(vb);
125 
126 	for (db = GTAGS; db < GTAGLIM; db++) {
127 		XARGS *xp;
128 		char *ctags_xid;
129 
130 		if ((xp = anchor_input[db]) == NULL)
131 			continue;
132 		/*
133 		 * Read from input stream until it reaches end of file
134 		 * or the line of another file appears.
135 		 */
136 		while ((ctags_xid = xargs_read(xp)) != NULL) {
137 			SPLIT ptable;
138 			struct anchor *a;
139 			int type, fid;
140 			const char *ctags_x = parse_xid(ctags_xid, NULL, &fid);
141 
142 			/*
143 			 * It is the following file.
144 			 */
145 			if (current_fid != fid) {
146 				xargs_unread(xp);
147 				break;
148 			}
149 			if (split(ctags_x, 4, &ptable) < 4) {
150 				recover(&ptable);
151 				die("too small number of parts in anchor_load().\n'%s'", ctags_x);
152 			}
153 			if (db == GTAGS) {
154 				char *p = ptable.part[PART_LINE].start;
155 
156 				for (; *p && isspace((unsigned char)*p); p++)
157 					;
158 				if (!*p) {
159 					recover(&ptable);
160 					die("The output of parser is invalid.\n%s", ctags_x);
161 				}
162 				/*
163 				 * Function header is applied only to the anchor whoes type is 'D'.
164 				 * (D: function, M: macro, T: type)
165 				 */
166 				type = 'T';
167 				if (*p == '#')
168 					type = 'M';
169 				else if (locatestring(p, "typedef", MATCH_AT_FIRST))
170 					type = 'T';
171 				else if ((p = locatestring(p, ptable.part[PART_TAG].start, MATCH_FIRST)) != NULL) {
172 					/* skip a tag and the following blanks */
173 					p += strlen(ptable.part[PART_TAG].start);
174 					for (; *p && isspace((unsigned char)*p); p++)
175 						;
176 					if (*p == '(')
177 						type = 'D';
178 				}
179 			}  else if (db == GRTAGS)
180 				type = 'R';
181 			else
182 				type = 'Y';
183 			/* allocate an entry */
184 			a = varray_append(vb);
185 			a->lineno = atoi(ptable.part[PART_LNO].start);
186 			a->type = type;
187 			a->done = 0;
188 			settag(a, ptable.part[PART_TAG].start);
189 			recover(&ptable);
190 		}
191 		if (ctags_xid == NULL) {
192 			xargs_close(anchor_input[db]);
193 			anchor_input[db] = NULL;
194 		}
195 	}
196 	if (vb->length == 0) {
197 		table = NULL;
198 	} else {
199 		int i, used = vb->length;
200 		/*
201 		 * Sort by lineno.
202 		 */
203 		table = varray_assign(vb, 0, 0);
204 		qsort(table, used, sizeof(struct anchor), cmp);
205 		/*
206 		 * Setup some lineno.
207 		 */
208 		for (i = 0; i < used; i++)
209 			if (table[i].type == 'D')
210 				break;
211 		if (i < used)
212 			FIRST = table[i].lineno;
213 		for (i = used - 1; i >= 0; i--)
214 			if (table[i].type == 'D')
215 				break;
216 		if (i >= 0)
217 			LAST = table[i].lineno;
218 	}
219 	/*
220 	 * Setup loop range.
221 	 */
222 	start = table;
223 	curp = NULL;
224 	end = &table[vb->length];
225 	/* anchor_dump(stderr, 0);*/
226 }
227 /**
228  * anchor_unload: unload anchor table
229  */
230 void
anchor_unload(void)231 anchor_unload(void)
232 {
233 	struct anchor *a;
234 
235 	for (a = start; a && a < end; a++) {
236 		if (a->reserve) {
237 			free(a->reserve);
238 			a->reserve = NULL;
239 		}
240 	}
241 	/* We don't free varray */
242 	/* varray_close(vb); */
243 	FIRST = LAST = 0;
244 	start = curp = end = NULL;
245 }
246 /**
247  * anchor_first: return the first anchor
248  */
249 struct anchor *
anchor_first(void)250 anchor_first(void)
251 {
252 	if (!start || start == end)
253 		return NULL;
254 	CURRENT = start;
255 	if (CURRENT->type == 'D')
256 		CURRENTDEF = CURRENT;
257 	return CURRENT;
258 }
259 /**
260  * anchor_next: return the next anchor
261  */
262 struct anchor *
anchor_next(void)263 anchor_next(void)
264 {
265 	if (!start)
266 		return NULL;
267 	if (++CURRENT >= end)
268 		return NULL;
269 	if (CURRENT->type == 'D')
270 		CURRENTDEF = CURRENT;
271 	return CURRENT;
272 }
273 /**
274  * anchor_get: return the specified anchor
275  *
276  *	@param[in]	name	name of anchor
277  *	@param[in]	length	lenght of the name
278  *	@param[in]	type	==0: not specified,
279  *			!=0: D, M, T, R, Y
280  *	@param[in]	lineno	line number
281  */
282 struct anchor *
anchor_get(const char * name,int length,int type,int lineno)283 anchor_get(const char *name, int length, int type, int lineno)
284 {
285 	struct anchor *p = curp ? curp : start;
286 
287 	if (table == NULL)
288 		return NULL;
289 	if (p->lineno > lineno)
290 		return NULL;
291 	/*
292 	 * set pointer to the top of the cluster.
293 	 */
294 	for (; p < end && p->lineno < lineno; p++)
295 		;
296 	if (p >= end || p->lineno != lineno)
297 		return NULL;
298 	curp = p;
299 	for (; p < end && p->lineno == lineno; p++)
300 		if (!p->done && p->length == length && !strcmp(gettag(p), name))
301 			if (!type || p->type == type)
302 				return p;
303 	return NULL;
304 }
305 /**
306  * define_line: check whether or not this is a define line.
307  *
308  *	@param[in]	lineno	line number
309  *	@return		1: definition, 0: not definition
310  *	Globals used (output):
311  *		curp	pointer to the current cluster
312  */
313 int
define_line(int lineno)314 define_line(int lineno)
315 {
316 	struct anchor *p = curp ? curp : start;
317 
318 	if (table == NULL)
319 		return 0;
320 	if (p->lineno > lineno)
321 		return 0;
322 	/*
323 	 * set pointer to the top of the cluster.
324 	 */
325 	for (; p < end && p->lineno < lineno; p++)
326 		;
327 	if (p >= end || p->lineno != lineno)
328 		return 0;
329 	curp = p;
330 	for (; p < end && p->lineno == lineno; p++)
331 		if (p->type == 'D')
332 			return 1;
333 	return 0;
334 }
335 /**
336  * anchor_getlinks: return anchor link array
337  *		(previous, next, first, last, top, bottom)
338  *
339  *	@param[in]	lineno	line number
340  */
341 int *
anchor_getlinks(int lineno)342 anchor_getlinks(int lineno)
343 {
344 	static int ref[A_SIZE];
345 	int i;
346 
347 	for (i = 0; i < A_SIZE; i++)
348 		ref[i] = 0;
349 	if (lineno >= 1 && start) {
350 		struct anchor *c, *p;
351 
352 		if (CURRENTDEF == NULL) {
353 			for (c = start; c < end; c++)
354 				if (c->lineno == lineno && c->type == 'D')
355 					break;
356 			CURRENTDEF = c;
357 		} else {
358 			for (c = CURRENTDEF; c >= start; c--)
359 				if (c->lineno == lineno && c->type == 'D')
360 					break;
361 		}
362 		for (p = c - 1; p >= start; p--)
363 			if (p->type == 'D') {
364 				ref[A_PREV] = p->lineno;
365 				break;
366 			}
367 		for (p = c + 1; p < end; p++)
368 			if (p->type == 'D') {
369 				ref[A_NEXT] = p->lineno;
370 				break;
371 			}
372 	}
373 	if (FIRST > 0 && lineno != FIRST)
374 		ref[A_FIRST] = FIRST;
375 	if (LAST > 0 && lineno != LAST)
376 		ref[A_LAST] = LAST;
377 	if (lineno != 0)
378 		ref[A_TOP] = -1;
379 	if (lineno != -1)
380 		ref[A_BOTTOM] = -2;
381 	if (FIRST > 0 && FIRST == LAST) {
382 		if (lineno == 0)
383 			ref[A_LAST] = 0;
384 		if (lineno == -1)
385 			ref[A_FIRST] = 0;
386 	}
387 	return ref;
388 }
389 void
anchor_dump(FILE * op,int lineno)390 anchor_dump(FILE *op, int lineno)
391 {
392 	struct anchor *a;
393 
394 	if (op == NULL)
395 		op = stderr;
396 	for (a = start; a < end ; a++)
397 		if (lineno == 0 || a->lineno == lineno)
398 			fprintf(op, "%d %s(%c)\n",
399 				a->lineno, gettag(a), a->type);
400 }
401