/* This file is part of GSC
Copyright (C) 2007 Sergey Poznyakoff
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
#include
#include
#include
#include
#include
#include "xalloc.h"
#include "gsc.h"
/* Configuration file handling (general) */
static gsc_config_fun
find_cfg_fun (struct gsc_config_keyword *kwtab, char *kw)
{
for (; kwtab->kw; kwtab++)
if (strcmp (kwtab->kw, kw) == 0)
break;
return kwtab->handler;
}
ssize_t
read_cont_line (gsc_config_file_t *file)
{
ssize_t rc;
ssize_t len = rc;
len = getline (&file->buf, &file->size, file->fp);
file->line++;
if (len <= 0)
return len;
if (file->buf[len-1] != '\n')
return len;
file->buf[--len] = 0;
if (len > 0 && file->buf[len-1] == '\\')
{
size_t newsize = file->size;
char *newbuf = x2realloc (NULL, &newsize);
strcpy (newbuf, file->buf);
do
{
rc = getline (&file->buf, &file->size, file->fp);
if (rc <= 0)
break;
file->line++;
if (file->buf[rc-1] == '\n')
file->buf[--rc] = 0;
newbuf[--len] = 0;
len += rc;
if (len + 1 > newsize)
{
newsize = len + 1;
newbuf = x2realloc (newbuf, &newsize);
}
strcat (newbuf, file->buf);
}
while (rc > 0 && file->buf[rc-1] == '\\');
free (file->buf);
file->buf = newbuf;
file->size = newsize;
}
return len + 1;
}
void
gsc_config_parse_block (gsc_config_file_t *file, void *data,
struct gsc_config_keyword *kwtab, const char *end)
{
while (read_cont_line (file) > 0)
{
size_t len;
char *p, *kw, *val = NULL;
gsc_config_fun fun;
for (p = file->buf; *p && isspace (*p); p++)
;
if (*p == '#' || *p == 0)
continue;
len = strlen (p);
for (;len > 0 && isspace (p[len-1]); len--)
;
if (len > 0)
p[len] = 0;
else
continue;
kw = p;
for (; *p && !isspace (*p); p++)
;
if (*p)
{
*p++ = 0;
for (; *p && isspace (*p); p++)
;
val = p;
}
if (end && val == NULL && strcmp (kw, end) == 0)
break;
fun = find_cfg_fun (kwtab, kw);
if (fun)
{
file->kwtab = kwtab;
fun (file, kw, val, data);
}
else
file->error_msg (file->file_name, file->line, "unrecognized line");
}
}
static void
default_error_msg (const char *name, unsigned line, const char *fmt, ...)
{
va_list ap;
fprintf (stderr, "%s:%u: ", name, line);
va_start (ap, fmt);
vfprintf (stderr, fmt, ap);
va_end (ap);
fputc ('\n', stderr);
}
gsc_config_status_t
gsc_config_parse (const char *config_file,
void (*errmsg) (const char *, unsigned, const char *, ...),
struct gsc_config_keyword *kwh)
{
gsc_config_file_t file;
struct stat st;
if (stat (config_file, &st))
return gsc_config_open;
file.fp = fopen (config_file, "r");
if (!file.fp)
return gsc_config_open;
file.buf = NULL;
file.size = 0;
file.line = 0;
file.ino = st.st_ino;
file.dev = st.st_dev;
file.prev = NULL;
file.file_name = config_file;
file.error_msg = errmsg ? errmsg : default_error_msg;
file.error_count = 0;
gsc_config_parse_block (&file, NULL, kwh, NULL);
free (file.buf);
fclose (file.fp);
return file.error_count ? gsc_config_error : gsc_config_success;
}