7b385ac14f4498d19b27f5ccd359f54c85a2e071
[citadel.git] / citadel / commands.c
1 /*
2  * Citadel/UX
3  *
4  * commands.c - front end for Citadel
5  *
6  * This version is the traditional command parser for room prompts.
7  *
8  */
9
10 #include "sysdep.h"
11 #include <stdlib.h>
12 #include <unistd.h>
13 #include <stdio.h>
14 #include <fcntl.h>
15 #include <ctype.h>
16 #include <string.h>
17 #include <sys/types.h>
18 #include <sys/time.h>
19
20 #ifdef HAVE_TERMIOS_H
21 #include <termios.h>
22 #else
23 #include <sgtty.h>
24 #endif
25
26 #ifdef HAVE_SYS_SELECT_H
27 #include <sys/select.h>
28 #endif
29
30
31 #include <signal.h>
32 #include <errno.h>
33 #include "citadel.h"
34 #include "commands.h"
35 #include "messages.h"
36 #include "citadel_decls.h"
37 #include "routines.h"
38 #include "routines2.h"
39
40 struct citcmd {
41         struct citcmd *next;
42         int c_cmdnum;
43         int c_axlevel;
44         char c_keys[5][64];
45         };
46
47 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
48
49
50 int rc_exp_beep;
51 char rc_exp_cmd[256];
52 int rc_allow_attachments;
53 int rc_display_message_numbers;
54 int rc_force_mail_prompts;
55 int rc_ansi_color;
56
57 char *gl_string;
58 int next_lazy_cmd = 5;
59
60 struct citcmd *cmdlist = NULL;
61
62
63 /* these variables are local to this module */
64 char keepalives_enabled = KA_YES;       /* send NOOPs to server when idle */
65 int ok_to_interrupt = 0;                /* print express msgs asynchronously */
66 time_t AnsiDetect;                      /* when did we send the detect code? */
67 int enable_color = 0;                   /* nonzero for ANSI color */
68
69
70 /*
71  * print_express()  -  print express messages if there are any
72  */
73 void print_express(void) {
74         char buf[256];
75         FILE *outpipe;
76
77         if (express_msgs == 0) return;
78         express_msgs = 0;
79         serv_puts("PEXP");
80         serv_gets(buf);
81         if (buf[0]!='1') return;
82
83         if (strlen(rc_exp_cmd) > 0) {
84                 outpipe = popen(rc_exp_cmd, "w");
85                 if (outpipe != NULL) {
86                         while (serv_gets(buf), strcmp(buf,"000")) {
87                                 fprintf(outpipe, "%s\n", buf);
88                                 }
89                         pclose(outpipe);
90                         return;
91                         }
92                 }
93
94         /* fall back to built-in express message display */
95         if (rc_exp_beep) {
96                 putc(7,stdout);
97                 }
98         color(1);
99         printf("---\n");
100         while (serv_gets(buf), strcmp(buf,"000")) {
101                 printf("%s\n",buf);
102                 }
103         printf("---\n");
104         color(7);
105         }
106
107
108 void set_keepalives(int s)
109 {
110         keepalives_enabled = (char)s;
111         }
112
113 /* 
114  * This loop handles the "keepalive" messages sent to the server when idling.
115  */
116 void do_keepalive(void) {
117         char buf[256];
118         static time_t idlet = 0;
119         time_t now;
120
121         time(&now);
122         if ((now - idlet) < ((long)S_KEEPALIVE)) return;
123         time(&idlet);
124
125         if (keepalives_enabled != KA_NO) {
126                 serv_puts("NOOP");
127                 if (keepalives_enabled == KA_YES) {
128                         serv_gets(buf);
129                         if (buf[3]=='*') {
130                                 express_msgs = 1;
131                                 if (ok_to_interrupt == 1) {
132                                         printf("\r                    \r");
133                                         print_express();
134                                         printf("%s%c ",room_name,
135                                                 room_prompt(room_flags));
136                                         fflush(stdout); 
137                                         }
138                                 }
139                         }
140                 }
141         }
142
143
144 int inkey(void) {               /* get a character from the keyboard, with   */
145         int a;          /* the watchdog timer in effect if necessary */
146         fd_set rfds;
147         struct timeval tv;
148         time_t start_time, now;
149         char inbuf[2];
150
151         time(&start_time);
152         
153         do {
154
155                 /* This loop waits for keyboard input.  If the keepalive
156                  * timer expires, it sends a keepalive to the server if
157                  * necessary and then waits again.
158                  */
159                 do {
160                         do_keepalive();
161                         fflush(stdout);
162                         FD_ZERO(&rfds);
163                         FD_SET(0,&rfds);
164                         tv.tv_sec = S_KEEPALIVE;
165                         tv.tv_usec = 0;
166
167                         time(&now);
168                         if (((now-start_time) > SLEEPING)
169                            && (SLEEPING != 0) && (getppid()==1)) {
170                                 printf("Sleeping? Call again.\n");
171                                 logoff(SIGALRM);
172                                 }
173
174                         select(1, &rfds, NULL, NULL, &tv);
175                         } while (!FD_ISSET(0, &rfds));
176
177
178
179
180                 /* At this point, there's input, so fetch it.
181                  * (There's a hole in the bucket...)
182                  */
183                 read(0, inbuf, 1);
184                 a = inbuf[0];
185                 if (a==127) a=8;
186                 if (a>126) a=0;
187                 if (a==10) a=13;
188                 if (((a!=4)&&(a!=13)&&(a!=8)&&(a!=NEXT_KEY)&&(a!=STOP_KEY))
189                         && ((a<32)||(a>126))) a=0;
190                 } while(a==0);
191         return(a);
192         }
193
194 void getline(char *string, int lim)     /* Gets a line from the terminal */
195                         /* Pointer to string buffer */
196                         /* Maximum length - if negative, no-show */
197 {
198         int a,b;
199         char flag = 0;
200
201         if (lim<0) { lim=(0-lim); flag=1; }
202         strcpy(string,"");
203         gl_string = string;
204 GLA:    a=inkey(); a=(a&127);
205         if ((a==8)&&(strlen(string)==0)) goto GLA;
206         if ((a!=13)&&(a!=8)&&(strlen(string)==lim)) goto GLA;
207         if ((a==8)&&(string[0]!=0)) {
208                 string[strlen(string)-1]=0;
209                 putc(8,stdout); putc(32,stdout); putc(8,stdout); goto GLA; }
210         if ((a==13)||(a==10)) {
211                 putc(13,stdout);
212                 putc(10,stdout);
213                 return;
214                 }
215         if (a<32) a='.';
216         b=strlen(string);
217         string[b]=a;
218         string[b+1]=0;
219         if (flag==0) putc(a,stdout);
220         if (flag==1) putc('*',stdout);
221         goto GLA;
222         }
223
224
225 /*
226  * strprompt()  -  prompt for a string, print the existing value and
227  *                 allow the user to press return to keep it...
228  */
229 void strprompt(char *prompt, char *str, int len)
230 {
231         char buf[128];
232         print_express();
233         color(3);
234         printf("%s [", prompt);
235         color(1);
236         printf("%s", str);
237         color(3);
238         printf("]: ");
239         color(2);
240         getline(buf,len);
241         color(7);
242         if (buf[0]!=0) strcpy(str,buf);
243         }
244
245 /* 
246  * intprompt()  -  like strprompt(), except for an integer
247  *                 (note that it RETURNS the new value!)
248  */
249 int intprompt(char *prompt, int ival, int imin, int imax)
250 {
251         char buf[16];
252         int i;
253         i = ival;
254         do {
255                 sprintf(buf,"%d",i);
256                 strprompt(prompt,buf,15);
257                 i=atoi(buf);
258                 if (i<imin) printf("*** Must be no less than %d.\n",imin);
259                 if (i>imax) printf("*** Must be no more than %d.\n",imax);
260                 } while((i<imin)||(i>imax));
261         return(i);
262         }
263
264 /* 
265  * newprompt()  -  prompt for a string with no existing value
266  *                 (clears out string buffer first)
267  */
268 void newprompt(char *prompt, char *str, int len)
269 {
270         color(3);
271         printf("%s",prompt);
272         color(2);
273         getline(str,len);
274         color(7);
275         }
276
277
278 int lkey(void) {        /* returns a lower case value */
279         int a;
280         a=inkey();
281         if (isupper(a)) a=tolower(a);
282         return(a);
283         }
284
285 /*
286  * parse the citadel.rc file
287  */
288 void load_command_set(void) {
289         FILE *ccfile;
290         char buf[256];
291         struct citcmd *cptr;
292         struct citcmd *lastcmd = NULL;
293         int a,d;
294         int b = 0;
295
296
297         /* first, set up some defaults for non-required variables */
298
299         strcpy(editor_path,"");
300         strcpy(printcmd,"");
301         strcpy(rc_username,"");
302         strcpy(rc_password,"");
303         rc_floor_mode = 0;
304         rc_exp_beep = 1;
305         rc_allow_attachments = 0;
306         strcpy(rc_exp_cmd, "");
307         rc_display_message_numbers = 0;
308         rc_force_mail_prompts = 0;
309         rc_ansi_color = 0;
310
311         /* now try to open the citadel.rc file */
312
313         ccfile = NULL;
314         if (getenv("HOME") != NULL) {
315                 sprintf(buf,"%s/.citadelrc",getenv("HOME"));
316                 ccfile = fopen(buf,"r");
317                 }
318         if (ccfile==NULL) {
319                 ccfile = fopen("/usr/local/lib/citadel.rc","r");
320                 }
321         if (ccfile==NULL) {
322                 sprintf(buf,"%s/citadel.rc",BBSDIR);
323                 ccfile = fopen(buf,"r");
324                 }
325         if (ccfile==NULL) {
326                 perror("commands: cannot open citadel.rc");
327                 logoff(errno);
328                 }
329
330         while (fgets(buf, 256, ccfile) != NULL) {
331             while ( (strlen(buf)>0) ? (isspace(buf[strlen(buf)-1])) : 0 )
332                 buf[strlen(buf)-1] = 0;
333
334             if (!struncmp(buf,"editor=",7))
335                 strcpy(editor_path,&buf[7]);
336
337             if (!struncmp(buf,"printcmd=",9))
338                 strcpy(printcmd,&buf[9]);
339
340             if (!struncmp(buf,"expcmd=",7))
341                 strcpy(rc_exp_cmd,&buf[7]);
342
343             if (!struncmp(buf,"local_screen_dimensions=",24))
344                 have_xterm = (char)atoi(&buf[24]);
345
346             if (!struncmp(buf,"use_floors=",11)) {
347                 if (!strucmp(&buf[11],"yes")) rc_floor_mode = RC_YES;
348                 if (!strucmp(&buf[11],"no")) rc_floor_mode = RC_NO;
349                 if (!strucmp(&buf[11],"default")) rc_floor_mode = RC_DEFAULT;
350                 }
351
352             if (!struncmp(buf,"beep=",5)) {
353                 rc_exp_beep = atoi(&buf[5]);
354                 }
355
356             if (!struncmp(buf,"allow_attachments=", 18)) {
357                 rc_allow_attachments = atoi(&buf[18]);
358                 }
359
360             if (!struncmp(buf,"display_message_numbers=", 24)) {
361                 rc_display_message_numbers = atoi(&buf[24]);
362                 }
363
364             if (!struncmp(buf,"force_mail_prompts=", 19)) {
365                 rc_force_mail_prompts = atoi(&buf[19]);
366                 }
367
368             if (!struncmp(buf,"ansi_color=", 11)) {
369                 if (!strncasecmp(&buf[11], "on", 2)) 
370                         rc_ansi_color = 1;
371                 if (!strncasecmp(&buf[11], "auto", 4)) 
372                         rc_ansi_color = 2;      /* autodetect */
373                 }
374
375             if (!struncmp(buf,"username=",9))
376                 strcpy(rc_username,&buf[9]);
377
378             if (!struncmp(buf,"password=",9))
379                 strcpy(rc_password,&buf[9]);
380
381             if (!struncmp(buf,"cmd=",4)) {
382                 strcpy(buf,&buf[4]);
383
384                 cptr = (struct citcmd *) malloc (sizeof(struct citcmd));
385                 
386                 cptr->c_cmdnum = atoi (buf);
387                 for (d=strlen(buf); d>=0; --d)
388                         if (buf[d]==',') b=d;
389                 strcpy (buf, &buf[b+1]);
390
391                 cptr->c_axlevel = atoi (buf);
392                 for (d=strlen(buf); d>=0; --d)
393                         if (buf[d]==',') b=d;
394                 strcpy (buf, &buf[b+1]);
395
396                 for (a=0; a<5; ++a) cptr->c_keys[a][0] = 0;
397
398                 a = 0;  b = 0;
399                 buf[strlen(buf)+1] = 0;
400                 while (strlen(buf) > 0) {
401                         b = strlen(buf);
402                         for (d=strlen(buf); d>=0; --d)
403                                 if (buf[d]==',') b=d;
404                         strncpy(cptr->c_keys[a],buf,b);
405                         cptr->c_keys[a][b] = 0;
406                         if (buf[b]==',')
407                                 strcpy(buf,&buf[b+1]);
408                         else
409                                 strcpy(buf,"");
410                         ++a;
411                         }
412
413                 cptr->next = NULL;
414                 if (cmdlist == NULL) cmdlist = cptr;
415                 else lastcmd->next = cptr;
416                 lastcmd = cptr;
417                 }
418             }
419         fclose(ccfile);
420         }
421
422
423
424 /*
425  * return the key associated with a command
426  */
427 char keycmd(char *cmdstr)
428 {
429         int a;
430
431         for (a=0; a<strlen(cmdstr); ++a)
432                 if (cmdstr[a]=='&')
433                         return(tolower(cmdstr[a+1]));
434         return(0);
435         }
436
437
438 /*
439  * Output the string from a key command without the ampersand
440  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
441  */
442 char *cmd_expand(char *strbuf, int mode)
443 {
444         int a;
445         static char exp[64];
446         char buf[256];
447                 
448         strcpy(exp,strbuf);
449         
450         for (a=0; a<strlen(exp); ++a) {
451                 if (strbuf[a] == '&') {
452
453                     if (mode == 0) {
454                         strcpy(&exp[a],&exp[a+1]);
455                         }
456
457                     if (mode == 1) {
458                         exp[a] = '<';
459                         strcpy(buf,&exp[a+2]);
460                         exp[a+2] = '>';
461                         exp[a+3] = 0;
462                         strcat(exp,buf);
463                         }
464
465                     }
466
467                 if (!strncmp(&exp[a],"^r",2)) {
468                         strcpy(buf,exp);
469                         strcpy(&exp[a],room_name);
470                         strcat(exp,&buf[a+2]);
471                         }
472
473                 if (!strncmp(&exp[a],"^c",2)) {
474                         exp[a] = ',';
475                         strcpy(&exp[a+1],&exp[a+2]);
476                         }
477
478                 }
479
480         return(exp);
481         }
482
483
484
485 /*
486  * Comparison function to determine if entered commands match a
487  * command loaded from the config file.
488  */
489 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
490 {
491         int a;
492         int cmdax;
493
494         cmdax = 0;
495         if (is_room_aide) cmdax = 1;
496         if (axlevel >=6) cmdax = 2;
497
498         for (a=0; a<ncomp; ++a) {
499                 if ( (tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
500                    || (cptr->c_axlevel > cmdax) )
501                         return(0);
502                 }
503         return(1);
504         }
505
506
507 /*
508  * This function returns 1 if a given command requires a string input
509  */
510 int requires_string(struct citcmd *cptr, int ncomp)
511 {
512         int a;
513         char buf[64];
514         
515         strcpy(buf,cptr->c_keys[ncomp-1]);
516         for (a=0; a<strlen(buf); ++a) {
517                 if (buf[a]==':') return(1);
518                 }
519         return(0);
520         }
521
522
523 /*
524  * Input a command at the main prompt.
525  * This function returns an integer command number.  If the command prompts
526  * for a string then it is placed in the supplied buffer.
527  */
528 int getcmd(char *argbuf)
529 {
530         char cmdbuf[5];
531         int cmdspaces[5];
532         int cmdpos;
533         int ch;
534         int a;
535         int got;
536         int this_lazy_cmd;
537         struct citcmd *cptr;
538
539         /* if we're running in idiot mode, display a cute little menu */
540         IFNEXPERT formout("mainmenu");
541
542         print_express(); /* print express messages if there are any */
543         strcpy(argbuf, "");
544         cmdpos = 0;
545         for (a=0; a<5; ++a) cmdbuf[a]=0;
546         /* now the room prompt... */
547         ok_to_interrupt = 1;
548         printf("\n%s%c ",room_name,room_prompt(room_flags));
549         fflush(stdout);
550         
551         while(1) {
552                 ch = inkey();
553                 ok_to_interrupt = 0;
554
555                 /* Handle the backspace key, but only if there's something
556                  * to backspace over...
557                  */
558                 if ( (ch == 8) && (cmdpos > 0) ) {
559                         back(cmdspaces[cmdpos-1] + 1);
560                         cmdbuf[cmdpos] = 0;
561                         --cmdpos;
562                         }
563
564                 /* Spacebar invokes "lazy traversal" commands */
565                 if ( (ch == 32) && (cmdpos == 0) ) {
566                         this_lazy_cmd = next_lazy_cmd;
567                         if (this_lazy_cmd == 13) next_lazy_cmd = 5;
568                         if (this_lazy_cmd == 5) next_lazy_cmd = 13;
569                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
570                                 if (cptr->c_cmdnum == this_lazy_cmd) {
571                                         for (a=0; a<5; ++a)
572                                             if (cptr->c_keys[a][0] != 0)
573                                                 printf("%s ", cmd_expand(
574                                                         cptr->c_keys[a], 0));
575                                         printf("\n");
576                                         return(this_lazy_cmd);
577                                         }
578                                 }
579                         printf("\n");
580                         return(this_lazy_cmd);
581                         }
582
583                 /* Otherwise, process the command */
584                 cmdbuf[cmdpos] = tolower(ch);
585
586                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
587                         if (cmdmatch(cmdbuf,cptr,cmdpos+1)) {
588                                 
589                                 printf("%s",cmd_expand(cptr->c_keys[cmdpos],0));
590                                 cmdspaces[cmdpos] = strlen(
591                                         cmd_expand(cptr->c_keys[cmdpos],0) );
592                                 if (cmdpos<4) 
593                                         if ((cptr->c_keys[cmdpos+1]) != 0)
594                                                 putc(' ',stdout);
595                                 ++cmdpos;
596                                 }
597                         }
598
599                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
600                         if (cmdmatch(cmdbuf,cptr,5)) {
601                                 /* We've found our command. */
602                                 if (requires_string(cptr,cmdpos)) {
603                                         getline(argbuf,32);
604                                         }
605                                 else {
606                                         printf("\n");
607                                         }
608
609                                 /* If this command is one that changes rooms,
610                                  * then the next lazy-command (space bar)
611                                  * should be "read new" instead of "goto"
612                                  */
613                                 if ((cptr->c_cmdnum==5)
614                                         ||(cptr->c_cmdnum==6)
615                                         ||(cptr->c_cmdnum==47)
616                                         ||(cptr->c_cmdnum==52)
617                                         ||(cptr->c_cmdnum==16)
618                                         ||(cptr->c_cmdnum==20))
619                                                 next_lazy_cmd = 13;
620
621                                 return(cptr->c_cmdnum);
622
623                                 }
624                         }
625
626                 if (ch == '?') {
627                         printf("\rOne of ...                         \n");
628                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
629                             if (cmdmatch(cmdbuf,cptr,cmdpos)) {
630                                 for (a=0; a<5; ++a) {
631                                     printf("%s ",cmd_expand(cptr->c_keys[a],1));
632                                     }
633                                 printf("\n");
634                                 }
635                             }
636
637                         printf("\n%s%c ",room_name,room_prompt(room_flags));
638                         got = 0;
639                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
640                             if ((got==0)&&(cmdmatch(cmdbuf,cptr,cmdpos))) {
641                                 for (a=0; a<cmdpos; ++a) {
642                                     printf("%s ",
643                                         cmd_expand(cptr->c_keys[a],0));
644                                     }
645                                 got = 1;
646                                 }
647                             }
648                         }
649
650                 }
651
652         }
653
654
655
656
657
658 /*
659  * set tty modes.  commands are:
660  * 
661  * 0 - set to bbs mode, intr/quit disabled
662  * 1 - set to bbs mode, intr/quit enabled
663  * 2 - save current settings for later restoral
664  * 3 - restore saved settings
665  */
666 #ifdef HAVE_TERMIOS_H
667 void sttybbs(int cmd)           /* SysV version of sttybbs() */
668          {
669         struct termios live;
670         static struct termios saved_settings;
671         static int last_cmd = 0;
672
673         if (cmd == SB_LAST)
674                 cmd = last_cmd;
675         else
676                 last_cmd = cmd;
677
678         if ( (cmd == 0) || (cmd == 1) ) {
679                 tcgetattr(0,&live);
680                 live.c_iflag=ISTRIP|IXON|IXANY;
681                 live.c_oflag=OPOST|ONLCR;
682                 live.c_lflag=ISIG|NOFLSH;
683
684                 if (cmd==SB_YES_INTR) {
685                         live.c_cc[VINTR]=NEXT_KEY;
686                         live.c_cc[VQUIT]=STOP_KEY;
687                         signal(SIGINT,*sighandler);
688                         signal(SIGQUIT,*sighandler);
689                         }
690                 else {
691                         signal(SIGINT,SIG_IGN);
692                         signal(SIGQUIT,SIG_IGN);
693                         live.c_cc[VINTR]=(-1);
694                         live.c_cc[VQUIT]=(-1);
695                         }
696
697                 /* do we even need this stuff anymore? */
698                 /* live.c_line=0; */
699                 live.c_cc[VERASE]=8;
700                 live.c_cc[VKILL]=24;
701                 live.c_cc[VEOF]=1;
702                 live.c_cc[VEOL]=255;
703                 live.c_cc[VEOL2]=0;
704                 live.c_cc[VSTART]=0;
705                 tcsetattr(0,TCSADRAIN,&live);
706                 }
707         if (cmd == 2) {
708                 tcgetattr(0,&saved_settings);
709                 }
710         if (cmd == 3) {
711                 tcsetattr(0,TCSADRAIN,&saved_settings);
712                 }
713         }
714 #else
715 void sttybbs(int cmd)           /* BSD version of sttybbs() */
716 {
717         struct sgttyb live;
718         static struct sgttyb saved_settings;
719
720         if ( (cmd == 0) || (cmd == 1) ) {
721                 gtty(0,&live);
722                 live.sg_flags |= CBREAK;
723                 live.sg_flags |= CRMOD;
724                 live.sg_flags |= NL1;
725                 live.sg_flags &= ~ECHO;
726                 if (cmd==1) live.sg_flags |= NOFLSH;
727                 stty(0,&live);
728                 }
729         if (cmd == 2) {
730                 gtty(0,&saved_settings);
731                 }
732         if (cmd == 3) {
733                 stty(0,&saved_settings);
734                 }
735         }
736 #endif
737
738
739 /*
740  * display_help()  -  help file viewer
741  */
742 void display_help(char *name)
743 {
744         formout(name);
745         }
746
747
748 /*
749  * fmout()  -  Citadel text formatter and paginator
750  */
751 int fmout(int width, FILE *fp, char pagin, int height, int starting_lp, char subst)
752                         /* screen width to use */
753                         /* file to read from, or NULL to read from server */
754                         /* nonzero if we should use the paginator */
755                         /* screen height to use */
756                         /* starting value for lines_printed, -1 for global */
757                         /* nonzero if we should use hypertext mode */
758         {
759         int a,b,c,d,old;
760         int real = (-1);
761         char aaa[140];
762         char buffer[512];
763         int eof_flag = 0;
764
765         if (starting_lp >= 0) { 
766                 lines_printed = starting_lp;
767                 }
768         strcpy(aaa,""); old=255;
769         strcpy(buffer,"");
770         c=1; /* c is the current pos */
771
772         sigcaught = 0;
773         sttybbs(1);
774
775 FMTA:   while ( (eof_flag==0) && (strlen(buffer)<126) ) {
776                 if (sigcaught) goto OOPS;
777                 if (fp!=NULL) { /* read from file */
778                         if (feof(fp)) eof_flag = 1;
779                         if (eof_flag==0) {
780                                 a=getc(fp);
781                                 buffer[strlen(buffer)+1] = 0;
782                                 buffer[strlen(buffer)] = a;
783                                 }
784                         }
785                 else {          /* read from server */
786                         d=strlen(buffer);
787                         serv_gets(&buffer[d]);
788 while ( (!isspace(buffer[d])) && (isspace(buffer[strlen(buffer)-1])) )
789         buffer[strlen(buffer)-1]=0;
790                         if (!strcmp(&buffer[d],"000")) {
791                                 buffer[d] = 0;
792                                 eof_flag = 1;
793                                 while(isspace(buffer[strlen(buffer)-1]))
794                                         buffer[strlen(buffer)-1] = 0;
795                                 }
796                         d=strlen(buffer);
797                         buffer[d] = 10;
798                         buffer[d+1] = 0;
799                         }
800                 }
801
802         buffer[strlen(buffer)+1] = 0;
803         a=buffer[0];
804         strcpy(buffer,&buffer[1]);
805         
806         old=real;
807         real=a;
808         if (a<=0) goto FMTEND;
809         
810         if ( ((a==13)||(a==10)) && (old!=13) && (old!=10) ) a=32;
811         if ( ((old==13)||(old==10)) && (isspace(real)) ) {
812                 printf("\n");
813                 ++lines_printed;
814                 lines_printed = checkpagin(lines_printed,pagin,height);
815                 c=1;
816                 }
817         if (a>126) goto FMTA;
818
819         if (a>32) {
820         if ( ((strlen(aaa)+c)>(width-5)) && (strlen(aaa)>(width-5)) ) {
821                 printf("\n%s",aaa); c=strlen(aaa); aaa[0]=0;
822                 ++lines_printed;
823                 lines_printed = checkpagin(lines_printed,pagin,height);
824                 }
825             b=strlen(aaa); aaa[b]=a; aaa[b+1]=0;
826             }
827         if (a==32) {
828                 if ((strlen(aaa)+c)>(width-5)) { 
829                         c=1;
830                         printf("\n");
831                         ++lines_printed;
832                         lines_printed = checkpagin(lines_printed,pagin,height);
833                         }
834                 printf("%s ",aaa); ++c; c=c+strlen(aaa);
835                 strcpy(aaa,"");
836                 goto FMTA;
837                 }
838         if ((a==13)||(a==10)) {
839                 printf("%s\n",aaa);
840                 c=1;
841                 ++lines_printed;
842                 lines_printed = checkpagin(lines_printed,pagin,height);
843                 strcpy(aaa,"");
844                 goto FMTA;
845                 }
846         goto FMTA;
847
848         /* signal caught; drain the server */
849 OOPS:   do {
850                 serv_gets(aaa);
851                 } while(strcmp(aaa,"000"));
852
853 FMTEND: printf("\n");
854         ++lines_printed;
855         lines_printed = checkpagin(lines_printed,pagin,height);
856         return(sigcaught);
857 }
858
859
860 /*
861  * support ANSI color if defined
862  */
863 void color(int colornum)
864 {
865         if (enable_color) {
866                 printf("\033[3%dm", colornum);
867                 /* printf("\033[1m"); */ /* uncomment for bold colours */
868                 fflush(stdout);
869                 }
870         }
871
872 void cls(int colornum) {
873         if (enable_color) {
874                 printf("\033[4%dm\033[2J\033[H", colornum);
875                 fflush(stdout);
876                 }
877         }
878
879
880 /*
881  * Detect whether ANSI color is available (answerback)
882  */
883 void send_ansi_detect(void) {
884         if (rc_ansi_color == 2) {
885                 printf("\033[c");
886                 fflush(stdout);
887                 time(&AnsiDetect);
888                 }
889         }
890
891 void look_for_ansi(void) {
892         fd_set rfds;
893         struct timeval tv;
894         char abuf[512];
895         time_t now;
896         int a;
897
898         if (rc_ansi_color == 0) {
899                 enable_color = 0;
900                 }
901         else if (rc_ansi_color == 1) {
902                 enable_color = 1;
903                 }
904
905         /* otherwise, do the auto-detect */
906
907         strcpy(abuf, "");
908
909         time(&now);
910         if ( (now - AnsiDetect) < 2 ) sleep(1);
911
912         do {
913                 FD_ZERO(&rfds);
914                 FD_SET(0,&rfds);
915                 tv.tv_sec = 0;
916                 tv.tv_usec = 1;
917
918                 select(1, &rfds, NULL, NULL, &tv);
919                 if (FD_ISSET(0, &rfds)) {
920                         abuf[strlen(abuf)+1] = 0;
921                         read(0, &abuf[strlen(abuf)], 1);
922                         }
923
924                 } while (FD_ISSET(0, &rfds));
925
926         for (a=0; a<strlen(abuf); ++a) {
927                 if ( (abuf[a] == 27) && (abuf[a+1] == '[')
928                    && (abuf[a+2] == '?') ) {
929                         enable_color = 1;
930                         }
931                 }
932         }