1 /***************************************************************************
2                           pythongdl.cpp  -  GDL embedded in python
3                                             too be included by gdlpython
4                              -------------------
5     begin                : July 22 2002
6     copyright            : (C) 2002 by Marc Schellens
7     email                : m_schellens@users.sourceforge.net
8  ***************************************************************************/
9 
10 /***************************************************************************
11  *                                                                         *
12  *   This program is free software; you can redistribute it and/or modify  *
13  *   it under the terms of the GNU General Public License as published by  *
14  *   the Free Software Foundation; either version 2 of the License, or     *
15  *   (at your option) any later version.                                   *
16  *                                                                         *
17  ***************************************************************************/
18 
19 // this has to be included from gdlpython because python
20 // (here numarray) duplicates the C API for each compilation unit module
21 // which includes it and hence numarray does not feel initialized in
22 // gdlpython.cpp
23 // and as topython.cpp has to be included from datatypes,
24 // they all have to be included from datatypes
25 #ifdef INCLUDE_PYTHONGDL_CPP
26 
27 #include "includefirst.hpp"
28 
29 #include <memory> // auto_ptr
30 #include <vector>
31 
32 #include "datatypes.hpp"
33 #include "envt.hpp"
34 #include "sigfpehandler.hpp"
35 #include "terminfo.hpp"
36 #include "dinterpreter.hpp"
37 
38 #include "gdleventhandler.hpp"
39 
40 // SA fix based on:
41 // http://synopsis.fresco.org/viewsvn/Synopsis/branches/Synopsis_0_8/src/Synopsis/Python/Object.hh?r2=1792&rev=1792&r1=1657&sortdir=down
42 #if PY_VERSION_HEX < 0x02050000
43   typedef int Py_ssize_t;
44 #endif
45 
46 void LibInit(); // defined in libinit.cpp
47 
48 using namespace std;
49 
50 // everything is executed within this interpreter
51 // initialized in the initGDL function
52 DInterpreter* interpreter;
53 PyObject*     gdlError;
54 
GetScript(PyObject * argTuple,DString & name)55 bool GetScript( PyObject *argTuple, DString& name)
56 {
57   if( argTuple == NULL)
58     {
59       PyErr_SetString( gdlError, "No input.");
60       return false;
61     }
62 
63   int nArg = PyTuple_Size( argTuple);
64   if( nArg == 0)
65     {
66       PyErr_SetString( gdlError, "No input.");
67       return false;
68     }
69 
70   PyObject* proPy = PyTuple_GetItem(argTuple, 0);
71   BaseGDL* proGDL = FromPython( proPy); // throws
72   if( proGDL->Type() != GDL_STRING)
73     {
74       PyErr_SetString( gdlError, "Script must be a tuple of strings.");
75       GDLDelete(proGDL);
76       return false;
77     }
78 
79   name = StrUpCase((*(static_cast< DStringGDL*>( proGDL)))[ 0]);
80   GDLDelete(proGDL);
81 
82   return true;
83 }
84 
GetFirstString(PyObject * argTuple,DString & name)85 bool GetFirstString( PyObject *argTuple, DString& name)
86 {
87   if( argTuple == NULL)
88     {
89       PyErr_SetString( gdlError, "No argument.");
90       return false;
91     }
92 
93   int nArg = PyTuple_Size( argTuple);
94   if( nArg == 0)
95     {
96       PyErr_SetString( gdlError, "No argument.");
97       return false;
98     }
99 
100   PyObject* proPy = PyTuple_GetItem(argTuple, 0);
101   BaseGDL* proGDL = FromPython( proPy); // throws
102   if( proGDL->Type() != GDL_STRING || proGDL->N_Elements() != 1)
103     {
104       PyErr_SetString( gdlError, "First argument must be a scalar string");
105       GDLDelete(proGDL);
106       return false;
107     }
108 
109   name = (*(static_cast< DStringGDL*>( proGDL)))[ 0];
110   GDLDelete(proGDL);
111 
112   return true;
113 }
114 
115 
CheckSub(DSub * sub,PyObject * argTuple,PyObject * kwDict)116 bool CheckSub( DSub* sub, PyObject *argTuple, PyObject *kwDict)
117 {
118   int     nKey   = sub->NKey();
119   int     nPar   = sub->NPar();
120 
121   int nArg = PyTuple_Size( argTuple);
122 
123   // check args and keywords
124   if( nPar != -1 && (nArg-1) > nPar)
125     {
126       string errString = "Only " + i2s(nPar) +
127 	" arguments are allowed in call to: " + sub->ObjectName();
128       PyErr_SetString( gdlError, errString.c_str());
129       return false;
130     }
131 
132   if( kwDict == NULL) return true; // finish
133 
134   int nKW = PyDict_Size( kwDict);
135 
136   if( nKW > nKey)
137     {
138       string errString = "Only " + i2s(nKey) +
139 	" keywords are allowed in call to: " + sub->ObjectName();
140       PyErr_SetString( gdlError, errString.c_str());
141       return false;
142     }
143   return true;
144 }
145 
CopyArgFromPython(vector<BaseGDL * > & parRef,vector<BaseGDL * > & kwRef,EnvBaseT & e,PyObject * argTuple,PyObject * kwDict)146 bool CopyArgFromPython( vector<BaseGDL*>& parRef,
147 			vector<BaseGDL*>& kwRef,
148 			EnvBaseT& e,
149 			PyObject *argTuple, PyObject *kwDict)
150 {
151   int nArg = PyTuple_Size( argTuple);
152 
153   if( nArg > 1)
154     parRef.reserve( nArg-1);
155 
156   // copy arguments
157   for( SizeT p=1; p<nArg; ++p)
158     {
159       PyObject *pyArg = PyTuple_GetItem(argTuple, p);
160       if( PyTuple_Check( pyArg)) // local variable (no cpy back)
161 	{
162 	  BaseGDL* pP = FromPython( PyTuple_GetItem( pyArg, 0)); // throws
163 	  parRef.push_back( NULL);
164 	  e.SetNextPar( pP);
165 	}
166       else
167 	{
168 	  BaseGDL* pP = FromPython( pyArg); // throws
169 	  parRef.push_back( pP);
170 	  e.SetNextPar( &(parRef.back()));
171 
172 	  //	  cout << "Set arg " << p << ": "; pP->ToStream( cout); cout << endl;
173 	  //	  cout << pP << " " << parRef.back() << "  &" << &parRef.back() << endl;
174 	}
175     }
176   if( kwDict != NULL)
177     {
178       PyObject *key, *value;
179       Py_ssize_t dictPos = 0;
180 
181       int nKW = PyDict_Size( kwDict);
182 
183       parRef.reserve( nKW);
184 
185       for( SizeT k=0; k<nKW; ++k)
186 	{
187 	  PyDict_Next( kwDict, &dictPos, &key, &value);
188 #if PY_MAJOR_VERSION >= 3
189 	  int keyIsString =  PyUnicode_Check( key);
190 #else
191 	  int keyIsString =  PyString_Check( key);
192 #endif
193 	  if( !keyIsString)
194 	    {
195 	      PyErr_SetString( gdlError,
196 			       "Keywords must be of type string");
197 	      return false;
198 	    }
199 #if PY_MAJOR_VERSION >= 3
200 	  const char* keyChar = PyUnicode_AsUTF8( key);
201 #else
202 	  const char* keyChar = PyString_AsString( key);
203 #endif
204 	  string keyString = StrUpCase( keyChar);
205 	  int kwIx = e.GetPro()->FindKey( keyString);
206 	  if( kwIx == -1)
207 	    {
208 	      string errString = "Keyword " + string(keyChar) +
209 		" not allowed in call to: " + e.GetPro()->ObjectName();
210 	      PyErr_SetString( gdlError, errString.c_str());
211 	      return false;
212 	    }
213 
214 	  if( PyTuple_Check( value)) // local keyword (no cpy back)
215 	    {
216 	      BaseGDL* pP = FromPython( PyTuple_GetItem( value, 0)); // throws
217 	      kwRef.push_back( NULL);
218 	      e.SetKeyword(  keyString, pP);
219 	    }
220 	  else
221 	    {
222 	      BaseGDL* pP = FromPython( value); // throws
223 	      kwRef.push_back( pP);
224 	      e.SetKeyword(  keyString, &kwRef.back());
225 	    }
226 	}
227     }
228 
229   e.ResolveExtra(); // expand _EXTRA
230 
231   return true;
232 }
233 
CopyArgToPython(vector<BaseGDL * > & parRef,vector<BaseGDL * > & kwRef,EnvBaseT & e,PyObject * argTuple,PyObject * kwDict)234 bool CopyArgToPython( vector<BaseGDL*>& parRef,
235 		      vector<BaseGDL*>& kwRef,
236 		      EnvBaseT& e,
237 		      PyObject *argTuple, PyObject *kwDict)
238 {
239   int nArg = PyTuple_Size( argTuple);
240   for( SizeT p=1; p<nArg; ++p)
241     {
242       BaseGDL* gdlPar = parRef[ p-1];
243       if( gdlPar != NULL)
244 	{
245 	  PyObject* pyObj = gdlPar->ToPython(); // throws
246 	  int success0 = PyTuple_SetItem( argTuple, p, pyObj);
247 	  // Py_DECREF(pyObj); not needed: PyTuple_SetItem steals
248 	}
249     }
250   if( kwDict != NULL)
251     {
252       PyObject *key, *value;
253       Py_ssize_t dictPos = 0;
254 
255       int nKW = PyDict_Size( kwDict);
256       for( SizeT k=0; k<nKW; ++k)
257 	{
258 	  BaseGDL* gdlKW = kwRef[ k];
259 	  PyDict_Next( kwDict, &dictPos, &key, &value);
260 	  if( gdlKW != NULL)
261 	    {
262 	      PyObject* pyObj = gdlKW->ToPython(); // throws
263 	      int success0 = PyDict_SetItem( kwDict, key, pyObj);
264 	      Py_DECREF( pyObj);
265 	    }
266 	}
267     }
268   return true;
269 }
270 
271 int (*oldInputHook)();
GDLEventHandlerPy()272 int GDLEventHandlerPy()
273 {
274   GDLEventHandler();
275   if( oldInputHook != NULL)
276     return (*oldInputHook)();
277   else
278     return 0;
279 }
280 
281 // Execute a GDL subroutine
GDLSub(PyObject * self,PyObject * argTuple,PyObject * kwDict,bool functionCall)282 PyObject *GDLSub( PyObject *self, PyObject *argTuple, PyObject *kwDict,
283 		  bool functionCall)
284 {
285   feclearexcept(FE_ALL_EXCEPT);
286 
287   PyOS_sighandler_t oldControlCHandler = PyOS_setsig(SIGINT,ControlCHandler);
288   PyOS_sighandler_t oldSigFPEHandler   = PyOS_setsig(SIGFPE,SigFPEHandler);
289 
290   PyObject *retVal = NULL; // init to error indicator
291 
292   vector<BaseGDL*> parRef;
293   vector<BaseGDL*> kwRef;
294   bool success;
295   DString pro;
296 
297   // handle GDL exceptions
298   try {
299 
300     success = GetFirstString( argTuple, pro);
301     if( !success) goto ret;
302 
303     pro = StrUpCase( pro);
304 
305     DSub*    sub;
306     bool     libCall = false;
307 
308     if( functionCall)
309       {
310 	// search for function pro
311 	// first search library functions
312 	int proIx = LibFunIx( pro);
313 	if( proIx != -1)
314 	  {
315 	    // PCALL_LIB
316 	    sub = libFunList[ proIx];
317 	    libCall = true;
318 	  }
319 	else
320 	  {
321 	    // FCALL - user defined procedures
322 	    proIx = FunIx( pro);
323 	    if( proIx == -1)
324 	      {
325 		/*bool found=*/ interpreter->SearchCompilePro( pro, false);
326 
327 		proIx = FunIx( pro);
328 		if( proIx == -1)
329 		  {
330 		    string errString = "Function " + pro + " not found.";
331 		    PyErr_SetString( gdlError, errString.c_str());
332 		    goto ret;
333 		  }
334 	      }
335 
336 	    sub = funList[ proIx];
337 	  }
338       }
339     else
340       {
341 	// search for procedure pro
342 	// first search library procedures
343 	int proIx = LibProIx( pro);
344 	if( proIx != -1)
345 	  {
346 	    // PCALL_LIB
347 	    sub = libProList[ proIx];
348 	    libCall = true;
349 	  }
350 	else
351 	  {
352 	    // PCALL - user defined procedures
353 	    proIx = ProIx( pro);
354 	    if( proIx == -1)
355 	      {
356 		/*bool found=*/ interpreter->SearchCompilePro( pro, true);
357 
358 		proIx = ProIx( pro);
359 		if( proIx == -1)
360 		  {
361 		    string errString = "Procedure " + pro + " not found.";
362 		    PyErr_SetString( gdlError, errString.c_str());
363 		    goto ret;
364 		  }
365 	      }
366 
367 	    sub = proList[ proIx];
368 	  }
369       }
370 
371     success = CheckSub( sub, argTuple, kwDict);
372     if( !success) goto ret;
373 
374     // build the environment
375     EnvBaseT* e;
376 
377     if( libCall)
378       e = new EnvT( NULL, sub);
379     else
380       e = new EnvUDT( NULL, static_cast<DSubUD*>(sub));
381 
382     Guard< EnvBaseT> e_guard( e);
383 
384     // copy arguments
385     success = CopyArgFromPython( parRef, kwRef, *e, argTuple, kwDict);
386     if( !success) goto ret;
387 
388     // make the call
389     StackSizeGuard<EnvStackT> guard( GDLInterpreter::CallStack());
390 
391     if( !libCall)
392     {
393       GDLInterpreter::CallStack().push_back( static_cast<EnvUDT*>(e));
394       e_guard.release();
395     }
396 
397     BaseGDL* retValGDL = NULL;
398     Guard<BaseGDL> retValGDL_guard;
399 
400     if (functionCall) {
401         DLibFun* sub_fun_chk = dynamic_cast<DLibFun *>(static_cast<EnvT *>(e)->GetPro());
402         if (sub_fun_chk) {
403             //handle direct call function first
404             if (sub_fun_chk->DirectCall()) {
405                 BaseGDL* directCallParameter = e->GetParDefined(0);
406                 retValGDL = static_cast<DLibFunDirect*>(sub_fun_chk)->FunDirect()(directCallParameter, true /*isReference*/);
407             }
408         } else if (libCall)
409         retValGDL = static_cast<DLibFun *>(static_cast<EnvT *>(e)->GetPro())
410                         ->Fun()(static_cast<EnvT *>(e));
411       else
412         retValGDL = interpreter->call_fun(
413             static_cast<DSubUD *>(static_cast<EnvUDT *>(e)->GetPro())
414                 ->GetTree());
415       retValGDL_guard.Reset(retValGDL);
416     } else {
417       if (libCall)
418         static_cast<DLibPro *>(e->GetPro())
419             ->Pro()(static_cast<EnvT *>(e)); // throws
420       else
421         interpreter->call_pro(
422             static_cast<DSubUD *>(e->GetPro())->GetTree()); // throws
423     }
424 
425     // copy back args and keywords
426     success = CopyArgToPython( parRef, kwRef, *e, argTuple, kwDict);
427     if( !success) goto ret;
428 
429     if( retValGDL != NULL)
430       {
431 	retVal = retValGDL->ToPython();
432       }
433   }
434   catch ( GDLException ex)
435     {
436       // ERROR GDL exception
437       string errString = "Calling " + pro + ": " + ex.toString();
438       PyErr_SetString( gdlError, errString.c_str());
439       goto ret;
440     }
441 
442   if( retVal == NULL)
443     {
444       // no error: return Py_None from procedure
445       Py_INCREF(Py_None);
446       retVal = Py_None;
447     }
448 
449  ret:
450   // free GDL parameters and keywords
451   PurgeContainer( parRef);
452   PurgeContainer( kwRef);
453 
454   // restore old signal handlers
455   PyOS_setsig(SIGINT,oldControlCHandler);
456   PyOS_setsig(SIGFPE,oldSigFPEHandler);
457 
458   return retVal;
459 }
460 
461 // GDL is a C++ program
462 extern "C" {
463 
464   // Execute a GDL procedure
GDL_script(PyObject * self,PyObject * argTuple,PyObject * kwDict)465   PyObject *GDL_script(PyObject *self, PyObject *argTuple, PyObject *kwDict)
466   {
467     PyOS_sighandler_t oldControlCHandler = PyOS_setsig(SIGINT,ControlCHandler);
468     PyOS_sighandler_t oldSigFPEHandler   = PyOS_setsig(SIGFPE,SigFPEHandler);
469 
470     PyObject *retVal = NULL; // init to error indicator
471 
472     bool success;
473     DString file;
474 
475     success = GetFirstString( argTuple, file);
476     if( !success) goto ret;
477 
478     {
479       ifstream in(file.c_str());
480       if( !in.good())
481 	{
482 	  string errString = "Error opening file: "+file;
483 	  PyErr_SetString( gdlError, errString.c_str());
484 	  goto ret;
485 	}
486 
487       success = interpreter->RunBatch( &in);
488       if( !success)
489 	{
490 	  string errString = "Error in batch file: "+file;
491 	  PyErr_SetString( gdlError, errString.c_str());
492 	  goto ret;
493 	}
494     }
495 
496     Py_INCREF(Py_None);
497     retVal = Py_None;
498 
499     ret:
500       // restore old signal handlers
501       PyOS_setsig(SIGINT,oldControlCHandler);
502       PyOS_setsig(SIGFPE,oldSigFPEHandler);
503 
504       return retVal;
505   }
506 
507   // Execute a GDL procedure
GDL_function(PyObject * self,PyObject * argTuple,PyObject * kwDict)508   PyObject *GDL_function(PyObject *self, PyObject *argTuple, PyObject *kwDict)
509   {
510     return GDLSub( self, argTuple, kwDict, true);
511   }
512 
513   // Execute a GDL procedure
GDL_pro(PyObject * self,PyObject * argTuple,PyObject * kwDict)514   PyObject *GDL_pro(PyObject *self, PyObject *argTuple, PyObject *kwDict)
515   {
516     return GDLSub( self, argTuple, kwDict, false);
517   }
518 
519   // python GDL module method table
520   PyMethodDef GDLMethods[] = {
521     {"pro",      (PyCFunction) GDL_pro,      METH_VARARGS | METH_KEYWORDS,
522      "Execute a GDL procedure."},
523     {"function", (PyCFunction) GDL_function, METH_VARARGS | METH_KEYWORDS,
524      "Execute a GDL function."},
525     {"script",   (PyCFunction) GDL_script,   METH_VARARGS | METH_KEYWORDS,
526      "Run a GDL script (sequence of commands)."},
527     {NULL, NULL, 0, NULL}        // Sentinel
528   };
529 
530 #if PY_MAJOR_VERSION >= 3
531   struct module_state {
532     PyObject *error;
533   };
534 
535   #define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
536 
GDL_traverse(PyObject * m,visitproc visit,void * arg)537   static int GDL_traverse(PyObject *m, visitproc visit, void *arg) {
538     Py_VISIT(GETSTATE(m)->error);
539     return 0;
540   }
541 
GDL_clear(PyObject * m)542   static int GDL_clear(PyObject *m) {
543     Py_CLEAR(GETSTATE(m)->error);
544     return 0;
545   }
546 
547   static struct PyModuleDef moduledef = {
548     PyModuleDef_HEAD_INIT,
549     "GDL",
550     NULL,
551     sizeof(struct module_state),
552     GDLMethods,
553     NULL,
554     GDL_traverse,
555     GDL_clear,
556     NULL
557   };
558 #endif
559 
560   // python GDL module init function
561 #if PY_MAJOR_VERSION >= 3
PyInit_GDL(void)562   PyMODINIT_FUNC PyInit_GDL(void)
563 #else
564   PyMODINIT_FUNC initGDL(void)
565 #endif
566   {
567     // http://docs.scipy.org/doc/numpy/reference/c-api.array.html#miscellaneous
568     import_array();
569 
570     // note: we don't use atexit here
571     // ncurses blurs the output, initialize TermWidth here
572     TermWidth();
573 
574     // initializations
575     InitObjects();
576 
577     // init library functions
578     LibInit();
579 
580     // instantiate the interpreter (creates $MAIN$ environment)
581     interpreter = new DInterpreter();
582 
583     // Ole: enable GDL PATH!
584     string gdlPath=GetEnvString("GDL_PATH");
585     if( gdlPath == "") gdlPath=GetEnvString("IDL_PATH");
586     if( gdlPath == "")
587       {
588         gdlPath = "+" GDLDATADIR "/lib";
589       }
590     SysVar::SetGDLPath( gdlPath);
591 
592 #if PY_MAJOR_VERSION >= 3
593     PyObject* m = PyModule_Create(&moduledef);
594 #else
595     PyObject* m = Py_InitModule("GDL", GDLMethods);
596 #endif
597 
598     gdlError = PyErr_NewException((char*)"GDL.error", NULL, NULL);
599     Py_INCREF(gdlError);
600     PyModule_AddObject(m, "error", gdlError);
601 
602     // GDL event handling
603     oldInputHook = PyOS_InputHook;
604     PyOS_InputHook = GDLEventHandlerPy;
605 #if PY_MAJOR_VERSION >= 3
606     return m;
607 #endif
608   }
609 
610 } // extern "C"
611 
612 //#endif
613 #endif // INCLUDE_PYTHONGDL_CPP
614