/* preserve.c Copyright (c) Codemist Ltd, 1990-2002 */
/*
* This code may be used and modified, and redistributed in binary
* or source form, subject to the "CCL Public License", which should
* accompany it. This license is a variant on the BSD license, and thus
* permits use of code derived from this in either open and commercial
* projects: but it does require that updates to this code be made
* available back to the originators of the package.
* Before merging other code in with this or linking this code
* with other packages or libraries please check that the license terms
* of the other material are compatible with those of this.
*/
/* Signature: 1936b719 10-Oct-2002 */
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "machine.h"
#include "version.h"
#include "tags.h"
#include "cslerror.h"
#include "externs.h"
#include "arith.h"
#include "read.h"
#include "stream.h"
#ifdef TIMEOUT
#include "timeout.h"
#endif
/*
* I perform file compression when making checkpoint images.
* This is achieved by having procedure Cfwrite and Cfread which
* are much like fwrite and fread but which are entitled to use a
* squashed format on the external medium. It is fairly important that
* Cfread should be reasonably fast - Cfwrite is just used by (preserve)
* and is not so critical. The overall compression strategy implemented
* here is a variant on LZ - the compressed file is made up of 12-bit
* characters. The first 256 codes stand for bytes present in the original
* data, while the remaining codes get allocated to stand for pairs of
* characters found adjacent in the data. Initial experiments show that
* the simple version of the idea implemented here squashes binary image
* files to about 60% of their original size, while more elaborate
* schemes can not do MUCH better.
*/
int32 compression_worth_while = 128;
#ifndef DEMO_MODE
static void Cfwrite(char *a, int32 size)
{
/*
* I keep a table showing how single 12-bit characters in the
* compressed file map onto character-pairs in the original. The
* field "where" in this table is notionally a quite separate
* array, used to give hashed access to compressed codes. The table is
* only needed at startup time and when I am dumping a checkpoint file -
* in each case nothing else is one the stack, so since the table is
* only 16/32 Kbytes or so I allocate it on the stack: this code is only
* used when the stack is otherwise almost empty. Well actually
* with the introduction of the (checkpoint) function that can be
* used to dump images whatever else is going on the stack may not
* be so empty after all. I will nevertheless continue to allocate
* my buffers on it.
*/
unsigned char pair_c[CODESIZE]; /* 4 Kbytes */
unsigned short int pair_prev[CODESIZE], pair_where[CODESIZE];
/* 16 Kbytes */
unsigned char *p = (unsigned char *)a;
int32 n = size, i;
unsigned32 prev1;
int hash, step, half;
unsigned int next_code, prev, c;
if (size < compression_worth_while)
{ if (size != 0) Iwrite(a, size);
return;
}
/*
* Clear the hash table and indicate that the next code to allocate is 256
*/
memset(pair_where, 0, sizeof(pair_where));
next_code = 256;
/*
* I deal with the first two characters by hand since they can not be
* subject to compression. After these first two I can apply uniform
* processing.
*/
prev = *p++;
c = *p++;
/*
* The hash function I use is not especially scientific, but a couple of
* exclusive-or operations and a shift will be cheap to compute, and I
* can eventually expect prev to be fairly evenly distributed, while the
* distribution of c depends a lot on what sort of data is in the file.
*/
hash = prev ^ c ^ (c << 5);
prev1 = ((unsigned32)hash << 20) | ((unsigned32)prev << 8) | c;
Iputc(prev >> 4);
half = prev & 0xf;
prev = c;
for (i=2; i<n; i++)
{ c = *p++;
hash = (prev ^ c ^ (c << 5)) & 0xfff;
step = (prev - (c << 4)) | 1;
/*
* I compute a hash value, and also a secondary hash to be used when
* making repeated probes. Since the table has size that is a power of
* two I will be OK provided by step is an odd number. When I am finished
* the table will have 4096-256 entries in it, i.e. it will be 94% full,
* so access to it will take about 16 probes to discover that some
* item is not present.
*/
for (;;)
{ int where = pair_where[hash];
if (where == 0) break;
if (pair_prev[where] == prev &&
pair_c[where] == c)
{ prev = where; /* squash 2 chars together */
hash = -1; /* set a flag to indicate it was done */
break;
}
hash = (hash + step) & 0xfff;
}
if (hash >= 0)
{
/*
* There is a delicacy here - so that the uncompression process can
* build its decoding tables on the fly I must delay entering items into
* the compression tables by about one character of output. This is
* achieved by keeping details of what is to be inserted stored in the
* variable "prev1", which is activated here.
* When all 4096 codes have been allocated I just flush out the
* table and start afresh. A scheme that started with 9-bit chunks and
* grew up to use longer ones up to (say) 15 or 16 bits could give
* significantly better compression, but at the cost of both more
* workspace here and (what is more to the point) seriously extra
* overhead picking bit-fields of variable length out of the stream of
* bytes in files.
*/
if (next_code >= CODESIZE)
{ memset(pair_where, 0, sizeof(pair_where));
next_code = 256;
}
else
{ pair_where[prev1 >> 20] = (unsigned short int)next_code;
pair_prev[next_code] =
(unsigned short int)(prev1 >> 8) & 0xfff;
pair_c[next_code] = (unsigned char)prev1;
next_code++;
}
/*
* Now the mess of collecting 12 bit items and paching them into sequences
* of 8 bit bytes.
*/
if (half < 0)
{ Iputc(prev >> 4);
half = prev & 0xf;
}
else
{ Iputc((half << 4) | ((prev >> 8) & 0xf));
Iputc(prev);
half = -1;
}
/*
* record the information that the decoder will in due course see.
*/
prev1 = ((unsigned32)hash << 20) | ((unsigned32)prev << 8) | c;
prev = c;
}
}
/*
* Now I have to flush out the final buffered character
*/
if (half < 0)
{ Iputc(prev >> 4);
Iputc(prev << 4);
}
else
{ Iputc((half << 4) | ((prev >> 8) & 0xf));
Iputc(prev);
}
}
#endif /* DEMO_MODE */
/*
* These routines pack multiple binary files into one big one. The
* good effect is that I expect fseek to be faster than fopen, and as
* a result accessing fasl files will be faster. The bad news is that
* when I update files I may need to compact them, and doing so will
* be very tedious. In this model I do not permit arbitrary interleaving
* of read and write operations.
*/
static void set_dirused(directory_header *h, int v)
{
h->dirused = (unsigned char)(v & 0xff);
h->dirext = (unsigned char)((h->dirext & 0xf0) + ((v>>8) & 0x0f));
}
static directory empty_directory =
{
{'C', MIDDLE_INITIAL, 'L', IMAGE_FORMAT_VERSION,
0, 0, 0, 0, 0},
NULL,
"EmptyFile",
{"\nEmpty ** *** not dated *** **"}
};
/*
* In a way that may look clumsy I store file offsets and lengths as
* sequences of three or four characters. The object of this
* explicit control over memory layout is so that directories produced by
* this code have a layout that is not sensitive to the byte-order used
* by the computer involved. I also put a few newline characters into
* my directory structure so that if one uses an ordinary text editor to
* inspect an image file the set of modules and their datestamps should
* be easily visible.
*/
static int32 bits32(char *v)
{
int32 r = v[3] & 0xff;
r = (r << 8) | (v[2] & 0xff);
r = (r << 8) | (v[1] & 0xff);
return (r << 8) | (v[0] & 0xff);
}
static int32 bits24(char *v)
{
int32 r = v[2] & 0xff;
r = (r << 8) | (v[1] & 0xff);
return (r << 8) | (v[0] & 0xff);
}
static void setbits32(char *v, int32 r)
{
*v++ = (char)r;
*v++ = (char)(r >> 8);
*v++ = (char)(r >> 16);
*v = (char)(r >> 24);
}
static void setbits24(char *v, int32 r)
{
*v++ = (char)r;
*v++ = (char)(r >> 8);
*v = (char)(r >> 16);
}
static directory *current_input_directory;
static directory_entry *current_output_entry;
static directory *current_output_directory = NULL;
static CSLbool any_output_request;
static char would_be_output_directory[DIRNAME_LENGTH];
#define I_INACTIVE 0
#define I_READING 1
#define I_WRITING 2
static int Istatus = I_INACTIVE;
FILE *binary_read_file;
static FILE *binary_write_file;
static unsigned32 subfile_checksum;
static long int read_bytes_remaining, write_bytes_written;
directory *fasl_files[MAX_FASL_PATHS];
static directory *make_empty_directory(char *name)
/*
* The sole purpose of this empty directory is to carry with it the
* name of the file that I had tried to open.
*/
{
directory *d;
d = (directory *) malloc(sizeof(directory) - sizeof(directory_entry));
if (d == NULL) return &empty_directory;
d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
d->h.version = IMAGE_FORMAT_VERSION;
d->h.dirsize = 0;
d->h.dirused = 0;
d->h.dirext = 0;
d->h.updated = 0; /* NB read-only */
d->f = NULL;
strncpy(d->filename, name, DIRNAME_LENGTH);
d->filename[DIRNAME_LENGTH-1] = 0;
memset(d->h.eof, 0, 4);
return d;
}
static directory *make_pending_directory(char *name)
/*
* The sole purpose of this empty directory is to carry with it the
* name of the file that I had tried to open.
*/
{
directory *d;
int n = sizeof(directory) + (DIRECTORY_SIZE-1)*sizeof(directory_entry);
int l = strlen(name) + 1 -
DIRNAME_LENGTH -
DIRECTORY_SIZE*sizeof(directory_entry);
/*
* Here I extend the directory header with enough extra bytes to hold the
* full name of the file... Once the file has been opened the (potential)
* extra data becomes unnecessary. However with room for DIRECTORY_SIZE
* entries already it would seem bizarre if the path-name ever actually
* overflowed here.
*/
if (l > 0) n += l;
d = (directory *)malloc(n);
if (d == NULL) return &empty_directory;
d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
d->h.version = IMAGE_FORMAT_VERSION;
d->h.dirsize = DIRECTORY_SIZE & 0xff;
d->h.dirused = 0;
d->h.dirext = (DIRECTORY_SIZE >> 4) & 0xf0;
d->h.updated = D_PENDING | D_WRITE_OK;
/* Well I HOPE that writing will be OK */
d->f = NULL;
strcpy(d->filename, name); /* guaranteed enough space here */
memset(d->h.eof, 0, 4);
return d;
}
static void clear_entry(directory_entry *d)
{
d->D_newline = NEWLINE_CHAR;
memset(&d->D_name, ' ', name_size);
memcpy(&d->D_name, "<Unused>", 8);
memset(&d->D_date, ' ', date_size);
(&d->D_date)[0] = '-';
memset(&d->D_position, 0, 4);
memset(&d->D_size, 0, 3);
}
static CSLbool version_moan(int v)
{
#if defined DEMO_MODE || defined DEMO_BUILD
if (v == 'd') return NO;
term_printf("\n");
term_printf("+++++ This image file is either corrupted or was not\n");
term_printf("+++++ built for use with the Demonstration version.\n");
term_printf("+++++ Unable to proceed - sorry.\n");
#else
if (v == IMAGE_FORMAT_VERSION ||
v == IMAGE_FORMAT_VERSION-1) return NO;
term_printf("\n");
if (v == 'd')
{ term_printf("+++++ This image file was built for use with the Demonstration\n");
term_printf("+++++ version of this software and can not be used with the\n");
term_printf("+++++ full product.\n");
}
else
{ term_printf("+++++ This image file seems to be from an old or incompatible\n");
term_printf("+++++ version of the software. Please check it by re-installing\n");
term_printf("+++++ or re-building.\n");
}
#endif
return YES;
}
directory *open_pds(char *name, CSLbool forinput)
/*
* Given a file-name, open the associated file, make space for
* a directory and return same.
*/
{
char expanded[LONGEST_LEGAL_FILENAME];
directory hdr, *d;
CSLbool write_OK = NO;
FILE *f;
int l, i, n;
l = strlen(name);
f = NULL;
/*
* If you are using "-z" for a cold start you may sometimes want to
* delete the image file (by hand) before running CSL
*/
if (!forinput)
{
#ifdef DEMO_MODE
f = NULL;
#else
f = open_file(expanded, name, l, "r+b", NULL);
any_output_request = YES;
strncpy(would_be_output_directory, expanded, DIRNAME_LENGTH-1);
if (f != NULL) write_OK = YES;
else
{
/*
* I first try to open in "r+" mode, which leaves data alone if there
* is already some in the file. If that fails, I try "w+" which can
* create a new file for me.
*/
f = open_file(expanded, name, l, "w+b", NULL);
if (f != NULL) write_OK = YES;
}
#endif /* DEMO_MODE */
}
/*
* If I wanted the file for input or if I tried it for output and failed
* then I open for input.
*/
if (f == NULL) f = open_file(expanded, name, l, "rb", NULL);
/*
* If the file does not exist I will just hand back a directory that shows
* no files in it. This seems as easy a thing to do at this stage as I can
* think of. Maybe I should warn the user?
*/
if (f == NULL) return make_empty_directory(expanded);
#ifndef SIXTEEN_BIT
#define BUFFER_SIZE 0x10000 /* Use 64 Kbyte buffers for extra speed? */
{ char *buffer = (char *)malloc(BUFFER_SIZE);
if (buffer != NULL) setvbuf(f, buffer, _IOFBF, BUFFER_SIZE);
}
#endif
fseek(f, 0, SEEK_SET); /* Ensure I am at start of the file */
hdr.h.C = hdr.h.S = hdr.h.L = 0;
if (fread(&hdr.h, sizeof(directory_header), 1, f) != 1 ||
hdr.h.C != 'C' ||
hdr.h.S != MIDDLE_INITIAL ||
hdr.h.L != 'L' ||
/*
* Image format versions are somewhat delicate things. I will not change
* this format often or lightly and the tests I make will then be set up to
* cope with updates from the immediately previous version. The testing code
* will need review each time I consider such a change. For the current
* upgrade I will allow opening of files from version N-1, but I will
* specifically lock out reading an initial heap-image from such. The issue
* of people who start with an old file and then write a fresh image back into
* it will be viewed as too messy to worry about in detail, but I hope that
* I have made it so that writing a new base image (via PRESERVE) updates the
* version info.
*/
version_moan(hdr.h.version) ||
get_dirused(hdr.h) > get_dirsize(hdr.h) ||
bits32(hdr.h.eof) < sizeof(directory_header))
{
/*
* Here I did not find a satisfactory header to the directory. If I wanted
* to open the file for input I just return an empty directory, otherwise I
* need to create a new one.
*/
if (!write_OK) return make_empty_directory(expanded);
fseek(f, 0, SEEK_SET);
n = DIRECTORY_SIZE; /* Size for a directory */
d = (directory *)
malloc(sizeof(directory)+(n-1)*sizeof(directory_entry));
if (d == NULL) return &empty_directory;
d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
d->h.version = IMAGE_FORMAT_VERSION;
d->h.dirsize = (unsigned char)(n & 0xff);
d->h.dirused = 0;
d->h.dirext = (unsigned char)((n >> 4) & 0xf0);
d->h.updated = D_WRITE_OK | D_UPDATED;
for (i=0; i<n; i++) clear_entry(&d->d[i]);
if (fwrite(&d->h, sizeof(directory_header), 1, f) != 1)
return make_empty_directory(expanded);
if (fwrite(&d->d[0], sizeof(directory_entry), (size_t)n, f) != (size_t)n)
return make_empty_directory(expanded);
d->f = f;
strncpy(d->filename, expanded, DIRNAME_LENGTH);
d->filename[DIRNAME_LENGTH-1] = 0;
if (fwrite(registration_data, REGISTRATION_SIZE, 1, f) != 1)
return make_empty_directory(expanded);
setbits32(d->h.eof, (int32)ftell(f));
return d;
}
hdr.h.updated = write_OK ? D_WRITE_OK : 0;
n = get_dirsize(hdr.h);
d = (directory *)
malloc(sizeof(directory)+(n-1)*sizeof(directory_entry));
if (d == NULL) return &empty_directory;
memcpy(&d->h, &hdr.h, sizeof(directory_header));
if (fread(&d->d[0], sizeof(directory_entry), (size_t)n, f) != (size_t)n)
return make_empty_directory(expanded);
/*
* Here the directory seemed OK
*/
d->f = f;
strncpy(d->filename, expanded, DIRNAME_LENGTH);
d->filename[DIRNAME_LENGTH-1] = 0;
/*
* For binary files ANSI specify that the values used with fseek and ftell
* are simple counts of the number of characters in the file, and hence
* it is proper to save ftell() values from one run to the next.
*/
return d;
}
directory *open_default_output_pds(char *name)
/*
* Given a file-name check if the file exists already. If so try to open
* it writable, and if that fails fall back to opening it read-only.
* if it does NOT exist yet then defer creating it until the first
* write operation on it is attempted.
*/
{
char expanded[LONGEST_LEGAL_FILENAME];
directory hdr, *d;
CSLbool write_OK = NO;
FILE *f;
int l, i, n;
l = strlen(name);
f = NULL;
#ifndef DEMO_MODE
/*
* See if I can read from the file. If so it must exist, so close it and
* try again for output.
*/
f = open_file(expanded, name, l, "r+b", NULL);
any_output_request = YES;
strncpy(would_be_output_directory, expanded, DIRNAME_LENGTH-1);
if (f != NULL) write_OK = YES;
else
{
/*
* I first try to open in "r+" mode, which leaves data alone if there
* is already some in the file. If that fails, I will hand back a special
* variant on an empty directory.
*/
f = open_file(expanded, name, l, "rb", NULL);
if (f == NULL) return make_pending_directory(expanded);
}
#endif /* DEMO_MODE */
/*
* If the file exists but I could not open it for output then I will
* use it read-only.
*/
if (f == NULL) f = open_file(expanded, name, l, "rb", NULL);
/*
* If the file does not exist I will just hand back a directory that shows
* no files in it. This seems as easy a thing to do at this stage as I can
* think of. Maybe I should warn the user?
*/
if (f == NULL) return make_empty_directory(expanded);
#ifndef SIXTEEN_BIT
#define BUFFER_SIZE 0x10000 /* Use 64 Kbyte buffers for extra speed? */
{ char *buffer = (char *)malloc(BUFFER_SIZE);
if (buffer != NULL) setvbuf(f, buffer, _IOFBF, BUFFER_SIZE);
}
#endif
fseek(f, 0, SEEK_SET); /* Ensure I am at start of the file */
if (fread(&hdr.h, sizeof(directory_header), 1, f) != 1 ||
hdr.h.C != 'C' ||
hdr.h.S != MIDDLE_INITIAL ||
hdr.h.L != 'L' ||
version_moan(hdr.h.version) ||
get_dirused(hdr.h) > get_dirsize(hdr.h) ||
bits32(hdr.h.eof) < sizeof(directory_header))
{
/*
* Here I did not find a satisfactory header to the directory. If I wanted
* to open the file for input I just return an empty directory, otherwise I
* need to create a new one.
*/
if (!write_OK) return make_empty_directory(expanded);
fseek(f, 0, SEEK_SET);
n = DIRECTORY_SIZE; /* Size for a directory */
d = (directory *)
malloc(sizeof(directory)+(n-1)*sizeof(directory_entry));
if (d == NULL) return &empty_directory;
d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
d->h.version = IMAGE_FORMAT_VERSION;
d->h.dirsize = (unsigned char)(n & 0xff);
d->h.dirused = 0;
d->h.dirext = (unsigned char)((n >> 4) & 0xf0);
d->h.updated = D_WRITE_OK | D_UPDATED;
for (i=0; i<n; i++) clear_entry(&d->d[i]);
if (fwrite(&d->h, sizeof(directory_header), 1, f) != 1)
return make_empty_directory(expanded);
if (fwrite(&d->d[0], sizeof(directory_entry), (size_t)n, f) != (size_t)n)
return make_empty_directory(expanded);
d->f = f;
strncpy(d->filename, expanded, DIRNAME_LENGTH);
d->filename[DIRNAME_LENGTH-1] = 0;
if (fwrite(registration_data, REGISTRATION_SIZE, 1, f) != 1)
return make_empty_directory(expanded);
setbits32(d->h.eof, (int32)ftell(f));
return d;
}
hdr.h.updated = write_OK ? D_WRITE_OK : 0;
n = get_dirsize(hdr.h);
d = (directory *)
malloc(sizeof(directory)+(n-1)*sizeof(directory_entry));
if (d == NULL) return &empty_directory;
memcpy(&d->h, &hdr.h, sizeof(directory_header));
if (fread(&d->d[0], sizeof(directory_entry), (size_t)n, f) != (size_t)n)
return make_empty_directory(expanded);
/*
* Here the directory seemed OK
*/
d->f = f;
strncpy(d->filename, expanded, DIRNAME_LENGTH);
d->filename[DIRNAME_LENGTH-1] = 0;
/*
* For binary files ANSI specify that the values used with fseek and ftell
* are simple counts of the number of characters in the file, and hence
* it is proper to save ftell() values from one run to the next.
*/
return d;
}
static int unpending(directory *d)
{
FILE *f = fopen(d->filename, "w+b");
int32 i, n;
if (f == NULL) return YES;
d->f = f;
d->filename[DIRNAME_LENGTH-1] = 0; /* truncate the name now */
n = DIRECTORY_SIZE; /* Size for a directory */
/* (the next bits were done when the pending directory was first created
d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
d->h.version = IMAGE_FORMAT_VERSION;
d->h.dirsize = n & 0xff;
d->h.dirused = 0;
d->h.dirext = (n >> 4) & 0xf0;
*/
d->h.updated = D_WRITE_OK | D_UPDATED;
for (i=0; i<n; i++) clear_entry(&d->d[i]);
if (fwrite(&d->h, sizeof(directory_header), 1, f) != 1)
return YES;
if (fwrite(&d->d[0], sizeof(directory_entry), (size_t)n, f) != (size_t)n)
return YES;
if (fwrite(registration_data, REGISTRATION_SIZE, 1, f) != 1)
return YES;
setbits32(d->h.eof, (int32)ftell(f));
return NO;
}
void Iinit(void)
{
int i;
Istatus = I_INACTIVE;
current_input_directory = NULL;
current_output_entry = NULL;
current_output_directory = NULL;
binary_read_file = binary_write_file = NULL;
read_bytes_remaining = write_bytes_written = 0;
any_output_request = NO;
strcpy(would_be_output_directory, "<unknown>");
for (i=0; i<number_of_fasl_paths; i++)
{ if (0x40000000+i == output_directory)
fasl_files[i] = open_default_output_pds(fasl_paths[i]);
else
fasl_files[i] = open_pds(fasl_paths[i], i != output_directory);
}
MD5_Update((unsigned char *)"Copyright 1997 Codemist Ltd", 24);
}
void Icontext(Ihandle *where)
/*
* This and the next are used so that reading from binary files can be
* nested, as may be needed while loading fasl files. An Ihandle should
* be viewed as an abstract handle on the input stream. Beware however that
* if input is from a regular Lisp stream (indicated by read_bytes_remaining
* being negative) that standard_input is NOT saved here. Therefore in
* some cases it needs to be stacked elsewhere. The reason I do not save
* it here is that it is a Lisp_Object and needs garbage collection
* protection, which is not easily provided here.
*/
{
switch (where->status = Istatus)
{
case I_INACTIVE:
break;
case I_READING:
where->f = binary_read_file;
if (read_bytes_remaining >= 0) where->o = ftell(binary_read_file);
where->n = read_bytes_remaining;
where->chk = subfile_checksum;
break;
case I_WRITING:
where->f = binary_write_file;
where->o = ftell(binary_write_file);
where->n = write_bytes_written;
where->chk = subfile_checksum;
break;
}
Istatus = I_INACTIVE;
}
void Irestore_context(Ihandle x)
{
switch (Istatus = x.status)
{
case I_INACTIVE:
return;
case I_READING:
binary_read_file = x.f;
read_bytes_remaining = x.n;
if (read_bytes_remaining >= 0) fseek(binary_read_file, x.o, SEEK_SET);
subfile_checksum = x.chk;
return;
case I_WRITING:
binary_write_file = x.f;
fseek(binary_write_file, x.o, SEEK_SET);
write_bytes_written = x.n;
subfile_checksum = x.chk;
return;
}
}
#define IMAGE_CODE (-1000)
#define HELP_CODE (-1001)
#define BANNER_CODE (-1002)
/*
* The code here was originally written to support module names up to
* 11 characters, but it has now been extended to support long names as
* well.
* The mechanism used is as follows:
* The name field in a directory entry is 12 characters long. For system
* special pseudo-modules all 12 characters are used for a name, and the
* cases used at present are InitialImage and HelpDataFile. For all
* user names the name is padded with blanks, and so user names of up
* to 11 characters live in the field with the 12th character a blank.
* To support long names I use values 0x80 and up in this 12th position.
* (NB position 12 is at offset 11 because of zero-base counting!)
* The first segment of a long name uses 11 characters of the user name
* and puts 0x80 in the 12th. Subsequent directory entries are used
* to hold more characters of the name. These hold 11 characters in the
* name field and 24 in the date, and put values 0x81, 0x82 etc in
* character 12. They will have a zero length field, but their position
* field MUST match that of the first record. This requirement is so that
* when I sort a directory the parts of a long name are kept both
* together and in the correct order. The last part of a long name has
* 0xff in position 12. The result is that I can distinguish the case
* of
* a regular username of up to 11 chars (blank in position 12)
* a system special file (non-bloank, but under 0x80 in posn 12)
* the start of a long name (0x80)
* a middle part of a long name (0x81 ...)
* the final part of a long name (0xff).
* when I match names here I will only allow a long-name match if my
* directory is pointing at the first part of a long name.
* As a further minor usefulness here if I find a match the non-zero value I
* return is the number of entries involved.
*/
static int samename(char *n1, directory *d, int j, int len)
/*
* Compare the given names, given that n1 is of length len and n2 is
* blank-padded to exactly name_size characters. The special cases
* with n1 NULL allow len to encode what I am looking for.
*/
{
char *n2 = &d->d[j].D_name;
int i, n, recs;
if (len == IMAGE_CODE)
return (memcmp(n2, "InitialImage", 12) == 0);
if (len == HELP_CODE)
return (memcmp(n2, "HelpDataFile", 12) == 0);
if (len == BANNER_CODE)
return (memcmp(n2, "Start-Banner", 12) == 0);
if (len < 0)
{ char hard[16];
sprintf(hard, "HardCode<%.2x>", (-len) & 0xff);
return (memcmp(n2, hard, 12) == 0);
}
if ((n2[11] & 0xff) > 0x80) return 0;
n = 0;
#define next_char_of_name (n++ < len ? *n1++ : ' ')
for (i=0; i<11; i++)
if (n2[i] != next_char_of_name) return 0;
if ((n2[11] & 0x80) == 0) return ((n >= len) ? 1 : 0);
recs = 1;
do
{ n2 = &d->d[++j].D_name;
for (i=0; i<11; i++)
if (n2[i] != next_char_of_name) return 0;
for (i=12; i<36; i++)
if (n2[i] != next_char_of_name) return 0;
recs++;
} while ((n2[11] & 0xff) != 0xff);
#undef next_char_of_name
if (n < len) return 0;
else return recs;
}
static CSLbool open_input(directory *d, char *name, int len, int32 offset)
/*
* Set up binary_read_file to access the given module, returning YES
* if it was not found in the given directory. I used to pass the
* names "InitialImage" and "HelpDataFile" directly to this function, but
* to allow for long module names I am changing things so that these special
* cases are indicated by passing down a NULL string for the name and giving
* an associated length of -1 or -2 (resp).
*/
{
int i;
if (Istatus != I_INACTIVE || d == NULL) return YES;
subfile_checksum = 0;
/*
* I use simple linear search to scan the directory - mainly because I
* expect directories to be fairly small and once I have found a file
* I will take a long while to process it, so any clumsiness here is
* not critical. I will not allow myself to read from whichever file
* is currently open for output.
* Because samename() is careful to ensure it only reports a match when
* pointed at the start of a long name it is OK to search in steps of 1
* here.
*/
for (i=0; i<get_dirused(d->h); i++)
{ if (samename(name, d, i, len) &&
&d->d[i] != current_output_entry)
{ binary_read_file = d->f;
read_bytes_remaining = bits24(&d->d[i].D_size);
i = fseek(binary_read_file,
bits32(&d->d[i].D_position)+offset, SEEK_SET);
if (i == 0) /* If fseek succeeded it returned zero */
{ Istatus = I_READING;
return NO;
}
else return YES;
}
}
return YES;
}
void IreInit(void)
{
MD5_Update((unsigned char *)"Copyright 1997 Codemist Ltd", 24);
MD5_Update((unsigned char *)"memory.u", 8);
}
static int MS_CDECL for_qsort(void const *aa, void const *bb)
{
directory_entry *a = (directory_entry *)aa,
*b = (directory_entry *)bb;
long int ap = bits32(&a->D_position), bp = bits32(&b->D_position);
if (ap < bp) return -1;
else if (ap > bp) return 1;
/*
* I make the position of the module in the image my primary sort key.
* Over-long module names are coped with by giving each part of the
* name the same position field, but marking the 12th character of the
* name field (D_space) with 0x80, 0x81 ... in extension records. Note that
* a regular short module name has a blank character there, while the special
* cases of "InitialImage" and "HelpDataFile" each have 'e' there,
* "Start-Banner" has 'r', while hard code has '>'.
* So bytes 0x80 and up are clearly (if hackily!) distinguished.
*/
ap = a->D_space & 0xff, bp = b->D_space & 0xff;
if (ap < bp) return -1;
else if (ap > bp) return 1;
else return 0;
}
static void sort_directory(directory *d)
{
qsort((void *)d->d, (size_t)get_dirused(d->h),
sizeof(directory_entry), for_qsort);
}
static directory *enlarge_directory(int current_size)
{
nil_as_base
int n = (3*current_size)/2;
int newsize = sizeof(directory)+(n-1)*sizeof(directory_entry);
int newpos = sizeof(directory_header)+n*sizeof(directory_entry);
/*
* enlarge_directory() is only called when an output library is known
* to exist, so I do not need to check for that here.
*/
int dirno = library_number(qvalue(output_library));
directory *d1 = fasl_files[dirno];
if (n > current_size+20) n = current_size+20;
for (;;)
{ directory_entry *first;
FILE *f;
char buffer[512]; /* I hope this is not done too often, since this */
/* is not a very big buffer size for the copy. */
int32 firstpos, firstlen, newfirst, eofpos;
sort_directory(d1);
first = &d1->d[0];
firstpos = bits32(&first->D_position);
if (firstpos >= newpos + REGISTRATION_SIZE) break;
/*
* Here I need to copy a module up to the end of the file to make room
* for the enlarged directory
*/
firstlen = bits24(&first->D_size);
newfirst = eofpos = bits32(d1->h.eof);
f = d1->f;
firstlen += 4; /* Allow for the checksum */
while (firstlen >= sizeof(buffer))
{ fseek(f, firstpos, SEEK_SET);
if (fread(buffer, sizeof(buffer), 1, f) != 1) return NULL;
fseek(f, eofpos, SEEK_SET);
if (fwrite(buffer, sizeof(buffer), 1, f) != 1) return NULL;
firstlen -= sizeof(buffer);
firstpos += sizeof(buffer);
eofpos += sizeof(buffer);
}
if (firstlen != 0)
{ fseek(f, firstpos, SEEK_SET);
if (fread(buffer, firstlen, 1, f) != 1) return NULL;
fseek(f, eofpos, SEEK_SET);
if (fwrite(buffer, firstlen, 1, f) != 1) return NULL;
eofpos += firstlen;
}
setbits32(&first->D_position, newfirst);
if ((first->D_space & 0xff) == 0x80)
{ do
{ first++;
setbits32(&first->D_position, newfirst);
} while ((first->D_space & 0xff) != 0xff);
}
setbits32(d1->h.eof, eofpos);
}
fseek(d1->f, newpos, SEEK_SET);
fwrite(registration_data, REGISTRATION_SIZE, 1, d1->f);
d1 = (directory *)realloc((void *)d1, newsize);
if (d1 == NULL) return NULL;
d1->h.dirsize = (unsigned char)(n & 0xff);
d1->h.dirext = (unsigned char)((d1->h.dirext & 0x0f) + ((n>>4) & 0xf0));
d1->h.updated |= D_COMPACT | D_UPDATED;
while (n>current_size) clear_entry(&d1->d[--n]);
fasl_files[dirno] = d1;
return d1;
}
static CSLbool open_output(char *name, int len)
/*
* Set up binary_write_file to access the given module, returning YES
* if anything went wrong. Remember name==NULL for initial image & help
* data.
*/
{
#ifdef DEMO_MODE
return YES;
#else
nil_as_base
int i, j, n;
char *ct;
char hard[16];
directory *d;
time_t t = time(NULL);
Lisp_Object oo = qvalue(output_library);
if (!is_library(oo)) return YES;
d = fasl_files[library_number(oo)];
if (d == NULL) return YES; /* closed handle, I guess */
if ((d->h.updated & D_WRITE_OK) == 0) return YES;
/*
* The main effect of the next line will be to prohibit opening a new
* FASL file while I am in the middle of reading one that already exists.
* Indeed this is a restriction, but at present it seems a very reasonable
* on for me to apply.
*/
if (Istatus != I_INACTIVE) return YES;
if (d->h.updated & D_PENDING)
{ if (unpending(d)) return YES;
}
subfile_checksum = 0;
current_output_directory = d;
/*
* I use simple linear search to scan the directory - mainly because I
* expect directories to be fairly small and once I have found a file
* I will take a long while to process it, so any clumsiness here is
* not critical. Again note it is OK to scan in steps of 1 despite the
* fact that long-names are stored split across consecutive directory slots.
*/
for (i=0; i<get_dirused(d->h); i++)
{ if (samename(name, d, i, len))
{ current_output_entry = &d->d[i];
d->h.updated |= D_COMPACT | D_UPDATED;
if (t == (time_t)(-1)) ct = "not dated";
else ct = ctime(&t);
/*
* Note that I treat the result handed back by ctime() as delicate, in that
* I do not do any library calls between calling ctime and copying the
* string it returns to somewhere that is under my own control.
*/
strncpy(&d->d[i].D_date, ct, date_size);
binary_write_file = d->f;
write_bytes_written = 0;
memcpy(&d->d[i].D_position, d->h.eof, 4);
/* For long names I must put the location in each record */
if (d->d[i].D_space & 0x80)
{ j = 0;
do
{ j++;
memcpy(&d->d[i+j].D_position, d->h.eof, 4);
} while ((d->d[i+j].D_space & 0xff) != 0xff);
}
i = fseek(binary_write_file, bits32(d->h.eof), SEEK_SET);
if (i == 0) Istatus = I_WRITING;
else current_output_directory = NULL;
if (name == NULL && len == IMAGE_CODE)
d->h.version = IMAGE_FORMAT_VERSION;
return i;
}
}
/*
* Here the name did not already exist, and so I will need to enter it into
* the directory. If I get here the variable i points to the first unused
* directory entry.
*/
if (len == IMAGE_CODE)
{ name = "InitialImage";
n = 1;
d->h.version = IMAGE_FORMAT_VERSION;
}
else if (len == HELP_CODE) name = "HelpDataFile", len = IMAGE_CODE, n = 1;
else if (len == BANNER_CODE) name = "Start-Banner", len = IMAGE_CODE, n = 1;
else if (len < 0)
{ sprintf(hard, "HardCode<%.2x>", (-len) & 0xff);
name = hard, len = IMAGE_CODE, n = 1;
}
else if (len <= 11) n = 1;
else if (len <= 11+11+24) n = 2;
else if (len <= 11+11+11+24+24) n = 3;
else return YES; /* Name longer than 81 chars not supported, sorry */
while (i+n > (int)get_dirsize(d->h))
{ d = enlarge_directory(i);
current_output_directory = d;
if (d == NULL) return YES;
}
current_output_entry = &d->d[i];
if (len == IMAGE_CODE)
{ d->d[i].D_newline = NEWLINE_CHAR;
memcpy(&d->d[i].D_name, name, 12);
memset(&d->d[i].D_date, ' ', date_size);
memset(&d->d[i].D_size, 0, 3);
memcpy(&d->d[i].D_position, d->h.eof, 4);
}
else
{ int np;
char *p;
/*
* First I will clear all the relevant fields to blanks.
*/
for (j=0; j<n; j++)
{ d->d[i+j].D_newline = '\n';
memset(&d->d[i+j].D_name, ' ', name_size);
memset(&d->d[i+j].D_date, ' ', date_size);
memset(&d->d[i+j].D_size, 0, 3);
memcpy(&d->d[i+j].D_position, d->h.eof, 4);
}
#define next_char_of_name (np++ >= len ? ' ' : *p++)
np = 0;
p = name;
for (j=0; j<n; j++)
{ int k;
for (k=0; k<11; k++) (&d->d[i+j].D_name)[k] = next_char_of_name;
if (j != 0)
for (k=0; k<24; k++)
(&d->d[i+j].D_date)[k] = next_char_of_name;
if (j == 0 && n == 1) d->d[i+j].D_space = ' ';
else if (j == n-1) d->d[i+j].D_space = 0xff;
else d->d[i+j].D_space = (char)(0x80+j);
#undef next_char_of_name
}
}
if (t == (time_t)(-1)) ct = "** *** not dated *** ** ";
else ct = ctime(&t);
strncpy(&d->d[i].D_date, ct, date_size);
set_dirused(&d->h, get_dirused(d->h)+n);
binary_write_file = d->f;
write_bytes_written = 0;
d->h.updated |= D_UPDATED;
i = fseek(binary_write_file, bits32(d->h.eof), SEEK_SET);
if (i == 0)
{ Istatus = I_WRITING;
return NO;
}
else
{ current_output_directory = NULL;
return YES;
}
#endif /* DEMO_MODE */
}
static void list_one_library(Lisp_Object oo, CSLbool out_only)
{
int j;
directory *d = fasl_files[library_number(oo)];
trace_printf("\nFile %s (dirsize %ld length %ld",
d->filename, (long)get_dirsize(d->h), (long)bits32(d->h.eof));
j = d->h.updated;
if (j != 0) trace_printf(",");
if (j & D_WRITE_OK) trace_printf(" Writable");
if (j & D_UPDATED) trace_printf(" Updated");
if (j & D_COMPACT) trace_printf(" NeedsCompaction");
if (j & D_PENDING) trace_printf(" Pending");
if (out_only) trace_printf(" OutputOnly");
trace_printf("):\n");
/*
* The format string used here will need adjustment if you ever change the
* number of characters used to store names or dates.
*/
for (j=0; j<get_dirused(d->h); j++)
{ int n = 0;
if (d->d[j].D_space & 0x80)
{ trace_printf(" %.11s", &d->d[j].D_name);
do
{ n++;
trace_printf("%.11s%.24s",
&d->d[j+n].D_name, &d->d[j+n].D_date);
} while ((d->d[j+n].D_space & 0xff) != 0xff);
trace_printf(
"\n %-24.24s position %-7ld size: %ld\n",
&d->d[j].D_date,
(long)bits32(&d->d[j].D_position),
(long)bits24(&d->d[j].D_size));
j += n;
}
else trace_printf(
" %-12.12s %-24.24s position %-7ld size: %ld\n",
&d->d[j].D_name, &d->d[j].D_date,
(long)bits32(&d->d[j].D_position),
(long)bits24(&d->d[j].D_size));
}
}
void Ilist(void)
{
Lisp_Object nil = C_nil;
Lisp_Object il = qvalue(input_libraries), w;
Lisp_Object ol = qvalue(output_library);
while (consp(il))
{ w = qcar(il); il = qcdr(il);
if (!is_library(w)) continue;
if (w == ol) ol = nil;
list_one_library(w, NO);
}
if (is_library(ol)) list_one_library(ol, YES);
}
Lisp_Object Llibrary_members(Lisp_Object nil, Lisp_Object oo)
{
int i, j, k;
directory *d = fasl_files[library_number(oo)];
Lisp_Object v, r = C_nil;
char *p;
for (j=0; j<get_dirused(d->h); j++)
{ int n = 0;
p = (char *)&celt(boffo, 0);
k = 0;
if (d->d[j].D_space & 0x80)
{ for (i=0; i<11; i++)
{ *p++ = (&d->d[j].D_name)[i];
k++;
}
do
{ n++;
for (i=0; i<11; i++)
{ *p++ = (&d->d[j+n].D_name)[i];
k++;
}
} while ((d->d[j+n].D_space & 0xff) != 0xff);
j += n;
}
else
{ if (memcmp(&d->d[j].D_name, "InitialImage", 12) == 0 ||
memcmp(&d->d[j].D_name, "HelpDataFile", 12) == 0 ||
memcmp(&d->d[j].D_name, "Start-Banner", 12) == 0 ||
memcmp(&d->d[j].D_name, "HardCode<", 9) == 0 &&
(&d->d[j].D_name)[11] == '>')
continue; /* not user modules */
for (i=0; i<12; i++)
{ *p++ = (&d->d[j].D_name)[i];
k++;
}
}
while (k>0 && p[-1] == ' ') k--, p--;
*p = 0;
push(r);
v = iintern(boffo, k, lisp_package, 0);
pop(r);
errexit();
r = cons(v, r);
errexit();
}
return onevalue(r);
}
Lisp_Object MS_CDECL Llibrary_members0(Lisp_Object nil, int nargs, ...)
/*
* This returns a list of the modules in the first library on the current
* search path.
*/
{
Lisp_Object il = qvalue(input_libraries), w;
Lisp_Object ol = qvalue(output_library);
argcheck(nargs, 0, "library-members");
while (consp(il))
{ w = qcar(il); il = qcdr(il);
if (!is_library(w)) continue;
return Llibrary_members(nil, w);
}
if (is_library(ol)) return Llibrary_members(nil, ol);
else return onevalue(nil);
}
CSLbool Imodulep(char *name, int len, char *datestamp, int32 *size,
char *expanded_name)
/*
* Hands back information about whether the given module exists, and
* if it does when it was written. Code should be very similar to
* that in Iopen.
*/
{
int i;
Lisp_Object nil = C_nil;
Lisp_Object il = qvalue(input_libraries);
/*
* nil is conditionally needed for two reasons here:
* (a) if NILSEG_EXTERNS was not selected it is needed as a base register for
* access to input_libraries
* (b) if COMMON was selected it is needed for the expansion of the
* consp test.
* If neither of the above apply its is redundant, but not a very greate pain.
*/
CSL_IGNORE(nil);
while (consp(il))
{ int j;
directory *d;
Lisp_Object oo = qcar(il); il = qcdr(il);
if (!is_library(oo)) continue;
i = library_number(oo);
d = fasl_files[i];
if (d == NULL) continue;
for (j=0; j<get_dirused(d->h); j++)
{ if (samename(name, d, j, len))
{ char *n = fasl_files[i]->filename;
memcpy(datestamp, &d->d[j].D_date, date_size);
*size = bits24(&d->d[j].D_size);
if (name == NULL) sprintf(expanded_name, "%s(InitialImage)", n);
else sprintf(expanded_name, "%s(%.*s)", n, len, name);
return NO;
}
}
}
return YES;
}
CSLbool IopenRoot(char *expanded_name, int hard)
/*
* Opens the "InitialImage" file so that it can be loaded. Note that
* when I am about to do this I do not have a valid heap image loaded, and
* so it would NOT be possible to use the regular search-path mechanism for
* libraries. Therefore I will just use images as specified from the
* command line (or by default).
*/
{
char *n;
int i;
if (hard == 0) hard = IMAGE_CODE;
for (i=0; i<number_of_fasl_paths; i++)
{ CSLbool bad;
bad = open_input(fasl_files[i], NULL, hard, 0);
/*
* The name that I return (for possible display in error messages) will be
* either that of the file that was opened, or one relating to the last
* entry in the search path.
*/
n = fasl_files[i]->filename;
if (expanded_name != NULL)
{ if (hard == IMAGE_CODE)
{ if (!bad)
{ long int pos = ftell(binary_read_file);
directory *d = fasl_files[i];
unsigned char rr[REGISTRATION_SIZE];
int n = get_dirsize(d->h) * sizeof(directory_entry);
n += sizeof(directory_header);
fseek(binary_read_file, n, SEEK_SET);
fread(rr, REGISTRATION_SIZE,
1, binary_read_file);
if (memcmp(rr, REGISTRATION_VERSION, 4) == 0)
memcpy(registration_data, rr, REGISTRATION_SIZE);
fseek(binary_read_file, pos, SEEK_SET);
}
sprintf(expanded_name, "%s(InitialImage)", n);
}
else if (hard == BANNER_CODE)
sprintf(expanded_name, "%s(InitialImage)", n);
else sprintf(expanded_name, "%s(HardCode<%.2x>)",
n, (-hard) & 0xff);
}
if (!bad) return NO;
}
return YES;
}
CSLbool Iopen(char *name, int len, CSLbool forinput, char *expanded_name)
/*
* Make file with the given name available through this package of
* routines. (name) is a pointer to a string (len characters valid) that
* names a fasl file. (forinput) specifies the direction of the transfer
* to set up. Returns YES if something failed.
* name can be NULL when a module is opened for output, and then output
* is sent to "InitialImage".
* The same is done for input, but it would be more sensible to use
* IopenRoot() to access the root image.
*/
{
char *n;
Lisp_Object nil = C_nil;
CSL_IGNORE(nil);
if (name == NULL) len = IMAGE_CODE;
if (forinput)
{ int i;
Lisp_Object il = qvalue(input_libraries);
while (consp(il))
{ CSLbool bad;
Lisp_Object oo = qcar(il); il = qcdr(il);
if (!is_library(oo)) continue;
i = library_number(oo);
bad = open_input(fasl_files[i], name, len, 0);
/*
* The name that I return (for possible display in error messages) will be
* either that of the file that was opened, or one relating to the last
* entry in the search path.
*/
n = fasl_files[i]->filename;
if (expanded_name != NULL)
sprintf(expanded_name, "%s(%.*s)", n, len, name);
if (!bad) return NO;
}
return YES;
}
#ifndef DEMO_MODE
if (!any_output_request)
#endif
{ if (expanded_name != NULL)
strcpy(expanded_name, "<no output file specified>");
return YES;
}
#ifndef DEMO_MODE
n = would_be_output_directory;
if (expanded_name != NULL)
{ if (len == IMAGE_CODE)
sprintf(expanded_name, "%s(InitialImage)", n);
else sprintf(expanded_name, "%s(%.*s)", n, len, name);
}
return open_output(name, len);
#endif
}
CSLbool Iwriterootp(char *expanded_name)
/*
* Test if it will be possible to write out an image file. Used
* by (preserve) so it can report that this would fail without actually
* doing anything too drastic.
*/
{
#ifdef DEMO_MODE
strcpy(expanded_name, "<demo-system>");
return YES;
#else
Lisp_Object nil = C_nil;
directory *d;
Lisp_Object oo = qvalue(output_library);
CSL_IGNORE(nil);
if (!any_output_request)
{ strcpy(expanded_name, "<no output file specified>");
return YES;
}
sprintf(expanded_name, "%s(InitialImage)", would_be_output_directory);
if (!is_library(oo)) return YES;
d = fasl_files[library_number(oo)];
if (d == NULL) return YES; /* closed handle, I guess */
if ((d->h.updated & D_WRITE_OK) == 0) return YES;
if (Istatus != I_INACTIVE) return YES;
return NO;
#endif /* DEMO_MODE */
}
CSLbool Iopen_help(int32 offset)
/*
* Get ready to handle the HELP subfile. offset >= 0 will open an
* existing help module for input and position at the given location.
* A negative offset indicates that the help module should be opened
* for writing.
*/
{
Lisp_Object nil = C_nil;
CSL_IGNORE(nil);
if (offset >= 0)
{ Lisp_Object il = qvalue(input_libraries);
while (consp(il))
{ CSLbool bad;
Lisp_Object oo = qcar(il); il = qcdr(il);
if (!is_library(oo)) continue;
bad = open_input(fasl_files[library_number(oo)],
NULL, HELP_CODE, offset);
if (!bad) return NO;
}
return YES;
}
#ifdef DEMO_MODE
return YES;
#else
if (!any_output_request) return YES;
return open_output(NULL, HELP_CODE);
#endif
}
CSLbool Iopen_banner(int code)
/*
* Get ready to handle the startup banner.
* code = 0 open for reading
* code = -1 open for writing
* code = -2 delete banner file
*/
{
Lisp_Object nil = C_nil;
CSL_IGNORE(nil);
if (code == -2) return Idelete(NULL, BANNER_CODE);
else if (code == 0)
{ Lisp_Object il = qvalue(input_libraries);
while (consp(il))
{ CSLbool bad;
Lisp_Object oo = qcar(il); il = qcdr(il);
if (!is_library(oo)) continue;
bad = open_input(fasl_files[library_number(oo)],
NULL, BANNER_CODE, 0);
if (!bad) return NO;
}
return YES;
}
#ifdef DEMO_MODE
return YES;
#else
if (!any_output_request) return YES;
return open_output(NULL, BANNER_CODE);
#endif
}
/*
* Set up binary_read_file to read from standard input. Return YES if
* things fail.
*/
CSLbool Iopen_from_stdin(void)
{
if (Istatus != I_INACTIVE) return YES;
subfile_checksum = 0;
binary_read_file = NULL;
read_bytes_remaining = -1;
Istatus = I_READING;
return NO;
}
CSLbool Iopen_to_stdout(void)
{
if (Istatus != I_INACTIVE) return YES;
subfile_checksum = 0;
Istatus = I_WRITING;
return NO;
}
CSLbool Idelete(char *name, int len)
{
#ifdef DEMO_MODE
return YES;
#else
nil_as_base
int i, nrec;
directory *d;
Lisp_Object oo = qvalue(output_library);
if (!is_library(oo)) return YES;
d = fasl_files[library_number(oo)];
if (d == NULL ||
(d->h.updated && D_WRITE_OK) == 0 ||
Istatus != I_INACTIVE) return YES;
for (i=0; i<get_dirused(d->h); i++)
{ if ((nrec = samename(name, d, i, len)) != 0)
{ int j;
set_dirused(&d->h, get_dirused(d->h)-nrec);
for (j=i; j<get_dirused(d->h); j++)
d->d[j] = d->d[j+nrec];
/*
* I tidy up the now-unused entry - in some sense this is a redundant
* operation, but I think it makes the file seem neater, which may possibly
* help avoid confusion and ease debugging.
*/
while (nrec-- != 0)
{ memset(&d->d[j].D_name, ' ', name_size);
memcpy(&d->d[j].D_name, "<Unused>", 8);
memset(&d->d[j].D_date, ' ', date_size);
(&d->d[j].D_date)[0] = '-';
setbits32(&d->d[j].D_position, 0);
setbits24(&d->d[j].D_size, 0);
j++;
}
d->h.updated |= D_COMPACT | D_UPDATED;
return NO;
}
}
return YES;
#endif /* DEMO_MODE */
}
#define update_crc(chk, c) \
chk_temp = (chk << 7); \
chk = ((chk >> 25) ^ \
(chk_temp >> 1) ^ \
(chk_temp >> 4) ^ \
(0xff & (unsigned32)c)) & 0x7fffffff;
static int validate_checksum(FILE *f, unsigned32 chk1)
{
int c;
unsigned32 chk2 = 0;
if (read_bytes_remaining < 0)
{ if ((c = Igetc()) == EOF) goto failed;
chk2 = c & 0xff;
if ((c = Igetc()) == EOF) goto failed;
chk2 = (chk2 << 8) | (c & 0xff);
if ((c = Igetc()) == EOF) goto failed;
chk2 = (chk2 << 8) | (c & 0xff);
if ((c = Igetc()) == EOF) goto failed;
chk2 = (chk2 << 8) | (c & 0xff);
if (chk1 == chk2) return NO; /* All went well */
}
else
{ if ((c = getc(f)) == EOF) goto failed;
chk2 = c & 0xff;
if ((c = getc(f)) == EOF) goto failed;
chk2 = (chk2 << 8) | (c & 0xff);
if ((c = getc(f)) == EOF) goto failed;
chk2 = (chk2 << 8) | (c & 0xff);
if ((c = getc(f)) == EOF) goto failed;
chk2 = (chk2 << 8) | (c & 0xff);
if (chk1 == chk2) return NO; /* All went well */
}
failed:
err_printf("\n+++ FASL module checksum failure (%.8x instead of %.8x)\n",
chk2, chk1);
return YES;
}
#ifndef DEMO_MODE
static int put_checksum(FILE *f, unsigned32 chk)
{
Lisp_Object nil = C_nil;
/*
* NB that while I am writing out the root section of a checkpoint image
* I will have unadjusted all Lisp variables, and in particular this will
* mean that anything that used to have the value NIL will then be
* SPID_NIL instead. Part of what I should remember here is that
* in consequence I can not send a main image to a Lisp stream. But I
* think that is OK, since the only way I have of setting up fasl_stream
* is via the FASLOUT mechanism.
*/
if (fasl_stream != nil && fasl_stream != SPID_NIL)
{ putc_stream((int)(chk>>24), fasl_stream);
putc_stream((int)(chk>>16), fasl_stream);
putc_stream((int)(chk>>8), fasl_stream);
putc_stream((int)chk, fasl_stream);
return NO;
}
if (putc((int)(chk>>24), f) == EOF) return YES;
if (putc((int)(chk>>16), f) == EOF) return YES;
if (putc((int)(chk>>8), f) == EOF) return YES;
return (putc((int)chk, f) == EOF);
}
#endif /* DEMO_MODE */
CSLbool Icopy(char *name, int len)
/*
* Find the named module in one of the input files, and if the place that
* it is found is not already the output file copy it to the output.
*/
{
#ifdef DEMO_MODE
return YES;
#else
int i, ii, j, n;
long int k, l, save = read_bytes_remaining;
unsigned32 chk1;
char hard[16];
directory *d, *id;
Lisp_Object nil = C_nil;
Lisp_Object il, oo = qvalue(output_library);
CSL_IGNORE(nil);
if (!is_library(oo)) return YES;
d = fasl_files[library_number(oo)];
/*
* Only valid if there is an output file and nothing else is going on.
*/
if (d == NULL ||
(d->h.updated & D_WRITE_OK) == 0 ||
Istatus != I_INACTIVE) return YES;
if (d->h.updated & D_PENDING)
{ if (unpending(d)) return YES;
}
/*
* Search for a suitable input module to copy...
*/
for (il=qvalue(input_libraries); consp(il); il = qcdr(il))
{ oo = qcar(il);
if (!is_library(oo)) continue;
i = library_number(oo);
id = fasl_files[i];
for (ii=0; ii<get_dirused(id->h); ii++)
if (samename(name, id, ii, len)) goto found;
}
return YES; /* Module to copy not found */
found:
/*
* If the potential input module found was in the output directory exit now.
*/
if (id == d) return NO;
/*
* Now scan output directory to see where to put result
*/
for (i=0; i<get_dirused(d->h); i++)
if (samename(name, d, i, len))
{ d->h.updated |= D_UPDATED | D_COMPACT;
goto ofound;
}
/*
* The file was not previously present in the output directory, so
* I need to insert it. The code here is copies from open_output and is
* now messy enoug that I should really move it to a sub-function.
*/
if (len == IMAGE_CODE)
name = "InitialImage", n = 1;
else if (len == HELP_CODE)
name = "HelpDataFile", len = IMAGE_CODE, n = 1;
else if (len == BANNER_CODE)
name = "Start-Banner", len = IMAGE_CODE, n = 1;
else if (len < 0)
{ sprintf(hard, "HardCode<%.2x>", (-len) & 0xff);
name = hard, len = IMAGE_CODE, n = 1;
}
else if (len <= 11) n = 1;
else if (len <= 11+11+24) n = 2;
else if (len <= 11+11+11+24+24) n = 3;
else return YES; /* Name longer than 81 chars not supported, sorry */
while (i+n > (int)get_dirsize(d->h))
{ d = enlarge_directory(i);
current_output_directory = d;
if (d == NULL) return YES;
}
current_output_entry = &d->d[i];
if (len == IMAGE_CODE)
{ d->d[i].D_newline = NEWLINE_CHAR;
memcpy(&d->d[i].D_name, name, 12);
memset(&d->d[i].D_date, ' ', date_size);
memset(&d->d[i].D_size, 0, 3);
memcpy(&d->d[i].D_position, d->h.eof, 4);
}
else
{ int np;
char *p;
/*
* First I will clear all the relevant fields to blanks.
*/
for (j=0; j<n; j++)
{ d->d[i+j].D_newline = '\n';
memset(&d->d[i+j].D_name, ' ', name_size);
memset(&d->d[i+j].D_date, ' ', date_size);
memset(&d->d[i+j].D_size, 0, 3);
memcpy(&d->d[i+j].D_position, d->h.eof, 4);
}
#define next_char_of_name (np++ >= len ? ' ' : *p++)
np = 0;
p = name;
for (j=0; j<n; j++)
{ for (k=0; k<11; k++) (&d->d[i+j].D_name)[k] = next_char_of_name;
if (j != 0)
for (k=0; k<24; k++)
(&d->d[i+j].D_date)[k] = next_char_of_name;
if (j == 0 && n == 1) d->d[i+j].D_space = ' ';
else if (j == n-1) d->d[i+j].D_space = 0xff;
else d->d[i+j].D_space = (char)(0x80+j);
#undef next_char_of_name
}
}
set_dirused(&d->h, get_dirused(d->h)+n);
ofound:
memcpy(&d->d[i].D_date, &id->d[ii].D_date, date_size);
trace_printf("\nCopy %.*s from %s to %s\n",
len, name, id->filename, d->filename);
memcpy(&d->d[i].D_position, d->h.eof, 4);
if (d->d[i].D_space & 0x80)
{ n = 0;
do
{ n++;
memcpy(&d->d[i+n].D_position, d->h.eof, 4);
} while ((d->d[i+n].D_space & 0xff) != 0xff);
}
/*
* I provisionally set the size to zero so that if something goes wrong
* I will still have a tolerably sensible image file.
*/
memset(&d->d[i].D_size, 0, 3);
d->h.updated |= D_UPDATED;
if (fseek(d->f, bits32(&d->d[i].D_position), SEEK_SET) != 0 ||
fseek(id->f, bits32(&id->d[ii].D_position), SEEK_SET) != 0) return YES;
l = bits24(&id->d[ii].D_size);
chk1 = 0;
for (k=0; k<l; k++)
{ int c = getc(id->f);
unsigned32 chk_temp;
/*
* I do not have to do anything special about encryption here...
*/
update_crc(chk1, c);
if (c == EOF) return YES;
putc(c, d->f);
}
read_bytes_remaining = 0;
j = validate_checksum(id->f, chk1);
read_bytes_remaining = save;
if (j) return YES;
if (put_checksum(d->f, chk1)) return YES;
if (fflush(d->f) != 0) return YES;
setbits24(&d->d[i].D_size, (int32)l);
setbits32(d->h.eof, (int32)ftell(d->f));
return NO;
#endif /* DEMO_MODE */
}
CSLbool IcloseInput(int check_checksum)
/*
* Terminate processing one whatever subfile has been being processed.
* returns nonzero if there was trouble.
* read and verify checksum if arg is TRUE.
*/
{
Istatus = I_INACTIVE;
if (check_checksum)
return validate_checksum(binary_read_file, subfile_checksum);
else return NO;
}
CSLbool IcloseOutput(void)
/*
* Terminate processing one whatever subfile has been being processed.
* returns nonzero if there was trouble. Write a checksum to the file.
* There is a jolly joke here! I MUST NOT try to pick up the identification
* of the output directory from the lisp-level variable output_directory
* because (preserve) calls this AFTER it has utterly mangled the heap (to
* put all pointers into relative form). To alloc for this the variable
* current_output_directory identifies the directory within which a file
* was most recently opened.
*/
{
#ifdef DEMO_MODE
return YES;
#else
int r;
Lisp_Object nil = C_nil;
directory *d = current_output_directory;
Istatus = I_INACTIVE;
if (fasl_stream != nil && fasl_stream != SPID_NIL)
{ put_checksum(NULL, subfile_checksum);
return NO;
}
current_output_directory = NULL;
/* Here I have to write a checksum to the current ouput dir */
if (d == NULL || (d->h.updated & D_WRITE_OK) == 0) return NO;
put_checksum(d->f, subfile_checksum);
setbits24(¤t_output_entry->D_size, (int32)write_bytes_written);
r = fflush(d->f);
setbits32(d->h.eof, (int32)ftell(d->f));
/*
* I bring the directory at the start of the output file up to date at this
* stage - the effect is that if things crash somehow I have a better
* chance of resuming from where disaster hit.
*/
fseek(d->f, 0, SEEK_SET);
if (fwrite(&d->h, sizeof(directory_header), 1, d->f) != 1) r = YES;
if (fwrite(&d->d[0], sizeof(directory_entry),
(size_t)get_dirsize(d->h), d->f) !=
(size_t)get_dirsize(d->h)) r = YES;
if (fflush(d->f) != 0) r = YES;
d->h.updated &= ~D_UPDATED;
current_output_entry = NULL;
return r;
#endif /* DEMO_MODE */
}
CSLbool finished_with(int j)
{
#ifdef DEMO_MODE
return YES;
#else
directory *d = fasl_files[j];
fasl_files[j] = NULL;
/*
* If the library concerned had been opened using (open-library ...) then
* the name stored in fasl_paths[] would have been allocated using malloc(),
* and just discarding it as here will represent a space-leak. Just for now
* I am going to accept that as an unimportant detail.
*/
fasl_paths[j] = NULL;
if (d == NULL) return NO;
if (d->h.updated & D_COMPACT)
{ int i;
long int hwm;
if (d->f == NULL) return YES;
d->h.updated |= D_UPDATED;
sort_directory(d);
hwm = sizeof(directory_header) +
get_dirsize(d->h)*(long int)sizeof(directory_entry) +
REGISTRATION_SIZE;
for (i=0; i<get_dirused(d->h); i++)
{ long int pos = bits32(&d->d[i].D_position);
if (pos != hwm)
{ char *b = 16 + (char *)stack;
char small_buffer[64];
/* I add 4 to the length specified here to allow for checksums */
long int len = bits24(&d->d[i].D_size) + 4L;
long int newpos = hwm;
while (len != 0)
{ size_t n =
(size_t)((CSL_PAGE_SIZE - 64 -
((char *)stack - (char *)stackbase)) &
(~(int32)0xff));
/*
* I only perform compression of the file when I am in the process of stopping,
* and in that case the Lisp stack is not in use, so I use if as a buffer.
* WELL the above statement used to be true, but now it is not, since the
* function CLOSE-LIBRARY does exactly what I have declared is never
* possible. But all is not lost - I can afford to use that part of
* the stack that remains unused. In cases where CLOSE-LIBRARY is called
* just before a stack overflow was due the result will be utterly terrible
* (on speed) but it should still be correct. So what you will see is that
* I start my buffer 16 bytes above the active part of the stack, and
* let it run to within 48 bytes of the top of the stack page, but
* rounded down so I do transfers in multiples of 256 bytes. If there
* is really no (Lisp) stack free I use a 64 byte local buffer.
*/
if (n == 0) b = small_buffer, n = sizeof(small_buffer);
if (len < (long int)n) n = (size_t)len;
fseek(d->f, pos, SEEK_SET);
fread(b, 1, n, d->f);
pos = ftell(d->f);
fseek(d->f, newpos, SEEK_SET);
fwrite(b, 1, n, d->f);
newpos = ftell(d->f);
len -= n;
}
setbits32(&d->d[i].D_position, (int32)hwm);
}
hwm += bits24(&d->d[i].D_size) + 4L;
}
fflush(d->f);
if (hwm != bits32(d->h.eof))
{ truncate_file(d->f, hwm);
setbits32(d->h.eof, (int32)hwm);
}
}
if (d->h.updated & D_UPDATED)
{
if (d->f == NULL || fflush(d->f) != 0) return YES;
fseek(d->f, 0, SEEK_SET);
if (fwrite(&d->h, sizeof(directory_header), 1, d->f) != 1) return YES;
if (fwrite(&d->d[0], sizeof(directory_entry),
(size_t)get_dirsize(d->h), d->f) !=
(size_t)get_dirsize(d->h)) return YES;
if (fflush(d->f) != 0) return YES;
}
if (d->h.updated & D_PENDING) return NO;
else if (d->f != NULL && fclose(d->f) != 0) return YES;
else return NO;
#endif /* DEMO_MODE */
}
CSLbool Ifinished(void)
/*
* Indicates total completion of all work on image files, and so calls
* for things to be (finally) tidied up. Again returns YES of anything
* has gone wrong.
*/
{
/*
* Need to close all files here... loads of calls to fflush and fclose.
* Actually only output files are a real issue here. And then only
* the ones that are flagged as needing compaction.
*/
int j;
CSLbool failed = NO;
for (j=0; j<number_of_fasl_paths; j++)
if (finished_with(j)) failed = YES;
return failed;
}
int Igetc(void)
/*
* Returns next byte from current image sub-file, or EOF if either
* real end-of-file or on failure. As a special fudge here (ugh) I
* use a negative value of read_bytes_remaining to indicate that
* input should NOT be from the usual image-file mechanism, but from
* the currently selected standard input. Setting things up that way
* then supports processing of FASL files from almost arbitrary
* sources.
*/
{
long int nn = read_bytes_remaining;
int c;
unsigned32 chk_temp;
if (nn <= 0)
{ if (nn == 0) return EOF;
else
{ Lisp_Object nil = C_nil;
Lisp_Object stream = qvalue(standard_input);
if (!is_stream(stream)) return EOF;
c = getc_stream(stream);
nil = C_nil;
if (exception_pending()) return EOF;
}
}
else
{ read_bytes_remaining = nn - 1;
c = getc(binary_read_file);
}
if (c == EOF) return c;
update_crc(subfile_checksum, c);
if (crypt_active >= 0)
{ if (crypt_count >= CRYPT_BLOCK)
{ crypt_get_block(crypt_buffer);
crypt_count = 0;
}
c ^= crypt_buffer[crypt_count++];
}
return (c & 0xff);
}
#ifdef SIXTEEN_BIT
#define FREAD_CHUNK 0x4000
#endif
int32 Iread(void *buff, int32 size)
/*
* Reads (size) bytes into the indicated buffer. Returns number of
* bytes read. Decrypts if crypt_active >= 0.
*/
{
unsigned char *p = (unsigned char *)buff;
int32 n = 0;
unsigned32 chk_temp;
int i;
size_t n1;
long int nn = read_bytes_remaining;
if (nn < 0)
{ for (i=0; i<size; i++)
{ int c = Igetc();
if (c == EOF) return i;
p[i] = (char)c;
}
return i;
}
if (size > nn) size = (int32)nn; /* Do not go beyond end of file */
#ifdef FREAD_CHUNK
/*
* Iread can read a number of bytes that is specified by an int32, so on
* 16 bit implementations I will need to issue a sequence of calls to fread(),
* each transferring < 64Kbytes (in fact I do 16K at a time).
*/
while (size >= FREAD_CHUNK)
{ n1 = fread(p, 1, FREAD_CHUNK, binary_read_file);
for (i=0; i<(int)n1; i++)
{ int c = p[i];
update_crc(subfile_checksum, c);
if (crypt_active >= 0)
{ if (crypt_count >= CRYPT_BLOCK)
{ crypt_get_block(crypt_buffer);
crypt_count = 0;
}
c ^= crypt_buffer[crypt_count++];
p[i] = c;
}
}
read_bytes_remaining -= n1;
if (n1 != FREAD_CHUNK) return n + n1;
p += n1;
size -= n1;
n += n1;
}
#endif
if (size == 0) return n;
n1 = fread(p, 1, (size_t)size, binary_read_file);
/*
* Updating the checksum here is probably a painful extra cost, but I count
* the security it gives me as worthwhile. I compute the checksum byte at a
* time so that it is not sensitive to the byte ordering of the machine used.
*/
for (i=0; i<(int)n1; i++)
{ int c = p[i];
update_crc(subfile_checksum, c);
if (crypt_active >= 0)
{ if (crypt_count >= CRYPT_BLOCK)
{ crypt_get_block(crypt_buffer);
crypt_count = 0;
}
c ^= crypt_buffer[crypt_count++];
p[i] = (char)c;
}
}
read_bytes_remaining -= n1;
return n + n1;
}
long int Ioutsize(void)
{
return write_bytes_written;
}
CSLbool Iputc(int ch)
/*
* Puts one character into image system, returning YES if there
* was trouble.
*/
{
#ifdef DEMO_MODE
return YES;
#else
unsigned32 chk_temp;
Lisp_Object nil = C_nil;
write_bytes_written++;
if (crypt_active >= 0)
{ if (crypt_count >= CRYPT_BLOCK)
{ crypt_get_block(crypt_buffer);
crypt_count = 0;
}
ch ^= crypt_buffer[crypt_count++];
}
update_crc(subfile_checksum, ch);
if (fasl_stream != nil && fasl_stream != SPID_NIL)
putc_stream(ch, fasl_stream);
else if (putc(ch, binary_write_file) == EOF) return YES;
return NO;
#endif /* DEMO_MODE */
}
#define FWRITE_CHUNK 0x4000
CSLbool Iwrite(void *buff, int32 size)
/*
* Writes (size) bytes from the given buffer, returning YES if trouble.
*/
{
#ifdef DEMO_MODE
return YES;
#else
unsigned char *p = (unsigned char *)buff;
int32 i;
unsigned32 chk_temp;
Lisp_Object nil = C_nil;
if (crypt_active >= 0 ||
(fasl_stream != nil && fasl_stream != SPID_NIL))
{
/*
* Note that in this case the checksum is updated within Iputc() so I do
* not have to do anything special about it here.
*/
for (i=0; i<size; i++)
if (Iputc(p[i])) return YES;
return NO;
}
/*
* If encrypted writing is active I will have gone through Iputc for
* every individual character and so will not get down to here. Thus the
* optimised calls to fwrite() can remain intact.
*/
for (i=0; i<size; i++)
{ /* Beware - update_crc is a macro and the {} block here is essential */
update_crc(subfile_checksum, p[i]);
}
write_bytes_written += size;
while (size >= FWRITE_CHUNK)
{ if (fwrite(p, 1, FWRITE_CHUNK, binary_write_file) != FWRITE_CHUNK)
return YES;
p += FWRITE_CHUNK;
size -= FWRITE_CHUNK;
}
if (size == 0) return NO;
else return
(fwrite(p, 1, (size_t)size, binary_write_file) != (size_t)size);
#endif /* DEMO_MODE */
}
/*
* Now code that maps real pointers into references relative
* to page numbers. Here I will also go to the trouble of putting zero
* bytes in unused bits of memory - that will make checkpoint files
* compress better and will also make them independent of all actual
* addresses used on the host machine. Observe that the representation
* created has to depend a bit on the current page size.
*/
#define PACK_PAGE_OFFSET(pg, of) ((pg << PAGE_BITS) + of)
static void unadjust(Lisp_Object *cp)
/*
* If p is a pointer to an object that has moved, unadjust it.
*/
{
#ifndef DEMO_MODE
Lisp_Object nil = C_nil, p = (*cp); /* Beware "=*" anachronism! */
if (p == nil)
{ *cp = SPID_NIL; /* Marks NIL in preserve files */
return;
}
else if (is_cons(p))
{ int32 i;
for (i=0; i<heap_pages_count; i++)
{ void *page = heap_pages[i];
char *base = (char *)quadword_align_up((intxx)page);
/*
* The next line is pretty dodgy - I want to decide which segment a
* pointer references, but pointer comparisons are only valid within
* single segments. I cast to int and cross my fingers! Actually no
* REASONABLE C system would fail on this - it is just that ANSI specifies
* that you can only do any address arithmetic WITHIN the area returned
* by a single malloc() (etc).
*/
if ((intxx)base <= (intxx)p &&
(intxx)p <= (intxx)(base+CSL_PAGE_SIZE))
{ unsigned int offset = (unsigned int)((char *)p - base);
*cp = PACK_PAGE_OFFSET(i, offset);
return;
}
}
term_printf("\n[%lx] Cons address %lx not found in heap\n",
(long)cp, (long)p);
abort();
}
else if (!is_immed_or_cons(p))
{ int32 i; /* vectors get relocated here */
for (i=0; i<vheap_pages_count; i++)
{ void *page = vheap_pages[i];
char *base = (char *)doubleword_align_up((intxx)page);
/* see comments above re the next line */
if ((intxx)base <= (intxx)p &&
(intxx)p <= (intxx)(base+CSL_PAGE_SIZE))
{ unsigned int offset = (unsigned int)((char *)p - base);
*cp = PACK_PAGE_OFFSET(i, offset);
return;
}
}
term_printf("\n[%.8lx] Vector address %.8lx not found in heap\n",
(long)cp, (long)p);
abort();
}
#endif /* DEMO_MODE */
}
static void unadjust_consheap(void)
{
#ifndef DEMO_MODE
int32 page_number;
for (page_number = 0; page_number < heap_pages_count; page_number++)
{ void *page = heap_pages[page_number];
char *low = (char *)quadword_align_up((intxx)page);
char *start = low + CSL_PAGE_SIZE;
char *fr = low + car32(low);
/* The next line sets unused space in the page to be zero */
while ((fr -= sizeof(Lisp_Object)) != low) qcar(fr) = 0;
fr = low + car32(low);
while (fr < start)
{ unadjust((Lisp_Object *)fr);
fr += sizeof(Lisp_Object);
}
}
#endif /* DEMO_MODE */
}
static void convert_word_order(void *p)
{
/*
* This bit seems a bit strange to me. I cope with all other
* byte order issues by having the exporting machine dump data
* in its own native format and then fixing things up again when
* I re-load. Why not do that here? However what I *do* do is to keep
* image files in a single WORD order in image files but let the bytes
* within words fall how they do. But during the transition to support
* of full 64-bit machines I will disable all attempts at byte correction
* when in 64-bit mode...
*/
#ifndef ADDRESS_64
if ((current_fp_rep & FP_WORD_ORDER) != 0)
{ unsigned32 *f = (unsigned32 *)p;
unsigned32 w = f[0];
f[0] = f[1];
f[1] = w;
}
#endif
}
static struct entry_lookup
{ int32 code;
intxx entry;
char *s;
} entry_lookup[entry_table_size];
static int MS_CDECL order_lookup_entries(void const *aa, void const *bb)
{
struct entry_lookup *a = (struct entry_lookup *)aa,
*b = (struct entry_lookup *)bb;
intxx ap = a->entry, bp = b->entry;
if (ap < bp) return -1;
else if (ap > bp) return 1;
else return 0;
}
static void set_up_entry_lookup(void)
/*
* This makes a sorted version of entries_table. Since the table is
* only a few dozen words long it hardly seems worth being too clever,
* but the C library provides qsort() for me so I use it.
*/
{
int i;
for (i=0; i<entry_table_size; i++)
{ entry_lookup[i].code = i;
entry_lookup[i].entry = (intxx)entries_table[i].p;
entry_lookup[i].s = entries_table[i].s;
}
qsort((void *)entry_lookup,
entry_table_size, sizeof(struct entry_lookup),
order_lookup_entries);
}
static int32 code_up_fn(intxx e)
{
int low = 0, high = entry_table_size-1;
while (low < high)
{ int mid = (high + low)/2;
intxx s = entry_lookup[mid].entry;
if (s == e) return entry_lookup[mid].code;
if (s < e) low = mid + 1;
else high = mid - 1;
}
if (low == high &&
entry_lookup[low].entry == e) return entry_lookup[low].code;
else return 0;
}
static void unadjust_vecheap(void)
{
#ifndef DEMO_MODE
int32 page_number, i;
for (page_number = 0; page_number < vheap_pages_count; page_number++)
{ void *page = vheap_pages[page_number];
char *low = (char *)doubleword_align_up((intxx)page);
char *high = low + (CSL_PAGE_SIZE - 8);
char *fr = low + car32(low);
low += 8;
while (low < fr)
{ Header h = *(Header *)low;
if (is_symbol_header(h))
{ Lisp_Object s = (Lisp_Object)(low+TAG_SYMBOL);
ifn1(s) = code_up_fn(ifn1(s));
ifn2(s) = code_up_fn(ifn2(s));
ifnn(s) = code_up_fn(ifnn(s));
unadjust(&qvalue(s));
unadjust(&qenv(s));
unadjust(&qpname(s));
unadjust(&qplist(s));
unadjust(&qfastgets(s));
#ifdef COMMON
unadjust(&qpackage(s));
#endif
low += symhdr_length;
continue;
}
else switch (type_of_header(h))
{
#ifdef COMMON
case TYPE_RATNUM:
case TYPE_COMPLEX_NUM:
unadjust((Lisp_Object *)(low+CELL));
unadjust((Lisp_Object *)(low+2*CELL));
break;
#endif
case TYPE_HASH:
case TYPE_SIMPLE_VEC:
case TYPE_ARRAY:
case TYPE_STRUCTURE:
for (i=CELL;
i<doubleword_align_up(length_of_header(h));
i+=CELL)
unadjust((Lisp_Object *)(low+i));
break;
case TYPE_STREAM:
{ Lisp_Object ss = (Lisp_Object)(low+TAG_VECTOR);
/*
* It might make rather good sense to close any file or pipe streams
* that I come across at this stage...
*/
if (elt(ss, 4) == (intxx)char_to_file &&
elt(ss, 3) != 0)
{ fclose(stream_file(ss));
set_stream_write_fn(ss, char_to_illegal);
set_stream_write_other(ss, write_action_illegal);
set_stream_file(ss, NULL);
}
#ifdef PIPES
if (elt(ss, 4) == (intxx)char_to_pipeout &&
elt(ss, 3) != 0)
{ my_pclose(stream_file(ss));
set_stream_write_fn(ss, char_to_illegal);
set_stream_write_other(ss, write_action_illegal);
set_stream_file(ss, NULL);
}
#endif
if (elt(ss, 8) == (intxx)char_from_file &&
elt(ss, 3) != 0)
{ fclose(stream_file(ss));
set_stream_read_fn(ss, char_from_illegal);
set_stream_read_other(ss, read_action_illegal);
set_stream_file(ss, NULL);
}
elt(ss, 4) = code_up_fn(elt(ss, 4));
elt(ss, 5) = code_up_fn(elt(ss, 5));
elt(ss, 8) = code_up_fn(elt(ss, 8));
elt(ss, 9) = code_up_fn(elt(ss, 9));
}
case TYPE_MIXED1:
case TYPE_MIXED2:
case TYPE_MIXED3:
for (i=CELL; i<4*CELL; i+=CELL)
unadjust((Lisp_Object *)(low+i));
break;
case TYPE_DOUBLE_FLOAT:
convert_word_order((void *)(low + 8));
break;
#ifdef COMMON
case TYPE_SINGLE_FLOAT:
break;
case TYPE_LONG_FLOAT:
/* If long floats were 3 words long I might need to adjust this code... */
convert_word_order((void *)(low + 8));
break;
#endif
default:
break;
}
low += doubleword_align_up(length_of_header(h));
}
/*
* Now clean up the unused space in the page...
*/
while (low <= high)
{ qcar(low) = 0;
qcdr(low) = 0;
low += 2*sizeof(Lisp_Object);
}
}
#endif /* DEMO_MODE */
}
static void unadjust_bpsheap(void)
{
#ifndef DEMO_MODE
int32 page_number;
for (page_number = 0; page_number < bps_pages_count; page_number++)
{ void *page = bps_pages[page_number];
char *low = (char *)doubleword_align_up((intxx)page);
char *fr = low + car32(low);
/* Clean up unused space */
while ((fr -= sizeof(Lisp_Object)) != low) qcar(fr) = 0;
fr = low + qcar(low);
while (fr < low + CSL_PAGE_SIZE)
{ Header h = *(Header *)fr;
#ifdef ENVIRONMENT_VECTORS_IN_BPS_HEAP
switch (type_of_header(h))
{
/* This option is not actually used at present... */
case TYPE_SIMPLE_VEC:
for (i=CELL;
i<doubleword_align_up(length_of_header(h));
i+=CELL)
unadjust((Lisp_Object *)(fr+i));
break;
default:
break;
}
#endif
fr += doubleword_align_up(length_of_header(h));
}
}
#endif /* DEMO_MODE */
}
static void unadjust_all(void)
{
#ifndef DEMO_MODE
int32 i;
Lisp_Object nil = C_nil;
set_up_entry_lookup();
qheader(nil) = TAG_ODDS+TYPE_SYMBOL+SYM_SPECIAL_VAR;
qvalue(nil) = 0;
qenv(nil) = 0;
ifn1(nil) = 0;
ifn2(nil) = 0;
ifnn(nil) = 0;
unadjust(&(qpname(nil))); /* not a gensym */
unadjust(&(qplist(nil)));
unadjust(&(qfastgets(nil)));
#ifdef COMMON
unadjust(&(qpackage(nil)));
#endif
copy_into_nilseg(YES);
eq_hash_table_list = eq_hash_tables;
equal_hash_table_list = equal_hash_tables;
for (i = first_nil_offset; i<last_nil_offset; i++)
unadjust(&BASE[i]);
copy_out_of_nilseg(YES);
unadjust_consheap();
unadjust_vecheap();
unadjust_bpsheap();
#endif /* DEMO_MODE */
}
void preserve_native_code(void)
{
#ifndef DEMO_MODE
/*
* I should maybe worry a little more here about IO errors...
*/
int i;
if (!native_pages_changed) return;
if (open_output(NULL, -native_code_tag))
{ term_printf("Failed to open module for native code storage\n");
return;
}
Iputc(native_pages_count & 0xff);
Iputc((native_pages_count>>8) & 0xff);
/*
* The FINAL native page will in general not be full, so I put a count of
* the number of bytes in it that are in use in its first word, and
* zero out the parts of it beyond there. Then the file compression that
* routinely use when writing into image files.
*/
if (native_pages_count != 0)
{ intxx p = (intxx)native_pages[native_pages_count-1];
p = doubleword_align_up(p);
car32(p) = native_fringe;
memset((char *)p+native_fringe, 0, CSL_PAGE_SIZE-native_fringe);
}
for (i=0; i<native_pages_count; i++)
{ intxx p = (intxx)native_pages[i];
p = doubleword_align_up(p);
Cfwrite((char *)p, CSL_PAGE_SIZE);
}
IcloseOutput();
#endif /* DEMO_MODE */
}
void preserve(char *banner)
{
#ifdef DEMO_MODE
err_printf("\nThe demo systen can not save a checkpoint file\n");
give_up();
return;
#else
int32 i;
CSLbool int_flag = NO;
Lisp_Object nil = C_nil;
/*
* I dump out any altered chunk of native code before I mangle the heap
* up.
*/
preserve_native_code();
if (Iopen(NULL, 0, NO, NULL))
{ err_printf("+++ PRESERVE failed to open image file\n");
return;
}
/*
* I set a whole bunch of things to NIL here. If spurious data is left over
* in global list-bases from a previous calculation it could clog up the
* heap and waste a lot of space...
*/
#ifdef NILSEG_EXTERNS
for (i=0; i<=50; i++) workbase[i] = nil;
#else
for (i=work_0_offset; i<last_nil_offset; i++)
BASE[i] = nil;
#endif
exit_tag = exit_value = catch_tags =
codevec = litvec = B_reg = faslvec = faslgensyms = nil;
reclaim(nil, "preserve", GC_PRESERVE, 0); /* FULL garbage collection */
nil = C_nil;
/*
* if the user generated a SIGINT this is where it gets noticed...
*/
if (exception_pending())
{ flip_exception();
int_flag = YES;
}
{ char msg[128];
time_t t0 = time(0);
for (i=0; i<128; i++) msg[i] = ' ';
if (banner[0] == 0) msg[0] = 0;
else sprintf(msg, "%.60s", banner);
/* 26 bytes starting from byte 64 shows the time of the dump */
sprintf(msg+64, "%.25s\n", ctime(&t0));
/* 16 bytes starting at byte 90 are for a checksum of the u01.c etc checks */
get_user_files_checksum((unsigned char *)&msg[90]);
/* 106 to 109 free at present but available if checksum goes to 160 bits */
/* 1 byte at 110 marks an encrypted image (work in progress!) */
msg[110] = 0;
/* The final byte at 111 indicates whether compression is to be used */
{ int32 cc = compression_worth_while;
int fg = 0;
while (cc > 128) fg++, cc >>= 1;
msg[111] = (char)fg;
}
Cfwrite(msg, 112); /* Exactly 112 bytes in the header records */
}
unadjust_all(); /* Turn all pointers into base-offset form */
Cfwrite("\nNilseg:", 8);
copy_into_nilseg(YES);
{ Lisp_Object saver[9];
for (i=0; i<9; i++)
saver[i] = BASE[i+13],
BASE[i+13] = 0;
/* codefringe */
/* codelimit */
/* stacklimit */
/* ... ditto */
/* ... ditto */
/* fringe */
/* heaplimit */
/* vheaplimit */
/* vfringe */
Cfwrite((char *)BASE, sizeof(Lisp_Object)*last_nil_offset);
for (i=0; i<9; i++)
BASE[i+13] = saver[i];
}
Cfwrite((char *)&heap_pages_count, sizeof(heap_pages_count));
Cfwrite((char *)&vheap_pages_count, sizeof(vheap_pages_count));
Cfwrite((char *)&bps_pages_count, sizeof(bps_pages_count));
Cfwrite("\nVecseg:", 8);
for (i=0; i<vheap_pages_count; i++)
{ intxx p = (intxx)vheap_pages[i];
Cfwrite((char *)doubleword_align_up(p), CSL_PAGE_SIZE);
}
Cfwrite("\nConsseg", 8);
for (i=0; i<heap_pages_count; i++)
{ intxx p = (intxx)heap_pages[i];
Cfwrite((char *)quadword_align_up(p), CSL_PAGE_SIZE);
}
Cfwrite("\nCodeseg", 8);
for (i=0; i<bps_pages_count; i++)
{ intxx p = (intxx)bps_pages[i];
Cfwrite((char *)doubleword_align_up(p), CSL_PAGE_SIZE);
}
#ifndef COMMON
Cfwrite("\n\nEnd of CSL dump file\n\n", 24);
#else
Cfwrite("\n\nEnd of CCL dump file\n\n", 24);
#endif
/*
* Here I pad the image file to be a multiple of 4 bytes long. Since it is a
* binary file the '\n' characters I put in will always be just 1 byte each
* (for text files that might have expanded). See comments in fasl.c for
* a diatribe about why I do this, or at least why rather a long while ago
* this was necessary on at least one sort of computer.
*/
{ int k = (int)((-write_bytes_written) & 3);
while (k != 0) k--, Iputc(NEWLINE_CHAR);
}
/*
flip_needed = NO; Since I stop after (preserve) these lines are unnecessary?
old_fp_rep = current_fp_rep;
*/
/*
* I need to check for write errors here and moan if there were any...
*/
if (IcloseOutput()) error(0, err_write_err);
if (int_flag) term_printf("\nInterrupt during (preserve) was ignored\n");
return;
#endif /* DEMO_MODE */
}
/* end of file preserve.c */