Bash-4.3 distribution sources and documentation
[platform/upstream/bash.git] / lib / malloc / malloc.c
index 0b4c847..cd987c0 100644 (file)
@@ -1,24 +1,22 @@
 /* malloc.c - dynamic memory allocation for bash. */
 
-/*  Copyright (C) 1985, 1987, 1997 Free Software Foundation, Inc.
+/*  Copyright (C) 1985-2005 Free Software Foundation, Inc.
 
-    This program is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation; either version 2, or (at your option)
-    any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
+    This file is part of GNU Bash, the Bourne-Again SHell.
+    
+   Bash is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
 
-    You should have received a copy of the GNU General Public License
-    along with this program; if not, write to the Free Software
-    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA.
+   Bash is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
 
-In other words, you are welcome to use, share and improve this program.
-You are forbidden to forbid anyone else to use, share and improve
-what you give them.   Help stamp out software-hoarding!  */
+   You should have received a copy of the GNU General Public License
+   along with Bash.  If not, see <http://www.gnu.org/licenses/>.
+*/
 
 /*
  * @(#)nmalloc.c 1 (Caltech) 2/21/82
@@ -174,7 +172,7 @@ typedef union _malloc_guard {
 #define ASSERT(p) \
   do \
     { \
-      if (!(p)) xbotch((PTR_T)0, ERR_ASSERT_FAILED, __STRING(p), file, line); \
+      if (!(p)) xbotch((PTR_T)0, ERR_ASSERT_FAILED, CPP_STRING(p), file, line); \
     } \
   while (0)
 
@@ -221,7 +219,7 @@ typedef union _malloc_guard {
 
 static union mhead *nextf[NBUCKETS];
 
-/* busy[i] is nonzero while allocation of block size i is in progress.  */
+/* busy[i] is nonzero while allocation or free of block size i is in progress. */
 
 static char busy[NBUCKETS];
 
@@ -231,12 +229,12 @@ static int maxbuck;       /* highest bucket receiving allocation request. */
 
 static char *memtop;   /* top of heap */
 
-static unsigned long binsizes[NBUCKETS] = {
+static const unsigned long binsizes[NBUCKETS] = {
        8UL, 16UL, 32UL, 64UL, 128UL, 256UL, 512UL, 1024UL, 2048UL, 4096UL,
        8192UL, 16384UL, 32768UL, 65536UL, 131072UL, 262144UL, 524288UL,
        1048576UL, 2097152UL, 4194304UL, 8388608UL, 16777216UL, 33554432UL,
        67108864UL, 134217728UL, 268435456UL, 536870912UL, 1073741824UL,
-       2147483648UL, 4294967296UL-1
+       2147483648UL, 4294967295UL
 };
 
 /* binsizes[x] == (1 << ((x) + 3)) */
@@ -246,7 +244,7 @@ static unsigned long binsizes[NBUCKETS] = {
 static PTR_T internal_malloc __P((size_t, const char *, int, int));
 static PTR_T internal_realloc __P((PTR_T, size_t, const char *, int, int));
 static void internal_free __P((PTR_T, const char *, int, int));
-static PTR_T internal_memalign __P((unsigned int, size_t, const char *, int, int));
+static PTR_T internal_memalign __P((size_t, size_t, const char *, int, int));
 #ifndef NO_CALLOC
 static PTR_T internal_calloc __P((size_t, size_t, const char *, int, int));
 static void internal_cfree __P((PTR_T, const char *, int, int));
@@ -267,7 +265,7 @@ extern char *sbrk ();
 #endif /* !HAVE_DECL_SBRK */
 
 #ifdef SHELL
-extern int interrupt_immediately;
+extern int interrupt_immediately, running_trap;
 extern int signal_is_trapped __P((int));
 #endif
 
@@ -291,8 +289,11 @@ extern void mtrace_free __P((PTR_T, int, const char *, int));
 #if !defined (botch)
 static void
 botch (s, file, line)
+     const char *s;
+     const char *file;
+     int line;
 {
-  fprintf (stderr, "malloc: failed assertion: %s\n", s);
+  fprintf (stderr, _("malloc: failed assertion: %s\n"), s);
   (void)fflush (stderr);
   abort ();
 }
@@ -308,8 +309,8 @@ xbotch (mem, e, s, file, line)
      const char *file;
      int line;
 {
-  fprintf (stderr, "\r\nmalloc: %s:%d: assertion botched\r\n",
-                       file ? file : "unknown", line);
+  fprintf (stderr, _("\r\nmalloc: %s:%d: assertion botched\r\n"),
+                       file ? file : _("unknown"), line);
 #ifdef MALLOC_REGISTER
   if (mem != NULL && malloc_register)
     mregister_describe_mem (mem, stderr);
@@ -320,7 +321,8 @@ xbotch (mem, e, s, file, line)
 
 /* Coalesce two adjacent free blocks off the free list for size NU - 1,
    as long as we can find two adjacent free blocks.  nextf[NU -1] is
-   assumed to not be busy; the caller (morecore()) checks for this. */
+   assumed to not be busy; the caller (morecore()) checks for this. 
+   BUSY[NU] must be set to 1. */
 static void
 bcoalesce (nu)
      register int nu;
@@ -330,9 +332,10 @@ bcoalesce (nu)
   unsigned long siz;
 
   nbuck = nu - 1;
-  if (nextf[nbuck] == 0)
+  if (nextf[nbuck] == 0 || busy[nbuck])
     return;
 
+  busy[nbuck] = 1;
   siz = binsize (nbuck);
 
   mp2 = mp1 = nextf[nbuck];
@@ -343,22 +346,27 @@ bcoalesce (nu)
       mp1 = mp;
       mp = CHAIN (mp);
     }
+
   if (mp == 0)
-    return;
+    {
+      busy[nbuck] = 0;
+      return;
+    }
 
   /* OK, now we have mp1 pointing to the block we want to add to nextf[NU].
      CHAIN(mp2) must equal mp1.  Check that mp1 and mp are adjacent. */
   if (mp2 != mp1 && CHAIN(mp2) != mp1)
-    xbotch ((PTR_T)0, 0, "bcoalesce: CHAIN(mp2) != mp1", (char *)NULL, 0);
+    {
+      busy[nbuck] = 0;
+      xbotch ((PTR_T)0, 0, "bcoalesce: CHAIN(mp2) != mp1", (char *)NULL, 0);
+    }
 
 #ifdef MALLOC_DEBUG
   if (CHAIN (mp1) != (union mhead *)((char *)mp1 + siz))
-    return;    /* not adjacent */
-#endif
-
-#ifdef MALLOC_STATS
-  _mstats.tbcoalesce++;
-  _mstats.ncoalesce[nbuck]++;
+    {
+      busy[nbuck] = 0;
+      return;  /* not adjacent */
+    }
 #endif
 
   /* Since they are adjacent, remove them from the free list */
@@ -366,6 +374,12 @@ bcoalesce (nu)
     nextf[nbuck] = CHAIN (mp);
   else
     CHAIN (mp2) = CHAIN (mp);
+  busy[nbuck] = 0;
+
+#ifdef MALLOC_STATS
+  _mstats.tbcoalesce++;
+  _mstats.ncoalesce[nbuck]++;
+#endif
 
   /* And add the combined two blocks to nextf[NU]. */
   mp1->mh_alloc = ISFREE;
@@ -377,7 +391,7 @@ bcoalesce (nu)
 /* Split a block at index > NU (but less than SPLIT_MAX) into a set of
    blocks of the correct size, and attach them to nextf[NU].  nextf[NU]
    is assumed to be empty.  Must be called with signals blocked (e.g.,
-   by morecore()). */
+   by morecore()).  BUSY[NU] must be set to 1. */
 static void
 bsplit (nu)
      register int nu;
@@ -413,6 +427,12 @@ bsplit (nu)
   /* XXX might want to split only if nextf[nbuck] has >= 2 blocks free
      and nbuck is below some threshold. */
 
+  /* Remove the block from the chain of larger blocks. */
+  busy[nbuck] = 1;
+  mp = nextf[nbuck];
+  nextf[nbuck] = CHAIN (mp);
+  busy[nbuck] = 0;
+
 #ifdef MALLOC_STATS
   _mstats.tbsplit++;
   _mstats.nsplit[nbuck]++;
@@ -422,10 +442,6 @@ bsplit (nu)
   siz = binsize (nu);
   nblks = binsize (nbuck) / siz;
 
-  /* Remove the block from the chain of larger blocks. */
-  mp = nextf[nbuck];
-  nextf[nbuck] = CHAIN (mp);
-
   /* Split the block and put it on the requested chain. */
   nextf[nu] = mp;
   while (1)
@@ -439,8 +455,51 @@ bsplit (nu)
   CHAIN (mp) = 0;
 }
 
+/* Take the memory block MP and add it to a chain < NU.  NU is the right bucket,
+   but is busy.  This avoids memory orphaning. */
 static void
-block_signals (setp, osetp)
+xsplit (mp, nu)
+     union mhead *mp;
+     int nu;
+{
+  union mhead *nh;
+  int nbuck, nblks, split_max;
+  unsigned long siz;
+
+  nbuck = nu - 1;
+  while (nbuck >= SPLIT_MIN && busy[nbuck])
+    nbuck--;
+  if (nbuck < SPLIT_MIN)
+    return;
+
+#ifdef MALLOC_STATS
+  _mstats.tbsplit++;
+  _mstats.nsplit[nu]++;
+#endif
+
+  /* Figure out how many blocks we'll get. */
+  siz = binsize (nu);                  /* original block size */
+  nblks = siz / binsize (nbuck);       /* should be 2 most of the time */
+
+  /* And add it to nextf[nbuck] */
+  siz = binsize (nbuck);               /* XXX - resetting here */
+  nh = mp;
+  while (1)
+    {
+      mp->mh_alloc = ISFREE;
+      mp->mh_index = nbuck;
+      if (--nblks <= 0) break;
+      CHAIN (mp) = (union mhead *)((char *)mp + siz);
+      mp = (union mhead *)((char *)mp + siz);
+    }
+  busy[nbuck] = 1;
+  CHAIN (mp) = nextf[nbuck];
+  nextf[nbuck] = nh;
+  busy[nbuck] = 0;
+}
+
+void
+_malloc_block_signals (setp, osetp)
      sigset_t *setp, *osetp;
 {
 #ifdef HAVE_POSIX_SIGNALS
@@ -454,8 +513,8 @@ block_signals (setp, osetp)
 #endif
 }
 
-static void
-unblock_signals (setp, osetp)
+void
+_malloc_unblock_signals (setp, osetp)
      sigset_t *setp, *osetp;
 {
 #ifdef HAVE_POSIX_SIGNALS
@@ -487,9 +546,10 @@ lesscore (nu)                      /* give system back some memory */
   _mstats.nlesscore[nu]++;
 #endif
 }
-  
+
+/* Ask system for more memory; add to NEXTF[NU].  BUSY[NU] must be set to 1. */  
 static void
-morecore (nu)                  /* ask system for more memory */
+morecore (nu)
      register int nu;          /* size index to get more of  */
 {
   register union mhead *mp;
@@ -502,10 +562,14 @@ morecore (nu)                     /* ask system for more memory */
   /* Block all signals in case we are executed from a signal handler. */
   blocked_sigs = 0;
 #ifdef SHELL
-  if (interrupt_immediately || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
+#  if defined (SIGCHLD)
+  if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT) || signal_is_trapped (SIGCHLD))
+#  else
+  if (interrupt_immediately || running_trap || signal_is_trapped (SIGINT))
+#  endif
 #endif
     {
-      block_signals (&set, &oset);
+      _malloc_block_signals (&set, &oset);
       blocked_sigs = 1;
     }
 
@@ -528,7 +592,7 @@ morecore (nu)                       /* ask system for more memory */
     }
 
   /* Try to coalesce two adjacent blocks from the free list on nextf[nu - 1],
-     if we can, and we're withing the range of the block coalescing limits. */
+     if we can, and we're within the range of the block coalescing limits. */
   if (nu >= COMBINE_MIN && nu < COMBINE_MAX && busy[nu - 1] == 0 && nextf[nu - 1])
     {
       bcoalesce (nu);
@@ -592,7 +656,7 @@ morecore (nu)                       /* ask system for more memory */
 
 morecore_done:
   if (blocked_sigs)
-    unblock_signals (&set, &oset);
+    _malloc_unblock_signals (&set, &oset);
 }
 
 static void
@@ -734,7 +798,7 @@ internal_malloc (n, file, line, flags)              /* get a block */
   /* If not for this check, we would gobble a clobbered free chain ptr
      and bomb out on the NEXT allocate of this size block */
   if (p->mh_alloc != ISFREE || p->mh_index != nunits)
-    xbotch ((PTR_T)(p+1), 0, "malloc: block on free list clobbered", file, line);
+    xbotch ((PTR_T)(p+1), 0, _("malloc: block on free list clobbered"), file, line);
 
   /* Fill in the info, and set up the magic numbers for range checking. */
   p->mh_alloc = ISALLOC;
@@ -811,10 +875,10 @@ internal_free (mem, file, line, flags)
     {
       if (p->mh_alloc == ISFREE)
        xbotch (mem, ERR_DUPFREE,
-               "free: called with already freed block argument", file, line);
+               _("free: called with already freed block argument"), file, line);
       else
        xbotch (mem, ERR_UNALLOC,
-               "free: called with unallocated block argument", file, line);
+               _("free: called with unallocated block argument"), file, line);
     }
 
   ASSERT (p->mh_magic2 == MAGIC2);
@@ -833,25 +897,24 @@ internal_free (mem, file, line, flags)
 
   if (IN_BUCKET(nbytes, nunits) == 0)
     xbotch (mem, ERR_UNDERFLOW,
-           "free: underflow detected; mh_nbytes out of range", file, line);
+           _("free: underflow detected; mh_nbytes out of range"), file, line);
 
   ap += p->mh_nbytes;
   z = mg.s;
   *z++ = *ap++, *z++ = *ap++, *z++ = *ap++, *z++ = *ap++;  
   if (mg.i != p->mh_nbytes)
-    xbotch (mem, ERR_ASSERT_FAILED, "free: start and end chunk sizes differ", file, line);
+    xbotch (mem, ERR_ASSERT_FAILED, _("free: start and end chunk sizes differ"), file, line);
 
-#if 1
-  if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == memtop))
+#if GLIBC21
+  if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == sbrk (0)))
 #else
-  if (((char *)p + binsize(nunits) == memtop) && nunits >= LESSCORE_MIN)
+  if (nunits >= LESSCORE_MIN && ((char *)p + binsize(nunits) == memtop))
 #endif
     {
       /* If above LESSCORE_FRC, give back unconditionally.  This should be set
         high enough to be infrequently encountered.  If between LESSCORE_MIN
-        and LESSCORE_FRC, call lesscore if the bucket is marked as busy (in
-        which case we would punt below and leak memory) or if there's already
-        a block on the free list. */
+        and LESSCORE_FRC, call lesscore if the bucket is marked as busy or if
+        there's already a block on the free list. */
       if ((nunits >= LESSCORE_FRC) || busy[nunits] || nextf[nunits] != 0)
        {
          lesscore (nunits);
@@ -866,11 +929,14 @@ internal_free (mem, file, line, flags)
 #endif
 
   ASSERT (nunits < NBUCKETS);
-  p->mh_alloc = ISFREE;
 
   if (busy[nunits] == 1)
-    return;    /* this is bogus, but at least it won't corrupt the chains */
+    {
+      xsplit (p, nunits);      /* split block and add to different chain */
+      goto free_return;
+    }
 
+  p->mh_alloc = ISFREE;
   /* Protect against signal handlers calling malloc.  */
   busy[nunits] = 1;
   /* Put this block on the free list.  */
@@ -879,6 +945,7 @@ internal_free (mem, file, line, flags)
   busy[nunits] = 0;
 
 free_return:
+  ;            /* Empty statement in case this is the end of the function */
 
 #ifdef MALLOC_STATS
   _mstats.nmalloc[nunits]--;
@@ -935,7 +1002,7 @@ internal_realloc (mem, n, file, line, flags)
 
   if (p->mh_alloc != ISALLOC)
     xbotch (mem, ERR_UNALLOC,
-           "realloc: called with unallocated block argument", file, line);
+           _("realloc: called with unallocated block argument"), file, line);
 
   ASSERT (p->mh_magic2 == MAGIC2);
   nbytes = ALLOCATED_BYTES(p->mh_nbytes);
@@ -950,13 +1017,13 @@ internal_realloc (mem, n, file, line, flags)
      original number of bytes requested. */
   if (IN_BUCKET(nbytes, nunits) == 0)
     xbotch (mem, ERR_UNDERFLOW,
-           "realloc: underflow detected; mh_nbytes out of range", file, line);
+           _("realloc: underflow detected; mh_nbytes out of range"), file, line);
 
   m = (char *)mem + (tocopy = p->mh_nbytes);
   z = mg.s;
   *z++ = *m++, *z++ = *m++, *z++ = *m++, *z++ = *m++;
   if (mg.i != p->mh_nbytes)
-    xbotch (mem, ERR_ASSERT_FAILED, "realloc: start and end chunk sizes differ", file, line);
+    xbotch (mem, ERR_ASSERT_FAILED, _("realloc: start and end chunk sizes differ"), file, line);
 
 #ifdef MALLOC_WATCH
   if (_malloc_nwatch > 0)
@@ -1022,7 +1089,7 @@ internal_realloc (mem, n, file, line, flags)
 
 static PTR_T
 internal_memalign (alignment, size, file, line, flags)
-     unsigned int alignment;
+     size_t alignment;
      size_t size;
      const char *file;
      int line, flags;
@@ -1141,7 +1208,7 @@ sh_free (mem, file, line)
 
 PTR_T
 sh_memalign (alignment, size, file, line)
-     unsigned int alignment;
+     size_t alignment;
      size_t size;
      const char *file;
      int line;
@@ -1208,7 +1275,7 @@ free (mem)
 
 PTR_T
 memalign (alignment, size)
-     unsigned int alignment;
+     size_t alignment;
      size_t size;
 {
   return internal_memalign (alignment, size, (char *)NULL, 0, 0);