1 /*    cfte.cpp
2  *
3  *    Copyright (c) 1994-1997, Marko Macek
4  *
5  *    You may distribute under the terms of either the GNU General Public
6  *    License or the Artistic License, as specified in the README file.
7  *
8  */
9 
10 #include "c_fconfig.h"
11 #include "c_hilit.h"
12 #include "c_mode.h"
13 #include "console.h"
14 #include "ftever.h"
15 #include "s_files.h"
16 #include "s_string.h"
17 #include "sysdep.h"
18 
19 #include "c_commands.h"
20 #include "c_cmdtab.h"
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <errno.h>
25 #include <stdarg.h>
26 #include <ctype.h>
27 #include <fcntl.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 
31 
32 #define slen(s) ((s) ? (strlen(s) + 1) : 0)
33 
34 struct ExMacroCFTE {
35     char *Name;
36 };
37 
38 static unsigned CMacrosCFTE = 0;
39 static ExMacroCFTE *MacrosCFTE = 0;
40 
41 static FILE *output = 0;
42 static int lntotal = 0;
43 static long offset = -1;
44 static long pos = 0;
45 static char XTarget[MAXPATH] = "";
46 static char StartDir[MAXPATH] = "";
47 static bool preprocess_only = false;
48 
49 struct CurPos {
50     off_t sz;
51     char *a;
52     char *c;
53     char *z;
54     int line;
55     const char *name; // filename
56 };
57 
cleanup(int xerrno)58 static void cleanup(int xerrno) {
59     if (output)
60         fclose(output);
61     if (XTarget[0] != 0)
62         unlink(XTarget);
63     exit(xerrno);
64 }
65 
Fail(CurPos & cp,const char * s,...)66 static void Fail(CurPos &cp, const char *s, ...) {
67     va_list ap;
68     char msgbuf[1024];
69 
70     va_start(ap, s);
71     vsprintf(msgbuf, s, ap);
72     va_end(ap);
73 
74     fprintf(stderr, "%s:%d: Error: %s\n", cp.name, cp.line, msgbuf);
75     cleanup(1);
76 }
77 
78 static int LoadFile(const char *WhereName, const char *CfgName, int Level = 1);
79 static void DefineWord(const char *w);
80 
PutObject(CurPos & cp,int xtag,size_t xlen,void * obj)81 static void PutObject(CurPos &cp, int xtag, size_t xlen, void *obj) {
82     unsigned char tag = (unsigned char)xtag;
83     unsigned short len = (unsigned short)xlen;
84     unsigned char l[2];
85 
86     if (preprocess_only == false) {
87 	l[0] = (char)len;
88 	l[1] = (char)(len >> 8);
89 
90 	if (fwrite(&tag, 1, 1, output) != 1
91 	    || fwrite(l, 2, 1, output) != 1
92 	    || fwrite(obj, 1, len, output) != len)
93 	    Fail(cp, "Disk full!");
94     }
95     pos += 1 + 2 + len;
96     if (offset != -1 && pos >= offset)
97         Fail(cp, "Error location found at %ld", pos);
98 }
99 
PutNull(CurPos & cp,int xtag)100 static void PutNull(CurPos &cp, int xtag) {
101     PutObject(cp, xtag, 0, 0);
102 }
103 
PutString(CurPos & cp,int xtag,char * str)104 static void PutString(CurPos &cp, int xtag, char *str) {
105     PutObject(cp, xtag, slen(str), str);
106 }
107 
PutNumber(CurPos & cp,int xtag,long num)108 static void PutNumber(CurPos &cp, int xtag, long num) {
109     unsigned long l = num;
110     unsigned char b[4];
111 
112     b[0] = (unsigned char)(l & 0xFF);
113     b[1] = (unsigned char)((l >> 8) & 0xFF);
114     b[2] = (unsigned char)((l >> 16) & 0xFF);
115     b[3] = (unsigned char)((l >> 24) & 0xFF);
116     PutObject(cp, xtag, 4, b);
117 }
118 
main(int argc,char ** argv)119 int main(int argc, char **argv) {
120     char Source[MAXPATH];
121     char Target[MAXPATH];
122     unsigned char b[4];
123     unsigned long l;
124     int n = 0;
125 
126     fprintf(stderr, PROG_CFTE " " VERSION "\n" COPYRIGHT "\n");
127     if (argc < 2 || argc > 5) {
128         fprintf(stderr, "Usage: " PROG_CFTE " [-o<offset>] [-p[reprocess]] "
129 #ifndef UNIX
130                 "config/"
131 #endif
132                 "main.fte [fte-new.cnf]\n");
133         exit(1);
134     }
135 
136     DefineWord("OS_"
137 #if defined(OS2)
138                "OS2"
139 #elif defined(UNIX)
140                "UNIX"
141 #elif defined(NT)
142                "NT"
143 #elif defined(DOSP32)
144                "DOS32"
145 #endif
146               );
147 
148     // setup defaults
149     strcpy(Source, "");
150     strcpy(Target, "fte-new.cnf");
151     preprocess_only = false;
152     offset = -1;
153 
154     // parse arguments
155     for (int i = 1; i < argc; i++) {
156 	if (argv[i][0] == '-') {
157 	    if ((strcmp(argv[i], "-p") == 0) || (strcmp(argv[i], "-preprocess") == 0))
158 		preprocess_only = true;
159 	    else if (strncmp(argv[i], "-o", 2) == 0) {
160 		char *p;
161 
162 		p = argv[i];
163 		p += 2;
164 		offset = atol(p);
165 	    } else {
166 		fprintf(stderr, "Invalid option '%s'\n", argv[i]);
167 		exit(1);
168 	    }
169 	} else {
170 	    switch(n) {
171 	    case 0:
172 		strlcpy(Source, argv[i], sizeof(Source));
173 		break;
174 
175 	    case 1:
176                 strlcpy(Target, argv[i], sizeof(Target));
177 		break;
178 
179 	    default:
180 		fprintf(stderr, "Invalid option '%s'\n", argv[i]);
181 		exit(1);
182 	    }
183             n++;
184 	}
185     }
186 
187     if (n == 0) {
188 	fprintf(stderr, "No configuration file specified\n");
189 	exit(1);
190     }
191 
192     JustDirectory(Target, XTarget, sizeof(XTarget));
193     Slash(XTarget, 1);
194 
195     if (preprocess_only == false) {
196 	sprintf(XTarget + strlen(XTarget), "cfte%ld.tmp", (long)getpid());
197 	output = fopen(XTarget, "wb");
198 	if (output == 0) {
199 	    fprintf(stderr, "Cannot create '%s', errno=%d!\n", XTarget, errno);
200 	    cleanup(1);
201 	}
202 
203 	b[0] = b[1] = b[2] = b[3] = 0;
204 
205 	if (fwrite(b, sizeof(b), 1, output) != 1) {
206 	    fprintf(stderr, "Disk full!");
207 	    cleanup(1);
208 	}
209 
210 	l = VERNUM;
211 
212 	b[0] = (unsigned char)(l & 0xFF);
213 	b[1] = (unsigned char)((l >> 8) & 0xFF);
214 	b[2] = (unsigned char)((l >> 16) & 0xFF);
215 	b[3] = (unsigned char)((l >> 24) & 0xFF);
216 
217 	if (fwrite(b, 4, 1, output) != 1) {
218 	    fprintf(stderr, "Disk full!");
219 	    cleanup(1);
220 	}
221 	pos = 2 * 4;
222 
223 	fprintf(stderr, "Compiling to '%s'\n", Target);
224     } else
225     {
226         pos = 2 * 4;
227     }
228     /*{
229         char PrevDir[MAXPATH];
230         sprintf(PrevDir, "%s/..", Target);
231         ExpandPath(PrevDir, StartDir);
232         Slash(StartDir, 1);
233     }*/
234 
235     ExpandPath("."
236 #ifdef UNIX
237                "."
238 #endif
239                , StartDir, sizeof(StartDir));
240     Slash(StartDir, 1);
241 
242     if (preprocess_only == false) {
243         CurPos cp;
244         char FSource[MAXPATH];
245 
246         if (ExpandPath(Source, FSource, sizeof(FSource)) != 0) {
247             fprintf(stderr, "Could not expand path %s\n", Source);
248             exit(1);
249         }
250 
251         cp.sz = 0;
252         cp.c = 0;
253         cp.a = cp.c = 0;
254         cp.z = cp.a + cp.sz;
255         cp.line = 0;
256         cp.name = "<cfte-start>";
257 
258         PutString(cp, CF_STRING, FSource);
259     }
260 
261     if (LoadFile(StartDir, Source, 0) != 0) {
262         fprintf(stderr, "\nCompile failed\n");
263         cleanup(1);
264     }
265 
266     if (preprocess_only == true)
267         return 0;
268 
269     l = CONFIG_ID;
270     b[0] = (unsigned char)(l & 0xFF);
271     b[1] = (unsigned char)((l >> 8) & 0xFF);
272     b[2] = (unsigned char)((l >> 16) & 0xFF);
273     b[3] = (unsigned char)((l >> 24) & 0xFF);
274     fseek(output, 0, SEEK_SET);
275     fwrite(b, 4, 1, output);
276     fclose(output);
277 
278     if (unlink(Target) != 0 && errno != ENOENT) {
279         fprintf(stderr, "Remove of '%s' failed, result left in %s, errno=%d\n",
280                 Target, XTarget, errno);
281         exit(1);
282     }
283 
284     if (rename(XTarget, Target) != 0) {
285         fprintf(stderr, "Rename of '%s' to '%s' failed, errno=%d\n",
286                 XTarget, Target, errno);
287         exit(1);
288     }
289 
290     fprintf(stderr, "\nDone.\n");
291     return 0;
292 }
293 
294 #define MODE_BFI(x) { #x, BFI_##x }
295 #define MODE_BFS(x) { #x, BFS_##x }
296 #define MODE_FLG(x) { #x, FLAG_##x }
297 #define EVENT_FLG(x) { #x, EM_##x }
298 #define COLORIZE_FLG(x) { #x, COL_##x }
299 #define HILIT_CLR(x) { #x, CLR_##x }
300 
301 struct OrdLookup {
302     const char *Name;
303     int num;
304 };
305 
306 static const OrdLookup mode_num[] = {
307 MODE_BFI(AutoIndent),
308 MODE_BFI(Insert),
309 MODE_BFI(DrawOn),
310 MODE_BFI(HilitOn),
311 MODE_BFI(ExpandTabs),
312 MODE_BFI(Trim),
313 MODE_BFI(TabSize),
314 MODE_BFI(ShowTabs),
315 MODE_BFI(LineChar),
316 MODE_BFI(StripChar),
317 MODE_BFI(AddLF),
318 MODE_BFI(AddCR),
319 MODE_BFI(ForceNewLine),
320 MODE_BFI(HardMode),
321 MODE_BFI(Undo),
322 MODE_BFI(ReadOnly),
323 MODE_BFI(AutoSave),
324 MODE_BFI(KeepBackups),
325 MODE_BFI(LoadMargin),
326 MODE_BFI(UndoLimit),
327 MODE_BFI(MatchCase),
328 MODE_BFI(BackSpKillTab),
329 MODE_BFI(DeleteKillTab),
330 MODE_BFI(BackSpUnindents),
331 MODE_BFI(SpaceTabs),
332 MODE_BFI(IndentWithTabs),
333 MODE_BFI(LeftMargin),
334 MODE_BFI(RightMargin),
335 MODE_BFI(SeeThruSel),
336 MODE_BFI(WordWrap),
337 MODE_BFI(ShowMarkers),
338 MODE_BFI(CursorThroughTabs),
339 MODE_BFI(SaveFolds),
340 MODE_BFI(MultiLineHilit),
341 MODE_BFI(AutoHilitParen),
342 MODE_BFI(Abbreviations),
343 MODE_BFI(BackSpKillBlock),
344 MODE_BFI(DeleteKillBlock),
345 MODE_BFI(PersistentBlocks),
346 MODE_BFI(InsertKillBlock),
347 MODE_BFI(UndoMoves),
348 MODE_BFI(DetectLineSep),
349 MODE_BFI(TrimOnSave),
350 MODE_BFI(SaveBookmarks),
351 MODE_BFI(HilitTags),
352 MODE_BFI(ShowBookmarks),
353 MODE_BFI(MakeBackups),
354 { 0, 0 },
355 };
356 
357 static const OrdLookup mode_string[] = {
358 MODE_BFI(Colorizer),
359 MODE_BFI(IndentMode),
360 MODE_BFS(RoutineRegexp),
361 MODE_BFS(DefFindOpt),
362 MODE_BFS(DefFindReplaceOpt),
363 MODE_BFS(CommentStart),
364 MODE_BFS(CommentEnd),
365 MODE_BFS(WordChars),
366 MODE_BFS(CapitalChars),
367 MODE_BFS(FileNameRx),
368 MODE_BFS(FirstLineRx),
369 MODE_BFS(CompileCommand),
370 MODE_BFI(EventMap),
371 { 0, 0 },
372 };
373 
374 static const OrdLookup global_num[] = {
375 #ifdef CONFIG_INDENT_C
376 MODE_FLG(C_Indent),
377 MODE_FLG(C_BraceOfs),
378 MODE_FLG(C_CaseOfs),
379 MODE_FLG(C_CaseDelta),
380 MODE_FLG(C_ClassOfs),
381 MODE_FLG(C_ClassDelta),
382 MODE_FLG(C_ColonOfs),
383 MODE_FLG(C_CommentOfs),
384 MODE_FLG(C_CommentDelta),
385 MODE_FLG(C_FirstLevelWidth),
386 MODE_FLG(C_FirstLevelIndent),
387 MODE_FLG(C_Continuation),
388 MODE_FLG(C_ParenDelta),
389 MODE_FLG(FunctionUsesContinuation),
390 #endif
391 #ifdef CONFIG_INDENT_REXX
392 MODE_FLG(REXX_Indent),
393 MODE_FLG(REXX_Do_Offset),
394 #endif
395 MODE_FLG(ScreenSizeX),
396 MODE_FLG(ScreenSizeY),
397 MODE_FLG(CursorInsertStart),
398 MODE_FLG(CursorInsertEnd),
399 MODE_FLG(CursorOverStart),
400 MODE_FLG(CursorOverEnd),
401 MODE_FLG(SysClipboard),
402 MODE_FLG(OpenAfterClose),
403 MODE_FLG(ShowVScroll),
404 MODE_FLG(ShowHScroll),
405 MODE_FLG(ScrollBarWidth),
406 MODE_FLG(SelectPathname),
407 MODE_FLG(ShowToolBar),
408 MODE_FLG(ShowMenuBar),
409 MODE_FLG(KeepHistory),
410 MODE_FLG(LoadDesktopOnEntry),
411 MODE_FLG(SaveDesktopOnExit),
412 MODE_FLG(KeepMessages),
413 MODE_FLG(ScrollBorderX),
414 MODE_FLG(ScrollBorderY),
415 MODE_FLG(ScrollJumpX),
416 MODE_FLG(ScrollJumpY),
417 MODE_FLG(GUIDialogs),
418 MODE_FLG(PMDisableAccel),
419 MODE_FLG(SevenBit),
420 MODE_FLG(WeirdScroll),
421 MODE_FLG(LoadDesktopMode),
422 MODE_FLG(IgnoreBufferList),
423 MODE_FLG(ReassignModelIds),
424 MODE_FLG(RecheckReadOnly),
425 MODE_FLG(CursorBlink),
426 MODE_FLG(ShowTildeFilesInDirList),
427 { 0, 0 },
428 };
429 
430 static const OrdLookup global_string[] = {
431 MODE_FLG(DefaultModeName),
432 MODE_FLG(CompletionFilter),
433 MODE_FLG(PrintDevice),
434 MODE_FLG(CompileCommand),
435 MODE_FLG(WindowFont),
436 MODE_FLG(HelpCommand),
437 MODE_FLG(GUICharacters),
438 MODE_FLG(CvsCommand),
439 MODE_FLG(CvsLogMode),
440 MODE_FLG(SvnCommand),
441 MODE_FLG(SvnLogMode),
442 MODE_FLG(XShellCommand),
443 MODE_FLG(RGBColor),
444 { 0, 0 },
445 };
446 
447 static const OrdLookup event_string[] = {
448 EVENT_FLG(MainMenu),
449 EVENT_FLG(LocalMenu),
450 { 0, 0 },
451 };
452 
453 static const OrdLookup colorize_string[] = {
454 COLORIZE_FLG(SyntaxParser),
455 { 0, 0 },
456 };
457 
458 static const OrdLookup hilit_colors[] = {
459 HILIT_CLR(Normal),
460 HILIT_CLR(Keyword),
461 HILIT_CLR(String),
462 HILIT_CLR(Comment),
463 HILIT_CLR(CPreprocessor),
464 HILIT_CLR(Regexp),
465 HILIT_CLR(Header),
466 HILIT_CLR(Quotes),
467 HILIT_CLR(Number),
468 HILIT_CLR(HexNumber),
469 HILIT_CLR(OctalNumber),
470 HILIT_CLR(FloatNumber),
471 HILIT_CLR(Function),
472 HILIT_CLR(Command),
473 HILIT_CLR(Tag),
474 HILIT_CLR(Punctuation),
475 HILIT_CLR(New),
476 HILIT_CLR(Old),
477 HILIT_CLR(Changed),
478 HILIT_CLR(Control),
479 HILIT_CLR(Separator),
480 HILIT_CLR(Variable),
481 HILIT_CLR(Symbol),
482 HILIT_CLR(Directive),
483 HILIT_CLR(Label),
484 HILIT_CLR(Special),
485 HILIT_CLR(QuoteDelim),
486 HILIT_CLR(RegexpDelim),
487 { 0, 0 },
488 };
489 
Lookup(const OrdLookup * where,char * what)490 static int Lookup(const OrdLookup *where, char *what) {
491     int i;
492 
493     for (i = 0; where[i].Name != 0; i++) {
494         if (strcmp(what, where[i].Name) == 0)
495             return where[i].num;
496     }
497 //    fprintf(stderr, "\nBad name: %s (i = %d)\n", what, i);
498     return -1;
499 }
500 
501 #define P_EOF         0  // end of file
502 #define P_SYNTAX      1  // unknown
503 #define P_WORD        2  // a-zA-Z_
504 #define P_NUMBER      3  // 0-9
505 #define P_STRING      4  // "'`
506 #define P_ASSIGN      5  // =
507 #define P_EOS         6  // ;
508 #define P_KEYSPEC     7  // []
509 #define P_OPENBRACE   8  // {
510 #define P_CLOSEBRACE  9  // }
511 #define P_COLON      10  // :
512 #define P_COMMA      11  // ,
513 #define P_QUEST      12
514 #define P_VARIABLE   13  // $
515 #define P_DOT        14  // . (concat)
516 
517 #define K_UNKNOWN     0
518 #define K_MODE        1
519 #define K_KEY         2
520 #define K_COLOR       3
521 #define K_KEYWORD     4
522 #define K_OBJECT      5
523 #define K_MENU        6
524 #define K_ITEM        7
525 #define K_SUBMENU     8
526 #define K_COMPILERX   9
527 #define K_EXTERN     10
528 #define K_INCLUDE    11
529 #define K_SUB        12
530 #define K_EVENTMAP   13
531 #define K_COLORIZE   14
532 #define K_ABBREV     15
533 #define K_HSTATE     16
534 #define K_HTRANS     17
535 #define K_HWORDS     18
536 #define K_SUBMENUCOND 19
537 #define K_HWTYPE     20
538 #define K_COLPALETTE 21
539 #define K_CVSIGNRX   22
540 #define K_SVNIGNRX   23
541 
542 typedef char Word[64];
543 
544 static const OrdLookup CfgKW[] = {
545     { "mode", K_MODE },
546     { "eventmap", K_EVENTMAP },
547     { "key", K_KEY },
548     { "color", K_COLOR },
549     { "color_palette", K_COLPALETTE },
550     { "keyword", K_KEYWORD },
551     { "object", K_OBJECT },
552     { "menu", K_MENU },
553     { "item", K_ITEM },
554     { "submenu", K_SUBMENU },
555     { "CompileRx", K_COMPILERX },
556     { "extern", K_EXTERN },
557     { "include", K_INCLUDE },
558     { "sub", K_SUB },
559     { "colorize", K_COLORIZE },
560     { "abbrev", K_ABBREV },
561     { "h_state", K_HSTATE },
562     { "h_trans", K_HTRANS },
563     { "h_words", K_HWORDS },
564     { "h_wtype", K_HWTYPE },
565     { "submenucond", K_SUBMENUCOND },
566     { "CvsIgnoreRx", K_CVSIGNRX },
567     { "SvnIgnoreRx", K_SVNIGNRX },
568     { 0, 0 },
569 };
570 
571 static const OrdLookup CfgVar[] = {
572     { "FilePath", mvFilePath },
573     { "FileName", mvFileName },
574     { "FileDirectory", mvFileDirectory },
575     { "FileBaseName", mvFileBaseName },
576     { "FileExtension", mvFileExtension },
577     { "CurDirectory", mvCurDirectory },
578     { "CurRow", mvCurRow, },
579     { "CurCol", mvCurCol },
580     { "Char", mvChar },
581     { "Word", mvWord },
582     { "Line", mvLine },
583     { "FTEVer", mvFTEVer },
584     { 0, 0 },
585 };
586 
587 static char **words = 0;
588 static unsigned int wordCount = 0;
589 
DefinedWord(const char * w)590 static int DefinedWord(const char *w) {
591     if (words == 0 || wordCount == 0)
592         return 0;
593     for (unsigned int i = 0; i < wordCount; i++)
594         if (strcmp(w, words[i]) == 0)
595             return 1;
596     return 0;
597 }
598 
DefineWord(const char * w)599 static void DefineWord(const char *w) {
600     if (!w || !w[0])
601         return ;
602     if (!DefinedWord(w)) {
603         words = (char **)realloc(words, sizeof (char *) * (wordCount + 1));
604         assert(words != 0);
605         words[wordCount] = strdup(w);
606         assert(words[wordCount] != 0);
607         wordCount++;
608     }
609 }
610 
611 static int colorCount;
612 static struct _color {
613     char *colorName;
614     char *colorValue;
615 } *colors;
616 
DefineColor(char * name,char * value)617 static int DefineColor(char *name, char *value) {
618     if (!name || !value)
619         return 0;
620     colors = (struct _color *)realloc(colors, sizeof (struct _color) * (colorCount + 1));
621     assert(colors != 0);
622     colors[colorCount].colorName = strdup(name);
623     colors[colorCount].colorValue = strdup(value);
624     assert(colors != NULL);
625     assert(colors[colorCount].colorName != 0);
626     assert(colors[colorCount].colorValue != 0);
627     colorCount++;
628     return 1;
629 }
630 
DefinedColor(char * name)631 static char *DefinedColor(char *name) {
632     if (colors == 0 || colorCount == 0)
633         return 0;
634     for (int i = 0; i < colorCount; i++)
635         if (strcmp(name, colors[i].colorName) == 0)
636             return colors[i].colorValue;
637     return 0;
638 }
639 
GetColor(CurPos & cp,char * name)640 static char *GetColor(CurPos &cp, char *name) {
641     char *c;
642     static char color[4];
643 
644     // add support for fore:back and remove it from fte itself
645     if ((c = strchr(name, ' ')) != NULL) {
646     } else if ((c = strchr(name, ':')) != NULL) {
647         char clr[4];
648         *c++ = 0;
649         clr[0] = GetColor(cp, name)[0];
650         clr[1] = ' ';
651         clr[2] = GetColor(cp, c)[2];
652         clr[3] = 0;
653 
654         memcpy(color, clr, sizeof(color));
655         name = color;
656     } else {
657         char *p = DefinedColor(name);
658         if (!p)
659             Fail(cp, "Unknown symbolic color %s", name);
660         name = p;
661     }
662 
663     if (!isxdigit(name[0]) && name[1] != ' ' &&
664         !isxdigit(name[2]) && name[3] != 0)
665         Fail(cp, "Malformed color specification: %s", name);
666 
667     return name;
668 }
669 
GetWord(CurPos & cp,char * w)670 static int GetWord(CurPos &cp, char *w) {
671     char *p = w;
672     int len = 0;
673 
674     while (len < int(sizeof(Word)) && cp.c < cp.z &&
675            ((*cp.c >= 'a' && *cp.c <= 'z') ||
676             (*cp.c >= 'A' && *cp.c <= 'Z') ||
677             (*cp.c >= '0' && *cp.c <= '9') ||
678             (*cp.c == '_'))) {
679         *p++ = *cp.c++;
680         len++;
681     }
682     if (len == sizeof(Word))
683         return -1;
684     *p = 0;
685 
686     return 0;
687 }
688 
689 
Parse(CurPos & cp)690 static int Parse(CurPos &cp) {
691     while (cp.c < cp.z) {
692         switch (*cp.c) {
693 #ifndef UNIX
694         case '\x1A': /* ^Z :-* */ return P_EOF;
695 #endif
696         case '#':
697             while (cp.c < cp.z && *cp.c != '\n') cp.c++;
698             break;
699         case '\n':
700             cp.line++;
701 	    lntotal++;
702         case ' ':
703         case '\t':
704         case '\r':
705             cp.c++;
706             break;
707         case '=': return P_ASSIGN;
708         case ';': return P_EOS;
709         case ',': return P_COMMA;
710         case ':': return P_COLON;
711         case '.': return P_DOT;
712         case '\'':
713         case '"':
714         case '`':
715         case '/': return P_STRING;
716         case '[': return P_KEYSPEC;
717         case '{': return P_OPENBRACE;
718         case '}': return P_CLOSEBRACE;
719         case '?': return P_QUEST;
720         case '$': return P_VARIABLE;
721         case '-': case '+':
722         case '0': case '1': case '2': case '3': case '4':
723         case '5': case '6': case '7': case '8': case '9': return P_NUMBER;
724         default:
725             if ((*cp.c >= 'a' && *cp.c <= 'z') ||
726                 (*cp.c >= 'A' && *cp.c <= 'Z') ||
727                 (*cp.c == '_'))
728                 return P_WORD;
729             else
730                 return P_SYNTAX;
731         }
732     }
733     return P_EOF;
734 }
735 
GetOp(CurPos & cp,int what)736 static void GetOp(CurPos &cp, int what) {
737     switch (what) {
738     case P_COMMA:
739     case P_OPENBRACE:
740     case P_CLOSEBRACE:
741     case P_ASSIGN:
742     case P_EOS:
743     case P_COLON:
744     case P_QUEST:
745     case P_VARIABLE:
746     case P_DOT:
747         cp.c++;
748         break;
749     }
750 }
751 
GetString(CurPos & cp)752 static char *GetString(CurPos &cp) {
753     char c = *cp.c;
754     char *p = cp.c;
755     char *d = cp.c;
756     int n;
757 
758     if (c == '[') c = ']';
759 
760     cp.c++; // skip '"`
761     while (cp.c < cp.z) {
762         if (*cp.c == '\\') {
763             if (c == '/')
764                 *p++ = *cp.c;
765             cp.c++;
766             if (cp.c == cp.z) return 0;
767             if (c == '"') {
768                 switch (*cp.c) {
769                 case 'e': *cp.c = '\x1B'; break;
770                 case 't': *cp.c = '\t'; break;
771                 case 'r': *cp.c = '\r'; break;
772                 case 'n': *cp.c = '\n'; break;
773                 case 'b': *cp.c = '\b'; break;
774                 case 'v': *cp.c = '\v'; break;
775                 case 'a': *cp.c = '\a'; break;
776                 case 'x':
777                     cp.c++;
778                     if (cp.c == cp.z) return 0;
779                     if (*cp.c >= '0' && *cp.c <= '9') n = *cp.c - '0';
780                     else if (*cp.c >='a' && *cp.c <= 'f') n = *cp.c - 'a' + 10;
781                     else if (*cp.c >='A' && *cp.c <= 'F') n = *cp.c - 'A' + 10;
782                     else return 0;
783                     cp.c++;
784                     if (cp.c == cp.z) cp.c--;
785                     else if (*cp.c >= '0' && *cp.c <= '9') n = n * 16 + *cp.c - '0';
786                     else if (*cp.c >= 'a' && *cp.c <= 'f') n = n * 16 + *cp.c - 'a' + 10;
787                     else if (*cp.c >= 'A' && *cp.c <= 'F') n = n * 16 + *cp.c - 'A' + 10;
788                     else cp.c--;
789                     *cp.c = (char)n;
790                     break;
791                 }
792             }
793         } else if (*cp.c == c) {
794             cp.c++;
795             *p = 0;
796             return d;
797         } else if (*cp.c == '\n') {
798             cp.line++;
799             return 0;
800         } else if (*cp.c == '\r') {
801             cp.c++;
802             if (cp.c == cp.z) return 0;
803         }
804         *p++ = *cp.c++;
805     }
806     return 0;
807 }
808 
GetNumber(CurPos & cp)809 static int GetNumber(CurPos &cp) {
810     int value = 0;
811     int neg = 0;
812 
813     if (cp.c < cp.z && (*cp.c == '-' || *cp.c == '+')) {
814         if (*cp.c == '-') neg = 1;
815         cp.c++;
816     }
817     while (cp.c < cp.z && (*cp.c >= '0' && *cp.c <= '9')) {
818         value = value * 10 + (*cp.c - '0');
819         cp.c++;
820     }
821     return neg ? -value : value;
822 }
823 
CmdNumCFTE(const char * Cmd)824 static int CmdNumCFTE(const char *Cmd) {
825 
826     for (size_t i = 0; i < FTE_ARRAY_SIZE(Command_Table); ++i)
827         if (strcmp(Cmd, Command_Table[i].Name) == 0)
828             return Command_Table[i].CmdId;
829     for (unsigned i = 0; i < CMacrosCFTE; i++)
830         if (MacrosCFTE[i].Name && (strcmp(Cmd, MacrosCFTE[i].Name)) == 0)
831             return i | CMD_EXT;
832     return 0; // Nop
833 }
834 
NewCommandCFTE(const char * Name)835 static int NewCommandCFTE(const char *Name) {
836     if (Name == 0)
837         Name = "";
838     MacrosCFTE = (ExMacroCFTE *) realloc(MacrosCFTE, sizeof(ExMacroCFTE) * (1 + CMacrosCFTE));
839     MacrosCFTE[CMacrosCFTE].Name = strdup(Name);
840     CMacrosCFTE++;
841     return CMacrosCFTE - 1;
842 }
843 
ParseCommands(CurPos & cp,char * Name)844 static int ParseCommands(CurPos &cp, char *Name) {
845     //if (!Name)
846     //    return 0;
847     Word cmd;
848     int p;
849     long Cmd = NewCommandCFTE(Name) | CMD_EXT;
850 
851     long cnt;
852     long ign = 0;
853 
854     PutNumber(cp, CF_INT, Cmd);
855     GetOp(cp, P_OPENBRACE);
856     cnt = 1;
857     while (1) {
858         p = Parse(cp);
859         if (p == P_CLOSEBRACE) break;
860         if (p == P_EOF) Fail(cp, "Unexpected EOF");
861 
862         if (p == P_DOT) {
863             GetOp(cp, P_DOT);
864             PutNull(cp, CF_CONCAT);
865         } else if (p == P_NUMBER) {
866             long num = GetNumber(cp);
867             if (Parse(cp) != P_COLON) {
868                 PutNumber(cp, CF_INT, num);
869             } else {
870                 cnt = num;
871                 GetOp(cp, P_COLON);
872             }
873         } else if (p == P_WORD) {
874             long Command;
875 
876             if (GetWord(cp, cmd) == -1) Fail(cp, "Syntax error");
877             Command = CmdNumCFTE(cmd);
878             if (Command == 0)
879                 Fail(cp, "Unrecognised command: %s", cmd);
880             PutNumber(cp, CF_COMMAND, Command);
881             PutNumber(cp, CF_INT, cnt);
882             PutNumber(cp, CF_INT, ign);
883             ign = 0;
884             cnt = 1;
885         } else if (p == P_STRING) {
886             char *s = GetString(cp);
887             PutString(cp, CF_STRING, s);
888         } else if (p == P_QUEST) {
889             ign = 1;
890             GetOp(cp, P_QUEST);
891         } else if (p == P_VARIABLE) {
892             GetOp(cp, P_VARIABLE);
893             if (Parse(cp) != P_WORD) Fail(cp, "Syntax error (variable name expected)");
894             Word w;
895             if (GetWord(cp, w) != 0) Fail(cp, "Syntax error (bad variable name)");
896             long var = Lookup(CfgVar, w);
897             if (var == -1) Fail(cp, "Unrecognised variable");
898             PutNumber(cp, CF_VARIABLE, var);
899         } else if (p == P_EOS) {
900             GetOp(cp, P_EOS);
901             cnt = 1;
902         } else
903             Fail(cp, "Syntax error");
904     }
905     GetOp(cp, P_CLOSEBRACE);
906     return 0;
907 }
908 
ParseConfigFile(CurPos & cp)909 static int ParseConfigFile(CurPos &cp) {
910     Word w = "";
911     char *s = 0;
912     int p = 0;
913 
914     Word ObjName = "", UpMode = "";
915 
916     while (1) {
917         p = Parse(cp);
918 
919         switch (p) {
920         case P_WORD:
921             if (GetWord(cp, w) != 0) Fail(cp, "Syntax error");
922             switch (Lookup(CfgKW, w)) {
923             case K_SUB:
924                 {
925                     Word Name;
926 
927                     if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
928                     if (GetWord(cp, Name) != 0) Fail(cp, "Syntax error");
929                     PutString(cp, CF_SUB, Name);
930                     if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
931                     GetOp(cp, P_OPENBRACE);
932                     if (ParseCommands(cp, strdup(Name)) == -1)
933                         Fail(cp, "Parse failed");
934                     PutNull(cp, CF_END);
935                 }
936                 break;
937             case K_MENU:
938 
939                 {
940                     Word MenuName;
941                     //int menu = -1, item = -1;
942 
943                     if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");;
944                     if (GetWord(cp, MenuName) != 0) Fail(cp, "Syntax error");;
945                     if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
946                     GetOp(cp, P_OPENBRACE);
947 
948                     PutString(cp, CF_MENU, MenuName);
949 
950                     while (1) {
951                         p = Parse(cp);
952                         if (p == P_CLOSEBRACE) break;
953                         if (p == P_EOF) Fail(cp, "Unexpected EOF");
954                         if (p != P_WORD) Fail(cp, "Syntax error");
955 
956                         if (GetWord(cp, w) != 0) Fail(cp, "Parse failed");
957                         switch (Lookup(CfgKW, w)) {
958                         case K_ITEM: // menu::item
959                             switch (Parse(cp)) {
960                             case P_EOS:
961                                 PutNull(cp, CF_ITEM);
962                                 break;
963                             case P_STRING:
964                                 s = GetString(cp);
965                                 PutString(cp, CF_ITEM, s);
966                                 break;
967                             default:
968                                 Fail(cp, "Syntax error");;
969                             }
970                             if (Parse(cp) == P_EOS) {
971                                 GetOp(cp, P_EOS);
972                                 break;
973                             }
974                             if (Parse(cp) != P_OPENBRACE)
975                                 Fail(cp, "'{' expected");
976 
977                             PutNull(cp, CF_MENUSUB);
978                             if (ParseCommands(cp, 0) == -1)
979                                 Fail(cp, "Parse failed");
980                             PutNull(cp, CF_END);
981                             break;
982                         case K_SUBMENU: // menu::submenu
983                             if (Parse(cp) != P_STRING)
984                                 Fail(cp, "String expected");
985                             s = GetString(cp);
986                             if (Parse(cp) != P_COMMA)
987                                 Fail(cp, "',' expected");
988                             GetOp(cp, P_COMMA);
989                             if (Parse(cp) != P_WORD)
990                                 Fail(cp, "Syntax error");
991                             if (GetWord(cp, w) == -1)
992                                 Fail(cp, "Parse failed");
993 
994                             PutString(cp, CF_SUBMENU, s);
995                             PutString(cp, CF_STRING, w);
996                             if (Parse(cp) != P_EOS)
997                                 Fail(cp, "';' expected");
998                             GetOp(cp, P_EOS);
999                             break;
1000 
1001                         case K_SUBMENUCOND: // menu::submenu
1002                             if (Parse(cp) != P_STRING)
1003                                 Fail(cp, "String expected");
1004                             s = GetString(cp);
1005                             if (Parse(cp) != P_COMMA)
1006                                 Fail(cp, "',' expected");
1007                             GetOp(cp, P_COMMA);
1008                             if (Parse(cp) != P_WORD)
1009                                 Fail(cp, "Syntax error");
1010                             if (GetWord(cp, w) == -1)
1011                                 Fail(cp, "Parse failed");
1012 
1013                             PutString(cp, CF_SUBMENUCOND, s);
1014                             PutString(cp, CF_STRING, w);
1015                             if (Parse(cp) != P_EOS)
1016                                 Fail(cp, "';' expected");
1017                             GetOp(cp, P_EOS);
1018                             break;
1019                         default:  // menu::
1020                             Fail(cp, "Syntax error");
1021                         }
1022                     }
1023                     GetOp(cp, P_CLOSEBRACE);
1024                     PutNull(cp, CF_END);
1025                 }
1026                 break;
1027             case K_EVENTMAP:
1028                 {
1029                     if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
1030                     if (GetWord(cp, ObjName) != 0) Fail(cp, "Parse failed");
1031                     PutString(cp, CF_EVENTMAP, ObjName);
1032 
1033                     UpMode[0] = 0;
1034                     if (Parse(cp) == P_COLON) {
1035                         GetOp(cp, P_COLON);
1036                         if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
1037                         if (GetWord(cp, UpMode) != 0) Fail(cp, "Parse failed");
1038                     }
1039                     PutString(cp, CF_PARENT, UpMode);
1040                     if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1041                     GetOp(cp, P_OPENBRACE);
1042 
1043                     while (1) {
1044                         p = Parse(cp);
1045                         if (p == P_CLOSEBRACE) break;
1046                         if (p == P_EOF) Fail(cp, "Unexpected EOF");
1047                         if (p != P_WORD) Fail(cp, "Syntax error");
1048 
1049                         if (GetWord(cp, w) != 0) Fail(cp, "Parse failed");
1050                         switch (Lookup(CfgKW, w)) {
1051                         case K_KEY: // mode::key
1052                             if (Parse(cp) != P_KEYSPEC) Fail(cp, "'[' expected");
1053                             s = GetString(cp);
1054                             PutString(cp, CF_KEY, s);
1055                             if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1056                             PutNull(cp, CF_KEYSUB);
1057                             if (ParseCommands(cp, 0) == -1) Fail(cp, "Parse failed");
1058                             PutNull(cp, CF_END);
1059                             break;
1060 
1061                         case K_ABBREV:
1062                             if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1063                             s = GetString(cp);
1064                             PutString(cp, CF_ABBREV, s);
1065                             switch (Parse(cp)) {
1066                             case P_OPENBRACE:
1067                                 PutNull(cp, CF_KEYSUB);
1068                                 if (ParseCommands(cp, 0) == -1) Fail(cp, "Parse failed");
1069                                 PutNull(cp, CF_END);
1070                                 break;
1071                             case P_STRING:
1072                                 s = GetString(cp);
1073                                 PutString(cp, CF_STRING, s);
1074                                 if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1075                                 GetOp(cp, P_EOS);
1076                                 break;
1077                             default:
1078                                 Fail(cp, "Syntax error");
1079                             }
1080                             break;
1081 
1082                         default:  // mode::
1083                             if (Parse(cp) != P_ASSIGN) Fail(cp, "'=' expected");
1084                             GetOp(cp, P_ASSIGN);
1085 
1086                             switch (Parse(cp)) {
1087                                 /*  case P_NUMBER:
1088                                  {
1089                                  long var;
1090                                  long num;
1091 
1092                                  num = GetNumber(cp);
1093                                  var = LookupEventNumber(w);
1094                                  if (var == -1) return -1;
1095                                  PutObj(cp, CF_SETVAR, sizeof(long), &var);
1096                                  PutObj(cp, CF_INT, sizeof(long), &num);
1097                                  }
1098                                  break;*/
1099                             case P_STRING:
1100                                 {
1101                                     long var;
1102 
1103                                     s = GetString(cp);
1104                                     if (s == 0) Fail(cp, "String expected");
1105                                     var = Lookup(event_string, w);
1106                                     if (var == -1) Fail(cp, "event_string Lookup of '%s' failed", w);
1107                                     PutNumber(cp, CF_SETVAR, var);
1108                                     PutString(cp, CF_STRING, s);
1109                                 }
1110                                 break;
1111                             default:
1112                                 return -1;
1113                             }
1114                             if (Parse(cp) != P_EOS) return -1;
1115                             GetOp(cp, P_EOS);
1116                             break;
1117                         }
1118                     }
1119                     GetOp(cp, P_CLOSEBRACE);
1120                     PutNull(cp, CF_END);
1121                 }
1122                 break;
1123 
1124             case K_COLORIZE:
1125                 {
1126                     long LastState = -1;
1127 
1128                     if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
1129                     if (GetWord(cp, ObjName) != 0) Fail(cp, "Parse failed");
1130                     PutString(cp, CF_COLORIZE, ObjName);
1131 
1132                     UpMode[0] = 0;
1133                     if (Parse(cp) == P_COLON) {
1134                         GetOp(cp, P_COLON);
1135                         if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
1136                         if (GetWord(cp, UpMode) != 0) Fail(cp, "Parse failed");
1137                     }
1138                     PutString(cp, CF_PARENT, UpMode);
1139                     if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1140                     GetOp(cp, P_OPENBRACE);
1141 
1142                     while (1) {
1143                         p = Parse(cp);
1144                         if (p == P_CLOSEBRACE) break;
1145                         if (p == P_EOF) Fail(cp, "Unexpected EOF");
1146                         if (p != P_WORD) Fail(cp, "Syntax error");
1147 
1148                         if (GetWord(cp, w) != 0) Fail(cp, "Parse failed");
1149                         switch (Lookup(CfgKW, w)) {
1150                         case K_COLOR: // mode::color
1151                             if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1152                             GetOp(cp, P_OPENBRACE);
1153                             PutNull(cp, CF_COLOR);
1154 
1155                             while (1) {
1156                                 char *sname, *svalue;
1157                                 long cidx;
1158 
1159                                 if (Parse(cp) == P_CLOSEBRACE) break;
1160                                 if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1161                                 GetOp(cp, P_OPENBRACE);
1162                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1163                                 sname = GetString(cp);
1164                                 if ((cidx = Lookup(hilit_colors, sname)) == -1)
1165                                     Fail(cp, "hilit_colors Lookup of '%s' failed", sname);
1166                                 PutNumber(cp, CF_INT, cidx);
1167                                 if (Parse(cp) != P_COMMA)
1168                                     Fail(cp, "',' expected");
1169                                 GetOp(cp, P_COMMA);
1170                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1171                                 svalue = GetString(cp);
1172                                 svalue = GetColor(cp, svalue);
1173                                 PutString(cp, CF_STRING, svalue);
1174                                 if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1175                                 GetOp(cp, P_CLOSEBRACE);
1176                                 if (Parse(cp) != P_COMMA)
1177                                     break;
1178                                 else
1179                                     GetOp(cp, P_COMMA);
1180                             }
1181                             if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1182                             GetOp(cp, P_CLOSEBRACE);
1183                             if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1184                             GetOp(cp, P_EOS);
1185                             PutNull(cp, CF_END);
1186                             break;
1187 
1188                         case K_KEYWORD: // mode::keyword
1189                             {
1190                                 char *colorstr, *kname;
1191                                 //int color;
1192 
1193                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1194                                 colorstr = GetString(cp);
1195                                 colorstr = GetColor(cp, colorstr);
1196                                 if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1197                                 GetOp(cp, P_OPENBRACE);
1198 
1199                                 PutString(cp, CF_KEYWORD, colorstr);
1200 
1201                                 while (1) {
1202                                     if (Parse(cp) == P_CLOSEBRACE) break;
1203                                     if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1204                                     kname = GetString(cp);
1205 
1206                                     if (strlen(kname) >= CK_MAXLEN) Fail(cp, "Keyword name is too long");
1207 
1208                                     PutString(cp, CF_STRING, kname);
1209 
1210                                     if (Parse(cp) != P_COMMA)
1211                                         break;
1212                                     else
1213                                         GetOp(cp, P_COMMA);
1214                                 }
1215                             }
1216                             if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1217                             GetOp(cp, P_CLOSEBRACE);
1218                             if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1219                             GetOp(cp, P_EOS);
1220                             PutNull(cp, CF_END);
1221                             break;
1222 
1223                         case K_HSTATE:
1224                             {
1225                                 long stateno;
1226                                 char *cname;
1227                                 long cidx;
1228 
1229                                 if (Parse(cp) != P_NUMBER) Fail(cp, "state index expected");
1230                                 stateno = GetNumber(cp);
1231                                 if (stateno != LastState + 1) Fail(cp, "invalid state index");
1232 
1233                                 if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1234                                 GetOp(cp, P_OPENBRACE);
1235                                 PutNumber(cp, CF_HSTATE, stateno);
1236 
1237                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1238                                 cname = GetString(cp);
1239                                 if ((cidx = Lookup(hilit_colors, cname)) == -1)
1240                                     Fail(cp, "hilit_colors Lookup of '%s' failed", cname);
1241                                 PutNumber(cp, CF_INT, cidx);
1242                                 if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1243                                 GetOp(cp, P_CLOSEBRACE);
1244                                 LastState = stateno;
1245                             }
1246                             break;
1247 
1248                         case K_HTRANS:
1249                             {
1250                                 long next_state;
1251                                 char *opts, *match;
1252                                 long match_opts;
1253                                 char *cname;
1254                                 long cidx;
1255 
1256                                 if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1257                                 GetOp(cp, P_OPENBRACE);
1258 
1259                                 if (Parse(cp) != P_NUMBER) Fail(cp, "next_state index expected");
1260                                 next_state = GetNumber(cp);
1261                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1262                                 GetOp(cp, P_COMMA);
1263                                 if (Parse(cp) != P_STRING) Fail(cp, "match options expected");
1264                                 opts = GetString(cp);
1265                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1266                                 GetOp(cp, P_COMMA);
1267                                 if (Parse(cp) != P_STRING) Fail(cp, "match string expected");
1268                                 match = GetString(cp);
1269                                 PutNumber(cp, CF_HTRANS, next_state);
1270                                 match_opts = 0;
1271                                 if (strchr(opts, '^')) match_opts |= MATCH_MUST_BOL;
1272                                 if (strchr(opts, '$')) match_opts |= MATCH_MUST_EOL;
1273                                 //if (strchr(opts, 'b')) match_opts |= MATCH_MUST_BOLW;
1274                                 //if (strchr(opts, 'e')) match_opts |= MATCH_MUST_EOLW;
1275                                 if (strchr(opts, 'i')) match_opts |= MATCH_NO_CASE;
1276                                 if (strchr(opts, 's')) match_opts |= MATCH_SET;
1277                                 if (strchr(opts, 'S')) match_opts |= MATCH_NOTSET;
1278                                 if (strchr(opts, '-')) match_opts |= MATCH_NOGRAB;
1279                                 if (strchr(opts, '<')) match_opts |= MATCH_TAGASNEXT;
1280                                 if (strchr(opts, '>')) match_opts &= ~MATCH_TAGASNEXT;
1281                                 //if (strchr(opts, '!')) match_opts |= MATCH_NEGATE;
1282                                 if (strchr(opts, 'q')) match_opts |= MATCH_QUOTECH;
1283                                 if (strchr(opts, 'Q')) match_opts |= MATCH_QUOTEEOL;
1284                                 if (strchr(opts, 'x')) match_opts |= MATCH_REGEXP;
1285 
1286                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1287                                 GetOp(cp, P_COMMA);
1288                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1289                                 cname = GetString(cp);
1290                                 if ((cidx = Lookup(hilit_colors, cname)) == -1)
1291                                     Fail(cp, "hilit_colors Lookup of '%s' failed", cname);
1292 
1293                                 PutNumber(cp, CF_INT, match_opts);
1294                                 PutNumber(cp, CF_INT, cidx);
1295                                 PutString(cp, match_opts & MATCH_REGEXP ? CF_REGEXP : CF_STRING, match);
1296 
1297                                 if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1298                                 GetOp(cp, P_CLOSEBRACE);
1299                             }
1300                             break;
1301 
1302                         case K_HWTYPE:
1303                             if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1304                             GetOp(cp, P_OPENBRACE);
1305 
1306                             {
1307                                 long options = 0;
1308                                 long nextKwdMatchedState;
1309                                 long nextKwdNotMatchedState;
1310                                 long nextKwdNoCharState;
1311                                 char *opts;
1312                                 char *wordChars;
1313 
1314 
1315                                 if (Parse(cp) != P_NUMBER) Fail(cp, "next_state index expected");
1316                                 nextKwdMatchedState = GetNumber(cp);
1317 
1318                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1319                                 GetOp(cp, P_COMMA);
1320 
1321                                 if (Parse(cp) != P_NUMBER) Fail(cp, "next_state index expected");
1322                                 nextKwdNotMatchedState = GetNumber(cp);
1323 
1324                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1325                                 GetOp(cp, P_COMMA);
1326 
1327                                 if (Parse(cp) != P_NUMBER) Fail(cp, "next_state index expected");
1328                                 nextKwdNoCharState = GetNumber(cp);
1329 
1330                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1331                                 GetOp(cp, P_COMMA);
1332 
1333                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1334                                 opts = GetString(cp);
1335                                 if (strchr(opts, 'i')) options |= STATE_NOCASE;
1336                                 if (strchr(opts, '<')) options |= STATE_TAGASNEXT;
1337                                 if (strchr(opts, '>')) options &= ~STATE_TAGASNEXT;
1338                                 if (strchr(opts, '-')) options |= STATE_NOGRAB;
1339 
1340                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1341                                 GetOp(cp, P_COMMA);
1342 
1343                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1344                                 wordChars = GetString(cp);
1345 
1346                                 PutNull(cp, CF_HWTYPE);
1347                                 PutNumber(cp, CF_INT, nextKwdMatchedState);
1348                                 PutNumber(cp, CF_INT, nextKwdNotMatchedState);
1349                                 PutNumber(cp, CF_INT, nextKwdNoCharState);
1350                                 PutNumber(cp, CF_INT, options);
1351                                 PutString(cp, CF_STRING, wordChars);
1352                             }
1353                             if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1354                             GetOp(cp, P_CLOSEBRACE);
1355                             break;
1356 
1357                         case K_HWORDS:
1358                             {
1359                                 char *colorstr, *kname;
1360                                 //int color;
1361 
1362                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1363                                 colorstr = GetString(cp);
1364                                 colorstr = GetColor(cp, colorstr);
1365 
1366                                 if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1367                                 GetOp(cp, P_OPENBRACE);
1368 
1369                                 PutString(cp, CF_HWORDS, colorstr);
1370 
1371                                 while (1) {
1372                                     if (Parse(cp) == P_CLOSEBRACE) break;
1373                                     if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1374                                     kname = GetString(cp);
1375                                     PutString(cp, CF_STRING, kname);
1376 
1377                                     if (Parse(cp) != P_COMMA)
1378                                         break;
1379                                     else
1380                                         GetOp(cp, P_COMMA);
1381                                 }
1382                             }
1383                             if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1384                             GetOp(cp, P_CLOSEBRACE);
1385 
1386                             PutNull(cp, CF_END);
1387                             break;
1388 
1389                         default:
1390                             if (Parse(cp) != P_ASSIGN) Fail(cp, "'=' expected");
1391                             GetOp(cp, P_ASSIGN);
1392                             switch (Parse(cp)) {
1393                                 /*case P_NUMBER:
1394                                  {
1395                                  long var;
1396                                  long num;
1397 
1398                                  num = GetNumber(cp);
1399                                  var = LookupColorizeNumber(w);
1400                                  if (var == -1) return -1;
1401                                  PutObj(cp, CF_SETVAR, sizeof(long), &var);
1402                                  PutObj(cp, CF_INT, sizeof(long), &num);
1403                                  }
1404                                  break;*/
1405                             case P_STRING:
1406                                 {
1407                                     long var;
1408 
1409                                     s = GetString(cp);
1410                                     if (s == 0) Fail(cp, "Parse failed");
1411                                     var = Lookup(colorize_string, w);
1412                                     if (var == -1)
1413                                         Fail(cp, "colorize_string Lookup of '%s' failed", w);
1414                                     PutNumber(cp, CF_SETVAR, var);
1415                                     PutString(cp, CF_STRING, s);
1416                                 }
1417                                 break;
1418                             default:
1419                                 return -1;
1420                             }
1421                             if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1422                             GetOp(cp, P_EOS);
1423                             break;
1424                         }
1425                     }
1426                     GetOp(cp, P_CLOSEBRACE);
1427                     PutNull(cp, CF_END);
1428                 }
1429                 break;
1430 
1431             case K_MODE: // mode::
1432                 {
1433                     if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
1434                     if (GetWord(cp, ObjName) != 0) Fail(cp, "Parse failed");
1435                     PutString(cp, CF_MODE, ObjName);
1436 
1437                     UpMode[0] = 0;
1438                     if (Parse(cp) == P_COLON) {
1439                         GetOp(cp, P_COLON);
1440                         if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
1441                         if (GetWord(cp, UpMode) != 0) Fail(cp, "Parse failed");
1442                     }
1443                     PutString(cp, CF_PARENT, UpMode);
1444                     if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1445                     GetOp(cp, P_OPENBRACE);
1446 
1447                     while (1) {
1448                         p = Parse(cp);
1449                         if (p == P_CLOSEBRACE) break;
1450                         if (p == P_EOF) Fail(cp, "Unexpected EOF");
1451                         if (p != P_WORD) Fail(cp, "Syntax error");
1452 
1453                         if (GetWord(cp, w) != 0) Fail(cp, "Parse failed");
1454                         //switch (Lookup(CfgKW, w)) {
1455                         //default:  // mode::
1456                         if (Parse(cp) != P_ASSIGN) Fail(cp, "'=' expected");
1457                         GetOp(cp, P_ASSIGN);
1458                         switch (Parse(cp)) {
1459                         case P_NUMBER:
1460                             {
1461                                 long var;
1462                                 long num;
1463 
1464                                 num = GetNumber(cp);
1465                                 var = Lookup(mode_num, w);
1466                                 if (var == -1)
1467                                     Fail(cp, "mode_num Lookup of '%s' failed", w);
1468                                 PutNumber(cp, CF_SETVAR, var);
1469                                 PutNumber(cp, CF_INT, num);
1470                             }
1471                             break;
1472                         case P_STRING:
1473                             {
1474                                 long var;
1475 
1476                                 s = GetString(cp);
1477                                 if (s == 0) Fail(cp, "Parse failed");
1478                                 var = Lookup(mode_string, w);
1479                                 if (var == -1)
1480                                     Fail(cp, "mode_string Lookup of '%s' filed", w);
1481                                 PutNumber(cp, CF_SETVAR, var);
1482                                 PutString(cp, CF_STRING, s);
1483                             }
1484                             break;
1485                         default:
1486                             return -1;
1487                         }
1488                         if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1489                         GetOp(cp, P_EOS);
1490                         //    break;
1491                         //}
1492                     }
1493                     GetOp(cp, P_CLOSEBRACE);
1494                     PutNull(cp, CF_END);
1495                 }
1496                 break;
1497             case K_OBJECT:
1498                 {
1499                     if (Parse(cp) != P_WORD) Fail(cp, "Syntax error");
1500                     if (GetWord(cp, ObjName) != 0) Fail(cp, "Parse failed");
1501                     if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1502                     GetOp(cp, P_OPENBRACE);
1503 
1504                     PutString(cp, CF_OBJECT, ObjName);
1505 
1506                     while (1) {
1507                         p = Parse(cp);
1508                         if (p == P_CLOSEBRACE) break;
1509                         if (p == P_EOF) Fail(cp, "Unexpected EOF");
1510                         if (p != P_WORD) Fail(cp, "Syntax error");
1511 
1512                         if (GetWord(cp, w) != 0) Fail(cp, "Parse failed");
1513                         switch (Lookup(CfgKW, w)) {
1514                         case K_COLOR: // mode::color
1515                             if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1516                             GetOp(cp, P_OPENBRACE);
1517                             PutNull(cp, CF_COLOR);
1518 
1519                             while (1) {
1520                                 char *sname, *svalue;
1521 
1522                                 if (Parse(cp) == P_CLOSEBRACE) break;
1523                                 if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1524                                 GetOp(cp, P_OPENBRACE);
1525                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1526                                 sname = GetString(cp);
1527                                 PutString(cp, CF_STRING, sname);
1528                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1529                                 GetOp(cp, P_COMMA);
1530                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1531                                 svalue = GetString(cp);
1532                                 svalue = GetColor(cp, svalue);
1533                                 PutString(cp, CF_STRING, svalue);
1534                                 if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1535                                 GetOp(cp, P_CLOSEBRACE);
1536                                 if (Parse(cp) != P_COMMA)
1537                                     break;
1538                                 else
1539                                     GetOp(cp, P_COMMA);
1540                             }
1541                             if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1542                             GetOp(cp, P_CLOSEBRACE);
1543                             if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1544                             GetOp(cp, P_EOS);
1545                             PutNull(cp, CF_END);
1546                             break;
1547 
1548                         case K_COMPILERX:
1549                             {
1550                                 long file, line, msg;
1551                                 char *regexp;
1552 
1553                                 if (Parse(cp) != P_ASSIGN) Fail(cp, "'=' expected");
1554                                 GetOp(cp, P_ASSIGN);
1555                                 if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1556                                 GetOp(cp, P_OPENBRACE);
1557                                 if (Parse(cp) != P_NUMBER) Fail(cp, "Number expected");
1558                                 file = GetNumber(cp);
1559                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1560                                 GetOp(cp, P_COMMA);
1561                                 if (Parse(cp) != P_NUMBER) Fail(cp, "Number expected");
1562                                 line = GetNumber(cp);
1563                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1564                                 GetOp(cp, P_COMMA);
1565                                 if (Parse(cp) != P_NUMBER) Fail(cp, "Number expected");
1566                                 msg = GetNumber(cp);
1567                                 if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1568                                 GetOp(cp, P_COMMA);
1569                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1570                                 regexp = GetString(cp);
1571                                 if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1572                                 GetOp(cp, P_CLOSEBRACE);
1573                                 PutNull(cp, CF_COMPRX);
1574                                 PutNumber(cp, CF_INT, file);
1575                                 PutNumber(cp, CF_INT, line);
1576                                 PutNumber(cp, CF_INT, msg);
1577                                 PutString(cp, CF_REGEXP, regexp);
1578                                 if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1579                                 GetOp(cp, P_EOS);
1580                             }
1581                             break;
1582                         case K_CVSIGNRX:
1583                             {
1584                                 char *regexp;
1585 
1586                                 if (Parse(cp) != P_ASSIGN) Fail(cp, "'=' expected");
1587                                 GetOp(cp, P_ASSIGN);
1588                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1589                                 regexp = GetString(cp);
1590                                 PutNull(cp, CF_CVSIGNRX);
1591                                 PutString(cp, CF_REGEXP, regexp);
1592                                 if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1593                                 GetOp(cp, P_EOS);
1594                             }
1595                             break;
1596                         case K_SVNIGNRX:
1597                             {
1598                                 char *regexp;
1599 
1600                                 if (Parse(cp) != P_ASSIGN) Fail(cp, "'=' expected");
1601                                 GetOp(cp, P_ASSIGN);
1602                                 if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1603                                 regexp = GetString(cp);
1604                                 PutNull(cp, CF_SVNIGNRX);
1605                                 PutString(cp, CF_REGEXP, regexp);
1606                                 if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1607                                 GetOp(cp, P_EOS);
1608                             }
1609                             break;
1610                         default:  // mode::
1611                             if (Parse(cp) != P_ASSIGN) Fail(cp, "'=' expected");
1612                             GetOp(cp, P_ASSIGN);
1613 
1614                             switch (Parse(cp)) {
1615                             case P_NUMBER:
1616                                 {
1617                                     long var;
1618                                     long num;
1619 
1620                                     num = GetNumber(cp);
1621                                     var = Lookup(global_num, w);
1622                                     if (var == -1)
1623                                         Fail(cp, "global_num Lookup of '%s' failed", w);
1624                                     PutNumber(cp, CF_SETVAR, var);
1625                                     PutNumber(cp, CF_INT, num);
1626                                 }
1627                                 break;
1628                             case P_STRING:
1629                                 {
1630                                     long var;
1631 
1632                                     s = GetString(cp);
1633                                     if (s == 0) Fail(cp, "Parse failed");
1634                                     var = Lookup(global_string, w);
1635                                     if (var == -1) Fail(cp, "global_string Lookup of '%s' failed", w);
1636                                     PutNumber(cp, CF_SETVAR, var);
1637                                     PutString(cp, CF_STRING, s);
1638                                 }
1639                                 break;
1640                             default:
1641                                 Fail(cp, "Syntax error");
1642                             }
1643                             if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1644                             GetOp(cp, P_EOS);
1645                             break;
1646                         }
1647                     }
1648                     GetOp(cp, P_CLOSEBRACE);
1649                     PutNull(cp, CF_END);
1650                 }
1651                 break;
1652 
1653             case K_COLPALETTE:
1654                 {
1655                     if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1656                     GetOp(cp, P_OPENBRACE);
1657 
1658                     while (1) {
1659                         char *sname, *svalue;
1660 
1661                         if (Parse(cp) == P_CLOSEBRACE) break;
1662                         if (Parse(cp) != P_OPENBRACE) Fail(cp, "'{' expected");
1663                         GetOp(cp, P_OPENBRACE);
1664                         if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1665                         sname = GetString(cp);
1666                         if (Parse(cp) != P_COMMA) Fail(cp, "',' expected");
1667                         GetOp(cp, P_COMMA);
1668                         if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1669                         svalue = GetString(cp);
1670                         svalue = GetColor(cp, svalue);
1671                         if (DefineColor(sname, svalue) != 1)
1672                             Fail(cp, "DefineColor failed\n");
1673                         if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1674                         GetOp(cp, P_CLOSEBRACE);
1675                         if (Parse(cp) != P_COMMA)
1676                             break;
1677                         else
1678                             GetOp(cp, P_COMMA);
1679                     }
1680                     if (Parse(cp) != P_CLOSEBRACE) Fail(cp, "'}' expected");
1681                     GetOp(cp, P_CLOSEBRACE);
1682                 }
1683                 break;
1684             case K_INCLUDE:
1685                 {
1686                     char *fn;
1687 
1688                     if (Parse(cp) != P_STRING) Fail(cp, "String expected");
1689                     fn = GetString(cp);
1690 
1691                     if (LoadFile(cp.name, fn) != 0) Fail(cp, "Include of file '%s' failed", fn);
1692                     if (Parse(cp) != P_EOS) Fail(cp, "';' expected");
1693                     GetOp(cp, P_EOS);
1694                 }
1695                 break;
1696             default:
1697                 Fail(cp, "Syntax error");
1698             }
1699             break;
1700         case P_EOF: return 0;
1701         default:    Fail(cp, "Syntax error");
1702         }
1703     }
1704 }
1705 
PreprocessConfigFile(CurPos & cp)1706 static int PreprocessConfigFile(CurPos &cp) {
1707     char *wipe = NULL;
1708     char *wipe_end = NULL;
1709     bool rem_active = false;
1710     bool string_open = false;
1711 
1712     while (cp.c < cp.z) {
1713 	switch(*cp.c)
1714 	{
1715 	case '#':
1716 	    if (string_open == true) break;
1717 
1718 	    rem_active = true;
1719 	    break;
1720 
1721 	case '\\':
1722             cp.c++;
1723             break;
1724 
1725 	case '"':
1726 	case '\'':
1727 	    if (rem_active == true) break;
1728 
1729 	    string_open = !string_open;
1730 	    break;
1731 
1732 	case '%':
1733 	    if (string_open == true) break;
1734 	    if (rem_active == true) break;
1735 
1736 	    wipe = cp.c;
1737             wipe_end = NULL;
1738 
1739             if (cp.c + 8 < cp.z && strncmp(cp.c, "%define(", 8) == 0) {
1740                 Word w;
1741                 cp.c += 8;
1742 
1743                 while (cp.c < cp.z && *cp.c != ')') {
1744                     GetWord(cp, w);
1745                     //printf("define '%s'\n", w);
1746                     DefineWord(w);
1747                     if (cp.c < cp.z && *cp.c != ',' && *cp.c != ')' )
1748                         Fail(cp, "unexpected: %c", cp.c[0]);
1749                     if (cp.c < cp.z && *cp.c == ',')
1750                         cp.c++;
1751                 }
1752 		cp.c++;
1753 /*            } else if (cp.c + 6 && strcmp(cp.c, "undef(", 6) == 0) {
1754                 Word w;
1755                 cp.c += 6;
1756 
1757                 while (cp.c < cp.z && *cp.c != ')') {
1758                     GetWord(cp, w);
1759                     UndefWord(w);
1760                 }*/
1761             } else if (cp.c + 4 < cp.z && strncmp(cp.c, "%if(", 4) == 0) {
1762                 Word w;
1763                 int wasWord = 0;
1764                 cp.c += 4;
1765 
1766                 while (cp.c < cp.z && *cp.c != ')') {
1767                     int neg = 0;
1768                     if (*cp.c == '!') {
1769                         cp.c++;
1770                         neg = 1;
1771                     }
1772                     GetWord(cp, w);
1773                     if (DefinedWord(w))
1774                         wasWord = 1;
1775                     if (neg)
1776                         wasWord = wasWord ? 0 : 1;
1777                     /*if (wasWord)
1778                         printf("yes '%s'\n", w);
1779                     else
1780                         printf("not '%s'\n", w);*/
1781 
1782                     if (cp.c < cp.z && *cp.c != ',' && *cp.c != ')' )
1783                         Fail(cp, "unexpected: %c", cp.c[0]);
1784                     if (cp.c < cp.z && *cp.c == ',')
1785                         cp.c++;
1786                 }
1787                 cp.c++;
1788                 if (!wasWord) {
1789                     int nest = 1;
1790                     while (cp.c < cp.z) {
1791                         if (*cp.c == '\n') {
1792                             cp.line++;
1793                             lntotal++;
1794                         } else if (*cp.c == '%') {
1795                             if (cp.c + 6 < cp.z &&
1796                                 strncmp(cp.c, "%endif", 6) == 0)
1797                             {
1798                                 cp.c += 6;
1799                                 if (--nest == 0)
1800                                     break;
1801                             }
1802                             if (cp.c + 3 < cp.z &&
1803                                 strncmp(cp.c, "%if", 3) == 0)
1804                             {
1805                                 cp.c += 3;
1806                                 ++nest;
1807                             }
1808                         } else if (*cp.c == '#') {
1809                             // we really shouldn't process hashed % directives
1810 				while (cp.c < cp.z && *cp.c != '\n' )
1811 					cp.c++;
1812 
1813                             // workaround to make line numbering correct
1814                             cp.line++;
1815                             lntotal++;
1816                         }
1817                         cp.c++;
1818                     }
1819                 }
1820             } else if (cp.c + 6 < cp.z && strncmp(cp.c, "%endif", 6) == 0) {
1821                 cp.c += 6;
1822             }
1823             if (cp.c < cp.z && *cp.c != '\n' && *cp.c != '\r')
1824                 Fail(cp, "syntax error %30.30s", cp.c);
1825 
1826 	    wipe_end = cp.c;
1827 
1828             // wipe preprocessor macros with space
1829 	    while (wipe < wipe_end)
1830 	    {
1831                 *wipe++ = ' ';
1832 	    }
1833 
1834 	    break;
1835 
1836         case '\n':
1837 	    cp.line++;
1838 	    rem_active = false;
1839             string_open = false;
1840 	    break;
1841 
1842 	default:;
1843 	}
1844 
1845         cp.c++;
1846     }
1847 
1848     return 0;
1849 }
1850 
LoadFile(const char * WhereName,const char * CfgName,int Level)1851 static int LoadFile(const char *WhereName, const char *CfgName, int Level) {
1852     int fd, rc;
1853     char *buffer = 0;
1854     struct stat statbuf;
1855     CurPos cp;
1856     char last[MAXPATH];
1857     char Cfg[MAXPATH];
1858 
1859     //fprintf(stderr, "Loading file %s %s\n", WhereName, CfgName);
1860 
1861     JustDirectory(WhereName, last, sizeof(last));
1862 
1863     if (IsFullPath(CfgName)) {
1864         strlcpy(Cfg, CfgName, sizeof(Cfg));
1865     } else {
1866         // here we will try relative to a number of places.
1867         // 1. User's .fte directory.
1868         // 2. System's "local config" directory.
1869         // 3. /usr/share/fte (FHS compliant - from Gentoo)
1870         // 3. Initial file's directory.
1871         // 4. Current directory.
1872         // This means that a user's directory will always win out,
1873         // allowing a given user to always be able to override everything,
1874         // followed by a system standard to override anything.
1875 
1876         // #'s 1 and 2 are unix-only.
1877 #ifdef UNIX
1878         // 1. User's .fte directory.
1879         char tmp[MAXPATH];
1880         sprintf(tmp, "~/.fte/%s", CfgName);
1881         ExpandPath(tmp, Cfg, sizeof(Cfg));
1882         //fprintf(stderr, "Looking for %s\n", Cfg);
1883         if (!FileExists(Cfg)) {
1884             // 2. try "local config".
1885             sprintf(tmp, "%slocalconfig/%s", StartDir, CfgName);
1886             ExpandPath(tmp, Cfg, sizeof(Cfg));
1887             //fprintf(stderr, "Looking for %s\n", Cfg);
1888             if (!FileExists(Cfg)) {
1889                 // 3. /usr/share/fte
1890                 sprintf(tmp, "/usr/share/fte/%s", CfgName);
1891                 ExpandPath(tmp, Cfg, sizeof(Cfg));
1892                 if (!FileExists(Cfg)) {
1893                     sprintf(tmp, "%sconfig/%s", StartDir, CfgName);
1894                     ExpandPath(tmp, Cfg, sizeof(Cfg));
1895                     //fprintf(stderr, "Looking for %s\n", Cfg);
1896                     if (!FileExists(Cfg)) {
1897                         sprintf(tmp, "./%s", CfgName);
1898                         ExpandPath(tmp, Cfg, sizeof(Cfg));
1899                         //fprintf(stderr, "Looking for %s\n", Cfg);
1900                         if (!FileExists(Cfg)) {
1901                             fprintf(stderr, "Cannot find '%s' in:\n"
1902                                     "\t~/.fte,\n""\t%slocalconfig,\n\t/usr/share/fte,\n"
1903                                     "\t%sconfig, or\n"
1904                                     "\t.",
1905                                     CfgName, StartDir, StartDir);
1906                         }
1907                     }
1908                 }
1909             }
1910         }
1911 #else // UNIX
1912         SlashDir(last);
1913         strlcat(last, CfgName, sizeof(last));
1914         ExpandPath(last, Cfg, sizeof(Cfg));
1915 #endif // UNIX
1916     }
1917     // puts(Cfg);
1918 
1919     //fprintf(stderr, "Loading file %s\n", Cfg);
1920     if ((fd = open(Cfg, O_RDONLY | O_BINARY)) == -1) {
1921         fprintf(stderr, "Cannot open '%s', errno=%d\n", Cfg, errno);
1922         return -1;
1923     }
1924     if (fstat(fd, &statbuf) != 0) {
1925         close(fd);
1926         fprintf(stderr, "Cannot stat '%s', errno=%d\n", Cfg, errno);
1927         return -1;
1928     }
1929     buffer = (char *) malloc((size_t)statbuf.st_size+1);
1930     if (buffer == NULL) {
1931         close(fd);
1932         return -1;
1933     }
1934 
1935     buffer[statbuf.st_size] = 0; // add null to end of buffer, NOTE: allocated statbuf.st_size + 1
1936 
1937     if (read(fd, buffer, (size_t)statbuf.st_size) != statbuf.st_size) {
1938         close(fd);
1939         free(buffer);
1940         return -1;
1941     }
1942     close(fd);
1943 
1944     cp.sz = statbuf.st_size;
1945     cp.a = cp.c = buffer;
1946     cp.z = cp.a + cp.sz;
1947     cp.line = 1;
1948     cp.name = Cfg;
1949 
1950     // preprocess configuration file
1951     rc = PreprocessConfigFile(cp);
1952     if (rc == -1)
1953         Fail(cp, "Preprocess failed");
1954 
1955     if (preprocess_only == true)
1956         printf("%s", cp.a);
1957 
1958     // reset pointers
1959     cp.a = cp.c = buffer;
1960     cp.z = cp.a + cp.sz;
1961     cp.line = 1;
1962 
1963     rc = ParseConfigFile(cp);
1964     // puts("End Loading file");
1965     if (Level == 0)
1966         PutNull(cp, CF_EOF);
1967 
1968     if (rc == -1)
1969         Fail(cp, "Parse failed");
1970 
1971     free(buffer);
1972     return rc;
1973 }
1974