/* mr_conf.c * * $Id$ * * Based on the work done by Anton (c)2002-2004 Anton Kulchitsky mailto:anton@kulchitsky.org * Code (c)2006-2014 Bruno Cornec * * Main file of mr_conf : a very small and simple * library for configuration file reading * * Provided under the GPLv2 */ #include #include #include #include "mr_types.h" #include "mr_msg.h" #include "mr_mem.h" #include "mr_gettext.h" /* error flags */ #define MRCONF_NO_ERROR 0x0 #define MRCONF_BAD_FILE 0x1 #define MRCONF_READING_FAILED 0x3 #define MRCONF_NO_GROUP_START 0x4 #define MRCONF_NO_GROUP_END 0x5 #define MRCONF_FIELD_NOT_FOUND 0x6 #define MRCONF_FIELD_NO_VALUE 0x7 #define MRCONF_TOO_LONG_STRING 0x8 #define MRCONF_CLOSE_BUT_NOT_OPEN 0x9 #define MRCONF_OPEN_OPENED 0xA #define MRCONF_CALL_BUT_NOT_OPEN 0xB #define MRCONF_STRING_QUOTE 0xC #define MRCONF_STRING_ENDQUOTE 0xD /*setting flags*/ #define MRCONF_FLAG_VERBOSE 0x1 /*All strings of the library are here*/ #define MRCONF_STR_ERROR _("MRCONF: Error:") #define MRCONF_STR_WARNING _("MRCONF: Warning:") #define MRCONF_STR_BAD_FILE _("cannot open the file: ") #define MRCONF_STR_ALLOC_FAILED _("cannot allocate the memory") #define MRCONF_STR_READING_FAILED _("cannot read file into buffer") #define MRCONF_STR_DEFAULT_ERROR _("default") #define MRCONF_STR_FIELD_NOT_FOUND _("the field is not found, field:") #define MRCONF_STR_FIELD_NO_VALUE _("the field :") #define MRCONF_STR_OPEN_OPENED _("attempt to open mr_conf, but it is opened: aborted") #define MRCONF_STR_HALT _("MRCONF: Error occured: immidiate halt") /*warnings*/ #define MRCONF_STR_IGNORE _("has no value, ignoring it") #define MRCONF_STR_CLOSE_BUT_NOT_OPEN _("attempt to close mr_conf but it has not been opened yet") #define MRCONF_STR_CALL_BUT_NOT_OPEN _("attempt to use mr_conf when it has not been opened yet") #define MRCONF_STR_STRING_QUOTE _("string should be surrounded by quotes") #define MRCONF_STR_STRING_ENDQUOTE _("a string is not finished by a quote") /*Flags of internal state*/ #define MRCONF_INTFLAG_OPEN 0x1 /*set if memory allocated */ /* Character for comments */ #define MRCONF_COMM_CHAR '#' /*"private" members declarations*/ static size_t mr_conf_filesize(const char *name); #define mr_conf_error_msg(x, y) {mr_conf_error_msg_int(x, y, __LINE__,__FILE__);} static void mr_conf_error_msg_int(int error_code, const char *add_line, int line, const char *file); static void mr_conf_remove_comments(void); static int mr_conf_check_int_flag(const int flag); static void mr_conf_set_int_flag(const int flag); static void mr_conf_drop_int_flag(const int flag); /*global "private" variables*/ static char *buffer = NULL; /*buffer for configuration file */ static int internal_flags = 0; /*state of the module */ static FILE *CONF = NULL; /* Configuration file FD */ static char *mr_conf_filename = NULL; /* Configuration filename */ /*if output all error and warning messages*/ static int mr_conf_flags = MRCONF_FLAG_VERBOSE; /* * Format of the configuration file is as follows: * * attribute1 = int_value * attribute2 = float_value * attribute3 = string_value */ /*open and read file: each call must be coupled with mr_conf_close function: return 0 if success*/ int mr_conf_open(const char *filename) { size_t length; /*length of the buffer/file */ /* check if mr_conf is already opened? */ if (mr_conf_check_int_flag(MRCONF_INTFLAG_OPEN)) { mr_conf_error_msg(MRCONF_OPEN_OPENED, NULL); return MRCONF_OPEN_OPENED; } length = mr_conf_filesize(filename); CONF = fopen(filename, "r"); mr_asprintf(mr_conf_filename, "%s", filename); /*if file is empty or not exist => error */ if (length == 0) { mr_conf_error_msg(MRCONF_BAD_FILE, filename); return (MRCONF_BAD_FILE); } /*creating and reading buffer for file content */ /*allocate memory for the buffers */ buffer = (char *) mr_malloc(sizeof(char) * (length + 1)); /*set flag that module is in "open" state */ mr_conf_set_int_flag(MRCONF_INTFLAG_OPEN); /*reading file in buffer (skip all 0 characters) */ if (fread(buffer, sizeof(char), length, CONF)) { // FIXME } buffer[length] = (char) 0; /*finalize the string */ if (ferror(CONF)) { mr_conf_error_msg(MRCONF_READING_FAILED, ""); return (MRCONF_READING_FAILED); } /* finally we have to remove all comment lines */ mr_conf_remove_comments(); return MRCONF_NO_ERROR; } /*release all memory and prepare to the next possiable config file*/ void mr_conf_close(void) { /* if not opened => error */ if (!mr_conf_check_int_flag(MRCONF_INTFLAG_OPEN)) { mr_conf_error_msg(MRCONF_CLOSE_BUT_NOT_OPEN, NULL); } mr_free(buffer); fclose(CONF); mr_free(mr_conf_filename); /*set flag that module is in "close" state */ mr_conf_drop_int_flag(MRCONF_INTFLAG_OPEN); } /*read field value after string str in the current file*/ static char *mr_conf_read(const char *field_name) { char *p = NULL; /*pointer to the field */ /* check if mr_conf is not yet opened? */ if (!mr_conf_check_int_flag(MRCONF_INTFLAG_OPEN)) { mr_conf_error_msg(MRCONF_CALL_BUT_NOT_OPEN, NULL); return NULL; } /*read the number */ p = strstr(buffer, field_name); if (p == NULL) { mr_conf_error_msg(MRCONF_FIELD_NOT_FOUND, field_name); return NULL; } else { p += strlen(field_name); while ((*p != '\n') && (*p != '\0') && (*p != '=')) { p++; } if (*p != '=') { mr_conf_error_msg(MRCONF_FIELD_NO_VALUE, field_name); return NULL; } else { /* points after the = sign */ p++; } } /* skip initial spaces and tabs after = */ while ((*p == ' ') || (*p == '\t')) { p++; } return p; } /*read integer number after string str in the current file*/ char *mr_conf_iread(const char *field_name) { char *p = NULL; /*pointer to the field */ char *p1 = NULL; /*return field */ p = mr_conf_read(field_name); if (p != NULL) { mr_asprintf(p1, "%d", p); } return p1; } /*read float/double number after string str in the current file*/ char *mr_conf_fread(const char *field_name) { char *p = NULL; /*pointer to the field */ char *p1 = NULL; /*return field */ p = mr_conf_read(field_name); if (p != NULL) { mr_asprintf(p1, "%f", p); } return p1; } /* */ char *mr_conf_sread(const char *field_name) { char *p = NULL; /*pointer to the field */ char *q = NULL; /*pointer to the field */ char *r = NULL; /*pointer to the field */ char *ret = NULL; /*return value */ int size = 0; /*size of returned string */ int i = 0; ret = NULL; p = mr_conf_read(field_name); if (p == NULL) { return(p); } mr_asprintf(q, "%s", p); if (*p != '"') { mr_conf_error_msg(MRCONF_STRING_QUOTE, field_name); return (NULL); } p++; /* trunk at first \n */ r = index(q,'\n'); r--; if (*r != '"') { mr_conf_error_msg(MRCONF_STRING_QUOTE, field_name); return (NULL); } r--; size = r-q+1; /*copy filtered data to the buffer */ ret = (char *) mr_malloc(sizeof(char) * (size)); while (i < size - 1) { ret[i] = *p; i++; p++; } ret[i] = (char) 0; /* and set its length */ mr_free(q); return ret; } /*read boolean after string str in the current file*/ char *mr_conf_bread(const char *field_name) { char *p = NULL; /*pointer to the field */ char *p1 = NULL; /*pointer to the field */ p = mr_conf_read(field_name); if (p != NULL) { /* match if yes/true/1 */ if ((strncasecmp(p, "y" , (size_t)1) == 0) || (strncasecmp(p, "t" , (size_t)1) == 0) || (strncasecmp(p, "1" , (size_t)1) == 0)) { mr_asprintf(p1, "%d", TRUE); } else { mr_asprintf(p1, "%d", FALSE); } } return p1; } /* Convert a string with decimal value of TRUE/FALSE in boolean */ bool mr_atob(const char *str) { bool ret = FALSE; char *p = NULL; mr_asprintf(p, "%d", FALSE); if (strcmp(str,p) != 0) { ret = TRUE; } mr_free(p); return(ret); } /*removes all comments from the buffer*/ static void mr_conf_remove_comments(void) { char *tmp_buf; /*temporary buffer without comments */ size_t length /*initial length */ ; size_t i; /*iterator */ size_t k; /*conditioned iterator for tmp_buffer */ bool found_quote = FALSE; bool found_comment = FALSE; length = strlen(buffer); /*sizing the new chain */ k = 0; i = 0; while (i < length) { /* Handle quotes to detect strings */ if ((buffer[i] == '"') && (! found_comment)) { if (found_quote) { found_quote = FALSE; } else { found_quote = TRUE; } } /* Handle start of comment - only when not in a string */ if (buffer[i] == MRCONF_COMM_CHAR) { if (found_quote) { found_comment = FALSE; } else { found_comment = TRUE; } } /* Comments end with EOL */ if (buffer[i] == '\n') { found_comment = FALSE; } if (! found_comment) { k++; i++; } else { /* Skip comment */ while ((buffer[i] != '\n') && (buffer[i] != (char) 0)) { i++; } if (buffer[i] == (char) 0) { mr_conf_error_msg(MRCONF_STRING_ENDQUOTE, buffer); } } } /* k is new buffer length now */ tmp_buf = (char *) mr_malloc(sizeof(char) * (k + 1)); k = 0; i = 0; while (i < length) { if (buffer[i] == '"') { if (found_quote) { found_quote = FALSE; } else { found_quote = TRUE; } } if ((buffer[i] != MRCONF_COMM_CHAR) || (found_quote)) { tmp_buf[k++] = buffer[i++]; } else { /* Skip comment as it's not inside a string */ while ((buffer[i] != '\n') && (buffer[i] != (char) 0)) { i++; } } } tmp_buf[k] = (char) 0; /*and set its length */ mr_free(buffer); /*copy filtered data to the buffer */ buffer = tmp_buf; } static int mr_conf_check_int_flag(const int flag) { return (flag & internal_flags); } static void mr_conf_set_int_flag(const int flag) { internal_flags = flag | internal_flags; } static void mr_conf_drop_int_flag(const int flag) { internal_flags = (~flag) & internal_flags; } /*local function to define size of a file. Return 0 for mistake*/ static size_t mr_conf_filesize(const char *name) { FILE *F = fopen(name, "r"); /*file to open */ size_t length; /*number to return */ if (F == NULL) { return 0; } fseek(F, 0, SEEK_END); /*set position to the end of file */ length = ftell(F); /*define number of position=> this is its length */ fclose(F); return length; } /*output error message*/ static void mr_conf_error_msg_int(int error_code, const char *add_line, int line, const char *file) { if ((mr_conf_flags & MRCONF_FLAG_VERBOSE)) { /*if verbose mode */ switch (error_code) { case MRCONF_BAD_FILE: mr_msg_int(1,line,file,"%s (%s) %s %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_BAD_FILE, add_line); break; case MRCONF_READING_FAILED: mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_READING_FAILED); break; case MRCONF_FIELD_NOT_FOUND: mr_msg_int(1,line,file,"%s (%s) %s \"%s\"", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_FIELD_NOT_FOUND, add_line); break; case MRCONF_FIELD_NO_VALUE: mr_msg_int(1,line,file,"%s (%s) %s \"%s\"", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_FIELD_NO_VALUE, add_line); mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_IGNORE); break; case MRCONF_CLOSE_BUT_NOT_OPEN: mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_CLOSE_BUT_NOT_OPEN); break; case MRCONF_CALL_BUT_NOT_OPEN: mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_WARNING, mr_conf_filename, MRCONF_STR_CALL_BUT_NOT_OPEN); break; case MRCONF_OPEN_OPENED: mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_OPEN_OPENED); break; case MRCONF_STRING_QUOTE: mr_msg_int(1,line,file,"%s: %s (%s) %s", add_line, MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_STRING_QUOTE); break; case MRCONF_STRING_ENDQUOTE: mr_msg_int(1,line,file,"%s: %s (%s) %s", add_line, MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_STRING_ENDQUOTE); break; default: mr_msg_int(1,line,file,"%s (%s) %s", MRCONF_STR_ERROR, mr_conf_filename, MRCONF_STR_DEFAULT_ERROR); break; } } }