ds@1952: /** ds@1952: * vim: set ts=4 : ds@1952: * ============================================================================= ds@1952: * SourceMod ds@1952: * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved. ds@1952: * ============================================================================= ds@1952: * ds@1952: * This program is free software; you can redistribute it and/or modify it under ds@1952: * the terms of the GNU General Public License, version 3.0, as published by the ds@1952: * Free Software Foundation. ds@1952: * ds@1952: * This program is distributed in the hope that it will be useful, but WITHOUT ds@1952: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS ds@1952: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more ds@1952: * details. ds@1952: * ds@1952: * You should have received a copy of the GNU General Public License along with ds@1952: * this program. If not, see . ds@1952: * ds@1952: * As a special exception, AlliedModders LLC gives you permission to link the ds@1952: * code of this program (as well as its derivative works) to "Half-Life 2," the ds@1952: * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software ds@1952: * by the Valve Corporation. You must obey the GNU General Public License in ds@1952: * all respects for all other code used. Additionally, AlliedModders LLC grants ds@1952: * this exception to all derivative works. AlliedModders LLC defines further ds@1952: * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007), ds@1952: * or . ds@1952: * ds@1952: * Version: $Id$ ds@1952: */ ds@1952: ds@1952: #include ds@1952: #include ds@1952: #include ds@1952: #include ds@1952: #include ds@1952: #include ds@1952: #include "TextParsers.h" ds@1952: #include "ShareSys.h" ds@1952: #include "sm_stringutil.h" ds@1952: #include "LibrarySys.h" ds@1952: ds@1952: TextParsers g_TextParser; ds@1952: ITextParsers *textparsers = &g_TextParser; ds@1952: ds@1952: static int g_ini_chartable1[255] = {0}; ds@1952: static int g_ws_chartable[255] = {0}; ds@1952: ds@1952: bool TextParsers::IsWhitespace(const char *stream) ds@1952: { ds@1952: return g_ws_chartable[(unsigned)*stream] == 1; ds@1952: } ds@1952: ds@1952: TextParsers::TextParsers() ds@1952: { ds@1952: g_ini_chartable1[(unsigned)'_'] = 1; ds@1952: g_ini_chartable1[(unsigned)'-'] = 1; ds@1952: g_ini_chartable1[(unsigned)','] = 1; ds@1952: g_ini_chartable1[(unsigned)'+'] = 1; ds@1952: g_ini_chartable1[(unsigned)'.'] = 1; ds@1952: g_ini_chartable1[(unsigned)'$'] = 1; ds@1952: g_ini_chartable1[(unsigned)'?'] = 1; ds@1952: g_ini_chartable1[(unsigned)'/'] = 1; ds@1952: g_ws_chartable[(unsigned)'\n'] = 1; ds@1952: g_ws_chartable[(unsigned)'\v'] = 1; ds@1952: g_ws_chartable[(unsigned)'\r'] = 1; ds@1952: g_ws_chartable[(unsigned)'\t'] = 1; ds@1952: g_ws_chartable[(unsigned)'\f'] = 1; ds@1952: g_ws_chartable[(unsigned)' '] = 1; ds@1952: } ds@1952: ds@1952: void TextParsers::OnSourceModAllInitialized() ds@1952: { ds@1952: g_ShareSys.AddInterface(NULL, this); ds@1952: } ds@1952: ds@1952: unsigned int TextParsers::GetUTF8CharBytes(const char *stream) ds@1952: { ds@1952: return _GetUTF8CharBytes(stream); ds@1952: } ds@1952: ds@1952: /** ds@1952: * Character streams ds@1952: */ ds@1952: ds@1952: struct CharStream ds@1952: { ds@1952: const char *curpos; ds@1952: }; ds@1952: ds@1952: bool CharStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read) ds@1952: { ds@1952: CharStream *srdr = (CharStream *)stream; ds@1952: ds@1952: const char *ptr = srdr->curpos; ds@1952: for (size_t i=0; icurpos; ds@1952: ds@1952: srdr->curpos = ptr; ds@1952: ds@1952: return true; ds@1952: } ds@1952: ds@1952: SMCError TextParsers::ParseString_SMC(const char *stream, ds@1952: ITextListener_SMC *smc, ds@1952: SMCStates *states) ds@1952: { ds@1952: CharStream srdr = { stream }; ds@1952: ds@1952: return ParseStream_SMC(&srdr, CharStreamReader, smc, states); ds@1952: } ds@1952: ds@1952: /** ds@1952: * File streams ds@1952: */ ds@1952: ds@1952: bool FileStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read) ds@1952: { ds@1952: size_t num = fread(buffer, 1, maxlength, (FILE *)stream); ds@1952: ds@1952: *read = static_cast(num); ds@1952: ds@1952: if (num == 0 && feof((FILE *)stream)) ds@1952: { ds@1952: return true; ds@1952: } ds@1952: ds@1952: return (ferror((FILE *)stream) == 0); ds@1952: } ds@1952: ds@1952: SMCError TextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc, SMCStates *states) ds@1952: { ds@1952: FILE *fp = fopen(file, "rt"); ds@1952: ds@1952: if (!fp) ds@1952: { ds@1952: if (states != NULL) ds@1952: { ds@1952: states->line = 0; ds@1952: states->col = 0; ds@1952: } ds@1952: return SMCError_StreamOpen; ds@1952: } ds@1952: ds@1952: SMCError result = ParseStream_SMC(fp, FileStreamReader, smc, states); ds@1952: ds@1952: fclose(fp); ds@1952: ds@1952: return result; ds@1952: } ds@1952: ds@1952: SMCError TextParsers::ParseSMCFile(const char *file, ds@1952: ITextListener_SMC *smc_listener, ds@1952: SMCStates *states, ds@1952: char *buffer, ds@1952: size_t maxsize) ds@1952: { ds@1952: const char *errstr; ds@1952: FILE *fp = fopen(file, "rt"); ds@1952: ds@1952: if (fp == NULL) ds@1952: { ds@1952: char error[256] = "unknown"; ds@1952: if (states != NULL) ds@1952: { ds@1952: states->line = 0; ds@1952: states->col = 0; ds@1952: } ds@1952: g_LibSys.GetPlatformError(error, sizeof(error)); ds@1952: UTIL_Format(buffer, maxsize, "File could not be opened: %s", error); ds@1952: return SMCError_StreamOpen; ds@1952: } ds@1952: ds@1952: SMCError result = ParseStream_SMC(fp, FileStreamReader, smc_listener, states); ds@1952: ds@1952: fclose(fp); ds@1952: ds@1952: errstr = GetSMCErrorString(result); ds@1952: UTIL_Format(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error"); ds@1952: ds@1952: return result; ds@1952: } ds@1952: ds@1952: /** ds@1952: * Raw parsing of streams with helper functions ds@1952: */ ds@1952: ds@1952: struct StringInfo ds@1952: { ds@1952: StringInfo() : quoted(false), ptr(NULL), end(NULL), special(false) { } ds@1952: bool quoted; ds@1952: char *ptr; ds@1952: char *end; ds@1952: bool special; ds@1952: }; ds@1952: ds@1952: const char *FixupString(StringInfo &data) ds@1952: { ds@1952: if (!data.ptr) ds@1952: { ds@1952: return NULL; ds@1952: } ds@1952: ds@1952: if (data.quoted) ds@1952: { ds@1952: data.ptr++; ds@1952: } ds@1952: #if defined _DEBUG ds@1952: else { ds@1952: /* A string will never have beginning whitespace because we ignore it in the stream. ds@1952: * Furthermore, if there is trailing whitespace, the end ptr will point to it, so it is valid ds@1952: * to overwrite! Lastly, the last character must be whitespace or a comment/invalid character. ds@1952: */ ds@1952: } ds@1952: #endif ds@1952: ds@1952: /* Do some extra work on strings that have special quoted characters. */ ds@1952: if (data.special) ds@1952: { ds@1952: char *outptr = data.ptr; ds@1952: size_t len = data.end - data.ptr; ds@1952: if (len >= 2) ds@1952: { ds@1952: for (size_t i=0; i=0; i--) ds@1952: { ds@1952: if (info[i].ptr) ds@1952: { ds@1952: return info[i].ptr; ds@1952: } ds@1952: } ds@1952: ds@1952: return NULL; ds@1952: } ds@1952: ds@1952: SMCError TextParsers::ParseStream_SMC(void *stream, ds@1952: STREAMREADER srdr, ds@1952: ITextListener_SMC *smc, ds@1952: SMCStates *pStates) ds@1952: { ds@1952: char *reparse_point = NULL; ds@1952: char in_buf[4096]; ds@1952: char *parse_point = in_buf; ds@1952: char *line_begin = in_buf; ds@1952: unsigned int read; ds@1952: unsigned int curlevel = 0; ds@1952: bool in_quote = false; ds@1952: bool ignoring = false; ds@1952: bool eol_comment = false; ds@1952: bool ml_comment = false; ds@1952: unsigned int i; ds@1952: SMCError err = SMCError_Okay; ds@1952: SMCResult res; ds@1952: SMCStates states; ds@1952: char c; ds@1952: ds@1952: StringInfo strings[3]; ds@1952: StringInfo emptystring; ds@1952: ds@1952: states.line = 1; ds@1952: states.col = 0; ds@1952: ds@1952: smc->ReadSMC_ParseStart(); ds@1952: ds@1952: /** ds@1952: * The stream reader reads in as much as it can fill the buffer with. ds@1952: * It then processes the buffer. If the buffer cannot be fully processed, for example, ds@1952: * a line is left hanging with no newline, then the contents of the buffer is shifted ds@1952: * down, and the buffer is filled from the stream reader again. ds@1952: * ds@1952: * What makes this particularly annoying is that we cache pointers everywhere, so when ds@1952: * the shifting process takes place, all those pointers must be shifted as well. ds@1952: */ ds@1952: while (srdr(stream, parse_point, sizeof(in_buf) - (parse_point - in_buf) - 1, &read)) ds@1952: { ds@1952: if (!read) ds@1952: { ds@1952: break; ds@1952: } ds@1952: ds@1952: /* Check for BOM markings, which is only relevant on the first line. ds@1952: * Not worth it, but it could be moved out of the loop. ds@1952: */ ds@1952: if (states.line == 1 && ds@1952: in_buf[0] == (char)0xEF && ds@1952: in_buf[1] == (char)0xBB && ds@1952: in_buf[2] == (char)0xBF) ds@1952: { ds@1952: /* Move EVERYTHING down :\ */ ds@1952: memmove(in_buf, &in_buf[3], read - 3); ds@1952: read -= 3; ds@1952: } ds@1952: ds@1952: if (reparse_point) ds@1952: { ds@1952: read += (parse_point - reparse_point); ds@1952: parse_point = reparse_point; ds@1952: reparse_point = NULL; ds@1952: } ds@1952: ds@1952: for (i=0; iReadSMC_RawLine(&states, line_begin)) != SMCResult_Continue) ds@1952: { ds@1952: err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; ds@1952: goto failed; ds@1952: } ds@1952: parse_point[i] = '\n'; ds@1952: ds@1952: /* Now we check the sanity of our staged strings! */ ds@1952: if (strings[2].ptr) ds@1952: { ds@1952: if (!curlevel) ds@1952: { ds@1952: err = SMCError_InvalidProperty1; ds@1952: goto failed; ds@1952: } ds@1952: /* Assume the next string is a property and pass the info on. */ ds@1952: if ((res=smc->ReadSMC_KeyValue( ds@1952: &states, ds@1952: FixupString(strings[2]), ds@1952: FixupString(strings[1]))) != SMCResult_Continue) ds@1952: { ds@1952: err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; ds@1952: goto failed; ds@1952: } ds@1952: scrap(strings); ds@1952: } ds@1952: ds@1952: /* Change the states for the next line */ ds@1952: states.col = 0; ds@1952: states.line++; ds@1952: line_begin = &parse_point[i+1]; //Note: safe because this gets relocated later ds@1952: } ds@1952: else if (ignoring) ds@1952: { ds@1952: if (in_quote) ds@1952: { ds@1952: /* If i was 0, we could have reparsed, so make sure there's no buffer underrun */ ds@1952: if ((&parse_point[i] != in_buf) && c == '"' && parse_point[i-1] != '\\') ds@1952: { ds@1952: /* If we reached a quote in an ignore phase, ds@1952: * we're staging a string and we must rotate it out. ds@1952: */ ds@1952: in_quote = false; ds@1952: ignoring = false; ds@1952: /* Set our info */ ds@1952: strings[0].end = &parse_point[i]; ds@1952: strings[0].quoted = true; ds@1952: if (rotate(strings) != NULL) ds@1952: { ds@1952: /* If we rotated too many strings, there was too much crap on one line */ ds@1952: err = SMCError_InvalidTokens; ds@1952: goto failed; ds@1952: } ds@1952: } ds@1952: else if (c == '\\') ds@1952: { ds@1952: strings[0].special = true; ds@1952: if (i == (read - 1)) ds@1952: { ds@1952: reparse_point = &parse_point[i]; ds@1952: break; ds@1952: } ds@1952: } ds@1952: } ds@1952: else if (ml_comment) ds@1952: { ds@1952: if (c == '*') ds@1952: { ds@1952: /* Check if we need to get more input first */ ds@1952: if (i == read - 1) ds@1952: { ds@1952: reparse_point = &parse_point[i]; ds@1952: break; ds@1952: } ds@1952: if (parse_point[i+1] == '/') ds@1952: { ds@1952: ml_comment = false; ds@1952: ignoring = false; ds@1952: /* We should not be staging anything right now. */ ds@1952: assert(strings[0].ptr == NULL); ds@1952: /* Advance the input stream so we don't choke on this token */ ds@1952: i++; ds@1952: states.col++; ds@1952: } ds@1952: } ds@1952: } ds@1952: } ds@1952: else ds@1952: { ds@1952: /* Check if we're whitespace or not */ ds@1952: if (!g_ws_chartable[(unsigned)c]) ds@1952: { ds@1952: bool restage = false; ds@1952: /* Check various special tokens: ds@1952: * ; ds@1952: * // ds@1952: * / * ds@1952: * { ds@1952: * } ds@1952: */ ds@1952: if (c == ';' || c == '/') ds@1952: { ds@1952: /* If it's a line-based comment (that is, ; or //) ds@1952: * we will need to scrap everything until the end of the line. ds@1952: */ ds@1952: if (c == '/') ds@1952: { ds@1952: if (i == read - 1) ds@1952: { ds@1952: /* If we reached the end of the look-ahead, we need to re-check our input. ds@1952: * Breaking out will force this to be the new reparse point! ds@1952: */ ds@1952: reparse_point = &parse_point[i]; ds@1952: break; ds@1952: } ds@1952: if (parse_point[i + 1] == '/') ds@1952: { ds@1952: /* standard comment */ ds@1952: ignoring = true; ds@1952: eol_comment = true; ds@1952: restage = true; ds@1952: } ds@1952: else if (parse_point[i+1] == '*') ds@1952: { ds@1952: /* inline comment - start ignoring */ ds@1952: ignoring = true; ds@1952: ml_comment = true; ds@1952: /* yes, we restage, meaning that: ds@1952: * STR/ *stuff* /ING (space because ml comments don't nest in C++) ds@1952: * will not generate 'STRING', but rather 'STR' and 'ING'. ds@1952: * This should be a rare occurrence and is done here for convenience. ds@1952: */ ds@1952: restage = true; ds@1952: } ds@1952: } ds@1952: else ds@1952: { ds@1952: ignoring = true; ds@1952: eol_comment = true; ds@1952: restage = true; ds@1952: } ds@1952: } ds@1952: else if (c == '{') ds@1952: { ds@1952: /* If we are staging a string, we must rotate here */ ds@1952: if (strings[0].ptr) ds@1952: { ds@1952: /* We have unacceptable tokens on this line */ ds@1952: if (rotate(strings) != NULL) ds@1952: { ds@1952: err = SMCError_InvalidSection1; ds@1952: goto failed; ds@1952: } ds@1952: } ds@1952: /* Sections must always be alone */ ds@1952: if (strings[2].ptr != NULL) ds@1952: { ds@1952: err = SMCError_InvalidSection1; ds@1952: goto failed; ds@1952: } ds@1952: else if (strings[1].ptr == NULL) ds@1952: { ds@1952: err = SMCError_InvalidSection2; ds@1952: goto failed; ds@1952: } ds@1952: if ((res=smc->ReadSMC_NewSection(&states, FixupString(strings[1]))) ds@1952: != SMCResult_Continue) ds@1952: { ds@1952: err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; ds@1952: goto failed; ds@1952: } ds@1952: strings[1] = emptystring; ds@1952: curlevel++; ds@1952: } ds@1952: else if (c == '}') ds@1952: { ds@1952: /* Unlike our matching friend, this can be on the same line as something prior */ ds@1952: if (rotate(strings) != NULL) ds@1952: { ds@1952: err = SMCError_InvalidSection3; ds@1952: goto failed; ds@1952: } ds@1952: if (strings[2].ptr) ds@1952: { ds@1952: if (!curlevel) ds@1952: { ds@1952: err = SMCError_InvalidProperty1; ds@1952: goto failed; ds@1952: } ds@1952: if ((res=smc->ReadSMC_KeyValue( ds@1952: &states, ds@1952: FixupString(strings[2]), ds@1952: FixupString(strings[1]))) ds@1952: != SMCResult_Continue) ds@1952: { ds@1952: err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; ds@1952: goto failed; ds@1952: } ds@1952: } ds@1952: else if (strings[1].ptr) ds@1952: { ds@1952: err = SMCError_InvalidSection3; ds@1952: goto failed; ds@1952: } ds@1952: else if (!curlevel) ds@1952: { ds@1952: err = SMCError_InvalidSection4; ds@1952: goto failed; ds@1952: } ds@1952: /* Now it's safe to leave the section */ ds@1952: scrap(strings); ds@1952: if ((res=smc->ReadSMC_LeavingSection(&states)) != SMCResult_Continue) ds@1952: { ds@1952: err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay; ds@1952: goto failed; ds@1952: } ds@1952: curlevel--; ds@1952: } ds@1952: else if (c == '"') ds@1952: { ds@1952: /* If we get a quote mark, we always restage, but we need to do it beforehand */ ds@1952: if (strings[0].ptr) ds@1952: { ds@1952: strings[0].end = &parse_point[i]; ds@1952: if (rotate(strings) != NULL) ds@1952: { ds@1952: err = SMCError_InvalidTokens; ds@1952: goto failed; ds@1952: } ds@1952: } ds@1952: strings[0].ptr = &parse_point[i]; ds@1952: in_quote = true; ds@1952: ignoring = true; ds@1952: } ds@1952: else if (!strings[0].ptr) ds@1952: { ds@1952: /* If we have no string, we must start one */ ds@1952: strings[0].ptr = &parse_point[i]; ds@1952: } ds@1952: if (restage && strings[0].ptr) ds@1952: { ds@1952: strings[0].end = &parse_point[i]; ds@1952: if (rotate(strings) != NULL) ds@1952: { ds@1952: err = SMCError_InvalidTokens; ds@1952: goto failed; ds@1952: } ds@1952: } ds@1952: } ds@1952: else ds@1952: { ds@1952: /* If we're eating a string and get whitespace, we need to restage. ds@1952: * (Note that if we are quoted, this is being ignored) ds@1952: */ ds@1952: if (strings[0].ptr) ds@1952: { ds@1952: /* ds@1952: * The specification says the second string in a pair does not need to be quoted. ds@1952: * Thus, we check if there's already a string on the stack. ds@1952: * If there's a newline, we always rotate so the newline has an empty starter. ds@1952: */ ds@1952: if (!strings[1].ptr) ds@1952: { ds@1952: /* There's no string, so we must move this one down and eat up another */ ds@1952: strings[0].end = &parse_point[i]; ds@1952: rotate(strings); ds@1952: } ds@1952: else if (!strings[1].quoted) ds@1952: { ds@1952: err = SMCError_InvalidTokens; ds@1952: goto failed; ds@1952: } ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: /* Advance which token we're on */ ds@1952: states.col++; ds@1952: } ds@1952: ds@1952: if (line_begin != in_buf) ds@1952: { ds@1952: /* The line buffer has advanced, so it's safe to copy N bytes back to the beginning. ds@1952: * What's N? N is the lowest point we're currently relying on. ds@1952: */ ds@1952: char *stage = lowstring(strings); ds@1952: if (!stage || stage > line_begin) ds@1952: { ds@1952: stage = line_begin; ds@1952: } ds@1952: unsigned int bytes = read - (stage - parse_point); ds@1952: ds@1952: /* It is now safe to delete everything before the staged point */ ds@1952: memmove(in_buf, stage, bytes); ds@1952: ds@1952: /* Calculate the number of bytes in the new buffer */ ds@1952: bytes = stage - in_buf; ds@1952: /* Relocate all the cached pointers to our new base */ ds@1952: line_begin -= bytes; ds@1952: reloc(strings[0], bytes); ds@1952: reloc(strings[1], bytes); ds@1952: reloc(strings[2], bytes); ds@1952: if (reparse_point) ds@1952: { ds@1952: reparse_point -= bytes; ds@1952: } ds@1952: if (parse_point) ds@1952: { ds@1952: parse_point = &parse_point[read]; ds@1952: parse_point -= bytes; ds@1952: } ds@1952: } ds@1952: else if (read == sizeof(in_buf) - 1) ds@1952: { ds@1952: err = SMCError_TokenOverflow; ds@1952: goto failed; ds@1952: } ds@1952: } ds@1952: ds@1952: /* If we're done parsing and there are tokens left over... */ ds@1952: if (curlevel) ds@1952: { ds@1952: err = SMCError_InvalidSection5; ds@1952: goto failed; ds@1952: } ds@1952: else if (strings[0].ptr || strings[1].ptr) ds@1952: { ds@1952: err = SMCError_InvalidTokens; ds@1952: goto failed; ds@1952: } ds@1952: ds@1952: smc->ReadSMC_ParseEnd(false, false); ds@1952: ds@1952: if (pStates != NULL) ds@1952: { ds@1952: *pStates = states; ds@1952: } ds@1952: ds@1952: return SMCError_Okay; ds@1952: ds@1952: failed: ds@1952: if (pStates != NULL) ds@1952: { ds@1952: *pStates = states; ds@1952: } ds@1952: ds@1952: smc->ReadSMC_ParseEnd(true, (err == SMCError_Custom)); ds@1952: ds@1952: return err; ds@1952: } ds@1952: ds@1952: ds@1952: /** ds@1952: * INI parser ds@1952: */ ds@1952: ds@1952: bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col) ds@1952: { ds@1952: FILE *fp = fopen(file, "rt"); ds@1952: unsigned int curline = 0; ds@1952: unsigned int curtok; ds@1952: size_t len; ds@1952: ds@1952: if (!fp) ds@1952: { ds@1952: if (line) ds@1952: { ds@1952: *line = 0; ds@1952: } ds@1952: ds@1952: return false; ds@1952: } ds@1952: ds@1952: char buffer[2048]; ds@1952: char *ptr, *save_ptr; ds@1952: bool in_quote; ds@1952: ds@1952: while (!feof(fp)) ds@1952: { ds@1952: curline++; ds@1952: curtok = 0; ds@1952: buffer[0] = '\0'; ds@1952: if (fgets(buffer, sizeof(buffer), fp) == NULL) ds@1952: { ds@1952: break; ds@1952: } ds@1952: ds@1952: //:TODO: this will only run once, so find a nice way to move it out of the while loop ds@1952: /* If this is the first line, check the first three bytes for BOM */ ds@1952: if (curline == 1 && ds@1952: buffer[0] == (char)0xEF && ds@1952: buffer[1] == (char)0xBB && ds@1952: buffer[2] == (char)0xBF) ds@1952: { ds@1952: /* We have a UTF-8 marked file... skip these bytes */ ds@1952: ptr = &buffer[3]; ds@1952: } else { ds@1952: ptr = buffer; ds@1952: } ds@1952: ds@1952: /*************************************************** ds@1952: * We preprocess the string before parsing tokens! * ds@1952: ***************************************************/ ds@1952: ds@1952: /* First strip beginning whitespace */ ds@1952: while (*ptr != '\0' && g_ws_chartable[(unsigned)*ptr] != 0) ds@1952: { ds@1952: ptr++; ds@1952: } ds@1952: ds@1952: len = strlen(ptr); ds@1952: ds@1952: if (!len) ds@1952: { ds@1952: continue; ds@1952: } ds@1952: ds@1952: /* Now search for comment characters */ ds@1952: in_quote = false; ds@1952: save_ptr = ptr; ds@1952: for (size_t i=0; i=0 && iReadINI_RawLine(ptr, &curtok)) ds@1952: { ds@1952: goto event_failed; ds@1952: } ds@1952: ds@1952: if (*ptr == '[') ds@1952: { ds@1952: bool invalid_tokens = false; ds@1952: bool got_bracket = false; ds@1952: bool extra_tokens = false; ds@1952: char c; ds@1952: bool alnum; ds@1952: wchar_t wc; ds@1952: ds@1952: for (size_t i=1; iReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok)) ds@1952: { ds@1952: goto event_failed; ds@1952: } ds@1952: } else { ds@1952: char *key_ptr = ptr; ds@1952: char *val_ptr = NULL; ds@1952: char c; ds@1952: size_t first_space = 0; ds@1952: bool invalid_tokens = false; ds@1952: bool equal_token = false; ds@1952: bool quotes = false; ds@1952: bool alnum; ds@1952: wchar_t wc; ds@1952: ds@1952: for (size_t i=0; iReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok)) ds@1952: { ds@1952: curtok = 0; ds@1952: goto event_failed; ds@1952: } ds@1952: } ds@1952: } ds@1952: ds@1952: if (line) ds@1952: { ds@1952: *line = curline; ds@1952: } ds@1952: ds@1952: fclose(fp); ds@1952: ds@1952: return true; ds@1952: ds@1952: event_failed: ds@1952: if (line) ds@1952: { ds@1952: *line = curline; ds@1952: } ds@1952: ds@1952: if (col) ds@1952: { ds@1952: *col = curtok; ds@1952: } ds@1952: ds@1952: fclose(fp); ds@1952: ds@1952: return false; ds@1952: } ds@1952: ds@1952: const char *TextParsers::GetSMCErrorString(SMCError err) ds@1952: { ds@1952: static const char *s_errors[] = ds@1952: { ds@1952: NULL, ds@1952: "Stream failed to open", ds@1952: "Stream returned read error", ds@1952: NULL, ds@1952: "Un-quoted section has invalid tokens", ds@1952: "Section declared without header", ds@1952: "Section declared with unknown tokens", ds@1952: "Section ending without a matching section beginning", ds@1952: "Section beginning without a matching ending", ds@1952: "Line contained too many invalid tokens", ds@1952: "Token buffer overflowed", ds@1952: "A property was declared outside of a section", ds@1952: }; ds@1952: ds@1952: if (err < SMCError_Okay || err > SMCError_InvalidProperty1) ds@1952: { ds@1952: return NULL; ds@1952: } ds@1952: ds@1952: return s_errors[err]; ds@1952: }