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 read.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 #ifdef __LCLINT__
37 #endif
38 
39 /*
40  * str2mem - copies a string without the trailing null character.
41  * used to correctly fill the Qmail header.
42  */
43 void
str2mem(char * mem,const char * str)44 str2mem(char *mem, const char *str)
45 {
46     while (*str != NUL_CHAR) {
47         *mem = *str;
48         str++;
49         mem++;
50     }
51 }
52 
53 
54 /*
55  * Numeric - returns TRUE if the string is a number or FALSE if not.
56  */
57 atp_BOOL_T
Numeric(const char * str)58 Numeric(const char *str)
59 {
60     atp_BOOL_T ret_code = FALSE;
61     if (*str != NUL_CHAR) {
62         while (*str != NUL_CHAR && '0' <= *str && *str <= '9')
63             str++;
64         if (*str == NUL_CHAR)
65             ret_code = TRUE;
66     }
67     return ret_code;
68 }
69 
70 
71 /*
72  * Date - displays the date and time.
73  */
74 void
Date(void)75 Date(void)
76 {
77     time_t t;
78     const size_t length_of_date_string = DATE_LEN ;
79     char date_string[DATE_LEN+1];
80 
81     blue();
82     high();
83     /*@i1*/ time(&t);
84     tzset();
85     /* this macro is used only by msdos and its relatives */
86     ADJUST_DOS_TIME
87     (void) memcpy(date_string, ctime(&t), length_of_date_string);
88     date_string[ length_of_date_string ] = (char)0;
89     printf("%s", date_string);
90     green();
91     printf("\n");
92 }
93 
94 
95 /*
96  * show_again - re-displays the current message.
97  */
98 void
show_again(void)99 show_again(void)
100 {
101     if (get_FirstDone())
102         Display(NEXT, NULL, 0);
103     else
104         ReadNext(NEXT);
105 }
106 
107 
108 /*
109  * enter_msg - invokes Reply() routines to post a message.
110  */
111 void
enter_msg(char * tmp)112 enter_msg(char *tmp)
113 {
114     if (get_CurConf() == RCONF_IDX && tmp[1] == NUL_CHAR && get_FirstDone())
115         Reply(CHANGE, tmp);
116     else
117         Reply(ENTER, tmp);
118 }
119 
120 
121 /*
122  * reply_msg - post reply to current message.
123  */
124 void
reply_msg(const char * tmp)125 reply_msg(const char *tmp)
126 {
127     if (get_FirstDone() && get_CurConf() != RCONF_IDX)
128         Reply(REPLY, tmp);
129 }
130 
131 
132 /*
133  * xpost_msg - copy a perviously entered reply to another message area.
134  */
135 void
xpost_msg(const char * tmp)136 xpost_msg(const char *tmp)
137 {
138     if (get_CurConf() == RCONF_IDX && get_FirstDone())
139         Reply(XPOST, tmp);
140 }
141 
142 
143 /*
144  * change_msg - re-edit a message in the reply conference.
145  */
146 void
change_msg(const char * tmp)147 change_msg(const char *tmp)
148 {
149     if (get_CurConf() == RCONF_IDX && get_FirstDone())
150         Reply(CHANGE, tmp);
151 }
152 
153 
154 /*
155  * mark_private - marks the current message as private.
156  */
157 void
mark_private(void)158 mark_private(void)
159 {
160     if (get_CurConf() == RCONF_IDX)
161         ReadNext(PRIVATE);
162 }
163 
164 
165 /*
166  * FindActive, search conference array for next active conference.
167  * it will wrap around to start if need be.
168  */
169 static int
FindActive(const int curcon)170 FindActive(const int curcon)
171 {
172     int i;
173     for (i = curcon + 1; i <= LastConf; i++) {
174         if (ConfActive[i])
175             return (i);
176     }
177     for (i = 0; i <= curcon; i++) {
178         if (ConfActive[i])
179             return (i);
180     }
181     return FAILURE;
182 }
183 
184 
185 /*
186  * more - prompts "more  ?" and wait for yes/oui/ja and returns TRUE for yes.
187  *  returns FALSE for no, 'def ' manages the CR default answer.
188  */
189 atp_BOOL_T
more(atp_BOOL_T def)190 more(atp_BOOL_T def)
191 {
192     atp_BOOL_T ret_code = TRUE;
193     atp_BOOL_T loop_flag = TRUE;
194     char *response, tmp[80];
195 
196     do {
197 	high();
198 	yellow();
199 	luxptr = NULL;
200 	sprintf(tmp, "\r %s ? %s : ", txt[60], def? txt[106] : txt[107]);
201 	do {
202 	    response = readline(tmp, no_scroll);
203 	} while (response == NULL);
204 	strcpy(tmp, response);
205 	free_string(response);
206 	clear();
207 	printf("\r %-75s \r", " ");	/* erase prompt */
208 	switch ((int) tmp[0]) {
209 	case 'o':
210 	case 'y':
211 	case 'O':
212 	case 'Y':
213 	case 'j':
214 	case 'J':
215 	    ret_code = TRUE;
216 	    loop_flag = FALSE;
217 	    break;
218 	case 'N':
219 	case 'n':
220 	    ret_code = FALSE;
221 	    loop_flag = FALSE;
222 	    break;
223 	case '+':
224 	case '-':
225 	case 0:		/* CR */
226 	case 10:
227 	    if (def) {
228 		ret_code = TRUE;
229 	    } else {
230 		ret_code = FALSE;
231 	    }
232 	    loop_flag = FALSE;
233 	}
234     } while (loop_flag);
235     return ret_code;
236 }
237 
238 
239 /*
240  * YesNo - prompts "Y/N : " and wait for yes/oui/ja then returns TRUE for yes.
241  */
242 atp_BOOL_T
YesNo(atp_BOOL_T def,const char * prmt)243 YesNo(atp_BOOL_T def, const char *prmt)
244 {
245     atp_BOOL_T ret_code = TRUE;
246     atp_BOOL_T loop_flag = TRUE;
247     char *response, tmp[80];
248 
249     do {
250 	luxptr = NULL;
251 	sprintf(tmp, "\r%s %s : ", prmt, def? txt[106] : txt[107]);
252 	do {
253 	    response = readline(tmp, no_scroll);
254 	} while (response == NULL);
255 	strcpy(tmp, response);
256 	free_string(response);
257 	clear();
258 	printf("\r %-75s \r", " ");	/* erase prompt */
259 	switch ((int) tmp[0]) {
260 	case 'o':
261 	case 'y':
262 	case 'O':
263 	case 'Y':
264 	case 'j':
265 	case 'J':
266 	    ret_code = TRUE;
267 	    loop_flag = FALSE;
268 	    break;
269 	case 'N':
270 	case 'n':
271 	    ret_code = FALSE;
272 	    loop_flag = FALSE;
273 	    break;
274 	case 0:		/* CR */
275 	case 10:
276 	    if (def)
277 		ret_code = TRUE;
278 	    else
279 		ret_code = FALSE;
280 	    loop_flag = FALSE;
281 	}
282     } while (loop_flag);
283     return ret_code;
284 }
285 
286 
287 /*
288  * Chk4RepPkt - returns TRUE if bbsname.rep exists.
289  */
290 atp_BOOL_T
Chk4RepPkt(void)291 Chk4RepPkt(void)
292 {
293     atp_BOOL_T ret_code = FALSE;
294     char RepFile[MAXPATHS];
295 
296     sprintf(RepFile, "%s%s.rep", ReplyPath, CurBoard);
297     if (access(RepFile, F_OK) == SUCCESS) {
298 	char prmbuf[80];
299 	red();
300 	high();
301 	/* "Warning ! file already exist " "delete it ?" */
302 	printf("%s   %s %s %s...!\n", txt[2], txt[62], RepFile, txt[63]);
303 	sprintf(prmbuf, "            %s ", txt[64]);
304 	if (YesNo(YES, prmbuf))
305 	    do_unlink(RepFile);
306 	else
307 	    ret_code = TRUE;
308     }
309     return ret_code;
310 }
311 
312 
313 /*
314  * chkerror - si un rigolo tape ce mot dans le shell.
315  */
316 void
chkerror(void)317 chkerror(void)
318 {
319     magenta();
320     high();
321     printf("\n");
322     switch (get_charset()) {
323     case ISOLAT1:
324 	printf("Calice! Esp�ce");
325 	break;
326     case CHRDOS:
327 	printf("Calice! Esp�ce");
328 	break;
329     default:
330 	printf("Calice! Espece");
331     }
332     printf(" de petit connard, on t'a pas appris la politesse  ???%c\n", BELL);
333     printf("Va te faire foutre !!!\n\n");
334     clear();
335     fflush(stdout);
336 }
337 
338 
339 /*
340  * LoadStat, called by DoLoad().
341  */
342 static atp_BOOL_T
LoadStat(char * OldDat,char * NewDat)343 LoadStat(char *OldDat, char *NewDat)
344 {
345     atp_BOOL_T ret_code = TRUE;
346     struct stat Oldst, Newst;
347     Newst.st_mtime = Oldst.st_mtime = 0L;
348     if (stat(OldDat, &Oldst) == FAILURE) {
349         printf("Load() Can't stat OldDat %s:%d\n",__FILE__,__LINE__);
350         (void) sleep(3);
351     }
352     if (stat(NewDat, &Newst) == FAILURE) {
353         printf("Load() Can't stat NewDat %s:%d\n",__FILE__,__LINE__);
354         (void) sleep(3);
355     }
356     if (Newst.st_mtime <= Oldst.st_mtime) {
357         char CONSPTR prmbuf = NewDat;
358         red();
359         high();
360         /* "New packet older..." */
361         printf("%s\n%s.\n", txt[2], txt[11]);
362         /* " Do you  want to load it? " */
363         sprintf(prmbuf, "%s...", txt[12]);
364         if (!YesNo(NO, prmbuf))
365             ret_code = FALSE;
366     }
367     return ret_code;
368 }
369 
370 /* used by DoLoad(), AutoJoin(), Read(), */
371 static atp_BOOL_T newmail = TRUE;
372 
373 /* used by many routines in this file */
374 static atp_BOOL_T IsEmpty = TRUE;       /* Flag false if a BBS is loaded */
375 
376 /*
377  * get_isempty - allows global read access to IsEmpty variable.
378  */
379 atp_BOOL_T
get_isempty(void)380 get_isempty(void)
381 {
382     return IsEmpty;
383 }
384 
385 /*
386  * UpdateConf - update last message read in index file.
387  */
388 void
UpdateConf(const update_command_t mode)389 UpdateConf(const update_command_t mode)
390 {
391     assert(/*@i2*/ mode == update_last_read_ptr || mode == reset_last_read_ptr);
392     if (get_ActvCnt() < 1 || !ConfActive[get_CurConf()]) {
393 	deleol();
394 	printf("No active conference to update.\n");
395 	if (get_FilesOpen()) {
396 	    printf("ERROR: %s:%d UpdateConf() -- open files, inactive Conf\n", __FILE__, __LINE__);
397 	    printf("ActvCnt = %d  CurAct = %d \n", get_ActvCnt(), (int) ConfActive[get_CurConf()]);
398 	};
399     } else if (!get_FilesOpen()) {
400 	printf("ERROR: module UpdateConf() -- bad file pointer for fseek()\n");
401     } else if (fseek(fidx, 0L, SEEK_SET) != SUCCESS) {
402 	printf("UpdateConf() Seek error...\n");
403     } else {
404 	struct MyIndex Idx;
405 	fread((char *) &Idx, index_SIZE, one_record, fidx);
406 	if (((Idx.LastRead < get_MsgNum()) || /*@i1*/ mode == reset_last_read_ptr) && get_FirstDone()) {
407 	    long here;
408 	    Idx.LastRead = get_MsgNum();
409 	    rewind(fidx);
410 	    fwrite((char *) &Idx, index_SIZE, one_record, fidx);
411 	    printf("%s.\n", txt[66]);	/* "Last read pointer updated" */
412 	    here = Idx.LastRead * IDXSIZE;
413 	    fseek(fidx, here, SEEK_SET);
414 	}
415     }
416 }
417 
418 
419 /*
420  * DoLoad, update BBS archive from files in working directory.
421  * called from Load(). OldDat points to buffers of size MAXPATHS.
422  * NewDat points to the new control.dat file in the work directory.
423  */
424 
425 static int CurConf = 0;
426 
427 /*
428  * get_CurConf - returns the number of the current conference.
429  */
430 int
get_CurConf(void)431 get_CurConf(void)
432 {
433     return CurConf;
434 }
435 
436 
437 /*
438  * set_CurConf - sets the number of the current conference.
439  */
440 void
set_CurConf(const int num,const scnf_access_t perm)441 set_CurConf(const int num, const scnf_access_t perm)
442 {
443     if (/*@i1*/ scnf_GetConf == perm)
444 	CurConf = num;
445 #ifdef ATPDBG
446     else
447 	assert(/*@i1*/ scnf_GetConf == perm);
448 #endif
449 }
450 
451 
452 /*
453  * DoLoad_MkIndex, after DoLoad() sets up, make index and clean up.
454  */
455 static void
DoLoad_MkIndex(const char * BbsDir,const char * BbsName)456 DoLoad_MkIndex(const char *BbsDir, const char *BbsName)
457 {
458     if (/*@i1*/ MkIndex(WorkPath, BbsDir) == ATP_OK) {
459         /* update array of active conferences */
460         set_CurBoard(BbsName, cb_DoLoad_MkIndex);
461         ActvConf();
462         (void) Chk4RepPkt();
463         Chk4RepCnf(BbsDir);
464         if (get_FilesOpen()) {
465             fclose_fmsg_fidx();
466         }
467         IsEmpty = FALSE;
468         newmail = TRUE;
469         set_caps(fido ? FALSE : TRUE);
470         if (get_pmail())
471             CurConf = findCindex(PERS_CONF);
472         else
473             CurConf = FindActive(LastConf);
474         if (CurConf < 0)
475             CurConf = 0;
476     }
477 }
478 
479 
480 /*
481  * DoLoad, called by LoadExtract(), does setup for DoLoad_MkIndex().
482  */
483 static void
DoLoad(char * BbsName,char * OldDat,char * NewDat)484 DoLoad(char *BbsName, char *OldDat, char *NewDat)
485 {
486     char BbsDir[MAXPATHS];
487     /* Access to a board subdir */
488     sprintf(BbsDir, "%s%s", HomePath, BbsName);
489     if (access(BbsDir, R_OK | W_OK) == FAILURE)
490         my_mkdir(BbsDir);
491     sprintf(OldDat, "%s%s%c%s", HomePath, BbsName, SEP, CNTRL_FILE);
492 
493     /* If there exists an old control.dat */
494     if (access(OldDat, F_OK) == FAILURE || LoadStat(OldDat, NewDat)) {
495         if (!IsEmpty && ConfActive[CurConf])
496             UpdateConf(update_last_read_ptr);
497         /* Create Index Files */
498         DoLoad_MkIndex(BbsDir,BbsName);
499         GetConf(CurConf);
500     }
501     assert(!get_isempty() || (fmsg == NULL && fidx == NULL));
502 }
503 
504 #ifdef NONUM
505 /*
506  * For problems with Searchlight BBS,
507  * don't use it unless you *really* need it.
508  */
509 static void
SearchlightCruft(char * BbsName,int i)510 SearchlightCruft(char *BbsName, int i)
511 {
512     char *tptr = BbsName;
513     if (!Numeric(tptr))         /* strip trailing packet sequence numbers */
514         while (isdigit(BbsName[--i]))
515             BbsName[i] = NUL_CHAR;
516 }
517 #endif
518 
519 /*
520  * LoadExtract, called by Load().
521  */
522 static void
LoadExtract(char * BbsName,char * tmp1)523 LoadExtract(char *BbsName, char *tmp1)
524 {
525     if (access(tmp1, R_OK) == FAILURE) {
526         red();
527         /* No mail found        */
528         printf("%s : %s\n", txt[6], tmp1);
529         printf("%s\n", txt[7]); /* Try the read command */
530     } else {
531         char tmp2[MAXPATHS];
532         /* Don't forget to clean work directory ! */
533         Clean();
534         yellow();
535         high();
536         /* " Extracting Messages..." */
537         printf("%s\n", txt[8]);
538         green();
539         fflush(stdout);
540 	/*@-usedef */
541         /* save current directory    */
542         GETCWD(tmp2, MAXPATHS);
543 	/*@=usedef */
544         /* change to work directory  */
545         if (CHPATH(WorkPath) != SUCCESS) {
546             yellow();
547             printf("Error: can't access workpath: %s\n", WorkPath);
548         } else {
549             char CONSPTR unarc = get_atprc_str(unarchvr);
550             assert(unarc != NULL);
551             fork_execvp(unarc, tmp1);
552             free_string(unarc);
553             CHPATH(tmp2);       /* restore current directory */
554             yellow();
555             if(verify_new_file( tmp2, WorkPath, CNTRL_FILE) == ATP_ERROR)
556                 printf("txt[49]: %s\n", tmp2);
557             else
558                 DoLoad(BbsName, tmp1, tmp2);
559         }
560     }
561 }
562 
563 
564 /*
565  * Load - get a qwk packet; toss it then display it.
566  */
567 void
Load(const char * name)568 Load(const char *name)
569 {
570     char BbsName[50], tmp1[MAXPATHS];
571     /* reset global flag to indicate no personal mail yet */
572     set_pmail(FALSE, pmLoad);	/* should this be moved ??? */
573     query_rep_exists();
574     BbsName[0] = NUL_CHAR;
575     sscanf(name, "%s %s", tmp1, BbsName);
576     if (BbsName[0] == NUL_CHAR) {
577 	red();
578 	printf("%s  %s\n", txt[87], txt[88]);	/* "usage" " load.." */
579     } else {
580 	int i = -1;
581 	printf("\n%s %s\n", txt[5], BbsName);	/* "Loading" */
582 	if (strstr(BbsName, ".qw") == NULL)
583 	    strcat(BbsName, ".qwk");
584 	sprintf(tmp1, "%s%s", MailPath, BbsName);
585 	/*
586 	 *    Now that we've used BbsName to id the packet file,
587 	 *    strip any trailing suffix.
588 	 */
589 	while (BbsName[++i] != NUL_CHAR)/* empty loop */
590 	    ;
591 	while (BbsName[--i] != '.')	/* empty loop */
592 	    ;
593 	BbsName[i] = NUL_CHAR;
594 #ifdef NONUM
595 	/* do not use, this is a kludge only for Searchlight BBS problems */
596 	SearchlightCruft(BbsName, i);
597 #endif
598 	LoadExtract(BbsName, tmp1);
599     }
600 }
601 
602 
603 /*
604  * VerifyBBS, check for existing BBS and control.dat file.
605  *
606  */
607 static atp_BOOL_T
VerifyBBS(const char * BbsName,char * buf)608 VerifyBBS(const char *BbsName, char *buf)
609 {
610     atp_BOOL_T ret_code = FALSE;
611     sprintf(buf, "%s%s%c%s", HomePath, BbsName, SEP, CNTRL_FILE);
612     if (access(buf, F_OK) == FAILURE) {
613         printf("%s : %s\n", txt[13], buf);      /* "No bbs found " */
614     } else {
615         sprintf(buf, "%s%s", HomePath, BbsName);
616         if (/*@i1*/ RdCn_ReadControl(buf) != ATP_OK) {
617             printf("%s: %s\n", buf, txt[14]);   /* "Error in CONTROL.DAT." */
618         } else {
619             ret_code = TRUE;
620         }
621     }
622     return ret_code;
623 }
624 
625 
626 /*
627  * ReadExecute, after setup by Read() this changes to another BBS.
628  */
629 static void
ReadExecute(const char * BbsName,char * tmp_buffer)630 ReadExecute(const char *BbsName, char *tmp_buffer)
631 {
632     if (VerifyBBS(BbsName, tmp_buffer)) {
633         printf("%s %s\n", txt[5], BbsName);     /* "Loading" */
634         set_CurBoard(BbsName, cb_Read);
635         (void) Chk4RepPkt();
636         Chk4RepCnf(tmp_buffer);
637         if (get_FilesOpen()) {
638             fclose_fmsg_fidx();
639         }
640         /* update boolean array of active/inactive flags */
641         ActvConf();
642         IsEmpty = FALSE; /* should this be set inside of GetConf()?  !!! */
643         newmail = FALSE;
644         set_caps(fido ? FALSE : TRUE);
645         CurConf = FindActive(LastConf);
646         if (CurConf < 0)
647             CurConf = 0;
648     }
649 }
650 
651 
652 /*
653  * Read - join a BBS without extracting new mail.
654  */
655 void
Read(const char * cmd_line)656 Read(const char *cmd_line)
657 {
658     char BbsName[50];
659     char tmp_buffer[MAXPATHS];
660     query_rep_exists();
661     BbsName[0] = NUL_CHAR;
662     sscanf(cmd_line, "%s %s", tmp_buffer, BbsName);
663     if (BbsName[0] == NUL_CHAR) {
664         red();
665         printf("%s %s\n", txt[87], txt[89]);    /* "usage " "rev..." */
666     } else {
667         if (!IsEmpty && ConfActive[CurConf])
668             UpdateConf(update_last_read_ptr);
669         ReadExecute(BbsName, tmp_buffer);
670         GetConf(CurConf);
671     }
672 #ifdef ATPDBG
673     assert(!get_isempty() || !get_FilesOpen());
674     assert(!get_FilesOpen() || ( fmsg != NULL && fidx != NULL ));
675 #endif
676 }
677 
678 static const int not_found = -1 ;
679 /*
680  * chcon_string_parse, turn a conference string into a conference index.
681  */
682 static int
chcon_string_parse(char * Name,const char * buf,char * tmp)683 chcon_string_parse(char *Name, const char *buf, char *tmp)
684 {
685     int i = 0 ;
686     strcpy(tmp, strstr(buf, Name));
687     strcpy(Name, tmp);
688     for (i = 0; i <= LastConf; i++) {
689         if (stricmp(Name, ConfNames[i]) == SUCCESS)
690             break;
691     }
692     if (i > LastConf) {
693         const size_t m = strlen(Name);
694         for (i = 0; i <= LastConf; i++) {
695             if (strnicmp(Name, ConfNames[i], m) == SUCCESS)
696                 break;
697         }
698     }
699     return ( i > LastConf ? not_found : i ) ;
700 }
701 
702 
703 /*
704  * chcon_scan, turn absolute conf number or name into a conference index.
705  */
706 static int
chcon_scan(char * Name,const char * buf,char * tmp)707 chcon_scan(char *Name, const char *buf, char *tmp)
708 {
709     int i;
710     if (Numeric(Name)) {
711         /* a number is given */
712         i = atoi(Name);
713         if ((i = findCindex(i)) > -1)
714             strcpy(Name, ConfNames[i]);
715     } else {
716         /* a string name is given   */
717         i = chcon_string_parse(Name, buf, tmp);
718     }
719     return i;
720 }
721 
722 
723 /*
724  * chconf_join, actually invoke GetConf() to change current conference.
725  */
726 static void
chconf_join(int new_conference)727 chconf_join(int new_conference)
728 {
729     /* Save current conf pointer */
730     UpdateConf(update_last_read_ptr);
731     if (/*@i1*/ GetConf(new_conference) == ATP_OK) {
732         printf("\n ");
733         blue();
734         high();
735         printf("*  %s %s %s. *", txt[29], ConfNames[CurConf], txt[30]);
736         green();
737         printf("\n\n");
738         /* "Conference " "joined" */
739         clear();
740         fflush(stdout);
741     }
742 }
743 
744 
745 /*
746  * chconf - change active conference.
747  */
748 void
chconf(const char * buf)749 chconf(const char *buf)
750 {
751     char Name[100];
752     char tmp[MAXPATHS];
753     if (IsEmpty) {
754         EmptyMsg();
755     } else {
756         Name[0] = NUL_CHAR;
757         sscanf(buf, "%s %s", tmp, Name);
758         if (Name[0] == NUL_CHAR) {
759             red();
760             printf("%s\n", txt[87]);    /* "Usage" */
761             printf("\tj %s\n\t\t%s\n", txt[90], txt[91]);       /* "Conf #" "or" */
762             printf("\tj %s\n", txt[92]);        /* "conf name" */
763             printf("%s\n", txt[93]);    /* "type conf to list..." */
764         } else {
765             int new_conference = chcon_scan(Name, buf, tmp);
766             if ( stricmp(Name, "MAIN") == SUCCESS)
767                 new_conference = 0;     /* Special case for "main board" */
768             if (new_conference > LastConf || new_conference < 0) {
769                 /* "Unknown conference" */
770                 printf("\n%s\n", txt[28]);
771             } else {
772                 cnf_path(tmp, ConfNumbers[new_conference]);
773                 if (access(tmp, F_OK) == FAILURE) {
774                     red();      /* "No mail found" */
775                     printf("%s : %s  %s\n", txt[6], tmp, ConfNames[new_conference]);
776                 } else
777                     chconf_join(new_conference);
778             }
779         }
780     }
781 }
782 
783 
784 /*
785  * AutoJoin - loads next valid conferences.
786  */
787 #define ILR get_MsgLastRead()
788 
789 void
AutoJoin(void)790 AutoJoin(void)
791 {
792     if (IsEmpty) {
793 	EmptyMsg();
794     } else if (get_ActvCnt() == 0) {
795 	chconf("j MAIN");
796     } else {
797 	char tmp[MAXPATHS];
798 	int i = CurConf;
799 	int j = get_ActvCnt();
800 
801 	for (; j != 0; j--) {
802 	    i = FindActive(i);
803 	    sprintf(tmp, "j %d\n", ConfNumbers[i]);
804 	    chconf(tmp);
805 	    if (ILR < 0L || ILR != (get_TotMsg() - 1L) || !newmail) {
806 		return;
807 	    } else
808 		printf("\t%s.\n", txt[31]);	/* "No new mail" */
809 	}
810 	newmail = FALSE;
811 	printf("\n");
812     }
813 }
814 
815