/*
* Copyright (c) 2003-2017 Hypertriton, Inc.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
/*
* Simple file-based table implementation.
*/
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include "mailprocd.h"
#define TBL_FIELD_SEP " \t"
/* Initialize a table. */
void
TBL_Init(TBL *t, const char *path, int nbuckets)
{
int i;
if ((t->path = strdup(path)) == NULL) {
Fatal("Out of memory");
}
t->nbuckets = nbuckets;
t->buckets = Malloc(nbuckets*sizeof(TBL_Bucket));
for (i = 0; i < nbuckets; i++) {
SLIST_INIT(&t->buckets[i].ents);
}
SLIST_INIT(&t->ents);
}
/* Release the resources allocated by a table. */
void
TBL_Destroy(TBL *t)
{
TBL_Entry *ent, *nent;
int i;
for (i = 0; i < t->nbuckets; i++) {
for (ent = SLIST_FIRST(&t->buckets[i].ents);
ent != SLIST_END(&t->buckets[i].ents);
ent = nent) {
nent = SLIST_NEXT(ent, bents);
free(ent->key);
free(ent->data);
free(ent);
}
}
free(t->buckets);
free(t->path);
free(t);
}
/* Insert a new table entry. */
int
TBL_Insert(TBL *ta, TBL_Entry *ent)
{
Uint h = TBL_Hash(ta,ent->key);
if (TBL_LookupHash(ta, ent->key, h) != NULL) {
MPD_SetError("Failed to insert %s (exists)", ent->key);
return (-1);
}
SLIST_INSERT_HEAD(&ta->buckets[h].ents, ent, bents);
SLIST_INSERT_HEAD(&ta->ents, ent, ents);
ta->buckets[h].nents++;
return (0);
}
/* Remove a named table entry. */
int
TBL_Delete(TBL *ta, const char *key)
{
Uint h = TBL_Hash(ta,key);
TBL_Entry *ent;
if ((ent = TBL_LookupHash(ta, key, h)) == NULL) {
MPD_SetError("Failed to delete %s (not found)", key);
return (-1);
}
SLIST_REMOVE(&ta->buckets[h].ents, ent, tbl_entry, bents);
SLIST_REMOVE(&ta->ents, ent, tbl_entry, ents);
free(ent);
return (0);
}
static int
ParseLine(TBL *ta, char *s)
{
char *data;
TBL_Entry *nent;
char *key, *p;
size_t len;
int i;
len = strlen(s)+1;
if ((data = malloc(len)) == NULL) {
MPD_SetErrorS("Out of memory");
return (-1);
}
if ((key = strsep(&s, TBL_FIELD_SEP)) == NULL || key[0] == '\0') {
MPD_SetErrorS("Bad key");
goto fail_data;
}
if ((nent = malloc(sizeof(TBL_Entry))) == NULL) {
MPD_SetErrorS("Out of memory");
goto fail_data;
}
if ((nent->key = strdup(key)) == NULL) {
MPD_SetErrorS("Out of memory");
free(nent);
goto fail_data;
}
data[0] = '\0';
i = 0;
while ((p = strsep(&s, " ")) != NULL) {
if (i > 0) {
Strlcat(data, " ", len);
}
Strlcat(data, p, len);
i++;
}
if ((nent->data = strdup(data)) == NULL) {
MPD_SetErrorS("Out of memory");
goto fail;
}
if (TBL_Insert(ta, nent) == -1) {
free(nent->data);
goto fail;
}
free(data);
return (0);
fail:
free(nent->key);
free(nent);
fail_data:
free(data);
return (-1);
}
/*
* Load a table from the given file. Leading spacing characters and characters
* between '#' and end of line are ignored.
*/
TBL *
TBL_Load(const char *path)
{
TBL *ta;
char *data, *p, *eol;
size_t len, nRead;
ssize_t rv;
Uint nLines;
int fd;
if ((fd = open(path, O_RDONLY)) == -1) {
MPD_SetError("%s: %s", path, strerror(errno));
return (NULL);
}
len = (size_t)lseek(fd, 0, SEEK_END);
(void)lseek(fd, 0, SEEK_SET);
if (len == 0) {
ta = Malloc(sizeof(TBL));
TBL_Init(ta, path, 0);
close(fd);
return (ta);
}
if ((data = malloc(len+1)) == NULL) {
MPD_SetErrorS("Out of memory");
close(fd);
return (NULL);
}
for (nRead = 0; nRead < len; ) {
rv = read(fd, &data[nRead], len-nRead);
if (rv == -1) {
if (errno == EINTR) { continue; }
goto fail_read;
} else if (rv == 0) {
goto fail_read;
}
nRead += rv;
}
close(fd);
data[len] = '\0';
nLines = 0;
for (p = &data[0]; *p != '\0'; p++) { /* Auto-size */
if (*p == '\n')
nLines++;
}
ta = Malloc(sizeof(TBL));
TBL_Init(ta, path, nLines);
nLines = 0;
for (p = &data[0]; *p != '\0'; p++) {
while (isspace(*p)) {
p++;
}
if (*p == '#') {
while (*(++p) != '\n')
;;
}
for (eol = p;
*eol != '\n' && *eol != '\0';
eol++) {
if (*eol == '#') {
for (eol--; isspace(*eol); eol--)
;;
eol++;
break;
}
}
*eol = '\0';
if (p < eol) {
char *sp;
for (sp = &eol[-1]; isspace(*sp); sp--)
;;
sp[1] = '\0';
if (ParseLine(ta, p) == -1) {
Debug("%s:%u: %s", path, nLines, MPD_GetError());
syslog(LOG_ERR, "%s:%u: %s", path, nLines,
MPD_GetError());
}
}
p = eol;
nLines++;
}
free(data);
return (ta);
fail_read:
free(data);
close(fd);
return (NULL);
}
/* Save the contents of the given table to the named file. */
int
TBL_Save(TBL *ta, const char *path, mode_t mode)
{
TBL_Entry *ent;
FILE *f;
if ((f = fopen(path, "w")) == NULL) {
MPD_SetError("%s: %s", path, strerror(errno));
return (-1);
}
TBL_FOREACH(ent, ta) {
fputs(ent->key, f);
fputc(' ', f);
fputs(ent->data, f);
fputc('\n', f);
}
fclose(f);
chmod(path, mode);
return (0);
}