#include "arc/std/config.h" #include "arc/std/errno.h" #include "arc/std/io.h" #include #include #include #include struct ARC_Config { ARC_Hashtable *currgroup; ARC_Hashtable *groups; ARC_Hashtable *keys; }; typedef struct ARC_ConfigKey { ARC_ConfigKeyRead Read; ARC_ConfigKeyDelete Delete; } ARC_ConfigKey; typedef struct ARC_ConfigTypeTemplate { ARC_ConfigKey *key; void *data; } ARC_ConfigTypeTemplate; typedef struct ARC_DeleteUserData { ARC_Config *config; const char* data; ARC_StringSubstr *subdata; } ARC_DeleteUserData; int32_t ARC_Config_KeyComp(void *key1, size_t *key1size, void *key2, size_t *key2size){ if(*key1size - *key2size){ return -1; } return strncmp((const char *)key1, (const char *)key2, *key1size); } int32_t ARC_ConfigKey_Add(ARC_Config *config, char *type, ARC_ConfigKeyRead keyRead, ARC_ConfigKeyDelete keyDelete){ ARC_ConfigKey *temp = (ARC_ConfigKey *)malloc(sizeof(ARC_ConfigKey)); temp->Read = keyRead; temp->Delete = keyDelete; return ARC_Hashtable_Add(config->keys, (void *)type, strlen(type), (void *)temp); } int32_t ARC_ConfigKey_Destroy(ARC_HashtableNode *node, void *userdata){ if(!node->data){ return ARC_ERRNO_NULL; } free((ARC_ConfigKey *)node->data); return 0; } int32_t ARC_ConfigGroup_Create(ARC_Config *config, const char *name){ ARC_Hashtable *data; ARC_Hashtable_Create(&data, ARC_GROUP_DATA_BUCKET_SIZE, NULL, ARC_Config_KeyComp); char *group = (char *) malloc(sizeof(char) * strlen(name)); strncpy(group, name, strlen(name)); return ARC_Hashtable_Add(config->groups, (void *)group, strlen(name), (void *)data); } int32_t ARC_ConfigGroupNode_Destroy(ARC_HashtableNode *node, void *userdata){ free((char *)node->key); ARC_ConfigTypeTemplate *temp = (ARC_ConfigTypeTemplate *)node->data; if(temp->key){ ARC_DeleteUserData *deldata = (ARC_DeleteUserData *)userdata; temp->key->Delete(deldata->config, deldata->data, deldata->subdata, temp->data); } free(temp); return 0; } int32_t ARC_ConfigGroup_Destroy(ARC_HashtableNode *group, void *userdata){ free((char *)group->key); return ARC_Hashtable_Destroy((ARC_Hashtable *)group->data, ARC_ConfigGroupNode_Destroy, userdata); } int32_t ARC_Config_Create(ARC_Config **config, ARC_ConfigKey_AddFunc keysAdd){ *config = (ARC_Config *) malloc(sizeof(ARC_Config)); (*config)->currgroup = NULL; ARC_Hashtable *groups; ARC_Hashtable_Create(&groups, ARC_GROUP_BUCKET_SIZE, NULL, ARC_Config_KeyComp); (*config)->groups = groups; ARC_ConfigGroup_Create(*config, ""); ARC_Hashtable *keys; ARC_Hashtable_Create(&keys, ARC_KEY_BUCKET_SIZE, NULL, ARC_Config_KeyComp); (*config)->keys = keys; #ifdef ARC_DEFAULT_CONFIG ARC_Defaults_ConfigKey_Create(*config); #endif if(keysAdd){ keysAdd(*config); } return 0; } int32_t ARC_Config_Destroy(ARC_Config *config){ ARC_DeleteUserData deldata = { .config = config, .data = NULL, .subdata = NULL }; int32_t err = ARC_Hashtable_Destroy(config->groups, ARC_ConfigGroup_Destroy, (void *)&deldata); if(err){ return err; } err = ARC_Hashtable_Destroy(config->keys, ARC_ConfigKey_Destroy, NULL); if(err){ return err; } free(config); return 0; } int32_t ARC_Config_Get(ARC_Config *config, char *keyname, void **value){ uint64_t len = 0; ARC_ConfigTypeTemplate *temp; int32_t err = ARC_String_Find(keyname, (char *)"::", &len); if(err){ return err; } if(len != ~((uint64_t)0)){ char group[len + 1]; strncpy(group, keyname, len); group[len] = '\0'; ARC_Hashtable *currgroup = config->currgroup; err = ARC_Config_SetGroup(config, group); if(err){ return err; } char *namestr = (len + 2) + keyname; char name[strlen(namestr)]; strcpy(name, namestr); err = ARC_Hashtable_Get(config->currgroup, (void *)name, strlen(name), (void **)&temp); if(err){ return err; } config->currgroup = currgroup; *value = temp->data; return 0; } err = ARC_Hashtable_Get(config->currgroup, (void *)keyname, strlen(keyname), (void **)&temp); if(err){ return err; } *value = temp->data; return 0; } int32_t ARC_Config_Remove(ARC_Config *config, const char *keyname, const char* data, ARC_StringSubstr *subdata){ ARC_DeleteUserData deldata = { .config = config, .data = data, .subdata = subdata }; return ARC_Hashtable_Remove(config->currgroup, (void *)keyname, strlen(keyname), ARC_ConfigGroupNode_Destroy, (void *)&deldata); } int32_t ARC_Config_SetGroup(ARC_Config *config, char *groupstr){ int err = ARC_Hashtable_Get(config->groups, groupstr, strlen(groupstr), (void **)&(config->currgroup)); if(err && err != ARC_ERRNO_NULL){ return err; } if(!(config->currgroup)){ err = ARC_ConfigGroup_Create(config, groupstr); if(err){ return err; } err = ARC_Hashtable_Get(config->groups, groupstr, strlen(groupstr), (void **)&(config->currgroup)); if(err){ return err; } } return 0; } void ARC_ConfigPath_Create(char *data, ARC_StringSubstr *subpath, char **path){ if(*(data + subpath->index) == '~'){ *path = (char *) malloc(sizeof(char) * (subpath->length + strlen(ARC_HOME_PATH) + 1)); strcpy(*path, ARC_HOME_PATH); strncpy((*path) + strlen(ARC_HOME_PATH) - 1, data + subpath->index, subpath->length); (*path)[subpath->length + strlen(ARC_HOME_PATH)] = '\0'; return; } *path = (char *) malloc(sizeof(char) * (subpath->length + 1)); strncpy(*path, data + subpath->index, subpath->length); (*path)[subpath->length] = '\0'; } void ARC_ConfigPath_Destroy(char *url){ free(url); } int32_t ARC_Config_Recurse(ARC_Config *config, char *data, uint64_t *size, char *groupstr, ARC_StringSubstr *subkey, uint8_t *command); int32_t ARC_ConfigKey_Command(ARC_Config *config, char *data, uint64_t *size, ARC_StringSubstr *subkey){ ARC_StringSubstr subcommand = { subkey->index + 1, 0 }; int32_t err = ARC_String_Find(data + subcommand.index, " ", &(subcommand.length)); if(err){ return err; } if(subcommand.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subcommand.length + 1; ARC_StringSubstr_StripEnds(data, NULL, &subcommand); if(strncmp("load", data + subcommand.index, strlen("load")) && strncmp("unload", data + subcommand.index, strlen("unload"))){ return ARC_ERRNO_DATA; } ARC_StringSubstr subpath = { subkey->index, 0 }; err = ARC_String_Find(data + subpath.index, (char *)"\"", &(subpath.index)); if(err){ return err; } if(subpath.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subpath.length + 2; //we want to skip over the first \" that is why it is 2 not 1 subpath = (ARC_StringSubstr) { subkey->index, 0 }; err = ARC_String_Find(data + subpath.index, (char *)"\"", &(subpath.length)); if(err){ return err; } if(subpath.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subpath.length + 1; char *path; ARC_ConfigPath_Create(data, &subpath, &path); if (!strncmp( "load", data + subcommand.index, strlen( "load"))){ err = ARC_Config_FileIO(config, path, ARC_CONFIG_FILE_IO_LOAD ); } else if(!strncmp("unload", data + subcommand.index, strlen("unload"))){ err = ARC_Config_FileIO(config, path, ARC_CONFIG_FILE_IO_UNLOAD); } else { return ARC_ERRNO_DATA; } ARC_ConfigPath_Destroy(path); return err; } int32_t ARC_ConfigKey_Comment(ARC_Config *config, char *data, uint64_t *size, ARC_StringSubstr *subkey){ uint64_t commentlen = 0; if(data[subkey->index + 1] == '*'){ int32_t err = ARC_String_Find(data + subkey->index, (char *)"*/", &commentlen); if(err){ return err; } subkey->index += commentlen + 1; return 0; } int32_t err = ARC_String_Find(data + subkey->index, (char *)"\n", &commentlen); if(err){ return err; } subkey->index += commentlen + 1; return 0; } int32_t ARC_ConfigKey_Group(ARC_Config *config, char *data, uint64_t *size, ARC_StringSubstr *subkey, uint8_t *command){ ARC_StringSubstr subgroup = { subkey->index, 0 }; int32_t err = ARC_String_Find(data + subgroup.index, "{", &(subgroup.length)); if(err){ return err; } if(subgroup.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subgroup.length + 1; ARC_StringSubstr_StripEnds(data, NULL, &subgroup); char groupstr[subgroup.length + 1]; strncpy(groupstr, data + subgroup.index, subgroup.length); groupstr[subgroup.length] = '\0'; return ARC_Config_Recurse(config, data, size, groupstr, subkey, command); } int32_t ARC_ConfigKey_Load(ARC_Config *config, char *data, uint64_t *size, char *keyname, ARC_StringSubstr *subkey){ ARC_ConfigKey *key; int32_t err = ARC_Hashtable_Get(config->keys, (void *)keyname, strlen(keyname), (void **)&key); if(err){ return err; } ARC_StringSubstr subname = { subkey->index, 0 }; err = ARC_String_Find(data + subname.index, (char *)"=", &(subname.length)); if(err){ return err; } if(subname.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subname.length + 1; ARC_StringSubstr_StripEnds(data, NULL, &subname); ARC_StringSubstr subvalue = { subkey->index, 0 }; err = ARC_String_Find(data + subvalue.index, (char *)";", &(subvalue.length)); if(err){ return err; } if(subvalue.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subvalue.length + 1; ARC_StringSubstr_StripEnds(data, NULL, &subvalue); ARC_ConfigTypeTemplate *templateVal = NULL; char *name = malloc(sizeof(char) * subname.length + 1); strncpy(name, data + subname.index, sizeof(char) * subname.length); name[subname.length] = '\0'; templateVal = (ARC_ConfigTypeTemplate *) malloc(sizeof(ARC_ConfigTypeTemplate)); templateVal->key = NULL; templateVal->data = ARC_Config_GetReference(config, data, &subvalue); if(!templateVal->data){ err = key->Read(config, data, &subvalue, &(templateVal->data)); if(err){ return err; } templateVal->key = key; } return ARC_Hashtable_Add(config->currgroup, (void *)name, strlen(name), (void *)templateVal); } int32_t ARC_ConfigKey_Unload(ARC_Config *config, char *data, uint64_t *size, char *keyname, ARC_StringSubstr *subkey){ ARC_StringSubstr subname = { subkey->index, 0 }; int32_t err = ARC_String_Find(data + subname.index, (char *)"=", &(subname.length)); if(err){ return err; } if(subname.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subname.length + 1; ARC_StringSubstr_StripEnds(data, NULL, &subname); char name[subname.length + 1]; strncpy(name, data + subname.index, subname.length); name[subname.length] = '\0'; subname = (ARC_StringSubstr){ subkey->index, 0 }; err = ARC_String_Find(data + subname.index, (char *)";", &(subname.length)); if(err){ return err; } if(subname.length == ~((uint64_t)0)){ return ARC_ERRNO_DATA; } subkey->index += subname.length + 1; return ARC_Config_Remove(config, name, data, &subname); } int32_t ARC_Config_Recurse(ARC_Config *config, char *data, uint64_t *size, char *groupstr, ARC_StringSubstr *subkey, uint8_t *command){ int32_t err = ARC_Config_SetGroup(config, groupstr); if(err){ return err; } ARC_Hashtable *group = config->currgroup; while(subkey->index < *size){ if(data[subkey->index] == ' ' || data[subkey->index] == '\n' || data[subkey->index] == '\t' || data[subkey->index] == '\r'){ subkey->index++; continue; } if(data[subkey->index] == '}'){ config->currgroup = NULL; subkey->index++; return 0; } if(data[subkey->index] == '#'){ err = ARC_ConfigKey_Command(config, data, size, subkey); if(err){ return err; } continue; } if(data[subkey->index] == '/'){ err = ARC_ConfigKey_Comment(config, data, size, subkey); if(err){ return err; } continue; } err = ARC_String_Find(data + subkey->index, (char *)" ", &(subkey->length)); if(err){ return err; } if(subkey->length == ~((uint64_t)0)){ return 0; } if(!(config->currgroup)){ config->currgroup = group; } char keyname[subkey->length + 1]; strncpy(keyname, data + subkey->index, subkey->length); keyname[subkey->length] = '\0'; subkey->index += subkey->length + 1; if(subkey->length == strlen("group") && !strcmp(keyname, "group")){ err = ARC_ConfigKey_Group(config, data, size, subkey, command); if(err){ return err; } continue; } if(*command == ARC_CONFIG_FILE_IO_LOAD){ err = ARC_ConfigKey_Load(config, data, size, keyname, subkey); if(err){ return err; } continue; } if(*command == ARC_CONFIG_FILE_IO_UNLOAD){ err = ARC_ConfigKey_Unload(config, data, size, keyname, subkey); if(err){ return err;} continue; } return ARC_ERRNO_DATA; } config->currgroup = group; return 0; } int32_t ARC_Config_FileIO(ARC_Config *config, const char *pathstr, uint8_t command){ char *path, *data; uint64_t size; ARC_StringSubstr subpath = { 0, strlen(pathstr) }; ARC_ConfigPath_Create((char *)pathstr, &subpath, &path); int32_t err = ARC_IO_FileToStr(path, &data, &size); if(err){ ARC_DEBUG_LOG(err, "ARC_IO_FileToStr(%s, &data, &size);\n", path); ARC_ConfigPath_Destroy(path); return err; } ARC_ConfigPath_Destroy(path); ARC_StringSubstr subkey = { 0, 0 }; err = ARC_Config_Recurse(config, data, &size, "", &subkey, &command); if(err){ return err; } free(data); return 0; } void *ARC_Config_GetReference(ARC_Config *config, char *data, ARC_StringSubstr *subdata){ if(ARC_String_Alpha(data + subdata->index, 1) && *(data + subdata->index) != ':'){ return NULL; } char refname[subdata->length + 1]; strncpy(refname, data + subdata->index, subdata->length); refname[subdata->length] = 0; void *value; int32_t err = ARC_Config_Get(config, refname, &value); return (err)? NULL : value; }