/* * Copyright (c) 1998, 1999, 2000, 2001, 2002, 2005 * 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 . */ #ifdef HAVE_CONFIG_H #include #endif #include #include #ifdef STDC_HEADERS #include #endif #ifdef HAVE_STRING_H #include #else #include #endif #include "gparam.h" #include "checkalloc.h" #include "conf.h" #include "die.h" #include "env.h" #include "langmap.h" #include "locatestring.h" #include "makepath.h" #include "path.h" #include "strbuf.h" #include "strlimcpy.h" #include "strmake.h" #include "test.h" #include "usable.h" static FILE *fp; static STRBUF *ib; static char *confline; /* * 8 level nested tc= or include= is allowed. */ static int allowed_nest_level = 8; static int opened; static void trim(char *); static const char *readrecord(const char *); static void includelabel(STRBUF *, const char *, int); #ifndef isblank #define isblank(c) ((c) == ' ' || (c) == '\t') #endif /* * trim: trim string. * * : var1=a b : * | * v * :var1=a b : */ static void trim(char *l) { char *f, *b; int colon = 0; /* * delete blanks. */ for (f = b = l; *f; f++) { if (colon && isblank(*f)) continue; colon = 0; if ((*b++ = *f) == ':') colon = 1; } *b = 0; /* * delete duplicate semi colons. */ for (f = b = l; *f;) { if ((*b++ = *f++) == ':') { while (*f == ':') f++; } } *b = 0; } /* * readrecord: read recoed indexed by label. * * i) label label in config file * r) record * * Jobs: * o skip comment. * o append following line. * o format check. */ static const char * readrecord(const char *label) { char *p; int flag = STRBUF_NOCRLF; int count = 0; rewind(fp); while ((p = strbuf_fgets(ib, fp, flag)) != NULL) { count++; /* * ignore \. */ flag &= ~STRBUF_APPEND; if (*p == '#' || *p == '\0') continue; if (strbuf_unputc(ib, '\\')) { flag |= STRBUF_APPEND; continue; } trim(p); for (;;) { const char *candidate; /* * pick up candidate. */ if ((candidate = strmake(p, "|:")) == NULL) die("invalid config file format (line %d).", count); if (!strcmp(label, candidate)) { if (!(p = locatestring(p, ":", MATCH_FIRST))) die("invalid config file format (line %d).", count); return check_strdup(p); } /* * locate next candidate. */ p += strlen(candidate); if (*p == ':') break; else if (*p == '|') p++; else die("invalid config file format (line %d).", count); } } /* * config line not found. */ return NULL; } /* * includelabel: procedure for tc= (or include=) * * o) sb string buffer * i) label record label * i) level nest level for check */ static void includelabel(STRBUF *sb, const char *label, int level) { const char *savep, *p, *q; if (++level > allowed_nest_level) die("nested include= (or tc=) over flow."); if (!(savep = p = readrecord(label))) die("label '%s' not found.", label); while ((q = locatestring(p, ":include=", MATCH_FIRST)) || (q = locatestring(p, ":tc=", MATCH_FIRST))) { STRBUF *inc = strbuf_open(0); strbuf_nputs(sb, p, q - p); q = locatestring(q, "=", MATCH_FIRST) + 1; for (; *q && *q != ':'; q++) strbuf_putc(inc, *q); includelabel(sb, strbuf_value(inc), level); p = q; strbuf_close(inc); } strbuf_puts(sb, p); free((void *)savep); } /* * configpath: get path of configuration file. */ static char * configpath(void) { STATIC_STRBUF(sb); const char *p; strbuf_clear(sb); /* * at first, check environment variable GTAGSCONF. */ if (getenv("GTAGSCONF") != NULL) strbuf_puts(sb, getenv("GTAGSCONF")); /* * if GTAGSCONF not set then check standard config files. */ else if ((p = get_home_directory()) && test("r", makepath(p, GTAGSRC, NULL))) strbuf_puts(sb, makepath(p, GTAGSRC, NULL)); #ifdef __DJGPP__ else if ((p = get_home_directory()) && test("r", makepath(p, DOS_GTAGSRC, NULL))) strbuf_puts(sb, makepath(p, DOS_GTAGSRC, NULL)); #endif else if (test("r", GTAGSCONF)) strbuf_puts(sb, GTAGSCONF); else if (test("r", OLD_GTAGSCONF)) strbuf_puts(sb, OLD_GTAGSCONF); else if (test("r", DEBIANCONF)) strbuf_puts(sb, DEBIANCONF); else if (test("r", OLD_DEBIANCONF)) strbuf_puts(sb, OLD_DEBIANCONF); else return NULL; return strbuf_value(sb); } /* * openconf: load configuration file. * * go) line specified entry */ void openconf(void) { STRBUF *sb; const char *config; extern int vflag; assert(opened == 0); opened = 1; /* * if config file not found then return default value. */ if (!(config = configpath())) { if (vflag) fprintf(stderr, " Using default configuration.\n"); confline = check_strdup(""); } /* * if it is not an absolute path then assumed config value itself. */ else if (!isabspath(config)) { confline = check_strdup(config); if (!locatestring(confline, ":", MATCH_FIRST)) die("GTAGSCONF must be absolute path name."); } /* * else load value from config file. */ else { const char *label; if (test("d", config)) die("config file '%s' is a directory.", config); if (!test("f", config)) die("config file '%s' not found.", config); if (!test("r", config)) die("config file '%s' is not readable.", config); if ((label = getenv("GTAGSLABEL")) == NULL) label = "default"; if (!(fp = fopen(config, "r"))) die("cannot open '%s'.", config); if (vflag) fprintf(stderr, " Using config file '%s'.\n", config); ib = strbuf_open(MAXBUFLEN); sb = strbuf_open(0); includelabel(sb, label, 0); confline = check_strdup(strbuf_value(sb)); strbuf_close(ib); strbuf_close(sb); fclose(fp); } /* * make up lacked variables. */ sb = strbuf_open(0); strbuf_puts(sb, confline); strbuf_unputc(sb, ':'); if (!getconfs("suffixes", NULL)) { STRBUF *tmp = strbuf_open(0); const char *langmap = NULL; /* * Variable 'suffixes' is obsoleted. But it is generated * internally from the value of variable 'langmap'. */ if (getconfs("langmap", tmp)) langmap = strbuf_value(tmp); else langmap = DEFAULTLANGMAP; strbuf_puts(sb, ":suffixes="); make_suffixes(langmap, sb); strbuf_close(tmp); } if (!getconfs("skip", NULL)) { strbuf_puts(sb, ":skip="); strbuf_puts(sb, DEFAULTSKIP); } /* * GTAGS, GRTAGS and GSYMS have no default values but non of them * specified then use default values. * (Otherwise, nothing to do for gtags.) */ if (!getconfs("GTAGS", NULL) && !getconfs("GRTAGS", NULL) && !getconfs("GSYMS", NULL)) { const char *path; /* * usable search in BINDIR at first. */ #if defined(_WIN32) path = "gtags-parser.exe"; #elif defined(__DJGPP__) path = usable("gtags-parser") ? "gtags-parser.exe" : "gtags-~1.exe"; #else path = usable("gtags-parser"); if (!path) path = "gtags-parser"; #endif /* _WIN32 */ strbuf_puts(sb, ":GTAGS="); strbuf_puts(sb, path); strbuf_puts(sb, " -dt %s"); strbuf_puts(sb, ":GRTAGS="); strbuf_puts(sb, path); strbuf_puts(sb, " -dtr %s"); strbuf_puts(sb, ":GSYMS="); strbuf_puts(sb, path); strbuf_puts(sb, " -dts %s"); } strbuf_unputc(sb, ':'); strbuf_putc(sb, ':'); confline = check_strdup(strbuf_value(sb)); strbuf_close(sb); trim(confline); return; } /* * getconfn: get property number * * i) name property name * o) num value (if not NULL) * r) 1: found, 0: not found */ int getconfn(const char *name, int *num) { const char *p; char buf[MAXPROPLEN+1]; if (!opened) openconf(); snprintf(buf, sizeof(buf), ":%s#", name); if ((p = locatestring(confline, buf, MATCH_FIRST)) != NULL) { p += strlen(buf); if (num != NULL) *num = atoi(p); return 1; } return 0; } /* * getconfs: get property string * * i) name property name * o) sb string buffer (if not NULL) * r) 1: found, 0: not found */ int getconfs(const char *name, STRBUF *sb) { const char *p; char buf[MAXPROPLEN+1]; int all = 0; int exist = 0; if (!opened) openconf(); if (!strcmp(name, "suffixes") || !strcmp(name, "skip")) all = 1; snprintf(buf, sizeof(buf), ":%s=", name); p = confline; while ((p = locatestring(p, buf, MATCH_FIRST)) != NULL) { if (exist && sb) strbuf_putc(sb, ','); exist = 1; for (p += strlen(buf); *p && *p != ':'; p++) { if (*p == '\\') /* quoted character */ p++; if (sb) strbuf_putc(sb, *p); } if (!all) break; } /* * If 'bindir' and 'datadir' are not defined then * return system configuration value. */ if (!exist) { if (!strcmp(name, "bindir")) { strbuf_puts(sb, BINDIR); exist = 1; } else if (!strcmp(name, "datadir")) { strbuf_puts(sb, DATADIR); exist = 1; } } return exist; } /* * getconfb: get property bool value * * i) name property name * r) 1: TRUE, 0: FALSE */ int getconfb(const char *name) { char buf[MAXPROPLEN+1]; if (!opened) openconf(); snprintf(buf, sizeof(buf), ":%s:", name); if (locatestring(confline, buf, MATCH_FIRST) != NULL) return 1; return 0; } /* * getconfline: print loaded config entry. */ const char * getconfline(void) { if (!opened) openconf(); return confline; } void closeconf(void) { if (!opened) return; free(confline); confline = NULL; opened = 0; }