1 /*
2      ATP QWK MAIL READER FOR READING AND REPLYING TO QWK MAIL PACKETS.
3      Copyright (C) 1992, 1993, 1997  Thomas McWilliams
4      Copyright (C) 1990  Rene Cougnenc
5 
6      This program is free software; you can redistribute it and/or modify
7      it under the terms of the GNU General Public License as published by
8      the Free Software Foundation; either version 2, or (at your option)
9      any later version.
10 
11      This program is distributed in the hope that it will be useful,
12      but WITHOUT ANY WARRANTY; without even the implied warranty of
13      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14      GNU General Public License for more details.
15 
16      You should have received a copy of the GNU General Public License
17      along with this program; if not, write to the Free Software
18      Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 */
20 
21 /*
22 display.c
23 */
24 
25 #include <stdio.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <sys/types.h>
29 #include <sys/stat.h>
30 
31 #include "reader.h"
32 #include "readlib.h"
33 #include "makemail.h"
34 #include "qlib.h"
35 #include "ansi.h"
36 
37 /*
38  * START OF NEW DISPLAY ROUTINES
39  */
40 
41 /* length of QWK author, recipient, and subject fields */
42 const size_t field_len = (size_t) QWK_FIELD_LEN ;
43 
44 /*
45  * nfprint, print a string not null-terminated in file fp.
46  */
47 static void
nfprint(FILE * fp,const char * str,size_t len)48 nfprint(FILE * fp, const char *str, size_t len)
49 {
50     assert(fp != NULL);
51     while (len > (size_t)0) {
52 	fputc(*str, fp);
53 	str++;
54 	len--;
55     }
56 }
57 #define nprint(s,n) nfprint(stdout,(s),(n))
58 static const char CONSPTR Space = "\t\t";
59 
60 /*
61  * prn_hdr_first_line, prints to a file, called by PrintHeader().
62  */
63 static void
prn_hdr_first_line(char * Header,FILE * fp)64 prn_hdr_first_line(char *Header, FILE * fp)
65 {
66     fprintf(fp, "\n%s", txt[19]);	/* "from:" */
67     nfprint(fp, Header + HAuthor, field_len);
68     fprintf(fp, Space);
69     fprintf(fp, "%s", txt[23]);	/* "number:" */
70     nfprint(fp, Header + HNumMsg, (size_t)7);
71 }
72 
73 /*
74  * prn_hdr_second_line, prints to a file, called by PrintHeader().
75  */
76 static void
prn_hdr_second_line(char * Header,FILE * fp)77 prn_hdr_second_line(char *Header, FILE * fp)
78 {
79     fprintf(fp, "\n%s", txt[20]);	/* "to :" */
80     nfprint(fp, Header + HForWhom, field_len);
81     fprintf(fp, Space);
82     fprintf(fp, "%s", txt[24]);	/* "ref#" */
83     nfprint(fp, Header + HRefMsg, (size_t)7);
84     fprintf(fp, "\n%s", txt[21]);	/* "subj." */
85     nfprint(fp, Header + HSubject, field_len);
86     fprintf(fp, Space);
87     fprintf(fp, "%s", txt[25]);	/* "Conf : " */
88     fprintf(fp, "%s", ConfNames[findCindex(readCnum(Header + HBinConfN))]);
89     fprintf(fp, "\n%s", txt[22]);	/* "date :" */
90     nfprint(fp, Header + HMsgDate, (size_t)8);
91     fprintf(fp, "%s", txt[26]);	/* "time:" */
92     nfprint(fp, Header + HMsgTime, (size_t)5);
93     /* stored 0 based, printed 1 based. */
94     fprintf(fp, "\t[%ld/%ld]", get_MsgNum() + 1L, get_TotMsg());
95 }
96 
97 /*
98  * PrintHeader - prints the Qmail message header in file fp.
99  */
100 void
PrintHeader(FILE * fp)101 PrintHeader(FILE * fp)
102 {
103     char Header[HDRSIZE];
104     int i;
105     byte *pntr;
106     (void)memcpy(Header, rbuf, header_SIZE);
107     pntr = (byte *) Header + HMsgDate;
108 
109     /* translate header info */     /*@-strictops */
110     if (get_charset() == ISOLAT1) { /*@=strictops */
111 	for (i = HMsgDate; i < HPassWrd; i++, pntr++)
112 	    *pntr = codelu[(unsigned) *pntr]; /*@-strictops */
113     } else if (get_charset() == CHR7BIT) {    /*@=strictops */
114 	for (i = HMsgDate; i < HPassWrd; i++, pntr++)
115 	    *pntr = code7bit[(unsigned) *pntr];
116     }
117     /* First line */
118     prn_hdr_first_line(Header, fp);
119 
120     /* Second line */
121     prn_hdr_second_line(Header, fp);
122     /*@-strictops */
123     if (Header[HStatus] == '+' || Header[HStatus] == '*') { /*@=strictops */
124 	fprintf(fp, "\t\t");
125 	fprintf(fp, "  %s\n", txt[27]);		/* "PRIVATE MESSAGE" */
126     }
127     fprintf(fp, "\n\n");
128 }
129 
130 #define SBUF_LEN 100
131 static char SubjBuf[SBUF_LEN];   /* Long subject for PCBoard */
132 
133 /*
134  * subj_is_long - returns TRUE if the long subject buffer is full.
135  */
136 atp_BOOL_T
subj_is_long(void)137 subj_is_long(void)
138 {       /*@-strictops */
139 	return SubjBuf[0] == NUL_CHAR ? FALSE : TRUE ; /*@=strictops */
140 }
141 
142 /*
143  * get_long_subj - returns a strdup() of the long subject buffer.
144  */
145 char *
get_long_subj(void)146 get_long_subj(void)
147 {
148     char *tp;
149     assert(LSUBJ_LEN < SBUF_LEN);
150     SubjBuf[LSUBJ_LEN] = NUL_CHAR;
151     tp = strdup(SubjBuf);
152     test_fatal_malloc(tp, __FILE__, __LINE__);
153     return tp ;
154 }
155 
156 /*
157  * put_long_subj, called from PutHdrScan().
158  */
159 static void
put_long_subj(size_t blen)160 put_long_subj(size_t blen)
161 {
162     const size_t fentry_size = sizeof(struct fentry);
163     struct fentry *dum1 = NULL, *dum2;
164 
165     /* find string in long subject buffer */
166     if (blen != (size_t)0 )
167 	findstr((byte *) SubjBuf, NULL, &dum1);
168 
169     /* print long Subject */
170     hprint(SubjBuf, (size_t)0, strlen(SubjBuf), &dum1, blen);
171 
172     /* free linked list */
173     while (dum1 != NULL) {
174 	dum2 = dum1->fnext;
175 	free_buffer(dum1, fentry_size);
176 	dum1 = dum2;
177     }
178 }
179 
180 static char *UserName ;
181 static size_t UserLen ;
182 /*
183  * PutHdrScan, fully display each message header, called from PutHeader().
184  */
185 static void
PutHdrScan(struct fentry ** sptr,size_t blen,const char * Header)186 PutHdrScan(struct fentry **sptr, size_t blen, const char *Header)
187 {
188     struct fentry **dum1, *dum2 = NULL;
189     /* don't dereference null pointer */
190     dum1 = (sptr == NULL) ? &dum2 : sptr;
191 
192     /* print first line */
193     {
194 	high();
195 	green();
196 	printf("\n%s", txt[19]);	/* " From :" */
197 
198 	/* print Author */
199 	yellow();
200 	*dum1 = hprint(rbuf, (size_t)HAuthor, field_len, sptr, blen);
201 
202 	/* print name of conference */
203 	{
204 	    const int conf_array_idx = findCindex(readCnum(Header + HBinConfN));
205 #if 0 && defined(ATPDBG)
206 /*xxx !! */ if (!(0 <= conf_array_idx && conf_array_idx < RCONF_IDX)) {
207 		printf("\nconf_array_idx: %d  LastConf: %d\n", conf_array_idx, LastConf);
208 		(void)sleep(4);
209 	    }
210 	    assert(0 <= conf_array_idx && conf_array_idx < RCONF_IDX);
211 #endif
212 	    magenta();
213 	    if (conf_array_idx < 0)
214 		printf(" : %s", txt[28]);	/* unknown conference */
215 	    else
216 		printf(" : %s", ConfNames[conf_array_idx]);
217 	}
218     }
219 
220     /* print second line */
221     {
222 	green();
223 	printf("\n%s", txt[20]);	/* "To:" */
224 	red();
225 	if (strnicmp(UserName, Header + HForWhom, UserLen) == SUCCESS)
226 	    blink();
227 	/* print ForWhom */
228 	*dum1 = hprint(rbuf, (size_t)HForWhom, field_len, sptr, blen);
229 	clear();
230 	green();
231 	high();
232 	printf(" : %s", txt[23]);	/* "Number" */
233 	red();
234 	nprint(Header + HNumMsg, (size_t)7);
235 	green();
236 	printf(" %s", txt[24]);	/* " Ref # " */
237 	yellow();
238 	nprint(Header + HRefMsg, (size_t)7);
239 	green();
240 	printf("\n%s", txt[22]);	/* "date:" */
241 	red();
242 	nprint(Header + HMsgDate, (size_t)8);
243 	green();
244 	printf("%s", txt[26]);	/* "time:" */
245 	red();
246 	nprint(Header + HMsgTime, (size_t)5);
247 	clear();
248 	printf("    : ");
249 	blue();
250 	/* MsgNum is stored 0 based, but printed 1 based. */
251 	printf("Index  : %ld/%ld", get_MsgNum() + 1L, get_TotMsg());
252 
253 	/* get message status and display it */
254 	{
255 	    const byte statu = Header[HStatus];
256 	    /*@-strictops */
257 	    if (statu == (byte) '+' || statu == (byte) '*' || statu >= HIGH_ASCII) {
258 		red();
259 		printf("\t");
260 		high();
261 		blink();
262 		if (statu >= HIGH_ASCII)	/*@=strictops */
263 		    printf(" %s%c", txt[105], BELL);	/* "KILLED MESSAGE" */
264 		else
265 		    printf(" %s%c", txt[27], BELL);	/* "PRIVATE MESSAGE" */
266 	    }
267 	}
268     }
269 
270     /* print subject line */
271     {
272 	clear();
273 	green();
274 	printf("\n%s", txt[21]);	/* "Subject" */
275 	cyan();			/*@-strictops */
276 	if (SubjBuf[0] == NUL_CHAR) {	/*@=strictops */
277 	    /* print normal subject */
278 	    *dum1 = hprint(rbuf, (size_t)HSubject, field_len, sptr, blen);
279 	} else {
280 	    /* print long subject */
281 	    put_long_subj(blen);
282 	}
283 	cyan();
284 	printf("\n\n");
285     }
286 }
287 
288 
289 /*
290  * PutHdrQuick, displays abbreviated message headers, one per line.
291  */
292 static void
PutHdrQuick(const char * Header)293 PutHdrQuick(const char *Header)
294 {
295     byte statu;
296     high();
297     yellow();
298     nprint(Header + HAuthor, field_len);
299     red();
300     if (strnicmp(UserName, Header + HForWhom, strlen(UserName)) == SUCCESS
301 	&& get_CurConf() != PCONF_IDX)
302 	blink();
303     nprint(Header + HForWhom, field_len);
304     clear();
305     red();
306     nprint(Header + HSubject, field_len);
307     blue();
308     printf("%ld", get_MsgNum() + 1L);	/* 0 based, print 1 based. */
309     statu = Header[HStatus]; /*@-strictops */
310     if (statu == (byte)'+' || statu == (byte)'*' || statu >= HIGH_ASCII) {
311 	red();
312 	high();
313 	blink();
314 	if (statu >= HIGH_ASCII) /*@=strictops */
315 	    printf("%c\n", 'K');	/* "KILLED MESSAGE" */
316 	else
317 	    printf("%c\n", 'P');	/* "PRIVATE MESSAGE" */
318     } else
319 	printf("\n");
320     clear();
321     cyan();
322 }
323 
324 
325 
326 /*
327  * PutHeader - prints the Qmail message header pointed by buf.
328  */
329 void
PutHeader(const read_command_t mode,struct fentry ** sptr,size_t blen)330 PutHeader(const read_command_t mode, struct fentry **sptr, size_t blen)
331 {
332     char Header[HDRSIZE];
333     int i;
334     byte *pntr;                            /*@-strictops */
335     assert(mode == SCAN || mode == QUICK); /*@=strictops */
336     UserName = get_cntrl_str(usrnm);
337     UserLen = strlen(UserName);
338     (void)memcpy(Header, rbuf, header_SIZE);
339     pntr = (byte *) Header + HMsgDate; /*@-strictops */
340     if (get_charset() == ISOLAT1) {
341 	for (i = HMsgDate; i < HPassWrd; i++, pntr++)	/* translate header info */
342 	    *pntr = codelu[(unsigned) *pntr];
343     } else if (get_charset() == CHR7BIT) { /*@=strictops */
344 	for (i = HMsgDate; i < HPassWrd; i++, pntr++)	/* translate header info */
345 	    *pntr = code7bit[(unsigned) *pntr];
346     }
347     /* check for lowercase letters in to/from fields */
348     test_for_caps(Header);
349 
350     /* normal scan */ /*@-strictops */
351     if (mode == SCAN) /*@=strictops */
352 	PutHdrScan(sptr, blen, Header);
353     /* quick scan */
354     else {
355 	SubjBuf[0] = NUL_CHAR;
356 	PutHdrQuick(Header);
357     }
358     free_string(UserName);
359     UserName = NULL;
360 }
361 
362 
363 /* static const byte norm[] = "@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_\140abcdefghijklmnopqrstuvwxyz"; */
364 static const byte rot[] = "@NOPQRSTUVWXYZABCDEFGHIJKLM[\\]^_\140nopqrstuvwxyzabcdefghijklm";
365 
366 /*
367  * rot_buf, used by Display() to decode rot13 messages.
368  */
369 static void
rot_buf(byte * rb_ptr,const byte * rb_badptr)370 rot_buf(byte * rb_ptr, const byte * rb_badptr)
371 {
372     const byte RB_MASK = (byte)0x3F ;
373     int B;
374     while (rb_ptr < rb_badptr) {                               /*@-strictops */
375 	if ((byte)'A' <= *rb_ptr && *rb_ptr <= (byte)'z') {
376 	    B = (*rb_ptr & RB_MASK);  /* mask out high bits */ /*@=strictops */
377 	    assert(0 < B && B < 59);
378 	    *rb_ptr = rot[B];
379 	}
380 	rb_ptr++;
381     }
382 }
383 
384 
385 static const int quote_depth = 6;
386 
387 /*
388  * IsQuoted, returns TRUE if the string contains ">" in the 6 left chars.
389  */
390 static atp_BOOL_T
IsQuoted(const int c,const byte * str)391 IsQuoted(const int c, const byte * str)
392 {
393     atp_BOOL_T ret_code = FALSE;
394     int i = 0; /*@-strictops */
395     for (; *str != (byte)NUL_CHAR && !ret_code && i < quote_depth; str++,i++) /*@=strictops */
396 	ret_code = ((const int)*str == c) ? TRUE : FALSE;
397     return ret_code;
398 }
399 
400 
401 static atp_BOOL_T xterm_flag;
402 static char *start_of_text ;
403 
404 /*
405  * get_sot - returns pointer to message start_of_text.
406  */
407 char *
get_sot(void)408 get_sot(void)
409 {
410 	return start_of_text ;
411 }
412 
413 /*
414  * loop_pre_hdr, setup for output display.
415  */
416 static void
loop_pre_hdr(read_command_t mode,struct fentry ** bptr,const size_t blen)417 loop_pre_hdr(read_command_t mode, struct fentry **bptr, const size_t blen)
418 {
419     if (stricmp(atp_termtype, "xterm") == SUCCESS)
420 	xterm_flag = TRUE;   /*@-strictops */
421     if (mode != SCAN)        /*@=strictops */
422 	CLSCRN();
423     /* support for PC Board BBS long subject lines */
424     SubjBuf[0] = NUL_CHAR;
425     start_of_text = rbuf + header_SIZE;
426     if (PCBLONG)
427 	Check4LongSubj();
428     PutHeader(SCAN, bptr, blen);
429 }
430 
431 /* used by Display for rot13 */
432 static atp_BOOL_T rot_13 = FALSE;
433 
434 /*
435  * rot_toggle - toggles rot13 flag when called by ReadShell().
436  */
437 void
rot_toggle(void)438 rot_toggle(void)
439 {
440     rot_13 = TRUE;
441     if (get_FirstDone())
442 	Display(NEXT, NULL, 0);
443     else
444 	ReadNext(NEXT);
445     rot_13 = FALSE;
446 }
447 
448 
449 /*
450  * loop_pre_buf, setup for output display.
451  */
452 static byte *
loop_pre_buf(byte CONSPTR badptr)453 loop_pre_buf(byte CONSPTR badptr)
454 {
455     /* point to last char of message */
456     byte *ptr = badptr - 1;
457     ptr--;
458 
459     /* strip trailing spaces and carriage returns */ /*@-strictops */
460     while (*ptr == (byte)NUL_CHAR || *ptr == (byte)SPC_CHAR || *ptr == (byte)'\n') { /*@=strictops */
461 	*ptr = NUL_CHAR;
462 	/* don't munge header */
463 	if ((--ptr) < (byte *) (rbuf + header_SIZE))
464 	    break;
465     }
466 
467     /* insure message terminates with newline */
468     if ((++ptr) < badptr)
469 	*ptr = '\n';
470 
471     /* point to beginning of message text, skipping header */
472     ptr = (byte *)start_of_text ;
473 
474     /* if rot13 flag is set, decode buffer */
475     if (rot_13)
476 	rot_buf(ptr, badptr);
477     cyan();
478     return ptr;
479 }
480 
481 /*
482    Display(), prints the current message stored in rbuf message buffer.
483    'bptr' points to a linked list of strings found by "find" routine.
484    'blen' is the length of the string found.
485    set bptr to NULL and blen to 0 when calling normally.
486    'mode' is:   0       display full message
487    SCAN    display message header only
488    KILL    display one page only
489    NUKE    display one page only
490    PRIVATE display one page only
491 
492    algorithm: display the message header, then read in characters of
493    message text, building output lines. Start at beginning of rbuf
494    working toward the end, reading one byte at at time. While doing
495    this, if 'bptr' is not NULL, check for a match with the current
496    position in rbuf. If 'bptr' and ptr match, highlight "found" text
497    by putting ansi reverse video screen codes in the line buffer. Then
498    after 'blen' characters have been processed turn off reverse video by
499    putting cancel screen codes in the line buffer. When the line buffer
500    is full, dump it to screen. Check to make sure that no more than one
501    screen full of lines is displayed at a time by promptng user as each
502    screen is filled.
503 
504  */
505 /* globals used by Display() routines */
506 #define OUTBUF_SIZE 800
507 static byte OutLineBuf[OUTBUF_SIZE];
508 
509 /*
510  * more_output, terminate output line with '\n' and prompt for more output.
511  */
512 static atp_BOOL_T
more_output(const byte * output_line)513 more_output(const byte *output_line)
514 {
515     int c_cnt = 0;
516     /*@-strictops */
517     while (*output_line != (byte) NUL_CHAR)
518 	output_line++, c_cnt++;
519     if (c_cnt != 0 && *--output_line != (byte) '\n')	/*@=strictops */
520 	printf("\n");
521     return more(YES);
522 }
523 
524 /*
525  * loop_out, the work horse for Display().
526  */
527 static atp_BOOL_T
loop_out(read_command_t mode,struct fentry * bptr,const size_t blen,byte CONSPTR badptr,byte * ptr,int * line_ct)528 loop_out(read_command_t mode, struct fentry *bptr, const size_t blen, byte CONSPTR badptr, byte * ptr, int *line_ct)
529 {
530     atp_BOOL_T QuotedColor = FALSE;
531     atp_BOOL_T vidon = FALSE;
532     const atp_CODE_T chrset = get_charset();
533     const int line_limit = OUTBUF_SIZE - 30;
534     const int spaces_per_tab = 8;
535     int count = 0, bufct = 0, spc_ct = 0;
536     byte CONSPTR linbuf = OutLineBuf;
537     byte *pt = linbuf;
538     int line = 5;		/* offset output by six for header */
539 
540     *pt = NUL_CHAR;			/* initialize line buffer */
541 
542     while (ptr < badptr) {
543 	assert((byte *) (rbuf + header_SIZE) <= ptr);
544 	/* skip over bells and null characters */        /*@-strictops */
545 	if (*ptr == (byte)NUL_CHAR || (*ptr == (byte)BEEP && silent)) {
546 	    ptr++;                                       /*@=strictops */
547 	} else {
548             const int rows = get_ScrnRows();
549 	    const int columns = get_ScrnCols()-1;
550 	    /* begin highlighting code */
551 	    if (bptr != NULL && blen > 0) {	/* re-synch pointers */
552 		while (bptr != NULL && (((char *) ptr) > (bptr->findpt + blen))) {
553 		    bptr = bptr->fnext;
554 		}
555 	    }
556 	    if (bptr != NULL && blen > 0) {
557 		int ht;
558 		if (ptr == (byte *) bptr->findpt) {	/* Beginning of the found string */
559 		    if (ansi) {	/* highlight found string */
560 			strcpy((char *) pt, A_REV_ON);
561 			bufct += 4, pt += 4;
562 		    } else {	/* termcap */
563 #ifdef HAVE_LIBTERMCAP
564 			ht = tputs_cpy((char *) pt, rev_on);
565 #else
566 			strcpy((char *) pt, "**");
567 			ht = 2;
568 #endif
569 			bufct += ht, pt += ht;
570 		    }
571 		    vidon = TRUE;
572 		}
573 		if (ptr == (byte *) (bptr->findpt + blen)) {	/* End of the found string */
574 		    if (ansi) {	/* turn off highlighting */
575 #ifdef VT220
576 			/* If NOT an xterm, use vt220 stuff */
577 			if (!xterm_flag) {
578 			    strcpy((char *) pt, VT220_REV_OFF);
579 			    bufct += 5;
580 			    pt += 5;
581 			} else
582 #endif
583 			{
584 			    strcpy((char *) pt, A_REV_OFF);
585 			    bufct += 3, pt += 3;
586 			}
587 		    } else {	/* non-ansi */
588 #ifdef HAVE_LIBTERMCAP
589 			ht = tputs_cpy((char *) pt, rev_off);
590 #else
591 			strcpy((char *) pt, "**");
592 			ht = 2;
593 #endif
594 			bufct += ht, pt += ht;
595 		    }
596 		    vidon = FALSE;
597 		}
598 	    }
599 /* end highlighting code */
600 
601 	    /* blow long line saftey valve to avoid buffer overrun */
602 	    if (bufct > line_limit)
603 		*ptr = '\n';
604 
605 	    /* begin fill output line buffer */               /*@-strictops */
606 	    if (*ptr == (byte)'\t') {	/* expand tabs */     /*@=strictops */
607 		/* calculate number of spaces to pad */
608 		spc_ct = spaces_per_tab - (count % spaces_per_tab);
609 		/* do housekeeping, track various counts */
610 		count += spc_ct, bufct += spc_ct;
611 		if (count >= columns) {
612 		    count -= columns;
613 		    line++;
614 		}
615 		/* fill output buffer with expanded tab */
616 		while (spc_ct != 0) {
617 		    *pt = (byte) SPC_CHAR;
618 		    pt++, spc_ct--;
619 		} /*@-strictops */
620 	    } else if (*ptr != (byte)NUL_CHAR ) {/* Non 0 chars */
621 		if (chrset != CHRDOS) {
622 		    if (graphics && vtspecial(*ptr)) {	/* use VT102 line graphics codes */
623 			*pt++ = '\016'; /*@=strictops */
624 			*pt++ = codevt[(unsigned) (*ptr)];
625 			*pt = '\017';
626 			bufct += 2;               /*@-strictops */
627 		    } else if (chrset == ISOLAT1) /*@=strictops */
628 			*pt = codelu[(unsigned) (*ptr)];	/* use iso lookup table */
629 		    else	/* use 7 bit character set */
630 			*pt = code7bit[(unsigned) (*ptr)];
631 		} else
632 		    *pt = *ptr;
633 		pt++;
634 		count++;	/* Security for line length ... */
635 		bufct++;
636 	    }			/* end fill output line buffer */
637 	    /* begin process output line buffer to screen when ready */ /*@-strictops */
638 	    if (*ptr == (byte)'\n' || count >= columns) {
639 		if (*ptr != (byte)'\n' && ((ptr + 1) < badptr) && *(ptr + 1) == (byte)'\n')
640 		    ptr++;	/* avoid to '\n' in a row */            /*@=strictops */
641 		count = bufct = 0;
642 		*pt = NUL_CHAR;
643 		line++;
644 		pt = linbuf;
645 		/* Process colors on quoted line */
646 		if (IsQuoted( (int)'>', pt) || IsQuoted( (int)'|', pt)) {
647 		    if (!color)
648 			high();                                    /*@-strictops */
649 		    while (*pt != (byte)'>' && *pt != (byte)'|') { /*@=strictops */
650 			(void)putchar(*pt);
651 			pt++ ;               /*@-strictops */
652 			assert((pt - linbuf) < quote_depth);
653 		    }                        /*@=strictops */
654 		    QuotedColor = TRUE;
655 		    if (color)
656 			red();	/* Quote char in red... */
657 		    (void)putchar(*pt);
658 		    pt++ ;                           /*@-strictops */
659 		    if (*pt != (byte)SPC_CHAR)       /*@=strictops */
660 			(void)putchar(SPC_CHAR);
661 		    if (color)
662 			green();	/* Quoted line in green.. */
663 		    else
664 			clear();
665 		    fputs((char *) pt, stdout);
666 		    cyan();
667 		} else {
668 		    if (QuotedColor) {
669 			cyan();
670 			QuotedColor = FALSE;
671 		    }
672 		    fputs((char *) linbuf, stdout);
673 		}
674 		pt = linbuf;
675 	    }			/* end process output line buffer */
676 	    /* prompt user for more output (yes/no) */        /*@-strictops */
677 	    if ((++ptr) < badptr && *ptr != (byte)NUL_CHAR && line > (rows - ROW_OFFSET)) {
678 		line = 0;
679 		if (mode == CROSS || mode == KILL || mode == NUKE || mode == PRIVATE || !more_output(linbuf))
680 		    break;                                    /*@=strictops */
681 		cyan();		/* restore default color */
682 	    }			/* end prompt user  */
683 	}
684     }				/* end while ptr < badptr */
685 
686     /* flush output line buffer if needed */
687     if (bufct != 0) {
688 	*pt = NUL_CHAR;
689 	printf("%s",(char *) linbuf);
690     }
691     assert(ptr <= badptr);
692     *line_ct = line;
693     return vidon;
694 }
695 
696 
697 /*
698  * Display - page message buffer to the screen.
699  */
700 void
Display(const read_command_t mode,struct fentry * bptr,const size_t blen)701 Display(const read_command_t mode, struct fentry *bptr, const size_t blen)
702 {
703     if (get_isempty()) {
704 	EmptyMsg();
705     } else {
706 	/* first output header */
707 	loop_pre_hdr(mode, &bptr, blen);
708         /*@-strictops */
709 	if (mode != SCAN) {     /*@=strictops */
710 	    int line = -8382;	/* arbitrary init constant */
711 	    atp_BOOL_T vidon;
712 
713 	    /* badptr points one byte past end of message text */
714 	    byte CONSPTR badptr = (byte *) (rbuf + (size_t) get_MsgSize() + header_SIZE);
715 	    byte CONSPTR ptr = loop_pre_buf(badptr);
716 
717 	    /* loop till end of message processing and displaying output lines */
718 	    vidon = loop_out(mode, bptr, blen, badptr, ptr, &line);
719 	    assert( -1 < line );
720 	    if (line != 0 && line < (get_ScrnRows() - 2))
721 		printf("\n");
722 #ifdef VT220
723 	    if (vidon && !xterm_flag) {
724 		printf(VT220_REV_OFF);
725 	    } else
726 #endif
727 #ifdef HAVE_LIBTERMCAP
728 	    if (vidon)
729 		printf("%s", ansi ? A_REV_OFF : tputs_ptr(rev_off));
730 #else
731 	    if (vidon && ansi)
732 		printf("%s", A_REV_OFF);
733 #endif
734 	    fflush(stdout);	/* fail safe cancel of highlighting */
735 	}
736     }
737 }
738 
739 /*
740  * BEGIN LONG SUBJECT ROUTINES
741  */
742 
743 /*
744  * adjust_sot, adjust start of text pointer.
745  */
746 static char *
adjust_sot(char * SOSubj,char * badptr)747 adjust_sot(char *SOSubj, char *badptr)
748 {
749     char *p = SOSubj;
750     char *SOText = p + LSUBJ_BODY_LEN + LSUBJ_TAIL_LEN;
751     char *Saved = NULL;
752 
753     /*@-strictops */
754     while (p < SOText && p < badptr) {
755 	if (*(SOText - 2) == 'N' && strnicmp(SOText - 8, LSUBJ_TAIL, 8) == SUCCESS)
756 	    break;
757 	if (*p == '\n') {
758 	    SOText++;
759 	    if (!Saved)
760 		Saved = p + 1;
761 	}
762 	p++;
763     }
764     if (strnicmp(p - 7, LSUBJ_TAIL, 8) != SUCCESS && Saved != NULL) {
765 	if (Saved > (SOSubj + field_len))
766 	    SOText = Saved;
767 	else
768 	    SOText = rbuf + header_SIZE;
769     }
770     if (*SOText == '\n')
771 	SOText++;		/*@=strictops */
772 
773     return SOText;
774 }
775 
776 
777 /*
778  * find_long_subj, parse first message block for embedded long subject.
779  */
780 static char *
find_long_subj(char * txt_start,char * badptr)781 find_long_subj(char *txt_start, char *badptr)
782 {
783     char *subj_start = txt_start;
784     size_t ct = 0;
785     badptr -= LSUBJ_BUF_LEN;	/*@-strictops */
786     txt_start--;
787     for (; ct < block_SIZE && txt_start < badptr; txt_start++, ct++) {
788 	if (ct == (size_t) 0 || *txt_start == '@') {
789 	    if (++txt_start < badptr && strnicmp(txt_start, "SUBJECT:", LSUBJ_NAME_LEN) == SUCCESS) {
790 		subj_start = txt_start + LSUBJ_NAME_LEN;
791 		if (*subj_start == SPC_CHAR)	/*@=strictops */
792 		    subj_start++;
793 		break;
794 	    } else
795 		txt_start--;	/* adjust backward after look-ahead */
796 	}
797     }
798     return subj_start;
799 }
800 
801 
802 /*
803  * process_subjbuf, strip newlines, padding and junk from subject buffer.
804  */
805 static void
process_subjbuf(void)806 process_subjbuf(void)
807 {
808     char *subptr = SubjBuf;
809     char *endmarker = SubjBuf + LSUBJ_BODY_LEN;
810     *endmarker = NUL_CHAR;
811     while (subptr < endmarker) {
812         /*@-strictops */
813 	if (*subptr == '\n' || *subptr == '\r') {
814 #if 0
815 	    assert(*subptr == '\r' || (subptr - SubjBuf) > field_len); /*@=strictops */
816 #endif
817 	    ShiftLeft(subptr, 1);
818 	    endmarker--;
819 	} else {
820 	    subptr++;
821 	}
822     } /*@-strictops */
823     for (; (char *)SubjBuf < subptr;) {
824 	subptr--;
825 	if (*subptr != SPC_CHAR && *subptr != TAB_CHAR)  /*@=strictops */
826 	    break;
827 	*subptr = NUL_CHAR;
828     }
829 }
830 
831 /*
832  * Check4LongSubj - checks message for PCBoard-style long subject line.
833  */
834 void
Check4LongSubj(void)835 Check4LongSubj(void)
836 {
837     char *SOSubj;
838     char CONSPTR badptr = rbuf + (size_t) get_MsgSize() + header_SIZE;
839 
840 /* reset variables to normal state, i.e. no long subject */
841     SubjBuf[0] = NUL_CHAR;
842     start_of_text = rbuf + header_SIZE;
843 
844 /* check first line of message for @SUBJECT (ignore white space) */
845     SOSubj = find_long_subj(start_of_text, badptr);
846 
847 /* find end of long subject, i.e. the start of message text */
848     if (SOSubj != start_of_text && SOSubj < (start_of_text = adjust_sot(SOSubj, badptr))) {
849 	assert(SOSubj < start_of_text && start_of_text < badptr);
850 	assert(start_of_text < (SOSubj + block_SIZE + (LSUBJ_BUF_LEN - 2)));
851 
852 	/* zero out subject buffer and copy long subject into it */
853 	(void) memset(SubjBuf, 0, (size_t) SBUF_LEN);	/*@-strictops */
854 	(void) memcpy(SubjBuf, SOSubj, (size_t) (start_of_text - SOSubj)); /*@=strictops */
855 	process_subjbuf();
856     }
857 }
858 
859