ClamAV is an open source (GPLv2) anti-virus toolkit.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 
clamav/libclamav/debug.c

4166 lines
94 KiB

/*
* Copyright (C) 2006 Nigel Horne <njh@clamav.net>
*
* 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 of the License, 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.
*
* 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., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*
* Debug package. Don't use it in a production environment unless you have
* a spare Cray. Bug reports about performance hit will be ignored unless
* attached with patches that don't impact on results
*/
#if HAVE_CONFIG_H
#include "clamav-config.h"
#endif
#ifdef CL_DEBUG
#ifdef C_LINUX
#define LINUX
#define ANSI
#endif
/*LINTLIBRARY*/
/*
* 1.1: Unix: _realloc gave bogue memcpy errors
* 1.2: Unix: now includes own malloc/free
* 1.3: ptr2slot added cache and sentinal as it was(is?) a bottleneck
* 1.4: Unix: added isptrok().
* Creation of FILE_ID.DIZ file.
* 1.5: Unix: Reduced platform dependancies. Ported to AIX Release 2.
* DOS: Removed cprintf - now uses stderr.
* 1.5.1: isptrok now checks for NULL pointer
* 1.5.2: ptr2slot: Installed direct cache
* 1.5.3: _realloc: Call clear_cache
* 2.0: Now debugs more than just malloc calls
* 2.1: Added checkembedded pointer and mmap stuff
* 2.1.1: Check strcpy arguments are different
* 2.1.2: mprotect caused mem faults in realloc
* 2.1.3: fixed strdup dumps not appearing
* 2.1.4: 2.1 fix now handles strdup messages better
* 2.1.5: checkembeddedptr() now checks it's argument is valid
* 2.2: _error: no longer repeats last message
* 2.3: Added underflowcheck
* Added total summary of leaks
* Added call to heapchk in exit routine leaks()
* 2.3.1: Reported to solaris: vsprintf format isn't const
* 2.4: Added information on leaked file descriptors
* 2.4.1: Need clear_cache when allocating new slot table if the
* slot table has moved
* 2.5: Support process watches
* 2.6: added last_free_size to report needless mallocs of something
* just freed
*
* LATER:
* Stats on number of alloc/free calls
*/
/*
* To compile on AIX 4.1.5
* cc -DAIX -DANSI -O2 -qroconst -U__STR__ -qro *.c
* With gcc on SunOs:
* gcc -ansi -Wshadow -Wall -Wwrite-strings -pipe -O2 -Dsun *.c
* With cc on SunOs
* cc -Dsun -O4 -pipe *.c
* For perpos:
* cc8 -O2 -c -DPERPOS *.c
* With cc on Solaris
* cc -DANSI -Dsolaris -fast -xO4 -fd -mc -v -Xc -xCC -xstrconst *.c
* With gcc on linux
* gcc -DLINUX -pipe -Wshadow -Wwrite-strings -Wall -O2 -c debug.c
*/
#ifdef lint
#define void char
#endif
#ifdef solaris
#ifndef sun
#define sun
#endif
#define WATCH
#endif
#if defined(sun) || defined(AIX) || defined(LINUX) || defined(_HPUX_SOURCE)
#ifdef MAP_ANONYMOUS
#define MPROTECT /* has the mprotect system call */
#endif
#endif
#ifdef WATCH
#undef MPROTECT
#include <procfs.h>
static int watchfd = -1;
#endif
#include "debug.h"
#ifdef UNIX
static void none(const caddr_t addr, size_t size);
static void ro(const caddr_t addr, size_t size);
static void rw(const caddr_t addr, size_t size);
static void slots_readonly(void);
static void slots_rw(void);
#else
#define none(addr, size)
#define ro(addr, size)
#define rw(addr, size)
#define slots_readonly()
#define slots_rw()
#endif
#ifdef MPROTECT
#include <sys/mman.h>
#endif
/*
* If MAX_STACK_DEPTH is defined we try a stack trace.
*/
#if defined(sun) && !defined(solaris)
#define MAX_STACK_DEPTH 128
#endif
#ifdef __sparc
#define getpagesize() 4096
#endif
#ifdef MAX_STACK_DEPTH
#ifdef solaris
#include <nlist.h>
#include <sys/exechdr.h>
#else
#include <a.out.h>
#endif
#ifdef sun
#include <stab.h>
#endif
#include <sys/file.h>
#endif
#define NJH_DEBUG
#ifndef min
#define min(a,b) (((a) < (b)) ? (a) : (b))
#endif
#ifdef MAX_STACK_DEPTH
/* for ultrix 0x38, 4.3 bsd 0x3d, other? */
#ifdef vax
#define CRT0_ADDRESS 0x3d
#endif
#ifdef sun
#define CRT0_ADDRESS 0x204c
#endif
#ifdef M_XENIX
#define CRT0_ADDRESS 0xD4 /* SCO Unix */
#endif
#ifdef mips
/*#define CRT0_ADDRESS 0x0 /* to be filled in later */
#endif
unsigned mp_root_address = CRT0_ADDRESS;
#endif /*MAX_STACK_DEPTH*/
/*#undef PP2WIN*/
#if defined(M_I86MM) || defined(M_I86SM)
static void *lastfreed = (void *)-1;
#else
static void *lastfreed = (void *)-1L;
#endif
static int pagesz; /* page size */
#ifndef _NFILE
#define _NFILE NOFILE
#endif
static int fds[_NFILE];
#ifdef NJH_DEBUG
#ifdef PERPOS
#define size_t long
#define pid_t int
#define INT_MAX MAXINT
#define UINT_MAX (0xFFFFFFFF)
char *getenv();
#endif
/*
* LATER:
* Replace s_ptr with a pointer to a structure that contains
* long underflowcheck;
* void *data;
* long overflowcheck;
* free will check these values as will db_heapchk and blkchk, and where
* possible access to these areas will be restricted. This would add an underrun
* check
* ptr2slot will need modifying, as will anything that looks for 'B'
*/
typedef struct _slotstr {
#ifdef UNDERFLOW /* meaningless code! */
char s_underflowcheck;
#endif
void *s_ptr; /* the allocated area */
size_t s_size; /* its size */
unsigned int s_blkno; /* program block reference number */
unsigned int s_freed:1; /* whether it's been freed yet */
const char *s_file;
unsigned int s_line;
#ifdef MAX_STACK_DEPTH
caddr_t s_history[MAX_STACK_DEPTH];
#endif
} SLOT;
#define MAXSLOT 1024L
#if defined(sun) && !defined(ANSI)
#define atexit(f) on_exit(f)
#endif
#ifdef PERPOS
#define atexit(f)
static void cdecl leaks();
int
per_exit(status)
{
leaks();
exit(status);
}
#endif
static unsigned long maxslots;
bool check_for_leaks;
static unsigned int in_db;
#ifdef MSDOS
typedef char *caddr_t;
static SLOT huge *slots;
#else
static SLOT *slots;
#endif
static unsigned long slotc;
static unsigned int blkno;
#define wsize sizeof(unsigned int) /* 386BSD stuff */
#define wmask (wsize - 1) /* 386BSD stuff */
#ifndef ANSI
static void _error();
static void *_malloc();
static void *_calloc();
static void *_realloc();
static void _free();
static void cdecl leaks();
#ifdef MAX_STACK_DEPTH
static void mprof();
static void st_read();
#endif
static const char *symname();
#else
#ifdef solaris
static void _error(const char *file, unsigned int line, const caddr_t *history, char *format, ...);
#else
static void _error(const char *file, unsigned int line, const caddr_t *history, const char *format, ...);
#endif
static void *_malloc(size_t size, const char *file, unsigned int line);
static void *_calloc(size_t nel, size_t size, const char *file, unsigned int line);
static void *_realloc(char *ptr, size_t size, const char *file, unsigned int line);
static void _free(char *ptr, const char *file, unsigned int line);
static void cdecl leaks(void);
#ifdef MAX_STACK_DEPTH
static void mprof(SLOT *sp);
static void st_read(void);
static const char *symname(caddr_t pc);
#endif
#endif
#ifndef MSDOS
#undef strcmp
#undef memcpy
#undef malloc
#undef calloc
#undef realloc
#undef strdup
#undef strcpy
#undef free
#undef heapchk
#undef alloca
#undef memset
#undef strcat
#endif
#ifdef MSDOS
static void *real_memcpy(register void *m1, register const void *m2, register size_t n);
static void *real_malloc(size_t nbytes);
static void real_free(void *cp);
static void *real_realloc(void *cp, size_t nbytes);
#else
#ifdef ANSI
static void *real_memcpy();
static void *real_malloc();
static void *real_realloc();
#else
static char *real_memcpy();
static char *real_malloc();
static char *real_realloc();
#endif /*ANSI*/
static void real_free();
#endif /*MSDOS*/
static void
#ifdef ANSI
#ifdef solaris
_error(const char *file, unsigned int line, const caddr_t *history, char *format, ...) /* stdargs method */
#else
_error(const char *file, unsigned int line, const caddr_t *history, const char *format, ...) /* stdargs method */
#endif
#else
_error(file, line, history, format, va_alist) /* varargs method */
char *file;
unsigned int line;
char *format;
const caddr_t *history;
va_dcl
#endif
{
va_list v;
char thismessage[160]; /* 2.2 */
static char lastmessage[160];
in_db++;
#ifdef MSDOS
if(file /*&& (_fstrcmp(file, __FILE__) != 0)*/)
#else
if(file /*&& (strcmp(file, __FILE__) != 0)*/)
#endif
fprintf(stderr, "%u of %s: ", line, file);
#ifdef MAX_STACK_DEPTH
else {
register int i;
register const char *sym;
SLOT sp;
register const char **fn;
/*
* Functions to ignore in stack tracing
*/
static const char *ignore_functions[] = {
"db_callocchk",
"db_mallocchk",
"db_strdupchk",
"db_setname",
"calloc",
"malloc",
"etext",
NULL
};
st_read();
if(history == NULL) {
mprof(&sp);
history = sp.s_history;
}
for(i = 0; (history[i] > (caddr_t)mp_root_address) && (i < MAX_STACK_DEPTH); i++)
if(sym = symname(history[i])) {
if(*sym) {
for(fn = ignore_functions; *fn; fn++)
if(strcmp(sym, *fn) == 0)
break;
if(*fn)
continue;
fprintf(stderr, "Around %s: ", sym);
/*if(strcmp(sym, "main") == 0)*/
break;
}
} else
break;
}
#endif /*MAX_STACK_DEPTH*/
#if defined(__STDC__) || defined(_MSC_VER) || defined(ANSI)
va_start(v, format);
#else
va_start(v);
#endif
vsprintf(thismessage, format, v);
va_end(v);
if(file || (strcmp(lastmessage, thismessage) != 0)) {
fputs(thismessage, stderr);
putc('\n', stderr);
strcpy(lastmessage, thismessage);
}
in_db--;
}
#ifdef UNIX
/*
* Does ptr point to a valid array of at least fd bytes
*/
static bool
isptrok(ptr, size)
const void *ptr;
size_t size;
{
static int fd;
if(size == 0)
return(true);
if(ptr == NULL) {
_error(NULL, 0, NULL, "Null pointer to %u bytes", size);
return(false);
}
if(in_db)
return(true);
if(fd == 0)
fd = open("/dev/null", O_WRONLY);
if(fd > 0) {
register int i;
register const char *cptr;
if(size == INT_MAX)
size = 1;
#if 0
if((write(fd, ptr, size) < 0) && (errno == EFAULT)) {
_error(NULL, 0, NULL, "Invalid pointer to %u bytes", size);
return(false);
}
#else
cptr = (const char *)ptr;
for(i = 0; i < size; i++)
if((write(fd, cptr++, 1) < 0) && (errno == EFAULT)) {
if(i == 0)
_error(NULL, 0, NULL, "Invalid Pointer to %u bytes", size);
else
_error(NULL, 0, NULL, "Pointer to %u bytes only points to %u bytes", size, i);
return(false);
}
#endif
}
return(true);
}
#else
static bool
isptrok(ptr, size)
const void *ptr;
size_t size;
{
if(ptr == NULL) {
#ifdef M_I86LM
_error(NULL, 0, NULL, "Null pointer to %lu bytes", (unsigned long)size);
#else
_error(NULL, 0, NULL, "Null pointer to %u bytes", (unsigned int)size);
#endif
return(false);
}
return(true);
}
#endif
#ifdef MSDOS
#define CACHE_SIZE 32
#define CACHE_SHIFT 5
#define CACHE_MASK 0x1F
#else
#define CACHE_SIZE 64
#define CACHE_SHIFT 6
#define CACHE_MASK 0x3F
#endif
/*#define CACHE_TRACE*/
#if CACHE_SIZE
static struct cache {
unsigned long index;
bool isvalid;
#ifdef M_I86LM
unsigned long tag;
#else
unsigned int tag;
#endif
#ifdef CACHE_TRACE
unsigned int hits;
unsigned int misses;
#endif
} cache[CACHE_SIZE];
static void
clear_cache()
{
register struct cache *c;
for(c = cache; c < &cache[CACHE_SIZE]; c++)
c->isvalid = false;
}
#else
static void
clear_cache()
{
}
#endif
/*
* NJH - get an sp from a pointer
*/
#ifdef MSDOS
static SLOT huge *
#else
static SLOT *
#endif
#ifdef ANSI
ptr2slot(const void *ptr)
#else
ptr2slot(ptr)
void *ptr;
#endif
{
#ifdef M_I86LM
register unsigned long tag;
#else
register unsigned int tag;
#endif
#ifdef MSDOS
register SLOT huge *sp;
#else
register SLOT *sp;
#endif
register void *optr;
#if CACHE_SIZE
register struct cache *cacheelem;
#endif
if(slotc) {
#if CACHE_SIZE
/*
* cache, but not if NULL - it may have been allocated
* since the last call
*/
#ifdef M_I86LM
cacheelem = &cache[(unsigned long)ptr & CACHE_MASK];
tag = (unsigned long)ptr >> CACHE_SHIFT;
#else
cacheelem = &cache[(unsigned int)ptr & CACHE_MASK];
tag = (unsigned int)ptr >> CACHE_SHIFT;
#endif
if(cacheelem->isvalid && (tag == cacheelem->tag) && slots[cacheelem->index].s_ptr) {
#ifdef assert
assert(slots[cacheelem->index].s_ptr == ptr);
#endif
#ifdef CACHE_TRACE
cacheelem->hits++;
#endif
return(&slots[cacheelem->index]);
}
#ifdef CACHE_TRACE
/*
* Strictly speaking all times we're here counts as a miss,
* but I'm really interested in looking for hot-spots,
* not the initial filling of the cache
*/
if(cacheelem->isvalid)
cacheelem->misses++;
#endif
sp = slots;
if(sp->s_ptr == ptr) {
cacheelem->tag = tag;
cacheelem->index = 0L;
cacheelem->isvalid = true;
return(sp);
}
#else
sp = slots;
if(sp->s_ptr == ptr)
return(sp);
#endif /*CACHE_SIZE*/
optr = slots[0].s_ptr;
slots_rw();
slots[0].s_ptr = (void *)ptr; /* post a sentinal */
/* start search at end - most likely that a match'll be there */
for(sp = &slots[slotc - 1L]; sp->s_ptr != ptr; --sp)
;
slots[0].s_ptr = optr;
slots_readonly();
#if CACHE_SIZE
if(sp != slots) {
cacheelem->tag = tag;
cacheelem->index = sp - slots;
cacheelem->isvalid = true;
return(sp);
}
#else
if(sp != slots)
return(sp);
#endif
}
return(NULL);
}
#ifdef MSDOS
#ifndef PP2WIN
static void huge *
hmemcpy(register void huge *m1, register const void huge *m2, unsigned long l)
{
register unsigned char huge *dest = m1;
register const unsigned char huge *source = m2;
while(l--)
*dest++ = *source++;
return(m1);
}
static void huge *
hmemset(register void huge *s, unsigned char ch, unsigned long l)
{
register unsigned char huge *t;
t = s;
while(l--)
*t++ = (unsigned char)ch;
return(s);
}
#endif
#endif
static size_t last_free_size;
/*
* _malloc - wrapper around malloc. Warns if unusual size given, or the
* real malloc returns a NULL pointer. Returns a pointer to the
* malloc'd area
*/
static void *
#ifdef ANSI
_malloc(size_t size, const char *file, unsigned int line)
#else
_malloc(size, file, line)
size_t size;
char *file;
unsigned int line;
#endif
{
#ifdef MSDOS
#ifdef PP2WIN
static HGLOBAL hslots;
register HGLOBAL hnewslots;
#endif
register SLOT huge *sp;
#else
register SLOT *sp, *oldslots;
#endif
register char *ptr;
register int i;
static bool hasprinted;
bool dofds;
if(!hasprinted) {
check_for_leaks = hasprinted = true;
atexit(leaks);
dofds = 1;
} else
dofds = 0;
if(size == 0) {
_error(file, line, NULL, "malloc: 0 bytes wanted");
/*return(NULL);*/
} else if(size == last_free_size) {
in_db++;
for(sp = slots; sp != &slots[slotc]; sp++)
if(sp->s_freed && sp->s_file && file &&
(strcmp(sp->s_file, file) == 0) &&
(sp->s_size == last_free_size)) {
_error(file, line, NULL, "malloc: %d bytes wanted which was freed at %d of %s", size, sp->s_line, sp->s_file);
break;
}
in_db--;
}
#ifdef PP2WIN
if((ptr = (char *)rhpaca(1, size + 1)) == (char *) NULL) {
_error(file, line, NULL, "malloc: unable to malloc %u bytes", size);
return(NULL);
}
#elif defined(M_I86LM)
if((ptr = (char *)_fmalloc(size + 1)) == (char *) NULL) {
_error(file, line, NULL, "malloc: unable to malloc %u bytes", size);
return(NULL);
}
#elif defined(MSDOS) && (defined(M_I86MM) || defined(M_I86SM))
if((ptr = (char *)_nmalloc(size + 1)) == (char *) NULL) {
_error(file, line, NULL, "malloc: unable to malloc %u bytes", size);
return(NULL);
}
#elif defined(MSDOS)
if((ptr = (char *)malloc(size + 1)) == (char *) NULL) {
_error(file, line, NULL, "malloc: unable to malloc %u bytes", size);
return(NULL);
}
#elif defined(MPROTECT)
if(!in_db) {
/*
* Wasteful of memory - but it allows mprotect to work
*/
unsigned int alignment = (pagesz) ? pagesz : getpagesize();
size_t nsize;
#define round_to(i, nearest) \
((((i) + (nearest) - 1) / (nearest)) * (nearest))
nsize = round_to(size + 1, sizeof(int)) + alignment;
ptr = real_malloc(nsize);
if(ptr == (char *)NULL) {
_error(file, line, NULL, "malloc: unable to malloc %u bytes", size);
return(NULL);
}
mprotect(ptr, nsize, PROT_READ|PROT_WRITE);
ptr = (char *)round_to((unsigned int)ptr, alignment);
} else if((ptr = (char *)real_malloc(size + 1)) == (char *) NULL) {
_error(file, line, NULL, "malloc: unable to malloc %u bytes", size);
return(NULL);
} else
mprotect(ptr, size + 1, PROT_READ|PROT_WRITE);
#else
if((ptr = (char *)real_malloc(size + 1)) == (char *) NULL) {
_error(file, line, NULL, "malloc: unable to malloc %u bytes", size);
return(NULL);
}
#ifdef WATCH
rw(ptr, size + 1);
#endif
#endif
ptr[size] = 'B'; /* crude bounds check */
ro(&ptr[size], 1);
if(slots == NULL) {
in_db++;
#ifdef MSDOS
#ifdef PP2WIN
hslots = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_ZEROINIT,
MAXSLOT * sizeof(SLOT));
slots = (SLOT huge *)GlobalLock(hslots);
#else
slots = (SLOT huge *)halloc(MAXSLOT, sizeof(SLOT));
hmemset(slots, '\0', MAXSLOT * sizeof(SLOT));
#endif
#else
slots = (SLOT *)real_malloc(MAXSLOT * sizeof(SLOT));
memset(slots, '\0', MAXSLOT * sizeof(SLOT));
#endif
maxslots = MAXSLOT;
clear_cache();
in_db--;
slots_rw();
}
sp = ptr2slot(ptr);
if(sp == NULL) {
if(slotc == maxslots - 1L) {
/* maybe we can salvage something */
for(sp = slots; sp != &slots[slotc]; sp++)
if(sp->s_ptr == NULL)
break; /* probably realloced */
if(sp == &slots[slotc]) {
/* run out of slots */
#ifdef MSDOS
#ifdef PP2WIN
hnewslots = GlobalAlloc(GMEM_MOVEABLE|GMEM_NODISCARD|GMEM_ZEROINIT,
(maxslots + MAXSLOT) * sizeof(SLOT));
if(hnewslots == (HGLOBAL)NULL) {
rhpfree(ptr);
rhpw_mbox("can't increase maxslots", "Callbook on disc");
return(NULL);
}
sp = (SLOT huge *)GlobalLock(hnewslots);
hmemcpy(sp, slots, maxslots * sizeof(SLOT));
GlobalUnlock(hslots);
GlobalFree(hslots);
slots = sp;
hslots = hnewslots;
#else /*!PP2WIN*/
if(sp = (SLOT huge *)halloc(maxslots + MAXSLOT, sizeof(SLOT)))
hmemcpy(sp, slots, maxslots * sizeof(SLOT));
else {
#ifdef M_I86LM
_ffree(ptr);
#else
_nfree(ptr);
#endif
_error(file, line, NULL, "can't increase maxslots to %lu", maxslots);
return(NULL);
}
hfree(slots);
slots = sp;
hmemset(&slots[maxslots], '\0', MAXSLOT * sizeof(SLOT));
#endif /*PP2WIN*/
maxslots += MAXSLOT;
#else /*!MSDOS*/
slots_rw();
maxslots += MAXSLOT;
oldslots = slots;
slots = (SLOT *)real_realloc((char *)slots, maxslots * sizeof(SLOT));
slots_rw();
in_db++;
memset(&slots[maxslots - MAXSLOT], '\0', MAXSLOT * sizeof(SLOT));
in_db--;
#endif
sp = &slots[slotc++];
/*
* 2.4.1 fix
*/
#ifdef UNIX
if(slots != oldslots) {
clear_cache();
none((caddr_t)oldslots, (maxslots - MAXSLOT) * sizeof(SLOT));
}
#else
clear_cache();
#endif
}
} else
sp = &slots[slotc++];
} else if(!sp->s_freed)
_error(file, line, NULL, "malloc: malloc returned a non-freed pointer");
#ifdef MAX_STACK_DEPTH
if(!in_db)
mprof(sp);
#endif
if((unsigned)(sp - slots) > maxslots)
fputs("sp overflow\n", stderr);
slots_rw();
sp->s_size = size;
sp->s_freed = false;
sp->s_ptr = ptr;
sp->s_blkno = blkno;
sp->s_file = file;
sp->s_line = line;
#ifdef UNDERFLOW
sp->s_underflowcheck = 'B';
#endif
slots_readonly();
if(dofds)
/*
* Leave this to the last possible moment so that /dev/null
* and /dev/zero have already been opened. That should stop us
* getting bogus fd leak errors
*/
for(i = 0; i < _NFILE; i++)
fds[i] = ((lseek(i, (off_t)0, SEEK_CUR) >= 0) || (errno != EBADF));
return(ptr);
}
/*
* _calloc - wrapper for calloc. Calls _malloc to allocate the area, and
* then sets the contents of the area to NUL bytes. Returns its address.
*/
static void *
#ifdef ANSI
_calloc(size_t nel, size_t size, const char *file, unsigned int line)
#else
_calloc(nel, size, file, line)
size_t nel, size;
char *file;
unsigned int line;
#endif
{
register size_t tot;
register void *ptr;
tot = nel * size;
if(ptr = _malloc(tot, file, line)) {
in_db++;
memset(ptr, '\0', tot);
in_db--;
}
return(ptr);
}
/*
* _realloc - wrapper for realloc. Checks area already alloc'd and
* not freed. Returns its address
*/
static void *
#ifdef ANSI
_realloc(char *ptr, size_t size, const char *file, unsigned int line)
#else
_realloc(ptr, size, file, line)
char *ptr;
size_t size;
char *file;
unsigned int line;
#endif
{
register char *optr = ptr;
#ifdef MSDOS
register SLOT huge *sp, const huge *nsp;
#ifdef PP2WIN
register char *nptr;
#endif
#else
register SLOT *sp, *nsp;
#endif
if(slots == NULL)
_error(file, line, NULL, "realloc: called before any alloc");
sp = ptr2slot(ptr);
if(sp == NULL) {
_error(file, line, NULL, "realloc: realloc on unallocated area");
return(NULL);
}
if(sp->s_freed) {
_error(file, line, NULL, "realloc: realloc on freed area (block freed at %d of %s)", sp->s_line, sp->s_file);
return(NULL);
}
in_db++;
if(ptr[sp->s_size] != 'B') /* bounds check */
_error(file, line, NULL, "realloc: overflow of %u bytes", sp->s_size);
#ifdef UNDERFLOW
else if(sp->s_underflowcheck != 'B')
_error(file, line, NULL, "realloc: underflow of %u bytes", sp->s_size);
#endif
else if(sp->s_size == size) {
if(sp->s_file)
_error(file, line, NULL, "realloc: want same size %u bytes as %d of %s",
size, sp->s_line, sp->s_file);
else
_error(file, line, NULL, "realloc: want same size %u bytes", size);
} else if(size == 0)
_error(file, line, NULL, "realloc: 0 bytes wanted");
#ifdef PP2WIN
else if((nptr = rhpaca(1, size + 1)) == (char *)NULL)
_error(file, line, NULL, "realloc: realloc failure %u bytes", size);
#elif defined(M_I86LM)
else if((ptr = _frealloc(ptr, size + 1)) == (char *)NULL)
_error(file, line, NULL, "realloc: realloc failure %u bytes", size);
#elif defined(MSDOS) && (defined(M_I86MM) || defined(M_I86SM))
else if((ptr = _nrealloc(ptr, size + 1)) == (char *)NULL)
_error(file, line, NULL, "realloc: realloc failure %u bytes", size);
#else
else if((ptr = real_realloc(ptr, size + 1)) == (char *)NULL)
_error(file, line, NULL, "realloc: realloc failure %u bytes", size);
#endif
else {
if(ptr != optr)
if(nsp = ptr2slot(optr))
none(optr, nsp->s_size);
rw(ptr, size);
#ifdef PP2WIN
real_memcpy(nptr, ptr, min(sp->s_size, size));
rhpfree(ptr); /* don't call freechk here */
ptr[size - 1] = '\0';
ptr = nptr;
#endif
/*
* If it's a completely new pointer, mark that
* the old pointer is no longer used.
*
* If we're still using the old pointer to save
* reshuffle/memcpy in real_realloc, make sure that the new
* pointer is a sensible one
*/
slots_rw();
for(nsp = slots; nsp != &slots[slotc]; nsp++)
if((nsp->s_ptr == optr) && (ptr != optr))
nsp->s_freed = true;
else if((nsp->s_ptr == ptr) && (nsp != sp)) {
if(!nsp->s_freed)
_error(file, line, NULL, "realloc: returns pointer already in use");
nsp->s_ptr = NULL;
}
/* Clearing the entire cache is OTT but safe */
clear_cache();
rw(&ptr[size], 1);
ptr[size] = 'B'; /* crude bounds check */
ro(&ptr[size], 1);
sp->s_ptr = ptr;
sp->s_size = size;
sp->s_blkno = blkno;
sp->s_freed = false;
sp->s_file = file;
sp->s_line = line;
#ifdef UNDERFLOW
sp->s_underflowcheck = 'B';
#endif
slots_readonly();
}
in_db--;
return(ptr);
}
#ifdef sun
static const SLOT *
checkembeddedptr(memblock, size)
const void *memblock;
int size;
{
(void)isptrok(memblock, size);
return(NULL);
}
#else
#ifdef MSDOS
static const SLOT huge *
#else
static const SLOT *
#endif
#ifdef ANSI
checkembeddedptr(const void *memblock, int size)
#else
checkembeddedptr(memblock, size)
char *memblock;
int size;
#endif
{
#ifdef ANSI
const void **p;
#else
void **p;
#endif
#ifdef MSDOS
register const SLOT huge *sp;
#else
register const SLOT *sp;
#endif
#ifdef sun
/* Need to word align the pointer - sigh */
{
unsigned long t;
t = (unsigned long)memblock;
if(t & wmask) {
t &= ~wmask;
p = (void **)t;
p += sizeof(void *);
size -= sizeof(void *);
} else
p = (void **)memblock;
}
#else
#ifdef ANSI
p = (const void **)memblock;
#else
p = (void **)memblock;
#endif
#endif
if(!isptrok(p, size)) /* 2.1.5 */
return(NULL);
while(size > 0) {
if(*p && (sp = ptr2slot(*p)))
if(!sp->s_freed)
return(sp);
p++; /* 2.1.5 - was p += sizeof(void *) */
size -= sizeof(void *);
}
return(NULL);
}
#endif
/* 2.1.3 added const */
static const unsigned int strdupline = __LINE__;
/*
* _free - wrapper for free. Loop through allocated slots, until you
* find the one corresponding to pointer. If none, then it's an attempt
* to free an unallocated area. If it's already freed, then tell user.
*/
static void
#ifdef ANSI
_free(char *memblock, const char *file, unsigned int line)
#else
_free(memblock, file, line)
char *memblock;
char *file;
unsigned int line;
#endif
{
#ifdef MSDOS
register SLOT huge *sp;
register const SLOT huge *ssp;
#else
register SLOT *sp;
register const SLOT *ssp;
#endif
sp = ptr2slot(memblock);
in_db++;
if(sp == NULL) {
_error(file, line, NULL, "free: free not previously malloc'd");
in_db--;
return;
}
if(sp->s_freed)
_error(file, line, NULL, "free after previous freeing %u bytes", sp->s_size);
else if(memblock[sp->s_size] != 'B') /* bounds check */
_error(file, line, NULL, "free: overflow of %u bytes allocated %u of %s", sp->s_size,
sp->s_line, sp->s_file);
#ifdef UNDERFLOW
else if(sp->s_underflowcheck != 'B')
_error(file, line, NULL, "free: underflow of %u bytes allocated %u of %s", sp->s_size,
sp->s_line, sp->s_file);
#endif
else {
/*
* LATER: warn if the block to be freed contains any pointers
* which haven't been freed. Warn if it does, otherwise
* clear the area
*/
rw(memblock, sp->s_size);
ssp = NULL;
if(file /*&& (strcmp(file, __FILE__) != 0)*/) {
/*
* Find the first part of the memory that was
* actually used at some time
*/
register char *ptr;
register size_t nbytes = sp->s_size;
for(ptr = memblock; nbytes && (*ptr == '\0'); ptr++)
nbytes--;
/*
* The checkembeddedptr code produces a lot of false
* positives when freeing a linked list, even
* if the code is correct.
* TODO: only warn on first embeddedpoiner free
*/
if(nbytes == 0) {
if(sp->s_file)
_error(file, line, NULL, "free: %u bytes (allocated %d of %s) may never have been used",
sp->s_size, sp->s_line, sp->s_file);
else
_error(file, line, NULL, "free: %u bytes may never have been used", sp->s_size);
/*} else if(ssp = checkembeddedptr(ptr, nbytes)) {*/
} else if(ssp = checkembeddedptr(memblock, sp->s_size)) {
if(ssp->s_file) {
if((strcmp(ssp->s_file, __FILE__) == 0) && (ssp->s_line == strdupline))
_error(file, line, NULL, "free: pointer possibly points to data which contains unfree strdup: \"%s\"", (const char *)ssp->s_ptr);
else
_error(file, line, NULL, "free: pointer possibly points to data which contains unfree pointer from %d of %s", ssp->s_line, ssp->s_file);
} else
_error(file, line, NULL, "free: pointer possibly points to data which contains unfree pointer to %u bytes", ssp->s_size);
}
} else if(ssp = checkembeddedptr(memblock, sp->s_size)) {
if(ssp->s_file) {
if((strcmp(ssp->s_file, __FILE__) == 0) && (ssp->s_line == strdupline))
_error(NULL, 0L, NULL, "free: pointer possibly points to data which contains unfree strdup: \"%s\"", (const char *)ssp->s_ptr);
else
_error(NULL, 0, NULL, "free: pointer possibly points to data which contains unfree pointer from %d of %s", ssp->s_line, ssp->s_file);
} else
_error(NULL, 0, NULL, "free: pointer possibly points to data which contains unfree pointer to %u bytes", ssp->s_size);
}
#ifdef PP2WIN
rhpfree(memblock);
#elif defined(M_I86LM)
(void)_ffree(memblock);
#elif defined(MSDOS) && (defined(M_I86MM) || defined(M_I86SM))
(void)_nfree(memblock);
#else
(void)real_free(memblock);
#endif
/* if(sp->s_file[0])
sp->s_file[0] = '\0'; */
if(ssp == NULL) {
#ifdef MSDOS
memset(memblock, '\0', sp->s_size); /* stop re-use of the area */
#else
/*
* Also stop access to the 'B' byte
*/
/*rw(&memblock[sp->s_size], 1);*/
none(memblock, sp->s_size + 1);
#endif
}
}
slots_rw();
sp->s_freed = true;
sp->s_file = file;
sp->s_line = line;
slots_readonly();
last_free_size = sp->s_size;
in_db--;
}
void _pascal /* NJH */
#ifdef ANSI
db_heapchk(const char *file, int line)
#else
db_heapchk(file, line)
char *file;
int line;
#endif
{
#ifdef MSDOS
register SLOT huge *sp;
#else
register SLOT *sp;
#endif
#if defined(_MSC_VER) && (_MSC_VER >= 600)
#ifndef PP2WIN
if(_heapchk() != _HEAPOK)
_error(file, line, NULL, "Possible memory corruption");
#endif
#endif
for(sp = slots; sp != &slots[slotc]; sp++)
if((!sp->s_freed) && sp->s_file) {
register const char *ptr = sp->s_ptr;
if(ptr == NULL) {
_error(file, line, NULL, "Unexpected NULL pointer");
return;
}
#ifndef MSDOS
rw(&ptr[sp->s_size], 1);
#endif
if(!isptrok(ptr, sp->s_size))
_error(file, line, NULL, "Pointer to %u bytes from %d of %s has become invalid", sp->s_size, sp->s_line, sp->s_file);
#ifdef UNDERFLOW
else if((ptr[sp->s_size] != 'B') || (sp->s_underflowcheck != 'B'))
#else
else if(ptr[sp->s_size] != 'B')
#endif
_error(file, line, NULL, "Corruption of %u bytes from %d of %s", sp->s_size, sp->s_line, sp->s_file);
}
}
/*
* _blkstart - start of a program block. Increase the block reference
* number by one.
*/
void
_blkstart()
{
blkno++;
}
/*
* _blkend - end of a program block. Check all areas allocated in this
* block have been freed. Decrease the block number by one.
*/
void
_blkend()
{
#ifdef MSDOS
register SLOT huge *sp;
#else
register SLOT *sp;
#endif
if(blkno == 0) {
_error(NULL, 0, NULL, "_blkend: unmatched call to _blkend");
return;
}
for(sp = slots; sp != &slots[slotc]; sp++)
if(sp->s_blkno == blkno && !sp->s_freed)
_error(NULL, 0, NULL, "_blkend: %u bytes unfreed", sp->s_size);
blkno--;
}
/*
* _blkignore - find the slot corresponding to ptr, and set its block
* number to zero, to avoid _blkend picking it up when checking.
*/
void
_blkignore(ptr)
void *ptr;
{
#ifdef MSDOS
register SLOT huge *sp;
#else
register SLOT *sp;
#endif
if(sp = ptr2slot(ptr))
sp->s_blkno = 0;
else
_error(NULL, 0, NULL, "_blkignore: pointer has not been allocated");
}
/*
* NJH:
* Check a block is still valid
*/
bool
#ifdef ANSI
_blkchk(const char *ptr)
#else
_blkchk(ptr)
char *ptr;
#endif
{
#ifdef MSDOS
register SLOT huge *sp;
#else
register SLOT *sp;
#endif
if(sp = ptr2slot(ptr)) {
#ifdef UNDERFLOW
if((!sp->s_freed) && ((ptr[sp->s_size] != 'B') || (sp->s_underflowcheck != 'B'))) {
#else
if((!sp->s_freed) && (ptr[sp->s_size] != 'B')) {
#endif
_error(NULL, 0, NULL, "_blkcheck: possible corruption of %u bytes", sp->s_size);
return(false);
}
return(true); /* it's one of ours so it's safe */
}
return(isptrok(ptr, (sp) ? sp->s_size : 1));
}
/* what's the length of the thing this points to */
static size_t
#ifdef ANSI
blklen(const void *ptr, bool isstring)
#else
blklen(ptr, isstring)
void *ptr;
bool isstring;
#endif
{
register size_t len = (_blkchk(ptr) && ptr && isstring) ? strlen(ptr) : 0;
if(slots) {
#ifdef MSDOS
register const SLOT huge *sp;
#else
register const SLOT *sp;
#endif
if(sp = ptr2slot(ptr))
if(!sp->s_freed) { /* may be allocated elsewhere */
if(len > sp->s_size)
_error(NULL, 0, NULL, "string may not be NUL terminated");
return(sp->s_size);
}
}
if(ptr && isstring)
return(len + 1);
/*
* It would seem sensible to return UINT_MAX here, but on some systems
* size_t is a signed int (e.g. on sunos)
*/
return(INT_MAX);
}
#endif /*NJH_DEBUG*/
/*
* common/debug.c:
* Allocate & free memory plus checks
*/
void *pascal
db_mallocchk(size, file, line)
size_t size;
const char *file;
int line;
{
void *p = db_callocchk(1, size, file, line);
if(p) {
in_db++;
memset(p, 0xEE, size);
in_db--;
}
return(p);
}
void *pascal
db_callocchk(nelem, size, file, line) /* like calloc */
size_t nelem, size;
const char *file;
int line;
{
register void *area;
#ifdef MSDOS
if((long)nelem * (long)size > 65535L) {
#ifdef NJH_DEBUG
_error(file, line, NULL, "Too big");
#else
errstring = "Too big";
#endif
return(NULL);
}
#endif
lastfreed = NULL;
#ifdef NJH_DEBUG
if(area = _calloc(nelem, size, file, line))
return(area);
#else
#ifdef PP2WIN
if(area = rhpaca((PP16)nelem, (PP16)size))
return(area);
#else
if(area = calloc(nelem, size))
return(area);
#endif /*PP2WIN*/
#endif
/* remove_urgent(); */
#ifdef PP2WIN
if(area = rhpaca((PP16)nelem, (PP16)size))
return(area);
#else
#ifdef MSDOS
_heapmin();
#endif
#ifdef NJH_DEBUG
if(area = _calloc(nelem, size, file, line))
return(area);
#else
if(area = calloc(nelem, size))
return(area);
#endif
#endif /*PP2WIN*/
#ifdef NJH_DEBUG
_error(file, line, NULL, "calloc of %u bytes failed", nelem * size);
#endif
return((void *)NULL);
}
void *pascal
db_reallocchk(oarea, size, file, line) /* like realloc */
void *oarea;
size_t size;
const char *file;
int line;
{
register void *area;
if(oarea == NULL)
return(db_mallocchk(size, file, line));
lastfreed = NULL;
#ifdef NJH_DEBUG
if(area = _realloc(oarea, size, file, line))
return(area);
#else
#ifdef PP2WIN
area = db_mallocchk(size, file, line);
if(area == NULL)
return((void *)NULL);
real_memcpy(area, oarea, size);
rhpfree(oarea);
return(area);
#else
if(area = realloc(oarea, size))
return(area);
#endif
#endif
/* remove_urgent(); */
#ifdef MSDOS
_heapmin();
#endif
#ifdef NJH_DEBUG
if(area = _realloc(oarea, size, file, line))
return(area);
_error(file, line, NULL, "realloc: No more memory");
return((void *)NULL);
#else
return(realloc(oarea, size));
#endif
}
#ifdef NJH_DEBUG
#undef strdup
#ifndef PERPOS
char *cdecl
#ifdef ANSI
strdup(const char *string)
#else
strdup(string)
char *string;
#endif
{
/* 2.1.3 changed NULL to __FILE__ */
return(db_strdupchk(string, __FILE__, strdupline));
}
#endif
char *cdecl
#ifdef ANSI
_strdup(const char *string)
#else
_strdup(string)
char *string;
#endif
{
/* 2.1.3 changed NULL to __FILE__ */
return(db_strdupchk(string, __FILE__, strdupline));
}
char *pascal
#ifdef ANSI
db_strdupchk(const char *string, const char *file, int line) /* like strdup */
#else
db_strdupchk(string, file, line) /* like strdup */
char *string, *file;
int line;
#endif
{
register char *area;
#ifndef PP2WIN
register size_t len;
#endif
if(string == NULL) {
_error(file, line, NULL, "Attempt to strdup NULL");
return(NULL);
}
if(!_blkchk(string))
return(NULL);
lastfreed = NULL;
#ifdef PP2WIN
area = db_mallocchk(strlen(string) + 1, file, line);
if(area == NULL)
return((char *)NULL);
return(strcpy(area, string));
#else
len = strlen(string) + 1;
if(area = _malloc(len, file, line))
return(real_memcpy(area, string, len));
#ifdef MSDOS
_heapmin();
if(area = _malloc(len, file, line))
return(real_memcpy(area, string, len));
#endif
#ifdef NJH_DEBUG
_error(file, line, NULL, "strdupchk: No more memory");
#endif
return((char *)NULL);
#endif /*PP2WIN*/
}
#ifndef DBMALLOC
int cdecl
#ifdef ANSI
strcmp(register const char *s1, register const char *s2)
#else
strcmp(s1, s2)
register char *s1, *s2;
#endif
{
if(in_db == 0) {
if((s1 == NULL) || (s2 == NULL)) {
_error(NULL, 0, NULL, "Attempt to strcmp NULL");
return(0);
}
if(s1 == s2) {
_error(NULL, 0, NULL, "strcmp: no affect (\"%s\")", s1);
return(0);
}
_blkchk(s1);
_blkchk(s2);
}
while(*s1 == *s2++)
if(*s1++ == '\0')
return(0);
return(*(unsigned char *)s1 - *(unsigned char *)--s2);
}
char *cdecl
#ifdef ANSI
strcpy(register char *s1, register const char *s2)
#else
strcpy(s1, s2)
register char *s1, *s2;
#endif
{
if(in_db == 0) {
register size_t sz1, sz2;
if(s1 == NULL) {
_error(NULL, 0, NULL, "Attempt to strcpy to NULL");
return(NULL);
}
if(s2 == NULL) {
_error(NULL, 0, NULL, "Attempt to strcpy from NULL");
return(NULL);
}
if(s1 == s2) {
_error(NULL, 0, NULL, "strcpy: %s (no affect)", s1);
return(NULL);
}
sz1 = blklen(s1, false);
if(sz1 != INT_MAX) {
/* +1 because of the null byte */
sz2 = strlen(s2) + 1;
if(sz2 > sz1) {
_error(NULL, 0, NULL, "Attempt to strcpy \"%s\" on top of %u bytes", s2, sz1);
return(NULL);
}
if(sz2 > 3 * wsize) {
/* We know the length */
return(real_memcpy(s1, s2, sz2));
}
}
}
{
register char *ret = s1;
while(*s1++ = *s2++)
;
return(ret);
}
/*{
register char c;
register const int off = s1 - s2 - 1;
do
c = *s2++;
while(s1[off] = c]);
return(s1);
}*/
}
char *cdecl
#ifdef ANSI
strcat(register char *s1, register const char *s2)
#else
strcat(s1, s2)
register char *s1, *s2;
#endif
{
register char *ret = s1;
if(in_db == 0) {
if((s1 == NULL) || (s2 == NULL)) {
_error(NULL, 0, NULL, "Attempt to strcat NULL");
return(NULL);
}
_blkchk(s1);
_blkchk(s2);
if(strlen(s2) > strlen(s2) + blklen(s2, false))
_error(NULL, 0, NULL, "May not be enough room for strcat");
}
while(*s1++)
;
--s1;
while(*s1++ = *s2++)
;
return(ret);
}
#ifdef ANSI
void *_pascal
db_memcpy(register void *m1, register const void *m2, size_t n, const char *file, int line)
#else
void *_pascal
db_memcpy(m1, m2, n, file, line)
register char *m1, *m2;
size_t n;
char *file;
#endif
{
if((n == 0) || (m1 == m2)) { /* nothing to do */
/*
* On SunOs printfs generate memcpy with 0 bytes, so
* this message appears from printfs in this file, hence
* the need to check even if in_db - we don't want to call
* real_memcpy with 0 bytes as it'll dump core
*/
if(!in_db)
_error(file, line, NULL, "memcpy: %s (no affect)", (n == 0) ? "n == 0" : "m1 == m2");
return(m1);
}
if(in_db == 0) {
register size_t size;
if((m1 == NULL) || (m2 == NULL)) {
_error(file, line, NULL, "Attempt to memcpy NULL");
return(NULL);
}
size = blklen(m1, false);
if(n > size) {
_error(file, line, NULL, "Attempt to memcpy %u on top of %u bytes", n, size);
n = size;
}
if(!isptrok(m1, size))
return(NULL);
size = blklen(m2, false);
if(n > size) {
_error(file, line, NULL, "Attempt to memcpy %u from %u bytes", n, size);
n = size;
#ifdef M_I86SM
} else if(((unsigned short)m1 > (unsigned short)m2) && ((unsigned short)m1 < (unsigned short)m2 + n))
#else
} else if(((unsigned long)m1 > (unsigned long)m2) && ((unsigned long)m1 < (unsigned long)m2 + n))
#endif
_error(file, line, NULL, "Warning: overlapping memcpy");
if(!isptrok(m2, size))
return(NULL);
}
return(real_memcpy(m1, m2, n));
}
#ifdef MSDOS /* can't trust sizeofs */
#ifdef ANSI
void *cdecl
memset(void *s, int ch, size_t n)
#else
char *cdecl
memset(s, ch, n)
void *s;
int ch;
size_t n;
#endif
{
register unsigned char *t;
if(in_db == 0) {
register size_t size;
if(s == NULL) {
_error(NULL, 0, NULL, "Attempt to memset NULL");
return(NULL);
}
size = blklen(s, false);
if(n > size) {
_error(NULL, 0, NULL, "Attempt to memset %u with %u bytes", size, n);
return(NULL);
}
}
t = s;
while(n--)
*t++ = (unsigned char)ch;
return(s);
}
static void *
real_memcpy(register void *m1, register const void *m2, register size_t n)
{
register unsigned char *dest;
register const unsigned char *source;
dest = m1;
source = m2;
while(n--)
*dest++ = *source++;
return(m1);
}
#ifdef _MSC_VER
/* the MSC provided fread/fwrite have a near call to memcmp */
size_t __cdecl
fread(void *buffer, size_t size, size_t count, FILE *stream)
{
register size_t ret = 0;
register size_t offset, buflen;
register char *buf = (char *)buffer;
register int c;
buflen = blklen(buffer, false);
if((count * size) > buflen) {
_error(NULL, 0, NULL, "Attempt to fread %u on top of %u bytes", count * size, buflen);
return(0);
}
while(count--) {
for(offset = size; offset; offset--) {
c = getc(stream);
if(c == EOF)
return(ret);
*buf++ = (char)c;
}
ret++;
}
return(ret);
}
size_t __cdecl
fwrite(const void *buffer, size_t size, size_t count, FILE *stream)
{
register size_t ret = 0;
register size_t offset, buflen;
register char *buf = (char *)buffer;
buflen = blklen(buffer, false);
if((count * size) > buflen) {
_error(NULL, 0, NULL, "Attempt to fwrite %u on top of %u bytes", count * size, buflen);
return(0);
}
while(count--) {
for(offset = size; offset; offset--) {
putc(*buf, stream);
if(ferror(stream))
return(ret);
buf++;
}
ret++;
}
return(ret);
}
#endif /*MSC*/
#else /*!MSDOS*/
#ifdef ANSI
void *cdecl
memset(void *s, int ch, size_t n)
#else
char *cdecl
memset(s, ch, n)
char *s;
int ch; size_t n;
#endif
{
register size_t size, t;
register unsigned int c;
register unsigned char *dst;
if(in_db == 0) {
if(s == NULL) {
_error(NULL, 0, NULL, "Attempt to memset NULL");
return(NULL);
}
size = blklen(s, false);
if(n > size) {
_error(NULL, 0, NULL, "Attempt to memset %u with %u bytes", size, n);
return(NULL);
}
}
/*for(p = s; n--; *p++ = (unsigned char)ch)
;*/
dst = (unsigned char *)s;
/*
* If not enough words, just fill bytes. A length >= 2 words
* guarantees that at least one of them is `complete' after
* any necessary alignment. For instance:
*
* |-----------|-----------|-----------|
* |00|01|02|03|04|05|06|07|08|09|0A|00|
* ^---------------------^
* dst dst+length-1
*
* but we use a minimum of 3 here since the overhead of the code
* to do word writes is substantial.
*/
if(n < 3 * wsize) {
while(n--)
*dst++ = (unsigned char)ch;
return(s);
}
if((c = (unsigned char)ch) != 0) { /* Fill the word. */
c = (c << 8) | c; /* u_int is 16 bits. */
#if UINT_MAX > 0xffff
c = (c << 16) | c; /* u_int is 32 bits. */
#endif
#if UINT_MAX > 0xffffffff
c = (c << 32) | c; /* u_int is 64 bits. */
#endif
}
/* Align destination by filling in bytes. */
if((t = (int)dst & wmask) != 0) {
t = wsize - t;
n -= t;
do
*dst++ = (unsigned char)ch;
while(--t != 0);
}
/* Fill words. Length was >= 2*words so we know t >= 1 here. */
t = n / wsize;
do {
*(unsigned int *)dst = c;
dst += wsize;
} while (--t != 0);
/* Mop up trailing bytes, if any. */
t = n & wmask;
/*if(t != 0)
do
*dst++ = (unsigned char)ch;
while (--t != 0);*/
while(t--)
*dst++ = (unsigned char)ch;
return(s);
}
#ifdef ANSI
static void *cdecl
real_memcpy(void *m1, const void *m2, size_t n)
#else
static char *cdecl
real_memcpy(m1, m2, n)
char *m1, *m2;
register size_t n;
#endif
{
register char *dst = m1;
register const char *src = m2;
register int t;
if ((unsigned long)dst < (unsigned long)src) {
/*
* Copy forward.
*/
t = (int)src; /* only need low bits */
if ((t | (int)dst) & wmask) {
/*
* Try to align operands. This cannot be done
* unless the low bits match.
*/
if ((t ^ (int)dst) & wmask || n < wsize)
t = n;
else
t = wsize - (t & wmask);
n -= t;
do
*dst++ = *src++;
while(--t);
}
/*
* Copy whole words, then mop up any trailing bytes.
*/
t = n / wsize;
/*if(t)
do {
*(unsigned int *)dst = *(unsigned int *)src;
src += wsize;
dst += wsize;
} while(--t);*/
while(t--) {
*(unsigned int *)dst = *(unsigned int *)src;
src += wsize;
dst += wsize;
}
t = n & wmask;
/*if(t)
do
*dst++ = *src++;
while(--t);*/
while(t--)
*dst++ = *src++;
} else {
/*
* Copy backwards. Otherwise essentially the same.
* Alignment works as before, except that it takes
* (t&wmask) bytes to align, not wsize-(t&wmask).
*/
src += n;
dst += n;
t = (int)src;
if ((t | (int)dst) & wmask) {
if ((t ^ (int)dst) & wmask || n <= wsize)
t = n;
else
t &= wmask;
n -= t;
do
*--dst = *--src;
while(--t);
}
t = n / wsize;
/*if(t)
do {
src -= wsize;
dst -= wsize;
*(unsigned int *)dst = *(unsigned int *)src;
} while(--t);*/
while(t--) {
src -= wsize;
dst -= wsize;
*(unsigned int *)dst = *(unsigned int *)src;
}
t = n & wmask;
/*if(t)
do
*--dst = *--src;
while(--t);*/
while(t--)
*--dst = *--src;
}
return(m1);
}
#endif /*MSDOS*/
int cdecl
#ifdef ANSI
memcmp(const void *m1, const void *m2, size_t n)
#else
memcmp(m1, m2, n)
register void *m1, *m2;
size_t n;
#endif
{
register const unsigned char *s1 = m1, *s2 = m2;
if(in_db == 0) {
register size_t size;
if((m1 == NULL) || (m2 == NULL)) {
_error(NULL, 0, NULL, "Attempt to memcmp NULL");
return(0);
}
if((n == 0) || (m1 == m2)) { /* nothing to do */
_error(NULL, 0, NULL, "memcmp: %s (no affect)", (n == 0) ? "n == 0" : "m1 == m2");
return(0);
}
size = blklen(m1, false);
if(n > size) {
_error(NULL, 0, NULL, "Attempt to memcmp arg1 %u for %u bytes", size, n);
return(0);
}
size = blklen(m2, false);
if(n > size) {
_error(NULL, 0, NULL, "Attempt to memcmp arg2 %u for %u bytes", size, n);
return(0);
}
}
/*
* We know n >= 1 because of the above test and because memcmp
* isn't called from within this package, so do while is better
*/
do
if(*s1++ != *s2++)
return(*--s1 - *--s2);
while(--n);
return(0);
}
#endif /*DBMALLOC*/
#else /*!NJH_DEBUG*/
char *pascal
db_strdupchk(string, file, line) /* like strdup */
const char *string, *file;
{
register char *area;
if(string == NULL) {
errstring = "Attempt to strdup NULL";
return(NULL);
}
lastfreed = NULL;
#ifdef PP2WIN
area = db_mallocchk(strlen(string) + 1, file, line);
if(area == NULL)
return((char *)NULL);
return(strcpy(area, string));
#else
if(area = strdup(string))
return(area);
remove_urgent();
#ifdef MSDOS
_heapmin();
#endif
if(area = strdup(string))
return(area);
if(errstring == (char *)NULL)
errstring = MEM;
return((char *)NULL);
#endif /*PP2WIN*/
}
#endif
void pascal
db_freechk(memblock, file, line)
void *memblock;
const char *file;
int line;
{
if(memblock) {
if(memblock == lastfreed) {
_error(file, line, NULL, "Attempt to refree pointer");
return;
}
#ifdef NJH_DEBUG
_free(memblock, file, line);
#else
#ifdef PP2WIN
rhpfree(memblock);
#else
free(memblock);
#endif
#endif
lastfreed = memblock;
} else
_error(file, line, NULL, "Attempt to free NULL pointer");
}
#if defined(unix) || defined(_HPUX_SOURCE) || defined(UNIX)
/* write around for DOS alloca: NJH call mallocchk and freechk */
/* alloca.c -- allocate automatically reclaimed memory
(Mostly) portable public-domain implementation -- D A Gwyn
This implementation of the PWB library alloca function,
which is used to allocate space off the run-time stack so
that it is automatically reclaimed upon procedure exit,
was inspired by discussions with J. Q. Johnson of Cornell.
J.Otto Tennant <jot@cray.com> contributed the Cray support.
There are some preprocessor constants that can
be defined when compiling for your specific system, for
improved efficiency; however, the defaults should be okay.
The general concept of this implementation is to keep
track of all alloca-allocated blocks, and reclaim any
that are found to be deeper in the stack than the current
invocation. This heuristic does not reclaim storage as
soon as it becomes invalid, but it will do so eventually.
As a special case, alloca(0) reclaims storage without
allocating any. It is a good idea to use alloca(0) in
your main control loop, etc. to force garbage collection. */
/*#ifdef HAVE_CONFIG_H*/
#if 0
#if defined (emacs) || defined (CONFIG_BROKETS)
#include <config.h>
#else
#include "config.h"
#endif
#endif
/* If someone has defined alloca as a macro,
there must be some other way alloca is supposed to work. */
#ifndef alloca
#ifdef emacs
#ifdef static
/* actually, only want this if static is defined as ""
-- this is for usg, in which emacs must undefine static
in order to make unexec workable
*/
#ifndef STACK_DIRECTION
you
lose
-- must know STACK_DIRECTION at compile-time
#endif /* STACK_DIRECTION undefined */
#endif /* static */
#endif /* emacs */
/* If your stack is a linked list of frames, you have to
provide an "address metric" ADDRESS_FUNCTION macro. */
#if defined (CRAY) && defined (CRAY_STACKSEG_END)
long i00afunc ();
#define ADDRESS_FUNCTION(arg) (char *) i00afunc (&(arg))
#else
#define ADDRESS_FUNCTION(arg) &(arg)
#endif
#if __STDC__
typedef void *pointer;
#else
typedef char *pointer;
#endif
#ifndef NULL
#define NULL 0
#endif
/* Different portions of Emacs need to call different versions of
malloc. The Emacs executable needs alloca to call xmalloc, because
ordinary malloc isn't protected from input signals. On the other
hand, the utilities in lib-src need alloca to call malloc; some of
them are very simple, and don't have an xmalloc routine.
Non-Emacs programs expect this to call use xmalloc.
Callers below should use malloc. */
#ifndef emacs
/* #define malloc xmalloc */
#endif
/* Define STACK_DIRECTION if you know the direction of stack
growth for your system; otherwise it will be automatically
deduced at run-time.
STACK_DIRECTION > 0 => grows toward higher addresses
STACK_DIRECTION < 0 => grows toward lower addresses
STACK_DIRECTION = 0 => direction of growth unknown */
#ifndef STACK_DIRECTION
#define STACK_DIRECTION 0 /* Direction unknown. */
#endif
#if STACK_DIRECTION != 0
#define STACK_DIR STACK_DIRECTION /* Known at compile-time. */
#else /* STACK_DIRECTION == 0; need run-time code. */
static int stack_dir; /* 1 or -1 once known. */
#define STACK_DIR stack_dir
static void
find_stack_direction ()
{
static char *addr = NULL; /* Address of first `dummy', once known. */
auto char dummy; /* To get stack address. */
if (addr == NULL)
{ /* Initial entry. */
addr = ADDRESS_FUNCTION (dummy);
find_stack_direction (); /* Recurse once. */
}
else
{
/* Second entry. */
if (ADDRESS_FUNCTION (dummy) > addr)
stack_dir = 1; /* Stack grew upward. */
else
stack_dir = -1; /* Stack grew downward. */
}
}
#endif /* STACK_DIRECTION == 0 */
/* An "alloca header" is used to:
(a) chain together all alloca'ed blocks;
(b) keep track of stack depth.
It is very important that sizeof(header) agree with malloc
alignment chunk size. The following default should work okay. */
#ifndef ALIGN_SIZE
#define ALIGN_SIZE sizeof(double)
#endif
typedef union hdr
{
char align[ALIGN_SIZE]; /* To force sizeof(header). */
struct
{
union hdr *next; /* For chaining headers. */
char *deep; /* For stack depth measure. */
} h;
} header;
static header *last_alloca_header = NULL; /* -> last alloca header. */
/* Return a pointer to at least SIZE bytes of storage,
which will be automatically reclaimed upon exit from
the procedure that called alloca. Originally, this space
was supposed to be taken from the current stack frame of the
caller, but that method cannot be made to work for some
implementations of C, for example under Gould's UTX/32. */
void *pascal
alloca(size)
size_t size;
{
return(db_alloca(size, NULL, 0));
}
void *pascal
db_alloca(size, file, line)
size_t size;
const char *file;
int line;
{
auto char probe; /* Probes stack depth: */
register char *depth = ADDRESS_FUNCTION (probe);
#if STACK_DIRECTION == 0
if (STACK_DIR == 0) /* Unknown growth direction. */
find_stack_direction ();
#endif
/* Reclaim garbage, defined as all alloca'd storage that
was allocated from deeper in the stack than currently. */
{
register header *hp; /* Traverses linked list. */
for (hp = last_alloca_header; hp != NULL;)
if ((STACK_DIR > 0 && hp->h.deep > depth)
|| (STACK_DIR < 0 && hp->h.deep < depth))
{
register header *np = hp->h.next;
db_freechk ((pointer) hp, file, line); /* Collect garbage. */
hp = np; /* -> next header. */
}
else
break; /* Rest are not deeper. */
last_alloca_header = hp; /* -> last valid storage. */
}
if (size == 0)
return NULL; /* No allocation required. */
/* Allocate combined header + user data storage. */
{
#ifdef NJH_DEBUG
register pointer new = db_mallocchk (sizeof (header) + size, file, line);
#else
register pointer new = mallocchk (sizeof (header) + size);
#endif
/* Address of header. */
((header *) new)->h.next = last_alloca_header;
((header *) new)->h.deep = depth;
last_alloca_header = (header *) new;
/* User storage begins just after header. */
return (pointer) ((char *) new + sizeof (header));
}
}
#if defined (CRAY) && defined (CRAY_STACKSEG_END)
#ifdef DEBUG_I00AFUNC
#include <stdio.h>
#endif
#ifndef CRAY_STACK
#define CRAY_STACK
#ifndef CRAY2
/* Stack structures for CRAY-1, CRAY X-MP, and CRAY Y-MP */
struct stack_control_header
{
long shgrow:32; /* Number of times stack has grown. */
long shaseg:32; /* Size of increments to stack. */
long shhwm:32; /* High water mark of stack. */
long shsize:32; /* Current size of stack (all segments). */
};
/* The stack segment linkage control information occurs at
the high-address end of a stack segment. (The stack
grows from low addresses to high addresses.) The initial
part of the stack segment linkage control information is
0200 (octal) words. This provides for register storage
for the routine which overflows the stack. */
struct stack_segment_linkage
{
long ss[0200]; /* 0200 overflow words. */
long sssize:32; /* Number of words in this segment. */
long ssbase:32; /* Offset to stack base. */
long:32;
long sspseg:32; /* Offset to linkage control of previous
segment of stack. */
long:32;
long sstcpt:32; /* Pointer to task common address block. */
long sscsnm; /* Private control structure number for
microtasking. */
long ssusr1; /* Reserved for user. */
long ssusr2; /* Reserved for user. */
long sstpid; /* Process ID for pid based multi-tasking. */
long ssgvup; /* Pointer to multitasking thread giveup. */
long sscray[7]; /* Reserved for Cray Research. */
long ssa0;
long ssa1;
long ssa2;
long ssa3;
long ssa4;
long ssa5;
long ssa6;
long ssa7;
long sss0;
long sss1;
long sss2;
long sss3;
long sss4;
long sss5;
long sss6;
long sss7;
};
#else /* CRAY2 */
/* The following structure defines the vector of words
returned by the STKSTAT library routine. */
struct stk_stat
{
long now; /* Current total stack size. */
long maxc; /* Amount of contiguous space which would
be required to satisfy the maximum
stack demand to date. */
long high_water; /* Stack high-water mark. */
long overflows; /* Number of stack overflow ($STKOFEN) calls. */
long hits; /* Number of internal buffer hits. */
long extends; /* Number of block extensions. */
long stko_mallocs; /* Block allocations by $STKOFEN. */
long underflows; /* Number of stack underflow calls ($STKRETN). */
long stko_free; /* Number of deallocations by $STKRETN. */
long stkm_free; /* Number of deallocations by $STKMRET. */
long segments; /* Current number of stack segments. */
long maxs; /* Maximum number of stack segments so far. */
long pad_size; /* Stack pad size. */
long current_address; /* Current stack segment address. */
long current_size; /* Current stack segment size. This
number is actually corrupted by STKSTAT to
include the fifteen word trailer area. */
long initial_address; /* Address of initial segment. */
long initial_size; /* Size of initial segment. */
};
/* The following structure describes the data structure which trails
any stack segment. I think that the description in 'asdef' is
out of date. I only describe the parts that I am sure about. */
struct stk_trailer
{
long this_address; /* Address of this block. */
long this_size; /* Size of this block (does not include
this trailer). */
long unknown2;
long unknown3;
long link; /* Address of trailer block of previous
segment. */
long unknown5;
long unknown6;
long unknown7;
long unknown8;
long unknown9;
long unknown10;
long unknown11;
long unknown12;
long unknown13;
long unknown14;
};
#endif /* CRAY2 */
#endif /* not CRAY_STACK */
#ifdef CRAY2
/* Determine a "stack measure" for an arbitrary ADDRESS.
I doubt that "lint" will like this much. */
static long
i00afunc (long *address)
{
struct stk_stat status;
struct stk_trailer *trailer;
long *block, size;
long result = 0;
/* We want to iterate through all of the segments. The first
step is to get the stack status structure. We could do this
more quickly and more directly, perhaps, by referencing the
$LM00 common block, but I know that this works. */
STKSTAT (&status);
/* Set up the iteration. */
trailer = (struct stk_trailer *) (status.current_address
+ status.current_size
- 15);
/* There must be at least one stack segment. Therefore it is
a fatal error if "trailer" is null. */
if (trailer == 0)
abort ();
/* Discard segments that do not contain our argument address. */
while (trailer != 0)
{
block = (long *) trailer->this_address;
size = trailer->this_size;
if (block == 0 || size == 0)
abort ();
trailer = (struct stk_trailer *) trailer->link;
if ((block <= address) && (address < (block + size)))
break;
}
/* Set the result to the offset in this segment and add the sizes
of all predecessor segments. */
result = address - block;
if (trailer == 0)
{
return result;
}
do
{
if (trailer->this_size <= 0)
abort ();
result += trailer->this_size;
trailer = (struct stk_trailer *) trailer->link;
}
while (trailer != 0);
/* We are done. Note that if you present a bogus address (one
not in any segment), you will get a different number back, formed
from subtracting the address of the first block. This is probably
not what you want. */
return (result);
}
#else /* not CRAY2 */
/* Stack address function for a CRAY-1, CRAY X-MP, or CRAY Y-MP.
Determine the number of the cell within the stack,
given the address of the cell. The purpose of this
routine is to linearize, in some sense, stack addresses
for alloca. */
static long
i00afunc (long address)
{
long stkl = 0;
long size, pseg, this_segment, stack;
long result = 0;
struct stack_segment_linkage *ssptr;
/* Register B67 contains the address of the end of the
current stack segment. If you (as a subprogram) store
your registers on the stack and find that you are past
the contents of B67, you have overflowed the segment.
B67 also points to the stack segment linkage control
area, which is what we are really interested in. */
stkl = CRAY_STACKSEG_END ();
ssptr = (struct stack_segment_linkage *) stkl;
/* If one subtracts 'size' from the end of the segment,
one has the address of the first word of the segment.
If this is not the first segment, 'pseg' will be
nonzero. */
pseg = ssptr->sspseg;
size = ssptr->sssize;
this_segment = stkl - size;
/* It is possible that calling this routine itself caused
a stack overflow. Discard stack segments which do not
contain the target address. */
while (!(this_segment <= address && address <= stkl))
{
#ifdef DEBUG_I00AFUNC
fprintf (stderr, "%011o %011o %011o\n", this_segment, address, stkl);
#endif
if (pseg == 0)
break;
stkl = stkl - pseg;
ssptr = (struct stack_segment_linkage *) stkl;
size = ssptr->sssize;
pseg = ssptr->sspseg;
this_segment = stkl - size;
}
result = address - this_segment;
/* If you subtract pseg from the current end of the stack,
you get the address of the previous stack segment's end.
This seems a little convoluted to me, but I'll bet you save
a cycle somewhere. */
while (pseg != 0)
{
#ifdef DEBUG_I00AFUNC
fprintf (stderr, "%011o %011o\n", pseg, size);
#endif
stkl = stkl - pseg;
ssptr = (struct stack_segment_linkage *) stkl;
size = ssptr->sssize;
pseg = ssptr->sspseg;
result += size;
}
return (result);
}
#endif /* not CRAY2 */
#endif /* CRAY */
#endif /* no alloca */
#endif /*UNIX*/
#undef memcpy
#undef malloc
#undef calloc
#undef realloc
#undef free
#undef strdup
/*
* Stubs to force calls of our code which call rhpfree etc.
* rhpaca and rhpfree call better windows routines and yield
*/
#ifdef ANSI
void *cdecl
memcpy(register void *m1, register const void *m2, size_t n)
#else
char *cdecl
memcpy(m1, m2, n)
register char *m1, *m2;
size_t n;
#endif
{
/*return(db_memcpy(m1, m2, n, __FILE__, __LINE__));*/
return(db_memcpy(m1, m2, n, NULL, 0));
}
#ifdef ANSI
void * cdecl
malloc(size_t size)
#else
char *cdecl
malloc(size)
size_t size;
#endif
{
return(db_mallocchk(size, NULL, 0));
}
#ifdef ANSI
void *cdecl
calloc(size_t nelem, size_t size)
#else
char *cdecl
calloc(nelem, size)
size_t nelem, size;
#endif
{
return(db_callocchk(nelem, size, NULL, 0));
}
#ifdef ANSI
void *cdecl
realloc(void *oarea, size_t size)
#else
char *
realloc(oarea, size)
void *oarea;
size_t size;
#endif
{
return(db_reallocchk(oarea, size, NULL, 0));
}
#ifndef NJH_DEBUG
char * cdecl
strdup(const char *string)
{
return(db_strdupchk(string, NULL, 0));
}
#endif
#ifdef ANSI
void cdecl
free(void *memblock)
#else
#ifndef sun
void
#endif
free(memblock)
char *memblock;
#endif
{
db_freechk(memblock, NULL, 0);
}
#ifndef MSDOS
static char *exec_name;
void
db_setname(progname)
const char *progname;
{
if(exec_name)
db_freechk(exec_name, NULL, 0);
exec_name = db_strdupchk(progname, NULL, 0);
}
#endif
static void cdecl
#ifdef ANSI
leaks(void)
#else
leaks()
#endif
{
unsigned long leaks = 0L;
register int i;
#ifdef CACHE_TRACE
register const struct cache *c;
for(c = cache, i = 0; i < CACHE_SIZE; c++, i++)
printf("cache %d: %d/%d\n", i, c->hits, c->misses);
#endif
if(check_for_leaks && slotc) {
#ifdef MSDOS
register SLOT const huge *sp;
#else
register SLOT const *sp;
#endif
register int count = 0;
#ifdef WATCH
if(watchfd >= 0) {
close(watchfd);
watchfd = -1;
}
#endif
db_heapchk(NULL, 0);
#ifdef UNIX
db_alloca(0, __FILE__, __LINE__);
#endif
#ifdef MAX_STACK_DEPTH
st_read();
#endif
if(in_db++)
/*
* This can occur if a signal such as SIGINT
* is caught and the handler calls exit if the
* interrupt happens when in_db is set
*/
fputs("Warning: Unexpected check for leaks call\n", stderr);
for(sp = slots; sp != &slots[slotc]; sp++)
if((!sp->s_freed) && sp->s_size) {
count++;
leaks += sp->s_size;
#ifdef MSDOS
if(sp->s_file && (_fstrcmp(sp->s_file, __FILE__) == 0) && (sp->s_line == strdupline))
#else
if(sp->s_file && (strcmp(sp->s_file, __FILE__) == 0) && (sp->s_line == strdupline))
#endif
#ifdef MAX_STACK_DEPTH
_error(NULL, 0L, sp->s_history, "unfree strdup: \"%s\"", (const char *)sp->s_ptr);
#else
_error(NULL, 0L, NULL, "unfree strdup: \"%s\"", (const char *)sp->s_ptr);
#endif
else
_error(sp->s_file, sp->s_line, NULL, "unfree memory %u bytes", sp->s_size);
}
#ifdef MAX_STACK_DEPTH
else if(exec_name && sp->s_history[0])
_error(NULL, 0, sp->s_history, "unfree memory %u bytes", sp->s_size);
#endif
if(leaks)
fprintf(stderr, "Summary:\n\t%lu bytes in %d memory leak%c\n",
leaks, count, (count == 1) ? ' ' : 's');
count = 0;
for(i = 0; i < _NFILE; i++)
if((lseek(i, 0, SEEK_CUR) >= 0) || (errno != EBADF))
if(!fds[i]) {
#ifdef MSDOS
fprintf(stderr, "leaked file descriptor %d\n", i);
#else
int flags = 0, ispipe;
if(fcntl(i, F_GETFL, &flags) < 0)
perror("fcntl");
fputs("leaked ", stderr);
switch(flags & 3) {
case O_RDONLY:
fputs("read ", stderr);
break;
case O_WRONLY:
fputs("write ", stderr);
break;
case O_RDWR:
fputs("read/write ", stderr);
break;
}
ispipe = ((lseek(i, 0, SEEK_CUR) < 0) && (errno == ESPIPE));
if(ispipe)
fputs("pipe ", stderr);
else if(flags & O_APPEND)
fputs("append ", stderr);
fprintf(stderr, "file descriptor %d ", i);
#ifdef __BEOS__
putchar('\n');
#else
if(ispipe)
putchar('\n');
else {
struct stat statb;
if(fstat(i, &statb) < 0)
perror("fstat");
fprintf(stderr, "to inode %d\n", (int)statb.st_ino);
fprintf(stderr, "to inode %d on device %d/%d\n", (int)statb.st_ino, (int)major(statb.st_dev), (int)minor(statb.st_dev));
}
#endif
#endif
count++;
}
if(count > 1)
fprintf(stderr, "%d file descriptor leaks\n", count);
check_for_leaks = false;
in_db--;
}
}
#ifndef MSDOS
#define RCHECK
/*
* Copyright (c) 1983 Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if defined(LIBC_SCCS) && !defined(lint)
// static char sccsid[] = "@(#)malloc.c 5.11 (Berkeley) 2/23/91";
#endif /* LIBC_SCCS and not lint */
/*
* malloc.c (Caltech) 2/21/82
* Chris Kingsley, kingsley@cit-20.
*
* This is a very fast storage allocator. It allocates blocks of a small
* number of different sizes, and keeps free lists of each size. Blocks that
* don't exactly fit are passed up to the next larger size. In this
* implementation, the available sizes are 2^n-4 (or 2^n-10) bytes long.
* This is designed for use in a virtual memory environment.
*/
#include <sys/types.h>
/* #include <stdlib.h>
#include <string.h>
#include <unistd.h> */
#ifndef NULL
#define NULL 0
#endif
static void morecore();
static int findbucket();
/*
* The overhead on a block is at least 4 bytes. When free, this space
* contains a pointer to the next free block, and the bottom two bits must
* be zero. When in use, the first byte is set to MAGIC, and the second
* byte is the size index. The remaining bytes are for alignment.
* If range checking is enabled then a second word holds the size of the
* requested block, less 1, rounded up to a multiple of sizeof(RMAGIC).
* The order of elements is critical: ov_magic must overlay the low order
* bits of ov_next, and ov_magic can not be a valid ov_next bit pattern.
*/
union overhead {
union overhead *ov_next; /* when free */
struct {
unsigned char ovu_magic; /* magic number */
unsigned char ovu_index; /* bucket # */
#ifdef RCHECK
unsigned short ovu_rmagic; /* range magic number */
unsigned int ovu_size; /* actual block size */
#endif
} ovu;
#define ov_magic ovu.ovu_magic
#define ov_index ovu.ovu_index
#define ov_rmagic ovu.ovu_rmagic
#define ov_size ovu.ovu_size
};
#define MAGIC 0xef /* magic # on accounting info */
#define RMAGIC 0x5555 /* magic # on range info */
#ifdef RCHECK
#define RSLOP sizeof (unsigned short)
#else
#define RSLOP 0
#endif
/*
* nextf[i] is the pointer to the next free block of size 2^(i+3). The
* smallest allocatable block is 8 bytes. The overhead information
* precedes the data area returned to the user.
*/
#define NBUCKETS 30
static union overhead *nextf[NBUCKETS];
#if defined(_HPUX_SOURCE) || defined(solaris) || defined(AIX) || defined(LINUX)
extern void *sbrk();
#else
extern char *sbrk();
#endif
static int pagebucket; /* page size bucket */
#ifdef MSTATS
/*
* nmalloc[i] is the difference between the number of mallocs and frees
* for a given block size.
*/
static unsigned int nmalloc[NBUCKETS];
#include <stdio.h>
#endif
#if defined(DEBUG) || defined(RCHECK)
#define ASSERT(p) if (!(p)) botch("p")
/* #include <stdio.h> */
static void
#ifdef ANSI
botch(const char *s)
#else
botch(s)
char *s;
#endif
{
/* fprintf(stderr, "\r\nassertion botched: %s\r\n", s); */
_error(NULL, 0, NULL, "\r\nassertion botched: %s\r", s);
(void) fflush(stderr); /* just in case user buffered it */
abort();
}
#else
#define ASSERT(p)
#endif
#if defined(MAP_ANONYMOUS) && defined(MPROTECT)
static void *
#ifdef ANSI
grab_mem(size_t size)
#else
grab_mem(size)
size_t size;
#endif
{
static caddr_t startAddr;
caddr_t allocation = (caddr_t) mmap(
startAddr
,(int)size
,PROT_READ|PROT_WRITE
,MAP_PRIVATE|MAP_ANONYMOUS
,-1
,0);
if ( allocation == (caddr_t)-1 ) {
_error(NULL, 0, NULL, "mmap failed - using sbrk");
return(sbrk(size));
}
#ifndef __hpux
/*
* Set the "address hint" for the next mmap() so that it will abut
* the mapping we just created.
*
* HP/UX 9.01 has a kernel bug that makes mmap() fail sometimes
* when given a non-zero address hint, so we'll leave the hint set
* to zero on that system. HP recently told me this is now fixed.
* Someone please tell me when it is probable to assume that most
* of those systems that were running 9.01 have been upgraded.
*/
startAddr = allocation + size;
#endif
return((void *)allocation);
}
#else
static void *
grab_mem(size)
size_t size;
{
return(sbrk(size));
}
#endif
#ifdef ANSI
static void *
real_malloc(size_t nbytes)
#else
static char *
real_malloc(nbytes)
size_t nbytes;
#endif
{
register union overhead *op;
register int bucket, n;
register unsigned amt;
/*
* First time malloc is called, setup page size and
* align break pointer so all data will be page aligned.
*/
if (pagesz == 0) {
#if defined(sun) || defined(AIX)
pagesz = n = getpagesize();
#else
pagesz = n = 4096;
#endif
op = (union overhead *)sbrk(0);
n = n - sizeof (*op) - ((int)op & (n - 1));
if (n < 0)
n += pagesz;
if (n) {
if (grab_mem(n) == (char *)-1)
return (NULL);
}
bucket = 0;
amt = 8;
while (pagesz > amt) {
amt <<= 1;
bucket++;
}
pagebucket = bucket;
}
/*
* Convert amount of memory requested into closest block size
* stored in hash buckets which satisfies request.
* Account for space used per block for accounting.
*/
if (nbytes <= (n = pagesz - sizeof (*op) - RSLOP)) {
#ifndef RCHECK
amt = 8; /* size of first bucket */
bucket = 0;
#else
amt = 16; /* size of first bucket */
bucket = 1;
#endif
n = -(sizeof (*op) + RSLOP);
} else {
amt = pagesz;
bucket = pagebucket;
}
while (nbytes > amt + n) {
amt <<= 1;
if (amt == 0)
return (NULL);
bucket++;
}
/*
* If nothing in hash bucket right now,
* request more memory from the system.
*/
if ((op = nextf[bucket]) == NULL) {
morecore(bucket);
if ((op = nextf[bucket]) == NULL)
return (NULL);
}
/* remove from linked list */
nextf[bucket] = op->ov_next;
op->ov_magic = MAGIC;
op->ov_index = (unsigned char)bucket;
#ifdef MSTATS
nmalloc[bucket]++;
#endif
#ifdef RCHECK
/*
* Record allocated size of block and
* bound space with magic numbers.
*/
op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
op->ov_rmagic = RMAGIC;
rw((caddr_t)(op + 1), op->ov_size + 1);
*(unsigned short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
#endif
return ((char *)(op + 1));
}
/*
* Allocate more memory to the indicated bucket.
*/
static void
morecore(bucket)
int bucket;
{
register union overhead *op;
register int sz; /* size of desired block */
int amt; /* amount to allocate */
int nblks; /* how many blocks we get */
/*
* sbrk_size <= 0 only for big, FLUFFY, requests (about
* 2^30 bytes on a VAX, I think) or for a negative arg.
*/
sz = 1 << (bucket + 3);
#ifdef DEBUG
ASSERT(sz > 0);
#else
if (sz <= 0)
return;
#endif
ASSERT(pagesz > 0);
if (sz < pagesz) {
amt = pagesz;
nblks = amt / sz;
} else {
amt = sz + pagesz;
nblks = 1;
}
#ifdef MPROTECT
op = (union overhead *)grab_mem(amt + sz); /* 2.1.2 fix */
#else
op = (union overhead *)grab_mem(amt);
#endif
/* no more room! */
if ((int)op == -1)
return;
/*
* Add new memory allocated to that on
* free list for this hash bucket.
*/
nextf[bucket] = op;
while (--nblks > 0) {
op->ov_next = (union overhead *)((caddr_t)op + sz);
op = (union overhead *)((caddr_t)op + sz);
}
}
static void
#ifdef ANSI
real_free(void *cp)
#else
real_free(cp)
void *cp;
#endif
{
register int size;
register union overhead *op;
if (cp == NULL)
return;
op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
#ifdef DEBUG
ASSERT(op->ov_magic == MAGIC); /* make sure it was in use */
#else
if (op->ov_magic != MAGIC)
return; /* sanity */
#endif
#ifdef RCHECK
ASSERT(op->ov_rmagic == RMAGIC);
ASSERT(*(unsigned short *)((caddr_t)(op + 1) + op->ov_size) == RMAGIC);
#endif
size = op->ov_index;
ASSERT(size < NBUCKETS);
op->ov_next = nextf[size]; /* also clobbers ov_magic */
nextf[size] = op;
#ifdef MSTATS
nmalloc[size]--;
#endif
}
/*
* When a program attempts "storage compaction" as mentioned in the
* old malloc man page, it realloc's an already freed block. Usually
* this is the last block it freed; occasionally it might be farther
* back. We have to search all the free lists for the block in order
* to determine its bucket: 1st we make one pass thru the lists
* checking only the first block in each; if that fails we search
* ``realloc_srchlen'' blocks in each list for a match (the variable
* is extern so the caller can modify it). If that fails we just copy
* however many bytes was given to realloc() and hope it's not huge.
*/
#define realloc_srchlen 4 /* 4 should be plenty, -1 =>'s whole list */
#ifdef ANSI
static void *
real_realloc(void *cp, size_t nbytes)
#else
static char *
real_realloc(cp, nbytes)
char *cp;
size_t nbytes;
#endif
{
register unsigned int onb;
register int i;
union overhead *op;
char *res;
int was_alloced = 0;
if (cp == NULL)
return (real_malloc(nbytes));
op = (union overhead *)((caddr_t)cp - sizeof (union overhead));
if (op->ov_magic == MAGIC) {
was_alloced++;
i = op->ov_index;
} else {
/*
* Already free, doing "compaction".
*
* Search for the old block of memory on the
* free list. First, check the most common
* case (last element free'd), then (this failing)
* the last ``realloc_srchlen'' items free'd.
* If all lookups fail, then assume the size of
* the memory block being realloc'd is the
* largest possible (so that all "nbytes" of new
* memory are copied into). Note that this could cause
* a memory fault if the old area was tiny, and the moon
* is gibbous. However, that is very unlikely.
*/
if ((i = findbucket(op, 1)) < 0 &&
(i = findbucket(op, realloc_srchlen)) < 0)
i = NBUCKETS;
}
onb = 1 << (i + 3);
if (onb < pagesz)
onb -= sizeof (*op) + RSLOP;
else
onb += pagesz - sizeof (*op) - RSLOP;
/* avoid the copy if same size block */
if (was_alloced) {
if (i) {
i = 1 << (i + 2);
if (i < pagesz)
i -= sizeof (*op) + RSLOP;
else
i += pagesz - sizeof (*op) - RSLOP;
}
if (nbytes <= onb && nbytes > i) {
#ifdef RCHECK
op->ov_size = (nbytes + RSLOP - 1) & ~(RSLOP - 1);
*(unsigned short *)((caddr_t)(op + 1) + op->ov_size) = RMAGIC;
#endif
return(cp);
} else
real_free(cp);
}
if ((res = real_malloc(nbytes)) == NULL)
return (NULL);
if (cp != res) /* common optimization if "compacting" */
return(real_memcpy(res, cp, (nbytes < onb) ? nbytes : onb));
return (res);
}
/*
* Search ``srchlen'' elements of each free list for a block whose
* header starts at ``freep''. If srchlen is -1 search the whole list.
* Return bucket number, or -1 if not found.
*/
static int
findbucket(freep, srchlen)
union overhead *freep;
int srchlen;
{
register int i, j;
for (i = 0; i < NBUCKETS; i++) {
register union overhead *p;
j = 0;
for (p = nextf[i]; p && j != srchlen; p = p->ov_next) {
if (p == freep)
return (i);
j++;
}
}
return (-1);
}
#ifdef MSTATS
/*
* mstats - print out statistics about malloc
*
* Prints two lines of numbers, one showing the length of the free list
* for each size category, the second showing the number of mallocs -
* frees for each size category.
*/
mstats(s)
char *s;
{
register int i, j;
register union overhead *p;
int totfree = 0,
totused = 0;
fprintf(stderr, "Memory allocation statistics %s\nfree:\t", s);
for (i = 0; i < NBUCKETS; i++) {
for (j = 0, p = nextf[i]; p; p = p->ov_next, j++)
;
fprintf(stderr, " %d", j);
totfree += j * (1 << (i + 3));
}
fputs("\nused:\t", stderr);
for (i = 0; i < NBUCKETS; i++) {
fprintf(stderr, " %d", nmalloc[i]);
totused += nmalloc[i] * (1 << (i + 3));
}
fprintf(stderr, "\n\tTotal in use: %d, total free: %d\n",
totused, totfree);
}
#endif /* MSTATS */
#endif /* UNIX */
#ifdef UNIX
#define tst(a,b) (*mode == 'r' ? (b) : (a))
#define RDR 0
#define WTR 1
extern FILE *fdopen();
#ifndef NOFILES_MIN
#ifdef NOFILE
#define NOFILES_MIN NOFILE
#elif defined(_NFILE)
#define NOFILES_MIN _NFILE
#elif defined(OPEN_MAX)
#define NOFILES_MIN OPEN_MAX
#else
#define NOFILES_MIN 20
#endif
#endif
#ifndef OPEN_MAX
#define OPEN_MAX 64
#endif
static pid_t popen_pid[NOFILES_MIN];
static void
execshell(cmd)
const char *cmd;
{
register const char *shell;
if(strpbrk(cmd, " =?*[|&$<>") == (char *)NULL) {
(void)execl(cmd, cmd, (char *)0);
(void)execlp(cmd, cmd, (char *)0);
}
shell = getenv("SHELL");
(void) execl((shell) ? shell : "/bin/sh", "sh", "-c", cmd, (char *)0);
}
#ifndef PERPOS
#define WAITPID
#endif
FILE *
popen(cmd, mode)
const char *cmd, *mode;
{
register pid_t *poptr, pid;
register int myside, yourside;
int stdio;
int p[2];
if(!_blkchk(cmd) || !_blkchk(mode) || (*mode != 'r' && *mode != 'w')) {
errno = EINVAL;
return(NULL);
}
if(pipe(p) < 0)
return(NULL);
myside = tst(p[WTR], p[RDR]);
yourside = tst(p[RDR], p[WTR]);
#ifdef SIGCLD
signal(SIGCLD, SIG_DFL);
#endif
switch(pid = fork()) {
case 0:
/* myside and yourside reverse roles in child */
/* close all pipes from other popen's */
for (poptr = popen_pid; poptr < popen_pid+NOFILES_MIN; poptr++)
if(*poptr)
close(poptr - popen_pid);
stdio = tst(0, 1);
(void) close(myside);
(void) close(stdio);
(void) fcntl(yourside, F_DUPFD, stdio);
(void) close(yourside);
setuid(getuid());
execshell(cmd);
_exit(1);
case -1:
return(NULL);
default:
popen_pid[myside] = pid;
(void) close(yourside);
return(fdopen(myside, mode));
}
}
int
pclose(ptr)
FILE *ptr;
{
register int f, r;
#ifdef sun
int status;
void (*hstat)(), (*istat)(), (*qstat)(), (*astat)();
#else
int status, (*hstat)(), (*istat)(), (*qstat)(), (*astat)();
#endif
register int oalarm;
if(!_blkchk((char *)ptr))
return(-1);
f = fileno(ptr);
if(popen_pid[f] == 0) {
_error(NULL, 0, NULL, "pclose with no popen");
return(-1);
}
(void) fclose(ptr);
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
hstat = signal(SIGHUP, SIG_IGN);
astat = signal(SIGALRM, SIG_IGN);
oalarm = alarm(0);
#ifdef WAITPID
do
r = waitpid(popen_pid[f], &status, 0);
while((r == -1) && errno == EINTR);
#else
while(((r = wait(&status)) != popen_pid[f]) && (r != -1))
;
#endif
(void) signal(SIGINT, istat);
(void) signal(SIGQUIT, qstat);
(void) signal(SIGHUP, hstat);
(void) signal(SIGALRM, astat);
if(oalarm)
alarm(oalarm);
/* mark this pipe closed */
popen_pid[f] = 0;
return((r == -1) ? -1 : status);
}
int
system(s)
const char *s;
{
int status;
register pid_t pid;
#ifdef sun
register void (*cstat)(), (*istat)(), (*qstat)();
register int w;
#else
register int (*cstat)(), (*istat)(), (*qstat)(), w;
#endif
if(!_blkchk(s))
return(-1);
cstat = signal(SIGCHLD, SIG_IGN);
switch(pid = fork()) {
case 0:
setuid(getuid());
signal(SIGCHLD, cstat);
execshell(s);
_exit(127);
case -1:
return(-1);
}
istat = signal(SIGINT, SIG_IGN);
qstat = signal(SIGQUIT, SIG_IGN);
#ifdef WAITPID
w = waitpid(pid, &status, 0);
#else
while((w = wait(&status)) != pid && w != -1)
;
#endif
(void) signal(SIGINT, istat);
(void) signal(SIGQUIT, qstat);
(void)signal(SIGCHLD, cstat);
return((w == -1) ? -1 : status);
}
#endif /*UNIX*/
/*-
* Copyright (c) 1980, 1983, 1990 The Regents of the University of California.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)qsort.c 5.9 (Berkeley) 2/23/91";
#endif /* LIBC_SCCS and not lint */
/*
* MTHRESH is the smallest partition for which we compare for a median
* value instead of using the middle value.
*/
#define MTHRESH 6
/*
* THRESH is the minimum number of entries in a partition for continued
* partitioning.
*/
#define THRESH 4
static void insertion_sort();
static void quick_sort();
#ifdef ANSI
/*
* Nightmare definition because different Unix suppliers can't get it right
*/
#if defined(sun) && !defined(solaris)
int
#else
void cdecl
#endif
qsort(bot, nmemb, size, compar)
void *bot;
size_t nmemb, size;
int (cdecl *compar)(const void *, const void *);
#else
#ifdef _HPUX_SOURCE
void
qsort(bot, nmemb, size, compar)
void *bot;
size_t nmemb, size;
int (cdecl *compar)();
#else
qsort(bot, nmemb, size, compar)
char *bot;
int (*compar)();
#endif
#endif
{
if (nmemb <= 1) {
_error(NULL, 0, NULL, "qsort called on 0 or 1 records");
#if defined(sun) && !defined(solaris)
return(0);
#else
return;
#endif
}
if(blklen(bot, false) < (nmemb * size)) {
_error(NULL, 0, NULL, "qsort: buffer isn't that big");
#if defined(sun) && !defined(solaris)
return(0);
#else
return;
#endif
}
if (nmemb >= THRESH)
quick_sort(bot, nmemb, size, compar);
else
insertion_sort(bot, nmemb, size, compar);
#if defined(sun) && !defined(solaris)
return(0);
#endif
}
/*
* Swap two areas of size number of bytes. Although qsort(3) permits random
* blocks of memory to be sorted, sorting pointers is almost certainly the
* common case (and, were it not, could easily be made so). Regardless, it
* isn't worth optimizing; the SWAP's get sped up by the cache, and pointer
* arithmetic gets lost in the time required for comparison function calls.
*/
#define SWAP(a, b) { \
cnt = size; \
do { \
ch = *a; \
*a++ = *b; \
*b++ = ch; \
} while (--cnt); \
}
/*
* Knuth, Vol. 3, page 116, Algorithm Q, step b, argues that a single pass
* of straight insertion sort after partitioning is complete is better than
* sorting each small partition as it is created. This isn't correct in this
* implementation because comparisons require at least one (and often two)
* function calls and are likely to be the dominating expense of the sort.
* Doing a final insertion sort does more comparisons than are necessary
* because it compares the "edges" and medians of the partitions which are
* known to be already sorted.
*
* This is also the reasoning behind selecting a small THRESH value (see
* Knuth, page 122, equation 26), since the quicksort algorithm does less
* comparisons than the insertion sort.
*/
#define SORT(bot, n) { \
if (n > 1) \
if (n == 2) { \
t1 = bot + size; \
if (compar(t1, bot) < 0) \
SWAP(t1, bot); \
} else \
insertion_sort(bot, n, size, compar); \
}
static void
quick_sort(bot, nmemb, size, compar)
register char *bot;
register int size;
int nmemb, (cdecl *compar)();
{
register int cnt;
register unsigned char ch;
register char *top, *mid, *t1, *t2;
register int n1, n2;
char *bsv;
/* bot and nmemb must already be set. */
partition:
/* find mid and top elements */
mid = bot + size * (nmemb >> 1);
top = bot + (nmemb - 1) * size;
/*
* Find the median of the first, last and middle element (see Knuth,
* Vol. 3, page 123, Eq. 28). This test order gets the equalities
* right.
*/
if (nmemb >= MTHRESH) {
n1 = compar(bot, mid);
n2 = compar(mid, top);
if (n1 < 0 && n2 > 0)
t1 = compar(bot, top) < 0 ? top : bot;
else if (n1 > 0 && n2 < 0)
t1 = compar(bot, top) > 0 ? top : bot;
else
t1 = mid;
/* if mid element not selected, swap selection there */
if (t1 != mid) {
SWAP(t1, mid);
mid -= size;
}
}
/* Standard quicksort, Knuth, Vol. 3, page 116, Algorithm Q. */
#define didswap n1
#define newbot t1
#define replace t2
didswap = 0;
for (bsv = bot;;) {
for (; bot < mid && compar(bot, mid) <= 0; bot += size);
while (top > mid) {
if (compar(mid, top) <= 0) {
top -= size;
continue;
}
newbot = bot + size; /* value of bot after swap */
if (bot == mid) /* top <-> mid, mid == top */
replace = mid = top;
else { /* bot <-> top */
replace = top;
top -= size;
}
goto swap;
}
if (bot == mid)
break;
/* bot <-> mid, mid == bot */
replace = mid;
newbot = mid = bot; /* value of bot after swap */
top -= size;
swap: SWAP(bot, replace);
bot = newbot;
didswap = 1;
}
/*
* Quicksort behaves badly in the presence of data which is already
* sorted (see Knuth, Vol. 3, page 119) going from O N lg N to O N^2.
* To avoid this worst case behavior, if a re-partitioning occurs
* without swapping any elements, it is not further partitioned and
* is insert sorted. This wins big with almost sorted data sets and
* only loses if the data set is very strangely partitioned. A fix
* for those data sets would be to return prematurely if the insertion
* sort routine is forced to make an excessive number of swaps, and
* continue the partitioning.
*/
if (!didswap) {
insertion_sort(bsv, nmemb, size, compar);
return;
}
/*
* Re-partition or sort as necessary. Note that the mid element
* itself is correctly positioned and can be ignored.
*/
#define nlower n1
#define nupper n2
bot = bsv;
nlower = (mid - bot) / size; /* size of lower partition */
mid += size;
nupper = nmemb - nlower - 1; /* size of upper partition */
/*
* If must call recursively, do it on the smaller partition; this
* bounds the stack to lg N entries.
*/
if (nlower > nupper) {
if (nupper >= THRESH)
quick_sort(mid, nupper, size, compar);
else {
SORT(mid, nupper);
if (nlower < THRESH) {
SORT(bot, nlower);
return;
}
}
nmemb = nlower;
} else {
if (nlower >= THRESH)
quick_sort(bot, nlower, size, compar);
else {
SORT(bot, nlower);
if (nupper < THRESH) {
SORT(mid, nupper);
return;
}
}
bot = mid;
nmemb = nupper;
}
goto partition;
/* NOTREACHED */
}
static void
insertion_sort(bot, nmemb, size, compar)
char *bot;
register int size;
int nmemb, (cdecl *compar)();
{
register int cnt;
register unsigned char ch;
register char *s1, *s2, *t1, *t2, *top;
/*
* A simple insertion sort (see Knuth, Vol. 3, page 81, Algorithm
* S). Insertion sort has the same worst case as most simple sorts
* (O N^2). It gets used here because it is (O N) in the case of
* sorted data.
*/
top = bot + nmemb * size;
for (t1 = bot + size; t1 < top;) {
for (t2 = t1; (t2 -= size) >= bot && compar(t1, t2) < 0;);
if (t1 != (t2 += size)) {
/* Bubble bytes up through each element. */
for (cnt = size; cnt--; ++t1) {
ch = *t1;
for (s1 = s2 = t1; (s2 -= size) >= t2; s1 = s2)
*s1 = *s2;
*s1 = ch;
}
} else
t1 += size;
}
}
#if 0
mprotect(addr, len, prot)
caddr_t addr;
{
errno = EINVAL;
return(-1);
}
#endif
#ifdef MAX_STACK_DEPTH
#define SHORT_CALLSTACK_SIZE 5
static int fstk_i;
static int no_call_graph = 0;
#if (defined(vax) || (defined(sun) && !defined(sun4)))
#define get_current_fp(first_local) ((unsigned)&(first_local) + 4)
#endif
#if (defined(vax) || defined(sun))
#include <sys/types.h>
#ifdef solaris
#include <sys/frame.h>
#else
#include <frame.h>
#endif
#define prev_fp_from_fp(fp) (unsigned)(((struct frame *)(fp))->fr_savfp)
#define ret_addr_from_fp(fp) (unsigned)(((struct frame *)(fp))->fr_savpc)
#endif
static void
mprof(sp)
SLOT *sp;
{
unsigned first_local; /* WARNING -- This MUST be the first
* local variable in this function.
*/
unsigned fp;
unsigned ret_addr;
register int i;
#ifdef mips
pPDR pdr;
#endif
fstk_i = 0;
/* gather return addresses from the callstack
*/
#ifndef mips
fp = get_current_fp(first_local);
ret_addr = ret_addr_from_fp(fp);
/* Step back 1 frame (to the caller of malloc)
*/
fp = prev_fp_from_fp(fp);
ret_addr = ret_addr_from_fp(fp);
i = 0;
while((ret_addr > mp_root_address) && (i < MAX_STACK_DEPTH)){
if (no_call_graph && (fstk_i > SHORT_CALLSTACK_SIZE))
break;
sp->s_history[i++] = (caddr_t)ret_addr;
fstk_i++;
fp = prev_fp_from_fp(fp);
if (fp == 0) break;
ret_addr = ret_addr_from_fp(fp);
}
#else
get31();
pdr = getpdr(intloc);
getsp();
fp = intloc;
ret_addr = getretaddr(&fp, pdr); /* fp is changed */
/* Step back 1 frame (to the caller of malloc) */
pdr = getpdr(ret_addr);
ret_addr = getretaddr(&fp, pdr); /* fp is changed */
while (ret_addr > mp_root_address) {
if (no_call_graph && (fstk_i > SHORT_CALLSTACK_SIZE))
break;
fpcs[fstk_i] = ret_addr;
fstk_i++;
pdr = getpdr(ret_addr);
ret_addr = getretaddr(&fp, pdr); /* fp is updated */
}
#endif
}
static struct sym {
char sym[33];
caddr_t off;
} *s;
static unsigned long size_s;
static void
st_read()
{
struct exec e;
register struct nlist *n, *nlist;
register struct sym *sym;
register int fd;
register long l;
if((exec_name == NULL) || (s != NULL))
return;
fd = open(exec_name, 0);
read(fd, &e, sizeof(e));
if(e.a_syms == 0) {
/*puts("stripped");*/
close(fd);
return;
}
in_db++;
lseek(fd, N_SYMOFF(e), SEEK_SET);
nlist = n = (struct nlist *)db_mallocchk(e.a_syms * sizeof(struct nlist), NULL, 0);
sym = s = (struct sym *)db_mallocchk(e.a_syms * sizeof(struct sym), NULL, 0);
read(fd, n, e.a_syms * sizeof(struct nlist));
size_s = 0;
for(l = 0; l < e.a_syms; l++, n++) {
register char *ptr;
if(n->n_type&N_STAB) {
/* compiled with `-g' */
if(n->n_type != N_FUN)
continue;
} else {
if((n->n_type&N_TYPE) != N_TEXT)
continue;
if(!(n->n_type&N_EXT))
continue;
}
sym->off = (caddr_t)n->n_value;
lseek(fd, N_STROFF(e) + n->n_un.n_strx, SEEK_SET);
read(fd, sym->sym, sizeof(sym->sym) - 1);
if(ptr = strrchr(sym->sym, ':'))
*ptr = '\0';
size_s++;
sym++;
}
#ifdef assert
assert(size_s <= e.a_syms);
#endif
close(fd);
db_freechk(nlist, NULL, 0);
if(size_s == 0) {
db_freechk(s, NULL, 0);
s = NULL;
} else
s = (struct sym *)db_reallocchk(s, size_s * sizeof(struct sym), NULL, 0);
in_db--;
}
static const char *
symname(pc)
caddr_t pc;
{
register struct sym *sym, *keep;
register long l;
register int diff;
if(s == NULL)
return(NULL);
diff = 0;
keep = NULL;
for(sym = s, l = 0; l < size_s; l++, sym++)
if((pc > sym->off) && ((pc < sym->off + diff) || (diff == 0))) {
diff = pc - sym->off;
keep = sym;
}
if(keep)
return((keep->sym[0] == '_') ? &keep->sym[1] : keep->sym);
return(NULL);
}
#endif /*MAX_STACK_DEPTH*/
#ifdef UNIX
static void
slots_rw(void)
{
rw((caddr_t)slots, maxslots * sizeof(SLOT));
}
static void
slots_readonly(void)
{
ro((caddr_t)slots, maxslots * sizeof(SLOT));
}
static void
ro(const caddr_t addr, size_t size)
{
#ifdef MPROTECT
mprotect(addr, size, PROT_READ);
#endif
#ifdef WATCH
struct {
int ctl;
prwatch_t p;
} msg;
if(watchfd < 0) {
watchfd = open("/proc/self/ctl", O_WRONLY|O_EXCL);
if(watchfd < 0)
return;
}
msg.p.pr_vaddr = (uintptr_t)addr;
msg.p.pr_size = size;
msg.p.pr_wflags = WA_WRITE;
msg.ctl = PCWATCH;
if(write(watchfd, &msg, sizeof(msg)) < 0)
puts("ro");
#endif
}
static void
rw(const caddr_t addr, size_t size)
{
#ifdef MPROTECT
mprotect(addr, size, PROT_READ|PROT_WRITE);
#endif
#ifdef WATCH
struct {
int ctl;
prwatch_t p;
} msg;
if(watchfd < 0)
return;
msg.p.pr_vaddr = (uintptr_t)addr;
msg.p.pr_size = size;
msg.p.pr_wflags = 0;
msg.ctl = PCWATCH;
if(write(watchfd, &msg, sizeof(msg)) < 0)
puts("rw");
#endif
}
static void
none(const caddr_t addr, size_t size)
{
#ifdef MPROTECT
mprotect(addr, size, PROT_NONE);
#endif
#ifdef WATCH
struct {
int ctl;
prwatch_t p;
} msg;
if(watchfd < 0) {
watchfd = open("/proc/self/ctl", O_WRONLY|O_EXCL);
if(watchfd < 0)
return;
}
msg.p.pr_vaddr = (uintptr_t)addr;
msg.p.pr_size = size;
msg.p.pr_wflags = WA_WRITE|WA_READ;
msg.ctl = PCWATCH;
if(write(watchfd, &msg, sizeof(msg)) < 0)
puts("none");
#endif
}
#endif /*UNIX*/
#endif /*CL_DEBUG*/