/* BLURB lgpl
Coda File System
Release 6
Copyright (c) 1987-2003 Carnegie Mellon University
Additional copyrights listed below
This code is distributed "AS IS" without warranty of any kind under
the terms of the GNU Library General Public Licence Version 2, as
shown in the file LICENSE. The technical and financial contributors to
Coda are listed in the file CREDITS.
Additional copyrights
none currently
#*/
#include <sys/param.h>
#include <stdio.h>
#include "coda_string.h"
#include <assert.h>
#include <stdlib.h>
#include <unistd.h>
#include "codaconf.h"
#include "coda_config.h"
/* some hardcoded options, probably both undef'd for normal use */
/* CONFDEBUG: annoying debugging related messages */
#undef CONFDEBUG
#undef CONFWRITE
/* default configuration file search path used by codaconf_init */
static const char *default_codaconfpath =
SYSCONFDIR ":/usr/local/etc/coda:/etc/coda:";
/* buffer to read lines of config data */
#define MAXLINELEN 256
static char line[MAXLINELEN];
static char conffile[MAXPATHLEN+1];
/* this global is exported to surpress codaconf_init_one verbosity */
int codaconf_quiet = 0;
/* nobody outside of this file needs to be exposed to these structures. */
typedef char *string_t;
typedef struct _item {
struct _item *next;
string_t name;
string_t value;
} *item_t;
static item_t codaconf_table = NULL;
/* Add a name=value pair to the codaconf_table. */
/* The passed name and value strings are copied. */
static item_t codaconf_add(string_t name, string_t value)
{
item_t n;
n = (item_t)malloc(sizeof(struct _item));
assert(n != NULL);
n->name = strdup(name);
assert(n->name != NULL);
n->value = strdup(value);
assert(n->value != NULL);
n->next = codaconf_table;
codaconf_table = n;
return(n);
}
/* Return the value associated with a name. */
/* If value is specified and replace is true and the found entry is
* replaced with the new value. If a value is given, the entry is not
* found, and CONFWRITE is defined, the name=value pair is appended
* to the last read configuration file. */
static item_t codaconf_find(string_t name, string_t value, int replace)
{
item_t cp;
#ifdef CONFWRITE
FILE *conf;
#endif
for(cp = codaconf_table; cp; cp = cp->next) {
if (strcmp(name, cp->name) == 0) {
if (replace && value) {
free(cp->value);
cp->value = strdup(value);
assert(cp->value != NULL);
}
return(cp);
}
}
if (!value) return(NULL);
#ifdef CONFWRITE
/* append the new value to the last read configuration file, but only if
* we are being called from codaconf_lookup (i.e. replace is false) and a
* default value was given */
if (!replace) {
conf = fopen(conffile, "a");
if (conf) {
fputs(name, conf);
fputs("=\"", conf);
fputs(value, conf);
fputs("\"\n", conf);
fclose(conf);
}
}
#endif
return(codaconf_add(name, value));
}
/* parse a configuration line */
static void codaconf_parse_line(char *line, int lineno,
string_t *name, string_t *value)
{
char *eon, *eov, *val;
*name = *value = NULL;
/* skip blanks at the beginning of the line */
while (*line == ' ' || *line == '\t') line++;
/* ignore comments and empty lines */
if (*line == '#' || *line == '\0' || *line == '\n') return;
/* find the beginning of the value */
eon = val = strchr(line, '=');
if (!eon) {
fprintf(stderr, "Configuration error in line %d, "
"missing '='.\n", lineno);
return;
}
/* strip trailing blanks from the name */
eon--; while (*eon == ' ' || *eon == '\t') eon--;
eon[1] = '\0';
/* blanks before the value are an error (for bash, so also for us) */
val++;
if (*val == ' ' || *val == '\t') {
fprintf(stderr, "Configuration error in line %d, "
"no blanks allowed after the '='.\n", lineno);
return;
}
/* sort of handle quoting */
if (*val == '"' || *val == '\'') val++;
/* find the end of the line */
eov = val; while (*eov && *eov != '\n') eov++;
/* strip trailing blanks from the value */
eov--; while (*eov == ' ' || *eov == '\t') eov--;
/* sort of handle quoting */
if (*eov == '"' || *eov == '\'') eov--;
eov[1] = '\0';
/* got all the pointers, now allocate the string_t's */
*name = strdup(line);
assert(*name != NULL);
*value = strdup(val);
assert(*value != NULL);
}
/* codaconf_init_one reads (or merges) the name=value tuples from the conffile.
* If a name is seen multiple times, only the last value is remembered. Empty
* lines and lines starting with '#' are ignored. */
int codaconf_init_one(char *cf)
{
FILE *conf;
int lineno = 0;
string_t name, value;
item_t item;
conf = fopen(cf, "r");
if (!conf) {
if (!codaconf_quiet)
fprintf(stderr, "Cannot read configuration file '%s', "
"will use default values.\n", cf);
return(-1);
}
/* remember the last read configuration file */
if (cf != conffile)
strcpy(conffile, cf);
while(fgets(line, MAXLINELEN, conf)) {
lineno++;
codaconf_parse_line(line, lineno, &name, &value);
if (name == NULL) continue; /* skip comments and blank lines */
item = codaconf_find(name, value, 1);
#ifdef CONFDEBUG
printf("line: %d, name: '%s', value: '%s'", lineno, name, value);
if (item)
printf("stored-value: '%s'\n", item->value);
else
printf("not found?\n");
#endif
free(name); free(value);
}
fclose(conf);
return(0);
}
/* codaconf_file searches all directories specified by the environment variable
* CODACONFPATH for 'confname'
*
* If the CODACONFPATH is not present the search defaults to,
* @sysconfdir@:/usr/local/etc/coda:/etc/coda
*/
char *codaconf_file(const char *confname)
{
const char *codaconfpath, *end;
int pathlen, filelen = strlen(confname);
codaconfpath = getenv("CODACONFPATH");
if (!codaconfpath)
codaconfpath = default_codaconfpath;
while(1) {
end = strchr(codaconfpath, ':');
if (!end) pathlen = strlen(codaconfpath);
else pathlen = end - codaconfpath;
/* don't overflow the buffer */
if ((pathlen + filelen + 1) <= MAXPATHLEN) {
memcpy(conffile, codaconfpath, pathlen);
/* don't append an additional one if the path ends in a '/' */
if (conffile[pathlen-1] != '/')
conffile[pathlen++] = '/';
strcpy(conffile + pathlen, confname);
/* we should be done as soon as we find a readable file */
if (access(conffile, R_OK) == 0)
return conffile;
}
if (!end) break;
codaconfpath = end + 1;
}
return NULL;
}
/* codaconf_init tries to load the first file that matches 'confname' in
* CODACONFPATH */
int codaconf_init(const char *confname)
{
char *cf = codaconf_file(confname);
if (!cf || codaconf_init_one(cf) != 0)
return -1;
return 0;
}
/* codaconf_lookup returns the value associated with name, or NULL on error. */
char *codaconf_lookup(char *name, char *defaultvalue)
{
item_t cp;
cp = codaconf_find(name, defaultvalue, 0);
return cp ? cp->value : NULL;
}
/* release all allocated resources */
void codaconf_free(void)
{
item_t cp;
while((cp = codaconf_table) != NULL) {
codaconf_table = cp->next;
free(cp->name);
free(cp->value);
free(cp);
}
}
#ifdef TESTCONF
char configpath[MAXPATHLEN]=TESTCONF;
int main(int argc, char *argv[])
{
char *var, *val = NULL;
if (argc < 2) {
fprintf(stderr, "Usage: %s <conffile>.<confvar> [defaultvalue]\n",
argv[0]);
fprintf(stderr, "e.g. %s venus.cachesize\n", argv[0]);
exit(-1);
}
var = strchr(argv[1], '.');
if (!var) {
fprintf(stderr, "Didn't find the '.' separator\n");
exit(-1);
}
*(var++) = '\0';
if (argc == 3) val = argv[2];
strcat(configpath, argv[1]);
strcat(configpath, ".conf");
codaconf_init(configpath);
val = codaconf_lookup(var, val);
codaconf_free();
if (!val) {
fprintf(stderr, "Couldn't find a value for '%s'\n", var);
exit(-2);
}
fputs(val, stdout);
fputc('\n', stdout);
exit(0);
}
#endif
syntax highlighted by Code2HTML, v. 0.9.1