1 /*
2  * File Tokeniser/Parser/Whatever
3  * by Jonathon Fowler
4  * Remixed completely by Ken Silverman
5  * See the included license file "BUILDLIC.TXT" for license info.
6  */
7 
8 #include "build.h"
9 #include "scriptfile.h"
10 #include "cache1d.h"
11 #include <math.h>
12 
13 
14 #define ISWS(x) ((x == ' ') || (x == '\t') || (x == '\r') || (x == '\n'))
skipoverws(scriptfile * sf)15 static void skipoverws(scriptfile *sf) { if ((sf->textptr < sf->eof) && (!sf->textptr[0])) sf->textptr++; }
skipovertoken(scriptfile * sf)16 static void skipovertoken(scriptfile *sf) { while ((sf->textptr < sf->eof) && (sf->textptr[0])) sf->textptr++; }
17 
scriptfile_gettoken(scriptfile * sf)18 char *scriptfile_gettoken(scriptfile *sf)
19 {
20 	char *start;
21 
22 	skipoverws(sf);
23 	if (sf->textptr >= sf->eof) return NULL;
24 
25 	start = sf->ltextptr = sf->textptr;
26 	skipovertoken(sf);
27 	return start;
28 }
29 
scriptfile_peektoken(scriptfile * sf)30 char *scriptfile_peektoken(scriptfile *sf)
31 {
32 	scriptfile dupe;
33 
34 	memcpy(&dupe, sf, sizeof(scriptfile));
35 
36 	skipoverws(&dupe);
37 	if (dupe.textptr >= dupe.eof) return NULL;
38 	return dupe.textptr;
39 }
40 
scriptfile_getstring(scriptfile * sf,char ** retst)41 int scriptfile_getstring(scriptfile *sf, char **retst)
42 {
43 	(*retst) = scriptfile_gettoken(sf);
44 	if (*retst == NULL)
45 	{
46 		buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
47 		return(-2);
48 	}
49 	return(0);
50 }
51 
scriptfile_getnumber_radix(scriptfile * sf,int * num,int radix)52 static int scriptfile_getnumber_radix(scriptfile *sf, int *num, int radix)
53 {
54 	skipoverws(sf);
55 	if (sf->textptr >= sf->eof)
56 	{
57 		buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
58 		return -1;
59 	}
60 
61 	while ((sf->textptr[0] == '0') && (sf->textptr[1] >= '0') && (sf->textptr[1] <= '9'))
62 		sf->textptr++; //hack to treat octal numbers like decimal
63 
64 	sf->ltextptr = sf->textptr;
65 	(*num) = strtol((const char *)sf->textptr,&sf->textptr,radix);
66 	if (!ISWS(*sf->textptr) && *sf->textptr) {
67 		char *p = sf->textptr;
68 		skipovertoken(sf);
69 		buildprintf("Error on line %s:%d: expecting int, got \"%s\"\n",sf->filename,scriptfile_getlinum(sf,sf->ltextptr),p);
70 		return -2;
71 	}
72 	return 0;
73 }
74 
scriptfile_getnumber(scriptfile * sf,int * num)75 int scriptfile_getnumber(scriptfile *sf, int *num)
76 {
77 	return scriptfile_getnumber_radix(sf, num, 0);
78 }
79 
scriptfile_gethex(scriptfile * sf,int * num)80 int scriptfile_gethex(scriptfile *sf, int *num)
81 {
82 	return scriptfile_getnumber_radix(sf, num, 16);
83 }
84 
parsedouble(char * ptr,char ** end)85 static double parsedouble(char *ptr, char **end)
86 {
87 	int beforedecimal = 1, negative = 0, dig;
88 	int exposgn = 0, expo = 0;
89 	double num = 0.0, decpl = 0.1;
90 	char *p;
91 
92 	p = ptr;
93 	if (*p == '-') negative = 1, p++;
94 	else if (*p == '+') p++;
95 	for (;; p++) {
96 		if (*p >= '0' && *p <= '9') {
97 			dig = *p - '0';
98 			if (beforedecimal) num = num * 10.0 + dig;
99 			else if (exposgn) expo = expo*10 + dig;
100 			else {
101 				num += (double)dig * decpl;
102 				decpl /= 10.0;
103 			}
104 		} else if (*p == '.') {
105 			if (beforedecimal) beforedecimal = 0;
106 			else break;
107 		} else if ((*p == 'E') || (*p == 'e')) {
108 			exposgn = 1;
109 			if (p[1] == '-') { exposgn = -1; p++; }
110 			else if (p[1] == '+') p++;
111 		} else break;
112 	}
113 
114 	if (end) *end = p;
115 	if (exposgn) num *= pow(10.0,(double)(expo*exposgn));
116 	return negative ? -num : num;
117 }
118 
scriptfile_getdouble(scriptfile * sf,double * num)119 int scriptfile_getdouble(scriptfile *sf, double *num)
120 {
121 	skipoverws(sf);
122 	if (sf->textptr >= sf->eof)
123 	{
124 		buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
125 		return -1;
126 	}
127 
128 	sf->ltextptr = sf->textptr;
129 
130 	// On Linux, locale settings interfere with interpreting x.y format numbers
131 	//(*num) = strtod((const char *)sf->textptr,&sf->textptr);
132 	(*num) = parsedouble(sf->textptr, &sf->textptr);
133 
134 	if (!ISWS(*sf->textptr) && *sf->textptr) {
135 		char *p = sf->textptr;
136 		skipovertoken(sf);
137 		buildprintf("Error on line %s:%d: expecting float, got \"%s\"\n",sf->filename,scriptfile_getlinum(sf,sf->ltextptr),p);
138 		return -2;
139 	}
140 	return 0;
141 }
142 
scriptfile_getsymbol(scriptfile * sf,int * num)143 int scriptfile_getsymbol(scriptfile *sf, int *num)
144 {
145 	char *t, *e;
146 	int v;
147 
148 	t = scriptfile_gettoken(sf);
149 	if (!t) return -1;
150 
151 	v = Bstrtol(t, &e, 10);
152 	if (*e) {
153 		// looks like a string, so find it in the symbol table
154 		if (scriptfile_getsymbolvalue(t, num)) return 0;
155 		buildprintf("Error on line %s:%d: expecting symbol, got \"%s\"\n",sf->filename,scriptfile_getlinum(sf,sf->ltextptr),t);
156 		return -2;   // not found
157 	}
158 
159 	*num = v;
160 	return 0;
161 }
162 
scriptfile_getbraces(scriptfile * sf,char ** braceend)163 int scriptfile_getbraces(scriptfile *sf, char **braceend)
164 {
165 	int bracecnt;
166 	char *bracestart;
167 
168 	skipoverws(sf);
169 	if (sf->textptr >= sf->eof)
170 	{
171 		buildprintf("Error on line %s:%d: unexpected eof\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
172 		return -1;
173 	}
174 
175 	if (sf->textptr[0] != '{') {
176 		buildprintf("Error on line %s:%d: expecting '{'\n",sf->filename,scriptfile_getlinum(sf,sf->textptr));
177 		return -1;
178 	}
179 	bracestart = ++sf->textptr; bracecnt = 1;
180 	while (1)
181 	{
182 		if (sf->textptr >= sf->eof) return(0);
183 		if (sf->textptr[0] == '{') bracecnt++;
184 		if (sf->textptr[0] == '}') { bracecnt--; if (!bracecnt) break; }
185 		sf->textptr++;
186 	}
187 	(*braceend) = sf->textptr;
188 	sf->textptr = bracestart;
189 	return 0;
190 }
191 
192 
scriptfile_getlinum(scriptfile * sf,char * ptr)193 int scriptfile_getlinum (scriptfile *sf, char *ptr)
194 {
195 	int i, stp, ind;
196 
197 	//for(i=0;i<sf->linenum;i++) if (sf->lineoffs[i] >= ind) return(i+1); //brute force algo
198 
199 	ind = ((intptr_t)ptr) - ((intptr_t)sf->textbuf);
200 
201 	for(stp=1;stp+stp<sf->linenum;stp+=stp); //stp = highest power of 2 less than sf->linenum
202 	for(i=0;stp;stp>>=1)
203 		if ((i+stp < sf->linenum) && (sf->lineoffs[i+stp] < ind)) i += stp;
204 	return(i+1); //i = index to highest lineoffs which is less than ind; convert to 1-based line numbers
205 }
206 
scriptfile_preparse(scriptfile * sf,char * tx,int flen)207 void scriptfile_preparse (scriptfile *sf, char *tx, int flen)
208 {
209 	int i, cr, numcr, nflen, ws, cs, inquote;
210 
211 		//Count number of lines
212 	numcr = 1;
213 	for(i=0;i<flen;i++)
214 	{
215 			//detect all 4 types of carriage return (\r, \n, \r\n, \n\r :)
216 		cr=0;if (tx[i] == '\r') { i += (tx[i+1] == '\n'); cr = 1; }
217 		else if (tx[i] == '\n') { i += (tx[i+1] == '\r'); cr = 1; }
218 		if (cr) { numcr++; continue; }
219 	}
220 
221 	sf->linenum = numcr;
222 	sf->lineoffs = (int *)malloc(sf->linenum*sizeof(int));
223 
224 		//Preprocess file for comments (// and /*...*/, and convert all whitespace to single spaces)
225 	nflen = 0; ws = 0; cs = 0; numcr = 0; inquote = 0;
226 	for(i=0;i<flen;i++)
227 	{
228 			//detect all 4 types of carriage return (\r, \n, \r\n, \n\r :)
229 		cr=0;if (tx[i] == '\r') { i += (tx[i+1] == '\n'); cr = 1; }
230 		else if (tx[i] == '\n') { i += (tx[i+1] == '\r'); cr = 1; }
231 		if (cr)
232 		{
233 				//Remember line numbers by storing the byte index at the start of each line
234 				//Line numbers can be retrieved by doing a binary search on the byte index :)
235 			sf->lineoffs[numcr++] = nflen;
236 			if (cs == 1) cs = 0;
237 			ws = 1; continue; //strip CR/LF
238 		}
239 
240 		if ((!inquote) && ((tx[i] == ' ') || (tx[i] == '\t'))) { ws = 1; continue; } //strip Space/Tab
241 		if ((tx[i] == ';') && (!cs)) cs = 1;	// ; comment
242 		if ((tx[i] == '/') && (tx[i+1] == '/') && (!cs)) cs = 1;
243 		if ((tx[i] == '/') && (tx[i+1] == '*') && (!cs)) { ws = 1; cs = 2; }
244 		if ((tx[i] == '*') && (tx[i+1] == '/') && (cs == 2)) { cs = 0; i++; continue; }
245 		if (cs) continue;
246 
247 		if (ws) { tx[nflen++] = 0; ws = 0; }
248 
249 			//quotes inside strings: \"
250 		if ((tx[i] == '\\') && (tx[i+1] == '\"')) { i++; tx[nflen++] = '\"'; continue; }
251 		if (tx[i] == '\"') { inquote ^= 1; continue; }
252 		tx[nflen++] = tx[i];
253 	}
254 	tx[nflen++] = 0; sf->lineoffs[numcr] = nflen;
255 	tx[nflen++] = 0;
256 
257 #if 0
258 		//for debugging only:
259 	printf("pre-parsed file:flen=%d,nflen=%d\n",flen,nflen);
260 	for(i=0;i<nflen;i++) { if (tx[i] < 32) printf("_"); else printf("%c",tx[i]); }
261 	printf("[eof]\nnumlines=%d\n",sf->linenum);
262 	for(i=0;i<sf->linenum;i++) printf("line %d = byte %d\n",i,sf->lineoffs[i]);
263 #endif
264 	flen = nflen;
265 
266 	sf->textbuf = sf->textptr = tx;
267 	sf->textlength = nflen;
268 	sf->eof = &sf->textbuf[nflen-1];
269 }
270 
scriptfile_fromfile(const char * fn)271 scriptfile *scriptfile_fromfile(const char *fn)
272 {
273 	int fp;
274 	scriptfile *sf;
275 	char *tx;
276 	unsigned int flen;
277 
278 	fp = kopen4load(fn,0);
279 	if (fp<0) return NULL;
280 
281 	flen = kfilelength(fp);
282 	tx = (char *) malloc(flen + 2);
283 	if (!tx) {
284 		kclose(fp);
285 		return NULL;
286 	}
287 
288 	sf = (scriptfile*) malloc(sizeof(scriptfile));
289 	if (!sf) {
290 		kclose(fp);
291 		free(tx);
292 		return NULL;
293 	}
294 
295 	kread(fp, tx, flen);
296 	tx[flen] = tx[flen+1] = 0;
297 
298 	kclose(fp);
299 
300 	scriptfile_preparse(sf,tx,flen);
301 	sf->filename = strdup(fn);
302 
303 	return sf;
304 }
305 
scriptfile_fromstring(const char * string)306 scriptfile *scriptfile_fromstring(const char *string)
307 {
308 	scriptfile *sf;
309 	char *tx;
310 	unsigned int flen;
311 
312 	if (!string) return NULL;
313 
314 	flen = strlen(string);
315 
316 	tx = (char *) malloc(flen + 2);
317 	if (!tx) return NULL;
318 
319 	sf = (scriptfile*) malloc(sizeof(scriptfile));
320 	if (!sf) {
321 		free(tx);
322 		return NULL;
323 	}
324 
325 	memcpy(tx, string, flen);
326 	tx[flen] = tx[flen+1] = 0;
327 
328 	scriptfile_preparse(sf,tx,flen);
329 	sf->filename = NULL;
330 
331 	return sf;
332 }
333 
scriptfile_close(scriptfile * sf)334 void scriptfile_close(scriptfile *sf)
335 {
336 	if (!sf) return;
337 	if (sf->lineoffs) free(sf->lineoffs);
338 	if (sf->textbuf) free(sf->textbuf);
339 	if (sf->filename) free(sf->filename);
340 	sf->textbuf = NULL;
341 	sf->filename = NULL;
342 	free(sf);
343 }
344 
scriptfile_eof(scriptfile * sf)345 int scriptfile_eof(scriptfile *sf)
346 {
347 	skipoverws(sf);
348 	if (sf->textptr >= sf->eof) return 1;
349 	return 0;
350 }
351 
352 #define SYMBTABSTARTSIZE 256
353 static int symbtablength=0, symbtaballoclength=0;
354 static char *symbtab = NULL;
355 
getsymbtabspace(int reqd)356 static char * getsymbtabspace(int reqd)
357 {
358 	char *pos,*np;
359 	int i;
360 
361 	if (symbtablength + reqd > symbtaballoclength)
362 	{
363 		for(i=max(symbtaballoclength,SYMBTABSTARTSIZE);symbtablength+reqd>i;i<<=1);
364 		np = (char *)realloc(symbtab, i); if (!np) return NULL;
365 		symbtab = np; symbtaballoclength = i;
366 	}
367 
368 	pos = &symbtab[symbtablength];
369 	symbtablength += reqd;
370 	return pos;
371 }
372 
scriptfile_getsymbolvalue(const char * name,int * val)373 int scriptfile_getsymbolvalue(const char *name, int *val)
374 {
375 	char *scanner = symbtab;
376 
377 	if (!symbtab) return 0;
378 	while (scanner - symbtab < symbtablength) {
379 		if (!Bstrcasecmp(name, scanner)) {
380 			*val = *(int*)(scanner + strlen(scanner) + 1);
381 			return 1;
382 		}
383 
384 		scanner += strlen(scanner) + 1 + sizeof(int);
385 	}
386 
387 	return 0;
388 }
389 
scriptfile_addsymbolvalue(const char * name,int val)390 int scriptfile_addsymbolvalue(const char *name, int val)
391 {
392 	int x;
393 	char *sp;
394 	//if (scriptfile_getsymbolvalue(name, &x)) return -1;   // already exists
395 
396 	if (symbtab) {
397 		char *scanner = symbtab;
398 		while (scanner - symbtab < symbtablength) {
399 			if (!Bstrcasecmp(name, scanner)) {
400 				*(int*)(scanner + strlen(scanner) + 1) = val;
401 				return 1;
402 			}
403 
404 			scanner += strlen(scanner) + 1 + sizeof(int);
405 		}
406 	}
407 
408 	sp = getsymbtabspace(strlen(name) + 1 + sizeof(int));
409 	if (!sp) return 0;
410 	strcpy(sp, name);
411 	sp += strlen(name)+1;
412 	*(int*)sp = val;
413 	return 1;   // added
414 }
415 
scriptfile_clearsymbols(void)416 void scriptfile_clearsymbols(void)
417 {
418 	if (symbtab) free(symbtab);
419 	symbtab = NULL;
420 	symbtablength = 0;
421 	symbtaballoclength = 0;
422 }
423