core/CTextParsers.cpp
author David Anderson <dvander@alliedmods.net>
Sat Nov 11 11:10:24 2006 +0000 (2006-11-11)
changeset 177 d29ade52d953
parent 159 43c471413f70
child 197 dd30d3d6a51f
permissions -rw-r--r--
Fleshed out forward system and extensively documented its design considerations
Implemented and did basic tests on new IPluginFunction type
Split function types into a separate file
     1 #include <stdio.h>
     2 #include <ctype.h>
     3 #include <wctype.h>
     4 #include <string.h>
     5 #include <stdlib.h>
     6 #include "CTextParsers.h"
     7 
     8 CTextParsers g_TextParse;
     9 
    10 static int g_ini_chartable1[255] = {0};
    11 static int g_ws_chartable[255] = {0};
    12 
    13 CTextParsers::CTextParsers()
    14 {
    15 	g_ini_chartable1['_'] = 1;
    16 	g_ini_chartable1['-'] = 1;
    17 	g_ini_chartable1[','] = 1;
    18 	g_ini_chartable1['+'] = 1;
    19 	g_ini_chartable1['.'] = 1;
    20 	g_ini_chartable1['$'] = 1;
    21 	g_ini_chartable1['?'] = 1;
    22 	g_ini_chartable1['/'] = 1;
    23 	g_ws_chartable['\n'] = 1;
    24 	g_ws_chartable['\v'] = 1;
    25 	g_ws_chartable['\r'] = 1;
    26 	g_ws_chartable['\t'] = 1;
    27 	g_ws_chartable['\f'] = 1;
    28 	g_ws_chartable[' '] = 1;
    29 }
    30 
    31 unsigned int CTextParsers::GetUTF8CharBytes(const char *stream)
    32 {
    33 	return _GetUTF8CharBytes(stream);
    34 }
    35 
    36 bool CTextParsers::ParseFile_SMC(const char *file, ITextListener_SMC *smc_listener, unsigned int *line, unsigned int *col, bool strict)
    37 {
    38 	/* This beast is a lot more rigorous than our INI friend. */
    39 
    40 	return false;
    41 }
    42 
    43 bool CTextParsers::ParseFile_INI(const char *file, ITextListener_INI *ini_listener, unsigned int *line, unsigned int *col)
    44 {
    45 	FILE *fp = fopen(file, "rt");
    46 	unsigned int curline = 0;
    47 	unsigned int curtok;
    48 	size_t len;
    49 
    50 	if (!fp)
    51 	{
    52 		if (line)
    53 		{
    54 			*line = 0;
    55 		}
    56 
    57 		return false;
    58 	}
    59 
    60 	char buffer[2048];
    61 	char *ptr, *save_ptr;
    62 	bool in_quote;
    63 
    64 	while (!feof(fp))
    65 	{
    66 		curline++;
    67 		curtok = 0;
    68 		buffer[0] = '\0';
    69 		if (fgets(buffer, sizeof(buffer), fp) == NULL)
    70 		{
    71 			break;
    72 		}
    73 
    74 		//:TODO: this will only run once, so find a nice way to move it out of the while loop
    75 		/* If this is the first line, check the first three bytes for BOM */
    76 		if (curline == 1 && 
    77 			buffer[0] == (char)0xEF && 
    78 			buffer[1] == (char)0xBB && 
    79 			buffer[2] == (char)0xBF)
    80 		{
    81 			/* We have a UTF-8 marked file... skip these bytes */
    82 			ptr = &buffer[3];
    83 		} else {
    84 			ptr = buffer;
    85 		}
    86 
    87 		/***************************************************
    88 		 * We preprocess the string before parsing tokens! *
    89 		 ***************************************************/
    90 
    91 		/* First strip beginning whitespace */
    92 		while (*ptr != '\0' && g_ws_chartable[*ptr] != 0)
    93 		{
    94 			ptr++;
    95 		}
    96 
    97 		len = strlen(ptr);
    98 
    99 		if (!len)
   100 		{
   101 			continue;
   102 		}
   103 
   104 		/* Now search for comment characters */
   105 		in_quote = false;
   106 		save_ptr = ptr;
   107 		for (size_t i=0; i<len; i++,ptr++)
   108 		{
   109 			if (!in_quote)
   110 			{
   111 				switch (*ptr)
   112 				{
   113 				case '"':
   114 					{
   115 						in_quote = true;
   116 						break;
   117 					}
   118 				case ';':
   119 					{
   120 						/* Stop the loop */
   121 						len = i;
   122 						/* Terminate the string here */
   123 						*ptr = '\0';
   124 						break;
   125 					}
   126 				}
   127 			} else {
   128 				if (*ptr == '"')
   129 				{
   130 					in_quote = false;
   131 				}
   132 			}
   133 		}
   134 
   135 		if (!len)
   136 		{
   137 			continue;
   138 		}
   139 
   140 		ptr = save_ptr;
   141 
   142 		/* Lastly, strip ending whitespace off */
   143 		for (size_t i=len-1; i>=0 && i<len; i--)
   144 		{
   145 			if (g_ws_chartable[ptr[i]])
   146 			{
   147 				ptr[i] = '\0';
   148 				len--;
   149 			} else {
   150 				break;
   151 			}
   152 		}
   153 
   154 		if (!len)
   155 		{
   156 			continue;
   157 		}
   158 
   159 		if (!ini_listener->ReadINI_RawLine(ptr, &curtok))
   160 		{
   161 			goto event_failed;
   162 		}
   163 
   164 		if (*ptr == '[')
   165 		{
   166 			bool invalid_tokens = false;
   167 			bool got_bracket = false;
   168 			bool extra_tokens = false;
   169 			char c;
   170 			bool alnum;
   171 			wchar_t wc;
   172 
   173 			for (size_t i=1; i<len; i++)
   174 			{
   175 				c = ptr[i];
   176 				alnum = false;
   177 
   178 				if (c & (1<<7))
   179 				{
   180 					if (mbtowc(&wc, &ptr[i], len-i) != -1)
   181 					{
   182 						alnum = (iswalnum(wc) != 0);
   183 						i += _GetUTF8CharBytes(&ptr[i]) - 1;
   184 					}
   185 				} else {
   186 					alnum = (isalnum(c) != 0) || (g_ini_chartable1[c] != 0);
   187 				}
   188 				if (!alnum)
   189 				{
   190 					/* First check - is this a bracket? */
   191 					if (c == ']')
   192 					{
   193 						/* Yes! */
   194 						got_bracket = true;
   195 						/* If this isn't the last character... */
   196 						if (i != len - 1)
   197 						{
   198 							extra_tokens = true;
   199 						}
   200 						/* terminate */
   201 						ptr[i] = '\0';
   202 						break;
   203 					} else {
   204 						/* n...No! Continue copying. */
   205 						invalid_tokens = true;
   206 					}
   207 				}
   208 			}
   209 
   210 			/* Tell the handler */
   211 			if (!ini_listener->ReadINI_NewSection(&ptr[1], invalid_tokens, got_bracket, extra_tokens, &curtok))
   212 			{
   213 				goto event_failed;
   214 			}
   215 		} else {
   216 			char *key_ptr = ptr;
   217 			char *val_ptr = NULL;
   218 			char c;
   219 			size_t first_space = 0;
   220 			bool invalid_tokens = false;
   221 			bool equal_token = false;
   222 			bool quotes = false;
   223 			bool alnum;
   224 			wchar_t wc;
   225 
   226 			for (size_t i=0; i<len; i++)
   227 			{
   228 				c = ptr[i];
   229 				alnum = false;
   230 				/* is this an invalid char? */
   231 				if (c & (1<<7))
   232 				{
   233 					if (mbtowc(&wc, &ptr[i], len-i) != -1)
   234 					{
   235 						alnum = (iswalnum(wc) != 0);
   236 						i += _GetUTF8CharBytes(&ptr[i]) - 1;
   237 					}
   238 				} else {
   239 					alnum = (isalnum(c) != 0) || (g_ini_chartable1[c] != 0);
   240 				}
   241 
   242 				if (!alnum)
   243 				{
   244 					if (g_ws_chartable[c])
   245 					{
   246 						/* if it's a space, keep track of the first occurring space */
   247 						if (!first_space)
   248 						{
   249 							first_space = i;
   250 						}
   251 					} else {
   252 						if (c == '=')
   253 						{
   254 							/* if it's an equal sign, we're done with the key */
   255 							if (first_space)
   256 							{
   257 								/* remove excess whitespace */
   258 								key_ptr[first_space] = '\0';
   259 							} else {
   260 								/* remove the equal sign */
   261 								key_ptr[i] = '\0';
   262 							}
   263 							if (ptr[++i] != '\0')
   264 							{
   265 								/* If this isn't the end, set next pointer */
   266 								val_ptr = &ptr[i];
   267 							}
   268 							equal_token = true;
   269 							break;
   270 						} else {
   271 							/* Mark that we got something invalid! */
   272 							invalid_tokens = true;
   273 							first_space = 0;
   274 						}
   275 					}
   276 				}
   277 			}
   278 
   279 			/* Now we need to parse the value, if any */
   280 			if (val_ptr)
   281 			{
   282 				/* eat up spaces! there shouldn't be any h*/
   283 				while ((*val_ptr != '\0') && g_ws_chartable[*val_ptr] != 0)
   284 				{
   285 					val_ptr++;
   286 				}
   287 				if (*val_ptr == '\0')
   288 				{
   289 					val_ptr = NULL;
   290 					goto skip_value;
   291 				}
   292 				/* Do we have an initial quote? If so, the parsing rules change! */
   293 				if (*val_ptr == '"' && *val_ptr != '\0')
   294 				{
   295 					len = strlen(val_ptr);
   296 					if (val_ptr[len-1] == '"')
   297 					{
   298 						/* Strip quotes! */
   299 						val_ptr[--len] = '\0';
   300 						val_ptr++;
   301 						quotes = true;
   302 					}
   303 				}
   304 			}
   305 skip_value:
   306 			/* We're done! */
   307 			curtok = val_ptr - buffer;
   308 			if (!ini_listener->ReadINI_KeyValue(key_ptr, val_ptr, invalid_tokens, equal_token, quotes, &curtok))
   309 			{
   310 				curtok = 0;
   311 				goto event_failed;
   312 			}
   313 		}
   314 	}
   315 
   316 	if (line)
   317 	{
   318 		*line = curline;
   319 	}
   320 
   321 	fclose(fp);
   322 
   323 	return true;
   324 
   325 event_failed:
   326 	if (line)
   327 	{
   328 		*line = curline;
   329 	}
   330 
   331 	if (col)
   332 	{
   333 		*col = curtok;
   334 	}
   335 
   336 	fclose(fp);
   337 
   338 	return false;
   339 }