/************************************************************************
 *   IRC - Internet Relay Chat, src/support.c
 *   Copyright (C) 1990, 1991 Armin Gruner
 *
 *   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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA.
 */

/* $Id: support.c,v 1.4 2005/07/05 21:53:42 sheik Exp $ */

#include "struct.h"
#include "common.h"
#include "sys.h"
#include "h.h"
#include "numeric.h"
#include "memcount.h"

#define FOREVER for(;;)

extern int  errno;		/*
				 * ...seems that errno.h doesn't define this
				 * * everywhere 
				 */
extern void outofmemory();

#if !defined( HAVE_STRTOKEN )
/*
 * *  strtoken.c --   walk through a string of tokens, using a set
 * of separators 
 * argv 9/90
 * 
 *      $Id: support.c,v 1.4 2005/07/05 21:53:42 sheik Exp $
 */

char *strtoken(char **save, char *str, char *fs)
{
    char       *pos = *save;	/* keep last position across calls */
    char   *tmp;
    
    if (str)
	pos = str;		/* new string scan */
    
    while (pos && *pos && strchr(fs, *pos) != NULL)
	pos++;			/* skip leading separators */
    
    if (!pos || !*pos)
	return (pos = *save = NULL);	/* string contains only sep's */
    
    tmp = pos;			/* now, keep position of the token */

    while (*pos && strchr(fs, *pos) == NULL)
	pos++;			/* skip content of the token */

    if (*pos)
	*pos++ = '\0';		/* remove first sep after the token */
    else
	pos = NULL;		/* end of string */

    *save = pos;
    return (tmp);
}
#endif /* !HAVE_STRTOKEN */

#if !defined( HAVE_STRTOK )
/* NOT encouraged to use! */

char *strtok(char *str, char *fs)
{
    static char *pos;
    return strtoken(&pos, str, fs);
}
#endif /* !HAVE_STRTOK */

#if !defined( HAVE_STRERROR )
/*
 * strerror - return an appropriate system error string to a given errno
 * 
 */

char *strerror(int err_no)
{
#if !defined(__FreeBSD__) && !defined(__NetBSD__)
    extern char *sys_errlist[];	/* Sigh... hopefully on all systems */
    extern int  sys_nerr;
#endif
    static char buff[40];
    char       *errp;

    errp = (err_no > sys_nerr ? (char *) NULL : sys_errlist[err_no]);

    if (errp == (char *) NULL)
    {
	errp = buff;
	(void) sprintf(errp, "Unknown Error %d", err_no);
    }
    return errp;
}
#endif /* !HAVE_STRERROR */

/*
 * inetntoa  --    changed name to remove collision possibility
 * and so behaviour is gaurunteed to take a pointer arg.
 *                       -avalon 23/11/92
 * inet_ntoa -- returned the dotted notation of a given 
 * internet number (some ULTRIX don't have this)
 */

char *inetntoa(char *in)
{
    static char buf[16];
    u_char *s = (u_char *) in;
    int     a, b, c, d;
    
    a = (int) *s++;
    b = (int) *s++;
    c = (int) *s++;
    d = (int) *s++;
    (void) ircsprintf(buf, "%d.%d.%d.%d", a, b, c, d);
    
    return buf;
}

#if !defined( HAVE_INET_NETOF )
/* inet_netof --   return the net portion of an internet number */

int inet_netof(struct in_addr in)
{
    int         addr = in.s_net;

    if (addr & 0x80 == 0)
	return ((int) in.s_net);

    if (addr & 0x40 == 0)
	return ((int) in.s_net * 256 + in.s_host);

    return ((int) in.s_net * 256 + in.s_host * 256 + in.s_lh);
}

#endif /* !HAVE_INET_NETOF */

#ifdef MEMTRACE

typedef struct {
    MemTracer   *tracer;
    size_t       length;
} MemTag;

static MemTracer *mtrace_list;

u_long
memtrace_count(TracedCount *mc, const char *file)
{
    MemTracer *mt;

    if (file)
    {
        for (mt = mtrace_list; mt; mt = mt->next)
        {
            if (strcmp(mt->file, file))
                continue;

            mc->allocated.c += mt->objects;
            mc->allocated.m += mt->allocated;
            mc->management.c += mt->objects;
            mc->management.m += mt->objects * sizeof(MemTag);

            mt->initialized = 2;    /* mark as counted */
        }

        return mc->allocated.m;
    }

    /* no file, so count unmarked ones */
    for (mt = mtrace_list; mt; mt = mt->next)
    {
        if (mt->initialized == 1)
        {
            mc->allocated.c += mt->objects;
            mc->allocated.m += mt->allocated;
            mc->management.c += mt->objects;
            mc->management.m += mt->objects * sizeof(MemTag);
        }
    }

    return mc->allocated.m;
}

void
memtrace_report(aClient *cptr, const char *file)
{
    MemTracer   *mt;

    /* display unmarked tracers */
    if (!file)
    {
        for (mt = mtrace_list; mt; mt = mt->next)
        {
            if (mt->initialized == 1 && mt->allocated)
                sendto_one(cptr, ":%s %d %s :    %s:%d objects: %d  bytes: %lu",
                           me.name, RPL_STATSDEBUG, cptr->name, mt->file,
                           mt->line, mt->objects, mt->allocated);
        }

        return;
    }

    /* display for one file */
    for (mt = mtrace_list; mt; mt = mt->next)
    {
        if (strcmp(mt->file, file))
            continue;
        if (!mt->allocated)
            continue;
        sendto_one(cptr, ":%s %d %s :    %s:%d objects: %d  bytes: %lu",
                   me.name, RPL_STATSDEBUG, cptr->name, mt->file, mt->line,
                   mt->objects, mt->allocated);
    }
}

void
memtrace_reset(void)
{
    MemTracer *mt;

    /* reset counted mark */
    for (mt = mtrace_list; mt; mt = mt->next)
        mt->initialized = 1;
}


void *MyMalloc_impl(MemTracer *mt, size_t mlen)
{
    MemTag *tag;

    if (!mt->initialized)
    {
        mt->next = mtrace_list;
        mtrace_list = mt;
        mt->initialized = 1;
    }

    tag = malloc(mlen + sizeof(MemTag));

    if (!tag)
        outofmemory();

    tag->tracer = mt;
    tag->length = mlen;
    mt->objects++;
    mt->allocated += mlen;

    return tag + 1;
}

void MyFree_impl(void *obj)
{
    MemTag *tag;

    if (!obj)
        return;

    tag = (MemTag *)obj - 1;
    tag->tracer->objects--;
    tag->tracer->allocated -= tag->length;

    free(tag);
}

void *MyRealloc_impl(MemTracer *mt, void *obj, size_t mlen)
{
    MemTag *tag;

    if (!mt->initialized)
    {
        mt->next = mtrace_list;
        mtrace_list = mt;
        mt->initialized = 1;
    }

    if (obj)
    {
        tag = (MemTag *)obj - 1;
        tag->tracer->objects--;
        tag->tracer->allocated -= tag->length;
        obj = tag; /* subtle */
    }

    tag = realloc(obj, mlen + sizeof(MemTag));

    if (!tag)
        outofmemory();

    tag->tracer = mt;
    tag->length = mlen;
    mt->objects++;
    mt->allocated += mlen;

    return tag + 1;
}

#else   /* MEMTRACE */

void *MyMalloc(size_t x)
{
    void       *ret = malloc(x);

    if (!ret)
    {
	outofmemory();
    }
    return ret;
}

void *MyRealloc(void *x, size_t y)
{
    void       *ret = realloc(x, y);

    if (!ret)
    {
	outofmemory();
    }
    return ret;
}

#endif /* MEMTRACE */

/*
 * read a string terminated by \r or \n in from a fd
 * 
 * Created: Sat Dec 12 06:29:58 EST 1992 by avalon 
 * Returns: 
 * 0 - EOF 
 * -1 - error on read 
 * >0 - number of bytes returned (<=num)
 * After opening a fd, it is necessary to init dgets() by calling it as
 * dgets(x,y,0); * to mark the buffer as being empty.
 * 
 * cleaned up by - Dianora aug 7 1997 *argh*
 */
int dgets(int fd, char *buf, int num)
{
    static char dgbuf[8192];
    static char *head = dgbuf, *tail = dgbuf;
    char *s, *t;
    int n, nr;

    /* Sanity checks. */
    if (head == tail)
	*head = '\0';

    if (!num) 
    {
	head = tail = dgbuf;
	*head = '\0';
	return 0;
    }

    if (num > sizeof(dgbuf) - 1)
	num = sizeof(dgbuf) - 1;

    FOREVER
    {
	if (head > dgbuf)
	{
	    for (nr = tail - head, s = head, t = dgbuf; nr > 0; nr--)
		*t++ = *s++;
	    tail = t;
	    head = dgbuf;
	}
	/* check input buffer for EOL and if present return string. */
	if (head < tail &&
	    ((s = strchr(head, '\n')) ||
	     (s = strchr(head, '\r'))) && s < tail)
	{
	    n = MIN(s - head + 1, num);	/* at least 1 byte */
	    memcpy(buf, head, n);
	    head += n;
	    if (head == tail)
		head = tail = dgbuf;
	    return n;
	}
	
	if (tail - head >= num) 
	{      /* dgets buf is big enough */
	    n = num;
	    memcpy(buf, head, n);
	    head += n;
	    if (head == tail)
		head = tail = dgbuf;
	    return n;
	}
	
	n = sizeof(dgbuf) - (tail - dgbuf) - 1;
	nr = read(fd, tail, n);
	if (nr == -1)
	{
	    head = tail = dgbuf;
	    return -1;
	}
	
	if (!nr)
	{
	    if (tail > head)
	    {
		n = MIN(tail - head, num);
		memcpy(buf, head, n);
		head += n;
		if (head == tail)
		    head = tail = dgbuf;
		return n;
	    }
	    head = tail = dgbuf;
	    return 0;
	}
	
	tail += nr;
	*tail = '\0';
	
	for (t = head; (s = strchr(t, '\n'));)
	{
	    if ((s > head) && (s > dgbuf))
	    {
		t = s - 1;
		for (nr = 0; *t == '\\'; nr++)
		    t--;
		if (nr & 1)
		{
		    t = s + 1;
		    s--;
		    nr = tail - t;
		    while (nr--)
			*s++ = *t++;
		    tail -= 2;
		    *tail = '\0';
		    }
		else
		    s++;
	    }
	    else
		s++;
	    t = s;
	}
	*tail = '\0';
    }
}


syntax highlighted by Code2HTML, v. 0.9.1