/* Library for inifile like data files. Copyright (C) 2006 Olaf Klein, o.b.klein@gpsbabel.org 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 USA */ #include #include #include #include #include "defs.h" #include "inifile.h" #define MYNAME "inifile" typedef struct inifile_entry_s { queue Q; char *key; char *val; } inifile_entry_t; typedef struct inifile_section_s { queue Q; char *name; int ientries; queue entries; } inifile_section_t; /* internal procedures */ #define START_BUFSIZE 257 #define DELTA_BUFSIZE 128 #define GPSBABEL_INIFILE "gpsbabel.ini" /* Remember the filename we used so we can include it in errors. */ char *gbinipathname; static char * find_gpsbabel_inifile(const char *path) /* can be empty or NULL */ { FILE *test; char *buff; int len; if (path == NULL) return NULL; len = strlen(path); buff = xmalloc(len + 1 + strlen(GPSBABEL_INIFILE) + 1); strcpy(buff, path); if (len > 0) { char test = buff[len - 1]; #ifdef __WIN32__ if ((test != '\\') && (test != ':')) strcat(buff, "\\"); #else if (test != '/') strcat(buff, "/"); #endif } strcat(buff, GPSBABEL_INIFILE); test = fopen(buff, "rb"); if (test) { fclose(test); return buff; } xfree(buff); return NULL; } static gbfile * open_gpsbabel_inifile(void) { char *name; char *envstr; gbfile *res = NULL; envstr = getenv("GPSBABELINI"); if (envstr != NULL) { FILE *test; test = fopen(envstr, "r"); if (test != NULL) { fclose(test); return gbfopen(envstr, "r", "GPSBabel"); } warning("WARNING: GPSBabel-inifile, defined in environment, NOT found!\n"); return NULL; } name = find_gpsbabel_inifile(""); /* PWD */ if (name == NULL) { #ifdef __WIN32__ name = find_gpsbabel_inifile(getenv("APPDATA")); if (name == NULL) name = find_gpsbabel_inifile(getenv("WINDIR")); if (name == NULL) name = find_gpsbabel_inifile(getenv("SYSTEMROOT")); #else if ((envstr = getenv("HOME")) != NULL) { char *path; path = xmalloc(strlen(envstr) + 11); strcpy(path, envstr); strcat(path, "/.gpsbabel"); name = find_gpsbabel_inifile(path); xfree(path); } if (name == NULL) name = find_gpsbabel_inifile("/usr/local/etc"); if (name == NULL) name = find_gpsbabel_inifile("/etc"); #endif } if (name != NULL) { res = gbfopen(name, "r", "GPSBabel"); if (gbinipathname) { xfree(gbinipathname); } gbinipathname = name; } return res; } static void inifile_load_file(gbfile *fin, inifile_t *inifile, const char *myname) { char *buf; inifile_section_t *sec = NULL; while ((buf = gbfgetstr(fin))) { char *cin = lrtrim(buf); if (*cin == '\0') continue; /* skip empty lines */ if ((*cin == '#') || (*cin == ';')) continue; /* skip comments */ if (*cin == '[') { char *cend = strchr(++cin, ']'); if (cend != NULL) { *cend = '\0'; cin = lrtrim(cin); } if ((*cin == '\0') || (cend == NULL)) fatal("%s: invalid section header '%s' in '%s'.\n", myname, cin, gbinipathname); sec = xcalloc(1, sizeof(*sec)); sec->name = xstrdup(cin); QUEUE_INIT(&sec->entries); ENQUEUE_TAIL(&inifile->secs, &sec->Q); inifile->isecs++; } else { char *cx; inifile_entry_t *entry; if (sec == NULL) fatal("%s: missing section header in '%s'.\n", myname,gbinipathname); entry = xcalloc(1, sizeof(*entry)); ENQUEUE_TAIL(&sec->entries, &entry->Q); sec->ientries++; cx = strchr(cin, '='); if (cx != NULL) { *cx = '\0'; cin = lrtrim(cin); } entry->key = xstrdup(cin); if (cx != NULL) { cx = lrtrim(++cx); entry->val = xstrdup(cx); } else entry->val = xstrdup(""); } } } static char * inifile_find_value(const inifile_t *inifile, const char *sec_name, const char *key) { queue *elem, *tmp; if (inifile == NULL) return NULL; QUEUE_FOR_EACH(&inifile->secs, elem, tmp) { inifile_section_t *sec = (inifile_section_t *) elem; if (case_ignore_strcmp(sec->name, sec_name) == 0) { queue *elem, *tmp; QUEUE_FOR_EACH(&sec->entries, elem, tmp) { inifile_entry_t *entry = (inifile_entry_t *) elem; if (case_ignore_strcmp(entry->key, key) == 0) { return entry->val; } } } } return NULL; } /* public procedures */ /* inifile_init: reads inifile filename into memory myname represents the calling module filename == NULL: try to open global gpsbabel.ini */ inifile_t * inifile_init(const char *filename, const char *myname) { inifile_t *result; gbfile *fin = NULL; if (filename == NULL) { fin = open_gpsbabel_inifile(); if (fin == NULL) return NULL; } else fin = gbfopen(filename, "rb", myname); result = xcalloc(1, sizeof(*result)); QUEUE_INIT(&result->secs); inifile_load_file(fin, result, myname); gbfclose(fin); return result; } void inifile_done(inifile_t *inifile) { if (inifile == NULL) return; if (inifile->isecs > 0) { queue *elem, *tmp; QUEUE_FOR_EACH(&inifile->secs, elem, tmp) { inifile_section_t *sec = (inifile_section_t *) elem; if (sec->ientries > 0) { queue *elem, *tmp; QUEUE_FOR_EACH(&sec->entries, elem, tmp) { inifile_entry_t *entry = (inifile_entry_t *) elem; if (entry->key) xfree(entry->key); if (entry->val) xfree(entry->val); dequeue(elem); xfree(entry); } } dequeue(elem); if (sec->name) xfree(sec->name); xfree(sec); } xfree(inifile); } if (gbinipathname) { xfree(gbinipathname); gbinipathname = NULL; } } int inifile_has_section(const inifile_t *inifile, const char *section) { queue *elem, *tmp; QUEUE_FOR_EACH(&inifile->secs, elem, tmp) { inifile_section_t *sec = (inifile_section_t *) elem; if (case_ignore_strcmp(sec->name, section) == 0) return 1; } return 0; } /* inifile_readstr: returns NULL if not found, otherwise a pointer to the value of key ... all key values are valid entities until "inifile_done" */ char * inifile_readstr(const inifile_t *inifile, const char *section, const char *key) { return inifile_find_value(inifile, section, key); } /* inifile_readint: on success the value is stored into "*value" and "inifile_readint" returns 1, otherwise inifile_readint returns 0 */ int inifile_readint(const inifile_t *inifile, const char *section, const char *key, int *value) { char *str; str = inifile_find_value(inifile, section, key); if (str == NULL) { return 0; } if (value != NULL) { *value = atoi(str); } return 1; } /* inifile_readint_def: if found inifile_readint_def returns value of key, otherwise a default value "def" */ int inifile_readint_def(const inifile_t *inifile, const char *section, const char *key, const int def) { int result; if (inifile_readint(inifile, section, key, &result) == 0) { return def; } else { return result; } }