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