* Macro deobfuscation.
[ginac.git] / cint / ginaccint.bin.cpp
1 /* ginaccint.bin.cpp:  Binary depends on CINTSYSDIR, better don't call it
2  * directly.  Use the launcher (compiled from ginaccint.cpp) instead. */
3
4 #include "G__ci.h"   /* G__atpause is defined in G__ci.h */
5
6 #if (!defined(G__CINTVERSION)) || (G__CINTVERSION < 501460)
7 #error You need at least cint 5.14.60 to compile GiNaC-cint. Download it via http from root.cern.ch/root/Cint.html or via ftp from ftpthep.physik.uni-mainz.de/pub/cint
8 #endif // (!defined(G__CINTVERSION)) || (G__CINTVERSION < 501438)
9
10 #include <iostream>
11 #include <fstream>
12 #include <string>
13 #include <stdio.h>
14 #include <stdlib.h>
15 #include "ginac/ginac.h"
16 #include "config.h"
17 #include <list>
18
19 using namespace GiNaC;
20
21 extern "C" G__value G__exec_tempfile G__P((char *file));
22 extern "C" void G__store_undo_position(void);
23
24 #define PROMPT1 "GiNaC> "
25 #define PROMPT2 "     > "
26
27 #ifdef OBSCURE_CINT_HACK
28
29 #include <strstream>
30
31 template<class T>
32 std::string ToString(const T & t)
33 {
34         char buf[256];
35         ostrstream(buf,sizeof(buf)) << t << ends;
36         return buf;
37 }
38
39 basic * ex::last_created_or_assigned_bp = 0;
40 basic * ex::dummy_bp = 0;
41 long ex::last_created_or_assigned_exp = 0;
42
43 #endif // def OBSCURE_CINT_HACK
44
45 G__value exec_tempfile(std::string const & command);
46 char * process_permanentfile(std::string const & command);
47 void process_tempfile(std::string const & command);
48 void printversionmessage(std::ostream & os);
49 void greeting(void);
50 void helpmessage(void);
51 std::string preprocess(char const * const line, bool & comment, bool & single_quote,
52                        bool & double_quote, unsigned & open_braces);
53 void cleanup(void);
54 void sigterm_handler(int n);
55 void initialize(void);
56 void initialize_cint(void);
57 void restart(void);
58 bool is_command(std::string const & command, std::string & preprocessed,
59                 std::string const & comparevalue, bool substr=false);
60 bool readlines(istream * is, std::string & allcommands);
61 bool readfile(std::string const & filename, std::string & allcommands, bool shutup=false);
62 void savefile(std::string const & filename, std::string const & allcommands);
63
64 typedef list<char *> cplist;
65 cplist filenames;
66 bool redirect_output = false;
67 bool silent = false;
68
69 G__value exec_tempfile(std::string const & command)
70 {
71         G__value retval;
72         char *tmpfilename = tempnam(NULL,"ginac");
73         std::ofstream fout;
74         fout.open(tmpfilename);
75         fout << "{" << std::endl << command << std::endl << "}" << std::endl;
76         fout.close();
77         G__store_undo_position();
78         retval = G__exec_tempfile(tmpfilename);
79         G__security_recover(stdout);
80         remove(tmpfilename);
81         free(tmpfilename);
82         return retval;
83 }
84
85 char * process_permanentfile(std::string const & command)
86 {
87         char *tmpfilename = tempnam(NULL,"ginac");
88         if (!silent)
89                 std::cout << "creating file " << tmpfilename << std::endl;
90         std::ofstream fout;
91         fout.open(tmpfilename);
92         fout << command << std::endl;
93         fout.close();
94         G__store_undo_position();
95         G__loadfile(tmpfilename);
96         G__security_recover(stdout);
97         return tmpfilename;
98 }
99
100 void process_tempfile(std::string const & command)
101 {
102 #ifdef OBSCURE_CINT_HACK
103         static G__value ref_symbol = exec_tempfile("symbol ginac_cint_internal_symbol; ginac_cint_internal_symbol;");
104         static G__value ref_constant = exec_tempfile("constant ginac_cint_internal_constant; ginac_cint_internal_constant;");
105         static G__value ref_function = exec_tempfile("sin(ginac_cint_internal_symbol);");
106         static G__value ref_power = exec_tempfile("power(ex(ginac_cint_internal_symbol),ex(ginac_cint_internal_symbol));");
107         static G__value ref_numeric = exec_tempfile("numeric ginac_cint_internal_numeric; ginac_cint_internal_numeric;");
108         static G__value ref_ex = exec_tempfile("ex ginac_cint_internal_ex; ginac_cint_internal_ex;");
109         static bool basic_type_warning_already_displayed = false;
110 #endif // def OBSCURE_CINT_HACK
111
112         G__value retval = exec_tempfile(command);
113
114 #ifdef OBSCURE_CINT_HACK
115
116         #define TYPES_EQUAL(A,B) (((A).type==(B).type) && ((A).tagnum==(B).tagnum))
117         
118         static unsigned out_count = 0;
119         if (TYPES_EQUAL(retval,ref_ex)) {
120                 std::string varname = "Out"+ToString(++out_count);
121                 if (retval.obj.i!=ex::last_created_or_assigned_exp) {
122                         // an ex was returned, but this is not the ex which was created last
123                         // => this is not a temporary ex, but one that resides safely in memory
124                         
125                         // std::cout << "warning: using ex from retval (experimental)" << std::endl;
126                         ex::dummy_bp = ((ex *)(void *)(retval.obj.i))->bp;
127                         exec_tempfile("ex "+varname+"(*ex::dummy_bp);");
128                 } else if (ex::last_created_or_assigned_bp_can_be_converted_to_ex()) {
129                         exec_tempfile("ex "+varname+"(*ex::last_created_or_assigned_bp);");
130                 } else {
131                         std::cout << "warning: last_created_or_assigned_bp modified 0 or not evaluated or not dynallocated" << std::endl;
132                 }
133                 exec_tempfile(std::string()+"LLLAST=LLAST;\n"
134                               +"LLAST=LAST;\n"
135                               +"LAST="+varname+";\n"
136                               +"if (ginac_cint_internal_redirect_output&&"
137                               +"    ginac_cint_internal_fout.good()) {" 
138                               +"    ginac_cint_internal_fout << \""+varname+" = \" << "+varname+" << endl << endl;"
139                               +"} else {"
140                               +"    std::cout << \""+varname+" = \" << "+varname+" << endl << endl;"
141                               +"}");
142         } else if (TYPES_EQUAL(retval,ref_symbol)||
143                    TYPES_EQUAL(retval,ref_constant)||
144                    TYPES_EQUAL(retval,ref_function)||
145                    TYPES_EQUAL(retval,ref_power)||
146                    TYPES_EQUAL(retval,ref_numeric)) {
147                 if (!basic_type_warning_already_displayed) {
148                         std::cout << std::endl
149                                   <<"WARNING: The return value of the last expression you entered was a symbol," << std::endl
150                                   << "constant, function, power or numeric, which cannot be safely displayed." << std::endl
151                                   << "To force the output, cast it explicitly to type 'ex' or use 'cout'," << std::endl
152                                   << "for example (assume 'x' is a symbol):" << std::endl
153                                   << PROMPT1 "ex(x);" << std::endl
154                                   << "OutX = x" << std::endl << std::endl
155                                   << PROMPT1 "cout << x << endl;" << std::endl
156                                   << "x" << std::endl << std::endl
157                                   << "This warning will not be shown again." << std::endl;
158                         basic_type_warning_already_displayed = true;
159                 }
160         }
161 #endif // def OBSCURE_CINT_HACK
162         return;
163 }
164
165 void printversionmessage(std::ostream & os)
166 {
167         os << "GiNaC-cint (" << PACKAGE << " V" << VERSION
168            << ", Cint V" << G__CINTVERSION/1000000
169            << '.' << G__CINTVERSION/1000%1000
170            << '.' << G__CINTVERSION%1000 << ')' << endl;
171         return;
172 }
173
174 void greeting(void)
175 {
176         std::cout << "Welcome to ";
177         printversionmessage(std::cout);
178         std::cout << "  __,  _______  GiNaC: (C) 1999-2001 Johannes Gutenberg University Mainz,\n"
179                   << " (__) *       | Germany.  Cint C/C++ interpreter: (C) 1995-2001 Masaharu\n"
180                   << "  ._) i N a C | Goto and Agilent Technologies, Japan.  This is free software\n"
181                   << "<-------------' with ABSOLUTELY NO WARRANTY.  For details, type `.warranty'\n"
182                   << "Type `.help' for help.\n\n";
183         return;
184 }
185
186 void helpmessage(void)
187 {
188     std::cout << "GiNaC-cint recognizes some special commands which start with a dot:\n\n"
189                   << "  .cint                    switch to cint interactive mode (see cint\n"
190                   << "                           documentation for further details)\n"
191                   << "  .function                define the body of a function (necessary due to a\n"
192                   << "                           cint limitation)\n"
193                   << "  .help                    the text you are currently reading\n"
194                   << "  .q, .quit, .exit, .bye   quit GiNaC-cint\n"
195                   << "  .read filename           read a file from disk and execute it in GiNaC-cint\n"
196                   << "                           (recursive call is possible)\n"
197                   << "  .redirect [filename]     redirect 'OutXY = ...' output to a file\n"
198                   << "                           (.redirect alone redirects output back to console)\n"
199                   << "  .restart                 restart GiNaC-cint (does not re-read command line\n"
200                   << "                           files)\n"
201                   << "  .save filename           save the commands you have entered so far in a file\n"
202                   << "  .silent                  suppress 'OutXY = ...' output (variables are still\n"
203                   << "                           accessible)\n"
204                   << "  .warranty                information on redistribution and warranty\n"
205                   << "  .> [filename]            same as .redirect [filename]\n\n"
206                   << "Instead of '.cmd' you can also write '//GiNaC-cint.cmd' to be compatible with\n"
207                   << "programs that will be compiled later.\n"
208                   << "Additionally you can exit GiNaC-cint with quit; exit; or bye;\n\n";
209     return;
210 }
211
212 void warrantymessage(void)
213 {
214         std::cout << "GiNaC is free software; you can redistribute it and/or modify it under the\n"
215                   << "the terms of the GNU General Public License as published by the Free Software\n"
216                   << "Foundation; either version 2 of the License, or (at your option) any later\n"
217                   << "version.\n"
218                   << "This program is distributed in the hope that it will be useful, but WITHOUT\n"
219                   << "ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS\n"
220                   << "FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more\n"
221                   << "details.\n"
222                   << "You should have received a copy of the GNU General Public License along with\n"
223                   << "this program. If not, write to the Free Software Foundation, 675 Mass Ave,\n"
224                   << "Cambridge, MA 02139, USA.\n\n";
225         std::cout << "Cint and associated tools are copyright by Agilent Technologies Japan Company\n"
226                   << "and Masaharu Goto <MXJ02154@niftyserve.or.jp>.\n"
227                   << "Source code, binary executable or library of Cint and associated tools can be\n"
228                   << "used, modified and distributed with no royalty for any purpose provided that\n"
229                   << "the copyright notice appear in all copies and that both that copyright notice\n"
230                   << "and this permission notice appear in supporting documentation.\n"
231                   << "Agilent Technologies Japan and the author make no representations about the\n"
232                   << "suitability of this software for any purpose.  It is provided \"AS IS\"\n"
233                   << "without express or implied warranty.\n";
234     return;
235 }
236
237 /** "preprocess" the line entered to be able to decide if the command shall be
238  *  executed directly or more input is needed or this is a special command.
239  *  All whitespace will be removed.  All comments will be removed.  Open and
240  *  close braces ( { and } ) outside strings will be counted. */
241 std::string preprocess(char const * const line, bool & comment, bool & single_quote,
242                        bool & double_quote, unsigned & open_braces)
243 {
244         std::string preprocessed;
245         int pos = 0;
246         bool end = false;
247         bool escape = false;
248         bool slash = false;
249         bool asterisk = false;
250         while ((line[pos]!='\0')&&!end) {
251                 if (escape) {
252                         // last character was a \, ignore this one
253                         escape = false;
254                 } else if (slash) {
255                         // last character was a /, test if * or /
256                         slash = false;
257                         if (line[pos]=='/') {
258                                 end = true;
259                         } else if (line[pos]=='*') {
260                                 comment = true;
261                         } else {
262                                 preprocessed += '/';
263                                 preprocessed += line[pos];
264                         }
265                 } else if (asterisk) {
266                         // last character was a *, test if /
267                         asterisk = false;
268                         if (line[pos]=='/') {
269                                 comment = false;
270                         } else if (line[pos]=='*') {
271                                 preprocessed += '*';
272                                 asterisk = true;
273                         }
274                 } else {
275                         switch (line[pos]) {
276                         case ' ':
277                         case '\t':
278                         case '\n':
279                         case '\r':
280                                 // whitespace: ignore
281                                 break;
282                         case '\\':
283                                 // escape character, ignore next
284                                 escape = true;
285                                 break;
286                         case '"':
287                                 if ((!single_quote)&&(!comment)) {
288                                         double_quote = !double_quote;
289                                 }
290                                 break;
291                                 case '\'':
292                                         if ((!double_quote)&&(!comment)) {
293                                                 single_quote = !single_quote;
294                                         }
295                                         break;
296                         case '{':
297                                 if ((!single_quote)&&(!double_quote)&&(!comment)) {
298                                         ++open_braces;
299                                 }
300                                 break;
301                         case '}':
302                                 if ((!single_quote)&&(!double_quote)&&(!comment)&&(open_braces>0)) {
303                                         --open_braces;
304                                 }
305                                 break;
306                         case '/':
307                                 slash = true;
308                                 break;
309                         case '*':
310                                 asterisk = true;
311                                 break;
312                         default:
313                                 preprocessed += line[pos];
314                         }
315                 }
316                 ++pos;
317         }
318         
319         return preprocessed;
320 }
321
322 void cleanup(void)
323 {
324         for (cplist::iterator it=filenames.begin(); it!=filenames.end(); ++it) {
325                 if (!silent)
326                         std::cout << "removing file " << *it << std::endl;
327                 remove(*it);
328                 free(*it);
329         }
330 }
331
332 void sigterm_handler(int n)
333 {
334         G__scratch_all();
335         exit(1);
336 }
337
338 void initialize(void)
339 {
340         atexit(cleanup);
341         signal(SIGTERM, sigterm_handler);
342         initialize_cint();
343 }    
344
345 void initialize_cint(void)
346 {
347         G__init_cint("cint");    /* initialize cint */
348         
349         exec_tempfile("using namespace GiNaC;");
350         exec_tempfile("ex LAST,LLAST,LLLAST;\n");
351         exec_tempfile("bool ginac_cint_internal_redirect_output = false;\n");
352         exec_tempfile("ofstream ginac_cint_internal_fout;\n");
353 }    
354
355 void restart(void)
356 {
357         std::cout << "Restarting GiNaC-cint." << std::endl;
358         G__scratch_all();
359         initialize_cint();
360 }
361
362 void redirect(std::string const & filename,
363               bool shutup=false)
364 {
365         if (filename=="") {
366                 if (!shutup)
367                         std::cout << "Redirecting output back to console..." << std::endl;
368                 exec_tempfile(std::string()
369                               +"ginac_cint_internal_redirect_output=false;\n"
370                               +"ginac_cint_internal_fout.close();");
371         } else {
372                 if (!shutup)
373                         std::cout << "Redirecting output to " << filename << "..." << std::endl;
374                 exec_tempfile(std::string()
375                               +"ginac_cint_internal_redirect_output=true;\n"
376                               +"ginac_cint_internal_fout.open(\""+filename+"\");\n");
377         }
378 }
379
380 /** Sort out command line options and evaluate them.  Returns true if it
381  *  succeeds and false otherwise. */
382 bool evaloption(const std::string & option)
383 {
384         if (option=="--version") {
385                 printversionmessage(std::cout);
386                 exit(0);
387         }
388         if (option=="--help") {
389                 printversionmessage(std::cout);
390                 std::cout << "usage: ginaccint [option] [file ...]\n";
391                 std::cout << " --help           print this help message and exit\n"
392                           << " --silent         invoke ginaccint in silent mode\n"
393                           << " --version        print GiNaC version and Cint version and exit\n";
394                 exit(0);
395         }
396         if (option=="--silent") {
397                 redirect("/dev/null",true);
398                 silent = true;
399                 return true;
400         }
401         return false;
402 }
403
404 bool is_command(std::string const & command,
405                 std::string & preprocessed,
406                 std::string const & comparevalue,
407                 bool substr)
408 {
409         bool single_quote = false;
410         bool double_quote = false;
411         bool comment = false;
412         unsigned open_braces = 0;
413         if ((preprocessed=="."+comparevalue)||
414                 substr&&(preprocessed.substr(0,comparevalue.length()+1)==
415                                  "."+comparevalue)) {
416                 return true;
417         }
418         if ((command=="//GiNaC-cint."+comparevalue+"\n") ||
419                 substr &&
420                 (command.substr(0,comparevalue.length()+13)=="//GiNaC-cint."+comparevalue)) {
421                 preprocessed = preprocess(command.substr(12).c_str(),comment,
422                                           single_quote,double_quote,open_braces);
423                 return true;
424         }
425         return false;
426 }
427
428 bool readlines(istream * is,
429                std::string & allcommands)
430 {
431         char const * line;
432         char prompt[G__ONELINE];
433         std::string linebuffer;
434         
435         bool quit = false;
436         bool eof = false;
437         bool next_command_is_function = false;
438         bool single_quote = false;
439         bool double_quote = false;
440         bool comment = false;
441         unsigned open_braces = 0;
442
443         while ((!quit)&&(!eof)) {
444                 strcpy(prompt,PROMPT1);
445                 bool end_of_command = false;
446                 std::string command;
447                 std::string preprocessed;
448                 while (!end_of_command) {
449                         if (is==NULL) {
450                                 line = G__input(prompt);
451                         } else {
452                                 getline(*is,linebuffer);
453                                 line = linebuffer.c_str();
454                         }
455                         command += line;
456                         command += "\n";
457                         preprocessed += preprocess(line,comment,single_quote,double_quote,open_braces);
458                         if ((open_braces==0)&&(!single_quote)&&(!double_quote)&&(!comment)) {
459                                 unsigned l = preprocessed.length();
460                                 if ((l==0)||
461                                     (preprocessed[0]=='#')||
462                                     (preprocessed[0]=='.')||
463                                     (preprocessed[l-1]==';')||
464                                     (preprocessed[l-1]=='}')) {
465                                     end_of_command = true;
466                                 }
467                         }
468                         strcpy(prompt,PROMPT2);
469                 }
470                 if ((preprocessed=="quit;")||
471                     (preprocessed=="exit;")||
472                     (preprocessed=="bye;")||
473                     (is_command(command,preprocessed,"quit"))||
474                     (is_command(command,preprocessed,"exit"))||
475                     (is_command(command,preprocessed,"bye"))||
476                     (is_command(command,preprocessed,"q"))) {
477                     quit = true;
478                 } else if (is_command(command,preprocessed,"function")) {
479                         if (!silent)
480                                 std::cout << "next expression can be a function definition" << std::endl;
481                         next_command_is_function = true;
482                 } else if (is_command(command,preprocessed,"cint")) {
483                         std::cout << std::endl << "switching to cint interactive mode" << std::endl;
484                         std::cout << "'h' for help, 'q' to quit, '{ statements }' or 'p [expression]' to evaluate" << std::endl;
485                         G__pause();
486                         std::cout << "back from cint" << std::endl;
487                 } else if (is_command(command,preprocessed,"help")) {
488                         helpmessage();
489                 } else if (is_command(command,preprocessed,"read",true)) {
490                         quit = readfile(preprocessed.substr(5),allcommands);
491                 } else if (is_command(command,preprocessed,"save",true)) {
492                         command = "/* "+command+" */"; // we do not want the .save command itself in saved files
493                         savefile(preprocessed.substr(5),allcommands);
494                 } else if (is_command(command,preprocessed,"restart")) {
495                         restart();
496                 } else if (is_command(command,preprocessed,"redirect",true)) {
497                         redirect(preprocessed.substr(9));
498                 } else if (is_command(command,preprocessed,">",true)) {
499                         redirect(preprocessed.substr(2));
500                 } else if (is_command(command,preprocessed,"silent")) {
501                         if (!silent) {
502                                 redirect("/dev/null");
503                                 silent = true;
504                         } else {
505                                 redirect("");
506                                 silent = false;
507                         }
508                 } else if (is_command(command,preprocessed,"warranty")) {
509                         warrantymessage();
510                 /* insert tests for more special commands here */
511                 } else if (command.substr(0,2)=="#!") {
512                         // ignore lines which indicate that this file is executed as a script
513                 } else {
514                         // std::cout << "now processing: " << command << std::endl;
515                         if (next_command_is_function) {
516                                 next_command_is_function = false;
517                                 filenames.push_back(process_permanentfile(command));
518                         } else {
519                                 process_tempfile(command);
520                         }
521                 }
522                 if (is!=NULL) {
523                         // test for end of file if reading from a stream
524                         eof = is->eof();
525                 } else {
526                         // save commands only when reading from keyboard
527                         allcommands += command;
528                 }
529
530         }
531         return quit;
532
533
534 bool readfile(std::string const & filename,
535               std::string & allcommands,
536               bool shutup = false)
537 {
538         if (!shutup)
539                 std::cout << "Reading commands from file " << filename << "." << std::endl;
540         bool quit = false;
541         std::ifstream fin;
542         fin.open(filename.c_str());
543         if (fin.good())
544                 quit = readlines(&fin,allcommands);
545         else
546                 std::cout << "Cannot open " << filename << " for reading." << std::endl;
547         fin.close();
548         return quit;
549 }
550
551 void savefile(std::string const & filename, std::string const & allcommands)
552 {
553         std::cout << "Saving commands to file " << filename << "." << std::endl;
554         std::ofstream fout;
555         fout.open(filename.c_str());
556         if (fout.good()) {
557                 fout << allcommands;
558                 if (!fout.good()) {
559                         std::cout << "Cannot save commands to " << filename << "." << std::endl;
560                 }
561         } else {
562                 std::cout << "Cannot open " << filename << " for writing." << std::endl;
563         }
564         fout.close();
565 }
566
567 int main(int argc, char * *argv) 
568 {
569         std::string allcommands;
570         initialize();
571         
572         bool quit = false;
573         // sort out and evaluate recognized options from the argument list
574         for (int i=1; i<argc; ++i)
575                 if (evaloption(argv[i])) {
576                         for (int j=i; j<argc-1; ++j)
577                                 argv[j] = argv[j+1];
578                         --argc;
579                 }
580         bool argsexist = argc>1;
581         
582         // greet the user if it makes sense
583         if (isatty(0) && !silent)
584                 greeting();
585         
586         // evaluate files given as command line arguments
587         if (argsexist) {
588                 allcommands = "/* Files given as command line arguments:\n";
589                 --argc;
590                 while (argc && !quit) {
591                         allcommands += std::string(argv[argc])+'\n';
592                         quit = readfile(argv[argc], allcommands, silent);
593                         --argc;
594                 }
595                 allcommands += "*/\n";
596         }
597         
598         // evaluate input from command line or script
599         if (!quit)
600                 readlines(NULL, allcommands);
601         
602         return 0;
603 }