/* */
This source file includes following definitions.
- cmp
- load_notfunction
- isnotfunction
- load_plugin_parser
- unload_plugin_parser
- get_lang_entry
- parser_init
- parser_exit
- parse_file
- dbg_print
1 /*
2 * Copyright (c) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2010, 2011
3 * 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
21 #ifdef HAVE_CONFIG_H
22 #include <config.h>
23 #endif
24 #include <stdio.h>
25 #ifdef STDC_HEADERS
26 #include <stdlib.h>
27 #endif
28 #ifdef HAVE_STRING_H
29 #include <string.h>
30 #else
31 #include <strings.h>
32 #endif
33 #include <ltdl.h>
34
35 #include "parser.h"
36 #include "internal.h"
37 #include "checkalloc.h"
38 #include "die.h"
39 #include "langmap.h"
40 #include "locatestring.h"
41 #include "queue.h"
42 #include "strbuf.h"
43 #include "test.h"
44
45 #define NOTFUNCTION ".notfunction"
46 #ifdef __DJGPP__
47 #define DOS_NOTFUNCTION "_notfunction"
48 #endif
49
50 struct words {
51 const char *name;
52 };
53 static struct words *words;
54 static int tablesize;
55
56 static int
57 cmp(const void *s1, const void *s2)
58 {
59 return strcmp(((struct words *)s1)->name, ((struct words *)s2)->name);
60 }
61
62 static void
63 load_notfunction(const char *filename)
64 {
65 FILE *ip;
66 STRBUF *sb = strbuf_open(0);
67 STRBUF *ib = strbuf_open(0);
68 char *p;
69 int i;
70
71 if ((ip = fopen(filename, "r")) == NULL)
72 die("'%s' cannot read.", filename);
73 for (tablesize = 0; (p = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL; tablesize++)
74 strbuf_puts0(sb, p);
75 fclose(ip);
76 words = (struct words *)check_malloc(sizeof(struct words) * tablesize);
77 /*
78 * Don't free *p.
79 */
80 p = (char *)check_malloc(strbuf_getlen(sb) + 1);
81 memcpy(p, strbuf_value(sb), strbuf_getlen(sb) + 1);
82 for (i = 0; i < tablesize; i++) {
83 words[i].name = p;
84 p += strlen(p) + 1;
85 }
86 qsort(words, tablesize, sizeof(struct words), cmp);
87 strbuf_close(sb);
88 strbuf_close(ib);
89 }
90
91 static int
92 isnotfunction(const char *name)
93 {
94 struct words tmp;
95 struct words *result;
96
97 if (words == NULL)
98 return 0;
99 tmp.name = name;
100 result = (struct words *)bsearch(&tmp, words, tablesize, sizeof(struct words), cmp);
101 return (result != NULL) ? 1 : 0;
102 }
103
104 /*----------------------------------------------------------------------*/
105 /* Parser switch */
106 /*----------------------------------------------------------------------*/
107 /**
108 * This is the linkage section of each parsers.
109 * If you want to support new language, you must define parser procedure
110 * which requires file name as an argument.
111 */
112 struct lang_entry {
113 const char *lang_name;
114 void (*parser)(const struct parser_param *); /**< parser procedure */
115 const char *parser_name;
116 const char *lt_dl_name;
117 };
118
119 struct plugin_entry {
120 STAILQ_ENTRY(plugin_entry) next;
121 lt_dlhandle handle;
122 struct lang_entry entry;
123 };
124
125 #ifndef IS__DOXYGEN_
126 static STAILQ_HEAD(plugin_list, plugin_entry)
127 plugin_list = STAILQ_HEAD_INITIALIZER(plugin_list);
128
129 #else
130 static struct plugin_list {
131 struct plugin_entry *stqh_first; /**< first element */
132 struct plugin_entry **stqh_last; /**< addr of last next element */
133 } plugin_list = { NULL, &(plugin_list).stqh_first };
134 #endif
135 static char *langmap_saved, *pluginspec_saved;
136
137 /**
138 * load_plugin_parser: Load plug-in parsers.
139 *
140 * @param[in] pluginspec described below
141 *
142 * @par Syntax:
143 * @code{.txt}
144 * <pluginspec> ::= <map> | <map>","<pluginspec>
145 * <map> ::= <language name>":"<shared object path>
146 * | <language name>":"<shared object path>":"<function name>
147 * @endcode
148 */
149 static void
150 load_plugin_parser(const char *pluginspec)
151 {
152 char *p, *q;
153 const char *lt_dl_name, *parser_name;
154 struct plugin_entry *pent;
155
156 pluginspec_saved = check_strdup(pluginspec);
157 if (lt_dlinit() != 0)
158 die("cannot initialize libltdl.");
159 p = pluginspec_saved;
160 while (*p != '\0') {
161 pent = check_malloc(sizeof(*pent));
162 pent->entry.lang_name = p;
163 p = strchr(p, ':');
164 if (p == NULL)
165 die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
166 *p++ = '\0';
167 if (*p == '\0')
168 die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
169 lt_dl_name = p;
170 p = strchr(p, ',');
171 if (p != NULL)
172 *p++ = '\0';
173 q = strchr(lt_dl_name, ':');
174 if (q == NULL) {
175 parser_name = "parser";
176 } else {
177 *q++ = '\0';
178 if (*q == '\0')
179 die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
180 parser_name = q;
181 }
182 pent->handle = lt_dlopen(lt_dl_name);
183 if (pent->handle == NULL)
184 die_with_code(2, "cannot open shared object '%s'.", lt_dl_name);
185 pent->entry.lt_dl_name = lt_dl_name;
186 pent->entry.parser = lt_dlsym(pent->handle, parser_name);
187 if (pent->entry.parser == NULL)
188 die_with_code(2, "cannot find symbol '%s' in '%s'.", parser_name, lt_dl_name);
189 pent->entry.parser_name = parser_name;
190 STAILQ_INSERT_TAIL(&plugin_list, pent, next);
191 if (p == NULL)
192 break;
193 }
194 }
195
196 /**
197 * unload_plugin_parser: Unload plug-in parsers.
198 */
199 static void
200 unload_plugin_parser(void)
201 {
202 struct plugin_entry *pent;
203
204 if (pluginspec_saved == NULL)
205 return;
206 while (!STAILQ_EMPTY(&plugin_list)) {
207 pent = STAILQ_FIRST(&plugin_list);
208 lt_dlclose(pent->handle);
209 STAILQ_REMOVE_HEAD(&plugin_list, next);
210 free(pent);
211 }
212 lt_dlexit();
213 free(pluginspec_saved);
214 }
215
216 /**
217 * The first entry is default language.
218 */
219 static const struct lang_entry lang_switch[] = {
220 /* lang_name parser_proc parser_name lt_dl_name, */
221 /* (for debug) (for debug) */
222 {"c", C, "C", "builtin"}, /* DEFAULT */
223 {"yacc", yacc, "yacc", "builtin"},
224 {"cpp", Cpp, "Cpp", "builtin"},
225 {"java", java, "java", "builtin"},
226 {"php", php, "php", "builtin"},
227 {"asm", assembly, "assembly", "builtin"}
228 };
229 #define DEFAULT_ENTRY &lang_switch[0]
230 /**
231 * get language entry.
232 *
233 * @param[in] lang language name (@CODE{NULL} means 'not specified'.)
234 * @return language entry
235 */
236 static const struct lang_entry *
237 get_lang_entry(const char *lang)
238 {
239 int i, size = sizeof(lang_switch) / sizeof(struct lang_entry);
240 struct plugin_entry *pent;
241
242 if (lang == NULL)
243 die("get_lang_entry: something is wrong.");
244 /*
245 * Priority 1: locates in the plugin parser list.
246 */
247 STAILQ_FOREACH(pent, &plugin_list, next) {
248 if (strcmp(lang, pent->entry.lang_name) == 0)
249 return &pent->entry;
250 }
251 /*
252 * Priority 2: locates in the built-in parser list.
253 */
254 for (i = 0; i < size; i++)
255 if (!strcmp(lang, lang_switch[i].lang_name))
256 return &lang_switch[i];
257 /*
258 * if specified language not found, it assumes default language, that is C.
259 */
260 return DEFAULT_ENTRY;
261 }
262
263 /**
264 * @par Usage:
265 * @code{.txt}
266 * [gtags.conf]
267 * +----------------------------
268 * |...
269 * |gtags_parser=<pluginspec>
270 * |langmap=<langmap>
271 * @endcode
272 *
273 * 1. Load langmap and pluginspec, and initialize parsers.
274 *
275 * @par
276 * @CODE{parser_init(langmap, plugin_parser);}
277 *
278 * 2. Execute parsers
279 *
280 * @par
281 * @CODE{#parse_file(...);}
282 *
283 * 3. Unload parsers.
284 *
285 * @par
286 * @CODE{parser_exit();}
287 */
288 /**
289 * parser_init: load langmap and shared libraries.
290 *
291 * @param[in] langmap the value of @CODE{langmap=\<langmap\>}
292 * @param[in] pluginspec the value of @CODE{gtags_parser=\<pluginspec\>}
293 */
294 void
295 parser_init(const char *langmap, const char *pluginspec)
296 {
297 /* setup language mapping. */
298 if (langmap == NULL)
299 langmap = DEFAULTLANGMAP;
300 setup_langmap(langmap);
301 langmap_saved = check_strdup(langmap);
302
303 /* load shared objects. */
304 if (pluginspec != NULL)
305 load_plugin_parser(pluginspec);
306
307 /*
308 * This is a hack for FreeBSD.
309 * In the near future, it will be removed.
310 */
311 if (test("r", NOTFUNCTION))
312 load_notfunction(NOTFUNCTION);
313 #ifdef __DJGPP__
314 else if (test("r", DOS_NOTFUNCTION))
315 load_notfunction(DOS_NOTFUNCTION);
316 #endif
317 }
318
319 /**
320 * parser_exit: unload shared libraries.
321 */
322 void
323 parser_exit(void)
324 {
325 unload_plugin_parser();
326 free(langmap_saved);
327 }
328
329 /**
330 * parse_file: select and execute a parser.
331 *
332 * @param[in] path path name
333 * @param[in] flags #PARSER_WARNING: print warning messages
334 * @param[in] put callback routine <br>
335 * each parser use this routine for output
336 * @param[in] arg argument for callback routine
337 */
338 void
339 parse_file(const char *path, int flags, PARSER_CALLBACK put, void *arg)
340 {
341 const char *lang, *suffix;
342 const struct lang_entry *ent;
343 struct parser_param param;
344
345 /* get suffix of the path. */
346 suffix = locatestring(path, ".", MATCH_LAST);
347 if (suffix == NULL)
348 return;
349 lang = decide_lang(suffix);
350 if (lang == NULL)
351 return;
352 /*
353 * Select parser.
354 * If lang == NULL then default parser is selected.
355 */
356 ent = get_lang_entry(lang);
357 if (flags & PARSER_DEBUG) {
358 fprintf(stderr, "File '%s' is handled as follows:\n", path);
359 fprintf(stderr, "\tsuffix: |%s|\n", suffix);
360 fprintf(stderr, "\tlanguage: |%s|\n", lang);
361 fprintf(stderr, "\tparser: |%s|\n", ent->parser_name);
362 fprintf(stderr, "\tlibrary: |%s|\n", ent->lt_dl_name ? ent->lt_dl_name : "builtin library");
363 }
364 /*
365 * call language specific parser.
366 */
367 param.size = sizeof(param);
368 param.flags = flags;
369 param.file = path;
370 param.put = put;
371 param.arg = arg;
372 param.isnotfunction = isnotfunction;
373 param.langmap = langmap_saved;
374 param.die = die;
375 param.warning = warning;
376 param.message = message;
377 ent->parser(¶m);
378 }
379
380 void
381 dbg_print(int level, const char *s)
382 {
383 fprintf(stderr, "[%04d]", lineno);
384 for (; level > 0; level--)
385 fprintf(stderr, " ");
386 fprintf(stderr, "%s\n", s);
387 }
/* */