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