summary refs log tree commit diff
path: root/Src
diff options
context:
space:
mode:
Diffstat (limited to 'Src')
-rw-r--r--Src/builtin.c2
-rw-r--r--Src/mem.c376
-rw-r--r--Src/params.c213
3 files changed, 388 insertions, 203 deletions
diff --git a/Src/builtin.c b/Src/builtin.c
index 7dd9317b1..477e984cd 100644
--- a/Src/builtin.c
+++ b/Src/builtin.c
@@ -1634,7 +1634,6 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 		    pm->env = addenv(pname, getsparam(pname), pm->flags);
 	    } else if (pm->env && !(pm->flags & PM_HASHELEM)) {
 		delenv(pm->env);
-		zsfree(pm->env);
 		pm->env = NULL;
 	    }
 	    if (value)
@@ -1711,7 +1710,6 @@ typeset_single(char *cname, char *pname, Param pm, int func,
 	tpm->ct = pm->ct;
 	if (pm->env) {
 	    delenv(pm->env);
-	    zsfree(pm->env);
 	}
 	tpm->env = pm->env = NULL;
 
diff --git a/Src/mem.c b/Src/mem.c
index 1145f8c5e..d97fee592 100644
--- a/Src/mem.c
+++ b/Src/mem.c
@@ -34,7 +34,7 @@
 	There are two ways to allocate memory in zsh.  The first way is
 	to call zalloc/zcalloc, which call malloc/calloc directly.  It
 	is legal to call realloc() or free() on memory allocated this way.
-	The second way is to call halloc/hcalloc, which allocates memory
+	The second way is to call zhalloc/hcalloc, which allocates memory
 	from one of the memory pools on the heap stack.  Such memory pools 
 	will automatically created when the heap allocation routines are
 	called.  To be sure that they are freed at appropriate times
@@ -54,35 +54,29 @@
 	Memory allocated in this way does not have to be freed explicitly;
 	it will all be freed when the pool is destroyed.  In fact,
 	attempting to free this memory may result in a core dump.
-	The pair of pointers ncalloc and alloc may point to either
-	zalloc & zcalloc or halloc & hcalloc; permalloc() sets them to the
-	former, and heapalloc() sets them to the latter. This can be useful.
-	For example, the dupstruct() routine duplicates a syntax tree,
-	allocating the new memory for the tree using alloc().  If you want
-	to duplicate a structure for a one-time use (i.e. to execute the list
-	in a for loop), call heapalloc(), then dupstruct().  If you want
-	to duplicate a structure in order to preserve it (i.e. a function
-	definition), call permalloc(), then dupstruct().
-
-	If we use zsh's own allocator we use a simple trick to avoid that
-	the (*real*) heap fills up with empty zsh-heaps: we allocate a
-	large block of memory before allocating a heap pool, this memory
-	is freed again immediately after the pool is allocated. If there
-	are only small blocks on the free list this guarantees that the
-	memory for the pool is at the end of the memory which means that
-	we can give it back to the system when the pool is freed.
+
+	If possible, the heaps are allocated using mmap() so that the
+	(*real*) heap isn't filled up with empty zsh heaps. If mmap()
+	is not available and zsh's own allocator is used, we use a simple trick
+	to avoid that: we allocate a large block of memory before allocating
+	a heap pool, this memory is freed again immediately after the pool
+	is allocated. If there are only small blocks on the free list this
+	guarantees that the memory for the pool is at the end of the memory
+	which means that we can give it back to the system when the pool is
+	freed.
 */
 
-/* != 0 if we are allocating in the heaplist */
- 
-/**/
-int useheap;
+#if defined(HAVE_SYS_MMAN_H) && defined(HAVE_MMAP) && defined(HAVE_MUNMAP)
 
-/* Current allocation pointers.  ncalloc() is either zalloc() or halloc(); *
- * alloc() is either zcalloc() or hcalloc().                               */
+#include <sys/mman.h>
 
-/**/
-void *(*ncalloc) _((size_t)), *(*alloc) _((size_t));
+#if defined(MAP_ANONYMOUS) && defined(MAP_PRIVATE)
+
+#define USE_MMAP 1
+#define MMAP_FLAGS (MAP_ANONYMOUS | MAP_PRIVATE)
+
+#endif
+#endif
 
 #ifdef ZSH_MEM_WARNING
 # ifndef DEBUG
@@ -96,63 +90,77 @@ static int h_m[1025], h_push, h_pop, h_free;
 
 #endif
 
-#define H_ISIZE  sizeof(long)
-#define HEAPSIZE (8192 - H_ISIZE)
+/* Make sure we align to the longest fundamental type. */
+union mem_align {
+    zlong l;
+    double d;
+};
+
+#define H_ISIZE  sizeof(union mem_align)
+#define HEAPSIZE (16384 - H_ISIZE)
 #define HEAP_ARENA_SIZE (HEAPSIZE - sizeof(struct heap))
 #define HEAPFREE (16384 - H_ISIZE)
 
-/* set default allocation to heap stack */
+/* list of zsh heaps */
+
+static Heap heaps;
+
+/* first heap with free space, not always correct */
+
+static Heap fheap;
+
+/* Use new heaps from now on. This returns the old heap-list. */
 
 /**/
-int
-global_heapalloc(void)
+mod_export Heap
+new_heaps(void)
 {
-    int luh = useheap;
+    Heap h = heaps;
+
+    fheap = heaps = NULL;
 
-    alloc = hcalloc;
-    ncalloc = halloc;
-    useheap = 1;
-    return luh;
+    return h;
 }
 
-/* set default allocation to malloc() */
+/* Re-install the old heaps again, freeing the new ones. */
 
 /**/
-int
-global_permalloc(void)
+mod_export void
+old_heaps(Heap old)
 {
-    int luh = useheap;
+    Heap h, n;
 
-    alloc = zcalloc;
-    ncalloc = zalloc;
-    useheap = 0;
-    return luh;
+    for (h = heaps; h; h = n) {
+	n = h->next;
+	DPUTS(h->sp, "BUG: old_heaps() with pushed heaps");
+#ifdef USE_MMAP
+	munmap((void *) h, sizeof(*h));
+#else
+	zfree(h, sizeof(*h));
+#endif
+    }
+    heaps = old;
+    fheap = NULL;
 }
 
-/* heappush saves the current heap state using this structure */
-
-struct heapstack {
-    struct heapstack *next;	/* next one in list for this heap */
-    size_t used;
-};
+/* Temporarily switch to other heaps (or back again). */
 
-/* A zsh heap. */
-
-struct heap {
-    struct heap *next;		/* next one                                  */
-    size_t used;		/* bytes used from the heap                  */
-    struct heapstack *sp;	/* used by pushheap() to save the value used */
-#define arena(X)	((char *) (X) + sizeof(struct heap))
-};
+/**/
+mod_export Heap
+switch_heaps(Heap new)
+{
+    Heap h = heaps;
 
-/* list of zsh heap */
+    heaps = new;
+    fheap = NULL;
 
-static Heap heaps;
+    return h;
+}
 
 /* save states of zsh heaps */
 
 /**/
-void
+mod_export void
 pushheap(void)
 {
     Heap h;
@@ -174,7 +182,7 @@ pushheap(void)
 /* reset heaps to previous state */
 
 /**/
-void
+mod_export void
 freeheap(void)
 {
     Heap h, hn, hl = NULL;
@@ -182,6 +190,8 @@ freeheap(void)
 #if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG)
     h_free++;
 #endif
+
+    fheap = NULL;
     for (h = heaps; h; h = hn) {
 	hn = h->next;
 	if (h->sp) {
@@ -189,9 +199,16 @@ freeheap(void)
 	    memset(arena(h) + h->sp->used, 0xff, h->used - h->sp->used);
 #endif
 	    h->used = h->sp->used;
+	    if (!fheap && h->used < HEAP_ARENA_SIZE)
+		fheap = h;
 	    hl = h;
-	} else
+	} else {
+#ifdef USE_MMAP
+	    munmap((void *) h, h->size);
+#else
 	    zfree(h, HEAPSIZE);
+#endif
+	}
     }
     if (hl)
 	hl->next = NULL;
@@ -202,7 +219,7 @@ freeheap(void)
 /* reset heap to previous state and destroy state information */
 
 /**/
-void
+mod_export void
 popheap(void)
 {
     Heap h, hn, hl = NULL;
@@ -212,6 +229,7 @@ popheap(void)
     h_pop++;
 #endif
 
+    fheap = NULL;
     for (h = heaps; h; h = hn) {
 	hn = h->next;
 	if ((hs = h->sp)) {
@@ -220,11 +238,18 @@ popheap(void)
 	    memset(arena(h) + hs->used, 0xff, h->used - hs->used);
 #endif
 	    h->used = hs->used;
+	    if (!fheap && h->used < HEAP_ARENA_SIZE)
+		fheap = h;
 	    zfree(hs, sizeof(*hs));
 
 	    hl = h;
-	} else
+	} else {
+#ifdef USE_MMAP
+	    munmap((void *) h, h->size);
+#else
 	    zfree(h, HEAPSIZE);
+#endif
+	}
     }
     if (hl)
 	hl->next = NULL;
@@ -235,8 +260,8 @@ popheap(void)
 /* allocate memory from the current memory pool */
 
 /**/
-void *
-halloc(size_t size)
+mod_export void *
+zhalloc(size_t size)
 {
     Heap h;
     size_t n;
@@ -244,22 +269,21 @@ halloc(size_t size)
     size = (size + H_ISIZE - 1) & ~(H_ISIZE - 1);
 
 #if defined(ZSH_MEM) && defined(ZSH_MEM_DEBUG)
-    h_m[size < 1024 ? (size / H_ISIZE) : 1024]++;
+    h_m[size < (1024 * H_ISIZE) ? (size / H_ISIZE) : 1024]++;
 #endif
 
     /* find a heap with enough free space */
 
-    for (h = heaps; h; h = h->next) {
+    for (h = (fheap ? fheap : heaps); h; h = h->next) {
 	if (HEAP_ARENA_SIZE >= (n = size + h->used)) {
 	    h->used = n;
 	    return arena(h) + n - size;
 	}
     }
-
     {
 	Heap hp;
         /* not found, allocate new heap */
-#ifdef ZSH_MEM
+#if defined(ZSH_MEM) && !defined(USE_MMAP)
 	static int called = 0;
 	void *foo = called ? (void *)malloc(HEAPFREE) : NULL;
             /* tricky, see above */
@@ -269,9 +293,38 @@ halloc(size_t size)
 	n = HEAP_ARENA_SIZE > size ? HEAPSIZE : size + sizeof(*h);
 	for (hp = NULL, h = heaps; h; hp = h, h = h->next);
 
+#ifdef USE_MMAP
+	{
+	    static size_t pgsz = 0;
+
+	    if (!pgsz) {
+
+#ifdef _SC_PAGESIZE
+		pgsz = sysconf(_SC_PAGESIZE);     /* SVR4 */
+#else
+# ifdef _SC_PAGE_SIZE
+		pgsz = sysconf(_SC_PAGE_SIZE);    /* HPUX */
+# else
+		pgsz = getpagesize();
+# endif
+#endif
+
+		pgsz--;
+	    }
+	    n = (n + pgsz) & ~pgsz;
+	    h = (Heap) mmap(NULL, n, PROT_READ | PROT_WRITE,
+			    MMAP_FLAGS, -1, 0);
+	    if (h == ((Heap) -1)) {
+		zerr("fatal error: out of heap memory", NULL, 0);
+		exit(1);
+	    }
+	    h->size = n;
+	}
+#else
 	h = (Heap) zalloc(n);
+#endif
 
-#ifdef ZSH_MEM
+#if defined(ZSH_MEM) && !defined(USE_MMAP)
 	if (called)
 	    zfree(foo, HEAPFREE);
 	called = 1;
@@ -285,6 +338,7 @@ halloc(size_t size)
 	    hp->next = h;
 	else
 	    heaps = h;
+	fheap = NULL;
 
 	unqueue_signals();
 	return arena(h);
@@ -292,7 +346,7 @@ halloc(size_t size)
 }
 
 /**/
-void *
+mod_export void *
 hrealloc(char *p, size_t old, size_t new)
 {
     Heap h, ph;
@@ -303,7 +357,7 @@ hrealloc(char *p, size_t old, size_t new)
     if (old == new)
 	return p;
     if (!old && !p)
-	return halloc(new);
+	return zhalloc(new);
 
     /* find the heap with p */
 
@@ -317,8 +371,11 @@ hrealloc(char *p, size_t old, size_t new)
 
     if (p + old < arena(h) + h->used) {
 	if (new > old) {
-	    char *ptr = (char *) halloc(new);
+	    char *ptr = (char *) zhalloc(new);
 	    memcpy(ptr, p, old);
+#ifdef ZSH_MEM_DEBUG
+	    memset(p, 0xff, old);
+#endif
 	    return ptr;
 	} else
 	    return new ? p : NULL;
@@ -332,9 +389,15 @@ hrealloc(char *p, size_t old, size_t new)
 		ph->next = h->next;
 	    else
 		heaps = h->next;
+	    fheap = NULL;
+#ifdef USE_MMAP
+	    munmap((void *) h, h->size);
+#else
 	    zfree(h, HEAPSIZE);
+#endif
 	    return NULL;
 	}
+#ifndef USE_MMAP
 	if (old > HEAP_ARENA_SIZE || new > HEAP_ARENA_SIZE) {
 	    size_t n = HEAP_ARENA_SIZE > new ? HEAPSIZE : new + sizeof(*h);
 
@@ -345,13 +408,16 @@ hrealloc(char *p, size_t old, size_t new)
 	}
 	h->used = new;
 	return arena(h);
+#endif
     }
+#ifndef USE_MMAP
     DPUTS(h->used > HEAP_ARENA_SIZE, "BUG: hrealloc at invalid address");
+#endif
     if (h->used + (new - old) <= HEAP_ARENA_SIZE) {
 	h->used += new - old;
 	return p;
     } else {
-	char *t = halloc(new);
+	char *t = zhalloc(new);
 	memcpy(t, p, old > new ? new : old);
 	h->used -= old;
 #ifdef ZSH_MEM_DEBUG
@@ -364,12 +430,12 @@ hrealloc(char *p, size_t old, size_t new)
 /* allocate memory from the current memory pool and clear it */
 
 /**/
-void *
+mod_export void *
 hcalloc(size_t size)
 {
     void *ptr;
 
-    ptr = halloc(size);
+    ptr = zhalloc(size);
     memset(ptr, 0, size);
     return ptr;
 }
@@ -377,7 +443,7 @@ hcalloc(size_t size)
 /* allocate permanent memory */
 
 /**/
-void *
+mod_export void *
 zalloc(size_t size)
 {
     void *ptr;
@@ -393,7 +459,7 @@ zalloc(size_t size)
 }
 
 /**/
-void *
+mod_export void *
 zcalloc(size_t size)
 {
     void *ptr;
@@ -416,7 +482,7 @@ zcalloc(size_t size)
  * POSIX compliant, but I'm not sure how to do that.                */
 
 /**/
-void *
+mod_export void *
 zrealloc(void *ptr, size_t size)
 {
     if (ptr) {
@@ -441,20 +507,20 @@ zrealloc(void *ptr, size_t size)
 }
 
 /**/
-char *
+mod_export char *
 dupstring(const char *s)
 {
     char *t;
 
     if (!s)
 	return NULL;
-    t = (char *)ncalloc(strlen((char *)s) + 1);
+    t = (char *) zhalloc(strlen((char *)s) + 1);
     strcpy(t, s);
     return t;
 }
 
 /**/
-char *
+mod_export char *
 ztrdup(const char *s)
 {
     char *t;
@@ -466,17 +532,18 @@ ztrdup(const char *s)
     return t;
 }
 
+/**/
 #ifdef ZSH_MEM
 
 /*
    Below is a simple segment oriented memory allocator for systems on
    which it is better than the system's one. Memory is given in blocks
-   aligned to an integer multiple of sizeof(long) (4 bytes on most machines,
-   but 8 bytes on e.g. a dec alpha). Each block is preceded by a header
-   which contains the length of the data part (in bytes). In allocated
-   blocks only this field of the structure m_hdr is senseful. In free
-   blocks the second field (next) is a pointer to the next free segment
-   on the free list.
+   aligned to an integer multiple of sizeof(union mem_align), which will
+   probably be 64-bit as it is the longer of zlong or double. Each block is
+   preceded by a header which contains the length of the data part (in
+   bytes). In allocated blocks only this field of the structure m_hdr is
+   senseful. In free blocks the second field (next) is a pointer to the next
+   free segment on the free list.
 
    On top of this simple allocator there is a second allocator for small
    chunks of data. It should be both faster and less space-consuming than
@@ -532,22 +599,33 @@ ztrdup(const char *s)
 
 struct m_shdr {
     struct m_shdr *next;	/* next one on free list */
+#ifdef PAD_64_BIT
+    /* dummy to make this 64-bit aligned */
+    struct m_shdr *dummy;
+#endif
 };
 
 struct m_hdr {
-    long len;			/* length of memory block */
+    zlong len;			/* length of memory block */
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+    /* either 1 or 2 zlong's, whichever makes up 64 bits. */
+    zlong dummy1;
+#endif
     struct m_hdr *next;		/* if free: next on free list
 				   if block of small blocks: next one with
 				                 small blocks of same size*/
     struct m_shdr *free;	/* if block of small blocks: free list */
-    long used;			/* if block of small blocks: number of used
+    zlong used;			/* if block of small blocks: number of used
 				                                     blocks */
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+    zlong dummy2;
+#endif
 };
 
 
 /* alignment for memory blocks */
 
-#define M_ALIGN (sizeof(long))
+#define M_ALIGN (sizeof(union mem_align))
 
 /* length of memory header, length of first field of memory header and
    minimal size of a block left free (if we allocate memory and take a
@@ -556,9 +634,23 @@ struct m_hdr {
    the free list) */
 
 #define M_HSIZE (sizeof(struct m_hdr))
-#define M_ISIZE (sizeof(long))
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+# define M_ISIZE (2*sizeof(zlong))
+#else
+# define M_ISIZE (sizeof(zlong))
+#endif
 #define M_MIN   (2 * M_ISIZE)
 
+/* M_FREE  is the number of bytes that have to be free before memory is
+ *         given back to the system
+ * M_KEEP  is the number of bytes that will be kept when memory is given
+ *         back; note that this has to be less than M_FREE
+ * M_ALLOC is the number of extra bytes to request from the system */
+
+#define M_FREE  32768
+#define M_KEEP  16384
+#define M_ALLOC M_KEEP
+
 /* a pointer to the last free block, a pointer to the free list (the blocks
    on this list are kept in order - lowest address first) */
 
@@ -589,13 +681,21 @@ static char *m_high, *m_low;
 
 
 #define M_SIDX(S)  ((S) / M_ISIZE)
-#define M_SNUM     50
+#define M_SNUM     128
 #define M_SLEN(M)  ((M)->len / M_SNUM)
+#if defined(PAD_64_BIT) && !defined(ZSH_64_BIT_TYPE)
+/* Include the dummy in the alignment */
 #define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) +  \
-		    sizeof(long) + sizeof(struct m_hdr *))
+		    2*sizeof(zlong) + sizeof(struct m_hdr *))
 #define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) -  \
-		     sizeof(long) - sizeof(struct m_hdr *)) / M_SNUM)
-#define M_NSMALL 8
+		     2*sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM)
+#else
+#define M_SBLEN(S) ((S) * M_SNUM + sizeof(struct m_shdr *) +  \
+		    sizeof(zlong) + sizeof(struct m_hdr *))
+#define M_BSLEN(S) (((S) - sizeof(struct m_shdr *) -  \
+		     sizeof(zlong) - sizeof(struct m_hdr *)) / M_SNUM)
+#endif
+#define M_NSMALL    8
 
 static struct m_hdr *m_small[M_NSMALL];
 
@@ -613,7 +713,9 @@ malloc(MALLOC_ARG_T size)
 {
     struct m_hdr *m, *mp, *mt;
     long n, s, os = 0;
+#ifndef USE_MMAP
     struct heap *h, *hp, *hf = NULL, *hfp = NULL;
+#endif
 
     /* some systems want malloc to return the highest valid address plus one
        if it is called with an argument of zero */
@@ -657,9 +759,9 @@ malloc(MALLOC_ARG_T size)
 	    m->used++;
 
 	    /* if all small blocks in this block are allocated, the block is 
-	       put at the end of the list blocks wth small blocks of this
+	       put at the end of the list blocks with small blocks of this
 	       size (i.e., we try to keep blocks with free blocks at the
-	       beginning of the list, to make the search faster */
+	       beginning of the list, to make the search faster) */
 
 	    if (m->used == M_SNUM && m->next) {
 		for (mt = m; mt->next; mt = mt->next);
@@ -686,12 +788,14 @@ malloc(MALLOC_ARG_T size)
     } else
 	s = 0;
 
-/* search the free list for an block of at least the requested size */
+    /* search the free list for an block of at least the requested size */
     for (mp = NULL, m = m_free; m && m->len < size; mp = m, m = m->next);
 
- /* if there is an empty zsh heap at a lower address we steal it and take
-    the memory from it, putting the rest on the free list (remember
-    that the blocks on the free list are ordered) */
+#ifndef USE_MMAP
+
+    /* if there is an empty zsh heap at a lower address we steal it and take
+       the memory from it, putting the rest on the free list (remember
+       that the blocks on the free list are ordered) */
 
     for (hp = NULL, h = heaps; h; hp = h, h = h->next)
 	if (!h->used &&
@@ -718,16 +822,26 @@ malloc(MALLOC_ARG_T size)
 
 	for (mp = NULL, m = m_free; m && m->len < size; mp = m, m = m->next);
     }
+#endif
     if (!m) {
+	long nal;
 	/* no matching free block was found, we have to request new
 	   memory from the system */
-	n = (size + M_HSIZE + m_pgsz - 1) & ~(m_pgsz - 1);
+	n = (size + M_HSIZE + M_ALLOC + m_pgsz - 1) & ~(m_pgsz - 1);
 
 	if (((char *)(m = (struct m_hdr *)sbrk(n))) == ((char *)-1)) {
 	    DPUTS(1, "MEM: allocation error at sbrk.");
 	    unqueue_signals();
 	    return NULL;
 	}
+	if ((nal = ((long)(char *)m) & (M_ALIGN-1))) {
+	    if ((char *)sbrk(M_ALIGN - nal) == (char *)-1) {
+		DPUTS(1, "MEM: allocation error at sbrk.");
+		unqueue_signals();
+		return NULL;
+	    }
+	    m = (struct m_hdr *) ((char *)m + (M_ALIGN - nal));
+	}
 	/* set m_low, for the check in free() */
 	if (!m_low)
 	    m_low = (char *)m;
@@ -822,7 +936,7 @@ malloc(MALLOC_ARG_T size)
    0 for this parameter means: `don't know' */
 
 /**/
-void
+mod_export void
 zfree(void *p, int sz)
 {
     struct m_hdr *m = (struct m_hdr *)(((char *)p) - M_ISIZE), *mp, *mt = NULL;
@@ -1000,12 +1114,18 @@ zfree(void *p, int sz)
        and now there is more than one page size of memory, we can give
        it back to the system (and we do it ;-) */
     if ((((char *)m_lfree) + M_ISIZE + m_lfree->len) == m_high &&
-	m_lfree->len >= m_pgsz + M_MIN) {
-	long n = (m_lfree->len - M_MIN) & ~(m_pgsz - 1);
+	m_lfree->len >= m_pgsz + M_MIN + M_FREE) {
+	long n = (m_lfree->len - M_MIN - M_KEEP) & ~(m_pgsz - 1);
 
 	m_lfree->len -= n;
-	if (brk(m_high -= n) == -1)
+#ifdef HAVE_BRK
+	if (brk(m_high -= n) == -1) {
+#else
+	m_high -= n;
+	if (sbrk(-n) == (void *)-1) {
+#endif /* HAVE_BRK */
 	    DPUTS(1, "MEM: allocation error at brk.");
+	}
 
 #ifdef ZSH_MEM_DEBUG
 	m_b += n;
@@ -1028,7 +1148,7 @@ free(FREE_ARG_T p)
    those that have a zero byte at the end) */
 
 /**/
-void
+mod_export void
 zsfree(char *p)
 {
     if (p)
@@ -1104,7 +1224,7 @@ bin_mem(char *name, char **argv, char *ops, int func)
     int i, ii, fi, ui, j;
     struct m_hdr *m, *mf, *ms;
     char *b, *c, buf[40];
-    long u = 0, f = 0;
+    long u = 0, f = 0, to, cu;
 
     if (ops['v']) {
 	printf("The lower and the upper addresses of the heap. Diff gives\n");
@@ -1127,13 +1247,17 @@ bin_mem(char *name, char **argv, char *ops, int func)
 	printf("values, i.e. the number of blocks of that size that is\n");
 	printf("currently allocated. Total is the product of size and diff,\n");
 	printf("i.e. the number of bytes that are allocated for blocks of\n");
-	printf("this size.\n");
+	printf("this size. The last field gives the accumulated number of\n");
+	printf("bytes for all sizes.\n");
     }
-    printf("\nsize\tmalloc\tfree\tdiff\ttotal\n");
-    for (i = 0; i < 1024; i++)
-	if (m_m[i] || m_f[i])
-	    printf("%ld\t%d\t%d\t%d\t%ld\n", (long)i * M_ISIZE, m_m[i], m_f[i],
-		   m_m[i] - m_f[i], (long)i * M_ISIZE * (m_m[i] - m_f[i]));
+    printf("\nsize\tmalloc\tfree\tdiff\ttotal\tcum\n");
+    for (i = 0, cu = 0; i < 1024; i++)
+	if (m_m[i] || m_f[i]) {
+	    to = (long) i * M_ISIZE * (m_m[i] - m_f[i]);
+	    printf("%ld\t%d\t%d\t%d\t%ld\t%ld\n",
+		   (long)i * M_ISIZE, m_m[i], m_f[i], m_m[i] - m_f[i],
+		   to, (cu += to));
+	}
 
     if (m_m[i] || m_f[i])
 	printf("big\t%d\t%d\t%d\n", m_m[i], m_f[i], m_m[i] - m_f[i]);
@@ -1157,7 +1281,7 @@ bin_mem(char *name, char **argv, char *ops, int func)
 	printf("blocks is shown. For otherwise used blocks the first few\n");
 	printf("bytes are shown as an ASCII dump.\n");
     }
-    printf("\nblock list:\nnum\ttnum\taddr\tlen\tstate\tcum\n");
+    printf("\nblock list:\nnum\ttnum\taddr\t\tlen\tstate\tcum\n");
     for (m = m_l, mf = m_free, ii = fi = ui = 1; ((char *)m) < m_high;
 	 m = (struct m_hdr *)(((char *)m) + M_ISIZE + m->len), ii++) {
 	for (j = 0, ms = NULL; j < M_NSMALL && !ms; j++)
@@ -1168,8 +1292,9 @@ bin_mem(char *name, char **argv, char *ops, int func)
 	if (m == mf)
 	    buf[0] = '\0';
 	else if (m == ms)
-	    sprintf(buf, "%ld %ld %ld", M_SNUM - ms->used, ms->used,
-		    (m->len - sizeof(struct m_hdr)) / M_SNUM + 1);
+	    sprintf(buf, "%ld %ld %ld", (long)(M_SNUM - ms->used),
+		    (long)ms->used,
+		    (long)(m->len - sizeof(struct m_hdr)) / M_SNUM + 1);
 
 	else {
 	    for (i = 0, b = buf, c = (char *)&m->next; i < 20 && i < m->len;
@@ -1180,7 +1305,7 @@ bin_mem(char *name, char **argv, char *ops, int func)
 
 	printf("%d\t%d\t%ld\t%ld\t%s\t%ld\t%s\n", ii,
 	       (m == mf) ? fi++ : ui++,
-	       (long)m, m->len,
+	       (long)m, (long)m->len,
 	       (m == mf) ? "free" : ((m == ms) ? "small" : "used"),
 	       (m == mf) ? (f += m->len) : (u += m->len),
 	       buf);
@@ -1201,7 +1326,8 @@ bin_mem(char *name, char **argv, char *ops, int func)
 	    printf("%ld\t", (long)i * M_ISIZE);
 
 	    for (ii = 0, m = m_small[i]; m; m = m->next) {
-		printf("(%ld/%ld) ", M_SNUM - m->used, m->used);
+		printf("(%ld/%ld) ", (long)(M_SNUM - m->used),
+		       (long)m->used);
 		if (!((++ii) & 7))
 		    printf("\n\t");
 	    }
@@ -1233,10 +1359,11 @@ bin_mem(char *name, char **argv, char *ops, int func)
 
 #endif
 
+/**/
 #else				/* not ZSH_MEM */
 
 /**/
-void
+mod_export void
 zfree(void *p, int sz)
 {
     if (p)
@@ -1244,11 +1371,12 @@ zfree(void *p, int sz)
 }
 
 /**/
-void
+mod_export void
 zsfree(char *p)
 {
     if (p)
 	free(p);
 }
 
+/**/
 #endif
diff --git a/Src/params.c b/Src/params.c
index 4f519d476..c433b78df 100644
--- a/Src/params.c
+++ b/Src/params.c
@@ -455,10 +455,13 @@ void
 createparamtable(void)
 {
     Param ip, pm;
-    char **new_environ, **envp, **envp2, **sigptr, **t;
-    char **old_environ = environ;
+#ifndef HAVE_PUTENV
+    char **new_environ;
+    int  envsize;
+#endif
+    char **envp, **envp2, **sigptr, **t;
     char buf[50], *str, *iname, *hostnam;
-    int num_env, oae = opts[ALLEXPORT];
+    int  oae = opts[ALLEXPORT];
 #ifdef HAVE_UNAME
     struct utsname unamebuf;
     char *machinebuf;
@@ -501,15 +504,19 @@ createparamtable(void)
 
     setsparam("LOGNAME", ztrdup((str = getlogin()) && *str ? str : cached_username));
 
+#ifndef HAVE_PUTENV
     /* Copy the environment variables we are inheriting to dynamic *
      * memory, so we can do mallocs and frees on it.               */
-    num_env = arrlen(environ);
-    new_environ = (char **) zalloc(sizeof(char *) * (num_env + 1));
-    *new_environ = NULL;
+    envsize = sizeof(char *)*(1 + arrlen(environ));
+    new_environ = (char **) zalloc(envsize);
+    memcpy (new_environ, environ, envsize);
+    environ = new_environ;
+#endif
 
     /* Now incorporate environment variables we are inheriting *
-     * into the parameter hash table.                          */
-    for (envp = new_environ, envp2 = environ; *envp2; envp2++) {
+     * into the parameter hash table. Copy them into dynamic   *
+     * memory so that we can free them if needed               */
+    for (envp = envp2 = environ; *envp2; envp2++) {
 	for (str = *envp2; *str && *str != '='; str++);
 	if (*str == '=') {
 	    iname = NULL;
@@ -517,25 +524,22 @@ createparamtable(void)
 	    if (!idigit(**envp2) && isident(*envp2) && !strchr(*envp2, '[')) {
 		iname = *envp2;
 		if ((!(pm = (Param) paramtab->getnode(paramtab, iname)) ||
-		     !(pm->flags & PM_DONTIMPORT)) &&
-		    (pm = setsparam(iname, metafy(str + 1, -1, META_DUP))) &&
-		    !(pm->flags & PM_EXPORTED)) {
+		     !(pm->flags & PM_DONTIMPORT || pm->flags & PM_EXPORTED)) &&
+		    (pm = setsparam(iname, metafy(str + 1, -1, META_DUP)))) {
 		    *str = '=';
 		    pm->flags |= PM_EXPORTED;
-		    pm->env = *envp++ = ztrdup(*envp2);
-		    *envp = NULL;
-		    if (pm->flags & PM_SPECIAL) {
-			environ = new_environ;
-			pm->env = replenv(pm->env, getsparam(pm->nam),
-					  pm->flags);
-			environ = old_environ;
-		    }
+		    if (pm->flags & PM_SPECIAL)
+			pm->env = mkenvstr (pm->nam,
+					    getsparam(pm->nam), pm->flags);
+		    else
+			pm->env = ztrdup(*envp2);
+		    *envp++ = pm->env;
 		}
 	    }
 	    *str = '=';
 	}
     }
-    environ = new_environ;
+    *envp = '\0';
     opts[ALLEXPORT] = oae;
 
     pm = (Param) paramtab->getnode(paramtab, "HOME");
@@ -660,7 +664,6 @@ createparam(char *name, int flags)
 		 */
 		if (oldpm->env) {
 		    delenv(oldpm->env);
-		    zsfree(oldpm->env);
 		    oldpm->env = NULL;
 		}
 		paramtab->removenode(paramtab, name);
@@ -1489,7 +1492,7 @@ export_param(Param pm)
     else
 	val = pm->gets.cfn(pm);
     if (pm->env)
-	pm->env = replenv(pm->env, val, pm->flags);
+	pm->env = replenv(pm->nam, val, pm->flags);
     else {
 	pm->flags |= PM_EXPORTED;
 	pm->env = addenv(pm->nam, val, pm->flags);
@@ -2006,7 +2009,6 @@ unsetparam_pm(Param pm, int altflag, int exp)
     pm->unsetfn(pm, exp);
     if ((pm->flags & PM_EXPORTED) && pm->env) {
 	delenv(pm->env);
-	zsfree(pm->env);
 	pm->env = NULL;
     }
 
@@ -2824,8 +2826,7 @@ pipestatsetfn(Param pm, char **x)
 void
 arrfixenv(char *s, char **t)
 {
-    char **ep, *u;
-    int len_s;
+    char *u;
     Param pm;
 
     pm = (Param) paramtab->getnode(paramtab, s);
@@ -2838,24 +2839,47 @@ arrfixenv(char *s, char **t)
     if (pm->flags & PM_HASHELEM)
 	return;
     u = t ? zjoin(t, ':', 1) : "";
-    len_s = strlen(s);
-    for (ep = environ; *ep; ep++)
-	if (!strncmp(*ep, s, len_s) && (*ep)[len_s] == '=') {
-	    pm->env = replenv(*ep, u, pm->flags);
-	    return;
-	}
+    if (findenv(s, 0)) {
+	pm->env = replenv(s, u, pm->flags);
+	return;
+    }
     if (isset(ALLEXPORT))
 	pm->flags |= PM_EXPORTED;
     if (pm->flags & PM_EXPORTED)
 	pm->env = addenv(s, u, pm->flags);
 }
 
-/* Given *name = "foo", it searchs the environment for string *
- * "foo=bar", and returns a pointer to the beginning of "bar" */
+#ifndef HAVE_PUTENV
 
-/**/
-mod_export char *
-zgetenv(char *name)
+static int
+putenv(char *str)
+{
+    char **ep;
+    int num_env;
+
+
+    /* First check if there is already an environment *
+     * variable matching string `name'.               */
+    if (findenv (str, &num_env)) {
+	environ[num_env] = str;
+    } else {
+    /* Else we have to make room and add it */
+	num_env = arrlen(environ);
+	environ = (char **) zrealloc(environ, (sizeof(char *)) * (num_env + 2));
+
+	/* Now add it at the end */
+	ep = environ + num_env;
+	*ep = str;
+	*(ep + 1) = NULL;
+    }
+    return 0;
+}
+#endif
+
+#ifndef HAVE_GETENV
+
+static char *
+getenv(char *name)
 {
     char **ep, *s, *t;
  
@@ -2866,6 +2890,37 @@ zgetenv(char *name)
     }
     return NULL;
 }
+#endif
+
+/**/
+static int
+findenv (char *name, int *pos)
+{
+    char **ep, *eq;
+    int  nlen;
+
+
+    eq = strchr (name, '=');
+    nlen = eq ? eq - name : strlen (name);
+    for (ep = environ; *ep; ep++) 
+	if (!strncmp (*ep, name, nlen) && *((*ep)+nlen) == '=') {
+	    if (pos)
+		*pos = ep - environ;
+	    return 1;
+	}
+    
+    return 0;
+}
+
+/* Given *name = "foo", it searchs the environment for string *
+ * "foo=bar", and returns a pointer to the beginning of "bar" */
+
+/**/
+mod_export char *
+zgetenv(char *name)
+{
+    return getenv(name);
+}
 
 /**/
 static void
@@ -2881,27 +2936,51 @@ copyenvstr(char *s, char *value, int flags)
     }
 }
 
+static char *
+addenv_internal(char *name, char *value, int flags, int add)
+{
+    char *oldenv = 0, *newenv = 0, *env = 0;
+    int pos;
+
+    /* First check if there is already an environment *
+     * variable matching string `name'. If not, and   *
+     * we are not requested to add new, return        */
+    if (findenv (name, &pos))
+	oldenv = environ[pos];
+    else if (!add)
+	return NULL;
+
+    newenv = mkenvstr (name, value, flags);
+    if (putenv (newenv)) {
+	zsfree (newenv);
+	return NULL;
+    }
+    /*
+     * Under Cygwin we must use putenv() to maintain consistency.
+     * Unfortunately, current version (1.1.2) copies argument and may
+     * silently reuse exisiting environment string. This tries to
+     * check for both cases
+     */
+    if (findenv (name, &pos)) {
+	env = environ[pos];
+	if (env != oldenv)
+	    zsfree (oldenv);
+	if (env != newenv)
+	    zsfree (newenv);
+	return env;
+    }
+
+    return NULL; /* Cannot happen */
+}
+
 /* Change the value of an existing environment variable */
 
 /**/
 char *
-replenv(char *e, char *value, int flags)
-{
-    char **ep, *s;
-    int len_value;
-
-    for (ep = environ; *ep; ep++)
-	if (*ep == e) {
-	    for (len_value = 0, s = value;
-		 *s && (*s++ != Meta || *s++ != 32); len_value++);
-	    s = e;
-	    while (*s++ != '=');
-	    *ep = (char *) zrealloc(e, s - e + len_value + 1);
-	    s = s - e + *ep - 1;
-	    copyenvstr(s, value, flags);
-	    return *ep;
-	}
-    return NULL;
+replenv(char *name, char *value, int flags)
+{
+
+    return addenv_internal (name, value, flags, 0);
 }
 
 /* Given strings *name = "foo", *value = "bar", *
@@ -2934,28 +3013,7 @@ mkenvstr(char *name, char *value, int flags)
 char *
 addenv(char *name, char *value, int flags)
 {
-    char **ep, *s, *t;
-    int num_env;
-
-    /* First check if there is already an environment *
-     * variable matching string `name'.               */
-    for (ep = environ; *ep; ep++) {
-	for (s = *ep, t = name; *s && *s == *t; s++, t++);
-	if (*s == '=' && !*t) {
-	    zsfree(*ep);
-	    return *ep = mkenvstr(name, value, flags);
-	}
-    }
-
-    /* Else we have to make room and add it */
-    num_env = arrlen(environ);
-    environ = (char **) zrealloc(environ, (sizeof(char *)) * (num_env + 2));
-
-    /* Now add it at the end */
-    ep = environ + num_env;
-    *ep = mkenvstr(name, value, flags);
-    *(ep + 1) = NULL;
-    return *ep;
+    return addenv_internal (name, value, flags, 1);
 }
 
 /* Delete a pointer from the list of pointers to environment *
@@ -2971,8 +3029,10 @@ delenv(char *x)
 	if (*ep == x)
 	    break;
     }
-    if (*ep)
+    if (*ep) {
 	for (; (ep[0] = ep[1]); ep++);
+    }
+    zsfree(x);
 }
 
 /**/
@@ -3105,7 +3165,6 @@ scanendscope(HashNode hn, int flags)
 	    pm->ct = tpm->ct;
 	    if (pm->env) {
 		delenv(pm->env);
-		zsfree(pm->env);
 	    }
 	    pm->env = NULL;