#include #include "pine-ini.h" #define isWhitespace(c) ((c) == ' ' || c == '\r' || c == '\n' || c == '\t') const char * PINE_INI_ERRMSG[] = { "No error", "Illegal pattern", "Empty key", "Sections exceed", "Parameters exceed" }; // // * Parser // #define PARSER_ACTION_CONTINUE 1 #define PARSER_ACTION_END 0 typedef struct tagPineIniParser { const char * textBuffer; const char * currentPointer; int lineNumber; } PineIniParser; static PineIniParser* PineIni_Parser_New(const char* textBuffer) { PineIniParser* parser = (PineIniParser *)malloc(sizeof(PineIniParser)); parser->textBuffer = textBuffer; parser->currentPointer = parser->textBuffer; parser->lineNumber = 0; return parser; } static void PineIni_Parser_Destory(PineIniParser* parser) { if (parser) { free(parser); } } // // Return // PARSER_ACTION_END -> buffer end // PARSER_ACTION_CONTINUE -> buffer has follow-up content // static int PineIni_Parser_Gets(PineIniParser* parser, char* buf) { char *pBuf = buf; parser->lineNumber++; while (*parser->currentPointer != '\n' && *parser->currentPointer != '\0') { *(pBuf++) = *(parser->currentPointer++); } *pBuf = '\0'; if (*parser->currentPointer == '\0') { return PARSER_ACTION_END; } else { // Skip LF parser->currentPointer++; return PARSER_ACTION_CONTINUE; } } static PINE_BOOL PineIni_Line_Empty(const char* line, const int length) { if (length <= 0 || PineIni_StringEquals("", line)) { return PINE_TRUE; } return PINE_FALSE; } static PINE_BOOL PineIni_Line_IsComment(const char* line, const int length) { if (length >= 1 && line[0] == ';') { return PINE_TRUE; } return PINE_FALSE; } static PINE_BOOL PineIni_Line_IsSection(const char* line, const int length) { if (length >= 2 && line[0] == '[' && line[length - 1] == ']') { return PINE_TRUE; } return PINE_FALSE; } static PINE_BOOL PineIni_Line_IsParameter(const char* line, const int length) { int i; for (i = 0; i < length; ++i) { if (line[i] == '=') { return PINE_TRUE; } } return PINE_FALSE; } // // * Parse API // #define PARSER_RETURN_ERROR(error_code) (void *) 0; \ errorRet->lineNumber = parser->lineNumber; \ errorRet->errorCode = (error_code); \ strcpy(errorRet->lineContent, lineBuffer); \ PineIni_Parser_Destory(parser); \ PineIni_Destory(iniResult); \ return NULL; PineIniFile* PineIni_Parse(const char* iniText, PineIniError* errorRet) { char lineBuffer[PINE_INI_LINE_MAX_LEN]; int lineLength; int getsRetCode; PineIniParser* parser; PineIniFile* iniResult; PineIniSection* currentSection; parser = PineIni_Parser_New(iniText); // Create iniResult iniResult = (PineIniFile *)malloc(sizeof(PineIniFile)); iniResult->numSection = 0; // Create a default section for file // and append to iniResult currentSection = PineIni_Section_New("_default"); PineIni_Append(iniResult, currentSection); // Clear error info errorRet->lineNumber = 0; errorRet->errorCode = PINE_INI_ERRCODE_NO; strcpy(errorRet->lineContent, ""); do { // Get line from parser getsRetCode = PineIni_Parser_Gets(parser, lineBuffer); // Trim white spaces PineIni_StringTrim(lineBuffer); // Check pattern lineLength = strlen(lineBuffer); // Pattern : empty if (PineIni_Line_Empty(lineBuffer, lineLength)) { continue; } // Pattern : comment else if (PineIni_Line_IsComment(lineBuffer, lineLength)) { continue; } // Pattern : section else if (PineIni_Line_IsSection(lineBuffer, lineLength)) { char sectionName[PINE_INI_LINE_MAX_LEN]; PineIniSection* section; PineIni_Substring(sectionName, lineBuffer, 1, lineLength - 2); section = PineIni_Find(iniResult, sectionName); // Section name not exist in iniResult if (section == NULL) { // Check if exceed if (iniResult->numSection >= PINE_INI_MAX_NUM_SECTIONS) { PARSER_RETURN_ERROR(PINE_INI_ERRCODE_SECTION_EXCEED); } // Create a new section and append in iniResult section = PineIni_Section_New(sectionName); PineIni_Append(iniResult, section); } currentSection = section; continue; } // Pattern : parameter else if (PineIni_Line_IsParameter(lineBuffer, lineLength)) { char key[PINE_INI_LINE_MAX_LEN]; char value[PINE_INI_LINE_MAX_LEN]; int equalPos; PineIniParameter* param; // Get key and value equalPos = PineIni_FindChar(lineBuffer, '='); PineIni_Substring(key, lineBuffer, 0, equalPos - 1); PineIni_Substring(value, lineBuffer, equalPos + 1, lineLength - 1); PineIni_StringTrim(key); PineIni_StringRemoveQuotes(value); // Error: empty key if (strlen(key) <= 0) { PARSER_RETURN_ERROR(PINE_INI_ERRCODE_EMPTY_KEY); } // Check if key already exist param = PineIni_Section_Find(currentSection, key); // Exist, update value if (param) { PineIni_Parameter_Assign(param, value); } // Not exist, append to current section else { // Check if exceed if (currentSection->numParam >= PINE_INI_MAX_NUM_PARAMETERS) { PARSER_RETURN_ERROR(PINE_INI_ERRCODE_PARAMS_EXCEED); } PineIni_Section_Append(currentSection, key, value); } continue; } // Illegal Pattern! else { PARSER_RETURN_ERROR(PINE_INI_ERRCODE_ILLEGAL_PATTERN); } } while(getsRetCode != PARSER_ACTION_END); PineIni_Parser_Destory(parser); return iniResult; } PineIniSection* PineIni_Find(PineIniFile* file, const char* sectionName) { if (file) { int i; for (i = 0; i < file->numSection; ++i) { PineIniSection* section = file->sections[i]; if (PineIni_StringEquals(sectionName, section->name)) { return section; } } } return NULL; } PineIniFile* PineIni_Append(PineIniFile* file, PineIniSection* section) { if (file) { file->sections[file->numSection++] = section; } return file; } void PineIni_Destory(PineIniFile* file) { if (file) { int i; // destory sections in file for (i = 0; i < file->numSection; ++i) { PineIni_Section_Destory(file->sections[i]); } // free file self free(file); } } // Section PineIniSection* PineIni_Section_New(const char* sectionName) { PineIniSection* section = (PineIniSection *)malloc(sizeof(PineIniSection)); section->name = PineIni_StringDump(sectionName); section->numParam = 0; return section; } PineIniParameter* PineIni_Section_Find(PineIniSection* section, const char* key) { if (section) { int i; for (i = 0; i < section->numParam; ++i) { PineIniParameter* param = section->params[i]; if (PineIni_StringEquals(key, param->key)) { return param; } } } return NULL; } PineIniSection* PineIni_Section_Append(PineIniSection* section, const char* key, const char* value) { if (section) { section->params[section->numParam++] = PineIni_Parameter_New(key, value); } return section; } void PineIni_Section_Destory(PineIniSection* section) { if (section) { int i; // free section name if (section->name) { free(section->name); } // destory parameters in section for (i = 0; i < section->numParam; ++i) { PineIni_Parameter_Destory(section->params[i]); } // free section self free(section); } } // Parameter PineIniParameter* PineIni_Parameter_New(const char* key, const char* value) { PineIniParameter* param; param = (PineIniParameter *)malloc(sizeof(PineIniParameter)); param->key = PineIni_StringDump(key); param->value = PineIni_StringDump(value); return param; } PineIniParameter* PineIni_Parameter_Assign(PineIniParameter* param, const char* value) { if (param) { if (param->value) { free(param->value); } param->value = PineIni_StringDump(value); } return param; } void PineIni_Parameter_Destory(PineIniParameter* param) { if (param) { if (param->key) { free(param->key); } if (param->value) { free(param->value); } free(param); } } // Utils functions int PineIni_FindChar(const char* src, const char find) { int i; for (i = 0; src[i]; ++i) { if (src[i] == find) { return i; } } return -1; } char* PineIni_Substring(char* dest, const char* src, int left, int right) { char* buf = dest; int newLength = 0; int i; for (i = left; i <= right; ++i) { buf[newLength++] = src[i]; } buf[newLength] = 0; return dest; } char* PineIni_ToSubstring (char* src, int left, int right) { int length = strlen(src); char* buf = (char *)malloc(length + 1); int newLength = 0; int i; for (i = left; i <= right; ++i) { buf[newLength++] = src[i]; } buf[newLength] = 0; strcpy(src, buf); free(buf); return src; } char* PineIni_StringDump (const char* src) { int length = strlen(src); char* dup = (char *)malloc(length + 1); strcpy(dup, src); return dup; } char* PineIni_StringTrim(char* src) { int length = strlen(src); int left; int right; int newLength = 0; for (left = 0; left < length; ++left) { if (!isWhitespace(src[left])) { break; } } for (right = length - 1; right >= 0; --right) { if (!isWhitespace(src[right])) { break; } } return PineIni_ToSubstring(src, left, right); } char* PineIni_StringRemoveQuotes(char* src) { int length; int isSingleQuotesAround; int isDoubleQuotesAround; PineIni_StringTrim(src); // Too short, no need to check if ((length = strlen(src)) < 2) { return src; } isSingleQuotesAround = (src[0] == '"' && src[length - 1] == '"'); isDoubleQuotesAround = (src[0] == '\'' && src[length - 1] == '\''); if (!isSingleQuotesAround && !isDoubleQuotesAround) { return src; } return PineIni_ToSubstring(src, 1, length - 2); }