/* $Id: cfg.c,v 1.47 2005/05/08 04:32:43 reinelt Exp $^ * * config file stuff * * Copyright (C) 1999, 2000 Michael Reinelt <reinelt@eunet.at> * Copyright (C) 2004 The LCD4Linux Team <lcd4linux-devel@users.sourceforge.net> * * This file is part of LCD4Linux. * * LCD4Linux 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, or (at your option) * any later version. * * LCD4Linux 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * * $Log: cfg.c,v $ * Revision 1.47 2005/05/08 04:32:43 reinelt * CodingStyle added and applied * * Revision 1.46 2005/05/02 05:15:46 reinelt * make busy-flag checking configurable for LCD-Linux driver * * Revision 1.45 2005/01/18 06:30:21 reinelt * added (C) to all copyright statements * * Revision 1.44 2005/01/17 06:29:24 reinelt * added software-controlled backlight support to HD44780 * * Revision 1.43 2004/11/29 04:42:06 reinelt * removed the 99999 msec limit on widget update time (thanks to Petri Damsten) * * Revision 1.42 2004/06/26 12:04:59 reinelt * * uh-oh... the last CVS log message messed up things a lot... * * Revision 1.41 2004/06/26 09:27:20 reinelt * * added '-W' to CFLAGS * changed all C++ comments to C ones * cleaned up a lot of signed/unsigned mistakes * * Revision 1.40 2004/06/20 10:09:52 reinelt * * 'const'ified the whole source * * Revision 1.39 2004/03/11 06:39:58 reinelt * big patch from Martin: * - reuse filehandles * - memory leaks fixed * - earlier busy-flag checking with HD44780 * - reuse memory for strings in RESULT and hash * - netdev_fast to wavid time-consuming regex * * Revision 1.38 2004/03/08 16:26:26 reinelt * re-introduced \nnn (octal) characters in strings * text widgets can have a 'update' speed of 0 which means 'never' * (may be used for static content) * * Revision 1.37 2004/03/06 20:31:16 reinelt * Complete rewrite of the evaluator to get rid of the code * from mark Morley (because of license issues). * The new Evaluator does a pre-compile of expressions, and * stores them in trees. Therefore it should be reasonable faster... * * Revision 1.36 2004/03/03 03:47:04 reinelt * big patch from Martin Hejl: * - use qprintf() where appropriate * - save CPU cycles on gettimeofday() * - add quit() functions to free allocated memory * - fixed lots of memory leaks * * Revision 1.35 2004/03/01 04:29:51 reinelt * cfg_number() returns -1 on error, 0 if value not found (but default val used), * and 1 if value was used from the configuration. * HD44780 driver adopted to new cfg_number() * Crystalfontz 631 driver nearly finished * * Revision 1.34 2004/02/18 06:39:20 reinelt * T6963 driver for graphic displays finished * * Revision 1.33 2004/02/01 18:08:50 reinelt * removed strtok() from layout processing (took me hours to find this bug) * further strtok() removind should be done! * * Revision 1.32 2004/01/30 20:57:55 reinelt * HD44780 patch from Martin Hejl * dmalloc integrated * * Revision 1.31 2004/01/29 0<style>pre { line-height: 125%; } td.linenos .normal { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } span.linenos { color: inherit; background-color: transparent; padding-left: 5px; padding-right: 5px; } td.linenos .special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } span.linenos.special { color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888888 } /* Comment */ .highlight .err { color: #a61717; background-color: #e3d2d2 } /* Error */ .highlight .k { color: #008800; font-weight: bold } /* Keyword */ .highlight .ch { color: #888888 } /* Comment.Hashbang */ .highlight .cm { color: #888888 } /* Comment.Multiline */ .highlight .cp { color: #cc0000; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888888 } /* Comment.Single */ .highlight .cs { color: #cc0000; font-weight: bold; background-color: #fff0f0 } /* Comment.Special */ .highlight .gd { color: #000000; background-color: #ffdddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */</style><div class="highlight"><pre><span></span># 2014-04-18 Antti Palosaari <crope@iki.fi> # generated from http://www.digita.fi/kuluttajat/tv/nakyvyysalueet/kanavanumerot_ja_taajuudet [Fiskars] DELIVERY_SYSTEM = DVBT FREQUENCY = 562000000 BANDWIDTH_HZ = 8000000 [Fiskars] DELIVERY_SYSTEM = DVBT FREQUENCY = 658000000 BANDWIDTH_HZ = 8000000 [Fiskars] DELIVERY_SYSTEM = DVBT FREQUENCY = 674000000 BANDWIDTH_HZ = 8000000 [Fiskars] DELIVERY_SYSTEM = DVBT FREQUENCY = 770000000 BANDWIDTH_HZ = 8000000 </pre></div> </code></pre></td></tr></table> </div> <!-- class=content --> <div class='footer'>generated by <a href='https://git.zx2c4.com/cgit/about/'>cgit v1.2.3</a> (<a href='https://git-scm.com/'>git 2.39.1</a>) at 2025-03-31 08:54:24 +0000</div> </div> <!-- id=cgit --> </body> </html> { ENTRY *ea = (ENTRY *) a; ENTRY *eb = (ENTRY *) b; return strcasecmp(ea->key, eb->key); } /* remove leading and trailing whitespace */ static char *strip(char *s, const int strip_comments) { char *p; while (isblank(*s)) s++; for (p = s; *p; p++) { if (*p == '"') do p++; while (*p && *p != '\n' && *p != '"'); if (*p == '\'') do p++; while (*p && *p != '\n' && *p != '\''); if (*p == '\n' || (strip_comments && *p == '#' && (p == s || *(p - 1) != '\\'))) { *p = '\0'; break; } } for (p--; p > s && isblank(*p); p--) *p = '\0'; return s; } /* unquote a string */ static char *dequote(char *string) { int quote = 0; char *s = string; char *p = string; do { if (*s == '\'') { quote = !quote; *p++ = *s; } else if (quote && *s == '\\') { s++; if (*s >= '0' && *s <= '7') { int n; unsigned int c = 0; sscanf(s, "%3o%n", &c, &n); if (c == 0 || c > 255) { error("WARNING: illegal '\\' in <%s>", string); } else { *p++ = c; s += n - 1; } } else { *p++ = *s; } } else { *p++ = *s; } } while (*s++); return string; } /* which if a string contains only valid chars */ /* i.e. start with a char and contains chars and nums */ static int validchars(const char *string) { const char *c; for (c = string; *c; c++) { /* first and following chars */ if ((*c >= 'A' && *c <= 'Z') || (*c >= 'a' && *c <= 'z') || (*c == '_')) continue; /* only following chars */ if ((c > string) && ((*c >= '0' && *c <= '9') || (*c == '.') || (*c == '-'))) continue; return 0; } return 1; } static void cfg_add(const char *section, const char *key, const char *val, const int lock) { char *buffer; ENTRY *entry; /* allocate buffer */ buffer = malloc(strlen(section) + strlen(key) + 2); *buffer = '\0'; /* prepare section.key */ if (section != NULL && *section != '\0') { strcpy(buffer, section); strcat(buffer, "."); } strcat(buffer, key); /* does the key already exist? */ entry = bsearch(buffer, Config, nConfig, sizeof(ENTRY), c_lookup); if (entry != NULL) { if (entry->lock > lock) return; debug("Warning: key <%s>: value <%s> overwritten with <%s>", buffer, entry->val, val); free(buffer); if (entry->val) free(entry->val); entry->val = dequote(strdup(val)); return; } nConfig++; Config = realloc(Config, nConfig * sizeof(ENTRY)); Config[nConfig - 1].key = buffer; Config[nConfig - 1].val = dequote(strdup(val)); Config[nConfig - 1].lock = lock; qsort(Config, nConfig, sizeof(ENTRY), c_sort); } int cfg_cmd(const char *arg) { char *key, *val; char buffer[256]; strncpy(buffer, arg, sizeof(buffer)); key = strip(buffer, 0); for (val = key; *val; val++) { if (*val == '=') { *val++ = '\0'; break; } } if (*key == '\0' || *val == '\0') return -1; if (!validchars(key)) return -1; cfg_add("", key, val, 1); return 0; } char *cfg_list(const char *section) { int i, len; char *key, *list; /* calculate key length */ len = strlen(section) + 1; /* prepare search key */ key = malloc(len + 1); strcpy(key, section); strcat(key, "."); /* start with empty string */ list = malloc(1); *list = '\0'; /* search matching entries */ for (i = 0; i < nConfig; i++) { if (strncasecmp(Config[i].key, key, len) == 0) { list = realloc(list, strlen(list) + strlen(Config[i].key) - len + 2); if (*list != '\0') strcat(list, "|"); strcat(list, Config[i].key + len); } } free(key); return list; } static char *cfg_lookup(const char *section, const char *key) { int len; char *buffer; ENTRY *entry; /* calculate key length */ len = strlen(key) + 1; if (section != NULL) len += strlen(section) + 1; /* allocate buffer */ buffer = malloc(len); *buffer = '\0'; /* prepare section:key */ if (section != NULL && *section != '\0') { strcpy(buffer, section); strcat(buffer, "."); } strcat(buffer, key); /* search entry */ entry = bsearch(buffer, Config, nConfig, sizeof(ENTRY), c_lookup); /* free buffer again */ free(buffer); if (entry != NULL) return entry->val; return NULL; } char *cfg_get_raw(const char *section, const char *key, const char *defval) { char *val = cfg_lookup(section, key); if (val != NULL) return val; return (char *) defval; } char *cfg_get(const char *section, const char *key, const char *defval) { char *expression; char *retval; void *tree = NULL; RESULT result = { 0, 0, 0, NULL }; expression = cfg_lookup(section, key); if (expression != NULL) { if (*expression == '\0') return ""; if (Compile(expression, &tree) == 0 && Eval(tree, &result) == 0) { retval = strdup(R2S(&result)); DelTree(tree); DelResult(&result); return (retval); } DelTree(tree); DelResult(&result); } if (defval) return strdup(defval); return NULL; } int cfg_number(const char *section, const char *key, const int defval, const int min, const int max, int *value) { char *expression; void *tree = NULL; RESULT result = { 0, 0, 0, NULL }; /* start with default value */ /* in case of an (uncatched) error, you have the */ /* default value set, which may be handy... */ *value = defval; expression = cfg_get_raw(section, key, NULL); if (expression == NULL || *expression == '\0') { return 0; } if (Compile(expression, &tree) != 0) { DelTree(tree); return -1; } if (Eval(tree, &result) != 0) { DelTree(tree); DelResult(&result); return -1; } *value = R2N(&result); DelTree(tree); DelResult(&result); if (*value < min) { error("bad '%s' value '%d' in %s, minimum is %d", key, *value, cfg_source(), min); *value = min; return -1; } if (max > min && max != -1 && *value > max) { error("bad '%s' value '%d' in %s, maximum is %d", key, *value, cfg_source(), max); *value = max; return -1; } return 1; } static int cfg_check_source(const char *file) { /* as passwords and commands are stored in the config file, * we will check that: * - file is a normal file (or /dev/null) * - file owner is owner of program * - file is not accessible by group * - file is not accessible by other */ struct stat stbuf; uid_t uid, gid; int error; uid = geteuid(); gid = getegid(); if (stat(file, &stbuf) == -1) { error("stat(%s) failed: %s", file, strerror(errno)); return -1; } if (S_ISCHR(stbuf.st_mode) && strcmp(file, "/dev/null") == 0) return 0; error = 0; if (!S_ISREG(stbuf.st_mode)) { error("security error: '%s' is not a regular file", file); error = -1; } if (stbuf.st_uid != uid || stbuf.st_gid != gid) { error("security error: owner and/or group of '%s' don't match", file); error = -1; } if (stbuf.st_mode & S_IRWXG || stbuf.st_mode & S_IRWXO) { error("security error: group or other have access to '%s'", file); error = -1; } return error; } static int cfg_read(const char *file) { FILE *stream; char buffer[256]; char section[256]; char *line, *key, *val, *end; int section_open, section_close; int error, lineno; stream = fopen(file, "r"); if (stream == NULL) { error("open(%s) failed: %s", file, strerror(errno)); return -1; } /* start with empty section */ strcpy(section, ""); error = 0; lineno = 0; while ((line = fgets(buffer, 256, stream)) != NULL) { /* increment line number */ lineno++; /* skip empty lines */ if (*(line = strip(line, 1)) == '\0') continue; /* reset section flags */ section_open = 0; section_close = 0; /* key is first word */ key = line; /* search first blank between key and value */ for (val = line; *val; val++) { if (isblank(*val)) { *val++ = '\0'; break; } } /* strip value */ val = strip(val, 1); /* search end of value */ if (*val) for (end = val; *(end + 1); end++); else end = val; /* if last char is '{', a section has been opened */ if (*end == '{') { section_open = 1; *end = '\0'; val = strip(val, 0); } /* provess "value" in double-quotes */ if (*val == '"' && *end == '"') { *end = '\0'; val++; } /* if key is '}', a section has been closed */ if (strcmp(key, "}") == 0) { section_close = 1; *key = '\0'; } /* sanity check: '}' should be the only char in a line */ if (section_close && (section_open || *val != '\0')) { error("error in config file '%s' line %d: garbage after '}'", file, lineno); error = 1; break; } /* check key for valid chars */ if (!validchars(key)) { error("error in config file '%s' line %d: key '%s' is invalid", file, lineno, key); error = 1; break; } /* on section-open, check value for valid chars */ if (section_open && !validchars(val)) { error("error in config file '%s' line %d: section '%s' is invalid", file, lineno, val); error = 1; break; } /* on section-open, append new section name */ if (section_open) { /* is the section[] array big enough? */ if (strlen(section) + strlen(key) + 3 > sizeof(section)) { error("error in config file '%s' line %d: section buffer overflow", file, lineno); error = 1; break; } if (*section != '\0') strcat(section, "."); strcat(section, key); if (*val != '\0') { strcat(section, ":"); strcat(section, val); } continue; } /* on section-close, remove last section name */ if (section_close) { /* sanity check: section already empty? */ if (*section == '\0') { error("error in config file '%s' line %d: unmatched closing brace", file, lineno); error = 1; break; } end = strrchr(section, '.'); if (end == NULL) *section = '\0'; else *end = '\0'; continue; } /* finally: add key */ cfg_add(section, key, val, 0); } /* sanity check: are the braces balanced? */ if (!error && *section != '\0') { error("error in config file '%s' line %d: unbalanced braces", file, lineno); error = 1; } fclose(stream); return -error; } int cfg_init(const char *file) { if (cfg_check_source(file) == -1) { return -1; } if (cfg_read(file) < 0) return -1; if (Config_File) free(Config_File); Config_File = strdup(file); return 0; } char *cfg_source(void) { if (Config_File) return Config_File; else return ""; } int cfg_exit(void) { int i; for (i = 0; i < nConfig; i++) { if (Config[i].key) free(Config[i].key); if (Config[i].val) free(Config[i].val); } if (Config) { free(Config); Config = NULL; } if (Config_File) { free(Config_File); Config_File = NULL; } return 0; }