summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Sharp <ken.sharp@artifex.com>2013-06-14 10:33:19 +0100
committerKen Sharp <ken.sharp@artifex.com>2013-06-14 11:09:52 +0100
commit1ebbb14391559b4da0c623e6c02840f7e41aa10b (patch)
tree25871d3d727017edcaf3dd249338b1f1adbb67cf
parentcb2e0f784882696e930e5077ecb1a2feef76fadf (diff)
downloadghostpdl-1ebbb14391559b4da0c623e6c02840f7e41aa10b.tar.gz
temporary commit of ramfs changes for chris to look at
-rw-r--r--gs/Makefile.in4
-rw-r--r--gs/base/gsioram.c527
-rw-r--r--gs/base/lib.mak15
-rw-r--r--gs/base/ramfs.c453
-rw-r--r--gs/base/ramfs.h62
-rw-r--r--gs/psi/msvc.mak2
6 files changed, 1060 insertions, 3 deletions
diff --git a/gs/Makefile.in b/gs/Makefile.in
index 6808a0de2..3111b3f0d 100644
--- a/gs/Makefile.in
+++ b/gs/Makefile.in
@@ -451,11 +451,11 @@ DYNANIC_LIB_EXT = @DYNANIC_LIB_EXT@
# Choose the language feature(s) to include. See gs.mak for details.
-FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)epsf.dev $(GLD)pipe.dev $(GLD)gsnogc.dev $(GLD)htxlib.dev @JBIG2DEVS@ @JPXDEVS@ @UTF8DEVS@
+FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)epsf.dev $(GLD)pipe.dev $(GLD)gsnogc.dev $(GLD)htxlib.dev @JBIG2DEVS@ @JPXDEVS@ @UTF8DEVS@ $(GLD)ramfs.dev
#FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev
#FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)rasterop.dev $(GLD)pipe.dev
# The following is strictly for testing.
-FEATURE_DEVS_ALL=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)rasterop.dev $(PSD)double.dev $(PSD)trapping.dev $(PSD)stocht.dev $(GLD)pipe.dev $(GLD)gsnogc.dev $(GLD)htxlib.dev @JBIG2DEVS@ @JPXDEVS@ @UTF8DEVS@
+FEATURE_DEVS_ALL=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)rasterop.dev $(PSD)double.dev $(PSD)trapping.dev $(PSD)stocht.dev $(GLD)pipe.dev $(GLD)gsnogc.dev $(GLD)htxlib.dev @JBIG2DEVS@ @JPXDEVS@ @UTF8DEVS@ $(GLD)ramfs.dev
#FEATURE_DEVS=$(FEATURE_DEVS_ALL)
# The list of resources to be included in the %rom% file system.
diff --git a/gs/base/gsioram.c b/gs/base/gsioram.c
new file mode 100644
index 000000000..bd2cee302
--- /dev/null
+++ b/gs/base/gsioram.c
@@ -0,0 +1,527 @@
+/* $Id$ */
+/* %ram% file device implementation */
+
+/*
+ * This file implements a simple ram-based file system.
+ *
+ * The fs has no subdirs, only a root directory. Files are stored as a
+ * resizable array of pointers to blocks. Timestamps are not implemented
+ * for performance reasons.
+ *
+ * The implementation is in two parts - a ramfs interface that works
+ * mostly like the unix file system calls, and a layer to hook this up
+ * to ghostscript.
+ *
+ * Macros define an upper limit on the number of blocks a ram device
+ * can take up, and (in ramfs.c) the size of each block.
+ *
+ * Routines for the gs stream interface were graciously stolen from
+ * sfxstdio.c et al.
+ */
+
+#include "string_.h"
+#include "unistd_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gp.h"
+#include "gscdefs.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+#include "gxiodev.h"
+#include "gsutil.h"
+#include "stream.h"
+#include "ramfs.h"
+#include <malloc.h>
+
+/* Function prototypes */
+static iodev_proc_init(iodev_ram_init);
+static iodev_proc_open_file(ram_open_file);
+static iodev_proc_delete_file(ram_delete);
+static iodev_proc_rename_file(ram_rename);
+static iodev_proc_file_status(ram_status);
+static iodev_proc_enumerate_files(ram_enumerate_init);
+static iodev_proc_enumerate_next(ram_enumerate_next);
+static iodev_proc_enumerate_close(ram_enumerate_close);
+static iodev_proc_get_params(ram_get_params);
+static iodev_proc_put_params(ram_put_params);
+static void ram_finalize(const gs_memory_t *memory, void * vptr);
+
+const gx_io_device gs_iodev_ram = {
+ "%ram%", "FileSystem", {
+ iodev_ram_init, iodev_no_open_device,
+ ram_open_file, iodev_no_fopen, iodev_no_fclose,
+ ram_delete, ram_rename, ram_status,
+ ram_enumerate_init, ram_enumerate_next, ram_enumerate_close,
+ ram_get_params, iodev_no_put_params
+ }
+};
+
+typedef struct ramfs_state_s {
+ char *t;
+ ramfs* fs;
+} ramfs_state;
+
+#define GETRAMFS(state) (((ramfs_state*)(state))->fs)
+
+gs_private_st_ptrs2_final(st_ramfs_state, struct ramfs_state_s,
+ "ramfs_state", ramfs_state_enum_ptrs, ramfs_state_reloc_ptrs, ram_finalize, t, fs);
+
+typedef struct gsram_enum_s {
+ char *pattern;
+ ramfs_enum* e;
+ gs_memory_t *memory;
+} gsram_enum;
+
+gs_private_st_ptrs3(st_gsram_enum, struct gsram_enum_s, "gsram_enum",
+ gsram_enum_enum_ptrs, gsram_enum_reloc_ptrs, pattern, e, memory);
+
+/* could make this runtime configurable later. It doesn't allocate
+ all the blocks in one go so it's not critical */
+#define MAXBLOCKS 2000000
+
+#define DEFAULT_BUFFER_SIZE 2048
+
+/* stream stuff */
+
+static int
+s_ram_available(stream *, gs_offset_t *),
+ s_ram_read_seek(stream *, gs_offset_t),
+ s_ram_read_close(stream *),
+ s_ram_read_process(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool);
+static int
+s_ram_write_seek(stream *, gs_offset_t),
+ s_ram_write_flush(stream *),
+ s_ram_write_close(stream *),
+ s_ram_write_process(stream_state *, stream_cursor_read *,
+ stream_cursor_write *, bool);
+static int
+s_ram_switch(stream *, bool);
+
+static int
+ramfs_errno_to_code(int errno) {
+ switch (errno) {
+ case RAMFS_NOTFOUND:
+ return_error(gs_error_undefinedfilename);
+ case RAMFS_NOACCESS:
+ return_error(gs_error_invalidfileaccess);
+ case RAMFS_NOMEM:
+ return_error(gs_error_VMerror);
+ /* just in case */
+ default:
+ return_error(gs_error_ioerror);
+ }
+}
+
+static void
+sread_ram(register stream * s, ramhandle * file, byte * buf, uint len),
+ swrite_ram(register stream * s, ramhandle * file, byte * buf, uint len),
+ sappend_ram(register stream * s, ramhandle * file, byte * buf, uint len);
+
+static int
+ram_open_file(gx_io_device * iodev, const char *fname, uint len,
+ const char *file_access, stream ** ps, gs_memory_t * mem)
+{
+ int code = 0;
+ ramhandle * file;
+ char fmode[4]; /* r/w/a, [+], [b], null */
+ int openmode=RAMFS_READ;
+ ramfs * fs = GETRAMFS(iodev->state);
+ char * namestr = NULL;
+
+ /* Is there a more efficient way to do this? */
+ namestr = malloc(len+1);
+ if(!namestr) return_error(gs_error_VMerror);
+ strncpy(namestr,fname,len);
+ namestr[len] = 0;
+
+ if (!iodev) /*iodev = iodev_default;*/
+ return gs_note_error(gs_error_invalidaccess);
+ code = file_prepare_stream(fname, len, file_access, DEFAULT_BUFFER_SIZE,
+ ps, fmode, mem
+ );
+ if (code < 0) goto error;
+ if (fname == 0) return 0;
+
+ if (fmode[0] != 'r' || fmode[1] == '+') openmode |= RAMFS_WRITE;
+ if (fmode[0] != 'r') openmode |= RAMFS_CREATE;
+
+ /* For now, we cheat here in the same way that sfxstdio.c et al cheat -
+ append mode is faked by opening in write mode and seeking to EOF just
+ once. This is different from unix semantics, which seeks atomically
+ before each write, and is actually useful as a distinct mode. */
+ /* if (fmode[0] == 'a') openmode |= RAMFS_APPEND; */
+
+ file = ramfs_open(mem, fs,namestr,openmode);
+ if(!file) { code = ramfs_errno_to_code(ramfs_error(fs)); goto error; }
+
+ switch (fmode[0]) {
+ case 'a':
+ sappend_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
+ break;
+ case 'r':
+ sread_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
+ break;
+ case 'w':
+ swrite_ram(*ps, file, (*ps)->cbuf, (*ps)->bsize);
+ }
+ (*ps)->save_close = (*ps)->procs.close;
+ (*ps)->procs.close = file_close_file;
+ error:
+ free(namestr);
+ /* XXX free stream stuff? */
+ return code;
+}
+
+/* Initialize a stream for reading an OS file. */
+static void
+sread_ram(register stream * s, ramhandle * file, byte * buf, uint len)
+{
+ static const stream_procs p = {
+ s_ram_available, s_ram_read_seek, s_std_read_reset,
+ s_std_read_flush, s_ram_read_close, s_ram_read_process,
+ s_ram_switch
+ };
+
+ s_std_init(s, buf, len, &p,s_mode_read + s_mode_seek);
+ s->file = (FILE*)file;
+ s->file_modes = s->modes;
+ s->file_offset = 0;
+ /* XXX get a more sensible number from the fs? */
+ s->file_limit = max_long;
+}
+
+/* Procedures for reading from a file */
+static int
+s_ram_available(register stream * s, gs_offset_t *pl)
+{
+ long max_avail = s->file_limit - stell(s);
+ long buf_avail = sbufavailable(s);
+
+ *pl = min(max_avail, buf_avail);
+ if(*pl == 0 && ramfile_eof((ramhandle*)s->file))
+ *pl = -1; /* EOF */
+ return 0;
+}
+
+static int
+s_ram_read_seek(register stream * s, gs_offset_t pos)
+{
+ uint end = s->srlimit - s->cbuf + 1;
+ long offset = pos - s->position;
+
+ if (offset >= 0 && offset <= end) { /* Staying within the same buffer */
+ s->srptr = s->cbuf + offset - 1;
+ return 0;
+ }
+ if (pos < 0 || pos > s->file_limit ||
+ ramfile_seek((ramhandle*)s->file, s->file_offset + pos, RAMFS_SEEK_SET) != 0
+ )
+ return ERRC;
+ s->srptr = s->srlimit = s->cbuf - 1;
+ s->end_status = 0;
+ s->position = pos;
+ return 0;
+}
+static int
+s_ram_read_close(stream * s)
+{
+ ramhandle *file = (ramhandle*)s->file;
+
+ if (file != 0) {
+ s->file = 0;
+ ramfile_close(file);
+ }
+ return 0;
+}
+
+/*
+ * Process a buffer for a file reading stream.
+ * This is the first stream in the pipeline, so pr is irrelevant.
+ */
+static int
+s_ram_read_process(stream_state * st, stream_cursor_read * ignore_pr,
+ stream_cursor_write * pw, bool last)
+{
+ stream *s = (stream *)st; /* no separate state */
+ ramhandle *file = (ramhandle*)s->file;
+ uint max_count = pw->limit - pw->ptr;
+ int status = 1;
+ int count;
+
+ if (s->file_limit < max_long) {
+ long limit_count = s->file_offset + s->file_limit -
+ ramfile_tell(file);
+
+ if (max_count > limit_count)
+ max_count = limit_count, status = EOFC;
+ }
+ count = ramfile_read(file,pw->ptr + 1, max_count);
+ if (count < 0) return ERRC;
+ pw->ptr += count;
+ /* process_interrupts(s->memory); */
+ return ramfile_eof(file) ? EOFC : status;
+}
+
+/* ------ File writing ------ */
+
+/* Initialize a stream for writing a file. */
+static void
+swrite_ram(register stream * s, ramhandle * file, byte * buf, uint len)
+{
+ static const stream_procs p = {
+ s_std_noavailable, s_ram_write_seek, s_std_write_reset,
+ s_ram_write_flush, s_ram_write_close, s_ram_write_process,
+ s_ram_switch
+ };
+
+ s_std_init(s, buf, len, &p, s_mode_write + s_mode_seek);
+ s->file = (FILE*)file;
+ s->file_modes = s->modes;
+ s->file_offset = 0; /* in case we switch to reading later */
+ s->file_limit = max_long; /* ibid. */
+}
+
+/* Initialize for appending to a file. */
+static void
+sappend_ram(register stream * s, ramhandle * file, byte * buf, uint len)
+{
+ swrite_ram(s, file, buf, len);
+ s->modes = s_mode_write + s_mode_append; /* no seek */
+ s->file_modes = s->modes;
+ ramfile_seek(file,0,RAMFS_SEEK_END);
+ s->position = ramfile_tell(file);
+}
+
+/* Procedures for writing on a file */
+static int
+s_ram_write_seek(stream * s, gs_offset_t pos)
+{
+ /* We must flush the buffer to reposition. */
+ int code = sflush(s);
+
+ if (code < 0) return code;
+ if (ramfile_seek((ramhandle*)s->file, pos, RAMFS_SEEK_SET) != 0)
+ return ERRC;
+ s->position = pos;
+ return 0;
+}
+
+static int
+s_ram_write_flush(register stream * s)
+{
+ int result = s_process_write_buf(s, false);
+ return result;
+}
+
+static int
+s_ram_write_close(register stream * s)
+{
+ s_process_write_buf(s, true);
+ return s_ram_read_close(s);
+}
+
+/*
+ * Process a buffer for a file writing stream.
+ * This is the last stream in the pipeline, so pw is irrelevant.
+ */
+static int
+s_ram_write_process(stream_state * st, stream_cursor_read * pr,
+ stream_cursor_write * ignore_pw, bool last)
+{
+ uint count = pr->limit - pr->ptr;
+
+ ramhandle *file = (ramhandle*)((stream *) st)->file;
+ int written = ramfile_write(file,pr->ptr + 1, count);
+
+ if (written < 0) return ERRC;
+ pr->ptr += written;
+ return 0;
+}
+
+static int
+s_ram_switch(stream * s, bool writing)
+{
+ uint modes = s->file_modes;
+ ramhandle *file = (ramhandle*)s->file;
+ long pos;
+
+ if (writing) {
+ if (!(s->file_modes & s_mode_write)) return ERRC;
+ pos = stell(s);
+ ramfile_seek(file, pos, RAMFS_SEEK_SET);
+ if (modes & s_mode_append) {
+ sappend_ram(s, file, s->cbuf, s->cbsize); /* sets position */
+ } else {
+ swrite_ram(s, file, s->cbuf, s->cbsize);
+ s->position = pos;
+ }
+ s->modes = modes;
+ } else {
+ if (!(s->file_modes & s_mode_read)) return ERRC;
+ pos = stell(s);
+ if (sflush(s) < 0) return ERRC;
+ sread_ram(s, file, s->cbuf, s->cbsize);
+ s->modes |= modes & s_mode_append; /* don't lose append info */
+ s->position = pos;
+ }
+ s->file_modes = modes;
+ return 0;
+}
+
+
+/* gx_io_device stuff */
+
+static int
+iodev_ram_init(gx_io_device * iodev, gs_memory_t * mem)
+{
+ ramfs* fs = ramfs_new(mem, MAXBLOCKS);
+ ramfs_state* state = gs_alloc_struct(mem, ramfs_state, &st_ramfs_state,
+ "ramfs_init(state)"
+ );
+ if (fs && state) {
+ state->fs = fs;
+ iodev->state = state;
+ return 0;
+ }
+ if(fs) ramfs_destroy(mem, fs);
+ if(state) gs_free_object(mem,state,"iodev_ram_init(state)");
+ return gs_error_VMerror;
+}
+
+static void
+ram_finalize(const gs_memory_t *memory, void * vptr)
+{
+ ramfs* fs = GETRAMFS((ramfs_state*)vptr);
+ ramfs_destroy((gs_memory_t *)memory, fs);
+}
+
+static int
+ram_delete(gx_io_device * iodev, const char *fname)
+{
+ ramfs* fs = GETRAMFS(iodev->state);
+
+ if(ramfs_unlink(fs,fname)!=0) {
+ return_error(ramfs_errno_to_code(ramfs_error(fs)));
+ }
+ return 0;
+}
+
+static int
+ram_rename(gx_io_device * iodev, const char *from, const char *to)
+{
+ ramfs* fs = GETRAMFS(iodev->state);
+
+ if(ramfs_rename(fs,from,to)!=0) {
+ return_error(ramfs_errno_to_code(ramfs_error(fs)));
+ }
+ return 0;
+}
+
+static int
+ram_status(gx_io_device * iodev, const char *fname, struct stat *pstat)
+{
+ ramhandle * f;
+ ramfs* fs = GETRAMFS(iodev->state);
+ int blocksize = ramfs_blocksize(fs);
+
+ f = ramfs_open(fs,fname,RAMFS_READ);
+ if(!f) return_error(ramfs_errno_to_code(ramfs_error(fs)));
+
+ memset(pstat, 0, sizeof(*pstat));
+ pstat->st_size = ramfile_size(f);
+ /* The Windows definition of struct stat doesn't include a st_blocks member
+ pstat->st_blocks = (pstat->st_size+blocksize-1)/blocksize;*/
+ /* XXX set mtime & ctime */
+ ramfile_close(f);
+ return 0;
+}
+
+static file_enum *
+ram_enumerate_init(gx_io_device *iodev, const char *pat, uint patlen,
+ gs_memory_t *mem)
+{
+ gsram_enum * penum = gs_alloc_struct(
+ mem, gsram_enum, &st_gsram_enum,
+ "ram_enumerate_files_init(file_enum)"
+ );
+ char *pattern = (char *)gs_alloc_bytes(
+ mem, patlen+1, "ram_enumerate_file_init(pattern)"
+ );
+ ramfs_enum * e = ramfs_enum_new(GETRAMFS(iodev->state));
+ if(penum && pattern && e) {
+ memcpy(pattern, pat, patlen);
+ pattern[patlen]=0;
+
+ penum->memory = mem;
+ penum->pattern = pattern;
+ penum->e = e;
+ return (file_enum *)penum;
+ }
+ if (penum) gs_free_object(mem,penum,"ramfs_enum_init(ramfs_enum)");
+ if (pattern)
+ gs_free_object(mem, penum->pattern, "ramfs_enum_init(pattern)");
+ if(e) ramfs_enum_end(e);
+ return NULL;
+}
+
+static void
+ram_enumerate_close(file_enum *pfen)
+{
+ gsram_enum *penum = (gsram_enum *)pfen;
+ gs_memory_t *mem = penum->memory;
+
+ ramfs_enum_end(penum->e);
+ gs_free_object(mem, penum->pattern, "ramfs_enum_init(pattern)");
+ gs_free_object(mem, penum, "ramfs_enum_init(ramfs_enum)");
+}
+
+static uint
+ram_enumerate_next(file_enum *pfen, char *ptr, uint maxlen)
+{
+ gsram_enum *penum = (gsram_enum *)pfen;
+
+ char * filename;
+ while (filename = ramfs_enum_next(penum->e)) {
+ if (string_match((byte *)filename, strlen(filename),
+ (byte *)penum->pattern,
+ strlen(penum->pattern), 0)) {
+ if (strlen(filename) < maxlen)
+ memcpy(ptr, filename, strlen(filename));
+ return strlen(filename); /* if > maxlen, caller will detect rangecheck */
+ }
+ }
+ /* ran off end of list, close the enum */
+ ram_enumerate_close(pfen);
+ return ~(uint)0;
+}
+
+static int
+ram_get_params(gx_io_device * iodev, gs_param_list * plist)
+{
+ int code;
+ int i0 = 0, so = 1;
+ bool btrue = true, bfalse = false;
+ ramfs* fs = GETRAMFS(iodev->state);
+ int BlockSize;
+ long Free, LogicalSize;
+
+ BlockSize = ramfs_blocksize(fs);
+ LogicalSize = MAXBLOCKS;
+ Free = ramfs_blocksfree(fs);
+
+ if (
+ (code = param_write_bool(plist, "HasNames", &btrue)) < 0 ||
+ (code = param_write_int (plist, "BlockSize", &BlockSize)) < 0 ||
+ (code = param_write_long(plist, "Free", &Free)) < 0 ||
+ (code = param_write_int (plist, "InitializeAction",&i0)) < 0 ||
+ (code = param_write_bool(plist, "Mounted", &btrue)) < 0 ||
+ (code = param_write_bool(plist, "Removable", &bfalse)) < 0 ||
+ (code = param_write_bool(plist, "Searchable", &btrue)) < 0 ||
+ (code = param_write_int (plist, "SearchOrder", &so)) < 0 ||
+ (code = param_write_bool(plist, "Writeable", &btrue)) < 0 ||
+ (code = param_write_long(plist, "LogicalSize", &LogicalSize)) < 0
+ )
+ return code;
+ return 0;
+}
diff --git a/gs/base/lib.mak b/gs/base/lib.mak
index 653243d7e..703493cc8 100644
--- a/gs/base/lib.mak
+++ b/gs/base/lib.mak
@@ -3164,6 +3164,21 @@ MKROMFS_ZLIB_OBJS=$(AUX)compress.$(OBJ) $(AUX)deflate.$(OBJ) \
MKROMFS_COMMON_DEPS=$(stdpre_h) $(stdint__h) $(gsiorom_h) $(arch_h)\
$(gsmemret_h) $(gsmalloc_h) $(gsstype_h) $(gp_h) $(time__h)
+# ---------------- Support for %ram% IODevice ----------------- #
+gsioram_h=$(GLSRC)gsioram.h $(GLSRC)ramfs.h
+ramfs_=$(GLOBJ)gsioram.$(OBJ) $(GLOBJ)ramfs.$(OBJ)
+$(GLD)ramfs.dev : $(LIB_MAK) $(ECHOGS_XE) $(ramfs_)
+ $(SETMOD) $(GLD)ramfs $(ramfs_)
+ $(ADDMOD) $(GLD)ramfs -iodev ram
+ $(ADDMOD) $(GLD)ramfs -obj $(GLOBJ)ramfs.$(OBJ)
+
+$(GLOBJ)ramfs.$(OBJ) : $(GLSRC)ramfs.c
+ $(GLCC) $(GLO_)ramfs.$(OBJ) $(C_) $(GLSRC)ramfs.c
+
+$(GLOBJ)gsioram.$(OBJ) : $(GLSRC)gsioram.c
+ $(GLCC) $(GLO_)gsioram.$(OBJ) $(C_) $(GLSRC)gsioram.c
+
+
# ---------------- Support for %disk IODevices ---------------- #
# The following module is included only if the diskn.dev FEATURE is included
$(GLOBJ)gsiodisk.$(OBJ) : $(GLSRC)gsiodisk.c $(AK) $(gx_h)\
diff --git a/gs/base/ramfs.c b/gs/base/ramfs.c
new file mode 100644
index 000000000..644eaf192
--- /dev/null
+++ b/gs/base/ramfs.c
@@ -0,0 +1,453 @@
+/*
+ memory-based simulated file system
+
+ files only, no directories (well, one)
+ (C) 2006 Michael Slade <micksa@knobbits.org>
+ */
+
+#include "unistd_.h"
+#include "string_.h"
+#include "malloc_.h"
+#include "gx.h"
+#include "gserrors.h"
+#include "gp.h"
+#include "gscdefs.h"
+#include "gsparam.h"
+#include "gsstruct.h"
+#include "ramfs.h"
+
+#define MACROBLOCK_REALLOC_MAX 128
+
+typedef struct _ramfs {
+ struct _ramdirent * files;
+ struct _ramfs_enum* active_enums;
+ int blocksfree;
+ int last_error;
+};
+
+gs_private_st_ptrs2(st_ramfs, struct _ramfs, "gsram_ramfs",
+ _ramfs_enum_ptrs, _ramfs_reloc_ptrs, files, active_enums);
+
+struct _ramdirent {
+ char* filename;
+ struct _ramfile* inode;
+ struct _ramdirent* next;
+};
+
+gs_private_st_ptrs3(st_ramdirent, struct _ramdirent, "gsram_ramdirent",
+ _ramdirent_enum_ptrs, _ramdirent_reloc_ptrs, filename, inode, next);
+
+typedef struct _ramfile {
+ ramfs* fs;
+ int refcount;
+ int size;
+ int blocks;
+ int blocklist_size;
+ char** data;
+} ramfile;
+
+gs_private_st_ptrs2(st_ramfile, struct _ramfile, "gsram_ramfile",
+ _ramfile_enum_ptrs, _ramfile_reloc_ptrs, fs, data);
+
+struct _ramhandle {
+ ramfile * file;
+ int last_error;
+ int filepos;
+ int mode;
+};
+
+gs_private_st_ptrs1(st_ramhandle, struct _ramhandle, "gsram_ramhandle",
+ _ramhandle_enum_ptrs, _ramhandle_reloc_ptrs, file);
+
+struct _ramfs_enum {
+ ramfs* fs;
+ ramdirent * current;
+ struct _ramfs_enum* next;
+};
+
+gs_private_st_ptrs3(st_ramfs_enum, struct _ramfs_enum, "gsram_ramfs_enum",
+ _ramfs_enum_enum_ptrs, _ramfs_enum_reloc_ptrs, fs, current, next);
+
+static void unlink_node(ramfile * inode);
+static int ramfile_truncate(ramhandle * handle,int size);
+
+
+ramfs * ramfs_new(gs_memory_t *mem, int size) {
+ ramfs * fs = gs_alloc_struct(mem, ramfs, &st_ramfs,
+ "ramfs_new"
+ );
+
+ if (fs == NULL) {
+ fs->last_error = RAMFS_NOMEM;
+ return NULL;
+ }
+ size = size/(RAMFS_BLOCKSIZE/1024);
+ fs->files = NULL;
+ fs->active_enums = NULL;
+ fs->blocksfree = size;
+ fs->last_error = 0;
+ return fs;
+}
+
+/* This function makes no attempt to check that there are no open files or
+ enums. If there are any when this function is called, memory leakage will
+ result and any attempt to access the open files or enums will probably
+ cause a segfault. Caveat emptor, or something.
+*/
+void ramfs_destroy(gs_memory_t *mem, ramfs * fs) {
+ ramdirent * ent;
+
+ if(fs == NULL) return;
+
+ ent = fs->files;
+ while(ent) {
+ ramdirent* prev;
+ free(ent->filename);
+ unlink_node(ent->inode);
+ prev = ent;
+ ent = ent->next;
+ free(prev);
+ }
+ gs_free_object(mem, fs, "ramfs_destroy");
+}
+
+int ramfs_error(const ramfs* fs) { return fs->last_error; }
+
+static int resize(ramfile * file,int size) {
+ int newblocks = (size+RAMFS_BLOCKSIZE-1)/RAMFS_BLOCKSIZE;
+ if(newblocks > file->blocks) {
+ /* allocate blocks for file as necessary */
+
+ if(newblocks-file->blocks > file->fs->blocksfree) {
+ return -RAMFS_NOSPACE;
+ }
+ if(file->blocklist_size < newblocks) {
+ int newsize = file->blocklist_size;
+ if (newsize > MACROBLOCK_REALLOC_MAX) {
+ newsize = ((newblocks+MACROBLOCK_REALLOC_MAX-1)/
+ MACROBLOCK_REALLOC_MAX) * MACROBLOCK_REALLOC_MAX;
+ } else {
+ if(!newsize) newsize = 1;
+ while(newsize < newblocks) newsize *= 2;
+ }
+ file->data = realloc(file->data,newsize * sizeof(char*));
+ file->blocklist_size = newsize;
+ }
+ while(file->blocks<newblocks) {
+ char * block = file->data[file->blocks] = malloc(RAMFS_BLOCKSIZE);
+ if(!block) {
+ return -RAMFS_NOMEM;
+ }
+ file->blocks++;
+ file->fs->blocksfree--;
+ }
+ } else if (newblocks < file->blocks) {
+ /* don't bother shrinking the block array */
+ file->fs->blocksfree += (file->blocks-newblocks);
+ while(file->blocks > newblocks) {
+ free(file->data[--file->blocks]);
+ }
+ }
+ file->size = size;
+ return 0;
+}
+
+static ramdirent * ramfs_findfile(const ramfs* fs,const char *filename) {
+ ramdirent * this = fs->files;
+ while(this) {
+ if(strcmp(this->filename,filename) == 0) break;
+ this = this->next;
+ }
+ return this;
+}
+
+ramhandle * ramfs_open(gs_memory_t *mem, ramfs* fs,const char * filename,int mode) {
+ ramdirent * this;
+ ramfile* file;
+ ramhandle* handle;
+
+ if(mode & (RAMFS_CREATE|RAMFS_APPEND)) mode |= RAMFS_WRITE;
+
+ this = ramfs_findfile(fs,filename);
+
+ if(!this) {
+ /* create file? */
+ char * dirent_filename;
+
+ if(!(mode & RAMFS_CREATE)) {
+ fs->last_error = RAMFS_NOTFOUND;
+ return NULL;
+ }
+ this = malloc(sizeof(ramdirent));
+ file = malloc(sizeof(ramfile));
+ dirent_filename = malloc(strlen(filename)+1);
+ if(!(this && file && dirent_filename)) {
+ free(this);
+ free(file);
+ free(dirent_filename);
+ fs->last_error = RAMFS_NOMEM;
+ return NULL;
+ }
+ strcpy(dirent_filename,filename);
+ this->filename = dirent_filename;
+ file->refcount = 1;
+ file->size = 0;
+ file->blocks = 0;
+ file->blocklist_size = 0;
+ file->data = NULL;
+ file->fs = fs;
+ this->inode = file;
+ this->next = fs->files;
+ fs->files = this;
+ }
+ file = this->inode;
+ file->refcount++;
+
+ handle = malloc(sizeof(ramhandle));
+ if(!handle) {
+ fs->last_error = RAMFS_NOMEM;
+ return NULL;
+ }
+ handle->file = file;
+ handle->filepos = 0;
+ handle->mode = mode;
+
+ if(mode & RAMFS_TRUNC) {
+ resize(file,0);
+ }
+ return handle;
+}
+
+int ramfs_blocksize(ramfs * fs) { return RAMFS_BLOCKSIZE; }
+int ramfs_blocksfree(ramfs * fs) { return fs->blocksfree; }
+int ramfile_error(ramhandle * handle) { return handle->last_error; }
+
+static void unlink_node(ramfile * inode) {
+ int c;
+
+ --inode->refcount;
+ if(inode->refcount) return;
+
+ /* remove the file and its data */
+ for(c=0;c<inode->blocks;c++) {
+ free(inode->data[c]);
+ }
+ inode->fs->blocksfree += c;
+ free(inode->data);
+ free(inode);
+}
+
+int ramfs_unlink(ramfs * fs,const char *filename) {
+ ramdirent ** last;
+ ramdirent * this;
+ ramfs_enum* e;
+
+ last = &fs->files;
+ while(1) {
+ if(!(this = *last)) {
+ fs->last_error = RAMFS_NOTFOUND;
+ return -1;
+ }
+ if(strcmp(this->filename,filename) == 0) break;
+ last = &(this->next);
+ }
+
+ unlink_node(this->inode);
+ free(this->filename);
+ (*last) = this->next;
+
+ e = fs->active_enums;
+ /* advance enums that are pointing to the just-deleted file */
+ while(e) {
+ if(e->current == this) e->current = this->next;
+ e = e->next;
+ }
+ free(this);
+ return 0;
+}
+
+int ramfs_rename(ramfs * fs,const char* oldname,const char* newname) {
+ ramdirent * this;
+ char * newnamebuf;
+
+ this = ramfs_findfile(fs,oldname);
+
+ if(!this) {
+ fs->last_error = RAMFS_NOTFOUND;
+ return -1;
+ }
+
+ /* just in case */
+ if(strcmp(oldname,newname) == 0) return 0;
+
+ newnamebuf = realloc(this->filename,strlen(newname)+1);
+ if(!newnamebuf) {
+ fs->last_error = RAMFS_NOMEM;
+ return -1;
+ }
+
+ /* this may return RAMFS_NOTFOUND, which can be ignored. */
+ ramfs_unlink(fs,newname);
+
+ strcpy(newnamebuf,newname);
+ this->filename = newnamebuf;
+ return 0;
+}
+
+ramfs_enum * ramfs_enum_new(ramfs * fs) {
+ ramfs_enum * e;
+
+ e = malloc(sizeof(ramfs_enum));
+ if(!e) {
+ fs->last_error = RAMFS_NOMEM;
+ return NULL;
+ }
+ e->current = fs->files;
+ e->next = fs->active_enums;
+ e->fs = fs;
+ fs->active_enums = e;
+ return e;
+}
+
+char* ramfs_enum_next(ramfs_enum * e) {
+ char * filename = NULL;
+ if(e->current) {
+ filename = e->current->filename;
+ e->current = e->current->next;
+ }
+ return filename;
+}
+
+void ramfs_enum_end(ramfs_enum * e) {
+ ramfs_enum** last = &e->fs->active_enums;
+ while(*last) {
+ if(*last == e) {
+ *last = e->next;
+ break;
+ }
+ last = &(e->next);
+ }
+ free(e);
+}
+
+int ramfile_read(ramhandle * handle,void * buf,int len) {
+ ramfile * file = handle->file;
+ int left;
+ char *t = (char *)buf;
+
+ if(len>file->size - handle->filepos) len = file->size-handle->filepos;
+ if(len<0) return 0;
+
+ left = len;
+ while(left) {
+ char * p = file->data[handle->filepos/RAMFS_BLOCKSIZE]+handle->filepos%RAMFS_BLOCKSIZE;
+ int x = RAMFS_BLOCKSIZE-handle->filepos%RAMFS_BLOCKSIZE;
+ if(x>left) x = left;
+
+ memcpy(t,p,x);
+ handle->filepos += x;
+ left -= x;
+ t += x;
+ }
+ buf = (void *)t;
+ return len;
+}
+
+int ramfile_write(ramhandle * handle,const void * buf,int len) {
+ ramfile * file = handle->file;
+ int left;
+ char *t = (char *)buf;
+
+ if(!handle->mode & RAMFS_WRITE) {
+ handle->last_error = RAMFS_NOACCESS;
+ return -1;
+ }
+
+ if(handle->mode & RAMFS_APPEND) {
+ handle->filepos = file->size;
+ }
+
+ if(file->size < handle->filepos) {
+ /* if this fails then pass the error on */
+ if(ramfile_truncate(handle,handle->filepos) == -1) return -1;
+ }
+
+ if(file->size < handle->filepos+len) {
+ int x = resize(file,handle->filepos+len);
+ if(x) {
+ handle->last_error = -x;
+ return -1;
+ }
+ }
+
+ /* This is exactly the same as for reading, cept the copy is in the
+ other direction. */
+ left = len;
+ while(left) {
+ char * p = file->data[handle->filepos/RAMFS_BLOCKSIZE] +
+ handle->filepos%RAMFS_BLOCKSIZE;
+ int x = RAMFS_BLOCKSIZE-handle->filepos%RAMFS_BLOCKSIZE;
+ if(x>left) x = left;
+
+ memcpy(p,buf,x);
+ handle->filepos += x;
+ left -= x;
+ t += x;
+ }
+ buf = (void *)t;
+ return len;
+}
+
+int ramfile_seek(ramhandle * handle,int pos,int whence) {
+ /* Just set the handle's file position. The effects become noticeable
+ at the next read or write.
+ */
+ if(whence == RAMFS_SEEK_CUR) {
+ handle->filepos += pos;
+ } else if(whence == RAMFS_SEEK_END) {
+ handle->filepos = handle->file->size+pos;
+ } else {
+ handle->filepos = pos;
+ }
+ return 0;
+}
+
+int ramfile_size(ramhandle * handle) {
+ return handle->file->size;
+}
+
+static int ramfile_truncate(ramhandle * handle,int size) {
+ ramfile * file = handle->file;
+ int oldsize = file->size;
+ int x = resize(file,size);
+
+ if(x) {
+ handle->last_error = -x;
+ return -1;
+ }
+ if(oldsize >= size) return 0;
+
+ /* file was expanded. fill the new space with zeros. */
+ while(oldsize < file->size) {
+ char * p = file->data[oldsize/RAMFS_BLOCKSIZE]+oldsize%RAMFS_BLOCKSIZE;
+ int len = RAMFS_BLOCKSIZE - oldsize%RAMFS_BLOCKSIZE;
+ if(len>file->size-oldsize) len = file->size-oldsize;
+ oldsize += len;
+ memset(p,0,len);
+ }
+ return 0;
+}
+
+void ramfile_close(ramhandle * handle) {
+ ramfile * file = handle->file;
+ unlink_node(file);
+ free(handle);
+}
+
+int ramfile_tell(ramhandle* handle) {
+ return handle->filepos;
+}
+
+int ramfile_eof(ramhandle* handle) {
+ return (handle->filepos >= handle->file->size);
+}
diff --git a/gs/base/ramfs.h b/gs/base/ramfs.h
new file mode 100644
index 000000000..b2d6f29a8
--- /dev/null
+++ b/gs/base/ramfs.h
@@ -0,0 +1,62 @@
+#ifndef __RAMFS_H__
+#define __RAMFS_H__
+
+#define RAMFS_BLOCKSIZE 1024
+
+typedef struct _ ;
+typedef struct _ramdirent ramdirent;
+typedef struct _ramhandle ramhandle;
+typedef struct _ _enum _enum;
+
+/*
+ _new: NOMEM
+ _open: NOTFOUND
+ _unlink: NOTFOUND
+ _enum_new: NOMEM
+ _enum_next: none
+ _enum_end: none
+ ramfile_read: none
+ ramfile_write: NOSPACE, NOMEM, NOACCESS
+ ramfile_seek: none
+ ramfile_pos: none
+ ramfile_close: none
+*/
+
+/* Error constants */
+#define RAMFS_NOTFOUND 2
+#define RAMFS_NOACCESS 5
+#define RAMFS_NOMEM 6
+#define RAMFS_NOSPACE 7
+
+/* Open mode flags */
+#define RAMFS_READ 1
+#define RAMFS_CREATE 2
+#define RAMFS_WRITE 4
+#define RAMFS_TRUNC 8
+#define RAMFS_APPEND 16
+
+#define RAMFS_SEEK_SET 0
+#define RAMFS_SEEK_CUR 1
+#define RAMFS_SEEK_END 2
+
+ * _new(gs_memory_t *mem, int size); /* size is in KiB */
+void _destroy(gs_memory_t *, * fs);
+int _error(const * fs);
+ramhandle * _open(gs_memory_t *mem, * fs,const char * filename,int mode);
+int _blocksize( * fs);
+int _blocksfree( * fs);
+int _unlink( * fs,const char *filename);
+int _rename( * fs,const char *oldname,const char *newname);
+ _enum * _enum_new( * fs);
+char* _enum_next( _enum * e);
+void _enum_end( _enum * e);
+int ramfile_read(ramhandle * handle,void * buf,int len);
+int ramfile_write(ramhandle * handle,const void * buf,int len);
+int ramfile_seek(ramhandle * handle,int pos,int whence);
+int ramfile_eof(ramhandle * handle);
+int ramfile_tell(ramhandle * handle);
+int ramfile_size(ramhandle * handle);
+void ramfile_close(ramhandle * handle);
+int ramfile_error(ramhandle * handle);
+
+#endif /* __RAMFS_H__ */
diff --git a/gs/psi/msvc.mak b/gs/psi/msvc.mak
index 0da900e45..4e9316cf2 100644
--- a/gs/psi/msvc.mak
+++ b/gs/psi/msvc.mak
@@ -935,7 +935,7 @@ JPX_CFLAGS = $JPX_CFLAGS -DUSE_OPENJPEG_JP2
# Choose the language feature(s) to include. See gs.mak for details.
!ifndef FEATURE_DEVS
-FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)rasterop.dev $(PSD)epsf.dev $(PSD)mshandle.dev $(PSD)mspoll.dev $(PSD)fapi_ps.dev $(PSD)jbig2.dev $(PSD)jpx.dev $(PSD)winutf8.dev
+FEATURE_DEVS=$(PSD)psl3.dev $(PSD)pdf.dev $(PSD)dpsnext.dev $(PSD)ttfont.dev $(PSD)rasterop.dev $(PSD)epsf.dev $(PSD)mshandle.dev $(PSD)mspoll.dev $(PSD)fapi_ps.dev $(PSD)jbig2.dev $(PSD)jpx.dev $(PSD)winutf8.dev $(PSD)ramfs.dev
!ifndef METRO
FEATURE_DEVS=$(FEATURE_DEVS) $(PSD)msprinter.dev $(GLD)pipe.dev
!endif