
/*
 *  RNEWS.C
 *
 *  Copyright 1988 by William Loftus.  All rights reserved.
 *  Extensive Changes Copyright 1990 by Matthew Dillon, All Rights Reserved
 *
 *  This is rnews for compressed news.	News 2.11 will uux
 *  a file to this system that will be in compressed format.
 *  This program will strip off the "#! cunbatch", uncompress
 *  the news, and call unbatch.  If the news is not in compressed
 *  format it will just pass it to unbatch.
 *
 *  Each newsgroup directory has a .next file which indicates the next
 *  article number to write.  This number rolls over at 32767.
 */

#include "news.h"
#include <errno.h>
#include <ctype.h>
#include <sys/stat.h>	    /* outrageous place for the mkdir() proto! */
#include <log.h>

extern char *TmpFileName();

IDENT(".04");

static int dofile(void);
static void unbatch(char *);
static long store_art(long size, char *);
static void uncompress_news(void);
static void news_exit(int stat);

#define exit news_exit

static char TmpBuf[2048];
char homedir[256];

char *NewsDir;
char *WKName;

void copy_uualtspool();

#define PROG_NAME "RNews"

int
main(ac, av)
int ac;
char *av[];
{
    int ret = 0;
    short i;

    LogProgram = PROG_NAME;
    fclose(stderr);         /* Assume we are running in the background */

    getcwd(homedir, sizeof(homedir));
    NewsDir = GetConfigDir(UUNEWS);

    if (chdir(NewsDir) != 0) {
	ulog(0, "NewsDir %s doesn't exist; quitting", NewsDir);
	exit(1);
    }
    WKName = strdup(TmpFileName("news-work"));

    for (i = 1; i < ac; ++i) {
	char *ptr = av[i];
	if (*ptr != '=')
	    break;
	switch(ptr[1]) {
	case 'x':
	    LogLevel = atoi(ptr + 2);
	    break;
	default:
	    printf("unknown flag '%s' ignored\n", ptr + 1);
	    break;
	}
    }
    if (i < ac && strcmp(av[i], "UseNet") == 0) {
	ulog(1, "Starting standard input");
	if (atoi(GetConfig(RNEWSDEBUG, "0"))) {
	    ulog(-1, "RNewsDebug, copying to UUAltSpool:");
	    copy_uualtspool();
	}

	if (dofile() < 0)
	    ret = 1;
    } else {
	for (; i < ac; ++i) {
	    char *ptr = av[i];

	    chdir(homedir);         /* so relative file names will work */
	    ulog(1, "Starting file %s", ptr);
	    if (freopen(ptr, "r", stdin) == NULL) {
		ulog(0, "Cannot open news file %s", ptr);
		continue;
	    }
	    chdir(NewsDir);
	    if (dofile() < 0)
		ret = 1;
	}
    }
    if (ret == 0)
	remove(WKName);
    exit(ret);
}

static int
dofile(void)
{
    char buf[64];

    if (fgets(buf, sizeof(buf), stdin)) {
	if (strncmp(buf, "#! cunbatch", 11) == 0) {
	    uncompress_news();
	} else {
	    unbatch(buf);
	}
    }
    fclose(stdin);
    return(0);
}

/*
 * Unbatch, an Amiga unbatcher.
 *
 *	Written by Fred Cassirer, 10/8/88.
 *	Some unbatch code originally taken from News2.11 sources.
 *
 *	Ported to Lattice 5.0
 *	Added config.h file
 *	Added use of ERRORFILE
 *	11/23/1988  Dan 'Sneakers' Schein
 *
 * This code (as well as the unbatch stuff) is free for anyone who thinks
 * they can get some use out of it.
 *
 *	Articles will only be placed in newsgroups as defined in the
 *	"LIB/NewsGroups" control file.  Articles which are not listed
 *	in the control file are placed in the "junk" directory.  Articles
 *	are sequenced by the sequencer in "LIB/seqnews". This could
 *	possibly be updated to use a sequencer within each of the news
 *	subdirectories, to more closely resemble the News system under Unix.
 *
 *	(sequence numbers are not kept in the NewsGroups file itself but in
 *	each news directory.  This protects against accidents)
 *
 *	The NewsGroups file contains one newsgroup per line, plus an optional
 *	# days expiration.  For example:
 *
 *	    comp.sys.amiga  30
 *
 *	Unbatch will also take command line args of files to be unbatched.
 *	Files on the command line are not removed, they should be removed
 *	after running unbatch.
 */

static struct group {
    struct group *next;
    char name[1];
} groups;

static void
initgroups(void)
{
    struct group *gp;
    long len;
    FILE *fp;
    char buf[50];

    if (groups.next != NULL)
	return;

    if ((fp = openlib("newsgroups")) == NULL) {
	ulog(0, "Couldn't open LIB/newsgroups file");
	exit(2);
    }

    gp = &groups;
    while (fgets(buf, sizeof(buf), fp)) {
	short c;
	for (len = 0; (c = buf[len]) != ' ' && c != 9 && c != '\n'; ++len);
	buf[len] = 0;
	if ((gp->next = (struct group *)malloc(4+len+1)) == NULL) {
	    ulog(0, "Malloc failed!");
	    exit(4);
	}
	gp = gp->next;
	strcpy(gp->name, buf);
    }
    gp->next = NULL;
    fclose(fp);
}

static char *
finddir(dir)
char *dir;
{
    struct group *gp, *dp = NULL;
    char *p;
    int c;

    do {
	p = dir;
	do {
	    c = *++dir;
	} while (c != ' ' && c != ',' && c != '\n' && c != '\0');
	*dir++ = '\0';

	for (gp = groups.next; gp != NULL; gp = gp->next) {
	    if (gp == dp)
		break;
	    if (strcmp(gp->name, p) == 0) {
		dp = gp;
		break;
	    }
	}
    } while (c != '\n' && c != '\0');

    if (dp)
	return (dp->name);
    return ("junk");        /* Oops, not a known newsgroup */
}

/*
 * unbatchnews: extract news in batched format and process it one article
 * at a time.  The format looks like
 *	#! rnews 1234
 *	article containing 1234 characters
 *	#! rnews 4321
 *	article containing 4321 characters
 */

static void
unbatch(hdr)
char *hdr;
{
    long size;
    char buf[64];

    if (hdr) {
	strcpy(buf, hdr);
    } else {
	buf[0] = 0;
	fgets(buf, sizeof(buf), stdin);
    }
    if (strncmp(buf, "#! rnews ", 9) != 0) {
	store_art(10000000L, buf);
	return;
    }
    do {
	/* second strcmp is kludge for bug */

	if (strncmp(buf, "#! rnews ", 9) != 0 && strncmp(buf, "! rnews ", 8) != 0) {
	    char *cp;

	    for (cp = buf; *cp; ++cp) {
		if (!isprint(*cp) && !isspace(*cp))
		    *cp = '?';
	    }
	    *--cp = '\0';
	    ulog(0, "out of sync, skipping <%s>", buf);
	    continue;
	}

	size = atol(buf + 8 + (buf[0] == '#'));
	if (size <= 0) {
	    ulog(0, "nonsense size %ld", size);
	    continue;
	}

	if (store_art(size, NULL) > 0) {
	    /*
	     * If we got a truncated batch, don't process the
	     * last article; it will probably be received again.
	     */
	    ulog(0, "truncated batch");
	    break;
	}
    } while (fgets(buf, sizeof(buf), stdin));
}

static long
store_art(size, ibuf)
long size;
char *ibuf;
{
    int c;
    FILE *pfn;
    FILE *seq;
    unsigned long seqno;
    char *dir;

    initgroups();

    ulog(2, "SIZE %ld", size);

    if ((pfn = fopen("ArticleTemp", "w")) == NULL) {
	ulog(0, "Could not creat article temp file");
	exit(3);
    }

    dir = "Rejects";

    if (ibuf)
	fputs(ibuf, pfn);

    /*
     *	search for Newsgroups: line in a manner independant of the line
     *	length, which can be anything.
     */

    {
	int i = 0;

	while (size && (c = getc(stdin)) != EOF) {
	    putc(c, pfn);
	    --size;
	    TmpBuf[i++] = c;
	    if (i == sizeof(TmpBuf))    /*  very long non-NewsGroups: header  */
		i = 0;
	    if (c == '\n')
		i = 0;
	    if (c == ':') {
		if (strncmp(TmpBuf, "Newsgroups:", 11) == 0) {
		    if (fgets(TmpBuf, sizeof(TmpBuf), stdin)) {
			fputs(TmpBuf, pfn);
			size -= strlen(TmpBuf);
			for (i = 0; TmpBuf[i] == ' ' || TmpBuf[i] == 9; ++i);
			dir = finddir(TmpBuf + i);
			break;
		    }
		}
	    }
	}
    }
    while (size > 0) {
	int n = (size > sizeof(TmpBuf)) ? sizeof(TmpBuf) : size;
	int r = fread(TmpBuf, 1, n, stdin);

	if (r <= 0)
	    break;
	fwrite(TmpBuf, 1, r, pfn);
	size -= r;
    }
    fflush(pfn);
    if (ferror(pfn)) {              /* disk full? */
	fclose(pfn);
	remove("ArticleTemp");
	ulog(0, "error writing temporary file");
	return (-1);
    }
    fclose(pfn);

    sprintf(TmpBuf, "%s/.next", dir);
    if (IsDir(dir)) {
	if ((seq = fopen(TmpBuf, "r")) == NULL) {
	    ulog(0, "sequencer file '%s' not found, will create", TmpBuf);
	    seqno = 1000;
	} else {
	    fscanf(seq, "%ld", &seqno);
	    fclose(seq);
	}
    } else if (mkdir(dir) != 0) {
	ulog(0, "Cannot create directory for '%s'; article dropped", dir);
	return (-1);
    } else {
	seqno = 1;
    }
    if ((seq = fopen(TmpBuf, "w")) == NULL) {
	ulog(0, "Couldn't create sequencer for '%s'; article dropped", dir);
	return (-1);
    } else {
	fprintf(seq, "%ld\n", (seqno + 1) & 0x7FFF);
	fclose(seq);
    }

    sprintf(TmpBuf, "%s/%ld", dir, seqno);
    ulog(2, "FILE %s", TmpBuf);
    if (rename("ArticleTemp", TmpBuf) != 0) {
	remove("ArticleTemp");
	ulog(0, "Could not rename article to %s", TmpBuf);
    }
    return (size);
}

/*
 * Set USERMEM to the maximum amount of physical user memory available
 * in bytes.  USERMEM is used to determine the maximum BITS that can be used
 * for compression.
 *
 * SACREDMEM is the amount of physical memory saved for others; compress
 * will hog the rest.
 */

#ifndef SACREDMEM
#define SACREDMEM	0
#endif

#ifndef USERMEM
#  define USERMEM	450000	/* default user memory */
#endif

#ifdef AMIGA			/* Commodore Amiga */
# define BITS		16
# define M_XENIX	/* so we can use small model (faster) */
# undef USERMEM
#endif

#ifdef interdata		/* (Perkin-Elmer) */
#define SIGNED_COMPARE_SLOW	/* signed compare is slower than unsigned */
#endif

#ifdef pdp11
# define BITS	12	/* max bits/code for 16-bit machine */
# define NO_UCHAR	/* also if "unsigned char" functions as signed char */
# undef USERMEM
#endif /* pdp11 */	/* don't forget to compile with -i */

#ifdef z8000
# define BITS	12
# undef vax		/* weird preprocessor */
# undef USERMEM
#endif /* z8000 */

#ifdef pcxt
# define BITS	12
# undef USERMEM
#endif /* pcxt */

#ifdef USERMEM
# if USERMEM >= (433484+SACREDMEM)
#  define PBITS 16
# else
#  if USERMEM >= (229600+SACREDMEM)
#   define PBITS	15
#  else
#   if USERMEM >= (127536+SACREDMEM)
#    define PBITS	14
#   else
#    if USERMEM >= (73464+SACREDMEM)
#     define PBITS	13
#    else
#     define PBITS	12
#    endif
#   endif
#  endif
# endif
# undef USERMEM
#endif /* USERMEM */

#ifdef PBITS		/* Preferred BITS for this memory size */
# ifndef BITS
#  define BITS PBITS
# endif BITS
#endif /* PBITS */

#if BITS == 16
# define HSIZE	69001		/* 95% occupancy */
#endif
#if BITS == 15
# define HSIZE	35023		/* 94% occupancy */
#endif
#if BITS == 14
# define HSIZE	18013		/* 91% occupancy */
#endif
#if BITS == 13
# define HSIZE	9001		/* 91% occupancy */
#endif
#if BITS <= 12
# define HSIZE	5003		/* 80% occupancy */
#endif

#ifdef M_XENIX			/* Stupid compiler can't handle arrays with */
# if BITS == 16 		/* more than 65535 bytes - so we fake it */
#  define XENIX_16
# else
#  if BITS > 13 		/* Code only handles BITS = 12, 13, or 16 */
#   define BITS 13
#  endif
# endif
#endif

/*
 * a code_int must be able to hold 2**BITS values of type int, and also -1
 */
#if BITS > 15
typedef long int	code_int;
#else
typedef int		code_int;
#endif

#ifdef SIGNED_COMPARE_SLOW
typedef unsigned long int count_int;
#else
typedef long int	count_int;
#endif

#ifdef NO_UCHAR
typedef char		char_type;
#else
typedef unsigned char	char_type;
#endif /* UCHAR */
char_type magic_header[] = { "\037\235" };      /* 1F 9D */

/* Defines for third byte of header */
#define BIT_MASK	0x1f
#define BLOCK_MASK	0x80
/* Masks 0x40 and 0x20 are free.  I think 0x20 should mean that there is
   a fourth header byte (for expansion).
*/
#define INIT_BITS	9	/* initial number of bits/code */

int n_bits;			/* number of bits/code */
int maxbits = BITS;		/* user settable max # bits/code */
code_int maxcode;		/* maximum code, given n_bits */
code_int maxmaxcode;	/* 1 << BITS */ /* should NEVER generate this code */
#define MAXCODE(n_bits)         ((((code_int)1) << (n_bits)) - 1)

#ifdef XENIX_16
count_int * htab[9] = { NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL };
unsigned short * codetab[5] = { NULL, NULL, NULL, NULL, NULL };
#else	/* Normal machine */
count_int	htab[HSIZE];
unsigned short	codetab[HSIZE];
#endif /* XENIX_16 */
code_int hsize = HSIZE; 	/* for dynamic table sizing */

/*
 * The tab_suffix table needs 2**BITS characters.  We get this from the
 * beginning of htab.  The output stack uses the rest of htab, and contains
 * characters.	There is plenty of room for any possible stack (stack used
 * to be 8000 characters).
 */

#ifdef XENIX_16
# define tab_prefixof(i)        (codetab[(i)>>14][(i) & 0x3FFF])
# define tab_suffixof(i)        ((char_type *)htab[(i)>>15])[(i) & 0x7FFF]
# define de_stack		((char_type *)(htab[2]))
#else	/* Normal machine */
# define tab_prefixof(i)        codetab[i]
# define tab_suffixof(i)        (((char_type *)htab)[i])
/* FIXME??? This next line is suspect..... */
# define de_stack		((char_type *)(((long)htab)+(((code_int)1)<<BITS)))
#endif /* XENIX_16 */

code_int free_ent = 0;	/* first unused entry */

static code_int getcode(void);
static void writeerr(void);

/*
 * block compression parameters -- after all codes are used up,
 * and compression rate changes, start over.
 */
int block_compress = BLOCK_MASK;
int clear_flg = 0;

/*
 * the next two codes should not be changed lightly, as they must not
 * lie within the contiguous general code space.
 */
#define FIRST	257	/* first free entry */
#define CLEAR	256	/* table clear output code */

/*
 * Decompress stdin to stdout.	This routine adapts to the codes in the
 * file building the "string" table on-the-fly, requiring no table to
 * be stored in the compressed file.
 */

#ifdef XENIX_16

static void *
safealloc(long size)
{
    int retry = 30;
    void *p;

    while ((p = malloc(size)) == NULL) {
	if (--retry < 0) {
	    ulog(0, "Can't get memory to decompress news batch -- terminating");
	    exit(20);
	}
	ulog(0, "Can't get memory to decompress news batch -- will retry");
	sleep(30);
    }
    return p;
}

#endif /* XENIX_16 */

static void
decompress(void)
{
	register char_type *stackp;
	register int finchar;
	register code_int code, oldcode, incode;

	maxcode = MAXCODE(n_bits = INIT_BITS);
	free_ent = ((block_compress) ? FIRST : 256);
	finchar = oldcode = getcode();
	if(oldcode == -1)       /* EOF already? */
		return; 	/* Get out of here */

#ifdef XENIX_16
	htab[0] = (code_int *)safealloc(8192*sizeof(code_int));
	htab[1] = (code_int *)safealloc(8192*sizeof(code_int));
	htab[2] = (code_int *)safealloc(8192*sizeof(char));
	for (code = 0; code < 5; ++code)
		codetab[code] = (unsigned short *)
				safealloc(16384*sizeof(unsigned short));
#endif /* XENIX_16 */

	/*
	 * As above, initialize the first 256 entries in the table.
	 */
	for (code = 255; code >= 0; code--) {
		tab_prefixof(code) = 0;
		tab_suffixof(code) = (char_type)code;
	}

	putchar((char)finchar); /* first code must be 8 bits = char */
	if(ferror(stdout))      /* Crash if can't write */
		writeerr();
	stackp = de_stack;

	while ((code = getcode()) > -1) {
		if ((code == CLEAR) && block_compress) {
			for (code = 255; code >= 0; code--)
				tab_prefixof(code) = 0;
			clear_flg = 1;
			free_ent = FIRST - 1;
			if ((code = getcode()) == -1)   /* O, untimely death! */
				break;
		}
		incode = code;
		/*
		 * Special case for KwKwK string.
		 */
		if (code >= free_ent) {
			*stackp++ = finchar;
			code = oldcode;
		}

		/*
		 * Generate output characters in reverse order
		 */
#ifdef SIGNED_COMPARE_SLOW
		while (((unsigned long)code) >= ((unsigned long)256))
#else
		while (code >= 256)
#endif
		{
			*stackp++ = tab_suffixof(code);
			code = tab_prefixof(code);
		}
		*stackp++ = finchar = tab_suffixof(code);

		/*
		 * And put them out in forward order
		 */
		do {	register int c = *--stackp;
			putc(c, stdout);        /* putc is often a macro! */
		} while (stackp > de_stack);

		/*
		 * Generate the new entry.
		 */
		if ((code=free_ent) < maxmaxcode) {
			tab_prefixof(code) = (unsigned short)oldcode;
			tab_suffixof(code) = finchar;
			free_ent = code+1;
		}
		/*
		 * Remember previous code.
		 */
		oldcode = incode;
	}

	fflush(stdout);
	if(ferror(stdout))
		writeerr();

#ifdef XENIX_16
	free(htab[0]), htab[0] = NULL;
	free(htab[1]), htab[1] = NULL;
	free(htab[2]), htab[2] = NULL;
	for (code = 0; code < 5; ++code)
		free(codetab[code]), codetab[code] = NULL;
#endif /* XENIX_16 */
}

/*****************************************************************
 * TAG(getcode)
 *
 * Read one code from the standard input.  If EOF, return -1.
 * Inputs:
 *	stdin
 * Outputs:
 *	code or -1 is returned.
 */

#ifndef vax
char_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
#endif /* vax */

static code_int
getcode(void)
{
	/*
	 * On the VAX, it is important to have the register declarations
	 * in exactly the order given, or the asm will break.
	 */
	register code_int code;
	static int offset = 0, size = 0;
	static char_type buf[BITS];
	register int r_off, bits;
	register char_type *bp = buf;

	if (clear_flg > 0 || offset >= size || free_ent > maxcode) {
		/*
		 * If the next entry will be too big for the current code
		 * size, then we must increase the size.  This implies reading
		 * a new buffer full, too.
		 */
		if (free_ent > maxcode) {
			n_bits++;
			if (n_bits == maxbits)
				/* won't get any bigger now */
				maxcode = maxmaxcode;
			else
				maxcode = MAXCODE(n_bits);
		}
		if (clear_flg > 0) {
			maxcode = MAXCODE(n_bits = INIT_BITS);
			clear_flg = 0;
		}
		size = fread(buf, 1, n_bits, stdin);
		if (size <= 0)
			return -1;	/* end of file */
		offset = 0;
		/* Round size down to integral number of codes */
		size = (size << 3) - (n_bits - 1);
	}
	r_off = offset;
	bits = n_bits;
#ifdef vax
	asm("extzv      r10,r9,(r8),r11");
#else /* not a vax */
	/*
	 * Get to the first byte.
	 */
	bp += (r_off >> 3);
	r_off &= 7;
	/* Get first part (low order bits) */
#ifdef NO_UCHAR
	code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff;
#else
	code = (*bp++ >> r_off);
#endif /* NO_UCHAR */
	bits -= (8 - r_off);
	r_off = 8 - r_off;	/* now, offset into code word */
	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
	if (bits >= 8) {
#ifdef NO_UCHAR
		code |= (*bp++ & 0xff) << r_off;
#else
		code |= *bp++ << r_off;
#endif /* NO_UCHAR */
		r_off += 8;
		bits -= 8;
	}
	/* high order bits. */
	code |= (*bp & rmask[bits]) << r_off;
#endif /* vax */
	offset += n_bits;

	return code;
}

#ifdef DEBUG

static void _fprintf(FILE *fi, const char *ctl, ...) { }

static /*volatile*/ void
printcodes(void)
{
	/*
	 * Just print out codes from input file.  For debugging.
	 */
	code_int code;
	int col = 0, bits;

	bits = n_bits = INIT_BITS;
	maxcode = MAXCODE(n_bits);
	free_ent = ((block_compress) ? FIRST : 256);
	while ((code = getcode()) >= 0) {
		if ((code == CLEAR) && block_compress) {
			free_ent = FIRST - 1;
			clear_flg = 1;
		} else if (free_ent < maxmaxcode)
			free_ent++;
		if (bits != n_bits) {
			_fprintf(stderr, "\nChange to %d bits\n", n_bits);
			bits = n_bits;
			col = 0;
		}
		_fprintf(stderr, "%5d%c", code,
			(col+=6) >= 74 ? (col = 0, '\n') : ' ');
	}
	_fprintf(stderr, "\n");
	exit(0);
}

static int
in_stack(register int c, register int stack_top)
{
	if ((isprint(c) && c != '\\') || c == ' ') {
		de_stack[--stack_top] = c;
	} else {
		switch(c) {
		case '\n': de_stack[--stack_top] = 'n'; break;
		case '\t': de_stack[--stack_top] = 't'; break;
		case '\b': de_stack[--stack_top] = 'b'; break;
		case '\f': de_stack[--stack_top] = 'f'; break;
		case '\r': de_stack[--stack_top] = 'r'; break;
		case '\\': de_stack[--stack_top] = '\\'; break;
		default:
			de_stack[--stack_top] = '0' + c % 8;
			de_stack[--stack_top] = '0' + (c / 8) % 8;
			de_stack[--stack_top] = '0' + c / 64;
			break;
		}
		de_stack[--stack_top] = '\\';
	}
	return stack_top;
}
#endif /* DEBUG */

static void
writeerr(void)
{
    ulog(0, "Error while writing to '%s'", WKName);
    remove(WKName);
    exit(1);
}

static void
uncompress_news(void)
{
    if (freopen(WKName, "w", stdout) == NULL) {
	ulog(0, "Can't open uncompressed file for output");
	return;
    }

    /* Check the magic number */

    if ((getc(stdin)!=(magic_header[0] & 0xFF))
      || (getc(stdin)!=(magic_header[1] & 0xFF))) {
	    ulog(0, "input file not in compressed format");
	    exit(1);
    }
    maxbits = getc(stdin);  /* set -b from file */
    block_compress = maxbits & BLOCK_MASK;
    maxbits &= BIT_MASK;
    maxmaxcode = ((code_int)1) << maxbits;

    decompress();

    fclose(stdout);
    if (freopen(WKName, "r", stdin) == NULL) {
	ulog(0, "Can't reopen uncompressed file for input");
	return;
    }
    unbatch(NULL);
}

#undef exit
static /*volatile*/ void
news_exit(int stat)
{
    chdir(homedir);
    exit(stat);
}

/*
 *  When debugging RNews, this copied the input file to somewhere else to save
 *  it.
 */

void
copy_uualtspool()
{
    char *ident = MakeConfigPath(UUALTSPOOL, "altseqno");
    FILE *fi;
    int seqno = 0;
    int error = 0;
    char buf[32];

    if (fi = fopen(ident, "r+")) {
	fscanf(fi, "%d", &seqno);
	fclose(fi);
    }
    if (fi = fopen(ident, "w")) {
	fprintf(fi, "%d\n", seqno + 1);
	fclose(fi);
    } else {
	ulog(-1, "Unable to update %s\n", ident);
    }

    sprintf(buf, "ND.%d", seqno);
    ident = MakeConfigPath(UUALTSPOOL, buf);

    if (fi = fopen(ident, "w")) {
	int n;

	while ((n = fread(TmpBuf, 1, sizeof(TmpBuf), stdin)) > 0) {
	    if (fwrite(TmpBuf, 1, n, fi) != n) {
		ulog(-1, "Error writing to alternate spool!");
		error = 1;
		break;
	    }
	}
	fclose(fi);

	/*
	 *  if error use stdin.  If re-openning of ident fails use stdin.
	 */

	rewind(stdin);
	if (error == 0)
	    freopen(ident, "r", stdin);
    }
}

