1 /***************************************************************************
2                           saverestore.cpp  -  GDL library procedure
3                              -------------------
4     begin                : Dec 10 2017
5     copyright            : (C) 2017 by Gilles Duvert
6 
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 #include "datatypes.hpp"
20 #include "envt.hpp"
21 #include "dinterpreter.hpp"
22 #include "nullgdl.hpp"
23 #include <queue>
24 
25 //Useful for debugging...
26 #define DEBUG_SAVERESTORE 0
27 
28 using namespace std;
29 
30 const string rectypes[] = {"START_MARKER", //0
31   "COMMON_VARIABLE", //1
32   "VARIABLE", //2
33   "SYSTEM_VARIABLE", //3
34   "", "", "END_MARKER", //6
35   "", "", "", "TIMESTAMP", //10
36   "", "COMPILED", //12
37   "IDENTIFICATION", //13
38   "VERSION", //14
39   "HEAP_HEADER", //15
40   "HEAP_DATA", //16
41   "PROMOTE64", //17
42   "", "NOTICE", //19
43   "DESCRIPTION", //20
44   "UNKNOWN", "UNKNOWN", "UNKNOWN"};
45 
46 const int sizeOfType[] = {0, 1, 2, 4, 4, 8, 8, 0, 0, 16,0,0,2,4,8,8};
47 
48 namespace lib {
49 
50   using namespace std;
51 enum {
52   START_MARKER=0, // Start of SAVE file
53   COMMONBLOCK=1, // Block contains a common block definition
54   VARIABLE=2, // Block contains variable data
55   SYSTEM_VARIABLE=3, // Block contains system variable data
56   END_MARKER=6, // End of SAVE file
57   VARSTART=7, //start of variable data
58   ARRAYSTART=8,
59   STRUCTSTART=9,
60   TIMESTAMP=10, //Block contains time stamp information
61   COMPILED=12, // Block contains compiled procedure or function
62   IDENTIFICATION=13, // Block contains author information
63   VERSION_MARKER=14, // Block contains IDL version information
64   HEAP_HEADER=15, // Block contains heap index information
65   HEAP_DATA=16, // Block contains heap data
66   PROMOTE64=17, // Flags start of 64-bit record file offsets
67   ARRAYSTART64=18,
68   NOTICE=19, // Disclaimer notice
69   DESCRIPTION_MARKER=20 //description ?
70 } Markers;
71 
72   typedef std::map<DPtr, SizeT> heapT;
73   static heapT heapIndexMapSave;  //list of [ heap pointer, heap index ] used when saving.
74   static std::map<long, std::pair<BaseGDL*,DPtr>> heapIndexMapRestore; //list of [heap index , [variable,heap pointer]] used when reading.
75   static std::vector<std::string> predeflist;
76   static char* saveFileAuthor;
77   static char* saveFileDatestring;
78   static char* saveFileUser;
79   static char* saveFileHost;
80 
81   static char* notice;
82   static int32_t format;
83   static  char* arch = 0;
84   static  char* os = 0;
85   static  char* release = 0;
86 
87   static bool safetyTested=false;
88   static bool isSafe=false;
89 
90   static bool save_compress=false;
91   static FILE* save_fid=NULL;
92 
93 #include <rpc/xdr.h>
94 
95   // AC 2017-12-13 : missing in  <rpc/xdr.h> for OSX
96   // Following https://www.gnu.org/software/gnulib/manual/html_node/xdr_005fint16_005ft.html
97   // it may be needed for others OS : Cygwin, Mingw (seems to be OK for *BSD)
98 
99 #ifdef __APPLE__
100 #define xdr_uint16_t xdr_u_int16_t
101 #define xdr_uint32_t xdr_u_int32_t
102 #define xdr_uint64_t xdr_u_int64_t
103 #endif
104 
105 #ifdef __DragonFly__
106 #define xdr_uint16_t xdr_u_int16_t
107 #endif
108 
109   //this is the routined used by IDL as per the documentation.
110 
xdr_complex(XDR * xdrs,DComplex * p)111   bool_t xdr_complex(XDR *xdrs, DComplex *p) {
112     return (xdr_float(xdrs, reinterpret_cast<float *> (p)) && xdr_float(xdrs, reinterpret_cast<float *> (p) + 1));
113   }
114   //this is the routined used by IDL as per the documentation.
115 
xdr_dcomplex(XDR * xdrs,DComplexDbl * p)116   bool_t xdr_dcomplex(XDR *xdrs, DComplexDbl *p) {
117     return (xdr_double(xdrs, reinterpret_cast<double *> (p)) && xdr_double(xdrs, reinterpret_cast<double *> (p) + 1));
118   }
119 
getTimeUserHost(XDR * xdrs)120   void getTimeUserHost(XDR *xdrs) {
121     int32_t UnknownLong;
122     for (int i = 0; i < 256; ++i) if (!xdr_int32_t(xdrs, &UnknownLong)) break;
123     {
124       free(saveFileDatestring);
125       saveFileDatestring = 0;
126       if (!xdr_string(xdrs, &saveFileDatestring, 2048)) cerr << "read error" << endl;
127       //      else        fprintf(stderr, "date: \"%s\"\n", saveFileDatestring);
128     }
129     {
130       free(saveFileUser);
131       saveFileUser = 0;
132       if (!xdr_string(xdrs, &saveFileUser, 2048)) cerr << "read error" << endl;
133       //      else        fprintf(stderr, "user: \"%s\"\n", saveFileUser);
134     }
135     {
136       free(saveFileHost);
137       saveFileHost = 0;
138       if (!xdr_string(xdrs, &saveFileHost, 2048)) cerr << "read error" << endl;
139       //      else        fprintf(stderr, "host: \"%s\"\n", saveFileHost);
140     }
141   }
142 
writeNewRecordHeader(XDR * xdrs,int code)143   inline uint32_t writeNewRecordHeader(XDR *xdrs, int code){
144     int32_t rectype=code;
145     xdr_int32_t(xdrs, &rectype); //-16
146     uint32_t ptrs0=0;
147     uint32_t ptrs1=0;
148     xdr_uint32_t(xdrs, &ptrs0); //-12 //void, to be updated
149     xdr_uint32_t(xdrs, &ptrs1); //-8
150     int32_t UnknownLong=0;
151     xdr_int32_t(xdrs, &UnknownLong);
152     return xdr_getpos(xdrs); //end of header
153   }
154 
updateNewRecordHeader(XDR * xdrs,uint32_t cur)155   inline uint32_t updateNewRecordHeader(XDR *xdrs, uint32_t cur) {
156     uint32_t next = xdr_getpos(xdrs);
157     //dirty trick for compression: write uncompressed, rewind, read what was just written, compress, write over, reset positions.
158     if (save_compress)
159     {
160       uint32_t uLength = next - cur;
161       uLong cLength = compressBound(uLength);
162       char* uncompressed = (char*) calloc(uLength+1,1);
163       xdr_setpos(xdrs, cur);
164       size_t retval = fread(uncompressed, 1, uLength, save_fid);
165       if (retval!=uLength) cerr<<"(compress) read error:"<<retval<<"eof:"<<feof(save_fid)<<", error:"<<ferror(save_fid)<<endl;
166       char* compressed = (char*) calloc(cLength + 1,1);
167       // Deflate
168       compress2((Bytef *) compressed, &cLength, (Bytef *) uncompressed, uLength, Z_BEST_SPEED);
169       //cLength is the good length now.
170       xdr_setpos(xdrs, cur);
171       xdr_opaque(xdrs,compressed,cLength);
172       next = cur+cLength;
173       xdr_setpos(xdrs, next);
174       //if (next!=(cur+cLength)) cerr<<"problem:"<<cur+cLength<<":"<<next<<"\n";
175     }
176     xdr_setpos(xdrs, cur-12); //ptrs0
177     xdr_uint32_t(xdrs, &next);
178     xdr_setpos(xdrs, next);
179     return next;
180   }
181 
writeTimeUserHost(XDR * xdrs,char * FileDatestring,char * FileUser,char * FileHost)182   uint32_t writeTimeUserHost(XDR *xdrs, char* FileDatestring, char* FileUser, char* FileHost) {
183     uint32_t cur=writeNewRecordHeader(xdrs, TIMESTAMP);
184     int32_t UnknownLong=0;
185     for (int i = 0; i < 256; ++i) if (!xdr_int32_t(xdrs, &UnknownLong)) cerr << "write error" << endl;
186     if (!xdr_string(xdrs, &FileDatestring, strlen(FileDatestring))) cerr << "write error" << endl;
187     if (!xdr_string(xdrs, &FileUser, strlen(FileUser))) cerr << "write error" << endl;
188     if (!xdr_string(xdrs, &FileHost, strlen(FileHost))) cerr << "write error" << endl;
189     uint32_t next=updateNewRecordHeader(xdrs, cur);
190     return next;
191   }
192 
writeEnd(XDR * xdrs)193   uint32_t writeEnd(XDR *xdrs) {
194     uint32_t cur=writeNewRecordHeader(xdrs, END_MARKER);
195     return cur;
196   }
197 
getVersion(XDR * xdrs)198   int getVersion(XDR* xdrs) {
199     if (!xdr_int32_t(xdrs, &format)) return 0;
200     //    cerr << "Format: " << format << endl;
201     free(arch); arch = 0;
202     if (!xdr_string(xdrs, &arch, 2048)) return 0;
203     //    cerr << arch << endl;
204     free(os); os = 0;
205     if (!xdr_string(xdrs, &os, 2048)) return 0;
206     //    cerr << os << endl;
207     free(release); release = 0;
208     if (!xdr_string(xdrs, &release, 2048)) return 0;
209     //    cerr << release << endl;
210     return 1;
211   }
212 
writeVersion(XDR * xdrs,int32_t * format,char * arch,char * os,char * release)213   uint32_t writeVersion(XDR* xdrs, int32_t *format, char* arch, char* os , char* release) {
214     uint32_t cur=writeNewRecordHeader(xdrs, VERSION_MARKER);
215     xdr_int32_t(xdrs, format);
216     xdr_string(xdrs, &arch, strlen(arch));
217     xdr_string(xdrs, &os, strlen(os));
218     xdr_string(xdrs, &release, strlen(release));
219     uint32_t next=updateNewRecordHeader(xdrs, cur);
220     return next;
221   }
222 
getNotice(XDR * xdrs)223   int getNotice(XDR* xdrs) {
224     free(notice); notice = 0;
225     if (!xdr_string(xdrs, &notice, 20480)) return 0;
226 //    cerr << notice << endl;
227     return 1;
228   }
229 
writeNotice(XDR * xdrs,char * notice)230   uint32_t writeNotice(XDR* xdrs, char* notice) {
231     uint32_t cur=writeNewRecordHeader(xdrs, NOTICE);
232     xdr_string(xdrs, &notice, strlen(notice));
233     uint32_t next=updateNewRecordHeader(xdrs, cur);
234     return next;
235   }
236 
getDescription(XDR * xdrs)237   char* getDescription(XDR *xdrs) {
238     int32_t length = 0;
239     if (!xdr_int32_t(xdrs, &length)) cerr << "error reading description string length" << endl;
240     if (length > 0)
241     {
242       char* chars = 0;
243       if (!xdr_string(xdrs, &chars, length)) cerr << "error getting string" << endl;
244       return chars;
245     } else return NULL;
246   }
247 
writeDescription(XDR * xdrs,char * descr)248   uint32_t writeDescription(XDR *xdrs, char* descr) {
249     uint32_t cur=writeNewRecordHeader(xdrs, DESCRIPTION_MARKER);
250     int32_t length = strlen(descr);
251     if (!xdr_int32_t(xdrs, &length)) cerr << "error writing description string length" << endl;
252     if (!xdr_string(xdrs, &descr, length)) cerr << "error writing string" << endl;
253     uint32_t next=updateNewRecordHeader(xdrs, cur);
254     return next;
255    }
256 
getIdentification(XDR * xdrs)257   int getIdentification(XDR *xdrs) {
258     free(saveFileAuthor);
259     saveFileAuthor = 0;
260     if (!xdr_string(xdrs, &saveFileAuthor, 2048)) return 0;
261     //    cerr << author << endl;
262     char* title = 0;
263     if (!xdr_string(xdrs, &title, 2048)) return 0;
264     //    cerr << title << endl;
265     char* otherinfo = 0;
266     if (!xdr_string(xdrs, &otherinfo, 2048)) return 0;
267     //    cerr << otherinfo << endl;
268     return 1;
269   }
270 
writeIdentification(XDR * xdrs,char * saveFileAuthor,char * title,char * otherinfo)271   uint32_t writeIdentification(XDR *xdrs, char *saveFileAuthor, char* title, char* otherinfo ) {
272     uint32_t cur=writeNewRecordHeader(xdrs, IDENTIFICATION);
273     xdr_string(xdrs, &saveFileAuthor, strlen(saveFileAuthor));
274     xdr_string(xdrs, &title, strlen(title) );
275     xdr_string(xdrs, &otherinfo, strlen(otherinfo) );
276     uint32_t next=updateNewRecordHeader(xdrs, cur);
277     return next;
278   }
279 
getArrDesc64(XDR * xdrs)280   dimension* getArrDesc64(XDR* xdrs) {
281     if (DEBUG_SAVERESTORE) cerr << "get 64-Bits Array descriptor ," ;
282     int64_t UnknownLong;
283     if (!xdr_int64_t(xdrs, &UnknownLong)) return NULL;
284     int64_t nbytes;
285     if (!xdr_int64_t(xdrs, &nbytes)) return NULL;
286     int64_t nEl;
287     if (!xdr_int64_t(xdrs, &nEl)) return NULL;
288     int32_t nDims;
289     if (!xdr_int32_t(xdrs, &nDims)) return NULL; //on 32 bits
290     if (!xdr_int64_t(xdrs, &UnknownLong)) return NULL; //ignored as we ignore the 2x32 bits integers in the other version.
291     if (DEBUG_SAVERESTORE) cerr << "nbytes:" << nbytes << " ,nEl:" << nEl << ", nDims:" << nDims<<" ";
292     int64_t dims[8];
293     if (!xdr_vector(xdrs, (char*) dims, 8, sizeof (int64_t), (xdrproc_t) xdr_int64_t)) return NULL;
294     SizeT k = dims[0];
295     dimension* theDim = new dimension(k);
296     for (int i = 1; i < 8; ++i)
297     {
298       k = dims[i];
299       *theDim << k;
300     }
301     theDim->Purge();
302     if (DEBUG_SAVERESTORE) cerr<<*theDim<<endl;
303     return theDim;
304   }
305 
getArrDesc(XDR * xdrs)306   dimension* getArrDesc(XDR* xdrs) {
307     if (DEBUG_SAVERESTORE) cerr << "get standard 32-Bits Array descriptor ," ;
308     int32_t arrstart;
309     int32_t UnknownLong;
310     if (!xdr_int32_t(xdrs, &arrstart)) return NULL;
311     if (arrstart != ARRAYSTART && arrstart != ARRAYSTART64) //'10'o and '22'o
312     {
313       cerr << "array is not a array! abort." << endl;
314       return 0;
315     }
316     if (arrstart == ARRAYSTART64) return getArrDesc64(xdrs); //as the rest is specially coded on 8 bytes.
317 
318     if (!xdr_int32_t(xdrs, &UnknownLong)) return NULL;
319     int32_t nbytes;
320     if (!xdr_int32_t(xdrs, &nbytes)) return NULL;
321     int32_t nEl;
322     if (!xdr_int32_t(xdrs, &nEl)) return NULL;
323     int32_t nDims;
324     if (!xdr_int32_t(xdrs, &nDims)) return NULL;
325     if (!xdr_int32_t(xdrs, &UnknownLong)) return NULL;
326     if (!xdr_int32_t(xdrs, &UnknownLong)) return NULL;
327     int32_t nmax;
328     if (!xdr_int32_t(xdrs, &nmax)) return NULL;
329     if (DEBUG_SAVERESTORE) cerr << "nbytes:" << nbytes << " ,nEl:" << nEl << ", nDims:" << nDims<<" ";
330     int32_t dims[nmax];
331     if (!xdr_vector(xdrs, (char*) dims, nmax, sizeof (int32_t), (xdrproc_t) xdr_int32_t)) return NULL;
332     SizeT k = dims[0];
333     dimension* theDim = new dimension(k);
334     for (int i = 1; i < nmax; ++i)
335     {
336       k = dims[i];
337       *theDim << k;
338     }
339     theDim->Purge();
340     if (DEBUG_SAVERESTORE)    cerr<<*theDim<<endl;
341     return theDim;
342   }
343 
writeArrDesc64(XDR * xdrs,BaseGDL * var)344   void writeArrDesc64(XDR* xdrs, BaseGDL* var) {
345     int32_t arrstart=ARRAYSTART64;
346     xdr_int32_t(xdrs, &arrstart);
347     //very important:
348     int64_t typeLength=sizeOfType[var->Type()];
349     if (var->Type()==GDL_STRING) typeLength=(var->NBytes()/var->N_Elements())-1;
350     if (typeLength==0) typeLength=(var->NBytes());
351     xdr_int64_t(xdrs, &typeLength);
352     int64_t nbytes=var->NBytes();
353     xdr_int64_t(xdrs, &nbytes);
354     int64_t nEl=var->N_Elements();
355     xdr_int64_t(xdrs, &nEl);
356     int32_t nDims=var->Rank();
357     if (nDims==0 && var->Type()==GDL_STRUCT ) nDims=1;
358     xdr_int32_t(xdrs, &nDims);
359     int32_t UnknownLong=0;
360     xdr_int32_t(xdrs, &UnknownLong);
361     xdr_int32_t(xdrs, &UnknownLong);
362     int64_t nmax=8;
363     int64_t dims[nmax];
364     for (int i=0; i < nmax; ++i) dims[i]=1; //yes.
365     // not written xdr_int32_t(xdrs, &nmax);
366     for (int i=0; i < nDims; ++i) if (var->Dim(i) > 0) dims[i]=var->Dim(i);
367     if (DEBUG_SAVERESTORE) std::cerr<<var->Dim()<<std::endl;
368     xdr_vector(xdrs, (char*) dims, nmax, sizeof (int64_t), (xdrproc_t) xdr_int64_t);
369   }
370 
writeArrDesc32(XDR * xdrs,BaseGDL * var)371   void writeArrDesc32(XDR* xdrs, BaseGDL* var) {
372     int32_t arrstart=ARRAYSTART;
373     xdr_int32_t(xdrs, &arrstart);
374     //very important:
375     int32_t typeLength=sizeOfType[var->Type()];
376     if (var->Type()==GDL_STRING) typeLength=(var->NBytes()/var->N_Elements())-1;
377     if (typeLength==0) typeLength=(var->NBytes());
378     xdr_int32_t(xdrs, &typeLength);
379     int32_t nbytes=var->NBytes();
380     xdr_int32_t(xdrs, &nbytes);
381     int32_t nEl=var->N_Elements();
382     xdr_int32_t(xdrs, &nEl);
383     int32_t nDims=var->Rank();
384     if (nDims==0 && var->Type()==GDL_STRUCT ) nDims=1;
385     xdr_int32_t(xdrs, &nDims);
386     int32_t UnknownLong=0;
387     xdr_int32_t(xdrs, &UnknownLong);
388     xdr_int32_t(xdrs, &UnknownLong);
389     int32_t nmax=8;
390     int32_t dims[nmax];
391     for (int i=0; i < nmax; ++i) dims[i]=1; //yes.
392     xdr_int32_t(xdrs, &nmax);
393     for (int i=0; i < nDims; ++i) if (var->Dim(i) > 0) dims[i]=var->Dim(i);
394     if (DEBUG_SAVERESTORE) std::cerr<<var->Dim()<<std::endl;
395     xdr_vector(xdrs, (char*) dims, nmax, sizeof (int32_t), (xdrproc_t) xdr_int32_t);
396   }
397 
writeArrDesc(XDR * xdrs,BaseGDL * var)398   void writeArrDesc(XDR* xdrs, BaseGDL* var) {
399     //very important check total size and switch if size is >2GO.
400     //Warning do not use a SizeT as typelength as this does not like negative values returned by for 0 length strings
401     DLong64 typeLength=sizeOfType[var->Type()];
402     if (var->Type()==GDL_STRING) typeLength=(var->NBytes()/var->N_Elements());
403     SizeT nEl=var->N_Elements();
404     if (nEl*typeLength > 2000000000ULL) writeArrDesc64(xdrs,var); else writeArrDesc32(xdrs,var);
405   }
406 
defineCommonBlock(EnvT * e,XDR * xdrs,int verboselevel)407   int defineCommonBlock(EnvT* e, XDR* xdrs, int verboselevel) {
408     int32_t ncommonvars;
409     if (!xdr_int32_t(xdrs, &ncommonvars)) return 0;
410     char* commonname = 0;
411     if (!xdr_string(xdrs, &commonname, 2048)) return 0;
412     char* varnames[ncommonvars];
413     for (int i = 0; i < ncommonvars; ++i) varnames[i] = 0;
414     for (int i = 0; i < ncommonvars; ++i) if (!xdr_string(xdrs, &varnames[i], 2048)) return 0;
415 
416     if (verboselevel>1){
417       cerr << "Common " << commonname << " consists of the following variables:" << endl;
418       for (int i = 0; i < ncommonvars; ++i) cerr << varnames[i] << ",";
419       cerr << endl;
420     }
421     //populate common block
422 
423     //behaviour is: if one of these variables already exist, the whole common is not defined, and
424     //the common variables become just normal variables:
425 
426     EnvStackT& callStack = e->Interpreter()->CallStack();
427     int32_t curlevnum = callStack.size();
428     DSubUD* pro = static_cast<DSubUD*> (callStack[curlevnum - 1]->GetPro());
429 
430     for (int i = 0; i < ncommonvars; ++i)
431     {
432       std::string varName = varnames[i];
433       if (pro->FindVar(varName) >= 0)
434       {
435         Message(varName + " is already defined with a conflicting definition.");
436         return 1; //this should not produce an error.
437       }
438     }
439 
440     std::string commonName = commonname;
441 
442     if (pro->Common(commonName) == NULL)
443     { //does not exist: create
444       DCommon* newCommon = new DCommon(commonName);
445       pro->AddCommon(newCommon);
446       if (verboselevel>0) Message("Restored common block: " + commonName);
447     }
448     commonName.clear();
449     DCommonBase* currentcommon = pro->Common(std::string(commonname));
450     for (int i = 0; i < ncommonvars; ++i)
451     {
452       std::string varName = varnames[i];
453       currentcommon->AddVar(varName);
454       varName.clear();
455     }
456 
457     return 1;
458   }
459 
getDStruct(EnvT * e,XDR * xdrs,dimension * inputdims,bool & isObjStruct)460   DStructGDL* getDStruct(EnvT* e, XDR* xdrs, dimension* inputdims, bool &isObjStruct) {
461     isObjStruct=false;
462     int32_t structstart;
463     if (!xdr_int32_t(xdrs, &structstart)) return NULL;
464     if (structstart != STRUCTSTART)
465     {
466       cerr << "structure is not a structure! abort." << endl;
467       return NULL;
468     }
469     char* structname = 0;
470     if (!xdr_string(xdrs, &structname, 2048)) return NULL;
471     int32_t structure_def_flags;
472     if (!xdr_int32_t(xdrs, &structure_def_flags)) return NULL;
473     bool ispredef = false; //ispredef means that this struct has been already defined inside the save file,
474     // but its definition may clash with an existing definition already made in GDL previous to the RESTORE command.
475     if (structure_def_flags & 0x01)
476     {
477       ispredef = true;
478     }
479     bool unknown_flag = false;
480     bool is_super = false;
481     bool inherits = false;
482     if (structure_def_flags & 0x08)
483     {
484       unknown_flag = true;
485     }
486     if (structure_def_flags & 0x02)
487     {
488       inherits = true;
489     }
490     if (structure_def_flags & 0x04)
491     {
492       is_super = true;
493     }
494 
495     if (is_super || inherits) isObjStruct=true;
496     if (DEBUG_SAVERESTORE) {
497      if (isObjStruct) {
498        cerr<<std::hex<<structure_def_flags<<std::dec<<std::endl;
499        cerr << "Object name:\"" << structname << "\"";
500        if (inherits) cerr << ", inherits";
501        if (is_super) cerr << ", is a Superclass";
502      } else {
503        cerr << "Structure name:\"" << structname << "\"";
504      }
505      if (unknown_flag) cerr << ", is tagged by unknown flag 0x08";
506      if (ispredef) cerr << ", is already defined";
507      cerr<<endl;
508     }
509 
510     int32_t ntags;
511     if (!xdr_int32_t(xdrs, &ntags)) return NULL;
512     int32_t struct_nbytes;
513     if (!xdr_int32_t(xdrs, &struct_nbytes)) return NULL;
514     //if predef == 1 this ends the Struct_desc, meaning that the definition of such a
515     //structure has already been presented and we should reuse it.
516     // otherwise, we define the structure using the following entries:
517     if (ispredef) {
518       std::string name = std::string(structname);
519       //beautify
520       assert(name != "$truct") ; // named struct
521         name = StrUpCase(name);
522       if( name == "IDL_OBJECT") name = GDL_OBJECT_NAME; // replacement also done in GDLParser
523       if( name == "IDL_CONTAINER") name = GDL_CONTAINER_NAME; // replacement also done in GDLParser
524       DStructDesc* desc = e->Interpreter()->GetStruct(name, e->CallingNode()); //will throw if does not exist.
525       return new DStructGDL(desc, *inputdims);
526 
527     } else {
528 
529       //definition of a new structure or class. Structure, if named, may be different from already existing (prior to RESTORE): clash!
530       //this is why the "RELAXED" option of RESTORE exists.
531       // Besides, if we create an Object, (a named structure) it is necessary to check wether it can be defined using XXXX__define.pro
532       // and if this definition is in accordance with the structure defined here, i.e., in the save file.
533       //TAG_DESC repated ntags times:
534       int32_t tag_typecode[ntags];
535       char* tag_name[ntags];
536       int32_t tag_flag[ntags];
537       int32_t tag_offset[ntags];
538       for (int i = 0; i < ntags; ++i) tag_name[i] = 0;
539       for (int i = 0; i < ntags; ++i)
540       {
541         //TAG_DESC:
542         if (!xdr_int32_t(xdrs, &tag_offset[i])) break;
543         if (!xdr_int32_t(xdrs, &tag_typecode[i])) break;
544         if (!xdr_int32_t(xdrs, &tag_flag[i])) break;
545       }
546       for (int i = 0; i < ntags; ++i)
547       {
548         //TAGNAMES x NTAGS:
549         if (!xdr_string(xdrs, &tag_name[i], 2048)) break;
550       }
551 
552       int32_t narrays = 0;
553       int32_t nstructs = 0;
554       for (int i = 0; i < ntags; ++i) if (tag_flag[i] & 0x20) nstructs++;
555       for (int i = 0; i < ntags; ++i) if (tag_flag[i] & 0x04) narrays++;
556       dimension * tagdimensions[narrays + 1]; //Always >0: FIXME IF MEMORY LEAK!
557       //if there are any tag flags indicating the tag is an array, read the ARRDESC
558       for (int i = 0; i < narrays; ++i)
559       {
560         tagdimensions[i] = getArrDesc(xdrs);
561         if (tagdimensions[i] == NULL) return NULL;
562       }
563 
564       std::string stru_name = std::string(structname);
565       if( stru_name == "IDL_OBJECT") stru_name = GDL_OBJECT_NAME; // replacement also done in GDLParser
566       if( stru_name == "IDL_CONTAINER") stru_name = GDL_CONTAINER_NAME; // replacement also done in GDLParser
567       DStructDesc* stru_desc=NULL;
568       DStructDesc* ref_desc=NULL;
569       bool checkStruct=false;
570       //take care of named structures. Here is also where one should try to create an object, since, if it exists, its structure must be
571       //compared with the current one.
572       if (stru_name.length() > 0 && stru_name[0] != '$')
573       {
574         if (isObjStruct) { //create a dummy object of this name, remove it.
575           DString objName=StrUpCase(stru_name);
576           DStructDesc* objDesc;
577           try {
578             objDesc = e->Interpreter()->GetStruct(objName, e->CallingNode());
579             DStructGDL* objStruct = new DStructGDL(objDesc, dimension(1));
580             DObj objID = e->NewObjHeap(1, objStruct); // owns objStruct
581             DObjGDL* newObj = new DObjGDL(objID); // the object
582             //calling the INIT function seems to intrusive.
583 //            try {
584 //              // call INIT function
585 //              DFun* objINIT = objDesc->GetFun("INIT");
586 //              if (objINIT != NULL) {
587 //                StackGuard<EnvStackT> guard(e->Interpreter()->CallStack());
588 //
589 //                // morph to obj environment and push it onto the stack again
590 //                e->PushNewEnvUD(objINIT, 1, &newObj);
591 //
592 //                BaseGDL* res = e->Interpreter()->call_fun(objINIT->GetTree());
593 //                GDLDelete(res);
594 //              }
595 //            } catch (...) {
596 //            }
597             e->FreeObjHeap(objID); // delete objStruct
598             GDLDelete(newObj);
599           } catch (...) {
600             if (DEBUG_SAVERESTORE) std::cerr << stru_name<<": is NOT a known Object."<<endl;
601           }
602         }
603         stru_desc = FindInStructList(structList, stru_name);
604 
605         if (stru_desc == NULL)
606         {
607           stru_desc = new DStructDesc(stru_name);
608           structList.push_back(stru_desc);
609         } else {
610           if (DEBUG_SAVERESTORE) cerr<<stru_name<<": is a known Object."<<endl;
611           checkStruct=true;
612           ref_desc=stru_desc;
613           stru_desc=new DStructDesc("$truct"); //make it anonymous, test if equality at end!
614         }
615       } else stru_desc=new DStructDesc("$truct");
616       //summary & tag population:
617       for (int i = 0, j = 0, k = 0; i < ntags; ++i)
618       {
619         //reserved a dimension
620         dimension pardim = dimension();
621 
622         if (tag_flag[i] & 0x04)
623         {
624           // modify pardim and push index;
625           pardim = *(tagdimensions[j++]); //memory leak?
626         }
627 
628         switch (tag_typecode[i]) {
629           case GDL_BYTE: //	Byte
630           {
631             SpDByte entry(pardim);
632             stru_desc->AddTag(tag_name[i], &entry);
633           }
634             break;
635 
636           case GDL_INT: //	16-bit Integer
637           {
638             SpDInt entry(pardim);
639             stru_desc->AddTag(tag_name[i], &entry);
640           }
641             break;
642 
643           case GDL_LONG: //	32-bit Long Integer
644           {
645             SpDLong entry(pardim);
646             stru_desc->AddTag(tag_name[i], &entry);
647           }
648             break;
649 
650           case GDL_FLOAT: //	32-bit Floating Point Number
651           {
652             SpDFloat entry(pardim);
653             stru_desc->AddTag(tag_name[i], &entry);
654           }
655             break;
656 
657           case GDL_DOUBLE: //	64-bit Floating Point Number
658           {
659             SpDDouble entry(pardim);
660             stru_desc->AddTag(tag_name[i], &entry);
661           }
662             break;
663 
664           case GDL_COMPLEX: //	Complex Floating Point Number (32-bits each)
665           {
666             SpDComplex entry(pardim);
667             stru_desc->AddTag(tag_name[i], &entry);
668           }
669             break;
670           case GDL_STRING: //	String
671           {
672             SpDString entry(pardim);
673             stru_desc->AddTag(tag_name[i], &entry);
674           }
675             break;
676           case GDL_STRUCT: //	Structure (never a scalar)
677           {
678             bool dummy;
679             DStructGDL* parStruct = getDStruct(e, xdrs, &pardim, dummy);
680             if (parStruct == NULL) return NULL;
681             stru_desc->AddTag(tag_name[i], parStruct);
682           }
683             break;
684 
685           case GDL_COMPLEXDBL: //	Complex Floating Point Number (64-bits each)
686           {
687             SpDComplexDbl entry(pardim);
688             stru_desc->AddTag(tag_name[i], &entry);
689           }
690             break;
691 
692           case GDL_PTR: //	Heap Pointer
693           {
694             DPtrGDL entry(pardim);
695             stru_desc->AddTag(tag_name[i], &entry);
696           }
697             break;
698           case GDL_OBJ: //	Object Reference (not supported by CMSVLIB)
699           {
700             DObjGDL entry(pardim);
701             stru_desc->AddTag(tag_name[i], &entry);
702           }
703             break;
704           case GDL_UINT: //	16-bit Unsigned Integer
705           {
706             SpDUInt entry(pardim);
707             stru_desc->AddTag(tag_name[i], &entry);
708           }
709             break;
710 
711           case GDL_ULONG: //	32-bit Unsigned Integer
712           {
713             SpDULong entry(pardim);
714             stru_desc->AddTag(tag_name[i], &entry);
715           }
716             break;
717 
718           case GDL_LONG64: //	64-bit Integer
719           {
720             SpDLong64 entry(pardim);
721             stru_desc->AddTag(tag_name[i], &entry);
722           }
723             break;
724 
725           case GDL_ULONG64: //	64-bit Unsigned Integer
726           {
727             SpDULong64 entry(pardim);
728             stru_desc->AddTag(tag_name[i], &entry);
729           }
730             break;
731 
732           default: //	0 ? Undefined (not allowed)
733             if (DEBUG_SAVERESTORE) cerr << "Should not happen: undefined typecode tag in getDStruct()." << endl;
734             break;
735         }
736 
737 
738 
739       }
740 
741       //Then there should be CLASSNAME if INHERITS or IS_SUPER
742       if (isObjStruct || is_super)
743       {
744         char* classname = 0;
745         if (!xdr_string(xdrs, &classname, 2048)) return NULL;
746         if (DEBUG_SAVERESTORE) cerr << "CLASSNAME: \"" << classname << "\"" << endl;
747         //NSUPCLASSES:
748         int32_t nsupclasses = 0;
749         if (!xdr_int32_t(xdrs, &nsupclasses)) return NULL;
750         if (DEBUG_SAVERESTORE)  cerr << "NSUPCLASSES=" << nsupclasses << endl;
751         if (nsupclasses > 0)
752         {
753           for (int i = 0; i < nsupclasses; ++i)
754           {
755             char* supclassname = 0;
756             if (!xdr_string(xdrs, &supclassname, 2048)) return NULL;
757             if (DEBUG_SAVERESTORE) cerr << "SUPCLASSNAME " << i << ": " << supclassname << endl;
758           }
759           for (int i = 0; i < nsupclasses; ++i)
760           {
761             //define all parent classes in objheap.
762             DStructGDL* superclass = NULL;
763             bool dummy=false;
764             superclass = getDStruct(e, xdrs, new dimension(1), dummy); // will define the class as an object.
765             if (superclass) stru_desc->AddParentListOnly(superclass->Desc());
766 //            if (isObjStruct)  {
767 //             DPtr ptr= e->NewObjHeap(1, static_cast<DStructGDL*>(superclass));
768 //            }
769           }
770         }
771       }
772       if (checkStruct)
773       {
774         try
775         {
776           ref_desc->AssureIdentical(stru_desc);
777           stru_desc=ref_desc; //OK, switch back.
778         }        catch (GDLException& ex)
779         {
780           e->Throw("Structure not restored due to conflict with existing definition: "+stru_name);
781           return NULL;
782         }
783       }
784       return new DStructGDL(stru_desc, *inputdims);
785     }
786   }
787 
writeStructDesc(XDR * xdrs,DStructGDL * var,bool isObject=false,bool is_super=false)788   void writeStructDesc(XDR* xdrs, DStructGDL* var, bool isObject=false, bool is_super=false) {
789     DStructDesc* str=var->Desc();
790     int32_t structstart=STRUCTSTART;
791     xdr_int32_t(xdrs, &structstart);
792     const char* structname = str->Name().c_str();
793     if (DEBUG_SAVERESTORE && isObject) cerr << "Writing Object \""<< str->Name() <<"\" @"<< var << endl;
794     if (DEBUG_SAVERESTORE && !isObject) cerr << "Writing Structure \""<< str->Name() <<"\" @"<< var << endl;
795     //predef: struct name is known
796     bool ispredef = (str->Name() != "$truct"); // named struct
797     //void name if anonymous struct!
798     if (!ispredef) {
799       std::string nullstr=""; char* voidchar=(char*)nullstr.c_str();
800       xdr_string(xdrs,(char**)&voidchar, 0);
801       }
802     else xdr_string(xdrs, (char**) &structname, str->Name().size());
803     //now, did we already define this named structure in the file?
804     //if no, add name to list of defined (for further use) and remove is_predef (to write it).
805     if (ispredef) {
806       bool found=false;
807       for (SizeT i=0; i<predeflist.size(); ++i)
808       {
809         if (predeflist[i]==str->Name()) {found=true; break;}
810       }
811       if (!found) {
812         predeflist.push_back(str->Name());
813         ispredef=false;
814       }
815     }
816     //flags.
817 
818     int32_t structure_def_flags=0;
819     if (ispredef) structure_def_flags |= 0x01;
820     if (isObject) structure_def_flags |= 0x08; //it is a CLASS
821     if (isObject) structure_def_flags |= 0x02;
822     if (is_super) structure_def_flags |= 0x04;
823     xdr_int32_t(xdrs, &structure_def_flags);
824     int32_t ntags=str->NTags();
825     xdr_int32_t(xdrs, &ntags);
826     int32_t struct_nbytes=(ispredef)?str->NBytes():0;
827     xdr_int32_t(xdrs, &struct_nbytes);
828     //if predef == 1  this ends the Struct_desc, meaning that we have already presented this structure.
829     if (ispredef) return;
830     //TAG_DESC repated ntags times:
831     int32_t tag_typecode[ntags];
832     char* tag_name[ntags];
833     int32_t tag_flag[ntags];
834     int32_t tag_offset[ntags];
835     int32_t byteoff=0;
836     for (int i = 0; i < ntags; ++i) tag_name[i] = (char*) str->TagName(i).c_str();
837     for (SizeT i = 0; i < ntags; ++i) { tag_offset[i] = byteoff; byteoff+=var->GetTag(i)->NBytes();} //probably OK
838     for (int i = 0; i < ntags; ++i)
839     {
840       tag_flag[i]=0;
841       if (var->GetTag(i,0)->Rank()> 0) tag_flag[i]|=0x04;
842       switch (var->GetTag(i,0)->Type()) {
843         case GDL_STRUCT:
844           tag_flag[i] |=0x20;
845         default:
846           tag_typecode[i] = var->GetTag(i,0)->Type();
847       }
848     }
849     for (int i = 0; i < ntags; ++i)
850     {
851       //TAG_DESC:
852       xdr_int32_t(xdrs, &tag_offset[i]);
853       xdr_int32_t(xdrs, &tag_typecode[i]);
854       xdr_int32_t(xdrs, &tag_flag[i]);
855     }
856     for (int i = 0; i < ntags; ++i)
857     {
858       //TAGNAMES x NTAGS:
859       xdr_string(xdrs, &tag_name[i], strlen(tag_name[i]));
860     }
861     //ARRDESC x NARRAYS:
862     for (int i = 0; i < ntags; ++i)
863     {
864       if (tag_flag[i] & 0x04) writeArrDesc(xdrs, var->GetTag(i));
865     }
866     //STRUCTDESC x NARRAYS:
867     for (int i = 0; i < ntags; ++i)
868     {
869       if (tag_flag[i] & 0x20) writeStructDesc(xdrs, static_cast<DStructGDL*>(var->GetTag(i)));
870     }
871     //TBD: CLASSES
872     if (isObject) {
873       xdr_string(xdrs, (char**) &structname, str->Name().size()); //CLASSNAME
874       if (DEBUG_SAVERESTORE) std::cerr << "Object CLASS is \""<< str->Name() << std::endl;
875       std::vector< std::string> pNames;
876       str->GetParentNames(pNames);
877 //TBD: get super classes and write structs accordingly.
878       int32_t nsupclasses=pNames.size(); //do they exist???
879       for (int i=0 ; i< nsupclasses;++i) {
880           DStructGDL* parent = new DStructGDL( pNames[i]);
881           if (parent == NULL) nsupclasses--;
882           GDLDelete(parent);
883         }
884       xdr_int32_t(xdrs, &nsupclasses);
885       if (nsupclasses > 0) {
886         DStructGDL* parents[nsupclasses];
887         // AC not OK on OSX12: std::string pnames[nsupclasses];
888 	string *pnames = new string[nsupclasses];
889         int k=0;
890         for (int i=0 ; i< pNames.size();++i) {
891           DStructGDL* parent = new DStructGDL( pNames[i]);
892           if (parent != NULL) {
893             pnames[k]=pNames[i];
894             parents[k]=parent;
895             if (DEBUG_SAVERESTORE) std::cerr << "     SUPCLASS:  \""<< pNames[k] << std::endl;
896             k++;
897           }
898         }
899         for (int i=0 ; i< nsupclasses;++i) {char* str=(char*)(pnames[i].c_str()); xdr_string(xdrs, &str,pnames[i].size());}
900         for (int i=0 ; i< nsupclasses;++i) {
901             DStructGDL* parent = parents[i];
902             Guard<DStructGDL> parent_guard(parent);
903             writeStructDesc(xdrs, parent, true, true);
904           }
905 	delete [] pnames;
906 	}
907     }
908   }
909 
910 
getVariable(EnvT * e,XDR * xdrs,int & isSysVar,bool & isObjStruct)911   BaseGDL* getVariable(EnvT* e, XDR* xdrs, int &isSysVar, bool &isObjStruct) {
912     bool isStructure = false;
913     bool isArray = false;
914     // start of TYPEDESC
915     // common for VARIABLE, SYSTEM_VARIABLE and HEAP_DATA:
916     // 1) TYPECODE
917     int32_t typecode;
918     if (!xdr_int32_t(xdrs, &typecode)) return NULL;
919     // 2) VARFLAGS
920     int32_t varflags;
921     if (!xdr_int32_t(xdrs, &varflags)) return NULL;
922 
923     if (varflags & 0x40) return NullGDL::GetSingleInstance(); //special !NULL variable, no variable content follows.
924 
925     if (varflags & 0x02) //defines a system variable.
926     {
927       isSysVar |= 0x02;
928 //           cerr << " system " << endl;
929     }
930     if (varflags & 0x01)
931     {
932       isSysVar |= 0x01;
933 //            cerr << " readonly " << endl;
934     }
935 
936 
937     if (varflags & 0x20)
938     {
939       isStructure = true; //may also be an array!
940     }
941     else if (varflags & 0x04)
942     {
943       isArray = true;
944     }
945     //This is not signaled in C. Marqwardt doc: a system variable has two supplemental int32 (0x04 and 0x02) here, that we skip.
946     if (isSysVar & 0x02)
947     {
948       int32_t dummy;
949       if (!xdr_int32_t(xdrs, &dummy)) return NULL;
950       if (!xdr_int32_t(xdrs, &dummy)) return NULL;
951     }
952     //we gonnna create a BaseGDL:
953 
954     BaseGDL* var;
955     dimension* dims;
956     // if ARRAY or STRUCTURE, Read ARRAY_DESC that follows:
957     if (isStructure)
958     {//if This was a Structure, it has an ARRAY_DESC plus a STRUCT_DESC
959       dims = getArrDesc(xdrs);
960       if (dims == NULL) return NULL;
961       var = getDStruct(e, xdrs, dims, isObjStruct);
962     } else
963     {
964       if (isArray)
965       { //and NOT a structure...
966         dims = getArrDesc(xdrs);
967         if (dims == NULL) return NULL;
968       } else
969       { //normal plain variable
970         dims = new dimension();
971       }
972       switch (typecode) {
973         case GDL_BYTE: //	Byte
974           var = new DByteGDL(*dims);
975           break;
976         case GDL_INT: //	16-bit Integer
977           var = new DIntGDL(*dims);
978           break;
979         case GDL_LONG: //	32-bit Long Integer
980           var = new DLongGDL(*dims);
981           break;
982         case GDL_FLOAT: //	32-bit Floating Point Number
983           var = new DFloatGDL(*dims);
984           break;
985         case GDL_DOUBLE: //	64-bit Floating Point Number
986           var = new DDoubleGDL(*dims);
987           break;
988         case GDL_COMPLEX: //	Complex Floating Point Number (32-bits each)
989           var = new DComplexGDL(*dims);
990           break;
991         case GDL_STRING: //	String
992           var = new DStringGDL(*dims);
993           break;
994         case GDL_STRUCT: //	Structure (never a scalar)
995           cerr << "Should not happen: struct" << endl;
996           break;
997         case GDL_COMPLEXDBL: //	Complex Floating Point Number (64-bits each)
998           var = new DComplexDblGDL(*dims);
999           break;
1000         case GDL_PTR: //	Heap Pointer
1001           var = new DPtrGDL(*dims);
1002           break;
1003         case GDL_OBJ: //	Object Reference (not supported by CMSVLIB)
1004           var = new DObjGDL(*dims);
1005           break;
1006         case GDL_UINT: //	16-bit Unsigned Integer
1007           var = new DUIntGDL(*dims);
1008           break;
1009         case GDL_ULONG: //	32-bit Unsigned Integer
1010           var = new DULongGDL(*dims);
1011           break;
1012         case GDL_LONG64: //	64-bit Integer
1013           var = new DLong64GDL(*dims);
1014           break;
1015         case GDL_ULONG64: //	64-bit Unsigned Integer
1016           var = new DULong64GDL(*dims);
1017           break;
1018         default: //	0 ? Undefined (not allowed)
1019           var = NullGDL::GetSingleInstance(); //          cerr <<"Should not happen"<<endl;
1020           break;
1021       }
1022     }
1023 
1024     return var;
1025   }
1026 
fillVariableData(XDR * xdrs,BaseGDL * var)1027   void fillVariableData(XDR* xdrs, BaseGDL* var) {
1028     u_int nEl = var->N_Elements();
1029     switch (var->Type()) {
1030 
1031       case GDL_BYTE:
1032       {
1033         char* addr = (char*) var->DataAddr();
1034         if (!xdr_bytes(xdrs, &addr, &nEl, nEl)) cerr << "error GDL_BYTE" << endl;
1035       }
1036         break;
1037       case GDL_INT:
1038       {
1039         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DInt), (xdrproc_t) xdr_int16_t)) cerr << "error GDL_INT" << endl;
1040       }
1041         break;
1042       case GDL_UINT:
1043       {
1044         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DUInt), (xdrproc_t) xdr_uint16_t)) cerr << "error GDL_UINT" << endl;
1045       }
1046         break;
1047       case GDL_LONG:
1048       {
1049         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (int32_t), (xdrproc_t) xdr_int32_t)) cerr << "error GDL_LONG" << endl;
1050       }
1051         break;
1052       case GDL_ULONG:
1053       {
1054         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DULong), (xdrproc_t) xdr_uint32_t)) cerr << "error GDL_ULONG" << endl;
1055       }
1056         break;
1057       case GDL_LONG64:
1058       {
1059         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DLong64), (xdrproc_t) xdr_int64_t)) cerr << "error GDL_LONG64" << endl;
1060       }
1061         break;
1062       case GDL_ULONG64:
1063       {
1064         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DULong64), (xdrproc_t) xdr_uint64_t)) cerr << "error GDL_ULONG64" << endl;
1065       }
1066         break;
1067       case GDL_FLOAT:
1068       {
1069         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DFloat), (xdrproc_t) xdr_float)) cerr << "error GDL_FLOAT" << endl;
1070       }
1071         break;
1072       case GDL_DOUBLE:
1073       {
1074         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DDouble), (xdrproc_t) xdr_double)) cerr << "error GDL_DOUBLE" << endl;
1075       }
1076         break;
1077       case GDL_COMPLEX:
1078       {
1079         u_int nEl2 = nEl * 2;
1080         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl2, sizeof (DFloat), (xdrproc_t) xdr_float)) cerr << "error GDL_COMPLEX" << endl;
1081       }
1082         break;
1083       case GDL_COMPLEXDBL:
1084       {
1085         u_int nEl2 = nEl * 2;
1086         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl2, sizeof (DDouble), (xdrproc_t) xdr_double)) cerr << "error GDL_COMPLEXDBL" << endl;
1087       }
1088         break;
1089       case GDL_STRING:
1090       {
1091         for (SizeT i = 0; i < nEl; ++i)
1092         {
1093           int32_t length;
1094           if (!xdr_int32_t(xdrs, &length)) cerr << "error reading string length" << endl;
1095           if (length > 0)
1096           {
1097             char* chars = 0;
1098             if (!xdr_string(xdrs, &chars, length)) cerr << "error getting string" << endl;
1099             (*static_cast<DStringGDL*> (var))[i].assign(chars);
1100           }
1101         }
1102       }
1103         break;
1104       case GDL_STRUCT:
1105       {
1106         DStructGDL* str = static_cast<DStructGDL*> (var);
1107         SizeT nTags = str->Desc()->NTags();
1108         for (SizeT ix = 0; ix < nEl; ++ix) for (SizeT t = 0; t < nTags; ++t) fillVariableData(xdrs, str->GetTag(t, ix));
1109         break;
1110       }
1111       case GDL_PTR:
1112       {
1113         int32_t heapNumber[nEl];
1114         DPtrGDL* ptr = static_cast<DPtrGDL*> (var);
1115         for (SizeT ix = 0; ix < nEl; ++ix) xdr_int32_t(xdrs, &(heapNumber[ix]));
1116         for (SizeT ix = 0; ix < nEl; ++ix)
1117         {
1118           DPtr heapptr = heapIndexMapRestore.find(heapNumber[ix])->second.second;
1119            (*ptr)[ix] = heapptr;
1120            GDLInterpreter::IncRef(heapptr);
1121           if (DEBUG_SAVERESTORE) std::cerr<<"PTR at #"<<heapNumber[ix]<<" restored at "<<heapptr<<std::endl;
1122         }
1123         break;
1124       }
1125       case GDL_OBJ:
1126       {
1127         int32_t heapNumber[nEl];
1128         DObjGDL* ptr = static_cast<DObjGDL*> (var);
1129         for (SizeT ix = 0; ix < nEl; ++ix) xdr_int32_t(xdrs, &(heapNumber[ix]));
1130         for (SizeT ix = 0; ix < nEl; ++ix)
1131         {
1132           DObj heapptr = heapIndexMapRestore.find(heapNumber[ix])->second.second;
1133             (*ptr)[ix] = heapptr;
1134            GDLInterpreter::IncRefObj(heapptr);
1135           if (DEBUG_SAVERESTORE) std::cerr<<"OBJ at #"<<heapNumber[ix]<<" restored at "<<heapptr<<std::endl;
1136         }
1137         break;
1138       }
1139       default: assert(false);
1140     }
1141   }
1142 
writeVariableData(XDR * xdrs,BaseGDL * var)1143   void writeVariableData(XDR* xdrs, BaseGDL* var) {
1144     u_int nEl = var->N_Elements();
1145     switch (var->Type()) {
1146 
1147       case GDL_BYTE:
1148       {
1149         char* addr = (char*) var->DataAddr();
1150         if (!xdr_bytes(xdrs, &addr, &nEl, nEl)) cerr << "error GDL_BYTE" << endl;
1151       }
1152         break;
1153       case GDL_INT:
1154       {
1155         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DInt), (xdrproc_t) xdr_int16_t)) cerr << "error GDL_INT" << endl;
1156       }
1157         break;
1158       case GDL_UINT:
1159       {
1160         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DUInt), (xdrproc_t) xdr_uint16_t)) cerr << "error GDL_UINT" << endl;
1161       }
1162         break;
1163       case GDL_LONG:
1164       {
1165         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (int32_t), (xdrproc_t) xdr_int32_t)) cerr << "error GDL_LONG" << endl;
1166       }
1167         break;
1168       case GDL_ULONG:
1169       {
1170         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DULong), (xdrproc_t) xdr_uint32_t)) cerr << "error GDL_ULONG" << endl;
1171       }
1172         break;
1173       case GDL_LONG64:
1174       {
1175         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DLong64), (xdrproc_t) xdr_int64_t)) cerr << "error GDL_LONG64" << endl;
1176       }
1177         break;
1178       case GDL_ULONG64:
1179       {
1180         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DULong64), (xdrproc_t) xdr_uint64_t)) cerr << "error GDL_ULONG64" << endl;
1181       }
1182         break;
1183       case GDL_FLOAT:
1184       {
1185         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DFloat), (xdrproc_t) xdr_float)) cerr << "error GDL_FLOAT" << endl;
1186       }
1187         break;
1188       case GDL_DOUBLE:
1189       {
1190         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl, sizeof (DDouble), (xdrproc_t) xdr_double)) cerr << "error GDL_DOUBLE" << endl;
1191       }
1192         break;
1193       case GDL_COMPLEX:
1194       {
1195         u_int nEl2 = nEl * 2;
1196         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl2, sizeof (DFloat), (xdrproc_t) xdr_float)) cerr << "error GDL_COMPLEX" << endl;
1197       }
1198         break;
1199       case GDL_COMPLEXDBL:
1200       {
1201         u_int nEl2 = nEl * 2;
1202         if (!xdr_vector(xdrs, (char*) var->DataAddr(), nEl2, sizeof (DDouble), (xdrproc_t) xdr_double)) cerr << "error GDL_COMPLEXDBL" << endl;
1203       }
1204         break;
1205       case GDL_STRING:
1206       {
1207         for (SizeT i = 0; i < nEl; ++i)
1208         {
1209           const char *chars=(*static_cast<DStringGDL*> (var))[i].c_str();
1210           int32_t length=(*static_cast<DStringGDL*> (var))[i].length();
1211           if (!xdr_int32_t(xdrs, &length)) cerr << "error writing string length" << endl;
1212           if (length > 0) if (!xdr_string(xdrs, (char**)&chars, length)) cerr << "error writing string" << endl;
1213         }
1214       }
1215         break;
1216       case GDL_STRUCT:
1217       {
1218         DStructGDL* str = static_cast<DStructGDL*> (var);
1219         SizeT nTags = str->Desc()->NTags();
1220         for (SizeT ix = 0; ix < nEl; ++ix) for (SizeT t = 0; t < nTags; ++t) writeVariableData(xdrs, str->GetTag(t, ix));
1221         break;
1222       }
1223       case GDL_PTR:  //we must translate PTRs as they may have been scrambled in the writing of the heap indexes.
1224       {
1225         uint32_t heapNumber[nEl];
1226         for (SizeT i = 0; i < nEl; ++i)
1227         {
1228           heapNumber[i]=0;
1229           DPtr ptr = (*static_cast<DPtrGDL*>(var))[i];
1230           heapT::iterator it=heapIndexMapSave.find(ptr);
1231           if (it!=heapIndexMapSave.end()) heapNumber[i]=(*it).second;
1232         }
1233         if (!xdr_vector(xdrs, (char*) &heapNumber, nEl, sizeof (int32_t), (xdrproc_t) xdr_uint32_t)) cerr << "error PTR" << endl;
1234         break;
1235       }
1236       case GDL_OBJ:
1237       {
1238         uint32_t heapNumber[nEl];
1239         for (SizeT i = 0; i < nEl; ++i)
1240         {
1241           heapNumber[i]=0;
1242           DObj ptr = (*static_cast<DObjGDL*>(var))[i];
1243           heapT::iterator it=heapIndexMapSave.find(ptr);
1244           if (it!=heapIndexMapSave.end()) heapNumber[i]=(*it).second;
1245         }
1246         if (!xdr_vector(xdrs, (char*) &heapNumber, nEl, sizeof (int32_t), (xdrproc_t) xdr_uint32_t)) cerr << "error OBJ" << endl;
1247         break;
1248       }
1249       default: assert(false);
1250     }
1251   }
1252 
writeVariableHeader(XDR * xdrs,BaseGDL * var,bool isSysVar=false,bool readonly=false,bool isObject=false)1253   void writeVariableHeader(XDR* xdrs, BaseGDL* var, bool isSysVar=false, bool readonly=false, bool isObject=false) {
1254     if (DEBUG_SAVERESTORE)  std::cerr<<"Writing normal Variable Header, isObject="<<isObject<<std::endl;
1255     int32_t varflags=0;
1256     bool isStructure = (var->Type()==GDL_STRUCT);
1257     bool isArray = (var->Rank() > 0);
1258     // start of TYPEDESC
1259     // common for VARIABLE, SYSTEM_VARIABLE and HEAP_DATA:
1260     // 1) TYPECODE
1261     int32_t typecode=0;
1262     // special case for !NULL
1263     bool nullsize=false;
1264     if ( var->N_Elements() == 0 ) nullsize=true;
1265     typecode=var->Type(); //these are the same.
1266     xdr_int32_t(xdrs, &typecode);
1267     // 2) VARFLAGS
1268     if (isSysVar) varflags |= 0x02;
1269     if (readonly) varflags |= 0x01;
1270     if (isObject) varflags |= 0x34; if (isStructure) varflags |= 0x24; else if (isArray) varflags |= 0x04;
1271     if (nullsize) varflags=0x40;
1272     xdr_int32_t(xdrs, &varflags);
1273     if (nullsize) return;
1274     //This is not signaled in C. Marqwardt doc: a system variable has two supplemental int32 (0x04 and 0x02).
1275     if (isSysVar)
1276     {
1277       int32_t dummy;
1278       xdr_int32_t(xdrs, &dummy);
1279       xdr_int32_t(xdrs, &dummy);
1280     }
1281 
1282     // if ARRAY or STRUCTURE, write ARRAY_DESC that follows:
1283     if (isStructure||isArray) writeArrDesc(xdrs, var);
1284     if (isStructure) writeStructDesc(xdrs, static_cast<DStructGDL*>(var), isObject);
1285   }
1286 
writeNormalVariable(XDR * xdrs,std::string varName,BaseGDL * var,int varflags=0x0)1287   uint32_t writeNormalVariable(XDR *xdrs, std::string varName, BaseGDL* var, int varflags=0x0) {
1288     bool isSysVar=false;
1289     bool readonly=false;
1290     if (varflags & 0x02) //defines a system variable.
1291     {
1292       isSysVar = true;
1293     }
1294     if (varflags & 0x01)
1295     {
1296       readonly = true;
1297     }
1298     const char* varname=varName.c_str();
1299     uint32_t cur=writeNewRecordHeader(xdrs, isSysVar?SYSTEM_VARIABLE:VARIABLE);
1300     xdr_string(xdrs, (char**)&varname, 2048);
1301     if (var==NULL) return updateNewRecordHeader(xdrs, cur); //unexistent var
1302     if (DEBUG_SAVERESTORE)  std::cerr<<"Writing normal Variable "<<varName<<std::endl;
1303     writeVariableHeader(xdrs, var, isSysVar, readonly);
1304     // !NULL variable stops here since no data
1305     if (var->N_Elements() == 0) return updateNewRecordHeader(xdrs, cur);
1306     // varstat=7 to read data
1307     int32_t varstart = VARSTART;
1308     xdr_int32_t(xdrs, &varstart);
1309     writeVariableData(xdrs, var);
1310     return updateNewRecordHeader(xdrs, cur);
1311   }
1312 
writeHeapVariable(EnvT * e,XDR * xdrs,DPtr ptr,bool isObject=false)1313     uint32_t writeHeapVariable(EnvT* e, XDR *xdrs, DPtr ptr, bool isObject=false) {
1314     //what is passed is the list of existent heap positions occupied.
1315     //we  write only the ones that are actuall in , depending, the
1316     heapT::iterator itheap;
1317     bool unknown=false;
1318       itheap=heapIndexMapSave.find(ptr);
1319       if ( itheap==heapIndexMapSave.end() ) unknown=true;
1320     if (unknown) {
1321       if (DEBUG_SAVERESTORE) std::cerr<<"ignoring unused heap_index "<<ptr<<std::endl;
1322       return xdr_getpos(xdrs); //do nothing.
1323     }
1324 
1325     bool isSysVar=false;
1326     bool readonly=false;
1327     uint32_t cur=writeNewRecordHeader(xdrs, HEAP_DATA); //HEAP_DATA
1328     int32_t heap_index=ptr;
1329     if (DEBUG_SAVERESTORE) {
1330       if (isObject) {
1331         std::cerr << "write OBJ at heap_index " << heap_index << std::endl;
1332       } else {
1333         std::cerr << "write PTR at heap_index " << heap_index << std::endl;
1334       }
1335     }
1336     //rest is normally the same as for any other variable
1337     xdr_int32_t(xdrs, &heap_index);
1338     int32_t heap_type = 0x02;
1339     if (isObject) heap_type = 0x04;
1340     xdr_int32_t(xdrs, &heap_type);
1341 
1342     // start of TYPEDESC. Structures may be objects, in which case ptr.second is negative.
1343     BaseGDL* var;
1344     try{
1345     if (isObject) var=e->GetObjHeap(ptr); else var=e->GetHeap(ptr); //TRICK!
1346     } catch( GDLInterpreter::HeapException& hEx)
1347     {
1348       e->Throw("ID <"+i2s(ptr)+"> not found.");
1349     }
1350     if (var==NULL) return updateNewRecordHeader(xdrs, cur); //unexistent var
1351     if (DEBUG_SAVERESTORE) std::cerr<<var->TypeStr()<<" @ "<<std::hex<<var<<std::dec<<std::endl;
1352     writeVariableHeader(xdrs, var, isSysVar, readonly, isObject );
1353     // !NULL variable stops here since no data
1354     if (var->N_Elements() == 0) return updateNewRecordHeader(xdrs, cur);
1355     // varstat=7 to read data
1356     int32_t varstart = VARSTART;
1357     xdr_int32_t(xdrs, &varstart);
1358     writeVariableData(xdrs, var);
1359     return updateNewRecordHeader(xdrs, cur);
1360   }
restoreNormalVariable(EnvT * e,std::string varName,BaseGDL * ret)1361   void restoreNormalVariable(EnvT* e, std::string varName, BaseGDL* ret) {
1362     //write variable back
1363     EnvStackT& callStack = e->Interpreter()->CallStack();
1364     DLong curlevnum = callStack.size();
1365     DSubUD* pro = static_cast<DSubUD*> (callStack[curlevnum - 1]->GetPro());
1366     int nKey = pro->NKey();
1367     //    cout << "nKey:" << nKey << endl;
1368     //    cout << "nVar:" << nVar << endl;
1369     //    cout << pro->Name() << endl;
1370     int xI = pro->FindVar(varName);
1371     //    cout << "varName: " << varName << " xI: " << xI << endl;
1372     SizeT s;
1373     if (xI != -1)
1374     {
1375       s = xI;
1376       //      cout << "Found Already existing Var \""<< varName <<" s=" << s << endl;
1377       //the existing var is deleted (including heap if it pointed to heap values), and restored anew.
1378      GDLDelete( ((EnvT*) (callStack[curlevnum - 1]))->GetPar(s - nKey));
1379       ((EnvT*) (callStack[curlevnum - 1]))->GetPar(s - nKey) = ret;
1380 
1381     } else
1382     {
1383       BaseGDL** varPtr = pro->GetCommonVarPtr(varName);
1384       if (varPtr)
1385       {
1386         pro->ReplaceExistingCommonVar(varName, ret);
1387       } else
1388       {
1389         SizeT u = pro->AddVar(varName);
1390         s = callStack[curlevnum - 1]->AddEnv();
1391         //        cout << "AddVar u: " << u << endl;
1392         //        cout << "AddEnv s: " << s << endl;
1393         ((EnvT*) (callStack[curlevnum - 1]))->GetPar(s - nKey) = ret;
1394       }
1395     }
1396   }
1397 
restoreSystemVariable(EnvT * e,std::string sysVarNameFull,BaseGDL * ret,bool rdOnly=false)1398   void restoreSystemVariable(EnvT* e, std::string sysVarNameFull, BaseGDL* ret, bool rdOnly = false) {
1399     //more or less a copy of "DEFSYSV" code...
1400     if (sysVarNameFull.length() < 2 || sysVarNameFull[0] != '!')
1401     {
1402       Warning("Not restoring illegal system variable name: " + sysVarNameFull + ".");
1403       GDLDelete(ret);
1404       return;
1405     }
1406     // strip "!", uppercase
1407     DString sysVarName = StrUpCase(sysVarNameFull.substr(1));
1408 
1409     DVar* sysVar = FindInVarList(sysVarList, sysVarName);
1410 
1411     if (sysVar == NULL)
1412     {
1413       // define new
1414       DVar *newSysVar = new DVar(sysVarName, ret);
1415       sysVarList.push_back(newSysVar);
1416 
1417       // rdOnly is only set at the first definition
1418       if (rdOnly) sysVarRdOnlyList.push_back(newSysVar);
1419       return;
1420     }
1421 
1422     // else: re-set
1423     // make sure type and size are kept
1424     BaseGDL* oldVar = sysVar->Data();
1425     if (oldVar->Type() != ret->Type() || oldVar->N_Elements() != ret->N_Elements())
1426     {
1427       Message("Conflicting definition for " + sysVarNameFull + ".");
1428       GDLDelete(ret);
1429       return;
1430     }
1431 
1432     // if struct -> assure equal descriptors
1433     if (oldVar->Type() == GDL_STRUCT)
1434     {
1435       DStructGDL *oldStruct = static_cast<DStructGDL*> (oldVar);
1436       // types are same -> static cast
1437       DStructGDL *newStruct = static_cast<DStructGDL*> (ret);
1438 
1439       // note that IDL handles different structs more relaxed
1440       // ie. just the structure pattern is compared.
1441       if (*oldStruct->Desc() != *newStruct->Desc())
1442       {
1443         Warning("Conflicting definition for " + sysVarNameFull + ".");
1444         GDLDelete(ret);
1445         return;
1446       }
1447 
1448       DVar* sysVarRdOnly = FindInVarList(sysVarRdOnlyList, sysVarName);
1449       if (sysVarRdOnly != NULL)
1450       {
1451         // rdOnly set and is already rdOnly: do nothing
1452         if (rdOnly) return;
1453 
1454         // else complain
1455         Warning("Attempt to write to a readonly variable: " + sysVarNameFull + ".");
1456       } else
1457       {
1458         // not read only
1459         GDLDelete(oldVar);
1460         sysVar->Data() = ret;
1461       }
1462     }
1463   }
1464 
1465   //clever but very VERY VERY dirty trick not gentle with memory and full of leaks:
1466 
uncompress_trick(FILE * fid,XDR * xdrsmem,char * & expanded,DULong64 nextptr,DULong64 currentptr)1467   XDR* uncompress_trick(FILE* fid, XDR* xdrsmem, char* &expanded, DULong64 nextptr, DULong64 currentptr) {
1468     if (expanded!=NULL) free(expanded);
1469     uLong compsz = nextptr - currentptr;
1470     char* expandable = (char*) malloc(compsz);
1471     size_t retval = fread(expandable, 1, compsz, fid);
1472     int iloop = 1;
1473     uLong uncompsz;
1474     //of course one should never do like that. One should do as in gzstream.hpp...
1475     while (1)
1476     {
1477       uncompsz = 10 * iloop*compsz; //a default starting value...
1478       expanded = (char*) malloc(uncompsz);
1479       int retval = uncompress((Bytef *) expanded, &uncompsz, (Bytef *) expandable, compsz);
1480       if (retval == Z_OK) break; //ok length was sufficient
1481       free(expanded);
1482       if (retval != Z_BUF_ERROR) throw GDLException("fatal error when uncompressing data.");
1483       iloop++;
1484     }
1485     free(expandable);
1486     xdrmem_create(xdrsmem, NULL, 0, XDR_FREE);
1487     xdrmem_create(xdrsmem, expanded, uncompsz, XDR_DECODE);
1488     return xdrsmem;
1489   }
1490 
testSafety()1491   bool testSafety() {
1492     if (sizeof(int8_t) != sizeof(DByte)) return false;
1493     if (sizeof(int16_t) != sizeof(DInt)) return false;
1494     if (sizeof(int32_t) != sizeof(DLong)) return false;
1495     if (sizeof(int64_t) != sizeof(DLong64)) return false;
1496     if (sizeof(uint16_t) != sizeof(DUInt)) return false;
1497     if (sizeof(uint32_t) != sizeof(DULong)) return false;
1498     if (sizeof(uint64_t) != sizeof(DULong64)) return false;
1499     if (sizeof(double) != sizeof(DDouble)) return false;
1500     if (sizeof(float) != sizeof(DFloat)) return false;
1501     return true;
1502   }
1503 
1504   // new fast restore.
1505 
gdl_restore(EnvT * e)1506   void gdl_restore(EnvT* e) {
1507 
1508     // xdr() is used through all the following. I program here xdr to use the 8, 16, 32 and 64 bits length
1509     // types of GDL/IDL (BYTE,INT,LONG,LONG64). However, a risk exist in some architectures that the real length
1510      // of DInt, DLong etc is not really the expected one (16 and 32 respectively). So the following is a test on these
1511     // lengths. If the test fails, 1) GDL is false for this architecture and that needs to be reported and
1512     // 2) gdl_restore cannot work.
1513 
1514     if (safetyTested == false)
1515     {
1516       isSafe = testSafety();
1517       if (!isSafe) e->Throw("Severe: internal representation of integers in this version of GDL is wrong, please report. Aborting unsafe use of RESTORE.");
1518       safetyTested = true;
1519     }
1520     //if testSafety is correct, DLong and int32_t , DInt and int16_t etc have the same meaning.
1521 
1522     static int FILENAME = e->KeywordIx("FILENAME");
1523 
1524     static int VERBOSE = e->KeywordIx("VERBOSE");
1525     bool verbose = e->KeywordSet(VERBOSE);
1526     DLong verboselevel=(verbose?1:0);
1527     if (verbose) e->AssureLongScalarKW(VERBOSE,verboselevel);
1528 
1529     static int DESCRIPTION = e->KeywordIx("DESCRIPTION");
1530     bool hasDescription = e->KeywordPresent(DESCRIPTION);
1531     // AC 20200323 : we may have this keyword set but no value in the file : should return ""
1532     if (hasDescription)	e->SetKW(DESCRIPTION, new DStringGDL(""));
1533 
1534     //empty heap map by security.
1535     heapIndexMapRestore.clear();
1536 
1537     std::vector<Guard<BaseGDL>* > guardVector;
1538     //    std::vector<BaseGDL*> myObj;
1539     std::vector<std::pair<std::string, BaseGDL*> > variableVector;
1540     std::vector<std::pair<std::string, BaseGDL*> > systemVariableVector;
1541     std::vector<std::pair<std::string, BaseGDL*> > systemReadonlyVariableVector; //for readonly variables
1542 
1543     DString name;
1544     // IDL allows here also arrays of length 1
1545     SizeT nparam = e->NParam();
1546     if (nparam > 0)
1547     {
1548       e->AssureScalarPar<DStringGDL>(0, name);
1549     } else if (e->KeywordPresent(FILENAME))
1550     {
1551       e->AssureScalarKW<DStringGDL>(FILENAME, name);
1552     } else name = "idlsave.dat";
1553 
1554     WordExp(name);
1555 
1556     bool isCompress = false;
1557 
1558     FILE *fid;
1559     fid = fopen(name.c_str(), "rb");
1560     if (fid == NULL) e->Throw("Error opening file. Unit: XXXX, File: " + name + ".");
1561 
1562     XDR* xdrsmem = new XDR;
1563     XDR* xdrs;
1564     XDR* xdrsfile = new XDR;
1565     xdrstdio_create(xdrsfile, fid, XDR_DECODE);
1566     xdrs = xdrsfile;
1567     char* expanded = NULL;
1568 
1569     SizeT returned;
1570     char signature[4];
1571     returned = fread(signature, 4, 1, fid);
1572     if (signature[0] != 'S' || signature[1] != 'R')
1573     {
1574       fclose(fid);
1575       delete xdrsmem;
1576       delete xdrsfile;
1577       e->Throw("Not a valid save file: " + name + ".");
1578     }
1579     if (signature[3]==0x06) isCompress = true; //cerr<<"probably compressed"<<endl;
1580 #define LONG sizeof(int32_t) //sizeof(DInt)
1581 #define ULONG LONG
1582 
1583 
1584     bool isHdr64 = false;
1585     int isSysVar = 0x00;
1586     bool isArray = false;
1587     bool isStructure = false;
1588     //will start at TMESTAMP
1589     uint64_t currentptr = 0;
1590     uint64_t nextptr = LONG;
1591     uint32_t ptrs0, ptrs1;
1592     int32_t rectype;
1593     int32_t UnknownLong;
1594     bool SomethingFussyHappened = true;
1595 
1596     //pass twice. First to define heap variables only (and ancillary data).
1597 
1598     while (1)
1599     {
1600       xdrs = xdrsfile; //back to file if we were smarting the xdr to read a char* due to compression.
1601       if (fseek(fid, nextptr, SEEK_SET)) break;
1602       if (!xdr_int32_t(xdrs, &rectype)) break;
1603 
1604       if (DEBUG_SAVERESTORE) cerr << "Offset " << nextptr << ": record type " << rectypes[rectype] << endl;
1605 
1606       if (rectype == 6)
1607       {
1608         SomethingFussyHappened = false;
1609         break;
1610       }
1611       if (isHdr64)
1612       {
1613         uint64_t my_ulong64;
1614         if (!xdr_uint64_t(xdrs, &my_ulong64)) break;
1615         nextptr = my_ulong64;
1616         if (!xdr_int32_t(xdrs, &UnknownLong)) break;
1617         if (!xdr_int32_t(xdrs, &UnknownLong)) break;
1618       } else
1619       {
1620         if (!xdr_uint32_t(xdrs, &ptrs0)) break;
1621         if (!xdr_uint32_t(xdrs, &ptrs1)) break;
1622         if (!xdr_int32_t(xdrs, &UnknownLong)) break;
1623         nextptr = ptrs0;
1624         if (ptrs1 > 0)
1625         {
1626           DULong64 tmp = ptrs1;
1627           nextptr &= (tmp << 32);
1628         }
1629       }
1630 
1631       //dispatch accordingly:
1632 
1633       isSysVar = 0x00;
1634       isArray = false;
1635       isStructure = false;
1636       currentptr = ftell(fid);
1637 
1638       switch ((int) rectype) {
1639         case TIMESTAMP:
1640           if (nextptr < 1024)
1641           {
1642             //            cerr << "version offset < 1024... probably a compressed file" << endl;
1643             //            Message("sorry, can''t deal with this yet.  if possible, save without the \"COMPRESS\" flag.");
1644             isCompress = true;
1645           }
1646           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1647           getTimeUserHost(xdrs);
1648           if (verbose)
1649           {
1650             if (isCompress) Message("Portable (XDR) compressed SAVE/RESTORE file.");
1651             else Message("Portable (XDR) SAVE/RESTORE file.");
1652             Message("Save file written by " + std::string(saveFileUser) + "@" + std::string(saveFileHost) + ", " + std::string(saveFileDatestring));
1653           }
1654           break;
1655         case VERSION_MARKER:
1656           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1657           if (!getVersion(xdrs))
1658           {
1659             cerr << "error in VERSION" << endl;
1660             break;
1661           }
1662           break;
1663       case PROMOTE64:
1664           isHdr64 = true;
1665           break;
1666       case IDENTIFICATION:
1667           if (verbose)
1668           {
1669             if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1670             if (!getIdentification(xdrs))
1671             {
1672               cerr << "error in AUTHOR" << endl;
1673               break;
1674             }
1675           }
1676           break;
1677       case NOTICE:
1678           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1679           if (!getNotice(xdrs))
1680           {
1681             cerr << "error in NOTICE" << endl;
1682             break;
1683           }
1684           break;
1685         case DESCRIPTION_MARKER:
1686           if (verbose || hasDescription)
1687           {
1688             if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1689             std::string descr(getDescription(xdrs));
1690             if (verbose) Message("Description: " + descr);
1691 	    // AC 20200323 no "else" possible here, see above
1692             if (hasDescription) e->SetKW(DESCRIPTION, new DStringGDL(descr));
1693           }
1694           break;
1695       case COMMONBLOCK:
1696           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1697           if (!defineCommonBlock(e, xdrs, verboselevel))
1698           {
1699             cerr << "error in COMMONBLOCK" << endl;
1700             break;
1701           }
1702           break;
1703       case HEAP_HEADER: // IS IN PREAMBLE since version 5. BEFORE ANY REFERENCE TO HEAP.
1704           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1705         {
1706           int32_t elementcount;
1707           if (!xdr_int32_t(xdrs, &elementcount)) break;
1708           int32_t indices[elementcount];
1709           if (!xdr_vector(xdrs, (char*) indices, elementcount, sizeof (int32_t), (xdrproc_t) xdr_int32_t)) break;
1710           if (DEBUG_SAVERESTORE)
1711           {
1712             cerr << "Heap indexes, " << elementcount << " elements: ";
1713             for (int i = 0; i < elementcount; ++i) cerr << indices[i] << ",";
1714             cerr << endl;
1715           }
1716 
1717           break;
1718         }
1719         case HEAP_DATA: //define all HEAP_DATA variable but do not fill them yet
1720           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1721         {
1722           int32_t heap_index = 0;
1723           if (!xdr_int32_t(xdrs, &heap_index)) break;
1724           int32_t heap_unknown = 0;
1725           if (!xdr_int32_t(xdrs, &heap_unknown)) break; // start of TYPEDESC
1726           bool isObjStruct=false;
1727           BaseGDL* ret = getVariable(e, xdrs, isSysVar, isObjStruct);
1728           if (ret == NULL)
1729           {
1730             fclose(fid);
1731             delete xdrsmem;
1732             delete xdrsfile;
1733             e->Throw("error reading heap variable definition.");
1734           }
1735 
1736          //allocate corresponding heap entries and store gdl variable and heap entry in heapIndexMapRestore:
1737           //if ret is a struct defining an object, use ObjHeap.
1738           DPtr ptr;
1739           if (isObjStruct) ptr = e->NewObjHeap(1, static_cast<DStructGDL*> (ret)); else ptr = e->NewHeap(1, ret);
1740           heapIndexMapRestore.insert(std::pair<long, std::pair<BaseGDL*,DPtr>>(heap_index, std::make_pair(ret,ptr)));
1741 
1742           if (ret == NullGDL::GetSingleInstance()) break; //no data follows
1743 
1744           // should be at varstat=VARSTAT to read data
1745           int32_t varstart = 0;
1746           if (!xdr_int32_t(xdrs, &varstart)) break;
1747           if (varstart != VARSTART)
1748           {
1749             fclose(fid);
1750             delete xdrsmem;
1751             delete xdrsfile;
1752             e->Throw("Lost track in HEAP_DATA definition at offset " + i2s(nextptr));
1753 
1754           }
1755         }
1756           break;
1757         default:
1758           break;
1759       }
1760     }
1761 
1762     if (SomethingFussyHappened)
1763     {
1764       fclose(fid);
1765       delete xdrsmem;
1766       delete xdrsfile;
1767       e->Throw("Error Reading File: " + name + ".");
1768     }
1769 
1770     //from then, saveFileHeapMap.second contains heap DPtr.
1771 
1772     //Then on second pass, define normal variables that may be pointers to heap.
1773     rewind(fid);
1774     if (DEBUG_SAVERESTORE) cerr << "Second Pass"<<endl;
1775     currentptr = 0;
1776     nextptr = LONG;
1777     SomethingFussyHappened = true;
1778 
1779     while (1)
1780     {
1781 
1782       xdrs = xdrsfile; //back to file if we were smarting the xdr to read a char* due to compression.
1783       if (fseek(fid, nextptr, SEEK_SET)) break;
1784       if (!xdr_int32_t(xdrs, &rectype)) break;
1785 
1786       if (DEBUG_SAVERESTORE) cerr << "Offset " << nextptr << ": record type " << rectypes[rectype] << endl;
1787 
1788       if (rectype == 6)
1789       {
1790         SomethingFussyHappened = false;
1791         break;
1792       }
1793       if (isHdr64)
1794       {
1795         uint64_t my_ulong64;
1796         if (!xdr_uint64_t(xdrs, &my_ulong64)) break;
1797         nextptr = my_ulong64;
1798         if (!xdr_int32_t(xdrs, &UnknownLong)) break;
1799         if (!xdr_int32_t(xdrs, &UnknownLong)) break;
1800       } else
1801       {
1802         if (!xdr_uint32_t(xdrs, &ptrs0)) break;
1803         if (!xdr_uint32_t(xdrs, &ptrs1)) break;
1804         if (!xdr_int32_t(xdrs, &UnknownLong)) break;
1805         nextptr = ptrs0;
1806         if (ptrs1 > 0)
1807         {
1808           DULong64 tmp = ptrs1;
1809           nextptr &= (tmp << 32);
1810         }
1811       }
1812 
1813       //dispatch accordingly:
1814 
1815       isSysVar = 0x00;
1816       isArray = false;
1817       isStructure = false;
1818       currentptr = ftell(fid);
1819 
1820       switch ((int) rectype) {
1821       case SYSTEM_VARIABLE:
1822         if (DEBUG_SAVERESTORE) cerr<<"SYSTEM ";
1823           isSysVar = 0x02; //see? no break. defines a read-write system variable (default)
1824       case VARIABLE:
1825         if (DEBUG_SAVERESTORE) cerr<<"Variable ";
1826           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1827         {
1828           char* varname = 0;
1829           if (!xdr_string(xdrs, &varname, 2048)) break;
1830           string varName(varname);
1831           if (DEBUG_SAVERESTORE) cerr<<varname<<endl;
1832           bool isObjStruct=false;
1833           BaseGDL* ret = getVariable(e, xdrs, isSysVar, isObjStruct);
1834           if (ret == NULL)
1835           {
1836             Message("Unable to restore " + varName +".");
1837             break;
1838           }
1839           if (isObjStruct) std::cerr<<"Problem: found an Object in normal variable processing -- should not happen.\n";
1840           // should be at varstat=VARSTAT to read data
1841           int32_t varstart = 0;
1842           if (!xdr_int32_t(xdrs, &varstart)) break;
1843           if (varstart != VARSTART)
1844           {
1845             fclose(fid);
1846             delete xdrsmem;
1847             delete xdrsfile;
1848             e->Throw("Lost track in VARIABLE definition at offset " + i2s(nextptr));
1849           }
1850 
1851           fillVariableData(xdrs, ret);
1852 
1853           if (isSysVar & 0x01) systemReadonlyVariableVector.push_back(make_pair(varName, ret));
1854           else if (isSysVar & 0x02) systemVariableVector.push_back(make_pair(varName, ret));
1855           else variableVector.push_back(make_pair(varName, ret));
1856           Guard<BaseGDL>* guard = new Guard<BaseGDL>;
1857           guard->Reset(ret);
1858           guardVector.push_back(guard);
1859         }
1860           break;
1861       case HEAP_DATA: //use previous variable, now that the list of heap pointers is complete.
1862           if (isCompress) xdrs = uncompress_trick(fid, xdrsmem, expanded, nextptr, currentptr);
1863         {
1864           int32_t heap_index = 0;
1865           if (!xdr_int32_t(xdrs, &heap_index)) break;
1866           int32_t heap_unknown = 0;
1867           if (!xdr_int32_t(xdrs, &heap_unknown)) break; // start of TYPEDESC
1868           bool isObjStruct=false;
1869           BaseGDL* dummy = getVariable(e, xdrs, isSysVar, isObjStruct); //obliged to read all that infortunately.
1870           // we are at varstat=VARSTAT since this has already been seen above
1871           int32_t varstart = 0;
1872           if (!xdr_int32_t(xdrs, &varstart)) break;
1873           GDLDelete(dummy); //get rid of variable that may have wrong pointers and restore the good one:
1874           if (DEBUG_SAVERESTORE) cerr<<"Restoring Heap Data initially at #"<<heap_index<<"...";
1875           if (heapIndexMapRestore.find(heap_index) == heapIndexMapRestore.end()) {
1876             e->Throw("Lost track in HEAP VARIABLE definition at offset " + i2s(nextptr));
1877           } else {
1878             BaseGDL* ret = heapIndexMapRestore.find(heap_index)->second.first;
1879             if (ret == NullGDL::GetSingleInstance()) break; //no data follows as this is a !NULL
1880             fillVariableData(xdrs, ret);
1881             if (DEBUG_SAVERESTORE) std::cerr<<"at offset #"<<heapIndexMapRestore.find(heap_index)->second.second<<std::endl;
1882           }
1883 
1884 
1885         }
1886           break;
1887       default:
1888           break;
1889       }
1890     }
1891 
1892     if (expanded!=NULL) free(expanded);
1893     fclose(fid);
1894     delete xdrsmem;
1895     delete xdrsfile;
1896 
1897     //if problem, guards should deleted the allocated BaseGDLs.
1898     if (SomethingFussyHappened) e->Throw("Error Reading File: " + name + ".");
1899     //here everything was ok
1900 
1901 
1902 
1903     while (!systemVariableVector.empty())
1904     {
1905       restoreSystemVariable(e, systemVariableVector.back().first, (systemVariableVector.back()).second);
1906       if (verbose) Message("Restored system variable: " + (systemVariableVector.back()).first);
1907       systemVariableVector.pop_back();
1908     }
1909     while (!systemReadonlyVariableVector.empty())
1910     {
1911       restoreSystemVariable(e, systemReadonlyVariableVector.back().first, (systemReadonlyVariableVector.back()).second, true);
1912       if (verbose) Message("Restored system variable: " + (systemReadonlyVariableVector.back()).first);
1913       systemReadonlyVariableVector.pop_back();
1914     }
1915     while (!variableVector.empty())
1916     {
1917       restoreNormalVariable(e, variableVector.back().first, (variableVector.back()).second);
1918       if (verbose) Message("Restored variable: " + (variableVector.back()).first+".");
1919       variableVector.pop_back();
1920     }
1921 
1922     //heap variables have one more reference that necessary (1 at creation of the heap variable + 1 each time somthing points to it).
1923     //decrease each of them by 1:
1924     std::map<long, std::pair<BaseGDL*,DPtr>>::iterator imap;
1925     for (imap=heapIndexMapRestore.begin(); imap!=heapIndexMapRestore.end(); ++imap) {
1926       DPtr ptr=(*imap).second.second;
1927       GDLInterpreter::DecRef(ptr);
1928       GDLInterpreter::DecRefObj(ptr); //if not PTR then try Obj --- will always decrement the good one now that ptr is unique between Obj and Ptr.
1929     }
1930     //everything ok, remove guards and exit
1931     while (!guardVector.empty())
1932     {
1933       guardVector.back()->Release();
1934       guardVector.pop_back();
1935     }
1936 
1937   }
1938 // This adds in heaplist map:: all the OBJ or normal HEAP adresses of variables that are part of any named variable that are to be SAVEd.
1939 // GDL has 2 heaplists, one for OBJ and the other for normal pointers, but I've insured that the index in these maps is common, so
1940 // it is just as if there was only one heap list, as in IDL.
addToHeapList(EnvT * e,BaseGDL * var)1941   void addToHeapList(EnvT* e, BaseGDL* var)
1942   {
1943     if (var->Type() == GDL_PTR) {
1944       for (SizeT ielem = 0; ielem < var->N_Elements(); ++ielem) {
1945         DPtr subptr = (*static_cast<DPtrGDL*> (var))[ielem];
1946         if (subptr > 0) {
1947           heapT::iterator itheap=heapIndexMapSave.find(subptr);
1948           if ( itheap==heapIndexMapSave.end() ) {
1949             try {
1950               BaseGDL* v = e->GetHeap(subptr);  //this pointed-to heap value MAY NOT EXIST. It may have been destroyed.
1951             heapIndexMapSave.insert(std::pair<DPtr, SizeT>(subptr, subptr));
1952               if (DEBUG_SAVERESTORE)   std::cerr<<"Adding Heaplist PTR: HeapIndex="<<subptr<<", id="<<std::hex<<v<<std::dec<<std::endl;
1953             if (v && v!=NullGDL::GetSingleInstance()) addToHeapList(e, v);
1954             } catch( GDLInterpreter::HeapException& hEx)
1955             {
1956               if (DEBUG_SAVERESTORE) std::cerr<<"PTR ID<"<<subptr<<"> not found."<<std::endl;
1957             }
1958           }
1959         }
1960       }
1961     } else if (var->Type() == GDL_OBJ) {
1962       for (SizeT ielem = 0; ielem < var->N_Elements(); ++ielem) {
1963         DObj subptr = (*static_cast<DObjGDL*> (var))[ielem];
1964         if (subptr > 0) {
1965           heapT::iterator itheap=heapIndexMapSave.find(subptr);
1966           if ( itheap==heapIndexMapSave.end() ) {
1967             try {
1968               BaseGDL* v = e->GetObjHeap(subptr); //remember subptr IS uniquely increasing between OBJ and PTR Heaps indexes, but v is to be found in OBJ.
1969               heapIndexMapSave.insert(std::pair<DPtr, SizeT>(subptr, subptr));
1970               if (DEBUG_SAVERESTORE)   std::cerr<<"Adding Heaplist OBJ: HeapIndex="<<subptr<<", id="<<std::hex<<v<<std::dec<<std::endl;
1971             if (v && v!=NullGDL::GetSingleInstance() ) addToHeapList(e, v);
1972             } catch( GDLInterpreter::HeapException& hEx)
1973             {
1974               if (DEBUG_SAVERESTORE) std::cerr<<"OBJ ID <"<<subptr<<"> not found."<<std::endl;
1975             }
1976           }
1977         }
1978       }
1979     } else if (var->Type() == GDL_STRUCT) {
1980       DStructGDL* str = static_cast<DStructGDL*> (var);
1981       for (SizeT ielem = 0; ielem < var->N_Elements(); ++ielem) {
1982         for (int itag = 0; itag < str->NTags(); ++itag) {
1983           BaseGDL* v = str->GetTag(itag, ielem);
1984           if (v && v!=NullGDL::GetSingleInstance() ) addToHeapList(e, v);
1985         }
1986       }
1987     }
1988     return;
1989   }
1990 
writeHeapList(XDR * xdrs)1991   uint32_t writeHeapList(XDR* xdrs) {
1992 // writing heap list for IDL compatiblilty implies to "mimic" the single heap list of IDL.
1993 //We write the PTRs first, then the OBJs after. OBJ ptrs will thus start at the last value held by PTRs plus one.
1994     int32_t elementcount = heapIndexMapSave.size();
1995     if (elementcount < 1) return xdr_getpos(xdrs);
1996     uint32_t cur = writeNewRecordHeader(xdrs, HEAP_HEADER); //HEAP_HEADER
1997     xdr_int32_t(xdrs, &elementcount);
1998     int32_t indices[elementcount];
1999     SizeT i = 0;
2000     heapT::iterator it;
2001     for (it = heapIndexMapSave.begin(); it != heapIndexMapSave.end(); ++it) indices[i++] = (*it).second;
2002 
2003     xdr_vector(xdrs, (char*) indices, elementcount, sizeof (int32_t), (xdrproc_t) xdr_int32_t);
2004     if (DEBUG_SAVERESTORE) {
2005       cerr << "Heap indexes, " << elementcount << " elements: ";
2006       for (int i = 0; i < elementcount; ++i) cerr << indices[i] << ",";
2007       cerr << endl;
2008     }
2009     uint32_t next = updateNewRecordHeader(xdrs, cur);
2010     return next;
2011   }
2012 
writeCommonList(EnvT * e,XDR * xdrs,std::string commonname)2013   uint32_t writeCommonList(EnvT*e, XDR* xdrs, std::string commonname) {
2014    if (DEBUG_SAVERESTORE) std::cerr<<"Writing Common "<<commonname<<std::endl;
2015     EnvStackT& callStack = e->Interpreter()->CallStack();
2016     int32_t curlevnum = callStack.size();
2017     DSubUD* pro = static_cast<DSubUD*> (callStack[curlevnum - 1]->GetPro());
2018     DCommon* c=pro->Common(commonname);
2019     int32_t ncommonvars = c->NVar();
2020     if (ncommonvars < 1) return xdr_getpos(xdrs);
2021     uint32_t cur = writeNewRecordHeader(xdrs, COMMONBLOCK); //COMMON
2022     xdr_int32_t(xdrs, &ncommonvars);
2023     char* name = (char*)commonname.c_str();
2024     u_int len=c->Name().size();
2025     xdr_string(xdrs, &name, len);
2026     char* varnames[ncommonvars];
2027     u_int lens[ncommonvars];
2028     for (int i = 0; i < ncommonvars; ++i) {
2029       varnames[i] = (char*)c->VarName(i).c_str();
2030      if (DEBUG_SAVERESTORE)     std::cerr<<"        Name   "<<c->VarName(i)<<std::endl;
2031      }
2032     for (int i = 0; i < ncommonvars; ++i) lens[i] = c->VarName(i).size();
2033     for (int i = 0; i < ncommonvars; ++i) xdr_string(xdrs, &varnames[i], lens[i]);
2034     uint32_t next = updateNewRecordHeader(xdrs, cur);
2035     if (DEBUG_SAVERESTORE) std::cerr<<std::endl;
2036     return next;
2037   }
myfunctionToSortStringsInPair(std::pair<std::string,BaseGDL * > i,std::pair<std::string,BaseGDL * > j)2038   bool myfunctionToSortStringsInPair (std::pair<std::string, BaseGDL*> i, std::pair<std::string, BaseGDL*> j) { return (i.first>j.first); }
2039   // new fast save.
gdl_save(EnvT * e)2040   void gdl_save(EnvT* e) {
2041 
2042     // xdr() is used through all the following. I program here xdr to use the 8, 16, 32 and 64 bits length
2043     // types of GDL/IDL (BYTE,INT,LONG,LONG64). However, a risk exist in some architectures that the real length
2044     // of DInt, DLong etc is not really the expected one (16 and 32 respectively). So the following is a test on these
2045     // lengths. If the test fails, 1) GDL is false for this architecture and that needs to be reported and
2046     // 2) gdl_restore cannot work.
2047 
2048     if (safetyTested == false)
2049     {
2050       isSafe = testSafety();
2051       if (!isSafe) e->Throw("Severe: internal representation of integers in this version of GDL is wrong, please report. Aborting unsafe use of RESTORE.");
2052       safetyTested = true;
2053     }
2054     //if testSafety is correct, DLong and int32_t , DInt and int16_t etc have the same meaning.
2055 
2056     //empty maps etc by security.
2057     heapIndexMapSave.clear();
2058     predeflist.clear();
2059 
2060     static int VERBOSE = e->KeywordIx("VERBOSE");
2061     bool verbose = e->KeywordSet(VERBOSE);
2062     DLong verboselevel=(verbose?1:0);
2063     if (verbose) e->AssureLongScalarKW(VERBOSE,verboselevel);
2064 
2065 
2066     static int VARIABLES = e->KeywordIx("VARIABLES");
2067     bool doVars=e->KeywordSet(VARIABLES);
2068     static int ALL = e->KeywordIx("ALL");
2069     bool allVars=e->KeywordSet(ALL);
2070     static int SYSTEM_VARIABLES = e->KeywordIx("SYSTEM_VARIABLES");
2071     bool doSys=e->KeywordSet(SYSTEM_VARIABLES);
2072     static int COMM = e->KeywordIx("COMM");
2073     bool doComm=e->KeywordSet(COMM);
2074     static int COMPRESS = e->KeywordIx("COMPRESS");
2075     save_compress=e->KeywordSet(COMPRESS);
2076 
2077     if (allVars) {
2078       doSys=true;
2079       doComm=true;
2080       doVars=true;
2081     }
2082 
2083     static int FILENAME = e->KeywordIx("FILENAME");
2084     static int DESCRIPTION = e->KeywordIx("DESCRIPTION");
2085 
2086     bool needsDescription = e->KeywordPresent(DESCRIPTION);
2087 
2088     DStringGDL* description=NULL;
2089     if (needsDescription) description=e->GetKWAs<DStringGDL>(DESCRIPTION);
2090 
2091     std::vector<std::pair<std::string, BaseGDL*> > variableVector;
2092     std::vector<std::pair<std::string, BaseGDL*> > systemVariableVector;
2093 //    std::vector<std::pair<std::string, BaseGDL*> > systemReadonlyVariableVector; //for readonly variables //not used
2094     set<string> commonList;
2095 
2096 //Variables
2097     std::queue<std::pair<std::string, BaseGDL*> >varNameList;
2098 
2099     long nparam=e->NParam();
2100     if (!doComm && !doSys) doVars=(doVars||(nparam==0));
2101 
2102     if (doSys)
2103     {
2104       SizeT nVar = sysVarList.size();
2105       for (SizeT v = 0; v < nVar; ++v)
2106       {
2107         DVar* var = sysVarList[v];
2108         DString sysVarName = var->Name();
2109         if (FindInVarList(sysVarRdOnlyList, sysVarName) != NULL) continue; //systemReadonlyVariableVector.push_back(make_pair("!" + sysVarName, sysVarRdOnly->Data()));
2110         if (FindInVarList(sysVarNoSaveList, sysVarName) != NULL) continue;
2111         systemVariableVector.push_back(make_pair("!" + sysVarName, var->Data()));
2112       }
2113       std::sort (systemVariableVector.begin(), systemVariableVector.end(),  myfunctionToSortStringsInPair);
2114     }
2115 
2116     if (doVars)
2117     {
2118       //will list (all) variables, incl. common defined, at desired level.
2119       EnvStackT& callStack = e->Interpreter()->CallStack();
2120       DLong curlevnum = callStack.size();
2121       DSubUD* pro = static_cast<DSubUD*> (callStack[curlevnum-1]->GetPro());
2122 
2123       SizeT nVar = pro->Size(); // # var in GDL for desired level
2124       SizeT nComm = pro->CommonsSize(); // # has commons?
2125       SizeT nTotVar = nVar + nComm; //All the variables availables at that lev.
2126 
2127       if (nTotVar > 0)
2128       {
2129         if (nComm > 0)
2130         {
2131           DStringGDL* list = static_cast<DStringGDL*> (pro->GetCommonVarNameList());
2132           for (SizeT i = 0; i < list->N_Elements(); ++i) {
2133             BaseGDL** var =  pro->GetCommonVarPtr((*list)[i]);
2134             if (*var != NULL) varNameList.push(make_pair((*list)[i],*var));
2135           }
2136         }
2137         if (nVar > 0 )
2138         {
2139           for (SizeT i = 0; i < nVar; ++i) {
2140             BaseGDL* var = ((EnvT*) (callStack[curlevnum - 1]))->GetKW(i);
2141             if (var != NULL) varNameList.push(make_pair(pro->GetVarName(i),var));
2142           }
2143         }
2144       }
2145     }
2146 
2147     if (doComm)
2148     {
2149       //will list (all) variables, incl. common defined, at desired level.
2150       EnvStackT& callStack = e->Interpreter()->CallStack();
2151       DLong curlevnum = callStack.size();
2152       DSubUD* pro = static_cast<DSubUD*> (callStack[curlevnum - 1]->GetPro());
2153 
2154       SizeT nComm = pro->CommonsSize(); // # has commons?
2155       if (nComm > 0)
2156       {
2157         DStringGDL* list = static_cast<DStringGDL*> (pro->GetCommonVarNameList());
2158         for (SizeT i = 0; i < list->N_Elements(); ++i)
2159         {
2160           DCommonBase* common = pro->FindCommon((*list)[i]);
2161           commonList.insert(common->Name());
2162         }
2163       }
2164     }
2165 
2166 
2167     for (int i = 0; i < nparam; ++i)
2168     {
2169       BaseGDL* var = e->GetPar(i);
2170       if (var == NULL)
2171       {
2172         Message("Undefined item not saved: " + e->GetParString(i));
2173       } else //var exists, but may have been already done by doVars.
2174       {
2175         if (!doVars) varNameList.push(make_pair(e->GetParString(i),var));
2176       }
2177     }
2178 
2179     while (!varNameList.empty())
2180     {
2181       std::string varName = varNameList.front().first;
2182       BaseGDL* var = varNameList.front().second;
2183       varNameList.pop();
2184       if (var->N_Elements()==0) Message("Undefined item not saved: "+varName+".");
2185       else {
2186           //sytem variables are saved with /SYSTEM. This is a special case, test: try "SAVE,!P" with IDL:
2187           //<Expression> xxx generates an error.
2188           if (varName.substr(0, 1) == "<") e->Throw("Expression must be named variable in this context:" + varName);
2189           else
2190           {
2191             //examine variable. Cases: in common, normal. remove common name if necessary.
2192             std::size_t pos = varName.find("(", 0);
2193             if (pos != std::string::npos) varName = varName.substr(0, pos - 1); //one Blank.
2194             variableVector.push_back(make_pair(varName, var));
2195           }
2196         }
2197     }
2198 
2199     //Now, do we have heap variables in the variableVector (pointers)? If yes, we add these BaseGDL* to the heaplist unique list.
2200     //we then write the heap variables first.
2201     //then, anytime a pointer is found in any normal variable, instead of writing the data we will
2202     //just write the index of the pointed-to address in the heaplist.
2203     std::vector<std::pair<std::string, BaseGDL*> >::iterator itvar;
2204     for (itvar=variableVector.begin(); itvar!=variableVector.end(); ++itvar) {
2205       addToHeapList(e, itvar->second);
2206     }
2207     //NOTE: the following will not presently keep the information of a sysvar and readonly sysvar in heap. Which
2208     //is probably overkill?
2209     for (itvar=systemVariableVector.begin(); itvar!=systemVariableVector.end(); ++itvar) {
2210       addToHeapList(e, itvar->second);
2211     }
2212 //    for (itvar=systemReadonlyVariableVector.begin(); itvar!=systemReadonlyVariableVector.end(); ++itvar) {
2213 //      addToHeapList(e, itvar->second);
2214 //    }
2215 
2216     DString name;
2217     if (e->KeywordPresent(FILENAME))
2218     {
2219       e->AssureScalarKW<DStringGDL>(FILENAME, name);
2220     } else name = "idlsave.dat";
2221 
2222     WordExp(name);
2223 
2224     save_fid = fopen(name.c_str(), "wb+");
2225     if (save_fid == NULL) e->Throw("Error opening file. Unit: XXXX, File: " + name + ".");
2226 
2227     XDR* xdrs = new XDR;
2228     xdrstdio_create(xdrs, save_fid, XDR_ENCODE);
2229 
2230     SizeT returned;
2231     char signature[4]={'S','R',0x00,0x04};
2232     if (save_compress) signature[3]=0x06;
2233     returned = fwrite(signature, 4, 1, save_fid);
2234 
2235 #define LONG sizeof(int32_t) //sizeof(DInt)
2236 #define ULONG LONG
2237 
2238 
2239     //will start at TIMESTAMP
2240     uint64_t currentptr = LONG;
2241     uint64_t nextptr = 0;
2242 
2243     fseek(save_fid, currentptr, SEEK_SET);
2244 
2245     const int    MAX_DATE_STRING_LENGTH = 80;
2246     time_t t=time(0);
2247     struct tm * tstruct;
2248     tstruct=localtime(&t);
2249     char *saveFileDatestring=new char[MAX_DATE_STRING_LENGTH];
2250     const char *dateformat="%a %h %d %T %Y";// day,month,day number,time,year
2251     SizeT res=strftime(saveFileDatestring,MAX_DATE_STRING_LENGTH,dateformat,tstruct);
2252     std::string saveFileUser = GetEnvString( "USER");
2253     std::string saveFileHost = GetEnvString( "HOST");
2254     if (saveFileHost == "") saveFileHost = GetEnvString( "HOSTNAME");
2255     if (saveFileHost == "") {
2256 #define GDL_HOST_NAME_MAX 255
2257       char gethost[GDL_HOST_NAME_MAX];
2258       size_t lgethost=GDL_HOST_NAME_MAX;
2259       // don't know if this primitive is available on Mac OS X
2260       int success = gethostname(gethost, lgethost);
2261       if( success == 0) saveFileHost=string(gethost);
2262     }
2263     //TIMESTAMP
2264     nextptr=writeTimeUserHost(xdrs, saveFileDatestring, (char*)saveFileUser.c_str(), (char*)saveFileHost.c_str());
2265     int32_t format=9; //IDL v. 6.1 ++
2266     DStructGDL* version = SysVar::Version();
2267     static unsigned osTag = version->Desc()->TagIndex( "OS");
2268     static unsigned archTag = version->Desc()->TagIndex( "ARCH");
2269     static unsigned releaseTag = version->Desc()->TagIndex( "RELEASE");
2270     DString os = (*static_cast<DStringGDL*>( version->GetTag( osTag, 0)))[0];
2271     DString arch = (*static_cast<DStringGDL*>( version->GetTag( archTag, 0)))[0];
2272     DString release = (*static_cast<DStringGDL*>( version->GetTag( releaseTag, 0)))[0];
2273     //VERSION
2274     nextptr=writeVersion(xdrs, &format, (char*)arch.c_str(), (char*) os.c_str() , (char*) release.c_str());
2275     //HEAPLIST
2276     if (heapIndexMapSave.size() > 0) nextptr=writeHeapList(xdrs);
2277     // promote64: NO!
2278 //    //notice:
2279 //    std::string notice="Made by GDL, a free software program that you can redistribute and/or modify"
2280 //                       " under the terms of the GPL, use at your own risks.";
2281 //    nextptr=writeNotice(xdrs, (char*)notice.c_str());
2282     if (description!=NULL)  nextptr=writeDescription(xdrs,(char*)((*description)[0].c_str()));
2283 
2284     //COMMON
2285     std::set<std::string>::iterator itcommon;
2286     for (itcommon=commonList.begin(); itcommon!=commonList.end(); ++itcommon) {
2287       nextptr=writeCommonList(e, xdrs, *itcommon);
2288       if (verboselevel>0) Message("Saved common block: " + *itcommon);
2289     }
2290     //HEAP Variables: all terminal variables pointed by, OBJs or PTRs
2291 
2292     if (heapIndexMapSave.size() > 0) { //there is some heap...
2293       DPtrGDL* heapPtrList=e->Interpreter( )->GetAllHeap( );
2294       for (SizeT i=0; i<heapPtrList->N_Elements(); ++i) nextptr=writeHeapVariable(e, xdrs, (*heapPtrList)[i], false);
2295       GDLDelete(heapPtrList);
2296       DObjGDL* heapObjPtrList=e->Interpreter( )->GetAllObjHeap( );
2297       for (SizeT i=0; i<heapObjPtrList->N_Elements(); ++i) nextptr=writeHeapVariable(e, xdrs, (*heapObjPtrList)[i], true);
2298       GDLDelete(heapObjPtrList);
2299     }
2300 
2301 //
2302 //    while (!systemReadonlyVariableVector.empty())
2303 //    {
2304 //      nextptr = writeNormalVariable(xdrs, systemReadonlyVariableVector.back().first, (systemReadonlyVariableVector.back()).second, 0x3);
2305 //      if (verboselevel > 0) Message("Saved variable: " + (systemReadonlyVariableVector.back()).first + ".");
2306 //      systemReadonlyVariableVector.pop_back();
2307 //    }
2308     while (!systemVariableVector.empty())
2309     {
2310       nextptr = writeNormalVariable(xdrs, systemVariableVector.back().first, (systemVariableVector.back()).second, 0x2);
2311       if (verboselevel > 0) Message("SAVE: Saved system variable: " + (systemVariableVector.back()).first + ".");
2312       systemVariableVector.pop_back();
2313     }
2314 
2315     while (!variableVector.empty())
2316     {
2317       nextptr=writeNormalVariable(xdrs, variableVector.back().first, (variableVector.back()).second);
2318       if (verboselevel>0) Message("SAVE: Saved variable: " + (variableVector.back()).first+".");
2319       variableVector.pop_back();
2320     }
2321 
2322     nextptr=writeEnd(xdrs);
2323     xdr_destroy(xdrs);
2324     fclose(save_fid);
2325   }
2326 
2327 }
2328 
2329