1.1 --- a/tools/gamedata md5/TextParsers.cpp Fri Jul 11 06:51:21 2008 +0000
1.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000
1.3 @@ -1,1082 +0,0 @@
1.4 -/**
1.5 - * vim: set ts=4 :
1.6 - * =============================================================================
1.7 - * SourceMod
1.8 - * Copyright (C) 2004-2008 AlliedModders LLC. All rights reserved.
1.9 - * =============================================================================
1.10 - *
1.11 - * This program is free software; you can redistribute it and/or modify it under
1.12 - * the terms of the GNU General Public License, version 3.0, as published by the
1.13 - * Free Software Foundation.
1.14 - *
1.15 - * This program is distributed in the hope that it will be useful, but WITHOUT
1.16 - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
1.17 - * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
1.18 - * details.
1.19 - *
1.20 - * You should have received a copy of the GNU General Public License along with
1.21 - * this program. If not, see <http://www.gnu.org/licenses/>.
1.22 - *
1.23 - * As a special exception, AlliedModders LLC gives you permission to link the
1.24 - * code of this program (as well as its derivative works) to "Half-Life 2," the
1.25 - * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
1.26 - * by the Valve Corporation. You must obey the GNU General Public License in
1.27 - * all respects for all other code used. Additionally, AlliedModders LLC grants
1.28 - * this exception to all derivative works. AlliedModders LLC defines further
1.29 - * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
1.30 - * or <http://www.sourcemod.net/license.php>.
1.31 - *
1.32 - * Version: $Id$
1.33 - */
1.34 -
1.35 -#include <stdio.h>
1.36 -#include <ctype.h>
1.37 -#include <wctype.h>
1.38 -#include <string.h>
1.39 -#include <stdlib.h>
1.40 -#include <assert.h>
1.41 -#include "TextParsers.h"
1.42 -//#include "ShareSys.h"
1.43 -//#include "sm_stringutil.h"
1.44 -//#include "LibrarySys.h"
1.45 -
1.46 -TextParsers g_TextParser;
1.47 -ITextParsers *textparsers = &g_TextParser;
1.48 -
1.49 -static int g_ini_chartable1[255] = {0};
1.50 -static int g_ws_chartable[255] = {0};
1.51 -
1.52 -bool TextParsers::IsWhitespace(const char *stream)
1.53 -{
1.54 - return g_ws_chartable[(unsigned)*stream] == 1;
1.55 -}
1.56 -
1.57 -TextParsers::TextParsers()
1.58 -{
1.59 - g_ini_chartable1[(unsigned)'_'] = 1;
1.60 - g_ini_chartable1[(unsigned)'-'] = 1;
1.61 - g_ini_chartable1[(unsigned)','] = 1;
1.62 - g_ini_chartable1[(unsigned)'+'] = 1;
1.63 - g_ini_chartable1[(unsigned)'.'] = 1;
1.64 - g_ini_chartable1[(unsigned)'$'] = 1;
1.65 - g_ini_chartable1[(unsigned)'?'] = 1;
1.66 - g_ini_chartable1[(unsigned)'/'] = 1;
1.67 - g_ws_chartable[(unsigned)'\n'] = 1;
1.68 - g_ws_chartable[(unsigned)'\v'] = 1;
1.69 - g_ws_chartable[(unsigned)'\r'] = 1;
1.70 - g_ws_chartable[(unsigned)'\t'] = 1;
1.71 - g_ws_chartable[(unsigned)'\f'] = 1;
1.72 - g_ws_chartable[(unsigned)' '] = 1;
1.73 -}
1.74 -
1.75 -unsigned int TextParsers::GetUTF8CharBytes(const char *stream)
1.76 -{
1.77 - return _GetUTF8CharBytes(stream);
1.78 -}
1.79 -
1.80 -/**
1.81 - * Character streams
1.82 - */
1.83 -
1.84 -struct CharStream
1.85 -{
1.86 - const char *curpos;
1.87 -};
1.88 -
1.89 -bool CharStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read)
1.90 -{
1.91 - CharStream *srdr = (CharStream *)stream;
1.92 -
1.93 - const char *ptr = srdr->curpos;
1.94 - for (size_t i=0; i<maxlength; i++)
1.95 - {
1.96 - if (*ptr == '\0')
1.97 - {
1.98 - break;
1.99 - }
1.100 - *buffer++ = *ptr++;
1.101 - }
1.102 -
1.103 - *read = ptr - srdr->curpos;
1.104 -
1.105 - srdr->curpos = ptr;
1.106 -
1.107 - return true;
1.108 -}
1.109 -
1.110 -SMCError TextParsers::ParseString_SMC(const char *stream,
1.111 - ITextListener_SMC *smc,
1.112 - SMCStates *states)
1.113 -{
1.114 - CharStream srdr = { stream };
1.115 -
1.116 - return ParseStream_SMC(&srdr, CharStreamReader, smc, states);
1.117 -}
1.118 -
1.119 -/**
1.120 - * File streams
1.121 - */
1.122 -
1.123 -bool FileStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read)
1.124 -{
1.125 - size_t num = fread(buffer, 1, maxlength, (FILE *)stream);
1.126 -
1.127 - *read = static_cast<unsigned int>(num);
1.128 -
1.129 - if (num == 0 && feof((FILE *)stream))
1.130 - {
1.131 - return true;
1.132 - }
1.133 -
1.134 - return (ferror((FILE *)stream) == 0);
1.135 -}
1.136 -
1.137 -SMCError TextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc, SMCStates *states)
1.138 -{
1.139 - FILE *fp = fopen(file, "rt");
1.140 -
1.141 - if (!fp)
1.142 - {
1.143 - if (states != NULL)
1.144 - {
1.145 - states->line = 0;
1.146 - states->col = 0;
1.147 - }
1.148 - return SMCError_StreamOpen;
1.149 - }
1.150 -
1.151 - SMCError result = ParseStream_SMC(fp, FileStreamReader, smc, states);
1.152 -
1.153 - fclose(fp);
1.154 -
1.155 - return result;
1.156 -}
1.157 -
1.158 -SMCError TextParsers::ParseSMCFile(const char *file,
1.159 - ITextListener_SMC *smc_listener,
1.160 - SMCStates *states,
1.161 - char *buffer,
1.162 - size_t maxsize)
1.163 -{
1.164 - const char *errstr;
1.165 - FILE *fp = fopen(file, "rt");
1.166 -
1.167 - if (fp == NULL)
1.168 - {
1.169 - char error[256] = "unknown";
1.170 - if (states != NULL)
1.171 - {
1.172 - states->line = 0;
1.173 - states->col = 0;
1.174 - }
1.175 - //g_LibSys.GetPlatformError(error, sizeof(error));
1.176 - UTIL_Format(buffer, maxsize, "File could not be opened: %s", error);
1.177 - return SMCError_StreamOpen;
1.178 - }
1.179 -
1.180 - SMCError result = ParseStream_SMC(fp, FileStreamReader, smc_listener, states);
1.181 -
1.182 - fclose(fp);
1.183 -
1.184 - errstr = GetSMCErrorString(result);
1.185 - UTIL_Format(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error");
1.186 -
1.187 - return result;
1.188 -}
1.189 -
1.190 -/**
1.191 - * Raw parsing of streams with helper functions
1.192 - */
1.193 -
1.194 -struct StringInfo
1.195 -{
1.196 - StringInfo() : quoted(false), ptr(NULL), end(NULL), special(false) { }
1.197 - bool quoted;
1.198 - char *ptr;
1.199 - char *end;
1.200 - bool special;
1.201 -};
1.202 -
1.203 -const char *FixupString(StringInfo &data)
1.204 -{
1.205 - if (!data.ptr)
1.206 - {
1.207 - return NULL;
1.208 - }
1.209 -
1.210 - if (data.quoted)
1.211 - {
1.212 - data.ptr++;
1.213 - }
1.214 -#if defined _DEBUG
1.215 - else {
1.216 - /* A string will never have beginning whitespace because we ignore it in the stream.
1.217 - * Furthermore, if there is trailing whitespace, the end ptr will point to it, so it is valid
1.218 - * to overwrite! Lastly, the last character must be whitespace or a comment/invalid character.
1.219 - */
1.220 - }
1.221 -#endif
1.222 -
1.223 - /* Do some extra work on strings that have special quoted characters. */
1.224 - if (data.special)
1.225 - {
1.226 - char *outptr = data.ptr;
1.227 - size_t len = data.end - data.ptr;
1.228 - if (len >= 2)
1.229 - {
1.230 - for (size_t i=0; i<len; i++)
1.231 - {
1.232 - if (data.ptr[i] == '\\' && i < len - 1)
1.233 - {
1.234 - /* Resolve the next character. */
1.235 - i++;
1.236 - if (data.ptr[i] == 'n')
1.237 - {
1.238 - data.ptr[i] = '\n';
1.239 - } else if (data.ptr[i] == 't') {
1.240 - data.ptr[i] = '\t';
1.241 - } else if (data.ptr[i] == 'r') {
1.242 - data.ptr[i] = '\r';
1.243 - } else if (data.ptr[i] != '\\'
1.244 - && data.ptr[i] != '"') {
1.245 - /* This character is invalid, so go back one */
1.246 - i--;
1.247 - }
1.248 - }
1.249 - *outptr++ = data.ptr[i];
1.250 - }
1.251 - *outptr = '\0';
1.252 - }
1.253 - }
1.254 -
1.255 - if (data.end)
1.256 - {
1.257 - *(data.end) = '\0';
1.258 - }
1.259 -
1.260 - return data.ptr;
1.261 -}
1.262 -
1.263 -const char *rotate(StringInfo info[3])
1.264 -{
1.265 - if (info[2].ptr != NULL)
1.266 - {
1.267 - return info[2].ptr;
1.268 - }
1.269 -
1.270 - if (info[0].ptr != NULL)
1.271 - {
1.272 - info[2] = info[1];
1.273 - info[1] = info[0];
1.274 - info[0] = StringInfo();
1.275 - }
1.276 -
1.277 - return NULL;
1.278 -}
1.279 -
1.280 -void scrap(StringInfo info[3])
1.281 -{
1.282 - info[2] = StringInfo();
1.283 - info[1] = StringInfo();
1.284 - info[0] = StringInfo();
1.285 -}
1.286 -
1.287 -void reloc(StringInfo &data, unsigned int bytes)
1.288 -{
1.289 - if (data.ptr)
1.290 - {
1.291 - data.ptr -= bytes;
1.292 - }
1.293 - if (data.end)
1.294 - {
1.295 - data.end -= bytes;
1.296 - }
1.297 -}
1.298 -
1.299 -char *lowstring(StringInfo info[3])
1.300 -{
1.301 - for (int i=2; i>=0; i--)
1.302 - {
1.303 - if (info[i].ptr)
1.304 - {
1.305 - return info[i].ptr;
1.306 - }
1.307 - }
1.308 -
1.309 - return NULL;
1.310 -}
1.311 -
1.312 -SMCError TextParsers::ParseStream_SMC(void *stream,
1.313 - STREAMREADER srdr,
1.314 - ITextListener_SMC *smc,
1.315 - SMCStates *pStates)
1.316 -{
1.317 - char *reparse_point = NULL;
1.318 - char in_buf[4096];
1.319 - char *parse_point = in_buf;
1.320 - char *line_begin = in_buf;
1.321 - unsigned int read;
1.322 - unsigned int curlevel = 0;
1.323 - bool in_quote = false;
1.324 - bool ignoring = false;
1.325 - bool eol_comment = false;
1.326 - bool ml_comment = false;
1.327 - unsigned int i;
1.328 - SMCError err = SMCError_Okay;
1.329 - SMCResult res;
1.330 - SMCStates states;
1.331 - char c;
1.332 -
1.333 - StringInfo strings[3];
1.334 - StringInfo emptystring;
1.335 -
1.336 - states.line = 1;
1.337 - states.col = 0;
1.338 -
1.339 - smc->ReadSMC_ParseStart();
1.340 -
1.341 - /**
1.342 - * The stream reader reads in as much as it can fill the buffer with.
1.343 - * It then processes the buffer. If the buffer cannot be fully processed, for example,
1.344 - * a line is left hanging with no newline, then the contents of the buffer is shifted
1.345 - * down, and the buffer is filled from the stream reader again.
1.346 - *
1.347 - * What makes this particularly annoying is that we cache pointers everywhere, so when
1.348 - * the shifting process takes place, all those pointers must be shifted as well.
1.349 - */
1.350 - while (srdr(stream, parse_point, sizeof(in_buf) - (parse_point - in_buf) - 1, &read))
1.351 - {
1.352 - if (!read)
1.353 - {
1.354 - break;
1.355 - }
1.356 -
1.357 - /* Check for BOM markings, which is only relevant on the first line.
1.358 - * Not worth it, but it could be moved out of the loop.
1.359 - */
1.360 - if (states.line == 1 &&
1.361 - in_buf[0] == (char)0xEF &&
1.362 - in_buf[1] == (char)0xBB &&
1.363 - in_buf[2] == (char)0xBF)
1.364 - {
1.365 - /* Move EVERYTHING down :\ */
1.366 - memmove(in_buf, &in_buf[3], read - 3);
1.367 - read -= 3;
1.368 - }
1.369 -
1.370 - if (reparse_point)
1.371 - {
1.372 - read += (parse_point - reparse_point);
1.373 - parse_point = reparse_point;
1.374 - reparse_point = NULL;
1.375 - }
1.376 -
1.377 - for (i=0; i<read; i++)
1.378 - {
1.379 - c = parse_point[i];
1.380 - if (c == '\n')
1.381 - {
1.382 - /* If we got a newline, there's a lot of things that could have happened in the interim.
1.383 - * First, let's make sure the staged strings are rotated.
1.384 - */
1.385 - if (strings[0].ptr)
1.386 - {
1.387 - strings[0].end = &parse_point[i];
1.388 - if (rotate(strings) != NULL)
1.389 - {
1.390 - err = SMCError_InvalidTokens;
1.391 - goto failed;
1.392 - }
1.393 - }
1.394 -
1.395 - /* Next, let's clear some line-based values that may no longer have meaning */
1.396 - eol_comment = false;
1.397 - in_quote = false;
1.398 - if (ignoring && !ml_comment)
1.399 - {
1.400 - ignoring = false;
1.401 - }
1.402 -
1.403 - /* Pass the raw line onto the listener. We terminate the line so the receiver
1.404 - * doesn't get tons of useless info. We restore the newline after.
1.405 - */
1.406 - parse_point[i] = '\0';
1.407 - if ((res=smc->ReadSMC_RawLine(&states, line_begin)) != SMCResult_Continue)
1.408 - {
1.409 - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
1.410 - goto failed;
1.411 - }
1.412 - parse_point[i] = '\n';
1.413 -
1.414 - /* Now we check the sanity of our staged strings! */
1.415 - if (strings[2].ptr)
1.416 - {
1.417 - if (!curlevel)
1.418 - {
1.419 - err = SMCError_InvalidProperty1;
1.420 - goto failed;
1.421 - }
1.422 - /* Assume the next string is a property and pass the info on. */
1.423 - if ((res=smc->ReadSMC_KeyValue(
1.424 - &states,
1.425 - FixupString(strings[2]),
1.426 - FixupString(strings[1]))) != SMCResult_Continue)
1.427 - {
1.428 - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
1.429 - goto failed;
1.430 - }
1.431 - scrap(strings);
1.432 - }
1.433 -
1.434 - /* Change the states for the next line */
1.435 - states.col = 0;
1.436 - states.line++;
1.437 - line_begin = &parse_point[i+1]; //Note: safe because this gets relocated later
1.438 - }
1.439 - else if (ignoring)
1.440 - {
1.441 - if (in_quote)
1.442 - {
1.443 - /* If i was 0, we could have reparsed, so make sure there's no buffer underrun */
1.444 - if ((&parse_point[i] != in_buf) && c == '"' && parse_point[i-1] != '\\')
1.445 - {
1.446 - /* If we reached a quote in an ignore phase,
1.447 - * we're staging a string and we must rotate it out.
1.448 - */
1.449 - in_quote = false;
1.450 - ignoring = false;
1.451 - /* Set our info */
1.452 - strings[0].end = &parse_point[i];
1.453 - strings[0].quoted = true;
1.454 - if (rotate(strings) != NULL)
1.455 - {
1.456 - /* If we rotated too many strings, there was too much crap on one line */
1.457 - err = SMCError_InvalidTokens;
1.458 - goto failed;
1.459 - }
1.460 - }
1.461 - else if (c == '\\')
1.462 - {
1.463 - strings[0].special = true;
1.464 - if (i == (read - 1))
1.465 - {
1.466 - reparse_point = &parse_point[i];
1.467 - break;
1.468 - }
1.469 - }
1.470 - }
1.471 - else if (ml_comment)
1.472 - {
1.473 - if (c == '*')
1.474 - {
1.475 - /* Check if we need to get more input first */
1.476 - if (i == read - 1)
1.477 - {
1.478 - reparse_point = &parse_point[i];
1.479 - break;
1.480 - }
1.481 - if (parse_point[i+1] == '/')
1.482 - {
1.483 - ml_comment = false;
1.484 - ignoring = false;
1.485 - /* We should not be staging anything right now. */
1.486 - assert(strings[0].ptr == NULL);
1.487 - /* Advance the input stream so we don't choke on this token */
1.488 - i++;
1.489 - states.col++;
1.490 - }
1.491 - }
1.492 - }
1.493 - }
1.494 - else
1.495 - {
1.496 - /* Check if we're whitespace or not */
1.497 - if (!g_ws_chartable[(unsigned)c])
1.498 - {
1.499 - bool restage = false;
1.500 - /* Check various special tokens:
1.501 - * ;
1.502 - * //
1.503 - * / *
1.504 - * {
1.505 - * }
1.506 - */
1.507 - if (c == ';' || c == '/')
1.508 - {
1.509 - /* If it's a line-based comment (that is, ; or //)
1.510 - * we will need to scrap everything until the end of the line.
1.511 - */
1.512 - if (c == '/')
1.513 - {
1.514 - if (i == read - 1)
1.515 - {
1.516 - /* If we reached the end of the look-ahead, we need to re-check our input.
1.517 - * Breaking out will force this to be the new reparse point!
1.518 - */
1.519 - reparse_point = &parse_point[i];
1.520 - break;
1.521 - }
1.522 - if (parse_point[i + 1] == '/')
1.523 - {
1.524 - /* standard comment */
1.525 - ignoring = true;
1.526 - eol_comment = true;
1.527 - restage = true;
1.528 - }
1.529 - else if (parse_point[i+1] == '*')
1.530 - {
1.531 - /* inline comment - start ignoring */
1.532 - ignoring = true;
1.533 - ml_comment = true;
1.534 - /* yes, we restage, meaning that:
1.535 - * STR/ *stuff* /ING (space because ml comments don't nest in C++)
1.536 - * will not generate 'STRING', but rather 'STR' and 'ING'.
1.537 - * This should be a rare occurrence and is done here for convenience.
1.538 - */
1.539 - restage = true;
1.540 - }
1.541 - }
1.542 - else
1.543 - {
1.544 - ignoring = true;
1.545 - eol_comment = true;
1.546 - restage = true;
1.547 - }
1.548 - }
1.549 - else if (c == '{')
1.550 - {
1.551 - /* If we are staging a string, we must rotate here */
1.552 - if (strings[0].ptr)
1.553 - {
1.554 - /* We have unacceptable tokens on this line */
1.555 - if (rotate(strings) != NULL)
1.556 - {
1.557 - err = SMCError_InvalidSection1;
1.558 - goto failed;
1.559 - }
1.560 - }
1.561 - /* Sections must always be alone */
1.562 - if (strings[2].ptr != NULL)
1.563 - {
1.564 - err = SMCError_InvalidSection1;
1.565 - goto failed;
1.566 - }
1.567 - else if (strings[1].ptr == NULL)
1.568 - {
1.569 - err = SMCError_InvalidSection2;
1.570 - goto failed;
1.571 - }
1.572 - if ((res=smc->ReadSMC_NewSection(&states, FixupString(strings[1])))
1.573 - != SMCResult_Continue)
1.574 - {
1.575 - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
1.576 - goto failed;
1.577 - }
1.578 - strings[1] = emptystring;
1.579 - curlevel++;
1.580 - }
1.581 - else if (c == '}')
1.582 - {
1.583 - /* Unlike our matching friend, this can be on the same line as something prior */
1.584 - if (rotate(strings) != NULL)
1.585 - {
1.586 - err = SMCError_InvalidSection3;
1.587 - goto failed;
1.588 - }
1.589 - if (strings[2].ptr)
1.590 - {
1.591 - if (!curlevel)
1.592 - {
1.593 - err = SMCError_InvalidProperty1;
1.594 - goto failed;
1.595 - }
1.596 - if ((res=smc->ReadSMC_KeyValue(
1.597 - &states,
1.598 - FixupString(strings[2]),
1.599 - FixupString(strings[1])))
1.600 - != SMCResult_Continue)
1.601 - {
1.602 - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
1.603 - goto failed;
1.604 - }
1.605 - }
1.606 - else if (strings[1].ptr)
1.607 - {
1.608 - err = SMCError_InvalidSection3;
1.609 - goto failed;
1.610 - }
1.611 - else if (!curlevel)
1.612 - {
1.613 - err = SMCError_InvalidSection4;
1.614 - goto failed;
1.615 - }
1.616 - /* Now it's safe to leave the section */
1.617 - scrap(strings);
1.618 - if ((res=smc->ReadSMC_LeavingSection(&states)) != SMCResult_Continue)
1.619 - {
1.620 - err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
1.621 - goto failed;
1.622 - }
1.623 - curlevel--;
1.624 - }
1.625 - else if (c == '"')
1.626 - {
1.627 - /* If we get a quote mark, we always restage, but we need to do it beforehand */
1.628 - if (strings[0].ptr)
1.629 - {
1.630 - strings[0].end = &parse_point[i];
1.631 - if (rotate(strings) != NULL)
1.632 - {
1.633 - err = SMCError_InvalidTokens;
1.634 - goto failed;
1.635 - }
1.636 - }
1.637 - strings[0].ptr = &parse_point[i];
1.638 - in_quote = true;
1.639 - ignoring = true;
1.640 - }
1.641 - else if (!strings[0].ptr)
1.642 - {
1.643 - /* If we have no string, we must start one */
1.644 - strings[0].ptr = &parse_point[i];
1.645 - }
1.646 - if (restage && strings[0].ptr)
1.647 - {
1.648 - strings[0].end = &parse_point[i];
1.649 - if (rotate(strings) != NULL)
1.650 - {
1.651 - err = SMCError_InvalidTokens;
1.652 - goto failed;
1.653 - }
1.654 - }
1.655 - }
1.656 - else
1.657 - {
1.658 - /* If we're eating a string and get whitespace, we need to restage.
1.659 - * (Note that if we are quoted, this is being ignored)
1.660 - */
1.661 - if (strings[0].ptr)
1.662 - {
1.663 - /*
1.664 - * The specification says the second string in a pair does not need to be quoted.
1.665 - * Thus, we check if there's already a string on the stack.
1.666 - * If there's a newline, we always rotate so the newline has an empty starter.
1.667 - */
1.668 - if (!strings[1].ptr)
1.669 - {
1.670 - /* There's no string, so we must move this one down and eat up another */
1.671 - strings[0].end = &parse_point[i];
1.672 - rotate(strings);
1.673 - }
1.674 - else if (!strings[1].quoted)
1.675 - {
1.676 - err = SMCError_InvalidTokens;
1.677 - goto failed;
1.678 - }
1.679 - }
1.680 - }
1.681 - }
1.682 -
1.683 - /* Advance which token we're on */
1.684 - states.col++;
1.685 - }
1.686 -
1.687 - if (line_begin != in_buf)
1.688 - {
1.689 - /* The line buffer has advanced, so it's safe to copy N bytes back to the beginning.
1.690 - * What's N? N is the lowest point we're currently relying on.
1.691 - */
1.692 - char *stage = lowstring(strings);
1.693 - if (!stage || stage > line_begin)
1.694 - {
1.695 - stage = line_begin;
1.696 - }
1.697 - unsigned int bytes = read - (stage - parse_point);
1.698 -
1.699 - /* It is now safe to delete everything before the staged point */
1.700 - memmove(in_buf, stage, bytes);
1.701 -
1.702 - /* Calculate the number of bytes in the new buffer */
1.703 - bytes = stage - in_buf;
1.704 - /* Relocate all the cached pointers to our new base */
1.705 - line_begin -= bytes;
1.706 - reloc(strings[0], bytes);
1.707 - reloc(strings[1], bytes);
1.708 - reloc(strings[2], bytes);
1.709 - if (reparse_point)
1.710 - {
1.711 - reparse_point -= bytes;
1.712 - }
1.713 - if (parse_point)
1.714 - {
1.715 - parse_point = &parse_point[read];
1.716 - parse_point -= bytes;
1.717 - }
1.718 - }
1.719 - else if (read == sizeof(in_buf) - 1)
1.720 - {
1.721 - err = SMCError_TokenOverflow;
1.722 - goto failed;
1.723 - }
1.724 - }
1.725 -
1.726 - /* If we're done parsing and there are tokens left over... */
1.727 - if (curlevel)
1.728 - {
1.729 - err = SMCError_InvalidSection5;
1.730 - goto failed;
1.731 - }
1.732 - else if (strings[0].ptr || strings[1].ptr)
1.733 - {
1.734 - err = SMCError_InvalidTokens;
1.735 - goto failed;
1.736 - }
1.737 -
1.738 - smc->ReadSMC_ParseEnd(false, false);
1.739 -
1.740 - if (pStates != NULL)
1.741 - {
1.742 - *pStates = states;
1.743 - }
1.744 -
1.745 - return SMCError_Okay;
1.746 -
1.747 -failed:
1.748 - if (pStates != NULL)
1.749 - {
1.750 - *pStates = states;
1.751 - }
1.752 -
1.753 - smc->ReadSMC_ParseEnd(true, (err == SMCError_Custom));
1.754 -
1.755 - return err;
1.756 -}
1.757 -
1.758 -
1.759 -/**
1.760 - * INI parser
1.761 - */
1.762 -
1.763 -bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col)
1.764 -{
1.765 - FILE *fp = fopen(file, "rt");
1.766 - unsigned int curline = 0;
1.767 - unsigned int curtok;
1.768 - size_t len;
1.769 -
1.770 - if (!fp)
1.771 - {
1.772 - if (line)
1.773 - {
1.774 - *line = 0;
1.775 - }
1.776 -
1.777 - return false;
1.778 - }
1.779 -
1.780 - char buffer[2048];
1.781 - char *ptr, *save_ptr;
1.782 - bool in_quote;
1.783 -
1.784 - while (!feof(fp))
1.785 - {
1.786 - curline++;
1.787 - curtok = 0;
1.788 - buffer[0] = '\0';
1.789 - if (fgets(buffer, sizeof(buffer), fp) == NULL)
1.790 - {
1.791 - break;
1.792 - }
1.793 -
1.794 - //:TODO: this will only run once, so find a nice way to move it out of the while loop
1.795 - /* If this is the first line, check the first three bytes for BOM */
1.796 - if (curline == 1 &&
1.797 - buffer[0] == (char)0xEF &&
1.798 - buffer[1] == (char)0xBB &&
1.799 - buffer[2] == (char)0xBF)
1.800 - {
1.801 - /* We have a UTF-8 marked file... skip these bytes */
1.802 - ptr = &buffer[3];
1.803 - } else {
1.804 - ptr = buffer;
1.805 - }
1.806 -
1.807 - /***************************************************
1.808 - * We preprocess the string before parsing tokens! *
1.809 - ***************************************************/
1.810 -
1.811 - /* First strip beginning whitespace */
1.812 - while (*ptr != '\0' && g_ws_chartable[(unsigned)*ptr] != 0)
1.813 - {
1.814 - ptr++;
1.815 - }
1.816 -
1.817 - len = strlen(ptr);
1.818 -
1.819 - if (!len)
1.820 - {
1.821 - continue;
1.822 - }
1.823 -
1.824 - /* Now search for comment characters */
1.825 - in_quote = false;
1.826 - save_ptr = ptr;
1.827 - for (size_t i=0; i<len; i++,ptr++)
1.828 - {
1.829 - if (!in_quote)
1.830 - {
1.831 - switch (*ptr)
1.832 - {
1.833 - case '"':
1.834 - {
1.835 - in_quote = true;
1.836 - break;
1.837 - }
1.838 - case ';':
1.839 - {
1.840 - /* Stop the loop */
1.841 - len = i;
1.842 - /* Terminate the string here */
1.843 - *ptr = '\0';
1.844 - break;
1.845 - }
1.846 - }
1.847 - } else {
1.848 - if (*ptr == '"')
1.849 - {
1.850 - in_quote = false;
1.851 - }
1.852 - }
1.853 - }
1.854 -
1.855 - if (!len)
1.856 - {
1.857 - continue;
1.858 - }
1.859 -
1.860 - ptr = save_ptr;
1.861 -
1.862 - /* Lastly, strip ending whitespace off */
1.863 - for (size_t i=len-1; i>=0 && i<len; i--)
1.864 - {
1.865 - if (g_ws_chartable[(unsigned)ptr[i]])
1.866 - {
1.867 - ptr[i] = '\0';
1.868 - len--;
1.869 - } else {
1.870 - break;
1.871 - }
1.872 - }
1.873 -
1.874 - if (!len)
1.875 - {
1.876 - continue;
1.877 - }
1.878 -
1.879 - if (!ini_listener->ReadINI_RawLine(ptr, &curtok))
1.880 - {
1.881 - goto event_failed;
1.882 - }
1.883 -
1.884 - if (*ptr == '[')
1.885 - {
1.886 - bool invalid_tokens = false;
1.887 - bool got_bracket = false;
1.888 - bool extra_tokens = false;
1.889 - char c;
1.890 - bool alnum;
1.891 - wchar_t wc;
1.892 -
1.893 - for (size_t i=1; i<len; i++)
1.894 - {
1.895 - c = ptr[i];
1.896 - alnum = false;
1.897 -
1.898 - if (c & (1<<7))
1.899 - {
1.900 - if (mbtowc(&wc, &ptr[i], len-i) != -1)
1.901 - {
1.902 - alnum = (iswalnum(wc) != 0);
1.903 - i += _GetUTF8CharBytes(&ptr[i]) - 1;
1.904 - }
1.905 - } else {
1.906 - alnum = (isalnum(c) != 0) || (g_ini_chartable1[(unsigned)c] != 0);
1.907 - }
1.908 - if (!alnum)
1.909 - {
1.910 - /* First check - is this a bracket? */
1.911 - if (c == ']')
1.912 - {
1.913 - /* Yes! */
1.914 - got_bracket = true;
1.915 - /* If this isn't the last character... */
1.916 - if (i != len - 1)
1.917 - {
1.918 - extra_tokens = true;
1.919 - }
1.920 - /* terminate */
1.921 - ptr[i] = '\0';
1.922 - break;
1.923 - } else {
1.924 - /* n...No! Continue copying. */
1.925 - invalid_tokens = true;
1.926 - }
1.927 - }
1.928 - }
1.929 -
1.930 - /* Tell the handler */
1.931 - if (!ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok))
1.932 - {
1.933 - goto event_failed;
1.934 - }
1.935 - } else {
1.936 - char *key_ptr = ptr;
1.937 - char *val_ptr = NULL;
1.938 - char c;
1.939 - size_t first_space = 0;
1.940 - bool invalid_tokens = false;
1.941 - bool equal_token = false;
1.942 - bool quotes = false;
1.943 - bool alnum;
1.944 - wchar_t wc;
1.945 -
1.946 - for (size_t i=0; i<len; i++)
1.947 - {
1.948 - c = ptr[i];
1.949 - alnum = false;
1.950 - /* is this an invalid char? */
1.951 - if (c & (1<<7))
1.952 - {
1.953 - if (mbtowc(&wc, &ptr[i], len-i) != -1)
1.954 - {
1.955 - alnum = (iswalnum(wc) != 0);
1.956 - i += _GetUTF8CharBytes(&ptr[i]) - 1;
1.957 - }
1.958 - } else {
1.959 - alnum = (isalnum(c) != 0) || (g_ini_chartable1[(unsigned)c] != 0);
1.960 - }
1.961 -
1.962 - if (!alnum)
1.963 - {
1.964 - if (g_ws_chartable[(unsigned)c])
1.965 - {
1.966 - /* if it's a space, keep track of the first occurring space */
1.967 - if (!first_space)
1.968 - {
1.969 - first_space = i;
1.970 - }
1.971 - } else {
1.972 - if (c == '=')
1.973 - {
1.974 - /* if it's an equal sign, we're done with the key */
1.975 - if (first_space)
1.976 - {
1.977 - /* remove excess whitespace */
1.978 - key_ptr[first_space] = '\0';
1.979 - } else {
1.980 - /* remove the equal sign */
1.981 - key_ptr[i] = '\0';
1.982 - }
1.983 - if (ptr[++i] != '\0')
1.984 - {
1.985 - /* If this isn't the end, set next pointer */
1.986 - val_ptr = &ptr[i];
1.987 - }
1.988 - equal_token = true;
1.989 - break;
1.990 - } else {
1.991 - /* Mark that we got something invalid! */
1.992 - invalid_tokens = true;
1.993 - first_space = 0;
1.994 - }
1.995 - }
1.996 - }
1.997 - }
1.998 -
1.999 - /* Now we need to parse the value, if any */
1.1000 - if (val_ptr)
1.1001 - {
1.1002 - /* eat up spaces! there shouldn't be any h*/
1.1003 - while ((*val_ptr != '\0') && g_ws_chartable[(unsigned)*val_ptr] != 0)
1.1004 - {
1.1005 - val_ptr++;
1.1006 - }
1.1007 - if (*val_ptr == '\0')
1.1008 - {
1.1009 - val_ptr = NULL;
1.1010 - goto skip_value;
1.1011 - }
1.1012 - /* Do we have an initial quote? If so, the parsing rules change! */
1.1013 - if (*val_ptr == '"' && *val_ptr != '\0')
1.1014 - {
1.1015 - len = strlen(val_ptr);
1.1016 - if (val_ptr[len-1] == '"')
1.1017 - {
1.1018 - /* Strip quotes! */
1.1019 - val_ptr[--len] = '\0';
1.1020 - val_ptr++;
1.1021 - quotes = true;
1.1022 - }
1.1023 - }
1.1024 - }
1.1025 -skip_value:
1.1026 - /* We're done! */
1.1027 - curtok = val_ptr - buffer;
1.1028 - if (!ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok))
1.1029 - {
1.1030 - curtok = 0;
1.1031 - goto event_failed;
1.1032 - }
1.1033 - }
1.1034 - }
1.1035 -
1.1036 - if (line)
1.1037 - {
1.1038 - *line = curline;
1.1039 - }
1.1040 -
1.1041 - fclose(fp);
1.1042 -
1.1043 - return true;
1.1044 -
1.1045 -event_failed:
1.1046 - if (line)
1.1047 - {
1.1048 - *line = curline;
1.1049 - }
1.1050 -
1.1051 - if (col)
1.1052 - {
1.1053 - *col = curtok;
1.1054 - }
1.1055 -
1.1056 - fclose(fp);
1.1057 -
1.1058 - return false;
1.1059 -}
1.1060 -
1.1061 -const char *TextParsers::GetSMCErrorString(SMCError err)
1.1062 -{
1.1063 - static const char *s_errors[] =
1.1064 - {
1.1065 - NULL,
1.1066 - "Stream failed to open",
1.1067 - "Stream returned read error",
1.1068 - NULL,
1.1069 - "Un-quoted section has invalid tokens",
1.1070 - "Section declared without header",
1.1071 - "Section declared with unknown tokens",
1.1072 - "Section ending without a matching section beginning",
1.1073 - "Section beginning without a matching ending",
1.1074 - "Line contained too many invalid tokens",
1.1075 - "Token buffer overflowed",
1.1076 - "A property was declared outside of a section",
1.1077 - };
1.1078 -
1.1079 - if (err < SMCError_Okay || err > SMCError_InvalidProperty1)
1.1080 - {
1.1081 - return NULL;
1.1082 - }
1.1083 -
1.1084 - return s_errors[err];
1.1085 -}