/*
 * 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: more.c,v 1.20 88/08/16 20:56:53 chris Exp $";
#endif

/* 
 * more.c - routines for displaying messages (the 'type' command, etc),
 * by piping them through "more" if appropriate
 *
 */

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

char *malloc ();
FILE *popen ();

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

int display_width, display_length;

/*
 * more_init:
 * initializes display_width and display_length for "more" decisions
 */
more_init()
{
    char buf[4096];
    char *term, *getenv();

    if ((term = getenv ("TERM")) == NULL)
	term = "unknown";
    if (tgetent (buf, term) == -1) {
	display_width = 80;
	display_length = 24;
    }
    else {
	if ((display_width = tgetnum("co")) == -1) /* failed */
	    display_width = 80;
	if ((display_length = tgetnum("li")) == -1)
	    display_length = 24;
    }
}

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

int
bad_header (line, only, dont)
char *line;
char **only;
char **dont;
{
    char *p;

    if (only != (char **) NULL) {
	while (p = *only++)
	    if (hdrcmp (p, line))
		return false;
	return true;
    }
    if (dont != (char **) NULL) {
	while (p = *dont++)
	    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
span (s1, s2)
char *s1, *s2;
{
    char c;
    char *p;
    int n = 0;

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

int
logical_lines (s, max)
char *s;
int max;
{
    int n, pos;

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

char *
fmt_message (m, only, dont)
message *m;
char **only, **dont;
{
    char *buf, *cur;
    char *p = m->text;
    int left = m->size;
    int context = H_OK;
    int linelength;
    char c;

    buf = malloc (m->size + 4);

    cur = buf;
    while (left > 0 && context != H_END) {
	if (*p == '\n')
	    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 */
	  case H_SKIP:			/* header user doesn't want */
	    p += linelength;
	    left -= linelength;
	    break;
	  case H_END:			/* end of headers or error */
	    left--;			/* end of headers is one char */
	    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 %d chars, had %d left over\n",
		 m->size, left);
    return buf;
}

display_message (out, m, maybeclear, only, dont)
FILE *out;
message *m;
int maybeclear;
char **only, **dont;
{
    char *msg;
    FILE *fp;
    FILE *more_pipe_open();
    extern int use_crt_filter_always;
    int c;
#ifdef sun_stdio_bug
    char tmpbuf[BUFSIZ];
#endif
    msg = fmt_message (m, only, dont);
    if (msg == NULL) return 0;
    
    if (maybeclear && clear_screen && (out == stdout))
	blank ();

    if (use_crt_filter_always ||
	(logical_lines (msg, display_length) +1 >= display_length)) {
	fp = more_pipe_open(out);
#ifdef sun_stdio_bug
	setbuf (fp, tmpbuf);
#endif
    }
    else
	fp = out;			/* not long, just write it */

    c = (int) (m - cf->msgs);
    fprintf (fp, " Message %d (%d chars)\n", c, m->size);
    fputs (msg, fp);
    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 signalhandler (*old_sigpipe)();
static int pipe_broke;
static signalhandler (*sigint)(), (*sigtstp)(), (*sigquit)();

signalhandler
on_sigpipe ()
{
    pipe_broke = 1;
}

/*
 * more_pipe_open:
 * try to open the pipe to more our message, return either the pipe FILE *
 * or the FILE * we were given
 */
FILE *
more_pipe_open (out)
FILE *out;
{
    FILE *fp;
    int n;

    if ((out == stdout) &&
	(isatty (fileno (out))) &&
	(display_length > 0) &&
	(crt_filter[0] != 0)) {
	fflush (stdout);
	fflush (stderr);
	cmtend ();			/* shut off ccmd's control of tty */
	if (fp = popen (crt_filter, "w")) {
	    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
 */
more_pipe_close(fp)
FILE *fp;
{
    signal (SIGPIPE, SIG_IGN);
    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;
}
    
