/*
 * CDE - Common Desktop Environment
 *
 * Copyright (c) 1993-2012, The Open Group. All rights reserved.
 *
 * These libraries and programs are free software; you can
 * redistribute them and/or modify them under the terms of the GNU
 * Lesser General Public License as published by the Free Software
 * Foundation; either version 2 of the License, or (at your option)
 * any later version.
 *
 * These libraries and programs are distributed in the hope that
 * they will be useful, but WITHOUT ANY WARRANTY; without even the
 * implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
 * PURPOSE. See the GNU Lesser General Public License for more
 * details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with these libraries and programs; if not, write
 * to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
 * Floor, Boston, MA 02110-1301 USA
 */
/* $XConsortium: malloc.c /main/2 1996/05/08 20:00:43 drk $ */
/***************************************************************
*                                                              *
*                      AT&T - PROPRIETARY                      *
*                                                              *
*         THIS IS PROPRIETARY SOURCE CODE LICENSED BY          *
*                          AT&T CORP.                          *
*                                                              *
*                Copyright (c) 1995 AT&T Corp.                 *
*                     All Rights Reserved                      *
*                                                              *
*           This software is licensed by AT&T Corp.            *
*       under the terms and conditions of the license in       *
*       http://www.research.att.com/orgs/ssr/book/reuse        *
*                                                              *
*               This software was created by the               *
*           Software Engineering Research Department           *
*                    AT&T Bell Laboratories                    *
*                                                              *
*               For further information contact                *
*                     gsf@research.att.com                     *
*                                                              *
***************************************************************/
#include	<ast_lib.h>

#if _std_malloc || _INSTRUMENT_ || cray

int	_STUB_malloc;

#else

#include	"vmhdr.h"

/*	malloc compatibility functions.
**	These are aware of debugging/profiling and driven by the environment variables:
**	VMETHOD: select an allocation method by name.
**	VMPROFILE: if is a file name, profile memory usage and write output to it.
**	VMDEBUG:
**		a:	abort on any warning
**		#:	period to check arena.
**		0x#:	address to watch.
**	VMTRACE: if is a file name, turn on tracing and output
**		trace data to the given file.
**
**	Written by (Kiem-)Phong Vo, kpv@research.att.com, 01/16/94.
*/

#if !_PACKAGE_ast
_BEGIN_EXTERNS_
extern int	atexit _ARG_(( void(*)() ));
extern char*	getenv _ARG_(( const char* ));
extern int	creat _ARG_(( const char*, int ));
extern int	close _ARG_(( int ));
extern int	getpid _ARG_(( void ));
_END_EXTERNS_
#endif

#ifdef malloc
#undef malloc
#endif
#ifdef free
#undef free
#endif
#ifdef realloc
#undef realloc
#endif
#ifdef calloc
#undef calloc
#endif
#ifdef cfree
#undef cfree
#endif
#ifdef memalign
#undef memalign
#endif
#ifdef valloc
#undef valloc
#endif

#if __STD_C
static ulong atou(char** sp)
#else
static ulong atou(sp)
char**	sp;
#endif
{
	char*	s = *sp;
	ulong	v = 0;

	if(s[0] == '0' && (s[1] == 'x' || s[1] == 'X') )
	{	for(s += 2; *s; ++s)
		{	if(*s >= '0' && *s <= '9')
				v = (v << 4) + (*s - '0');
			else if(*s >= 'a' && *s <= 'f')
				v = (v << 4) + (*s - 'a') + 10;
			else if(*s >= 'A' && *s <= 'F')
				v = (v << 4) + (*s - 'A') + 10;
			else break;
		}
	}
	else
	{	for(; *s; ++s)
		{	if(*s >= '0' && *s <= '9')
				v = v*10 + (*s - '0');
			else break;
		}
	}

	*sp = s;
	return v;
}

static int		_Vmflinit = 0;
static ulong		_Vmdbcheck = 0;
static ulong		_Vmdbtime = 0;
static int		_Vmpffd = -1;
#define VMFLINIT() \
	{ if(!_Vmflinit)	vmflinit(); \
	  if(_Vmdbcheck && (_Vmdbtime++ % _Vmdbcheck) == 0 && \
	     Vmregion->meth.meth == VM_MTDEBUG) \
		vmdbcheck(Vmregion); \
	}

#if __STD_C
static char* insertpid(char* begs, char* ends)
#else
static char* insertpid(begs,ends)
char*	begs;
char*	ends;
#endif
{	int	pid;
	char*	s;

	if((pid = getpid()) < 0)
		return NIL(char*);

	s = ends;
	do
	{	if(s == begs)
			return NIL(char*);
		*--s = '0' + pid%10;
	} while((pid /= 10) > 0);
	while(s < ends)
		*begs++ = *s++;

	return begs;
}

#if __STD_C
static createfile(char* file)
#else
static createfile(file)
char*	file;
#endif
{
	char	buf[1024];
	char	*next, *endb;

	next = buf;
	endb = buf + sizeof(buf);
	while(*file)
	{	if(*file == '%')
		{	switch(file[1])
			{
			case 'p' :
				if(!(next = insertpid(next,endb)) )
					return -1;
				file += 2;
				break;
			default :
				goto copy;
			}
		}
		else
		{ copy:
			*next++ = *file++;
		}

		if(next >= endb)
			return -1;
	}

	*next = '\0';
	return creat(buf,0644);
}

static void pfprint()
{
	if(Vmregion->meth.meth == VM_MTPROFILE)
		vmprofile(Vmregion,_Vmpffd);
}

static vmflinit()
{
	char*		env;
	Vmalloc_t*	vm;
	int		fd;
	ulong		addr;

	/* this must be done now to avoid any inadvertent recursion (more below) */
	_Vmflinit = 1;

	/* if getenv() calls malloc(), this may not be caught by the eventual region */
	vm = NIL(Vmalloc_t*);
	if((env = getenv("VMETHOD")) )
	{	if(strcmp(env,"Vmdebug") == 0 || strcmp(env,"vmdebug") == 0)
			vm = vmopen(Vmdcsbrk,Vmdebug,0);
		else if(strcmp(env,"Vmprofile") == 0 || strcmp(env,"vmprofile") == 0 )
			vm = vmopen(Vmdcsbrk,Vmprofile,0);
		else if(strcmp(env,"Vmlast") == 0 || strcmp(env,"vmlast") == 0 )
			vm = vmopen(Vmdcsbrk,Vmlast,0);
		else if(strcmp(env,"Vmpool") == 0 || strcmp(env,"vmpool") == 0 )
			vm = vmopen(Vmdcsbrk,Vmpool,0);
		else if(strcmp(env,"Vmbest") == 0 || strcmp(env,"vmbest") == 0 )
			vm = Vmheap;
	}

	if((!vm || vm->meth.meth == VM_MTDEBUG) &&
	   (env = getenv("VMDEBUG")) && env[0] )
	{	if(vm || (vm = vmopen(Vmdcsbrk,Vmdebug,0)) )
		{	reg int	setcheck = 0;

			while(*env)
			{	if(*env == 'a')
					vmset(vm,VM_DBABORT,1);

				if(*env < '0' || *env > '9')
					env += 1;
				else if(env[0] == '0' &&
					(env[1] == 'x' || env[1] == 'X') )
				{	if((addr = atou(&env)) != 0)
						vmdbwatch((Void_t*)addr);
				}
				else
				{	_Vmdbcheck = atou(&env);
					setcheck = 1;
				}
			}
			if(!setcheck)
				_Vmdbcheck = 1;
		}
	}

	if((!vm || vm->meth.meth == VM_MTPROFILE) &&
	   (env = getenv("VMPROFILE")) && env[0] )
	{	_Vmpffd = createfile(env);
		if(!vm)
			vm = vmopen(Vmdcsbrk,Vmprofile,0);
	}

	/* slip in the new region now so that malloc() will work fine */
	if(vm)
		Vmregion = vm;

	/* turn on tracing if requested */
	if((env = getenv("VMTRACE")) && env[0] && (fd = createfile(env)) >= 0)
	{	vmset(Vmregion,VM_TRACE,1);
		vmtrace(fd);
	}

	/* make sure that profile data is output upon exiting */
	if(vm && vm->meth.meth == VM_MTPROFILE)
	{	if(_Vmpffd < 0)
			_Vmpffd = 2;
		/* this may wind up calling malloc(), but region is ok now */
		atexit(pfprint);
	}
	else if(_Vmpffd >= 0)
	{	close(_Vmpffd);
		_Vmpffd = -1;
	}

	return 0;
}

#if __STD_C
Void_t* malloc(reg size_t size)
#else
Void_t* malloc(size)
reg size_t	size;
#endif
{
	VMFLINIT();
	return (*Vmregion->meth.allocf)(Vmregion,size);
}

#if __STD_C
Void_t* realloc(reg Void_t* data, reg size_t size)
#else
Void_t* realloc(data,size)
reg Void_t*	data;	/* block to be reallocated	*/
reg size_t	size;	/* new size			*/
#endif
{
	VMFLINIT();
	return (*Vmregion->meth.resizef)(Vmregion,data,size,VM_RSCOPY|VM_RSFREE);
}

#if __STD_C
void free(reg Void_t* data)
#else
void free(data)
reg Void_t*	data;
#endif
{
	VMFLINIT();
	(void)(*Vmregion->meth.freef)(Vmregion,data);
}

#if __STD_C
Void_t* calloc(reg size_t n_obj, reg size_t s_obj)
#else
Void_t* calloc(n_obj, s_obj)
reg size_t	n_obj;
reg size_t	s_obj;
#endif
{
	reg int*	di;
	reg char*	dc;
	reg Void_t*	data;

	VMFLINIT();
	if(!(data = (*Vmregion->meth.allocf)(Vmregion,n_obj*s_obj)) )
		return NIL(Void_t*);

	n_obj *= s_obj;
	s_obj  = n_obj/sizeof(int);
	n_obj -= s_obj*sizeof(int);
	for(di = (int*)data; s_obj > 0; --s_obj )
		*di++ = 0;
	for(dc = (char*)di; n_obj > 0; --n_obj )
		*dc++ = 0;

	return data;
}

#if __STD_C
void cfree(reg Void_t* data)
#else
void cfree(data)
reg Void_t*	data;
#endif
{
	VMFLINIT();
	(void)(*Vmregion->meth.freef)(Vmregion,data);
}

#if __STD_C
Void_t* memalign(reg size_t align, reg size_t size)
#else
Void_t* memalign(align, size)
reg size_t	align;
reg size_t	size;
#endif
{
	VMFLINIT();
	return (*Vmregion->meth.alignf)(Vmregion,size,align);
}

#if __STD_C
Void_t* valloc(reg size_t size)
#else
Void_t* valloc(size)
reg size_t	size;
#endif
{
	VMFLINIT();
	GETPAGESIZE(_Vmpagesize);
	return (*Vmregion->meth.alignf)(Vmregion,size,_Vmpagesize);
}

/* the below collection of functions are aware of files and line numbers */
#define VMSETFL(f,l)	(_Vmfile = (f), _Vmline = (l))
#if __STD_C
Void_t* _vmflmalloc(size_t size, char* file, int line)
#else
Void_t* _vmflmalloc(size, file, line)
size_t	size;
char*	file;
int	line;
#endif
{
	VMFLINIT();
	VMSETFL(file,line);
	return malloc(size);
}

#if __STD_C
Void_t* _vmflrealloc(Void_t* data, size_t size, char* file, int line)
#else
Void_t* _vmflrealloc(data,size,file,line)
Void_t*	data;
size_t	size;
char*	file;
int	line;
#endif
{
	VMFLINIT();
	VMSETFL(file,line);
	return realloc(data,size);
}

#if __STD_C
void _vmflfree(Void_t* data, char* file, int line)
#else
void _vmflfree(data,file,line)
Void_t*	data;
char*	file;
int	line;
#endif
{
	VMFLINIT();
	VMSETFL(file,line);
	free(data);
}

#if __STD_C
Void_t* _vmflcalloc(size_t n_obj, size_t s_obj, char* file, int line)
#else
Void_t* _vmflcalloc(n_obj, s_obj, file, line)
size_t	n_obj;
size_t	s_obj;
char*	file;
int	line;
#endif
{
	VMFLINIT();
	VMSETFL(file,line);
	return calloc(n_obj, s_obj);
}

#if __STD_C
Void_t* _vmflmemalign(size_t align, size_t size, char* file, int line)
#else
Void_t* _vmflmemalign(align, size, file, line)
size_t	align;
size_t	size;
char*	file;
int	line;
#endif
{
	VMFLINIT();
	VMSETFL(file,line);
	return memalign(size,align);
}

#if __STD_C
Void_t* _vmflvalloc(size_t size, char* file, int line)
#else
Void_t* _vmflvalloc(size, file, line)
size_t	size;
char*	file;
int	line;
#endif
{
	VMFLINIT();
	VMSETFL(file,line);
	return valloc(size);
}

#endif
