1 /***************************************************************************
2                           gdl.cpp  -  main program
3                              -------------------
4     begin                : Wed Apr 18 16:58:14 JST 2001
5     copyright            : (C) 2002-2006 by Marc Schellens
6     email                : m_schellens@users.sourceforge.net
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "includefirst.hpp"
19 
20 // #ifndef VERSION
21 // #define VERSION "0.9"
22 // #endif
23 
24 #include <string>
25 #include <csignal>
26 #include <cstdlib>
27 #if defined(_MSC_VER) || defined (_WIN32)
28 #include <io.h>
29 #define isatty _isatty
30 #else
31 #include <unistd.h> // isatty
32 #endif
33 #include <climits> // PATH_MAX
34 //patch #90
35 #ifndef PATH_MAX
36 #define PATH_MAX 4096
37 #endif
38 #ifndef _WIN32
39 #include <sys/resource.h> //rlimits to augment stack size (needed fot DICOM objects)
40 #endif
41 
42 //#include <fenv.h>
43 
44 #include "str.hpp"
45 #include "dinterpreter.hpp"
46 #include "terminfo.hpp"
47 #include "sigfpehandler.hpp"
48 #include "gdleventhandler.hpp"
49 
50 #ifdef _OPENMP
51 #include <omp.h>
52 #endif
53 
54 #ifdef USE_MPI
55 #  include "mpi.h"
56 #endif
57 
58 #ifdef HAVE_LOCALE_H
59 #  include <locale.h>
60 #endif
61 
62 // GDLDATADIR
63 #include "config.h"
64 
65 //initialize wxWidgets system
66 #ifdef HAVE_LIBWXWIDGETS
67 #include "gdlwidget.hpp"
68 #ifndef __WXMAC__
69 wxIMPLEMENT_APP_NO_MAIN( wxAppGDL);
70 #else
71 wxIMPLEMENT_APP_NO_MAIN( wxApp);
72 #endif
73 #endif
74 
75 using namespace std;
76 
StartupMessage()77 static void StartupMessage()
78 {
79   cerr << "  GDL - GNU Data Language, Version " << VERSION << endl;
80   cerr << "- For basic information type HELP,/INFO" << endl;
81 }
82 
83 void LibInit(); // defined in libinit.cpp
84 
85 namespace lib {
86 void SetGDLGenericGSLErrorHandler(); // defined in gsl_fun.cpp
87 }
88 
89 // Nodar and Alain, May 2013: we try to optimize the value for CpuTPOOL_NTHREADS
90 // if *valid* external value for OMP_NUM_THREADS (0 < OMP_NUM_THREADS < nb_cores) we used it
91 // if not provided, we try to estimate a value looking at the average load
92 
InitOpenMP()93 void InitOpenMP() {
94 #ifdef _OPENMP
95   int suggested_num_threads, omp_num_core;
96   suggested_num_threads=get_suggested_omp_num_threads();
97   omp_num_core=omp_get_num_procs();
98 
99   //  cerr << "estimated Threads :" << suggested_num_threads << endl;
100 
101   // we update iff needed (by default, "omp_num_threads" is initialiazed to "omp_num_core"
102   if ((suggested_num_threads > 0) && (suggested_num_threads < omp_num_core)) {
103 
104     // update of !cpu.TPOOL_NTHREADS
105     DStructGDL* cpu = SysVar::Cpu();
106     static unsigned NTHREADSTag = cpu->Desc()->TagIndex( "TPOOL_NTHREADS");
107     (*static_cast<DLongGDL*>( cpu->GetTag( NTHREADSTag, 0)))[0] =suggested_num_threads;
108 
109     // effective global change of num of treads using omp_set_num_threads()
110     CpuTPOOL_NTHREADS=suggested_num_threads;
111     omp_set_num_threads(suggested_num_threads);
112   } else {
113     CpuTPOOL_NTHREADS=omp_get_num_procs();
114     omp_set_num_threads(CpuTPOOL_NTHREADS);
115   }
116   //  cout << CpuTPOOL_NTHREADS <<endl;
117 #endif
118 }
119 
AtExit()120 void AtExit()
121 {
122   //this function probably cleans otherwise cleaned objets and should be called only for debugging purposes.
123   cerr << "Using AtExit() for debugging" << endl;
124   cerr << flush; cout << flush; clog << flush;
125   // clean up everything
126   // (for debugging memory leaks)
127   ResetObjects();
128   PurgeContainer(libFunList);
129   PurgeContainer(libProList);
130 }
131 
132 #ifndef _WIN32
GDLSetLimits()133 void GDLSetLimits()
134 {
135 #define GDL_PREFERED_STACKSIZE 20480000 //20000*1024 OK for the time being
136 struct rlimit* gdlstack=new struct rlimit;
137   int r=getrlimit(RLIMIT_STACK,gdlstack);
138 //  cerr <<"Current rlimit = "<<gdlstack->rlim_cur<<endl;
139 //  cerr<<"Max rlimit = "<<  gdlstack->rlim_max<<endl;
140   if (gdlstack->rlim_cur >= GDL_PREFERED_STACKSIZE ) return; //the bigger the better.
141   if (gdlstack->rlim_max > GDL_PREFERED_STACKSIZE ) gdlstack->rlim_cur=GDL_PREFERED_STACKSIZE; //not completely satisfactory.
142   r=setrlimit(RLIMIT_STACK,gdlstack);
143 }
144 #endif
145 
InitGDL()146 void InitGDL()
147 {
148 #ifndef _WIN32
149   GDLSetLimits();
150 #endif
151 
152 //rl_event_hook (defined below) uses a wxwidgets event loop, so wxWidgets must be started
153 #ifdef HAVE_LIBWXWIDGETS
154     if (useWxWidgets) GDLWidget::Init();
155 #endif
156 
157 #if defined(HAVE_LIBREADLINE)
158   // initialize readline (own version - not pythons one)
159   // in includefirst.hpp readline is disabled for python_module
160   rl_initialize();
161   char rlName[] = "GDL";
162   rl_readline_name = rlName;
163   //Our handler takes too long
164   //when editing the command line with ARROW keys. (bug 562). (used also in dinterpreted.cpp )
165   //but... without it we have no graphics event handler! FIXME!!!
166   rl_event_hook = GDLEventHandler;
167 #endif
168 
169   // ncurses blurs the output, initialize TermWidth here
170   TermWidth();
171 
172   // initializations
173   InitObjects();
174 
175   // init library functions
176   LibInit();
177 
178   // ensuring we work in the C locale (needs to be called after InitObjects and LibInit!!!
179   // as some code there calls setlocale as well, e.g. MagickInit)
180 #ifdef HAVE_LOCALE_H
181   setlocale(LC_ALL, "C");
182 #endif
183 
184   // turn on all floating point exceptions
185   //  feenableexcept(FE_DIVBYZERO | FE_INVALID | FE_OVERFLOW );
186 
187   signal(SIGINT,ControlCHandler);
188   signal(SIGFPE,SigFPEHandler);
189 
190   lib::SetGDLGenericGSLErrorHandler();
191 }
192 
193 static bool trace_me;
194 
195 // SA: for use in COMMAND_LINE_ARGS()
196 namespace lib {
197   extern std::vector<std::string> command_line_args;
198 #ifdef _WIN32
199   bool posixpaths;
200 #endif
gdlarg_present(const char * s)201   bool gdlarg_present(const char* s)
202   {
203 		for (size_t i = 0; i < command_line_args.size(); i++)
204 			  if( command_line_args[i] == s )  return true;
205 		return false;
206 		  }
trace_arg()207   bool trace_arg()
208   {
209 		for (size_t i = 0; i < command_line_args.size(); i++)
210 			  if( command_line_args[i] == "trace" )  return true;
211 		return false;
212    }
213 
214 }
215 
main(int argc,char * argv[])216 int main(int argc, char *argv[])
217 {
218 #if GDL_DEBUG
219   if( atexit( AtExit) != 0) cerr << "atexit registration failed." << endl;
220 #endif
221   // indicates if the user wants to see the welcome message
222   bool quiet = false;
223   bool gdlde = false;
224 
225   // keeps a list of files to be executed after the startup file
226   // and before entering the interactive mode
227   vector<string> batch_files;
228   string statement;
229   string pretendRelease;
230   bool strict_syntax=false;
231   bool syntaxOptionSet=false;
232 
233   bool force_no_wxgraphics = false;
234   usePlatformDeviceName=false;
235   forceWxWidgetsUglyFonts = false;
236   useDSFMTAcceleration = true;
237   iAmANotebook=false; //option --notebook
238  #ifdef HAVE_LIBWXWIDGETS
239   useWxWidgets=true;
240 #else
241   useWxWidgets=false;
242 #endif
243 #ifdef _WIN32
244   lib::posixpaths = false;
245 #endif
246   for( SizeT a=1; a< argc; ++a)
247     {
248       if( string( argv[a]) == "--help" | string( argv[a]) == "-h") {
249       cerr << "Usage: gdl [ OPTIONS ] [ batch_file ... ]" << endl;
250       cerr << "Start the GDL interpreter (incremental compiler)" << endl;
251       cerr << endl;
252       cerr << "GDL options:" << endl;
253       cerr << "  --help (-h)        display this message" << endl;
254       cerr << "  --version (-V, -v) show version information" << endl;
255       cerr << "  --fakerelease X.y  pretend that !VERSION.RELEASE is X.y" << endl;
256       cerr << "  --fussy            implies that procedures adhere with modern IDL, where \"()\" are for functions and \"[]\" are for arrays." << endl;
257       cerr << "                     This speeds up (sometimes terribly) compilation but choke on every use of \"()\" with arrays." << endl;
258       cerr << "                     Conversion of procedures to modern IDL can be done with D. Landsman's idlv4_to_v5 procedure." << endl;
259       cerr << "                     Use enviromnment variable \"GDL_IS_FUSSY\" to set up permanently this feature." << endl;
260       cerr << "  --sloppy           Sets the traditional (default) compiling option where \"()\"  can be used both with functions and arrays." << endl;
261       cerr << "                     Needed to counteract temporarily the effect of the enviromnment variable \"GDL_IS_FUSSY\"." << endl;
262       cerr << "  --MAC              Graphic device will be called 'MAC' on MacOSX. (default: 'X')" << endl;
263       cerr << "  --no-use-wx        Tells GDL not to use WxWidgets graphics." << endl;
264       cerr << "                     Also enabled by setting the environment variable GDL_DISABLE_WX_PLOTS to a non-null value." << endl;
265       cerr << "  --notebook         Force SVG-only device, used only when GDL is a Python Notebook Kernel." << endl;
266       cerr << "  --widget-compat    Tells GDL to use a default (rather ugly) fixed pitch font for compatiblity with IDL widgets." << endl;
267       cerr << "                     Also enabled by setting the environment variable GDL_WIDGET_COMPAT to a non-null value." << endl;
268       cerr << "                     Using this option may render some historical widgets unworkable (as they are based on fixed sizes)." << endl;
269       cerr << "  --no-dSFMT         Tells GDL not to use double precision SIMD oriented Fast Mersenne Twister(dSFMT) for random doubles." << endl;
270       cerr << "                     Also disable by setting the environment variable GDL_NO_DSFMT to a non-null value." << endl;
271 #ifdef _WIN32
272       cerr << "  --posix (Windows only): paths will be posix paths (experimental)." << endl;
273 #endif
274       cerr << endl;
275       cerr << "IDL-compatible options:" << endl;
276       cerr << "  -arg value tells COMMAND_LINE_ARGS() to report" << endl;
277       cerr << "             the following argument (may be specified more than once)" << endl;
278       cerr << "  -args ...  tells COMMAND_LINE_ARGS() to report " << endl;
279       cerr << "             all following arguments" << endl;
280       cerr << "  -e value   execute given statement and exit (last occurrence taken into account only," << endl;
281       cerr << "             executed after startup file, may not be specified together with batch files)" << endl;
282       cerr << "  -pref=/path/to/params_file  loads the specified preference file" << endl;
283       cerr << "  -quiet (--quiet, -q) suppress welcome messages" << endl;
284       cerr << endl;
285       cerr << "Homepage: https://gnudatalanguage.github.io" << endl;
286       return 0;
287     }
288       else if (string(argv[a])=="--version" | string(argv[a])=="-v" | string(argv[a])=="-V")
289 	{
290 	  cerr << "GDL - GNU Data Language, Version " << VERSION << endl;
291 	  return 0;
292 	}
293       else if( string( argv[a]) == "-arg")
294       {
295         if (a == argc - 1)
296         {
297           cerr << "gdl: -arg must be followed by a user argument." << endl;
298           return 0;
299         }
300         lib::command_line_args.push_back(string(argv[++a]));
301       }
302       else if( string( argv[a]) == "-args")
303       {
304         for (int i = a + 1; i < argc; i++) lib::command_line_args.push_back(string(argv[i]));
305         break;
306       }
307       else if (string(argv[a])=="-quiet" | string(argv[a])=="--quiet" | string(argv[a])=="-q")
308 	{
309 	  quiet = true;
310 	}
311       else if (string(argv[a]).find("-pref=") ==0)
312 	{
313 	  cerr << "This option is not operational now" << endl;
314 	  string tmp;
315 	  tmp=string(argv[a]);
316 	  string params_file(tmp.begin()+6,tmp.end());
317 	  //cerr << "(not ready) to be processed file >>" << params_file << "<<" << endl;
318 	  WordExp(params_file);
319 	  ifstream file_params;
320 	  file_params.open(params_file.c_str());
321 	  if (!file_params.is_open()) {
322 	    cerr << "Error opening file. File: "<< params_file << endl;
323 	    cerr << "No such file or directory"<< endl;
324 	    return 0;
325 	  }
326 	  file_params.close();
327 	}
328       else if (string(argv[a]) == "-e")
329 	{
330 	  if (a == argc - 1)
331 	    {
332 	      cerr << "gdl: -e must be followed by a user argument." << endl;
333 	      return 0;
334 	    }
335 	  statement = string(argv[++a]);
336 	  statement.append("\n"); // apparently not needed but this way the empty-string case is covered
337 	  // (e.g. $ gdl -e "")
338 	}
339       else if (
340 	    string(argv[a]) == "-demo" ||
341         string(argv[a]) == "-em" ||
342         string(argv[a]) == "-novm" ||
343         string(argv[a]) == "-queue" ||
344         string(argv[a]) == "-rt" ||
345         string(argv[a]) == "-ulicense" ||
346         string(argv[a]) == "-vm"
347       )
348         cerr << argv[0] << ": " << argv[a] << " option ignored." << endl;
349       else if (string(argv[a]) == "-gdlde")
350       {
351           gdlde = true;
352       }
353       else if (string(argv[a]) == "--fussy")
354       {
355           strict_syntax = true;
356           syntaxOptionSet = true;
357       }
358       else if (string(argv[a]) == "--sloppy")
359       {
360           strict_syntax = false;
361           syntaxOptionSet = true;
362       }
363       else if (string(argv[a]) == "--no-dSFMT")
364       {
365            useDSFMTAcceleration = false;
366       }
367       else if (string(argv[a]) == "--widget-compat")
368       {
369           forceWxWidgetsUglyFonts = true;
370       }
371 #ifdef _WIN32
372       else if (string(argv[a]) == "--posix") lib::posixpaths=true;
373 #endif
374       else if (string(argv[a]) == "--MAC")
375       {
376          usePlatformDeviceName = true;
377       }
378       else if (string(argv[a]) == "--no-use-wx")
379       {
380          force_no_wxgraphics = true;
381       }
382       else if (string(argv[a]) == "--notebook")
383       {
384          iAmANotebook = true;
385       }
386       else if (string(argv[a]) == "--fakerelease")
387       {
388         if (a == argc - 1)
389           {
390             cerr << "gdl: --fakerelease must be followed by a string argument like \"6.4\"" << endl;
391             return 0;
392           }
393         pretendRelease = string(argv[++a]);
394       }
395       else if (*argv[a] == '-')
396       {
397         cerr << argv[0] << ": " << argv[a] << " option not recognized." << endl;
398         return 0;
399       }
400       else
401       {
402         batch_files.push_back(argv[a]);
403       }
404     }
405 
406   if (0&&statement.length() > 0 && batch_files.size() > 0)
407   {
408     cerr << argv[0] << ": " << "-e option cannot be specified with batch files" << endl;
409     return 0;
410   }
411 
412   //before InitGDL() as InitGDL() starts graphic!
413 
414 #ifdef HAVE_LIBWXWIDGETS
415   //tells if wxWidgets is working (may not be the case if DISPLAY is not set) by setting useWxWidgets to false
416   useWxWidgets=GDLWidget::InitWx();
417   // default is wx Graphics...
418   useWxWidgetsForGraphics=useWxWidgets;
419 #else
420   useWxWidgetsForGraphics=false;
421 #endif
422 #ifdef HAVE_X
423   // unless we have X and want to see it for plots
424   std::string disableWXPlots=GetEnvString("GDL_DISABLE_WX_PLOTS");
425   if ( disableWXPlots.length() > 0) useWxWidgetsForGraphics=false; //not necessary "YES".
426   if (force_no_wxgraphics) useWxWidgetsForGraphics=false; //this has the last answer, whatever the setup.
427 #endif
428   std::string doUseUglyFonts=GetEnvString("GDL_WIDGETS_COMPAT");
429   if ( doUseUglyFonts.length() > 0) forceWxWidgetsUglyFonts=true;
430 
431   InitGDL();
432 
433   // must be after !cpu initialisation
434   InitOpenMP();
435 
436   if (gdlde || (isatty(0) && !quiet)) StartupMessage();
437 
438   // instantiate the interpreter
439   DInterpreter interpreter;
440 
441   string gdlPath=GetEnvString("GDL_PATH");
442   if( gdlPath == "") gdlPath=GetEnvString("IDL_PATH");
443   if( gdlPath == "")
444     {
445       gdlPath = "+" GDLDATADIR "/lib";
446       if (gdlde || (isatty(0) && !quiet)) cerr <<
447         "- Default library routine search path used (GDL_PATH/IDL_PATH env. var. not set): " GDLDATADIR "/lib" << endl;
448     }
449   if (useWxWidgetsForGraphics) {
450       if (gdlde || (isatty(0) && !quiet)) cerr << "- Using WxWidgets as graphics library (windows and widgets)." << endl;
451     }
452 
453 
454   if (useDSFMTAcceleration && (GetEnvString("GDL_NO_DSFMT").length() > 0)) useDSFMTAcceleration=false;
455 
456   //report in !GDL status struct
457   DStructGDL* gdlconfig = SysVar::GDLconfig();
458   unsigned  DSFMTTag= gdlconfig->Desc()->TagIndex("GDL_USE_DSFMT");
459   (*static_cast<DByteGDL*> (gdlconfig->GetTag(DSFMTTag, 0)))[0]=useDSFMTAcceleration;
460 
461   //same for use of wxwidgets
462   unsigned  useWXTAG= gdlconfig->Desc()->TagIndex("GDL_USE_WX");
463   (*static_cast<DByteGDL*> (gdlconfig->GetTag(useWXTAG, 0)))[0]=useWxWidgetsForGraphics;
464 
465   SysVar::SetGDLPath( gdlPath); //probably duplicate with the one in initobjects!
466 
467   if (!pretendRelease.empty()) SysVar::SetFakeRelease(pretendRelease);
468   //fussyness setup and change if switch at start
469   if (syntaxOptionSet) { //take it no matters any env. var.
470     if (strict_syntax == true) SetStrict(true);
471   } else {
472     if (GetEnvString("GDL_IS_FUSSY").size()> 0) SetStrict(true);
473   }
474 
475 
476   string startup=GetEnvString("GDL_STARTUP");
477   if( startup == "") startup=GetEnvString("IDL_STARTUP");
478   if( startup == "")
479     {
480       if (gdlde || (isatty(0) && !quiet)) cerr <<
481         "- No startup file read (GDL_STARTUP/IDL_STARTUP env. var. not set). " << endl;
482     }
483 
484   if (gdlde || (isatty(0) && !quiet))
485   {
486     cerr << "- Please report bugs, feature or help requests and patches at: https://github.com/gnudatalanguage/gdl" << endl << endl;
487   }
488 //   else
489 //     {
490 //       // if path not given, add users home
491 //       if( !PathGiven(startup))
492 // 	{
493 // 	  string home=GetEnvString("HOME");
494 // 	  if( home != "")
495 // 	    {
496 // 	      AppendIfNeeded(home,"/");
497 // 	      startup=home+startup;
498 // 	    }
499 // 	}
500 //     }
501 
502 #ifdef USE_MPI
503   {
504     // warning the user if MPI changes the working directory of GDL
505     char wd1[PATH_MAX], wd2[PATH_MAX];
506     char *wd1p, *wd2p;
507     wd1p = getcwd(wd1, PATH_MAX);
508     MPI_Init(&argc, &argv);
509     wd2p = getcwd(wd2, PATH_MAX);
510     if (strcmp(wd1, wd2) != 0)
511       cerr << "Warning: MPI has changed the working directory of GDL!" << endl;
512   }
513   int myrank = 0;
514   MPI_Comm_rank( MPI_COMM_WORLD, &myrank);
515   int size;
516   MPI_Comm_size(MPI_COMM_WORLD, &size);
517 
518   int tag = 0;
519   char* mpi_procedure = getenv("GDL_MPI");
520   if (myrank == 0 && mpi_procedure != NULL){
521     for( SizeT i = 0; i < size; i++)
522       MPI_Send(mpi_procedure, strlen(mpi_procedure)+1, MPI_CHAR, i,
523 	       tag, MPI_COMM_WORLD);
524   }
525 #endif // USE_MPI
526 
527   interpreter.InterpreterLoop( startup, batch_files, statement);
528 
529   return 0;
530 }
531