#include "args.h"
#include "config.h"

/*
 * Copyright (c) 1986, 2014 by The Trustees of Columbia University in
 * the City of New York.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  + Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 *  + Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *
 *  + Neither the name of Columbia University nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 */

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

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

static char *parse_listfile ARGS((int *sw));

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

int
#if HAVE_STDC
cmd_list (int n)
#else /* K&R style */
cmd_list (n)
int n;
#endif /* HAVE_STDC */
{
    int sw = NULLSWIT;
    static char *listdest = NULL;
    FILE *od;
    int pipep;
    int last_message;
    message *m;
    char *msgtxt;
    keylist only, dont;
    int seq;

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

    if (!check_cf(O_RDONLY))		/* have to have a current file */
      return(false);
    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 = mm_popen (print_filter, "w");
	}
	else if (listdest[0] == '|' && listdest[1] == ' ') { /* pipe */
	    pipep = true;
	    od = mm_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, true); /* 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 {
		    fwrite (msgtxt, sizeof(char), strlen(msgtxt), od);
		    fputc('\n', od);
		    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 */
	    mm_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.
 */

#define pipe pipe_		/* avoid warnings about shadowed global */
#define used used_		/* avoid warnings about shadowed global */

static char *
#if HAVE_STDC
parse_listfile (int *sw)
        				/* switch parsed if any */
#else /* K&R style */
parse_listfile (sw)
int *sw;				/* switch parsed if any */
#endif /* HAVE_STDC */
{
    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_VAL|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
#if HAVE_STDC
cmd_print (int n)
#else /* K&R style */
cmd_print (n)
int n;
#endif /* HAVE_STDC */
{
    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;
    message *m;
    char *msgtxt;
    keylist only, dont;
    int seq;
    int last_message;
    int sw = NULLSWIT;

    if (!check_cf(O_RDONLY))		/* need a file of messages */
      return (false);

    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 = mm_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 {
		fwrite(msgtxt, sizeof(char), strlen(msgtxt), pp);
		fputc('\n', pp);
		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));

	mm_pclose (pp);
	if (mode == MM_TOP_LEVEL)
	    seq_print (true);
    }
    return true;
}

/* Code for downloading messages (fdc, Sep 2002) */

static brktab nambrk = {
/* _CMFLD break table for foreign filename: */
/* Allows letters, digits, hyphens (also +%_.!) */
    {
/*      0-7     8-15    16-23   24-31   32-39   40-47   48-55   56-63   */
	0xff,   0xff,   0xff,   0xff,   0x80,   0x00,   0x00,   0x01,
	0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x01
/*      64-71   72-79   80-87   88-95   96-103  104-111 112-119 120-127 */
    },
    {
#ifdef COMMENT
	0xff, 0xff, 0xff, 0xff, 0xb2, 0xe8, 0x00, 0x3f,
	0x80, 0x00, 0x00, 0x16, 0x80, 0x00, 0x00, 0x1f
#else
	0xff,   0xff,   0xff,   0xff,   0x80,   0x00,   0x00,   0x01,
	0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x00,   0x01
#endif /* COMMENT */
    }
};

#define DNCMDBUF 511

int
#if HAVE_STDC
cmd_download (int n)
#else /* K&R style */
cmd_download (n)
int n;
#endif /* HAVE_STDC */
{
    static keywrd swkeys[] = {
	{ "filename:", 0, DOWNLOAD_FILE },
	{ "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 fdb nametext = { _CMFLD,0,NULL,NULL,NULL,"mm.eml",&nambrk,NULL };
    fdb *u, * u2;
    pval parseval;
    FILE *pp, *mm_popen();
    message *m;
    char *msgtxt, *fmt_message();
    keylist only, dont;
    int len, len2;
    int seq;
    int last_message;
    int separate = 0;			/* Flag for /SEPARATE */
    char dlname[DNCMDBUF+1];		/* User-provided filename */
    char dlcmd[DNCMDBUF+1];		/* Constructed download command */

    if (!check_cf(O_RDONLY))		/* Need a file of messages */
      return (false);

    dlname[0] = 0;			/* Stays empty if no /FILENAME: */
    while (1) {				/* Parse switches or msg sequence */
	seq = parse_sequence("current", fdbchn(&switches,NULL), &u);
	if (!u)
	  break;
	if (pv._pvint == DOWNLOAD_FILE) { /* If /FILENAME:, get arg */
	    parse(&nametext,&parseval,&u2);
	    strncpy(dlname,atmbuf,DNCMDBUF);
	    dlname[1023] = 0;
	} else {
	    separate++;
	}
    }
    if (!seq || sequence_start(cf->sequence)) {	/* got a sequence? */

	char * cp;
	int count = 0;
	int len3 = 0;

	last_message = sequence_last (cf->sequence); /* remember last msg */

	/* Get download filter and maybe plug filename into it */
	/* Maybe more than once - but first make sure result is not too long */

	if ((len = strlen(download_filter)) == 0)
	  cmerr ("no download filter - use SET DOWNLOAD-FILTER to define one");
	if (!dlname[0])
	  strncpy(dlname,"mm.eml",DNCMDBUF);
	len2 = strlen(dlname);
	len3 = len;
	cp = dlname;
	while ((cp = index(cp,'%'))) {
	    if (cp[1] == 's') {
		count++;
		len3 += (len2 - 2);
	    }
	    cp++;
	}
	if (len3 > DNCMDBUF-1 || count > 4)
	  cmerr ("download filter string too long");
	sprintf(dlcmd,download_filter,dlname,dlname,dlname,dlname); /* SAFE */
	dlname[1023] = 0;

	if ((pp = mm_popen (dlcmd, "w")) == NULL) {
	    cmxeprintf ("?could not start process: %s\n", download_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 {
		fwrite(msgtxt, sizeof(char), strlen(msgtxt), pp);
		fputc('\n', pp);
		free (msgtxt);
	    }
	    if ((separate || list_on_separate_pages)
		&& (cf->current != last_message)) {
		fputc ('\f', pp);
		fputc ('\n', pp);
	    }
	} while (seq && sequence_next (cf->sequence));
	
	mm_pclose (pp);
	if (mode == MM_TOP_LEVEL)
	  seq_print (true);
    }
    return true;
}

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

void
#if HAVE_STDC
cmd_literal(int n)
#else /* K&R style */
cmd_literal(n)
int n;
#endif /* HAVE_STDC */
{
    static keywrd litkeys[] = {
	{ "download", 0, CMD_DOWNLOAD },
	{ "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;

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