/* * This file is part of the Poliqarp suite. * * Copyright (C) 2004-2009 by Instytut Podstaw Informatyki Polskiej * Akademii Nauk (IPI PAN; Institute of Computer Science, Polish * Academy of Sciences; cf. www.ipipan.waw.pl). All rights reserved. * * This file may be distributed and/or modified under the terms of the * GNU General Public License version 2 as published by the Free Software * Foundation and appearing in the file gpl.txt included in the packaging * of this file. (See http://www.gnu.org/licenses/translations.html for * unofficial translations.) * * A commercial license is available from IPI PAN (contact * Michal.Ciesiolka@ipipan.waw.pl or ipi@ipipan.waw.pl for more * information). Licensees holding a valid commercial license from IPI * PAN may use this file in accordance with that license. * * This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING * THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE. */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <errno.h> #include <foostring/foostring.h> #include <sakura/common/hash-table.h> #include "configuration.h" #define DFL_HOSTNAME "127.0.0.1" #define MAX_PORT ((1 << 16) - 1) #define DFL_PORT 4567 #define DFL_LOGFILE "poliqarpd.log" #define MIN_BUFFER_SIZE 1 #define DFL_BUFFER_SIZE 1000 #define MAX_BUFFER_SIZE 50000 #define MIN_IDLE_TIME 1 #define DFL_IDLE_TIME 1200 #define MAX_IDLE_TIME 86400 #define MIN_MATCH_LENGTH 10 #define DFL_MATCH_LENGTH 1000 #define MAX_MATCH_LENGTH 1000 struct configuration cfg = { .hostname = NULL, .port = DFL_PORT, .logging_on = 0, .logfile = NULL, .match_buffer_size = DFL_BUFFER_SIZE, .max_session_idle = DFL_IDLE_TIME, .max_match_length = DFL_MATCH_LENGTH, .corpora = NULL, .allow_any_corpus = false, .gui_mode = false, .detach = false, .notify_thread_id = 0, .log = NULL, .logmutex = PTHREAD_MUTEX_INITIALIZER, }; void init_default_cfg() { cfg.hostname = string_init(DFL_HOSTNAME); cfg.logfile = string_init(DFL_LOGFILE); } void done_cfg() { if (cfg.hostname) { string_free(cfg.hostname); cfg.hostname = NULL; } if (cfg.logfile) { string_free(cfg.logfile); cfg.logfile = NULL; } if (cfg.corpora) { destroy_hash_table(cfg.corpora, free); free(cfg.corpora); cfg.corpora = NULL; } } struct config_hook { char *option; int (*proc)(const char *, char **); }; #define STR_RANGE(x, y) STR_RANGE_(x, y) #define STR_RANGE_(x, y) "" # x " and " # y static char err_nan[] = _M("not a number"); static char err_invalid_hostname[] = _M("unspecified host name"); static char err_invalid_port[] = _M("port number out of range"); static char err_invalid_logging[] = _M("must be \"on\" or \"off\""); static char err_invalid_logfile[] = _M("unspecified log file name"); static char err_invalid_buffer_size[] = _M("must be between " STR_RANGE(MIN_BUFFER_SIZE, MAX_BUFFER_SIZE)); static char err_invalid_idle_time[] = _M("must be between " STR_RANGE(MIN_IDLE_TIME, MAX_IDLE_TIME)); static char err_invalid_key_value[] = _M("invalid key:value pair"); static char err_invalid_match_length[] = _M("must be between " STR_RANGE(MIN_MATCH_LENGTH, MAX_MATCH_LENGTH)); static int hostname_hook(const char *val, char **err) { if (strlen(val) == 0) { *err = err_invalid_hostname; return -1; } if (cfg.hostname) { string_free(cfg.hostname); cfg.hostname = NULL; } cfg.hostname = string_init(val); return 0; } static int port_hook(const char *val, char **err) { char *serr; long port; errno = 0; port = strtol(val, &serr, 10); if (*val == '\0' || *serr != '\0') { *err = err_nan; return -1; } if (port < 0 || port > MAX_PORT) { *err = err_invalid_port; return -1; } cfg.port = (uint16_t)port; return 0; } static int logging_hook(const char *val, char **err) { if (!strcmp(val, "on")) { cfg.logging_on = 1; return 0; } if (!strcmp(val, "off")) { cfg.logging_on = 0; return 0; } *err = err_invalid_logging; return -1; } static int logfile_hook(const char *val, char **err) { if (strlen(val) == 0) { *err = err_invalid_logfile; return -1; } if (cfg.logfile) { string_free(cfg.logfile); cfg.logfile = NULL; } cfg.logfile = string_init(val); return 0; } static int match_buffer_size_hook(const char *val, char **err) { char *serr; unsigned long size; size = strtoul(val, &serr, 10); if (*val == '\0' || *serr != '\0') { *err = err_nan; return -1; } if (size < MIN_BUFFER_SIZE) { cfg.match_buffer_size = MIN_BUFFER_SIZE; *err = err_invalid_buffer_size; return -1; } if (size > MAX_BUFFER_SIZE) { cfg.match_buffer_size = MAX_BUFFER_SIZE; *err = err_invalid_buffer_size; return -1; } cfg.match_buffer_size = size; return 0; } static int max_match_length_hook(const char *val, char **err) { char *serr; unsigned long size; size = strtoul(val, &serr, 10); if (*val == '\0' || *serr != '\0') { *err = err_nan; return -1; } if (size < MIN_MATCH_LENGTH) { cfg.max_match_length = MIN_MATCH_LENGTH; *err = err_invalid_match_length; return -1; } if (size > MAX_MATCH_LENGTH) { cfg.max_match_length = MAX_MATCH_LENGTH; *err = err_invalid_match_length; return -1; } cfg.max_match_length = size; return 0; } static int max_session_idle_hook(const char *val, char **err) { char *serr; unsigned long msi; msi = strtoul(val, &serr, 10); if (*val == '\0' || *serr != '\0') { *err = err_nan; return -1; } if (msi < MIN_IDLE_TIME) { cfg.max_session_idle = MIN_IDLE_TIME; *err = err_invalid_idle_time; return -1; } if (msi > MAX_IDLE_TIME) { cfg.max_session_idle = MAX_IDLE_TIME; *err = err_invalid_idle_time; return -1; } cfg.max_session_idle = msi; return 0; } static int split(char *buf, int len, char ch, char **start1, char **start2); static int corpus_hook(const char *val, char **err) { char *corpus_key, *corpus_value, *corpus_item; if (strcmp(val, "any") == 0) { cfg.allow_any_corpus = true; return 0; } corpus_item = strdup(val); if (corpus_item == NULL) abort(); if (split(corpus_item, strlen(corpus_item), ':', &corpus_key, &corpus_value) != 0) { *err = err_invalid_key_value; free(corpus_item); return -1; } corpus_value = strdup(corpus_value); if (corpus_value == NULL) abort(); if (cfg.corpora == NULL) { cfg.corpora = malloc(sizeof(*cfg.corpora)); if (cfg.corpora == NULL) abort(); if (create_hash_table(cfg.corpora, 16, HASHTABLE_DUPLICATE_KEYS, NULL) != 0) abort(); } if (hash_table_set(cfg.corpora, corpus_key, corpus_value) != 0) abort(); free(corpus_item); return 0; } static const struct config_hook hooks[] = { { "hostname", hostname_hook }, { "port", port_hook }, { "logging", logging_hook }, { "logfile", logfile_hook }, { "log-file", logfile_hook }, { "match-buffer-size", match_buffer_size_hook }, { "max-match-length", max_match_length_hook }, { "max-session-idle", max_session_idle_hook }, { "corpus", corpus_hook }, }; /** Searches for a character in the text buffer and splits the text into * two strings (before and after the character), trimming whitespaces from * the beginning and end of each of the two strings. * * @param buf the buffer to search in * @param len the length of the buffer * @param ch the character to search for * @param start1 pointer to a variable that will store the beginning of first * string * @param start2 ditto, second string * @return 0 if the split was successful * @return -1 if the character was not found */ static int split(char *buf, int len, char ch, char **start1, char **start2) { char *end1, *end2, *charpos; charpos = strchr(buf, ch); if (!charpos) return -1; *start1 = buf; end1 = charpos - 1; *start2 = charpos + 1; end2 = buf + len - 1; while (end1 > *start1 && ascii_isspace(*end1)) end1--; while (end2 > *start2 && ascii_isspace(*end2)) end2--; while (*start1 < end1 && ascii_isspace(**start1)) (*start1)++; while (*start2 < end2 && ascii_isspace(**start2)) (*start2)++; end1[1] = 0; end2[1] = 0; return 0; } static void scan_hooks(const char *name, const char *value, const char *filename, int lineno) { size_t i; bool found = false; char *err; for (i = 0; i < sizeof(hooks) / sizeof(struct config_hook); i++) { if (!strcmp(name, hooks[i].option)) { found = true; if (hooks[i].proc(value, &err) == -1) { fprintf(stderr, "%s(%d): %s: %s\n", filename, lineno, name, _(err)); fflush(stderr); } } } if (!found) { fprintf(stderr, "%s(%d): %s \"%s\"\n", filename, lineno, _("unknown configuration option"), name); fflush(stderr); } } int read_cfg(const char *filename) { FILE *f; string_t str; int lineno = 0; if ((f = fopen(filename, "rt")) == NULL) return -1; while ((str = string_fgets(f)) != NULL) { int len = string_len(str); char *name, *value; char *buf = string_free_and_get_buffer(str); lineno++; if (buf[0] == '#' || split(buf, len, '=', &name, &value) == -1) { free(buf); continue; } scan_hooks(name, value, filename, lineno); free(buf); } if (fclose(f) == EOF) return -1; if (cfg.corpora == NULL) cfg.allow_any_corpus = true; return 0; }