core/TextParsers.cpp
author David Anderson <dvander@alliedmods.net>
Sun Feb 08 15:40:48 2009 -0500 (2009-02-08)
changeset 2571 5c995a647735
parent 2558 178d7954939d
child 2702 e5478415190e
permissions -rw-r--r--
Fixed documentation typos in dbi.inc (bug 3587).
dvander@591
     1
/**
ds@653
     2
 * vim: set ts=4 :
ds@1330
     3
 * =============================================================================
dvander@1231
     4
 * SourceMod
ds@1947
     5
 * Copyright (C) 2004-2008 AlliedModders LLC.  All rights reserved.
ds@1330
     6
 * =============================================================================
dvander@591
     7
 *
ds@1330
     8
 * This program is free software; you can redistribute it and/or modify it under
ds@1330
     9
 * the terms of the GNU General Public License, version 3.0, as published by the
ds@1330
    10
 * Free Software Foundation.
dvander@1231
    11
 * 
ds@1330
    12
 * This program is distributed in the hope that it will be useful, but WITHOUT
ds@1330
    13
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
ds@1330
    14
 * FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
ds@1330
    15
 * details.
dvander@1231
    16
 *
ds@1330
    17
 * You should have received a copy of the GNU General Public License along with
ds@1330
    18
 * this program.  If not, see <http://www.gnu.org/licenses/>.
dvander@1231
    19
 *
ds@1330
    20
 * As a special exception, AlliedModders LLC gives you permission to link the
ds@1330
    21
 * code of this program (as well as its derivative works) to "Half-Life 2," the
ds@1330
    22
 * "Source Engine," the "SourcePawn JIT," and any Game MODs that run on software
ds@1330
    23
 * by the Valve Corporation.  You must obey the GNU General Public License in
ds@1330
    24
 * all respects for all other code used.  Additionally, AlliedModders LLC grants
ds@1330
    25
 * this exception to all derivative works.  AlliedModders LLC defines further
ds@1330
    26
 * exceptions, found in LICENSE.txt (as of this writing, version JULY-31-2007),
ds@1330
    27
 * or <http://www.sourcemod.net/license.php>.
dvander@591
    28
 *
dvander@591
    29
 * Version: $Id$
dvander@591
    30
 */
dvander@591
    31
dvander@591
    32
#include <stdio.h>
dvander@591
    33
#include <ctype.h>
dvander@591
    34
#include <wctype.h>
dvander@591
    35
#include <string.h>
dvander@591
    36
#include <stdlib.h>
dvander@591
    37
#include <assert.h>
dvander@591
    38
#include "TextParsers.h"
dvander@591
    39
#include "ShareSys.h"
dvander@1815
    40
#include "sm_stringutil.h"
dvander@1815
    41
#include "LibrarySys.h"
dvander@591
    42
dvander@616
    43
TextParsers g_TextParser;
dvander@1406
    44
ITextParsers *textparsers = &g_TextParser;
dvander@591
    45
dvander@591
    46
static int g_ini_chartable1[255] = {0};
dvander@591
    47
static int g_ws_chartable[255] = {0};
dvander@591
    48
dvander@1406
    49
bool TextParsers::IsWhitespace(const char *stream)
dvander@631
    50
{
dvander@631
    51
	return g_ws_chartable[(unsigned)*stream] == 1;
dvander@631
    52
}
dvander@631
    53
dvander@616
    54
TextParsers::TextParsers()
dvander@591
    55
{
dvander@591
    56
	g_ini_chartable1[(unsigned)'_'] = 1;
dvander@591
    57
	g_ini_chartable1[(unsigned)'-'] = 1;
dvander@591
    58
	g_ini_chartable1[(unsigned)','] = 1;
dvander@591
    59
	g_ini_chartable1[(unsigned)'+'] = 1;
dvander@591
    60
	g_ini_chartable1[(unsigned)'.'] = 1;
dvander@591
    61
	g_ini_chartable1[(unsigned)'$'] = 1;
dvander@591
    62
	g_ini_chartable1[(unsigned)'?'] = 1;
dvander@591
    63
	g_ini_chartable1[(unsigned)'/'] = 1;
dvander@591
    64
	g_ws_chartable[(unsigned)'\n'] = 1;
dvander@591
    65
	g_ws_chartable[(unsigned)'\v'] = 1;
dvander@591
    66
	g_ws_chartable[(unsigned)'\r'] = 1;
dvander@591
    67
	g_ws_chartable[(unsigned)'\t'] = 1;
dvander@591
    68
	g_ws_chartable[(unsigned)'\f'] = 1;
dvander@591
    69
	g_ws_chartable[(unsigned)' '] = 1;
dvander@591
    70
}
dvander@591
    71
dvander@616
    72
void TextParsers::OnSourceModAllInitialized()
dvander@591
    73
{
dvander@591
    74
	g_ShareSys.AddInterface(NULL, this);
dvander@591
    75
}
dvander@591
    76
dvander@616
    77
unsigned int TextParsers::GetUTF8CharBytes(const char *stream)
dvander@591
    78
{
dvander@591
    79
	return _GetUTF8CharBytes(stream);
dvander@591
    80
}
dvander@591
    81
dvander@591
    82
/**
dvander@591
    83
 * File streams
dvander@591
    84
 */
dvander@591
    85
dvander@591
    86
bool FileStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read)
dvander@591
    87
{
dvander@591
    88
	size_t num = fread(buffer, 1, maxlength, (FILE *)stream);
dvander@591
    89
dvander@591
    90
	*read = static_cast<unsigned int>(num);
dvander@591
    91
dvander@591
    92
	if (num == 0 && feof((FILE *)stream))
dvander@591
    93
	{
dvander@591
    94
		return true;
dvander@591
    95
	}
dvander@591
    96
dvander@591
    97
	return (ferror((FILE *)stream) == 0);
dvander@591
    98
}
dvander@591
    99
dvander@1656
   100
SMCError TextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc, SMCStates *states)
dvander@591
   101
{
dvander@591
   102
	FILE *fp = fopen(file, "rt");
dvander@591
   103
dvander@591
   104
	if (!fp)
dvander@591
   105
	{
dvander@1656
   106
		if (states != NULL)
dvander@1656
   107
		{
dvander@1656
   108
			states->line = 0;
dvander@1656
   109
			states->col = 0;
dvander@1656
   110
		}
dvander@1656
   111
		return SMCError_StreamOpen;
dvander@591
   112
	}
dvander@591
   113
dvander@1656
   114
	SMCError result = ParseStream_SMC(fp, FileStreamReader, smc, states);
dvander@591
   115
dvander@591
   116
	fclose(fp);
dvander@591
   117
dvander@591
   118
	return result;
dvander@591
   119
}
dvander@591
   120
dvander@1815
   121
SMCError TextParsers::ParseSMCFile(const char *file,
dvander@1815
   122
								   ITextListener_SMC *smc_listener,
dvander@1815
   123
								   SMCStates *states,
dvander@1815
   124
								   char *buffer,
dvander@1815
   125
								   size_t maxsize)
dvander@1815
   126
{
dvander@1815
   127
	const char *errstr;
dvander@1815
   128
	FILE *fp = fopen(file, "rt");
dvander@1815
   129
dvander@1815
   130
	if (fp == NULL)
dvander@1815
   131
	{
dvander@1815
   132
		char error[256] = "unknown";
dvander@1815
   133
		if (states != NULL)
dvander@1815
   134
		{
dvander@1815
   135
			states->line = 0;
dvander@1815
   136
			states->col = 0;
dvander@1815
   137
		}
dvander@1815
   138
		g_LibSys.GetPlatformError(error, sizeof(error));
dvander@1815
   139
		UTIL_Format(buffer, maxsize, "File could not be opened: %s", error);
dvander@1815
   140
		return SMCError_StreamOpen;
dvander@1815
   141
	}
dvander@1815
   142
dvander@1815
   143
	SMCError result = ParseStream_SMC(fp, FileStreamReader, smc_listener, states);
dvander@1815
   144
dvander@1815
   145
	fclose(fp);
dvander@1815
   146
dvander@1815
   147
	errstr = GetSMCErrorString(result);
dvander@1815
   148
	UTIL_Format(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error");
dvander@1815
   149
dvander@1815
   150
	return result;
dvander@1815
   151
}
dvander@1815
   152
dvander@2558
   153
struct RawStream
dvander@2558
   154
{
dvander@2558
   155
	const char *stream;
dvander@2558
   156
	size_t length;
dvander@2558
   157
	size_t pos;
dvander@2558
   158
};
dvander@2558
   159
dvander@2558
   160
bool RawStreamReader(void *stream, char *buffer, size_t maxlength, unsigned int *read)
dvander@2558
   161
{
dvander@2558
   162
	RawStream *rs = (RawStream *)stream;
dvander@2558
   163
dvander@2558
   164
	if (rs->pos >= rs->length)
dvander@2558
   165
	{
dvander@2558
   166
		return false;
dvander@2558
   167
	}
dvander@2558
   168
dvander@2558
   169
	size_t remaining = rs->length - rs->pos;
dvander@2558
   170
dvander@2558
   171
	/* Use the smaller of the two */
dvander@2558
   172
	size_t copy = (remaining > maxlength) ? maxlength : remaining;
dvander@2558
   173
dvander@2558
   174
	memcpy(buffer, &rs->stream[rs->pos], copy);
dvander@2558
   175
	rs->pos += copy;
dvander@2558
   176
	*read = copy;
dvander@2558
   177
	assert(rs->pos <= rs->length);
dvander@2558
   178
dvander@2558
   179
	return true;
dvander@2558
   180
}
dvander@2558
   181
dvander@2558
   182
SMCError TextParsers::ParseSMCStream(const char *stream,
dvander@2558
   183
									 size_t length,
dvander@2558
   184
									 ITextListener_SMC *smc_listener,
dvander@2558
   185
									 SMCStates *states,
dvander@2558
   186
									 char *buffer,
dvander@2558
   187
									 size_t maxsize)
dvander@2558
   188
{
dvander@2558
   189
	RawStream rs;
dvander@2558
   190
	SMCError result;
dvander@2558
   191
dvander@2558
   192
	rs.stream = stream;
dvander@2558
   193
	rs.length = length;
dvander@2558
   194
	rs.pos = 0;
dvander@2558
   195
dvander@2558
   196
	result = ParseStream_SMC(&rs, RawStreamReader, smc_listener, states);
dvander@2558
   197
dvander@2558
   198
	const char *errstr = GetSMCErrorString(result);
dvander@2558
   199
	UTIL_Format(buffer, maxsize, "%s", errstr != NULL ? errstr : "Unknown error");
dvander@2558
   200
dvander@2558
   201
	return result;
dvander@2558
   202
}
dvander@2558
   203
dvander@591
   204
/** 
dvander@591
   205
 * Raw parsing of streams with helper functions
dvander@591
   206
 */
dvander@591
   207
dvander@591
   208
struct StringInfo
dvander@591
   209
{
dvander@591
   210
	StringInfo() : quoted(false), ptr(NULL), end(NULL), special(false) { }
dvander@591
   211
	bool quoted;
dvander@591
   212
	char *ptr;
dvander@591
   213
	char *end;
dvander@591
   214
	bool special;
dvander@591
   215
};
dvander@591
   216
dvander@591
   217
const char *FixupString(StringInfo &data)
dvander@591
   218
{
dvander@591
   219
	if (!data.ptr)
dvander@591
   220
	{
dvander@591
   221
		return NULL;
dvander@591
   222
	}
dvander@591
   223
dvander@591
   224
	if (data.quoted)
dvander@591
   225
	{
dvander@591
   226
		data.ptr++;
dvander@591
   227
	}
dvander@591
   228
#if defined _DEBUG
dvander@591
   229
	else {
dvander@591
   230
		/* A string will never have beginning whitespace because we ignore it in the stream.
dvander@591
   231
		 * Furthermore, if there is trailing whitespace, the end ptr will point to it, so it is valid
dvander@591
   232
		 * to overwrite!  Lastly, the last character must be whitespace or a comment/invalid character.
dvander@591
   233
		 */
dvander@591
   234
 	}
dvander@591
   235
#endif
dvander@591
   236
dvander@1073
   237
	/* Do some extra work on strings that have special quoted characters. */
dvander@591
   238
	if (data.special)
dvander@591
   239
	{
dvander@1073
   240
		char *outptr = data.ptr;
dvander@1073
   241
		size_t len = data.end - data.ptr;
dvander@1073
   242
		if (len >= 2)
dvander@1073
   243
		{
dvander@1073
   244
			for (size_t i=0; i<len; i++)
dvander@1073
   245
			{
dvander@1073
   246
				if (data.ptr[i] == '\\' && i < len - 1)
dvander@1073
   247
				{
dvander@1073
   248
					/* Resolve the next character. */
dvander@1073
   249
					i++;
dvander@1073
   250
					if (data.ptr[i] == 'n')
dvander@1073
   251
					{
dvander@1073
   252
						data.ptr[i] = '\n';
dvander@1073
   253
					} else if (data.ptr[i] == 't') {
dvander@1073
   254
						data.ptr[i] = '\t';
dvander@1073
   255
					} else if (data.ptr[i] == 'r') {
dvander@1073
   256
						data.ptr[i] = '\r';
dvander@1073
   257
					} else if (data.ptr[i] != '\\'
dvander@1073
   258
						&& data.ptr[i] != '"') {
dvander@1073
   259
						/* This character is invalid, so go back one */
dvander@1073
   260
						i--;
dvander@1073
   261
					}
dvander@1073
   262
				}
dvander@1073
   263
				*outptr++ = data.ptr[i];
dvander@1073
   264
			}
dvander@1073
   265
			*outptr = '\0';
dvander@1073
   266
		}
dvander@591
   267
	}
dvander@591
   268
dvander@1260
   269
	if (data.end)
dvander@1260
   270
	{
dvander@1260
   271
		*(data.end) = '\0';
dvander@1260
   272
	}
dvander@591
   273
dvander@591
   274
	return data.ptr;
dvander@591
   275
}
dvander@591
   276
dvander@591
   277
const char *rotate(StringInfo info[3])
dvander@591
   278
{
dvander@591
   279
	if (info[2].ptr != NULL)
dvander@591
   280
	{
dvander@591
   281
		return info[2].ptr;
dvander@591
   282
	}
dvander@591
   283
dvander@591
   284
	if (info[0].ptr != NULL)
dvander@591
   285
	{
dvander@591
   286
		info[2] = info[1];
dvander@591
   287
		info[1] = info[0];
dvander@591
   288
		info[0] = StringInfo();
dvander@591
   289
	}
dvander@591
   290
	
dvander@591
   291
	return NULL;
dvander@591
   292
}
dvander@591
   293
dvander@591
   294
void scrap(StringInfo info[3])
dvander@591
   295
{
dvander@591
   296
	info[2] = StringInfo();
dvander@591
   297
	info[1] = StringInfo();
dvander@591
   298
	info[0] = StringInfo();
dvander@591
   299
}
dvander@591
   300
dvander@591
   301
void reloc(StringInfo &data, unsigned int bytes)
dvander@591
   302
{
dvander@591
   303
	if (data.ptr)
dvander@591
   304
	{
dvander@591
   305
		data.ptr -= bytes;
dvander@591
   306
	}
dvander@591
   307
	if (data.end)
dvander@591
   308
	{
dvander@591
   309
		data.end -= bytes;
dvander@591
   310
	}
dvander@591
   311
}
dvander@591
   312
dvander@591
   313
char *lowstring(StringInfo info[3])
dvander@591
   314
{
dvander@591
   315
	for (int i=2; i>=0; i--)
dvander@591
   316
	{
dvander@591
   317
		if (info[i].ptr)
dvander@591
   318
		{
dvander@591
   319
			return info[i].ptr;
dvander@591
   320
		}
dvander@591
   321
	}
dvander@591
   322
dvander@591
   323
	return NULL;
dvander@591
   324
}
dvander@591
   325
dvander@1656
   326
SMCError TextParsers::ParseStream_SMC(void *stream, 
dvander@591
   327
								   STREAMREADER srdr, 
dvander@591
   328
								   ITextListener_SMC *smc, 
dvander@1656
   329
								   SMCStates *pStates)
dvander@591
   330
{
dvander@1503
   331
	char *reparse_point = NULL;
dvander@591
   332
	char in_buf[4096];
dvander@591
   333
	char *parse_point = in_buf;
dvander@591
   334
	char *line_begin = in_buf;
dvander@591
   335
	unsigned int read;
dvander@591
   336
	unsigned int curlevel = 0;
dvander@591
   337
	bool in_quote = false;
dvander@591
   338
	bool ignoring = false;
dvander@591
   339
	bool eol_comment = false;
dvander@591
   340
	bool ml_comment = false;
dvander@591
   341
	unsigned int i;
dvander@1656
   342
	SMCError err = SMCError_Okay;
dvander@1656
   343
	SMCResult res;
dvander@1656
   344
	SMCStates states;
dvander@591
   345
	char c;
dvander@591
   346
dvander@591
   347
	StringInfo strings[3];
dvander@591
   348
	StringInfo emptystring;
dvander@591
   349
dvander@1656
   350
	states.line = 1;
dvander@1656
   351
	states.col = 0;
dvander@1656
   352
dvander@591
   353
	smc->ReadSMC_ParseStart();
dvander@591
   354
dvander@1656
   355
	/**
dvander@1656
   356
	 * The stream reader reads in as much as it can fill the buffer with.
dvander@1656
   357
	 * It then processes the buffer.  If the buffer cannot be fully processed, for example, 
dvander@1656
   358
	 * a line is left hanging with no newline, then the contents of the buffer is shifted 
dvander@1656
   359
	 * down, and the buffer is filled from the stream reader again.
dvander@1656
   360
	 *
dvander@1656
   361
	 * What makes this particularly annoying is that we cache pointers everywhere, so when 
dvander@1656
   362
	 * the shifting process takes place, all those pointers must be shifted as well.
dvander@1656
   363
	 */
dvander@1503
   364
	while (srdr(stream, parse_point, sizeof(in_buf) - (parse_point - in_buf) - 1, &read))
dvander@591
   365
	{
dvander@591
   366
		if (!read)
dvander@591
   367
		{
dvander@591
   368
			break;
dvander@591
   369
		}
dvander@591
   370
dvander@1656
   371
		/* Check for BOM markings, which is only relevant on the first line.
dvander@1656
   372
		 * Not worth it, but it could be moved out of the loop.
dvander@591
   373
		 */
dvander@1656
   374
		if (states.line == 1 && 
dvander@591
   375
			in_buf[0] == (char)0xEF && 
dvander@591
   376
			in_buf[1] == (char)0xBB && 
dvander@591
   377
			in_buf[2] == (char)0xBF)
dvander@591
   378
		{
dvander@1143
   379
			/* Move EVERYTHING down :\ */
dvander@1143
   380
			memmove(in_buf, &in_buf[3], read - 3);
dvander@1143
   381
			read -= 3;
dvander@591
   382
		}
dvander@591
   383
dvander@591
   384
		if (reparse_point)
dvander@591
   385
		{
dvander@591
   386
			read += (parse_point - reparse_point);
dvander@591
   387
			parse_point = reparse_point;
dvander@591
   388
			reparse_point = NULL;
dvander@591
   389
		}
dvander@591
   390
dvander@591
   391
		for (i=0; i<read; i++)
dvander@591
   392
		{
dvander@591
   393
			c = parse_point[i];
dvander@591
   394
			if (c == '\n')
dvander@591
   395
			{
dvander@591
   396
				/* If we got a newline, there's a lot of things that could have happened in the interim.
dvander@591
   397
				 * First, let's make sure the staged strings are rotated.
dvander@591
   398
				 */
dvander@591
   399
				if (strings[0].ptr)
dvander@591
   400
				{
dvander@591
   401
					strings[0].end = &parse_point[i];
dvander@591
   402
					if (rotate(strings) != NULL)
dvander@591
   403
					{
dvander@1656
   404
						err = SMCError_InvalidTokens;
dvander@591
   405
						goto failed;
dvander@591
   406
					}
dvander@591
   407
				}
dvander@591
   408
dvander@591
   409
				/* Next, let's clear some line-based values that may no longer have meaning */
dvander@591
   410
				eol_comment = false;
dvander@591
   411
				in_quote = false;
dvander@591
   412
				if (ignoring && !ml_comment)
dvander@591
   413
				{
dvander@591
   414
					ignoring = false;
dvander@591
   415
				}
dvander@591
   416
dvander@1656
   417
				/* Pass the raw line onto the listener.  We terminate the line so the receiver 
dvander@1656
   418
				 * doesn't get tons of useless info.  We restore the newline after.
dvander@1656
   419
				 */
dvander@1656
   420
				parse_point[i] = '\0';
dvander@1656
   421
				if ((res=smc->ReadSMC_RawLine(&states, line_begin)) != SMCResult_Continue)
dvander@591
   422
				{
dvander@1656
   423
					err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
dvander@591
   424
					goto failed;
dvander@591
   425
				}
dvander@1656
   426
				parse_point[i] = '\n';
dvander@591
   427
dvander@591
   428
				/* Now we check the sanity of our staged strings! */
dvander@591
   429
				if (strings[2].ptr)
dvander@591
   430
				{
dvander@591
   431
					if (!curlevel)
dvander@591
   432
					{
dvander@1656
   433
						err = SMCError_InvalidProperty1;
dvander@591
   434
						goto failed;
dvander@591
   435
					}
dvander@591
   436
					/* Assume the next string is a property and pass the info on. */
dvander@591
   437
					if ((res=smc->ReadSMC_KeyValue(
dvander@1656
   438
						&states,
dvander@591
   439
						FixupString(strings[2]),
dvander@1656
   440
						FixupString(strings[1]))) != SMCResult_Continue)
dvander@591
   441
					{
dvander@1656
   442
						err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
dvander@591
   443
						goto failed;
dvander@591
   444
					}
dvander@591
   445
					scrap(strings);
dvander@591
   446
				}
dvander@591
   447
dvander@591
   448
				/* Change the states for the next line */
dvander@1656
   449
				states.col = 0;
dvander@1656
   450
				states.line++;
dvander@591
   451
				line_begin = &parse_point[i+1];		//Note: safe because this gets relocated later
dvander@1656
   452
			} 
dvander@1656
   453
			else if (ignoring) 
dvander@1656
   454
			{
dvander@591
   455
				if (in_quote)
dvander@591
   456
				{
dvander@1730
   457
					/* If i was 0, we could have reparsed, so make sure there's no buffer underrun */
dvander@1730
   458
					if ((&parse_point[i] != in_buf) && c == '"' && parse_point[i-1] != '\\')
dvander@591
   459
					{
dvander@591
   460
						/* If we reached a quote in an ignore phase,
dvander@591
   461
						 * we're staging a string and we must rotate it out.
dvander@591
   462
						 */
dvander@591
   463
						in_quote = false;
dvander@591
   464
						ignoring = false;
dvander@591
   465
						/* Set our info */
dvander@591
   466
						strings[0].end = &parse_point[i];
dvander@591
   467
						strings[0].quoted = true;
dvander@591
   468
						if (rotate(strings) != NULL)
dvander@591
   469
						{
dvander@591
   470
							/* If we rotated too many strings, there was too much crap on one line */
dvander@1656
   471
							err = SMCError_InvalidTokens;
dvander@591
   472
							goto failed;
dvander@591
   473
						}
dvander@1656
   474
					} 
dvander@1656
   475
					else if (c == '\\') 
dvander@1656
   476
					{
dvander@591
   477
						strings[0].special = true;
dvander@1073
   478
						if (i == (read - 1))
dvander@1073
   479
						{
dvander@1073
   480
							reparse_point = &parse_point[i];
dvander@1073
   481
							break;
dvander@1073
   482
						}
dvander@591
   483
					}
dvander@1656
   484
				} 
dvander@1656
   485
				else if (ml_comment) 
dvander@1656
   486
				{
dvander@591
   487
					if (c == '*')
dvander@591
   488
					{
dvander@591
   489
						/* Check if we need to get more input first */
dvander@591
   490
						if (i == read - 1)
dvander@591
   491
						{
dvander@591
   492
							reparse_point = &parse_point[i];
dvander@591
   493
							break;
dvander@591
   494
						}
dvander@591
   495
						if (parse_point[i+1] == '/')
dvander@591
   496
						{
dvander@591
   497
							ml_comment = false;
dvander@591
   498
							ignoring = false;
dvander@591
   499
							/* We should not be staging anything right now. */
dvander@591
   500
							assert(strings[0].ptr == NULL);
dvander@591
   501
							/* Advance the input stream so we don't choke on this token */
dvander@591
   502
							i++;
dvander@1656
   503
							states.col++;
dvander@591
   504
						}
dvander@591
   505
					}
dvander@591
   506
				}
dvander@1656
   507
			} 
dvander@1656
   508
			else 
dvander@1656
   509
			{
dvander@591
   510
				/* Check if we're whitespace or not */
dvander@591
   511
				if (!g_ws_chartable[(unsigned)c])
dvander@591
   512
				{
dvander@591
   513
					bool restage = false;
dvander@591
   514
					/* Check various special tokens:
dvander@591
   515
					 * ;
dvander@591
   516
					 * //
dvander@591
   517
					 * / *
dvander@591
   518
					 * {
dvander@591
   519
					 * }
dvander@591
   520
					 */
dvander@591
   521
					if (c == ';' || c == '/')
dvander@591
   522
					{
dvander@591
   523
						/* If it's a line-based comment (that is, ; or //)
dvander@591
   524
						 * we will need to scrap everything until the end of the line.
dvander@591
   525
						 */
dvander@591
   526
						if (c == '/')
dvander@591
   527
						{
dvander@591
   528
							if (i == read - 1)
dvander@591
   529
							{
dvander@591
   530
								/* If we reached the end of the look-ahead, we need to re-check our input.
dvander@591
   531
								 * Breaking out will force this to be the new reparse point!
dvander@591
   532
								 */
dvander@1109
   533
								reparse_point = &parse_point[i];
dvander@591
   534
								break;
dvander@591
   535
							}
dvander@591
   536
							if (parse_point[i + 1] == '/')
dvander@591
   537
							{
dvander@591
   538
								/* standard comment */
dvander@591
   539
								ignoring = true;
dvander@591
   540
								eol_comment = true;
dvander@591
   541
								restage = true;
dvander@1656
   542
							} 
dvander@1656
   543
							else if (parse_point[i+1] == '*') 
dvander@1656
   544
							{
dvander@591
   545
								/* inline comment - start ignoring */
dvander@591
   546
								ignoring = true;
dvander@591
   547
								ml_comment = true;
dvander@591
   548
								/* yes, we restage, meaning that:
dvander@591
   549
								 * STR/ *stuff* /ING  (space because ml comments don't nest in C++)
dvander@591
   550
								 * will not generate 'STRING', but rather 'STR' and 'ING'.
dvander@591
   551
								 * This should be a rare occurrence and is done here for convenience.
dvander@591
   552
								 */
dvander@591
   553
								restage = true;
dvander@591
   554
							}
dvander@1656
   555
						} 
dvander@1656
   556
						else 
dvander@1656
   557
						{
dvander@591
   558
							ignoring = true;
dvander@591
   559
							eol_comment = true;
dvander@591
   560
							restage = true;
dvander@591
   561
						}
dvander@1656
   562
					} 
dvander@1656
   563
					else if (c == '{') 
dvander@1656
   564
					{
dvander@591
   565
						/* If we are staging a string, we must rotate here */
dvander@591
   566
						if (strings[0].ptr)
dvander@591
   567
						{
dvander@591
   568
							/* We have unacceptable tokens on this line */
dvander@591
   569
							if (rotate(strings) != NULL)
dvander@591
   570
							{
dvander@1656
   571
								err = SMCError_InvalidSection1;
dvander@591
   572
								goto failed;
dvander@591
   573
							}
dvander@591
   574
						}
dvander@591
   575
						/* Sections must always be alone */
dvander@591
   576
						if (strings[2].ptr != NULL)
dvander@591
   577
						{
dvander@1656
   578
							err = SMCError_InvalidSection1;
dvander@591
   579
							goto failed;
dvander@1656
   580
						} 
dvander@1656
   581
						else if (strings[1].ptr == NULL)
dvander@1656
   582
						{
dvander@1656
   583
							err = SMCError_InvalidSection2;
dvander@591
   584
							goto failed;
dvander@591
   585
						}
dvander@1656
   586
						if ((res=smc->ReadSMC_NewSection(&states, FixupString(strings[1])))
dvander@1656
   587
							!= SMCResult_Continue)
dvander@591
   588
						{
dvander@1656
   589
							err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
dvander@591
   590
							goto failed;
dvander@591
   591
						}
dvander@591
   592
						strings[1] = emptystring;
dvander@591
   593
						curlevel++;
dvander@1656
   594
					} 
dvander@1656
   595
					else if (c == '}') 
dvander@1656
   596
					{
dvander@591
   597
						/* Unlike our matching friend, this can be on the same line as something prior */
dvander@591
   598
						if (rotate(strings) != NULL)
dvander@591
   599
						{
dvander@1656
   600
							err = SMCError_InvalidSection3;
dvander@591
   601
							goto failed;
dvander@591
   602
						}
dvander@591
   603
						if (strings[2].ptr)
dvander@591
   604
						{
dvander@591
   605
							if (!curlevel)
dvander@591
   606
							{
dvander@1656
   607
								err = SMCError_InvalidProperty1;
dvander@591
   608
								goto failed;
dvander@591
   609
							}
dvander@591
   610
							if ((res=smc->ReadSMC_KeyValue(
dvander@1656
   611
											&states,
dvander@591
   612
											FixupString(strings[2]),
dvander@1656
   613
											FixupString(strings[1])))
dvander@1656
   614
								!= SMCResult_Continue)
dvander@591
   615
							{
dvander@1656
   616
								err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
dvander@591
   617
								goto failed;
dvander@591
   618
							}
dvander@1656
   619
						} 
dvander@1656
   620
						else if (strings[1].ptr) 
dvander@1656
   621
						{
dvander@1656
   622
							err = SMCError_InvalidSection3;
dvander@591
   623
							goto failed;
dvander@1656
   624
						} 
dvander@1656
   625
						else if (!curlevel) 
dvander@1656
   626
						{
dvander@1656
   627
							err = SMCError_InvalidSection4;
dvander@591
   628
							goto failed;
dvander@591
   629
						}
dvander@591
   630
						/* Now it's safe to leave the section */
dvander@591
   631
						scrap(strings);
dvander@1656
   632
						if ((res=smc->ReadSMC_LeavingSection(&states)) != SMCResult_Continue)
dvander@591
   633
						{
dvander@1656
   634
							err = (res == SMCResult_HaltFail) ? SMCError_Custom : SMCError_Okay;
dvander@591
   635
							goto failed;
dvander@591
   636
						}
dvander@591
   637
						curlevel--;
dvander@1656
   638
					} 
dvander@1656
   639
					else if (c == '"') 
dvander@1656
   640
					{
dvander@591
   641
						/* If we get a quote mark, we always restage, but we need to do it beforehand */
dvander@591
   642
						if (strings[0].ptr)
dvander@591
   643
						{
dvander@591
   644
							strings[0].end = &parse_point[i];
dvander@591
   645
							if (rotate(strings) != NULL)
dvander@591
   646
							{
dvander@1656
   647
								err = SMCError_InvalidTokens;
dvander@591
   648
								goto failed;
dvander@591
   649
							}
dvander@591
   650
						}
dvander@591
   651
						strings[0].ptr = &parse_point[i];
dvander@591
   652
						in_quote = true;
dvander@591
   653
						ignoring = true;
dvander@1656
   654
					} 
dvander@1656
   655
					else if (!strings[0].ptr) 
dvander@1656
   656
					{
dvander@591
   657
						/* If we have no string, we must start one */
dvander@591
   658
						strings[0].ptr = &parse_point[i];
dvander@591
   659
					}
dvander@591
   660
					if (restage && strings[0].ptr)
dvander@591
   661
					{
dvander@591
   662
						strings[0].end = &parse_point[i];
dvander@591
   663
						if (rotate(strings) != NULL)
dvander@591
   664
						{
dvander@1656
   665
							err = SMCError_InvalidTokens;
dvander@591
   666
							goto failed;
dvander@591
   667
						}
dvander@591
   668
					}
dvander@1656
   669
				} 
dvander@1656
   670
				else 
dvander@1656
   671
				{
dvander@591
   672
					/* If we're eating a string and get whitespace, we need to restage.
dvander@591
   673
					 * (Note that if we are quoted, this is being ignored)
dvander@591
   674
					 */
dvander@591
   675
					if (strings[0].ptr)
dvander@591
   676
					{
dvander@591
   677
						/*
dvander@591
   678
						 * The specification says the second string in a pair does not need to be quoted.
dvander@591
   679
						 * Thus, we check if there's already a string on the stack.
dvander@591
   680
						 * If there's a newline, we always rotate so the newline has an empty starter.
dvander@591
   681
						 */
dvander@591
   682
						if (!strings[1].ptr)
dvander@591
   683
						{
dvander@591
   684
							/* There's no string, so we must move this one down and eat up another */
dvander@591
   685
							strings[0].end = &parse_point[i];
dvander@591
   686
							rotate(strings);
dvander@1656
   687
						} 
dvander@1656
   688
						else if (!strings[1].quoted) 
dvander@1656
   689
						{
dvander@1656
   690
							err = SMCError_InvalidTokens;
dvander@591
   691
							goto failed;
dvander@591
   692
						}
dvander@591
   693
					}
dvander@591
   694
				}
dvander@591
   695
			}
dvander@591
   696
dvander@591
   697
			/* Advance which token we're on */
dvander@1656
   698
			states.col++;
dvander@591
   699
		}
dvander@591
   700
dvander@591
   701
		if (line_begin != in_buf)
dvander@591
   702
		{
dvander@591
   703
			/* The line buffer has advanced, so it's safe to copy N bytes back to the beginning.
dvander@591
   704
			 * What's N?  N is the lowest point we're currently relying on.
dvander@591
   705
			 */
dvander@591
   706
			char *stage = lowstring(strings);
dvander@591
   707
			if (!stage || stage > line_begin)
dvander@591
   708
			{
dvander@591
   709
				stage = line_begin;
dvander@591
   710
			}
dvander@591
   711
			unsigned int bytes = read - (stage - parse_point);
dvander@591
   712
dvander@591
   713
			/* It is now safe to delete everything before the staged point */
dvander@591
   714
			memmove(in_buf, stage, bytes);
dvander@591
   715
dvander@591
   716
			/* Calculate the number of bytes in the new buffer */
dvander@591
   717
			bytes = stage - in_buf;
dvander@591
   718
			/* Relocate all the cached pointers to our new base */
dvander@591
   719
			line_begin -= bytes;
dvander@591
   720
			reloc(strings[0], bytes);
dvander@591
   721
			reloc(strings[1], bytes);
dvander@591
   722
			reloc(strings[2], bytes);
dvander@591
   723
			if (reparse_point)
dvander@591
   724
			{
dvander@591
   725
				reparse_point -= bytes;
dvander@591
   726
			}
dvander@591
   727
			if (parse_point)
dvander@591
   728
			{
dvander@591
   729
				parse_point = &parse_point[read];
dvander@591
   730
				parse_point -= bytes;
dvander@591
   731
			}
dvander@1656
   732
		} 
dvander@1656
   733
		else if (read == sizeof(in_buf) - 1) 
dvander@1656
   734
		{
dvander@1656
   735
			err = SMCError_TokenOverflow;
dvander@591
   736
			goto failed;
dvander@591
   737
		}
dvander@591
   738
	}
dvander@591
   739
dvander@591
   740
	/* If we're done parsing and there are tokens left over... */
dvander@591
   741
	if (curlevel)
dvander@591
   742
	{
dvander@1656
   743
		err = SMCError_InvalidSection5;
dvander@591
   744
		goto failed;
dvander@1656
   745
	} 
dvander@1656
   746
	else if (strings[0].ptr || strings[1].ptr) 
dvander@1656
   747
	{
dvander@1656
   748
		err = SMCError_InvalidTokens;
dvander@591
   749
		goto failed;
dvander@591
   750
	}
dvander@591
   751
	
dvander@591
   752
	smc->ReadSMC_ParseEnd(false, false);
dvander@1656
   753
	
dvander@1656
   754
	if (pStates != NULL)
dvander@1656
   755
	{
dvander@1656
   756
		*pStates = states;
dvander@1656
   757
	}
dvander@591
   758
dvander@1656
   759
	return SMCError_Okay;
dvander@591
   760
dvander@591
   761
failed:
dvander@1656
   762
	if (pStates != NULL)
dvander@591
   763
	{
dvander@1656
   764
		*pStates = states;
dvander@591
   765
	}
dvander@591
   766
dvander@1656
   767
	smc->ReadSMC_ParseEnd(true, (err == SMCError_Custom));
dvander@591
   768
dvander@591
   769
	return err;
dvander@591
   770
}
dvander@591
   771
dvander@591
   772
dvander@591
   773
/**
dvander@591
   774
 * INI parser 
dvander@591
   775
 */
dvander@591
   776
dvander@616
   777
bool TextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col)
dvander@591
   778
{
dvander@591
   779
	FILE *fp = fopen(file, "rt");
dvander@591
   780
	unsigned int curline = 0;
dvander@591
   781
	unsigned int curtok;
dvander@591
   782
	size_t len;
dvander@591
   783
dvander@591
   784
	if (!fp)
dvander@591
   785
	{
dvander@591
   786
		if (line)
dvander@591
   787
		{
dvander@591
   788
			*line = 0;
dvander@591
   789
		}
dvander@591
   790
dvander@591
   791
		return false;
dvander@591
   792
	}
dvander@591
   793
dvander@591
   794
	char buffer[2048];
dvander@591
   795
	char *ptr, *save_ptr;
dvander@591
   796
	bool in_quote;
dvander@591
   797
dvander@591
   798
	while (!feof(fp))
dvander@591
   799
	{
dvander@591
   800
		curline++;
dvander@591
   801
		curtok = 0;
dvander@591
   802
		buffer[0] = '\0';
dvander@591
   803
		if (fgets(buffer, sizeof(buffer), fp) == NULL)
dvander@591
   804
		{
dvander@591
   805
			break;
dvander@591
   806
		}
dvander@591
   807
dvander@591
   808
		//:TODO: this will only run once, so find a nice way to move it out of the while loop
dvander@591
   809
		/* If this is the first line, check the first three bytes for BOM */
dvander@591
   810
		if (curline == 1 && 
dvander@591
   811
			buffer[0] == (char)0xEF && 
dvander@591
   812
			buffer[1] == (char)0xBB && 
dvander@591
   813
			buffer[2] == (char)0xBF)
dvander@591
   814
		{
dvander@591
   815
			/* We have a UTF-8 marked file... skip these bytes */
dvander@591
   816
			ptr = &buffer[3];
dvander@591
   817
		} else {
dvander@591
   818
			ptr = buffer;
dvander@591
   819
		}
dvander@591
   820
dvander@591
   821
		/***************************************************
dvander@591
   822
		 * We preprocess the string before parsing tokens! *
dvander@591
   823
		 ***************************************************/
dvander@591
   824
dvander@591
   825
		/* First strip beginning whitespace */
dvander@591
   826
		while (*ptr != '\0' && g_ws_chartable[(unsigned)*ptr] != 0)
dvander@591
   827
		{
dvander@591
   828
			ptr++;
dvander@591
   829
		}
dvander@591
   830
dvander@591
   831
		len = strlen(ptr);
dvander@591
   832
dvander@591
   833
		if (!len)
dvander@591
   834
		{
dvander@591
   835
			continue;
dvander@591
   836
		}
dvander@591
   837
dvander@591
   838
		/* Now search for comment characters */
dvander@591
   839
		in_quote = false;
dvander@591
   840
		save_ptr = ptr;
dvander@591
   841
		for (size_t i=0; i<len; i++,ptr++)
dvander@591
   842
		{
dvander@591
   843
			if (!in_quote)
dvander@591
   844
			{
dvander@591
   845
				switch (*ptr)
dvander@591
   846
				{
dvander@591
   847
				case '"':
dvander@591
   848
					{
dvander@591
   849
						in_quote = true;
dvander@591
   850
						break;
dvander@591
   851
					}
dvander@591
   852
				case ';':
dvander@591
   853
					{
dvander@591
   854
						/* Stop the loop */
dvander@591
   855
						len = i;
dvander@591
   856
						/* Terminate the string here */
dvander@591
   857
						*ptr = '\0';
dvander@591
   858
						break;
dvander@591
   859
					}
dvander@591
   860
				}
dvander@591
   861
			} else {
dvander@591
   862
				if (*ptr == '"')
dvander@591
   863
				{
dvander@591
   864
					in_quote = false;
dvander@591
   865
				}
dvander@591
   866
			}
dvander@591
   867
		}
dvander@591
   868
dvander@591
   869
		if (!len)
dvander@591
   870
		{
dvander@591
   871
			continue;
dvander@591
   872
		}
dvander@591
   873
dvander@591
   874
		ptr = save_ptr;
dvander@591
   875
dvander@591
   876
		/* Lastly, strip ending whitespace off */
dvander@591
   877
		for (size_t i=len-1; i>=0 && i<len; i--)
dvander@591
   878
		{
dvander@591
   879
			if (g_ws_chartable[(unsigned)ptr[i]])
dvander@591
   880
			{
dvander@591
   881
				ptr[i] = '\0';
dvander@591
   882
				len--;
dvander@591
   883
			} else {
dvander@591
   884
				break;
dvander@591
   885
			}
dvander@591
   886
		}
dvander@591
   887
dvander@591
   888
		if (!len)
dvander@591
   889
		{
dvander@591
   890
			continue;
dvander@591
   891
		}
dvander@591
   892
dvander@591
   893
		if (!ini_listener->ReadINI_RawLine(ptr, &curtok))
dvander@591
   894
		{
dvander@591
   895
			goto event_failed;
dvander@591
   896
		}
dvander@591
   897
dvander@591
   898
		if (*ptr == '[')
dvander@591
   899
		{
dvander@591
   900
			bool invalid_tokens = false;
dvander@591
   901
			bool got_bracket = false;
dvander@591
   902
			bool extra_tokens = false;
dvander@591
   903
			char c;
dvander@591
   904
			bool alnum;
dvander@591
   905
			wchar_t wc;
dvander@591
   906
dvander@591
   907
			for (size_t i=1; i<len; i++)
dvander@591
   908
			{
dvander@591
   909
				c = ptr[i];
dvander@591
   910
				alnum = false;
dvander@591
   911
dvander@591
   912
				if (c & (1<<7))
dvander@591
   913
				{
dvander@591
   914
					if (mbtowc(&wc, &ptr[i], len-i) != -1)
dvander@591
   915
					{
dvander@591
   916
						alnum = (iswalnum(wc) != 0);
dvander@591
   917
						i += _GetUTF8CharBytes(&ptr[i]) - 1;
dvander@591
   918
					}
dvander@591
   919
				} else {
dvander@591
   920
					alnum = (isalnum(c) != 0) || (g_ini_chartable1[(unsigned)c] != 0);
dvander@591
   921
				}
dvander@591
   922
				if (!alnum)
dvander@591
   923
				{
dvander@591
   924
					/* First check - is this a bracket? */
dvander@591
   925
					if (c == ']')
dvander@591
   926
					{
dvander@591
   927
						/* Yes! */
dvander@591
   928
						got_bracket = true;
dvander@591
   929
						/* If this isn't the last character... */
dvander@591
   930
						if (i != len - 1)
dvander@591
   931
						{
dvander@591
   932
							extra_tokens = true;
dvander@591
   933
						}
dvander@591
   934
						/* terminate */
dvander@591
   935
						ptr[i] = '\0';
dvander@591
   936
						break;
dvander@591
   937
					} else {
dvander@591
   938
						/* n...No! Continue copying. */
dvander@591
   939
						invalid_tokens = true;
dvander@591
   940
					}
dvander@591
   941
				}
dvander@591
   942
			}
dvander@591
   943
dvander@591
   944
			/* Tell the handler */
dvander@591
   945
			if (!ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok))
dvander@591
   946
			{
dvander@591
   947
				goto event_failed;
dvander@591
   948
			}
dvander@591
   949
		} else {
dvander@591
   950
			char *key_ptr = ptr;
dvander@591
   951
			char *val_ptr = NULL;
dvander@591
   952
			char c;
dvander@591
   953
			size_t first_space = 0;
dvander@591
   954
			bool invalid_tokens = false;
dvander@591
   955
			bool equal_token = false;
dvander@591
   956
			bool quotes = false;
dvander@591
   957
			bool alnum;
dvander@591
   958
			wchar_t wc;
dvander@591
   959
dvander@591
   960
			for (size_t i=0; i<len; i++)
dvander@591
   961
			{
dvander@591
   962
				c = ptr[i];
dvander@591
   963
				alnum = false;
dvander@591
   964
				/* is this an invalid char? */
dvander@591
   965
				if (c & (1<<7))
dvander@591
   966
				{
dvander@591
   967
					if (mbtowc(&wc, &ptr[i], len-i) != -1)
dvander@591
   968
					{
dvander@591
   969
						alnum = (iswalnum(wc) != 0);
dvander@591
   970
						i += _GetUTF8CharBytes(&ptr[i]) - 1;
dvander@591
   971
					}
dvander@591
   972
				} else {
dvander@591
   973
					alnum = (isalnum(c) != 0) || (g_ini_chartable1[(unsigned)c] != 0);
dvander@591
   974
				}
dvander@591
   975
dvander@591
   976
				if (!alnum)
dvander@591
   977
				{
dvander@591
   978
					if (g_ws_chartable[(unsigned)c])
dvander@591
   979
					{
dvander@591
   980
						/* if it's a space, keep track of the first occurring space */
dvander@591
   981
						if (!first_space)
dvander@591
   982
						{
dvander@591
   983
							first_space = i;
dvander@591
   984
						}
dvander@591
   985
					} else {
dvander@591
   986
						if (c == '=')
dvander@591
   987
						{
dvander@591
   988
							/* if it's an equal sign, we're done with the key */
dvander@591
   989
							if (first_space)
dvander@591
   990
							{
dvander@591
   991
								/* remove excess whitespace */
dvander@591
   992
								key_ptr[first_space] = '\0';
dvander@591
   993
							} else {
dvander@591
   994
								/* remove the equal sign */
dvander@591
   995
								key_ptr[i] = '\0';
dvander@591
   996
							}
dvander@591
   997
							if (ptr[++i] != '\0')
dvander@591
   998
							{
dvander@591
   999
								/* If this isn't the end, set next pointer */
dvander@591
  1000
								val_ptr = &ptr[i];
dvander@591
  1001
							}
dvander@591
  1002
							equal_token = true;
dvander@591
  1003
							break;
dvander@591
  1004
						} else {
dvander@591
  1005
							/* Mark that we got something invalid! */
dvander@591
  1006
							invalid_tokens = true;
dvander@591
  1007
							first_space = 0;
dvander@591
  1008
						}
dvander@591
  1009
					}
dvander@591
  1010
				}
dvander@591
  1011
			}
dvander@591
  1012
dvander@591
  1013
			/* Now we need to parse the value, if any */
dvander@591
  1014
			if (val_ptr)
dvander@591
  1015
			{
dvander@591
  1016
				/* eat up spaces! there shouldn't be any h*/
dvander@591
  1017
				while ((*val_ptr != '\0') && g_ws_chartable[(unsigned)*val_ptr] != 0)
dvander@591
  1018
				{
dvander@591
  1019
					val_ptr++;
dvander@591
  1020
				}
dvander@591
  1021
				if (*val_ptr == '\0')
dvander@591
  1022
				{
dvander@591
  1023
					val_ptr = NULL;
dvander@591
  1024
					goto skip_value;
dvander@591
  1025
				}
dvander@591
  1026
				/* Do we have an initial quote? If so, the parsing rules change! */
dvander@591
  1027
				if (*val_ptr == '"' && *val_ptr != '\0')
dvander@591
  1028
				{
dvander@591
  1029
					len = strlen(val_ptr);
dvander@591
  1030
					if (val_ptr[len-1] == '"')
dvander@591
  1031
					{
dvander@591
  1032
						/* Strip quotes! */
dvander@591
  1033
						val_ptr[--len] = '\0';
dvander@591
  1034
						val_ptr++;
dvander@591
  1035
						quotes = true;
dvander@591
  1036
					}
dvander@591
  1037
				}
dvander@591
  1038
			}
dvander@591
  1039
skip_value:
dvander@591
  1040
			/* We're done! */
dvander@591
  1041
			curtok = val_ptr - buffer;
dvander@591
  1042
			if (!ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok))
dvander@591
  1043
			{
dvander@591
  1044
				curtok = 0;
dvander@591
  1045
				goto event_failed;
dvander@591
  1046
			}
dvander@591
  1047
		}
dvander@591
  1048
	}
dvander@591
  1049
dvander@591
  1050
	if (line)
dvander@591
  1051
	{
dvander@591
  1052
		*line = curline;
dvander@591
  1053
	}
dvander@591
  1054
dvander@591
  1055
	fclose(fp);
dvander@591
  1056
dvander@591
  1057
	return true;
dvander@591
  1058
dvander@591
  1059
event_failed:
dvander@591
  1060
	if (line)
dvander@591
  1061
	{
dvander@591
  1062
		*line = curline;
dvander@591
  1063
	}
dvander@591
  1064
dvander@591
  1065
	if (col)
dvander@591
  1066
	{
dvander@591
  1067
		*col = curtok;
dvander@591
  1068
	}
dvander@591
  1069
dvander@591
  1070
	fclose(fp);
dvander@591
  1071
dvander@591
  1072
	return false;
dvander@591
  1073
}
dvander@591
  1074
dvander@1656
  1075
const char *TextParsers::GetSMCErrorString(SMCError err)
dvander@591
  1076
{
dvander@591
  1077
	static const char *s_errors[] = 
dvander@591
  1078
	{
dvander@591
  1079
		NULL,
dvander@591
  1080
		"Stream failed to open",
dvander@591
  1081
		"Stream returned read error",
dvander@591
  1082
		NULL,
dvander@591
  1083
		"Un-quoted section has invalid tokens",
dvander@591
  1084
		"Section declared without header",
dvander@591
  1085
		"Section declared with unknown tokens",
dvander@591
  1086
		"Section ending without a matching section beginning",
dvander@591
  1087
		"Section beginning without a matching ending",
dvander@591
  1088
		"Line contained too many invalid tokens",
dvander@591
  1089
		"Token buffer overflowed",
dvander@591
  1090
		"A property was declared outside of a section",
dvander@591
  1091
	};
dvander@591
  1092
dvander@1656
  1093
	if (err < SMCError_Okay || err > SMCError_InvalidProperty1)
dvander@591
  1094
	{
dvander@591
  1095
		return NULL;
dvander@591
  1096
	}
dvander@591
  1097
dvander@591
  1098
	return s_errors[err];
dvander@591
  1099
}