/*
 * newsspool - copy incoming news into incoming directory
 *
 * The -i option relies on the parent setting (and exporting) $PATH.
 *
 * $Log$
 */

#ifdef AMIGA
	/*
	 *	If being compiled for an Amiga, then this code should be linked into
	 *	the executable for UUPC to replace the built-in "rnews" command.
	 *	Note that this bypasses the "rnews.{batch,immed}" script.
	 */
#  define MAIN		RNews
#  define LOCAL		static
#else
#  define MAIN		main
#  define LOCAL
#endif

#include <stdio.h>
#include <sys/types.h>

#ifndef AMIGA
#  include <sys/stat.h>
#else
#  include <libraries/dos.h>
#  define stat		FileInfoBlock
#  define st_mode	fib_DirEntryType
extern int *Lock();
#endif /* AMIGA */

#include <string.h>
#include <errno.h>
#include "libc.h"
#include "news.h"
#include "config.h"

#ifndef lint
static char RCSid[] = "$Header$";
#endif

#ifndef MAXTRIES
#define	MAXTRIES	100	/* limit on attempts to make links */
#endif

int debug = 0;
char *progname;

extern void error(), exit();
#ifdef UTZOOERR
extern char *mkprogname();
#else
#define	mkprogname(a)	(a)
#endif

char buf[BUFSIZ*16];	/* try to get a batch in a few gulps */
int immed = 0;		/* try an immediate newsrun? */

void process();
FILE *outopen();
void outclose();
extern time_t time();
char *outname();

/*
 * main - parse arguments and handle options
 */
MAIN (argc, argv)
int argc;
char *argv[];
{
	int c;
	int errflg = 0;
	FILE *in;
	struct stat statbuf;
	extern int optind;
	extern char *optarg;
	extern FILE *efopen();
	void process();

	progname = mkprogname(argv[0]);

	while ((c = getopt(argc, argv, "id")) != EOF)
		switch (c) {
		case 'i':	/* try immediate newsrun */
			immed++;
			break;
		case 'd':	/* Debugging. */
			debug++;
			setbuf(stderr, (char *)NULL);
			break;
		case '?':
		default:
			errflg++;
			break;
		}
	if (errflg) {
		fprintf(stderr, "Usage:  %s [file] ...\n", progname);
		exit(2);
	}
	/* probe to get unprivileged() called if necessary */
	(void) ctlfile((char *)NULL);

	/* mktemp() uses access(2) [ARGH!] so minimize chances of trouble */
	(void) setgid(getegid());
	(void) setuid(geteuid());

	(void) umask(newsumask());

	if (optind >= argc)
		process(stdin, "stdin");
	else
		for (; optind < argc; optind++)
			if (STREQ(argv[optind], "-"))
				process(stdin, "-");
			else {
#ifdef AMIGA
				int *lock;

				if (lock = Lock(argv[optind], ACCESS_READ)) {
					Examine(lock, &statbuf);
					UnLock(lock);
				} else
					statbuf.st_mode = -1;
				in = efopen(argv[optind], "r");
				if (statbuf.st_mode > 0)			/* AmigaDOS directory */
					error("`%s' is directory!", argv[optind]);
#else
				in = efopen(argv[optind], "r");
				if (fstat(fileno(in), &statbuf) < 0)
					error("can't fstat `%s'", argv[optind]);
				if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
					error("`%s' is directory!", argv[optind]);
#endif /* AMIGA */
				process(in, argv[optind]);
				(void) fclose(in);
			}
#ifndef AMIGA
	if (immed) {
		execlp("newsrun", "newsrun", (char *)NULL);
		error("attempt to run newsrun failed!", "");
	}
#endif /* AMIGA */
	exit(0);
}

/*
 * process - process input file
 */

/* ARGSUSED */
LOCAL void process(in, inname)
FILE *in;
char *inname;
{
	register int count;
	register int firstblock;
	FILE *out;
	register char *p;
	register int n;
	char *name;

	name = outname();
	out = outopen(name);

	/* do the copying */
	firstblock = 1;
	while ((count = fread(buf, sizeof(char), sizeof(buf), in)) > 0) {
		if (firstblock) {
			n = cunskip(buf, count);
			p = buf + n;
			count -= n;
			firstblock = 0;
		} else
			p = buf;
		n = fwrite(p, sizeof(char), count, out);
		if (n != count)
			error("write error in output to `%s'", name);
	}
	outclose(out, name);
}

/*
 * outname - construct name for the temporary output file
 */
LOCAL char *outname()
{
	register char *p;

	p = strsave(fullartfile("in.coming/nspool.XXXXXX"));
	mktemp(p);
	return(p);
}

/*
 * outopen - acquire an output file
 */
LOCAL FILE *outopen(name)
char *name;
{
	FILE *f;

	f = fopen(name, "w");
	if (f == NULL)
		error("unable to create temporary `%s'", name);
	if (debug)
		fprintf(stderr, "output into %s\n", name);

	return(f);
}

/*
 * outclose - close output file, moving it to the right place
 *
 * Names are based on the current time in hopes of keeping input in order.
 */
LOCAL void outclose(f, tmpname)
FILE *f;
char *tmpname;
{
	register char *p;
	register char *name;
	register int ntries;
	time_t now;
	extern int errno;

	if (fclose(f) == EOF)
		error("fclose error on file `%s'", tmpname);

	p = fullartfile("in.coming/");
	name = emalloc(strlen(p) + 20);	/* plenty for a number */
	(void) strcpy(name, p);
	p = name + strlen(name);

	ntries = 0;
	for (;;) {
		now = time((time_t *)NULL);
		sprintf(p, "%ld", now);
		if (debug)
			fprintf(stderr, "trying renaming to %s\n", name);
		if (link(tmpname, name) >= 0)
			break;				/* NOTE BREAK OUT */
		if (errno != EEXIST)	/* something strange is wrong */
			error("unable to link `%s'", tmpname);
		errno = 0;
		if (ntries > MAXTRIES)	/* sanity check */
			error("too many attempts to link `%s'", tmpname);
		if (debug)
			fprintf(stderr, "failed\n");
		sleep(2);	/* avoid rumored race in 1-sec sleep */
		ntries++;
	}
	if (debug)
		fprintf(stderr, "succeeded\n");
	(void) unlink(tmpname);
}

/*
 * cunskip - inspect block for silly #! cunbatch headers
 *
 * number of chars at start to skip
 */
LOCAL int cunskip(bufp, count)
char *bufp;
int count;
{
	static char goop[] = "cunbatch";
#	define	GOOPLEN	(sizeof(goop)-1)	/* strlen(goop) */
	static char goop2[] = "c7unbatch";
#	define	GOOP2LEN	(sizeof(goop2)-1)	/* strlen(goop2) */
	register char *p;
	register int nleft;

	nleft = count;
	p = bufp;

	if (nleft < 2)				/* no room for a header */
		return(0);
	if (*p++ != '#' || *p++ != '!')		/* doesn't start with #! */
		return(0);
	nleft -= 2;

	/* skip space */
	while (nleft > 0 && (*p == ' ' || *p == '\t')) {
		p++;
		nleft--;
	}
	/* recognize headers (the +1s ensure room for the newline) */
	if (nleft >= GOOPLEN+1 && STREQN(p, goop, GOOPLEN)) {
		p += GOOPLEN;
		nleft -= GOOPLEN;
	} else if (nleft >= GOOP2LEN+1 && STREQN(p, goop2, GOOP2LEN)) {
		p += GOOP2LEN;
		nleft -= GOOP2LEN;
	} else					/* no header */
		return(0);

	/* skip more space */
	while (nleft > 0 && (*p == ' ' || *p == '\t')) {
		p++;
		nleft--;
	}
	if (nleft == 0 || *p++ != '\n')		/* didn't end properly */
		return(0);
	return(p - bufp);
}

/*
 * unprivileged - drop setuid-ness if configuration is overridden
 */
void unprivileged()
{
	setgid(getgid());
	setuid(getuid());
}
