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