/*
* 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 <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <assert.h>
#include <ctype.h>
#ifdef STDC_HEADERS
#include <stdlib.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#else
#include <strings.h>
#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 \<new line>.
*/
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;
}
syntax highlighted by Code2HTML, v. 0.9.1