/*
#ident	"@(#)smail/src:RELEASE-3_2_0_114:log.c,v 1.42 2001/01/18 07:34:14 woods Exp"
 */

/*
 *    Copyright (C) 1987, 1988 Ronald S. Karr and Landon Curt Noll
 *    Copyright (C) 1992  Ronald S. Karr
 * 
 * See the file COPYING, distributed with smail, for restriction
 * and warranty information.
 */

/*
 * log.c:
 *	system and per-message logging functions
 *
 *	These functions send information to a per-system log file and to
 *	a per-message log file, manage the creation use and removal of
 *	per-message logs and handle panic and fatal messages.
 *
 *	external functions:  open_system_logs, close_system_logs,
 *			     open_msg_log, close_msg_log, unlink_msg_log,
 *			     panic, write_log, send_log, scan_msg_log
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include "defs.h"
#ifdef ANSI_C
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#ifdef HAVE_STDLIB_H
# include <stdlib.h>
#endif
#if defined(POSIX_OS)
# include <unistd.h>
#else /* not POSIX_OS */
# if defined (HAVE_UNISTD_H)
#  include <unistd.h>
# endif
#endif	/* not POSIX_OS */

#include "smail.h"
#include "main.h"
#include "addr.h"
#include "alloc.h"
#include "dys.h"
#include "log.h"
#include "spool.h"
#include "exitcodes.h"
#ifndef DEPEND
# include "debug.h"
# include "extern.h"
#endif

#if defined(UNIX_SYS5) || defined(POSIX_OS) || defined(USE_FCNTL)
# include <fcntl.h>
#else
# if defined(UNIX_BSD)
#  include <sys/file.h>
# endif
#endif

#ifdef STANDALONE
# define xmalloc malloc
# define xrealloc realloc
# define xfree free
#endif	/* STANDALONE */

/* whence values for lseek(2) */
#ifndef SEEK_SET
# define SEEK_SET	0	/* set file offset to offset */
#endif
#ifndef SEEK_CUR
# define SEEK_CUR	1	/* set file offset to current plus offset */
#endif
#ifndef SEEK_END
# define SEEK_END	2	/* set file offset to EOF plus offset */
#endif

/* exported variables */
FILE *msg_logfile = NULL;		/* open stream to per-message log */

/* variables local to this file */
static FILE *panicfile = NULL;		/* open stream to panic log file */
static FILE *logfile = NULL;		/* open stream to system log file */
static char *msg_log_fn;		/* name of per-message log file */

/* functions local to this file */
static void build_msg_log_fn __P((void));
static void try_mkdir __P((char *));
static void write_log_va __P((int, char *, va_list));


/*
 * open_system_logs - open the panic and the general information log files.
 *
 * Access to the system log should not require that much be functional
 * or that resources be allocated to send to the system log.  For that
 * reason, we allocate all resources in advance, while resources are
 * available and the system is assumed to be somewhat sane.
 *
 * If reasonable, the system logs should be opened after a message has
 * been read to a spool file so that if a panic occurs because a log
 * could not be opened we can recover mail later when the problem is
 * solved.
 */
void
open_system_logs()
{
    int fd;
    static char panicbuf[BUFSIZ];	/* stdio buffer to avoid mallocs */
    static char logbuf[BUFSIZ];		/* stdio buffer to avoid mallocs */

    /*
     * first open panic log so we can panic if we can't open
     * the system log
     */
#ifdef	O_APPEND
    fd = open(panic_fn, O_CREAT|O_APPEND|O_WRONLY, log_mode);
#else
    /* limited to V7 open semantics */
    fd = open(panic_fn, 1);
    if (fd < 0) {
	fd = creat(panic_fn, log_mode);
    } else {
	(void) lseek(fd, (off_t) 0L, SEEK_END);
    }
#endif
    if (fd < 0) {
	/* perhaps the directory just needs to be created */
	if (auto_mkdir && errno == ENOENT) {
	    try_mkdir(panic_fn);
	    /* try opening the file again */
#ifdef O_APPEND
	    fd = open(panic_fn, O_CREAT|O_APPEND|O_WRONLY, log_mode);
#else
	    fd = creat(panic_fn, log_mode);
#endif
	}
    }
    if (fd < 0) {
	fprintf(stderr, "%s: [%lu] cannot open %s: %s\n", program, (long unsigned int) getpid(), panic_fn, strerror(errno));
	panicfile = stderr;
    } else {
	panicfile = fdopen(fd, "a");
	(void) setbuf(panicfile, panicbuf);   /* associate stream and buffer */
    }

    /*
     * next open the system log file after which we can start issuing
     * log messages.
     */
#ifdef	O_APPEND
    fd = open(log_fn, O_CREAT|O_APPEND|O_WRONLY, log_mode);
#else
    /* limited to V7 open semantics */
    fd = open(log_fn, 1);
    if (fd < 0) {
	fd = creat(log_fn, log_mode);
    } else {
	(void) lseek(fd, (off_t) 0L, SEEK_END);
    }
#endif
    if (fd < 0) {
	/* perhaps the directory just needs to be created */
	if (auto_mkdir && errno == ENOENT) {
	    try_mkdir(log_fn);
	    /* try opening the file again */
#ifdef	O_APPEND
	    fd = open(log_fn, O_CREAT|O_APPEND|O_WRONLY, log_mode);
#else
	    fd = creat(log_fn, log_mode);
#endif
	}
	if (fd < 0) {
	    panic(EX_OSFILE, "cannot open %s: %s", log_fn, strerror(errno));
	    /*NOTREACHED*/
	}
    }
    logfile = fdopen(fd, "a");
    (void) setbuf(logfile, logbuf);	/* associate stream and buffer */
}

/*
 * try_mkdir - try to build the directory for the given filename
 */
static void
try_mkdir(fn)
    char *fn;
{
    char *slash = rindex(fn, '/');
    char *dr;
    struct stat st;

    if (slash == NULL) {
	return;				/* ignore bad filename */
    }

    /* figure directory name */
    while (slash > fn && *(slash - 1) == '/')
	--slash;
    if (slash == fn)
	return;			/* root directory */
    dr = xmalloc((unsigned) (slash - fn + 1));
    (void) memcpy(dr, fn, (size_t) (slash - fn));
    dr[slash - fn] = '\0';

    DEBUG1(DBG_LOG_LO, "make directory %s\n", dr);
    (void) mkdir(dr, auto_mkdir_mode);

    if (stat(dr, &st) == -1 && errno == ENOENT) {
	char *slash2 = rindex(dr, '/');

	if (slash2) {
	    while (slash2 > dr && *(slash2 - 1) == '/')
		--slash2;
	    if (slash2 != dr) {
		*slash2 = '\0';
		DEBUG1(DBG_LOG_LO, "    make parent directory %s\n", dr);
		(void) mkdir(dr, auto_mkdir_mode);
		*slash2 = '/';
		(void) mkdir(dr, auto_mkdir_mode);
	    }
	}
    }

    xfree(dr);
}

/*
 * close_system_logs - close the panic and general info log files.
 */
void
close_system_logs()
{
    if (logfile) {
	(void) fclose(logfile);
	logfile = NULL;
    }
    if (panicfile) {
	(void) fclose(panicfile);
	panicfile = NULL;
    }
}


/*
 * open_msg_log - open message log file, one per message
 *
 * a per-message log should be opened once for each message and closed
 * when done processing a message.  It is intended to be information
 * that a sender might be interested in, and will be sent back to
 * the sender if the return_to_sender flag is set when processing of
 * the message is completed.
 */
void
open_msg_log()
{
    int fd;
    static char msgbuf[BUFSIZ];		/* stdio buffer to avoid mallocs */

    /*
     * if msg_fn not yet set up create a suitably unique value
     */
    if (msg_logfile) {
	(void) fclose(msg_logfile);
    }
    build_msg_log_fn();
#ifdef	O_APPEND
    fd = open(msg_log_fn, O_CREAT|O_APPEND|O_WRONLY, log_mode);
#else
    /* limited to V7 open semantics */
    fd = open(msg_log_fn, 1);
    if (fd < 0) {
	fd = creat(msg_log_fn, log_mode);
    } else {
	(void) lseek(fd, (off_t) 0L, SEEK_END);
    }
#endif
    if (fd < 0) {
	if (errno == ENOENT) {
	    /* the directory did not exist, try to create it */
	    DEBUG1(DBG_LOG_LO, "make directory %s/msglog\n",
		   spool_dir);
	    (void) mkdir("msglog", auto_mkdir_mode);
	} else {
	    /* alternate idea, permissions wrong so try to remove it */
	    (void) unlink(msg_log_fn);
	}
#ifdef	O_APPEND
	fd = open(msg_log_fn, O_CREAT|O_APPEND|O_WRONLY, log_mode);
#else
	/* limited to V7 open semantics */
	fd = open(msg_log_fn, 1);
	if (fd < 0) {
	    fd = creat(msg_log_fn, log_mode);
	} else {
	    (void) lseek(fd, (off_t) 0L, SEEK_END);
	}
#endif
	if (fd < 0) {
	    /*
	     * otherwise, panic.  We are assuming that the mail
	     * queue entry can be scanned at a later date
	     */
	    panic(EX_OSFILE, "cannot open %s/%s: %s",
		  spool_dir, msg_log_fn, strerror(errno));
	    /*NOTREACHED*/
	}
    }
    msg_logfile = fdopen(fd, "a");
    (void) setbuf(msg_logfile, msgbuf);	/* associate stream and buffer */
}

/*
 * build_msg_log_fn - build the name for the per-message log file
 */
static void
build_msg_log_fn()
{
    static char buf[sizeof("msglog/") + SPOOL_FN_LEN + 1];

    (void) sprintf(buf, "msglog/%s", spool_fn);
    msg_log_fn = buf;
}

/*
 * close_msg_log - close the per-message log file
 *
 * This should be called when further processing of a message is
 * being postponed to some point in the future.
 */
void
close_msg_log()
{
    if (msg_logfile) {
	(void) fclose(msg_logfile);
	msg_logfile = NULL;
    }
}

/*
 * unlink_msg_log - close and unlink the per-message log file
 *
 * use this when a message has been processed completely.
 */
void
unlink_msg_log()
{
    close_msg_log();
    if (msg_log_fn) {
	(void) unlink(msg_log_fn);
	msg_log_fn = NULL;
    }
}


/*
 * panic - panic and die!
 *
 * Attempt to write to the panic and system log files. and no matter what we
 * also try to write to stderr.
 *
 * If this is called after spooling a message, then the message will be put in
 * the error queue so that the postmaster can reprocess it after correcting
 * whatever error caused the panic in the first place.
 *
 */
/*VARARGS2*/
#ifdef ANSI_C
void
panic(int exitcode, char *fmt, ...)
#else
void
panic(exitcode, fmt, va_alist)
    int exitcode;			/* we will call exit(exitcode) */
    char *fmt;				/* printf(3) format */
    va_dcl                              /* arguments for printf */
#endif
{
    va_list ap;

    if (errfile) {
#ifdef ANSI_C
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	write_log_va(WRITE_LOG_TTY, fmt, ap);
	va_end(ap);
    }
#ifdef ANSI_C
    va_start(ap, fmt);
#else
    va_start(ap);
#endif
    write_log_va(WRITE_LOG_PANIC, fmt, ap);
    va_end(ap);

#ifdef ANSI_C
    va_start(ap, fmt);
#else
    va_start(ap);
#endif
    write_log_va(WRITE_LOG_SYS, fmt, ap);
    va_end(ap);

    if (daemon_pid != 0 && daemon_pid == getpid()) {
	write_log(WRITE_LOG_SYS, "smail daemon exiting on panic");
    }
    if (spool_fn) {
	freeze_message();		/* put message in error/ directory */
    }
    if (force_zero_exitvalue) {
	exit(0);
    }
    exit(exitcode);
}

#ifndef NODEBUG
# define WRITE_LOG_DEBUG	(WRITE_LOG_LAST << 2) /* magic for write_log_va() */
#endif
/*
 * write_log - write to the per-message log, and perhaps the system log
 *
 * write a log message to the various log files, where `who' is the
 * bitwise OR of WRITE_LOG_SYS, WRITE_LOG_MLOG and/or WRITE_LOG_PANIC.
 *
 * Note that messages with a leading 'X' are for per-message logs.
 */
/*VARARGS2*/
#ifdef ANSI_C
void
write_log(int who, char *fmt, ...)
#else
void
write_log(who, fmt, va_alist)
    int who;				/* mask of log files to be written */
    char *fmt;				/* printf(3) format */
    va_dcl                              /* arguments for printf */
#endif
{
    va_list ap;

#ifndef NODEBUG
    if (debug && errfile && (debug > 1 || fmt[0] != 'X')) {
#ifdef ANSI_C
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	write_log_va(WRITE_LOG_DEBUG, fmt, ap);
	va_end(ap);
    }
#endif
    if (errfile && ((who & WRITE_LOG_TTY) ||
		   ((who & (WRITE_LOG_MLOG|WRITE_LOG_PANIC)) &&
		    (error_processing == TERMINAL ||
		     error_processing == ERROR_DEFAULT) && /* XXX ??? */
		    fmt[0] != 'X'))) {
#ifdef ANSI_C
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	write_log_va(WRITE_LOG_TTY, fmt, ap);
	va_end(ap);
    }
    if (who & WRITE_LOG_SYS) {
#ifdef ANSI_C
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	write_log_va(WRITE_LOG_SYS, fmt, ap);
	va_end(ap);
    }
    if (who & WRITE_LOG_PANIC) {
#ifdef ANSI_C
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	write_log_va(WRITE_LOG_PANIC, fmt, ap);
	va_end(ap);
    }
    if (who & WRITE_LOG_MLOG) {
#ifdef ANSI_C
	va_start(ap, fmt);
#else
	va_start(ap);
#endif
	write_log_va(WRITE_LOG_MLOG, fmt, ap);
	va_end(ap);
    }
}

static void
write_log_va(who, fmt, ap)
    int who;				/* mask of log files to be written */
    char *fmt;				/* printf(3) format */
    va_list ap;                         /* arguments for printf */
{
    int wct = 0;

    switch (who) {
#ifndef NODEBUG
    case WRITE_LOG_DEBUG:
	/* if we are debugging at all, print logging messages */
	(void) fprintf(errfile, "write_log:");

	/* when debugging, tell which log files are to be used */
	if (debug > 1 && (who & WRITE_LOG_SYS)) {
	    (void) fprintf(errfile, " SYS");
	    wct++;
	}
	if (debug > 1 && (who & WRITE_LOG_PANIC)) {
	    (void) fprintf(errfile, "%cPANIC", wct++ ? '|' : ' ');
	}
	if (debug > 1 && (who & WRITE_LOG_MLOG)) {
	    (void) fprintf(errfile, "%cMLOG", wct++ ? '|' : ' ');
	}
	if (debug > 1 && (who & WRITE_LOG_TTY)) {
	    (void) fprintf(errfile, "%cTTY", wct++ ? '|' : ' ');
	}
	if (debug > 1) {
	    (void) fprintf(errfile, " ");
	}
	(void) vfprintf(errfile, fmt, ap);
	/* log messages don't come with their own \n */
	(void) fprintf(errfile, "\n");
	(void) fflush(errfile);
	break;

#endif	/* NODEBUG */

    case WRITE_LOG_TTY:
	(void) fprintf(errfile, "%s: [%lu] ", program, (long unsigned int) getpid());
	if (message_id) {
	    (void) fprintf(errfile, "[%s] ", message_id);
	}
	(void) vfprintf(errfile, fmt, ap);
	/* log messages don't come with their own \n */
	(void) fprintf(errfile, "\n");
	(void) fflush(errfile);
	break;

    case WRITE_LOG_SYS:
	/* write out permanent log to the system log file */
	if (logfile == NULL || panicfile == NULL) {
	    open_system_logs();		/* if system log not open, open it */
	}
#ifdef LOG_EXTRA_NEWLINE
	(void) fprintf(logfile, "\n%s: [%lu] ", time_stamp(), (long unsigned int) getpid());
#else
	(void) fprintf(logfile, "%s: [%lu] ", time_stamp(), (long unsigned int) getpid());
#endif
	if (message_id) {
	    (void) fprintf(logfile, "[%s] ", message_id);
	}
	(void) vfprintf(logfile, fmt, ap);
	/* log messages don't come with their own \n */
	(void) fprintf(logfile, "\n");
	(void) fflush(logfile);
	if (ferror(logfile)) {
	    panic(EX_IOERR, "error writing %s: %s", log_fn, strerror(errno));
	    /*NOTREACHED*/
	}
	break;

    case WRITE_LOG_PANIC:
	if (panicfile == NULL) {
	    open_system_logs();		/* if system log not open, open it */
	}
#ifdef LOG_EXTRA_NEWLINE
	(void) fprintf(panicfile, "\n%s: [%lu] ", time_stamp(), (long unsigned int) getpid());
#else
	(void) fprintf(panicfile, "%s: [%lu] ", time_stamp(), (long unsigned int) getpid());
#endif
	if (message_id) {
	    (void) fprintf(panicfile, "[%s] ", message_id);
	}
	(void) vfprintf(panicfile, fmt, ap);
	/* log messages don't come with their own \n */
	(void) fprintf(panicfile, "\n");
	(void) fflush(panicfile);
	if (ferror(panicfile)) {
	    panicfile = NULL;
	    panic(EX_IOERR, "error writing %s: %s", log_fn, strerror(errno));
	    /*NOTREACHED*/
	}
	break;

    /*
     * NOTE:  if there is no spool_dir set, then a per-message logfile
     *	      cannot be created, so don't bother writing per-message
     *	      log entries.
     */
    case WRITE_LOG_MLOG:
	if (spool_dir) {
	    if (msg_logfile == NULL) {
		open_msg_log();		/* if message log not open, open it */
	    }

	    /* write out the message to the per-message log file */
	    (void) vfprintf(msg_logfile, fmt, ap);
	    /* log messages don't come with their own \n */
	    (void) fprintf(msg_logfile, "\n");
	    (void) fflush(msg_logfile);
	    if (ferror(msg_logfile)) {
		panic(EX_IOERR, "error writing %s/%s: %s",
		      spool_dir, msg_log_fn, strerror(errno));
		/*NOTREACHED*/
	    }
	}
	break;

    }
}

/*
 * send_log - write out the per-message log file.
 *
 * Send the per-message log to a file with or without lines that begin
 * with the magic X character.  Also, an optional banner string will
 * be output before any log data.  If no log data is output then the
 * banner string is also not output.
 *
 * Note: we reopen to keep from changing the write position while
 *       reading.
 */
void
send_log(f, all, banner)
    FILE *f;				/* send to this file */
    int all;				/* if TRUE, also send X lines */
    char *banner;			/* if non-NULL precedes log output */
{
    FILE *mlog;				/* message log file */
    char *mln;				/* line from log file */
    int lastx = FALSE;			/* last line started with 'X' */

    build_msg_log_fn();
    /* reopen log file to avoid changing the write position */
    if (! (mlog = fopen(msg_log_fn, "r"))) {
	DEBUG2(DBG_LOG_MID, "send_log(): no message log file: %s: %s.\n", msg_log_fn, strerror(errno));
	return;
    }
    /* copy from message-log to the specified file */
    while ((mln = read_line(mlog))) {
	int len;

	/* trim the trailing newline, if any */
	len = strlen(mln);
	if (mln[len - 1] == '\n') {
	    mln[len - 1] = '\0';
	}
	if (!all && *mln == 'X') {
	    lastx = TRUE;
	    continue;
	}
	if (lastx) {
	    /* ignore any SMTP replies following the 'X' line too */
	    if (strlen(mln) >= 4 && isdigit(mln[0]) && isdigit(mln[1]) && isdigit(mln[2])) {
		if (mln[3] == ' ') {
		    lastx = FALSE;
		}
		continue;
	    } else {
		lastx = FALSE;
	    }
	}
	if (banner) {
	    (void) fputs(banner, f);
	    banner = NULL;		/* output banner at most once */
	}
	(void) fprintf(f, " %s\n", mln);
    }
    (void) fclose(mlog);		/* don't need this anymore */
}

/*
 * scan_msg_log - scan for X entries in the per-message logfile
 *
 * if first is TRUE, open the message log and scan for the first line
 * marked with an X, return that line.  On successive calls, where
 * first is FALSE, return successive lines marked with an X.  Return
 * NULL if no more such lines are found.
 *
 * returned value points to a string area which may be reused on subsequent
 * calls to scan_msg_log.
 */
char *
scan_msg_log(first)
    int first;				/* TRUE to re-open the message log */
{
    static FILE *mlog = NULL;		/* opened message log */
    static struct str line;		/* line marked with X */
    static int inited_line = FALSE;	/* TRUE if STR_INIT called for line */
    register int c, last_c;

    build_msg_log_fn();
    if (!mlog) {
	DEBUG2(DBG_LOG_HI, "scan_msg_log(): %s scanning message log file: %s.\n", first ? "starting" : "re-starting", msg_log_fn);
	if (!(mlog = fopen(msg_log_fn, "r"))) {	/* reopen log file */
	    DEBUG2(DBG_LOG_MID, "scan_msg_log(): no message log file: %s: %s.\n", msg_log_fn, strerror(errno));
	    return NULL;
	}
    }
    if (first) {
	rewind(mlog);
    }
    last_c = '\n';

    /* scan for line beginning with X */
    while ((c = getc(mlog)) != EOF) {
	if (c == 'X' && last_c == '\n') {
	    break;
	}
	last_c = '\n';
    }
    if (c == EOF) {
	/* reached end of file without finding another X line */
	DEBUG1(DBG_LOG_HI, "scan_msg_log(): finished scanning message log file: %s.\n", msg_log_fn);
	(void) fclose(mlog);
	mlog = NULL;
	return NULL;
    }
    /* found a line marked with X, read it in */
    if (! inited_line) {
	STR_INIT(&line);
	inited_line = TRUE;
    } else {
	STR_CHECK(&line);
	STR_CLEAR(&line);
    }
    STR_NEXT(&line, 'X');
    last_c = 'X';
    while ((c = getc(mlog)) != EOF) {
	if (last_c == '\n' && c == 'X') {
	    break;
	}
	STR_NEXT(&line, c);
	last_c = c;
    }
    if (c == 'X') {
	ungetc(c, mlog);		/* back for next time */
    }
    if (last_c == '\n') {
	STR_PREV(&line);		/* trim off the last newline */
    }
    STR_NEXT(&line, '\0');

    /* return that line */
    DEBUG1(DBG_LOG_HI, "scan_msg_log() returns:\n'%s'\n", STR(&line));

    return STR(&line);
}


#ifdef STANDALONE

/*
cc -g -DSTANDALONE -o log.stand log.c spool.o alloc.o string.o sysdep.o config.o ascii.o
*/

int debug = 0;				/* adjust as desired */

static char *panic_fn = "paniclog";
static char *log_fn = "logfile";
static char *cons_fn = "/dev/console";
static char *spool_fn = "spoolfile";
char *msg_log_dir = "/tmp";
char *program = "log";
int exitvalue = 0;
enum er_proc error_processing = MAIL_BACK;
int return_to_sender = FALSE;
int islocal = TRUE;
char *sender = "nsc!tron";
char *sender_name = "MasterControlProgram";
char *local_sender;
int force_zero_exitvalue = FALSE;
FILE *errfile = stderr;
long unsigned int daemon_pid = 4000000000;	/* 0xEE6B2800 */
int real_uid = 0;

char *
bind_compute_domain()
{
    return NULL;
}

/*
 * excersize the logging code by performing the various operations
 * in a sequence given at run time on the standard input.
 */
/* ARGSUSED */
int
main(argc, argv)
    int argc;
    char *argv[];
{
    register int c;			/* hold key for current operation */
    char *line;				/* a line of input */
    char *p;

    while ((c = getchar()) != EOF) {
	switch (c) {
	case 'O':
	    open_system_logs();
	    break;
	case 'C':
	    close_system_logs();
	    break;
	case 'o':
	    open_msg_log();
	    break;
	case 'c':
	    close_msg_log();
	    break;
	case 's':
	    send_log(stdout, FALSE, "|----- log without X lines -----|\n");
	    break;
	case 'S':
	    send_log(stdout, TRUE, "|----- log with X lines -----|\n");
	    break;
	case 'X':
	    for (p = scan_msg_log(TRUE); p; p = scan_msg_log(FALSE)) {
		printf("%s\n", p);
	    }
	    break;
	case 'p':
	    if ((line = read_line(stdin)))
		panic(EX_OSERR, line);
	    break;
	case 'l':
	    if ((line = read_line(stdin)))
		write_log(WRITE_LOG_SYS, line);
	    break;
	case 'm':
	    if ((line = read_line(stdin)))
		write_log(WRITE_LOG_MLOG, line);
	    break;
	case 'L':
	    if ((line = read_line(stdin)))
		write_log(WRITE_LOG_MLOG|WRITE_LOG_SYS|WRITE_LOG_PANIC, line);
	    break;
	case ' ':
	case '\t':
	case '\n':
	    break;
	default:
	    (void)fprintf(stderr, "%c -- huh?\n", c);
	    break;
	}
    }
    exit(EX_OK);
}
#endif	/* STANDALONE */
