/* $XConsortium: utils.c,v 1.7 91/07/25 12:15:52 keith Exp $ */
/*
 * misc os utilities
 */
/*
 * Copyright 1990, 1991 Network Computing Devices;
 * Portions Copyright 1987 by Digital Equipment Corporation and the
 * Massachusetts Institute of Technology
 *
 * Permission to use, copy, modify, and distribute this protoype software
 * and its documentation to Members and Affiliates of the MIT X Consortium
 * any purpose and without fee is hereby granted, provided
 * that the above copyright notice appear in all copies and that both that
 * copyright notice and this permission notice appear in supporting
 * documentation, and that the names of Network Computing Devices, Digital or
 * MIT not be used in advertising or publicity pertaining to distribution of
 * the software without specific, written prior permission.
 *
 * NETWORK COMPUTING DEVICES, DIGITAL AND MIT DISCLAIM ALL WARRANTIES WITH
 * REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS, IN NO EVENT SHALL NETWORK COMPUTING DEVICES, DIGITAL OR MIT BE
 * LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 *
 * $NCDId: @(#)utils.c,v 4.9 1991/07/09 14:08:13 lemke Exp $
 *
 */

#include	<stdio.h>
#include	<X11/Xos.h>
#include	"misc.h"
#include	"globals.h"

#ifndef X_NOT_POSIX
#ifdef _POSIX_SOURCE
#include <limits.h>
#else
#define _POSIX_SOURCE
#include <limits.h>
#undef _POSIX_SOURCE
#endif
#endif /* X_NOT_POSIX */
#ifndef PATH_MAX
#include <sys/param.h>
#ifdef MAXPATHLEN
#define PATH_MAX MAXPATHLEN
#else
#define PATH_MAX 1024
#endif
#endif /* PATH_MAX */

#ifdef SIGNALRETURNSINT
#define SIGVAL int
#else
#define SIGVAL void
#endif

extern char *configfilename;
char       *progname;
Bool        CloneSelf;
int         ListenSock = -1;
extern int  ListenPort;

SIGVAL
AutoResetServer()
{

#ifdef DEBUG
    fprintf(stderr, "got a reset signal\n");
#endif

    dispatchException |= DE_RESET;
    isItTimeToYield = TRUE;
}

SIGVAL
GiveUp()
{

#ifdef DEBUG
    fprintf(stderr, "got a TERM signal\n");
#endif

    dispatchException |= DE_TERMINATE;
    isItTimeToYield = TRUE;
}

SIGVAL
ServerReconfig()
{

#ifdef DEBUG
    fprintf(stderr, "got a re-config signal\n");
#endif

    dispatchException |= DE_RECONFIG;
    isItTimeToYield = TRUE;
}

SIGVAL
ServerCacheFlush()
{

#ifdef DEBUG
    fprintf(stderr, "got a flush signal\n");
#endif

    dispatchException |= DE_FLUSH;
    isItTimeToYield = TRUE;
}

long
GetTimeInMillis()
{
    struct timeval tp;

    gettimeofday(&tp, 0);
    return ((tp.tv_sec * 1000) + (tp.tv_usec / 1000));
}

static void
usage()
{
    fprintf(stderr, "%s: [-cf config-file] [-p tcp_port] [-s server_number]\n", progname);
    exit(-1);
}

/* ARGSUSED */
void
ProcessCmdLine(argc, argv)
    int         argc;
    char      **argv;
{
    int         i;

#ifdef MEMBUG
    {
	extern pointer MemoryAllocationBase;

	if (!MemoryAllocationBase)
	    MemoryAllocationBase = (pointer) sbrk(0);
    }
#endif

    progname = argv[0];
    for (i = 1; i < argc; i++) {
	if (!strcmp(argv[i], "-port")) {
	    if (argv[i + 1])
		ListenPort = atoi(argv[++i]);
	    else
		usage();
	} else if (!strcmp(argv[i], "-ls")) {
	    if (argv[i + 1])
		ListenSock = atoi(argv[++i]);
	    else
		usage();
	} else if (!strcmp(argv[i], "-cf") || !strcmp(argv[i], "-config")) {
	    if (argv[i + 1])
		configfilename = argv[++i];
	    else
		usage();
	}
#ifdef MEMBUG
	else if ( strcmp( argv[i], "-alloc") == 0)
	{
	    extern unsigned long    MemoryFail;
	    if(++i < argc)
	        MemoryFail = atoi(argv[i]);
	    else
		usage ();
	}
	else if ( strcmp ( argv[i], "validateMemory") == 0)
	{
	    extern unsigned long MemoryValidate;
	    MemoryValidate = 1;
	}
	else if ( strcmp ( argv[i], "neverFreeMemory") == 0)
	{
	    extern unsigned long MemoryNeverFree;
	    MemoryNeverFree = 1;
	}
#endif
	else
	    usage();
    }
}


#ifndef SPECIAL_MALLOC

unsigned long	Must_have_memory;

#ifdef MEMBUG
#define FIRSTMAGIC 0x11aaaa11
#define SECONDMAGIC 0x22aaaa22
#define FREEDMAGIC  0x33aaaa33
#define BLANKMAGIC  0x44aaaa44
#define ALLOCMAGIC  0x55aaaa55
#define MEM_FAIL_SCALE	100000

typedef struct _MallocHeader	*MallocHeaderPtr;

typedef struct _MallocHeader {
	unsigned long	amount;
	unsigned long	time;
	MallocHeaderPtr	prev;
	MallocHeaderPtr	next;
	unsigned long	magic;
} MallocHeaderRec;

typedef struct _MallocTrailer {
	unsigned long	magic;
} MallocTrailerRec, *MallocTrailerPtr;

unsigned long	MemoryAllocTime;
unsigned long	MemoryAllocBreakpoint = ~0;
unsigned long	MemoryFreeBreakpoint = ~0;
unsigned long	MemoryActive = 0;
unsigned long	MemoryValidate;
unsigned long	MemoryNeverFree;
unsigned long	MemoryFail;
pointer		MemoryAllocationBase = NULL;

static void	CheckNode ();

MallocHeaderPtr	MemoryInUse;
MallocHeaderPtr	MemoryFreed;

#define request(amount)	((amount) + sizeof (MallocHeaderRec) + sizeof (MallocTrailerRec))
#define Header(ptr)	((MallocHeaderPtr) (((char *) ptr) - sizeof (MallocHeaderRec)))
#define Trailer(ptr)	((MallocTrailerPtr) (((char *) ptr) + Header(ptr)->amount))

static unsigned long *
SetupBlock(ptr, amount)
    unsigned long   *ptr;
{
    MallocHeaderPtr	head = (MallocHeaderPtr) ptr;
    MallocTrailerPtr	tail = (MallocTrailerPtr) (((char *) ptr) + amount + sizeof (MallocHeaderRec));

    MemoryActive += amount;
    head->magic = FIRSTMAGIC;
    head->amount = amount;
    if (MemoryAllocTime == MemoryAllocBreakpoint)
	head->amount = amount;
    head->time = MemoryAllocTime++;
    head->next = MemoryInUse;
    head->prev = 0;
    if (MemoryInUse)
	MemoryInUse->prev = head;
    MemoryInUse = head;

    tail->magic = SECONDMAGIC;
    
    return (unsigned long *)(((char *) ptr) + sizeof (MallocHeaderRec));
}

ValidateAllActiveMemory ()
{
    MallocHeaderPtr	head;
    MallocTrailerPtr	tail;

    for (head = MemoryInUse; head; head = head->next)
    {
	tail = (MallocTrailerPtr) (((char *) (head + 1)) + head->amount);
    	if (head->magic == FREEDMAGIC)
	    FatalError("Free data on active list");
    	if(head->magic != FIRSTMAGIC || tail->magic != SECONDMAGIC)
	    FatalError("Garbage object on active list");
    }
    for (head = MemoryFreed; head; head = head->next)
    {
	tail = (MallocTrailerPtr) (((char *) (head + 1)) + head->amount);
	if (head->magic != FREEDMAGIC || tail->magic != FREEDMAGIC)
	    FatalError("Non free data on free list");
	if (!CheckMemoryContents (head, BLANKMAGIC))
	    FatalError("Freed data reused");
    }
}

FillMemoryContents (head, value)
    MallocHeaderPtr head;
    long	    value;
{
    int		    count;
    long	    *store;

    count = head->amount / sizeof (long);
    store = (long *) (head + 1);
    while (count--)
	*store++ = value;
}

CheckMemoryContents (head, value)
    MallocHeaderPtr head;
    long	    value;
{
    int		    count;
    long	    *check;

    count = head->amount / sizeof (long);
    check = (long *) (head + 1);
    while (count--)
	if (*check++ != value)
	    return FALSE;
    return TRUE;
}

#endif

/* FSalloc -- FS's internal memory allocator.  Why does it return unsigned
 * int * instead of the more common char *?  Well, if you read K&R you'll
 * see they say that alloc must return a pointer "suitable for conversion"
 * to whatever type you really want.  In a full-blown generic allocator
 * there's no way to solve the alignment problems without potentially
 * wasting lots of space.  But we have a more limited problem. We know
 * we're only ever returning pointers to structures which will have to
 * be long word aligned.  So we are making a stronger guarantee.  It might
 * have made sense to make FSalloc return char * to conform with people's
 * expectations of malloc, but this makes lint happier.
 */

unsigned long * 
FSalloc (amount)
    unsigned long amount;
{
    char		*malloc();
    register pointer  ptr;
	
    if ((long)amount < 0)
	return (unsigned long *)NULL;
    if (amount == 0)
	amount++;
    /* aligned extra on long word boundary */
    amount = (amount + 3) & ~3;
#ifdef MEMBUG
    if (MemoryValidate)
	ValidateAllActiveMemory ();
    if (!Must_have_memory && MemoryFail &&
	((random() % MEM_FAIL_SCALE) < MemoryFail))
	return (unsigned long *)NULL;
    if (ptr = (pointer)malloc(request(amount)))
    {
	unsigned long	*ret;
	ret = SetupBlock (ptr, amount);
	FillMemoryContents ((MallocHeaderPtr) ptr, ALLOCMAGIC);
	return ret;
    }
#else
    if (ptr = (pointer)malloc(amount))
	return (unsigned long *)ptr;
#endif
    if (Must_have_memory)
	FatalError("Out of memory");
    return (unsigned long *)NULL;
}

/*****************
 * FScalloc
 *****************/

unsigned long *
FScalloc (amount)
    unsigned long   amount;
{
    unsigned long   *ret;

    ret = FSalloc (amount);
    if (ret)
	bzero ((char *) ret, (int) amount);
    return ret;
}

/*****************
 * FSrealloc
 *****************/

unsigned long *
FSrealloc (ptr, amount)
    register pointer ptr;
    unsigned long amount;
{
    char *malloc();
    char *realloc();

#ifdef MEMBUG
    if (ptr)
    {
    	if (MemoryValidate)
	    ValidateAllActiveMemory ();
    	if ((long)amount <= 0)
    	{
	    if (!amount)
	    	FSfree(ptr);
	    return (unsigned long *)NULL;
    	}
    	if (!Must_have_memory && MemoryFail &&
	    ((random() % MEM_FAIL_SCALE) < MemoryFail))
	    return (unsigned long *)NULL;
    	amount = (amount + 3) & ~3;
	CheckNode(ptr);
	ptr = (pointer)realloc((char *) Header (ptr), request(amount));
	if (ptr)
	    return SetupBlock (ptr, amount);
    }
    else
    {
	return FSalloc (amount);
    }
#else
    if ((long)amount <= 0)
    {
	if (ptr && !amount)
	    free(ptr);
	return (unsigned long *)NULL;
    }
    amount = (amount + 3) & ~3;
    if (ptr)
        ptr = (pointer)realloc((char *)ptr, amount);
    else
	ptr = (pointer)malloc(amount);
    if (ptr)
        return (unsigned long *)ptr;
#endif
    if (Must_have_memory)
	FatalError("Out of memory");
    return (unsigned long *)NULL;
}
                    
/*****************
 *  FSfree
 *    calls free 
 *****************/    

void
FSfree(ptr)
    register pointer ptr;
{
#ifdef MEMBUG
    if (MemoryValidate)
	ValidateAllActiveMemory ();
    if (ptr)
    {
	MallocHeaderPtr	    head;
	MallocTrailerPtr    trail;

	CheckNode(ptr);
	head = Header(ptr);
	trail = Trailer(ptr);
	if (head->time == MemoryFreeBreakpoint)
	    head->magic = FIRSTMAGIC;
	head->magic = FREEDMAGIC;
	trail->magic = FREEDMAGIC;
	FillMemoryContents (head, BLANKMAGIC);
	if (MemoryNeverFree)
	{
	    head->prev = 0;
	    head->next = MemoryFreed;
	    MemoryFreed = head;
	}
	else
	    free ((char *) head);
    }
#else
    if (ptr)
	free((char *)ptr); 
#endif
}

#ifdef MEMBUG
static void
CheckNode(ptr)
    pointer ptr;
{
    MallocHeaderPtr	head;
    MallocHeaderPtr	f, prev;

    if (ptr < MemoryAllocationBase)
	FatalError("Trying to free static storage");
    head = Header(ptr);
    if (((pointer) head) < MemoryAllocationBase)
	FatalError("Trying to free static storage");
    if (head->magic == FREEDMAGIC)
	FatalError("Freeing something already freed");
    if(head->magic != FIRSTMAGIC || Trailer(ptr)->magic != SECONDMAGIC)
	FatalError("Freeing a garbage object");
    if(head->prev)
	head->prev->next = head->next;
    else
	MemoryInUse = head->next;
    if (head->next)
	head->next->prev = head->prev;
    MemoryActive -= head->amount;
}

DumpMemoryInUse (time)
    unsigned long   time;
{
    MallocHeaderPtr	head;

    for (head = MemoryInUse; head; head = head->next)
	if (head->time >= time)
	    fprintf (stderr, "0x%08x %5d %6d\n", head,
					head->amount,
					head->time);
}

static unsigned long	MarkedTime;

MarkMemoryTime ()
{
    MarkedTime = MemoryAllocTime;
}

DumpMemorySince ()
{
    DumpMemoryInUse (MarkedTime);
}
#endif
#endif /* SPECIAL_MALLOC */
