#include "arc/std/config.h" #include "arc/std/errno.h" #include "arc/std/hashtable.h" #include "arc/std/io.h" #include "arc/std/string.h" #include #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_ConfigKeyDelete Delete; void *data; } ARC_ConfigTypeTemplate; typedef struct ARC_ConfigDeleteKeyArgs { ARC_Config *config; ARC_String *string; } ARC_ConfigDeleteKeyArgs; int8_t ARC_Config_KeyComp(void *key1, size_t *key1size, void *key2, size_t *key2size); void ARC_Config_CreateGroup(ARC_Config *config, ARC_String *name); void ARC_Config_DestroyGroup(ARC_HashtableNode *group, void *userdata); void ARC_Config_DestroyGroupNode(ARC_HashtableNode *node , void *userdata); void ARC_Config_RemoveKey(ARC_HashtableNode *node, void *userdata); void ARC_Config_AddKey(ARC_Config *config, ARC_String *type, ARC_ConfigKeyRead keyRead, ARC_ConfigKeyDelete keyDelete){ ARC_ConfigKey *newKey = (ARC_ConfigKey *)malloc(sizeof(ARC_ConfigKey)); newKey->Read = keyRead; newKey->Delete = keyDelete; char *typeval = (char *)malloc(sizeof(char) * type->length); strncpy(typeval, type->data, type->length); ARC_Hashtable_Add(config->keys, (void *)typeval, type->length, newKey); } void ARC_Config_AddKeyCString(ARC_Config *config, const char *type, uint64_t length, ARC_ConfigKeyRead keyRead, ARC_ConfigKeyDelete keyDelete){ ARC_ConfigKey *newKey = (ARC_ConfigKey *)malloc(sizeof(ARC_ConfigKey)); newKey->Read = keyRead; newKey->Delete = keyDelete; char *typeval = (char *)malloc(sizeof(char) * length); strncpy(typeval, type, length); ARC_Hashtable_Add(config->keys, (void *)typeval, length, newKey); } void ARC_Config_Create(ARC_Config **config){ *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_Config_CreateGroup(*config, NULL); 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 } void ARC_Config_Destroy(ARC_Config *config){ ARC_ConfigDeleteKeyArgs keyArgs = { .config = config, .string = NULL, }; ARC_Hashtable_Destroy(config->groups, ARC_Config_DestroyGroup, (void *)&keyArgs); ARC_Hashtable_Destroy(config->keys , ARC_Config_RemoveKey , NULL ); free(config); } //TODO: fix NULL group void ARC_Config_SetGroup(ARC_Config *config, ARC_String *groupname){ if(!config){ return; } if(groupname == NULL){ ARC_Hashtable_Get(config->groups, (void *)" ", 1, (void **)&(config->currgroup)); return; } ARC_Hashtable_Get(config->groups, (void *)groupname->data, groupname->length, (void **)&(config->currgroup)); if(arc_errno && arc_errno != ARC_ERRNO_NULL){ return; } if(config->currgroup){ return; } ARC_Config_CreateGroup(config, groupname); if(arc_errno){ return; } ARC_Hashtable_Get(config->groups, (void *)groupname->data, groupname->length, (void **)&(config->currgroup)); } void ARC_Config_Get(ARC_Config *config, ARC_String *keyname, void **value){ ARC_ConfigTypeTemplate *temp; uint64_t length = ARC_String_FindCString(keyname, "::", 2); if(arc_errno){ //TODO: Debug info here *value = NULL; return; } if(length != ~((uint64_t)0)){ length--; ARC_String *group = NULL; if(length != 0){ ARC_String_CopySubstring(&group, keyname, 0, length); } ARC_Hashtable *currgroup = config->currgroup; ARC_Config_SetGroup(config, group); if(arc_errno){ ARC_String_Destroy(group); *value = NULL; return; } ARC_String *name; ARC_String_CopySubstring(&name, keyname, length + 2, keyname->length - (length + 2)); ARC_Hashtable_Get(config->currgroup, (void *)name->data, name->length, (void **)&temp); ARC_String_Destroy(name); config->currgroup = currgroup; if(group){ ARC_String_Destroy(group); } *value = temp->data; return; } if(!keyname){ *value = NULL; return; } ARC_Hashtable_Get(config->currgroup, (void *)keyname->data, keyname->length, (void **)&temp); if(arc_errno || temp == NULL){ *value = NULL; return; } *value = temp->data; } void ARC_Config_Recurse(ARC_Config *config, ARC_String **data, ARC_String *groupstr, uint8_t *command); void ARC_Config_SetKeyGroup(ARC_Config *config, ARC_String **data, uint8_t *command){ uint64_t index = ARC_String_FindCString(*data, " ", 1); uint64_t nextIndex = ARC_String_FindCString(*data, "{", 1); if(index == ~(uint64_t)0 || nextIndex == ~(uint64_t)0){ arc_errno = ARC_ERRNO_DATA; } if(arc_errno){ return; } ARC_String *name, *temp; ARC_String_CopySubstring(&temp, *data, index, nextIndex - index - 1); ARC_String_StripEndsWhitespace(temp, &name); ARC_String_Destroy(temp); temp = *data; ARC_String_CopySubstring(data, temp, nextIndex + 1, (*data)->length - (nextIndex + 1)); ARC_String_Destroy(temp); ARC_Config_Recurse(config, data, name, command); ARC_String_Destroy(name); } void ARC_Config_LoadFromKey(ARC_Config *config, ARC_String *keyType, ARC_String *name, ARC_String *value){ ARC_ConfigKey *key; ARC_Hashtable_Get(config->keys, keyType->data, keyType->length, (void **)&key); if(key == NULL){ arc_errno = ARC_ERRNO_DATA; } if(arc_errno){ return; } ARC_ConfigTypeTemplate *templateVal = (ARC_ConfigTypeTemplate *) malloc(sizeof(ARC_ConfigTypeTemplate)); templateVal->Delete = NULL; templateVal->data = NULL; uint8_t reference = key->Read(config, value, &(templateVal->data)); if(!reference){ templateVal->Delete = key->Delete; } if(arc_errno){ return; } char *nameval = (char *)malloc(sizeof(char) * name->length + 1); strncpy(nameval, name->data, name->length); nameval[name->length] = '\0'; ARC_Hashtable_Add(config->currgroup, nameval, name->length, (void *)templateVal); } void ARC_Config_UnloadFromKey(ARC_Config *config, ARC_String *keyType, ARC_String *name, ARC_String *value){ ARC_ConfigDeleteKeyArgs keyArgs = { .config = config, .string = value, }; ARC_Hashtable_Remove(config->currgroup, name->data, name->length, ARC_Config_DestroyGroupNode, &keyArgs); } void ARC_Config_GetNameAndValue(ARC_String *data, ARC_String **name, ARC_String **value){ uint64_t index = ARC_String_FindCString(data, "=", 1); if(arc_errno || index == ~(uint64_t)0){ *name = NULL; *value = NULL; return; } ARC_String_CopySubstring(name, data, 0, index - 1); index++; ARC_String *dataTemp = *name; ARC_String_StripEndsWhitespace(dataTemp, name); ARC_String_Destroy(dataTemp); ARC_String_CopySubstring(&dataTemp, data, index, data->length - index); ARC_String_StripEndsWhitespace(dataTemp, value); ARC_String_Destroy(dataTemp); } void ARC_Config_Recurse(ARC_Config *config, ARC_String **data, ARC_String *groupstr, uint8_t *command){ ARC_Config_SetGroup(config, groupstr); if(arc_errno){ return; } ARC_Hashtable *group = config->currgroup; while(*data && (*data)->length){ ARC_String *dataTemp = *data; ARC_String_StripEndsWhitespace(dataTemp, data); ARC_String_Destroy(dataTemp); // break out of current group if((*data)->data[0] == '}'){ config->currgroup = NULL; dataTemp = *data; ARC_String_CopySubstring(data, dataTemp, 1, dataTemp->length - 1); ARC_String_Destroy(dataTemp); return; } // set group if(!(config->currgroup)){ config->currgroup = group; } // get keys type uint64_t index = ARC_String_FindCString(*data, " ", 1); if(arc_errno || index == ~(uint64_t)0){ return; } ARC_String *keyType, *keyTypeTemp; ARC_String_CopySubstring(&keyTypeTemp, *data, 0, index); ARC_String_StripEndsWhitespace(keyTypeTemp, &keyType); ARC_String_Destroy(keyTypeTemp); if(ARC_String_EqualsCString(keyType, "group", 5)){ ARC_Config_SetKeyGroup(config, data, command); ARC_String_Destroy(keyType); config->currgroup = group; if(arc_errno){ return; } continue; } // get and copy up to the ; ARC_String *nameAndValue; uint64_t nextIndex = ARC_String_FindCString(*data, ";", 1); if(nextIndex == ~(uint64_t)0){ arc_errno = ARC_ERRNO_DATA; } if(arc_errno){ ARC_String_Destroy(keyType); return; } ARC_String_CopySubstring(&nameAndValue, *data, index, nextIndex - (index + 1)); if(arc_errno){ ARC_String_Destroy(keyType); return; } // remove up to the ; from data string dataTemp = *data; ARC_String_CopySubstring(data, dataTemp, nextIndex, (*data)->length - nextIndex); ARC_String_Destroy(dataTemp); if(arc_errno){ ARC_String_Destroy(keyType); ARC_String_Destroy(nameAndValue); return; } // get name and value of string ARC_String *name, *value; ARC_Config_GetNameAndValue(nameAndValue, &name, &value); ARC_String_Destroy(nameAndValue); if(arc_errno){ ARC_String_Destroy(keyType); return; } // load from key if(*command == ARC_CONFIG_FILE_IO_LOAD){ ARC_Config_LoadFromKey(config, keyType, name, value); ARC_String_Destroy(keyType); ARC_String_Destroy(name ); ARC_String_Destroy(value ); if(arc_errno){ return; } continue; } // unload from key if(*command == ARC_CONFIG_FILE_IO_UNLOAD){ ARC_Config_UnloadFromKey(config, keyType, name, value); ARC_String_Destroy(keyType); ARC_String_Destroy(name ); ARC_String_Destroy(value ); if(arc_errno){ return; } continue; } // config file wasn't loaded correctly ARC_String_Destroy(keyType); ARC_String_Destroy(name ); ARC_String_Destroy(value ); arc_errno = ARC_ERRNO_DATA; return; } config->currgroup = group; } void ARC_Config_StripComment(ARC_String *original, ARC_String **stripped, ARC_String *lineStart, ARC_String *lineEnd){ ARC_String *current = NULL; ARC_String_Copy(¤t, original); uint64_t index = ARC_String_Find(original, lineStart); while(index != ~(uint64_t)0){ ARC_String *commentString; ARC_String_CopySubstring(&commentString, current, index + lineStart->length, current->length - (index + lineStart->length)); uint64_t endIndex = ARC_String_Find(commentString, lineEnd); ARC_String_Destroy(commentString); if(endIndex == ~(uint64_t)0){ ARC_DEBUG_ERR("ARC_Config_RemoveComments(original, commentRemoved); No newline found when stripping single line comment"); arc_errno = ARC_ERRNO_DATA; ARC_String_Destroy(current); *stripped = NULL; break; } ARC_String *currentTemp = current; ARC_String_RemoveSection(¤t, currentTemp, index, endIndex + lineStart->length + lineEnd->length); ARC_String_Destroy(currentTemp); index = ARC_String_Find(current, lineStart); } *stripped = current; } void ARC_Config_RemoveComments(ARC_String *original, ARC_String **commentRemoved){ ARC_String *lineStart, *lineEnd; //Single Line Comment ARC_String_Create(&lineStart, "//", 2); ARC_String_Create(&lineEnd , "\n", 1); ARC_String *singleLineStripped; ARC_Config_StripComment(original, &singleLineStripped, lineStart, lineEnd); ARC_String_Destroy(lineStart); ARC_String_Destroy(lineEnd ); if(arc_errno){ commentRemoved = NULL; return; } //Multi Line Comment ARC_String_Create(&lineStart, "/*", 2); ARC_String_Create(&lineEnd , "*/", 2); ARC_Config_StripComment(singleLineStripped, commentRemoved, lineStart, lineEnd); ARC_String_Destroy(singleLineStripped); ARC_String_Destroy(lineStart); ARC_String_Destroy(lineEnd ); } void ARC_Config_RunCommand(ARC_Config *config, ARC_String *command){ ARC_String *space; ARC_String_Create(&space, " " , 1); uint64_t index = ARC_String_Find(command, space); if(index == ~(uint64_t)0){ arc_errno = ARC_ERRNO_DATA; ARC_String_Destroy(space); return; } ARC_String *commandOpt; ARC_String_CopySubstring(&commandOpt, command, 0, index); ARC_String *commandArgTemp, *commandArg; ARC_String_CopySubstring(&commandArgTemp, command, index + space->length, command->length - (index + space->length)); ARC_String_StripWhitespace(commandArgTemp, &commandArg); ARC_String_Destroy(commandArgTemp); if(ARC_String_EqualsCString(command, "load", 4)){ ARC_Config_FileIO(config, commandArg, ARC_CONFIG_FILE_IO_LOAD); } else if(ARC_String_EqualsCString(command, "unload", 6)){ ARC_Config_FileIO(config, commandArg, ARC_CONFIG_FILE_IO_UNLOAD); } else { arc_errno = ARC_ERRNO_DATA; } ARC_String_Destroy(commandOpt); ARC_String_Destroy(commandArg); ARC_String_Destroy(space ); } void ARC_Config_RemoveAndRunCommands(ARC_Config *config, ARC_String *original, ARC_String **commandRemoved){ ARC_String *current; ARC_String_Copy(¤t, original); ARC_String *lineStart, *lineEnd; ARC_String_Create(&lineStart, "#" , 1); ARC_String_Create(&lineEnd , "\n", 1); uint64_t index = ARC_String_Find(current, lineStart); while(index != ~(uint64_t)0){ uint64_t endIndex = ARC_String_Find(current, lineEnd); if(endIndex == ~(uint64_t)0){ arc_errno = ARC_ERRNO_DATA; ARC_String_Destroy(current ); ARC_String_Destroy(lineStart); ARC_String_Destroy(lineEnd ); *commandRemoved = NULL; return; } ARC_String *command; ARC_String_CopySubstring(&command, current, index + lineStart->length, endIndex - (index + lineStart->length)); ARC_Config_RunCommand(config, command); ARC_String *currentTemp = current; ARC_String_RemoveSubstring(¤t, currentTemp, command); ARC_String_Destroy(command); ARC_String_Destroy(currentTemp); } ARC_String_Destroy(lineStart); ARC_String_Destroy(lineEnd ); *commandRemoved = current; } void ARC_Config_FileIO(ARC_Config *config, ARC_String *path, uint8_t command){ ARC_String *data; ARC_IO_FileToStr(path, &data); if(arc_errno){ ARC_DEBUG_LOG(arc_errno, "ARC_IO_FileToStr(%s, &data, &size);\n", path->data); return; } char *tempData = (char *)malloc(sizeof(char) * (data->length + 1)); strncpy(tempData, data->data, data->length); tempData[data->length] = '\n'; ARC_String *temp = data; ARC_String_Create(&temp, tempData, data->length + 1); free(tempData); ARC_String_Destroy(data); ARC_Config_RemoveComments(temp, &data); ARC_String_Destroy(temp); temp = data; ARC_Config_RemoveAndRunCommands(config, temp, &data); ARC_String_Destroy(temp); temp = data; ARC_String_StripEndsWhitespace(temp, &data); ARC_String_Destroy(temp); ARC_Config_Recurse(config, &data, NULL, &command); if(data){ ARC_String_Destroy(data); } } int8_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); } void ARC_Config_CreateGroup(ARC_Config *config, ARC_String *name){ ARC_Hashtable *data; ARC_Hashtable_Create(&data, ARC_GROUP_DATA_BUCKET_SIZE, NULL, ARC_Config_KeyComp); if(name){ char *nameval = (char *)malloc(sizeof(char) * name->length); strncpy(nameval, name->data, name->length); ARC_Hashtable_Add(config->groups, nameval, name->length, (void *)data); return; } char *emptyGroup = (char *)malloc(sizeof(char)); emptyGroup[0] = ' '; ARC_Hashtable_Add(config->groups, emptyGroup, 1, (void *)data); } void ARC_Config_DestroyGroup(ARC_HashtableNode *group, void *userdata){ free((char *)group->key); return ARC_Hashtable_Destroy((ARC_Hashtable *)group->data, ARC_Config_DestroyGroupNode, userdata); } void ARC_Config_DestroyGroupNode(ARC_HashtableNode *node, void *userdata){ free((char *)node->key); ARC_ConfigTypeTemplate *temp = (ARC_ConfigTypeTemplate *)node->data; if(temp->Delete && temp->data && userdata){ ARC_ConfigDeleteKeyArgs *args = (ARC_ConfigDeleteKeyArgs *)userdata; temp->Delete(args->config, args->string, temp->data); } free(temp); node->data = NULL; } void ARC_Config_RemoveKey(ARC_HashtableNode *node, void *userdata){ free((char *)node->key); if(!node->data){ arc_errno = ARC_ERRNO_NULL; return; } free((ARC_ConfigKey *)node->data); }