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