#ident "$URL: svn://elmer/devel/SVN/SDDAS/trunk/libCfg/Pconfig.c $ %D% SwRI" #include #include #include #include #include #include #include #include #include "libCfg.h" #include "Pconfig.h" #include "libCfg_local.h" void process_symbol( config_t *handle, def_t *deflist, char *symbol); int parse_config(config_t *handle, char *prefix, def_t *deflist) { char symbol[SYMBOLSIZE], *token; int deflist_len, i, prefix_len, ret=0; // JM - commented out so we can accept hierarchies of less than 5, undef=0; /* Determine the length of the definition list. Look for the first list element with a NULL value in the param field. */ for (deflist_len = 0; deflist[deflist_len].param != NULL; deflist_len++); /* Find the length of the prefix string. If the value of prefix is NULL, set the length to 0. */ prefix_len = (prefix) ? strlen(prefix) : 0; /* Search the definition list for a param entry that matches symbol. */ for (i = 0; i < deflist_len; i++) { /* If the required field of the current entry is negative, then this entry has already been processed in a previous line of the configuration file. Go to the next definition list entry. */ if (deflist[i].required < 0) { continue; } /* Initialize a blank symbol, concatenate the prefix, if any, then add the param to search for. */ memset(symbol, 0, SYMBOLSIZE); if (prefix_len) { strcat(symbol, prefix); } strcat(symbol, deflist[i].param); /* Look for the symbol in the symbol table, if the symbol cannot be found send a message to stderr and set ret to -1. */ if ((token = get_symbol(handle, symbol)) == NULL) { /* * JM - commented this out so we could accept hierarchies of less than 5 * if (deflist[i].required == IS_REQUIRED) { if (!undef) { fprintf(stderr, "parse_config: undefined symbol(s)\n"); undef = 1; } fprintf(stderr, "\t%s\n", symbol); ret = -1; } continue; */ ret = -1; continue; } /* Found the symbol in question, process the symbol and assign its value to the value field of the def list. */ process_symbol(handle, &deflist[i], token); } /* Return ret to indicate the success or failure of the routine. */ return ret; } void process_symbol( config_t *handle, def_t *deflist, char *symbol) { char **ptr_str, *end, *token; int ival, *ptr_ival = NULL, i, frst; short *ptr_sval = NULL; float *ptr_fval; double dval, *ptr_dval; /* Process the value symbol in accordance with the type field of the definition list entry. */ switch (deflist->type) { case T_CHAR: /* If the type is char the value symbol can have the form of a single character between single quotes (i.e. '*') or it can be an integer in base 8, 10, or 16 (C conventions for the formats). Determine which it is and process accordingly. */ if (symbol[0] == '\'' && symbol[2] == '\'') { /* For the form '*', take the character between the single quotes and store it in the location pointed to by the value field of the definition list entry. Give the required field a negative value. */ *((char *) (deflist->value)) = symbol[1]; if (deflist->required > 0) { deflist->required = -deflist->required; } break; } else { /* Try to interpret the symbol as an integer value which is stored in ival. If the conversion is successful and the character following the end of the conversion is a '\0' or whitespace, process the value. */ ival = strtol(symbol, &end, 0); if (symbol != end && (*end == '\0' || isspace((int) *end))) { /* If the absolute value of ival is greater than can be stored in 8 bits write a warning message to stderr. */ if (abs(ival) > 255) { fprintf(stderr, "parse_config: Value out of range for char in\n"); fprintf(stderr, "\t%s\n", deflist->param); } else { /* If ival is valid as an 8 bit integer, store it in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ *((char *) (deflist->value)) = ival; if (deflist->required > 0) { deflist->required = -deflist->required; } } break; } } /* If the symbol could not be successfully converted, write a warning message to stderr. */ fprintf(stderr, "parse_config: Expected a char in\n"); fprintf(stderr, "\t%s\n", deflist->param); break; case T_SHORT: case T_INT: /* Try to interpret the symbol as an integer value which is stored in ival. If the conversion is successful and the character following the end of the conversion is a '\0' or whitespace, process the value. If the symbol could not be successfully converted, write a warning message to stderr. */ ival = strtol(symbol, &end, 0); if (symbol == end || (*end != '\0' && !isspace((int) *end))) { fprintf(stderr, "parse_config: Expected an %s for\n", deflist->type==T_INT ? "int" : "short"); fprintf(stderr, "\t%s -> %s\n", deflist->param, symbol); } else { if (deflist->type == T_SHORT) { /* If the absolute value of ival is greater than can be stored in 16 bits write a warning message to stderr (for a short only). */ if (abs(ival) > 65535) { fprintf(stderr, "parse_config: Value out of range for short in\n"); fprintf(stderr, "\t%s\n", deflist->param); } else { /* If ival is valid as a 16 bit integer, store it in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ *((short *) (deflist->value)) = ival; } } else { /* Store ival in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ *((int *) (deflist->value)) = ival; } if (deflist->required > 0) { deflist->required = -deflist->required; } } break; case T_FLOAT: case T_DOUBLE: /* Try to interpret the symbol as a floating point value which is stored in dval. If the conversion is successful and the character following the end of the conversion is a '\0' or whitespace, process the value. */ dval = strtod(symbol, &end); if (symbol == end || (*end != '\0' && !isspace((int) *end))) { fprintf(stderr, "parse_config: Expected a %s in\n", deflist->type==T_FLOAT ? "float" : "double"); fprintf(stderr, "\t%s -> %s\n", deflist->param, symbol); } else { /* If the absolute value of dval is outside the valid range for a float, write a warning message to stderr. */ if (deflist->type == T_FLOAT) { if (fabs(dval) > FLT_MAX || fabs(dval) < FLT_MIN) { fprintf(stderr, "parse_config: Value out of range for float in\n"); fprintf(stderr, "\t%s\n", deflist->param); } else { /* If dval is valid as a float, store it in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ *((float *) (deflist->value)) = dval; } } else { *((double *) (deflist->value)) = dval; } if (deflist->required > 0) { deflist->required = -deflist->required; } } break; case T_STRING: case T_STRPTR: /* Try to interpret the symbol as a string of characters delimited by double quotes. Associate symbol with the first character after the first quote and end with the location of the ending quote. If an ending quote cannot be found to match the first, write a warning message to stderr. */ if (symbol[0] == '"') { end = symbol += 1; for (; *end != '\0' && *end != '"'; end++); if (*end != '"') { fprintf(stderr, "parse_config: Unbalanced quotes for\n"); fprintf(stderr, "\t%s -> %s\n", deflist->param, symbol); } else { /* NULL delimit the symbol string and copy it to the location pointed to by the value field of the current definition list entry. Give the required field of the entry a negative value. */ *end = '\0'; if (deflist->type == T_STRING) { strcpy((char *) (deflist->value), symbol); } else { (deflist->value) = (char*)strdup(symbol); } if (deflist->required > 0) { deflist->required = -deflist->required; } } } else { /* If the symbol could not be successfully converted, write a warning message to stderr. */ fprintf(stderr, "parse_config: Expected a string for\n"); fprintf(stderr, "\t%s -> %s\n", deflist->param, symbol); } break; case T_SHORT_ARRY: case T_INT_ARRY: if (deflist->type == T_SHORT_ARRY) ptr_sval = (short *) calloc(1, sizeof(short)); else ptr_ival = (int *) calloc(1, sizeof(int)); token = strtok(symbol, " "); frst = 1; i = 0; do { ival = strtol(token, &end, 0); if (token == end || (*end != '\0' && !isspace((int) *end))) { fprintf(stderr, "parse_config: Expected an %s value for\n", deflist->type == T_INT_ARRY ? "int" : "short"); fprintf(stderr, "\t%s[%d] -> %s\n", deflist->param, i, token); break; } else { if (deflist->type == T_SHORT_ARRY) { /* If the absolute value of ival is greater than can be stored in 16 bits write a warning message to stderr (for a short only). */ if (abs(ival) > 65535) { if (frst) { fprintf(stderr, "parse_config: Value(s) out of range for a short:\n"); frst = 0; } fprintf(stderr, "\t%s[%d] -> %s\n", deflist->param, i, token); ival = 0; } /* If ival is valid as a 16 bit integer, store it in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ ptr_sval = (short *) realloc(ptr_sval, (i+1) * sizeof(short)); *((short **) (deflist->value)) = ptr_sval; *(ptr_sval+i++) = ival; } else { /* Store ival in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ ptr_ival = (int *) realloc(ptr_ival, (i+1) * sizeof(int)); *((int **) (deflist->value)) = ptr_ival; *(ptr_ival+i++) = ival; } if (deflist->required > 0) { deflist->required = -deflist->required; } } token = strtok(NULL, " "); } while (token); deflist->n_values = i; break; case T_FLOAT_ARRY: ptr_fval = (float *) calloc(1, sizeof(float)); token = strtok(symbol, " "); frst = 1; i = 0; do { /* Try to interpret the symbol as a floating point value which is stored in dval. If the conversion is successful and the character following the end of the conversion is a '\0' or whitespace, process the value. */ dval = strtod(token, &end); if (token == end || (*end != '\0' && !isspace((int) *end))) { fprintf(stderr, "parse_config: Expected a float value for\n"); fprintf(stderr, "\t%s[%d] -> %s\n", deflist->param, i, token); break; } else { /* If the absolute value of dval is outside the valid range for a float, write a warning message to stderr. */ if (fabs(dval) > FLT_MAX || fabs(dval) < FLT_MIN) { if (frst) { fprintf(stderr, "parse_config: Value out of range for float in\n"); frst = 0; } fprintf(stderr, "\t%s\n", deflist->param); dval = 0; } else { /* If dval is valid as a float, store it in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ ptr_fval = (float *) realloc(ptr_fval, (i+1) * sizeof(float)); *((float **) (deflist->value)) = ptr_fval; *(ptr_fval+i++) = dval; } } if (deflist->required > 0) { deflist->required = -deflist->required; } token = strtok(NULL, " "); } while (token); deflist->n_values = i; break; case T_DOUBLE_ARRY: ptr_dval = (double *) calloc(1, sizeof(double)); token = strtok(symbol, " "); frst = 1; i = 0; do { /* Try to interpret the symbol as a floating point value which is stored in dval. If the conversion is successful and the character following the end of the conversion is a '\0' or whitespace, process the value. */ dval = strtod(token, &end); if (token == end || (*end != '\0' && !isspace((int) *end))) { fprintf(stderr, "parse_config: Expected a float value for\n"); fprintf(stderr, "\t%s[%d] -> %s\n", deflist->param, i, token); break; } else { /* If the absolute value of dval is outside the valid range for a float, write a warning message to stderr. */ if (fabs(dval) > DBL_MAX || fabs(dval) < DBL_MIN) { if (frst) { fprintf(stderr, "parse_config: Value out of range for double in\n"); frst = 0; } fprintf(stderr, "\t%s\n", deflist->param); dval = 0; } else { /* If dval is valid as a float, store it in the location pointed to by the value field of the definition list entry. Give the required field of the entry a negative value. */ ptr_dval = (double *) realloc(ptr_dval, (i+1) * sizeof(double)); *((double **) (deflist->value)) = ptr_dval; *(ptr_dval+i++) = dval; } } if (deflist->required > 0) { deflist->required = -deflist->required; } token = strtok(NULL, " "); } while (token); deflist->n_values = i; break; case T_STRPTR_ARRY: ptr_str = (char **) calloc(1, sizeof(char *)); token = symbol; i = 0; do { /* Try to interpret the symbol as a string of characters delimited by double quotes. Associate symbol with the first character after the first quote and end with the location of the ending quote. If an ending quote cannot be found to match the first, write a warning message to stderr. */ if (token[0] == '"') { end = token += 1; for (; *end != '\0' && *end != '"'; end++); if (*end != '"') { fprintf(stderr, "parse_config: Unbalanced quotes for\n"); fprintf(stderr, "\t%s[%d] -> %s\n", deflist->param, i, token); } else { /* If the last character is a *, null it out, needed to label SCFs */ if (*(end - 1) == '*') { *end = '\0'; end--; } /* NULL delimit the symbol string and copy it to the location pointed to by the value field of the current definition list entry. Give the required field of the entry a negative value. */ *end = '\0'; /* NULL delimit the symbol string and copy it to the location pointed to by the value field of the current definition list entry. Give the required field of the entry a negative value. */ ptr_str = (char **) realloc(ptr_str, (i+1) * sizeof(char *)); deflist->value = (void *)ptr_str; *(ptr_str+i) = strdup(token); i++; if (deflist->required > 0) { deflist->required = -deflist->required; } } for (token = end+1; *token != '\0' && isspace((int) *token); token++); } else { /* If the symbol could not be successfully converted, write a warning message to stderr. */ fprintf(stderr, "parse_config: Expected a string for\n"); fprintf(stderr, "\t%s[%d] -> %s\n", deflist->param, i, token); end = token; for (token = end+1; *token != '\0' && !isspace((int) *token); token++); } } while (*token); deflist->n_values = i; break; default: /* If the conversion type specification in the current definition list entry is unknown, report the error to stderr. */ fprintf(stderr, "parse_config: Unknown type expected in %s\n", deflist->param); break; } } /* Prototypes of all internal routines. */ char *pc_get_line(FILE *); char *pc_grow(char **, int); config_t *pc_create_config(); config_t *pc_destroy_config(config_t *); config_t *pc_load_config(FILE *, config_t *); FILE *pc_open_input(char *, int); int pc_add_symbol(config_t *, char *, char *); int pc_get_tokens(FILE *, char **, char **); void pc_close_input(FILE *, int); /* global for line buffer */ static char *GLOBAL_line_buffer = NULL; static int GLOBAL_line_bufsize = 0; config_t * open_config(char *file, int pre) { FILE *fp; config_t *ch; /* Get the stream file pointer. If there is an error, ch is set to NULL. */ if ((fp = pc_open_input(file, pre)) == NULL) { ch = CNULL; } /* Create the config symbol table. If there is an error, ch is set to NULL. */ else if ((ch = pc_create_config()) == CNULL) { ch = CNULL; } /* Load the config symbol table. If there is an error, ch is set to NULL. */ else if ((ch = pc_load_config(fp, ch)) == CNULL) { ch = CNULL; } /* If the input was not stdin and not NULL, close the input stream. */ if (fp != stdin && fp != NULL) pc_close_input(fp, pre); /* Return ch to the calling routine. */ return ch; } void close_config(config_t *ch) { /* Call pc_destroy_config to deallocate the config structures. */ (void)pc_destroy_config(ch); } void refresh_config(config_t *ch) { int i, j; /* Reset the referenced flag for all used symbol table elements. */ for (i = 0; i < CONFIG_TABLE_WIDTH; i++) { for (j = 0; j < ch->used[i]; j++) { ch->symbols[i][j].referenced = 0; } } } char * get_symbol(config_t *ch, char *param) { char *value; int i, index, used; symb_t *symbols; /* Initialize value to NULL. Get the index to the symbol array to use from the first character of the param modulo the table width. Get local copies of the symbols and used array elements for the index location. */ value = NULL; index = *param % CONFIG_TABLE_WIDTH; symbols = ch->symbols[index]; used = ch->used[index]; /* Check symbols to see if it is NULL. If it is not, continue. Compare each used element of the symbols array with param to see if there is a match. If there is and the element has not been previously referenced, return the value pointer to the calling routine. Set the referenced flag for the element. */ if (symbols != SNULL) { for (i = 0 ; i < used ; i++, symbols++) { if (!symbols->referenced && !strcmp(symbols->param, param)) { value = symbols->value; symbols->referenced = 1; break; } } } /* Return value to the calling routine. */ return value; } config_t * pc_load_config(FILE *fp, config_t *ch) { char *param, *value; int tokens; /* Get parameter/value token pairs from the input stream. Get_tokens returns a -1 when no more tokens are available. */ while ((tokens = pc_get_tokens(fp, ¶m, &value)) != -1) { /* If both a parameter and a value were matched, add the symbol pair to the symbol table. If there is an error adding the symbol, destroy the config structures and return a NULL. */ if (tokens == 2) { if (pc_add_symbol(ch, param, value) != 0) { ch = pc_destroy_config(ch); break; } } } /* Return the config structure pointer to the calling routine. */ return ch; } config_t * pc_create_config() { config_t *ch; /* Allocate a config_t structure and return a pointer to it to the calling program. If there is an error, report it. */ if ((ch = (config_t *)calloc(1, sizeof(config_t))) == CNULL) { perror("open_config.pc_create_config.calloc"); } return ch; } config_t * pc_destroy_config(config_t *ch) { int i, j; /* Free all used symbol table element param and value strings. Free all the symbol table arrays in the config structure. */ for (i = 0 ; i < CONFIG_TABLE_WIDTH ; i++) { for (j = 0 ; j < ch->used[i] ; j++) { if (ch->symbols[i][j].param != NULL) { (void)free(ch->symbols[i][j].param); } if (ch->symbols[i][j].value != NULL) { (void)free(ch->symbols[i][j].value); } } if (ch->symbols[i] != SNULL) { (void)free((char *)ch->symbols[i]); } } /* Free the config structure. */ (void)free((char *)ch); /* Return a NULL to the calling routine. */ return 0x0; } FILE * pc_open_input(char *file, int pre) { FILE *fp; char command[256], *pp, *opts; /* If pre is non-zero, the config file will be run through cpp first. In that case, the file pointer will be the output stream pointer from the popen call. If there is an error in opening the pipe to cpp, report the error on stderr. */ if (pre) { pp = getenv("PC_PP"); opts = getenv("PC_PPOPTS"); if (pp == NULL) pp = "/usr/lib/cpp -P -Y."; if (opts == NULL) opts = " "; if (file == NULL) file = "-"; (void)snprintf(command, 256, "%s %s %s", pp, opts, file); if ((fp = popen(command, "r")) == NULL) { perror("open_config.pc_open_input.popen"); } } /* If the pre option is zero, open the configuration file. If there is no file name given or the argument is "-" then assume the file is piped on stdin. Otherwise, open the file. If there is an error in opening the file, report the error on stderr. */ else if (file[0] == '\0' || file[0] == '-') { fp = stdin; } else { if ((fp = fopen(file, "r")) == NULL) { perror("open_config.pc_open_input.fopen"); } } /* Return the stream file pointer. This will be NULL if there was an error. */ return fp; } void pc_close_input(FILE *fp, int pre) { /* If preprocessing was enabled, close the stream with pclose. Otherwise, close the stream with fclose. */ if (pre) { (void)pclose(fp); } else { (void)fclose(fp); } free(GLOBAL_line_buffer); GLOBAL_line_buffer = 0; GLOBAL_line_bufsize = 0; } int pc_get_tokens(FILE *fp, char **oparam, char **ovalue) { char *end, *param, *value; char *line; int esc; /* Initialize oparam and ovalue to NULL. */ *oparam = *ovalue = NULL; /* Get a line from the input stream. If pc_get_line returns a NULL, then the input stream has terminated. In that case return a -1 to the calling routine. */ if ((line = pc_get_line(fp)) == NULL) { return -1; } /* Find the first non-whitespace character in line and associate the pointer param with that location. */ for (param = line ; isspace((int) *param) ; param++); /* If the first non-whitespace character is '#' or '\0', then the line is a comment or empty. In that case return a 0 to indicate no param or value found. */ if (*param == '#' || *param == '\0') { return 0; } /* Find the first character that follows that is not alphanumeric or punctuation. */ for (end = param ; (isalnum((int) *end) || ispunct((int) *end)) ; end++); /* If the end character is not whitespace, report an error to stderr and return 0. */ if (!isspace((int) *end)) { (void)fprintf(stderr, "open_config.pc_get_tokens: "); (void)fprintf(stderr, "Non-whitespace terminating name field\n"); (void)fprintf(stderr, "---->%s\n", line); (void)fflush(stderr); return 0; } /* Write a '\0' at the end of the param string. */ *end = '\0'; /* Set oparam to point to the location in line indicated by param. */ *oparam = param; /* Set value to point at the remainder of the line. Advance to the first non-whitespace character. */ for (value = end + 1 ; isspace((int) *value) ; value++); /* If the first non-whitespace character is a '#' or '\0', then the end of the line was reached or the remainder of the line is a comment and there was no value field. In this case report an error and return 1. */ if (*value == '#' || *value == '\0') { (void)fprintf(stderr, "open_config.pc_get_tokens: "); (void)fprintf(stderr, "No parameter value field found\n"); (void)fprintf(stderr, "---->%s\n", line); (void)fflush(stderr); return 1; } /* Find the end of the definition value field in the string pointed to by value. The value field is defined to be composed of any character except '#', unless the '#' is escaped by a backslash, like '\#'. Anything on a line following an unescaped '#' is a comment and is ignored. Trailing white space between the last non-whitespace character and the end of the line or beginning of the comment is ignored. A '\0' is written after the last element of the value field. */ for (end = value, esc = 0; *end != '\0' ; end++) { if (esc) { esc = 0; } else if (*end == '\\') { esc = 1; } else if (*end == '#') { break; } } for (end-- ; isspace((int) *end) ; end--); *(++end) = '\0'; /* Set ovalue to point to the location in line indicated by value. */ *ovalue = value; /* Return a 2 to indicate that a param and a value have been found. */ return 2; } char * pc_get_line(FILE *fp) { char *bp, *end; char line[256]; int buflen, eof, esc, line_len; /* Initialize buflen and eof to zero. */ buflen = eof = 0; /* Get a line from the input stream and append it to the contents of buffer. If the line ends with an escaped newline or no newline, get another line and append it to buffer. Continue until an unescaped newline is read or the input stream terminates. If the input terminates, set Eof to indicate the termination. */ do { if (fgets(line, 256, fp) == NULL) { eof = 1; break; } /* Get the length of the line. */ line_len = strlen(line); /* If the length of the line plus the current contents of the buffer is greater than the length of the buffer, grow the buffer. If there is an error, report it and return NULL. */ if (line_len + buflen > GLOBAL_line_bufsize) { GLOBAL_line_bufsize = buflen + line_len; if (pc_grow(&GLOBAL_line_buffer, GLOBAL_line_bufsize) == NULL) { perror("open_config.pc_get_line.pc_grow"); return NULL; } } /* Copy the line into buffer. While copying, scan the line to get the context of any ending newline. Anytime a backslash occurs, consider the next character to be escaped. When a newline is reached, end the search. The status of the newline will be shown in esc. If the line did not end on a newline, set esc as well, since it will indicate a need to read more input. Terminate the copy into buffer with a '\0'. Note that the newlines are not copied into buffer, and that the back- slash escaping a final newline is not kept either. */ bp = &GLOBAL_line_buffer[buflen]; for (end = line, esc = 0; *end != '\0' ; buflen++) { if (esc) { if ((*end == '\n') || (*end == '\r')) { buflen--; bp--; break; } else { esc = 0; } } else if (*end == '\\') { esc = 1; } else if (*end == '\n') { break; } *bp++ = *end++; } esc = (*end == '\0') ? 1 : esc; *bp = '\0'; /* As long as esc is set, indicating that more input needs to read, continue. */ } while (esc); /* If buflen is zero and eof is set when we are finished then no input was read and the input stream has terminated. In that case return a NULL, otherwise return a pointer to buffer. */ return ((!buflen && eof) ? NULL : GLOBAL_line_buffer); } int pc_add_symbol(config_t *ch, char *param, char *value) { int elements, index, used; symb_t *symbols; /* Get the index to the symbol array to use from the first character of the param modulo the table width. Get local copies of the symbols, elements, and used array elements for the index location. */ index = *param % CONFIG_TABLE_WIDTH; symbols = ch->symbols[index]; elements = ch->elements[index]; used = ch->used[index]; /* If the symbols value is NULL or all the allocated elements have been used, then grow symbols by a basic unit of symbols. Increment the elements value for this location. If there is an error, report it and return -1. Copy the local values of symbols and elements back to the array locations in the config structure. */ if (symbols == SNULL || used == elements) { elements += ALLOC_ATOM; if (pc_grow((char **)&symbols, elements * sizeof(symb_t)) == NULL) { perror("open_config.pc_add_symbol.pc_grow"); return -1; } ch->symbols[index] = symbols; ch->elements[index] = elements; } /* Copy the param and value strings into allocated storage and associate the param and value pointers in the first unused symbols array element. Set the referenced field of the symbols array element to zero. If there is an error, report it and return -1. If the error is in the second strdup call, free the first allocated string. Increment the used value for this location. */ if ((symbols[used].param = strdup(param)) == NULL) { perror("open_config.pc_add_symbol.strdup(param)"); return -1; } if ((symbols[used].value = strdup(value)) == NULL) { perror("open_config.pc_add_symbol.strdup(value)"); (void)free(symbols[used].param); return -1; } symbols[used].referenced = 0; ch->used[index]++; /* Return a zero to indicate success. */ return 0; } char * pc_grow(char **buffer, int len) { /* If the buffer argument is NULL, then allocate a block of memory of the length specified. If there is an error, the NULL value of buffer will be returned. */ if (*buffer == NULL) { *buffer = (char *) malloc((unsigned)len); } /* If the buffer argument is not NULL, then reallocate a block of memory of the length specified. If there is an error, the NULL value of buffer will be returned. */ else { *buffer = (char *) realloc(*buffer, (unsigned)len); } /* Return the address stored in buffer. */ return *buffer; }