/* * Copyright (c) 2020 YuQing <384681@qq.com> * * This program is free software: you can use, redistribute, and/or modify * it under the terms of the Lesser GNU General Public License, version 3 * or later ("LGPL"), as published by the Free Software Foundation. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. * * You should have received a copy of the Lesser GNU General Public License * along with this program. If not, see . */ //ini_file_reader.c #include #include #include #include #include #include #include #include "shared_func.h" #include "logger.h" #include "http_func.h" #include "local_ip_func.h" #include "pthread_func.h" #include "fc_memory.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 _INIT_DYNAMIC_CONTENTS 8 #define _BUILTIN_ANNOTATION_COUNT 3 static AnnotationEntry *g_annotations = NULL; static int g_annotation_count = 0; typedef struct { int count; int alloc; char **contents; } DynamicContents; typedef struct { int offset; //deal offset HashArray *vars; //variables with #@set } SetDirectiveVars; typedef struct { int count; int alloc; AnnotationEntry *annotations; } DynamicAnnotations; typedef struct { bool used; IniContext *context; DynamicContents dynamicContents; SetDirectiveVars set; DynamicAnnotations dynamicAnnotations; } CDCPair; typedef struct { volatile int init_counter; int alloc; int count; int index; CDCPair *contents; pthread_mutex_t lock; } DynamicContentArray; //dynamic alloced contents which will be freed when destroy static DynamicContentArray g_dynamic_content_array = {0, 0, 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 AnnotationEntry *iniGetAnnotations(IniContext *pContext); static SetDirectiveVars *iniGetVars(IniContext *pContext); #define STR_TRIM(pStr) \ do { \ trim_right(pStr); \ trim_left(pStr); \ } while (0) #define RETRY_FETCH_GLOBAL(szSectionName, bRetryGlobal) \ ((szSectionName != NULL && *szSectionName != '\0') && bRetryGlobal) static void iniDoSetAnnotations(AnnotationEntry *src, const int src_count, AnnotationEntry *dest, int *dest_count) { AnnotationEntry *pSrc; AnnotationEntry *pSrcEnd; AnnotationEntry *pDest; AnnotationEntry *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->arg = pSrc->arg; pDest->func_init = pSrc->func_init; pDest->func_destroy = pSrc->func_destroy; pDest->func_get = pSrc->func_get; pDest->func_free = pSrc->func_free; pDest->dlhandle = pSrc->dlhandle; pDest->inited = false; if (pDest == pDestEnd) //insert { ++(*dest_count); pDestEnd = dest + *dest_count; } } } static AnnotationEntry *iniFindAnnotation(AnnotationEntry *annotatios, const char *func_name) { AnnotationEntry *pAnnoEntry; if (annotatios == NULL) { return NULL; } pAnnoEntry = annotatios; while (pAnnoEntry->func_name != NULL) { if (strcmp(func_name, pAnnoEntry->func_name) == 0) { return pAnnoEntry; } pAnnoEntry++; } return NULL; } static int iniAnnotationFuncLocalIpGet(IniContext *context, struct ini_annotation_entry *annotation, const IniItem *item, char **pOutValue, int max_values) { bool need_private_ip; int count; int index; char param[FAST_INI_ITEM_VALUE_SIZE]; const char *next_ip; char *square_start; char name_part[16]; strcpy(param, item->value); 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, struct ini_annotation_entry *annotation, const IniItem *item, char **pOutValue, int max_values) { int count; int result; char *output; count = 0; output = (char *)fc_malloc(FAST_INI_ITEM_VALUE_SIZE); if (output == NULL) { return count; } if ((result=getExecResult(item->value, output, FAST_INI_ITEM_VALUE_SIZE)) != 0) { logWarning("file: "__FILE__", line: %d, " "exec %s fail, errno: %d, error info: %s", __LINE__, item->value, result, STRERROR(result)); free(output); return count; } if (*output == '\0') { logWarning("file: "__FILE__", line: %d, " "empty reply when exec: %s", __LINE__, item->value); } pOutValue[count++] = fc_trim(output); return count; } static int iniCopyBuffer(char *dest, const int size, const 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 char *doReplaceVars(IniContext *pContext, const char *param, const int max_size) { #define VARIABLE_TAG_MIN_LENGTH 4 //%{v} SetDirectiveVars *set; const char *p; const char *e; char name[64]; const char *start; const char *value; const char *pLoopEnd; const char *pEnd; char *pDest; char *output; int name_len; int len; output = (char *)fc_malloc(max_size); if (output == NULL) { return NULL; } set = iniGetVars(pContext); if (set == NULL || set->vars == 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); return output; } 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) { len = strlen(value); } else { logWarning("file: "__FILE__", line: %d, " "name: %s NOT found, keep the origin", __LINE__, name); value = start - 2; len = p - value; } pDest += iniCopyBuffer(pDest, FAST_INI_ITEM_VALUE_SIZE - (pDest - output), value, len); } 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'; return output; } static int iniAnnotationReplaceVars(IniContext *pContext, struct ini_annotation_entry *annotation, const IniItem *item, char **pOutValue, int max_values) { char *output; output = doReplaceVars(pContext, item->value, FAST_INI_ITEM_VALUE_SIZE); if (output == NULL) { return 0; } else { pOutValue[0] = output; return 1; } } void iniAnnotationFreeValues(struct ini_annotation_entry *annotation, char **values, const int count) { int i; for (i=0; ifunc_name = "LOCAL_IP_GET"; pAnnotation->func_get = iniAnnotationFuncLocalIpGet; pAnnotation++; pAnnotation->func_name = "REPLACE_VARS"; pAnnotation->func_get = iniAnnotationReplaceVars; pAnnotation->func_free = iniAnnotationFreeValues; pAnnotation++; if ((pContext->flags & FAST_INI_FLAGS_SHELL_EXECUTE) != 0) { pAnnotation->func_name = "SHELL_EXEC"; pAnnotation->func_get = iniAnnotationFuncShellExec; pAnnotation->func_free = iniAnnotationFreeValues; pAnnotation++; } iniDoSetAnnotations(builtins, pAnnotation - builtins, dest, dest_count); } static int iniSetAnnotations(IniContext *pContext, const char annotation_type, AnnotationEntry *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(AnnotationEntry *annotations, int count) { int bytes; if (count <= 0) { logWarning("file: "__FILE__", line: %d, " "iniSetAnnotationCallBack fail, count(%d) is invalid.", __LINE__, count); return EINVAL; } bytes = sizeof(AnnotationEntry) * (g_annotation_count + count + 1); g_annotations = (AnnotationEntry *)fc_realloc(g_annotations, bytes); if (g_annotations == NULL) { return ENOMEM; } memset(g_annotations + g_annotation_count, 0, sizeof(AnnotationEntry) * (count + 1)); iniDoSetAnnotations(annotations, count, g_annotations, &g_annotation_count); return 0; } void iniDestroyAnnotationCallBack() { AnnotationEntry *pAnnoEntry; if (g_annotations == NULL) { return; } pAnnoEntry = g_annotations; while (pAnnoEntry->func_name) { if (pAnnoEntry->func_destroy != NULL) { pAnnoEntry->func_destroy(pAnnoEntry); } if (pAnnoEntry->dlhandle != NULL) { dlclose(pAnnoEntry->dlhandle); } pAnnoEntry++; } free(g_annotations); g_annotations = 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, AnnotationEntry *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); } static void iniDestroyAnnotations(const int old_annotation_count) { AnnotationEntry *pAnnoEntry; if (g_annotations == NULL) { return; } logDebug("iniDestroyAnnotations, old_annotation_count: %d, " "g_annotation_count: %d", old_annotation_count, g_annotation_count); if (old_annotation_count == 0) { iniDestroyAnnotationCallBack(); return; } pAnnoEntry = g_annotations + old_annotation_count; while (pAnnoEntry->func_name) { if (pAnnoEntry->func_destroy != NULL) { pAnnoEntry->func_destroy(pAnnoEntry); } if (pAnnoEntry->dlhandle != NULL) { dlclose(pAnnoEntry->dlhandle); } pAnnoEntry++; } memset(g_annotations + old_annotation_count, 0, sizeof(AnnotationEntry) * (g_annotation_count - old_annotation_count)); g_annotation_count = old_annotation_count; } int iniLoadFromFileEx(const char *szFilename, IniContext *pContext, const char annotation_type, AnnotationEntry *annotations, const int count, const char flags) { int result; int len; char *pLast; char full_filename[MAX_PATH_SIZE]; int old_annotation_count; 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'; } } } old_annotation_count = g_annotation_count; result = iniDoLoadFromFile(full_filename, pContext); if (g_annotation_count > old_annotation_count) { iniDestroyAnnotations(old_annotation_count); } 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, AnnotationEntry *annotations, const int count, const char flags) { int result; int old_annotation_count; if ((result=iniInitContext(pContext, annotation_type, annotations, count, flags)) != 0) { return result; } old_annotation_count = g_annotation_count; result = iniLoadItemsFromBuffer(content, pContext); if (g_annotation_count > old_annotation_count) { iniDestroyAnnotations(old_annotation_count); } 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); } typedef int (*init_annotation_func0)(AnnotationEntry *annotation); typedef int (*init_annotation_func1)(AnnotationEntry *annotation, const char *arg1); typedef int (*init_annotation_func2)(AnnotationEntry *annotation, const char *arg1, const char *arg2); typedef int (*init_annotation_func3)(AnnotationEntry *annotation, const char *arg1, const char *arg2, const char *arg3); static int iniAddAnnotation(char *params) { #define MAX_PARAMS 5 char *cols[MAX_PARAMS]; char *func_name; char *library; AnnotationEntry annotation; void *dlhandle; void *init_func; char symbol[64]; int argc; int count; int result; trim(params); count = fc_split_string(params, " \t", cols, MAX_PARAMS); if (count < 2) { logError("file: "__FILE__", line: %d, " "invalid format, correct format: " "#@add_annotation FUNC_NAME library ...", __LINE__); return EINVAL; } func_name = fc_trim(cols[0]); library = fc_trim(cols[1]); if (*func_name == '\0') { logError("file: "__FILE__", line: %d, " "empty func name, correct format: " "#@add_annotation FUNC_NAME library ...", __LINE__); return EINVAL; } if (*library == '\0') { logError("file: "__FILE__", line: %d, " "empty library, correct format: " "#@add_annotation FUNC_NAME library ...", __LINE__); return EINVAL; } if (iniFindAnnotation(g_annotations, func_name) != NULL) { logWarning("file: "__FILE__", line: %d, " "function %s already exist", __LINE__, func_name); return EEXIST; } if (strcmp(library, "-") == 0) { library = NULL; } else if (!fileExists(library)) { logError("file: "__FILE__", line: %d, " "library %s not exist", __LINE__, library); return ENOENT; } dlhandle = dlopen(library, RTLD_LAZY); if (dlhandle == NULL) { logError("file: "__FILE__", line: %d, " "dlopen %s fail, error info: %s", __LINE__, library != NULL ? library : "", dlerror()); return EFAULT; } snprintf(symbol, sizeof(symbol), "%s_init_annotation", func_name); init_func = dlsym(dlhandle, symbol); if (init_func == NULL) { logError("file: "__FILE__", line: %d, " "dlsym function %s fail, error info: %s", __LINE__, symbol, dlerror()); dlclose(dlhandle); return ENOENT; } memset(&annotation, 0, sizeof(annotation)); argc = count - 2; switch (argc) { case 0: result = ((init_annotation_func0)init_func)(&annotation); break; case 1: result = ((init_annotation_func1)init_func)(&annotation, cols[2]); break; case 2: result = ((init_annotation_func2)init_func)(&annotation, cols[2], cols[3]); break; case 3: result = ((init_annotation_func3)init_func)(&annotation, cols[2], cols[3], cols[4]); break; default: result = 0; break; } if (result != 0) { logError("file: "__FILE__", line: %d, " "call function %s fail, ret: %d", __LINE__, symbol, result); dlclose(dlhandle); return EFAULT; } annotation.dlhandle = dlhandle; return iniSetAnnotationCallBack(&annotation, 1); } 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; } STR_TRIM(pLine); if (*pLine == '#' && \ strncasecmp(pLine+1, "include", 7) == 0 && \ (*(pLine+8) == ' ' || *(pLine+8) == '\t')) { pIncludeFilename = fc_strdup(pLine + 9); if (pIncludeFilename == NULL) { result = 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 == '#') { if (pContext->annotation_type == FAST_INI_ANNOTATION_DISABLE) { continue; } if (strncasecmp(pLine+1, "@function", 9) == 0 && (*(pLine+10) == ' ' || *(pLine+10) == '\t')) { 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__); } } else if (strncasecmp(pLine+1, "@add_annotation", 15) == 0 && (*(pLine+16) == ' ' || *(pLine+16) == '\t')) { result = iniAddAnnotation(pLine + 17); if (!(result == 0 || result == EEXIST)) { break; } } continue; } if (*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 *)fc_malloc(sizeof(IniSection)); if (pSection == NULL) { result = ENOMEM; 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; } } else if ((pContext->flags & FAST_INI_FLAGS_DISABLE_SAME_SECTION_MERGE) != 0) { result = EEXIST; logError("file: "__FILE__", line: %d, " "section [%s] already exist", __LINE__, section_name); break; } 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) { 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) { AnnotationEntry *pAnnoEntryBase; AnnotationEntry *pAnnoEntry = NULL; isAnnotation = 0; if ((pAnnoEntryBase=iniGetAnnotations(pContext)) == NULL) { pAnnoEntryBase = g_annotations; } if (pAnnoEntryBase == 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; } nItemCnt = -1; for (i=0; i<2; i++) { pAnnoEntry = iniFindAnnotation(pAnnoEntryBase, pFuncName); if (pAnnoEntry != NULL) { if (pAnnoEntry->func_init != NULL && !pAnnoEntry->inited) { pAnnoEntry->inited = true; pAnnoEntry->func_init(pAnnoEntry); } if (pAnnoEntry->func_get != NULL) { nItemCnt = pAnnoEntry->func_get(pContext, pAnnoEntry, pItem, pItemValues, 100); } break; } if (g_annotations != NULL && pAnnoEntryBase != g_annotations) { pAnnoEntryBase = g_annotations; } 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) { result = remallocSection(pSection, &pItem); if (result != 0) { break; } } } if (pAnnoEntry != NULL && pAnnoEntry->func_free != NULL) { pAnnoEntry->func_free(pAnnoEntry, 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 inline int checkInitDynamicContentArray() { if (__sync_fetch_and_add(&g_dynamic_content_array.init_counter, 1) != 0) { return 0; } return init_pthread_lock(&g_dynamic_content_array.lock); } static int checkAllocDynamicContentArray() { CDCPair *contents; int alloc; int bytes; if (g_dynamic_content_array.alloc > g_dynamic_content_array.count) { return 0; } alloc = (g_dynamic_content_array.alloc == 0) ? _INIT_DYNAMIC_CONTENTS : g_dynamic_content_array.alloc * 2; bytes = sizeof(CDCPair) * alloc; contents = (CDCPair *)fc_malloc(bytes); if (contents == NULL) { return ENOMEM; } memset(contents, 0, bytes); if (g_dynamic_content_array.alloc > 0) { memcpy(contents, g_dynamic_content_array.contents, sizeof(CDCPair) * g_dynamic_content_array.alloc); free(g_dynamic_content_array.contents); } g_dynamic_content_array.contents = contents; g_dynamic_content_array.alloc = alloc; return 0; } static CDCPair *iniGetCDCPair(IniContext *pContext) { int i; CDCPair *pair; if (checkInitDynamicContentArray() != 0) { return NULL; } pthread_mutex_lock(&g_dynamic_content_array.lock); if (g_dynamic_content_array.contents == NULL) { pair = NULL; } else if (g_dynamic_content_array.contents[g_dynamic_content_array.index]. context == pContext) { pair = g_dynamic_content_array.contents + g_dynamic_content_array.index; } else { pair = NULL; if (g_dynamic_content_array.count > 0) { for (i=0; idynamicContents; } static SetDirectiveVars *iniGetVars(IniContext *pContext) { CDCPair *pair; pair = iniGetCDCPair(pContext); if (pair == NULL) { return NULL; } return &pair->set; } static DynamicAnnotations *iniAllocDynamicAnnotation(IniContext *pContext) { CDCPair *pair; pair = iniAllocCDCPair(pContext); if (pair == NULL) { return NULL; } return &pair->dynamicAnnotations; } static AnnotationEntry *iniGetAnnotations(IniContext *pContext) { CDCPair *pair; pair = iniGetCDCPair(pContext); if (pair == NULL) { return NULL; } return pair->dynamicAnnotations.annotations; } static SetDirectiveVars *iniAllocVars(IniContext *pContext, const bool initVars) { 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 *)fc_malloc(sizeof(HashArray)); if (set->vars == NULL) { 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 (checkInitDynamicContentArray() != 0) { return; } pthread_mutex_lock(&g_dynamic_content_array.lock); do { if (g_dynamic_content_array.count == 0) { break; } if (g_dynamic_content_array.contents[g_dynamic_content_array.index]. context == pContext) { pCDCPair = g_dynamic_content_array.contents + g_dynamic_content_array.index; } else { pCDCPair = NULL; for (i=0; idynamicContents; 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 = 0; pDynamicContents->count = 0; } pDynamicAnnotations = &pCDCPair->dynamicAnnotations; if (pDynamicAnnotations->annotations != NULL) { free(pDynamicAnnotations->annotations); pDynamicAnnotations->annotations = NULL; pDynamicAnnotations->alloc = 0; pDynamicAnnotations->count = 0; } pCDCPair->used = false; pCDCPair->context = NULL; g_dynamic_content_array.count--; } while (0); pthread_mutex_unlock(&g_dynamic_content_array.lock); } 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) { int alloc; int bytes; char **contents; if (pDynamicContents->alloc == 0) { alloc = 8; } else { alloc = pDynamicContents->alloc * 2; } bytes = sizeof(char *) * alloc; contents = (char **)fc_malloc(bytes); if (contents == NULL) { 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 = alloc; } buff = fc_malloc(content_len); if (buff == NULL) { return NULL; } pDynamicContents->contents[pDynamicContents->count++] = buff; return buff; } static int iniCheckAllocAnnotations(DynamicAnnotations *pDynamicAnnotations, const int annotation_count) { int alloc; int bytes; AnnotationEntry *annotations; if (pDynamicAnnotations->count + annotation_count < pDynamicAnnotations->alloc) { return 0; } if (pDynamicAnnotations->alloc == 0) { alloc = 8; } else { alloc = pDynamicAnnotations->alloc * 2; } while (alloc <= pDynamicAnnotations->count + annotation_count) { alloc *= 2; } bytes = sizeof(AnnotationEntry) * alloc; annotations = (AnnotationEntry *)fc_malloc(bytes); if (annotations == NULL) { return ENOMEM; } memset(annotations, 0, bytes); if (pDynamicAnnotations->count > 0) { memcpy(annotations, pDynamicAnnotations->annotations, sizeof(AnnotationEntry) * pDynamicAnnotations->count); free(pDynamicAnnotations->annotations); } pDynamicAnnotations->annotations = annotations; pDynamicAnnotations->alloc = alloc; 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 + 1]; char output[256]; int result; int len; bool is_exec; char *parts[2]; char *key; char *value; int value_len; char *new_value; 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); is_exec = (value_len > 3 && (*value == '$' && *(value + 1) == '(') && *(value + value_len - 1) == ')'); pStart = strstr(value, "%{"); if (pStart != NULL && strchr(pStart + 2, '}') != NULL) { new_value = doReplaceVars(pContext, value, _LINE_BUFFER_SIZE); if (new_value != NULL) { value_len = strlen(new_value); } else { new_value = value; //rollback } } else { new_value = value; } if (is_exec) { char *cmd; cmd = new_value + 2; *(new_value + value_len - 1) = '\0'; //remove ')' logDebug("file: "__FILE__", line: %d, cmd: %s", __LINE__, cmd); 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); } fc_trim(output); value_len = strlen(output); if (new_value != value) { free(new_value); } new_value = fc_strdup(output); if (new_value == NULL) { new_value = value; value_len = 0; } } else { logWarning("file: "__FILE__", line: %d, " "shell execute disabled, cmd: %s", __LINE__, cmd); *new_value = '\0'; value_len = 0; } } result = hash_insert_ex(set->vars, key, strlen(key), new_value, value_len + 1, false); if (new_value != value) { free(new_value); } return result >= 0 ? 0 : -1 * result; } 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 alloc; IniItem *pNew; if (pSection->alloc == 0) { alloc = _INIT_ALLOC_ITEM_COUNT; } else { alloc = pSection->alloc * 2; } bytes = sizeof(IniItem) * alloc; pNew = (IniItem *)fc_malloc(bytes); if (pNew == NULL) { return ENOMEM; } if (pSection->count > 0) { memcpy(pNew, pSection->items, sizeof(IniItem) * pSection->count); free(pSection->items); } pSection->alloc = alloc; pSection->items = pNew; *pItem = pSection->items + pSection->count; memset(*pItem, 0, sizeof(IniItem) * (pSection->alloc - 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; set->offset = 0; } 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 *iniGetStrValueEx(const char *szSectionName, const char *szItemName, IniContext *pContext, const bool bRetryGlobal) { IniItem targetItem; IniSection *pSection; IniItem *pFound; IniItem *pItem; IniItem *pItemEnd; INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, targetItem, pFound, NULL); if (pFound == NULL) { if (RETRY_FETCH_GLOBAL(szSectionName, bRetryGlobal)) { szSectionName = NULL; INI_FIND_ITEM(szSectionName, szItemName, pContext, pSection, targetItem, pFound, NULL); if (pFound == NULL) { return NULL; } } else { return NULL; } } pItemEnd = pSection->items + pSection->count; for (pItem=pFound+1; pItemname, szItemName) == 0) { pFound = pItem; } else { break; } } return pFound->value; } int64_t iniGetInt64ValueEx(const char *szSectionName, const char *szItemName, IniContext *pContext, const int64_t nDefaultValue, const bool bRetryGlobal) { char *pValue; pValue = iniGetStrValueEx(szSectionName, szItemName, pContext, bRetryGlobal); if (pValue == NULL) { return nDefaultValue; } else { return strtoll(pValue, NULL, 10); } } int iniGetIntValueEx(const char *szSectionName, const char *szItemName, IniContext *pContext, const int nDefaultValue, const bool bRetryGlobal) { char *pValue; pValue = iniGetStrValueEx(szSectionName, szItemName, pContext, bRetryGlobal); if (pValue == NULL) { return nDefaultValue; } else { return atoi(pValue); } } double iniGetDoubleValueEx(const char *szSectionName, const char *szItemName, IniContext *pContext, const double dbDefaultValue, const bool bRetryGlobal) { char *pValue; pValue = iniGetStrValueEx(szSectionName, szItemName, pContext, bRetryGlobal); if (pValue == NULL) { return dbDefaultValue; } else { return strtod(pValue, NULL); } } bool iniGetBoolValueEx(const char *szSectionName, const char *szItemName, IniContext *pContext, const bool bDefaultValue, const bool bRetryGlobal) { char *pValue; pValue = iniGetStrValueEx(szSectionName, szItemName, pContext, bRetryGlobal); 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_name_walk_arg { IniSectionInfo *sections; IniSectionNameFilterFunc filter_func; void *args; int count; int size; }; static int iniSectionNameWalkCallback(const int index, const HashData *data, void *args) { struct section_name_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_name_walk_arg *)args; if (walk_arg->filter_func != NULL && !walk_arg-> filter_func(data->key, data->key_len, walk_arg->args)) { return 0; } 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 iniGetSectionNamesEx(IniContext *pContext, IniSectionNameFilterFunc filter_func, void *args, IniSectionInfo *sections, const int max_size, int *nCount) { struct section_name_walk_arg walk_arg; int result; walk_arg.sections = sections; walk_arg.filter_func = filter_func; walk_arg.args = args; walk_arg.size = max_size; walk_arg.count = 0; result = hash_walk(&pContext->sections, iniSectionNameWalkCallback, &walk_arg); *nCount = walk_arg.count; return result; } int iniGetSectionNames(IniContext *pContext, IniSectionInfo *sections, const int max_size, int *nCount) { return iniGetSectionNamesEx(pContext, NULL, NULL, sections, max_size, nCount); } static bool iniSectionNameFilterByPrefix(const char *section_name, const int name_len, void *args) { string_t *prefix; prefix = (string_t *)args; if (name_len < prefix->len) { return false; } return memcmp(section_name, prefix->str, prefix->len) == 0; } int iniGetSectionNamesByPrefix(IniContext *pContext, const char *szPrefix, IniSectionInfo *sections, const int max_size, int *nCount) { string_t prefix; FC_SET_STRING(prefix, (char *)szPrefix); return iniGetSectionNamesEx(pContext, iniSectionNameFilterByPrefix, &prefix, sections, max_size, nCount); } struct section_count_walk_arg { IniSectionNameFilterFunc filter_func; void *args; int count; }; static int iniSectionCountWalkCallback(const int index, const HashData *data, void *args) { struct section_count_walk_arg *walk_arg; IniSection *pSection; pSection = (IniSection *)data->value; if (pSection == NULL) { return 0; } walk_arg = (struct section_count_walk_arg *)args; if (walk_arg->filter_func == NULL || walk_arg-> filter_func(data->key, data->key_len, walk_arg->args)) { walk_arg->count++; } return 0; } int iniGetSectionCountEx(IniContext *pContext, IniSectionNameFilterFunc filter_func, void *args) { struct section_count_walk_arg walk_arg; walk_arg.filter_func = filter_func; walk_arg.args = args; walk_arg.count = 0; hash_walk(&pContext->sections, iniSectionCountWalkCallback, &walk_arg); return walk_arg.count; } int iniGetSectionCountByPrefix(IniContext *pContext, const char *szPrefix) { string_t prefix; FC_SET_STRING(prefix, (char *)szPrefix); return iniGetSectionCountEx(pContext, iniSectionNameFilterByPrefix, &prefix); } 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; } char *iniGetRequiredStrValueEx(const char *szSectionName, const char *szItemName, IniContext *pContext, const int nMinLength) { char *value; value = iniGetStrValue(szSectionName, szItemName, pContext); if (value == NULL) { logError("file: "__FILE__", line: %d, " "item: %s not exist", __LINE__, szItemName); return NULL; } if (nMinLength > 0) { if (nMinLength == 1 && *value == '\0') { logError("file: "__FILE__", line: %d, " "item: %s, value is empty", __LINE__, szItemName); return NULL; } else { int len; len = strlen(value); if (len < nMinLength) { logError("file: "__FILE__", line: %d, " "item: %s, value length: %d < min length: %d", __LINE__, szItemName, len, nMinLength); return NULL; } } } return value; } int iniGetPercentValueEx(IniFullContext *ini_ctx, const char *item_name, double *item_value, const double default_value, const bool retry_global) { char *value; char *last; value = iniGetStrValueEx(ini_ctx->section_name, item_name, ini_ctx->context, retry_global); if (value == NULL || *value == '\0') { *item_value = default_value; } else { double d; char *endptr; last = value + strlen(value) - 1; if (*last != '%') { logError("file: "__FILE__", line: %d, " "config file: %s, item: %s, value: %s " "is NOT a valid ratio, expect end char: %%", __LINE__, ini_ctx->filename, item_name, value); return EINVAL; } d = strtod(value, &endptr); if ((endptr != last) || (d <= 0.00001 || d >= 100.00001)) { logError("file: "__FILE__", line: %d, " "config file: %s, item: %s, " "value: %s is NOT a valid ratio", __LINE__, ini_ctx->filename, item_name, value); return EINVAL; } *item_value = d / 100.00; } return 0; }