* Disable any use of curses on Darwin (this is temporary until I beat
[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, unsigned int pagin, unsigned 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 #ifndef DISABLE_CURSES
503 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
504                 if (a == ERR)
505                         a = 0;
506 #endif
507 #endif
508
509         } while (a == 0);
510         return (a);
511 }
512
513
514 int yesno(void)
515 {                               /* Returns 1 for yes, 0 for no */
516         int a;
517         while (1) {
518                 a = inkey();
519                 a = tolower(a);
520                 if (a == 'y') {
521                         scr_printf("Yes\n");
522                         return (1);
523                 }
524                 if (a == 'n') {
525                         scr_printf("No\n");
526                         return (0);
527                 }
528         }
529 }
530
531 /* Returns 1 for yes, 0 for no, arg is default value */
532 int yesno_d(int d)
533 {
534         int a;
535         while (1) {
536                 a = inkey();
537                 a = tolower(a);
538                 if (a == 10)
539                         a = (d ? 'y' : 'n');
540                 if (a == 'y') {
541                         scr_printf("Yes\n");
542                         return (1);
543                 }
544                 if (a == 'n') {
545                         scr_printf("No\n");
546                         return (0);
547                 }
548         }
549 }
550
551
552
553
554 /* Gets a line from the terminal */
555 /* string == Pointer to string buffer */
556 /* lim == Maximum length - if negative, no-show */
557 void getline(char *string, int lim) 
558 {
559         int a, b;
560         char flag = 0;
561
562         if (lim < 0) {
563                 lim = (0 - lim);
564                 flag = 1;
565         }
566         strcpy(string, "");
567         gl_string = string;
568         async_ka_start();
569       GLA:a = inkey();
570         a = (a & 127);
571         if ((a == 8 || a == 23) && (strlen(string) == 0))
572                 goto GLA;
573         if ((a != 10) && (a != 8) && (strlen(string) == lim))
574                 goto GLA;
575         if ((a == 8) && (string[0] != 0)) {
576                 string[strlen(string) - 1] = 0;
577                 scr_putc(8); scr_putc(32); scr_putc(8);
578                 goto GLA;
579         }
580         if ((a == 23) && (string[0] != 0)) {
581                 do {
582                         string[strlen(string) - 1] = 0;
583                         scr_putc(8); scr_putc(32); scr_putc(8);
584                 } while (strlen(string) && string[strlen(string) - 1] != ' ');
585                 goto GLA;
586         }
587         if ((a == 10)) {
588                 scr_putc(10);
589                 async_ka_end();
590                 return;
591         }
592         if (a < 32)
593                 a = '.';
594         b = strlen(string);
595         string[b] = a;
596         string[b + 1] = 0;
597         if (flag == 0)
598                 scr_putc(a);
599         if (flag == 1)
600                 scr_putc('*');
601         goto GLA;
602 }
603
604
605 /*
606  * strprompt()  -  prompt for a string, print the existing value and
607  *                 allow the user to press return to keep it...
608  */
609 void strprompt(char *prompt, char *str, int len)
610 {
611         int i;
612         char buf[128];
613
614         print_express();
615         color(DIM_WHITE);
616         scr_printf("%s ", prompt);
617         color(DIM_MAGENTA);
618         scr_printf("[");
619         color(BRIGHT_MAGENTA);
620
621         if (len >= 0) {
622                 scr_printf("%s", str);
623         }
624         else {
625                 for (i=0; i<strlen(str); ++i) {
626                         scr_printf("*");
627                 }
628         }
629
630         color(DIM_MAGENTA);
631         scr_printf("]");
632         color(DIM_WHITE);
633         scr_printf(": ");
634         color(BRIGHT_CYAN);
635         getline(buf, len);
636         if (buf[0] != 0)
637                 strcpy(str, buf);
638         color(DIM_WHITE);
639 }
640
641 /*
642  * boolprompt()  -  prompt for a yes/no, print the existing value and
643  *                  allow the user to press return to keep it...
644  */
645 int boolprompt(char *prompt, int prev_val)
646 {
647         int r;
648
649         color(DIM_WHITE);
650         scr_printf("%s ", prompt);
651         color(DIM_MAGENTA);
652         scr_printf("[");
653         color(BRIGHT_MAGENTA);
654         scr_printf("%s", (prev_val ? "Yes" : "No"));
655         color(DIM_MAGENTA);
656         scr_printf("]: ");
657         color(BRIGHT_CYAN);
658         r = (yesno_d(prev_val));
659         color(DIM_WHITE);
660         return r;
661 }
662
663 /* 
664  * intprompt()  -  like strprompt(), except for an integer
665  *                 (note that it RETURNS the new value!)
666  */
667 int intprompt(char *prompt, int ival, int imin, int imax)
668 {
669         char buf[16];
670         int i;
671         int p;
672
673         do {
674                 i = ival;
675                 snprintf(buf, sizeof buf, "%d", i);
676                 strprompt(prompt, buf, 15);
677                 i = atoi(buf);
678                 for (p=0; p<strlen(buf); ++p) {
679                         if ( (!isdigit(buf[p]))
680                            && ( (buf[p]!='-') || (p!=0) )  )
681                                 i = imin - 1;
682                 }
683                 if (i < imin)
684                         scr_printf("*** Must be no less than %d.\n", imin);
685                 if (i > imax)
686                         scr_printf("*** Must be no more than %d.\n", imax);
687         } while ((i < imin) || (i > imax));
688         return (i);
689 }
690
691 /* 
692  * newprompt()  -  prompt for a string with no existing value
693  *                 (clears out string buffer first)
694  */
695 void newprompt(char *prompt, char *str, int len)
696 {
697         color(BRIGHT_MAGENTA);
698         scr_printf("%s", prompt);
699         color(DIM_MAGENTA);
700         getline(str, len);
701         color(DIM_WHITE);
702 }
703
704
705 int lkey(void)
706 {                               /* returns a lower case value */
707         int a;
708         a = inkey();
709         if (isupper(a))
710                 a = tolower(a);
711         return (a);
712 }
713
714 /*
715  * parse the citadel.rc file
716  */
717 void load_command_set(void)
718 {
719         FILE *ccfile;
720         char buf[1024];
721         struct citcmd *cptr;
722         struct citcmd *lastcmd = NULL;
723         int a, d;
724         int b = 0;
725
726
727         /* first, set up some defaults for non-required variables */
728
729         strcpy(editor_path, "");
730         strcpy(printcmd, "");
731         strcpy(rc_username, "");
732         strcpy(rc_password, "");
733         rc_floor_mode = 0;
734         rc_exp_beep = 1;
735         rc_allow_attachments = 0;
736         rc_remember_passwords = 0;
737         strcpy(rc_exp_cmd, "");
738         rc_display_message_numbers = 0;
739         rc_force_mail_prompts = 0;
740         rc_ansi_color = 0;
741         strcpy(rc_url_cmd, "");
742         strcpy(rc_gotmail_cmd, "");
743 #ifdef HAVE_OPENSSL
744         rc_encrypt = RC_DEFAULT;
745 #endif
746 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
747         rc_screen = RC_DEFAULT;
748 #endif
749         rc_alt_semantics = 0;
750
751         /* now try to open the citadel.rc file */
752
753         ccfile = NULL;
754         if (getenv("HOME") != NULL) {
755                 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
756                 ccfile = fopen(buf, "r");
757         }
758         if (ccfile == NULL) {
759                 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
760                 ccfile = fopen(buf, "r");
761         }
762         if (ccfile == NULL) {
763                 ccfile = fopen("/etc/citadel.rc", "r");
764         }
765         if (ccfile == NULL) {
766                 ccfile = fopen("./citadel.rc", "r");
767         }
768         if (ccfile == NULL) {
769                 perror("commands: cannot open citadel.rc");
770                 logoff(NULL, 3);
771         }
772         while (fgets(buf, sizeof buf, ccfile) != NULL) {
773                 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
774                         buf[strlen(buf) - 1] = 0;
775
776                 if (!strncasecmp(buf, "encrypt=", 8)) {
777                         if (!strcasecmp(&buf[8], "yes")) {
778 #ifdef HAVE_OPENSSL
779                                 rc_encrypt = RC_YES;
780 #else
781                                 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
782                                 logoff(NULL, 3);
783 #endif
784                         }
785 #ifdef HAVE_OPENSSL
786                         else if (!strcasecmp(&buf[8], "no")) {
787                                 rc_encrypt = RC_NO;
788                         }
789                         else if (!strcasecmp(&buf[8], "default")) {
790                                 rc_encrypt = RC_DEFAULT;
791                         }
792 #endif
793                 }
794
795 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
796                 if (!strncasecmp(buf, "fullscreen=", 11)) {
797                         if (!strcasecmp(&buf[11], "yes"))
798                                 rc_screen = RC_YES;
799                         else if (!strcasecmp(&buf[11], "no"))
800                                 rc_screen = RC_NO;
801                 }
802 #endif
803
804                 if (!strncasecmp(buf, "editor=", 7))
805                         strcpy(editor_path, &buf[7]);
806
807                 if (!strncasecmp(buf, "printcmd=", 9))
808                         strcpy(printcmd, &buf[9]);
809
810                 if (!strncasecmp(buf, "expcmd=", 7))
811                         strcpy(rc_exp_cmd, &buf[7]);
812
813                 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
814                         have_xterm = (char) atoi(&buf[24]);
815
816                 if (!strncasecmp(buf, "use_floors=", 11)) {
817                         if (!strcasecmp(&buf[11], "yes"))
818                                 rc_floor_mode = RC_YES;
819                         if (!strcasecmp(&buf[11], "no"))
820                                 rc_floor_mode = RC_NO;
821                         if (!strcasecmp(&buf[11], "default"))
822                                 rc_floor_mode = RC_DEFAULT;
823                 }
824                 if (!strncasecmp(buf, "beep=", 5)) {
825                         rc_exp_beep = atoi(&buf[5]);
826                 }
827                 if (!strncasecmp(buf, "allow_attachments=", 18)) {
828                         rc_allow_attachments = atoi(&buf[18]);
829                 }
830                 if (!strncasecmp(buf, "idle_threshold=", 15)) {
831                         rc_idle_threshold = atoi(&buf[15]);
832                 }
833                 if (!strncasecmp(buf, "remember_passwords=", 19)) {
834                         rc_remember_passwords = atoi(&buf[19]);
835                 }
836                 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
837                         rc_display_message_numbers = atoi(&buf[24]);
838                 }
839                 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
840                         rc_force_mail_prompts = atoi(&buf[19]);
841                 }
842                 if (!strncasecmp(buf, "ansi_color=", 11)) {
843                         if (!strncasecmp(&buf[11], "on", 2))
844                                 rc_ansi_color = 1;
845                         if (!strncasecmp(&buf[11], "auto", 4))
846                                 rc_ansi_color = 2;      /* autodetect */
847                         if (!strncasecmp(&buf[11], "user", 4))
848                                 rc_ansi_color = 3;      /* user config */
849                 }
850                 if (!strncasecmp(buf, "prompt_control=", 15)) {
851                         if (!strncasecmp(&buf[15], "on", 2))
852                                 rc_prompt_control = 1;
853                         if (!strncasecmp(&buf[15], "user", 4))
854                                 rc_prompt_control = 3;  /* user config */
855                 }
856                 if (!strncasecmp(buf, "username=", 9))
857                         strcpy(rc_username, &buf[9]);
858
859                 if (!strncasecmp(buf, "password=", 9))
860                         strcpy(rc_password, &buf[9]);
861
862                 if (!strncasecmp(buf, "urlcmd=", 7))
863                         strcpy(rc_url_cmd, &buf[7]);
864
865                 if (!strncasecmp(buf, "gotmailcmd=", 11))
866                         strcpy(rc_gotmail_cmd, &buf[11]);
867
868                 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
869                         if (!strncasecmp(&buf[11], "yes", 3))
870                                 rc_alt_semantics = 1;
871                         if (!strncasecmp(&buf[11], "no", 2))
872                                 rc_alt_semantics = 0;
873                 }
874                 if (!strncasecmp(buf, "cmd=", 4)) {
875                         strcpy(buf, &buf[4]);
876
877                         cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
878
879                         cptr->c_cmdnum = atoi(buf);
880                         for (d = strlen(buf); d >= 0; --d)
881                                 if (buf[d] == ',')
882                                         b = d;
883                         strcpy(buf, &buf[b + 1]);
884
885                         cptr->c_axlevel = atoi(buf);
886                         for (d = strlen(buf); d >= 0; --d)
887                                 if (buf[d] == ',')
888                                         b = d;
889                         strcpy(buf, &buf[b + 1]);
890
891                         for (a = 0; a < 5; ++a)
892                                 cptr->c_keys[a][0] = 0;
893
894                         a = 0;
895                         b = 0;
896                         buf[strlen(buf) + 1] = 0;
897                         while (strlen(buf) > 0) {
898                                 b = strlen(buf);
899                                 for (d = strlen(buf); d >= 0; --d)
900                                         if (buf[d] == ',')
901                                                 b = d;
902                                 strncpy(cptr->c_keys[a], buf, b);
903                                 cptr->c_keys[a][b] = 0;
904                                 if (buf[b] == ',')
905                                         strcpy(buf, &buf[b + 1]);
906                                 else
907                                         strcpy(buf, "");
908                                 ++a;
909                         }
910
911                         cptr->next = NULL;
912                         if (cmdlist == NULL)
913                                 cmdlist = cptr;
914                         else
915                                 lastcmd->next = cptr;
916                         lastcmd = cptr;
917                 }
918         }
919         fclose(ccfile);
920 }
921
922
923
924 /*
925  * return the key associated with a command
926  */
927 char keycmd(char *cmdstr)
928 {
929         int a;
930
931         for (a = 0; a < strlen(cmdstr); ++a)
932                 if (cmdstr[a] == '&')
933                         return (tolower(cmdstr[a + 1]));
934         return (0);
935 }
936
937
938 /*
939  * Output the string from a key command without the ampersand
940  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
941  */
942 char *cmd_expand(char *strbuf, int mode)
943 {
944         int a;
945         static char exp[64];
946         char buf[1024];
947
948         strcpy(exp, strbuf);
949
950         for (a = 0; a < strlen(exp); ++a) {
951                 if (strbuf[a] == '&') {
952
953                         if (mode == 0) {
954                                 strcpy(&exp[a], &exp[a + 1]);
955                         }
956                         if (mode == 1) {
957                                 exp[a] = '<';
958                                 strcpy(buf, &exp[a + 2]);
959                                 exp[a + 2] = '>';
960                                 exp[a + 3] = 0;
961                                 strcat(exp, buf);
962                         }
963                 }
964                 if (!strncmp(&exp[a], "^r", 2)) {
965                         strcpy(buf, exp);
966                         strcpy(&exp[a], room_name);
967                         strcat(exp, &buf[a + 2]);
968                 }
969                 if (!strncmp(&exp[a], "^c", 2)) {
970                         exp[a] = ',';
971                         strcpy(&exp[a + 1], &exp[a + 2]);
972                 }
973         }
974
975         return (exp);
976 }
977
978
979
980 /*
981  * Comparison function to determine if entered commands match a
982  * command loaded from the config file.
983  */
984 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
985 {
986         int a;
987         int cmdax;
988
989         cmdax = 0;
990         if (is_room_aide)
991                 cmdax = 1;
992         if (axlevel >= 6)
993                 cmdax = 2;
994
995         for (a = 0; a < ncomp; ++a) {
996                 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
997                     || (cptr->c_axlevel > cmdax))
998                         return (0);
999         }
1000         return (1);
1001 }
1002
1003
1004 /*
1005  * This function returns 1 if a given command requires a string input
1006  */
1007 int requires_string(struct citcmd *cptr, int ncomp)
1008 {
1009         int a;
1010         char buf[64];
1011
1012         strcpy(buf, cptr->c_keys[ncomp - 1]);
1013         for (a = 0; a < strlen(buf); ++a) {
1014                 if (buf[a] == ':')
1015                         return (1);
1016         }
1017         return (0);
1018 }
1019
1020
1021 /*
1022  * Input a command at the main prompt.
1023  * This function returns an integer command number.  If the command prompts
1024  * for a string then it is placed in the supplied buffer.
1025  */
1026 int getcmd(CtdlIPC *ipc, char *argbuf)
1027 {
1028         char cmdbuf[5];
1029         int cmdspaces[5];
1030         int cmdpos;
1031         int ch;
1032         int a;
1033         int got;
1034         int this_lazy_cmd;
1035         struct citcmd *cptr;
1036
1037         /*
1038          * Starting a new command now, so set sigcaught to 0.  This variable
1039          * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1040          * been interrupted by a keypress.
1041          */
1042         sigcaught = 0;
1043
1044         /* Switch color support on or off if we're in user mode */
1045         if (rc_ansi_color == 3) {
1046                 if (userflags & US_COLOR)
1047                         enable_color = 1;
1048                 else
1049                         enable_color = 0;
1050         }
1051         /* if we're running in idiot mode, display a cute little menu */
1052         IFNEXPERT formout(ipc, "mainmenu");
1053
1054         print_express();
1055         strcpy(argbuf, "");
1056         cmdpos = 0;
1057         for (a = 0; a < 5; ++a)
1058                 cmdbuf[a] = 0;
1059         /* now the room prompt... */
1060         ok_to_interrupt = 1;
1061         color(BRIGHT_WHITE);
1062         scr_printf("\n%s", room_name);
1063         color(DIM_WHITE);
1064         scr_printf("%c ", room_prompt(room_flags));
1065         scr_flush();
1066
1067         while (1) {
1068                 ch = inkey();
1069                 ok_to_interrupt = 0;
1070
1071                 /* Handle the backspace key, but only if there's something
1072                  * to backspace over...
1073                  */
1074                 if ((ch == 8) && (cmdpos > 0)) {
1075                         back(cmdspaces[cmdpos - 1] + 1);
1076                         cmdbuf[cmdpos] = 0;
1077                         --cmdpos;
1078                 }
1079                 /* Spacebar invokes "lazy traversal" commands */
1080                 if ((ch == 32) && (cmdpos == 0)) {
1081                         this_lazy_cmd = next_lazy_cmd;
1082                         if (this_lazy_cmd == 13)
1083                                 next_lazy_cmd = 5;
1084                         if (this_lazy_cmd == 5)
1085                                 next_lazy_cmd = 13;
1086                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1087                                 if (cptr->c_cmdnum == this_lazy_cmd) {
1088                                         for (a = 0; a < 5; ++a)
1089                                                 if (cptr->c_keys[a][0] != 0)
1090                                                         scr_printf("%s ", cmd_expand(
1091                                                                                         cptr->c_keys[a], 0));
1092                                         scr_printf("\n");
1093                                         return (this_lazy_cmd);
1094                                 }
1095                         }
1096                         scr_printf("\n");
1097                         return (this_lazy_cmd);
1098                 }
1099                 /* Otherwise, process the command */
1100                 cmdbuf[cmdpos] = tolower(ch);
1101
1102                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1103                         if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1104
1105                                 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1106                                 cmdspaces[cmdpos] = strlen(
1107                                     cmd_expand(cptr->c_keys[cmdpos], 0));
1108                                 if (cmdpos < 4)
1109                                         if ((cptr->c_keys[cmdpos + 1]) != 0)
1110                                                 scr_putc(' ');
1111                                 ++cmdpos;
1112                         }
1113                 }
1114
1115                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1116                         if (cmdmatch(cmdbuf, cptr, 5)) {
1117                                 /* We've found our command. */
1118                                 if (requires_string(cptr, cmdpos)) {
1119                                         getline(argbuf, 32);
1120                                 } else {
1121                                         scr_printf("\n");
1122                                 }
1123
1124                                 /* If this command is one that changes rooms,
1125                                  * then the next lazy-command (space bar)
1126                                  * should be "read new" instead of "goto"
1127                                  */
1128                                 if ((cptr->c_cmdnum == 5)
1129                                     || (cptr->c_cmdnum == 6)
1130                                     || (cptr->c_cmdnum == 47)
1131                                     || (cptr->c_cmdnum == 52)
1132                                     || (cptr->c_cmdnum == 16)
1133                                     || (cptr->c_cmdnum == 20))
1134                                         next_lazy_cmd = 13;
1135
1136                                 return (cptr->c_cmdnum);
1137
1138                         }
1139                 }
1140
1141                 if (ch == '?') {
1142                         pprintf("\rOne of ...                         \n");
1143                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1144                                 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1145                                         for (a = 0; a < 5; ++a) {
1146                                                 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1147                                         }
1148                                         pprintf("\n");
1149                                 }
1150                         }
1151
1152                         pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1153                         got = 0;
1154                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1155                                 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1156                                         for (a = 0; a < cmdpos; ++a) {
1157                                                 pprintf("%s ",
1158                                                        cmd_expand(cptr->c_keys[a], 0));
1159                                         }
1160                                         got = 1;
1161                                 }
1162                         }
1163                 }
1164         }
1165
1166 }
1167
1168
1169
1170
1171
1172 /*
1173  * set tty modes.  commands are:
1174  * 
1175  * 01- set to bbs mode
1176  * 2 - save current settings for later restoral
1177  * 3 - restore saved settings
1178  */
1179 #ifdef HAVE_TERMIOS_H
1180 void sttybbs(int cmd)
1181 {                               /* SysV version of sttybbs() */
1182         struct termios live;
1183         static struct termios saved_settings;
1184         static int last_cmd = 0;
1185
1186         if (cmd == SB_LAST)
1187                 cmd = last_cmd;
1188         else
1189                 last_cmd = cmd;
1190
1191         if ((cmd == 0) || (cmd == 1)) {
1192                 tcgetattr(0, &live);
1193                 live.c_iflag = ISTRIP | IXON | IXANY;
1194                 live.c_oflag = OPOST | ONLCR;
1195                 live.c_lflag = ISIG | NOFLSH;
1196
1197                 live.c_cc[VINTR] = 0;
1198                 live.c_cc[VQUIT] = 0;
1199
1200 #ifdef hpux
1201                 live.c_cc[VMIN] = 0;
1202                 live.c_cc[VTIME] = 0;
1203 #endif
1204
1205                 /* do we even need this stuff anymore? */
1206                 /* live.c_line=0; */
1207                 live.c_cc[VERASE] = 8;
1208                 live.c_cc[VKILL] = 24;
1209                 live.c_cc[VEOF] = 1;
1210                 live.c_cc[VEOL] = 255;
1211                 live.c_cc[VEOL2] = 0;
1212                 live.c_cc[VSTART] = 0;
1213                 tcsetattr(0, TCSADRAIN, &live);
1214         }
1215         if (cmd == 2) {
1216                 tcgetattr(0, &saved_settings);
1217         }
1218         if (cmd == 3) {
1219                 tcsetattr(0, TCSADRAIN, &saved_settings);
1220         }
1221 }
1222 #else
1223 void sttybbs(int cmd)
1224 {                               /* BSD version of sttybbs() */
1225         struct sgttyb live;
1226         static struct sgttyb saved_settings;
1227         static int last_cmd = 0;
1228
1229         if (cmd == SB_LAST)
1230                 cmd = last_cmd;
1231         else
1232                 last_cmd = cmd;
1233
1234         if ((cmd == 0) || (cmd == 1)) {
1235                 gtty(0, &live);
1236                 live.sg_flags |= CBREAK;
1237                 live.sg_flags |= CRMOD;
1238                 live.sg_flags |= NL1;
1239                 live.sg_flags &= ~ECHO;
1240                 if (cmd == 1)
1241                         live.sg_flags |= NOFLSH;
1242                 stty(0, &live);
1243         }
1244         if (cmd == 2) {
1245                 gtty(0, &saved_settings);
1246         }
1247         if (cmd == 3) {
1248                 stty(0, &saved_settings);
1249         }
1250 }
1251 #endif
1252
1253
1254 /*
1255  * display_help()  -  help file viewer
1256  */
1257 void display_help(CtdlIPC *ipc, char *name)
1258 {
1259         formout(ipc, name);
1260 }
1261
1262
1263 /*
1264  * fmout()  -  Citadel text formatter and paginator
1265  */
1266 int fmout(
1267         int width,      /* screen width to use */
1268         FILE *fpin,     /* file to read from, or NULL to format given text */
1269         char *text,     /* Text to be formatted (when fpin is NULL) */
1270         FILE *fpout,    /* File to write to, or NULL to write to screen */
1271         char pagin,     /* nonzero if we should use the paginator */
1272         int height,     /* screen height to use */
1273         int starting_lp,/* starting value for lines_printed, -1 for global */
1274         int subst)      /* nonzero if we should use hypertext mode */
1275 {
1276         int a, b, c, old;
1277         int real = (-1);
1278         char aaa[140];
1279         char buffer[512];
1280         char *e;
1281         int eof_flag = 0;
1282
1283         num_urls = 0;   /* Start with a clean slate of embedded URL's */
1284
1285         if (starting_lp >= 0) {
1286                 lines_printed = starting_lp;
1287         }
1288         strcpy(aaa, "");
1289         old = 255;
1290         strcpy(buffer, "");
1291         c = 1;                  /* c is the current pos */
1292         e = text;               /* e is pointer to current pos */
1293
1294 FMTA:   while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1295                 if (fpin != NULL) {     /* read from file */
1296                         if (feof(fpin))
1297                                 eof_flag = 1;
1298                         if (eof_flag == 0) {
1299                                 a = getc(fpin);
1300                                 buffer[strlen(buffer) + 1] = 0;
1301                                 buffer[strlen(buffer)] = a;
1302                         }
1303                 } else {        /* read from text */
1304                         if (!*e) {
1305                                 eof_flag = 1;
1306                                 while (isspace(buffer[strlen(buffer) - 1]))
1307                                         buffer[strlen(buffer) - 1] = 0;
1308                                 buffer[strlen(buffer) + 1] = 0;
1309                                 buffer[strlen(buffer)] = 10;
1310                         }
1311                         if (eof_flag == 0) {
1312                                 a = *e++;
1313                                 buffer[strlen(buffer) + 1] = 0;
1314                                 buffer[strlen(buffer)] = a;
1315                         }
1316                 }
1317         }
1318
1319         if ( (!strncasecmp(buffer, "http://", 7))
1320            || (!strncasecmp(buffer, "ftp://", 6)) ) {
1321                 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1322                 for (a=0; a<strlen(urls[num_urls]); ++a) {
1323                         b = urls[num_urls][a];
1324                         if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1325                            || (b==13) || (b==9) || (b=='\"') )
1326                                 urls[num_urls][a] = 0;
1327                 }
1328                 ++num_urls;
1329         }
1330
1331         buffer[strlen(buffer) + 1] = 0;
1332         a = buffer[0];
1333         strcpy(buffer, &buffer[1]);
1334
1335         old = real;
1336         real = a;
1337         if (a <= 0)
1338                 goto FMTEND;
1339
1340         if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1341                 a = 32;
1342         if (((old == 13) || (old == 10)) && (isspace(real))) {
1343                 if (fpout) {
1344                         fprintf(fpout, "\n");
1345                 } else {
1346                         scr_printf("\n");
1347                         ++lines_printed;
1348                         lines_printed = checkpagin(lines_printed, pagin, height);
1349                 }
1350                 c = 1;
1351         }
1352         if (a > 126)
1353                 goto FMTA;
1354
1355         if (a > 32) {
1356                 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1357                         if (fpout) {
1358                                 fprintf(fpout, "\n%s", aaa);
1359                         } else {
1360                                 scr_printf("\n%s", aaa);
1361                                 ++lines_printed;
1362                                 lines_printed = checkpagin(lines_printed, pagin, height);
1363                         }
1364                         c = strlen(aaa);
1365                         aaa[0] = 0;
1366                 }
1367                 b = strlen(aaa);
1368                 aaa[b] = a;
1369                 aaa[b + 1] = 0;
1370         }
1371         if (a == 32) {
1372                 if ((strlen(aaa) + c) > (width - 1)) {
1373                         c = 1;
1374                         if (fpout) {
1375                                 fprintf(fpout, "\n");
1376                         } else {
1377                                 scr_printf("\n");
1378                                 ++lines_printed;
1379                                 lines_printed = checkpagin(lines_printed, pagin, height);
1380                         }
1381                 }
1382                 if (fpout) {
1383                         fprintf(fpout, "%s ", aaa);
1384                 } else {
1385                         scr_printf("%s ", aaa);
1386                 }
1387                 ++c;
1388                 c = c + strlen(aaa);
1389                 strcpy(aaa, "");
1390                 goto FMTA;
1391         }
1392         if ((a == 13) || (a == 10)) {
1393                 if (fpout) {
1394                         fprintf(fpout, "%s\n", aaa);
1395                 } else {
1396                         scr_printf("%s\n", aaa);
1397                         ++lines_printed;
1398                         lines_printed = checkpagin(lines_printed, pagin, height);
1399                 }
1400                 c = 1;
1401                 if (sigcaught) goto OOPS;
1402                 strcpy(aaa, "");
1403                 goto FMTA;
1404         }
1405         goto FMTA;
1406
1407         /* keypress caught; drain the server */
1408 OOPS:   /* do {
1409                 CtdlIPC_getline(ipc, aaa);
1410         } while (strcmp(aaa, "000")); */
1411
1412 FMTEND:
1413         if (fpout) {
1414                 fprintf(fpout, "\n");
1415         } else {
1416                 scr_printf("\n");
1417                 ++lines_printed;
1418                 lines_printed = checkpagin(lines_printed, pagin, height);
1419         }
1420         return (sigcaught);
1421 }
1422
1423
1424 /*
1425  * support ANSI color if defined
1426  */
1427 void color(int colornum)
1428 {
1429         static int is_bold = 0;
1430         static int hold_color, current_color;
1431
1432         if (colornum == COLOR_PUSH) {
1433                 hold_color = current_color;
1434                 return;
1435         }
1436
1437         if (colornum == COLOR_POP) {
1438                 color(hold_color);
1439                 return;
1440         }
1441
1442         current_color = colornum;
1443         if (enable_color) {
1444 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
1445                 if (scr_color(colornum))
1446                         return;
1447 #endif
1448                 /* When switching to dim white, actually output an 'original
1449                  * pair' sequence -- this looks better on black-on-white
1450                  * terminals. - Changed to ORIGINAL_PAIR as this actually
1451                  * wound up looking horrible on black-on-white terminals, not
1452                  * to mention transparent terminals.
1453                  */
1454                 if (colornum == ORIGINAL_PAIR)
1455                         printf("\033[39;49m");
1456                 else
1457                         printf("\033[3%d;40m", (colornum & 7));
1458
1459                 if ((colornum >= 8) && (is_bold == 0)) {
1460                         printf("\033[1m");
1461                         is_bold = 1;
1462                 } else if ((colornum < 8) && (is_bold == 1)) {
1463                         printf("\033[0m");
1464                         is_bold = 0;
1465                 }
1466                 scr_flush();
1467         }
1468 }
1469
1470 void cls(int colornum)
1471 {
1472         if (enable_color) {
1473                 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1474                 scr_flush();
1475         }
1476 }
1477
1478
1479 /*
1480  * Detect whether ANSI color is available (answerback)
1481  */
1482 void send_ansi_detect(void)
1483 {
1484         if (rc_ansi_color == 2) {
1485                 printf("\033[c");
1486                 scr_flush();
1487                 time(&AnsiDetect);
1488         }
1489 }
1490
1491 void look_for_ansi(void)
1492 {
1493         fd_set rfds;
1494         struct timeval tv;
1495         char abuf[512];
1496         time_t now;
1497         int a;
1498
1499         if (rc_ansi_color == 0) {
1500                 enable_color = 0;
1501         } else if (rc_ansi_color == 1) {
1502                 enable_color = 1;
1503         } else if (rc_ansi_color == 2) {
1504
1505                 /* otherwise, do the auto-detect */
1506
1507                 strcpy(abuf, "");
1508
1509                 time(&now);
1510                 if ((now - AnsiDetect) < 2)
1511                         sleep(1);
1512
1513                 do {
1514                         FD_ZERO(&rfds);
1515                         FD_SET(0, &rfds);
1516                         tv.tv_sec = 0;
1517                         tv.tv_usec = 1;
1518
1519                         select(1, &rfds, NULL, NULL, &tv);
1520                         if (FD_ISSET(0, &rfds)) {
1521                                 abuf[strlen(abuf) + 1] = 0;
1522                                 read(0, &abuf[strlen(abuf)], 1);
1523                         }
1524                 } while (FD_ISSET(0, &rfds));
1525
1526                 for (a = 0; a < strlen(abuf); ++a) {
1527                         if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1528                             && (abuf[a + 2] == '?')) {
1529                                 enable_color = 1;
1530                         }
1531                 }
1532         }
1533 }
1534
1535
1536 /*
1537  * Display key options (highlight hotkeys inside angle brackets)
1538  */
1539 void keyopt(char *buf) {
1540         int i;
1541
1542         color(DIM_WHITE);
1543         for (i=0; i<strlen(buf); ++i) {
1544                 if (buf[i]=='<') {
1545                         scr_putc(buf[i]);
1546                         color(BRIGHT_MAGENTA);
1547                 } else {
1548                         if (buf[i]=='>') {
1549                                 color(DIM_WHITE);
1550                         }
1551                         scr_putc(buf[i]);
1552                 }
1553         }
1554         color(DIM_WHITE);
1555 }
1556
1557
1558
1559 /*
1560  * Present a key-menu line choice type of thing
1561  */
1562 char keymenu(char *menuprompt, char *menustring) {
1563         int i, c, a;
1564         int choices;
1565         int do_prompt = 0;
1566         char buf[1024];
1567         int ch;
1568         int display_prompt = 1;
1569
1570         choices = num_tokens(menustring, '|');
1571
1572         if (menuprompt != NULL) do_prompt = 1;
1573         if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1574
1575         while (1) {
1576                 if (display_prompt) {
1577                         if (do_prompt) {
1578                                 scr_printf("%s ", menuprompt);
1579                         } 
1580                         else {
1581                                 for (i=0; i<choices; ++i) {
1582                                         extract(buf, menustring, i);
1583                                         keyopt(buf);
1584                                         scr_printf(" ");
1585                                 }
1586                         }
1587                         scr_printf(" -> ");
1588                         display_prompt = 0;
1589                 }
1590                 ch = lkey();
1591         
1592                 if ( (do_prompt) && (ch=='?') ) {
1593                         scr_printf("\rOne of...                               ");
1594                         scr_printf("                                      \n");
1595                         for (i=0; i<choices; ++i) {
1596                                 extract(buf, menustring, i);
1597                                 scr_printf("   ");
1598                                 keyopt(buf);
1599                                 scr_printf("\n");
1600                         }
1601                         scr_printf("\n");
1602                         display_prompt = 1;
1603                 }
1604
1605                 for (i=0; i<choices; ++i) {
1606                         extract(buf, menustring, i);
1607                         for (c=1; c<strlen(buf); ++c) {
1608                                 if ( (ch == tolower(buf[c]))
1609                                    && (buf[c-1]=='<')
1610                                    && (buf[c+1]=='>') ) {
1611                                         for (a=0; a<strlen(buf); ++a) {
1612                                                 if ( (a!=(c-1)) && (a!=(c+1))) {
1613                                                         scr_putc(buf[a]);
1614                                                 }
1615                                         }
1616                                         scr_printf("\n\n");
1617                                         return ch;
1618                                 }
1619                         }
1620                 }
1621         }
1622 }