/* $Id: puf.c,v 1.10 2004/02/13 15:29:00 ossi Exp $ *
 *
 * puf 0.9  Copyright (C) 2000-2004 by Oswald Buddenhagen <puf@ossi.cjb.net>
 * based on puf 0.1.x (C) 1999,2000 by Anders Gavare <gavare@hotmail.com>
 *
 * You may modify and distribute this code under the terms of the GPL.
 * There is NO WARRANTY of any kind. See COPYING for details.
 *
 * puf.c - startup code and global functions
 *
 */

#include "puf.h"


int verbose;
char *progname;
#ifdef DEBUG
int debug;
#endif


#ifndef HAVE_STRDUP
char *
strdup(const char *s)
{
    int l = strlen(s) + 1;
    char *rt = malloc(l);
    if (rt)
	memcpy(rt, s, l);
    return rt;
}
#endif

#define FMTLEN 128

int 
errm(url_t *u, const char *msg, ...)
{
    char *pt;
    va_list va;
    int rtv, lv;
    unsigned l, i, fl;
    char tbuf[2 * SHORTSTR + FMTLEN], fmt[SHORTSTR];

    num_errors++;
    if (*msg == '!') {
	msg++;
	num_urls_fail++;
	rtv = RT_GIVEUP;
	lv = ERR;
    } else if (++u->attempt >= (unsigned)u->parm->opt->max_attempts) {
	num_urls_fail++;
	rtv = RT_GIVEUP;
	lv = ERR;
    } else {
	rtv = RT_RETRY;
	lv = WRN;
    }

    if (lv <= verbose) {
	snprintf(fmt, sizeof(fmt), 
		 rtv == RT_RETRY ? "%s warning: %s (try %d)\n" : "%s: %s\n", 
		 progname, msg, u->attempt);

	if ((pt = strstr(fmt, "$u"))) {
	    if (u->port == 80)
		l = snprintf(tbuf + SHORTSTR + FMTLEN, SHORTSTR,
			     "http://%s/%s",  u->host->name, u->local_part);
	    else
		l = snprintf(tbuf + SHORTSTR + FMTLEN, SHORTSTR,
			     "http://%s:%i/%s", u->host->name, u->port, u->local_part);
	    if (l >= SHORTSTR) {
		memcpy(tbuf + 2 * SHORTSTR + FMTLEN - 4, "...", 4);
		l = SHORTSTR - 1;
	    }
	    fl = (unsigned)(pt - fmt);
	    memcpy(tbuf, fmt, fl);
	    for (i = 0; i < l; i++) {
		if (tbuf[i + SHORTSTR + FMTLEN] == '%')
		    tbuf[fl++] = '%';
		tbuf[fl++] = tbuf[i + SHORTSTR + FMTLEN];
	    }
	    strcpy(tbuf + fl, pt + 2);
	    pt = tbuf;
	} else
	    pt = fmt;

	va_start(va, msg);
	vfprintf(stderr, pt, va);
	va_end(va);
    }

    return rtv;
}

void 
dbp(const char *msg, ...)
{
    char fmt[SHORTSTR];
    va_list va;

    snprintf(fmt, SHORTSTR, "%s debug: %s", progname, msg);
    va_start(va, msg);
    vfprintf(stderr, fmt, va);
    va_end(va);
}

void 
dbpe(const char *msg, ...)
{
    va_list va;

    va_start(va, msg);
    vfprintf(stderr, msg, va);
    va_end(va);
}

void 
prx(int lev, const char *msg, ...)
{
    char fmt[SHORTSTR];
    static const char *prfs[] = {" fatal", "", " warning", " info", " debug"};
    va_list va;

    if (lev <= verbose) {
	snprintf(fmt, SHORTSTR, "%s%s: %s", progname, prfs[lev], msg);
	va_start(va, msg);
	vfprintf(stderr, fmt, va);
	va_end(va);
    }
}

/*  print an error message and terminate  */
void 
die(int ret, const char *msg, ...)
{
    char fmt[SHORTSTR];
    va_list va;

    snprintf(fmt, SHORTSTR, "\n%s: %s\n", progname, msg);
    va_start(va, msg);
    vfprintf(stderr, fmt, va);
    va_end(va);
    exit(ret);
}


#ifndef __DMALLOC_H__
/*  allocate memory or ask user what to do, if none available  */
void *
mrealloc(void *ptr, size_t size)
{
    void *t;
    static int asked = 0;

    if (size <= 0 || size > 100000)
	ierr("Internal error: Invalid memory allocation request.");
    if (!(t = realloc(ptr, size))) {
	if (!asked && isatty(0) && isatty(2)) {
	    prx(0, "Out of memory. Strange things may happen. Continue [n]? ");
	    if (getchar() != 'y')
		byebye("aborting");
	    asked++;
	} else
	    prx(ERR, "Allocation of %d bytes failed!\n", size);
    }
    return t;
}

void *
mmalloc(size_t size)
{
    return mrealloc(0, size);
}
#endif

static long crc32_tab[256];

static void 
init_hash(void)
{
    int i, b;
    long by;

    for (i = 0; i < 256; i++) {
	for (by = i, b = 0; b < 8; b++)
	    by = (by >> 1) ^ (-(by & 1) & 0xedb88320);
	crc32_tab[i] = by;
    }
}


/*  this is the normal crc32 algorithm. the generated numbers are quite
    unique, but i don't know, if they are well dispersed, i.e., if they are
    adequate for a real hash table.  */
int 
calc_hash(u_char * data, int len)
{
    int i;
    long crc = 0xc0debabe;

    for (i = 0; i < len; i++, data++)
	crc = (crc >> 8) ^ crc32_tab[(crc & 255) ^ *data];
    return (int)crc;
}

#ifdef USE_MAGIC
void
magck(const char *msg)
{
    url_t *u;
    int i;

    for (u = urllist, i=0; u; u = u->next, i++) {
	if (!ichecken_url(u))
	    prx(ERR, "checksum error for url[%d] (%s/%s) in %s\n",
		i, u->host->name, u->local_part, msg);
	else if (!ichecken_gen(u->parm))
	    prx(ERR, "checksum error for url[%d]->parm (%s/%s) in %s\n",
		i, u->host->name, u->local_part, msg);
	else if (!ichecken_gen(u->parm->opt))
	    prx(ERR, "checksum error for url[%d]->parm->opt (%s/%s) in %s\n",
		i, u->host->name, u->local_part, msg);
	else
	    continue;
	ierr("internal error");
    }
}
#endif

int 
main(int argc, char *argv[])
{
#ifdef __DMALLOC_H__
    url_t *u, *nu;
    host_t *h, *nh;
    hinfo_t *hi;
#endif
    
    if (!isatty(1))
	show_stat = 0;
    else
	printf(PACKAGE " v" VERSION
	   "  Copyright (C) 2000-2004 by Oswald Buddenhagen\n"
	   "  based on puf v0.1.x  "
	     "Copyright (C) 1999,2000 by Anders Gavare\n");

    progname = strrchr(argv[0], '/');
    if (progname)
	progname++;
    else
	progname = argv[0];

    bind_addr.sin_family = AF_INET;	/*  the rest is already 0  */

    srand(time(0));

    init_hash();

    init_user_agent();

    getopts(argc, argv);

    setlocale(LC_TIME, "C");	/*  some voodoo for strftime  */

    fetch_all();

#ifdef __DMALLOC_H__
    for (u = urllist; u; u = nu) {
	nu = u->next;
	dbg(MEM, ("disposing http://%s/%s\n", u->host->name, u->local_part));
	free(u);
    }
    for (hi = 0, h = hostlist; h; h = nh) {
	if (h->info != hi) {
	    hi = h->info;
	    if (hi) {
		dbg(MEM, ("disposing host info for '%s'\n", hi->name));
		free(hi);
	    }
	}
	nh = h->next;
	dbg(MEM, ("disposing host '%s'\n", h->name));
	free(h);
    }
#endif

    return 0;
}


syntax highlighted by Code2HTML, v. 0.9.1