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