Robert said:
I am converting a Perl script over to "C" for a potential open source
project. I need some open source "C" code that will give me the same
functionality of a Perl Style associative array:
someArray["a_key_label"] = 6;
I know I can't get the same syntactic sugar as Perl offers, with the usage
of a string as the array key surrounded by square brackets. I just want the
general functionality, that's all. That is, a data container that will
maintain an internal list of key value pairs, with the ability to walk the
keys in sorted order. Also, the container should "auto-insert" a key-value
pair if it doesn't already exist in the container, when a particular key
value pair is referenced.
Do any of you know of any open source "C" code that provides a container
with the same behavior as Perl style arrays? URL's if you got 'em please.
Thanks.
You will have to do the sorting yourself, however I have attached my
implementation which I use in other projects that works great for this
type of thing (it's actually a configuration parser). Hope this helps.
Joe Estock
--BEGIN config.h:
/* $Id: config.h,v 1.3 2005/03/06 00:23:49 joe Exp $ */
#ifndef _CONFIG_H
#define _CONFIG_H
struct config_settings
{
char **key;
char **value;
};
int config_set(struct config_settings *, char *, char *);
int config_set_ex(struct config_settings *, int, char *);
char *config_get(struct config_settings *, char *);
char *config_get_ex(struct config_settings *, int);
void rehash_config(struct config_settings *, char *);
int save_config(struct config_settings *, char *);
void deinit_config(struct config_settings *);
void trim(char *);
int init_config(struct config_settings *, char *);
#endif /* !defined(_CONFIG_H) */
--END config.h
--BEGIN config.c
/* $Id: config.c,v 1.6 2005/03/12 07:59:40 joe Exp $ */
/**
* This is a general configuration parser. Nothing fancy
* is done here and nothing specific to the main configuration
* file is handled here. This file contains the building blocks
* for handling the configuration files by parsing the file and
* splitting up the various configuration settings and their
* values.
*/
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "config.h"
#include "stdbool.h"
/**
* Sets the specified key with the specified value. If an
* error is encountered, -1 is returned otherwise the index
* of the newly inserted key is returned.
*
* @param config Pointer to a previously malloced config_settings
* @param key Key to change or set
* @param value value for key
* @return -1 on failure, index of item otherwise
*/
int config_set(struct config_settings *config, char *key, char *value)
{
unsigned int i;
if(key == NULL || config == NULL || config->key == NULL)
{
return(-1);
}
for(i = 0; config->key
!= NULL; i++)
{
if(!strcasecmp(config->key, key))
{
return(config_set_ex(config, i, value));
}
}
return(-1);
}
/**
* Sets the specified key with the specified value. If an
* error is encountered, -1 is returned otherwise the index
* of the newly inserted key is returned.
*
* @see config_set()
* @param config Pointer to a previously malloced config_settings
* @param index index of key to change or set
* @param value value for key
* @return -1 on failure, index of item otherwise
*/
int config_set_ex(struct config_settings *config, int index, char *value)
{
char *tmp;
if((tmp = malloc(strlen(value) + 1)) == NULL)
{
perror("malloc");
return(-1);
}
memset(tmp, '\0', strlen(value) + 1);
strcpy(tmp, value);
free(config->value[index]);
config->value[index] = tmp;
return(index);
}
/**
* Returns the value associated with key
*
* @param config Pointer to a previously malloced config_settings
* @param key Key to retreive value for
* @return Value of key or NULL if nonexistant or empty
*/
char *config_get(struct config_settings *config, char *key)
{
unsigned int i;
if(key == NULL || config == NULL || config->key == NULL)
{
return(NULL);
}
for(i = 0; config->key != NULL; i++)
{
if(!strcasecmp(config->key, key))
{
return(config->value);
}
}
return(NULL);
}
/**
* Returns the value associated with key
*
* @see config_get()
* @param config Pointer to a previously malloced config_settings
* @param index Index of key to retreive value for
* @return Value of key or NULL if nonexistant or empty
*/
char *config_get_ex(struct config_settings *config, int index)
{
return(config->value[index]);
}
/**
* Reloads the configuration file
*
* @see save_config()
* @param config Pointer to a previously malloced config_settings
* @param filename Filename to read configuration data from
*/
void rehash_config(struct config_settings *config, char *filename)
{
deinit_config(config);
init_config(config, filename);
}
/**
* Saves the new configuration parameters to specified file
*
* @see rehash_config()
* @param config Pointer to a previously malloced config_settings
* @param filename Filename to store keys and values in
* @return true on success, false on failure
*/
bool save_config(struct config_settings *config, char *filename)
{
FILE *fp;
unsigned int i;
if(config == NULL || config->key == NULL || filename == NULL)
{
return(false);
}
if((fp = fopen(filename, "w")) == NULL)
{
return(false);
}
fprintf(fp, "# %s - %s\n",
filename,
"Automatically generated by geekbot via save_config");
for(i = 0; config->key != NULL; i++)
{
fprintf(fp, "%s = \"%s\"\n", config->key, config->value == NULL
? "" : config->value);
}
fclose(fp);
return(true);
}
/**
* Free up any resources used for specified configuration
*
* @see init_config()
* @param config Pointer to a previously malloced config_settings
*/
void deinit_config(struct config_settings *config)
{
unsigned int i;
if(config == NULL || (config->key == NULL && config->value == NULL))
{
return;
}
for(i = 0; config->key != NULL; i++)
{
if(config->key != NULL) { free(config->key); }
if(config->value != NULL) { free(config->value); }
}
free(config->key);
free(config->value);
}
/**
* Removes leading and trailing spaces as well as any
* newlines from the end of a string
*
* @param s string to trim
*/
void trim(char *s)
{
unsigned int len = strlen(s);
unsigned int i = 0;
if(len == 0)
{
return;
}
for(i = 0; i < len && isspace(s); i++) ; /* nothing */
if(i > 0)
{
memmove(s, &s, strlen(&s) + 1);
}
if((len = strlen(s)) == 0)
{
return;
}
i = len;
while(s[i - 1] == '\r' || s[i - 1] == '\n' || isspace(s[i - 1]))
{
s[i - 1] = '\0';
i--;
}
/*while(s[strlen(s) - 1] == '\r' || s[strlen(s) - 1] == '\n' ||
isspace(s[strlen(s) - 1]))
{
s[strlen(s) - 1] = '\0';
}*/
}
/**
* Initializes the configuration found in specified filename
*
* @param config Pointer to a previously malloced config_settings
* @param filename Filename to read keys and values from
* @return true on success, false on failure
*/
int init_config(struct config_settings *config, char *filename)
{
FILE *fp;
char buf[2048];
char *tokenptr;
unsigned int i;
char **test;
if(config == NULL)
return(false);
if(!(fp = fopen(filename, "r")))
{
return(false);
}
config->key = malloc(sizeof(char *) * 1);
config->value = malloc(sizeof(char *) * 1);
config->key[0] = NULL;
config->value[0] = NULL;
memset(buf, '\0', 2048);
i = 0;
while(!feof(fp))
{
memset(buf, '\0', 2048);
if(fgets(buf, 2048, fp) == NULL)
break;
trim(buf);
if(buf == NULL || strlen(buf) == 0 || buf[0] == '#')
continue;
tokenptr = strtok(buf, "=");
if(tokenptr != NULL)
{
trim(tokenptr);
if((config->key = malloc(strlen(tokenptr) + 1)) == NULL)
return(false);
memset(config->key, '\0', strlen(tokenptr) + 1);
strcpy(config->key, tokenptr);
tokenptr = strtok(NULL, "=");
if(tokenptr != NULL)
{
trim(tokenptr);
if(tokenptr[0] == '"')
memmove(tokenptr, &tokenptr[1], strlen(&tokenptr[1]) + 1);
if(tokenptr[strlen(tokenptr) - 1] == '"')
tokenptr[strlen(tokenptr) - 1] = '\0';
if(strlen(tokenptr) == 0)
{
config->value = NULL;
}
else
{
if((config->value = malloc(strlen(tokenptr) + 1)) == NULL)
return(false);
memset(config->value, '\0', strlen(tokenptr) + 1);
strcpy(config->value, tokenptr);
}
}
else
{
config->value = malloc(2);
strcpy(config->value, "\0");
}
i++;
if((test = realloc(config->key, sizeof(char *) * (i + 1))) == NULL)
return(false);
config->key = test;
config->key = NULL;
if((test = realloc(config->value, sizeof(char *) * (i + 1))) == NULL)
return(false);
config->value = test;
config->value = NULL;
}
}
fclose(fp);
return(true);
}
--END config.c