/* * Copyright (c) 2003-2016 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. */ #define _PERCGI_NO_FASTCGI #include "cgi.h" #include #include #include #include #include #include #include /* Allocate and initialize a new table. */ TBL * TBL_New(Uint nBuckets, Uint nFields) { TBL *t; if ((t = TryMalloc(sizeof(TBL))) == NULL) { return (NULL); } TBL_Init(t, nBuckets, nFields); return (t); } /* Initialize a table. */ void TBL_Init(TBL *t, Uint nBuckets, Uint nFields) { Uint i; t->data = NULL; t->dataSize = 0; t->nBuckets = nBuckets; t->buckets = Malloc(nBuckets*sizeof(TBL_Bucket)); t->nFields = nFields; t->sep = ":"; 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; Uint i, j; 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); if (t->data == NULL) { free(ent->key); for (j = 0; j < t->nFields; j++) free(ent->fields[j]); } free(ent); } } if (t->data != NULL) free(t->data); } void TBL_Free(TBL *t) { TBL_Destroy(t); free(t); } /* Look up a named table entry. */ TBL_Entry * TBL_Lookup(TBL *ta, const char *key, Uint h) { TBL_Entry *ent; SLIST_FOREACH(ent, &ta->buckets[h].ents, bents) { if (strcmp(key, ent->key) == 0) break; } if (ent == NULL) { CGI_SetError(_("Element not found in table: %s"), key); } return (ent); } /* Return a pointer to the given table entry field. */ char * TBL_LookupField(TBL *ta, const char *key, Uint field) { TBL_Entry *ent; if ((ent = TBL_Lookup(ta, key, TBL_Hash(ta,key))) != NULL) { if (field >= ta->nFields) { CGI_SetError(_("Element (%s) has no #%d field"), key, field); return (NULL); } return (ent->fields[field]); } CGI_SetError(_("Element not found in table: %s"), key); return (NULL); } /* Copy a named table entry field to a fixed-size buffer. */ int TBL_CopyField(TBL *ta, const char *key, Uint field, char *buf, size_t buf_len) { TBL_Entry *ent; if ((ent = TBL_Lookup(ta, key, TBL_Hash(ta,key))) != NULL) { if (field >= ta->nFields) { CGI_SetError(_("Element (%s) has no #%d field"), key, field); return (-1); } if (Strlcpy(buf, ent->fields[field], buf_len) >= buf_len) { CGI_SetError(_("Table string too large for buffer")); return (-1); } return (0); } return (-1); } /* Insert a new table entry (fail on collision) */ int TBL_InsertUnique(TBL *ta, TBL_Entry *ent) { Uint h = TBL_Hash(ta,ent->key); if (TBL_Lookup(ta, ent->key, h) != NULL) { CGI_SetError(_("Existing element in table: %s"), ent->key); return (-1); } TBL_Insert(ta, ent, h); return (0); } /* Remove a named table entry. */ int TBL_Delete(TBL *ta, const char *key) { Uint h = TBL_Hash(ta,key), i; TBL_Entry *ent; if ((ent = TBL_Lookup(ta, key, h)) == NULL) { CGI_SetError(_("Element not found in table: %s"), key); return (-1); } SLIST_REMOVE(&ta->buckets[h].ents, ent, tbl_entry, bents); SLIST_REMOVE(&ta->ents, ent, tbl_entry, ents); if (ta->data == NULL) { for (i = 0; i < ta->nFields; i++) { if (ent->fields[i] != NULL) free(ent->fields[i]); } free(ent->key); } free(ent); return (0); } static int TBL_LoadLine(TBL *ta, char *s, int flags) { TBL_Entry *ent; char *key, **pField; Uint i, h; if ((key = Strsep(&s, ta->sep)) == NULL) { return (0); /* Empty line, ignore */ } h = TBL_Hash(ta, key); if ((flags & TBL_LOAD_NODUPS) && TBL_Lookup(ta, key, h) != NULL) { CGI_SetError("Duplicate key: %s", key); return (-1); } if ((ent = TryMalloc(sizeof(TBL_Entry))) == NULL) { return (-1); } ent->key = key; for (i = 0; i < ta->nFields; i++) { ent->fields[i] = NULL; } for (pField = &ent->fields[0]; pField < &ent->fields[ta->nFields] && (*pField = Strsep(&s, ta->sep)) != NULL; ) { if (**pField != '\0') pField++; } #if 0 for (i = 0; i < ta->nFields; i++) { if (ent->fields[i] == NULL) { break; } CGI_Log(CGI_LOG_DEBUG, "Table: \"%s\"[%d] => %s", key, i, ent->fields[i]); } #endif SLIST_INSERT_HEAD(&ta->buckets[h].ents, ent, bents); SLIST_INSERT_HEAD(&ta->ents, ent, ents); ta->buckets[h].nEnts++; return (0); fail: free(ent); 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, Uint nFields, const char *sep, int flags) { TBL *ta; char *data, *p, *eol; size_t len, nRead; ssize_t rv; Uint nLines; int fd; if (nFields > TABLE_MAX_FIELDS) { CGI_SetError("Too many fields"); return (NULL); } if ((ta = TryMalloc(sizeof(TBL))) == NULL) { return (NULL); } if ((fd = open(path, O_RDONLY)) == -1) { CGI_SetError("%s: %s", path, strerror(errno)); free(ta); return (NULL); } len = (size_t)lseek(fd, 0, SEEK_END); (void)lseek(fd, 0, SEEK_SET); if ((data = TryMalloc(len+1)) == NULL) { goto fail; } nRead = 0; while (nRead < len) { rv = read(fd, &data[nRead], len-nRead); if (rv == -1) { if (errno == EINTR) { continue; } CGI_SetError("%s: %s", path, strerror(errno)); free(data); goto fail; } nRead += rv; } data[len] = '\0'; close(fd); /* Allocate the hash table. */ nLines = 0; for (p = &data[0]; *p != '\0'; p++) { if (*p == '\n') nLines++; } TBL_Init(ta, nLines, nFields); ta->data = data; ta->dataSize = len; ta->nFields = nFields; ta->sep = sep; if (data != NULL) { 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 (TBL_LoadLine(ta, p, flags) == -1) { if (flags & TBL_LOAD_NOERRORS) { goto fail; } CGI_Log(CGI_LOG_WARNING, "%s:%u: %s", path, nLines, CGI_GetError()); } } p = eol; nLines++; } } return (ta); fail: close(fd); free(ta); return (NULL); } /* Save table contents to named file. */ int TBL_Save(TBL *ta, const char *path) { TBL_Entry *ent; FILE *f; int i; if ((f = fopen(path, "w")) == NULL) { CGI_SetError("%s: %s", path, strerror(errno)); return (-1); } TBL_FOREACH(ent, ta) { if (ent->key != NULL) { fputs(ent->key, f); } for (i = 0; i < ta->nFields; i++) { if (ent->fields[i] == NULL) { break; } fputc(':', f); fputs(ent->fields[i], f); } fputc('\n', f); } fclose(f); return (0); } /* * Export table contents to a JavaScript array or object. * * (0 fields) -> JS Array * (1 field) -> JS Object strings * (>1 fields) -> JS Object arrays of strings */ void TBL_SaveJS(TBL *ta, VAR *v) { TBL_Entry *ent; Uint i; TBL_FOREACH(ent, ta) { if (ent->key == NULL) { continue; } if (ta->nFields == 0) { CatJS(v, ent->key); } else if (ta->nFields == 1) { CatC(v, '"'); CatS(v, ent->key); CatS(v, "\": \""); CatS(v, ent->fields[0]); CatC(v, '"'); } else if (ta->nFields > 1) { CatC(v, '"'); CatS(v, ent->key); CatS(v, "\": ["); for (i = 0; i < ta->nFields; i++) { if (ent->fields[i] == NULL) { break; } CatJS(v, ent->fields[i]); } CatC(v, ']'); } } }