]> code.citadel.org Git - citadel.git/blob - citadel/commands.c
* Changed all the "200 ok" responses to more descriptive strings
[citadel.git] / citadel / commands.c
1 /*
2  * $Id$
3  *
4  * This file contains functions which implement parts of the
5  * text-mode user interface.
6  *
7  */
8
9 #include "sysdep.h"
10 #include <stdlib.h>
11 #include <unistd.h>
12 #include <stdio.h>
13 #include <fcntl.h>
14 #include <ctype.h>
15 #include <string.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18
19 #ifdef HAVE_TERMIOS_H
20 #include <termios.h>
21 #else
22 #include <sgtty.h>
23 #endif
24
25 #ifdef HAVE_SYS_SELECT_H
26 #include <sys/select.h>
27 #endif
28
29 #ifdef THREADED_CLIENT
30 #include <pthread.h>
31 #endif
32
33 #include <signal.h>
34 #include <errno.h>
35 #include <stdarg.h>
36 #include "citadel.h"
37 #include "commands.h"
38 #include "messages.h"
39 #include "citadel_decls.h"
40 #include "routines.h"
41 #include "routines2.h"
42 #include "tools.h"
43 #include "rooms.h"
44 #ifndef HAVE_SNPRINTF
45 #include "snprintf.h"
46 #endif
47
48 struct citcmd {
49         struct citcmd *next;
50         int c_cmdnum;
51         int c_axlevel;
52         char c_keys[5][64];
53 };
54
55 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
56
57
58 int rc_exp_beep;
59 char rc_exp_cmd[256];
60 int rc_allow_attachments;
61 int rc_display_message_numbers;
62 int rc_force_mail_prompts;
63 int rc_remember_passwords;
64 int rc_ansi_color;
65 int num_urls = 0;
66 char urls[MAXURLS][256];
67 char rc_url_cmd[256];
68
69 char *gl_string;
70 int next_lazy_cmd = 5;
71
72 int lines_printed = 0;          /* line count for paginator */
73 extern int screenwidth, screenheight;
74
75 struct citcmd *cmdlist = NULL;
76
77
78 /* these variables are local to this module */
79 char keepalives_enabled = KA_YES;       /* send NOOPs to server when idle */
80 int ok_to_interrupt = 0;                /* print express msgs asynchronously */
81 time_t AnsiDetect;                      /* when did we send the detect code? */
82 int enable_color = 0;                   /* nonzero for ANSI color */
83
84
85
86
87 /*
88  * If an interesting key has been pressed, return its value, otherwise 0
89  */
90 char was_a_key_pressed(void) {
91         fd_set rfds;
92         struct timeval tv;
93         int the_character;
94         int retval;
95
96         FD_ZERO(&rfds);
97         FD_SET(0, &rfds);
98         tv.tv_sec = 0;
99         tv.tv_usec = 0;
100         retval = select(1, &rfds, NULL, NULL, &tv); 
101
102         if (FD_ISSET(0, &rfds)) {
103                 the_character = inkey();
104         }
105         else {
106                 the_character = 0;
107         }
108         return(the_character);
109 }
110
111
112
113
114
115 /*
116  * Check to see if we need to pause at the end of a screen.
117  * If we do, we have to disable server keepalives during the pause because
118  * we are probably in the middle of a server operation and the NOOP command
119  * would confuse everything.
120  */
121 int checkpagin(int lp, int pagin, int height)
122 {
123         int thekey;
124
125         if (sigcaught) return(lp);
126         thekey = was_a_key_pressed();
127         if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
128         if (sigcaught) return(lp);
129
130         if (!pagin) return(0);
131         if (lp>=(height-1)) {
132                 set_keepalives(KA_NO);
133                 hit_any_key();
134                 set_keepalives(KA_YES);
135                 return(0);
136                 }
137         return(lp);
138         }
139
140
141
142
143 /*
144  * pprintf()  ...   paginated version of printf()
145  */
146 void pprintf(const char *format, ...) {   
147         va_list arg_ptr;
148         static char buf[4096];  /* static for performance, change if needed */
149         int i;
150
151         /* If sigcaught is nonzero, a keypress has interrupted this and we
152          * should just drain output.
153          */
154         if (sigcaught) return;
155  
156         /* Otherwise, start spewing... */ 
157         va_start(arg_ptr, format);   
158         vsprintf(buf, format, arg_ptr);   
159         va_end(arg_ptr);   
160
161         for (i=0; i<strlen(buf); ++i) {
162                 putc(buf[i], stdout);
163                 if (buf[i]==10) {
164                         ++lines_printed;
165                         lines_printed = checkpagin(lines_printed,
166                                 (userflags & US_PAGINATOR),
167                                 screenheight);
168                 }
169         }
170 }   
171
172
173
174 /*
175  * print_express()  -  print express messages if there are any
176  */
177 void print_express(void)
178 {
179         char buf[256];
180         FILE *outpipe;
181
182         if (express_msgs == 0)
183                 return;
184         express_msgs = 0;
185         serv_puts("PEXP");
186         serv_gets(buf);
187         if (buf[0] != '1')
188                 return;
189
190         if (strlen(rc_exp_cmd) > 0) {
191                 outpipe = popen(rc_exp_cmd, "w");
192                 if (outpipe != NULL) {
193                         while (serv_gets(buf), strcmp(buf, "000")) {
194                                 fprintf(outpipe, "%s\n", buf);
195                         }
196                         pclose(outpipe);
197                         return;
198                 }
199         }
200         /* fall back to built-in express message display */
201         if (rc_exp_beep) {
202                 putc(7, stdout);
203         }
204         color(BRIGHT_RED);
205         printf("\r---\n");
206         while (serv_gets(buf), strcmp(buf, "000")) {
207                 printf("%s\n", buf);
208         }
209         printf("---\n");
210         color(BRIGHT_WHITE);
211 }
212
213
214 void set_keepalives(int s)
215 {
216         keepalives_enabled = (char) s;
217 }
218
219 /* 
220  * This loop handles the "keepalive" messages sent to the server when idling.
221  */
222
223 static time_t idlet = 0;
224 static void really_do_keepalive(void) {
225         char buf[256];
226
227         time(&idlet);
228         if (keepalives_enabled == KA_YES) {
229                 serv_puts("NOOP");
230                 serv_gets(buf);
231                 if (buf[3] == '*') {
232                         express_msgs = 1;
233                         if (ok_to_interrupt == 1) {
234                                 printf("\r%64s\r", "");
235                                 print_express();
236                                 printf("%s%c ", room_name,
237                                        room_prompt(room_flags));
238                                 fflush(stdout);
239                         }
240                 }
241         }
242 }
243
244 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
245    encapsulated interface; in theory there should be no need to touch these
246    globals outside of the async_ka_* functions. */
247
248 #ifdef THREADED_CLIENT
249 static pthread_t ka_thr_handle;
250 static int ka_thr_active = 0;
251 static int async_ka_enabled = 0;
252
253 static void *ka_thread(void *arg)
254 {
255         really_do_keepalive();
256         pthread_detach(ka_thr_handle);
257         ka_thr_active = 0;
258         return NULL;
259 }
260
261 /* start up a thread to handle a keepalive in the background */
262 static void async_ka_exec(void)
263 {
264         if (!ka_thr_active) {
265                 ka_thr_active = 1;
266                 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
267                         perror("pthread_create");
268                         exit(1);
269                 }
270         }
271 }
272 #endif /* THREADED_CLIENT */
273
274 static void do_keepalive(void)
275 {
276         time_t now;
277
278         time(&now);
279         if ((now - idlet) < ((long) S_KEEPALIVE))
280                 return;
281
282         /* Do a space-backspace to keep telnet sessions from idling out */
283         printf(" %c", 8);
284         fflush(stdout);
285
286 #ifdef THREADED_CLIENT
287         if (async_ka_enabled)
288                 async_ka_exec();
289         else
290 #endif
291                 really_do_keepalive();
292 }
293
294
295 /* Now the actual async-keepalve API that we expose to higher levels:
296    async_ka_start() and async_ka_end(). These do nothing when we don't have
297    threading enabled, so we avoid sprinkling ifdef's throughout the code. */
298
299 /* wait for a background keepalive to complete. this must be done before
300    attempting any further server requests! */
301 void async_ka_end(void)
302 {
303 #ifdef THREADED_CLIENT
304         if (ka_thr_active)
305                 pthread_join(ka_thr_handle, NULL);
306
307         async_ka_enabled--;
308 #endif
309 }
310
311 /* tell do_keepalive() that keepalives are asynchronous. */
312 void async_ka_start(void)
313 {
314 #ifdef THREADED_CLIENT
315         async_ka_enabled++;
316 #endif
317 }
318
319
320 int inkey(void)
321 {                               /* get a character from the keyboard, with   */
322         int a;                  /* the watchdog timer in effect if necessary */
323         fd_set rfds;
324         struct timeval tv;
325         time_t start_time, now;
326         char inbuf[2];
327
328         fflush(stdout);
329         lines_printed = 0;
330         time(&start_time);
331
332         do {
333
334                 /* This loop waits for keyboard input.  If the keepalive
335                  * timer expires, it sends a keepalive to the server if
336                  * necessary and then waits again.
337                  */
338                 do {
339                         do_keepalive();
340
341                         FD_ZERO(&rfds);
342                         FD_SET(0, &rfds);
343                         tv.tv_sec = S_KEEPALIVE;
344                         tv.tv_usec = 0;
345
346                         time(&now);
347                         if (((now - start_time) > SLEEPING)
348                             && (SLEEPING != 0) && (getppid() == 1)) {
349                                 printf("Sleeping? Call again.\n");
350                                 logoff(SIGALRM);
351                         }
352                         select(1, &rfds, NULL, NULL, &tv);
353                 } while (!FD_ISSET(0, &rfds));
354
355
356
357
358                 /* At this point, there's input, so fetch it.
359                  * (There's a hole in the bucket...)
360                  */
361                 read(0, inbuf, 1);
362                 a = inbuf[0];
363                 if (a == 127)
364                         a = 8;
365                 if (a > 126)
366                         a = 0;
367                 if (a == 10)
368                         a = 13;
369                 if (((a != 4) && (a != 13) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
370                     && ((a < 32) || (a > 126)))
371                         a = 0;
372         } while (a == 0);
373         return (a);
374 }
375
376
377 int yesno(void)
378 {                               /* Returns 1 for yes, 0 for no */
379         int a;
380         while (1) {
381                 a = inkey();
382                 a = tolower(a);
383                 if (a == 'y') {
384                         printf("Yes\n");
385                         return (1);
386                 }
387                 if (a == 'n') {
388                         printf("No\n");
389                         return (0);
390                 }
391         }
392 }
393
394 /* Returns 1 for yes, 0 for no, arg is default value */
395 int yesno_d(int d)
396 {
397         int a;
398         while (1) {
399                 a = inkey();
400                 a = tolower(a);
401                 if (a == 13)
402                         a = (d ? 'y' : 'n');
403                 if (a == 'y') {
404                         printf("Yes\n");
405                         return (1);
406                 }
407                 if (a == 'n') {
408                         printf("No\n");
409                         return (0);
410                 }
411         }
412 }
413
414
415
416
417 /* Gets a line from the terminal */
418 /* string == Pointer to string buffer */
419 /* lim == Maximum length - if negative, no-show */
420 void getline(char *string, int lim) 
421 {
422         int a, b;
423         char flag = 0;
424
425         if (lim < 0) {
426                 lim = (0 - lim);
427                 flag = 1;
428         }
429         strcpy(string, "");
430         gl_string = string;
431         async_ka_start();
432       GLA:a = inkey();
433         a = (a & 127);
434         if ((a == 8) && (strlen(string) == 0))
435                 goto GLA;
436         if ((a != 13) && (a != 8) && (strlen(string) == lim))
437                 goto GLA;
438         if ((a == 8) && (string[0] != 0)) {
439                 string[strlen(string) - 1] = 0;
440                 putc(8, stdout);
441                 putc(32, stdout);
442                 putc(8, stdout);
443                 goto GLA;
444         }
445         if ((a == 13) || (a == 10)) {
446                 putc(13, stdout);
447                 putc(10, stdout);
448                 async_ka_end();
449                 return;
450         }
451         if (a < 32)
452                 a = '.';
453         b = strlen(string);
454         string[b] = a;
455         string[b + 1] = 0;
456         if (flag == 0)
457                 putc(a, stdout);
458         if (flag == 1)
459                 putc('*', stdout);
460         goto GLA;
461 }
462
463
464 /*
465  * strprompt()  -  prompt for a string, print the existing value and
466  *                 allow the user to press return to keep it...
467  */
468 void strprompt(char *prompt, char *str, int len)
469 {
470         char buf[128];
471         print_express();
472         color(DIM_WHITE);
473         printf("%s ", prompt);
474         color(DIM_MAGENTA);
475         printf("[");
476         color(BRIGHT_MAGENTA);
477         printf("%s", str);
478         color(DIM_MAGENTA);
479         printf("]");
480         color(DIM_WHITE);
481         printf(": ");
482         color(BRIGHT_CYAN);
483         getline(buf, len);
484         if (buf[0] != 0)
485                 strcpy(str, buf);
486         color(DIM_WHITE);
487 }
488
489 /*
490  * boolprompt()  -  prompt for a yes/no, print the existing value and
491  *                  allow the user to press return to keep it...
492  */
493 int boolprompt(char *prompt, int prev_val)
494 {
495         int r;
496
497         color(DIM_WHITE);
498         printf("%s ", prompt);
499         color(DIM_MAGENTA);
500         printf(" [");
501         color(BRIGHT_MAGENTA);
502         printf("%s", (prev_val ? "Yes" : "No"));
503         color(DIM_MAGENTA);
504         printf("]: ");
505         color(BRIGHT_CYAN);
506         r = (yesno_d(prev_val));
507         color(DIM_WHITE);
508         return r;
509 }
510
511 /* 
512  * intprompt()  -  like strprompt(), except for an integer
513  *                 (note that it RETURNS the new value!)
514  */
515 int intprompt(char *prompt, int ival, int imin, int imax)
516 {
517         char buf[16];
518         int i;
519         int p;
520
521         do {
522                 i = ival;
523                 snprintf(buf, sizeof buf, "%d", i);
524                 strprompt(prompt, buf, 15);
525                 i = atoi(buf);
526                 for (p=0; p<strlen(buf); ++p) {
527                         if ( (!isdigit(buf[p]))
528                            && ( (buf[p]!='-') || (p!=0) )  )
529                                 i = imin - 1;
530                 }
531                 if (i < imin)
532                         printf("*** Must be no less than %d.\n", imin);
533                 if (i > imax)
534                         printf("*** Must be no more than %d.\n", imax);
535         } while ((i < imin) || (i > imax));
536         return (i);
537 }
538
539 /* 
540  * newprompt()  -  prompt for a string with no existing value
541  *                 (clears out string buffer first)
542  */
543 void newprompt(char *prompt, char *str, int len)
544 {
545         color(BRIGHT_MAGENTA);
546         printf("%s", prompt);
547         color(DIM_MAGENTA);
548         getline(str, len);
549         color(DIM_WHITE);
550 }
551
552
553 int lkey(void)
554 {                               /* returns a lower case value */
555         int a;
556         a = inkey();
557         if (isupper(a))
558                 a = tolower(a);
559         return (a);
560 }
561
562 /*
563  * parse the citadel.rc file
564  */
565 void load_command_set(void)
566 {
567         FILE *ccfile;
568         char buf[256];
569         struct citcmd *cptr;
570         struct citcmd *lastcmd = NULL;
571         int a, d;
572         int b = 0;
573
574
575         /* first, set up some defaults for non-required variables */
576
577         strcpy(editor_path, "");
578         strcpy(printcmd, "");
579         strcpy(rc_username, "");
580         strcpy(rc_password, "");
581         rc_floor_mode = 0;
582         rc_exp_beep = 1;
583         rc_allow_attachments = 0;
584         rc_remember_passwords = 0;
585         strcpy(rc_exp_cmd, "");
586         rc_display_message_numbers = 0;
587         rc_force_mail_prompts = 0;
588         rc_ansi_color = 0;
589         strcpy(rc_url_cmd, "");
590
591         /* now try to open the citadel.rc file */
592
593         ccfile = NULL;
594         if (getenv("HOME") != NULL) {
595                 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
596                 ccfile = fopen(buf, "r");
597         }
598         if (ccfile == NULL) {
599                 ccfile = fopen("/usr/local/lib/citadel.rc", "r");
600         }
601         if (ccfile == NULL) {
602                 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
603                 ccfile = fopen(buf, "r");
604         }
605         if (ccfile == NULL) {
606                 ccfile = fopen("./citadel.rc", "r");
607         }
608         if (ccfile == NULL) {
609                 perror("commands: cannot open citadel.rc");
610                 logoff(errno);
611         }
612         while (fgets(buf, 256, ccfile) != NULL) {
613                 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
614                         buf[strlen(buf) - 1] = 0;
615
616                 if (!strncasecmp(buf, "editor=", 7))
617                         strcpy(editor_path, &buf[7]);
618
619                 if (!strncasecmp(buf, "printcmd=", 9))
620                         strcpy(printcmd, &buf[9]);
621
622                 if (!strncasecmp(buf, "expcmd=", 7))
623                         strcpy(rc_exp_cmd, &buf[7]);
624
625                 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
626                         have_xterm = (char) atoi(&buf[24]);
627
628                 if (!strncasecmp(buf, "use_floors=", 11)) {
629                         if (!strcasecmp(&buf[11], "yes"))
630                                 rc_floor_mode = RC_YES;
631                         if (!strcasecmp(&buf[11], "no"))
632                                 rc_floor_mode = RC_NO;
633                         if (!strcasecmp(&buf[11], "default"))
634                                 rc_floor_mode = RC_DEFAULT;
635                 }
636                 if (!strncasecmp(buf, "beep=", 5)) {
637                         rc_exp_beep = atoi(&buf[5]);
638                 }
639                 if (!strncasecmp(buf, "allow_attachments=", 18)) {
640                         rc_allow_attachments = atoi(&buf[18]);
641                 }
642                 if (!strncasecmp(buf, "remember_passwords=", 19)) {
643                         rc_remember_passwords = atoi(&buf[19]);
644                 }
645                 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
646                         rc_display_message_numbers = atoi(&buf[24]);
647                 }
648                 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
649                         rc_force_mail_prompts = atoi(&buf[19]);
650                 }
651                 if (!strncasecmp(buf, "ansi_color=", 11)) {
652                         if (!strncasecmp(&buf[11], "on", 2))
653                                 rc_ansi_color = 1;
654                         if (!strncasecmp(&buf[11], "auto", 4))
655                                 rc_ansi_color = 2;      /* autodetect */
656                         if (!strncasecmp(&buf[11], "user", 4))
657                                 rc_ansi_color = 3;      /* user config */
658                 }
659                 if (!strncasecmp(buf, "username=", 9))
660                         strcpy(rc_username, &buf[9]);
661
662                 if (!strncasecmp(buf, "password=", 9))
663                         strcpy(rc_password, &buf[9]);
664
665                 if (!strncasecmp(buf, "urlcmd=", 7))
666                         strcpy(rc_url_cmd, &buf[7]);
667
668                 if (!strncasecmp(buf, "cmd=", 4)) {
669                         strcpy(buf, &buf[4]);
670
671                         cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
672
673                         cptr->c_cmdnum = atoi(buf);
674                         for (d = strlen(buf); d >= 0; --d)
675                                 if (buf[d] == ',')
676                                         b = d;
677                         strcpy(buf, &buf[b + 1]);
678
679                         cptr->c_axlevel = atoi(buf);
680                         for (d = strlen(buf); d >= 0; --d)
681                                 if (buf[d] == ',')
682                                         b = d;
683                         strcpy(buf, &buf[b + 1]);
684
685                         for (a = 0; a < 5; ++a)
686                                 cptr->c_keys[a][0] = 0;
687
688                         a = 0;
689                         b = 0;
690                         buf[strlen(buf) + 1] = 0;
691                         while (strlen(buf) > 0) {
692                                 b = strlen(buf);
693                                 for (d = strlen(buf); d >= 0; --d)
694                                         if (buf[d] == ',')
695                                                 b = d;
696                                 strncpy(cptr->c_keys[a], buf, b);
697                                 cptr->c_keys[a][b] = 0;
698                                 if (buf[b] == ',')
699                                         strcpy(buf, &buf[b + 1]);
700                                 else
701                                         strcpy(buf, "");
702                                 ++a;
703                         }
704
705                         cptr->next = NULL;
706                         if (cmdlist == NULL)
707                                 cmdlist = cptr;
708                         else
709                                 lastcmd->next = cptr;
710                         lastcmd = cptr;
711                 }
712         }
713         fclose(ccfile);
714 }
715
716
717
718 /*
719  * return the key associated with a command
720  */
721 char keycmd(char *cmdstr)
722 {
723         int a;
724
725         for (a = 0; a < strlen(cmdstr); ++a)
726                 if (cmdstr[a] == '&')
727                         return (tolower(cmdstr[a + 1]));
728         return (0);
729 }
730
731
732 /*
733  * Output the string from a key command without the ampersand
734  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
735  */
736 char *cmd_expand(char *strbuf, int mode)
737 {
738         int a;
739         static char exp[64];
740         char buf[256];
741
742         strcpy(exp, strbuf);
743
744         for (a = 0; a < strlen(exp); ++a) {
745                 if (strbuf[a] == '&') {
746
747                         if (mode == 0) {
748                                 strcpy(&exp[a], &exp[a + 1]);
749                         }
750                         if (mode == 1) {
751                                 exp[a] = '<';
752                                 strcpy(buf, &exp[a + 2]);
753                                 exp[a + 2] = '>';
754                                 exp[a + 3] = 0;
755                                 strcat(exp, buf);
756                         }
757                 }
758                 if (!strncmp(&exp[a], "^r", 2)) {
759                         strcpy(buf, exp);
760                         strcpy(&exp[a], room_name);
761                         strcat(exp, &buf[a + 2]);
762                 }
763                 if (!strncmp(&exp[a], "^c", 2)) {
764                         exp[a] = ',';
765                         strcpy(&exp[a + 1], &exp[a + 2]);
766                 }
767         }
768
769         return (exp);
770 }
771
772
773
774 /*
775  * Comparison function to determine if entered commands match a
776  * command loaded from the config file.
777  */
778 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
779 {
780         int a;
781         int cmdax;
782
783         cmdax = 0;
784         if (is_room_aide)
785                 cmdax = 1;
786         if (axlevel >= 6)
787                 cmdax = 2;
788
789         for (a = 0; a < ncomp; ++a) {
790                 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
791                     || (cptr->c_axlevel > cmdax))
792                         return (0);
793         }
794         return (1);
795 }
796
797
798 /*
799  * This function returns 1 if a given command requires a string input
800  */
801 int requires_string(struct citcmd *cptr, int ncomp)
802 {
803         int a;
804         char buf[64];
805
806         strcpy(buf, cptr->c_keys[ncomp - 1]);
807         for (a = 0; a < strlen(buf); ++a) {
808                 if (buf[a] == ':')
809                         return (1);
810         }
811         return (0);
812 }
813
814
815 /*
816  * Input a command at the main prompt.
817  * This function returns an integer command number.  If the command prompts
818  * for a string then it is placed in the supplied buffer.
819  */
820 int getcmd(char *argbuf)
821 {
822         char cmdbuf[5];
823         int cmdspaces[5];
824         int cmdpos;
825         int ch;
826         int a;
827         int got;
828         int this_lazy_cmd;
829         struct citcmd *cptr;
830
831         /*
832          * Starting a new command now, so set sigcaught to 0.  This variable
833          * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
834          * been interrupted by a keypress.
835          */
836         sigcaught = 0;
837
838         /* Switch color support on or off if we're in user mode */
839         if (rc_ansi_color == 3) {
840                 if (userflags & US_COLOR)
841                         enable_color = 1;
842                 else
843                         enable_color = 0;
844         }
845         /* if we're running in idiot mode, display a cute little menu */
846         IFNEXPERT formout("mainmenu");
847
848         print_express();        /* print express messages if there are any */
849         strcpy(argbuf, "");
850         cmdpos = 0;
851         for (a = 0; a < 5; ++a)
852                 cmdbuf[a] = 0;
853         /* now the room prompt... */
854         ok_to_interrupt = 1;
855         color(BRIGHT_WHITE);
856         printf("\n%s", room_name);
857         color(DIM_WHITE);
858         printf("%c ", room_prompt(room_flags));
859         fflush(stdout);
860
861         while (1) {
862                 ch = inkey();
863                 ok_to_interrupt = 0;
864
865                 /* Handle the backspace key, but only if there's something
866                  * to backspace over...
867                  */
868                 if ((ch == 8) && (cmdpos > 0)) {
869                         back(cmdspaces[cmdpos - 1] + 1);
870                         cmdbuf[cmdpos] = 0;
871                         --cmdpos;
872                 }
873                 /* Spacebar invokes "lazy traversal" commands */
874                 if ((ch == 32) && (cmdpos == 0)) {
875                         this_lazy_cmd = next_lazy_cmd;
876                         if (this_lazy_cmd == 13)
877                                 next_lazy_cmd = 5;
878                         if (this_lazy_cmd == 5)
879                                 next_lazy_cmd = 13;
880                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
881                                 if (cptr->c_cmdnum == this_lazy_cmd) {
882                                         for (a = 0; a < 5; ++a)
883                                                 if (cptr->c_keys[a][0] != 0)
884                                                         printf("%s ", cmd_expand(
885                                                                                         cptr->c_keys[a], 0));
886                                         printf("\n");
887                                         return (this_lazy_cmd);
888                                 }
889                         }
890                         printf("\n");
891                         return (this_lazy_cmd);
892                 }
893                 /* Otherwise, process the command */
894                 cmdbuf[cmdpos] = tolower(ch);
895
896                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
897                         if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
898
899                                 printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
900                                 cmdspaces[cmdpos] = strlen(
901                                     cmd_expand(cptr->c_keys[cmdpos], 0));
902                                 if (cmdpos < 4)
903                                         if ((cptr->c_keys[cmdpos + 1]) != 0)
904                                                 putc(' ', stdout);
905                                 ++cmdpos;
906                         }
907                 }
908
909                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
910                         if (cmdmatch(cmdbuf, cptr, 5)) {
911                                 /* We've found our command. */
912                                 if (requires_string(cptr, cmdpos)) {
913                                         getline(argbuf, 32);
914                                 } else {
915                                         printf("\n");
916                                 }
917
918                                 /* If this command is one that changes rooms,
919                                  * then the next lazy-command (space bar)
920                                  * should be "read new" instead of "goto"
921                                  */
922                                 if ((cptr->c_cmdnum == 5)
923                                     || (cptr->c_cmdnum == 6)
924                                     || (cptr->c_cmdnum == 47)
925                                     || (cptr->c_cmdnum == 52)
926                                     || (cptr->c_cmdnum == 16)
927                                     || (cptr->c_cmdnum == 20))
928                                         next_lazy_cmd = 13;
929
930                                 return (cptr->c_cmdnum);
931
932                         }
933                 }
934
935                 if (ch == '?') {
936                         printf("\rOne of ...                         \n");
937                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
938                                 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
939                                         for (a = 0; a < 5; ++a) {
940                                                 printf("%s ", cmd_expand(cptr->c_keys[a], 1));
941                                         }
942                                         printf("\n");
943                                 }
944                         }
945
946                         printf("\n%s%c ", room_name, room_prompt(room_flags));
947                         got = 0;
948                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
949                                 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
950                                         for (a = 0; a < cmdpos; ++a) {
951                                                 printf("%s ",
952                                                        cmd_expand(cptr->c_keys[a], 0));
953                                         }
954                                         got = 1;
955                                 }
956                         }
957                 }
958         }
959
960 }
961
962
963
964
965
966 /*
967  * set tty modes.  commands are:
968  * 
969  * 01- set to bbs mode
970  * 2 - save current settings for later restoral
971  * 3 - restore saved settings
972  */
973 #ifdef HAVE_TERMIOS_H
974 void sttybbs(int cmd)
975 {                               /* SysV version of sttybbs() */
976         struct termios live;
977         static struct termios saved_settings;
978         static int last_cmd = 0;
979
980         if (cmd == SB_LAST)
981                 cmd = last_cmd;
982         else
983                 last_cmd = cmd;
984
985         if ((cmd == 0) || (cmd == 1)) {
986                 tcgetattr(0, &live);
987                 live.c_iflag = ISTRIP | IXON | IXANY;
988                 live.c_oflag = OPOST | ONLCR;
989                 live.c_lflag = ISIG | NOFLSH;
990
991                 live.c_cc[VINTR] = (-1);
992                 live.c_cc[VQUIT] = (-1);
993
994                 /* do we even need this stuff anymore? */
995                 /* live.c_line=0; */
996                 live.c_cc[VERASE] = 8;
997                 live.c_cc[VKILL] = 24;
998                 live.c_cc[VEOF] = 1;
999                 live.c_cc[VEOL] = 255;
1000                 live.c_cc[VEOL2] = 0;
1001                 live.c_cc[VSTART] = 0;
1002                 tcsetattr(0, TCSADRAIN, &live);
1003         }
1004         if (cmd == 2) {
1005                 tcgetattr(0, &saved_settings);
1006         }
1007         if (cmd == 3) {
1008                 tcsetattr(0, TCSADRAIN, &saved_settings);
1009         }
1010 }
1011 #else
1012 void sttybbs(int cmd)
1013 {                               /* BSD version of sttybbs() */
1014         struct sgttyb live;
1015         static struct sgttyb saved_settings;
1016
1017         if ((cmd == 0) || (cmd == 1)) {
1018                 gtty(0, &live);
1019                 live.sg_flags |= CBREAK;
1020                 live.sg_flags |= CRMOD;
1021                 live.sg_flags |= NL1;
1022                 live.sg_flags &= ~ECHO;
1023                 if (cmd == 1)
1024                         live.sg_flags |= NOFLSH;
1025                 stty(0, &live);
1026         }
1027         if (cmd == 2) {
1028                 gtty(0, &saved_settings);
1029         }
1030         if (cmd == 3) {
1031                 stty(0, &saved_settings);
1032         }
1033 }
1034 #endif
1035
1036
1037 /*
1038  * display_help()  -  help file viewer
1039  */
1040 void display_help(char *name)
1041 {
1042         formout(name);
1043 }
1044
1045
1046 /*
1047  * fmout()  -  Citadel text formatter and paginator
1048  */
1049 int fmout(
1050         int width,      /* screen width to use */
1051         FILE *fp,       /* file to read from, or NULL to read from server */
1052         char pagin,     /* nonzero if we should use the paginator */
1053         int height,     /* screen height to use */
1054         int starting_lp,/* starting value for lines_printed, -1 for global */
1055         char subst)     /* nonzero if we should use hypertext mode */
1056 {
1057         int a, b, c, d, old;
1058         int real = (-1);
1059         char aaa[140];
1060         char buffer[512];
1061         int eof_flag = 0;
1062
1063         num_urls = 0;   /* Start with a clean slate of embedded URL's */
1064
1065         if (starting_lp >= 0) {
1066                 lines_printed = starting_lp;
1067         }
1068         strcpy(aaa, "");
1069         old = 255;
1070         strcpy(buffer, "");
1071         c = 1;                  /* c is the current pos */
1072
1073 FMTA:   while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1074                 if (fp != NULL) {       /* read from file */
1075                         if (feof(fp))
1076                                 eof_flag = 1;
1077                         if (eof_flag == 0) {
1078                                 a = getc(fp);
1079                                 buffer[strlen(buffer) + 1] = 0;
1080                                 buffer[strlen(buffer)] = a;
1081                         }
1082                 } else {        /* read from server */
1083                         d = strlen(buffer);
1084                         serv_gets(&buffer[d]);
1085                         while ((!isspace(buffer[d])) && (isspace(buffer[strlen(buffer) - 1])))
1086                                 buffer[strlen(buffer) - 1] = 0;
1087                         if (!strcmp(&buffer[d], "000")) {
1088                                 buffer[d] = 0;
1089                                 eof_flag = 1;
1090                                 while (isspace(buffer[strlen(buffer) - 1]))
1091                                         buffer[strlen(buffer) - 1] = 0;
1092                         }
1093                         d = strlen(buffer);
1094                         buffer[d] = 10;
1095                         buffer[d + 1] = 0;
1096                 }
1097         }
1098
1099         if ( (!strncasecmp(buffer, "http://", 7))
1100            || (!strncasecmp(buffer, "ftp://", 6)) ) {
1101                 safestrncpy(urls[num_urls], buffer, 255);
1102                 for (a=0; a<strlen(urls[num_urls]); ++a) {
1103                         b = urls[num_urls][a];
1104                         if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1105                            || (b==13) || (b==9) || (b=='\"') )
1106                                 urls[num_urls][a] = 0;
1107                 }
1108                 ++num_urls;
1109         }
1110
1111         buffer[strlen(buffer) + 1] = 0;
1112         a = buffer[0];
1113         strcpy(buffer, &buffer[1]);
1114
1115         old = real;
1116         real = a;
1117         if (a <= 0)
1118                 goto FMTEND;
1119
1120         if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1121                 a = 32;
1122         if (((old == 13) || (old == 10)) && (isspace(real))) {
1123                 printf("\n");
1124                 ++lines_printed;
1125                 lines_printed = checkpagin(lines_printed, pagin, height);
1126                 c = 1;
1127         }
1128         if (a > 126)
1129                 goto FMTA;
1130
1131         if (a > 32) {
1132                 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
1133                         printf("\n%s", aaa);
1134                         c = strlen(aaa);
1135                         aaa[0] = 0;
1136                         ++lines_printed;
1137                         lines_printed = checkpagin(lines_printed, pagin, height);
1138                 }
1139                 b = strlen(aaa);
1140                 aaa[b] = a;
1141                 aaa[b + 1] = 0;
1142         }
1143         if (a == 32) {
1144                 if ((strlen(aaa) + c) > (width - 5)) {
1145                         c = 1;
1146                         printf("\n");
1147                         ++lines_printed;
1148                         lines_printed = checkpagin(lines_printed, pagin, height);
1149                 }
1150                 printf("%s ", aaa);
1151                 ++c;
1152                 c = c + strlen(aaa);
1153                 strcpy(aaa, "");
1154                 goto FMTA;
1155         }
1156         if ((a == 13) || (a == 10)) {
1157                 printf("%s\n", aaa);
1158                 c = 1;
1159                 ++lines_printed;
1160                 lines_printed = checkpagin(lines_printed, pagin, height);
1161                 if (sigcaught) goto OOPS;
1162                 strcpy(aaa, "");
1163                 goto FMTA;
1164         }
1165         goto FMTA;
1166
1167         /* keypress caught; drain the server */
1168 OOPS:   do {
1169                 serv_gets(aaa);
1170         } while (strcmp(aaa, "000"));
1171
1172 FMTEND: printf("\n");
1173         ++lines_printed;
1174         lines_printed = checkpagin(lines_printed, pagin, height);
1175         return (sigcaught);
1176 }
1177
1178
1179 /*
1180  * support ANSI color if defined
1181  */
1182 void color(int colornum)
1183 {
1184         static int is_bold = 0;
1185         static int hold_color, current_color;
1186
1187         if (colornum == COLOR_PUSH) {
1188                 hold_color = current_color;
1189                 return;
1190         }
1191
1192         if (colornum == COLOR_POP) {
1193                 color(hold_color);
1194                 return;
1195         }
1196
1197         current_color = colornum;
1198         if (enable_color) {
1199                 /* When switching to dim white, actually output an 'original
1200                  * pair' sequence -- this looks better on black-on-white
1201                  * terminals.
1202                  */
1203                 if (colornum == DIM_WHITE)
1204                         printf("\033[39;49m");
1205                 else
1206                         printf("\033[3%d;40m", (colornum & 7));
1207
1208                 if ((colornum >= 8) && (is_bold == 0)) {
1209                         printf("\033[1m");
1210                         is_bold = 1;
1211                 } else if ((colornum < 8) && (is_bold == 1)) {
1212                         printf("\033[0m");
1213                         is_bold = 0;
1214                 }
1215                 fflush(stdout);
1216         }
1217 }
1218
1219 void cls(int colornum)
1220 {
1221         if (enable_color) {
1222                 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1223                 fflush(stdout);
1224         }
1225 }
1226
1227
1228 /*
1229  * Detect whether ANSI color is available (answerback)
1230  */
1231 void send_ansi_detect(void)
1232 {
1233         if (rc_ansi_color == 2) {
1234                 printf("\033[c");
1235                 fflush(stdout);
1236                 time(&AnsiDetect);
1237         }
1238 }
1239
1240 void look_for_ansi(void)
1241 {
1242         fd_set rfds;
1243         struct timeval tv;
1244         char abuf[512];
1245         time_t now;
1246         int a;
1247
1248         if (rc_ansi_color == 0) {
1249                 enable_color = 0;
1250         } else if (rc_ansi_color == 1) {
1251                 enable_color = 1;
1252         } else if (rc_ansi_color == 2) {
1253
1254                 /* otherwise, do the auto-detect */
1255
1256                 strcpy(abuf, "");
1257
1258                 time(&now);
1259                 if ((now - AnsiDetect) < 2)
1260                         sleep(1);
1261
1262                 do {
1263                         FD_ZERO(&rfds);
1264                         FD_SET(0, &rfds);
1265                         tv.tv_sec = 0;
1266                         tv.tv_usec = 1;
1267
1268                         select(1, &rfds, NULL, NULL, &tv);
1269                         if (FD_ISSET(0, &rfds)) {
1270                                 abuf[strlen(abuf) + 1] = 0;
1271                                 read(0, &abuf[strlen(abuf)], 1);
1272                         }
1273                 } while (FD_ISSET(0, &rfds));
1274
1275                 for (a = 0; a < strlen(abuf); ++a) {
1276                         if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1277                             && (abuf[a + 2] == '?')) {
1278                                 enable_color = 1;
1279                         }
1280                 }
1281         }
1282 }
1283
1284
1285 /*
1286  * Display key options (highlight hotkeys inside angle brackets)
1287  */
1288 void keyopt(char *buf) {
1289         int i;
1290
1291         color(DIM_WHITE);
1292         for (i=0; i<strlen(buf); ++i) {
1293                 if (buf[i]=='<') {
1294                         putc(buf[i], stdout);
1295                         color(BRIGHT_MAGENTA);
1296                 } else {
1297                         if (buf[i]=='>') {
1298                                 color(DIM_WHITE);
1299                         }
1300                         putc(buf[i], stdout);
1301                 }
1302         }
1303         color(DIM_WHITE);
1304 }
1305
1306
1307
1308 /*
1309  * Present a key-menu line choice type of thing
1310  */
1311 char keymenu(char *menuprompt, char *menustring) {
1312         int i, c, a;
1313         int choices;
1314         int do_prompt = 0;
1315         char buf[256];
1316         int ch;
1317         int display_prompt = 1;
1318
1319         choices = num_tokens(menustring, '|');
1320
1321         if (menuprompt != NULL) do_prompt = 1;
1322         if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1323
1324         while (1) {
1325                 if (display_prompt) {
1326                         if (do_prompt) {
1327                                 printf("%s ", menuprompt);
1328                         } 
1329                         else {
1330                                 for (i=0; i<choices; ++i) {
1331                                         extract(buf, menustring, i);
1332                                         keyopt(buf);
1333                                         printf(" ");
1334                                 }
1335                         }
1336                         printf(" -> ");
1337                         display_prompt = 0;
1338                 }
1339                 ch = lkey();
1340         
1341                 if ( (do_prompt) && (ch=='?') ) {
1342                         printf("\rOne of...                               ");
1343                         printf("                                      \n");
1344                         for (i=0; i<choices; ++i) {
1345                                 extract(buf, menustring, i);
1346                                 printf("   ");
1347                                 keyopt(buf);
1348                                 printf("\n");
1349                         }
1350                         printf("\n");
1351                         display_prompt = 1;
1352                 }
1353
1354                 for (i=0; i<choices; ++i) {
1355                         extract(buf, menustring, i);
1356                         for (c=1; c<strlen(buf); ++c) {
1357                                 if ( (ch == tolower(buf[c]))
1358                                    && (buf[c-1]=='<')
1359                                    && (buf[c+1]=='>') ) {
1360                                         for (a=0; a<strlen(buf); ++a) {
1361                                                 if ( (a!=(c-1)) && (a!=(c+1))) {
1362                                                         putc(buf[a], stdout);
1363                                                 }
1364                                         }
1365                                         printf("\n\n");
1366                                         return ch;
1367                                 }
1368                         }
1369                 }
1370         }
1371 }