File: parser.c

package info (click to toggle)
global 6.6.14-1.1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 12,612 kB
  • sloc: ansic: 132,180; sh: 5,017; javascript: 4,891; perl: 811; lisp: 676; makefile: 340; yacc: 122
file content (483 lines) | stat: -rw-r--r-- 12,186 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
/*
 * Copyright (c) 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2010, 2011,
 *	2015
 *	Tama Communications Corporation
 *
 * This file is part of GNU GLOBAL.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <stdio.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#endif
#include <ltdl.h>
#if defined(_WIN32) && !defined(__CYGWIN__)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#undef SLIST_ENTRY
#endif

#include "parser.h"
#include "internal.h"
#include "checkalloc.h"
#include "conf.h"
#include "die.h"
#include "gtagsop.h"
#include "langmap.h"
#include "locatestring.h"
#include "path.h"
#include "queue.h"
#include "strbuf.h"
#include "strmake.h"
#include "test.h"

#define NOTFUNCTION	".notfunction"
#ifdef __DJGPP__
#define DOS_NOTFUNCTION	"_notfunction"
#endif

struct words {
	const char *name;
};
static struct words *words;
static int tablesize;

static int
cmp(const void *s1, const void *s2)
{
	return strcmp(((struct words *)s1)->name, ((struct words *)s2)->name);
}

static void
load_notfunction(const char *filename)
{
	FILE *ip;
	STRBUF *sb = strbuf_open(0);
	STRBUF *ib = strbuf_open(0);
	char *p;
	int i;

	if ((ip = fopen(filename, "r")) == NULL)
		die("'%s' cannot read.", filename);
	for (tablesize = 0; (p = strbuf_fgets(ib, ip, STRBUF_NOCRLF)) != NULL; tablesize++) {
		if (*p == ';')		/* Lines starting with ';' are comment lines. */
			continue;
		strbuf_puts0(sb, p);
	}
	fclose(ip);
	words = (struct words *)check_malloc(sizeof(struct words) * tablesize);
	/*
	 * Don't free *p.
	 */
	p = (char *)check_malloc(strbuf_getlen(sb) + 1);
	memcpy(p, strbuf_value(sb), strbuf_getlen(sb) + 1);
	for (i = 0; i < tablesize; i++) {
		words[i].name = p;
		p += strlen(p) + 1;
	}
	qsort(words, tablesize, sizeof(struct words), cmp);
	strbuf_close(sb);
	strbuf_close(ib);
}

static int
isnotfunction(const char *name)
{
	struct words tmp;
	struct words *result;

	if (words == NULL)
		return 0;
	tmp.name = name;
	result = (struct words *)bsearch(&tmp, words, tablesize, sizeof(struct words), cmp);
	return (result != NULL) ? 1 : 0;
}

/*----------------------------------------------------------------------*/
/* Parser switch                                                        */
/*----------------------------------------------------------------------*/
/**
 * This is the linkage section of each parsers.
 * If you want to support new language, you must define parser procedure
 * which requires file name as an argument.
 */
struct lang_entry {
	const char *lang_name;
	PARSER parser;					/**< parser procedure */
	const char *parser_name;
	const char *lt_dl_name;
};

struct plugin_entry {
	STAILQ_ENTRY(plugin_entry) next;
	lt_dlhandle handle;
	struct lang_entry entry;
};

static STAILQ_HEAD(plugin_list, plugin_entry)
	plugin_list = STAILQ_HEAD_INITIALIZER(plugin_list);
static char *langmap_saved, *pluginspec_saved;

/**
 * load_plugin_parser: Load plug-in parsers.
 *
 *	@param[in]	pluginspec	described below
 *
 * Syntax:
 *   <pluginspec> ::= <map> | <map>","<pluginspec>
 *   <map>        ::= <language name>":"<shared object path>
 *                  | <language name>":"<shared object path>":"<function name>
 */
static void
load_plugin_parser(const char *pluginspec)
{
	char *p, *q;
	const char *lt_dl_name, *parser_name;
	struct plugin_entry *pent;

	pluginspec_saved = check_strdup(pluginspec);
	if (lt_dlinit() != 0)
		die("cannot initialize libltdl.");
	p = pluginspec_saved;
	while (*p != '\0') {
		pent = check_malloc(sizeof(*pent));
		pent->entry.lang_name = p;
		p = strchr(p, ':');
		if (p == NULL)
			die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
		*p++ = '\0';
		if (*p == '\0')
			die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
		lt_dl_name = p;
		p = strchr(p, ',');
		if (p != NULL)
			*p++ = '\0';
		q = strchr(lt_dl_name, ':');
#ifdef _WIN32
		/* Assume a single-character name is a drive letter. */
		if (q == lt_dl_name + 1)
			q = strchr(q + 1, ':');
#else
		/* skip quoted ':' */
		while (q && *q == ':' && *(q - 1) == '\\') {
			q = strchr(q + 1, ':');
		}
#endif
		if (q == NULL) {
			parser_name = "parser";
		} else {
			*q++ = '\0';
			if (*q == '\0')
				die_with_code(2, "syntax error in pluginspec '%s'.", pluginspec);
			parser_name = q;
		}
#ifndef _WIN32
		strremovechar((char *)lt_dl_name, '\\');
#endif
#if defined(_WIN32) && !defined(__CYGWIN__)
		/* Bypass libtool and load the DLL directly, relative to us. */
		pent->handle = (lt_dlhandle)LoadLibrary(lt_dl_name);
		if (pent->handle == NULL) {
			q = strrchr(lt_dl_name, '/');
			if (q == NULL)
				q = strrchr(lt_dl_name, '\\');
			if (q != NULL)
				lt_dl_name = q + 1;
			q = strrchr(lt_dl_name, '.');
			if (q != NULL && strcmp(q, ".la") == 0)
				*q = '\0';
			pent->handle = (lt_dlhandle)LoadLibrary(lt_dl_name);
			if (pent->handle == NULL) {
				char dll[MAX_PATH*2];
				GetModuleFileName(NULL, dll, MAX_PATH);
				q = strrchr(dll, '\\');
				sprintf(q+1, "..\\lib\\gtags\\%s", lt_dl_name);
				pent->handle = (lt_dlhandle)LoadLibrary(dll);
			}
		}
		if (pent->handle == NULL)
			die_with_code(2, "cannot open shared object '%s'.", lt_dl_name);
		pent->entry.lt_dl_name = lt_dl_name;
		pent->entry.parser = (PVOID)GetProcAddress((HINSTANCE)pent->handle, parser_name);
#else
		pent->handle = lt_dlopen(lt_dl_name);
		if (pent->handle == NULL) {
			/*
			 * Retry after removing the extension, because some packages
			 * don't have '.la' files.
			 */
			q = strrchr(lt_dl_name, '.');
			if (q)
				*q = '\0';
			pent->handle = lt_dlopenext(lt_dl_name);
			if (pent->handle == NULL)
				die_with_code(2, "cannot open shared object '%s'.", lt_dl_name);
		}
		pent->entry.lt_dl_name = lt_dl_name;
		pent->entry.parser = lt_dlsym(pent->handle, parser_name);
#endif
		if (pent->entry.parser == NULL)
			die_with_code(2, "cannot find symbol '%s' in '%s'.", parser_name, lt_dl_name);
		pent->entry.parser_name = parser_name;
		STAILQ_INSERT_TAIL(&plugin_list, pent, next);
		if (p == NULL)
			break;
	}
}

/**
 * unload_plugin_parser: Unload plug-in parsers.
 */
static void
unload_plugin_parser(void)
{
	struct plugin_entry *pent;

	if (pluginspec_saved == NULL)
		return;
	while (!STAILQ_EMPTY(&plugin_list)) {
		pent = STAILQ_FIRST(&plugin_list);
#if defined(_WIN32) && !defined(__CYGWIN__)
		FreeLibrary((HMODULE)pent->handle);
#else
		lt_dlclose(pent->handle);
#endif
		STAILQ_REMOVE_HEAD(&plugin_list, next);
		free(pent);
	}
	lt_dlexit();
	free(pluginspec_saved);
}

/**
 * The first entry is default language.
 */
static const struct lang_entry lang_switch[] = {
	/* lang_name    parser_proc	parser_name	lt_dl_name,	*/
	/*				(for debug)	(for debug)	*/
	{"c",		C,		"C",		"built-in"},	/* DEFAULT */
	{"yacc",	yacc,		"yacc",		"built-in"},
	{"cpp",		Cpp,		"Cpp",		"built-in"},
	{"java",	java,		"java",		"built-in"},
	{"php",		php,		"php",		"built-in"},
	{"asm",		assembly,	"assembly",	"built-in"}
};
#define DEFAULT_ENTRY &lang_switch[0]
/**
 * get language entry.
 *
 *      @param[in]      lang    language name (NULL means 'not specified'.)
 *      @return              language entry
 */
static const struct lang_entry *
get_lang_entry(const char *lang)
{
	int i, size = sizeof(lang_switch) / sizeof(struct lang_entry);
	struct plugin_entry *pent;

	if (lang == NULL)
		die("get_lang_entry: something is wrong.");
	/*
	 * Priority 1: locates in the plugin parser list.
	 */
	STAILQ_FOREACH(pent, &plugin_list, next) {
		if (strcmp(lang, pent->entry.lang_name) == 0)
			return &pent->entry;
	}
	/*
	 * Priority 2: locates in the built-in parser list.
	 */
	for (i = 0; i < size; i++)
		if (!strcmp(lang, lang_switch[i].lang_name))
			return &lang_switch[i];
	/*
	 * if specified language not found, it assumes default language, that is C.
	 */
	warning("'%s' parser not found. C parser is used instead.", lang);
	return DEFAULT_ENTRY;
}

/**
 * Usage:
 * [gtags.conf]
 * +----------------------------
 * |...
 * |gtags_parser=<pluginspec>
 * |langmap=<langmap>
 *
 * 1. Load langmap and pluginspec, and initialize parsers.
 *
 *	parser_init(langmap, plugin_parser);
 *
 * 2. Execute parsers
 *
 *	parse_file(...);
 *
 * 3. Unload parsers.
 *
 *	parser_exit();
 */
/*
 * parser_init: load langmap and shared libraries.
 *
 *	@param[in]	langmap		the value of langmap=<langmap>
 *	@param[in]	pluginspec	the value of gtags_parser=<pluginspec>
 */
void
parser_init(const char *langmap, const char *pluginspec)
{
	/* setup language mapping. */
	if (langmap == NULL)
		langmap = DEFAULTLANGMAP;
	langmap = trim_langmap(langmap);
	setup_langmap(langmap);
	langmap_saved = check_strdup(langmap);

	/* load shared objects. */
	if (pluginspec != NULL)
		load_plugin_parser(pluginspec);

	/*
	 * This is a hack for FreeBSD.
	 * In the near future, it will be removed.
	 */
	if (test("r", NOTFUNCTION))
		load_notfunction(NOTFUNCTION);
#ifdef __DJGPP__
	else if (test("r", DOS_NOTFUNCTION))
		load_notfunction(DOS_NOTFUNCTION);
#endif
}

/**
 * parser_exit: unload shared libraries.
 */
void
parser_exit(void)
{
	unload_plugin_parser();
	free(langmap_saved);
}

/**
 * getconf: return the value of the config variable by name.
 */
char *
getconf(const char *name)
{
	int num = 0;
	STATIC_STRBUF(sb);

	strbuf_clear(sb);
	if (getconfs(name, sb))
		;
	else if (getconfn(name, &num))
		strbuf_putn(sb, num);
	else if (getconfb(name))
		strbuf_putn(sb, 1);
	else
		return NULL;
	return check_strdup(strbuf_value(sb));
}
/**
 * parse_file: select and execute a parser.
 *
 *	@param[in]	path	path name
 *	@param[in]	flags	PARSER_WARNING: print warning messages
 *	@param[in]	put	callback routine,
 *			each parser use this routine for output
 *	@param[in]	arg	argument for callback routine
 */
void
parse_file(const char *path, int flags, PARSER_CALLBACK put, void *arg)
{
	const struct lang_entry *ent = get_parser(path);
	if (ent) {
		if (flags & PARSER_EXPLAIN)
			fputs(get_explain(path, ent), stderr);
		execute_parser(ent, path, flags, put, arg);
	}
}
/**
 * get_parser: get a parser entry from a path.
 */
const struct lang_entry *
get_parser(const char *path)
{
	const char *lang = decide_lang_path(path);
	if (lang == NULL)
		return NULL;
	/*
	 * Select parser.
	 * If lang == NULL then default parser is selected.
	 */
	return get_lang_entry(lang);
}
/**
 * execute_parser: execute parser.
 */
void
execute_parser(const struct lang_entry *ent, const char *path, int flags, PARSER_CALLBACK put, void *arg)
{
	struct parser_param param;
	/*
	 * call language specific parser.
	 */
	param.size = sizeof(param);
	param.flags = flags;
	param.file = path;
	param.put = put;
	param.arg = arg;
	param.isnotfunction = isnotfunction;
	param.langmap = langmap_saved;
	param.getconf = getconf;
	param.die = die;
	param.warning = warning;
	param.message = message;
	ent->parser(&param);
}
/**
 * get_explain: get explain messages.
 */
const char *
get_explain(const char *path, const struct lang_entry *ent)
{
	STATIC_STRBUF(sb);
	strbuf_clear(sb);

	strbuf_sprintf(sb, " - File '%s' is handled as follows:\n", trimpath(path));
	strbuf_sprintf(sb, "\tsuffix:   |%s|\n", get_last_match());
	strbuf_sprintf(sb, "\tlanguage: |%s|\n", ent->lang_name);
	strbuf_sprintf(sb, "\tparser:   |%s|\n", ent->parser_name);
	strbuf_sprintf(sb, "\tlibrary:  |%s|\n", ent->lt_dl_name ? ent->lt_dl_name : "builtin library");
	return strbuf_value(sb);
}
void
dbg_print(int level, const char *s)
{
	fprintf(stderr, "[%04d]", lineno);
	for (; level > 0; level--)
		fprintf(stderr, "    ");
	fprintf(stderr, "%s\n", s);
}