/* * 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); }