mirror of https://github.com/Cisco-Talos/clamav
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.
4166 lines
94 KiB
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*/
|
|
|