1 /***************************************************************************
2                           gdlhelp.cpp  - GDL help procedure
3                              -------------------
4     begin                : July 22 2002
5     copyright            : (C) 2002 by Marc Schellens
6     email                : m_schellens@users.sf.net
7 
8     - Numerous enhancements by Alain Coulais
9     - May 2015 code cleanup by Greg Jung:
10     * gdlhelp.cpp, gdlhelp.hpp for HELP and DELVAR
11     * OUTPUT= and NAMES= keywords implemented for help.
12     * /COMMON to list all common blocks anywhere
13     * Undefined variables normally not listed.
14 ***************************************************************************/
15 
16 /***************************************************************************
17  *                                                                         *
18  *   This program is free software; you can redistribute it and/or modify  *
19  *   it under the terms of the GNU General Public License as published by  *
20  *   the Free Software Foundation; either version 2 of the License, or     *
21  *   (at your option) any later version.                                   *
22  *                                                                         *
23  ***************************************************************************/
24 
25 #include "includefirst.hpp"
26 
27 #include <sys/types.h>
28 
29 #include <string>
30 #include <fstream>
31 #include <memory>
32 
33 #include <set>
34 #include <iterator>
35 
36 #ifndef _WIN32
37 //#include <regex.h> // stregex
38 #include <fnmatch.h>
39 
40 #else
41 #include <shlwapi.h>
42 #endif
43 
44 #ifndef _WIN32
45 #include <termios.h>
46 #include <unistd.h>
47 #endif
48 
49 // used to defined GDL_TMPDIR: may have trouble on MSwin, help welcome
50 #ifndef _WIN32
51 #include <paths.h>
52 #endif
53 
54 
55 #ifndef _MSC_VER
56 #	include <dirent.h>
57 #else
58 // MSC workaround implementation in file.cpp
59 /*
60   Declaration of POSIX directory browsing functions and types for Win32.
61 
62   Author:  Kevlin Henney (kevlin@acm.org, kevlin@curbralan.com)
63   History: Created March 1997. Updated June 2003.
64   Rights:  See end of file.
65 */
66 extern "C" {
67 
68   typedef struct DIR DIR;
69 
70   struct dirent {
71     char *d_name;
72   };
73 
74   DIR           *opendir(const char *);
75   int           closedir(DIR *);
76   struct dirent *readdir(DIR *);
77   void          rewinddir(DIR *);
78 
79   /*
80     Copyright Kevlin Henney, 1997, 2003. All rights reserved.
81 
82     Permission to use, copy, modify, and distribute this software and its
83     documentation for any purpose is hereby granted without fee, provided
84     that this copyright and permissions notice appear in all copies and
85     derivatives.
86 
87     This software is supplied "as is" without express or implied warranty.
88 
89     But that said, if there are any problems please get in touch.
90   */
91 
92 } // extern "C"
93 #endif
94 
95 #if 0
96 #ifdef __APPLE__
97 # include <crt_externs.h>
98 # define environ (*_NSGetEnviron())
99 #else
100 #ifdef _WIN32
101 #include <direct.h>
102 #include <io.h>
103 #define R_OK    4       /* Test for read permission.  */
104 #define W_OK    2       /* Test for write permission.  */
105 #define F_OK    0       /* Test for existence.  */
106 #else
107 #include <unistd.h>
108 #endif
109 #endif
110 #endif
111 
112 
113 #include "dinterpreter.hpp"
114 #include "datatypes.hpp"
115 #include "envt.hpp"
116 #include "dpro.hpp"
117 
118 
119 #include "graphicsdevice.hpp"
120 #include "dcommon.hpp"
121 #include "dpro.hpp"
122 
123 #include "gdlhelp.hpp"
124 #include "nullgdl.hpp"
125 #include "terminfo.hpp"
126 
127 
128 // for sorting compiled pro/fun lists by name
129 struct CompFunName: public std::binary_function< DFun*, DFun*, bool>
130 {
operator ()CompFunName131   bool operator() ( DFun* f1, DFun* f2) const
132 	{
133     return f1->ObjectName() < f2->ObjectName();
134   }
135 };
136 
137 struct CompProName: public std::binary_function< DPro*, DPro*, bool>
138 {
operator ()CompProName139   bool operator() ( DPro* f1, DPro* f2) const
140 	{
141     return f1->ObjectName() < f2->ObjectName();
142   }
143 };
144 
CompareWithJokers(string names,string sourceFiles)145 static bool CompareWithJokers(string names, string sourceFiles) {
146 
147 #ifdef _WIN32
148   WCHAR wnames[MAX_PATH];
149   WCHAR wsourceFiles[MAX_PATH];
150 
151   const char* cnames = names.c_str();
152   const char* csourceFiles = sourceFiles.c_str();
153 
154   MultiByteToWideChar(CP_UTF8, 0, cnames, -1,
155     (LPWSTR) wnames, MAX_PATH);
156   MultiByteToWideChar(CP_UTF8, 0, csourceFiles, -1,
157     (LPWSTR) wsourceFiles, MAX_PATH);
158   int match = 1 - PathMatchSpecW(wsourceFiles, wnames);
159 #else
160   int match = fnmatch(names.c_str(), sourceFiles.c_str(), 0 );
161 #endif
162   if ( match == 0) 	return true;
163   else 		return false;
164   }
165 extern GDLFileListT  fileUnits;
166 
167     using namespace std;
168 // (static = internal) helper routines serving the lib:: routines called out in
169 // gdlhelper.hpp
170 
171 
help_files(ostream & os,EnvT * e)172 static void help_files(ostream& os, EnvT* e) {
173   // AC 2020-04-28
174   // see https://github.com/gnudatalanguage/gdl/issues/743
175   // pb1 : why maxUserLun ?? pb2 : no return for -1, 0, 1
176   cout << " maxUserLun : " << maxUserLun << " fileUnits.size() : " << fileUnits.size() << '\n';
177 
178   for( DLong lun=maxUserLun+1; lun <= fileUnits.size(); ++lun)
179     if( fileUnits[ lun-1].InUse() || fileUnits[ lun-1].GetGetLunLock())
180       {
181 	os << "	 lun "<< lun << ": "+fileUnits[lun-1].Name() << '\n';
182       }
183   return;
184 }
185 
186 // AC 2020-04-28 derivated work from get_kbrd().
187 // Not sure what happen without ReadLine
my_get_kbrd()188 char my_get_kbrd()
189 {
190 #if defined(HAVE_LIBREADLINE)
191 #include <readline/readline.h>
192   rl_prep_terminal (0);
193 #endif
194 
195   char c='\0'; //initialize is never a bad idea...
196 
197   int fd=fileno(stdin);
198 #ifndef _WIN32
199   struct termios orig, get;
200 #endif
201   // Get terminal setup to revert to it at end.
202 #ifndef _WIN32
203   (void)tcgetattr(fd, &orig);
204   // New terminal setup, non-canonical.
205   get.c_lflag = ISIG;
206 #endif
207     // will wait for a character
208 #ifndef _WIN32
209     get.c_cc[VTIME]=0;
210     get.c_cc[VMIN]=1;
211     (void)tcsetattr(fd, TCSANOW, &get);
212 #endif
213     cin.get(c);
214 
215     // Restore original terminal settings.
216 #ifndef _WIN32
217     (void)tcsetattr(fd, TCSANOW, &orig);
218 #endif
219 #if defined(HAVE_LIBREADLINE)
220     rl_deprep_terminal ();
221 #endif
222     return c;
223   }
224 
help_keys(ostream & ostr)225 static void help_keys(ostream& ostr)
226   {
227 	ostr << "GDL is using Readline to manage keys shortcuts, few useful listed below." << '\n';
228 	ostr << "A summary can be read here : http://www.bigsmoke.us/readline/shortcuts " << '\n';
229 	ostr << '\n';
230 	ostr << "Moving in the command line :"<< '\n';
231 	ostr << "  Ctrl-a          : going to the beginning of the line"<< '\n';
232 	ostr << "  Ctrl-e          : going to the end of the line"<< '\n';
233 	ostr << "  Ctrl-u          : removing from here to the beginning of the line"<< '\n';
234 	ostr << "  Ctrl-k          : removing from here to the end of the line"<< '\n';
235 	ostr << "  Ctrl-RightArrow : jumping one word on the right"<< '\n';
236 	ostr << "  Ctrl-LeftArrow  : jumping one word on the left"<< '\n';
237 	ostr << '\n';
238 	ostr << "Moving in the history :"<< '\n';
239 	ostr << "  HELP, /recall       : listing the whole history" << '\n';
240 	ostr << "  Ctrl-p or UpArrow   : previous entry in history"  << '\n';
241 	ostr << "  Ctrl-n or DownArrow : next entry in history"  << '\n';
242 	ostr << '\n';
243 	return;
244   }
245 
help_info()246 static void help_info()
247   {
248 
249 	  cout << "* Homepage: https://gnudatalanguage.github.io/" << '\n';
250 	  cout << '\n';
251 	  cout << "* #NameOfRoutine for list of params and keywords"
252 	    " for a given NameOfRoutine (internal or compiled pro/func)" << '\n';
253 	  cout << '\n';
254 	  cout << "* ?NameOfRoutine for starting a browser to access online doc"
255 	    " for a given routine (if exists ! internal or compiled pro/func)" ;
256 	  cout << '\n'; cout << '\n';
257 	  cout << "* HELP, /INTERNAL_LIB_GDL for a list of all internal library "
258 	    "functions/procedures." << '\n';
259 	  cout << "* HELP, /LIB Additional subroutines are written in GDL language, "
260 	    "look for *.pro files (e.g. in CVS in src/pro/)." << '\n';
261 	  cout << '\n';
262 	  cout << "* HELP, /KEYS for useful CLI keys shortcuts." << '\n';
263 	  cout << '\n';
264 		return;
265 }
help_sysvar(ostream & os,bool briefKW=true)266 static void help_sysvar(ostream& os , bool briefKW=true)
267   {
268   std::map<std::string, DVar*>list;
269   for (SizeT v = 0; v < sysVarList.size(); ++v) list.insert(std::pair<std::string, DVar*>(" !" +sysVarList[ v]->Name(), sysVarList[ v]));
270 	  if (briefKW) {
271     for (std::map<std::string, DVar*>::iterator it=list.begin(); it!=list.end(); ++it) { os << it->first << '\n';}
272   } else {
273     for (std::map<std::string, DVar*>::iterator it=list.begin(); it!=list.end(); ++it) {
274       DVar* v=it->second;
275       BaseGDL* var= v->Data();
276       lib::help_item(os, var , it->first, false);
277 		  }
278 		}
279 	return;
280   }
281 
help_Output(BaseGDL ** outputKW,ostringstream & ostr,SizeT & nlines,bool doOutput=true)282 static void help_Output(BaseGDL** outputKW, ostringstream& ostr, SizeT &nlines, bool doOutput=true)
283   {
284 	// Setup output return variable ostream& os, int &lines_count
285 
286 	std::string s = ostr.rdbuf()->str().erase(ostr.rdbuf()->str().length(),1);
287 	char delimiter = '\n';
288 	SizeT nOut = 0;
289 	size_t found=0;
290 	if(doOutput) {
291 
292 	  do {
293 		++nOut;
294 		found=s.find( delimiter,found);
295 	     }   while( (found++ != std::string::npos) );
296 
297 	  nlines = --nOut;	if(nlines == 0) return;
298 	  if (*outputKW!=NULL) GDLDelete((*outputKW));
299 	  dimension dim(&nlines, (size_t) 1);
300 	  *outputKW = new DStringGDL(dim, BaseGDL::NOZERO);
301 	}
302 	size_t pos = 0;
303 	nOut = 0;
304 	std::string token;
305 	while ((found = s.find(delimiter,pos)) != std::string::npos) {
306 		token = s.substr(pos, found-pos);
307 		if( doOutput and (nOut not_eq nlines)) (*(DStringGDL *) *outputKW)[nOut] = token;
308 		else cout << token << '\n';
309 		++nOut;
310 		pos = found+1;
311 		}
312 	    ostr.str("");
313 //	    if( nOut  not_eq nlines and debug) cout <<
314 //			" help_Output: Error counting lines -" <<
315 //			" nOut: "<<nOut<<" OutputLines:"<<nlines<<'\n';
316 	}
317 
318     // showing HELP, /path_cache
help_path_cached(ostream & ostr,SizeT & lines_count)319   void help_path_cached(ostream& ostr, SizeT &lines_count) {
320 
321     struct dirent *dp;
322     const char *ProSuffix = ".pro";
323     int ProSuffixLen = strlen(ProSuffix);
324 
325     string tmp_fname;
326     size_t found;
327 
328     StrArr path = SysVar::GDLPath();
329 
330     std::sort(path.begin(),path.end());
331     ostr << "!PATH (Disabled, "<< path.size() <<" directories)" << '\n';
332     lines_count = 1;
333 
334     for (StrArr::iterator CurrentDir = path.begin(); CurrentDir != path.end(); ++CurrentDir) {
335       //	  cout << "1>>" << (*CurrentDir).c_str() << "<<" <<'\n';
336       DIR* dirp = opendir((*CurrentDir).c_str());
337       //cout << "2>>" << dirp << "<<" <<'\n';
338       if (dirp != NULL) {
339 		int NbProFilesInCurrentDir = 0;
340         while ((dp = readdir(dirp)) != NULL) {
341           tmp_fname = dp->d_name;
342           found = tmp_fname.rfind(ProSuffix);
343           if (found != std::string::npos) {
344             if ((found + ProSuffixLen) == tmp_fname.length())
345               ++NbProFilesInCurrentDir;
346           }
347         }
348         closedir(dirp);
349         ++lines_count;
350         ostr << *CurrentDir << " (" << NbProFilesInCurrentDir << " files)" << '\n';
351       }
352     }
353   }
354 
355   // a simplification of codes (DebugMsg & DumpStack) in GDLInterpreter.cpp
356   // ProgNodeP cN = e->CallingNode();
357   // DInterpreter::DebugMsg(cN, "At ");
358   // DInterpreter::DumpStack(3);
359 
SimpleDumpStack(EnvT * e,ostream & ost=std::cerr)360   static void SimpleDumpStack(EnvT* e, ostream& ost=std::cerr) {
361 
362     EnvStackT& callStack = e->Interpreter()->CallStack();
363 
364     // simple way to manage the first line : exception
365     SizeT w = 0;
366     string msgPrefix = "% At ";
367 
368     long actIx = callStack.size() - 1;
369     for (; actIx >= 0; --actIx) {
370       EnvStackT::pointer_type upEnv = callStack[actIx];
371 
372       ost << msgPrefix << std::right << std::setw(w) << "";
373       // simple way to manage the first line : exception
374       msgPrefix = "";
375       w = 5;
376       ost << std::left << std::setw(16) << upEnv->GetProName();
377 
378       std::string file = upEnv->GetFilename();
379       if (file != "") {
380         int lineNumber = upEnv->GetLineNumber();
381         if (lineNumber != 0) {
382           ost << std::right << std::setw(6) << lineNumber;
383         } else {
384           ost << std::right << std::setw(6) << "";
385         }
386         ost << std::left << " " << file;
387       }
388       ost << '\n';
389     }
390 	  return;
391   }
help_object(std::ostream * ostrp,DStructDesc * objDesc,bool verbose=false)392 static void help_object(std::ostream* ostrp, DStructDesc* objDesc, bool verbose = false)
393 {
394   FunListT& funlist = objDesc->FunList();
395   ProListT& prolist = objDesc->ProList();
396   int num_methods = funlist.size() + prolist.size();
397   int numpar = objDesc->GetNumberOfParents();
398   if (numpar==1) *ostrp << "** Object class " << objDesc->Name() << ", " << numpar << " direct superclass, " << num_methods << " known methods" << '\n';
399   else *ostrp << "** Object class " << objDesc->Name() << ", " << numpar << " direct superclasses, " << num_methods << " known methods" << '\n';
400   if (numpar > 0) {
401     *ostrp << "   Superclasses:\n";
402     std::set< std::string> pNames;
403     objDesc->GetParentNames(pNames);
404     for (std::set<string>::iterator j = pNames.begin(); j != pNames.end(); ++j) *ostrp << "      " << (*j) << "  <Direct>\n";
405     //find all ancestors
406     pNames.clear();
407     objDesc->GetAncestorsNames(pNames);
408     for (std::set<string>::iterator j = pNames.begin(); j != pNames.end(); ++j) *ostrp << "      " << (*j) << "\n";
409 		}
410   if (num_methods > 0) {
411     if (funlist.size() > 0) {
412       *ostrp << "   Known Function Methods:\n";
413       for (int j = 0; j < funlist.size(); ++j) *ostrp << "      " << objDesc->Name() << "::" << funlist[j]->Name() << "\n";
414 		}
415     if (prolist.size() > 0) {
416       *ostrp << "   Known Procedure Methods:\n";
417       for (int j = 0; j < prolist.size(); ++j) *ostrp << "      " << objDesc->Name() << "::" << prolist[j]->Name() << "\n";
418 }
419 
420     if (!verbose) return;
421     DStructGDL* dumm = new DStructGDL(objDesc, dimension());
422       Guard<DStructGDL> guard(dumm);
423     lib::help_struct(*ostrp, dumm, 0, false);
424     }
425 	}
426 
help_ListLib(const DString names,ostream & ostr,bool internal=true)427 static void help_ListLib(const DString names, ostream& ostr, bool internal=true)
428   {
429 	bool searchbyname;
430 	  searchbyname = (names != "");
431 	  vector<DString> subList;
432 //	  for( libProListT::iterator i=libProList.begin(); i != libProList.end(); i++)
433 	  for( SizeT i = 0; i<libProList.size(); ++i)  {
434 	      if( internal == libProList[ i]->GetHideHelp()) {
435 	         if(searchbyname and not
436 				CompareWithJokers(names, libProList[i]->Name())) continue;
437 		      subList.push_back(libProList[ i]->ToString());
438 			}
439 	    }
440 	  sort( subList.begin(), subList.end());
441 	  if(internal) ostr << "Internal l";
442 	  else ostr << "L";
443 	  ostr << "ibrary procedures ("
444 				<< subList.size() <<"):" << '\n';
445 	  for( SizeT i = 0; i<subList.size(); ++i)
446 			ostr << subList[ i] << '\n';
447 
448 	  subList.clear();
449 
450 //	  for( libFunListT::iterator i=libFunList.begin(); i != libFunList.end(); i++)
451 	  for( SizeT i = 0; i<libFunList.size(); ++i)
452 	    {
453 	      if(  internal == libFunList[ i]->GetHideHelp()) {
454 	         if(searchbyname and not
455 				CompareWithJokers(names, libFunList[i]->Name())) continue;
456 			  subList.push_back(libFunList[ i]->ToString());
457 			}
458 	    }
459 	  sort( subList.begin(), subList.end());
460 
461 	  if(internal) ostr << "Internal l";
462 	  else ostr << "L";
463 	  ostr << "ibrary functions ("
464 				<< subList.size() <<"):" << '\n';
465 	  for( SizeT i = 0; i<subList.size(); ++i)
466 	    ostr << subList[ i] << '\n';
467 	  subList.clear();
468 
469 }
470 
help_heap_obj_ptr_head(EnvT * e,ostream & ostr)471 static void help_heap_obj_ptr_head(EnvT* e, ostream& ostr)
472 {
473   SizeT numPtr = e->Interpreter()->HeapSize();
474   SizeT numObj = e->Interpreter()->ObjHeapSize();
475   ostr << "Heap Variables:" << '\n';
476   ostr << "    # Pointer: " << numPtr << '\n';
477   ostr << "    # Object : " << numObj << '\n'<<'\n';
478 }
479 
480 
help_mix_heap_and_obj(EnvT * e,ostream & ostr)481 static void help_mix_heap_and_obj(EnvT* e, ostream& ostr)
482 {
483   std::vector<DObj>* objheap = e->Interpreter()->GetAllObjHeapSTL();
484   Guard< std::vector<DObj> > heap_objguard(objheap);
485   SizeT nobjH = objheap->size();
486   std::vector<DPtr>* heap = e->Interpreter()->GetAllHeapSTL();
487   Guard< std::vector<DPtr> > heap_guard(heap);
488   SizeT nH = heap->size();
489   SizeT tot=nH+nobjH;
490   if (tot <= 0) return;
491   // objHeap and heap contain different, globally increasing, integers.
492   // to show them in incresing order like in IDL one needs to put them in an ordered fashion:
493   std::set<DPtr> myHeapIndex;
494   SizeT k=0;
495   for (SizeT i=0; i<nobjH; ++i) myHeapIndex.insert((*objheap)[i]);
496   for (SizeT i=0; i<nH;    ++i) myHeapIndex.insert((*heap)[i]);
497 
498   std::set<DPtr>::iterator it;
499   for (it = myHeapIndex.begin(); it!=myHeapIndex.end(); ++it) {
500     DPtr h=(*it);
501     if (e->Interpreter()->ObjValid(h))
502     {
503       BaseGDL* hV = BaseGDL::interpreter->GetObjHeap(h);
504       SizeT refc = BaseGDL::interpreter->RefCountHeapObj(h);
505       lib::help_item(ostr, hV, DString("<ObjHeapVar") + i2s(h) + ">  refcount=" + i2s(refc), false);
506     } else {
507       if (e->Interpreter()->PtrValid(h))
508       {
509         BaseGDL* hV = BaseGDL::interpreter->GetHeap(h);
510         SizeT refc = BaseGDL::interpreter->RefCountHeap(h);
511         lib::help_item(ostr, hV, DString("<PtrHeapVar") + i2s(h) + ">  refcount=" + i2s(refc), false);
512       }
513     }
514   }
515   return;
516 }
517 
518 
help_lastmsg(EnvT * e)519 static void help_lastmsg(EnvT* e)
520   {
521       // if LAST_MESSAGE is present, it is the only output.
522       // All other kw are ignored *EXCEPT 'output'*.
523       BaseGDL** outputKW = NULL;
524 
525 	  DStructGDL* errorState = SysVar::Error_State();
526 	  static unsigned msgTag = errorState->Desc()->TagIndex( "MSG");
527 
528       static int outputIx = e->KeywordIx( "OUTPUT");
529 	  if (e->KeywordPresent( outputIx))
530 	    {    // Setup output return variable
531 	      outputKW = &e->GetKW( outputIx);
532 	      GDLDelete((*outputKW));
533 	      *outputKW = static_cast<DStringGDL*>((errorState->GetTag( msgTag))
534 	                                             ->Convert2( GDL_STRING, BaseGDL::COPY));
535 	      return;
536 			}
537 			else {
538 	    cout << (*static_cast<DStringGDL*>( errorState->GetTag( msgTag)))[0]<< '\n';
539 	    return;
540 	  }
541   }
542 
recall_commands_internal()543   static DStringGDL* recall_commands_internal()
544   {
545 
546 #if defined(HAVE_LIBREADLINE)
547     // http://cnswww.cns.cwru.edu/php/chet/readline/history.html#IDX14
548     HIST_ENTRY **the_list;
549     //    cout << "history_length" << history_length << '\n';
550     the_list = history_list();
551 
552     if (the_list) {
553       DStringGDL* retVal = new DStringGDL(history_length, BaseGDL::NOZERO);
554       for (SizeT i = 0; i < history_length; ++i)
555         (*retVal)[history_length-i-1] = the_list[i]->line;
556       return retVal;
557     } else return new DStringGDL("");
558 #else
559     Message("RECALL_COMMANDS: nothing done, because compiled without READLINE");
560     return new DStringGDL("");
561 #endif
562   }
563 
564 namespace lib {
565 
566   using namespace std;
567 
568 
recall_commands(EnvT * e)569 BaseGDL* recall_commands( EnvT* e)
570   {
571       return recall_commands_internal();
572   }
573 
574 
575   // display help for one variable or one structure tag
576 
help_item(ostream & ostr,BaseGDL * par,DString parString,bool doIndentation=false)577   void help_item(ostream& ostr,
578     BaseGDL* par, DString parString,
579     bool doIndentation = false)
580   {
581     static volatile bool debug(false);
582     if (debug and (par not_eq NULL)) {
583       cout << par->Type() << " :"
584         << par->TypeStr() << " :"
585         //		<< &par->TypeStr() << ": "
586         << parString << '\n';
587     }
588 
589     if (doIndentation) ostr << "   ";
590 
591     // Name display
592     ostr.width(16);
593     ostr << left << parString;
594     if (parString.length() >= 16) {
595       ostr << '\n'; // for cmsv compatible output (uses help,OUTPUT)
596       ostr.width(doIndentation ? 19 : 16);
597       ostr << "";
598     }
599 
600     // Type display (we have two "null" : defined !null and undefined variables ...
601     if (par == NULL) {
602       ostr << "UNDEFINED = <Undefined>" << '\n';
603       return;
604     }
605     if (par == NullGDL::GetSingleInstance()) {
606       ostr << "UNDEFINED = !NULL" << '\n';
607       return;
608     }
609     ostr.width(10);
610     bool doTypeString = true;
611 
612     // Data display
613     if (par->Type() == GDL_STRUCT) {
614       ostr << par->TypeStr() << right;
615       if (!doIndentation) ostr << "= ";
616       doTypeString = false;
617 
618       DStructGDL* s = static_cast<DStructGDL*> (par);
619       ostr << "-> ";
620       ostr << (s->Desc()->IsUnnamed() ? "<Anonymous>" : s->Desc()->Name());
621       ostr << " ";
622     } else if (par->Dim(0) == 0) {
623       if (par->Type() == GDL_STRING) {
624         ostr << par->TypeStr() << right;
625         if (!doIndentation) ostr << "= ";
626         doTypeString = false;
627 
628         // trim string larger than $COLUMNS- characters
629         DString dataString = (*static_cast<DStringGDL*> (par))[0];
630         int ncols=max(39,TermWidth()-12-29); //29 as this is the position where the string is writte, 11 for '... plus blank on rght
631         ostr << "'" << StrMid(dataString, 0,ncols, 0) << "'";
632         if (dataString.length() > ncols) ostr << "...";
633       } else if (par->Type() == GDL_OBJ && par->StrictScalar()) {
634         DObj s = (*static_cast<DObjGDL*> (par))[0]; // is StrictScalar()
635         if (s != 0) // no overloads for null object
636         {
637           DStructGDL* oStructGDL = GDLInterpreter::GetObjHeapNoThrow(s);
638           if (oStructGDL != NULL) // if object not valid -> default behaviour
639           {
640             DStructDesc* desc = oStructGDL->Desc();
641             static DString listName("LIST");
642             if (desc->IsParent(listName)) {
643               ostr << desc->Name();
644 
645               unsigned nListTag = desc->TagIndex("NLIST");
646               DLong nList = (*static_cast<DLongGDL*> (oStructGDL->GetTag(nListTag, 0)))[0];
647               ostr << left;
648               ostr << "<ID=";
649               ostr << i2s(s) << "  N_ELEMENTS=" << i2s(nList) << ">";
650 
651               doTypeString = false;
652             }
653             static DString hashName("HASH");
654             if (desc->IsParent(hashName)) {
655               ostr << desc->Name();
656 
657               unsigned nListTag = desc->TagIndex("TABLE_COUNT");
658               DLong nList = (*static_cast<DLongGDL*> (oStructGDL->GetTag(nListTag, 0)))[0];
659               ostr << left;
660               ostr << "<ID=";
661               ostr << i2s(s) << "  N_ELEMENTS=" << i2s(nList) << ">";
662 
663               doTypeString = false;
664             }
665           }
666         }
667       }
668       if (doTypeString) {
669         ostr << par->TypeStr() << right;
670         if (!doIndentation) ostr << "= ";
671         doTypeString = false;
672 
673         par->ToStream(ostr);
674       }
675     }
676 
677     if (doTypeString) {
678       ostr << par->TypeStr() << right;
679       if (!doIndentation) ostr << "= ";
680       if (par->IsAssoc())
681         par->ToStream(ostr);
682     }
683 
684     // Dimension display
685     if (par->Dim(0) != 0) ostr << par->Dim();
686 
687     // End of line
688     ostr << '\n';
689   }
690 
help_struct(ostream & ostr,BaseGDL * par,int indent=0,bool debug=false)691   void help_struct(ostream& ostr, BaseGDL* par, int indent = 0, bool debug = false)
692   {
693     // STRUCTURES
694     DStructGDL* s = static_cast<DStructGDL*> (par);
695     SizeT nTags = s->Desc()->NTags();
696 
697     for (int i = 0; i < indent; ++i) ostr << "   ";
698     ostr << "** Structure ";
699     ostr << (s->Desc()->IsUnnamed() ? "<Anonymous>" : s->Desc()->Name());
700     ostr << ", " << nTags << " tags";
701     if (indent == 0) {
702       ostr << ",memsize =" << s->Sizeof();
703       ostr << ", data length=" << s->NBytesToTransfer()
704         //			<< "/" << s->RealBytes() ; GJ has this but only applied here.
705         << "/" << s->SizeofTags();
706     }
707     ostr << ":" << '\n';
708 
709     for (SizeT t = 0; t < nTags; ++t) {
710       for (int i = 0; i < indent; ++i) ostr << "   ";
711       if (debug) ostr.width(18);
712       if (debug) ostr << "dbg: OFFSET=" << s->Desc()->Offset(t);
713       help_item(ostr, s->GetTag(t), s->Desc()->TagName(t), true);
714       // only one level visible in "help".
715       //		    if(s->GetTag(t)->Type() == GDL_STRUCT) help_struct(ostr, s->GetTag(t), indent+1);
716     }
717     //		lines_count += nTags;
718   }
719 #if 0
720 void help_struct(ostream& ostr,  DStructDesc* dsc)
721   {
722 
723 	   SizeT nTags = dsc->NTags();
724 		ostr << "** Structure ";
725 		ostr << (dsc->IsUnnamed() ? "<Anonymous>" : dsc->Name());
726 		ostr << ", " << nTags << " tags";
727 		ostr << ",length =" << dsc->NBytes();
728 		for (SizeT t=0; t < nTags; ++t) {
729 		    help_item( ostr, dsc->GetTag(t), dsc->TagName(t), true);
730 		    if(dsc->GetTag(t)->Type() == GDL_STRUCT)
731 				help_struct(ostr, dsc->GetTag(t)->Desc());
732 			}
733 		ostr << ":" << '\n';
734 }
735 #endif
736 
help_help(EnvT * e)737 void help_help(EnvT* e)
738   {
739 	string inline_help[]={"Usage: "+e->GetProName()+", expr1, ..., exprN,",
740 			      "          /ALL_KEYS, /BRIEF, /CALLS, /FUNCTIONS, /HELP, /INFO,",
741 			      "          /INTERNAL_LIB_GDL, /KEYS, /LAST_MESSAGE, /LIB, /MEMORY,",
742 			      "          NAMES=string_filter, OUTPUT=res, /PATH_CACHE, /FILES, ",
743 			      "          /PREFERENCES, /PROCEDURES, /RECALL_COMMANDS, /ROUTINES,",
744 			      "          /SOURCE_FILES, /STRUCTURES, /SYSTEM_VARIABLES, /TRACEBACK"};
745 	int size_of_s = sizeof(inline_help) / sizeof(inline_help[0]);
746 	e->Help(inline_help, size_of_s);
747 	return;
748   }
749 
SortAndPrintStream(ostringstream & oss)750   void SortAndPrintStream(ostringstream& oss) {
751     std::string delimiter = "\n";
752     std::string s = oss.rdbuf()->str().erase(oss.rdbuf()->str().length(), 1);
753     size_t pos = 0;
754     vector<std::string> stringList;
755 
756     while ((pos = s.find(delimiter)) != std::string::npos) {
757       stringList.push_back(s.substr(0, pos));
758       s.erase(0, pos + delimiter.length());
759     }
760     oss.str("");
761     sort(stringList.begin(), stringList.end());
762     vector<std::string>::iterator it = stringList.begin();
763 	while (it != stringList.end()) std::cout << *it++;
764     std::cout << '\n';
765 }
766 
StreamToGDLString(ostringstream & oss,bool sorted=false)767   DStringGDL* StreamToGDLString(ostringstream& oss, bool sorted=false) {
768 
769     std::string delimiter = "\n";
770     int nlines = 0;
771     size_t pos = 0;
772     while ((pos = oss.str().find(delimiter, pos + 1)) != std::string::npos) {
773       ++nlines;
774     }
775     if (!nlines) return new DStringGDL("");
776 
777     dimension dim(nlines, (size_t) 1);
778     DStringGDL* out = new DStringGDL(dim, BaseGDL::NOZERO);
779 
780     std::string s = oss.rdbuf()->str().erase(oss.rdbuf()->str().length(), 1);
781     pos = 0;
782     vector<std::string> stringList;
783     SizeT nOut = 0;
784 
785     while ((pos = s.find(delimiter)) != std::string::npos) {
786       stringList.push_back(s.substr(0, pos));
787       s.erase(0, pos + delimiter.length());
788     }
789     oss.str("");
790 
791     if (sorted) sort(stringList.begin(), stringList.end());
792     vector<std::string>::iterator it = stringList.begin();
793 	while (it != stringList.end()) (*out)[nOut++] = *it++;
794 
795     return out;
796   }
797 
help_pro(EnvT * e)798   void help_pro(EnvT* e)
799   {
800     // in order of priority
801     bool kw = false;
802     static int lastmKWIx = e->KeywordIx("LAST_MESSAGE");
803     bool lastmKW = e->KeywordPresent(lastmKWIx);
804     if (lastmKW) {
805       help_lastmsg(e);
806       return;
807     }
808     SizeT nParam = e->NParam();
809 
810     BaseGDL** outputKW = NULL;
811     static int outputIx = e->KeywordIx("OUTPUT");
812     bool doOutput = e->KeywordPresent(outputIx);
813 
814     if (doOutput) { // Setup output return variable
815       outputKW = &e->GetKW(outputIx);
816       GDLDelete((*outputKW));
817     }
818     static SizeT OutputLines;
819     OutputLines = 0;
820 
821     std::ostringstream ostr;
822     // Use mostly ostrp* << from here on and then push onto outputKW if need be.
823     std::ostream* ostrp = (doOutput) ? &ostr : &cout;
824 
825     static int helpIx = e->KeywordIx("HELP");
826     if (e->KeywordSet(helpIx)) {
827       help_help(e);
828       return;
829     }
830 
831     static int allkeysIx = e->KeywordIx("ALL_KEYS");
832     static int keysIx = e->KeywordIx("KEYS");
833     if (e->KeywordSet(allkeysIx) || e->KeywordSet(keysIx)) // ALL_KEYS is an obsolete keyword
834     {
835       help_keys(ostr);
836       if (doOutput) (*outputKW) = StreamToGDLString(ostr);
837       else cout << ostr.str();
838       return;
839     }
840 
841     static volatile bool debugKW(false);
842     static int debugIx = e->KeywordIx("DEBUG");
843     if (e->KeywordSet(debugIx)) {
844       debugKW = !debugKW;
845       cout << " Help debug option set/reset: " << debugKW << '\n';
846       return;
847     }
848 
849     static int pathIx = e->KeywordIx("PATH_CACHE");
850     if (e->KeywordSet(pathIx)) { // exercising two methods
851       help_path_cached(ostr, OutputLines);
852       if (debugKW) {
853         cout << OutputLines << '\n';
854         cout << "begin" << ostr.rdbuf()->str() << "end" << '\n';
855       }
856       help_Output(outputKW, ostr, OutputLines, doOutput);
857       return;
858     }
859     // if keyword /TraceBack then we return
860     static int tracebackKWIx = e->KeywordIx("TRACEBACK");
861     bool tracebackKW = e->KeywordSet(tracebackKWIx);
862 
863     if (tracebackKW) {
864       SimpleDumpStack(e, ostr);
865       if (doOutput) (*outputKW) = StreamToGDLString(ostr);
866       else cout << ostr.str();
867       return;
868     }
869 
870     static int briefKWIx = e->KeywordIx("BRIEF");
871     bool briefKW = e->KeywordSet(briefKWIx);
872     // briefKw should be default usage with DLM HEAP_VARIABLES
873     // also MESSAGES OBJECTS ROUTINES SOURCE_FILES STRUCTURES SYSTEM_VARIABLES
874     static int fullKWIx = e->KeywordIx("FULL");
875     bool fullKW = e->KeywordSet(fullKWIx);
876 
877     // AC 14-08-11 : detailed info (display size, deep ...) are missing
878     static int deviceKWIx = e->KeywordIx("DEVICE");
879     bool deviceKW = e->KeywordPresent(deviceKWIx);
880     if (deviceKW) {
881       GraphicsDevice::ListDevice(ostr);
882       DString name = (*static_cast<DStringGDL*> (SysVar::D()->GetTag(SysVar::D()->Desc()->TagIndex("NAME"), 0)))[0];
883       ostr << "Current graphics device: " << name << '\n';
884       if (doOutput) (*outputKW) = StreamToGDLString(ostr, true);
885       else cout << ostr.str();
886       return;
887     }
888 
889     static int heapIx = e->KeywordIx("HEAP");
890     if (e->KeywordSet(heapIx)) {
891       help_heap_obj_ptr_head(e, *ostrp);
892       if (briefKW) return;
893       help_mix_heap_and_obj(e, *ostrp);
894       if (doOutput) (*outputKW) = StreamToGDLString(ostr, true);
895       else SortAndPrintStream(ostr);
896       return;
897     }
898 
899     static int namesIx = e->KeywordIx("NAMES");
900     bool isKWSetNames = e->KeywordPresent(namesIx);
901 
902     static int sysvarIx = e->KeywordIx("SYSTEM_VARIABLES");
903     if (e->KeywordSet(sysvarIx)) {
904       help_sysvar(*ostrp, briefKW);
905       if (doOutput) (*outputKW) = StreamToGDLString(ostr, true);
906       else SortAndPrintStream(ostr);
907       return;
908     }
909     /*
910     OBJECTS
911     Set this keyword to display information on defined object classes.
912      *  If no arguments are provided, all currently-defined object classes are shown.
913      *  If arguments are provided, the definition of the object class
914      *  for the heap variables referred to is displayed.
915 
916      */
917     static int objectsIx = e->KeywordIx("OBJECTS");
918     if (e->KeywordSet(objectsIx)) {
919       if (nParam == 0) {
920         //sort alphabetically object names...
921         std::set< std::string> objNames;
922         for (SizeT i = 0; i < structList.size(); ++i) {
923           if ((structList[i]->FunList().size() + structList[i]->ProList().size()) == 0) continue;
924           objNames.insert(structList[i]->Name());
925       }
926         SizeT nObj = objNames.size();
927         if (nObj < 1) return;
928 
929         //these are objects that have at least one method.
930         //sort alphabetically
931 
932         for (std::set<string>::iterator iobj = objNames.begin(); iobj != objNames.end(); ++iobj) {
933           DStructDesc* objDesc = FindObjectInStructList(structList, *iobj);
934           if (objDesc != NULL) help_object(ostrp, objDesc, fullKW);
935         }
936       } else {
937         for (SizeT i = 0; i < nParam; ++i) {
938           BaseGDL*& par = e->GetPar(i);
939           if (par!=NULL && e->GetPar(i)->Type() == GDL_OBJ) {
940             DObjGDL* myObj = static_cast<DObjGDL*> (e->GetParDefined(i));
941             DStructDesc* objDesc = (BaseGDL::interpreter->GetObjHeap( (*myObj)[0]))->Desc();
942             if (objDesc != NULL) help_object(ostrp, objDesc, fullKW);
943           }
944         }
945       }
946       if (doOutput) (*outputKW) = StreamToGDLString(ostr, true);
947       return;
948     }
949 
950     DString names = "";
951     if (isKWSetNames) {
952       e->AssureStringScalarKWIfPresent(namesIx, names);
953       // since routines and var. are stored in Maj, we convert ...
954       names = StrUpCase(names);
955     }
956 
957     static int sourceFilesKWIx = e->KeywordIx("SOURCE_FILES");
958     bool sourceFilesKW = e->KeywordPresent(sourceFilesKWIx);
959 
960     static int routinesKWIx = e->KeywordIx("ROUTINES");
961     bool routinesKW = e->KeywordSet(routinesKWIx);
962 
963     static int ProceduresIx = e->KeywordIx("PROCEDURES");
964     bool isKWSetProcedures = e->KeywordSet(ProceduresIx);
965 
966     static int FunctionsIx = e->KeywordIx("FUNCTIONS");
967     bool isKWSetFunctions = e->KeywordSet(FunctionsIx);
968 
969 
970     if (sourceFilesKW) {
971       if (!isKWSetFunctions) {
972         // AC 2018-01-09 : Duplicating the pro list to avoid messing up the order of "proList"
973         // otherwise, a call to HELP,/source created in future calls
974         // e.g. of crashing sequence : TEST_TV & HELP, /source & TEST_TV
975         //
976         ProListT proList_tmp;
977         proList_tmp = proList;
978         sort(proList_tmp.begin(), proList_tmp.end(), CompProName());
979         *ostrp << "Compiled Procedures:" << '\n';
980         *ostrp << "$MAIN$" << '\n';
981         OutputLines += 2;
982         for (SizeT i = 0; i < proList_tmp.size(); ++i) {
983           if (proList_tmp[i]->isHidden() and !fullKW) continue;
984           if (isKWSetNames and
985             !(CompareWithJokers(names, proList_tmp[i]->ObjectName()))) continue;
986           *ostrp << setw(25) << left << proList_tmp[i]->ObjectName() << setw(0);
987           *ostrp << proList_tmp[i]->GetFilename() << '\n';
988         }
989       }
990 
991       if (!isKWSetProcedures) {
992         if (!isKWSetFunctions) *ostrp << '\n';
993         // AC 2018-01-09 : Duplicating the fun list to avoid messing up the order of "funList"
994         // see above in (do_pro).
995         FunListT funList_tmp;
996         funList_tmp = funList;
997         sort(funList_tmp.begin(), funList_tmp.end(), CompFunName());
998         *ostrp << "Compiled Functions:" << '\n';
999         ++OutputLines;
1000         for (SizeT i = 0; i < funList_tmp.size(); ++i) {
1001           if (funList_tmp[i]->isHidden() and !fullKW) continue;
1002           if (isKWSetNames and
1003             !(CompareWithJokers(names, funList_tmp[i]->ObjectName()))) continue;
1004           *ostrp << setw(25) << left << funList_tmp[i]->ObjectName() << setw(0);
1005           *ostrp << funList_tmp[i]->GetFilename() << '\n';
1006           ++OutputLines;
1007         }
1008       }
1009       if (doOutput) (*outputKW) = StreamToGDLString(ostr, true);
1010       else cout << ostr.str();
1011       return;
1012     }
1013 
1014     // Compiled Procedures & Functions
1015 
1016     vector<DString> fList;
1017     static volatile int npro = 0, nfun = 0;
1018     for (FunListT::iterator i = funList.begin(); i != funList.end(); ++i)
1019       if (fullKW or !((*i)->isHidden())) {
1020         fList.push_back((*i)->ObjectName());
1021         ++nfun;
1022       }
1023     sort(fList.begin(), fList.end());
1024     if (debugKW) cout << " #functions=" << nfun;
1025 
1026     vector<DString> pList;
1027     pList.push_back("$MAIN$");
1028     for (ProListT::iterator i = proList.begin(); i != proList.end(); ++i)
1029       if (fullKW or !((*i)->isHidden())) {
1030         pList.push_back((*i)->ObjectName());
1031         ++npro;
1032       }
1033     sort(pList.begin(), pList.end());
1034     if (debugKW) cout << " #procedures=" << npro;
1035     if (debugKW) cout << " #env:" << e->Caller()->EnvSize() << '\n';
1036 
1037     static int callsKWIx = e->KeywordIx("CALLS");
1038     bool callsKW = e->KeywordPresent(callsKWIx);
1039 
1040     if (callsKW) {
1041 
1042       // this is a code derived from SimpleDumpStack() above
1043       EnvStackT& callStack = e->Interpreter()->CallStack();
1044       long actIx = callStack.size() - 1;
1045       DStringGDL* retVal = new DStringGDL(dimension(actIx + 1), BaseGDL::NOZERO);
1046       SizeT rIx = 0;
1047       for (; actIx >= 0; --actIx) {
1048         EnvStackT::pointer_type upEnv = callStack[actIx];
1049         DString actString = upEnv->GetProName();
1050         std::string file = upEnv->GetFilename();
1051         if (file != "") {
1052           actString += " <" + file + "(";
1053           actString += i2s(upEnv->GetLineNumber(), 4);
1054           actString += ")>";
1055         }
1056         (*retVal)[rIx++] = actString;
1057       }
1058       e->SetKW(callsKWIx, retVal);
1059       return;
1060     }
1061     static int filesIx = e->KeywordIx("FILES");
1062     if (e->KeywordPresent(filesIx)) {
1063       help_files(ostr, e);
1064       if (doOutput) (*outputKW) = StreamToGDLString(ostr, true);
1065       else cout << ostr.str();
1066       return;
1067     }
1068 
1069     static int infoIx = e->KeywordIx("INFO");
1070     if (e->KeywordSet(infoIx)) {
1071       kw = true;
1072       help_info();
1073     }
1074 
1075     static int libIx = e->KeywordIx("LIB");
1076     // internal library functions
1077     static int INTERNAL_LIB_GDLIx = e->KeywordIx("INTERNAL_LIB_GDL");
1078     bool kwLibInternal = e->KeywordSet(INTERNAL_LIB_GDLIx);
1079     if (kwLibInternal) {
1080       cout << "NOTE: Internal subroutines are subject to change without notice." << '\n';
1081       cout << "They should never be called directly from a GDL program." << '\n';
1082     }
1083     if (e->KeywordSet(libIx)) {
1084       kw = true;
1085       help_ListLib(names, *ostrp, kwLibInternal);
1086     }
1087 
1088     static int STRUCTURESIx = e->KeywordIx("STRUCTURES");
1089     bool isKWSetStructures = e->KeywordSet(STRUCTURESIx);
1090     static int RECALL_COMMANDSIx = e->KeywordIx("RECALL_COMMANDS");
1091     bool isKWSetRecall = e->KeywordSet(RECALL_COMMANDSIx);
1092     static int MEMORYIx = e->KeywordIx("MEMORY");
1093     bool isKWSetMemory = e->KeywordSet(MEMORYIx);
1094     static int PREFERENCESIx = e->KeywordIx("PREFERENCES");
1095     bool isKWSetPreferences = e->KeywordSet("PREFERENCES");
1096     if (isKWSetStructures) kw = true;
1097 
1098     if ((isKWSetStructures or isKWSetRecall or isKWSetMemory or
1099       isKWSetPreferences) and (isKWSetProcedures or isKWSetFunctions))
1100       e->Throw("Conflicting keywords.");
1101 
1102     if (isKWSetRecall) {
1103       DStringGDL *previous_commands;
1104       previous_commands = recall_commands_internal();
1105       SizeT nEl2 = previous_commands->N_Elements();
1106       cout << "Recall buffer length: " << nEl2 << '\n';
1107 
1108       char ctmp;
1109       int nb_lines=TermHeight();
1110 
1111       for (SizeT i = 0; i < nEl2; ++i) {
1112         if (isKWSetNames and
1113           !CompareWithJokers(names, (*previous_commands)[i])) continue;
1114         *ostrp << i + 1 << "  " << (*previous_commands)[i] << '\n';
1115 	if (( i > 0) && (i % (nb_lines-4)) == 0) {
1116 	  *ostrp << "      < Press q or Q to quit, any key to continue, ? for help >" << '\n';
1117 	  ctmp=my_get_kbrd();
1118 	  nb_lines=TermHeight();
1119 	  if ((tolower(ctmp) == 'h') || (ctmp == '?')) {
1120 	    *ostrp << "---------------------------------------------------    " << '\n';
1121 	    *ostrp << "<space>		Display next page of text." << '\n';
1122 	    *ostrp << "<return>	Display next line of text. (TBD)" << '\n';
1123 	    *ostrp << "q or Q		Quit" << '\n';
1124 	    *ostrp << "h, H, or ?	Display this message." << '\n';
1125 	    *ostrp << "---------------------------------------------------" << '\n';
1126 	    ctmp=my_get_kbrd();
1127 	    nb_lines=TermHeight();
1128 	  }
1129 	  if (tolower(ctmp) == 'q') break;
1130 	}
1131       }
1132 
1133       GDLDelete(previous_commands);
1134       if (doOutput) (*outputKW) = StreamToGDLString(ostr);
1135       else cout << ostr.str();
1136       return;
1137     }
1138     if (isKWSetMemory) {
1139       ostr << "heap memory used: ";
1140       ostr << MemStats::GetCurrent();
1141       ostr << ", max: ";
1142       ostr << MemStats::GetHighWater();
1143       ostr << ", gets: ";
1144       ostr << MemStats::GetNumAlloc();
1145       ostr << ", frees: ";
1146       ostr << MemStats::GetNumFree() << '\n';
1147       if (doOutput) (*outputKW) = StreamToGDLString(ostr);
1148       else cout << ostr.str();
1149       return;
1150     }
1151     if (isKWSetPreferences) {
1152       //cout << "ici 1 " << isKWSetPreferences << '\n';
1153       *ostrp << "Preferences: this is not ready ..." << '\n';
1154       //*ostrp << GDL_GR_X_QSCREEN::GetValue();
1155       if (doOutput) help_Output(outputKW, ostr, OutputLines);
1156 
1157       return;
1158     }
1159     // switch to dec output (might be changed from formatted output)
1160 
1161     if (!doOutput) cout << dec;
1162 
1163     if ((nParam == 0 and !isKWSetMemory) or
1164       isKWSetFunctions or isKWSetProcedures) {
1165 
1166       if (nParam == 0 and !isKWSetFunctions and
1167         !isKWSetProcedures and !routinesKW)
1168         SimpleDumpStack(e, *ostrp);
1169 
1170       if (isKWSetProcedures or routinesKW) {
1171         *ostrp << "Compiled Procedures:" << '\n' << "$MAIN$" << '\n';
1172         OutputLines += 2;
1173         for (SizeT i = 1; i < pList.size(); ++i) {
1174 
1175           // Find DPro pointer for pList[i]
1176           ProListT::iterator p = std::find_if(proList.begin(), proList.end(),
1177             Is_eq<DPro>(pList[i]));
1178           if (p == proList.end()) continue;
1179           DPro *pro = *p;
1180 
1181           if (isKWSetNames and
1182             !CompareWithJokers(names, pro->ObjectName())) continue;
1183           *ostrp << setw(25) << left << pro->ObjectName() << setw(0);
1184 
1185           int nPar = pro->NPar();
1186           int nKey = pro->NKey();
1187           // Loop through parameters and keywords
1188           for (SizeT j = 0; j < pro->NPar(); ++j)
1189             *ostrp << StrLowCase(pro->GetVarName(nKey + j)) << " ";
1190           for (SizeT j = 0; j < pro->NKey(); ++j)
1191             *ostrp << StrUpCase(pro->GetVarName(j)) << " ";
1192           *ostrp << '\n';
1193           ++OutputLines;
1194         }
1195       }
1196 
1197       if (isKWSetFunctions or routinesKW) {
1198         *ostrp << "Compiled Functions:" << '\n';
1199         ++OutputLines;
1200         // Loop through functions
1201         for (SizeT i = 0; i < fList.size(); ++i) {
1202           // Find DFun pointer for fList[i]
1203           FunListT::iterator p = std::find_if(funList.begin(), funList.end(),
1204             Is_eq<DFun>(fList[i]));
1205           if (p == funList.end()) continue;
1206           DFun *pro = *p;
1207           if (isKWSetNames and
1208             !CompareWithJokers(names, pro->ObjectName())) continue;
1209           int nPar = pro->NPar();
1210           int nKey = pro->NKey();
1211           *ostrp << setw(25) << left << pro->ObjectName() << setw(0);
1212 
1213           for (SizeT j = 0; j < nPar; ++j)
1214             *ostrp << StrLowCase(pro->GetVarName(nKey + j)) << " ";
1215           for (SizeT j = 0; j < nKey; ++j)
1216             *ostrp << StrUpCase(pro->GetVarName(j)) << " ";
1217           *ostrp << '\n';
1218           OutputLines++;
1219         }
1220       }
1221       if (isKWSetProcedures or isKWSetFunctions) {
1222         if (doOutput) help_Output(outputKW, ostr, OutputLines);
1223         return;
1224       }
1225     }
1226 
1227     // Excluding keywords which are exclusive is not finished ...
1228 
1229     if (isKWSetPreferences) {
1230       //cout << "ici 1 " << isKWSetPreferences << '\n';
1231       *ostrp << "Preferences: this is not ready ..." << '\n';
1232       //*ostrp << GDL_GR_X_QSCREEN::GetValue();
1233       if (doOutput) help_Output(outputKW, ostr, OutputLines);
1234 
1235       return;
1236     }
1237     static int commonIx = e->KeywordIx("COMMON");
1238     if (e->KeywordSet(commonIx)) { // list in internal order
1239       CommonListT::iterator it;
1240       for (it = commonList.begin(); it != commonList.end(); ++it) {
1241         SizeT nVar = (*it)->NVar();
1242         if (nVar == 0) continue;
1243 	*ostrp << " Common block (" << (*it)->Name() <<
1244           ") listed in internal order:" << '\n';
1245         for (SizeT vIx = 0; vIx < nVar; ++vIx) {
1246           DString parString = (*it)->VarName(vIx) + " (" + (*it)->Name() + ')';
1247           DVar* var = (*it)->Var(vIx);
1248           if (var->Data() not_eq NULL || fullKW)
1249             help_item(*ostrp, var->Data(), parString, false);
1250         }
1251       }
1252       if (doOutput) (*outputKW) = StreamToGDLString(ostr);
1253       else cout << ostr.str();
1254       if (nParam == 0) return;
1255     }
1256 
1257     if (debugKW) std::cout << " help_pro: nParam=" << nParam;
1258     for (SizeT i = 0; i < nParam; ++i) {
1259       BaseGDL*& par = e->GetPar(i);
1260       DString parString = e->Caller()->GetString(par, true); //= string(" ??? ");
1261       if (debugKW) std::cout << ". ";
1262       if (par == NULL) {
1263         if (debugKW) std::cout << " par ==(NULL)" << '\n';
1264         if (!briefKW) help_item(ostr, par, parString, false);
1265         continue;
1266       }
1267 
1268       // NON-STRUCTURES except if one and only one param is a Struct.
1269       if (par->Type() == GDL_STRUCT && !briefKW ) {
1270         if (((par->N_Elements()==1) && nParam==1 ) || isKWSetStructures) {
1271           help_struct(ostr, par, 0, debugKW);
1272           continue;
1273         }
1274       }
1275       help_item(ostr, par, parString, false);
1276       if (nParam != 1) continue;
1277       else if (par->Type() == GDL_OBJ and par->StrictScalar()) {
1278         DObj s = (*static_cast<DObjGDL*> (par))[0];
1279         DStructGDL* oStructGDL;
1280         if (s != 0) oStructGDL = GDLInterpreter::GetObjHeapNoThrow(s);
1281         if (s != 0 and (isKWSetStructures or fullKW))
1282           help_struct(ostr, oStructGDL, 0, debugKW);
1283       } else if (par->Type() == GDL_OBJ) {
1284         SizeT nObj = par->N_Elements();
1285         if (debugKW) std::cout << "nObj=" << nObj;
1286         for (SizeT iobj = 0; iobj < nObj; ++iobj) {
1287           if (debugKW) std::cout << "-";
1288           DObj s = (*static_cast<DObjGDL*> (par))[iobj];
1289           //				object_help( ostr, s, false);
1290           DStructGDL* oStructGDL;
1291           DString classname;
1292           DString nameobj = "  [" + i2s(iobj) + "] ";
1293           if (s != 0) {
1294             // Name display
1295             if (debugKW) std::cout << "-";
1296             std::vector< std::string> pNames;
1297             ostr.width(16);
1298             ostr << left << nameobj;
1299             oStructGDL = GDLInterpreter::GetObjHeapNoThrow(s);
1300             if (oStructGDL == NULL) continue;
1301             if (debugKW) std::cout << ".";
1302             classname = oStructGDL->Desc()->Name();
1303             oStructGDL->Desc()->GetParentNames(pNames);
1304             ostr.width(10);
1305             ostr << classname << right;
1306             ostr << left << "<ID=" << i2s(s) << "> " << right;
1307             DString parents;
1308             if (pNames.size() == 0) parents = "";
1309             else if (pNames.size() == 1) parents = "  parent:";
1310             else parents = " parents:";
1311             ostr << left << parents;
1312             for (SizeT i = 0; i < pNames.size(); ++i) ostr << " " << pNames[i];
1313             ostr << '\n';
1314           } else
1315             help_item(ostr, NULL, nameobj, false);
1316         }
1317       }
1318 
1319     }
1320     if (debugKW) std::cout << " :=:" << '\n';
1321     if (nParam > 0) {
1322       if (doOutput) (*outputKW) = StreamToGDLString(ostr);
1323       else cout << ostr.str();
1324       return;
1325     }
1326 
1327 
1328     static int levelIx = e->KeywordIx("LEVEL");
1329     if (e->KeywordPresent(levelIx)) {
1330       std::ostringstream parameters_ostringstream;
1331       DLongGDL* level = e->IfDefGetKWAs<DLongGDL>(levelIx);
1332 
1333       //will list (all) variables, incl. common defined, at desired level.
1334       EnvStackT& callStack = e->Interpreter()->CallStack();
1335       DLong curlevnum = callStack.size();
1336       DLong desiredlevnum = curlevnum;
1337       if (level != NULL) {
1338         desiredlevnum = (*level)[0];
1339         if (desiredlevnum <= 0) desiredlevnum += curlevnum;
1340         if (desiredlevnum < 1) desiredlevnum = 0;
1341         if (desiredlevnum > curlevnum) desiredlevnum = curlevnum;
1342       }
1343 
1344       DSubUD* pro = static_cast<DSubUD*> (callStack[desiredlevnum - 1]->GetPro());
1345 
1346       SizeT nVar = pro->Size(); // # var in GDL for desired level
1347       SizeT nComm = pro->CommonsSize(); // # has commons?
1348       SizeT nTotVar = nVar + nComm; //All the variables availables at that lev.
1349       if (debugKW) cout << " Level section: nTotVar=" << nTotVar << '\n';
1350       if (nTotVar > 0) {
1351         set<string> helpStr; // "Sorted List"
1352         if (nVar > 0) {
1353           for (SizeT i = 0; i < nVar; ++i) {
1354             BaseGDL*& par = (static_cast<EnvUDT*> (callStack[desiredlevnum - 1]))->GetKW(i);
1355             if (par != NULL) {
1356               stringstream ss;
1357               string parName = pro->GetVarName(i);
1358               help_item(ss, par, parName, false);
1359               helpStr.insert(ss.str());
1360             }
1361           }
1362         }
1363 
1364         if (nComm > 0) {
1365           DStringGDL* list = static_cast<DStringGDL*> (pro->GetCommonVarNameList());
1366           for (SizeT i = 0; i < list->N_Elements(); ++i) {
1367             BaseGDL** par = pro->GetCommonVarPtr((*list)[i]);
1368             DCommonBase* common = pro->FindCommon((*list)[i]);
1369             DString parName = (*list)[i] + " (" + common->Name() + ')';
1370             stringstream ss;
1371             help_item(ss, (*par), parName, false);
1372             helpStr.insert(ss.str());
1373           }
1374         }
1375         copy(helpStr.begin(), helpStr.end(), ostream_iterator<string>(parameters_ostringstream));
1376       }
1377     }
1378     //					 if /brief and /routines then no var. to be shown
1379     if (routinesKW and briefKW) kw = true;
1380 
1381     if (nParam == 0 and !kw) {
1382       routinesKW = true;
1383       briefKW = true;
1384 
1385       // list all variables of caller
1386       EnvBaseT* caller = e->Caller();
1387 
1388       SizeT nEnv = caller->EnvSize();
1389 
1390       //cout << "EnvSize() " << nEnv << '\n';
1391 
1392       set<string> helpStr; // "Sorted List"
1393       stringstream ss;
1394 
1395       for (int i = 0; i < nEnv; ++i) {
1396         BaseGDL*& par = caller->GetKW(i);
1397 
1398         if (par == NULL && !fullKW) continue;
1399         // if( par == NULL) continue;
1400 
1401         DString parString = caller->GetString(par, true);
1402         if (debugKW) cout << " GetKW(i)->GetString(par) " << parString;
1403         if (isKWSetNames and
1404           !CompareWithJokers(names, parString)) continue;
1405         help_item(ss, par, parString, false);
1406         helpStr.insert(ss.str());
1407         ss.str("");
1408       }
1409       if (debugKW) cout << '\n';
1410       std::vector<DCommonBase*> cptr;
1411       DSubUD* proUD = dynamic_cast<DSubUD*> (caller->GetPro());
1412       proUD->commonPtrs(cptr);
1413       std::vector<DCommonBase*>::iterator ic;
1414       for (ic = cptr.begin(); ic != cptr.end(); ++ic) {
1415         SizeT nVar = (*ic)->NVar();
1416         for (SizeT vIx = 0; vIx < nVar; ++vIx) {
1417           DString parString = (*ic)->VarName(vIx) + " (" + (*ic)->Name() + ')';
1418           if (isKWSetNames and
1419             !CompareWithJokers(names, parString)) continue;
1420           DVar* var = (*ic)->Var(vIx);
1421           if ((var->Data() not_eq NULL) || fullKW) {
1422             help_item(ss, var->Data(), parString, false);
1423             helpStr.insert(ss.str());
1424             ss.str("");
1425           }
1426         }
1427       }
1428 
1429       copy(helpStr.begin(), helpStr.end(),
1430         ostream_iterator<string>(*ostrp));
1431       OutputLines += helpStr.size();
1432 
1433     } // if( nParam == 0  and !kw)
1434     if (routinesKW and briefKW) {
1435       // Display compiled procedures & functions
1436       if (!isKWSetProcedures and !isKWSetFunctions) {
1437 
1438         *ostrp << "Compiled Procedures:" << '\n';
1439         for (SizeT i = 0; i < pList.size(); ++i) *ostrp << pList[i] << " ";
1440         *ostrp << '\n' << '\n';
1441         *ostrp << "Compiled Functions:" << '\n';
1442         for (SizeT i = 0; i < fList.size(); ++i) *ostrp << fList[i] << " ";
1443         *ostrp << '\n';
1444         OutputLines += 4;
1445       }
1446     }
1447     if (doOutput) help_Output(outputKW, ostr, OutputLines);
1448     return;
1449   }
1450 
1451   } // namespace
1452