/*
 * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University
 * in the City of New York.  Permission is granted to any individual or
 * institution to use, copy, or redistribute this software so long as it
 * is not sold for profit, provided this copyright notice is retained.
 */

#ifndef lint
static char *rcsid = "$Header: print.c,v 1.13 88/07/22 20:03:23 howie Exp $";
#endif

#include "mm.h"
#include "parse.h"
#include "cmds.h"

#define NULLSWIT 0
#define HEADERS_ONLY 1
#define SEPARATE_PAGES 2


/*
 * cmd_list:
 * list messages to file or pipe.
 * a combination of the MM-20 LIST and FILE-LIST commands.
 */

int
cmd_list (n)
int n;
{
    int sw = NULLSWIT;
    static char *listdest = NULL;
    char *parse_listfile();
    FILE *od, *popen(), *fopen();
    int pipep;
    int last_message;
    message *m;
    char *msgtxt, *fmt_message();
    keylist only, dont;
    int seq;

    if (listdest != NULL) {		/* catch the memory leak */
	free (listdest);
	listdest = NULL;
    }

    check_cf(false);			/* have to have a current file */
    noise ("to");
    listdest = parse_listfile(&sw);	/* get destination file/pipe */
    seq = parse_sequence("current",NULL,NULL); /* get a sequence */

    /*
     * NOTE:
     * If seq is TRUE (we parsed a sequence from the top level)
     * then we want to check that there is something in the sequence,
     * so we call sequence_start.
     * If seq is FALSE we want to print the current message and DO NOT
     * want to call sequence_start or sequence_next down in the while().
     */
    if (!seq || sequence_start(cf->sequence)) {	/* do we have a sequence? */
	last_message = sequence_last (cf->sequence); /* remember last msg */

	if (listdest == NULL) {		/* /print */
	    pipep = true;
	    if (strlen(print_filter) == 0)
		cmerr ("no print filter - use SET PRINT-FILTER to define one");
	    od = popen (print_filter, "w");
	}
	else if (listdest[0] == '|' && listdest[1] == ' ') { /* pipe */
	    pipep = true;
	    od = popen (listdest+2, "w"); /* open the pipe */
	}
	else {
	    pipep = false;
	    od = fopen (listdest, "w");	/* open the output file */
	}

	if (od == NULL) {		/* do we have an output descriptor */
	    if (pipep)
		cmxeprintf ("?could not start process %s\n", listdest+2);
	    else
		perror (listdest);
	    free (listdest);		/* clean up */
	    listdest = NULL;
	    return false;
	}

	/* title (filename and date of listing) */
	fprintf (od, "-- Messages from file: %s --\n", cf->filename);
	fprintf (od, "   %s\n\n", daytime(0));

	if ((sw == HEADERS_ONLY) || list_include_headers) {
	    do {			/* pass one for headers */
		header_summary (cf->current, od); /* ta da! */
	    } while (seq && sequence_next (cf->sequence));
	}

	if (sw != HEADERS_ONLY) {	/* message bodies too */
	    dont = (n == CMD_LITERAL) ? nil : dont_print_headers;
	    only = (n == CMD_LITERAL) ? nil : only_print_headers;
	    if (seq)
	        sequence_start (cf->sequence); /* same sequence */
	    fputc ('\n', od);		/* separate headers from messages */
	    fputc ('\f', od);		/* with a formfeed */
	    fputc ('\n', od);
	    do {			/* pass 2 for message body */
		m = &cf->msgs[cf->current];
		fprintf(od, "Message %d -- *********************\n",
			cf->current);
		if ((msgtxt = fmt_message(m, only, dont)) == NULL)
		    fputc ('\n', od);
		else {
		    fprintf (od, "%s\n", msgtxt);
		    free (msgtxt);
		}
		if (((sw == SEPARATE_PAGES) || list_on_separate_pages)
		    && (cf->current != last_message)) {
		    fputc ('\f', od);
		    fputc ('\n', od);
		}
	    } while (seq && sequence_next (cf->sequence));
	}
	
	if (pipep)			/* close up */
	    pclose (od);
	else
	    fclose (od);
	if (mode == MM_TOP_LEVEL)	/* print that sequence so they */
	    seq_print (true);		/* know we did something */

    }

    if (listdest != NULL) {		/* clean up */
	free (listdest);
	listdest = NULL;
    }
    return true;			/* done! */
}


/*
 * parse_listfile:
 * parse for optional switches followed by a filename or '|' and command
 * on return, the return value is either a filename or a command starting
 * with "| ".
 * Or, if it returns NULL, then the /print switch was parsed instead of
 * a filename or command.
 * sw may also be set.
 * Note:
 * caller is responsible of freeing the return value when done.
 */

char *
parse_listfile (sw)
int *sw;				/* switch parsed if any */
{
    static keywrd swkeys[] = {
	{ "headers-only", 0, HEADERS_ONLY },
	{ "separate-pages", 0, SEPARATE_PAGES }
    };
    static keytab swtab = { sizeof(swkeys) / sizeof (keywrd), swkeys };
    static fdb switches = { _CMSWI, 0, NULL, (pdat) &swtab, NULL, NULL, NULL };
    static keywrd prkey[] = {
	{ "print", 0, 0 }
    };
    static keytab prtab = { sizeof(prkey) /sizeof (keywrd), prkey };
    static fdb prfdb = { _CMSWI, 0, NULL, (pdat) &prtab, NULL, NULL, NULL };
    static fdb filnam = { _CMFIL, CM_SDH|FIL_PO|FIL_NODIR, NULL, NULL, 
			    "file to list to", NULL, NULL };
    static fdb pipe = { _CMTOK, CM_SDH, NULL, (pdat) "|", 
			  "\"|\" followed by a quoted command line", 
			  NULL, NULL };
    static fdb comm = { _CMQST, CM_SDH, NULL, NULL, "quoted command line",
			  NULL, NULL };
    pval parseval;
    fdb *used;
    char *ret;

    *sw = NULLSWIT;
#ifdef undef
    pipe._cmdef = list_destination;	/* XXX add this var sometime */
#endif /* undef */
    parse (fdbchn(&switches, &prfdb, &pipe, &filnam, NULL), &parseval, &used);
    if (used == &switches) {
	*sw = parseval._pvint;
	parse (fdbchn(&prfdb, &pipe, &filnam, NULL), &parseval, &used);
    }

    if (used == &prfdb) {
	return (NULL);
    }
    else if (used == &filnam) {
	if (*parseval._pvfil[0] == '\0')
	    cmerr ("destination for output not specified");
	ret = (char *) malloc (strlen(parseval._pvfil[0])+1);
	strcpy (ret, parseval._pvfil[0]);
	return (ret);
    }
    else {				/* if (used == &pipe)  */
	parse (&comm, &parseval, &used);
	ret = (char *) malloc (strlen(parseval._pvstr)+3);
	sprintf (ret, "| %s", parseval._pvstr);
	return (ret);
    }
}


/*
 * cmd_print:
 */

int
cmd_print (n)
int n;
{
    static keywrd swkeys[] = {
	{ "separate-pages", 0, SEPARATE_PAGES }
    };
    static keytab swtab = { sizeof(swkeys) / sizeof (keywrd), swkeys };
    static fdb switches = { _CMSWI, 0, NULL, (pdat) &swtab, NULL, NULL, NULL };
    fdb *u;
    FILE *pp, *popen();
    message *m;
    char *msgtxt, *fmt_message();
    keylist only, dont;
    int seq;
    int last_message;
    int sw = NULLSWIT;

    check_cf(false);			/* need a file of messages */

    seq = parse_sequence("current", fdbchn(&switches,NULL), &u);
    if (u != NULL) {
	sw = pv._pvint;
	seq = parse_sequence("current", NULL, NULL);
    }


    /*
     * NOTE:  See NOTE in cmd_list
     */
    if (!seq || sequence_start(cf->sequence)) {	/* got a sequence? */
	last_message = sequence_last (cf->sequence); /* remember last msg */

	if (strlen(print_filter) == 0)
	    cmerr ("no print filter - use SET PRINT-FILTER to define one");
	if ((pp = popen (print_filter, "w")) == NULL) {
	    cmxeprintf ("?could not start process %s\n", print_filter);
	    return false;
	}
	
	dont = (n == CMD_LITERAL) ? nil : dont_print_headers;
	only = (n == CMD_LITERAL) ? nil : only_print_headers;
	do {
	    m = &cf->msgs[cf->current];
	    if ((msgtxt = fmt_message(m, only, dont)) == NULL)
		fputc ('\n', pp);
	    else {
		fprintf (pp, "%s\n", msgtxt);
		free (msgtxt);
	    }
	    if (((sw == SEPARATE_PAGES) || list_on_separate_pages)
		&& (cf->current != last_message)) {
		fputc ('\f', pp);
		fputc ('\n', pp);
	    }
	} while (seq && sequence_next (cf->sequence));
	
	pclose (pp);
	if (mode == MM_TOP_LEVEL)
	    seq_print (true);
    }
    return true;
}


/*
 * cmd_literal:
 * parse for type/print/list (default type) and call appropriate command
 */

cmd_literal(n)
int n;
{
    static keywrd litkeys[] = {
	{ "list", 0, CMD_LIST },
	{ "print", 0, CMD_PRINT },
	{ "type", 0, CMD_TYPE }
    };
    static keytab littab = { sizeof(litkeys) / sizeof (keywrd), litkeys };
    static fdb litfdb = { _CMKEY, 0, NULL, (pdat) &littab, NULL,
			      "type", NULL };
    int seq;
    fdb *u;

    check_cf (false);			/* since we call parse_sequence */
    seq = parse_sequence ("current", fdbchn(&litfdb,NULL), &u);
    if (u != NULL) {			/* parsed a command */
	(void) (*mm_cmds[pv._pvkey]) (CMD_LITERAL);
    }
    else {				/* got a sequence */
	check_mark();			/* default TYPE */
	real_type (CMD_LITERAL, seq);
    }
}
