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 readlib.c
23 */
24 
25 #include <fcntl.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 
32 #include "reader.h"
33 #include "readlib.h"
34 #include "ansi.h"
35 #include "makemail.h"
36 #include "qlib.h"
37 
38 static atp_BOOL_T FirstDone  = FALSE;  /* Set if 1st message in conf is read       */
39 
40 /*
41  * set_FirstDone - used to set the FirstDone flag.
42  */
43 void
set_FirstDone(const atp_BOOL_T mode,const fdone_access_t perm)44 set_FirstDone(const atp_BOOL_T mode, const fdone_access_t perm)
45 {
46 /*@-strictops */
47     if (perm == fd_ReadNext || perm == fd_PurgeAdjust )
48 	FirstDone = mode;
49 #ifdef ATPDBG
50     else
51         assert(perm == fd_ReadNext || perm == fd_PurgeAdjust );
52 #endif
53 /*@=strictops */
54 }
55 
56 
57 /*
58  * get_FirstDone - return the FirstDone flag.
59  */
60 atp_BOOL_T
get_FirstDone(void)61 get_FirstDone(void)
62 {
63     return FirstDone;
64 }
65 
66 /* internal structure of atp index files    */
67 static struct MyIndex Index;
68 
69 /*
70  * get_MsgLastRead - returns Index.LastRead.
71  */
72 long
get_MsgLastRead(void)73 get_MsgLastRead(void)
74 {
75     return Index.LastRead;
76 }
77 
78 /*
79  * get_MsgNum - returns Index.MsgNum.
80  */
81 long
get_MsgNum(void)82 get_MsgNum(void)
83 {
84     return Index.MsgNum;
85 }
86 
87 /*
88  * get_MsgOffset - returns Index.Offset.
89  */
90 long
get_MsgOffset(void)91 get_MsgOffset(void)
92 {
93     return Index.Offset;
94 }
95 
96 /*
97  * get_MsgSize - returns Index.Size.
98  */
99 unsigned long
get_MsgSize(void)100 get_MsgSize(void)
101 {
102     return Index.Size;
103 }
104 
105 /*
106  * get_ptrIndex - returns a pointer to the Index structure.
107  */
108 struct MyIndex *
get_ptrIndex(const indxptr_access_t perm)109 get_ptrIndex(const indxptr_access_t perm)
110 {
111     struct MyIndex *tp = &Index;
112 /*@-strictops */
113     if (perm != ip_ReadSeek) {
114 	tp = NULL;
115 	assert(perm == ip_ReadSeek);
116     }
117 /*@=strictops */
118     return tp;
119 }
120 
121 /*
122  * zero_Index - sets Index.LastRead and Index.MsgNum to 0L.
123  */
124 void
zero_Index(const indxptr_access_t perm)125 zero_Index(const indxptr_access_t perm)
126 {
127 /*@-strictops */
128     if (perm == ip_PurgeAdjust)
129 	Index.LastRead = Index.MsgNum = 0L;
130 #ifdef ATPDBG
131     else
132 	assert(perm == ip_PurgeAdjust);
133 #endif
134 /*@=strictops */
135 }
136 
137 
138 /*
139  * SkNumRead, helper function for SeekNum() to load buffer after seek.
140  */
141 static atp_BOOL_T
SkNumRead(void)142 SkNumRead(void)
143 {
144     atp_BOOL_T ret_code = FALSE;
145     const size_t nrecs = (size_t) ((Index.Size / block_SIZE) + 1);	/* same as (Index.Size+128)/128) */
146     if (nrecs <= (get_RbufSize() / block_SIZE) || reup(nrecs * block_SIZE)) {
147 	if ((fread(rbuf, block_SIZE, nrecs, fmsg)) != nrecs) {
148 	    printf("%s ? \n", txt[18]);		/* "read error" */
149 	} else {
150 	    rbuf[(size_t) Index.Size + block_SIZE] = NUL_CHAR;		/* terminate buffer like a string */
151 	    FirstDone = TRUE;	/* signal that there is a full buffer */
152 	    ret_code = TRUE;	/* good seek */
153 	}
154     }
155     return ret_code;
156 }
157 
158 /* Number of messages in conference */
159 static long int TotMsg = 0L;
160 
161 /*
162  * get_TotMsg - returns TotMsg the number of messages in a conference.
163  */
164 long
get_TotMsg(void)165 get_TotMsg(void)
166 {
167     return TotMsg;
168 }
169 
170 /*
171  * set_TotMsg - sets TotMsg the number of messages in conference.
172  */
173 void
set_TotMsg(const long val,const totmsg_access_t perm)174 set_TotMsg(const long val, const totmsg_access_t perm)
175 {
176 /*@-strictops */
177     if (perm == tm_PurgeAdjust || perm == tm_AddReply)
178 	TotMsg = val;
179 #ifdef ATPDBG
180     else
181 	assert(perm == tm_PurgeAdjust || perm == tm_AddReply);
182 #endif
183 /*@=strictops */
184 }
185 
186 /*
187  * SeekNum - seek to specific message number.
188  */
189 atp_BOOL_T
SeekNum(const char * str)190 SeekNum(const char *str)
191 {
192     atp_BOOL_T ret_code = FALSE;
193     const long int nb = atol(str) - 1;	/* message number to seek to */
194     if (get_isempty()) {
195 	EmptyMsg();
196     } else if (nb < 0 || nb >= TotMsg) {
197 	red();
198 	high();
199 	printf("%s\n", txt[81]);	/* "Number out of range ! " */
200 	printf("%s ", txt[82]);	/* "Valid msgs are" */
201 	magenta();
202 	printf("1 ");
203 	red();
204 	printf("%s ", txt[83]);	/* "to" */
205 	magenta();
206 	printf("%ld\n", TotMsg);
207     } else if (fseek(fidx, (nb * (long) IDXSIZE), SEEK_SET) != SUCCESS) {
208 	printf("%s !\n", txt[16]);	/* "seek error" */
209     } else if (fread(&Index, index_SIZE, one_record, fidx) != one_record) {
210 	printf("%s\n", txt[17]);	/* "End of messages " */
211     } else if (rewind(fmsg), fseek(fmsg, Index.Offset, SEEK_SET) != SUCCESS) {
212 	printf("%s !\n", txt[16]);	/* "seek error" */
213     } else {
214 	ret_code = SkNumRead();	/* load buffer with new message */
215     }
216     return ret_code;
217 }
218 
219 
220 /*
221  * GoToNum - go to the msg number represented by the string and print it.
222  *  ( Internal count, not BBS numbers ! )
223  */
224 void
GoToNum(const char * str)225 GoToNum(const char *str)
226 {
227     if (SeekNum(str))
228 	Display(NEXT, NULL, 0);
229 }
230 /* Flag if these files are open */
231 static atp_BOOL_T FilesOpen  = FALSE;
232 
233 /*
234  * get_FilesOpen - returns TRUE if any files are open.
235  */
236 atp_BOOL_T
get_FilesOpen(void)237 get_FilesOpen(void)
238 {
239     /* get_isempty => !FilesOpen */
240     assert( !get_isempty() || !FilesOpen);
241     return FilesOpen;
242 }
243 
244 /*
245  * fclose_fmsg_fidx - error checking wrapper to close fmsg and fidx streams.
246  */
247 void
fclose_fmsg_fidx(void)248 fclose_fmsg_fidx(void)
249 {
250     if (fidx == NULL || fmsg == NULL) {
251 	fprintf(stderr, "%s:%d  fclose() on NULL pointer\n", __FILE__, __LINE__);
252 	abort();
253     }
254     fclose(fidx);
255     fclose(fmsg);
256     FilesOpen = FALSE;
257     /*@i1*/ fidx = fmsg = NULL;
258 }
259 
260 /*
261  * StripRSpace, strip white space on right side of string.
262  */
263 ATP_INLINE void
StripRSpace(const char * limit,char * ptr)264 StripRSpace(const char *limit, char *ptr)
265 {
266     /*@-strictops */
267     while ( limit <= ptr && (*ptr == SPC_CHAR || *ptr == TAB_CHAR)) { /*@=strictops */
268 	*ptr = NUL_CHAR;
269 	ptr--;
270     }
271 }
272 
273 #define TBUF_SIZE 300
274 /*
275  * fget - get a string like fgets but without any space or lf on the right.
276  */
277 atp_BOOL_T
fget(char * s,const int n,FILE * fp)278 fget(char *s, const int n, FILE *fp)
279 {
280     atp_BOOL_T ret_code = FALSE;
281     int ch1 = EOF, ct = 0;
282     char *p2, tbuf[TBUF_SIZE + 1];
283 
284     if (s != NULL && n <= TBUF_SIZE) {
285 	p2 = tbuf;
286 	while (ct < (n - 1) && ct < TBUF_SIZE) {	/* get 'n' chars */
287 	    ch1 = fgetc(fp);
288 	    if (ch1 == EOF || ch1 == (int)'\n')
289 		ct = TBUF_SIZE;
290 	    else if (ch1 != (int)'\r') {
291 		*p2 = (char) ch1;
292 		ct++;
293 		p2++;
294 	    }
295 	}
296 	*p2 = NUL_CHAR;
297 	StripRSpace(tbuf, --p2);
298 	while (ct < TBUF_SIZE) {	/* keep getting chars till end of line is found */
299 	    ch1 = fgetc(fp);
300 	    if (ch1 == EOF)
301 		break;
302 	    if (ch1 == (int)'\n')
303 		break;
304 	    ct++;
305 	}
306 	/*@-usedef */
307 	strcpy(s, tbuf);
308 	/*@=usedef */
309 	if (ch1 != EOF)
310 	    ret_code = TRUE;
311     }
312     return ret_code;
313 }
314 
315 
316 /*
317  * GetConfFin, finish up for GetConf().
318  */
319 ATP_INLINE atp_ERROR_T
GetConfFin(FILE * t_fidx,char * tmp,const int num)320 GetConfFin(FILE *t_fidx, char *tmp, const int num)
321 {
322     atp_ERROR_T ret_code = ATP_ERROR;
323     FILE *t_fmsg;
324     cnf_path(tmp, ConfNumbers[num]);
325     if ((t_fmsg = fopen(tmp, "r+b")) == NULL) {
326 	fclose(t_fidx);
327 	/* "Error Reading file" */
328 	printf("%s %s ! \n", txt[58], tmp);
329     } else if (fread(&Index, index_SIZE, one_record, t_fidx) != one_record) {	/* Go  to Last message read */
330 	printf("Can't read index in GetConf()\n");
331 	fclose(t_fmsg);
332 	fclose(t_fidx);
333     } else {
334 	if (FilesOpen) {
335 	    fclose_fmsg_fidx();
336 	}
337 	fmsg = t_fmsg;
338 	fidx = t_fidx;
339 	FilesOpen = TRUE;
340 	if (Index.LastRead == VIRGINDX) {
341 	    /* virgin index, seek first message */
342 	    rewind(fidx);
343 	} else
344 	    fseek(fidx, (long) Index.LastRead * (long) IDXSIZE, SEEK_SET);
345 	set_CurConf(num, scnf_GetConf);
346 	FirstDone = FALSE;
347 	ret_code = ATP_OK;
348     }
349     return ret_code ;
350 }
351 
352 
353 /*
354  * GetConfStat, stat conference before joining.
355  */
356 ATP_INLINE atp_ERROR_T
GetConfStat(FILE * t_fidx,char * tmp,const int num)357 GetConfStat(FILE *t_fidx, char *tmp, const int num)
358 {
359     atp_ERROR_T ret_code = ATP_ERROR;
360     struct stat stats;
361 
362     if (stat(tmp, &stats) != SUCCESS) {
363 	red();
364 	perror("GetConfStat()");
365 	fclose(t_fidx);
366     } else if ((TotMsg = (long int) (stats.st_size / IDXSIZE)) < 1L) {
367 	red();
368 	/* "No mail on conf" */
369 	printf("%s %s\n", txt[44], tmp);
370 	fclose(t_fidx);
371     } else
372 	ret_code = GetConfFin(t_fidx, tmp, num);
373     return ret_code;
374 }
375 
376 
377 /*
378  * GetConf - join conference and open global files etc.
379  */
380 atp_ERROR_T
GetConf(const int num)381 GetConf(const int num)
382 {
383     atp_ERROR_T ret_code = ATP_ERROR;
384 
385     if (!get_isempty()) {
386 	char tmp[MAXPATHS];
387 	FILE *t_fidx;
388 	assert(ConfNumbers != NULL);
389 	idx_path(tmp, ConfNumbers[num]);
390 	if (access(tmp, F_OK) == FAILURE) {	/* File does not exists, no mail. */
391 	    red();
392 	    /* "No mail on conf" */
393 	    printf("%s %s\n", txt[44], tmp);
394 	} else if ((t_fidx = fopen(tmp, "r+b")) == NULL) {	/* Ok file exists, read it . */
395 	    /* "unable to read file" */
396 	    printf("%s %s\n", txt[49], tmp);
397 	} else {
398 	    ret_code = GetConfStat(t_fidx, tmp, num);
399 	}
400     }
401     return ret_code;
402 }
403 
404 
405 
406 static atp_BOOL_T ReplyExist = FALSE;
407 
408 /*
409  * set_ReplyExist - sets boolean ReplyExist flag.
410  */
411 void
set_ReplyExist(const atp_BOOL_T mode,const rexist_access_t perm)412 set_ReplyExist(const atp_BOOL_T mode, const rexist_access_t perm)
413 {
414 /*@-strictops */
415     if (rexist_AddReply <= perm && perm <= rexist_Chk4RepCnf)
416 	ReplyExist = mode;
417 #ifdef ATPDBG
418     else
419 	assert(rexist_AddReply <= perm && perm <= rexist_Chk4RepCnf);
420 #endif
421 /*@=strictops */
422 }
423 
424 
425 #define REP_FILE_EXISTS Chk4RepPkt()
426 /*
427  * PackReply, creates reply packets for uploading to BBS.
428  */
429 static void
PackReply(void)430 PackReply(void)
431 {
432     char MsgFile[MAXPATHS];
433     sprintf(MsgFile, "%s%c%s.msg", WorkPath, SEP, CurBoard);
434 
435     if ( REP_FILE_EXISTS || !Cnf2Msg(MsgFile)) {
436 	printf("No replies packed.\n");
437     } else {
438         /* make bbs.msg from 9001.cnf */
439 	char RepFile[MAXPATHS];
440 	char Command[MAXPATHS];
441 	char CONSPTR Archiver = get_atprc_str(archivr);
442 	assert( Archiver != NULL );
443 	yellow();
444 	high();
445 	sprintf(RepFile, "%s%s.rep", ReplyPath, CurBoard);
446 	printf("%s %s\n", txt[65], RepFile);	/* "packing replies in" */
447 	green();
448 	fflush(stdout);
449 	sprintf(RepFile, "%s%s.rep", ReplyPath, CurBoard);
450 	sprintf(Command, "%s %s", RepFile, MsgFile);
451 	fork_execvp(Archiver, Command);
452 	free_string(Archiver);
453 	ReplyExist = FALSE;
454 	do_unlink(MsgFile);
455     }
456 }
457 
458 /*
459  * query_rep_exists - prompts user if he wishes to delete reply conference.
460  */
461 void
query_rep_exists(void)462 query_rep_exists(void)
463 {
464     if (ReplyExist) {
465 	char prompt[80];
466 	red();
467 	high();
468 	printf("%s\n%s %s.\n", txt[2], txt[3], CurBoard);	/* Warning, you have  */
469 	sprintf(prompt, "%s", txt[4]);	/* Replies,pack them ? */
470 	if (YesNo(YES, prompt))
471 	    PackReply();
472     }
473 }
474 
475 
476 /*
477  * puts_xlate, translate line from DOS to LINUX character set and output it.
478  */
479 static void
puts_xlate(char * buf,char * tmp)480 puts_xlate(char *buf, char *tmp)
481 {
482     /* translate DOS character set to LINUX character set */
483     const atp_CODE_T chrset = get_charset();
484     byte *ptr, *bptr;
485     ptr = (byte *) buf;
486     bptr = (byte *) tmp;
487     *bptr = NUL_CHAR;
488 /*@-strictops */
489     while (*ptr != (byte)NUL_CHAR) {
490 	if (graphics && vtspecial(*ptr)) {	/* use VT102 line graphics codes */
491 	    *bptr = '\016';
492 	    bptr++;
493 	    *bptr = codevt[(unsigned) *ptr];
494 	    bptr++;
495 	    *bptr = '\017';
496 	} else
497 	    *bptr = chrset == ISOLAT1 ? codelu[(unsigned) (*ptr)]
498 		: code7bit[(unsigned) (*ptr)];
499 /*@=strictops */
500 	ptr++;
501 	bptr++;
502 	*bptr = NUL_CHAR;
503     }
504     puts(tmp);
505 }
506 
507 
508 /*
509  * view_lines, output lines for view().
510  */
511 static void
view_lines(char * buf,char * tmp,FILE * fp)512 view_lines(char *buf, char *tmp, FILE * fp)
513 {
514     const atp_CODE_T chrset = get_charset();
515     int line = 0;
516     clear();
517     cyan();
518     printf("\n\n");
519     while (fget(buf, 250, fp)) {
520 	line++;
521 	if (line > (get_ScrnRows() - 2)) {
522 	    line = 0;
523 	    if (!more(YES))
524 		break;
525 	    cyan();
526 	}
527 	if (/*@i1*/ chrset == CHRDOS)
528 	    puts(buf);
529 	else
530 	    puts_xlate(buf, tmp);
531     }
532 }
533 
534 /*
535  * view - page a file to screen while prompting user with "more".
536  *
537  *  note that the tmp buffer must be large enough to
538  *  handle all ansi escape sequences and VT102 line
539  *  graphics sequences (or you will bomb on a seg fault).
540  */
541 void
view(const char * Path,const char * File)542 view(const char *Path, const char *File)
543 {
544     const size_t VIEWBUF = 1000;
545     char *tmp;
546     if ((tmp = (char *) malloc(VIEWBUF)) == NULL) {
547 	/* malloc failure */
548 	fprintf(stderr, "%s %s:%d view()\n\n", txt[1], __FILE__, __LINE__);
549     } else {
550 	const size_t VIEWLINE = 600;
551 	char *buf;
552 	FILE *fp;
553 	sprintf(tmp, "%s%c%s", Path, SEP, File);
554 	if ((fp = fopen(tmp, "rb")) == NULL) {
555 	    red();
556 	    printf("%s %s\n", txt[67], tmp);	/* "No file" */
557 	} else if ((buf = (char *) malloc(VIEWLINE)) == NULL) {
558 	    /* malloc failure */
559 	    fprintf(stderr, "%s %s:%d view()\n\n", txt[1], __FILE__, __LINE__);
560 	    fclose(fp);
561 	} else {
562 	    view_lines(buf, tmp, fp);
563 	    fclose(fp);
564 	    free_buffer(buf, VIEWLINE);
565 	}
566 	free_buffer(tmp, VIEWBUF);
567     }
568 }
569 
570 
571 
572 /*
573  * EmptyMsg - informs the user that there is no BBS currently loaded.
574  */
575 void
EmptyMsg(void)576 EmptyMsg(void)
577 {
578     red();
579     high();
580     printf("%s ", txt[68]);	/* "Hey !" */
581     clear();
582     magenta();
583     printf("%s\n", txt[69]);	/* There is no bbs loaded !" */
584     printf("%s...\n", txt[70]);	/* use 'read' 'load' or 'help' command" */
585     clear();
586 }
587 
588 /*
589  * ShiftLeft - shifts a string to the left.
590  *      shifts a string left at p1, if n==2 then string is shifted
591  *      two spaces left
592  */
593 void
ShiftLeft(char * p1,const int n)594 ShiftLeft(char *p1, const int n)
595 {
596     char *p2;
597 
598     p2 = p1;
599     p2++;
600     if (n == 2)
601 	p2++;
602     while (/*@i1*/ *p2 != NUL_CHAR) {
603 	*p1 = *p2;
604 	p2++;
605 	p1++;
606     }
607     *p1 = NUL_CHAR;
608     return;
609 }
610 
611 
612 /*
613  * StripDel - scans a line removing backspace and delete characters.
614  */
615 void
StripDel(char * tp)616 StripDel(char *tp)
617 {
618     atp_BOOL_T done = FALSE;
619     char *p;
620     p = tp;
621     while (/*@i1*/ *p != NUL_CHAR && !done) {
622 	if (p == tp) {		/* first character on line */
623 	    switch (*p) {
624 	    case BS_CHAR:	/* backspace */
625 	    case DEL_CHAR:
626 		ShiftLeft(p, 1);	/* del       */
627 		break;
628 	    case CR_CHAR:	/* carriage return */
629 	        /*@-casebreak*/
630 		*p = NUL_CHAR;  /* no break needed here ... */
631 	    case NUL_CHAR:      /* yes, fall through is intentional! */
632 		done = TRUE;
633 	        /*@=casebreak*/
634 		break;
635 	    default:
636 		p++;
637 	    }
638 	} else {
639 	    switch (*p) {
640 	    case BS_CHAR:
641 	    case DEL_CHAR:
642 		p--;
643 		ShiftLeft(p, 2);
644 		break;
645 	    case CR_CHAR:	/* carriage return */
646 	        /*@-casebreak */
647 		*p = NUL_CHAR;
648 	    case NUL_CHAR:    /* no break needed, fall through intentional */
649 		done = TRUE;
650 	        /*@=casebreak */
651 		break;
652 	    default:
653 		p++;
654 	    }
655 	}
656     }
657     return;
658 }
659 
660 
661 /*
662  * tup8bit, table for international capitalization.
663  */
664 static const byte tup8bit[] =
665 {
666 
667     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
668     0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
669     0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
670     0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,
671     0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,
672     0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,
673     0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
674     0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,
675     0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
676     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
677     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
678     0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,
679     0x60, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
680     0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
681     0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
682     0x58, 0x59, 0x5a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,
683 
684     0x80, 0x9a, 0x90, 0x83, 0x8e, 0x85, 0x8f, 0x80,	/* 87 */
685     0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,	/* 8f */
686     0x90, 0x92, 0x92, 0x93, 0x99, 0x95, 0x96, 0x97,	/* 97 */
687     0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,	/* 9f */
688     0xa0, 0xa1, 0xa2, 0xa3, 0xa5, 0xa5, 0xa6, 0xa7,	/* a7 */
689     0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,	/* af */
690     0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,	/* b7 */
691     0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,	/* bf */
692     0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,	/* c7 */
693     0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,	/* cf */
694     0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,	/* d7 */
695     0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,	/* df */
696     0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,	/* e7 */
697     0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,	/* ef */
698     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,	/* f7 */
699     0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff};
700 
701 static char find_buf[FIND_BUF_SIZE + 1];
702 static struct fentry *entry = NULL;         /* remembers last link */
703 
704 /*
705  * set_list_tail, point to end of fentry linked list.
706  */
707 ATP_INLINE void
set_list_tail(struct fentry ** sptr)708 set_list_tail(struct fentry **sptr)
709 {
710     if (*sptr == NULL)
711         entry = NULL;
712 }
713 
714 
715 /*
716  * set_target, point to search string.
717  */
718 ATP_INLINE byte *
set_target(byte * needle)719 set_target(byte *needle)
720 {
721     if (needle == NULL)
722         needle = (byte *) find_buf;
723     return needle;
724 }
725 
726 
727 /*
728  * new_fentry, allocate an new item on linked list of found text.
729  */
730 ATP_INLINE atp_BOOL_T
new_fentry(char * haystack,struct fentry ** sptr)731 new_fentry(char *haystack, struct fentry **sptr)
732 {
733     atp_BOOL_T malloc_failure = FALSE;
734     struct fentry *tentry;      /* temporary pointer   */
735 
736     /* return ptr to position in string haystack where needle found */
737     if ((tentry = (struct fentry *) malloc(sizeof(struct fentry))) != NULL) {
738         tentry->fnext = NULL;
739         tentry->findpt = (char *) haystack;
740         if (*sptr != NULL) {
741             assert(entry != NULL);
742             entry->fnext = tentry;
743         } else
744             *sptr = tentry;     /* first in linked list */
745         entry = tentry;
746     } else {
747         printf("findstr(), %s\n", txt[1]);
748         (void) sleep(3);
749         malloc_failure = TRUE;
750     }
751     return malloc_failure;
752 }
753 
754 /*
755  * findstr - search conference for text string.
756  * function to look for search string needle in text string haystack.
757  */
758 struct fentry *
findstr(byte * haystack,byte * needle,struct fentry ** sptr)759 findstr(byte * haystack, byte * needle, struct fentry **sptr)
760 {
761     byte *hay = haystack, *ndl;
762     atp_BOOL_T new_has_failed = FALSE;
763 
764     assert(sptr != NULL && haystack != NULL);
765     set_list_tail(sptr);
766     ndl = needle = set_target(needle);
767 
768     /*
769      *  while the end of string haystack has not been reached,
770      *  look for search string needle, strip off lowercase if needed,
771      *  while needle matches within haystack, DTS pre-upshifted string
772      *  needle in calling routine
773      *//*@-strictops */
774     while (*haystack != (byte) NUL_CHAR) {
775         while (*hay != (byte) NUL_CHAR && tup8bit[(unsigned) *hay] == *ndl) {
776             hay++;              /* advance string compare pointers */
777             ndl++;
778             /* if end of search string needle is reached, *//*@=strictops */
779             if (*ndl == (byte) NUL_CHAR
780                 &&(new_has_failed = new_fentry((char *)haystack, sptr))==TRUE)
781                     break;
782         }
783         if ( /*@i1 */ *hay == (byte) NUL_CHAR || new_has_failed)    /* if end of string haystack reached prematurely, */
784             break;              /* return last entry in linked list */
785         hay = ++haystack;       /* if mismatch occurs, advance haystack string pointer */
786         ndl = needle;           /* and reset search string pointer needle */
787     }
788     return entry;               /* if end of string haystack reached, */
789 }                               /* return last entry in list */
790 
791 
792 static volatile atp_BOOL_T searchflag ;
793 
794 #ifdef __TURBOC__
795 #pragma argsused
796 #endif
797 /*@-paramuse */
798 /*
799  * stopfind - signal handler allows ^C aborting findtxt() during search.
800  */
801 void
stopfind(int dummy_arg)802 stopfind(int dummy_arg)
803 {
804 #ifndef HAVE_SIGACTION
805     signal(SIGINT, stopfind);
806 #endif
807     searchflag = FALSE;
808 }
809 /*@=paramuse */
810 
811 /* this has to be static for proper initialization with old compilers */
812 static const size_t hdr_offst[3] = {HAuthor, HForWhom, HSubject};
813 
814 /*
815  * do_search_buf, search buffer message header then message body.
816  */
817 ATP_INLINE void
do_search_buf(struct fentry ** sentry_adr)818 do_search_buf(struct fentry **sentry_adr )
819 {
820     char tchar = NUL_CHAR;
821     int i;
822 
823     /* search header author, recipient, and subject fields */
824     for (i = 0; i < 3; i++) {
825 	tchar = rbuf[hdr_offst[i] + field_len];
826 	rbuf[hdr_offst[i] + field_len] = NUL_CHAR;
827 	(void) findstr((byte *) (rbuf + hdr_offst[i]), (byte *) find_buf, sentry_adr);
828 	rbuf[hdr_offst[i] + field_len] = tchar;
829     }
830 
831     /* search message body */
832     (void) findstr((byte *) (rbuf + header_SIZE), (byte *) find_buf, sentry_adr);
833 }
834 
835 static size_t find_len = 0;
836 static long find_nmb;		/* message count for search */
837 
838 /*
839  * do_search, called after findtxt performs setup to execute search.
840  */
841 static void
do_search(void)842 do_search(void)
843 {
844     struct fentry *tentry, *sentry = NULL;
845 
846     /* setup ^C handler to enable abort search feature */
847     searchflag = TRUE;
848     sig_stopfind();
849 
850     /* walk through message base */
851     while (find_nmb <= TotMsg && searchflag) {
852 	char nmbuf[30];
853 	sprintf(nmbuf, "%ld", find_nmb);
854 	find_nmb++;
855 	/* seek to next message in conference */
856 	if (!SeekNum(nmbuf)) {
857 	    break;
858 	}
859 	/* search buffer for text */
860 	do_search_buf( &sentry ) ;
861 	if (sentry != NULL) {
862 	    Display(NEXT, sentry, find_len);
863 	    break;
864 	}
865     }
866 
867     /* reset signal handler */
868     sig_ignore(SIGINT);
869 
870     /* free linked list of found text */
871     while (sentry != NULL) {
872 	const size_t sentry_size = sizeof(struct fentry);
873 	tentry = sentry->fnext;
874 	free_buffer(sentry, sentry_size);
875 	sentry = tentry;
876     }
877 }
878 
879 
880 /*
881  * iso2dosup - translate latin1 or msdos string to msdos upper case.
882  */
883 ATP_INLINE size_t
iso2dosup(byte * dpt)884 iso2dosup(byte * dpt)
885 {
886     const atp_CODE_T chrset = get_charset();
887     size_t ct = 0;
888     while (/*@i1*/ *dpt != (byte)NUL_CHAR && ct < (size_t) 256) {
889 	/* must search with DOS char. set! */
890 	if (/*@i2*/ chrset == ISOLAT1 && *dpt >= HIGH_ASCII) {
891 	    /* translate back to DOS set */
892 	    *dpt = codepc[(unsigned)*dpt];
893 	}
894 	*dpt = tup8bit[(unsigned)*dpt];	/* international capitalization */
895 	ct++, dpt++;
896     }
897     return ct;
898 }
899 
900 
901 /*
902  * findtxt - wrapper sets up for do_search(); valid modes are FIND and NEXT.
903  */
904 void
findtxt(char * fndargs,const read_command_t mode)905 findtxt(char *fndargs, const read_command_t mode )
906 {
907 /*@-strictops */
908     assert(mode == FIND || mode == NEXT);
909     if (mode == FIND) {
910 	while (*fndargs != SPC_CHAR && *fndargs != NUL_CHAR)
911 	    fndargs++;
912 	if (*fndargs != NUL_CHAR) {
913 	    fndargs++;
914 /*@=strictops */
915 	    if (strlen(fndargs) >= (size_t) FIND_BUF_SIZE)
916 		*(fndargs+FIND_BUF_SIZE) = NUL_CHAR;
917 	    strcpy(find_buf, fndargs);
918 	    find_len = iso2dosup((byte *) find_buf);
919 	} else {
920 	    find_len = 0;
921 	    find_buf[0] = NUL_CHAR;
922 	}
923 	find_nmb = 1L;
924     }
925     if (/*@i1*/ find_buf[0] != NUL_CHAR && 1L <= find_nmb && find_nmb <= TotMsg)
926 	do_search();
927 }
928 
929 
930 /*
931  * hprint - print message header entries and highlighting if need be.
932  */
933 struct fentry *
hprint(char * hbuf,size_t off_set,size_t len,struct fentry ** bptr,const size_t blen)934 hprint(char *hbuf, size_t off_set, size_t len, struct fentry **bptr, const size_t blen)
935 {
936     const atp_CODE_T chrset = get_charset();
937     char linbuf[200], *pt, *chkptr;
938     byte *ptr = (byte *) (hbuf + off_set);
939     struct fentry *tpr = *bptr ;
940     atp_BOOL_T vidon = FALSE;
941     size_t i;
942 
943     linbuf[0] = NUL_CHAR ;
944     pt = linbuf;
945     chkptr = linbuf + 180;	/* security for buffer overun */
946     for (i = 0; i < len; i++) {
947 /* begininning of highlighting code */
948 	if (blen != (size_t)0 && tpr != NULL) {
949 	    if (ptr == (byte *) tpr->findpt) {	/* Beginning of the found string */
950 		if (ansi) {
951 		    strcpy(pt, "\033[7m");
952 		    pt += 4;	/* reverse video on */
953 		} else {	/* termcap */
954 #ifdef HAVE_LIBTERMCAP
955 		    pt += tputs_cpy(pt, rev_on);
956 #else
957 		    strcpy(pt, "**");
958 		    pt += 2;
959 #endif
960 		}
961 		vidon = TRUE;
962 	    }
963 	    if (ptr == (byte *) (tpr->findpt + blen)) {		/* End of the found string */
964 		if (ansi) {
965 #ifdef VT220
966 		    if (stricmp(atp_termtype, "xterm") != SUCCESS) {
967 			strcpy(pt, "\033[27m");		/* reverse video off */
968 			pt += 2;
969 		    } else
970 #endif
971 			strcpy(pt, "\033[m");	/* reverse video off */
972 		    pt += 3;
973 		} else {
974 #ifdef HAVE_LIBTERMCAP
975 		    pt += tputs_cpy(pt, rev_off);
976 #else
977 		    strcpy(pt, "**");
978 		    pt += 2;
979 #endif
980 		}
981 		tpr = tpr->fnext;
982 		vidon = FALSE;
983 	    }
984 	}
985 /* end of highlighting code */
986 /*@-strictops */
987 	if (*ptr != (byte)NUL_CHAR) {
988 	    if (chrset != CHRDOS) {
989 		if (vtspecial(*ptr) && graphics) {	/* use VT102 line graphics codes */
990 /*@=strictops */
991 		    *pt++ = '\016';
992 		    *pt++ = codevt[(unsigned) (*ptr)];
993 		    *pt = '\017';
994 		} else if (/*@i1*/ chrset == ISOLAT1)
995 		    *pt = codelu[(unsigned) (*ptr)];	/* use iso lookup table */
996 		else		/* use 7 bit character set */
997 		    *pt = code7bit[(unsigned) (*ptr)];
998 	    } else
999 		*pt = *ptr;
1000 	} else
1001 	    break;		/* done */
1002 	if (pt > chkptr)
1003 	    break;		/* buffer full, done */
1004 	pt++, ptr++;
1005     }
1006     if (vidon) {		/* turn off reverse video */
1007 	if (ansi) {
1008 #ifdef VT220
1009 	    if (stricmp(atp_termtype, "xterm") != SUCCESS) {
1010 		strcpy(pt, "\033[27m");		/* reverse video off */
1011 		pt += 2;
1012 	    } else
1013 #endif
1014 		strcpy(pt, "\033[m");	/* reverse video off */
1015 	    pt += 3;
1016 	}
1017 #ifdef HAVE_LIBTERMCAP
1018 	else {			/* termcap */
1019 	    pt += tputs_cpy(pt, rev_off);
1020 	}
1021 #endif
1022     }
1023     *pt = NUL_CHAR;			/* mark end of string */
1024     printf("%s", linbuf);
1025     fflush(stdout);
1026     return (tpr);
1027 }
1028 
1029