zlib support seems to work for GNU/Linux but segfaults on windoze; so be it
[k8-i-v-a-n.git] / src / felib / fesave.cpp
blob11ce2821e6a1c91b86b4f4046d84ccc75a68a55f
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 AbortOnErr) :
51 #ifdef USE_ZLIB
52 Buffer(gzopen(FileName.CStr(), "wb9")),
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 inputfile::inputfile (cfestring &FileName, const valuemap *ValueMap, truth AbortOnErr) :
146 #ifdef USE_ZLIB
147 Buffer(gzopen(FileName.CStr(), "rb")),
148 #else
149 Buffer(fopen(FileName.CStr(), "rb")),
150 #endif
151 FileName(FileName),
152 ValueMap(ValueMap),
153 lastWordWasString(false)
154 #ifdef USE_ZLIB
155 , mFileSize(-1)
156 #endif
158 if (AbortOnErr && !IsOpen()) ABORT("File %s not found!", FileName.CStr());
162 inputfile::~inputfile () {
163 Close();
167 void inputfile::Close () {
168 if (Buffer) {
169 #ifdef USE_ZLIB
170 gzclose(Buffer);
171 #else
172 fclose(Buffer);
173 #endif
174 Buffer = 0;
179 int inputfile::Get () {
180 return Xgetc(Buffer);
184 int inputfile::Unget (int ch) {
185 return Xungc(ch, Buffer);
189 void inputfile::Read (char *Offset, sLong Size) {
190 #ifdef USE_ZLIB
191 if (gzread(Buffer, Offset, Size) != Size) ABORT("File '%s' read error!", FileName.CStr());
192 #else
193 if (fread(Offset, Size, 1, Buffer) != 1) ABORT("File '%s' read error!", FileName.CStr());
194 #endif
198 truth inputfile::Eof () {
199 return Xfeof(Buffer);
203 void inputfile::SeekPosBegin (sLong Offset) {
204 if (Xseek(Buffer, Offset, SEEK_SET) < 0) ABORT("File '%s': seek error!", FileName.CStr());
208 void inputfile::SeekPosCurrent (sLong Offset) {
209 if (Xseek(Buffer, Offset, SEEK_CUR) < 0) ABORT("File '%s': seek error!", FileName.CStr());
213 #ifdef USE_ZLIB
214 void inputfile::SeekPosEnd (sLong Offset) {
215 //HACKHACK: emulate this
216 if (mFileSize < 0) {
217 //SLOOOW, but we have to do that
218 int opos;
219 char *buffer, buf[512];
220 int bufsize;
222 for (bufsize = 256*1024; bufsize > (int)sizeof(buf); bufsize /= 2) {
223 if ((buffer = (char *)malloc(bufsize)) != NULL) break;
225 if (buffer == NULL) { buffer = buf; bufsize = sizeof(buf); }
227 //fprintf(stderr, "determining file size...\n");
228 mFileSize = opos = gztell(Buffer);
229 for (;;) {
230 int len = gzread(Buffer, buffer, bufsize);
232 if (len < 0) { mFileSize = -1; break; } // error
233 mFileSize += len;
234 if (len < bufsize) break; // eof reached
236 if (buffer != buf) free(buffer);
237 //fprintf(stderr, "file size: %d\n", ctx->filesize);
240 if (mFileSize < 0) ABORT("File '%s': seek error!", FileName.CStr());
242 if (gzseek(Buffer, mFileSize+Offset, SEEK_SET) < 0) ABORT("File '%s': seek error!", FileName.CStr());
245 #else
247 void inputfile::SeekPosEnd (sLong Offset) {
248 if (fseek(Buffer, Offset, SEEK_END) < 0) ABORT("File '%s': seek error!", FileName.CStr());
250 #endif
253 sLong inputfile::TellPos () {
254 return Xtell(Buffer);
258 void inputfile::ClearFlags () {
259 Xclre(Buffer);
263 festring inputfile::findVar (cfestring &name, truth *found) const {
264 VarMap::const_iterator i = mVars.find(name);
265 if (i != mVars.end()) {
266 if (found) *found = true;
267 return i->second;
269 if (found) *found = false;
270 return "";
274 festring inputfile::getVar (cfestring &name) {
275 truth found;
276 festring res = findVar(name, &found);
278 if (!found) {
279 if (mGetVar) {
280 res = mGetVar(this, name);
281 } else {
282 festring s = "unknown variable: "+name;
283 die(s);
286 return res;
290 void inputfile::setVar (cfestring &name, cfestring &value) {
291 mVars[name] = value;
295 //TODO: invoke callback
296 truth inputfile::delVar (cfestring &name) {
297 VarMap::iterator i = mVars.find(name);
298 if (i != mVars.end()) {
299 mVars.erase(i);
300 return true;
302 return false;
306 void inputfile::die (cfestring &msg) {
307 ABORT("ERROR in file %s, line %d: %s", GetFileName().CStr(), TellLine(), msg.CStr());
311 // 0: term
312 // 1: unary
313 // 2: comparisons
314 // 3: &&
315 // 4: ||
316 const int maxCPrio = 4;
317 const char *opers[5][7] = {
318 {NULL},
319 {NULL},
320 {"<", ">", "<=", ">=", "==", "!=", NULL},
321 {"&&", NULL},
322 {"||", NULL}
325 festring inputfile::readCondition (festring &token, int prio, truth skipIt) {
326 festring res, op1, opc;
327 //fprintf(stderr, "IN: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
328 switch (prio) {
329 case 0: // term
330 if (token == "(") {
331 readWordIntr(token, true);
332 res = readCondition(token, maxCPrio, skipIt);
333 if (token != ")") die("')' expected");
334 } else if (token == "@") {
335 readWordIntr(token, true);
336 if (!skipIt) res = getVar(token);
337 } else {
338 res = token;
340 readWordIntr(token, true);
341 goto done;
342 //return res;
343 case 1:
344 if (token == "!") {
345 readWordIntr(token, true);
346 res = readCondition(token, 1, skipIt);
347 if (!skipIt) {
348 if (res == "") res = "tan"; else res = "";
350 } else {
351 res = readCondition(token, prio-1, skipIt);
353 goto done;
354 //return res;
357 if (prio > 4) return res;
358 res = readCondition(token, prio-1, skipIt);
359 for (;;) {
360 //readWordIntr(token, true);
361 bool myOp = false;
362 if (token == "=") die("no assignments yet!");
363 if (token == ";") {
364 //fprintf(stderr, " RET: [%s]\n", res.CStr());
365 break;
367 if (token == "less") token = "<";
368 else if (token == "great") token = ">";
369 else if (token == "equ") token = "==";
370 else if (token == "neq") token = "!=";
371 else if (token == "lessequ") token = "<=";
372 else if (token == "greatequ") token = ">=";
373 for (int f = 0; opers[prio][f]; f++) {
374 if (!strcmp(opers[prio][f], token.CStr())) { myOp = true; break; }
376 //fprintf(stderr, "tk: [%s]; %s\n", token.CStr(), myOp?"MY":"skip");
377 if (!myOp) break;
378 opc = token;
379 readWordIntr(token, true);
380 op1 = readCondition(token, prio-1, skipIt);
381 //fprintf(stderr, " prio: %d; opc=[%s]; res=[%s]; op1=[%s]\n", prio, opc.CStr(), res.CStr(), op1.CStr());
382 switch (prio) {
383 case 2: // comparisons
384 if (opc == "==") {
385 if (!skipIt) res = res==op1 ? "tan" : "";
386 } else if (opc == "!=") {
387 if (!skipIt) res = res!=op1 ? "tan" : "";
388 } else 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" : "";
397 break;
398 case 3: // &&
399 if (opc == "&&") {
400 if (!skipIt) {
401 res = res!=""&&op1!="" ? "tan" : "";
402 if (res == "") skipIt = true;
405 break;
406 case 4: // ||
407 if (opc == "||") {
408 if (!skipIt) {
409 res = res!=""||op1!="" ? "tan" : "";
410 if (res != "") skipIt = true;
413 break;
414 default:
415 die("invalid priority");
418 done:
419 //fprintf(stderr, "OUT: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
420 return res;
424 // stack top:
425 // 1: processing 'then'
426 // 2: processing 'else'
427 // -1: skiping 'then'
428 // -2: skiping 'else'
429 // -3: skiping whole 'if', 'then' part
430 // -4: skiping whole 'if', 'else' part
431 // -666: skiping '{}'
432 // 666: in '{}', processing
433 void inputfile::ReadWord (festring &str, truth AbortOnEOF, truth skipIt) {
434 int prc;
435 for (;;) {
436 if (!mIfStack.empty()) prc = mIfStack.top(); else prc = 0;
437 readWordIntr(str, AbortOnEOF);
438 if (str == "if") {
439 readWordIntr(str, true);
440 festring res = readCondition(str, maxCPrio, prc<0);
441 if (str != ";") die("';' expected");
442 if (prc < 0) {
443 // skiping
444 mIfStack.push(-3);
445 } else {
446 mIfStack.push(res!="" ? 1 : -1);
448 continue;
450 if (str == "else") {
451 switch (prc) {
452 case 1: // processing 'then'
453 mIfStack.pop();
454 mIfStack.push(-2);
455 break;
456 case -1: // skiping 'then'
457 mIfStack.pop();
458 mIfStack.push(2);
459 break;
460 case -3: // skiping whole, 'then'
461 mIfStack.pop();
462 mIfStack.push(-4);
463 break;
464 default: die("unexpected 'else'");
466 continue;
468 if (str == "endif") {
469 switch (prc) {
470 case 1: // processing 'then'
471 case 2: // processing 'else'
472 case -1: // skiping 'then'
473 case -2: // skiping 'else'
474 case -3: // skiping whole, 'then'
475 case -4: // skiping whole, 'else'
476 mIfStack.pop();
477 break;
478 default: die("unexpected 'endif'");
480 continue;
482 if (str == "{") {
483 mIfStack.push(prc>=0 ? 666 : -666);
484 if (prc >= 0) return;
485 continue;
487 if (str == "}") {
488 if (abs(prc) != 666) die("unexpected '}'");
489 mIfStack.pop();
490 if (prc >= 0) return;
491 continue;
493 if (prc >= 0) return;
498 festring inputfile::ReadWord (truth AbortOnEOF) {
499 festring ToReturn;
500 ReadWord(ToReturn, AbortOnEOF);
501 return ToReturn;
505 void inputfile::SkipSpaces () {
506 while (!Xfeof(Buffer)) {
507 int ch = Xgetc(Buffer);
508 if (ch == EOF) break;
509 if ((unsigned char)ch > ' ') {
510 Xungc(ch, Buffer);
511 break;
517 #define MODE_WORD 1
518 #define MODE_NUMBER 2
520 #define PUNCT_RETURN 0
521 #define PUNCT_CONTINUE 1
523 int inputfile::HandlePunct (festring &String, int Char, int Mode) {
524 if (Char == '/') {
525 // comment? (can be nested)
526 if (!Xfeof(Buffer)) {
527 Char = Xgetc(Buffer);
528 if (Char == '*') {
529 sLong StartPos = TellPos();
530 int OldChar = 0, CommentLevel = 1;
531 for (;;) {
532 if (Xfeof(Buffer)) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName.CStr(), TellLineOfPos(StartPos));
533 Char = Xgetc(Buffer);
534 if (OldChar != '*' || Char != '/') {
535 if (OldChar != '/' || Char != '*') OldChar = Char;
536 else {
537 ++CommentLevel;
538 OldChar = 0;
540 } else {
541 if (!--CommentLevel) break;
542 OldChar = 0;
545 return PUNCT_CONTINUE;
547 if (Char == '/') {
548 // comment (to eol)
549 while (!Xfeof(Buffer)) {
550 int ch = Xgetc(Buffer);
551 if (ch == '\n') break;
553 return PUNCT_CONTINUE;
555 Xungc(Char, Buffer);
556 Xclre(Buffer);
558 if (Mode) Xungc('/', Buffer); else String << '/';
559 return PUNCT_RETURN;
562 if (Mode) {
563 Xungc(Char, Buffer);
564 return PUNCT_RETURN;
567 if (Char == '"') {
568 // string
569 lastWordWasString = true;
570 sLong StartPos = TellPos();
571 for (;;) {
572 if (Xfeof(Buffer)) ABORT("Unterminated string in file %s, beginning at line %d!", FileName.CStr(), TellLineOfPos(StartPos));
573 Char = Xgetc(Buffer);
574 if (Char == '\\') {
575 Char = Xgetc(Buffer);
576 if (Char == EOF) ABORT("Unterminated string in file %s, beginning at line %d!", FileName.CStr(), TellLineOfPos(StartPos));
577 switch (Char) {
578 case 't': String << '\t'; break;
579 case 'n': String << '\n'; break;
580 case 'r': String << '\r'; break;
581 case '"': String << '"'; break;
582 default:
583 ABORT("Invalid escape in string in file %s at line %d!", FileName.CStr(), TellLine());
585 } else if (Char == '"') {
586 return PUNCT_RETURN;
587 } else {
588 String << char(Char);
591 if (Char != '"') {
592 String << char(Char);
593 OldChar = Char;
594 } else if (OldChar == '\\') {
595 String[String.GetSize()-1] = '"';
596 OldChar = 0;
597 } else return PUNCT_RETURN;
601 String << char(Char);
602 if (!Xfeof(Buffer)) {
603 if (Char == '=' || Char == '<' || Char == '>' || Char == '!') {
604 Char = Xgetc(Buffer);
605 if (Char == '=') String << char(Char); else Xungc(Char, Buffer);
606 } else if (Char == '&' || Char == '|') {
607 int ch = Xgetc(Buffer);
608 if (Char == ch) String << char(ch); else Xungc(ch, Buffer);
611 return PUNCT_RETURN;
615 void inputfile::readWordIntr (festring &String, truth AbortOnEOF) {
616 int Mode = 0;
617 String.Empty();
618 lastWordWasString = false;
619 for (int Char = Xgetc(Buffer); !Xfeof(Buffer); Char = Xgetc(Buffer)) {
620 if (isalpha(Char) || Char == '_') {
621 if (!Mode) Mode = MODE_WORD;
622 else if (Mode == MODE_NUMBER) {
623 Xungc(Char, Buffer);
624 return;
626 String << char(Char);
627 continue;
629 if (isdigit(Char)) {
630 if (!Mode) Mode = MODE_NUMBER;
631 else if (Mode == MODE_WORD) {
632 Xungc(Char, Buffer);
633 return;
635 String << char(Char);
636 continue;
638 if ((Char == ' ' || Char == '\n' || Char == '\r' || Char == '\t') && Mode) return;
639 if (ispunct(Char) && HandlePunct(String, Char, Mode) == PUNCT_RETURN) return;
641 if (AbortOnEOF) ABORT("Unexpected end of file %s!", FileName.CStr());
642 if (Mode) Xclre(Buffer);
646 char inputfile::ReadLetter (truth AbortOnEOF) {
647 for (int Char = Xgetc(Buffer); !Xfeof(Buffer); Char = Xgetc(Buffer)) {
648 if (isalpha(Char) || isdigit(Char)) return Char;
649 if (ispunct(Char)) {
650 if (Char == '/') {
651 if (!Xfeof(Buffer)) {
652 Char = Xgetc(Buffer);
653 if (Char == '*') {
654 sLong StartPos = TellPos();
655 int OldChar = 0, CommentLevel = 1;
656 for (;;) {
657 if (Xfeof(Buffer)) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName.CStr(), TellLineOfPos(StartPos));
658 Char = Xgetc(Buffer);
659 if (OldChar != '*' || Char != '/') {
660 if (OldChar != '/' || Char != '*') OldChar = Char;
661 else {
662 ++CommentLevel;
663 OldChar = 0;
665 } else {
666 if (!--CommentLevel) break;
667 OldChar = 0;
670 continue;
671 } else {
672 Xungc(Char, Buffer);
675 return '/';
677 return Char;
680 if (AbortOnEOF) ABORT("Unexpected end of file %s!", FileName.CStr());
681 return 0;
685 /* Reads a number or a formula from inputfile. Valid values could be for
686 instance "3", "5 * 4+5", "2+Variable%4" etc. */
687 //sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator) {
688 festring inputfile::ReadNumberIntr (int CallLevel, sLong *num, truth *isString, truth allowStr, truth PreserveTerminator) {
689 sLong Value = 0;
690 festring Word;
691 truth NumberCorrect = false;
692 truth firstWord = true;
693 *isString = false;
694 *num = 0;
695 festring res;
696 for (;;) {
697 ReadWord(Word);
698 if (Word == "@") {
699 // variable
700 ReadWord(Word, true);
701 //fprintf(stderr, "var: [%s]\n", Word.CStr());
702 Word = getVar(Word);
703 //fprintf(stderr, " value: [%s]\n", Word.CStr());
704 const char *s = Word.CStr();
705 char *e;
706 sLong l = strtoll(s, &e, 10);
707 if (*e == '\0') {
708 //fprintf(stderr, " number: [%d]\n", l);
709 Value = l;
710 NumberCorrect = true;
711 continue;
713 if (firstWord && allowStr) {
714 *isString = true;
715 return Word;
716 } else {
717 ABORT("Number expected in file %s, line %d!", FileName.CStr(), TellLine());
720 if (firstWord) {
721 if (allowStr && lastWordWasString) {
722 *isString = true;
723 ReadWord(res);
724 if (res.GetSize() == 1) {
725 if (res[0] != ';' && res[0] != ',' && res[0] != ':') {
726 ABORT("Invalid terminator in file %s, line %d!", FileName.CStr(), TellLine());
728 if (PreserveTerminator) Xungc(res[0], Buffer);
729 } else {
730 ABORT("Terminator expected in file %s, line %d!", FileName.CStr(), TellLine());
732 return Word;
734 firstWord = false;
736 char First = Word[0];
737 if (isdigit(First)) {
738 Value = atoi(Word.CStr());
739 NumberCorrect = true;
740 continue;
742 if (Word.GetSize() == 1) {
743 if (First == ';' || First == ',' || First == ':') {
744 if (CallLevel != HIGHEST || PreserveTerminator) Xungc(First, Buffer);
745 *num = Value;
746 return res;
748 if (First == ')') {
749 if ((CallLevel != HIGHEST && CallLevel != 4) || PreserveTerminator) Xungc(')', Buffer);
750 *num = Value;
751 return res;
753 if (First == '~') {
754 Value = ~ReadNumber(4);
755 NumberCorrect = true;
756 continue;
758 /* Convert this into an inline function! */
759 #define CHECK_OP(op, cl) \
760 if (First == #op[0]) { \
761 if (cl < CallLevel) {\
762 Value op##= ReadNumber(cl);\
763 NumberCorrect = true;\
764 continue;\
765 } else {\
766 Xungc(#op[0], Buffer);\
767 *num = Value;\
768 return res;\
771 CHECK_OP(&, 1); CHECK_OP(|, 1); CHECK_OP(^, 1);
772 CHECK_OP(*, 2); CHECK_OP(/, 2); CHECK_OP(%, 2);
773 CHECK_OP(+, 3); CHECK_OP(-, 3);
774 if (First == '<') {
775 char Next = Get();
776 if (Next == '<')
777 if (1 < CallLevel) {
778 Value <<= ReadNumber(1);
779 NumberCorrect = true;
780 continue;
781 } else {
782 Xungc('<', Buffer);
783 Xungc('<', Buffer);
784 *num = Value;
785 return res;
786 } else {
787 Xungc(Next, Buffer);
790 if (First == '>') {
791 char Next = Get();
792 if (Next == '>')
793 if (1 < CallLevel) {
794 Value >>= ReadNumber(1);
795 NumberCorrect = true;
796 continue;
797 } else {
798 Xungc('>', Buffer);
799 Xungc('>', Buffer);
800 *num = Value;
801 return res;
802 } else {
803 Xungc(Next, Buffer);
806 if (First == '(') {
807 if (NumberCorrect) {
808 Xungc('(', Buffer);
809 *num = Value;
810 return res;
811 } else {
812 Value = ReadNumber(4);
813 NumberCorrect = false;
814 continue;
817 if (First == '=' && CallLevel == HIGHEST) continue;
818 if (First == '#') {
819 // for #defines
820 Xungc('#', Buffer);
821 *num = Value;
822 return res;
826 if (Word == "enum" || Word == "bitenum") {
827 if (CallLevel != HIGHEST || PreserveTerminator) Xungc(';', Buffer);
828 *num = Value;
829 return res;
832 if (Word == "rgb") {
833 int Bits = ReadNumber();
834 if (Bits == 16) {
835 int Red = ReadNumber();
836 int Green = ReadNumber();
837 int Blue = ReadNumber();
838 Value = MakeRGB16(Red, Green, Blue);
839 } else if (Bits == 24) {
840 int Red = ReadNumber();
841 int Green = ReadNumber();
842 int Blue = ReadNumber();
843 Value = MakeRGB24(Red, Green, Blue);
844 } else {
845 ABORT("Illegal RGB bit size %d in file %s, line %d!", Bits, FileName.CStr(), TellLine());
847 NumberCorrect = true;
848 continue;
850 if (Word == "true" || Word == "tan") {
851 Value = 1;
852 NumberCorrect = true;
853 continue;
855 if (Word == "false" || Word == "ona") {
856 Value = 0;
857 NumberCorrect = true;
858 continue;
860 if (ValueMap) {
861 valuemap::const_iterator Iterator = ValueMap->find(Word);
862 if (Iterator != ValueMap->end()) {
863 Value = Iterator->second;
864 NumberCorrect = true;
865 continue;
868 ABORT("Odd numeric value \"%s\" encountered in file %s, line %d!",
869 Word.CStr(), FileName.CStr(), TellLine());
874 festring inputfile::ReadCode (truth AbortOnEOF) {
875 int sqLevel = 1;
876 char inString = 0;
877 festring res;
879 for (char Char = Xgetc(Buffer); !Xfeof(Buffer); Char = Xgetc(Buffer)) {
880 //fprintf(stderr, "char: [%c]; inString: %d; sqLevel: %d\n", (Char < 32 || Char > 126 ? '?' : Char), inString, sqLevel);
881 if (inString) {
882 res << Char;
883 if (Char == inString) {
884 inString = 0;
885 } else if (Char == '\\') {
886 if (Xfeof(Buffer)) break;
887 Char = Xgetc(Buffer);
888 res << Char;
890 } else {
891 if (Char == '[') {
892 ++sqLevel;
893 res << Char;
894 } else if (Char == ']') {
895 if (--sqLevel == 0) break;
896 res << Char;
897 } else if (Char == '/') {
898 if (Xfeof(Buffer)) { res << Char; break; }
899 switch ((Char = Xgetc(Buffer))) {
900 case '/': // eol comment
901 while (!Xfeof(Buffer)) if (Xgetc(Buffer) == '\n') break;
902 break;
903 case '*': // c-like comment
904 while (!Xfeof(Buffer)) {
905 if (Xgetc(Buffer) == '*') {
906 if (Xfeof(Buffer)) break;
907 if (Xgetc(Buffer) == '/') break;
910 break;
911 default:
912 res << '/';
913 res << Char;
914 break;
916 } else if (Char == '"' || Char == '\'') {
917 res << Char;
918 inString = Char;
919 } else {
920 res << Char;
924 if (AbortOnEOF && Xfeof(Buffer)) ABORT("Unexpected end of file %s!", FileName.CStr());
925 return res;
929 sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator) {
930 sLong num = 0;
931 truth isString = false;
932 ReadNumberIntr(CallLevel, &num, &isString, false, PreserveTerminator);
933 return num;
937 festring inputfile::ReadStringOrNumber (sLong *num, truth *isString, truth PreserveTerminator) {
938 return ReadNumberIntr(0xFF, num, isString, true, PreserveTerminator);
942 v2 inputfile::ReadVector2d () {
943 v2 Vector;
944 Vector.X = ReadNumber();
945 Vector.Y = ReadNumber();
946 return Vector;
950 rect inputfile::ReadRect () {
951 rect Rect;
952 Rect.X1 = ReadNumber();
953 Rect.Y1 = ReadNumber();
954 Rect.X2 = ReadNumber();
955 Rect.Y2 = ReadNumber();
956 return Rect;
960 outputfile &operator << (outputfile &SaveFile, cfestring &String) {
961 uShort Length = String.GetSize();
962 SaveFile << Length;
963 if (Length) SaveFile.Write(String.CStr(), Length);
964 return SaveFile;
968 inputfile &operator >> (inputfile &SaveFile, festring &String) {
969 char *RealBuffer, StackBuffer[1024];
970 uShort Length;
971 SaveFile >> Length;
972 RealBuffer = Length < 1024 ? StackBuffer : new char[Length+1];
973 String.Empty();
974 if (Length) {
975 SaveFile.Read(RealBuffer, Length);
976 RealBuffer[Length] = 0;
977 String << RealBuffer;
979 if (Length >= 1024) delete [] RealBuffer;
980 return SaveFile;
984 outputfile &operator << (outputfile &SaveFile, cchar *String) {
985 uShort Length = String ? strlen(String) : 0;
986 SaveFile << Length;
987 if (Length) SaveFile.Write(String, Length);
988 return SaveFile;
992 inputfile &operator >> (inputfile &SaveFile, char *&String) {
993 uShort Length;
994 SaveFile >> Length;
995 if (Length) {
996 String = new char[Length+1];
997 SaveFile.Read(String, Length);
998 String[Length] = 0;
999 } else {
1000 String = 0;
1002 return SaveFile;
1006 void ReadData (festring &String, inputfile &SaveFile) {
1007 SaveFile.ReadWord(String);
1008 if (String == "=") SaveFile.ReadWord(String);
1009 SaveFile.ReadWord();
1013 void ReadData (fearray<sLong> &Array, inputfile &SaveFile) {
1014 Array.Clear();
1015 festring Word;
1016 SaveFile.ReadWord(Word);
1017 //if (Word == "=") SaveFile.ReadWord(Word);
1018 if (Word == "==") {
1019 Array.Allocate(1);
1020 Array.Data[0] = SaveFile.ReadNumber();
1021 return;
1023 if (Word != "=") ABORT("Array syntax error: '=' or '==' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1024 SaveFile.ReadWord(Word);
1025 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1026 fearray<sLong>::sizetype Size = SaveFile.ReadNumber();
1027 Array.Allocate(Size);
1028 for (fearray<sLong>::sizetype c = 0; c < Size; ++c) Array.Data[c] = SaveFile.ReadNumber();
1029 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1033 void ReadData (fearray<festring> &Array, inputfile &SaveFile) {
1034 Array.Clear();
1035 festring Word;
1036 SaveFile.ReadWord(Word);
1037 //if (Word == "=") SaveFile.ReadWord(Word);
1038 if (Word == "==") {
1039 Array.Allocate(1);
1040 SaveFile.ReadWord(Array.Data[0]);
1041 if (SaveFile.ReadWord() != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1042 return;
1044 if (Word != "=") ABORT("Array syntax error: '=' or '==' expected in file %s, line %d!", SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1045 SaveFile.ReadWord(Word);
1046 if (Word != "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1047 fearray<festring>::sizetype Size = SaveFile.ReadNumber();
1048 Array.Allocate(Size);
1049 for (fearray<festring>::sizetype c = 0; c < Size; ++c) {
1050 SaveFile.ReadWord(Array.Data[c]);
1051 SaveFile.ReadWord(Word);
1052 if (Word != "," && Word != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1054 if (SaveFile.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word.CStr(), SaveFile.GetFileName().CStr(), SaveFile.TellLine());
1058 feuLong inputfile::TellLineOfPos (sLong Pos) {
1059 feuLong Line = 1;
1060 sLong BackupPos = TellPos();
1061 SeekPosBegin(0);
1062 while (TellPos() != Pos) { if (Xgetc(Buffer) == '\n') ++Line; }
1063 if (TellPos() != BackupPos) SeekPosBegin(BackupPos);
1064 return Line;
1068 ////////////////////////////////////////////////////////////////////////////////
1069 #ifdef USE_ZLIB
1070 meminputfile::meminputfile (cfestring &str, const valuemap *ValueMap) :
1071 inputfile("", ValueMap, false),
1072 buf(0),
1073 bufSize(0),
1074 tfname("")
1076 Close();
1077 #ifdef WIN32
1078 char nbuf[MAX_PATH+1], tfn[MAX_PATH+1];
1079 GetTempPath(MAX_PATH, nbuf);
1080 GetTempFileName(nbuf, "ivan", 0, tfn);
1081 tfname = tfn;
1082 FILE *fl = fopen(tfn, "wb");
1083 fwrite(str.CStr(), str.GetSize(), 1, fl);
1084 fclose(fl);
1085 Buffer = gzopen(tfn, "rb");
1086 #else
1087 char fname[1024];
1088 int fd;
1090 strcpy(fname, "/tmp/ivan.XXXXXX");
1091 fd = mkstemp(fname);
1092 tfname = fname;
1093 fprintf(stderr, "[%s]\n", tfname.CStr());
1094 if (fd < 0) ABORT("Can't create temporary file!");
1095 write(fd, str.CStr(), str.GetSize());
1096 close(fd);
1097 Buffer = gzopen(fname, "rb");
1098 #endif
1099 FileName = "<memory>";
1103 meminputfile::~meminputfile () {
1104 if (buf) free(buf);
1105 Close();
1106 unlink(tfname.CStr());
1110 #else
1113 meminputfile::meminputfile (cfestring &str, const valuemap *ValueMap) :
1114 inputfile("", ValueMap, false),
1115 buf(0),
1116 bufSize(0),
1117 tfname("")
1119 Close();
1120 #ifdef WIN32
1121 char nbuf[MAX_PATH+1], tfn[MAX_PATH+1];
1122 GetTempPath(MAX_PATH, nbuf);
1123 GetTempFileName(nbuf, "ivan", 0, tfn);
1124 tfname = tfn;
1125 FILE *fl = fopen(tfn, "wb");
1126 fwrite(str.CStr(), str.GetSize(), 1, fl);
1127 fclose(fl);
1128 Buffer = fopen(tfn, "rb");
1129 #else
1130 bufSize = str.GetSize();
1131 buf = (char *)calloc(1, bufSize+1);
1132 memmove(buf, str.CStr(), bufSize);
1133 Buffer = fmemopen(buf, bufSize, "rb");
1134 #endif
1135 FileName = "<memory>";
1139 meminputfile::~meminputfile () {
1140 Close();
1141 if (buf) free(buf);
1142 #ifdef WIN32
1143 DeleteFile(tfname.CStr());
1144 #endif
1146 #endif