it's now should be possible to do 'jam install'
[k8-i-v-a-n.git] / src / felib / fesave.cpp
blob75a20051e2ca12ac637a990d021c6efe25255dc4
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 #include "fesave.h"
25 #include "femath.h"
28 #ifdef USE_ZLIB
29 # define Xgetc gzgetc
30 # define Xfeof gzeof
31 # define Xungc gzungetc
32 # define Xseek gzseek
33 # define Xtell gztell
34 # define Xclre gzclearerr
35 #else
36 # define Xgetc fgetc
37 # define Xfeof feof
38 # define Xungc ungetc
39 # define Xseek fseek
40 # define Xtell ftell
41 # define Xclre clearerr
42 #endif
45 ////////////////////////////////////////////////////////////////////////////////
46 outputfile::outputfile (cfestring &FileName, truth maxcomp, truth AbortOnErr) :
47 #ifdef USE_ZLIB
48 Buffer(gzopen(FileName.CStr(), (maxcomp?"wb9":"wb1"))),
49 #else
50 Buffer(fopen(FileName.CStr(), "wb")),
51 #endif
52 FileName(FileName)
54 if (AbortOnErr && !IsOpen()) ABORT("Can't open %s for output!", FileName.CStr());
58 outputfile::~outputfile () {
59 Close();
63 void outputfile::Close () {
64 if (Buffer) {
65 #ifdef USE_ZLIB
66 gzclose(Buffer);
67 #else
68 fclose(Buffer);
69 #endif
70 Buffer = 0;
75 void outputfile::Flush () {
76 #ifdef USE_ZLIB
77 gzflush(Buffer, Z_FINISH);
78 #else
79 fflush(Buffer);
80 #endif
84 void outputfile::Put (char What) {
85 #ifdef USE_ZLIB
86 gzputc(Buffer, What);
87 #else
88 fputc(What, Buffer);
89 #endif
93 void outputfile::Write (cchar *Offset, sLong Size) {
94 #ifdef USE_ZLIB
95 gzwrite(Buffer, Offset, Size);
96 #else
97 fwrite(Offset, 1, Size, Buffer);
98 #endif
102 ////////////////////////////////////////////////////////////////////////////////
103 truth inputfile::fileExists (const festring &fname) {
104 struct stat st;
105 if (stat(fname.CStr(), &st)) return false;
106 if (!S_ISREG(st.st_mode)) return false;
107 return access(fname.CStr(), R_OK) == 0;
111 festring inputfile::GetMyDir (void) {
112 #ifdef IVAN_DATA_DIR
113 return IVAN_DATA_DIR;
114 #else
115 char myDir[8192];
116 char buf[128];
117 pid_t mypid = getpid();
118 memset(myDir, 0, sizeof(myDir));
119 sprintf(buf, "/proc/%u/exe", (unsigned int)mypid);
120 if (readlink(buf, myDir, sizeof(myDir)-1) < 0) strcpy(myDir, ".");
121 else {
122 char *p = (char *)strrchr(myDir, '/');
123 if (!p) strcpy(myDir, "."); else *p = '\0';
125 if (myDir[strlen(myDir)-1] == '/') myDir[strlen(myDir)-1] = '\0';
126 return myDir;
127 #endif
131 ////////////////////////////////////////////////////////////////////////////////
132 inputfile::inputfile (cfestring &FileName, const valuemap *ValueMap, truth AbortOnErr) :
133 #ifdef USE_ZLIB
134 Buffer(gzopen(FileName.CStr(), "rb")),
135 #else
136 Buffer(fopen(FileName.CStr(), "rb")),
137 #endif
138 FileName(FileName),
139 ValueMap(ValueMap),
140 lastWordWasString(false),
141 #ifdef USE_ZLIB
142 mFileSize(-1),
143 #endif
144 mCharBufPos(0),
145 mCurrentLine(1),
146 mTokenLine(1)
148 if (AbortOnErr && !IsOpen()) ABORT("File %s not found!", FileName.CStr());
152 inputfile::~inputfile () {
153 Close();
157 void inputfile::Close () {
158 if (Buffer) {
159 #ifdef USE_ZLIB
160 gzclose(Buffer);
161 #else
162 fclose(Buffer);
163 #endif
164 Buffer = 0;
169 int inputfile::Get () {
170 if (mCharBufPos > 0) {
171 return mCharBuf[--mCharBufPos];
172 } else if (Buffer) {
173 int ch = Xgetc(Buffer);
175 if (ch == '\n') ++mCurrentLine;
176 return (ch < 0 ? EOF : ch);
177 } else {
178 return EOF;
183 void inputfile::Unget (int ch) {
184 if (ch >= 0) {
185 if (mCharBufPos > 4) die("too many unread chars");
186 mCharBuf[mCharBufPos++] = ch;
191 truth inputfile::Eof () {
192 if (Buffer) return (mCharBufPos == 0 && Xfeof(Buffer));
193 return true;
197 void inputfile::Read (char *Offset, sLong Size) {
198 #ifdef USE_ZLIB
199 if (gzread(Buffer, Offset, Size) != Size) ABORT("File '%s' read error!", FileName.CStr());
200 #else
201 if (fread(Offset, Size, 1, Buffer) != 1) ABORT("File '%s' read error!", FileName.CStr());
202 #endif
206 void inputfile::SeekPosBegin (sLong Offset) {
207 if (Xseek(Buffer, Offset, SEEK_SET) < 0) ABORT("File '%s': seek error!", FileName.CStr());
211 void inputfile::SeekPosCurrent (sLong Offset) {
212 if (Xseek(Buffer, Offset, SEEK_CUR) < 0) ABORT("File '%s': seek error!", FileName.CStr());
216 #ifdef USE_ZLIB
217 void inputfile::SeekPosEnd (sLong Offset) {
218 //HACKHACK: emulate this
219 if (mFileSize < 0) {
220 //SLOOOW, but we have to do that
221 int opos;
222 char *buffer, buf[512];
223 int bufsize;
225 for (bufsize = 256*1024; bufsize > (int)sizeof(buf); bufsize /= 2) {
226 if ((buffer = (char *)malloc(bufsize)) != NULL) break;
228 if (buffer == NULL) { buffer = buf; bufsize = sizeof(buf); }
230 //fprintf(stderr, "determining file size...\n");
231 mFileSize = opos = gztell(Buffer);
232 for (;;) {
233 int len = gzread(Buffer, buffer, bufsize);
235 if (len < 0) { mFileSize = -1; break; } // error
236 mFileSize += len;
237 if (len < bufsize) break; // eof reached
239 if (buffer != buf) free(buffer);
240 //fprintf(stderr, "file size: %d\n", ctx->filesize);
243 if (mFileSize < 0) ABORT("File '%s': seek error!", FileName.CStr());
245 if (gzseek(Buffer, mFileSize+Offset, SEEK_SET) < 0) ABORT("File '%s': seek error!", FileName.CStr());
248 #else
250 void inputfile::SeekPosEnd (sLong Offset) {
251 if (fseek(Buffer, Offset, SEEK_END) < 0) ABORT("File '%s': seek error!", FileName.CStr());
253 #endif
256 sLong inputfile::TellPos () {
257 return Xtell(Buffer);
261 void inputfile::ClearFlags () {
262 Xclre(Buffer);
266 festring inputfile::findVar (cfestring &name, truth *found) const {
267 VarMap::const_iterator i = mVars.find(name);
268 if (i != mVars.end()) {
269 if (found) *found = true;
270 return i->second;
272 if (found) *found = false;
273 return "";
277 festring inputfile::getVar (cfestring &name) {
278 truth found;
279 festring res = findVar(name, &found);
281 if (!found) {
282 if (mGetVar) {
283 res = mGetVar(this, name);
284 } else {
285 festring s = "unknown variable: "+name;
286 die(s);
289 return res;
293 void inputfile::setVar (cfestring &name, cfestring &value) {
294 mVars[name] = value;
298 //TODO: invoke callback
299 truth inputfile::delVar (cfestring &name) {
300 VarMap::iterator i = mVars.find(name);
301 if (i != mVars.end()) {
302 mVars.erase(i);
303 return true;
305 return false;
309 void inputfile::die (cfestring &msg) {
310 ABORT("ERROR in file %s, line %d: %s", GetFileName().CStr(), mTokenLine, msg.CStr());
314 // 0: term
315 // 1: unary
316 // 2: comparisons
317 // 3: &&
318 // 4: ||
319 static const int maxCPrio = 4;
320 static const char *opers[5][7] = {
321 {NULL},
322 {NULL},
323 {"<", ">", "<=", ">=", "==", "!=", NULL},
324 {"&&", NULL},
325 {"||", NULL}
328 festring inputfile::readCondition (festring &token, int prio, truth skipIt) {
329 festring res, op1, opc;
331 //fprintf(stderr, "IN: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
332 switch (prio) {
333 case 0: // term
334 if (token == "(") {
335 readWordIntr(token, true);
336 res = readCondition(token, maxCPrio, skipIt);
337 if (token != ")") die("')' expected");
338 } else if (token == "@") {
339 readWordIntr(token, true);
340 if (!skipIt) res = getVar(token);
341 } else {
342 res = token;
344 readWordIntr(token, true);
345 goto done;
346 //return res;
347 case 1:
348 if (token == "!") {
349 readWordIntr(token, true);
350 res = readCondition(token, 1, skipIt);
351 if (!skipIt) {
352 if (res == "") res = "tan"; else res = "";
354 } else {
355 res = readCondition(token, prio-1, skipIt);
357 goto done;
358 //return res;
361 if (prio > 4) return res;
362 res = readCondition(token, prio-1, skipIt);
363 for (;;) {
364 //readWordIntr(token, true);
365 bool myOp = false;
366 if (token == "=") die("no assignments yet!");
367 if (token == ";") {
368 //fprintf(stderr, " RET: [%s]\n", res.CStr());
369 break;
371 if (token == "less") token = "<";
372 else if (token == "great") token = ">";
373 else if (token == "equ") token = "==";
374 else if (token == "neq") token = "!=";
375 else if (token == "lessequ") token = "<=";
376 else if (token == "greatequ") token = ">=";
377 for (int f = 0; opers[prio][f]; f++) {
378 if (!strcmp(opers[prio][f], token.CStr())) { myOp = true; break; }
380 //fprintf(stderr, "tk: [%s]; %s\n", token.CStr(), myOp?"MY":"skip");
381 if (!myOp) break;
382 opc = token;
383 readWordIntr(token, true);
384 op1 = readCondition(token, prio-1, skipIt);
385 //fprintf(stderr, " prio: %d; opc=[%s]; res=[%s]; op1=[%s]\n", prio, opc.CStr(), res.CStr(), op1.CStr());
386 switch (prio) {
387 case 2: // comparisons
388 if (opc == "==") {
389 if (!skipIt) res = res==op1 ? "tan" : "";
390 } else if (opc == "!=") {
391 if (!skipIt) res = res!=op1 ? "tan" : "";
392 } else if (opc == "<") {
393 if (!skipIt) res = res<op1 ? "tan" : "";
394 } else if (opc == ">") {
395 if (!skipIt) res = res>op1 ? "tan" : "";
396 } else if (opc == "<=") {
397 if (!skipIt) res = res<=op1 ? "tan" : "";
398 } else if (opc == ">=") {
399 if (!skipIt) res = res>=op1 ? "tan" : "";
401 break;
402 case 3: // &&
403 if (opc == "&&") {
404 if (!skipIt) {
405 res = res!=""&&op1!="" ? "tan" : "";
406 if (res == "") skipIt = true;
409 break;
410 case 4: // ||
411 if (opc == "||") {
412 if (!skipIt) {
413 res = res!=""||op1!="" ? "tan" : "";
414 if (res != "") skipIt = true;
417 break;
418 default:
419 die("invalid priority");
422 done:
423 //fprintf(stderr, "OUT: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
424 return res;
428 // stack top:
429 // 1: processing 'then'
430 // 2: processing 'else'
431 // -1: skiping 'then'
432 // -2: skiping 'else'
433 // -3: skiping whole 'if', 'then' part
434 // -4: skiping whole 'if', 'else' part
435 // -666: skiping '{}'
436 // 666: in '{}', processing
437 void inputfile::ReadWord (festring &str, truth AbortOnEOF, truth skipIt) {
438 int prc;
440 for (;;) {
441 if (!mIfStack.empty()) prc = mIfStack.top(); else prc = 0;
442 readWordIntr(str, AbortOnEOF);
443 if (str == "if") {
444 readWordIntr(str, true);
445 festring res = readCondition(str, maxCPrio, prc<0);
446 if (str != ";") die("';' expected");
447 if (prc < 0) {
448 // skiping
449 mIfStack.push(-3);
450 } else {
451 mIfStack.push(res!="" ? 1 : -1);
453 continue;
455 if (str == "else") {
456 switch (prc) {
457 case 1: // processing 'then'
458 mIfStack.pop();
459 mIfStack.push(-2);
460 break;
461 case -1: // skiping 'then'
462 mIfStack.pop();
463 mIfStack.push(2);
464 break;
465 case -3: // skiping whole, 'then'
466 mIfStack.pop();
467 mIfStack.push(-4);
468 break;
469 default: die("unexpected 'else'");
471 continue;
473 if (str == "endif") {
474 switch (prc) {
475 case 1: // processing 'then'
476 case 2: // processing 'else'
477 case -1: // skiping 'then'
478 case -2: // skiping 'else'
479 case -3: // skiping whole, 'then'
480 case -4: // skiping whole, 'else'
481 mIfStack.pop();
482 break;
483 default: die("unexpected 'endif'");
485 continue;
487 if (str == "{") {
488 mIfStack.push(prc>=0 ? 666 : -666);
489 if (prc >= 0) return;
490 continue;
492 if (str == "}") {
493 if (abs(prc) != 666) die("unexpected '}'");
494 mIfStack.pop();
495 if (prc >= 0) return;
496 continue;
498 if (prc >= 0) return;
503 festring inputfile::ReadWord (truth AbortOnEOF) {
504 festring ToReturn;
506 ReadWord(ToReturn, AbortOnEOF);
507 return ToReturn;
511 void inputfile::SkipSpaces () {
512 while (!Eof()) {
513 int ch = Get();
514 if (ch == EOF) break;
515 if ((unsigned char)ch > ' ') {
516 Unget(ch);
517 break;
523 #define MODE_WORD (1)
524 #define MODE_NUMBER (2)
526 #define PUNCT_RETURN (0)
527 #define PUNCT_CONTINUE (1)
530 int inputfile::HandlePunct (festring &String, int Char, int Mode) {
531 mTokenLine = mCurrentLine;
532 if (Char == '/') {
533 // comment? (can be nested)
534 if (!Eof()) {
535 Char = Get();
536 if (Char == '*') {
537 int OldChar = 0, CommentLevel = 1;
539 for (;;) {
540 if (Eof()) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
541 Char = Get();
542 if (OldChar != '*' || Char != '/') {
543 if (OldChar != '/' || Char != '*') OldChar = Char;
544 else {
545 ++CommentLevel;
546 OldChar = 0;
548 } else {
549 if (!--CommentLevel) break;
550 OldChar = 0;
553 return PUNCT_CONTINUE;
555 if (Char == '/') {
556 // comment (to eol)
557 while (!Eof()) {
558 int ch = Get();
559 if (ch == '\n') break;
561 return PUNCT_CONTINUE;
563 Unget(Char);
564 ClearFlags();
566 if (Mode) Unget('/'); else String << '/';
567 return PUNCT_RETURN;
570 if (Mode) {
571 Unget(Char);
572 return PUNCT_RETURN;
575 if (Char == '"') {
576 // string
577 lastWordWasString = true;
578 for (;;) {
579 if (Eof()) ABORT("Unterminated string in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
580 Char = Get();
581 if (Char == '\\') {
582 Char = Get();
583 if (Char == EOF) ABORT("Unterminated string in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
584 switch (Char) {
585 case 't': String << '\t'; break;
586 case 'n': String << '\n'; break;
587 case 'r': String << '\r'; break;
588 case '"': String << '"'; break;
589 default:
590 ABORT("Invalid escape in string in file %s at line %d!", FileName.CStr(), mTokenLine);
592 } else if (Char == '"') {
593 return PUNCT_RETURN;
594 } else {
595 String << char(Char);
598 if (Char != '"') {
599 String << char(Char);
600 OldChar = Char;
601 } else if (OldChar == '\\') {
602 String[String.GetSize()-1] = '"';
603 OldChar = 0;
604 } else return PUNCT_RETURN;
608 String << char(Char);
609 if (!Eof()) {
610 if (Char == '=' || Char == '<' || Char == '>' || Char == '!') {
611 Char = Get();
612 if (Char == '=') String << char(Char); else Unget(Char);
613 } else if (Char == '&' || Char == '|') {
614 int ch = Get();
615 if (Char == ch) String << char(ch); else Unget(ch);
616 } else if (Char == ':') {
617 Char = Get();
618 if (Char == '=') String << char(Char); else Unget(Char);
621 return PUNCT_RETURN;
625 void inputfile::readWordIntr (festring &String, truth AbortOnEOF) {
626 int Mode = 0;
628 String.Empty();
629 lastWordWasString = false;
630 mTokenLine = mCurrentLine;
631 for (int Char = Get(); !Eof(); Char = Get()) {
632 if (isalpha(Char) || Char == '_') {
633 if (!Mode) {
634 mTokenLine = mCurrentLine;
635 Mode = MODE_WORD;
636 } else if (Mode == MODE_NUMBER) {
637 Unget(Char);
638 return;
640 String << char(Char);
641 continue;
643 if (isdigit(Char)) {
644 if (!Mode) {
645 mTokenLine = mCurrentLine;
646 Mode = MODE_NUMBER;
647 } else if (Mode == MODE_WORD) {
648 Unget(Char);
649 return;
651 String << char(Char);
652 continue;
654 if ((Char == ' ' || Char == '\n' || Char == '\r' || Char == '\t') && Mode) return;
655 if (ispunct(Char) && HandlePunct(String, Char, Mode) == PUNCT_RETURN) return;
657 if (AbortOnEOF) ABORT("Unexpected end of file %s!", FileName.CStr());
658 if (Mode) ClearFlags();
662 char inputfile::ReadLetter (truth AbortOnEOF) {
663 mTokenLine = mCurrentLine;
664 for (int Char = Get(); !Eof(); mTokenLine = mCurrentLine, Char = Get()) {
665 if (isalpha(Char) || isdigit(Char)) return Char;
666 if (ispunct(Char)) {
667 if (Char == '/') {
668 if (!Eof()) {
669 Char = Get();
670 if (Char == '*') {
671 int OldChar = 0, CommentLevel = 1;
673 for (;;) {
674 if (Eof()) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName.CStr(), mTokenLine);
675 Char = Get();
676 if (OldChar != '*' || Char != '/') {
677 if (OldChar != '/' || Char != '*') OldChar = Char;
678 else {
679 ++CommentLevel;
680 OldChar = 0;
682 } else {
683 if (!--CommentLevel) break;
684 OldChar = 0;
687 continue;
688 } else {
689 Unget(Char);
692 return '/';
694 return Char;
697 if (AbortOnEOF) ABORT("Unexpected end of file %s!", FileName.CStr());
698 return 0;
702 /* Reads a number or a formula from inputfile. Valid values could be for
703 instance "3", "5 * 4+5", "2+Variable%4" etc. */
704 //sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator) {
705 festring inputfile::ReadNumberIntr (int CallLevel, sLong *num, truth *isString, truth allowStr, truth PreserveTerminator, truth *wasCloseBrc) {
706 sLong Value = 0;
707 festring Word, res;
708 truth NumberCorrect = false;
709 truth firstWord = true;
711 *isString = false;
712 *num = 0;
713 if (wasCloseBrc) *wasCloseBrc = false;
714 mTokenLine = mCurrentLine;
715 for (;;) {
716 ReadWord(Word);
717 if (Word == "@") {
718 // variable
719 ReadWord(Word, true);
720 //fprintf(stderr, "var: [%s]\n", Word.CStr());
721 Word = getVar(Word);
722 //fprintf(stderr, " value: [%s]\n", Word.CStr());
723 const char *s = Word.CStr();
724 char *e;
725 sLong l = strtoll(s, &e, 10);
726 if (*e == '\0') {
727 //fprintf(stderr, " number: [%d]\n", l);
728 Value = l;
729 NumberCorrect = true;
730 continue;
732 if (firstWord && allowStr) {
733 *isString = true;
734 return Word;
735 } else {
736 ABORT("Number expected in file %s, line %d!", FileName.CStr(), mTokenLine);
739 if (firstWord) {
740 if (allowStr && lastWordWasString) {
741 *isString = true;
742 ReadWord(res);
743 if (res.GetSize() == 1) {
744 if (res[0] != ';' && res[0] != ',' && res[0] != ':') {
745 ABORT("Invalid terminator in file %s, line %d!", FileName.CStr(), mTokenLine);
747 if (PreserveTerminator) Unget(res[0]);
748 } else {
749 ABORT("Terminator expected in file %s, line %d!", FileName.CStr(), mTokenLine);
751 return Word;
753 firstWord = false;
755 char First = Word[0];
756 if (isdigit(First)) {
757 Value = atoi(Word.CStr());
758 NumberCorrect = true;
759 continue;
761 if (Word.GetSize() == 1) {
762 if (First == ';' || First == ',' || First == ':' || (wasCloseBrc && First == '}')) {
763 if (First == '}' && wasCloseBrc) *wasCloseBrc = true;
764 if (CallLevel != HIGHEST || PreserveTerminator) Unget(First);
765 *num = Value;
766 return res;
768 if (First == ')') {
769 if ((CallLevel != HIGHEST && CallLevel != 4) || PreserveTerminator) Unget(')');
770 *num = Value;
771 return res;
773 if (First == '~') {
774 Value = ~ReadNumber(4);
775 NumberCorrect = true;
776 continue;
778 /* Convert this into an inline function! */
779 #define CHECK_OP(op, cl) \
780 if (First == #op[0]) { \
781 if (cl < CallLevel) {\
782 Value op##= ReadNumber(cl);\
783 NumberCorrect = true;\
784 continue;\
785 } else {\
786 Unget(#op[0]);\
787 *num = Value;\
788 return res;\
791 CHECK_OP(&, 1); CHECK_OP(|, 1); CHECK_OP(^, 1);
792 CHECK_OP(*, 2); CHECK_OP(/, 2); CHECK_OP(%, 2);
793 CHECK_OP(+, 3); CHECK_OP(-, 3);
794 if (First == '<') {
795 char Next = Get();
796 if (Next == '<')
797 if (1 < CallLevel) {
798 Value <<= ReadNumber(1);
799 NumberCorrect = true;
800 continue;
801 } else {
802 Unget('<');
803 Unget('<');
804 *num = Value;
805 return res;
806 } else {
807 Unget(Next);
810 if (First == '>') {
811 char Next = Get();
812 if (Next == '>')
813 if (1 < CallLevel) {
814 Value >>= ReadNumber(1);
815 NumberCorrect = true;
816 continue;
817 } else {
818 Unget('>');
819 Unget('>');
820 *num = Value;
821 return res;
822 } else {
823 Unget(Next);
826 if (First == '(') {
827 if (NumberCorrect) {
828 Unget('(');
829 *num = Value;
830 return res;
831 } else {
832 Value = ReadNumber(4);
833 NumberCorrect = false;
834 continue;
837 if (First == '=' && CallLevel == HIGHEST) continue;
838 if (First == '#') {
839 // for #defines
840 Unget('#');
841 *num = Value;
842 return res;
846 if (Word == "enum" || Word == "bitenum") {
847 if (CallLevel != HIGHEST || PreserveTerminator) Unget(';');
848 *num = Value;
849 return res;
852 if (Word == "rgb") {
853 int Bits = ReadNumber();
854 if (Bits == 16) {
855 int Red = ReadNumber();
856 int Green = ReadNumber();
857 int Blue = ReadNumber();
858 Value = MakeRGB16(Red, Green, Blue);
859 } else if (Bits == 24) {
860 int Red = ReadNumber();
861 int Green = ReadNumber();
862 int Blue = ReadNumber();
863 Value = MakeRGB24(Red, Green, Blue);
864 } else {
865 ABORT("Illegal RGB bit size %d in file %s, line %d!", Bits, FileName.CStr(), mTokenLine);
867 NumberCorrect = true;
868 continue;
870 if (Word == "true" || Word == "tan") {
871 Value = 1;
872 NumberCorrect = true;
873 continue;
875 if (Word == "false" || Word == "ona") {
876 Value = 0;
877 NumberCorrect = true;
878 continue;
880 if (ValueMap) {
881 valuemap::const_iterator Iterator = ValueMap->find(Word);
882 if (Iterator != ValueMap->end()) {
883 Value = Iterator->second;
884 NumberCorrect = true;
885 continue;
888 ABORT("Odd numeric value \"%s\" encountered in file %s, line %d!", Word.CStr(), FileName.CStr(), mTokenLine);
893 festring inputfile::ReadCode (truth AbortOnEOF) {
894 int sqLevel = 1;
895 char inString = 0;
896 festring res;
898 mTokenLine = mCurrentLine;
899 for (int Char = Get(); !Eof(); Char = Get()) {
900 //fprintf(stderr, "char: [%c]; inString: %d; sqLevel: %d\n", (Char < 32 || Char > 126 ? '?' : Char), inString, sqLevel);
901 if (inString) {
902 res << ((char)Char);
903 if (Char == inString) {
904 inString = 0;
905 } else if (Char == '\\') {
906 if (Eof()) break;
907 Char = Get();
908 res << ((char)Char);
910 } else {
911 if (Char == '[') {
912 ++sqLevel;
913 res << ((char)Char);
914 } else if (Char == ']') {
915 if (--sqLevel == 0) break;
916 res << ((char)Char);
917 } else if (Char == '/') {
918 if (Eof()) { res << ((char)Char); break; }
919 switch ((Char = Get())) {
920 case '/': // eol comment
921 while (!Eof()) if (Get() == '\n') break;
922 break;
923 case '*': // c-like comment
924 while (!Eof()) {
925 if (Get() == '*') {
926 if (Eof()) break;
927 if (Get() == '/') break;
930 break;
931 default:
932 res << '/';
933 res << ((char)Char);
934 break;
936 } else if (Char == '"' || Char == '\'') {
937 res << ((char)Char);
938 inString = ((char)Char);
939 } else {
940 res << ((char)Char);
944 if (AbortOnEOF && Eof()) ABORT("Unexpected end of file %s!", FileName.CStr());
945 return res;
949 sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator, truth *wasCloseBrc) {
950 sLong num = 0;
951 truth isString = false;
953 ReadNumberIntr(CallLevel, &num, &isString, false, PreserveTerminator, wasCloseBrc);
954 return num;
958 festring inputfile::ReadStringOrNumber (sLong *num, truth *isString, truth PreserveTerminator, truth *wasCloseBrc) {
959 return ReadNumberIntr(HIGHEST, num, isString, true, PreserveTerminator, wasCloseBrc);
963 v2 inputfile::ReadVector2d () {
964 v2 Vector;
966 Vector.X = ReadNumber();
967 Vector.Y = ReadNumber();
968 return Vector;
972 rect inputfile::ReadRect () {
973 rect Rect;
975 Rect.X1 = ReadNumber();
976 Rect.Y1 = ReadNumber();
977 Rect.X2 = ReadNumber();
978 Rect.Y2 = ReadNumber();
979 return Rect;
983 outputfile &operator << (outputfile &SaveFile, cfestring &String) {
984 uShort Length = String.GetSize();
985 SaveFile << Length;
986 if (Length) SaveFile.Write(String.CStr(), Length);
987 return SaveFile;
991 inputfile &operator >> (inputfile &SaveFile, festring &String) {
992 char *RealBuffer, StackBuffer[1024];
993 uShort Length;
994 SaveFile >> Length;
995 RealBuffer = Length < 1024 ? StackBuffer : new char[Length+1];
996 String.Empty();
997 if (Length) {
998 SaveFile.Read(RealBuffer, Length);
999 RealBuffer[Length] = 0;
1000 String << RealBuffer;
1002 if (Length >= 1024) delete [] RealBuffer;
1003 return SaveFile;
1007 outputfile &operator << (outputfile &SaveFile, cchar *String) {
1008 uShort Length = String ? strlen(String) : 0;
1009 SaveFile << Length;
1010 if (Length) SaveFile.Write(String, Length);
1011 return SaveFile;
1015 inputfile &operator >> (inputfile &SaveFile, char *&String) {
1016 uShort Length;
1017 SaveFile >> Length;
1018 if (Length) {
1019 String = new char[Length+1];
1020 SaveFile.Read(String, Length);
1021 String[Length] = 0;
1022 } else {
1023 String = 0;
1025 return SaveFile;
1029 void ReadData (festring &String, inputfile &SaveFile) {
1030 SaveFile.ReadWord(String);
1031 if (String == "=") SaveFile.ReadWord(String);
1032 SaveFile.ReadWord();
1036 void ReadData (fearray<sLong> &Array, inputfile &SaveFile) {
1037 Array.Clear();
1038 festring Word;
1039 SaveFile.ReadWord(Word);
1040 if (Word == "==") {
1041 Array.Allocate(1);
1042 Array.Data[0] = SaveFile.ReadNumber();
1043 } else if (Word == ":=") {
1044 SaveFile.ReadWord(Word);
1045 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1047 std::vector<sLong> v;
1049 for (;;) {
1050 truth wasCloseBrc = false;
1051 sLong n = SaveFile.ReadNumber(HIGHEST, false, &wasCloseBrc);
1052 if (wasCloseBrc) break;
1053 v.push_back(n);
1055 Array.Allocate(v.size());
1056 for (unsigned int f = 0; f < v.size(); ++f) Array.Data[f] = v[f];
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());
1060 fearray<sLong>::sizetype Size = SaveFile.ReadNumber();
1061 Array.Allocate(Size);
1062 for (fearray<sLong>::sizetype c = 0; c < Size; ++c) Array.Data[c] = SaveFile.ReadNumber();
1063 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1064 } else {
1065 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1070 void ReadData (fearray<festring> &Array, inputfile &SaveFile) {
1071 Array.Clear();
1072 festring Word;
1073 SaveFile.ReadWord(Word);
1074 if (Word == "==") {
1075 Array.Allocate(1);
1076 SaveFile.ReadWord(Array.Data[0]);
1077 if (SaveFile.ReadWord() != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1078 } else if (Word == ":=") {
1079 SaveFile.ReadWord(Word);
1080 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1082 std::vector<festring> v;
1084 for (;;) {
1085 SaveFile.ReadWord(Word);
1086 if (Word == "}") break;
1087 v.push_back(Word);
1088 SaveFile.ReadWord(Word);
1089 if (Word == "}") break;
1090 if (Word != "," && Word != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1092 Array.Allocate(v.size());
1093 for (unsigned int f = 0; f < v.size(); ++f) Array.Data[f] = v[f];
1094 } else if (Word == "=") {
1095 SaveFile.ReadWord(Word);
1096 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1097 fearray<festring>::sizetype Size = SaveFile.ReadNumber();
1098 Array.Allocate(Size);
1099 for (fearray<festring>::sizetype c = 0; c < Size; ++c) {
1100 SaveFile.ReadWord(Array.Data[c]);
1101 SaveFile.ReadWord(Word);
1102 if (Word != "," && Word != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1104 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1105 } else {
1106 ABORT("Array syntax error: '=', '==' or ':=' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TokenLine());
1112 feuLong inputfile::TellLineOfPos (sLong Pos) {
1113 feuLong Line = 1;
1114 sLong BackupPos = TellPos();
1115 SeekPosBegin(0);
1116 while (TellPos() != Pos) { if (Get() == '\n') ++Line; }
1117 if (TellPos() != BackupPos) SeekPosBegin(BackupPos);
1118 return Line;
1123 ////////////////////////////////////////////////////////////////////////////////
1124 #ifdef USE_ZLIB
1125 meminputfile::meminputfile (cfestring &str, const valuemap *ValueMap) :
1126 inputfile("", ValueMap, false),
1127 buf(0),
1128 bufSize(0),
1129 tfname("")
1131 Close();
1132 char fname[1024];
1133 int fd;
1135 strcpy(fname, "/tmp/ivan.XXXXXX");
1136 fd = mkstemp(fname);
1137 tfname = fname;
1138 //fprintf(stderr, "[%s]\n", tfname.CStr());
1139 if (fd < 0) ABORT("Can't create temporary file!");
1140 write(fd, str.CStr(), str.GetSize());
1141 close(fd);
1142 Buffer = gzopen(fname, "rb");
1143 FileName = "<memory>";
1147 meminputfile::~meminputfile () {
1148 if (buf) free(buf);
1149 Close();
1150 unlink(tfname.CStr());
1154 #else
1157 meminputfile::meminputfile (cfestring &str, const valuemap *ValueMap) :
1158 inputfile("", ValueMap, false),
1159 buf(0),
1160 bufSize(0),
1161 tfname("")
1163 Close();
1164 bufSize = str.GetSize();
1165 buf = (char *)calloc(1, bufSize+1);
1166 memmove(buf, str.CStr(), bufSize);
1167 Buffer = fmemopen(buf, bufSize, "rb");
1168 FileName = "<memory>";
1172 meminputfile::~meminputfile () {
1173 Close();
1174 if (buf) free(buf);
1176 #endif