script parser changed a little
[k8-i-v-a-n.git] / src / felib / fesave.cpp
blob0ad378c5754e2e7ea3380cd0df24cd9fb3a5c675
1 /*
3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
6 * Public License
8 * See LICENSING which should be included
9 * along with this file for more details
12 #ifndef _GNU_SOURCE
13 # define _GNU_SOURCE
14 #endif
16 #include <stdio.h>
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <unistd.h>
22 #include <cctype>
24 #ifdef WIN32
25 # include <windows.h>
26 #endif
28 #include "fesave.h"
29 #include "femath.h"
32 #ifdef USE_ZLIB
33 # define Xgetc gzgetc
34 # define Xfeof gzeof
35 # define Xungc gzungetc
36 # define Xseek gzseek
37 # define Xtell gztell
38 # define Xclre gzclearerr
39 #else
40 # define Xgetc fgetc
41 # define Xfeof feof
42 # define Xungc ungetc
43 # define Xseek fseek
44 # define Xtell ftell
45 # define Xclre clearerr
46 #endif
49 ////////////////////////////////////////////////////////////////////////////////
50 outputfile::outputfile (cfestring &FileName, truth maxcomp, truth AbortOnErr) :
51 #ifdef USE_ZLIB
52 Buffer(gzopen(FileName.CStr(), (maxcomp?"wb9":"wb1"))),
53 #else
54 Buffer(fopen(FileName.CStr(), "wb")),
55 #endif
56 FileName(FileName)
58 if (AbortOnErr && !IsOpen()) ABORT("Can't open %s for output!", FileName.CStr());
62 outputfile::~outputfile () {
63 Close();
67 void outputfile::Close () {
68 if (Buffer) {
69 #ifdef USE_ZLIB
70 gzclose(Buffer);
71 #else
72 fclose(Buffer);
73 #endif
74 Buffer = 0;
79 void outputfile::Flush () {
80 #ifdef USE_ZLIB
81 gzflush(Buffer, Z_FINISH);
82 #else
83 fflush(Buffer);
84 #endif
88 void outputfile::Put (char What) {
89 #ifdef USE_ZLIB
90 gzputc(Buffer, What);
91 #else
92 fputc(What, Buffer);
93 #endif
97 void outputfile::Write (cchar *Offset, sLong Size) {
98 #ifdef USE_ZLIB
99 gzwrite(Buffer, Offset, Size);
100 #else
101 fwrite(Offset, 1, Size, Buffer);
102 #endif
106 ////////////////////////////////////////////////////////////////////////////////
107 truth inputfile::fileExists (const festring &fname) {
108 #ifndef WIN32
109 struct stat st;
110 if (stat(fname.CStr(), &st)) return false;
111 if (!S_ISREG(st.st_mode)) return false;
112 return access(fname.CStr(), R_OK) == 0;
113 #else
114 FILE *fl = fopen(fname.CStr(), "rb");
115 if (fl) fclose(fl);
116 return fl != 0;
117 #endif
121 festring inputfile::GetMyDir (void) {
122 char myDir[8192];
123 #ifndef WIN32
124 char buf[128];
125 pid_t mypid = getpid();
126 memset(myDir, 0, sizeof(myDir));
127 sprintf(buf, "/proc/%u/exe", (unsigned int)mypid);
128 if (readlink(buf, myDir, sizeof(myDir)-1) < 0) strcpy(myDir, ".");
129 else {
130 char *p = (char *)strrchr(myDir, '/');
131 if (!p) strcpy(myDir, "."); else *p = '\0';
133 if (myDir[strlen(myDir)-1] == '/') myDir[strlen(myDir)-1] = '\0';
134 #else
135 char *p;
136 memset(myDir, 0, sizeof(myDir));
137 GetModuleFileName(GetModuleHandle(NULL), myDir, sizeof(myDir)-1);
138 p = strrchr(myDir, '\\');
139 if (!p) strcpy(myDir, "."); else *p = '\0';
140 #endif
141 return myDir;
145 ////////////////////////////////////////////////////////////////////////////////
146 inputfile::inputfile (cfestring &FileName, const valuemap *ValueMap, truth AbortOnErr) :
147 #ifdef USE_ZLIB
148 Buffer(gzopen(FileName.CStr(), "rb")),
149 #else
150 Buffer(fopen(FileName.CStr(), "rb")),
151 #endif
152 FileName(FileName),
153 ValueMap(ValueMap),
154 lastWordWasString(false),
155 #ifdef USE_ZLIB
156 mFileSize(-1),
157 #endif
158 mCharBufPos(0),
159 mCurrentLine(1),
160 mTokenLine(1)
162 if (AbortOnErr && !IsOpen()) ABORT("File %s not found!", FileName.CStr());
166 inputfile::~inputfile () {
167 Close();
171 void inputfile::Close () {
172 if (Buffer) {
173 #ifdef USE_ZLIB
174 gzclose(Buffer);
175 #else
176 fclose(Buffer);
177 #endif
178 Buffer = 0;
183 int inputfile::Get () {
184 if (mCharBufPos > 0) {
185 return mCharBuf[--mCharBufPos];
186 } else if (Buffer) {
187 int ch = Xgetc(Buffer);
189 if (ch == '\n') ++mCurrentLine;
190 return (ch < 0 ? EOF : ch);
191 } else {
192 return EOF;
197 void inputfile::Unget (int ch) {
198 if (ch >= 0) {
199 if (mCharBufPos > 4) die("too many unread chars");
200 mCharBuf[mCharBufPos++] = ch;
205 truth inputfile::Eof () {
206 if (Buffer) return (mCharBufPos == 0 && Xfeof(Buffer));
207 return true;
211 void inputfile::Read (char *Offset, sLong Size) {
212 #ifdef USE_ZLIB
213 if (gzread(Buffer, Offset, Size) != Size) ABORT("File '%s' read error!", FileName.CStr());
214 #else
215 if (fread(Offset, Size, 1, Buffer) != 1) ABORT("File '%s' read error!", FileName.CStr());
216 #endif
220 void inputfile::SeekPosBegin (sLong Offset) {
221 if (Xseek(Buffer, Offset, SEEK_SET) < 0) ABORT("File '%s': seek error!", FileName.CStr());
225 void inputfile::SeekPosCurrent (sLong Offset) {
226 if (Xseek(Buffer, Offset, SEEK_CUR) < 0) ABORT("File '%s': seek error!", FileName.CStr());
230 #ifdef USE_ZLIB
231 void inputfile::SeekPosEnd (sLong Offset) {
232 //HACKHACK: emulate this
233 if (mFileSize < 0) {
234 //SLOOOW, but we have to do that
235 int opos;
236 char *buffer, buf[512];
237 int bufsize;
239 for (bufsize = 256*1024; bufsize > (int)sizeof(buf); bufsize /= 2) {
240 if ((buffer = (char *)malloc(bufsize)) != NULL) break;
242 if (buffer == NULL) { buffer = buf; bufsize = sizeof(buf); }
244 //fprintf(stderr, "determining file size...\n");
245 mFileSize = opos = gztell(Buffer);
246 for (;;) {
247 int len = gzread(Buffer, buffer, bufsize);
249 if (len < 0) { mFileSize = -1; break; } // error
250 mFileSize += len;
251 if (len < bufsize) break; // eof reached
253 if (buffer != buf) free(buffer);
254 //fprintf(stderr, "file size: %d\n", ctx->filesize);
257 if (mFileSize < 0) ABORT("File '%s': seek error!", FileName.CStr());
259 if (gzseek(Buffer, mFileSize+Offset, SEEK_SET) < 0) ABORT("File '%s': seek error!", FileName.CStr());
262 #else
264 void inputfile::SeekPosEnd (sLong Offset) {
265 if (fseek(Buffer, Offset, SEEK_END) < 0) ABORT("File '%s': seek error!", FileName.CStr());
267 #endif
270 sLong inputfile::TellPos () {
271 return Xtell(Buffer);
275 void inputfile::ClearFlags () {
276 Xclre(Buffer);
280 festring inputfile::findVar (cfestring &name, truth *found) const {
281 VarMap::const_iterator i = mVars.find(name);
282 if (i != mVars.end()) {
283 if (found) *found = true;
284 return i->second;
286 if (found) *found = false;
287 return "";
291 festring inputfile::getVar (cfestring &name) {
292 truth found;
293 festring res = findVar(name, &found);
295 if (!found) {
296 if (mGetVar) {
297 res = mGetVar(this, name);
298 } else {
299 festring s = "unknown variable: "+name;
300 die(s);
303 return res;
307 void inputfile::setVar (cfestring &name, cfestring &value) {
308 mVars[name] = value;
312 //TODO: invoke callback
313 truth inputfile::delVar (cfestring &name) {
314 VarMap::iterator i = mVars.find(name);
315 if (i != mVars.end()) {
316 mVars.erase(i);
317 return true;
319 return false;
323 void inputfile::die (cfestring &msg) {
324 ABORT("ERROR in file %s, line %d: %s", GetFileName().CStr(), mTokenLine, msg.CStr());
328 // 0: term
329 // 1: unary
330 // 2: comparisons
331 // 3: &&
332 // 4: ||
333 static const int maxCPrio = 4;
334 static const char *opers[5][7] = {
335 {NULL},
336 {NULL},
337 {"<", ">", "<=", ">=", "==", "!=", NULL},
338 {"&&", NULL},
339 {"||", NULL}
342 festring inputfile::readCondition (festring &token, int prio, truth skipIt) {
343 festring res, op1, opc;
345 //fprintf(stderr, "IN: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
346 switch (prio) {
347 case 0: // term
348 if (token == "(") {
349 readWordIntr(token, true);
350 res = readCondition(token, maxCPrio, skipIt);
351 if (token != ")") die("')' expected");
352 } else if (token == "@") {
353 readWordIntr(token, true);
354 if (!skipIt) res = getVar(token);
355 } else {
356 res = token;
358 readWordIntr(token, true);
359 goto done;
360 //return res;
361 case 1:
362 if (token == "!") {
363 readWordIntr(token, true);
364 res = readCondition(token, 1, skipIt);
365 if (!skipIt) {
366 if (res == "") res = "tan"; else res = "";
368 } else {
369 res = readCondition(token, prio-1, skipIt);
371 goto done;
372 //return res;
375 if (prio > 4) return res;
376 res = readCondition(token, prio-1, skipIt);
377 for (;;) {
378 //readWordIntr(token, true);
379 bool myOp = false;
380 if (token == "=") die("no assignments yet!");
381 if (token == ";") {
382 //fprintf(stderr, " RET: [%s]\n", res.CStr());
383 break;
385 if (token == "less") token = "<";
386 else if (token == "great") token = ">";
387 else if (token == "equ") token = "==";
388 else if (token == "neq") token = "!=";
389 else if (token == "lessequ") token = "<=";
390 else if (token == "greatequ") token = ">=";
391 for (int f = 0; opers[prio][f]; f++) {
392 if (!strcmp(opers[prio][f], token.CStr())) { myOp = true; break; }
394 //fprintf(stderr, "tk: [%s]; %s\n", token.CStr(), myOp?"MY":"skip");
395 if (!myOp) break;
396 opc = token;
397 readWordIntr(token, true);
398 op1 = readCondition(token, prio-1, skipIt);
399 //fprintf(stderr, " prio: %d; opc=[%s]; res=[%s]; op1=[%s]\n", prio, opc.CStr(), res.CStr(), op1.CStr());
400 switch (prio) {
401 case 2: // comparisons
402 if (opc == "==") {
403 if (!skipIt) res = res==op1 ? "tan" : "";
404 } else if (opc == "!=") {
405 if (!skipIt) res = res!=op1 ? "tan" : "";
406 } else if (opc == "<") {
407 if (!skipIt) res = res<op1 ? "tan" : "";
408 } else if (opc == ">") {
409 if (!skipIt) res = res>op1 ? "tan" : "";
410 } else if (opc == "<=") {
411 if (!skipIt) res = res<=op1 ? "tan" : "";
412 } else if (opc == ">=") {
413 if (!skipIt) res = res>=op1 ? "tan" : "";
415 break;
416 case 3: // &&
417 if (opc == "&&") {
418 if (!skipIt) {
419 res = res!=""&&op1!="" ? "tan" : "";
420 if (res == "") skipIt = true;
423 break;
424 case 4: // ||
425 if (opc == "||") {
426 if (!skipIt) {
427 res = res!=""||op1!="" ? "tan" : "";
428 if (res != "") skipIt = true;
431 break;
432 default:
433 die("invalid priority");
436 done:
437 //fprintf(stderr, "OUT: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
438 return res;
442 // stack top:
443 // 1: processing 'then'
444 // 2: processing 'else'
445 // -1: skiping 'then'
446 // -2: skiping 'else'
447 // -3: skiping whole 'if', 'then' part
448 // -4: skiping whole 'if', 'else' part
449 // -666: skiping '{}'
450 // 666: in '{}', processing
451 void inputfile::ReadWord (festring &str, truth AbortOnEOF, truth skipIt) {
452 int prc;
454 for (;;) {
455 if (!mIfStack.empty()) prc = mIfStack.top(); else prc = 0;
456 readWordIntr(str, AbortOnEOF);
457 if (str == "if") {
458 readWordIntr(str, true);
459 festring res = readCondition(str, maxCPrio, prc<0);
460 if (str != ";") die("';' expected");
461 if (prc < 0) {
462 // skiping
463 mIfStack.push(-3);
464 } else {
465 mIfStack.push(res!="" ? 1 : -1);
467 continue;
469 if (str == "else") {
470 switch (prc) {
471 case 1: // processing 'then'
472 mIfStack.pop();
473 mIfStack.push(-2);
474 break;
475 case -1: // skiping 'then'
476 mIfStack.pop();
477 mIfStack.push(2);
478 break;
479 case -3: // skiping whole, 'then'
480 mIfStack.pop();
481 mIfStack.push(-4);
482 break;
483 default: die("unexpected 'else'");
485 continue;
487 if (str == "endif") {
488 switch (prc) {
489 case 1: // processing 'then'
490 case 2: // processing 'else'
491 case -1: // skiping 'then'
492 case -2: // skiping 'else'
493 case -3: // skiping whole, 'then'
494 case -4: // skiping whole, 'else'
495 mIfStack.pop();
496 break;
497 default: die("unexpected 'endif'");
499 continue;
501 if (str == "{") {
502 mIfStack.push(prc>=0 ? 666 : -666);
503 if (prc >= 0) return;
504 continue;
506 if (str == "}") {
507 if (abs(prc) != 666) die("unexpected '}'");
508 mIfStack.pop();
509 if (prc >= 0) return;
510 continue;
512 if (prc >= 0) return;
517 festring inputfile::ReadWord (truth AbortOnEOF) {
518 festring ToReturn;
520 ReadWord(ToReturn, AbortOnEOF);
521 return ToReturn;
525 void inputfile::SkipSpaces () {
526 while (!Eof()) {
527 int ch = Get();
528 if (ch == EOF) break;
529 if ((unsigned char)ch > ' ') {
530 Unget(ch);
531 break;
537 #define MODE_WORD (1)
538 #define MODE_NUMBER (2)
540 #define PUNCT_RETURN (0)
541 #define PUNCT_CONTINUE (1)
544 int inputfile::HandlePunct (festring &String, int Char, int Mode) {
545 mTokenLine = mCurrentLine;
546 if (Char == '/') {
547 // comment? (can be nested)
548 if (!Eof()) {
549 Char = Get();
550 if (Char == '*') {
551 int OldChar = 0, CommentLevel = 1;
553 for (;;) {
554 if (Eof()) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
555 Char = Get();
556 if (OldChar != '*' || Char != '/') {
557 if (OldChar != '/' || Char != '*') OldChar = Char;
558 else {
559 ++CommentLevel;
560 OldChar = 0;
562 } else {
563 if (!--CommentLevel) break;
564 OldChar = 0;
567 return PUNCT_CONTINUE;
569 if (Char == '/') {
570 // comment (to eol)
571 while (!Eof()) {
572 int ch = Get();
573 if (ch == '\n') break;
575 return PUNCT_CONTINUE;
577 Unget(Char);
578 ClearFlags();
580 if (Mode) Unget('/'); else String << '/';
581 return PUNCT_RETURN;
584 if (Mode) {
585 Unget(Char);
586 return PUNCT_RETURN;
589 if (Char == '"') {
590 // string
591 lastWordWasString = true;
592 for (;;) {
593 if (Eof()) ABORT("Unterminated string in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
594 Char = Get();
595 if (Char == '\\') {
596 Char = Get();
597 if (Char == EOF) ABORT("Unterminated string in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
598 switch (Char) {
599 case 't': String << '\t'; break;
600 case 'n': String << '\n'; break;
601 case 'r': String << '\r'; break;
602 case '"': String << '"'; break;
603 default:
604 ABORT("Invalid escape in string in file %s at line %d!", FileName.CStr(), mTokenLine);
606 } else if (Char == '"') {
607 return PUNCT_RETURN;
608 } else {
609 String << char(Char);
612 if (Char != '"') {
613 String << char(Char);
614 OldChar = Char;
615 } else if (OldChar == '\\') {
616 String[String.GetSize()-1] = '"';
617 OldChar = 0;
618 } else return PUNCT_RETURN;
622 String << char(Char);
623 if (!Eof()) {
624 if (Char == '=' || Char == '<' || Char == '>' || Char == '!') {
625 Char = Get();
626 if (Char == '=') String << char(Char); else Unget(Char);
627 } else if (Char == '&' || Char == '|') {
628 int ch = Get();
629 if (Char == ch) String << char(ch); else Unget(ch);
630 } else if (Char == ':') {
631 Char = Get();
632 if (Char == '=') String << char(Char); else Unget(Char);
635 return PUNCT_RETURN;
639 void inputfile::readWordIntr (festring &String, truth AbortOnEOF) {
640 int Mode = 0;
642 String.Empty();
643 lastWordWasString = false;
644 mTokenLine = mCurrentLine;
645 for (int Char = Get(); !Eof(); Char = Get()) {
646 if (isalpha(Char) || Char == '_') {
647 if (!Mode) {
648 mTokenLine = mCurrentLine;
649 Mode = MODE_WORD;
650 } else if (Mode == MODE_NUMBER) {
651 Unget(Char);
652 return;
654 String << char(Char);
655 continue;
657 if (isdigit(Char)) {
658 if (!Mode) {
659 mTokenLine = mCurrentLine;
660 Mode = MODE_NUMBER;
661 } else if (Mode == MODE_WORD) {
662 Unget(Char);
663 return;
665 String << char(Char);
666 continue;
668 if ((Char == ' ' || Char == '\n' || Char == '\r' || Char == '\t') && Mode) return;
669 if (ispunct(Char) && HandlePunct(String, Char, Mode) == PUNCT_RETURN) return;
671 if (AbortOnEOF) ABORT("Unexpected end of file %s!", FileName.CStr());
672 if (Mode) ClearFlags();
676 char inputfile::ReadLetter (truth AbortOnEOF) {
677 mTokenLine = mCurrentLine;
678 for (int Char = Get(); !Eof(); mTokenLine = mCurrentLine, Char = Get()) {
679 if (isalpha(Char) || isdigit(Char)) return Char;
680 if (ispunct(Char)) {
681 if (Char == '/') {
682 if (!Eof()) {
683 Char = Get();
684 if (Char == '*') {
685 int OldChar = 0, CommentLevel = 1;
687 for (;;) {
688 if (Eof()) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
689 Char = Get();
690 if (OldChar != '*' || Char != '/') {
691 if (OldChar != '/' || Char != '*') OldChar = Char;
692 else {
693 ++CommentLevel;
694 OldChar = 0;
696 } else {
697 if (!--CommentLevel) break;
698 OldChar = 0;
701 continue;
702 } else {
703 Unget(Char);
706 return '/';
708 return Char;
711 if (AbortOnEOF) ABORT("Unexpected end of file %s!", FileName.CStr());
712 return 0;
716 /* Reads a number or a formula from inputfile. Valid values could be for
717 instance "3", "5 * 4+5", "2+Variable%4" etc. */
718 //sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator) {
719 festring inputfile::ReadNumberIntr (int CallLevel, sLong *num, truth *isString, truth allowStr, truth PreserveTerminator, truth *wasCloseBrc) {
720 sLong Value = 0;
721 festring Word, res;
722 truth NumberCorrect = false;
723 truth firstWord = true;
725 *isString = false;
726 *num = 0;
727 if (wasCloseBrc) *wasCloseBrc = false;
728 mTokenLine = mCurrentLine;
729 for (;;) {
730 ReadWord(Word);
731 if (Word == "@") {
732 // variable
733 ReadWord(Word, true);
734 //fprintf(stderr, "var: [%s]\n", Word.CStr());
735 Word = getVar(Word);
736 //fprintf(stderr, " value: [%s]\n", Word.CStr());
737 const char *s = Word.CStr();
738 char *e;
739 sLong l = strtoll(s, &e, 10);
740 if (*e == '\0') {
741 //fprintf(stderr, " number: [%d]\n", l);
742 Value = l;
743 NumberCorrect = true;
744 continue;
746 if (firstWord && allowStr) {
747 *isString = true;
748 return Word;
749 } else {
750 ABORT("Number expected in file %s, line %d!", FileName.CStr(), mTokenLine);
753 if (firstWord) {
754 if (allowStr && lastWordWasString) {
755 *isString = true;
756 ReadWord(res);
757 if (res.GetSize() == 1) {
758 if (res[0] != ';' && res[0] != ',' && res[0] != ':') {
759 ABORT("Invalid terminator in file %s, line %d!", FileName.CStr(), mTokenLine);
761 if (PreserveTerminator) Unget(res[0]);
762 } else {
763 ABORT("Terminator expected in file %s, line %d!", FileName.CStr(), mTokenLine);
765 return Word;
767 firstWord = false;
769 char First = Word[0];
770 if (isdigit(First)) {
771 Value = atoi(Word.CStr());
772 NumberCorrect = true;
773 continue;
775 if (Word.GetSize() == 1) {
776 if (First == ';' || First == ',' || First == ':' || (wasCloseBrc && First == '}')) {
777 if (First == '}' && wasCloseBrc) *wasCloseBrc = true;
778 if (CallLevel != HIGHEST || PreserveTerminator) Unget(First);
779 *num = Value;
780 return res;
782 if (First == ')') {
783 if ((CallLevel != HIGHEST && CallLevel != 4) || PreserveTerminator) Unget(')');
784 *num = Value;
785 return res;
787 if (First == '~') {
788 Value = ~ReadNumber(4);
789 NumberCorrect = true;
790 continue;
792 /* Convert this into an inline function! */
793 #define CHECK_OP(op, cl) \
794 if (First == #op[0]) { \
795 if (cl < CallLevel) {\
796 Value op##= ReadNumber(cl);\
797 NumberCorrect = true;\
798 continue;\
799 } else {\
800 Unget(#op[0]);\
801 *num = Value;\
802 return res;\
805 CHECK_OP(&, 1); CHECK_OP(|, 1); CHECK_OP(^, 1);
806 CHECK_OP(*, 2); CHECK_OP(/, 2); CHECK_OP(%, 2);
807 CHECK_OP(+, 3); CHECK_OP(-, 3);
808 if (First == '<') {
809 char Next = Get();
810 if (Next == '<')
811 if (1 < CallLevel) {
812 Value <<= ReadNumber(1);
813 NumberCorrect = true;
814 continue;
815 } else {
816 Unget('<');
817 Unget('<');
818 *num = Value;
819 return res;
820 } else {
821 Unget(Next);
824 if (First == '>') {
825 char Next = Get();
826 if (Next == '>')
827 if (1 < CallLevel) {
828 Value >>= ReadNumber(1);
829 NumberCorrect = true;
830 continue;
831 } else {
832 Unget('>');
833 Unget('>');
834 *num = Value;
835 return res;
836 } else {
837 Unget(Next);
840 if (First == '(') {
841 if (NumberCorrect) {
842 Unget('(');
843 *num = Value;
844 return res;
845 } else {
846 Value = ReadNumber(4);
847 NumberCorrect = false;
848 continue;
851 if (First == '=' && CallLevel == HIGHEST) continue;
852 if (First == '#') {
853 // for #defines
854 Unget('#');
855 *num = Value;
856 return res;
860 if (Word == "enum" || Word == "bitenum") {
861 if (CallLevel != HIGHEST || PreserveTerminator) Unget(';');
862 *num = Value;
863 return res;
866 if (Word == "rgb") {
867 int Bits = ReadNumber();
868 if (Bits == 16) {
869 int Red = ReadNumber();
870 int Green = ReadNumber();
871 int Blue = ReadNumber();
872 Value = MakeRGB16(Red, Green, Blue);
873 } else if (Bits == 24) {
874 int Red = ReadNumber();
875 int Green = ReadNumber();
876 int Blue = ReadNumber();
877 Value = MakeRGB24(Red, Green, Blue);
878 } else {
879 ABORT("Illegal RGB bit size %d in file %s, line %d!", Bits, FileName.CStr(), mTokenLine);
881 NumberCorrect = true;
882 continue;
884 if (Word == "true" || Word == "tan") {
885 Value = 1;
886 NumberCorrect = true;
887 continue;
889 if (Word == "false" || Word == "ona") {
890 Value = 0;
891 NumberCorrect = true;
892 continue;
894 if (ValueMap) {
895 valuemap::const_iterator Iterator = ValueMap->find(Word);
896 if (Iterator != ValueMap->end()) {
897 Value = Iterator->second;
898 NumberCorrect = true;
899 continue;
902 ABORT("Odd numeric value \"%s\" encountered in file %s, line %d!", Word.CStr(), FileName.CStr(), mTokenLine);
907 festring inputfile::ReadCode (truth AbortOnEOF) {
908 int sqLevel = 1;
909 char inString = 0;
910 festring res;
912 mTokenLine = mCurrentLine;
913 for (int Char = Get(); !Eof(); Char = Get()) {
914 //fprintf(stderr, "char: [%c]; inString: %d; sqLevel: %d\n", (Char < 32 || Char > 126 ? '?' : Char), inString, sqLevel);
915 if (inString) {
916 res << Char;
917 if (Char == inString) {
918 inString = 0;
919 } else if (Char == '\\') {
920 if (Eof()) break;
921 Char = Get();
922 res << Char;
924 } else {
925 if (Char == '[') {
926 ++sqLevel;
927 res << Char;
928 } else if (Char == ']') {
929 if (--sqLevel == 0) break;
930 res << Char;
931 } else if (Char == '/') {
932 if (Eof()) { res << Char; break; }
933 switch ((Char = Get())) {
934 case '/': // eol comment
935 while (!Eof()) if (Get() == '\n') break;
936 break;
937 case '*': // c-like comment
938 while (!Eof()) {
939 if (Get() == '*') {
940 if (Eof()) break;
941 if (Get() == '/') break;
944 break;
945 default:
946 res << '/';
947 res << Char;
948 break;
950 } else if (Char == '"' || Char == '\'') {
951 res << Char;
952 inString = Char;
953 } else {
954 res << Char;
958 if (AbortOnEOF && Eof()) ABORT("Unexpected end of file %s!", FileName.CStr());
959 return res;
963 sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator, truth *wasCloseBrc) {
964 sLong num = 0;
965 truth isString = false;
967 ReadNumberIntr(CallLevel, &num, &isString, false, PreserveTerminator, wasCloseBrc);
968 return num;
972 festring inputfile::ReadStringOrNumber (sLong *num, truth *isString, truth PreserveTerminator, truth *wasCloseBrc) {
973 return ReadNumberIntr(HIGHEST, num, isString, true, PreserveTerminator, wasCloseBrc);
977 v2 inputfile::ReadVector2d () {
978 v2 Vector;
980 Vector.X = ReadNumber();
981 Vector.Y = ReadNumber();
982 return Vector;
986 rect inputfile::ReadRect () {
987 rect Rect;
989 Rect.X1 = ReadNumber();
990 Rect.Y1 = ReadNumber();
991 Rect.X2 = ReadNumber();
992 Rect.Y2 = ReadNumber();
993 return Rect;
997 outputfile &operator << (outputfile &SaveFile, cfestring &String) {
998 uShort Length = String.GetSize();
999 SaveFile << Length;
1000 if (Length) SaveFile.Write(String.CStr(), Length);
1001 return SaveFile;
1005 inputfile &operator >> (inputfile &SaveFile, festring &String) {
1006 char *RealBuffer, StackBuffer[1024];
1007 uShort Length;
1008 SaveFile >> Length;
1009 RealBuffer = Length < 1024 ? StackBuffer : new char[Length+1];
1010 String.Empty();
1011 if (Length) {
1012 SaveFile.Read(RealBuffer, Length);
1013 RealBuffer[Length] = 0;
1014 String << RealBuffer;
1016 if (Length >= 1024) delete [] RealBuffer;
1017 return SaveFile;
1021 outputfile &operator << (outputfile &SaveFile, cchar *String) {
1022 uShort Length = String ? strlen(String) : 0;
1023 SaveFile << Length;
1024 if (Length) SaveFile.Write(String, Length);
1025 return SaveFile;
1029 inputfile &operator >> (inputfile &SaveFile, char *&String) {
1030 uShort Length;
1031 SaveFile >> Length;
1032 if (Length) {
1033 String = new char[Length+1];
1034 SaveFile.Read(String, Length);
1035 String[Length] = 0;
1036 } else {
1037 String = 0;
1039 return SaveFile;
1043 void ReadData (festring &String, inputfile &SaveFile) {
1044 SaveFile.ReadWord(String);
1045 if (String == "=") SaveFile.ReadWord(String);
1046 SaveFile.ReadWord();
1050 void ReadData (fearray<sLong> &Array, inputfile &SaveFile) {
1051 Array.Clear();
1052 festring Word;
1053 SaveFile.ReadWord(Word);
1054 if (Word == "==") {
1055 Array.Allocate(1);
1056 Array.Data[0] = SaveFile.ReadNumber();
1057 } else if (Word == ":=") {
1058 SaveFile.ReadWord(Word);
1059 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1061 std::vector<sLong> v;
1063 for (;;) {
1064 truth wasCloseBrc = false;
1065 sLong n = SaveFile.ReadNumber(HIGHEST, false, &wasCloseBrc);
1066 if (wasCloseBrc) break;
1067 v.push_back(n);
1069 Array.Allocate(v.size());
1070 for (unsigned int f = 0; f < v.size(); ++f) Array.Data[f] = v[f];
1071 } else if (Word == "=") {
1072 SaveFile.ReadWord(Word);
1073 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1074 fearray<sLong>::sizetype Size = SaveFile.ReadNumber();
1075 Array.Allocate(Size);
1076 for (fearray<sLong>::sizetype c = 0; c < Size; ++c) Array.Data[c] = SaveFile.ReadNumber();
1077 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1078 } else {
1079 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1084 void ReadData (fearray<festring> &Array, inputfile &SaveFile) {
1085 Array.Clear();
1086 festring Word;
1087 SaveFile.ReadWord(Word);
1088 if (Word == "==") {
1089 Array.Allocate(1);
1090 SaveFile.ReadWord(Array.Data[0]);
1091 if (SaveFile.ReadWord() != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1092 } else if (Word == ":=") {
1093 SaveFile.ReadWord(Word);
1094 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1096 std::vector<festring> v;
1098 for (;;) {
1099 SaveFile.ReadWord(Word);
1100 if (Word == "}") break;
1101 v.push_back(Word);
1102 SaveFile.ReadWord(Word);
1103 if (Word == "}") break;
1104 if (Word != "," && Word != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1106 Array.Allocate(v.size());
1107 for (unsigned int f = 0; f < v.size(); ++f) Array.Data[f] = v[f];
1108 } else if (Word == "=") {
1109 SaveFile.ReadWord(Word);
1110 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1111 fearray<festring>::sizetype Size = SaveFile.ReadNumber();
1112 Array.Allocate(Size);
1113 for (fearray<festring>::sizetype c = 0; c < Size; ++c) {
1114 SaveFile.ReadWord(Array.Data[c]);
1115 SaveFile.ReadWord(Word);
1116 if (Word != "," && Word != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1118 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1119 } else {
1120 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1126 feuLong inputfile::TellLineOfPos (sLong Pos) {
1127 feuLong Line = 1;
1128 sLong BackupPos = TellPos();
1129 SeekPosBegin(0);
1130 while (TellPos() != Pos) { if (Get() == '\n') ++Line; }
1131 if (TellPos() != BackupPos) SeekPosBegin(BackupPos);
1132 return Line;
1137 ////////////////////////////////////////////////////////////////////////////////
1138 #ifdef USE_ZLIB
1139 meminputfile::meminputfile (cfestring &str, const valuemap *ValueMap) :
1140 inputfile("", ValueMap, false),
1141 buf(0),
1142 bufSize(0),
1143 tfname("")
1145 Close();
1146 #ifdef WIN32
1147 char nbuf[MAX_PATH+1], tfn[MAX_PATH+1];
1148 GetTempPath(MAX_PATH, nbuf);
1149 GetTempFileName(nbuf, "ivan", 0, tfn);
1150 tfname = tfn;
1151 FILE *fl = fopen(tfn, "wb");
1152 fwrite(str.CStr(), str.GetSize(), 1, fl);
1153 fclose(fl);
1154 Buffer = gzopen(tfn, "rb");
1155 #else
1156 char fname[1024];
1157 int fd;
1159 strcpy(fname, "/tmp/ivan.XXXXXX");
1160 fd = mkstemp(fname);
1161 tfname = fname;
1162 //fprintf(stderr, "[%s]\n", tfname.CStr());
1163 if (fd < 0) ABORT("Can't create temporary file!");
1164 write(fd, str.CStr(), str.GetSize());
1165 close(fd);
1166 Buffer = gzopen(fname, "rb");
1167 #endif
1168 FileName = "<memory>";
1172 meminputfile::~meminputfile () {
1173 if (buf) free(buf);
1174 Close();
1175 unlink(tfname.CStr());
1179 #else
1182 meminputfile::meminputfile (cfestring &str, const valuemap *ValueMap) :
1183 inputfile("", ValueMap, false),
1184 buf(0),
1185 bufSize(0),
1186 tfname("")
1188 Close();
1189 #ifdef WIN32
1190 char nbuf[MAX_PATH+1], tfn[MAX_PATH+1];
1191 GetTempPath(MAX_PATH, nbuf);
1192 GetTempFileName(nbuf, "ivan", 0, tfn);
1193 tfname = tfn;
1194 FILE *fl = fopen(tfn, "wb");
1195 fwrite(str.CStr(), str.GetSize(), 1, fl);
1196 fclose(fl);
1197 Buffer = fopen(tfn, "rb");
1198 #else
1199 bufSize = str.GetSize();
1200 buf = (char *)calloc(1, bufSize+1);
1201 memmove(buf, str.CStr(), bufSize);
1202 Buffer = fmemopen(buf, bufSize, "rb");
1203 #endif
1204 FileName = "<memory>";
1208 meminputfile::~meminputfile () {
1209 Close();
1210 if (buf) free(buf);
1211 #ifdef WIN32
1212 DeleteFile(tfname.CStr());
1213 #endif
1215 #endif