/* * libcfg+ - precise command line & config file parsing library * * cfgfile.c - config file parsing * ____________________________________________________________ * * Developed by Ondrej Jombik * and Lubomir Host * Copyright (c) 2001-2003 Platon SDG, http://platon.sk/ * All rights reserved. * * See README file for more information about this software. * See COPYING file for license information. * * Download the latest version from * http://platon.sk/projects/libcfg+/ */ /* $Platon: libcfg+/src/cfgfile.c,v 1.26 2003/11/07 17:26:48 nepto Exp $ */ /* Includes {{{ */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #if STDC_HEADERS # include #else # if HAVE_STDLIB_H # include # endif #endif #if HAVE_STRING_H # if !STDC_HEADERS && HAVE_MEMORY_H # include # endif # include #endif #if HAVE_STRINGS_H # include #endif #if HAVE_UNISTD_H # include #endif #include #include #include #include "cfg+.h" #include "shared.h" /* }}} */ /* Static function declarations {{{ */ static int get_multi_line(const CFG_CONTEXT con, char **buf); /* }}} */ int cfg_cfgfile_get_next_opt(con) const CFG_CONTEXT con; { /* {{{ */ char *buf; int ret_val; con->error_code = CFG_OK; /* Initial position seek */ if (con->fhandle == NULL) { con->fhandle = con->filename != NULL ? fopen(con->filename, "r") : NULL; if (con->fhandle == NULL) { con->error_code = CFG_ERROR_FILE_NOT_FOUND; return con->error_code; } if (con->flags & CFG_FILE_LINE_POS_USAGE) { /* If negative line is specified, returns seek error. If 0 is specified, do nothing. If number > 0 is specified make appropriate line seek. */ if (con->begin_pos < 0) { con->error_code = CFG_ERROR_SEEK_ERROR; return con->error_code; } if (con->begin_pos > 0) { con->cur_idx = 0; con->cur_idx_tmp = 0; /* Moving to begin_pos line */ while (con->cur_idx_tmp < con->begin_pos) { switch (fgetc(con->fhandle)) { case EOF: con->error_code = CFG_ERROR_SEEK_ERROR; return con->error_code; case '\n': con->cur_idx_tmp++; } } } } else { if (/* always do: con->begin_pos > 0 && */ fseek(con->fhandle, con->begin_pos, SEEK_SET) != 0) { con->error_code = CFG_ERROR_SEEK_ERROR; return con->error_code; } } } /* * Main loop */ while (1) { /* Updating cur_idx to set current line position */ if (con->flags & CFG_FILE_LINE_POS_USAGE) { con->cur_idx += con->cur_idx_tmp; con->cur_idx_tmp = 0; } /* Reading multi line and exit on error */ con->error_code = get_multi_line(con, &buf); if (con->error_code != CFG_OK) { if (buf != NULL) free(buf); return con->error_code; } /* Testing if file stop prefix was found */ if (buf != NULL && con->prop[CFG_FILE_STOP_PREFIX] != NULL) { if (buf == PLATON_FUNC(strdyn_str2)(buf, con->prop[CFG_FILE_STOP_PREFIX], NULL)) { __cfg_free_currents(con); con->cur_opt = buf; con->error_code = CFG_ERROR_STOP_STR; return con->error_code; } } /* Finished? */ if ((con->size >= 0 && cfg_get_cur_idx(con) >= con->begin_pos + con->size) || feof(con->fhandle)) { if (buf != NULL) free(buf); return con->error_code; /* always CFG_OK */ } __cfg_free_currents(con); if (__cfg_cfgfile_set_currents(con, buf) != CFG_OK) { con->error_code = CFG_ERROR_NOMEM; return con->error_code; } free(buf); con->error_code = __cfg_process_currents(con, &ret_val, NULL); if (con->error_code != CFG_OK) return con->error_code; if (ret_val > 0) return ret_val; } return con->error_code; /* CFG_OK */ } /* }}} */ /* * get_multi_line() * * Gets single line from file. If line continues on next line, returns * the whole line concatenated. Coments, remarks and empty lines are * skipped. If end of file is reached CFG_OK is returned and higher * level should determine that fact using feof() function. */ static int get_multi_line(con, buf) const CFG_CONTEXT con; char **buf; { /* {{{ */ register char **ar = NULL; register char *my_buf = NULL; register int state = 0; *buf = NULL; if ((ar = PLATON_FUNC(strdyn_create)()) == NULL) return CFG_ERROR_NOMEM; while (1) { if (state > 1) state = 1; if (my_buf != NULL) free(my_buf); my_buf = PLATON_FUNC(dynamic_fgets)(con->fhandle); if (my_buf == NULL) { if (feof(con->fhandle)) return CFG_OK; return CFG_ERROR_NOMEM; } str_trim(my_buf); /* Is empty line or comment? */ if (strlen(my_buf) == 0 || strdyn_str(my_buf, con->prop[CFG_FILE_COMMENT_PREFIX]) == my_buf) { if (con->flags & CFG_FILE_LINE_POS_USAGE) con->cur_idx++; if (state == 0) continue; else break; } else { if (con->flags & CFG_FILE_LINE_POS_USAGE) con->cur_idx_tmp++; } /* Multi line detection. */ { register char **pos; register int max_len = 0; register int len; for (pos = con->prop[CFG_FILE_MULTI_LINE_POSTFIX]; pos != NULL && *pos != NULL; pos++) { len = strlen(my_buf) - strlen(*pos); if (len > max_len && ! strcmp(*pos, my_buf + len)) max_len = len; } /* Multi line postfix found? */ if (max_len > 0) { my_buf[max_len] = '\0'; state = 2; len = strlen(my_buf); PLATON_FUNC(str_right_trim)(my_buf); if (len - strlen(my_buf) > 0) { /* Could be replaced with strcpy(my_buf + strlen(my_buf), " "); */ my_buf[strlen(my_buf) + 1] = '\0'; my_buf[strlen(my_buf)] = ' '; } } } ar = PLATON_FUNC(strdyn_add)(ar, my_buf); if (ar == NULL) return CFG_ERROR_NOMEM; if (state != 2) break; } if (my_buf != NULL) free(my_buf); my_buf = PLATON_FUNC(str_right_trim)(strdyn_implode(ar, "")); PLATON_FUNC(strdyn_free)(ar); if (my_buf == NULL) return CFG_ERROR_NOMEM; *buf = my_buf; return CFG_OK; } /* }}} */ /* Modeline for ViM {{{ * vim:set ts=4: * vim600:fdm=marker fdl=0 fdc=0: * }}} */