/** * Copyright (C) 2008 Happy Fish / YuQing * * FastDFS may be copied only under the terms of the GNU General * Public License V3, which may be found in the FastDFS source kit. * Please visit the FastDFS Home Page http://www.csource.org/ for more detail. **/ //ini_file_reader.c #include #include #include #include #include #include #include "shared_func.h" #include "logger.h" #include "http_func.h" #include "local_ip_func.h" #include "ini_file_reader.h" #define _LINE_BUFFER_SIZE 512 #define _INIT_ALLOC_ITEM_COUNT 32 #define _PREPROCESS_TAG_STR_IF "#@if " #define _PREPROCESS_TAG_STR_ELSE "#@else" #define _PREPROCESS_TAG_STR_ENDIF "#@endif" #define _PREPROCESS_TAG_STR_FOR "#@for " #define _PREPROCESS_TAG_STR_ENDFOR "#@endfor" #define _PREPROCESS_TAG_STR_SET "#@set " #define _PREPROCESS_TAG_LEN_IF (sizeof(_PREPROCESS_TAG_STR_IF) - 1) #define _PREPROCESS_TAG_LEN_ELSE (sizeof(_PREPROCESS_TAG_STR_ELSE) - 1) #define _PREPROCESS_TAG_LEN_ENDIF (sizeof(_PREPROCESS_TAG_STR_ENDIF) - 1) #define _PREPROCESS_TAG_LEN_FOR (sizeof(_PREPROCESS_TAG_STR_FOR) - 1) #define _PREPROCESS_TAG_LEN_ENDFOR (sizeof(_PREPROCESS_TAG_STR_ENDFOR) - 1) #define _PREPROCESS_TAG_LEN_SET (sizeof(_PREPROCESS_TAG_STR_SET) - 1) #define _PREPROCESS_VARIABLE_STR_LOCAL_IP "%{LOCAL_IP}" #define _PREPROCESS_VARIABLE_STR_LOCAL_HOST "%{LOCAL_HOST}" #define _PREPROCESS_VARIABLE_LEN_LOCAL_IP \ (sizeof(_PREPROCESS_VARIABLE_STR_LOCAL_IP) - 1) #define _PREPROCESS_VARIABLE_LEN_LOCAL_HOST \ (sizeof(_PREPROCESS_VARIABLE_STR_LOCAL_HOST) - 1) #define _PREPROCESS_TAG_STR_FOR_FROM "from" #define _PREPROCESS_TAG_LEN_FOR_FROM (sizeof(_PREPROCESS_TAG_STR_FOR_FROM) - 1) #define _PREPROCESS_TAG_STR_FOR_TO "to" #define _PREPROCESS_TAG_LEN_FOR_TO (sizeof(_PREPROCESS_TAG_STR_FOR_TO) - 1) #define _PREPROCESS_TAG_STR_FOR_STEP "step" #define _PREPROCESS_TAG_LEN_FOR_STEP (sizeof(_PREPROCESS_TAG_STR_FOR_STEP) - 1) #define _MAX_DYNAMIC_CONTENTS 8 #define _BUILTIN_ANNOTATION_COUNT 3 static AnnotationMap *g_annotation_map = NULL; static int g_annotation_count = 0; typedef struct { int count; int alloc_count; char **contents; } DynamicContents; typedef struct { int offset; //deal offset HashArray *vars; //variables with #@set } SetDirectiveVars; typedef struct { int count; int alloc_count; AnnotationMap *annotations; } DynamicAnnotations; typedef struct { bool used; IniContext *context; DynamicContents dynamicContents; SetDirectiveVars set; DynamicAnnotations dynamicAnnotations; } CDCPair; //dynamic alloced contents which will be freed when destroy static int g_dynamic_content_count = 0; static int g_dynamic_content_index = 0; static CDCPair g_dynamic_contents[_MAX_DYNAMIC_CONTENTS] = {{false, NULL, {0, 0, NULL}, {0, NULL}, {0, 0, NULL}}}; static int remallocSection(IniSection *pSection, IniItem **pItem); static int iniDoLoadFromFile(const char *szFilename, \ IniContext *pContext); static int iniLoadItemsFromBuffer(char *content, \ IniContext *pContext); static DynamicAnnotations *iniAllocAnnotations(IniContext *pContext, const int annotation_count); static AnnotationMap *iniGetAnnotations(IniContext *pContext); static SetDirectiveVars *iniGetVars(IniContext *pContext); #define STR_TRIM(pStr) \ do { \ trim_right(pStr); \ trim_left(pStr); \ } while (0) static void iniDoSetAnnotations(AnnotationMap *src, const int src_count, AnnotationMap *dest, int *dest_count) { AnnotationMap *pSrc; AnnotationMap *pSrcEnd; AnnotationMap *pDest; AnnotationMap *pDestEnd; pSrcEnd = src + src_count; pDestEnd = dest + *dest_count; for (pSrc=src; pSrcfunc_name, pDest->func_name) == 0) { break; } } pDest->func_name = pSrc->func_name; pDest->func_init = pSrc->func_init; pDest->func_destroy = pSrc->func_destroy; pDest->func_get = pSrc->func_get; if (pDest == pDestEnd) //insert { ++(*dest_count); pDestEnd = dest + *dest_count; } } } static int iniAnnotationFuncLocalIpGet(IniContext *context, char *param, char **pOutValue, int max_values) { bool need_private_ip; int count; int index; const char *next_ip; char *square_start; char name_part[16]; memset(name_part, 0, sizeof(name_part)); square_start = strchr(param, '['); if (square_start != NULL && param[strlen(param) - 1] == ']') { snprintf(name_part, sizeof(name_part) - 1, "%.*s", (int)(square_start - param), param); index = atoi(square_start + 1); } else { snprintf(name_part, sizeof(name_part) - 1, "%s", param); index = -2; } need_private_ip = strcasecmp(name_part, "inner") == 0 || strcasecmp(name_part, "private") == 0; next_ip = NULL; count = 0; while ((next_ip=get_next_local_ip(next_ip)) != NULL) { if (count >= max_values) { break; } if (is_private_ip(next_ip)) { if (need_private_ip) { pOutValue[count++] = (char *)next_ip; } } else { if (!need_private_ip) { pOutValue[count++] = (char *)next_ip; } } } if (count == 0) { pOutValue[count++] = ""; } else if (index > -2) { if (index == -1) { //get the last one if (count > 1) { pOutValue[0] = pOutValue[count - 1]; } } else if (index >= count) { //index overflow logWarning("file: "__FILE__", line: %d, " "index: %d >= count: %d, set value to empty", __LINE__, index, count); pOutValue[0] = ""; } else if (index > 0) { pOutValue[0] = pOutValue[index]; } count = 1; } return count; } static int iniAnnotationFuncShellExec(IniContext *context, char *param, char **pOutValue, int max_values) { int count; int result; char *output; count = 0; output = (char *)malloc(FAST_INI_ITEM_VALUE_SIZE); if (output == NULL) { logError("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, FAST_INI_ITEM_VALUE_LEN + 1); return count; } if ((result=getExecResult(param, output, FAST_INI_ITEM_VALUE_SIZE)) != 0) { logWarning("file: "__FILE__", line: %d, " "exec %s fail, errno: %d, error info: %s", __LINE__, param, result, STRERROR(result)); free(output); return count; } if (*output == '\0') { logWarning("file: "__FILE__", line: %d, " "empty reply when exec: %s", __LINE__, param); } pOutValue[count++] = fc_trim(output); return count; } static int iniCopyBuffer(char *dest, const int size, char *src, int len) { if (len == 0) { return 0; } if (len >= size) { logWarning("file: "__FILE__", line: %d, " "length: %d exceeds: %d", __LINE__, len, size); len = size - 1; if (len < 0) { len = 0; } } if (len > 0) { memcpy(dest, src, len); } return len; } static int iniAnnotationReplaceVars(IniContext *pContext, char *param, char **pOutValue, int max_values) { #define VARIABLE_TAG_MIN_LENGTH 4 //%{v} SetDirectiveVars *set; char *p; char *e; char name[64]; char *start; char *value; char *pLoopEnd; char *pEnd; char *pDest; char *output; int name_len; int len; output = (char *)malloc(FAST_INI_ITEM_VALUE_SIZE); if (output == NULL) { logError("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, FAST_INI_ITEM_VALUE_SIZE); return 0; } set = iniGetVars(pContext); if (set == NULL) { logWarning("file: "__FILE__", line: %d, " "NO set directives before, set value to %s", __LINE__, param); snprintf(output, FAST_INI_ITEM_VALUE_SIZE, "%s", param); pOutValue[0] = output; return 1; } pEnd = param + strlen(param); pLoopEnd = pEnd - (VARIABLE_TAG_MIN_LENGTH - 1); p = param; pDest = output; while (p < pLoopEnd) { if (*p == '%' && *(p+1) == '{') { start = e = p + 2; while (e < pEnd && *e != '}') { e++; } if (e == pEnd) { break; } name_len = e - start; p = e + 1; if (name_len >= sizeof(name)) { logWarning("file: "__FILE__", line: %d, " "name: %.*s is too long, truncate to length: %d", __LINE__, name_len, start, (int)(sizeof(name) - 1)); name_len = sizeof(name) - 1; } if (name_len > 0) { memcpy(name, start, name_len); } *(name + name_len) = '\0'; trim(name); name_len = strlen(name); if (name_len > 0) { value = (char *)hash_find(set->vars, name, name_len); } else { value = NULL; } if (value != NULL) { pDest += iniCopyBuffer(pDest, FAST_INI_ITEM_VALUE_SIZE - (pDest - output), value, strlen(value)); } else { logWarning("file: "__FILE__", line: %d, " "name: %s NOT found", __LINE__, name); } } else { if (pDest - output < FAST_INI_ITEM_VALUE_LEN) { *pDest++ = *p++; } else { logWarning("file: "__FILE__", line: %d, " "value too long, exceeds: %d", __LINE__, (int)sizeof(output)); break; } } } len = pEnd - p; pDest += iniCopyBuffer(pDest, FAST_INI_ITEM_VALUE_SIZE - (pDest - output), p, len); *pDest = '\0'; pOutValue[0] = output; return 1; } static void iniAnnotationFuncFree(char **values, const int count) { int i; for (i=0; ifunc_name = "LOCAL_IP_GET"; pAnnotation->func_init = NULL; pAnnotation->func_destroy = NULL; pAnnotation->func_get = iniAnnotationFuncLocalIpGet; pAnnotation->func_free = NULL; pAnnotation++; pAnnotation->func_name = "REPLACE_VARS"; pAnnotation->func_init = NULL; pAnnotation->func_destroy = NULL; pAnnotation->func_get = iniAnnotationReplaceVars; pAnnotation->func_free = iniAnnotationFuncFree; pAnnotation++; if ((pContext->flags & FAST_INI_FLAGS_SHELL_EXECUTE) != 0) { pAnnotation->func_name = "SHELL_EXEC"; pAnnotation->func_init = NULL; pAnnotation->func_destroy = NULL; pAnnotation->func_get = iniAnnotationFuncShellExec; pAnnotation->func_free = iniAnnotationFuncFree; pAnnotation++; } iniDoSetAnnotations(builtins, pAnnotation - builtins, dest, dest_count); } static int iniSetAnnotations(IniContext *pContext, const char annotation_type, AnnotationMap *annotations, const int count) { DynamicAnnotations *pDynamicAnnotations; pContext->annotation_type = annotation_type; if (pContext->annotation_type == FAST_INI_ANNOTATION_DISABLE) { return 0; } if (pContext->annotation_type == FAST_INI_ANNOTATION_WITHOUT_BUILTIN && annotations == NULL) { return 0; } if ((pDynamicAnnotations=iniAllocAnnotations(pContext, _BUILTIN_ANNOTATION_COUNT + count)) == NULL) { return ENOMEM; } if (pContext->annotation_type == FAST_INI_ANNOTATION_WITH_BUILTIN) { iniSetBuiltinAnnotations(pContext, pDynamicAnnotations->annotations, &pDynamicAnnotations->count); } if (annotations != NULL) { iniDoSetAnnotations(annotations, count, pDynamicAnnotations->annotations, &pDynamicAnnotations->count); } return 0; } int iniSetAnnotationCallBack(AnnotationMap *map, int count) { int bytes; AnnotationMap *pDest; if (count <= 0) { logWarning("file: "__FILE__", line: %d, " \ "iniSetAnnotationCallBack fail count(%d) is incorrectly.", \ __LINE__, count); return EINVAL; } bytes = sizeof(AnnotationMap) * (g_annotation_count + count + 1); g_annotation_map = (AnnotationMap *)realloc(g_annotation_map, bytes); if (g_annotation_map == NULL) { logError("file: "__FILE__", line: %d, " \ "realloc %d fail, errno: %d, error info: %s", \ __LINE__, bytes, errno, STRERROR(errno)); return ENOMEM; } iniDoSetAnnotations(map, count, g_annotation_map, &g_annotation_count); pDest = g_annotation_map + g_annotation_count; pDest->func_name = NULL; pDest->func_init = NULL; pDest->func_destroy = NULL; pDest->func_get = NULL; return 0; } void iniDestroyAnnotationCallBack() { AnnotationMap *pAnnoMap; if (g_annotation_map == NULL) { return; } pAnnoMap = g_annotation_map; while (pAnnoMap->func_name) { if (pAnnoMap->func_destroy != NULL) { pAnnoMap->func_destroy(); } pAnnoMap++; } free(g_annotation_map); g_annotation_map = NULL; g_annotation_count = 0; } static int iniCompareByItemName(const void *p1, const void *p2) { return strcmp(((IniItem *)p1)->name, ((IniItem *)p2)->name); } static int iniInitContext(IniContext *pContext, const char annotation_type, AnnotationMap *annotations, const int count, const char flags) { int result; memset(pContext, 0, sizeof(IniContext)); pContext->current_section = &pContext->global; if ((result=hash_init(&pContext->sections, Time33Hash, 32, 0.75)) != 0) { logError("file: "__FILE__", line: %d, " \ "hash_init fail, errno: %d, error info: %s", \ __LINE__, result, STRERROR(result)); } pContext->flags = flags; return iniSetAnnotations(pContext, annotation_type, annotations, count); } static int iniSortHashData(const int index, const HashData *data, void *args) { IniSection *pSection; pSection = (IniSection *)data->value; if (pSection->count > 1) { qsort(pSection->items, pSection->count, \ sizeof(IniItem), iniCompareByItemName); } return 0; } static void iniSortItems(IniContext *pContext) { if (pContext->global.count > 1) { qsort(pContext->global.items, pContext->global.count, \ sizeof(IniItem), iniCompareByItemName); } hash_walk(&pContext->sections, iniSortHashData, NULL); } int iniLoadFromFile(const char *szFilename, IniContext *pContext) { return iniLoadFromFileEx(szFilename, pContext, FAST_INI_ANNOTATION_WITH_BUILTIN, NULL, 0, FAST_INI_FLAGS_NONE); } int iniLoadFromFileEx(const char *szFilename, IniContext *pContext, const char annotation_type, AnnotationMap *annotations, const int count, const char flags) { int result; int len; char *pLast; char full_filename[MAX_PATH_SIZE]; if ((result=iniInitContext(pContext, annotation_type, annotations, count, flags)) != 0) { return result; } if (strncasecmp(szFilename, "http://", 7) == 0) { *pContext->config_path = '\0'; snprintf(full_filename, sizeof(full_filename),"%s",szFilename); } else { if (*szFilename == '/') { pLast = strrchr(szFilename, '/'); len = pLast - szFilename; if (len >= sizeof(pContext->config_path)) { logError("file: "__FILE__", line: %d, "\ "the path of the config file: %s is " \ "too long!", __LINE__, szFilename); return ENOSPC; } memcpy(pContext->config_path, szFilename, len); *(pContext->config_path + len) = '\0'; snprintf(full_filename, sizeof(full_filename), \ "%s", szFilename); } else { memset(pContext->config_path, 0, \ sizeof(pContext->config_path)); if (getcwd(pContext->config_path, sizeof( \ pContext->config_path)) == NULL) { logError("file: "__FILE__", line: %d, " \ "getcwd fail, errno: %d, " \ "error info: %s", \ __LINE__, errno, STRERROR(errno)); return errno != 0 ? errno : EPERM; } len = strlen(pContext->config_path); if (len > 0 && pContext->config_path[len - 1] == '/') { len--; *(pContext->config_path + len) = '\0'; } snprintf(full_filename, sizeof(full_filename), \ "%s/%s", pContext->config_path, szFilename); pLast = strrchr(szFilename, '/'); if (pLast != NULL) { int tail_len; tail_len = pLast - szFilename; if (len + 1 + tail_len >= sizeof( \ pContext->config_path)) { logError("file: "__FILE__", line: %d, "\ "the path of the config " \ "file: %s is too long!", \ __LINE__, szFilename); return ENOSPC; } *(pContext->config_path + len++) = '/'; memcpy(pContext->config_path + len, \ szFilename, tail_len); len += tail_len; *(pContext->config_path + len) = '\0'; } } } result = iniDoLoadFromFile(full_filename, pContext); if (result == 0) { iniSortItems(pContext); } else { iniFreeContext(pContext); } return result; } static int iniDoLoadFromFile(const char *szFilename, \ IniContext *pContext) { char *content; int result; int http_status; int content_len; int64_t file_size; char error_info[512]; if (strncasecmp(szFilename, "http://", 7) == 0) { if ((result=get_url_content(szFilename, 10, 60, &http_status, \ &content, &content_len, error_info)) != 0) { logError("file: "__FILE__", line: %d, " \ "get_url_content fail, " \ "url: %s, error info: %s", \ __LINE__, szFilename, error_info); return result; } if (http_status != 200) { free(content); logError("file: "__FILE__", line: %d, " \ "HTTP status code: %d != 200, url: %s", \ __LINE__, http_status, szFilename); return EINVAL; } } else { if ((result=getFileContent(szFilename, &content, \ &file_size)) != 0) { return result; } } result = iniLoadItemsFromBuffer(content, pContext); free(content); return result; } int iniLoadFromBufferEx(char *content, IniContext *pContext, const char annotation_type, AnnotationMap *annotations, const int count, const char flags) { int result; if ((result=iniInitContext(pContext, annotation_type, annotations, count, flags)) != 0) { return result; } result = iniLoadItemsFromBuffer(content, pContext); if (result == 0) { iniSortItems(pContext); } else { iniFreeContext(pContext); } return result; } int iniLoadFromBuffer(char *content, IniContext *pContext) { return iniLoadFromBufferEx(content, pContext, FAST_INI_ANNOTATION_WITH_BUILTIN, NULL, 0, FAST_INI_FLAGS_NONE); } static int iniDoLoadItemsFromBuffer(char *content, IniContext *pContext) { IniSection *pSection; IniItem *pItem; char *pLine; char *pLastEnd; char *pEqualChar; char pItemName[FAST_INI_ITEM_NAME_LEN + 1]; char *pAnnoItemLine; char *pIncludeFilename; char *pItemValues[100]; char pFuncName[FAST_INI_ITEM_NAME_LEN + 1]; char full_filename[MAX_PATH_SIZE]; int i; int nLineLen; int nNameLen; int nItemCnt; int nValueLen; int result; int isAnnotation; result = 0; pAnnoItemLine = NULL; isAnnotation = 0; *pFuncName = '\0'; pLastEnd = content - 1; pSection = pContext->current_section; pItem = pSection->items + pSection->count; while (pLastEnd != NULL) { pLine = pLastEnd + 1; pLastEnd = strchr(pLine, '\n'); if (pLastEnd != NULL) { *pLastEnd = '\0'; } if (isAnnotation && pLine != pAnnoItemLine) { logWarning("file: "__FILE__", line: %d, " \ "the @function annotation line " \ "must follow by key=value line!", __LINE__); isAnnotation = 0; } if (*pLine == '#' && \ strncasecmp(pLine+1, "include", 7) == 0 && \ (*(pLine+8) == ' ' || *(pLine+8) == '\t')) { pIncludeFilename = strdup(pLine + 9); if (pIncludeFilename == NULL) { logError("file: "__FILE__", line: %d, " \ "strdup %d bytes fail", __LINE__, \ (int)strlen(pLine + 9) + 1); result = errno != 0 ? errno : ENOMEM; break; } STR_TRIM(pIncludeFilename); if (strncasecmp(pIncludeFilename, "http://", 7) == 0) { snprintf(full_filename, sizeof(full_filename),\ "%s", pIncludeFilename); } else { if (*pIncludeFilename == '/') { snprintf(full_filename, sizeof(full_filename), \ "%s", pIncludeFilename); } else { snprintf(full_filename, sizeof(full_filename), \ "%s/%s", pContext->config_path, \ pIncludeFilename); } if (!fileExists(full_filename)) { logError("file: "__FILE__", line: %d, " \ "include file \"%s\" not exists, " \ "line: \"%s\"", __LINE__, \ pIncludeFilename, pLine); free(pIncludeFilename); result = ENOENT; break; } } pContext->current_section = &pContext->global; result = iniDoLoadFromFile(full_filename, pContext); if (result != 0) { free(pIncludeFilename); break; } pContext->current_section = &pContext->global; pSection = pContext->current_section; pItem = pSection->items + pSection->count; //must re-asign free(pIncludeFilename); continue; } else if ((*pLine == '#' && \ strncasecmp(pLine+1, "@function", 9) == 0 && \ (*(pLine+10) == ' ' || *(pLine+10) == '\t'))) { if (pContext->annotation_type != FAST_INI_ANNOTATION_DISABLE) { nNameLen = strlen(pLine + 11); if (nNameLen > FAST_INI_ITEM_NAME_LEN) { nNameLen = FAST_INI_ITEM_NAME_LEN; } memcpy(pFuncName, pLine + 11, nNameLen); pFuncName[nNameLen] = '\0'; STR_TRIM(pFuncName); if ((int)strlen(pFuncName) > 0) { isAnnotation = 1; pAnnoItemLine = pLastEnd + 1; } else { logWarning("file: "__FILE__", line: %d, " \ "the function name of annotation line is empty", \ __LINE__); } } continue; } STR_TRIM(pLine); if (*pLine == '#' || *pLine == '\0') { continue; } nLineLen = strlen(pLine); if (*pLine == '[' && *(pLine + (nLineLen - 1)) == ']') //section { char *section_name; int section_len; *(pLine + (nLineLen - 1)) = '\0'; section_name = pLine + 1; //skip [ STR_TRIM(section_name); if (*section_name == '\0') //global section { pContext->current_section = &pContext->global; pSection = pContext->current_section; pItem = pSection->items + pSection->count; continue; } section_len = strlen(section_name); pSection = (IniSection *)hash_find(&pContext->sections,\ section_name, section_len); if (pSection == NULL) { pSection = (IniSection *)malloc(sizeof(IniSection)); if (pSection == NULL) { result = errno != 0 ? errno : ENOMEM; logError("file: "__FILE__", line: %d, "\ "malloc %d bytes fail, " \ "errno: %d, error info: %s", \ __LINE__, \ (int)sizeof(IniSection), \ result, STRERROR(result)); break; } memset(pSection, 0, sizeof(IniSection)); result = hash_insert(&pContext->sections, \ section_name, section_len, pSection); if (result < 0) { result *= -1; logError("file: "__FILE__", line: %d, "\ "insert into hash table fail, "\ "errno: %d, error info: %s", \ __LINE__, result, \ STRERROR(result)); break; } else { result = 0; } } pContext->current_section = pSection; pItem = pSection->items + pSection->count; continue; } pEqualChar = strchr(pLine, '='); if (pEqualChar == NULL) { continue; } nNameLen = pEqualChar - pLine; nValueLen = strlen(pLine) - (nNameLen + 1); if (nNameLen > FAST_INI_ITEM_NAME_LEN) { logWarning("file: "__FILE__", line: %d, " "name length: %d exceeds %d, " "truncate it to \"%.*s\"", __LINE__, nNameLen, FAST_INI_ITEM_NAME_LEN, FAST_INI_ITEM_NAME_LEN, pLine); nNameLen = FAST_INI_ITEM_NAME_LEN; } if (nValueLen > FAST_INI_ITEM_VALUE_LEN) { logWarning("file: "__FILE__", line: %d, " "value length: %d exceeds %d, " "truncate it to \"%.*s\"", __LINE__, nValueLen, FAST_INI_ITEM_VALUE_LEN, FAST_INI_ITEM_VALUE_LEN, pEqualChar + 1); nValueLen = FAST_INI_ITEM_VALUE_LEN; } if (pSection->count >= pSection->alloc_count) { result = remallocSection(pSection, &pItem); if (result != 0) { break; } } memcpy(pItem->name, pLine, nNameLen); memcpy(pItem->value, pEqualChar + 1, nValueLen); STR_TRIM(pItem->name); STR_TRIM(pItem->value); if (isAnnotation) { AnnotationMap *pAnnoMapBase; AnnotationMap *pAnnoMap = NULL; bool found; isAnnotation = 0; if ((pAnnoMapBase=iniGetAnnotations(pContext)) == NULL) { pAnnoMapBase = g_annotation_map; } if (pAnnoMapBase == NULL) { logWarning("file: "__FILE__", line: %d, " \ "not set annotationMap and (%s) will use " \ "the item value (%s)", __LINE__, pItem->name, pItem->value); pSection->count++; pItem++; continue; } found = false; nItemCnt = -1; for (i=0; i<2; i++) { pAnnoMap = pAnnoMapBase; while (pAnnoMap->func_name != NULL) { if (strcmp(pFuncName, pAnnoMap->func_name) == 0) { if (pAnnoMap->func_init != NULL) { pAnnoMap->func_init(); } if (pAnnoMap->func_get != NULL) { nItemCnt = pAnnoMap->func_get(pContext, pItem->value, pItemValues, 100); } found = true; break; } pAnnoMap++; } if (found) { break; } if (g_annotation_map != NULL && pAnnoMapBase != g_annotation_map) { pAnnoMapBase = g_annotation_map; } else { break; } } if (nItemCnt == -1) { logWarning("file: "__FILE__", line: %d, " \ "not found corresponding annotation function: %s, " \ "\"%s\" will use the item value \"%s\"", __LINE__, pFuncName, pItem->name, pItem->value); pSection->count++; pItem++; continue; } else if (nItemCnt == 0) { logWarning("file: "__FILE__", line: %d, " \ "annotation function %s execute fail, " \ "\"%s\" will use the item value \"%s\"", __LINE__, pFuncName, pItem->name, pItem->value); pSection->count++; pItem++; continue; } strcpy(pItemName, pItem->name); nNameLen = strlen(pItemName); for (i = 0; i < nItemCnt; i++) { nValueLen = strlen(pItemValues[i]); if (nValueLen > FAST_INI_ITEM_VALUE_LEN) { logWarning("file: "__FILE__", line: %d, " "value length: %d exceeds %d, " "truncate it to \"%.*s\"", __LINE__, nValueLen, FAST_INI_ITEM_VALUE_LEN, FAST_INI_ITEM_VALUE_LEN, pItemValues[i]); nValueLen = FAST_INI_ITEM_VALUE_LEN; } strcpy(pItem->name, pItemName); memcpy(pItem->value, pItemValues[i], nValueLen); pItem->value[nValueLen] = '\0'; pSection->count++; pItem++; if (pSection->count >= pSection->alloc_count) { result = remallocSection(pSection, &pItem); if (result != 0) { break; } } } if (pAnnoMap != NULL && pAnnoMap->func_free != NULL) { pAnnoMap->func_free(pItemValues, nItemCnt); } continue; } pSection->count++; pItem++; } if (result == 0 && isAnnotation) { logWarning("file: "__FILE__", line: %d, " \ "the @function annotation line " \ "must follow by key=value line!", __LINE__); } return result; } static CDCPair *iniGetCDCPair(IniContext *pContext) { int i; if (g_dynamic_contents[g_dynamic_content_index].context == pContext) { return g_dynamic_contents + g_dynamic_content_index; } if (g_dynamic_content_count > 0) { for (i=0; i<_MAX_DYNAMIC_CONTENTS; i++) { if (g_dynamic_contents[i].context == pContext) { g_dynamic_content_index = i; return g_dynamic_contents + g_dynamic_content_index; } } } return NULL; } static CDCPair *iniAllocCDCPair(IniContext *pContext) { int i; CDCPair *pair; if ((pair=iniGetCDCPair(pContext)) != NULL) { return pair; } if (g_dynamic_content_count == _MAX_DYNAMIC_CONTENTS) { return NULL; } for (i=0; i<_MAX_DYNAMIC_CONTENTS; i++) { if (!g_dynamic_contents[i].used) { g_dynamic_contents[i].used = true; g_dynamic_contents[i].context = pContext; g_dynamic_content_index = i; g_dynamic_content_count++; return g_dynamic_contents + g_dynamic_content_index; } } return NULL; } static DynamicContents *iniAllocDynamicContent(IniContext *pContext) { static CDCPair *pair; pair = iniAllocCDCPair(pContext); if (pair == NULL) { return NULL; } return &pair->dynamicContents; } static SetDirectiveVars *iniGetVars(IniContext *pContext) { static CDCPair *pair; pair = iniGetCDCPair(pContext); if (pair == NULL) { return NULL; } return &pair->set; } static DynamicAnnotations *iniAllocDynamicAnnotation(IniContext *pContext) { static CDCPair *pair; pair = iniAllocCDCPair(pContext); if (pair == NULL) { return NULL; } return &pair->dynamicAnnotations; } static AnnotationMap *iniGetAnnotations(IniContext *pContext) { static CDCPair *pair; pair = iniGetCDCPair(pContext); if (pair == NULL) { return NULL; } return pair->dynamicAnnotations.annotations; } static SetDirectiveVars *iniAllocVars(IniContext *pContext, const bool initVars) { static CDCPair *pair; SetDirectiveVars *set; set = iniGetVars(pContext); if (set == NULL) { pair = iniAllocCDCPair(pContext); if (pair == NULL) { return NULL; } set = &pair->set; } if (initVars && set->vars == NULL) { set->vars = (HashArray *)malloc(sizeof(HashArray)); if (set->vars == NULL) { logWarning("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, (int)sizeof(HashArray)); return NULL; } if (hash_init_ex(set->vars, simple_hash, 17, 0.75, 0, true) != 0) { free(set->vars); set->vars = NULL; return NULL; } } return set; } static void iniFreeDynamicContent(IniContext *pContext) { CDCPair *pCDCPair; DynamicContents *pDynamicContents; DynamicAnnotations *pDynamicAnnotations; int i; if (g_dynamic_content_count == 0) { return; } if (g_dynamic_contents[g_dynamic_content_index].context == pContext) { pCDCPair = g_dynamic_contents + g_dynamic_content_index; } else { pCDCPair = NULL; for (i=0; i<_MAX_DYNAMIC_CONTENTS; i++) { if (g_dynamic_contents[i].context == pContext) { pCDCPair = g_dynamic_contents + i; break; } } if (pCDCPair == NULL) { return; } } pDynamicContents = &pCDCPair->dynamicContents; if (pDynamicContents->contents != NULL) { for (i=0; icount; i++) { if (pDynamicContents->contents[i] != NULL) { free(pDynamicContents->contents[i]); } } free(pDynamicContents->contents); pDynamicContents->contents = NULL; pDynamicContents->alloc_count = 0; pDynamicContents->count = 0; } pDynamicAnnotations = &pCDCPair->dynamicAnnotations; if (pDynamicAnnotations->annotations != NULL) { free(pDynamicAnnotations->annotations); pDynamicAnnotations->annotations = NULL; pDynamicAnnotations->alloc_count = 0; pDynamicAnnotations->count = 0; } pCDCPair->used = false; pCDCPair->context = NULL; g_dynamic_content_count--; } static char *iniAllocContent(IniContext *pContext, const int content_len) { char *buff; DynamicContents *pDynamicContents; pDynamicContents = iniAllocDynamicContent(pContext); if (pDynamicContents == NULL) { logError("file: "__FILE__", line: %d, " "malloc dynamic contents fail", __LINE__); return NULL; } if (pDynamicContents->count >= pDynamicContents->alloc_count) { int alloc_count; int bytes; char **contents; if (pDynamicContents->alloc_count == 0) { alloc_count = 8; } else { alloc_count = pDynamicContents->alloc_count * 2; } bytes = sizeof(char *) * alloc_count; contents = (char **)malloc(bytes); if (contents == NULL) { logError("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, bytes); return NULL; } memset(contents, 0, bytes); if (pDynamicContents->count > 0) { memcpy(contents, pDynamicContents->contents, sizeof(char *) * pDynamicContents->count); free(pDynamicContents->contents); } pDynamicContents->contents = contents; pDynamicContents->alloc_count = alloc_count; } buff = malloc(content_len); if (buff == NULL) { logError("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, content_len); return NULL; } pDynamicContents->contents[pDynamicContents->count++] = buff; return buff; } static int iniCheckAllocAnnotations(DynamicAnnotations *pDynamicAnnotations, const int annotation_count) { int alloc_count; int bytes; AnnotationMap *annotations; if (pDynamicAnnotations->count + annotation_count < pDynamicAnnotations->alloc_count) { return 0; } if (pDynamicAnnotations->alloc_count == 0) { alloc_count = 8; } else { alloc_count = pDynamicAnnotations->alloc_count * 2; } while (alloc_count <= pDynamicAnnotations->count + annotation_count) { alloc_count *= 2; } bytes = sizeof(AnnotationMap) * alloc_count; annotations = (AnnotationMap *)malloc(bytes); if (annotations == NULL) { logError("file: "__FILE__", line: %d, " "malloc %d bytes fail", __LINE__, bytes); return ENOMEM; } memset(annotations, 0, bytes); if (pDynamicAnnotations->count > 0) { memcpy(annotations, pDynamicAnnotations->annotations, sizeof(AnnotationMap) * pDynamicAnnotations->count); free(pDynamicAnnotations->annotations); } pDynamicAnnotations->annotations = annotations; pDynamicAnnotations->alloc_count = alloc_count; return 0; } static DynamicAnnotations *iniAllocAnnotations(IniContext *pContext, const int annotation_count) { DynamicAnnotations *pDynamicAnnotations; pDynamicAnnotations = iniAllocDynamicAnnotation(pContext); if (pDynamicAnnotations == NULL) { logError("file: "__FILE__", line: %d, " "malloc dynamic annotations fail", __LINE__); return NULL; } if (iniCheckAllocAnnotations(pDynamicAnnotations, annotation_count) == 0) { return pDynamicAnnotations; } else { return NULL; } } static bool iniMatchValue(const char *target, char **values, const int count) { int i; for (i=0; i= IP_ADDRESS_SIZE) { logWarning("file: "__FILE__", line: %d, " "invalid ip address: %s", __LINE__, ip_addr); return false; } memcpy(ip_part, ip_addr, ip_len); *(ip_part + ip_len) = '\0'; pReservedEnd = NULL; network_bits = strtol(pSlash + 1, &pReservedEnd, 10); if (!(pReservedEnd == NULL || *pReservedEnd == '\0')) { logError("file: "__FILE__", line: %d, " \ "ip address: %s, invalid network bits: %s", __LINE__, ip_addr, pSlash + 1); return false; } if (network_bits < 8 || network_bits > 30) { logError("file: "__FILE__", line: %d, " \ "ip address: %s, invalid network bits: %d, " \ "it should >= 8 and <= 30", \ __LINE__, ip_addr, network_bits); return false; } if (inet_pton(AF_INET, ip_part, &addr) != 1) { logError("file: "__FILE__", line: %d, " \ "ip address: %s, invalid ip part: %s", \ __LINE__, ip_addr, ip_part); return false; } network_hip = ntohl(addr.s_addr); if (inet_pton(AF_INET, target, &addr) != 1) { logError("file: "__FILE__", line: %d, " "invalid ip: %s", __LINE__, target); return false; } target_hip = ntohl(addr.s_addr); network_mask = ((1 << network_bits) - 1) << (32 - network_bits); return (target_hip & network_mask) == (network_hip & network_mask); } static bool iniMatchIP(const char *target, char **values, const int count) { int i; char *pSlash; for (i=0; i condition && (*p == ' ' || *p == '\t')) { p--; } if (*p != ']') { logWarning("file: "__FILE__", line: %d, " "expect \"]\", condition: %.*s", __LINE__, condition_len, condition); return false; } pSquareEnd = p; p = condition; while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } len = pEnd - p; if (len < 8 || !(*p == '%' && *(p+1) == '{')) { logWarning("file: "__FILE__", line: %d, " "invalid condition: %.*s, " "correct format: %%{variable} in [...]", __LINE__, condition_len, condition); return false; } if ((len > _PREPROCESS_VARIABLE_LEN_LOCAL_IP) && (memcmp(p, _PREPROCESS_VARIABLE_STR_LOCAL_IP, _PREPROCESS_VARIABLE_LEN_LOCAL_IP) == 0)) { varType = _PREPROCESS_VARIABLE_TYPE_LOCAL_IP; p += _PREPROCESS_VARIABLE_LEN_LOCAL_IP; } else if ((len > _PREPROCESS_VARIABLE_LEN_LOCAL_HOST) && memcmp(p, _PREPROCESS_VARIABLE_STR_LOCAL_HOST, _PREPROCESS_VARIABLE_LEN_LOCAL_HOST) == 0) { varType = _PREPROCESS_VARIABLE_TYPE_LOCAL_HOST; p += _PREPROCESS_VARIABLE_LEN_LOCAL_HOST; } else { varType = _PREPROCESS_VARIABLE_TYPE_SET; pBraceEnd = (char *)memchr(p + 2, '}', len - 2); if (pBraceEnd == NULL) { logWarning("file: "__FILE__", line: %d, " "invalid condition: %.*s, expect }", __LINE__, condition_len, condition); return false; } varStr = p + 2; varLen = pBraceEnd - varStr; if (varLen == 0) { logWarning("file: "__FILE__", line: %d, " "invalid condition: %.*s, " "expect variable name", __LINE__, condition_len, condition); return false; } p = pBraceEnd + 1; } while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } if (pEnd - p < 4 || memcmp(p, "in", 2) != 0) { logWarning("file: "__FILE__", line: %d, " "expect \"in\", condition: %.*s", __LINE__, condition_len, condition); return false; } p += 2; //skip in while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } if (*p != '[') { logWarning("file: "__FILE__", line: %d, " "expect \"[\", condition: %.*s", __LINE__, condition_len, condition); return false; } *pSquareEnd = '\0'; count = splitEx(p + 1, ',', values, _PREPROCESS_MAX_LIST_VALUE_COUNT); for (i=0; ivars != NULL) { value = (char *)hash_find(set->vars, varStr, varLen); if (value == NULL) { logWarning("file: "__FILE__", line: %d, " "variable \"%.*s\" not exist", __LINE__, varLen, varStr); } else { return iniMatchValue(value, values, count); } } else { logWarning("file: "__FILE__", line: %d, " "variable \"%.*s\" not exist", __LINE__, varLen, varStr); return false; } } return false; } static char *iniFindTag(char *content, char *pStart, const char *tagStr, const int tagLen) { char *p; while (1) { p = strstr(pStart, tagStr); if (p == NULL) { return NULL; } if (isLeadingSpacesLine(content, p)) { return p; } pStart = p + tagLen; } } static char *iniFindAloneTag(char *content, const int content_len, char *pStart, const char *tagStr, const int tagLen) { char *p; while ((p=iniFindTag(content, pStart, tagStr, tagLen)) != NULL) { if (isTrailingSpacesLine(p + tagLen, content + content_len)) { return p; } } return NULL; } static int iniDoProccessSet(char *pSet, char **ppSetEnd, IniContext *pContext) { char *pStart; char buff[FAST_INI_ITEM_NAME_LEN + FAST_INI_ITEM_VALUE_LEN]; char output[256]; int result; int len; char *parts[2]; char *key; char *value; int value_len; SetDirectiveVars *set; pStart = pSet + _PREPROCESS_TAG_LEN_SET; *ppSetEnd = strchr(pStart, '\n'); if (*ppSetEnd == NULL) { return EINVAL; } len = *ppSetEnd - pStart; if (len <= 1 || len >= (int)sizeof(buff)) { return EINVAL; } memcpy(buff, pStart, len); *(buff + len) = '\0'; if (splitEx(buff, '=', parts, 2) != 2) { logWarning("file: "__FILE__", line: %d, " "invalid set format: %s%s", __LINE__, _PREPROCESS_TAG_STR_SET, buff); return EFAULT; } if ((set=iniAllocVars(pContext, true)) == NULL) { return ENOMEM; } key = fc_trim(parts[0]); value = fc_trim(parts[1]); value_len = strlen(value); if (value_len > 3 && (*value == '$' && *(value + 1) == '(') && *(value + value_len - 1) == ')') { char *cmd; cmd = value + 2; *(value + value_len - 1) = '\0'; //remove ')' if ((pContext->flags & FAST_INI_FLAGS_SHELL_EXECUTE) != 0) { if ((result=getExecResult(cmd, output, sizeof(output))) != 0) { logWarning("file: "__FILE__", line: %d, " "exec %s fail, errno: %d, error info: %s", __LINE__, cmd, result, STRERROR(result)); return result; } if (*output == '\0') { logWarning("file: "__FILE__", line: %d, " "empty reply when exec: %s", __LINE__, cmd); } value = fc_trim(output); value_len = strlen(value); } else { logWarning("file: "__FILE__", line: %d, " "shell execute disabled, cmd: %s", __LINE__, cmd); } } return hash_insert_ex(set->vars, key, strlen(key), value, value_len + 1, false); } static int iniProccessSet(char *content, char *pEnd, IniContext *pContext) { int result; SetDirectiveVars *set; char *pStart; char *pSet; char *pSetEnd; if ((set=iniAllocVars(pContext, false)) == NULL) { return ENOMEM; } pStart = content + set->offset; while (pStart < pEnd) { pSet = iniFindTag(content, pStart, _PREPROCESS_TAG_STR_SET, _PREPROCESS_TAG_LEN_SET); if (pSet == NULL || pSet >= pEnd) { break; } if ((result=iniDoProccessSet(pSet, &pSetEnd, pContext)) == 0) { pStart = pSetEnd; } else { if (result == EINVAL) { char *pNewLine;; pNewLine = pSet + _PREPROCESS_TAG_LEN_SET; while (pNewLine < pEnd && *pNewLine != '\n') { ++pNewLine; } logWarning("file: "__FILE__", line: %d, " "invalid set format: %.*s", __LINE__, (int)(pNewLine - pSet), pSet); } pStart = pSet + _PREPROCESS_TAG_LEN_SET; } } set->offset = pEnd - content; return 0; } static char *iniProccessIf(char *content, const int content_len, int *offset, IniContext *pContext, int *new_content_len) { char *pStart; char *pEnd; char *pCondition; char *pElse; char *pIfPart; char *pElsePart; int conditionLen; int ifPartLen; int elsePartLen; int copyLen; char *newContent; char *pDest; *new_content_len = content_len; pStart = iniFindTag(content, content + (*offset), _PREPROCESS_TAG_STR_IF, _PREPROCESS_TAG_LEN_IF); if (pStart == NULL) { *offset = *new_content_len; iniProccessSet(content, content + content_len, pContext); return content; } iniProccessSet(content, pStart, pContext); pCondition = pStart + _PREPROCESS_TAG_LEN_IF; pIfPart = strchr(pCondition, '\n'); if (pIfPart == NULL) { logWarning("file: "__FILE__", line: %d, " "expect new line (\\n) for %s", __LINE__, pStart); *offset = *new_content_len; return content; } conditionLen = pIfPart - pCondition; pEnd = iniFindAloneTag(content, content_len, pIfPart, _PREPROCESS_TAG_STR_ENDIF, _PREPROCESS_TAG_LEN_ENDIF); if (pEnd == NULL) { logWarning("file: "__FILE__", line: %d, " "expect %s for %.*s", __LINE__, _PREPROCESS_TAG_STR_ENDIF, (int)(pIfPart - pStart), pStart); *offset = *new_content_len; return content; } pElse = iniFindAloneTag(content, content_len, pIfPart, _PREPROCESS_TAG_STR_ELSE, _PREPROCESS_TAG_LEN_ELSE); if (pElse == NULL || pElse > pEnd) { ifPartLen = pEnd - pIfPart; pElsePart = NULL; elsePartLen = 0; } else { ifPartLen = pElse - pIfPart; pElsePart = strchr(pElse + _PREPROCESS_TAG_LEN_ELSE, '\n'); if (pElsePart == NULL) { *offset = (pEnd + _PREPROCESS_TAG_LEN_ENDIF) - content; return content; } elsePartLen = pEnd - pElsePart; } newContent = iniAllocContent(pContext, content_len); if (newContent == NULL) { *offset = (pEnd + _PREPROCESS_TAG_LEN_ENDIF) - content; return NULL; } pDest = newContent; copyLen = pStart - content; if (copyLen > 0) { memcpy(pDest, content, copyLen); pDest += copyLen; } *offset = copyLen; if (iniCalcCondition(pCondition, conditionLen, pContext)) { if (ifPartLen > 0) { memcpy(pDest, pIfPart, ifPartLen); pDest += ifPartLen; } } else { if (elsePartLen > 0) { memcpy(pDest, pElsePart, elsePartLen); pDest += elsePartLen; } } copyLen = (content + content_len) - (pEnd + _PREPROCESS_TAG_LEN_ENDIF); if (copyLen > 0) { memcpy(pDest, pEnd + _PREPROCESS_TAG_LEN_ENDIF, copyLen); pDest += copyLen; } *pDest = '\0'; *new_content_len = pDest - newContent; return newContent; } static char *iniGetInteger(char *str, char *pEnd, int *nlen) { char *p; char *pNumber; p = str; while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } pNumber = p; while (p < pEnd && (*p >= '0' && *p <= '9')) { p++; } *nlen = p - pNumber; return pNumber; } static int iniParseForRange(char *range, const int range_len, char **id, int *idLen, int *start, int *end, int *step) { /** * * #@for i from 0 to 15 step 1 */ char *p; char *pEnd; char *pNumber; int nlen; pEnd = range + range_len; p = range; while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } if (pEnd - p < 10) { logWarning("file: "__FILE__", line: %d, " "unkown for range: %.*s", __LINE__, range_len, range); return EINVAL; } *id = p; while (p < pEnd && !(*p == ' ' || *p == '\t')) { p++; } *idLen = p - *id; if (*idLen == 0 || *idLen > 64) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } if (pEnd - p < 8) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } p++; while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } if (!(memcmp(p, _PREPROCESS_TAG_STR_FOR_FROM, _PREPROCESS_TAG_LEN_FOR_FROM) == 0 && (*(p+_PREPROCESS_TAG_LEN_FOR_FROM) == ' ' || *(p+_PREPROCESS_TAG_LEN_FOR_FROM) == '\t'))) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } p += _PREPROCESS_TAG_LEN_FOR_FROM + 1; pNumber = iniGetInteger(p, pEnd, &nlen); if (nlen == 0) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } *start = atoi(pNumber); p = pNumber + nlen; if (pEnd - p < 4 || !(*p == ' ' || *p == '\t')) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } p++; while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } if (!(memcmp(p, _PREPROCESS_TAG_STR_FOR_TO, _PREPROCESS_TAG_LEN_FOR_TO) == 0 && (*(p+_PREPROCESS_TAG_LEN_FOR_TO) == ' ' || *(p+_PREPROCESS_TAG_LEN_FOR_TO) == '\t'))) { logWarning("file: "__FILE__", line: %d, " "unkown for range: %.*s", __LINE__, range_len, range); return EINVAL; } p += _PREPROCESS_TAG_LEN_FOR_TO + 1; pNumber = iniGetInteger(p, pEnd, &nlen); if (nlen == 0) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } *end = atoi(pNumber); p = pNumber + nlen; if (p == pEnd) { *step = 1; return 0; } if (!(*p == ' ' || *p == '\t')) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } if (!(memcmp(p, _PREPROCESS_TAG_STR_FOR_STEP, _PREPROCESS_TAG_LEN_FOR_STEP) == 0 && (*(p+_PREPROCESS_TAG_LEN_FOR_STEP) == ' ' || *(p+_PREPROCESS_TAG_LEN_FOR_STEP) == '\t'))) { logWarning("file: "__FILE__", line: %d, " "unkown for range: %.*s", __LINE__, range_len, range); return EINVAL; } p += _PREPROCESS_TAG_LEN_FOR_STEP + 1; pNumber = iniGetInteger(p, pEnd, &nlen); if (nlen == 0) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } *step = atoi(pNumber); p = pNumber + nlen; while (p < pEnd && (*p == ' ' || *p == '\t')) { p++; } if (p != pEnd) { logWarning("file: "__FILE__", line: %d, " "invalid for range: %.*s", __LINE__, range_len, range); return EINVAL; } return 0; } static char *iniProccessFor(char *content, const int content_len, int *offset, IniContext *pContext, int *new_content_len) { char *pStart; char *pEnd; char *pForRange; char *pForBlock; char *id; char tag[80]; char value[16]; int idLen; int rangeLen; int forBlockLen; int start; int end; int step; int count; int i; int copyLen; int tagLen; int valueLen; char *newContent; char *pDest; *new_content_len = content_len; pStart = iniFindTag(content, content + (*offset), _PREPROCESS_TAG_STR_FOR, _PREPROCESS_TAG_LEN_FOR); if (pStart == NULL) { *offset = *new_content_len; return content; } pForRange = pStart + _PREPROCESS_TAG_LEN_FOR; pForBlock = strchr(pForRange, '\n'); if (pForBlock == NULL) { logWarning("file: "__FILE__", line: %d, " "expect new line (\\n) for %s", __LINE__, pStart); *offset = *new_content_len; return content; } rangeLen = pForBlock - pForRange; pEnd = iniFindAloneTag(content, content_len, pForBlock, _PREPROCESS_TAG_STR_ENDFOR, _PREPROCESS_TAG_LEN_ENDFOR); if (pEnd == NULL) { logWarning("file: "__FILE__", line: %d, " "expect %s for %s", __LINE__, _PREPROCESS_TAG_STR_ENDFOR, pStart); *offset = *new_content_len; return content; } forBlockLen = pEnd - pForBlock; if (iniParseForRange(pForRange, rangeLen, &id, &idLen, &start, &end, &step) != 0) { logWarning("file: "__FILE__", line: %d, " "invalid statement: %.*s", __LINE__, (int)(pForBlock - pStart), pStart); *offset = (pEnd + _PREPROCESS_TAG_LEN_ENDFOR) - content; return content; } if (step == 0) { logWarning("file: "__FILE__", line: %d, " "invalid step: %d for range: %.*s, set step to 1", __LINE__, step, rangeLen, pForRange); *offset = (pEnd + _PREPROCESS_TAG_LEN_ENDFOR) - content; return content; } else { count = (end - start) / step; if (count < 0) { logWarning("file: "__FILE__", line: %d, " "invalid step: %d for range: %.*s", __LINE__, step, rangeLen, pForRange); *offset = (pEnd + _PREPROCESS_TAG_LEN_ENDFOR) - content; return content; } } newContent = iniAllocContent(pContext, content_len + (forBlockLen + 16) * count); if (newContent == NULL) { *offset = (pEnd + _PREPROCESS_TAG_LEN_ENDFOR) - content; return NULL; } pDest = newContent; copyLen = pStart - content; if (copyLen > 0) { memcpy(pDest, content, copyLen); pDest += copyLen; } *offset = copyLen; tagLen = sprintf(tag, "{$%.*s}", idLen, id); for (i=start; i<=end; i+=step) { char *p; char *pRemain; int remainLen; valueLen = sprintf(value, "%d", i); pRemain = pForBlock; remainLen = forBlockLen; while (remainLen >= tagLen) { p = (char *)memmem(pRemain, remainLen, tag, tagLen); if (p == NULL) { memcpy(pDest, pRemain, remainLen); pDest += remainLen; break; } copyLen = p - pRemain; if (copyLen > 0) { memcpy(pDest, pRemain, copyLen); pDest += copyLen; } memcpy(pDest, value, valueLen); pDest += valueLen; pRemain = p + tagLen; remainLen -= copyLen + tagLen; } } copyLen = (content + content_len) - (pEnd + _PREPROCESS_TAG_LEN_ENDFOR); if (copyLen > 0) { memcpy(pDest, pEnd + _PREPROCESS_TAG_LEN_ENDFOR, copyLen); pDest += copyLen; } *pDest = '\0'; *new_content_len = pDest - newContent; return newContent; } static int iniLoadItemsFromBuffer(char *content, IniContext *pContext) { char *pContent; char *new_content; int content_len; int new_content_len; int offset; new_content = content; new_content_len = strlen(content); do { offset = 0; pContent = new_content; content_len = new_content_len; if ((new_content=iniProccessIf(pContent, content_len, &offset, pContext, &new_content_len)) == NULL) { return ENOMEM; } } while (offset < new_content_len); do { offset = 0; pContent = new_content; content_len = new_content_len; if ((new_content=iniProccessFor(pContent, content_len, &offset, pContext, &new_content_len)) == NULL) { return ENOMEM; } } while (offset < new_content_len); return iniDoLoadItemsFromBuffer(new_content, pContext); } static int remallocSection(IniSection *pSection, IniItem **pItem) { int bytes; int result; int alloc_count; IniItem *pNew; if (pSection->alloc_count == 0) { alloc_count = _INIT_ALLOC_ITEM_COUNT; } else { alloc_count = pSection->alloc_count * 2; } bytes = sizeof(IniItem) * alloc_count; pNew = (IniItem *)malloc(bytes); if (pNew == NULL) { logError("file: "__FILE__", line: %d, " \ "malloc %d bytes fail", __LINE__, bytes); result = errno != 0 ? errno : ENOMEM; return result; } if (pSection->count > 0) { memcpy(pNew, pSection->items, sizeof(IniItem) * pSection->count); free(pSection->items); } pSection->alloc_count = alloc_count; pSection->items = pNew; *pItem = pSection->items + pSection->count; memset(*pItem, 0, sizeof(IniItem) * (pSection->alloc_count - pSection->count)); return 0; } static int iniFreeHashData(const int index, const HashData *data, void *args) { IniSection *pSection; pSection = (IniSection *)data->value; if (pSection == NULL) { return 0; } if (pSection->items != NULL) { free(pSection->items); memset(pSection, 0, sizeof(IniSection)); } free(pSection); ((HashData *)data)->value = NULL; return 0; } void iniFreeContext(IniContext *pContext) { SetDirectiveVars *set; if (pContext == NULL) { return; } if (pContext->global.items != NULL) { free(pContext->global.items); memset(&pContext->global, 0, sizeof(IniSection)); } hash_walk(&pContext->sections, iniFreeHashData, NULL); hash_destroy(&pContext->sections); set = iniGetVars(pContext); if (set != NULL && set->vars != NULL) { hash_destroy(set->vars); free(set->vars); set->vars = NULL; } iniFreeDynamicContent(pContext); } #define INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \ targetItem, pItem, return_val) \ do { \ if (szSectionName == NULL || *szSectionName == '\0') \ { \ pSection = &pContext->global; \ } \ else \ { \ pSection = (IniSection *)hash_find(&pContext->sections, \ szSectionName, strlen(szSectionName)); \ if (pSection == NULL) \ { \ return return_val; \ } \ } \ \ if (pSection->count <= 0) \ { \ return return_val; \ } \ \ snprintf(targetItem.name, sizeof(targetItem.name), "%s", szItemName); \ pItem = (IniItem *)bsearch(&targetItem, pSection->items, \ pSection->count, sizeof(IniItem), iniCompareByItemName); \ } while (0) char *iniGetStrValue(const char *szSectionName, const char *szItemName, \ IniContext *pContext) { IniItem targetItem; IniSection *pSection; IniItem *pFound; IniItem *pItem; IniItem *pItemEnd; INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \ targetItem, pFound, NULL); if (pFound == NULL) { return NULL; } pItemEnd = pSection->items + pSection->count; for (pItem=pFound+1; pItemname, szItemName) != 0) { break; } pFound = pItem; } return pFound->value; } int64_t iniGetInt64Value(const char *szSectionName, const char *szItemName, \ IniContext *pContext, const int64_t nDefaultValue) { char *pValue; pValue = iniGetStrValue(szSectionName, szItemName, pContext); if (pValue == NULL) { return nDefaultValue; } else { return strtoll(pValue, NULL, 10); } } int iniGetIntValue(const char *szSectionName, const char *szItemName, \ IniContext *pContext, const int nDefaultValue) { char *pValue; pValue = iniGetStrValue(szSectionName, szItemName, pContext); if (pValue == NULL) { return nDefaultValue; } else { return atoi(pValue); } } double iniGetDoubleValue(const char *szSectionName, const char *szItemName, \ IniContext *pContext, const double dbDefaultValue) { char *pValue; pValue = iniGetStrValue(szSectionName, szItemName, pContext); if (pValue == NULL) { return dbDefaultValue; } else { return strtod(pValue, NULL); } } bool iniGetBoolValue(const char *szSectionName, const char *szItemName, \ IniContext *pContext, const bool bDefaultValue) { char *pValue; pValue = iniGetStrValue(szSectionName, szItemName, pContext); if (pValue == NULL) { return bDefaultValue; } else { return FAST_INI_STRING_IS_TRUE(pValue); } } int iniGetValues(const char *szSectionName, const char *szItemName, \ IniContext *pContext, char **szValues, const int max_values) { IniItem *pItem; IniItem *pItemEnd; char **ppValues; int count; if (max_values <= 0) { return 0; } pItem = iniGetValuesEx(szSectionName, szItemName, pContext, &count); if (count == 0) { return 0; } if (count > max_values) { count = max_values; } ppValues = szValues; pItemEnd = pItem + count; for (; pItemvalue; } return count; } IniItem *iniGetValuesEx(const char *szSectionName, const char *szItemName, \ IniContext *pContext, int *nTargetCount) { IniItem targetItem; IniSection *pSection; IniItem *pFound; IniItem *pItem; IniItem *pItemEnd; IniItem *pItemStart; *nTargetCount = 0; INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, \ targetItem, pFound, NULL); if (pFound == NULL) { return NULL; } *nTargetCount = 1; for (pItem=pFound-1; pItem>=pSection->items; pItem--) { if (strcmp(pItem->name, szItemName) != 0) { break; } (*nTargetCount)++; } pItemStart = pFound - (*nTargetCount) + 1; pItemEnd = pSection->items + pSection->count; for (pItem=pFound+1; pItemname, szItemName) != 0) { break; } (*nTargetCount)++; } return pItemStart; } static int iniPrintHashData(const int index, const HashData *data, void *args) { IniSection *pSection; IniItem *pItem; IniItem *pItemEnd; char section_name[256]; int section_len; int i; pSection = (IniSection *)data->value; if (pSection == NULL) { return 0; } section_len = data->key_len; if (section_len >= sizeof(section_name)) { section_len = sizeof(section_name) - 1; } memcpy(section_name, data->key, section_len); *(section_name + section_len) = '\0'; printf("section: %s, item count: %d\n", section_name, pSection->count); if (pSection->count > 0) { i = 0; pItemEnd = pSection->items + pSection->count; for (pItem=pSection->items; pItemname, pItem->value); } } printf("\n"); return 0; } void iniPrintItems(IniContext *pContext) { IniItem *pItem; IniItem *pItemEnd; int i; printf("global section, item count: %d\n", pContext->global.count); if (pContext->global.count > 0) { i = 0; pItemEnd = pContext->global.items + pContext->global.count; for (pItem=pContext->global.items; pItemname, pItem->value); } } printf("\n"); hash_walk(&pContext->sections, iniPrintHashData, NULL); } struct section_walk_arg { IniSectionInfo *sections; int count; int size; }; static int iniSectionWalkCallback(const int index, const HashData *data, void *args) { struct section_walk_arg *walk_arg; IniSection *pSection; char *section_name; int section_len; pSection = (IniSection *)data->value; if (pSection == NULL) { return 0; } walk_arg = (struct section_walk_arg *)args; if (walk_arg->count >= walk_arg->size) { return ENOSPC; } section_len = data->key_len; if (section_len > FAST_INI_ITEM_NAME_LEN) { section_len = FAST_INI_ITEM_NAME_LEN; } section_name = walk_arg->sections[walk_arg->count].section_name; memcpy(section_name, data->key, section_len); *(section_name + section_len) = '\0'; walk_arg->sections[walk_arg->count].pSection = pSection; walk_arg->count++; return 0; } int iniGetSectionNames(IniContext *pContext, IniSectionInfo *sections, const int max_size, int *nCount) { struct section_walk_arg walk_arg; int result; walk_arg.sections = sections; walk_arg.count = 0; walk_arg.size = max_size; result = hash_walk(&pContext->sections, iniSectionWalkCallback, &walk_arg); *nCount = walk_arg.count; return result; } IniItem *iniGetSectionItems(const char *szSectionName, IniContext *pContext, int *nCount) { IniSection *pSection; if (szSectionName == NULL || *szSectionName == '\0') { pSection = &pContext->global; } else { pSection = (IniSection *)hash_find(&pContext->sections, szSectionName, strlen(szSectionName)); if (pSection == NULL) { *nCount = 0; return NULL; } } *nCount = pSection->count; return pSection->items; }