/* */
This source file includes following definitions.
- trim
- readrecord
- includelabel
- configpath
- openconf
- getconfn
- getconfs
- getconfb
- getconfline
- closeconf
1 /*
2 * Copyright (c) 1998, 1999, 2000, 2001, 2002, 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 <assert.h>
25 #include <ctype.h>
26 #ifdef STDC_HEADERS
27 #include <stdlib.h>
28 #endif
29 #ifdef HAVE_STRING_H
30 #include <string.h>
31 #else
32 #include <strings.h>
33 #endif
34
35 #include "gparam.h"
36 #include "char.h"
37 #include "checkalloc.h"
38 #include "conf.h"
39 #include "die.h"
40 #include "env.h"
41 #include "langmap.h"
42 #include "locatestring.h"
43 #include "makepath.h"
44 #include "path.h"
45 #include "strbuf.h"
46 #include "strlimcpy.h"
47 #include "strmake.h"
48 #include "test.h"
49 #include "usable.h"
50
51 static FILE *fp;
52 static STRBUF *ib;
53 static char *confline;
54 /**
55 * 32 level nested @CODE{tc=} or @CODE{include=} is allowed.
56 */
57 static int allowed_nest_level = 32;
58 static int opened;
59
60 static void trim(char *);
61 static const char *readrecord(const char *);
62 static void includelabel(STRBUF *, const char *, int);
63
64 #ifndef isblank
65 #define isblank(c) ((c) == ' ' || (c) == '\t')
66 #endif
67
68 /**
69 * trim: trim string.
70 *
71 * @code
72 * : var1=a b :
73 * |
74 * v
75 * :var1=a b :
76 * @endcode
77 */
78 static void
79 trim(char *l)
80 {
81 char *f, *b;
82 int colon = 0;
83
84 /*
85 * delete blanks.
86 */
87 for (f = b = l; *f; f++) {
88 if (colon && isblank(*f))
89 continue;
90 colon = 0;
91 if ((*b++ = *f) == ':')
92 colon = 1;
93 }
94 *b = 0;
95 /*
96 * delete duplicate semi colons.
97 */
98 for (f = b = l; *f;) {
99 if ((*b++ = *f++) == ':') {
100 while (*f == ':')
101 f++;
102 }
103 }
104 *b = 0;
105 }
106 /**
107 * readrecord: read recoed indexed by label.
108 *
109 * @param[in] label label in config file
110 * @return record
111 *
112 * @par Jobs:
113 * - skip comment.
114 * - append following line.
115 * - format check.
116 */
117 static const char *
118 readrecord(const char *label)
119 {
120 char *p;
121 int flag = STRBUF_NOCRLF|STRBUF_SHARPSKIP;
122 int count = 0;
123
124 rewind(fp);
125 while ((p = strbuf_fgets(ib, fp, flag)) != NULL) {
126 count++;
127 /*
128 * ignore \<new line>.
129 */
130 flag &= ~STRBUF_APPEND;
131 if (*p == '\0')
132 continue;
133 if (strbuf_unputc(ib, '\\')) {
134 flag |= STRBUF_APPEND;
135 continue;
136 }
137 trim(p);
138 for (;;) {
139 const char *candidate;
140 /*
141 * pick up candidate.
142 */
143 if ((candidate = strmake(p, "|:")) == NULL)
144 die("invalid config file format (line %d).", count);
145 if (!strcmp(label, candidate)) {
146 if (!(p = locatestring(p, ":", MATCH_FIRST)))
147 die("invalid config file format (line %d).", count);
148 return check_strdup(p);
149 }
150 /*
151 * locate next candidate.
152 */
153 p += strlen(candidate);
154 if (*p == ':')
155 break;
156 else if (*p == '|')
157 p++;
158 else
159 die("invalid config file format (line %d).", count);
160 }
161 }
162 /*
163 * config line not found.
164 */
165 return NULL;
166 }
167 /**
168 * includelabel: procedure for @CODE{tc=} (or @CODE{include=})
169 *
170 * @param[out] sb string buffer
171 * @param[in] label record label
172 * @param[in] level nest level for check
173 */
174 static void
175 includelabel(STRBUF *sb, const char *label, int level)
176 {
177 const char *savep, *p, *q;
178
179 if (++level > allowed_nest_level)
180 die("nested include= (or tc=) over flow.");
181 if (!(savep = p = readrecord(label)))
182 die("label '%s' not found.", label);
183 while ((q = locatestring(p, ":include=", MATCH_FIRST)) || (q = locatestring(p, ":tc=", MATCH_FIRST))) {
184 STRBUF *inc = strbuf_open(0);
185
186 strbuf_nputs(sb, p, q - p);
187 q = locatestring(q, "=", MATCH_FIRST) + 1;
188 for (; *q && *q != ':'; q++)
189 strbuf_putc(inc, *q);
190 includelabel(sb, strbuf_value(inc), level);
191 p = q;
192 strbuf_close(inc);
193 }
194 strbuf_puts(sb, p);
195 free((void *)savep);
196 }
197 /**
198 * configpath: get path of configuration file.
199 */
200 static char *
201 configpath(void)
202 {
203 STATIC_STRBUF(sb);
204 const char *p;
205
206 strbuf_clear(sb);
207 /*
208 * at first, check environment variable GTAGSCONF.
209 */
210 if (getenv("GTAGSCONF") != NULL)
211 strbuf_puts(sb, getenv("GTAGSCONF"));
212 /*
213 * if GTAGSCONF not set then check standard config files.
214 */
215 else if ((p = get_home_directory()) && test("r", makepath(p, GTAGSRC, NULL)))
216 strbuf_puts(sb, makepath(p, GTAGSRC, NULL));
217 #ifdef __DJGPP__
218 else if ((p = get_home_directory()) && test("r", makepath(p, DOS_GTAGSRC, NULL)))
219 strbuf_puts(sb, makepath(p, DOS_GTAGSRC, NULL));
220 #endif
221 else if (test("r", GTAGSCONF))
222 strbuf_puts(sb, GTAGSCONF);
223 else if (test("r", OLD_GTAGSCONF))
224 strbuf_puts(sb, OLD_GTAGSCONF);
225 else if (test("r", DEBIANCONF))
226 strbuf_puts(sb, DEBIANCONF);
227 else if (test("r", OLD_DEBIANCONF))
228 strbuf_puts(sb, OLD_DEBIANCONF);
229 else if (test("r", makepath(SYSCONFDIR, "gtags.conf", NULL)))
230 strbuf_puts(sb, makepath(SYSCONFDIR, "gtags.conf", NULL));
231 else
232 return NULL;
233 return strbuf_value(sb);
234 }
235 /**
236 * openconf: load configuration file.
237 *
238 * @par Globals used (output):
239 * #confline: specified entry
240 */
241 void
242 openconf(void)
243 {
244 STRBUF *sb;
245 const char *config;
246 extern int vflag;
247
248 assert(opened == 0);
249 opened = 1;
250
251 /*
252 * if config file not found then return default value.
253 */
254 if (!(config = configpath())) {
255 if (vflag)
256 fprintf(stderr, " Using default configuration.\n");
257 confline = check_strdup("");
258 }
259 /*
260 * if it is not an absolute path then assumed config value itself.
261 */
262 else if (!isabspath(config)) {
263 confline = check_strdup(config);
264 if (!locatestring(confline, ":", MATCH_FIRST))
265 die("GTAGSCONF must be absolute path name.");
266 }
267 /*
268 * else load value from config file.
269 */
270 else {
271 const char *label;
272
273 if (test("d", config))
274 die("config file '%s' is a directory.", config);
275 if (!test("f", config))
276 die("config file '%s' not found.", config);
277 if (!test("r", config))
278 die("config file '%s' is not readable.", config);
279 if ((label = getenv("GTAGSLABEL")) == NULL)
280 label = "default";
281
282 if (!(fp = fopen(config, "r")))
283 die("cannot open '%s'.", config);
284 if (vflag)
285 fprintf(stderr, " Using config file '%s'.\n", config);
286 ib = strbuf_open(MAXBUFLEN);
287 sb = strbuf_open(0);
288 includelabel(sb, label, 0);
289 confline = check_strdup(strbuf_value(sb));
290 strbuf_close(ib);
291 strbuf_close(sb);
292 fclose(fp);
293 }
294 /*
295 * make up required variables.
296 */
297 sb = strbuf_open(0);
298 strbuf_puts(sb, confline);
299 strbuf_unputc(sb, ':');
300
301 if (!getconfs("langmap", NULL)) {
302 strbuf_puts(sb, ":langmap=");
303 strbuf_puts(sb, quote_chars(DEFAULTLANGMAP, ':'));
304 }
305 if (!getconfs("skip", NULL)) {
306 strbuf_puts(sb, ":skip=");
307 strbuf_puts(sb, DEFAULTSKIP);
308 }
309 strbuf_unputc(sb, ':');
310 strbuf_putc(sb, ':');
311 confline = check_strdup(strbuf_value(sb));
312 strbuf_close(sb);
313 trim(confline);
314 return;
315 }
316 /**
317 * getconfn: get property number
318 *
319 * @param[in] name property name
320 * @param[out] num value (if not @VAR{NULL})
321 * @return 1: found, 0: not found
322 */
323 int
324 getconfn(const char *name, int *num)
325 {
326 const char *p;
327 char buf[MAXPROPLEN];
328
329 if (!opened)
330 openconf();
331 snprintf(buf, sizeof(buf), ":%s#", name);
332 if ((p = locatestring(confline, buf, MATCH_FIRST)) != NULL) {
333 p += strlen(buf);
334 if (num != NULL)
335 *num = atoi(p);
336 return 1;
337 }
338 return 0;
339 }
340 /**
341 * getconfs: get property string
342 *
343 * @param[in] name property name
344 * @param[out] sb string buffer (if not @VAR{NULL})
345 * @return 1: found, 0: not found
346 */
347 int
348 getconfs(const char *name, STRBUF *sb)
349 {
350 const char *p;
351 char buf[MAXPROPLEN];
352 int all = 0;
353 int exist = 0;
354
355 if (!opened)
356 openconf();
357 if (!strcmp(name, "skip") || !strcmp(name, "gtags_parser") || !strcmp(name, "langmap"))
358 all = 1;
359 snprintf(buf, sizeof(buf), ":%s=", name);
360 p = confline;
361 while ((p = locatestring(p, buf, MATCH_FIRST)) != NULL) {
362 if (exist && sb)
363 strbuf_putc(sb, ',');
364 exist = 1;
365 for (p += strlen(buf); *p && *p != ':'; p++) {
366 if (*p == '\\') /* quoted character */
367 p++;
368 if (sb)
369 strbuf_putc(sb, *p);
370 }
371 if (!all)
372 break;
373 }
374 /*
375 * If 'bindir' and 'datadir' are not defined then
376 * return system configuration value.
377 */
378 if (!exist) {
379 if (!strcmp(name, "bindir")) {
380 strbuf_puts(sb, BINDIR);
381 exist = 1;
382 } else if (!strcmp(name, "datadir")) {
383 #if defined(_WIN32) && !defined(__CYGWIN__)
384 /*
385 * Test if this directory exists, and if not, take the
386 * directory relative to the binary.
387 */
388 if (test("d", DATADIR))
389 strbuf_puts(sb, DATADIR);
390 else {
391 const char *name = strrchr(_pgmptr, '\\');
392 if (name)
393 strbuf_nputs(sb, _pgmptr, name+1 - _pgmptr);
394 strbuf_puts(sb, "..\\share");
395 }
396 #else
397 strbuf_puts(sb, DATADIR);
398 #endif
399 exist = 1;
400 } else if (!strcmp(name, "localstatedir")) {
401 strbuf_puts(sb, LOCALSTATEDIR);
402 exist = 1;
403 } else if (!strcmp(name, "sysconfdir")) {
404 strbuf_puts(sb, SYSCONFDIR);
405 exist = 1;
406 }
407 }
408 return exist;
409 }
410 /**
411 * getconfb: get property bool value
412 *
413 * @param[in] name property name
414 * @return 1: @VAR{TRUE}, 0: @VAR{FALSE}
415 */
416 int
417 getconfb(const char *name)
418 {
419 char buf[MAXPROPLEN];
420
421 if (!opened)
422 openconf();
423 snprintf(buf, sizeof(buf), ":%s:", name);
424 if (locatestring(confline, buf, MATCH_FIRST) != NULL)
425 return 1;
426 return 0;
427 }
428 /**
429 * getconfline: print loaded config entry.
430 */
431 const char *
432 getconfline(void)
433 {
434 if (!opened)
435 openconf();
436 return confline;
437 }
438 void
439 closeconf(void)
440 {
441 if (!opened)
442 return;
443 free(confline);
444 confline = NULL;
445 opened = 0;
446 }
/* */