/* * Brutus source file implementing a configuration file class. * Copyright (C) 2004 OMC Denmark ApS. * * 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 2 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, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include #include #include #include #include #ifdef COMMENT_CHAR #undef COMMENT_CHAR #endif #define COMMENT_CHAR '#' #ifdef MAX_CONFIG_FILE_SIZE #undef MAX_CONFIG_FILE_SIZE #endif #define MAX_CONFIG_FILE_SIZE (20480) /* Will load the configuration file "file". Returns * zero on success or a positive number which indicates * a faulty configuration line or a negative number * which indicates that the configuration file is to * big or can not be read. */ int Config::load(const char *file) { if (!file) return -1; // initialize if (_items) { free(_items); _items = NULL; } if (_fbuf) { free(_fbuf); _fbuf = NULL; } _lnum = 1; // read file into buffer if (read_conf(file, &_fsize, (void**)&_fbuf)) return -1; return itemize(); } int Config::itemize(void) { size_t count = 0; char *p = _fbuf; // count maximum number of effective configuration lines for (size_t n = 0; n < _fsize; n++, p++) { // newline? if (*p == '\0') count++; } _items = (struct item*)malloc(sizeof(struct item) * (count + 1)); // get token/value pairs p = _fbuf; count = 0; if ((wspace(*p)) || (*p == COMMENT_CHAR)) if (get_next_line(&p)) return _lnum; while (1) { if (!p) break; _items[count].token = p; // p will be somewhere within the value string when the function returns if (point_to_value(&p, &_items[count++].value)) return _lnum; if (get_next_line(&p)) return _lnum; } _items[count].token = NULL; _items[count].value = NULL; return 0; } /* Upon return val points to the first character in the null- * or whitespace-terminated value string. * * *str is a pointer to the first character in a null-terminated * configuration line. * * *str is modified by this function to point to the first * character in the value string. * * Will return (-1) on error or zero on success. */ int Config::point_to_value(char **str, char **val) { // not possible if (wspace(**str)) return -1; // skip past token while ((!wspace(**str)) && (**str != '\0')) { (*str)++; } if (**str == '\0') return -1; // skip past whitespace to value while (wspace(**str)) { **str = '\0'; (*str)++; } if (**str == '\0') return -1; *val = *str; // remove trailing whitespace char *tmp = *val; while (*tmp != '\0') tmp++; tmp--; while (wspace(*tmp)) { *tmp = '\0'; tmp--; } return 0; } /* *str is a pointer to the first character * of the next configuration line skipping comments and * empty lines. * * *str is NULL on end-of-buffer. * * Returns (-1) on error or zero on success. */ int Config::get_next_line(char **str) { int retv = 0; size_t offset = *str - _fbuf; do { // skip printable characters while (**str != '\0') { (*str)++; offset++; if (offset == _fsize) { *str = NULL; goto out; } } // skip '\0's while (**str == '\0') { _lnum++; (*str)++; offset++; if (offset == _fsize) { *str = NULL; goto out; } } // quick check for floating text (TEXT) if (wspace(**str)) { while (wspace(**str)) { (*str)++; offset++; if (offset == _fsize) { *str = NULL; goto out; } } // floating text if (**str != '\0') { retv = -1; goto out; } continue; } // configuration line? if (**str != COMMENT_CHAR) break; } while (1); out: return retv; } /* Will return the corresponding value string or NULL * if non-existent. The token will be cleared if strip * is true. This makes multible like-named options * possible so as to sequentially read them all. * * The return value must be freed by callee. */ char *Config::get_value(const char *token) { size_t n = 0; if (_items) { while (_items[n].value) { if ((_items[n].token) && !strcmp(token, _items[n].token)) { if (_stack && !strcmp(token, _stack)) _items[n].token = NULL; return _strdup(_items[n].value); } n++; } } return NULL; } /* Reads all of file_name into *buf, as a set of adjoined * strings, correcting for CRLF. * * *size is the size of the file in bytes, not the size * of the buffer *buf. Callee must free() *buf if zero * is returned, otherwise not. * * I am reading one byte of the file at a time. This is to * avoid a second copy/allocation. Not that this method is * particular fast, but it makes the purpose so much clearer. * This function is not a bottleneck, therefore clarity before * speed. * * Returns -1 on error. */ int Config::read_conf(const char *file_name, size_t *size, void **buf) { FILE *file; int retv = 0; size_t len; char *tmp; char *first; struct stat fstatus; retv = stat(file_name, &fstatus); if (retv) return -1; if (fstatus.st_size > MAX_CONFIG_FILE_SIZE) return -1; tmp = (char*)malloc(fstatus.st_size); if (!tmp) return -1; memset((void*)tmp, '\0', fstatus.st_size); first = tmp; if (fopen_s(&file, file_name, "r")) { free(tmp); return -1; } char c; off_t pos = 0; while (1) { // For clarity, not speed len = fread((void*)&c, 1, 1, file); if (!len) { if (ferror(file)) retv = -1; break; } // create null-terminated string if (c == '\n') { c = '\0'; } // skip CR if (c != '\r') { *tmp = c; pos++; if (pos < fstatus.st_size) tmp++; } } if (file) fclose(file); if (retv) free(tmp); else { *buf = first; *size = pos; } return retv; } bool Config::get_token_value_pair(const char *token, const char *value) { static size_t n = 0; if (!_items[n].token) return false; token = _items[n].token; value = _items[n].value; n++; return true; }