1 /****************************************************************************
2 **
3 ** This file is part of GAP, a system for computational discrete algebra.
4 **
5 ** Copyright of GAP belongs to its developers, whose names are too numerous
6 ** to list here. Please refer to the COPYRIGHT file for details.
7 **
8 ** SPDX-License-Identifier: GPL-2.0-or-later
9 **
10 ** This file contains the functions concerned with saving and loading
11 ** the workspace. There are support functions in gasman.c and elsewhere
12 ** throughout the kernel
13 */
14
15 #include "saveload.h"
16
17 #include "bool.h"
18 #include "calls.h"
19 #include "compstat.h"
20 #include "error.h"
21 #include "finfield.h"
22 #ifdef USE_GASMAN
23 #include "gasman_intern.h"
24 #endif
25 #include "gvars.h"
26 #include "io.h"
27 #include "modules.h"
28 #include "read.h"
29 #include "streams.h"
30 #include "stringobj.h"
31 #include "sysfiles.h"
32 #include "sysopt.h"
33
34 #include <unistd.h>
35
36
37 /***************************************************************************
38 **
39 ** Temporary Stuff which will probably be revised to tie in with sysfiles
40 */
41
42
43 static Int SaveFile;
44 static Int LoadFile = -1;
45 static UInt1 LoadBuffer[100000];
46 static UInt1* LBPointer;
47 static UInt1* LBEnd;
48 static Obj userHomeExpand;
49
50 #ifdef USE_GASMAN
51
OpenForSave(Obj fname)52 static Int OpenForSave( Obj fname )
53 {
54 if (SaveFile != -1)
55 {
56 Pr("Already saving\n",0L,0L);
57 return 1;
58 }
59 SaveFile = SyFopen(CONST_CSTR_STRING(fname), "wb");
60 if (SaveFile == -1)
61 {
62 Pr("Couldn't open file %s to save workspace\n",
63 (UInt)CONST_CSTR_STRING(fname),0L);
64 return 1;
65 }
66 LBPointer = LoadBuffer;
67 LBEnd = LBPointer+sizeof(LoadBuffer);
68 return 0;
69 }
70
CloseAfterSave(void)71 static void CloseAfterSave( void )
72 {
73 if (SaveFile == -1)
74 {
75 Panic("Internal error -- this should never happen");
76 }
77
78 if (SyWrite(SaveFile, LoadBuffer, LBPointer - LoadBuffer) < 0)
79 ErrorQuit("Cannot write to file, see 'LastSystemError();'\n", 0L, 0L);
80 SyFclose(SaveFile);
81 SaveFile = -1;
82 }
83
84 #endif
85
OpenForLoad(const Char * fname)86 static void OpenForLoad( const Char *fname )
87 {
88 if (LoadFile != -1)
89 {
90 Panic("Internal error -- this should never happen");
91 }
92 LoadFile = SyFopen(fname, "rb");
93 if (LoadFile == -1)
94 {
95 Pr("Couldn't open saved workspace %s\n",(Int)fname,0L);
96 SyExit(1);
97 }
98 }
99
100
CloseAfterLoad(void)101 static void CloseAfterLoad( void )
102 {
103 if (!LoadFile)
104 {
105 Panic("Internal error -- this should never happen");
106 }
107 SyFclose(LoadFile);
108 LoadFile = -1;
109 }
110
SAVE_BYTE_BUF(void)111 static void SAVE_BYTE_BUF(void)
112 {
113 if (SyWrite(SaveFile, LoadBuffer, LBEnd - LoadBuffer) < 0)
114 ErrorQuit("Cannot write to file, see 'LastSystemError();'\n", 0L, 0L);
115 LBPointer = LoadBuffer;
116 return;
117 }
118
119 #define SAVE_BYTE(byte) {if (LBPointer >= LBEnd) {SAVE_BYTE_BUF();} \
120 *LBPointer++ = (UInt1)(byte);}
121
122 static const Char * LoadByteErrorMessage = "Unexpected End of File in Load\n";
123
LOAD_BYTE_BUF(void)124 static UInt1 LOAD_BYTE_BUF(void)
125 {
126 Int ret;
127 ret = SyRead(LoadFile, LoadBuffer, 100000);
128 if (ret <= 0)
129 {
130 Pr(LoadByteErrorMessage, 0L, 0L );
131 SyExit(2);
132 }
133 LBEnd = LoadBuffer + ret;
134 LBPointer = LoadBuffer;
135 return *LBPointer++;
136 }
137
138 #define LOAD_BYTE() (UInt1)((LBPointer >= LBEnd) ?\
139 (LOAD_BYTE_BUF()) : (*LBPointer++))
140
141 /***************************************************************************
142 **
143 ** Low level saving routines
144 */
145
SaveUInt1(UInt1 data)146 void SaveUInt1( UInt1 data )
147 {
148 SAVE_BYTE( data );
149 }
150
LoadUInt1(void)151 UInt1 LoadUInt1( void )
152 {
153 return LOAD_BYTE( );
154 }
155
SaveUInt2(UInt2 data)156 void SaveUInt2( UInt2 data )
157 {
158 SAVE_BYTE( (UInt1) (data & 0xFF) );
159 SAVE_BYTE( (UInt1) (data >> 8) );
160 }
161
LoadUInt2(void)162 UInt2 LoadUInt2 ( void )
163 {
164 UInt2 res;
165 res = (UInt2)LOAD_BYTE();
166 res |= (UInt2)LOAD_BYTE()<<8;
167 return res;
168 }
169
SaveUInt4(UInt4 data)170 void SaveUInt4( UInt4 data )
171 {
172 SAVE_BYTE( (UInt1) (data & 0xFF) );
173 SAVE_BYTE( (UInt1) ((data >> 8) &0xFF) );
174 SAVE_BYTE( (UInt1) ((data >> 16) &0xFF) );
175 SAVE_BYTE( (UInt1) ((data >> 24) &0xFF) );
176 }
177
LoadUInt4(void)178 UInt4 LoadUInt4 ( void )
179 {
180 UInt4 res;
181 res = (UInt)LOAD_BYTE();
182 res |= (UInt)LOAD_BYTE() << 8;
183 res |= (UInt)LOAD_BYTE() << 16;
184 res |= (UInt)LOAD_BYTE() << 24;
185 return res;
186 }
187
SaveUInt8(UInt8 data)188 void SaveUInt8( UInt8 data )
189 {
190 SAVE_BYTE( (UInt1) (data & 0xFF) );
191 SAVE_BYTE( (UInt1) ((data >> 8) &0xFF) );
192 SAVE_BYTE( (UInt1) ((data >> 16) &0xFF) );
193 SAVE_BYTE( (UInt1) ((data >> 24) &0xFF) );
194 SAVE_BYTE( (UInt1) ((data >> 32) &0xFF) );
195 SAVE_BYTE( (UInt1) ((data >> 40) &0xFF) );
196 SAVE_BYTE( (UInt1) ((data >> 48) &0xFF) );
197 SAVE_BYTE( (UInt1) ((data >> 56) &0xFF) );
198 }
199
LoadUInt8(void)200 UInt8 LoadUInt8 ( void )
201 {
202 UInt8 res;
203 res = (UInt8)LOAD_BYTE();
204 res |= (UInt8)LOAD_BYTE() << 8;
205 res |= (UInt8)LOAD_BYTE() << 16;
206 res |= (UInt8)LOAD_BYTE() << 24;
207 res |= (UInt8)LOAD_BYTE() << 32;
208 res |= (UInt8)LOAD_BYTE() << 40;
209 res |= (UInt8)LOAD_BYTE() << 48;
210 res |= (UInt8)LOAD_BYTE() << 56;
211
212 return res;
213 }
214
SaveUInt(UInt data)215 void SaveUInt( UInt data )
216 {
217 #ifdef SYS_IS_64_BIT
218 SaveUInt8(data);
219 #else
220 SaveUInt4(data);
221 #endif
222 }
223
LoadUInt(void)224 UInt LoadUInt ( void )
225 {
226 #ifdef SYS_IS_64_BIT
227 return LoadUInt8();
228 #else
229 return LoadUInt4();
230 #endif
231 }
232
SaveCStr(const Char * str)233 void SaveCStr( const Char * str)
234 {
235 do {
236 SAVE_BYTE( (UInt1) *str);
237 } while (*(str++));
238 }
239
240 #include <assert.h>
241
LoadCStr(Char * buf,UInt maxsize)242 void LoadCStr( Char *buf, UInt maxsize)
243 {
244 UInt nread = 0;
245 UInt1 c = 1;
246 assert(maxsize > 0);
247 while (c != '\0' && nread < maxsize )
248 {
249 c = LOAD_BYTE();
250 *buf++ = (Char) c;
251 nread++;
252 }
253 if (c != '\0')
254 {
255 Panic("Buffer overflow reading workspace");
256 }
257 }
258
259
260 /****************************************************************************
261 **
262 *F SaveString( <string> ) . . . . . . . . . . . . . . . . . . save a string
263 **
264 */
SaveString(Obj string)265 void SaveString ( Obj string )
266 {
267 UInt i, len = GET_LEN_STRING(string);
268 const UInt1 *p = CONST_CHARS_STRING(string);
269 SaveUInt(len);
270 for (i=0; i<len; i++)
271 SAVE_BYTE(p[i]);
272 }
273
274 /****************************************************************************
275 **
276 *F LoadString( <string> )
277 **
278 */
LoadString(Obj string)279 void LoadString ( Obj string )
280 {
281 UInt i, len;
282 UInt1 c;
283 UInt1 *p = (UInt1*)CHARS_STRING(string);
284 len = LoadUInt();
285 SET_LEN_STRING(string, len);
286 for (i=0; i<len; i++) {
287 c = LOAD_BYTE();
288 p[i] = c;
289 }
290 }
291
SaveSubObj(Obj subobj)292 void SaveSubObj( Obj subobj )
293 {
294 #ifndef USE_GASMAN
295 // FIXME: HACK
296 assert(0);
297 #else
298 if (!subobj)
299 SaveUInt(0);
300 else if (IS_INTOBJ(subobj))
301 SaveUInt((UInt) subobj);
302 else if (IS_FFE(subobj))
303 SaveUInt((UInt) subobj);
304 else if ((((UInt)subobj & 3) != 0) ||
305 subobj < (Bag)MptrBags ||
306 subobj > (Bag)MptrEndBags ||
307 (Bag *)PTR_BAG(subobj) < MptrEndBags)
308 {
309 Pr("#W bad bag id %d found, 0 saved\n", (Int)subobj, 0L);
310 SaveUInt(0);
311 }
312 else
313 SaveUInt(((UInt)LINK_BAG(subobj)) << 2);
314 #endif
315 }
316
LoadSubObj(void)317 Obj LoadSubObj( void )
318 {
319 #ifndef USE_GASMAN
320 // FIXME: HACK
321 assert(0);
322 #else
323 UInt word = LoadUInt();
324 if (word == 0)
325 return (Obj) 0;
326 if ((word & 0x3) == 1 || (word & 0x3) == 2)
327 return (Obj) word;
328 else
329 return (Obj)(MptrBags + (word >> 2)-1);
330 #endif
331 }
332
333
334 /***************************************************************************
335 **
336 ** Bag level saving routines
337 */
338
339 #ifdef USE_GASMAN
340
SaveBagData(Bag bag)341 static void SaveBagData (Bag bag )
342 {
343 BagHeader * header = BAG_HEADER(bag);
344 SaveUInt1(header->type);
345 SaveUInt1(header->flags);
346 SaveUInt(header->size);
347
348 /* dispatch */
349 (*(SaveObjFuncs[ header->type]))(bag);
350
351 }
352
353
354
LoadBagData(void)355 static void LoadBagData ( void )
356 {
357 Bag bag;
358 UInt type, flags, size;
359
360 /* Recover the size & type */
361 type = LoadUInt1();
362 flags = LoadUInt1();
363 size = LoadUInt();
364
365 if (TNAM_TNUM(type) == NULL)
366 Panic("Bad type %d, size %d\n", (int)type, (int)size);
367
368 /* Get GASMAN to set up the bag for me */
369 bag = NextBagRestoring( type, flags, size );
370
371 /* dispatch */
372 (*(LoadObjFuncs[ type ]))(bag);
373 }
374
375 #endif
376
377 /***************************************************************************
378 **
379 *F WriteSaveHeader() . . . . . and utility functions, and loading functions
380 **
381 */
382
383 #ifdef USE_GASMAN
384
WriteEndiannessMarker(void)385 static void WriteEndiannessMarker( void )
386 {
387 UInt x;
388 #ifdef SYS_IS_64_BIT
389 x = 0x0102030405060708L;
390 #else
391 x = 0x01020304L;
392 #endif
393 SAVE_BYTE(((UInt1 *)&x)[0]);
394 SAVE_BYTE(((UInt1 *)&x)[1]);
395 SAVE_BYTE(((UInt1 *)&x)[2]);
396 SAVE_BYTE(((UInt1 *)&x)[3]);
397 #ifdef SYS_IS_64_BIT
398 SAVE_BYTE(((UInt1 *)&x)[4]);
399 SAVE_BYTE(((UInt1 *)&x)[5]);
400 SAVE_BYTE(((UInt1 *)&x)[6]);
401 SAVE_BYTE(((UInt1 *)&x)[7]);
402 #endif
403 }
404
405 #endif
406
407
CheckEndiannessMarker(void)408 static void CheckEndiannessMarker( void )
409 {
410 UInt x;
411 ((UInt1 *)&x)[0] = LOAD_BYTE();
412 ((UInt1 *)&x)[1] = LOAD_BYTE();
413 ((UInt1 *)&x)[2] = LOAD_BYTE();
414 ((UInt1 *)&x)[3] = LOAD_BYTE();
415 #ifdef SYS_IS_64_BIT
416 ((UInt1 *)&x)[4] = LOAD_BYTE();
417 ((UInt1 *)&x)[5] = LOAD_BYTE();
418 ((UInt1 *)&x)[6] = LOAD_BYTE();
419 ((UInt1 *)&x)[7] = LOAD_BYTE();
420 if (x != 0x0102030405060708L)
421 #else
422 if (x != 0x01020304L)
423 #endif
424 {
425 Panic("Saved workspace with incompatible byte order");
426 }
427 }
428
429
430 /***************************************************************************
431 **
432 ** FuncBagStats
433 */
434
435 #ifdef USE_GASMAN
436
437 static FILE *file;
438
report(Bag bag)439 static void report( Bag bag)
440 {
441 fprintf(file,"%li %li\n", (long) TNUM_BAG(bag), (long) SIZE_BAG(bag));
442 }
443
FuncBagStats(Obj self,Obj filename)444 static Obj FuncBagStats(Obj self, Obj filename)
445 {
446 file = fopen((Char *)CHARS_STRING(filename),"w");
447 CallbackForAllBags(report);
448 fclose(file);
449 return (Obj) 0;
450 }
451
452 #endif
453
454
455 /***************************************************************************
456 **
457 ** Find Bags -- a useful debugging tool -- scan for a bag of specified
458 ** type and size and return it to the GAP level. Could be a problem
459 ** if the bag is not a valid GAP object -- eg a local variables bag or
460 ** a functions body.
461 */
462
463 #ifdef USE_GASMAN
464
465 static UInt fb_minsize, fb_maxsize, fb_tnum;
466 static Bag hit;
467
ScanBag(Bag bag)468 static void ScanBag( Bag bag)
469 {
470 if (hit == (Bag)0 &&
471 SIZE_BAG(bag) >= fb_minsize &&
472 SIZE_BAG(bag) <= fb_maxsize &&
473 TNUM_BAG(bag) == fb_tnum)
474 hit = bag;
475 }
476
FuncFindBag(Obj self,Obj minsize,Obj maxsize,Obj tnum)477 static Obj FuncFindBag(Obj self, Obj minsize, Obj maxsize, Obj tnum)
478 {
479 hit = (Bag) 0;
480 fb_minsize = INT_INTOBJ(minsize);
481 fb_maxsize = INT_INTOBJ(maxsize);
482 fb_tnum = INT_INTOBJ(tnum);
483 CallbackForAllBags(ScanBag);
484 return (hit != (Bag) 0) ? hit : Fail;
485 }
486
487 #endif
488
489
490 /***************************************************************************
491 **
492 *F SaveWorkspace( <fname> ) . . . . . save the workspace to the named file
493 **
494 ** 'SaveWorkspace' is the entry point to the workspace saving. It is not
495 ** installed as a GAP function, but instead as a keyword, so that we can be
496 ** sure it is only being called from the top-most prompt level
497 ** The file saveload.tex in the dev directory describes the saved format
498 ** in more detail. Most of the work will be done from inside GASMAN, because
499 ** we need to fiddle with Bag internals somewhat
500 **
501 ** The return value is either True or Fail
502 */
503
504 #ifdef USE_GASMAN
505
506 static UInt NextSaveIndex = 1;
507
AddSaveIndex(Bag bag)508 static void AddSaveIndex( Bag bag)
509 {
510 LINK_BAG(bag) = (Obj)NextSaveIndex++;
511 }
512
RemoveSaveIndex(Bag bag)513 static void RemoveSaveIndex( Bag bag)
514 {
515 LINK_BAG(bag) = bag;
516 }
517
518
GetKernelDescription(void)519 static Char * GetKernelDescription(void)
520 {
521 static Char SyKernelDescription[256];
522 strcpy(SyKernelDescription, SyKernelVersion);
523 if (SyUseReadline) {
524 strcat(SyKernelDescription, " with readline");
525 }
526 return SyKernelDescription;
527 }
528
WriteSaveHeader(void)529 static void WriteSaveHeader( void )
530 {
531 UInt i;
532
533 SaveCStr("GAP workspace");
534 SaveCStr(GetKernelDescription());
535
536 #ifdef SYS_IS_64_BIT
537 SaveCStr("64 bit");
538 #else
539 SaveCStr("32 bit");
540 #endif
541
542 WriteEndiannessMarker();
543
544 SaveCStr("Counts and Sizes");
545 for (i = 0; i < GlobalBags.nr; i++) {
546 GAP_ASSERT(GlobalBags.cookie[i] != NULL);
547 }
548 SaveUInt(GlobalBags.nr);
549 SaveUInt(NextSaveIndex-1);
550 SaveUInt(AllocBags - MptrEndBags);
551
552 SaveCStr("Loaded Modules");
553 SaveModules();
554
555 SaveCStr("Kernel to WS refs");
556 for (i = 0; i < GlobalBags.nr; i++)
557 {
558 GAP_ASSERT(GlobalBags.cookie[i] != NULL);
559 SaveCStr((const Char *)GlobalBags.cookie[i]);
560 SaveSubObj(*(GlobalBags.addr[i]));
561 }
562 }
563 #endif
564
SaveWorkspace(Obj fname)565 Obj SaveWorkspace( Obj fname )
566 {
567 #ifndef USE_GASMAN
568 Pr("SaveWorkspace is only supported when GASMAN is in use",0,0);
569 return Fail;
570
571 #else
572 Obj fullname;
573 Obj result;
574
575 if (!IsStringConv(fname))
576 ErrorQuit("usage: SaveWorkspace( <filename> )",0,0);
577 /* maybe expand fname starting with ~/... */
578 fullname = Call1ArgsInNewReader(userHomeExpand, fname);
579
580 if (ModulesPreSave())
581 return Fail;
582
583 /* Do a full garbage collection */
584 CollectBags( 0, 1);
585
586 /* Add indices in link words of all bags, for saving inter-bag references */
587 NextSaveIndex = 1;
588 CallbackForAllBags( AddSaveIndex );
589
590 /* Now do the work */
591 result = Fail;
592 if (!OpenForSave( fullname ))
593 {
594 result = True;
595 WriteSaveHeader();
596 SaveCStr("Bag data");
597 SortHandlers( 1 ); /* Sort by address to speed up CookieOfHandler */
598 CallbackForAllBags( SaveBagData );
599 CloseAfterSave();
600 }
601
602 /* Finally, reset all the link words */
603 CallbackForAllBags( RemoveSaveIndex );
604
605 /* Restore situation by calling all post-save methods */
606 ModulesPostSave();
607
608 return result;
609 #endif
610 }
611
612
FuncSaveWorkspace(Obj self,Obj filename)613 static Obj FuncSaveWorkspace(Obj self, Obj filename)
614 {
615 return SaveWorkspace( filename );
616 }
617
618
619 /***************************************************************************
620 **
621 *F LoadWorkspace( <fname> ) . . . . . load the workspace to the named file
622 **
623 ** 'LoadWorkspace' is the entry point to the workspace saving. It is not
624 ** installed as a GAP function, but instead called from InitGap when the
625 ** -L commad-line flag is given
626 **
627 ** The file saveload.tex in the dev directory describes the saved format
628 ** in more detail. Most of the work will be done from inside GASMAN, because
629 ** we need to fiddle with Bag internals somewhat
630 **
631 */
632
633
LoadWorkspace(Char * fname)634 void LoadWorkspace( Char * fname )
635 {
636 #ifndef USE_GASMAN
637 Pr("LoadWorkspace is only supported when GASMAN is in use\n",0,0);
638
639 #else
640 UInt nGlobs, nBags, i, maxSize;
641 Char buf[256];
642 Obj * glob;
643
644 /* Open saved workspace */
645 OpenForLoad( fname );
646
647 /* Check file header */
648
649 LoadCStr(buf,256);
650 if (strncmp (buf, "GAP ", 4) != 0) {
651 Pr("File %s does not appear to be a GAP workspae.\n", (long) fname, 0L);
652 SyExit(1);
653 }
654
655 if (strcmp (buf, "GAP workspace") == 0) {
656
657 LoadCStr(buf,256);
658 if (strcmp(buf, GetKernelDescription()) != 0) {
659 Pr("This workspace is not compatible with GAP kernel (%s, present: "
660 "%s).\n",
661 (long)buf, (long)GetKernelDescription());
662 SyExit(1);
663 }
664
665 LoadCStr(buf,256);
666 #ifdef SYS_IS_64_BIT
667 if (strcmp(buf,"64 bit") != 0)
668 #else
669 if (strcmp(buf,"32 bit") != 0)
670 #endif
671 {
672 Pr("This workspace was created by a %s version of GAP.\n", (long)buf, 0L);
673 SyExit(1);
674 }
675 } else {
676 Pr("File %s probably isn't a GAP workspace.\n", (long)fname, 0L);
677 SyExit(1);
678 }
679
680 CheckEndiannessMarker();
681
682 LoadCStr(buf,256);
683 if (strcmp(buf,"Counts and Sizes") != 0)
684 {
685 Panic("Bad divider");
686 }
687
688 nGlobs = LoadUInt();
689 nBags = LoadUInt();
690 maxSize = LoadUInt();
691
692 /* Make sure there is enough room, and signal GASMAN that
693 we are starting a restore */
694 StartRestoringBags(nBags, maxSize);
695
696 /* The restoring kernel must have at least as many compiled modules
697 as the saving one. */
698 LoadCStr(buf,256);
699 if (strcmp(buf,"Loaded Modules") != 0)
700 {
701 Panic("Bad divider");
702 }
703 LoadModules();
704
705 /* Now the kernel variables that point into the workspace */
706 LoadCStr(buf,256);
707 if (strcmp(buf,"Kernel to WS refs") != 0)
708 {
709 Panic("Bad divider");
710 }
711 SortGlobals(2); /* globals by cookie for quick
712 lookup */
713 for (i = 0; i < GlobalBags.nr; i++)
714 {
715 GAP_ASSERT(GlobalBags.cookie[i] != NULL);
716 }
717 // TODO: the goal here is to stop exporting `GlobalBags` completely...
718 if (nGlobs != GlobalBags.nr) {
719 Panic("Wrong number of global bags in saved workspace %d %d",
720 (int)nGlobs, (int)GlobalBags.nr);
721 }
722 for (i = 0; i < nGlobs; i++) {
723 LoadCStr(buf, 256);
724 glob = GlobalByCookie(buf);
725 if (glob == (Obj *)0) {
726 Panic(
727 "Global object cookie from workspace not found in kernel %s",
728 buf);
729 }
730 *glob = LoadSubObj();
731 if (SyDebugLoading)
732 Pr("Restored global %s\n", (Int)buf, 0L);
733 }
734
735 LoadCStr(buf,256);
736 if (strcmp(buf,"Bag data") != 0)
737 {
738 Panic("Bad divider");
739 }
740
741 SortHandlers(2);
742 for (i = 0; i < nBags; i++)
743 LoadBagData();
744
745 FinishedRestoringBags();
746
747 CloseAfterLoad();
748 #endif
749
750 ModulesPostRestore();
751 }
752
PrSavedObj(UInt x)753 static void PrSavedObj( UInt x)
754 {
755 if ((x & 3) == 1)
756 Pr("Immediate integer %d\n", INT_INTOBJ((Obj)x),0L);
757 else if ((x & 3) == 2)
758 Pr("Immediate FFE %d %d\n", VAL_FFE((Obj)x), SIZE_FF(FLD_FFE((Obj)x)));
759 else
760 Pr("Reference to bag number %d\n",x>>2,0L);
761 }
762
FuncDumpWorkspace(Obj self,Obj fname)763 static Obj FuncDumpWorkspace(Obj self, Obj fname)
764 {
765 UInt nMods, nGlobs, nBags, i, relative;
766 Char buf[256];
767 OpenForLoad( CONST_CSTR_STRING(fname) );
768 LoadCStr(buf,256);
769 Pr("Header string: %s\n",(Int) buf, 0L);
770 LoadCStr(buf,256);
771 Pr("GAP Version: %s\n",(Int)buf, 0L);
772 LoadCStr(buf,256);
773 Pr("Word length: %s\n",(Int)buf, 0L);
774 CheckEndiannessMarker();
775 LoadCStr(buf,256);
776 Pr("Divider string: %s\n",(Int)buf,0L);
777 if (strcmp(buf,"Counts and Sizes") != 0)
778 ErrorQuit("Bad divider",0L,0L);
779 Pr("Loaded modules: %d\n",nMods = LoadUInt(), 0L);
780 Pr("Global Bags : %d\n",nGlobs = LoadUInt(), 0L);
781 Pr("Total Bags : %d\n",nBags = LoadUInt(), 0L);
782 Pr("Maximum Size : %d\n",sizeof(Bag)*LoadUInt(), 0L);
783 LoadCStr(buf,256);
784 Pr("Divider string: %s\n",(Int)buf, 0L);
785 if (strcmp(buf,"Loaded Modules") != 0)
786 ErrorQuit("Bad divider",0L,0L);
787 for (i = 0; i < nMods; i++)
788 {
789 UInt type;
790 type = LoadUInt();
791 Pr("Type: %d ",type,0);
792 relative = LoadUInt();
793 if (relative)
794 Pr("GAP root relative ", 0L, 0L);
795 else
796 Pr("absolute ", 0L, 0L);
797 LoadCStr(buf,256);
798 Pr(" %s\n",(Int)buf,0L);
799 }
800 LoadCStr(buf,256);
801 Pr("Divider string: %s\n",(Int)buf,0L);
802 if (strcmp(buf,"Kernel to WS refs") != 0)
803 ErrorQuit("Bad divider",0L,0L);
804 for (i = 0; i < nGlobs; i++)
805 {
806 LoadCStr(buf,256);
807 Pr(" %s ",(Int)buf,0L);
808 PrSavedObj(LoadUInt());
809 }
810 LoadCStr(buf,256);
811 Pr("Divider string: %s\n",(Int)buf,0L);
812 if (strcmp(buf,"Bag data") != 0)
813 ErrorQuit("Bad divider",0L,0L);
814 CloseAfterLoad();
815 return (Obj) 0;
816 }
817
818
819 /****************************************************************************
820 **
821 *F * * * * * * * * * * * * * initialize module * * * * * * * * * * * * * * *
822 */
823
824
825 /****************************************************************************
826 **
827 *V GVarFuncs . . . . . . . . . . . . . . . . . . list of functions to export
828 */
829 static StructGVarFunc GVarFuncs [] = {
830
831 GVAR_FUNC(SaveWorkspace, 1, "fname"),
832 GVAR_FUNC(DumpWorkspace, 1, "fname"),
833 #ifdef USE_GASMAN
834 GVAR_FUNC(FindBag, 3, "minsize, maxsize, tnum"),
835 GVAR_FUNC(BagStats, 1, "filename"),
836 #endif
837 { 0, 0, 0, 0, 0 }
838
839 };
840
841
842 /****************************************************************************
843 **
844 *F InitKernel( <module> ) . . . . . . . . initialise kernel data structures
845 */
InitKernel(StructInitInfo * module)846 static Int InitKernel (
847 StructInitInfo * module )
848 {
849 SaveFile = -1;
850 LBPointer = LoadBuffer;
851 LBEnd = LoadBuffer;
852
853 /* init filters and functions */
854 InitHdlrFuncsFromTable( GVarFuncs );
855 /* allow ~/... expansion in SaveWorkspace */
856 ImportFuncFromLibrary("UserHomeExpand", &userHomeExpand);
857
858 /* return success */
859 return 0;
860 }
861
862
863 /****************************************************************************
864 **
865 *F InitLibrary( <module> ) . . . . . . . initialise library data structures
866 */
InitLibrary(StructInitInfo * module)867 static Int InitLibrary (
868 StructInitInfo * module )
869 {
870 /* init filters and functions */
871 InitGVarFuncsFromTable( GVarFuncs );
872
873 /* return success */
874 return 0;
875 }
876
877
878 /****************************************************************************
879 **
880 *F InitInfoSaveLoad() . . . . . . . . . . . . . . . table of init functions
881 */
882 static StructInitInfo module = {
883 // init struct using C99 designated initializers; for a full list of
884 // fields, please refer to the definition of StructInitInfo
885 .type = MODULE_BUILTIN,
886 .name = "saveload",
887 .initKernel = InitKernel,
888 .initLibrary = InitLibrary,
889 };
890
InitInfoSaveLoad(void)891 StructInitInfo * InitInfoSaveLoad ( void )
892 {
893 return &module;
894 }
895