"SfR Fresh" - the SfR Freeware/Shareware Archive

Member "petidomo-4.0b6/libconfigfile/config.c" of archive petidomo-4.0b6.tar.gz:


As a special service "SfR Fresh" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. That can be also achieved for any archive member file by clicking within an archive contents listing on the first character of the file(path) respectively on the according byte size field.
    1 /*
    2    $Source: /e/ossp/cvs/ossp-pkg/petidomo/libconfigfile/config.c,v $
    3    $Revision: 1.4 $
    4 
    5    Copyright (C) 2000 by CyberSolutions GmbH, Germany.
    6 
    7    This file is part of OpenPetidomo.
    8 
    9    OpenPetidomo is free software; you can redistribute it and/or modify
   10    it under the terms of the GNU General Public License as published by
   11    the Free Software Foundation; either version 2, or (at your option)
   12    any later version.
   13 
   14    OpenPetidomo is distributed in the hope that it will be useful, but
   15    WITHOUT ANY WARRANTY; without even the implied warranty of
   16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
   17    General Public License for more details.
   18 
   19    You should have received a copy of the GNU General Public License
   20    along with OpenPetidomo; see the file COPYING. If not, write to
   21    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   22    Boston, MA 02111-1307, USA.
   23 */
   24 
   25 #include <fcntl.h>
   26 #include <unistd.h>
   27 #include <errno.h>
   28 #include <syslog.h>
   29 
   30 #include "../liblists/lists.h"
   31 #include "../libtext/text.h"
   32 #include "configfile.h"
   33 
   34 char * loadfile(const char *);
   35 
   36 static List Files = NULL;
   37 
   38 /* Remove a trailing carrige return from a string */
   39 
   40 static void
   41 ChopCR(char * p)
   42 {
   43     while (*p != '\0' && *p != '\n')
   44       p++;
   45 
   46     *p = '\0';
   47 }
   48 
   49 /* Turn a string into the number of an array element that matches this
   50    string. */
   51 
   52 static int
   53 FindCFEntry(struct ConfigFile cf[], const char * keyword)
   54 {
   55     int  i;
   56 
   57     for (i = 0; (&(cf[i]))->keyword != NULL; i++) {
   58 	if ((strcasecmp((&(cf[i]))->keyword, keyword)) == 0)
   59 	  return i;
   60     }
   61 
   62     return -1;
   63 }
   64 
   65 /* ReadConfig() will read and parse a given config file and fill the
   66    parsed tags into the ConfigFile structure given on the command
   67    line. The 'ConfigFile' structure consists of three fields: The name
   68    of the config tag to look for, a description of how to interpret
   69    the parameter specified with it and a pointer to a 'void
   70    pointer'-type, which will contain the result when ReadConfig()
   71    returns.
   72 
   73    Valid tag types are CF_STRING (result buffer will contain a string
   74    pointer to the text), CF_INTEGER (parameter will be turned into an
   75    integer and stored into the result buffer) and CF_YES_NO
   76    (parameter is expected to be a 'yes', 'no', 'true' or 'false' and
   77    the appropriate binary setting will be stored).
   78 
   79    RETURNS: A return code of zero (0) indicates success, -1 indicates
   80    failure. In case of a failure, ReadConfig() will log an error
   81    description using the syslog(3) mechanism.
   82 
   83  */
   84 
   85 int
   86 ReadConfig(const char * filename, /* path to the config file to parse */
   87 	   struct ConfigFile cf[] /* structure describing the config tags */
   88 	   )
   89 {
   90     Node         node;
   91     char *       file_buf;
   92     char *       currLine;
   93     char *       nextLine;
   94     char *       keyword;
   95     char *       data;
   96     int          rc;
   97     int          i;
   98     unsigned int numLine;
   99 
  100     /* Initialize list of loaded config files. */
  101 
  102     if (Files == NULL) {
  103 	Files = InitList(NULL);
  104 	if (Files == NULL) {
  105 	    syslog(LOG_ERR, "ReadConfig: Failed to initialize my internal config file list.");
  106 	    return -1;
  107 	}
  108     }
  109     else {
  110 	/* Check whether we already parsed that file. */
  111 
  112 	node = FindNodeByKey(Files, filename);
  113 	if (node != NULL)
  114 	  return 0;
  115     }
  116 
  117     /* Load the file into memory. */
  118 
  119     file_buf = loadfile(filename);
  120     if (file_buf == NULL) {
  121 	syslog(LOG_WARNING, "ReadConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
  122 	return -1;
  123     }
  124 
  125     /* Store the buffer details in the linked list. */
  126 
  127     filename = strdup(filename);
  128     if (filename == NULL) {
  129 	syslog(LOG_ERR, "ReadConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
  130 	return -1;
  131     }
  132     node = AppendNode(Files, filename, file_buf);
  133     if (node == NULL) {
  134 	syslog(LOG_ERR, "ReadConfig: Internal Error: Couldn't add config file data to my list.");
  135 	return -1;
  136     }
  137 
  138     /* Parse the file line by line. */
  139 
  140     for (numLine = 1, nextLine = currLine = file_buf; *currLine != '\0'; currLine = nextLine, numLine++) {
  141 	nextLine = text_find_next_line(currLine);
  142 	ChopCR(currLine);
  143 
  144 	/* Ignore comments or empty lines. */
  145 
  146 	if ((text_easy_pattern_match(currLine, "^[\t ]*#|^[\t ]*$")) == TRUE)
  147 	  continue;		/* ignore it */
  148 
  149 	/* Line is supposed to contain a config statement, so we
  150            should do a consistency check first. If the line passes,
  151            there're no surprises when we actually parse it. */
  152 
  153 	if ((text_easy_pattern_match(currLine, "^[[:alnum:]_-]+[[:space:]]+[^[:space:]]+.*[^[:space:]]+[[:space:]]*$")) == FALSE) {
  154 	    syslog(LOG_WARNING, "ReadConfig: Line \"%s\" is syntactically incorrect.",
  155 		   currLine);
  156 	    continue;		/* ignore it */
  157 	}
  158 
  159 	/* Remove all unnecessary whitespace. */
  160 
  161 	rc = text_transform_text(currLine, currLine, "^([[:alnum:]_-]+)[[:space:]]+([^[:space:]]+.*[^[:space:]]+)[[:space:]]*$", "\\1 \\2");
  162 	if (rc != 0) {
  163 	    syslog(LOG_WARNING, "ReadConfig: Internal error while parsing line: %d.", rc);
  164 	    continue;		/* ignore it */
  165 	}
  166 
  167 	/* Locate the keyword and data part. */
  168 
  169 	for (keyword = currLine; *currLine != ' '; currLine++)
  170 	  ;
  171 	*currLine++ = '\0';
  172 	data = currLine;
  173 
  174 	/* Find appropriate entry in the ConfigFile structure and
  175            check whether we know the keyword. */
  176 
  177 	i = FindCFEntry(cf, keyword);
  178 	if (i == -1) {
  179 	    syslog(LOG_WARNING, "ReadConfig: Unrecognized keyword \"%s\" in file \"%s\", line %d.",
  180 		   keyword, filename, numLine);
  181 	    continue;		/* ignore it */
  182 	}
  183 
  184 	/* Determine type of data and store it appropriately. */
  185 
  186 	switch ((&(cf[i]))->type) {
  187 	  case CF_STRING:
  188 	      /* Handle strings included in quotes. */
  189 
  190 	      rc = text_transform_text(data, data, "^\"([^\"]*)\"$", "\\1");
  191 	      if (rc != 0 && rc != TEXT_REGEX_TRANSFORM_DIDNT_MATCH) {
  192 		  syslog(LOG_WARNING, "ReadConfig: Internal error while parsing file \"%s\", line: %d.",
  193 		      filename, numLine);
  194 		  continue;
  195 	      }
  196 
  197 	      /* Store the string. */
  198 
  199 	      if ((&(cf[i]))->data != NULL)
  200 		*((char **)(&(cf[i]))->data) = data;
  201 	      break;
  202 
  203 	  case CF_INTEGER:
  204 	      /* Integers will have an additional consistency check to
  205                  warn the user about typing errors. */
  206 
  207 	      if ((text_easy_pattern_match(data, "^[-+][[:digit:]]+$|^[[:digit:]]+$")) == FALSE) {
  208 		  syslog(LOG_WARNING, "ReadConfig: Specified parameter \"%s\" in file \"%s\", line %d, is not a number.", data, filename, numLine);
  209 		  continue;		/* ignore it */
  210 	      }
  211 	      if ((&(cf[i]))->data != NULL)
  212 		*((int *)(&(cf[i]))->data) = atoi(data);
  213 	      break;
  214 	  case CF_YES_NO:
  215 	      if ((text_easy_pattern_match(data, "^yes$|^true$|^y$")) == TRUE) {
  216 		  if ((&(cf[i]))->data != NULL)
  217 		    *((bool *)(&(cf[i]))->data) = TRUE;
  218 	      } else if ((text_easy_pattern_match(data, "^no$|^false$|^n$")) == TRUE) {
  219 		  if ((&(cf[i]))->data != NULL)
  220 		    *((bool *)(&(cf[i]))->data) = FALSE;
  221 	      } else {
  222 		  syslog(LOG_WARNING, "ReadConfig: Specified parameter \"%s\" in file \"%s\", line %d, is not a yes or a no.", data, filename, numLine);
  223 		  continue;		/* ignore it */
  224 	      }
  225 	      break;
  226 	  case CF_MULTI:
  227 	      syslog(LOG_WARNING, "ReadConfig: Method not supported at the moment.");
  228 	      break;
  229 	  default:
  230 	      syslog(LOG_ERR, "ReadConfig internal error: ConfigFile structure element %d has unknown type %d.", i, (&(cf[i]))->type);
  231 	}
  232 
  233     }
  234 
  235     return 0;
  236 }
  237 
  238 /* This routine must be used to return all resources to the system,
  239    that have been allocaged by a specific ReadConfig(3) call. After
  240    that, all result buffers provided by ReadConfig() will be invalid. */
  241 
  242 void
  243 FreeConfig(const char * filename)
  244 {
  245     Node   node;
  246 
  247     node = FindNodeByKey(Files, filename);
  248     if (node == NULL)
  249       return;
  250 
  251     free((void *) getNodeData(node));
  252     free((void *) getNodeKey(node));
  253     RemoveNode(node);
  254     FreeNode(node);
  255 }
  256 
  257 /* This function call will free all resources for all previous
  258    ReadConfig(3) calls.
  259 
  260    BUGS: Not implemented at the moment.
  261 */
  262 
  263 void
  264 FreeAllConfigs(void)
  265 {
  266 
  267 }
  268 
  269 static char *
  270 LocateKeywordInBuffer(char * buffer, char * keyword)
  271 {
  272     char *   p;
  273 
  274     for (p = buffer; p != NULL; ) {
  275 	p = text_find_string(p, keyword);
  276 	if (p != NULL) {
  277 	    if (p == buffer || p[-1] == '\n' || p[-1] == '\r')
  278 	      break;
  279 	    else
  280 	      p++;		/* Not a keyword, look again. */
  281 	}
  282     }
  283 
  284     return p;
  285 }
  286 
  287 /* Load a config file and parse it for the required keyword. Then
  288    return the parameter. The buffer containing the parameter should be
  289    free()'d by the caller. #
  290 
  291    RETURNS: If the keyword is not found, NULL is returned and errno is
  292    set to 0. If an error occurs, NULL is returned also, but errno is
  293    set accordingly.
  294 */
  295 
  296 char *
  297 GetConfig(const char * filename, char * keyword)
  298 {
  299     char *   file_buf;
  300     char *   result_buf;
  301     char *   p;
  302     int      rc;
  303 
  304     /* Load the config file. */
  305 
  306     file_buf = loadfile(filename);
  307     if (file_buf == NULL) {
  308 	syslog(LOG_ERR, "GetConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
  309 	return NULL;
  310     }
  311 
  312     /* Find the keyword. */
  313 
  314     p = LocateKeywordInBuffer(file_buf, keyword);
  315 
  316     if (p == NULL) {
  317 	/* No luck, keyword doesn't exist. */
  318 	errno = 0;
  319 	return NULL;
  320     }
  321 
  322     /* Okay, keyword found. Now extract the parameter. */
  323 
  324     ChopCR(p);
  325     rc = text_transform_text(p, p, "^[^\t ]+[\t ]+([^\t ].*)[\t ]*$", "\\1");
  326     if (rc != 0) {
  327 	syslog(LOG_WARNING, "GetConfig: Config file entry \"%s\" is corrupt.", keyword);
  328 	errno = -1;
  329 	return NULL;
  330     }
  331     rc = text_transform_text(p, p, "^\"([^\"]*)\"$", "\\1");
  332     if (rc != 0 && rc != TEXT_REGEX_TRANSFORM_DIDNT_MATCH) {
  333 	syslog(LOG_WARNING, "GetConfig: Config file entry \"%s\" is corrupt.", keyword);
  334 	errno = -1;
  335 	return NULL;
  336     }
  337 
  338 
  339     /* Copy the parameter into a new buffer. */
  340 
  341     result_buf = strdup(p);
  342     if (result_buf == NULL) {
  343 	syslog(LOG_ERR, "GetConfig: Failed to allocate %d byte of memory.", strlen(p));
  344 	errno = ENOMEM;
  345 	return NULL;
  346     }
  347 
  348     /* Free the used memory and return the result. */
  349 
  350     free(file_buf);
  351     return result_buf;
  352 }
  353 
  354 /* This routine will set a given config entry in a config file. When
  355    the keyword is already there, the parameter will be changed.
  356    Otherwise the string "keyword<tab>data" is appended at the end of
  357    the file.
  358 
  359    RETURNS: If something goes wrong, the routine returns -1 and sets
  360    errno. Otherwise we return 0.
  361 
  362    BUGS: This routine may DESTRUCT THE FILE if some I/O error occurs.
  363    It is the caller's responsibility to backup the file before calling
  364    us.
  365 */
  366 
  367 int
  368 SetConfig(const char * filename,
  369 	  char * keyword,	/* string pointer to the tag name */
  370 	  const char * data	/* string pointer to the tag data */
  371 	  )
  372 {
  373     struct flock  lock;
  374     char *        file_buf;
  375     int           file_len;
  376     char *        p;
  377     char *        nextLine;
  378     int           fd;
  379     int           rc;
  380 
  381     /* Load the config file. */
  382 
  383     file_buf = loadfile(filename);
  384     if (file_buf == NULL) {
  385 	syslog(LOG_ERR, "SetConfig: Failed to load config file \"%s\": %s", filename, strerror(errno));
  386 	return -1;
  387     }
  388     file_len = errno;
  389 
  390     /* Find the keyword. */
  391 
  392     p = LocateKeywordInBuffer(file_buf, keyword);
  393 
  394     if (p == NULL) {
  395 	/* No luck, keyword doesn't exist. Append the data. */
  396 
  397 	fd = open(filename, O_WRONLY | O_APPEND, 0666);
  398 	if (fd == -1)
  399 	  goto io_error;
  400 
  401 	lock.l_start  = 0;
  402 	lock.l_len    = 0;
  403 	lock.l_type   = F_WRLCK;
  404 	lock.l_whence = SEEK_SET;
  405 	fcntl(fd, F_SETLKW, &lock);
  406 
  407 	/* Does the file end in a '\n' or must we append one? */
  408 
  409 	if (file_buf[file_len - 1] != '\n') {
  410 	    rc = write(fd, "\n", 1);
  411 	    if (rc == -1)
  412 	      goto io_error;
  413 	}
  414 
  415 	/* Write the actual keyword and data. */
  416 
  417         if ((write(fd, keyword, strlen(keyword)) == -1)
  418 	    || (write(fd, "\t", 1) == -1)
  419 	    || (write(fd, data, strlen(data)) == -1)
  420 	    || (write(fd, "\n", 1) == -1)) {
  421 	    goto io_error;
  422 	}
  423     }
  424     else {
  425 	fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC, 0666);
  426 	if (fd == -1)
  427 	  goto io_error;
  428 
  429 	lock.l_start  = 0;
  430 	lock.l_len    = 0;
  431 	lock.l_type   = F_WRLCK;
  432 	lock.l_whence = SEEK_SET;
  433 	fcntl(fd, F_SETLKW, &lock);
  434 
  435 	nextLine = text_find_next_line(p);
  436 	*p = '\0';
  437 
  438 	/* Write buffer back until the to-be-replaced keyword. */
  439 
  440 	rc = write(fd, file_buf, strlen(file_buf));
  441 	if (fd == -1)
  442 	  goto io_error;
  443 
  444 	/* Write the actual keyword and data. */
  445 
  446         if ((write(fd, keyword, strlen(keyword)) == -1)
  447 	    || (write(fd, "\t", 1) == -1)
  448 	    || (write(fd, data, strlen(data)) == -1)
  449 	    || (write(fd, "\n", 1) == -1)) {
  450 	    goto io_error;
  451 	}
  452 
  453 	/* Write the rest of the buffer. */
  454 
  455 	rc = write(fd, nextLine, strlen(nextLine));
  456 	if (fd == -1)
  457 	  goto io_error;
  458     }
  459 
  460     close(fd);
  461     free(file_buf);
  462     return 0;
  463 
  464 io_error:
  465     syslog(LOG_ERR, "SetConfig: Failed to write to file \"%s\": %s", filename, strerror(errno));
  466     free(file_buf);
  467     return -1;
  468 }