1 #ifndef ZLIB_DEMO
2 // preserve.cpp Copyright (C) Codemist, 1990-2021
3 #else
4 // zlibdemo.cpp Copyright (C) Codemist, 1990-2021
5 #endif
6
7 // The file preserve.cpp can be preprocessed to generate zlibdemo.cpp,
8 // which is why the header line above is "strange".
9
10
11 /**************************************************************************
12 * Copyright (C) 2021, Codemist. A C Norman *
13 * *
14 * Redistribution and use in source and binary forms, with or without *
15 * modification, are permitted provided that the following conditions are *
16 * met: *
17 * *
18 * * Redistributions of source code must retain the relevant *
19 * copyright notice, this list of conditions and the following *
20 * disclaimer. *
21 * * Redistributions in binary form must reproduce the above *
22 * copyright notice, this list of conditions and the following *
23 * disclaimer in the documentation and/or other materials provided *
24 * with the distribution. *
25 * *
26 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS *
27 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT *
28 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS *
29 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE *
30 * COPYRIGHT OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, *
31 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, *
32 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS *
33 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND *
34 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
35 * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF *
36 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH *
37 * DAMAGE. *
38 *************************************************************************/
39
40 // $Id: preserve.cpp 5736 2021-03-16 10:41:22Z arthurcnorman $
41
42
43 #ifndef ZLIB_DEMO
44
45 // If this file is compiled with ZLIB_DEMO defined it will be a program that
46 // can be used as either
47 // zlibdemo source dest
48 // to compress the source file and create the destination one, or
49 // slibdemo -d source dest
50 // to uncompress from source to dest.
51 //
52 // I will use "unifdef" to create the separate file zlibdemo.cpp from this one
53 // but this is the master version...
54
55
56 #include "headers.h"
57
58 // The following extra includes should probably be hidden away elsewere
59 // and/or abtracted away a bit. They are here so I can check if a file-name
60 // refers to a directory.
61
62 #include <sys/stat.h>
63 #include <sys/types.h>
64 #include <zlib.h>
65
66 #ifndef S_IFMT
67 # ifdef __S_IFMT
68 # define S_IFMT __S_IFMT
69 # endif
70 #endif
71
72 #ifndef S_IFDIR
73 # ifdef __S_IFDIR
74 # define S_IFDIR __S_IFDIR
75 # endif
76 #endif
77
78 #ifndef S_IFREG
79 # ifdef __S_IFREG
80 # define S_IFREG __S_IFREG
81 # endif
82 #endif
83
84 #ifndef S_ISLNK
85 # ifdef S_IFLNK
86 # ifdef S_IFMT
87 # define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
88 # endif
89 # endif
90 #endif
91
92 #ifdef BUILTIN_IMAGE
93
94 // The following included file should define
95 // const unsigned char *reduce_image = "....";
96 // #define REDUCE_IMAGE_SIZE nnn
97 // where the data is that which might otherwise have lived in a file.
98
99 #include "image.cpp"
100 #endif
101
102 #else // ZLIB_DEMO
103
104 // Free-standing demonstration of how I use zlib to compress image files.
105
106 #include <cstdio>
107 #include <zlib.h>
108 #include <cstring>
109 #include <cassert>
110
111
112 std::FILE *src, *dest;
113
Igetc()114 static int Igetc()
115 { return std::getc(src);
116 }
117
Iputc(int ch)118 static bool Iputc(int ch)
119 { return (std::putc(ch, dest) == EOF);
120 }
121
Iread(void * buff,size_t size)122 bool Iread(void *buff, size_t size)
123 // Reads (size) bytes into the indicated buffer. Returns true if
124 // if fails to read the expected number of bytes.
125 { unsigned char *p = reinterpret_cast<unsigned char *>(buff);
126 while (size > 0)
127 { int c;
128 if ((c = Igetc()) == EOF) return true;
129 *p++ = c;
130 size--;
131 }
132 return false;
133 }
134
Iwrite(const void * buff,size_t size)135 bool Iwrite(const void *buff, size_t size)
136 // Writes (size) bytes from the given buffer, returning true if trouble.
137 { const unsigned char *p = reinterpret_cast<const unsigned char *>
138 (buff);
139 for (size_t i=0; i<size; i++)
140 if (Iputc(p[i])) return true;
141 return false;
142 }
143
144 #endif
145
146 // I will use zlib to compress image files. The code here arranges to
147 // buffer chunks of data for compressing or decompressing and adds
148 // CRC checking so that corrupted data can be noticed.
149
150 #define CHUNK ((size_t)16384)
151
152 static z_stream strm;
153 static unsigned char in[CHUNK];
154 static unsigned char out[CHUNK];
155
Zreturncode(int rc)156 const char *Zreturncode(int rc)
157 { switch (rc)
158 { default:
159 return "Unknown return code from zlib";
160 case Z_OK:
161 return "OK";
162 case Z_STREAM_END:
163 return "STREAM_END";
164 case Z_NEED_DICT:
165 return "NEED_DICT";
166 case Z_ERRNO:
167 return "ERRNO";
168 case Z_STREAM_ERROR:
169 return "STREAM_ERROR";
170 case Z_DATA_ERROR:
171 return "DATA_ERROR";
172 case Z_MEM_ERROR:
173 return "MEM_ERROR";
174 case Z_BUF_ERROR:
175 return "BUF_ERROR";
176 case Z_VERSION_ERROR:
177 return "VERSION_ERROR";
178 }
179 }
180
def_init()181 bool def_init()
182 { strm.zalloc = Z_NULL;
183 strm.zfree = Z_NULL;
184 strm.opaque = Z_NULL;
185 int rc = deflateInit(&strm, Z_BEST_COMPRESSION);
186 strm.avail_in = 0;
187 return (rc != Z_OK);
188 }
189
190 // Zputc will return true if it FAILS.
191
Zputc(int ch)192 bool Zputc(int ch)
193 { in[strm.avail_in++] = ch;
194 if (strm.avail_in != CHUNK) return false; // Just buffer the data
195 strm.next_in = in;
196 do
197 { strm.next_out = out;
198 strm.avail_out = CHUNK;
199 // Here I know I have plenty of space in the output buffer, and furthermore
200 // I should have CHUNK of data in the input buffer. So progress should be
201 // possible and hence Z_BUF_ERROR should never be returned. Z_STREAM_END
202 // can only arise if Z_FINISH had been passed as the flush input to deflate,
203 // and Z_STREAM_ERROR is a genuine error...
204 int rc = deflate(&strm, Z_NO_FLUSH);
205 if (rc != Z_OK) return true;
206 unsigned int n = CHUNK - strm.avail_out;
207 // Sometimes even though I have provided a whole chunk of input there
208 // will not (yet) be any output available. I do not want to emit a block
209 // of length zero both because that would be silly and because I use a
210 // block-length of zero to signify end of stream.
211 if (n != 0)
212 {
213 // Write the length of the block as 2 bytes.
214 if (Iputc(n >> 8)) return true;
215 if (Iputc(n)) return true;
216 // Calculate and write a CRC for this block.
217 int crc_out = crc32(crc32(0, nullptr, 0), out, n);
218 if (Iputc(crc_out>>24)) return true;
219 if (Iputc(crc_out>>16)) return true;
220 if (Iputc(crc_out>>8)) return true;
221 if (Iputc(crc_out)) return true;
222 if (Iwrite(out, n)) return true;
223 }
224 // I will keep going until I have used up all this input block.
225 }
226 while (strm.avail_in != 0);
227 return false;
228 }
229
def_finish()230 bool def_finish()
231 { do
232 { strm.next_in = in;
233 strm.avail_out = CHUNK;
234 strm.next_out = out;
235 int rc;
236 if ((rc = deflate(&strm, Z_FINISH)) != Z_OK &&
237 rc != Z_STREAM_END) return true;
238 size_t n = CHUNK - strm.avail_out;
239 if (n != 0)
240 { if (Iputc(n >> 8)) return true;
241 if (Iputc(n)) return true;
242 int crc_out = crc32(crc32(0, nullptr, 0), out, n);
243 if (Iputc(crc_out>>24)) return true;
244 if (Iputc(crc_out>>16)) return true;
245 if (Iputc(crc_out>>8)) return true;
246 if (Iputc(crc_out)) return true;
247 if (Iwrite(out, n)) return true;
248 }
249 }
250 while (strm.avail_out == 0);
251 int rc = deflateEnd(&strm);
252 if (rc != Z_OK) return true;
253 if (Iputc(0)) return true; // Termination bytes
254 if (Iputc(0)) return true;
255 return false;
256 }
257
Zwrite(const void * b,size_t n)258 bool Zwrite(const void *b, size_t n)
259 { const char *c = reinterpret_cast<const char *>(b);
260 while (n-- != 0) if (Zputc(*c++)) return true;
261 return false;
262 }
263
264 static size_t n_out;
265 static unsigned char *p_out;
266 static int z_eof;
267
inf_init()268 bool inf_init()
269 { strm.zalloc = Z_NULL;
270 strm.zfree = Z_NULL;
271 strm.opaque = Z_NULL;
272 n_out = 0;
273 p_out = out;
274 z_eof = 0;
275 int rc = inflateInit(&strm);
276 strm.avail_in = 0;
277 strm.next_in = in;
278 return (rc != Z_OK);
279 }
280
281 // Although the value of EOF on *many* systems will be -1, the standards do
282 // not guarantee that. So for Zgetc() I will arrange that I explictly return
283 // (-1) for end-of-file and error conditions, so that I can rely on that.
284
Zgetc()285 int Zgetc()
286 { for (;;)
287 { if (n_out !=
288 0) // Use byte from the current decompressed chunk.
289 { n_out--;
290 return *p_out++;
291 }
292 if (z_eof != 0) return (z_eof = -1);
293 // Here the zlib output buffer is empty. If the input buffer contains anything
294 // at all I will just call inflate() again.
295 // I do not believe that inflate can ever leave input untouched in its input
296 // buffer while there is space in the output buffer.
297 if (strm.avail_in == 0)
298 {
299 // The compressed material is arrabnged in blocks. Each block starts with
300 // a 2-byte length field.
301 int ch, n = Igetc();
302 // If I get an end of file (or error) report when trying to read part of the
303 // length information then the stream is corrupted, and I just return and end
304 // of file marker. When I do so I set z_eof so that further calls to Zgetc()
305 // will also return (-1).
306 if (n == EOF) return (z_eof = -1);
307 ch = Igetc();
308 if (ch == EOF) return (z_eof = -1);
309 n = (n << 8) | ch;
310 // If the block-length is 0 we have reached the end of the stream. Because
311 // I am trying to read data here I must have already emitted all the bytes
312 // that could be generated from everything that had been seen earlier, so
313 // I can return an end-of-file marker.
314 if (n == 0) return (z_eof = -1);
315 // Following the 2-byte length there is a 4-byte CRC. Again I watch carefully
316 // so that an EOF while trying to read this will be reported back to the
317 // caller (just as and end of file condition).
318 int crc_needed = Igetc();
319 if (crc_needed == EOF) return (z_eof = -1);
320 for (int i=0; i<3; i++)
321 { int w = Igetc();
322 if (w == EOF) return (z_eof = -1);
323 crc_needed = (crc_needed << 8) | w;
324 }
325 // Now I read the number of bytes I have been told to expect. If I am not
326 // able to read thet many it is an error.
327 if (Iread(in, n)) return (z_eof = -1);
328 // Compute a CRC on the block just read and complain if it is not as
329 // expected.
330 int crc = crc32(crc32(0, nullptr, 0), in, n);
331 if (crc != crc_needed) return (z_eof = -1);
332 // Set the zlib input buffer information so that this new block can be
333 // processed. There will be at least 1 new byte of data!
334 strm.next_in = in;
335 strm.avail_in = n;
336 }
337 strm.next_out = out;
338 strm.avail_out = CHUNK;
339 // Inflate whatever data is in the input buffer. I require that this EITHER
340 // removes at least one byte from the input buffer OR puts at least one
341 // byte in the output buffer, or that it return Z_STREAM_END. If it grabs
342 // input then it will eventually empty the input buffer so more will be read.
343 // Whenever it generates output that is offered to the caller.
344 int rc = inflate(&strm, Z_SYNC_FLUSH);
345 // I check the return code and exit if something has gone wrong.
346 if (rc != Z_OK &&
347 rc != Z_STREAM_END &&
348 rc != Z_BUF_ERROR) return (z_eof = -1);
349 if (rc == Z_STREAM_END) z_eof = (-1);
350 p_out = out;
351 n_out = CHUNK - strm.avail_out;
352 }
353 }
354
Zread(void * b,size_t n)355 bool Zread(void *b, size_t n)
356 { char *c = reinterpret_cast<char *>(b);
357 while (n-- != 0)
358 { int n = Zgetc();
359 if (n == -1) return true;
360 *c++ = n;
361 }
362 return false;
363 }
364
inf_finish()365 bool inf_finish()
366 { return (inflateEnd(&strm) != Z_OK);
367 }
368
369 #ifdef ZLIB_DEMO
370
371 // compress or decompress from src to dest
372
main(int argc,char ** argv)373 int main(int argc, char **argv)
374 {
375 // Check argument number and format. I expect either
376 // zlibdemo src compressed-dest
377 // OR zlibdemo -d compressed-src dest
378 if (argc < 3 ||
379 (argc == 3 && std::strcmp(argv[1], "-d") == 0) ||
380 (argc == 4 && std::strcmp(argv[1], "-d") != 0) ||
381 argc > 4)
382 { std::fputs("Usage: zlibdemo [-d] source dest\n", stderr);
383 return 1;
384 }
385
386 if (argc == 3)
387 { src = std::strcmp(argv[1], "-") == 0 ? stdin : std::fopen(argv[1],
388 "r");
389 assert(src != nullptr);
390 dest = std::fopen(argv[2], "wb");
391 assert(dest != nullptr);
392 def_init();
393 int ch;
394 while ((ch = std::getc(src)) != EOF) Zputc(ch);
395 def_finish();
396 std::fclose(src);
397 std::fclose(dest);
398 return 0;
399 }
400
401 else
402 { src = std::fopen(argv[2], "rb");
403 assert(src != nullptr);
404 dest = std::strcmp(argv[3], "-") == 0 ? stdout : std::fopen(argv[3],
405 "w");
406 assert(dest != nullptr);
407 inf_init();
408 int ch;
409 while ((ch = Zgetc()) != -1) std::putc(ch, dest);
410 inf_finish();
411 std::fclose(src);
412 std::fclose(dest);
413 return 0;
414 }
415 }
416
417 // end of zlibdemo.cpp
418
419
420 #else // ZLIB_DEMO
421
422 // These routines pack multiple binary files into one big one. The
423 // good effect is that I expect fseek to be faster than fopen, and as
424 // a result accessing fasl files will be faster. The bad news is that
425 // when I update files I may need to compact them, and doing so will
426 // be very tedious. In this model I do not permit arbitrary interleaving
427 // of read and write operations.
428
set_dirused(directory_header * h,int v)429 static void set_dirused(directory_header *h, int v)
430 { h->dirused = static_cast<unsigned char>(v & 0xff);
431 h->dirext = static_cast<unsigned char>((h->dirext & 0xf0) + ((
432 v>>8) & 0x0f));
433 }
434
435 static directory empty_directory =
436 {
437 // This statically allocated "directory" exists to use as a fall-back if
438 // it proves impossible to allocate space for a genuine directory record.
439 // Thus it only comes into play in situations when I am in the process
440 // of failing fairly drastically!
441 { 'C', MIDDLE_INITIAL, 'L', IMAGE_FORMAT_VERSION,
442 0, 0, 0, 0,
443 {0, 0, 0, 0}
444 },
445 nullptr,
446 nullptr,
447 "EmptyFile",
448 {{"\nEmpty ** *** not dated *** **"}}
449 };
450
451 // In a way that may look clumsy I store file offsets and lengths as
452 // sequences of three or four characters. The object of this
453 // explicit control over memory layout is so that directories produced by
454 // this code have a layout that is not sensitive to the byte-order used
455 // by the computer involved. I also put a few newline characters into
456 // my directory structure so that if one uses an ordinary text editor to
457 // inspect an image file the set of modules and their datestamps should
458 // be easily visible.
459
bits32(char * v)460 static int32_t bits32(char *v)
461 { int32_t r = v[3] & 0xff;
462 r = (r << 8) | (v[2] & 0xff);
463 r = (r << 8) | (v[1] & 0xff);
464 return (r << 8) | (v[0] & 0xff);
465 }
466
bits24(char * v)467 static int32_t bits24(char *v)
468 { int32_t r = v[2] & 0xff;
469 r = (r << 8) | (v[1] & 0xff);
470 return (r << 8) | (v[0] & 0xff);
471 }
472
setbits32(char * v,int32_t r)473 static void setbits32(char *v, int32_t r)
474 { *v++ = static_cast<char>(r);
475 *v++ = static_cast<char>(r >> 8);
476 *v++ = static_cast<char>(r >> 16);
477 *v = static_cast<char>(r >> 24);
478 }
479
setbits24(char * v,int32_t r)480 static void setbits24(char *v, int32_t r)
481 { *v++ = static_cast<char>(r);
482 *v++ = static_cast<char>(r >> 8);
483 *v = static_cast<char>(r >> 16);
484 }
485
486 static directory *current_input_directory; // not used!
487 static int nativedir = 0;
488 static directory_entry *current_output_entry;
489 static directory *current_output_directory = nullptr;
490 static bool any_output_request;
491 static char would_be_output_directory[DIRNAME_LENGTH];
492
493 #define I_INACTIVE 0
494 #define I_READING 1
495 #define I_WRITING 2
496
497 static int Istatus = I_INACTIVE;
498
499 #ifdef BUILTIN_IMAGE
500 const unsigned char *binary_read_filep;
501 #else
502 std::FILE *binary_read_file;
503 #endif
504 std::FILE *binary_write_file;
505 static long int read_bytes_remaining, write_bytes_written;
506
make_empty_directory(const char * name)507 static directory *make_empty_directory(const char *name)
508 // The sole purpose of this empty directory is to carry with it the
509 // name of the file that I had tried to open.
510 { directory *d = reinterpret_cast<directory *>(
511 new (std::nothrow) char[sizeof(directory)]);
512 if (d == nullptr) return &empty_directory;
513 d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
514 d->h.version = IMAGE_FORMAT_VERSION;
515 d->h.dirsize = 0;
516 d->h.dirused = 0;
517 d->h.dirext = 0;
518 d->h.updated = 0; // NB read-only
519 d->f = nullptr;
520 d->full_filename = nullptr;
521 std::strncpy(d->filename, name, DIRNAME_LENGTH);
522 d->filename[DIRNAME_LENGTH-1] = 0;
523 std::memset(d->h.eof, 0, 4);
524 return d;
525 }
526
make_pending_directory(const char * name,int pds)527 static directory *make_pending_directory(const char *name, int pds)
528 { directory *d;
529 int n = sizeof(directory) + (DIRECTORY_SIZE-1)*sizeof(directory_entry);
530 int l = std::strlen(name) + 1 -
531 DIRNAME_LENGTH -
532 DIRECTORY_SIZE*sizeof(directory_entry);
533 // Here I extend the directory header with enough extra bytes to hold the
534 // full name of the file... Once the file has been opened the (potential)
535 // extra data becomes unnecessary. However with room for DIRECTORY_SIZE
536 // entries already it would seem bizarre if the path-name ever actually
537 // overflowed here.
538 if (l > 0) n += l;
539 d = reinterpret_cast<directory *>(new (std::nothrow) char[n]);
540 if (d == nullptr) return &empty_directory;
541 d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
542 d->h.version = IMAGE_FORMAT_VERSION;
543 d->h.dirsize = DIRECTORY_SIZE & 0xff;
544 d->h.dirused = 0;
545 d->h.dirext = (DIRECTORY_SIZE >> 4) & 0xf0;
546 d->h.updated = D_PENDING | D_WRITE_OK;
547 // Well I HOPE that writing will be OK
548 d->f = nullptr;
549 std::strcpy(d->filename, name); // guaranteed enough space here
550 if (pds) d->full_filename = nullptr;
551 else
552 { char *s = new (std::nothrow) char[std::strlen(name)+1];
553 if (s == nullptr) my_abort();
554 std::strcpy(s, name);
555 d->full_filename = s;
556 }
557 std::memset(d->h.eof, 0, 4);
558 return d;
559 }
560
make_native_directory(const char * shortname,const char * fullname,int ro)561 static directory *make_native_directory(const char *shortname,
562 const char *fullname, int ro)
563 { directory *d = reinterpret_cast<directory *>(
564 new (std::nothrow) char[sizeof(directory)]);
565 if (d == nullptr) return &empty_directory;
566 d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
567 d->h.version = IMAGE_FORMAT_VERSION;
568 d->h.dirsize = DIRECTORY_SIZE & 0xff;
569 d->h.dirused = 0;
570 d->h.dirext = (DIRECTORY_SIZE >> 4) & 0xf0;
571 d->h.updated = ro ? 0 : D_WRITE_OK;
572 d->f = nullptr;
573 std::strncpy(d->filename, shortname, sizeof(d->filename));
574 d->filename[DIRNAME_LENGTH-1] = 0;
575 { char *s = new (std::nothrow) char[std::strlen(fullname)+1];
576 if (s == nullptr) my_abort();
577 std::strcpy(s, fullname);
578 d->full_filename = s;
579 }
580 std::memset(d->h.eof, 0, 4);
581 return d;
582 }
583
clear_entry(directory_entry * d)584 static void clear_entry(directory_entry *d)
585 { d->D_newline = NEWLINE_CHAR;
586 std::memset(&d->D_name, ' ', name_size);
587 std::memcpy(&d->D_name, "<Unused>", 8);
588 std::memset(&d->D_date, ' ', date_size);
589 (&d->D_date)[0] = '-';
590 std::memset(&d->D_position, 0, 4);
591 std::memset(&d->D_size, 0, 3);
592 }
593
version_moan(int v)594 static bool version_moan(int v)
595 { if (v == IMAGE_FORMAT_VERSION) return false;
596 return true;
597 }
598
599 #ifdef BUILTIN_IMAGE
builtinread(void * b,size_t s,size_t n)600 static int builtinread(void *b, size_t s, size_t n)
601 { size_t i;
602 unsigned char *w = reinterpret_cast<unsigned char *>(b);
603 for (i=0; i<n*s; i++)
604 *w++ = *binary_read_filep++;
605 return n;
606 }
607
608 #endif
609
open_pds(const char * name,int mode)610 directory *open_pds(const char *name, int mode)
611 // Given a file-name, open the associated file, make space for
612 // a directory and return same. This now has to detect if the file-name
613 // should refer to a directory rather than a composite file. This case
614 // arises if the file that is named already exists and is a directory OR
615 // if it does not exist and its name it specified with a trailing "/".
616 // The mode is one of PDS_INPUT, PDS_OUTPUT or PDS_PENDING where the last
617 // sets up for an output directory that must be created on first use.
618 // Also if BUILTING_DIRECTORY is true then only one image file can be used
619 // and that has to be the built-in one!
620 { char expanded[LONGEST_LEGAL_FILENAME];
621 directory hdr, *d;
622 bool write_OK = false, fileExists, nameDir, fileDir;
623 struct stat buf;
624 std::FILE *f;
625 int l, i, n;
626 std::memset(expanded, 0, sizeof(expanded));
627 l = std::strlen(name);
628 nameDir = (name[l-1] == '/') || (name[l-1] == '\\');
629 f = nullptr;
630 process_file_name(expanded, name, l);
631 i = std::strlen(expanded) - 1;
632 if (expanded[i] == '/' ||
633 expanded[i] == '\\') expanded[i] = 0; // Trim any final "/"
634 fileExists = fileDir = false;
635 #ifdef BUILTIN_IMAGE
636 fileExists = true;
637 binary_read_filep = reduce_image;
638 #else
639 if (stat(expanded, &buf) != -1)
640 { fileExists = true;
641 if ((buf.st_mode & S_IFMT) == S_IFDIR) fileDir = true;
642 }
643 if (nameDir && fileExists && !fileDir)
644 return make_empty_directory(expanded);
645 if (mode != PDS_INPUT)
646 { any_output_request = true;
647 std::strncpy(would_be_output_directory, expanded, DIRNAME_LENGTH-1);
648 would_be_output_directory[DIRNAME_LENGTH-1] = 0;
649 if (fileExists && fileDir)
650 return make_native_directory(name, expanded, 0);
651 else if (nameDir && mode == PDS_PENDING)
652 return make_pending_directory(expanded, 0);
653 else if (nameDir)
654 { Cmkdir(expanded);
655 return make_native_directory(name, expanded, 0);
656 }
657 else if (fileExists) f = std::fopen(expanded, "r+b");
658 else f = nullptr;
659 if (f != nullptr) write_OK = true;
660 else if (mode == PDS_PENDING)
661 { f = std::fopen(expanded, "rb");
662 if (f == nullptr) return make_pending_directory(expanded, !nameDir);
663 }
664 else
665 { f = std::fopen(expanded, "w+b");
666 if (f != nullptr) write_OK = true;
667 }
668 }
669 // If I wanted the file for input or if I tried it for output and failed
670 // then I open for input.
671 if (f == nullptr)
672 { if (!fileExists) return make_empty_directory(expanded);
673 if (fileDir) return make_native_directory(name, expanded, 1);
674 f = std::fopen(expanded, "rb");
675 }
676 // If the file does not exist I will just hand back a directory that shows
677 // no files in it. This seems as easy a thing to do at this stage as I can
678 // think of. Maybe I should warn the user?
679 if (f == nullptr) return make_empty_directory(expanded);
680 std::fseek(f, 0, SEEK_SET); // Ensure I am at start of the file
681 #endif
682 hdr.h.C = hdr.h.S = hdr.h.L = 0;
683 #ifdef BUILTIN_IMAGE
684 if (builtinread(&hdr.h, sizeof(directory_header), 1) != 1 ||
685 #else
686 if (std::fread(&hdr.h, sizeof(directory_header), 1, f) != 1 ||
687 #endif
688 hdr.h.C != 'C' ||
689 hdr.h.S != MIDDLE_INITIAL ||
690 hdr.h.L != 'L' ||
691 // Image format versions are somewhat delicate things. I will not change
692 // this format often or lightly and the tests I make will then be set up to
693 // cope with updates from the immediately previous version. The testing code
694 // will need review each time I consider such a change. For the current
695 // upgrade I will allow opening of files from version N-1, but I will
696 // specifically lock out reading an initial heap-image from such. The issue
697 // of people who start with an old file and then write a fresh image back into
698 // it will be viewed as too messy to worry about in detail, but I hope that
699 // I have made it so that writing a new base image (via PRESERVE) updates the
700 // version info.
701 version_moan(hdr.h.version) ||
702 get_dirused(hdr) > get_dirsize(hdr) ||
703 bits32(hdr.h.eof) < (int32_t)sizeof(directory_header))
704 {
705 // Here I did not find a satisfactory header to the directory. If I wanted
706 // to open the file for input I just return an empty directory, otherwise I
707 // need to create a new one.
708 if (!write_OK) return make_empty_directory(expanded);
709 // This next bit is never used in the BUILTIN_DIRECTOT case
710 std::fseek(f, 0, SEEK_SET);
711 n = DIRECTORY_SIZE; // Size for a directory
712 d = reinterpret_cast<directory *>(
713 new char[sizeof(directory)+(n-1)*sizeof(directory_entry)]);
714 if (d == nullptr) return &empty_directory;
715 d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
716 d->h.version = IMAGE_FORMAT_VERSION;
717 d->h.dirsize = static_cast<unsigned char>(n & 0xff);
718 d->h.dirused = 0;
719 d->h.dirext = static_cast<unsigned char>((n >> 4) & 0xf0);
720 d->h.updated = D_WRITE_OK | D_UPDATED;
721 d->full_filename = nullptr; // A PDS not a native dircetory
722 for (i=0; i<n; i++) clear_entry(&d->d[i]);
723 if (std::fwrite(&d->h, sizeof(directory_header), 1, f) != 1)
724 return make_empty_directory(expanded);
725 if (std::fwrite(&d->d[0], sizeof(directory_entry), (size_t)n,
726 f) != (size_t)n)
727 return make_empty_directory(expanded);
728 d->f = f;
729 std::strncpy(d->filename, expanded, DIRNAME_LENGTH);
730 d->filename[DIRNAME_LENGTH-1] = 0;
731 setbits32(d->h.eof, (int32_t)std::ftell(f));
732 return d;
733 }
734 hdr.h.updated = write_OK ? D_WRITE_OK : 0;
735 n = get_dirsize(hdr);
736 d = reinterpret_cast<directory *>(
737 new (std::nothrow) char[sizeof(directory)+
738 (n-1)*sizeof(directory_entry)]);
739 if (d == nullptr) return &empty_directory;
740 std::memcpy(&d->h, &hdr.h, sizeof(directory_header));
741 #ifdef BUILTIN_IMAGE
742 if (builtinread(&d->d[0], sizeof(directory_entry),
743 (size_t)n) != (size_t)n)
744 #else
745 if (std::fread(&d->d[0], sizeof(directory_entry), (size_t)n,
746 f) != (size_t)n)
747 #endif
748 return make_empty_directory(expanded);
749 // Here the directory seemed OK
750 d->f = f;
751 std::strncpy(d->filename, expanded, DIRNAME_LENGTH);
752 d->filename[DIRNAME_LENGTH-1] = 0;
753 d->full_filename = nullptr;
754 // For binary files ANSI specify that the values used with fseek and ftell
755 // are simple counts of the number of characters in the file, and hence
756 // it is proper to save ftell() values from one run to the next.
757 return d;
758 }
759
unpending(directory * d)760 static int unpending(directory *d)
761 { std::FILE *f;
762 int32_t i, n;
763 if (d->full_filename != nullptr)
764 { Cmkdir(d->full_filename);
765 d->h.updated &= ~D_PENDING;
766 d->h.updated |= D_WRITE_OK; // suppose directories always updatable
767 return false;
768 }
769 f = std::fopen(d->filename, "w+b");
770 if (f == nullptr) return true;
771 d->f = f;
772 d->filename[DIRNAME_LENGTH-1] = 0; // truncate the name now
773 n = DIRECTORY_SIZE; // Size for a directory
774 // (the next bits were done when the pending directory was first created
775 // d->h.C = 'C'; d->h.S = MIDDLE_INITIAL; d->h.L = 'L';
776 // d->h.version = IMAGE_FORMAT_VERSION;
777 // d->h.dirsize = n & 0xff;
778 // d->h.dirused = 0;
779 // d->h.dirext = (n >> 4) & 0xf0;
780 d->h.updated = D_WRITE_OK | D_UPDATED;
781 for (i=0; i<n; i++) clear_entry(&d->d[i]);
782 if (std::fwrite(&d->h, sizeof(directory_header), 1, f) != 1)
783 return true;
784 if (std::fwrite(&d->d[0], sizeof(directory_entry), (size_t)n,
785 f) != (size_t)n)
786 return true;
787 setbits32(d->h.eof, (int32_t)std::ftell(f));
788 return false;
789 }
790
Iinit()791 void Iinit()
792 { size_t i;
793 Istatus = I_INACTIVE;
794 current_input_directory = nullptr;
795 current_output_entry = nullptr;
796 current_output_directory = nullptr;
797 #ifdef BUILTIN_IMAGE
798 binary_read_filep = reduce_image;
799 #else
800 binary_read_file = binary_write_file = nullptr;
801 #endif
802 read_bytes_remaining = write_bytes_written = 0;
803 any_output_request = false;
804 std::strcpy(would_be_output_directory, "<unknown>");
805 for (i=0; i<fasl_files.size(); i++)
806 { if (!fasl_files[i].inUse ||
807 fasl_files[i].name == nullptr) continue;
808 else if (0x40000000+i == output_directory)
809 fasl_files[i].dir = open_pds(fasl_files[i].name, PDS_PENDING);
810 else
811 fasl_files[i].dir = open_pds(fasl_files[i].name,
812 i == output_directory ? PDS_OUTPUT :
813 PDS_INPUT);
814 }
815 }
816
817 // The code here was originally written to support module names up to
818 // 11 characters, but it has now been extended to support long names as
819 // well.
820 // The mechanism used is as follows:
821 // The name field in a directory entry is 12 characters long. For system
822 // special pseudo-modules all 12 characters are used for a name, and the
823 // cases used at present are InitialImage and HelpDataFile. For all
824 // user names the name is padded with blanks, and so user names of up
825 // to 11 characters live in the field with the 12th character a blank.
826 // To support long names I use values 0x80 and up in this 12th position.
827 // (NB position 12 is at offset 11 because of zero-base counting!)
828 // The first segment of a long name uses 11 characters of the user name
829 // and puts 0x80 in the 12th. Subsequent directory entries are used
830 // to hold more characters of the name. These hold 11 characters in the
831 // name field and 24 in the date, and put values 0x81, 0x82 etc in
832 // character 12. They will have a zero length field, but their position
833 // field MUST match that of the first record. This requirement is so that
834 // when I sort a directory the parts of a long name are kept both
835 // together and in the correct order. The last part of a long name has
836 // 0xff in position 12. The result is that I can distinguish the case
837 // of
838 // (.) a regular username of up to 11 chars (blank in position 12)
839 // (.) a system special file (non-blank, but under 0x80 in posn 12)
840 // (.) the start of a long name (0x80)
841 // (.) a middle part of a long name (0x81 ...)
842 // (.) the final part of a long name (0xff).
843 // when I match names here I will only allow a long-name match if my
844 // directory is pointing at the first part of a long name.
845 // As a further minor usefulness here if I find a match the non-zero value I
846 // return is the number of entries involved.
847
samename(const char * n1,directory * d,int j,size_t len)848 static int samename(const char *n1, directory *d, int j, size_t len)
849 // Compare the given names, given that n1 is of length len and n2 is
850 // blank-padded to exactly name_size characters. The special cases
851 // with n1 nullptr allow len to encode what I am looking for.
852 { const char *n2 = &d->d[j].D_name;
853 size_t i, n, recs;
854 if (len == IMAGE_CODE)
855 return (std::memcmp(n2, "InitialImage", 12) == 0);
856 if (len == HELP_CODE)
857 return (std::memcmp(n2, "HelpDataFile", 12) == 0);
858 if (len == BANNER_CODE)
859 return (std::memcmp(n2, "Start-Banner", 12) == 0);
860 if ((intptr_t)len < 0) // Hard code has never been fully supported
861 // and the use of "negative length codes" for
862 // using it is dodgy!
863 { char hard[16];
864 std::sprintf(hard, "HardCode<%.2x>", static_cast<int>((-len) & 0xff));
865 return (std::memcmp(n2, hard, 12) == 0);
866 }
867 if ((n2[11] & 0xff) > 0x80) return 0;
868 n = 0;
869 #define next_char_of_name (n++ < len ? *n1++ : ' ')
870 for (i=0; i<11; i++)
871 if (n2[i] != next_char_of_name) return 0;
872 if ((n2[11] & 0x80) == 0) return ((n >= len) ? 1 : 0);
873 recs = 1;
874 do
875 { n2 = &d->d[++j].D_name;
876 for (i=0; i<11; i++)
877 if (n2[i] != next_char_of_name) return 0;
878 for (i=12; i<36; i++)
879 if (n2[i] != next_char_of_name) return 0;
880 recs++;
881 }
882 while ((n2[11] & 0xff) != 0xff);
883 #undef next_char_of_name
884 if (n < len) return 0;
885 else return recs;
886 }
887
fasl_file_name(char * nn,directory * d,const char * name,size_t len)888 static void fasl_file_name(char *nn, directory *d, const char *name,
889 size_t len)
890 { size_t np;
891 std::strcpy(nn, d->full_filename);
892 np = std::strlen(nn);
893 #ifdef WIN32
894 nn[np++] = '\\';
895 #else
896 nn[np++] = '/';
897 #endif
898 if (name == nullptr)
899 { if (len == IMAGE_CODE) std::strcpy(&nn[np], "InitialImage");
900 else if (len == HELP_CODE) std::strcpy(&nn[np], "HelpDataFile");
901 else if (len == BANNER_CODE) std::strcpy(&nn[np], "Start-Banner");
902 else if ((intptr_t)len < 0) std::sprintf(&nn[np], "HardCode-%.2x",
903 static_cast<int>((-len) & 0xff));
904 }
905 else
906 { std::memcpy(&nn[np], name, len);
907 std::strcpy(&nn[np+len], ".fasl");
908 }
909 }
910
911
open_input(directory * d,const char * name,size_t len,size_t offset)912 static bool open_input(directory *d, const char *name, size_t len,
913 size_t offset)
914 // Set up binary_read_file to access the given module, returning true
915 // if it was not found in the given directory. I used to pass the
916 // names "InitialImage" and "HelpDataFile" directly to this function, but
917 // to allow for long module names I am changing things so that these special
918 // cases are indicated by passing down a nullptr string for the name and giving
919 // an associated length of -1 or -2 (resp).
920 { int i;
921 if (Istatus != I_INACTIVE || d == nullptr) return true;
922 nativedir = false;
923 if (d->full_filename != nullptr) // native directory mode
924 { char nn[LONGEST_LEGAL_FILENAME];
925 std::memset(nn, 0, sizeof(nn));
926 fasl_file_name(nn, d, name, len);
927 #ifdef BUILTIN_IMAGE
928 binary_read_filep = reduce_image;
929 read_bytes_remaining = REDUCE_IMAGE_SIZE;
930 #else
931 if ((binary_read_file = std::fopen(nn, "rb")) == nullptr) return true;
932 std::fseek(binary_read_file, 0L, SEEK_END);
933 read_bytes_remaining = std::ftell(binary_read_file);
934 #endif
935 #ifdef BUILTIN_IMAGE
936 binary_read_filep = reduce_image + offset;
937 #else
938 std::fseek(binary_read_file, static_cast<long>(offset), SEEK_SET);
939 #endif
940 Istatus = I_READING;
941 nativedir = true;
942 return false;
943 }
944 // I use simple linear search to scan the directory - mainly because I
945 // expect directories to be fairly small and once I have found a file
946 // I will take a long while to process it, so any clumsiness here is
947 // not critical.
948 // This linear search may not be so smart any more, in that REDUCE ends
949 // up with about 750 modules, and if I store machine code versions of all
950 // of these for (say) 4 architectures I end up with almost 4000 directory
951 // entries...
952 // I will not allow myself to read from whichever file is currently open
953 // for output.
954 // Because samename() is careful to ensure it only reports a match when
955 // pointed at the start of a long name it is OK to search in steps of 1
956 // here.
957 for (i=0; i<get_dirused(*d); i++)
958 { if (samename(name, d, i, len) &&
959 &d->d[i] != current_output_entry)
960 {
961 #ifdef BUILTIN_IMAGE
962 binary_read_filep = reduce_image;
963 read_bytes_remaining = bits24(&d->d[i].D_size);
964 binary_read_filep += bits32(&d->d[i].D_position)+offset;
965 i = 0;
966 #else
967 binary_read_file = d->f;
968 read_bytes_remaining = bits24(&d->d[i].D_size);
969 i = std::fseek(binary_read_file,
970 bits32(&d->d[i].D_position)+offset, SEEK_SET);
971 #endif
972 if (i == 0) // If fseek succeeded it returned zero
973 { Istatus = I_READING;
974 return false;
975 }
976 else return true;
977 }
978 }
979 return true;
980 }
981
IreInit()982 void IreInit()
983 {
984 }
985
for_qsort(void const * aa,void const * bb)986 static int for_qsort(void const *aa, void const *bb)
987 { directory_entry *a = (directory_entry *)aa,
988 *b = (directory_entry *)bb;
989 long int ap = bits32(&a->D_position), bp = bits32(&b->D_position);
990 if (ap < bp) return -1;
991 else if (ap > bp) return 1;
992 // I make the position of the module in the image my primary sort key.
993 // Over-long module names are coped with by giving each part of the
994 // name the same position field, but marking the 12th character of the
995 // name field (D_space) with 0x80, 0x81 ... in extension records. Note that
996 // a regular short module name has a blank character there, while the special
997 // cases of "InitialImage" and "HelpDataFile" each have 'e' there,
998 // "Start-Banner" has 'r', while hard code has '>'.
999 // So bytes 0x80 and up are clearly (if hackily!) distinguished.
1000 ap = a->D_space & 0xff, bp = b->D_space & 0xff;
1001 if (ap < bp) return -1;
1002 else if (ap > bp) return 1;
1003 else return 0;
1004 }
1005
sort_directory(directory * d)1006 static void sort_directory(directory *d)
1007 { std::qsort(reinterpret_cast<void *>(d->d),
1008 (size_t)get_dirused(*d),
1009 sizeof(directory_entry), for_qsort);
1010 }
1011
enlarge_directory(int current_size)1012 static directory *enlarge_directory(int current_size)
1013 { int n = (3*current_size)/2;
1014 int newsize = sizeof(directory)+(n-1)*sizeof(directory_entry);
1015 int newpos = sizeof(directory_header)+n*sizeof(directory_entry);
1016 // enlarge_directory() is only called when an output library is known
1017 // to exist, so I do not need to check for that here.
1018 int dirno = library_number(qvalue(output_library));
1019 directory *d1 = fasl_files[dirno].dir;
1020 if (n > current_size+20) n = current_size+20;
1021 for (;;)
1022 { directory_entry *first;
1023 std::FILE *f;
1024 char buffer[512]; // I hope this is not done too often, since this
1025 // is not a very big buffer size for the copy.
1026 int32_t firstpos, firstlen, newfirst, eofpos;
1027 sort_directory(d1);
1028 first = &d1->d[0];
1029 firstpos = bits32(&first->D_position);
1030 if (firstpos >= newpos) break;
1031 // Here I need to copy a module up to the end of the file to make room
1032 // for the enlarged directory
1033 firstlen = bits24(&first->D_size);
1034 newfirst = eofpos = bits32(d1->h.eof);
1035 f = d1->f;
1036 while (firstlen >= (int32_t)sizeof(buffer))
1037 { std::fseek(f, firstpos, SEEK_SET);
1038 if (std::fread(buffer, sizeof(buffer), 1, f) != 1) return nullptr;
1039 std::fseek(f, eofpos, SEEK_SET);
1040 if (std::fwrite(buffer, sizeof(buffer), 1, f) != 1) return nullptr;
1041 firstlen -= sizeof(buffer);
1042 firstpos += sizeof(buffer);
1043 eofpos += sizeof(buffer);
1044 }
1045 if (firstlen != 0)
1046 { std::fseek(f, firstpos, SEEK_SET);
1047 if (std::fread(buffer, firstlen, 1, f) != 1) return nullptr;
1048 std::fseek(f, eofpos, SEEK_SET);
1049 if (std::fwrite(buffer, firstlen, 1, f) != 1) return nullptr;
1050 eofpos += firstlen;
1051 }
1052 setbits32(&first->D_position, newfirst);
1053 if ((first->D_space & 0xff) == 0x80)
1054 { do
1055 { first++;
1056 setbits32(&first->D_position, newfirst);
1057 }
1058 while ((first->D_space & 0xff) != 0xff);
1059 }
1060 setbits32(d1->h.eof, eofpos);
1061 }
1062 std::fseek(d1->f, newpos, SEEK_SET);
1063 directory *d2 = reinterpret_cast<directory *>(new char[newsize]);
1064 if (d2 == nullptr) return nullptr;
1065 std::memcpy(d2, d1, sizeof(directory)+(current_size-1)*sizeof(directory_entry));
1066 d2->h.dirsize = static_cast<unsigned char>(n & 0xff);
1067 d2->h.dirext = static_cast<unsigned char>((d2->h.dirext & 0x0f) +
1068 ((n>>4) & 0xf0));
1069 d2->h.updated |= D_COMPACT | D_UPDATED;
1070 while (n>current_size) clear_entry(&d2->d[--n]);
1071 fasl_files[dirno].dir = d2;
1072 delete [] reinterpret_cast<char *>(d1);
1073 return d2;
1074 }
1075
open_output(const char * name,size_t len)1076 bool open_output(const char *name, size_t len)
1077 // Set up binary_write_file to access the given module, returning true
1078 // if anything went wrong. Remember name==nullptr for initial image & help
1079 // data.
1080 { int i, j, n;
1081 const char *ct;
1082 char hard[16];
1083 directory *d;
1084 std::time_t t = std::time(nullptr);
1085 LispObject oo = qvalue(output_library);
1086 #ifdef BUILTIN_IMAGE
1087 return true;
1088 #endif
1089 nativedir = false;
1090 if (!is_library(oo)) return true;
1091 d = fasl_files[library_number(oo)].dir;
1092 if (d == nullptr) return true; // closed handle, I guess
1093 if ((d->h.updated & D_WRITE_OK) == 0) return true;
1094 // The main effect of the next line will be to prohibit opening a new
1095 // FASL file while I am in the middle of reading one that already exists.
1096 // Indeed this is a restriction, but at present it seems a very reasonable
1097 // on for me to apply.
1098 if (Istatus != I_INACTIVE) return true;
1099 if (d->h.updated & D_PENDING)
1100 {
1101 // See comments in fasl.c under Lbanner for why I choose to report a failure
1102 // rather then do and unpending() when the output module I am creating is
1103 // just to contain banner information.
1104 if (name==nullptr && len==BANNER_CODE) return true;
1105 if (unpending(d)) return true;
1106 }
1107 current_output_directory = d;
1108 if (d->full_filename != nullptr) // native directory mode
1109 { char nn[LONGEST_LEGAL_FILENAME];
1110 std::memset(nn, 0, sizeof(nn));
1111 fasl_file_name(nn, d, name, len);
1112 if ((binary_write_file = std::fopen(nn,
1113 "wb")) == nullptr) return true;
1114 write_bytes_written = 0;
1115 Istatus = I_WRITING;
1116 nativedir = true;
1117 return false;
1118 }
1119 // I use simple linear search to scan the directory - mainly because I
1120 // expect directories to be fairly small and once I have found a file
1121 // I will take a long while to process it, so any clumsiness here is
1122 // not critical. Again note it is OK to scan in steps of 1 despite the
1123 // fact that long-names are stored split across consecutive directory slots.
1124 for (i=0; i<get_dirused(*d); i++)
1125 { if (samename(name, d, i, len))
1126 { current_output_entry = &d->d[i];
1127 d->h.updated |= D_COMPACT | D_UPDATED;
1128 if (t == (std::time_t)(-1)) ct = "not dated";
1129 else ct = std::ctime(&t);
1130 // Note that I treat the result handed back by ctime() as delicate, in that
1131 // I do not do any library calls between calling ctime and copying the
1132 // string it returns to somewhere that is under my own control.
1133 std::strncpy(&d->d[i].D_date, ct, date_size);
1134 binary_write_file = d->f;
1135 write_bytes_written = 0;
1136 std::memcpy(&d->d[i].D_position, d->h.eof, 4);
1137 // For long names I must put the location in each record
1138 if (d->d[i].D_space & 0x80)
1139 { j = 0;
1140 do
1141 { j++;
1142 std::memcpy(&d->d[i+j].D_position, d->h.eof, 4);
1143 }
1144 while ((d->d[i+j].D_space & 0xff) != 0xff);
1145 }
1146 i = std::fseek(binary_write_file, bits32(d->h.eof), SEEK_SET);
1147 if (i == 0) Istatus = I_WRITING;
1148 else current_output_directory = nullptr;
1149 if (name == nullptr && len == IMAGE_CODE)
1150 d->h.version = IMAGE_FORMAT_VERSION;
1151 return i;
1152 }
1153 }
1154 // Here the name did not already exist, and so I will need to enter it into
1155 // the directory. If I get here the variable i points to the first unused
1156 // directory entry.
1157 if (len == IMAGE_CODE)
1158 { name = "InitialImage";
1159 n = 1;
1160 d->h.version = IMAGE_FORMAT_VERSION;
1161 }
1162 else if (len == HELP_CODE) name = "HelpDataFile", len = IMAGE_CODE,
1163 n = 1;
1164 else if (len == BANNER_CODE) name = "Start-Banner", len = IMAGE_CODE,
1165 n = 1;
1166 else if ((intptr_t)len < 0)
1167 { std::sprintf(hard, "HardCode<%.2x>",
1168 static_cast<int>((-len) & 0xff));
1169 name = hard, len = IMAGE_CODE, n = 1;
1170 }
1171 else if (len <= 11) n = 1;
1172 else if (len <= 11+11+24) n = 2;
1173 else if (len <= 11+11+11+24+24) n = 3;
1174 else return true; // Name longer than 81 chars not supported, sorry
1175 while (i+n > static_cast<int>(get_dirsize(*d)))
1176 { d = enlarge_directory(i);
1177 current_output_directory = d;
1178 if (d == nullptr) return true;
1179 }
1180 current_output_entry = &d->d[i];
1181 if (len == IMAGE_CODE)
1182 { d->d[i].D_newline = NEWLINE_CHAR;
1183 std::memcpy(&d->d[i].D_name, name, 12);
1184 std::memset(&d->d[i].D_date, ' ', date_size);
1185 std::memset(&d->d[i].D_size, 0, 3);
1186 std::memcpy(&d->d[i].D_position, d->h.eof, 4);
1187 }
1188 else
1189 { size_t np;
1190 const char *p;
1191 // First I will clear all the relevant fields to blanks.
1192 for (j=0; j<n; j++)
1193 { d->d[i+j].D_newline = '\n';
1194 std::memset(&d->d[i+j].D_name, ' ', name_size);
1195 std::memset(&d->d[i+j].D_date, ' ', date_size);
1196 std::memset(&d->d[i+j].D_size, 0, 3);
1197 std::memcpy(&d->d[i+j].D_position, d->h.eof, 4);
1198 }
1199 #define next_char_of_name (np++ >= len ? ' ' : *p++)
1200 np = 0;
1201 p = name;
1202 for (j=0; j<n; j++)
1203 { int k;
1204 for (k=0; k<11; k++) (&d->d[i+j].D_name)[k] = next_char_of_name;
1205 if (j != 0)
1206 for (k=0; k<24; k++)
1207 (&d->d[i+j].D_date)[k] = next_char_of_name;
1208 if (j == 0 && n == 1) d->d[i+j].D_space = ' ';
1209 else if (j == n-1) d->d[i+j].D_space = 0xff;
1210 else d->d[i+j].D_space = static_cast<char>(0x80+j);
1211 #undef next_char_of_name
1212 }
1213 }
1214 if (t == (std::time_t)(-1)) ct = "** *** not dated *** ** ";
1215 else ct = std::ctime(&t);
1216 std::memcpy(&d->d[i].D_date, ct, date_size);
1217 set_dirused(&d->h, get_dirused(*d)+n);
1218 binary_write_file = d->f;
1219 write_bytes_written = 0;
1220 d->h.updated |= D_UPDATED;
1221 i = std::fseek(binary_write_file, bits32(d->h.eof), SEEK_SET);
1222 if (i == 0)
1223 { Istatus = I_WRITING;
1224 return false;
1225 }
1226 else
1227 { current_output_directory = nullptr;
1228 return true;
1229 }
1230 }
1231
list_one_native(string Cname,string Cleafname,int why,long int size)1232 static void list_one_native(string Cname, string Cleafname,
1233 int why, long int size)
1234 { const char *name = Cname.c_str();
1235 struct stat statbuff;
1236 char shortname[100];
1237 char *p;
1238 if (why != SCAN_FILE) return;
1239 stat(name, &statbuff); // read the date on the file
1240 while (*name != 0) name++;
1241 // I need a comment about why the loop on the next line is guaranteed to
1242 // terminate. Well I only ought to be executing this code if the image
1243 // is represented as an operating-system directory, and when scan_directory
1244 // inspects it all the files in it are within it (gee!) and so have names
1245 // along the line of "csl.img/compat.fasl". So I really do expect to find
1246 // a directory separator character within the name.
1247 while (*name != '/' && *name != '\\') name--;
1248 std::strncpy(shortname, name+1, sizeof(shortname)-1);
1249 shortname[sizeof(shortname)-1] = 0;
1250 p = shortname;
1251 while (*p != 0 && std::strcmp(p, ".fasl") != 0) p++;
1252 *p = 0;
1253 if (std::strlen(shortname) > 12) trace_printf(
1254 " %s\n %-24.24s size: %ld\n",
1255 shortname, std::ctime(&(statbuff.st_mtime)),
1256 size);
1257 else trace_printf(
1258 " %-12.12s %-24.24s size: %ld\n",
1259 shortname, std::ctime(&(statbuff.st_mtime)),
1260 size);
1261 }
1262
1263
list_one_library(LispObject oo,bool out_only)1264 static void list_one_library(LispObject oo, bool out_only)
1265 { int j;
1266 directory *d = fasl_files[library_number(oo)].dir;
1267 if (d->full_filename != nullptr)
1268 { string name = d->full_filename;
1269 trace_printf("Directory %s\n", d->full_filename);
1270 scan_directory(name, list_one_native);
1271 return;
1272 }
1273 trace_printf("\nFile %s (dirsize %ld length %ld",
1274 d->filename, static_cast<long>(get_dirsize(*d)),
1275 static_cast<long>(bits32(d->h.eof)));
1276 j = d->h.updated;
1277 if (j != 0) trace_printf(",");
1278 if (j & D_WRITE_OK) trace_printf(" Writable");
1279 if (j & D_UPDATED) trace_printf(" Updated");
1280 if (j & D_COMPACT) trace_printf(" NeedsCompaction");
1281 if (j & D_PENDING) trace_printf(" Pending");
1282 if (out_only) trace_printf(" OutputOnly");
1283 trace_printf("):\n");
1284 // The format string used here will need adjustment if you ever change the
1285 // number of characters used to store names or dates.
1286 for (j=0; j<get_dirused(*d); j++)
1287 { int n = 0;
1288 if (d->d[j].D_space & 0x80)
1289 { trace_printf(" %.11s", &d->d[j].D_name);
1290 do
1291 { n++;
1292 trace_printf("%.11s%.24s",
1293 &d->d[j+n].D_name, &d->d[j+n].D_date);
1294 }
1295 while ((d->d[j+n].D_space & 0xff) != 0xff);
1296 trace_printf(
1297 "\n %-24.24s position %-7ld size: %ld\n",
1298 &d->d[j].D_date,
1299 static_cast<long>(bits32(&d->d[j].D_position)),
1300 static_cast<long>(bits24(&d->d[j].D_size)));
1301 j += n;
1302 }
1303 else trace_printf(
1304 " %-12.12s %-24.24s position %-7ld size: %ld\n",
1305 &d->d[j].D_name, &d->d[j].D_date,
1306 static_cast<long>(bits32(&d->d[j].D_position)),
1307 static_cast<long>(bits24(&d->d[j].D_size)));
1308 }
1309 }
1310
Ilist()1311 void Ilist()
1312 { LispObject il = qvalue(input_libraries), w;
1313 LispObject ol = qvalue(output_library);
1314 while (consp(il))
1315 { w = car(il); il = cdr(il);
1316 if (!is_library(w)) continue;
1317 if (w == ol) ol = nil;
1318 list_one_library(w, false);
1319 }
1320 if (is_library(ol)) list_one_library(ol, true);
1321 }
1322
1323 static LispObject mods;
1324
collect_modules(string Cname,string Cleafname,int why,long int size)1325 static void collect_modules(string Cname, string Cleafname,
1326 int why, long int size)
1327 { int k = 0;
1328 LispObject v;
1329 char *p = reinterpret_cast<char *>(&celt(boffo, 0));
1330 if (why != SCAN_FILE) return;
1331 Save save(mods);
1332 const char *name = Cleafname.c_str();
1333 while (*name != '.' && *name != 0)
1334 { *p++ = *name++;
1335 k++;
1336 }
1337 if (std::strcmp(name, ".fasl") != 0) return;
1338 v = iintern(boffo, k, lisp_package, 0);
1339 if (exceptionPending()) return;
1340 save.restore(mods);
1341 mods = cons(v, mods);
1342 }
1343
Llibrary_members(LispObject env,LispObject oo)1344 LispObject Llibrary_members(LispObject env, LispObject oo)
1345 { int i, j, k;
1346 directory *d = fasl_files[library_number(oo)].dir;
1347 LispObject v, r = nil;
1348 char *p;
1349 if (d->full_filename != nullptr)
1350 { mods = nil;
1351 string name = d->full_filename;
1352 scan_directory(d->full_filename, collect_modules);
1353 return onevalue(mods);
1354 }
1355 for (j=0; j<get_dirused(*d); j++)
1356 { int n = 0;
1357 p = reinterpret_cast<char *>(&celt(boffo, 0));
1358 k = 0;
1359 if (d->d[j].D_space & 0x80)
1360 { for (i=0; i<11; i++)
1361 { *p++ = (&d->d[j].D_name)[i];
1362 k++;
1363 }
1364 do
1365 { n++;
1366 for (i=0; i<11; i++)
1367 { *p++ = (&d->d[j+n].D_name)[i];
1368 k++;
1369 }
1370 }
1371 while ((d->d[j+n].D_space & 0xff) != 0xff);
1372 j += n;
1373 }
1374 else
1375 { if (std::memcmp(&d->d[j].D_name, "InitialImage", 12) == 0 ||
1376 std::memcmp(&d->d[j].D_name, "HelpDataFile", 12) == 0 ||
1377 std::memcmp(&d->d[j].D_name, "Start-Banner", 12) == 0 ||
1378 (std::memcmp(&d->d[j].D_name, "HardCode<", 9) == 0 &&
1379 (&d->d[j].D_name)[11] == '>'))
1380 continue; // not user modules
1381 for (i=0; i<12; i++)
1382 { *p++ = (&d->d[j].D_name)[i];
1383 k++;
1384 }
1385 }
1386 while (k>0 && p[-1] == ' ') k--, p--;
1387 *p = 0;
1388 Save save(r);
1389 v = iintern(boffo, k, lisp_package, 0);
1390 errexit();
1391 save.restore(r);
1392 r = cons(v, r);
1393 }
1394 return onevalue(r);
1395 }
1396
Llibrary_members0(LispObject env)1397 LispObject Llibrary_members0(LispObject env)
1398 // This returns a list of the modules in the first library on the current
1399 // search path.
1400 { LispObject il = qvalue(input_libraries), w;
1401 LispObject ol = qvalue(output_library);
1402 while (consp(il))
1403 { w = car(il); il = cdr(il);
1404 if (!is_library(w)) continue;
1405 return Llibrary_members(nil, w);
1406 }
1407 if (is_library(ol)) return Llibrary_members(nil, ol);
1408 else return onevalue(nil);
1409 }
1410
Imodulep1(int i,const char * name,size_t len,char * datestamp,size_t * size,char * expanded_name)1411 bool Imodulep1(int i, const char *name, size_t len, char *datestamp,
1412 size_t *size,
1413 char *expanded_name)
1414 // Hands back information about whether the given module exists in the
1415 // image file with index i.
1416 { directory *d = fasl_files[i].dir;
1417 if (d == nullptr) return true;
1418 if (d->full_filename != nullptr)
1419 { char nn[LONGEST_LEGAL_FILENAME];
1420 struct stat statbuff;
1421 std::memset(nn, 0, sizeof(nn));
1422 fasl_file_name(nn, d, name, len);
1423 if (stat(nn, &statbuff) != 0) return true; // file not present
1424 std::strcpy(expanded_name, nn);
1425 std::strcpy(datestamp, std::ctime(&(statbuff.st_mtime)));
1426 // Note that FASL modules here will surely never even start to get towards
1427 // the size-limits of a 32-bit integer!
1428 *size = (int32_t)statbuff.st_size;
1429 return false;
1430 }
1431 for (int j=0; j<get_dirused(*d); j++)
1432 { if (samename(name, d, j, len))
1433 { const char *n = fasl_files[i].dir->filename;
1434 const char *p1 = "(", *p2 = ")";
1435 if (d->full_filename != nullptr)
1436 {
1437 #ifdef WIN32
1438 p1 = "\\";
1439 #else
1440 p1 = "/";
1441 #endif
1442 p2 = "";
1443 }
1444 std::memcpy(datestamp, &d->d[j].D_date, date_size);
1445 *size = bits24(&d->d[j].D_size);
1446 if (name == nullptr) std::sprintf(expanded_name,
1447 "%s%sInitialImage%s", n, p1, p2);
1448 else std::sprintf(expanded_name,
1449 "%s%s%.*s%s", n, p1, static_cast<int>(len), name, p2);
1450 return false;
1451 }
1452 }
1453 return true;
1454 }
1455
Imodulep(const char * name,size_t len,char * datestamp,size_t * size,char * expanded_name)1456 bool Imodulep(const char *name, size_t len, char *datestamp,
1457 size_t *size,
1458 char *expanded_name)
1459 // Hands back information about whether the given module exists, and
1460 // if it does when it was written. Code should be very similar to
1461 // that in Iopen.
1462 { for (LispObject il = qvalue(input_libraries); consp(il);
1463 il=cdr(il))
1464 { LispObject oo = car(il);
1465 if (!is_library(oo)) continue;
1466 if (!Imodulep1(library_number(oo), name, len, datestamp, size,
1467 expanded_name))
1468 return false;
1469 }
1470 return true;
1471 }
1472
1473 directory *rootDirectory = nullptr;
1474
IopenRoot(char * expanded_name,size_t hard,int sixtyfour)1475 bool IopenRoot(char *expanded_name, size_t hard, int sixtyfour)
1476 // Opens the "InitialImage" file so that it can be loaded. Note that
1477 // when I am about to do this I do not have a valid heap image loaded, and
1478 // so it would NOT be possible to use the regular search-path mechanism for
1479 // libraries. Therefore I will just use images as specified from the
1480 // command line (or by default).
1481 { const char *n;
1482 size_t i;
1483 if (hard == 0) hard = IMAGE_CODE;
1484 for (i=0; i<fasl_files.size(); i++)
1485 { if (!fasl_files[i].inUse) continue;
1486 bool bad = open_input(fasl_files[i].dir, nullptr, hard, 0);
1487 // The name that I return (for possible display in error messages) will be
1488 // either that of the file that was opened, or one relating to the last
1489 // entry in the search path.
1490 n = fasl_files[i].dir->filename;
1491
1492 if (hard == IMAGE_CODE) rootDirectory = fasl_files[i].dir;
1493
1494 if (expanded_name != nullptr)
1495 { if (hard == IMAGE_CODE)
1496 { std::sprintf(expanded_name, "%s(InitialImage)", n);
1497 }
1498 else if (hard == BANNER_CODE)
1499 std::sprintf(expanded_name, "%s(InitialImage)", n);
1500 else std::sprintf(expanded_name, "%s(HardCode<%.2x>)",
1501 n, static_cast<int>((-hard) & 0xff));
1502 }
1503 if (!bad) return false;
1504 }
1505 return true;
1506 }
1507
1508
Iopen(const char * name,size_t len,int forinput,char * expanded_name)1509 bool Iopen(const char *name, size_t len, int forinput,
1510 char *expanded_name)
1511 // Make file with the given name available through this package of
1512 // routines. (name) is a pointer to a string (len characters valid) that
1513 // names a fasl file. (forinput) specifies the direction of the transfer
1514 // to set up. Returns true if something failed.
1515 // name can be nullptr when a module is opened for output, and then output
1516 // is sent to "InitialImage".
1517 // The same is done for input, but it would be more sensible to use
1518 // IopenRoot() to access the root image.
1519 { const char *n;
1520 if (name == nullptr) len = IMAGE_CODE;
1521 if (forinput != IOPEN_OUT)
1522 { int i;
1523 LispObject il = qvalue(input_libraries);
1524 while (consp(il))
1525 { bool bad;
1526 LispObject oo = car(il); il = cdr(il);
1527 if (!is_library(oo)) continue;
1528 i = library_number(oo);
1529 bad = open_input(fasl_files[i].dir, name, len, 0);
1530 // The name that I return (for possible display in error messages) will be
1531 // either that of the file that was opened, or one relating to the last
1532 // entry in the search path.
1533 n = fasl_files[i].dir->filename;
1534 if (expanded_name != nullptr)
1535 { const char *p1 = "(", *p2 = ")";
1536 if (fasl_files[i].dir->full_filename != nullptr)
1537 {
1538 #ifdef WIN32
1539 p1 = "\\";
1540 #else
1541 p1 = "/";
1542 #endif
1543 p2 = "";
1544 }
1545 std::sprintf(expanded_name, "%s%s%.*s%s", n, p1,
1546 static_cast<int>(len), name, p2);
1547 }
1548 if (!bad) return false;
1549 }
1550 return true;
1551 }
1552 if (!any_output_request)
1553 { if (expanded_name != nullptr)
1554 std::strcpy(expanded_name, "<no output file specified>");
1555 return true;
1556 }
1557 n = would_be_output_directory;
1558 if (expanded_name != nullptr)
1559 { const char *p1 = "(", *p2 = ")";
1560 LispObject oo = qvalue(output_library);
1561 directory *d;
1562 if (!is_library(oo)) return true;
1563 d = fasl_files[library_number(oo)].dir;
1564 if (d->full_filename != nullptr)
1565 {
1566 #ifdef WIN32
1567 p1 = "\\";
1568 #else
1569 p1 = "/";
1570 #endif
1571 p2 = "";
1572 }
1573 if (len == IMAGE_CODE)
1574 std::sprintf(expanded_name, "%s%sInitialImage%s", p1, n, p2);
1575 else std::sprintf(expanded_name, "%s%s%.*s%s", n, p1,
1576 static_cast<int>(len), name, p2);
1577 }
1578 return open_output(name, len);
1579 }
1580
Iwriterootp(char * expanded_name)1581 bool Iwriterootp(char *expanded_name)
1582 // Test if it will be possible to write out an image file. Used
1583 // by (preserve) so it can report that this would fail without actually
1584 // doing anything too drastic.
1585 { directory *d;
1586 LispObject oo = qvalue(output_library);
1587 if (!any_output_request)
1588 { std::strcpy(expanded_name, "<no output file specified>");
1589 return true;
1590 }
1591 std::sprintf(expanded_name, "%s(InitialImage)",
1592 would_be_output_directory);
1593 if (!is_library(oo)) return true;
1594 d = fasl_files[library_number(oo)].dir;
1595 if (d == nullptr) return true; // closed handle, I guess
1596 // At present for native directories the WRITE_OK flag is left set without
1597 // proper checking of file access permissions.
1598 if ((d->h.updated & D_WRITE_OK) == 0) return true;
1599 if (Istatus != I_INACTIVE) return true;
1600 return false;
1601 }
1602
Iopen_banner(int code)1603 bool Iopen_banner(int code)
1604 // Get ready to handle the startup banner.
1605 // code = 0 open for reading
1606 // code = -1 open for writing
1607 // code = -2 delete banner file
1608 { if (code == -2) return Idelete(nullptr, BANNER_CODE);
1609 else if (code == 0)
1610 { LispObject il = qvalue(input_libraries);
1611 while (consp(il))
1612 { bool bad;
1613 LispObject oo = car(il); il = cdr(il);
1614 if (!is_library(oo)) continue;
1615 bad = open_input(fasl_files[library_number(oo)].dir,
1616 nullptr, BANNER_CODE, 0);
1617 if (!bad) return false;
1618 }
1619 return true;
1620 }
1621 if (!any_output_request) return true;
1622 else return open_output(nullptr, BANNER_CODE);
1623 }
1624
1625 // Set up binary_read_file to read from standard input. Return true if
1626 // things fail.
1627
Iopen_from_stdin()1628 bool Iopen_from_stdin()
1629 { if (Istatus != I_INACTIVE) return true;
1630 #ifdef BUILTIN_IMAGE
1631 binary_read_filep = nullptr;
1632 #else
1633 binary_read_file = nullptr;
1634 #endif
1635 read_bytes_remaining = -1;
1636 Istatus = I_READING;
1637 return false;
1638 }
1639
Iopen_to_stdout()1640 bool Iopen_to_stdout()
1641 { if (Istatus != I_INACTIVE) return true;
1642 Istatus = I_WRITING;
1643 return false;
1644 }
1645
Idelete(const char * name,size_t len)1646 bool Idelete(const char *name, size_t len)
1647 { int i, nrec;
1648 directory *d;
1649 LispObject oo = qvalue(output_library);
1650 if (!is_library(oo)) return true;
1651 d = fasl_files[library_number(oo)].dir;
1652 if (d == nullptr ||
1653 (d->h.updated && D_WRITE_OK) == 0 ||
1654 Istatus != I_INACTIVE) return true;
1655 if (d->full_filename != nullptr)
1656 { char nn[LONGEST_LEGAL_FILENAME];
1657 std::memset(nn, 0, sizeof(nn));
1658 fasl_file_name(nn, d, name, len);
1659 return (std::remove(nn) != 0);
1660 }
1661 for (i=0; i<get_dirused(*d); i++)
1662 { if ((nrec = samename(name, d, i, len)) != 0)
1663 { int j;
1664 set_dirused(&d->h, get_dirused(*d)-nrec);
1665 for (j=i; j<get_dirused(*d); j++)
1666 d->d[j] = d->d[j+nrec];
1667 // I tidy up the now-unused entry - in some sense this is a redundant
1668 // operation, but I think it makes the file seem neater, which may possibly
1669 // help avoid confusion and ease debugging.
1670 while (nrec-- != 0)
1671 { std::memset(&d->d[j].D_name, ' ', name_size);
1672 std::memcpy(&d->d[j].D_name, "<Unused>", 8);
1673 std::memset(&d->d[j].D_date, ' ', date_size);
1674 (&d->d[j].D_date)[0] = '-';
1675 setbits32(&d->d[j].D_position, 0);
1676 setbits24(&d->d[j].D_size, 0);
1677 j++;
1678 }
1679 d->h.updated |= D_COMPACT | D_UPDATED;
1680 return false;
1681 }
1682 }
1683 return true;
1684 }
1685
Icopy(const char * name,size_t len)1686 bool Icopy(const char *name, size_t len)
1687 // Find the named module in one of the input files, and if the place that
1688 // it is found is not already the output file copy it to the output. These days
1689 // either (or neither or both!) the places could be either my own 1-file
1690 // image-files or they could be native directories. But for now I will just
1691 // ignore that and only support the older situation. That is because I am lazy!
1692 { int i, ii, j, n;
1693 long int k, l, save = read_bytes_remaining;
1694 char hard[16];
1695 directory *d, *id;
1696 LispObject il, oo = qvalue(output_library);
1697 if (!is_library(oo)) return true;
1698 d = fasl_files[library_number(oo)].dir;
1699 // Only valid if there is an output file and nothing else is going on.
1700 if (d == nullptr ||
1701 (d->h.updated & D_WRITE_OK) == 0 ||
1702 Istatus != I_INACTIVE) return true;
1703 if (d->h.updated & D_PENDING)
1704 { if (unpending(d)) return true;
1705 }
1706 // The next line refuses to copy INTO a native directory...
1707 if (d->full_filename != nullptr) return true;
1708 // Search for a suitable input module to copy...
1709 for (il=qvalue(input_libraries); consp(il); il = cdr(il))
1710 { oo = car(il);
1711 if (!is_library(oo)) continue;
1712 i = library_number(oo);
1713 id = fasl_files[i].dir;
1714 // Not updated for native dirs yet
1715 if (id->full_filename != nullptr) continue;
1716 for (ii=0; ii<get_dirused(*id); ii++)
1717 if (samename(name, id, ii, len)) goto found;
1718 }
1719 return true; // Module to copy not found
1720 found:
1721 // If the potential input module found was in the output directory exit now.
1722 if (id == d) return false;
1723 // Now scan output directory to see where to put result
1724 for (i=0; i<get_dirused(*d); i++)
1725 // Not updated for native dirs yet
1726 if (samename(name, d, i, len))
1727 { d->h.updated |= D_UPDATED | D_COMPACT;
1728 goto ofound;
1729 }
1730 // The file was not previously present in the output directory, so
1731 // I need to insert it. The code here is copies from open_output and is
1732 // now messy enoug that I should really move it to a sub-function.
1733 if (len == IMAGE_CODE)
1734 name = "InitialImage", n = 1;
1735 else if (len == HELP_CODE)
1736 name = "HelpDataFile", len = IMAGE_CODE, n = 1;
1737 else if (len == BANNER_CODE)
1738 name = "Start-Banner", len = IMAGE_CODE, n = 1;
1739 else if ((intptr_t)len < 0)
1740 { std::sprintf(hard, "HardCode<%.2x>",
1741 static_cast<int>((-len) & 0xff));
1742 name = hard, len = IMAGE_CODE, n = 1;
1743 }
1744 else if (len <= 11) n = 1;
1745 else if (len <= 11+11+24) n = 2;
1746 else if (len <= 11+11+11+24+24) n = 3;
1747 else return true; // Name longer than 81 chars not supported, sorry
1748 while (i+n > static_cast<int>(get_dirsize(*d)))
1749 { d = enlarge_directory(i);
1750 current_output_directory = d;
1751 if (d == nullptr) return true;
1752 }
1753 current_output_entry = &d->d[i];
1754 if (len == IMAGE_CODE)
1755 { d->d[i].D_newline = NEWLINE_CHAR;
1756 std::memcpy(&d->d[i].D_name, name, 12);
1757 std::memset(&d->d[i].D_date, ' ', date_size);
1758 std::memset(&d->d[i].D_size, 0, 3);
1759 std::memcpy(&d->d[i].D_position, d->h.eof, 4);
1760 }
1761 else
1762 { size_t np;
1763 const char *p;
1764 // First I will clear all the relevant fields to blanks.
1765 for (j=0; j<n; j++)
1766 { d->d[i+j].D_newline = '\n';
1767 std::memset(&d->d[i+j].D_name, ' ', name_size);
1768 std::memset(&d->d[i+j].D_date, ' ', date_size);
1769 std::memset(&d->d[i+j].D_size, 0, 3);
1770 std::memcpy(&d->d[i+j].D_position, d->h.eof, 4);
1771 }
1772 #define next_char_of_name (np++ >= len ? ' ' : *p++)
1773 np = 0;
1774 p = name;
1775 for (j=0; j<n; j++)
1776 { for (k=0; k<11; k++) (&d->d[i+j].D_name)[k] = next_char_of_name;
1777 if (j != 0)
1778 for (k=0; k<24; k++)
1779 (&d->d[i+j].D_date)[k] = next_char_of_name;
1780 if (j == 0 && n == 1) d->d[i+j].D_space = ' ';
1781 else if (j == n-1) d->d[i+j].D_space = 0xff;
1782 else d->d[i+j].D_space = static_cast<char>(0x80+j);
1783 #undef next_char_of_name
1784 }
1785 }
1786 set_dirused(&d->h, get_dirused(*d)+n);
1787 ofound:
1788 std::memcpy(&d->d[i].D_date, &id->d[ii].D_date, date_size);
1789 trace_printf("\nCopy %.*s from %s to %s\n",
1790 len, name, id->filename, d->filename);
1791 std::memcpy(&d->d[i].D_position, d->h.eof, 4);
1792 if (d->d[i].D_space & 0x80)
1793 { n = 0;
1794 do
1795 { n++;
1796 std::memcpy(&d->d[i+n].D_position, d->h.eof, 4);
1797 }
1798 while ((d->d[i+n].D_space & 0xff) != 0xff);
1799 }
1800 // I provisionally set the size to zero so that if something goes wrong
1801 // I will still have a tolerably sensible image file.
1802 std::memset(&d->d[i].D_size, 0, 3);
1803 d->h.updated |= D_UPDATED;
1804 if (std::fseek(d->f, bits32(&d->d[i].D_position), SEEK_SET) != 0 ||
1805 std::fseek(id->f, bits32(&id->d[ii].D_position),
1806 SEEK_SET) != 0) return true;
1807 l = bits24(&id->d[ii].D_size);
1808 for (k=0; k<l; k++)
1809 { int c = std::getc(id->f);
1810 if (c == EOF) return true;
1811 std::putc(c, d->f);
1812 }
1813 read_bytes_remaining = 0;
1814 read_bytes_remaining = save;
1815 if (std::fflush(d->f) != 0) return true;
1816 setbits24(&d->d[i].D_size, (int32_t)l);
1817 setbits32(d->h.eof, (int32_t)std::ftell(d->f));
1818 return false;
1819 }
1820
IcloseInput()1821 bool IcloseInput()
1822 // Terminate processing one whatever subfile has been being processed.
1823 // returns nonzero if there was trouble.
1824 { Istatus = I_INACTIVE;
1825 #ifndef BUILTIN_IMAGE
1826 if (nativedir)
1827 { if (std::fclose(binary_read_file) != 0) return true;
1828 }
1829 #endif
1830 return false;
1831 }
1832
IcloseOutput()1833 bool IcloseOutput()
1834 // Terminate processing one whatever subfile has been being processed.
1835 // returns nonzero if there was trouble. Write a checksum to the file.
1836 // There is a jolly joke here! I MUST NOT try to pick up the identification
1837 // of the output directory from the lisp-level variable output_directory
1838 // because (preserve) calls this AFTER it has utterly mangled the heap (to
1839 // put all pointers into relative form). To allow for this the variable
1840 // current_output_directory identifies the directory within which a file
1841 // was most recently opened.
1842 { int r;
1843 directory *d = current_output_directory;
1844 Istatus = I_INACTIVE;
1845 current_output_directory = nullptr;
1846 if (d->full_filename != nullptr)
1847 { r = (std::fclose(binary_write_file) != 0);
1848 binary_write_file = nullptr;
1849 return r;
1850 }
1851 setbits24(¤t_output_entry->D_size,
1852 (int32_t)write_bytes_written);
1853 r = std::fflush(d->f);
1854 setbits32(d->h.eof, (int32_t)std::ftell(d->f));
1855 // I bring the directory at the start of the output file up to date at this
1856 // stage - the effect is that if things crash somehow I have a better
1857 // chance of resuming from where disaster hit.
1858 std::fseek(d->f, 0, SEEK_SET);
1859 if (std::fwrite(&d->h, sizeof(directory_header), 1,
1860 d->f) != 1) r = true;
1861 if (std::fwrite(&d->d[0], sizeof(directory_entry),
1862 (size_t)get_dirsize(*d), d->f) !=
1863 (size_t)get_dirsize(*d)) r = true;
1864 if (std::fflush(d->f) != 0) r = true;
1865 d->h.updated &= ~D_UPDATED;
1866 current_output_entry = nullptr;
1867 return r;
1868 }
1869
deldir(directory * d,bool r)1870 static bool deldir(directory *d, bool r)
1871 { delete [] reinterpret_cast<char *>(d);
1872 return r;
1873 }
1874
finished_with(int j)1875 bool finished_with(int j)
1876 { directory *d = fasl_files[j].dir;
1877 fasl_files[j].dir = nullptr;
1878 if (fasl_files[j].name != nullptr) delete [] fasl_files[j].name;
1879 fasl_files[j].name = nullptr;
1880 fasl_files[j].inUse = false;
1881 fasl_files[j].isOutput = false;
1882 if (d == nullptr) return false;
1883 if (d->h.updated & D_COMPACT)
1884 { int i;
1885 long int hwm;
1886 if (d->f == nullptr) return deldir(d, true);
1887 d->h.updated |= D_UPDATED;
1888 sort_directory(d);
1889 hwm = sizeof(directory_header) +
1890 get_dirsize(*d)*(long int)sizeof(directory_entry);
1891 for (i=0; i<get_dirused(*d); i++)
1892 { long int pos = bits32(&d->d[i].D_position);
1893 if (pos != hwm)
1894 { char *b = 16 + (char *)stack;
1895 char small_buffer[64];
1896 long int len = bits24(&d->d[i].D_size);
1897 long int newpos = hwm;
1898 while (len != 0)
1899 { size_t n =
1900 (size_t)((CSL_PAGE_SIZE - 64 -
1901 ((char *)stack -
1902 reinterpret_cast<char *>(stackBase))) &
1903 (~(int32_t)0xff));
1904 // I only perform compression of the file when I am in the process of stopping,
1905 // and in that case the Lisp stack is not in use, so I use if as a buffer.
1906 // WELL the above statement used to be true, but now it is not, since the
1907 // function CLOSE-LIBRARY does exactly what I have declared is never
1908 // possible. But all is not lost - I can afford to use that part of
1909 // the stack that remains unused. In cases where CLOSE-LIBRARY is called
1910 // just before a stack overflow was due the result will be utterly terrible
1911 // (on speed) but it should still be correct. So what you will see is that
1912 // I start my buffer 16 bytes above the active part of the stack, and
1913 // let it run to within 48 bytes of the top of the stack page, but
1914 // rounded down so I do transfers in multiples of 256 bytes. If there
1915 // is really no (Lisp) stack free I use a 64 byte local buffer.
1916 if (n == 0) b = small_buffer, n = sizeof(small_buffer);
1917 if (len < (long int)n) n = (size_t)len;
1918 std::fseek(d->f, pos, SEEK_SET);
1919 size_t rc = std::fread(b, 1, n, d->f);
1920 if (rc != n) fatal_error(err_read_failure);
1921 pos = std::ftell(d->f);
1922 std::fseek(d->f, newpos, SEEK_SET);
1923 std::fwrite(b, 1, n, d->f);
1924 newpos = std::ftell(d->f);
1925 len -= n;
1926 }
1927 setbits32(&d->d[i].D_position, (int32_t)hwm);
1928 }
1929 hwm += bits24(&d->d[i].D_size) + 4L;
1930 }
1931 std::fflush(d->f);
1932 if (hwm != bits32(d->h.eof))
1933 { truncate_file(d->f, hwm);
1934 setbits32(d->h.eof, (int32_t)hwm);
1935 }
1936 }
1937 if (d->h.updated & D_UPDATED)
1938 { if (d->f == nullptr || std::fflush(d->f) != 0) return deldir(d, true);
1939 std::fseek(d->f, 0, SEEK_SET);
1940 if (std::fwrite(&d->h, sizeof(directory_header), 1,
1941 d->f) != 1) return deldir(d, true);
1942 if (std::fwrite(&d->d[0], sizeof(directory_entry),
1943 (size_t)get_dirsize(*d), d->f) !=
1944 (size_t)get_dirsize(*d)) return deldir(d, true);
1945 if (std::fflush(d->f) != 0) return deldir(d, true);
1946 }
1947 if (d->h.updated & D_PENDING) return deldir(d, false);
1948 else if (d->f != nullptr && std::fclose(d->f) != 0) return deldir(d, true);
1949 else return deldir(d, false);
1950 }
1951
Ifinished()1952 bool Ifinished()
1953 // Indicates total completion of all work on image files, and so calls
1954 // for things to be (finally) tidied up. Again returns true of anything
1955 // has gone wrong.
1956 {
1957 // Need to close all files here... loads of calls to fflush and fclose.
1958 // Actually only output files are a real issue here. And then only
1959 // the ones that are flagged as needing compaction.
1960 size_t j;
1961 bool failed = false;
1962 for (j=0; j<fasl_files.size(); j++)
1963 { if (!fasl_files[j].inUse) continue;
1964 else if (finished_with(j)) failed = true;
1965 }
1966 return failed;
1967 }
1968
Igetc()1969 int Igetc()
1970 // Returns next byte from current image sub-file, or EOF if either
1971 // real end-of-file or on failure. As a special fudge here (ugh) I
1972 // use a negative value of read_bytes_remaining to indicate that
1973 // input should NOT be from the usual image-file mechanism, but from
1974 // the currently selected standard input. Setting things up that way
1975 // then supports processing of FASL files from almost arbitrary
1976 // sources.
1977 { long int n_left = read_bytes_remaining;
1978 int c;
1979 if (n_left <= 0)
1980 { if (n_left == 0) return EOF;
1981 else
1982 { LispObject stream = qvalue(standard_input);
1983 if (!is_stream(stream)) return EOF;
1984 if_error(c = getc_stream(stream),
1985 return EOF);
1986 }
1987 }
1988 else
1989 { read_bytes_remaining = n_left - 1;
1990 #ifdef BUILTIN_IMAGE
1991 if (binary_read_filep - reduce_image == REDUCE_IMAGE_SIZE) c = -1;
1992 else c = *binary_read_filep++;
1993 #else
1994 c = std::getc(binary_read_file);
1995 #endif
1996 }
1997 if (c == EOF) return c;
1998 return (c & 0xff);
1999 }
2000
Iread(void * buff,size_t size)2001 bool Iread(void *buff, size_t size)
2002 // Reads (size) bytes into the indicated buffer. Returns true if
2003 // if fails to read the expected number of bytes.
2004 { unsigned char *p = reinterpret_cast<unsigned char *>(buff);
2005 while (size > 0)
2006 { int c = Igetc();
2007 if (c == EOF) return true;
2008 *p++ = c;
2009 size--;
2010 }
2011 return false;
2012 }
2013
Ioutsize()2014 long int Ioutsize()
2015 { return write_bytes_written;
2016 }
2017
Iputc(int ch)2018 bool Iputc(int ch)
2019 // Puts one character into image system, returning true if there
2020 // was trouble.
2021 // If start-module is given a Lisp stream as an argument then it will
2022 // save that in fasl_stream and the code must write bytes to there. Otherwise
2023 // (ie in the normal situation!) I will have used Iopen to set up the
2024 // stream, and it will have set binary_write_file to the stream and positioned
2025 // it at the point I should start writing.
2026 { write_bytes_written++;
2027 if (fasl_stream != nil && fasl_stream != SPID_NIL)
2028 putc_stream(ch, fasl_stream);
2029 else if (std::putc(ch, binary_write_file) == EOF) return true;
2030 return false;
2031 }
2032
Iwrite(const void * buff,size_t size)2033 bool Iwrite(const void *buff, size_t size)
2034 // Writes (size) bytes from the given buffer, returning true if trouble.
2035 { const unsigned char *p = reinterpret_cast<const unsigned char *>
2036 (buff);
2037 for (size_t i=0; i<size; i++)
2038 if (Iputc(p[i])) return true;
2039 return false;
2040 }
2041
preserve(const char * banner,size_t len)2042 void preserve(const char *banner, size_t len)
2043 { int32_t i;
2044 if (Iopen(nullptr, 0, IOPEN_OUT, nullptr))
2045 { err_printf("+++ PRESERVE failed to open image file\n");
2046 return;
2047 }
2048 // I set a whole bunch of things to NIL here. If spurious data is left over
2049 // in global list-bases from a previous calculation it could clog up the
2050 // heap and waste a lot of space...
2051 for (i=0; i<=50; i++) workbase[i] = nil;
2052 exit_tag = exit_value = catch_tags =
2053 codevec = litvec = B_reg = faslvec = faslgensyms = nil;
2054 Lmapstore(nil, fixnum_of_int(4)); // Reset all counts to zero.
2055 { char msg[128];
2056 std::time_t t0 = std::time(0);
2057 for (i=0; i<128; i++) msg[i] = ' ';
2058 if (len > 60) len = 60; // truncate if necessary
2059 if (len == 0 || banner[0] == 0) msg[0] = 0;
2060 else std::sprintf(msg, "%.*s", static_cast<int>(len), banner);
2061 // 26 bytes starting from byte 64 shows the time of the dump
2062 std::sprintf(msg+64, "%.25s\n", std::ctime(&t0));
2063 // 16 bytes starting at byte 90 are for a checksum of the u01.c etc checks
2064 get_user_files_checksum(reinterpret_cast<unsigned char *>(&msg[90]));
2065 // 106 to 109 free at present but available if checksum goes to 160 bits
2066 msg[110] = 0;
2067 msg[111] = 0;
2068 // Write initial record uncompresssed...
2069 Iwrite(msg, 112); // Exactly 112 bytes in the header records
2070 }
2071 def_init(); // I should check the return code...
2072 write_everything(); // needs a return code to report any failure?
2073
2074 #ifndef COMMON
2075 Zwrite("\n\nEnd of CSL dump file\n\n", 24); // return code
2076 #else
2077 Zwrite("\n\nEnd of CCL dump file\n\n", 24); // return code
2078 #endif
2079 def_finish(); // I should check the return code...
2080 // Here I pad the image file to be a multiple of 4 bytes long. Since it is a
2081 // binary file the '\n' characters I put in will always be just 1 byte each
2082 // (for text files that might have expanded). See comments in fasl.c for
2083 // a diatribe about why I do this, or at least why rather a long while ago
2084 // this was necessary on at least one sort of computer.
2085 // Note that this is writing directly to the file... not via the compression
2086 // layer.
2087 { int k = static_cast<int>((-write_bytes_written) & 3);
2088 while (k != 0) k--, Iputc(NEWLINE_CHAR);
2089 }
2090 // I need to check for write errors here and moan if there were any...
2091 if (IcloseOutput()) error(0, err_write_err);
2092 return;
2093 }
2094
2095 #endif // ZLIB_DEMO
2096
2097 // end of file preserve.cpp
2098