#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.
 */

#ifndef lint
static const char *rcsid = "$Header: /usr/local/src/mm/mm-0.94/mm/RCS/more.c,v 1.1 2005/05/28 22:27:52 beebe Exp $";
#endif

/*
 * more.c - routines for displaying messages (the 'type' command, etc),
 * by piping them through "more" if appropriate
 * Changed 2014/02/23 to differentiate between messages (which might be
 * MIME encoded) and other text (e.g. headers listing, help text, etc).
 */

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

static int bad_header ARGS((char *line, char **only, char **dont));
static signalhandler on_sigpipe ARGS((int dummy));

extern int use_crt_filter_always, ltype;

#define H_END	0
#define H_SKIP	1
#define H_OK	2

/*
 * bad_header - succeeds if header should be skipped according to
 * how user has specified only-type-headers and dont-type-headers.
 */

static int
#if HAVE_STDC
bad_header (char *line, char **only, char **dont)
#else /* K&R style */
bad_header (line, only, dont)
char *line;
char **only;
char **dont;
#endif /* HAVE_STDC */
{
    char *p;

    if (only != (char **) NULL) {
	while ((p = *only++, p))
	    if (hdrcmp (p, line))
		return false;
	return true;
    }
    if (dont != (char **) NULL) {
	while ((p = *dont++, p))
	    if (hdrcmp (p, line))
		return true;
    }
    return false;
}

/*
 * span - find the length of the leading substring of s1 containing
 * only those characters from set s2
 */

int
#if HAVE_STDC
span (const char *s1, const char *s2)
#else /* K&R style */
span (s1, s2)
const char *s1, *s2;
#endif /* HAVE_STDC */
{
    char c;
    const char *p;
    int n = 0;

    while ((c = *s1++, c)) {
	p = s2;
	while (*p && c != *p)
	    p++;
	if (*p == 0)
	    break;
	n++;
    }
    return n;
}

int
#if HAVE_STDC
logical_lines (const char *s, int max)
#else /* K&R style */
logical_lines (s, max)
const char *s;
int max;
#endif /* HAVE_STDC */
{
    int n, pos;

    for (n = 0, pos = 0; *s; s++)	/* count newlines, long lines */
	if (*s == '\n' || ++pos > cmcsb._cmcmx) {
	    n++, pos = 0;
	    if (n == max) return n;	/* stop if no point in continuing */
	}
    if (pos) n++;			/* count partial last line */
    return n;
}

char *
#if HAVE_STDC
fmt_message (message *m, char **only, char **dont)
#else /* K&R style */
fmt_message (m, only, dont)
message *m;
char **only, **dont;
#endif /* HAVE_STDC */
{
    char *buf, *cur;
    char *p = m->text;
    int left = m->size;
    int context = H_OK;
    int linelength;
    char c;

    buf = (char*)malloc (m->size + 4);
    if (buf == NULL) {
	fprintf (stderr, "fmt_message: out of memory\n");
	return(NULL);
    }

    cur = buf;
    while (left > 0 && context != H_END) {
	if (*p == '\n' || *p == '\0')
	    context = H_END;
	else
	    context = (bad_header (p, only, dont) ? H_SKIP : H_OK);
	linelength = skipheader (p) - p;
	switch (context) {
	  case H_OK:			/* normal header */
	    strncpy (cur, p, linelength);
	    cur += linelength;
	    /* fall through */	/*FALLTHROUGH*/
	  case H_SKIP:			/* header user doesn't want */
	    p += linelength;
	    left -= linelength;
	    break;
	  case H_END:			/* end of headers or error */
	    break;
	}
    }

    /* copy out the rest of the message */
    while (left > 0 && (c = *p++)) {
	*cur++ = c;
	--left;
    }

    if (cur > buf && (cur[-1] != '\n'))
	*cur++ = '\n';			/* drop in terminating newline */

    *cur = 0;				/* null-terminate the message */

    if (left != 0)
	fprintf (stderr, "fmt_message: expected %lu chars, had %d left over\n",
		 m->size, left);
    return buf;
}

int
#if HAVE_STDC
display_message (FILE *out, message *m, int maybeclear, char **only, char **dont)
#else /* K&R style */
display_message (out, m, maybeclear, only, dont)
FILE *out;
message *m;
int maybeclear, literal;
char **only, **dont;
#endif /* HAVE_STDC */
{
    char *msg;
    FILE *fp;
    int c;
    msg = fmt_message (m, only, dont);
    if (msg == NULL) return 0;

    if (maybeclear && clear_screen && (out == stdout))
	blank ();

#ifdef COMMENT
    if (ltype) filter_to_use = crt_filter; /* fdc 2014-02-26 */
    if (logical_lines (msg, cmcsb._cmrmx) +1 >= cmcsb._cmrmx) {	/* fdc */
	fp = more_pipe_open(out, filter_to_use); /* fdc */
    }
#else  /* fdc 2014/03/03 */
    if (ltype) {			/* If LITERAL TYPE... */
        /* Check length */
	if (logical_lines (msg, cmcsb._cmrmx) +1 >= cmcsb._cmrmx) {
	    /* If longer than a screenful, pipe through CRT filter */
	    fp = more_pipe_open(out, crt_filter);
	} else {			/* Not long, just write it */
	    fp = out;
	}      
    } else {				/* Not LITERAL, use MIME filter */
	fp = more_pipe_open(out, mime_filter);
    }
#endif /* COMMENT */
    c = (int) (m - cf->msgs);
    fprintf (fp, " Message %d (%lu chars)\n", c, m->size);
#ifdef sun_stdio_bug
    fwrite (msg, sizeof(char), strlen(msg), fp);
#else
    fputs (msg, fp);
#endif /* sun_stdio_bug */
    free (msg);
    if (fp == out) {	 		/* not a pipe */
	fflush (fp);
	return(false);
    }
    more_pipe_close(fp);		/* really a pipe */
    return(true);
}


/* for more_pipe_open and _close */
static int pipe_broke;
static signalhandler (*old_sigpipe) ARGS((int n));
static signalhandler (*sigint) ARGS((int n));
static signalhandler (*sigtstp) ARGS((int n));
static signalhandler (*sigquit) ARGS((int n));

static signalhandler
#if HAVE_STDC
on_sigpipe (int dummy)
#else /* K&R style */
on_sigpipe (dummy)
int dummy;
#endif /* HAVE_STDC */
{
    pipe_broke = 1;
    signal (SIGPIPE, on_sigpipe);
    return signalhandler_retval;
}

/*
 * more_pipe_open:
 * try to open the pipe to more our message, return either the pipe FILE *
 * or the FILE * we were given
 */
FILE *
#if HAVE_STDC
more_pipe_open (FILE *out, char *filter)
#else /* K&R style */
more_pipe_open (out, filter)
    FILE *out; char *filter;
#endif /* HAVE_STDC */
{
    FILE *fp;

    if ((out == stdout) &&
	(isatty (fileno (out))) &&
	(cmcsb._cmrmx > 0) &&
	(crt_filter[0] != 0)) {
	fflush (stdout);
	fflush (stderr);
	cmtend ();			/* shut off ccmd's control of tty */
	if ((fp = mm_popen (filter, "w"), fp)) {
	    pipe_broke = 0;
	    old_sigpipe = signal (SIGPIPE, on_sigpipe);
	    fflush (stdout);
	    fflush (stderr);
#ifdef SIGTSTP
	    sigtstp = signal (SIGTSTP, SIG_DFL),
#endif
	    sigint = signal (SIGINT, SIG_IGN),
	    sigquit = signal (SIGQUIT, SIG_IGN);
	    return (fp);
	}
	else {
	    cmtset ();
	    cmcsb._cmwrp = autowrap_column; /* gets reset */
	    return (out);		/* can't open pipe, send to stdout */
	}
    }
    return(out);			/* use what they gave us */
}

/*
 * more_pipe_close:
 * clean up and close this pipe
 */
void
#if HAVE_STDC
more_pipe_close(FILE *fp)
#else /* K&R style */
more_pipe_close(fp)
FILE *fp;
#endif /* HAVE_STDC */
{
    signal (SIGPIPE, SIG_IGN);
    mm_pclose (fp);
    signal (SIGPIPE, old_sigpipe);
    signal (SIGINT, sigint),
    signal (SIGQUIT, sigquit);
#ifdef SIGTSTP
    signal (SIGTSTP, sigtstp);
#endif
    fflush(stdout);
    cmtset ();				/* setup tty for ccmd again */
    cmcsb._cmwrp = autowrap_column;	/* gets reset */
    return;
}
