From b39cafaa22ad7efc2ac4b64a5eeac8f49bc80e00 Mon Sep 17 00:00:00 2001 From: Tanaka Akira Date: Mon, 31 May 1999 17:10:16 +0000 Subject: Initial revision --- Config/.cvsignore | 0 Config/defs.mk.in | 80 +++++++++++ Doc/Zsh/mod_mapfile.yo | 46 ++++++ Src/Modules/mapfile.c | 369 ++++++++++++++++++++++++++++++++++++++++++++++++ Src/Modules/mapfile.mdd | 3 + 5 files changed, 498 insertions(+) create mode 100644 Config/.cvsignore create mode 100644 Config/defs.mk.in create mode 100644 Doc/Zsh/mod_mapfile.yo create mode 100644 Src/Modules/mapfile.c create mode 100644 Src/Modules/mapfile.mdd diff --git a/Config/.cvsignore b/Config/.cvsignore new file mode 100644 index 000000000..e69de29bb diff --git a/Config/defs.mk.in b/Config/defs.mk.in new file mode 100644 index 000000000..3394123d5 --- /dev/null +++ b/Config/defs.mk.in @@ -0,0 +1,80 @@ +# +# Basic Makefile definitions +# +# Copyright (c) 1995-1997 Richard Coleman +# All rights reserved. +# +# Permission is hereby granted, without written agreement and without +# license or royalty fees, to use, copy, modify, and distribute this +# software and to distribute modified versions of this software for any +# purpose, provided that the above copyright notice and the following +# two paragraphs appear in all copies of this software. +# +# In no event shall Richard Coleman or the Zsh Development Group be liable +# to any party for direct, indirect, special, incidental, or consequential +# damages arising out of the use of this software and its documentation, +# even if Richard Coleman and the Zsh Development Group have been advised of +# the possibility of such damage. +# +# Richard Coleman and the Zsh Development Group specifically disclaim any +# warranties, including, but not limited to, the implied warranties of +# merchantability and fitness for a particular purpose. The software +# provided hereunder is on an "as is" basis, and Richard Coleman and the +# Zsh Development Group have no obligation to provide maintenance, +# support, updates, enhancements, or modifications. +# + +# fundamentals +SHELL = /bin/sh +@SET_MAKE@ + +# installation directories +prefix = @prefix@ +exec_prefix = @exec_prefix@ +bindir = @bindir@ +libdir = @libdir@ +MODDIR = $(libdir)/zsh/$(VERSION) +infodir = @infodir@ +mandir = @mandir@ + +# compilation +CC = @CC@ +CPP = @CPP@ +CPPFLAGS = @CPPFLAGS@ +DEFS = @DEFS@ +CFLAGS = @CFLAGS@ +LDFLAGS = @LDFLAGS@ +EXTRA_LDFLAGS = @EXTRA_LDFLAGS@ +DLCFLAGS = @DLCFLAGS@ +DLLDFLAGS = @DLLDFLAGS@ +LIBLDFLAGS = @LIBLDFLAGS@ +EXELDFLAGS = @EXELDFLAGS@ +LIBS = @LIBS@ +DL_EXT = @DL_EXT@ +DLLD = @DLLD@ +EXPOPT = @EXPOPT@ +IMPOPT = @IMPOPT@ + +# utilities +AWK = @AWK@ +YODL = @YODL@ +YODL2TXT = $(YODL)2txt +YODL2HTML = $(YODL)2html + +# install utility +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ + +# flags passed to recursive makes in subdirectories +MAKEDEFS = \ +prefix='$(prefix)' exec_prefix='$(exec_prefix)' bindir='$(bindir)' \ +libdir='$(libdir)' MODDIR='$(MODDIR)' infodir='$(infodir)' mandir='$(mandir)' \ +CC='$(CC)' CPPFLAGS='$(CPPFLAGS)' DEFS='$(DEFS)' CFLAGS='$(CFLAGS)' \ +LDFLAGS='$(LDFLAGS)' EXTRA_LDFLAGS='$(EXTRA_LDFLAGS)' \ +DLCFLAGS='$(DLCFLAGS)' DLLDFLAGS='$(DLLDFLAGS)' \ +LIBLDFLAGS='$(LIBLDFLAGS)' EXELDFLAGS='$(EXELDFLAGS)' \ +LIBS='$(LIBS)' DL_EXT='$(DL_EXT)' DLLD='$(DLLD)' \ +AWK='$(AWK)' YODL='$(YODL)' YODL2TXT='$(YODL2TXT)' YODL2HTML='$(YODL2HTML)' + +# override built-in suffix list +.SUFFIXES: diff --git a/Doc/Zsh/mod_mapfile.yo b/Doc/Zsh/mod_mapfile.yo new file mode 100644 index 000000000..6d0475711 --- /dev/null +++ b/Doc/Zsh/mod_mapfile.yo @@ -0,0 +1,46 @@ +texinode(The mapfile Module)(The parameter Module)(The files Module)(Zsh Modules) +sect(The mapfile Module) +cindex(parameter, file access via) +The tt(mapfile) module provides one special associative array parameter of +the same name. + +startitem() +vindex(mapfile) +item(tt(mapfile))( +This associative array takes as keys the names of files; the resulting +value is the content of the file. The value is treated identically to any +other text coming from a parameter. The value may also be assigned to, in +which case the file in question is written (whether or not it originally +existed); or an element may be unset, which will delete the file in +question. For example, `tt(vared mapfile[myfile])' works as expected, +editing the file `tt(myfile)'. + +When the array is accessed as a whole, the keys are the names of files in +the current directory, and the values are empty (to save a huge overhead in +memory). Thus tt(${(k)mapfile}) has the same affect as the glob operator +tt(*(D)), since files beginning with a dot are not special. Care must be +taken with expressions such as tt(rm ${(k)mapfile}), which will delete +every file in the current directory without the usual `tt(rm *)' test. + +The parameter tt(mapfile) may be made read-only; in that case, files +referenced may not be written or deleted. +) +enditem() + +subsect(Limitations) + +Although reading and writing of the file in question is efficiently +handled, zsh's internal memory management may be arbitrarily baroque. Thus +it should not automatically be assumed that use of tt(mapfile) represents a +gain in efficiency over use of other mechanisms. Note in particular that +the whole contents of the file will always reside physically in memory when +accessed (possibly multiple times, due to standard parameter subsitution +operations). + +No errors are printed or flagged for non-existent, unreadable, or +unwriteable files, as the parameter mechanism is too low in the shell +execution hierarchy to make this convenient. + +It is unfortunate that the mechanism for loading modules does not yet allow +the user to specify the name of the shell parameter to be given the special +behaviour. diff --git a/Src/Modules/mapfile.c b/Src/Modules/mapfile.c new file mode 100644 index 000000000..e98da7889 --- /dev/null +++ b/Src/Modules/mapfile.c @@ -0,0 +1,369 @@ +/* + * mapfile.c - associative array interface to external files + * + * This file is part of zsh, the Z shell. + * + * Copyright (c) 1999 Sven Wischnowsky, Peter Stephenson + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and to distribute modified versions of this software for any + * purpose, provided that the above copyright notice and the following + * two paragraphs appear in all copies of this software. + * + * In no event shall Sven Wischnowsky, Peter Stephenson or the Zsh Development + * Group be liable to any party for direct, indirect, special, incidental, or + * consequential damages arising out of the use of this software and its + * documentation, even if Peter Stephenson, Sven Wischnowsky and the Zsh + * Development Group have been advised of the possibility of such damage. + * + * Peter Stephenson, Sven Wischnowsky and the Zsh Development Group + * specifically disclaim any warranties, including, but not limited to, the + * implied warranties of merchantability and fitness for a particular purpose. + * The softwareprovided hereunder is on an "as is" basis, and Peter + * Stephenson, Sven Wischnowsky and the Zsh Development Group have no + * obligation to provide maintenance, support, updates, enhancements, or + * modifications. + * + */ + +/* + * To do: worry about when keys of associative arrays get unmeta'd. + */ +#include "mapfile.mdh" +#include "mapfile.pro" + +/* + * Make sure we have all the bits I'm using for memory mapping, otherwise + * I don't know what I'm doing. + */ +#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_FTRUNCATE) +#if defined(HAVE_MMAP) && defined(HAVE_MUNMAP) && defined(HAVE_MSYNC) +#define USE_MMAP 1 + +#include + +#if !defined(MAP_VARIABLE) +#define MAP_VARIABLE 0 +#endif +#if !defined(MAP_FILE) +#define MAP_FILE 0 +#endif +#if !defined(MAP_NORESERVE) +#define MAP_NORESERVE 0 +#endif +#define MMAP_ARGS (MAP_FILE | MAP_VARIABLE | MAP_SHARED | MAP_NORESERVE) + +#endif /* HAVE_MMAP && HAVE_MUNMAP && HAVE_MSYNC */ +#endif /* HAVE_SYS_MMAN_H && HAVE_FTRUNCATE */ + +/* + * Name of the special parameter. If zmodload took arguments, + * we could make this selectable. + */ +static char mapfile_nam[] = "mapfile"; + +static Param mapfile_pm; + +/* Empty dummy function for special hash parameters. */ + +/**/ +static void +shempty(void) +{ +} + +/* Create the special hash parameter. */ + +/**/ +static Param +createmapfilehash() +{ + Param pm; + HashTable ht; + + unsetparam(mapfile_nam); + mapfile_pm = NULL; + + if (!(pm = createparam(mapfile_nam, PM_SPECIAL|PM_REMOVABLE|PM_HASHED))) + return NULL; + + pm->level = pm->old ? locallevel : 0; + pm->gets.hfn = hashgetfn; + pm->sets.hfn = setpmmapfiles; + pm->unsetfn = stdunsetfn; + pm->u.hash = ht = newhashtable(7, mapfile_nam, NULL); + + ht->hash = hasher; + ht->emptytable = (TableFunc) shempty; + ht->filltable = NULL; + ht->addnode = (AddNodeFunc) shempty; + ht->getnode = ht->getnode2 = getpmmapfile; + ht->removenode = (RemoveNodeFunc) shempty; + ht->disablenode = NULL; + ht->enablenode = NULL; + ht->freenode = (FreeNodeFunc) shempty; + ht->printnode = printparamnode; + ht->scantab = scanpmmapfile; + + return (mapfile_pm = pm); +} + +/* Functions for the options special parameter. */ + +/**/ +static void +setpmmapfile(Param pm, char *value) +{ + int fd = -1, len; + char *name = ztrdup(pm->nam); +#ifdef USE_MMAP + caddr_t mmptr; +#else + FILE *fout; +#endif + + /* + * First unmetafy the value, and the name since we don't + * where it's been. + */ + unmetafy(name, &len); + unmetafy(value, &len); + + /* Open the file for writing */ +#ifdef USE_MMAP + if (!(pm->flags & PM_READONLY) && + (fd = open(name, O_RDWR|O_CREAT|O_NOCTTY, 0666)) >= 0 && + (mmptr = (caddr_t)mmap((caddr_t)0, len, PROT_READ | PROT_WRITE, + MMAP_ARGS, fd, (off_t)0)) != (caddr_t)-1) { + /* + * First we need to make sure the file is long enough for + * when we msync. On AIX, at least, we just get zeroes otherwise. + */ + ftruncate(fd, len); + memcpy(mmptr, value, len); + msync(mmptr, len, MS_SYNC); + /* + * Then we need to truncate again, since mmap() always maps complete + * pages. Honestly, I tried it without, and you need both. + */ + ftruncate(fd, len); + } +#else /* don't USE_MMAP */ + /* can't be bothered to do anything too clever here */ + if ((fout = fopen(name, "w"))) { + while (len--) + putc(*value++, fout); + fclose(fout); + } +#endif /* USE_MMAP */ + if (fd >= 0) + close(fd); + free(name); + free(value); +} + +/**/ +static void +unsetpmmapfile(Param pm, int exp) +{ + /* Unlink the file given by pm->nam */ + char *fname = ztrdup(pm->nam); + int dummy; + unmetafy(fname, &dummy); + + if (!(pm->flags & PM_READONLY)) + unlink(fname); + + free(fname); +} + +/**/ +static void +setpmmapfiles(Param pm, HashTable ht) +{ + int i; + HashNode hn; + + /* just to see if I've understood what's happening */ + DPUTS(pm != mapfile_pm, "BUG: setpmmapfiles called for wrong param"); + + if (!ht) + return; + + if (!(pm->flags & PM_READONLY)) + for (i = 0; i < ht->hsize; i++) + for (hn = ht->nodes[i]; hn; hn = hn->next) { + struct value v; + + v.isarr = v.inv = v.a = 0; + v.b = -1; + v.arr = NULL; + v.pm = (Param) hn; + + setpmmapfile(v.pm, ztrdup(getstrvalue(&v))); + } + deleteparamtable(ht); +} + +/**/ +static char * +get_contents(char *fname) +{ + int fd; +#ifdef USE_MMAP + caddr_t mmptr; + struct stat sbuf; +#endif + char *val; + unmetafy(fname = ztrdup(fname), &fd); + +#ifdef USE_MMAP + if ((fd = open(fname, O_RDONLY | O_NOCTTY)) < 0 || + fstat(fd, &sbuf) || + (mmptr = (caddr_t)mmap((caddr_t)0, sbuf.st_size, PROT_READ, + MMAP_ARGS, fd, (off_t)0)) == (caddr_t)-1) { + if (fd >= 0) + close(fd); + free(fname); + return NULL; + } + + /* + * Sadly, we need to copy the thing even if metafying doesn't + * change it. We just don't know when we might get a chance to + * munmap it, otherwise. + */ + val = metafy((char *)mmptr, sbuf.st_size, META_HEAPDUP); + + munmap(mmptr, sbuf.st_size); + close(fd); +#else /* don't USE_MMAP */ + val = NULL; + if ((fd = open(fname, O_RDONLY | O_NOCTTY)) >= 0) { + LinkList ll; + MUSTUSEHEAP("mapfile:get_contents"); + if ((ll = readoutput(fd, 1))) + val = peekfirst(ll); + } +#endif /* USE_MMAP */ + free(fname); + return val; +} + +/**/ +static HashNode +getpmmapfile(HashTable ht, char *name) +{ + char *contents; + Param pm = NULL; + + HEAPALLOC { + pm = (Param) zhalloc(sizeof(struct param)); + pm->nam = dupstring(name); + pm->flags = PM_SCALAR; + pm->sets.cfn = setpmmapfile; + pm->gets.cfn = strgetfn; + pm->unsetfn = unsetpmmapfile; + pm->ct = 0; + pm->env = NULL; + pm->ename = NULL; + pm->old = NULL; + pm->level = 0; + + pm->flags |= (mapfile_pm->flags & PM_READONLY); + + /* Set u.str to contents of file given by name */ + if ((contents = get_contents(pm->nam))) + pm->u.str = contents; + else { + pm->u.str = ""; + pm->flags |= PM_UNSET; + } + } LASTALLOC; + + return (HashNode) pm; +} + +/**/ +static void +scanpmmapfile(HashTable ht, ScanFunc func, int flags) +{ + struct param pm; + DIR *dir; + + if (!(dir = opendir("."))) + return; + + pm.flags = PM_SCALAR; + pm.sets.cfn = setpmmapfile; + pm.gets.cfn = strgetfn; + pm.unsetfn = unsetpmmapfile; + pm.ct = 0; + pm.env = NULL; + pm.ename = NULL; + pm.old = NULL; + pm.level = 0; + + pm.flags |= (mapfile_pm->flags & PM_READONLY); + + /* Here we scan the current directory, calling func() for each file */ + while ((pm.nam = zreaddir(dir, 1))) { + /* + * Hmmm, it's rather wasteful always to read the contents. + * In fact, it's grotesequely wasteful, since that would mean + * we always read the entire contents of every single file + * in the directory into memory. Hence just leave it empty. + */ + pm.u.str = ""; + func((HashNode) &pm, flags); + } + closedir(dir); +} + +/**/ +int +setup_mapfile(Module m) +{ + return 0; +} + +/**/ +int +boot_mapfile(Module m) +{ + /* Create the special associative array. */ + + if (!createmapfilehash()) + return 1; + + return 0; +} + +#ifdef MODULE + +/**/ +int +cleanup_mapfile(Module m) +{ + Param pm; + + /* Remove the special parameter if it is still the same. */ + + if ((pm = (Param) paramtab->getnode(paramtab, mapfile_nam)) && + pm == mapfile_pm) { + pm->flags &= ~PM_READONLY; + unsetparam_pm(pm, 0, 1); + } + return 0; +} + +/**/ +int +finish_mapfile(Module m) +{ + return 0; +} + +#endif diff --git a/Src/Modules/mapfile.mdd b/Src/Modules/mapfile.mdd new file mode 100644 index 000000000..9adb36162 --- /dev/null +++ b/Src/Modules/mapfile.mdd @@ -0,0 +1,3 @@ +autoparams="mapfile" + +objects="mapfile.o" -- cgit 1.4.1