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