
/*
 *  SHOWART.C
 */

#include "news.h"
#include <ctype.h>

#define ON		scr_inverse_on()
#define OFF		scr_inverse_off()
#define Clear_Line	printf("\r\x9bK")
#define Clear_Screen	printf("\x9bH\x9bJ")

extern void scr_inverse_on(), scr_inverse_off();
extern char *get_next_art();

extern char *NewsDir;

static char *ArtHelp[] = {
	"(space)     Display next page of current article",
	"(return)    Display next line of current article (scroll)",
	"(backspace) Restart current article",
	"n/N         Goto the next article",
	"d/D         Delete this article",
	"u/U         Undelete this article",
	"-           Backup to previous article",
	"^           Goto the first article",
	"(number)    Goto the given article number",
	"$           Goto next news group",
	"r/R         Reply -- send mail to the poster of current article",
	"f/F         Followup -- post a response to the current article",
	"p/P         Post  -- post an article",
	"=           List article subjects for the remaining articles",
	"./>         Scan subject of next article, hit return to read it",
	",/<         Backup one article, displaying its subject",
	"s/S         Write the current article to a file",
	"w/W         Write the current article to a file",
	"h/H/?       Display help",
	"q           Quit newsgroup (qq = quit program)",
	"Q           Quit newsgroup without deleting any articles",
	0
};

static char *suppressed[] = {
	"Path",
	"Message-ID",
	"Message-Id",
	"Lines",
	"Xref",
	"References",
	"Nf-From",
	"Nf-ID",
	"In-reply-to",
	"From ",
	"Received",
	0
};

static int
showheader(register char *p)
{
    register char **pp;

    for (pp = suppressed; *pp != NULL; pp++) {
	if (strcmp(*pp, p) == 0)
	    return (0);
    }
    return (1);
}

static int EndOfFile, LineCount, CharCount;
static int LineMax, CharMax;
static long LastLine;

static void
SetupPage(void)
{
    Clear_Screen;
    LineCount = 1, CharCount = 1;
/* FIXME: determine size of page dynamically */
    LineMax = NumRows;
    CharMax = NumCols;
    raw(stdin);     /* permit typeahead while displaying page */
}

static int
outc(register int c)
{
    if (c == '\t') {
	switch(CharCount%8) {
	case 1: outc(' ');
	case 2: outc(' ');
	case 3: outc(' ');
	case 4: outc(' ');
	case 5: outc(' ');
	case 6: outc(' ');
	case 7: outc(' ');
	case 0: outc(' ');
	}
	return 0;
    }
    if (c == '\f') {
	LineCount = 999;
	c = '\n';
    }
    if (CharCount > CharMax) {
	++LineCount;
	CharCount = 1;
	if (c == '\n')
	    return (1);
    }
    putchar(c);
    ++CharCount;
    if (c == '\n') {
	++LineCount, CharCount = 1;
	return (1);
    }
    return (0);
}

static void
putline(register char *prefix, register FILE *fp)
{
    register int c;

    EndOfFile = 0;

    LastLine = ftell(fp);   /* remember where this line started */

    while (*prefix != '\0') {
	if (outc(*prefix))
	    return;
	++prefix;
    }
    while ((c = getc(fp)) != EOF) {
	if (outc(c)) {
	    if (c == '\f') {
		/* force page break after form feed */
		LastLine = ftell(fp);
	    }
	    if ((c = getc(fp)) == EOF)
		break;
	    ungetc(c, fp);
	    return;
	}
    }
    /* End-of-file */
    EndOfFile = 1;
    mark_cur_art(1);
    LineCount = 999;
}

static int
DisplayHdr(register FILE *fp)
{
    register char *p;
    register int c, result = 0;
    long here;
    char buf[100];

    for (;;) {
	/* Evaluate one header line */
	here = ftell(fp);
	p = buf;

	do {	/* extract command portion */
	    if ((c = getc(fp)) == EOF)
		return result;
	    if (c == '\n') {
		fseek(fp, here, 0);
		return result;
	    }
	    *p++ = c;
	    if (c == ' ') {
		++p;
		break;
	    }
	} while (c != ':');
	*--p = '\0';

	if ((c = getc(fp)) != ' ')      /* ignore space after colon */
	    ungetc(c, fp);

	/* check for special action on header line */
	if (strcmp(buf, "Subject") == 0) {
	    ON;
	    putline("", fp);
	    OFF;
	} else if (strcmp(buf, "Control") == 0) {
	    ON;
	    putline("Control: ", fp);
	    OFF;
	    result = 1;
	} else if (showheader(buf)) {
	    *p++ = ':';
	    *p++ = ' ';
	    *p = '\0';
	    putline(buf, fp);
	} else {
	    /* ignore skipped header, including continuations */

	    do {
		while ((c = getc(fp)) != '\n') {
		    if (c == EOF)
			return result;
		}
	    } while ((c = getc(fp)) == '\t' || c == ' ');

	    if (c == EOF)
		return result;
	    ungetc(c, fp);
	}

	while ((c = getc(fp)) != EOF) {
	    if (c == '\n') {        /* end of headers */
		ungetc(c, fp);
		return result;
	    }
	    if (c != '\t' && c != ' ')
		break;

	    /* continuation lines */
	    do {
		outc(c);
		if ((c = getc(fp)) == EOF)
		    return result;
	    } while (c != '\n');

	    outc(c);
	}
	ungetc(c, fp);
    }
}

static void	/* minimize swapping if copying on floppies */
copyfile(register FILE *in, char *new)
{
    register char *bp;
    register long size = 16*1024;
    register FILE *out;
    register long i;
    int append = 0;

    if (!*new)
	return;
    if (access(new, 0) == 0) {
	if ((out = fopen(new, "a")) == NULL) {
	    printf("could not open %s for append\n", new);
	    return;
	}
	append = 1;
    } else if ((out = fopen(new, "w")) == NULL) {
	printf("could not open %s for output\n", new);
	return;
    }
    while ((bp = malloc(size)) == NULL) {
	if ((size = size/2) < 1024) {
	    printf("NO MEMORY TO MAKE COPY!\n");
	    fclose(out);
	    return;
	}
    }
    rewind(in);
    while ((i = fread(bp, sizeof(char), size, in)) > 0)
	fwrite(bp, sizeof(char), i, out);
    fclose(out); free(bp);
    printf("Article %sed to %s\n", append ? "append" : "sav", new);
}

static void	/* sends subject via outc() with format "%4s:%s\n" */
outsub(char *s, char *t)
{
    register int i = 4 - strlen(s);

    while (i > 0)
	outc(' '), --i;
    while (*s != '\0')
	outc(*s++);
    outc(':');
    while (*t != '\0')
	outc(*t++);
    outc('\n');
}

void
NextArtPage(char *article, char *group)
{
    SetupPage();
    ON;
    printf("Article %s of %s (%d unread)", article, group, unread_count());
    OFF;
    outc('\n');
}

void
scan_subjects(char *group)
{
    register char *ptr, *s;
    register int ch;

    SetupPage();
    while (ptr = get_next_art()) {
	if (LineCount >= LineMax) {
	    ON;
	    printf("(Hit any key to continue)");
	    OFF;
	    ch = rawch();
	    Clear_Line;
	    if (ch == 'q')
		    break;
	    SetupPage();
	}
	if ((s = subs(art2file(group, ptr))) != NULL)
	    outsub(ptr, s);
	else
	    outsub(ptr, "(((No Subject Line Found!!!)))");
    }
}

int
showart(char *group, char *article)
{
    register FILE *fp;
    register int done, ch;
    char newname[100];

    if ((fp = fopen(art2file(group, article), "r")) == NULL) {
	printf("Error opening article %s in group %s\n", article, group);
	ON;
	printf("(Hit any key to continue)");
	OFF;
	rawch();
	Clear_Line;		/* Erase the prompt */
	fflush(stdout);
	return 'n';
    }

    /* display initial page with headers */
    DisplayArt:

    NextArtPage(article, group);
    done = DisplayHdr(fp);
    while (LineCount < LineMax)
	putline("", fp);

    ch = 'd';
    while (done == 0) {
	ON;
	printf((EndOfFile) ? "===== End of article =====" : "--- More ---");
	OFF;
	putchar(' ');
	ch = rawch();

	Clear_Line;		/* Erase the prompt */
	fflush(stdout);

	switch (ch) {
	case ' ':
	    if (EndOfFile) {
		done = 1;
		ch = 'd';
		break;
	    }
	    /* fall through to... */
	case '\t':
	    fseek(fp, LastLine, 0);
	    NextArtPage(article, group);
	    while (LineCount < LineMax)
	    putline("", fp);
	    break;

	case '\n':
	case '\r':
	    putline("", fp);
	    break;

	case '\b':
	    rewind(fp);
	    goto DisplayArt;

	case 'f':
	case 'F':
	    followup(ch, fp, group);
	    break;

	case 'r':
	case 'R':
	    reply(ch, fp, group);
	    break;

	case 'p':
	case 'P':
	    followup(ch, NULL, group);
	    break;

	case '?':
	case 'h':
	case 'H':
	    ON;
	    printf("%s [%s]?", group, article);
	    OFF;
	    do_help(ArtHelp);
	    break;

	case 'w':
	case 'W':
	case 's':
	case 'S':
	    ON;
	    printf("Save article %s as ??", article);
	    OFF;
	    putchar(' ');
	    fflush(stdout);
	    gets(newname);
	    copyfile(fp, newname);
	    break;

	case '=':
	    scan_subjects(group);
	    break;

	case 'u':
	case 'U':
	    del_cur_art(0);
	    break;

	    /* move to another article */
	case '-':               /* previous article */
	case '$':               /* next news group */
	case '0': case '1': case '2': case '3': case '4':
	case '5': case '6': case '7': case '8': case '9':
	case '.': case '>': case ',': case '<':
	    hold_cur_art();
	    /* fall through to... */
	    /* these cases are handled by the group-level processing */
	case 'd': case 'D':     /* delete article */
	case 'n': case 'N':     /* to next article */
	case 'q': case 'Q':     /* quit news */
	case '^':               /* first article */
	    done = 1;
	    /* fall through to... */
	default:
	    break;
	}
    }

    fclose(fp);
    return (ch);
}

