3 * Iter Vehemens ad Necem (IVAN)
4 * Copyright (C) Timo Kiviluoto
5 * Released under the GNU General
8 * See LICENSING which should be included
9 * along with this file for more details
18 #include <sys/types.h>
32 truth
inputfile::fileExists (const festring
&fname
) {
35 if (stat(fname
.CStr(), &st
)) return false;
36 if (!S_ISREG(st
.st_mode
)) return false;
37 return access(fname
.CStr(), R_OK
) == 0;
39 FILE *fl
= fopen(fname
.CStr(), "rb");
46 festring
inputfile::GetMyDir (void) {
50 pid_t mypid
= getpid();
51 memset(myDir
, 0, sizeof(myDir
));
52 sprintf(buf
, "/proc/%u/exe", (unsigned int)mypid
);
53 if (readlink(buf
, myDir
, sizeof(myDir
)-1) < 0) strcpy(myDir
, ".");
55 char *p
= (char *)strrchr(myDir
, '/');
56 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
58 if (myDir
[strlen(myDir
)-1] == '/') myDir
[strlen(myDir
)-1] = '\0';
61 memset(myDir
, 0, sizeof(myDir
));
62 GetModuleFileName(GetModuleHandle(NULL
), myDir
, sizeof(myDir
)-1);
63 p
= strrchr(myDir
, '\\');
64 if (!p
) strcpy(myDir
, "."); else *p
= '\0';
70 outputfile::outputfile (cfestring
&FileName
, truth AbortOnErr
) :
71 Buffer(fopen(FileName
.CStr(), "wb")),
74 if (AbortOnErr
&& !IsOpen()) ABORT("Can't open %s for output!", FileName
.CStr());
78 outputfile::~outputfile () {
79 if (Buffer
) fclose(Buffer
);
83 void outputfile::ReOpen () {
85 Buffer
= fopen(FileName
.CStr(), "ab");
89 inputfile::inputfile (cfestring
&FileName
, const valuemap
*ValueMap
, truth AbortOnErr
) :
90 Buffer(fopen(FileName
.CStr(), "rb")),
93 lastWordWasString(false)
95 if (AbortOnErr
&& !IsOpen()) ABORT("File %s not found!", FileName
.CStr());
99 inputfile::~inputfile () {
100 if (Buffer
) fclose(Buffer
);
104 festring
inputfile::findVar (cfestring
&name
, truth
*found
) const {
105 VarMap::const_iterator i
= mVars
.find(name
);
106 if (i
!= mVars
.end()) {
107 if (found
) *found
= true;
110 if (found
) *found
= false;
115 festring
inputfile::getVar (cfestring
&name
) {
117 festring res
= findVar(name
, &found
);
121 res
= mGetVar(this, name
);
123 festring s
= "unknown variable: "+name
;
131 void inputfile::setVar (cfestring
&name
, cfestring
&value
) {
136 //TODO: invoke callback
137 truth
inputfile::delVar (cfestring
&name
) {
138 VarMap::iterator i
= mVars
.find(name
);
139 if (i
!= mVars
.end()) {
147 void inputfile::die (cfestring
&msg
) {
148 ABORT("ERROR in file %s, line %d: %s", GetFileName().CStr(), TellLine(), msg
.CStr());
157 const int maxCPrio
= 4;
158 const char *opers
[5][7] = {
161 {"<", ">", "<=", ">=", "==", "!=", NULL
},
166 festring
inputfile::readCondition (festring
&token
, int prio
, truth skipIt
) {
167 festring res
, op1
, opc
;
168 //fprintf(stderr, "IN: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
172 readWordIntr(token
, true);
173 res
= readCondition(token
, maxCPrio
, skipIt
);
174 if (token
!= ")") die("')' expected");
175 } else if (token
== "@") {
176 readWordIntr(token
, true);
177 if (!skipIt
) res
= getVar(token
);
181 readWordIntr(token
, true);
186 readWordIntr(token
, true);
187 res
= readCondition(token
, 1, skipIt
);
189 if (res
== "") res
= "tan"; else res
= "";
192 res
= readCondition(token
, prio
-1, skipIt
);
198 if (prio
> 4) return res
;
199 res
= readCondition(token
, prio
-1, skipIt
);
201 //readWordIntr(token, true);
203 if (token
== "=") die("no assignments yet!");
205 //fprintf(stderr, " RET: [%s]\n", res.CStr());
208 if (token
== "less") token
= "<";
209 else if (token
== "great") token
= ">";
210 else if (token
== "equ") token
= "==";
211 else if (token
== "neq") token
= "!=";
212 else if (token
== "lessequ") token
= "<=";
213 else if (token
== "greatequ") token
= ">=";
214 for (int f
= 0; opers
[prio
][f
]; f
++) {
215 if (!strcmp(opers
[prio
][f
], token
.CStr())) { myOp
= true; break; }
217 //fprintf(stderr, "tk: [%s]; %s\n", token.CStr(), myOp?"MY":"skip");
220 readWordIntr(token
, true);
221 op1
= readCondition(token
, prio
-1, skipIt
);
222 //fprintf(stderr, " prio: %d; opc=[%s]; res=[%s]; op1=[%s]\n", prio, opc.CStr(), res.CStr(), op1.CStr());
224 case 2: // comparisons
226 if (!skipIt
) res
= res
==op1
? "tan" : "";
227 } else if (opc
== "!=") {
228 if (!skipIt
) res
= res
!=op1
? "tan" : "";
229 } else if (opc
== "<") {
230 if (!skipIt
) res
= res
<op1
? "tan" : "";
231 } else if (opc
== ">") {
232 if (!skipIt
) res
= res
>op1
? "tan" : "";
233 } else if (opc
== "<=") {
234 if (!skipIt
) res
= res
<=op1
? "tan" : "";
235 } else if (opc
== ">=") {
236 if (!skipIt
) res
= res
>=op1
? "tan" : "";
242 res
= res
!=""&&op1
!="" ? "tan" : "";
243 if (res
== "") skipIt
= true;
250 res
= res
!=""||op1
!="" ? "tan" : "";
251 if (res
!= "") skipIt
= true;
256 die("invalid priority");
260 //fprintf(stderr, "OUT: prio: %d; skip: %s; [%s]\n", prio, skipIt?"t":"o", token.CStr());
266 // 1: processing 'then'
267 // 2: processing 'else'
268 // -1: skiping 'then'
269 // -2: skiping 'else'
270 // -3: skiping whole 'if', 'then' part
271 // -4: skiping whole 'if', 'else' part
272 // -666: skiping '{}'
273 // 666: in '{}', processing
274 void inputfile::ReadWord (festring
&str
, truth AbortOnEOF
, truth skipIt
) {
277 if (!mIfStack
.empty()) prc
= mIfStack
.top(); else prc
= 0;
278 readWordIntr(str
, AbortOnEOF
);
280 readWordIntr(str
, true);
281 festring res
= readCondition(str
, maxCPrio
, prc
<0);
282 if (str
!= ";") die("';' expected");
287 mIfStack
.push(res
!="" ? 1 : -1);
293 case 1: // processing 'then'
297 case -1: // skiping 'then'
301 case -3: // skiping whole, 'then'
305 default: die("unexpected 'else'");
309 if (str
== "endif") {
311 case 1: // processing 'then'
312 case 2: // processing 'else'
313 case -1: // skiping 'then'
314 case -2: // skiping 'else'
315 case -3: // skiping whole, 'then'
316 case -4: // skiping whole, 'else'
319 default: die("unexpected 'endif'");
324 mIfStack
.push(prc
>=0 ? 666 : -666);
325 if (prc
>= 0) return;
329 if (abs(prc
) != 666) die("unexpected '}'");
331 if (prc
>= 0) return;
334 if (prc
>= 0) return;
339 festring
inputfile::ReadWord (truth AbortOnEOF
) {
341 ReadWord(ToReturn
, AbortOnEOF
);
346 void inputfile::SkipSpaces () {
347 while (!feof(Buffer
)) {
348 int ch
= fgetc(Buffer
);
349 if (ch
== EOF
) break;
350 if ((unsigned char)ch
> ' ') {
359 #define MODE_NUMBER 2
361 #define PUNCT_RETURN 0
362 #define PUNCT_CONTINUE 1
364 int inputfile::HandlePunct (festring
&String
, int Char
, int Mode
) {
366 // comment? (can be nested)
368 Char
= fgetc(Buffer
);
370 sLong StartPos
= TellPos();
371 int OldChar
= 0, CommentLevel
= 1;
373 if (feof(Buffer
)) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName
.CStr(), TellLineOfPos(StartPos
));
374 Char
= fgetc(Buffer
);
375 if (OldChar
!= '*' || Char
!= '/') {
376 if (OldChar
!= '/' || Char
!= '*') OldChar
= Char
;
382 if (!--CommentLevel
) break;
386 return PUNCT_CONTINUE
;
390 while (!feof(Buffer
)) {
391 int ch
= fgetc(Buffer
);
392 if (ch
== '\n') break;
394 return PUNCT_CONTINUE
;
396 ungetc(Char
, Buffer
);
399 if (Mode
) ungetc('/', Buffer
); else String
<< '/';
404 ungetc(Char
, Buffer
);
410 lastWordWasString
= true;
411 sLong StartPos
= TellPos();
413 if (feof(Buffer
)) ABORT("Unterminated string in file %s, beginning at line %d!", FileName
.CStr(), TellLineOfPos(StartPos
));
414 Char
= fgetc(Buffer
);
416 Char
= fgetc(Buffer
);
417 if (Char
== EOF
) ABORT("Unterminated string in file %s, beginning at line %d!", FileName
.CStr(), TellLineOfPos(StartPos
));
419 case 't': String
<< '\t'; break;
420 case 'n': String
<< '\n'; break;
421 case 'r': String
<< '\r'; break;
422 case '"': String
<< '"'; break;
424 ABORT("Invalid escape in string in file %s at line %d!", FileName
.CStr(), TellLine());
426 } else if (Char
== '"') {
429 String
<< char(Char
);
433 String << char(Char);
435 } else if (OldChar == '\\') {
436 String[String.GetSize()-1] = '"';
438 } else return PUNCT_RETURN;
442 String
<< char(Char
);
444 if (Char
== '=' || Char
== '<' || Char
== '>' || Char
== '!') {
445 Char
= fgetc(Buffer
);
446 if (Char
== '=') String
<< char(Char
); else ungetc(Char
, Buffer
);
447 } else if (Char
== '&' || Char
== '|') {
448 int ch
= fgetc(Buffer
);
449 if (Char
== ch
) String
<< char(ch
); else ungetc(ch
, Buffer
);
456 void inputfile::readWordIntr (festring
&String
, truth AbortOnEOF
) {
459 lastWordWasString
= false;
460 for (int Char
= fgetc(Buffer
); !feof(Buffer
); Char
= fgetc(Buffer
)) {
461 if (isalpha(Char
) || Char
== '_') {
462 if (!Mode
) Mode
= MODE_WORD
;
463 else if (Mode
== MODE_NUMBER
) {
464 ungetc(Char
, Buffer
);
467 String
<< char(Char
);
471 if (!Mode
) Mode
= MODE_NUMBER
;
472 else if (Mode
== MODE_WORD
) {
473 ungetc(Char
, Buffer
);
476 String
<< char(Char
);
479 if ((Char
== ' ' || Char
== '\n' || Char
== '\r' || Char
== '\t') && Mode
) return;
480 if (ispunct(Char
) && HandlePunct(String
, Char
, Mode
) == PUNCT_RETURN
) return;
482 if (AbortOnEOF
) ABORT("Unexpected end of file %s!", FileName
.CStr());
483 if (Mode
) clearerr(Buffer
);
487 char inputfile::ReadLetter (truth AbortOnEOF
) {
488 for (int Char
= fgetc(Buffer
); !feof(Buffer
); Char
= fgetc(Buffer
)) {
489 if (isalpha(Char
) || isdigit(Char
)) return Char
;
493 Char
= fgetc(Buffer
);
495 sLong StartPos
= TellPos();
496 int OldChar
= 0, CommentLevel
= 1;
498 if (feof(Buffer
)) ABORT("Unterminated comment in file %s, beginning at line %d!", FileName
.CStr(), TellLineOfPos(StartPos
));
499 Char
= fgetc(Buffer
);
500 if (OldChar
!= '*' || Char
!= '/') {
501 if (OldChar
!= '/' || Char
!= '*') OldChar
= Char
;
507 if (!--CommentLevel
) break;
513 ungetc(Char
, Buffer
);
521 if (AbortOnEOF
) ABORT("Unexpected end of file %s!", FileName
.CStr());
526 /* Reads a number or a formula from inputfile. Valid values could be for
527 instance "3", "5 * 4+5", "2+Variable%4" etc. */
528 //sLong inputfile::ReadNumber (int CallLevel, truth PreserveTerminator) {
529 festring
inputfile::ReadNumberIntr (int CallLevel
, sLong
*num
, truth
*isString
, truth allowStr
, truth PreserveTerminator
) {
532 truth NumberCorrect
= false;
533 truth firstWord
= true;
541 ReadWord(Word
, true);
542 //fprintf(stderr, "var: [%s]\n", Word.CStr());
544 //fprintf(stderr, " value: [%s]\n", Word.CStr());
545 const char *s
= Word
.CStr();
547 sLong l
= strtoll(s
, &e
, 10);
549 //fprintf(stderr, " number: [%d]\n", l);
551 NumberCorrect
= true;
554 if (firstWord
&& allowStr
) {
558 ABORT("Number expected in file %s, line %d!", FileName
.CStr(), TellLine());
562 if (allowStr
&& lastWordWasString
) {
565 if (res
.GetSize() == 1) {
566 if (res
[0] != ';' && res
[0] != ',' && res
[0] != ':') {
567 ABORT("Invalid terminator in file %s, line %d!", FileName
.CStr(), TellLine());
569 if (PreserveTerminator
) ungetc(res
[0], Buffer
);
571 ABORT("Terminator expected in file %s, line %d!", FileName
.CStr(), TellLine());
577 char First
= Word
[0];
578 if (isdigit(First
)) {
579 Value
= atoi(Word
.CStr());
580 NumberCorrect
= true;
583 if (Word
.GetSize() == 1) {
584 if (First
== ';' || First
== ',' || First
== ':') {
585 if (CallLevel
!= HIGHEST
|| PreserveTerminator
) ungetc(First
, Buffer
);
590 if ((CallLevel
!= HIGHEST
&& CallLevel
!= 4) || PreserveTerminator
) ungetc(')', Buffer
);
595 Value
= ~ReadNumber(4);
596 NumberCorrect
= true;
599 /* Convert this into an inline function! */
600 #define CHECK_OP(op, cl) \
601 if (First == #op[0]) { \
602 if (cl < CallLevel) {\
603 Value op##= ReadNumber(cl);\
604 NumberCorrect = true;\
607 ungetc(#op[0], Buffer);\
612 CHECK_OP(&, 1); CHECK_OP(|, 1); CHECK_OP(^, 1);
613 CHECK_OP(*, 2); CHECK_OP(/, 2); CHECK_OP(%, 2);
614 CHECK_OP(+, 3); CHECK_OP(-, 3);
619 Value
<<= ReadNumber(1);
620 NumberCorrect
= true;
628 ungetc(Next
, Buffer
);
635 Value
>>= ReadNumber(1);
636 NumberCorrect
= true;
644 ungetc(Next
, Buffer
);
653 Value
= ReadNumber(4);
654 NumberCorrect
= false;
658 if (First
== '=' && CallLevel
== HIGHEST
) continue;
667 if (Word == "enum" || Word == "bitenum") {
668 if (CallLevel != HIGHEST || PreserveTerminator) ungetc(';', Buffer);
674 int Bits
= ReadNumber();
676 int Red
= ReadNumber();
677 int Green
= ReadNumber();
678 int Blue
= ReadNumber();
679 Value
= MakeRGB16(Red
, Green
, Blue
);
680 } else if (Bits
== 24) {
681 int Red
= ReadNumber();
682 int Green
= ReadNumber();
683 int Blue
= ReadNumber();
684 Value
= MakeRGB24(Red
, Green
, Blue
);
686 ABORT("Illegal RGB bit size %d in file %s, line %d!", Bits
, FileName
.CStr(), TellLine());
688 NumberCorrect
= true;
691 if (Word
== "true" || Word
== "tan") {
693 NumberCorrect
= true;
696 if (Word
== "false" || Word
== "ona") {
698 NumberCorrect
= true;
702 valuemap::const_iterator Iterator
= ValueMap
->find(Word
);
703 if (Iterator
!= ValueMap
->end()) {
704 Value
= Iterator
->second
;
705 NumberCorrect
= true;
709 ABORT("Odd numeric value \"%s\" encountered in file %s, line %d!",
710 Word
.CStr(), FileName
.CStr(), TellLine());
715 festring
inputfile::ReadCode (truth AbortOnEOF
) {
720 for (char Char
= fgetc(Buffer
); !feof(Buffer
); Char
= fgetc(Buffer
)) {
721 //fprintf(stderr, "char: [%c]; inString: %d; sqLevel: %d\n", (Char < 32 || Char > 126 ? '?' : Char), inString, sqLevel);
724 if (Char
== inString
) {
726 } else if (Char
== '\\') {
727 if (feof(Buffer
)) break;
728 Char
= fgetc(Buffer
);
735 } else if (Char
== ']') {
736 if (--sqLevel
== 0) break;
738 } else if (Char
== '/') {
739 if (feof(Buffer
)) { res
<< Char
; break; }
740 switch ((Char
= fgetc(Buffer
))) {
741 case '/': // eol comment
742 while (!feof(Buffer
)) if (fgetc(Buffer
) == '\n') break;
744 case '*': // c-like comment
745 while (!feof(Buffer
)) {
746 if (fgetc(Buffer
) == '*') {
747 if (feof(Buffer
)) break;
748 if (fgetc(Buffer
) == '/') break;
757 } else if (Char
== '"' || Char
== '\'') {
765 if (AbortOnEOF
&& feof(Buffer
)) ABORT("Unexpected end of file %s!", FileName
.CStr());
770 sLong
inputfile::ReadNumber (int CallLevel
, truth PreserveTerminator
) {
772 truth isString
= false;
773 ReadNumberIntr(CallLevel
, &num
, &isString
, false, PreserveTerminator
);
778 festring
inputfile::ReadStringOrNumber (sLong
*num
, truth
*isString
, truth PreserveTerminator
) {
779 return ReadNumberIntr(0xFF, num
, isString
, true, PreserveTerminator
);
783 v2
inputfile::ReadVector2d () {
785 Vector
.X
= ReadNumber();
786 Vector
.Y
= ReadNumber();
791 rect
inputfile::ReadRect () {
793 Rect
.X1
= ReadNumber();
794 Rect
.Y1
= ReadNumber();
795 Rect
.X2
= ReadNumber();
796 Rect
.Y2
= ReadNumber();
801 outputfile
&operator << (outputfile
&SaveFile
, cfestring
&String
) {
802 uShort Length
= String
.GetSize();
804 if (Length
) SaveFile
.Write(String
.CStr(), Length
);
809 inputfile
&operator >> (inputfile
&SaveFile
, festring
&String
) {
810 char *RealBuffer
, StackBuffer
[1024];
813 RealBuffer
= Length
< 1024 ? StackBuffer
: new char[Length
+1];
816 SaveFile
.Read(RealBuffer
, Length
);
817 RealBuffer
[Length
] = 0;
818 String
<< RealBuffer
;
820 if (Length
>= 1024) delete [] RealBuffer
;
825 outputfile
&operator << (outputfile
&SaveFile
, cchar
*String
) {
826 uShort Length
= String
? strlen(String
) : 0;
828 if (Length
) SaveFile
.Write(String
, Length
);
833 inputfile
&operator >> (inputfile
&SaveFile
, char *&String
) {
837 String
= new char[Length
+1];
838 SaveFile
.Read(String
, Length
);
847 void ReadData (festring
&String
, inputfile
&SaveFile
) {
848 SaveFile
.ReadWord(String
);
849 if (String
== "=") SaveFile
.ReadWord(String
);
854 void ReadData (fearray
<sLong
> &Array
, inputfile
&SaveFile
) {
857 SaveFile
.ReadWord(Word
);
858 //if (Word == "=") SaveFile.ReadWord(Word);
861 Array
.Data
[0] = SaveFile
.ReadNumber();
864 if (Word
!= "=") ABORT("Array syntax error: '=' or '==' expected in file %s, line %d!", SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
865 SaveFile
.ReadWord(Word
);
866 if (Word
!= "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
867 fearray
<sLong
>::sizetype Size
= SaveFile
.ReadNumber();
868 Array
.Allocate(Size
);
869 for (fearray
<sLong
>::sizetype c
= 0; c
< Size
; ++c
) Array
.Data
[c
] = SaveFile
.ReadNumber();
870 if (SaveFile
.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
874 void ReadData (fearray
<festring
> &Array
, inputfile
&SaveFile
) {
877 SaveFile
.ReadWord(Word
);
878 //if (Word == "=") SaveFile.ReadWord(Word);
881 SaveFile
.ReadWord(Array
.Data
[0]);
882 if (SaveFile
.ReadWord() != ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
885 if (Word
!= "=") ABORT("Array syntax error: '=' or '==' expected in file %s, line %d!", SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
886 SaveFile
.ReadWord(Word
);
887 if (Word
!= "{") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
888 fearray
<festring
>::sizetype Size
= SaveFile
.ReadNumber();
889 Array
.Allocate(Size
);
890 for (fearray
<festring
>::sizetype c
= 0; c
< Size
; ++c
) {
891 SaveFile
.ReadWord(Array
.Data
[c
]);
892 SaveFile
.ReadWord(Word
);
893 if (Word
!= "," && Word
!= ";") ABORT("Array syntax error \"%s\" found in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
895 if (SaveFile
.ReadWord() != "}") ABORT("Illegal array terminator \"%s\" encountered in file %s, line %d!", Word
.CStr(), SaveFile
.GetFileName().CStr(), SaveFile
.TellLine());
899 uLong
inputfile::TellLineOfPos (sLong Pos
) {
901 sLong BackupPos
= TellPos();
903 while (TellPos() != Pos
) { if (fgetc(Buffer
) == '\n') ++Line
; }
904 if (TellPos() != BackupPos
) SeekPosBegin(BackupPos
);
909 meminputfile::meminputfile (cfestring
&str
, const valuemap
*ValueMap
) :
910 inputfile("", ValueMap
, false)
912 if (Buffer
) fclose(Buffer
);
914 char nbuf
[MAX_PATH
+1], tfn
[MAX_PATH
+1];
915 GetTempPath(MAX_PATH
, nbuf
);
916 GetTempFileName(nbuf
, "ivan", 0, tfn
);
918 FILE *fl
= fopen(tfn
, "wb");
919 fwrite(str
.CStr(), str
.GetSize(), 1, fl
);
921 Buffer
= fopen(tfn
, "rb");
923 bufSize
= str
.GetSize();
924 buf
= malloc(bufSize
+1);
925 memmove(buf
, str
.CStr(), bufSize
);
926 Buffer
= fmemopen(buf
, bufSize
, "rb");
928 FileName
= "<memory>";
931 meminputfile::~meminputfile () {
934 if (Buffer
) fclose(Buffer
);
935 unlink(tfname
.CStr());