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