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