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