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

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <errno.h>
#include "fixerrno.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? */
char *suffix = ".t";	/* suffix for filename, default is plain text */
char grade[3] = "";	/* 3 = digit, period, NUL */

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, "ig:d")) != EOF)
		switch (c) {
		case 'i':	/* try immediate newsrun */
			immed++;
			break;
		case 'g':	/* grade */
			if (strchr("0123456789", *optarg) == NULL)
				error("invalid grade `%s'", optarg);
			sprintf(grade, "%c.", *optarg);
			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 {
				in = efopen(argv[optind], "r");
				if (fstat(fileno(in), &statbuf) < 0)
					error("can't fstat `%s'", argv[optind]);
				process(in, argv[optind]);
				(void) fclose(in);
			}

	if (immed) {
		/* execlp because shell files may not be directly execable */
		execlp(binfile("input/newsrun"), "newsrun", (char *)NULL);
		error("attempt to run newsrun failed!", "");
	}
	exit(0);
}

/*
 * process - process input file
 */
/* ARGSUSED */
void
process(in, inname)
FILE *in;
char *inname;
{
	register int count;
	register int firstblock;
	FILE *out;
	register char *p;
	register int n;
	char *name;
	register int wrotesome = 0;

	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;
		if (count > 0) {
			n = fwrite(p, sizeof(char), count, out);
			if (n != count)
				error("write error in output to `%s'", name);
			wrotesome = 1;
		}
	}

	outclose(out, name, wrotesome);
}

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

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

/*
 - outopen - acquire an output file
 */
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.
 */
void
outclose(f, tmpname, wrotesome)
FILE *f;
char *tmpname;
int wrotesome;			/* did anything actually get written to it? */
{
	register char *p;
	register char *name;
	register int ntries;
	time_t now;

	if (nfclose(f) == EOF)
		error("close error on file `%s'", tmpname);
	if (!wrotesome) {
		(void) unlink(tmpname);
		return;
	}

	if (!mkinperm(tmpname, grade, suffix))
		error("couldn't move %s into the in.coming queue", tmpname);
	if (debug)
		fprintf(stderr, "succeeded\n");
}

/*
 - cunskip - inspect block for silly #! cunbatch headers, classify input
 */
int				/* number of chars at start to skip */
cunskip(bufp, count)
char *bufp;
int count;
{
	static char goop[] = "cunbatch";
#	define	GOOPLEN	(sizeof(goop)-1)	/* strlen(goop) */
	static char suf[] = ".Z";
	static char goop7[] = "c7unbatch";
#	define	GOOP7LEN	(sizeof(goop7)-1)	/* strlen(goop7) */
	static char suf7[] = ".7";
	static char comp[] = "\037\235";	/* compress's magic no. */
	register char *p;
	register int nleft;
#	define	MINCBATCH	5		/* one character, compressed */

	nleft = count;
	p = bufp;

	if (nleft < 2)				/* no room for a header */
		return(0);

	if (p[0] == comp[0] && p[1] == comp[1]) {	/* compressed */
		if (nleft < MINCBATCH)
			return(count);
		suffix = suf;
		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;
		suffix = suf;
	} else if (nleft >= GOOP7LEN+1 && STREQN(p, goop7, GOOP7LEN)) {
		p += GOOP7LEN;
		nleft -= GOOP7LEN;
		suffix = suf7;
	} 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);

	if (nleft < MINCBATCH)			/* null batch */
		return(count);
	return(p - bufp);
}

/*
 - unprivileged - drop setuidness if configuration is overridden
 */
void
unprivileged(reason)
char *reason;
{
	setgid(getgid());
	setuid(getuid());
	fprintf(stderr, "%s: renouncing setuid due to nonstandard `%s' in environment\n",
							progname, reason);
}
