diff options
Diffstat (limited to 'debian/transcode/transcode-1.1.7/libtc/cfgfile.c')
| -rw-r--r-- | debian/transcode/transcode-1.1.7/libtc/cfgfile.c | 697 |
1 files changed, 697 insertions, 0 deletions
diff --git a/debian/transcode/transcode-1.1.7/libtc/cfgfile.c b/debian/transcode/transcode-1.1.7/libtc/cfgfile.c new file mode 100644 index 00000000..ca0331ce --- /dev/null +++ b/debian/transcode/transcode-1.1.7/libtc/cfgfile.c @@ -0,0 +1,697 @@ +/* + * cfgfile.c -- routines to handle external configuration files + * Written by Andrew Church <achurch@achurch.org> + * + * This file is part of transcode, a video stream processing tool. + * transcode is free software, distributable under the terms of the GNU + * General Public License (version 2 or later). See the file COPYING + * for details. + */ + +#define _ISOC99_SOURCE /* needed by glibc to declare strtof() */ + +#include "transcode.h" +#include "libtc.h" +#include "cfgfile.h" + +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +static char *config_dir = NULL; + +static int parse_line(const char *buf, TCConfigEntry *conf, const char *tag, + const char *filename, int line); +static void parse_line_error(const char *buf, const char *filename, int line, + const char *tag, const char *format, ...) +#ifdef HAVE_GCC_ATTRIBUTES +__attribute__((format(printf,5,6))) +#endif +; + +/*************************************************************************/ +/* helpers macros and functions */ + +#define CLEANUP_LINE(line) do { \ + /* skip comments, if any */ \ + char *s = strchr((line), '#'); \ + if (s) { \ + *s = 0; \ + } \ + /* Remove leading and trailing spaces, if any */ \ + tc_strstrip((line)); \ +} while (0) + +/** + * fopen_verbose: Opens a configuration file in read only mode, printing + * meaningful error messages in case of failure. + * + * Parameters: + * name: Name of configuration file to open. + * tag: Tag to use in log messages. + * Return value: + * Read-only FILE pointer to configuration file, NULL if failed. + */ + + +static FILE *fopen_verbose(const char *name, const char *tag) +{ + FILE *f = fopen(name, "r"); + if (!f) { + if (errno == EEXIST) { + tc_log_warn(tag, "Configuration file %s does not exist!", + name); + } else if (errno == EACCES) { + tc_log_warn(tag, "Configuration file %s cannot be read!", + name); + } else { + tc_log_warn(tag, "Error opening configuration file %s: %s", + name, strerror(errno)); + } + } + return f; +} + +/** + * lookup_section: Move FILE pointer to begining of data belonging to + * given section. + * + * Parameters: + * f: Already open FILE pointer to configuration file. + * section: Name of section to lookup. + * tag: Tag to use in log messages. + * Return value: + * number of lines skipped (>= 0) if succesfull. + * < 0 if error occurs. + */ +static int lookup_section(FILE *f, const char *section, const char *tag) +{ + char expect[TC_BUF_MAX], buf[TC_BUF_MAX]; + int line = 0; + + tc_snprintf(expect, sizeof(expect), "[%s]", section); + do { + if (!fgets(buf, sizeof(buf), f)) { + tc_log_warn(tag, "Section [%s] not found in configuration" + " file!", section); + return -1; + } + line++; + CLEANUP_LINE(buf); + } while (strcmp(buf, expect) != 0); + return line; +} + +/*************************************************************************/ + +/** + * tc_set_config_dir: Sets the directory in which configuration files are + * searched for. + * + * Parameters: + * dir: Directory to search for configuration files in. If NULL, the + * current directory is used. + * Return value: + * None. + */ + +void tc_set_config_dir(const char *dir) +{ + tc_free(config_dir); + config_dir = dir ? tc_strdup(dir) : NULL; +} + + +/*************************************************************************/ + +/** + * module_read_config: Reads in configuration information from an external + * file. + * + * Parameters: + * filename: Name of the configuration file to read. + * section: Section to read within the file, or NULL to read the + * entire file regardless of sections. + * conf: Array of configuration entries. + * tag: Tag to use in log messages. + * Return value: + * Nonzero on success, zero on failure. + */ + +int module_read_config(const char *filename, const char *section, + TCConfigEntry *conf, const char *tag) +{ + char buf[TC_BUF_MAX], path_buf[PATH_MAX+1]; + FILE *f = NULL; + int line = 0; + + /* Sanity checks */ + if (!tag) + tag = __FILE__; + if (!filename || !conf) { + tc_log_error(tag, "module_read_config(): %s == NULL!", + !filename ? "filename" : !conf ? "conf" : "???"); + return 0; + } + + /* Open the file */ + tc_snprintf(path_buf, sizeof(path_buf), "%s/%s", + config_dir ? config_dir : ".", filename); + f = fopen_verbose(path_buf, tag); + if (!f) { + return 0; + } + + if (section) { + line = lookup_section(f, section, tag); + if (line == -1) { + /* error */ + fclose(f); + return 0; + } + } + + /* Read in the configuration values (up to the end of the section, if + * a section name was given) */ + while (fgets(buf, sizeof(buf), f)) { + line++; + CLEANUP_LINE(buf); + + /* Ignore empty lines and comment lines */ + if (!*buf || *buf == '#') + continue; + + /* If it's a section name, this is the end of the current section */ + if (*buf == '[') { + if (section) + break; + else + continue; + } + + /* Pass it on to the parser */ + parse_line(buf, conf, tag, path_buf, line); + } + + fclose(f); + return 1; +} + +/*************************************************************************/ + +/** + * module_read_config_line: Processes a string as if it were a line read + * from a configuration file. The string must have all leading and + * trailing whitespace stripped. + * + * Parameters: + * string: String to process. + * conf: Array of configuration entries. + * tag: Tag to use in log messages. + * Return value: + * Nonzero if the string was successfully processed, else zero. + */ + +int module_read_config_line(const char *string, TCConfigEntry *conf, + const char *tag) +{ + if (!tag) + tag = __FILE__; + if (!string || !conf) { + tc_log_error(tag, "module_read_config_line(): %s == NULL!", + !string ? "string" : !conf ? "conf" : "???"); + return 0; + } + return parse_line(string, conf, tag, NULL, 0); +} + +/*************************************************************************/ + +/** + * module_print_config: Prints the given array of configuration data. + * + * Parameters: + * conf: Array of configuration data. + * tag: Tag to use in log messages. + * Return value: + * None. + */ + +void module_print_config(const TCConfigEntry *conf, const char *tag) +{ + /* Sanity checks */ + if (!tag) + tag = __FILE__; + if (!conf) { + tc_log_error(tag, "module_print_config(): conf == NULL!"); + return; + } + + while (conf->name) { + char buf[TC_BUF_MAX]; + switch (conf->type) { + case TCCONF_TYPE_FLAG: + tc_snprintf(buf, sizeof(buf), "%d", *((int *)conf->ptr) ? 1 : 0); + break; + case TCCONF_TYPE_INT: + tc_snprintf(buf, sizeof(buf), "%d", *((int *)conf->ptr)); + break; + case TCCONF_TYPE_FLOAT: + tc_snprintf(buf, sizeof(buf), "%f", *((float *)conf->ptr)); + break; + case TCCONF_TYPE_STRING: + tc_snprintf(buf, sizeof(buf), "%s", *((char **)conf->ptr)); + break; + } + tc_log_info(tag, "%s = %s", conf->name, buf); + conf++; + } +} + +/*************************************************************************/ +/*************************************************************************/ + +/** + * parse_line: Internal routine to parse a single line of a configuration + * file and set the appropriate variable. + * + * Parameters: + * buf: Line to process. Leading and trailing whitespace + * (including newlines) are assumed to have been stripped. + * conf: Array of configuration entries. + * tag: Tag to use in log messages. + * filename: Name of file being processed, or NULL if none. + * line: Current line number in file. + * Return value: + * Nonzero if the buffer was successfully parsed, else 0. + * Preconditions: + * buf != NULL + * conf != NULL + * tag != NULL + */ + +static int parse_line(const char *buf, TCConfigEntry *conf, const char *tag, + const char *filename, int line) +{ + char workbuf[TC_BUF_MAX]; + char *name, *value, *s; + + /* Make a working copy of the string */ + if (strlcpy(workbuf, buf, sizeof(workbuf)) >= sizeof(workbuf)) { + parse_line_error(buf, filename, line, tag, + "Buffer overflow while parsing configuration data"); + return 0; + } + + /* Split string into name and value */ + name = workbuf; + s = strchr(workbuf, '='); + if (s) { + value = s+1; + while (s > workbuf && isspace(s[-1])) + s--; + *s = 0; + while (isspace(*value)) + value++; + } else { + value = NULL; + } + if (!*name) { + parse_line_error(buf, filename, line, tag, + "Syntax error in option (missing variable name)"); + return 0; + } else if (value && !*value) { + parse_line_error(buf, filename, line, tag, + "Syntax error in option (missing value)"); + return 0; + } + + /* Look for a matching configuration entry */ + while (conf->name) { + if (strcmp(conf->name, name) == 0) + break; + conf++; + } + if (!conf->name) { + parse_line_error(buf, filename, line, tag, + "Unknown configuration variable `%s'", name); + return 0; + } + /* Make sure non-flag entries have values */ + if (conf->type != TCCONF_TYPE_FLAG && !value) { + parse_line_error(buf, filename, line, tag, + "Syntax error in option (missing value)"); + return 0; + } + + /* Set the value appropriately */ + + switch (conf->type) { + case TCCONF_TYPE_FLAG: + if (!value + || strcmp(value,"1" ) == 0 + || strcmp(value,"yes" ) == 0 + || strcmp(value,"on " ) == 0 + || strcmp(value,"true") == 0 + ) { + *((int *)(conf->ptr)) = (int)conf->max; + } else if (strcmp(value,"0" ) == 0 + || strcmp(value,"no" ) == 0 + || strcmp(value,"off" ) == 0 + || strcmp(value,"false") == 0 + ) { + *((int *)(conf->ptr)) = 0; + } else { + parse_line_error(buf, filename, line, tag, + "Value for variable `%s' must be either 1 or 0", + name); + return 0; + } + break; + + case TCCONF_TYPE_INT: { + long lvalue; + errno = 0; + lvalue = strtol(value, &value, 0); + if (*value) { + parse_line_error(buf, filename, line, tag, + "Value for variable `%s' must be an integer", + name); + return 0; + } else if (errno == ERANGE +#if LONG_MIN < INT_MIN + || lvalue < INT_MIN +#endif +#if LONG_MAX < INT_MAX + || lvalue > INT_MAX +#endif + || ((conf->flags & TCCONF_FLAG_MIN) && lvalue < conf->min) + || ((conf->flags & TCCONF_FLAG_MAX) && lvalue > conf->max) + ) { + parse_line_error(buf, filename, line, tag, + "Value for variable `%s' is out of range", name); + return 0; + } else { + *((int *)(conf->ptr)) = (int)lvalue; + } + break; + } + + case TCCONF_TYPE_FLOAT: { + float fvalue; + errno = 0; +#ifdef HAVE_STRTOF + fvalue = strtof(value, &value); +#else + fvalue = (float)strtod(value, &value); +#endif + if (*value) { + parse_line_error(buf, filename, line, tag, + "Value for variable `%s' must be a number", name); + return 0; + } else if (errno == ERANGE + || ((conf->flags & TCCONF_FLAG_MIN) && fvalue < conf->min) + || ((conf->flags & TCCONF_FLAG_MAX) && fvalue > conf->max) + ) { + parse_line_error(buf, filename, line, tag, + "Value for variable `%s' is out of range", name); + return 0; + } else { + *((float *)(conf->ptr)) = fvalue; + } + break; + } + + case TCCONF_TYPE_STRING: { + char *newval = tc_strdup(value); + if (!newval) { + parse_line_error(buf, filename, line, tag, + "Out of memory setting variable `%s'", name); + return 0; + } else { + *((char **)(conf->ptr)) = newval; + } + break; + } + + default: + parse_line_error(buf, filename, line, tag, + "Unknown type %d for variable `%s' (bug?)", + conf->type, name); + return 0; + + } /* switch (conf->type) */ + + return 1; +} + +/*************************************************************************/ + +/** + * parse_line_error: Helper routine for parse_line() to print an error + * message, formatted appropriately depending on whether a filename was + * given or not. + * + * Parameters: + * buf: String that caused the error. + * filename: Name of file being processed, or NULL if none. + * line: Current line number in file. + * tag: Tag to use in log message. + * format: Format string for message. + * Return value: + * None. + * Preconditions: + * buf != NULL + * tag != NULL + * format != NULL + */ + +static void parse_line_error(const char *buf, const char *filename, int line, + const char *tag, const char *format, ...) +{ + char msgbuf[TC_BUF_MAX]; + va_list args; + + va_start(args, format); + tc_vsnprintf(msgbuf, sizeof(msgbuf), format, args); + va_end(args); + if (filename) { + tc_log_warn(tag, "%s:%d: %s", filename, line, msgbuf); + } else { + tc_log_warn(tag, "\"%s\": %s", buf, msgbuf); + } +} + +/*************************************************************************/ + +/** + * list_new: create a new list item storing a copy of given data string. + * + * Parameters: + * value: string to copy into new list item. + * Return value: + * pointer to new list item if succesfull. + * NULL otherwise. + */ + +static TCConfigList *list_new(const char *value) +{ + TCConfigList *list = tc_malloc(sizeof(TCConfigList)); + if (list) { + list->next = NULL; + list->last = NULL; + list->value = NULL; + + if (value) { + list->value = tc_strdup(value); + if (!list->value) { + tc_free(list); + list = NULL; + } + } + } + return list; +} + +/** + * list_append: append a new item storing given data on a given list. + * + * Parameters: + * list: list to append new item + * value: value to be copied into new item + * Return value: + * 0 succesfull + * !0 error + */ + +static int list_append(TCConfigList *list, const char *value) +{ + int ret = 1; + TCConfigList *item = list_new(value); + if (item) { + if (list->last != NULL) { + /* typical case */ + list->last->next = item; + } else { + /* second item, special case */ + list->next = item; + } + list->last = item; + + ret = 0; + } + return ret; +} + +/** + * module_read_config_list: Read a list section from given configuration + * file and return the corresponding data list. + * + * Parameters: + * filename: Name of file being processed. + * section: Name of the section to be read. + * tag: Tag to use in log message. + * Return value: + * A pointer to a valid configuration list structure if succesfull, + * NULL otherwise. + */ + +TCConfigList *module_read_config_list(const char *filename, + const char *section, const char *tag) +{ + char buf[TC_BUF_MAX], path_buf[PATH_MAX+1]; + TCConfigList *list = NULL; + FILE *f = NULL; + int line = 0; + + /* Sanity checks */ + if (!tag) + tag = __FILE__; + if (!filename) { + tc_log_error(tag, "module_read_config(): missing filename"); + return 0; + } + if (!section) { + tc_log_error(tag, "module_read_config(): missing section"); + return 0; + } + + /* Open the file */ + tc_snprintf(path_buf, sizeof(path_buf), "%s/%s", + config_dir ? config_dir : ".", filename); + f = fopen_verbose(path_buf, tag); + if (!f) { + return NULL; + } + line = lookup_section(f, section, tag); + if (line == -1) { + /* error */ + fclose(f); + return NULL; + } + + /* Read in the configuration values (up to the end of the section, if + * a section name was given) */ + while (fgets(buf, sizeof(buf), f)) { + line++; + CLEANUP_LINE(buf); + + /* Ignore empty lines and comment lines */ + if (!*buf || *buf == '#') + continue; + + /* If it's a section name, this is the end of the current section */ + if (*buf == '[') { + break; + } else { + int err = 1; + + if (list) { + err = list_append(list, buf); + } else { + list = list_new(buf); + err = (list == NULL) ?1 :0; + } + + if (err) { + tc_log_error(tag, "out of memory at line %i", line); + module_free_config_list(list, 0); + list = NULL; + } + } + } + + fclose(f); + return list; +} + +/** + * module_free_config_list: Dispose a configuration list created by + * module_read_config_list. + * + * Parameters: + * list: Head of configuration list to free. + * refonly: If !0, DO NOT free data pointed to list; + * If 0, free list itslef AND data as well. + * Recap: use 0 to free everything, use !0 if you + * do want to preserve data for some reasons. + * Return value: + * None. + */ + +void module_free_config_list(TCConfigList *list, int refonly) +{ + TCConfigList *item = NULL; + while (list) { + item = list->next; + if (!refonly) { + tc_free((void*)list->value); + } + tc_free(list); + list = item; + } +} + +/** + * module_print_config_list: Prints the given configuration list. + * + * Parameters: + * list: Head of configuration list. + * section: Name of section on which configuration list belongs. + * tag: Tag to use in log messages. + * Return value: + * None. + */ + +void module_print_config_list(const TCConfigList *list, + const char *section, const char *tag) +{ + /* Sanity checks */ + if (!tag) + tag = __FILE__; + if (!section) { + tc_log_error(tag, "module_print_config_list(): section == NULL!"); + return; + } + if (!list) { + tc_log_error(tag, "module_print_config_list(): list == NULL!"); + return; + } + + tc_log_info(tag, "[%s]", section); + for (; list != NULL; list = list->next) { + tc_log_info(tag, "%s", list->value); + } +} + +/*************************************************************************/ + +/* + * Local variables: + * c-file-style: "stroustrup" + * c-file-offsets: ((case-label . *) (statement-case-intro . *)) + * indent-tabs-mode: nil + * End: + * + * vim: expandtab shiftwidth=4: + */ |
