* Replaced the DEFAULT_ENTRY definition in sysconfig.h with a new option
[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         rc_reply_extedit = 0;
751         strcpy(rc_url_cmd, "");
752         strcpy(rc_gotmail_cmd, "");
753 #ifdef HAVE_OPENSSL
754         rc_encrypt = RC_DEFAULT;
755 #endif
756 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
757         rc_screen = RC_DEFAULT;
758 #endif
759         rc_alt_semantics = 0;
760
761         /* now try to open the citadel.rc file */
762
763         ccfile = NULL;
764         if (getenv("HOME") != NULL) {
765                 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
766                 ccfile = fopen(buf, "r");
767         }
768         if (ccfile == NULL) {
769                 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
770                 ccfile = fopen(buf, "r");
771         }
772         if (ccfile == NULL) {
773                 ccfile = fopen("/etc/citadel.rc", "r");
774         }
775         if (ccfile == NULL) {
776                 ccfile = fopen("./citadel.rc", "r");
777         }
778         if (ccfile == NULL) {
779                 perror("commands: cannot open citadel.rc");
780                 logoff(NULL, 3);
781         }
782         while (fgets(buf, sizeof buf, ccfile) != NULL) {
783                 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
784                         buf[strlen(buf) - 1] = 0;
785
786                 if (!strncasecmp(buf, "encrypt=", 8)) {
787                         if (!strcasecmp(&buf[8], "yes")) {
788 #ifdef HAVE_OPENSSL
789                                 rc_encrypt = RC_YES;
790 #else
791                                 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
792                                 logoff(NULL, 3);
793 #endif
794                         }
795 #ifdef HAVE_OPENSSL
796                         else if (!strcasecmp(&buf[8], "no")) {
797                                 rc_encrypt = RC_NO;
798                         }
799                         else if (!strcasecmp(&buf[8], "default")) {
800                                 rc_encrypt = RC_DEFAULT;
801                         }
802 #endif
803                 }
804
805 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
806                 if (!strncasecmp(buf, "fullscreen=", 11)) {
807                         if (!strcasecmp(&buf[11], "yes"))
808                                 rc_screen = RC_YES;
809                         else if (!strcasecmp(&buf[11], "no"))
810                                 rc_screen = RC_NO;
811                 }
812 #endif
813
814                 if (!strncasecmp(buf, "editor=", 7))
815                         strcpy(editor_paths[0], &buf[7]);
816
817                 for (i = 0; i < MAX_EDITORS; i++)
818                 {
819                         sprintf(editor_key, "editor%d=", i);
820                         if (!strncasecmp(buf, editor_key, strlen(editor_key)))
821                                 strcpy(editor_paths[i], &buf[strlen(editor_key)]);
822                 }
823
824                 if (!strncasecmp(buf, "printcmd=", 9))
825                         strcpy(printcmd, &buf[9]);
826
827                 if (!strncasecmp(buf, "expcmd=", 7))
828                         strcpy(rc_exp_cmd, &buf[7]);
829
830                 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
831                         have_xterm = (char) atoi(&buf[24]);
832
833                 if (!strncasecmp(buf, "use_floors=", 11)) {
834                         if (!strcasecmp(&buf[11], "yes"))
835                                 rc_floor_mode = RC_YES;
836                         if (!strcasecmp(&buf[11], "no"))
837                                 rc_floor_mode = RC_NO;
838                         if (!strcasecmp(&buf[11], "default"))
839                                 rc_floor_mode = RC_DEFAULT;
840                 }
841                 if (!strncasecmp(buf, "beep=", 5)) {
842                         rc_exp_beep = atoi(&buf[5]);
843                 }
844                 if (!strncasecmp(buf, "allow_attachments=", 18)) {
845                         rc_allow_attachments = atoi(&buf[18]);
846                 }
847                 if (!strncasecmp(buf, "idle_threshold=", 15)) {
848                         rc_idle_threshold = atoi(&buf[15]);
849                 }
850                 if (!strncasecmp(buf, "remember_passwords=", 19)) {
851                         rc_remember_passwords = atoi(&buf[19]);
852                 }
853                 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
854                         rc_display_message_numbers = atoi(&buf[24]);
855                 }
856                 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
857                         rc_force_mail_prompts = atoi(&buf[19]);
858                 }
859                 if (!strncasecmp(buf, "ansi_color=", 11)) {
860                         if (!strncasecmp(&buf[11], "on", 2))
861                                 rc_ansi_color = 1;
862                         if (!strncasecmp(&buf[11], "auto", 4))
863                                 rc_ansi_color = 2;      /* autodetect */
864                         if (!strncasecmp(&buf[11], "user", 4))
865                                 rc_ansi_color = 3;      /* user config */
866                 }
867                 if (!strncasecmp(buf, "use_background=", 15)) {
868                         if (!strncasecmp(&buf[15], "on", 2))
869                                 rc_color_use_bg = 9;
870                 }
871                 if (!strncasecmp(buf, "prompt_control=", 15)) {
872                         if (!strncasecmp(&buf[15], "on", 2))
873                                 rc_prompt_control = 1;
874                         if (!strncasecmp(&buf[15], "user", 4))
875                                 rc_prompt_control = 3;  /* user config */
876                 }
877                 if (!strncasecmp(buf, "username=", 9))
878                         strcpy(rc_username, &buf[9]);
879
880                 if (!strncasecmp(buf, "password=", 9))
881                         strcpy(rc_password, &buf[9]);
882
883                 if (!strncasecmp(buf, "urlcmd=", 7))
884                         strcpy(rc_url_cmd, &buf[7]);
885
886                 if (!strncasecmp(buf, "gotmailcmd=", 11))
887                         strcpy(rc_gotmail_cmd, &buf[11]);
888
889                 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
890                         if (!strncasecmp(&buf[20], "yes", 3)) {
891                                 rc_alt_semantics = 1;
892                         }
893                         else {
894                                 rc_alt_semantics = 0;
895                         }
896                 }
897                 if (!strncasecmp(buf, "reply_with_external_editor=", 27)) {
898                         if (!strncasecmp(&buf[27], "yes", 3)) {
899                                 rc_reply_extedit = 1;
900                         }
901                         else {
902                                 rc_reply_extedit = 0;
903                         }
904                 }
905                 if (!strncasecmp(buf, "cmd=", 4)) {
906                         strcpy(buf, &buf[4]);
907
908                         cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
909
910                         cptr->c_cmdnum = atoi(buf);
911                         for (d = strlen(buf); d >= 0; --d)
912                                 if (buf[d] == ',')
913                                         b = d;
914                         strcpy(buf, &buf[b + 1]);
915
916                         cptr->c_axlevel = atoi(buf);
917                         for (d = strlen(buf); d >= 0; --d)
918                                 if (buf[d] == ',')
919                                         b = d;
920                         strcpy(buf, &buf[b + 1]);
921
922                         for (a = 0; a < 5; ++a)
923                                 cptr->c_keys[a][0] = 0;
924
925                         a = 0;
926                         b = 0;
927                         buf[strlen(buf) + 1] = 0;
928                         while (strlen(buf) > 0) {
929                                 b = strlen(buf);
930                                 for (d = strlen(buf); d >= 0; --d)
931                                         if (buf[d] == ',')
932                                                 b = d;
933                                 strncpy(cptr->c_keys[a], buf, b);
934                                 cptr->c_keys[a][b] = 0;
935                                 if (buf[b] == ',')
936                                         strcpy(buf, &buf[b + 1]);
937                                 else
938                                         strcpy(buf, "");
939                                 ++a;
940                         }
941
942                         cptr->next = NULL;
943                         if (cmdlist == NULL)
944                                 cmdlist = cptr;
945                         else
946                                 lastcmd->next = cptr;
947                         lastcmd = cptr;
948                 }
949         }
950         fclose(ccfile);
951 }
952
953
954
955 /*
956  * return the key associated with a command
957  */
958 char keycmd(char *cmdstr)
959 {
960         int a;
961
962         for (a = 0; a < strlen(cmdstr); ++a)
963                 if (cmdstr[a] == '&')
964                         return (tolower(cmdstr[a + 1]));
965         return (0);
966 }
967
968
969 /*
970  * Output the string from a key command without the ampersand
971  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
972  */
973 char *cmd_expand(char *strbuf, int mode)
974 {
975         int a;
976         static char exp[64];
977         char buf[1024];
978
979         strcpy(exp, strbuf);
980
981         for (a = 0; a < strlen(exp); ++a) {
982                 if (strbuf[a] == '&') {
983
984                         if (mode == 0) {
985                                 strcpy(&exp[a], &exp[a + 1]);
986                         }
987                         if (mode == 1) {
988                                 exp[a] = '<';
989                                 strcpy(buf, &exp[a + 2]);
990                                 exp[a + 2] = '>';
991                                 exp[a + 3] = 0;
992                                 strcat(exp, buf);
993                         }
994                 }
995                 if (!strncmp(&exp[a], "^r", 2)) {
996                         strcpy(buf, exp);
997                         strcpy(&exp[a], room_name);
998                         strcat(exp, &buf[a + 2]);
999                 }
1000                 if (!strncmp(&exp[a], "^c", 2)) {
1001                         exp[a] = ',';
1002                         strcpy(&exp[a + 1], &exp[a + 2]);
1003                 }
1004         }
1005
1006         return (exp);
1007 }
1008
1009
1010
1011 /*
1012  * Comparison function to determine if entered commands match a
1013  * command loaded from the config file.
1014  */
1015 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
1016 {
1017         int a;
1018         int cmdax;
1019
1020         cmdax = 0;
1021         if (is_room_aide)
1022                 cmdax = 1;
1023         if (axlevel >= 6)
1024                 cmdax = 2;
1025
1026         for (a = 0; a < ncomp; ++a) {
1027                 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
1028                     || (cptr->c_axlevel > cmdax))
1029                         return (0);
1030         }
1031         return (1);
1032 }
1033
1034
1035 /*
1036  * This function returns 1 if a given command requires a string input
1037  */
1038 int requires_string(struct citcmd *cptr, int ncomp)
1039 {
1040         int a;
1041         char buf[64];
1042
1043         strcpy(buf, cptr->c_keys[ncomp - 1]);
1044         for (a = 0; a < strlen(buf); ++a) {
1045                 if (buf[a] == ':')
1046                         return (1);
1047         }
1048         return (0);
1049 }
1050
1051
1052 /*
1053  * Input a command at the main prompt.
1054  * This function returns an integer command number.  If the command prompts
1055  * for a string then it is placed in the supplied buffer.
1056  */
1057 int getcmd(CtdlIPC *ipc, char *argbuf)
1058 {
1059         char cmdbuf[5];
1060         int cmdspaces[5];
1061         int cmdpos;
1062         int ch;
1063         int a;
1064         int got;
1065         int this_lazy_cmd;
1066         struct citcmd *cptr;
1067
1068         /*
1069          * Starting a new command now, so set sigcaught to 0.  This variable
1070          * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1071          * been interrupted by a keypress.
1072          */
1073         sigcaught = 0;
1074
1075         /* Switch color support on or off if we're in user mode */
1076         if (rc_ansi_color == 3) {
1077                 if (userflags & US_COLOR)
1078                         enable_color = 1;
1079                 else
1080                         enable_color = 0;
1081         }
1082         /* if we're running in idiot mode, display a cute little menu */
1083         IFNEXPERT formout(ipc, "mainmenu");
1084
1085         print_express();
1086         strcpy(argbuf, "");
1087         cmdpos = 0;
1088         for (a = 0; a < 5; ++a)
1089                 cmdbuf[a] = 0;
1090         /* now the room prompt... */
1091         ok_to_interrupt = 1;
1092         color(BRIGHT_WHITE);
1093         scr_printf("\n%s", room_name);
1094         color(DIM_WHITE);
1095         scr_printf("%c ", room_prompt(room_flags));
1096         scr_flush();
1097
1098         while (1) {
1099                 ch = inkey();
1100                 ok_to_interrupt = 0;
1101
1102                 /* Handle the backspace key, but only if there's something
1103                  * to backspace over...
1104                  */
1105                 if ((ch == 8) && (cmdpos > 0)) {
1106                         back(cmdspaces[cmdpos - 1] + 1);
1107                         cmdbuf[cmdpos] = 0;
1108                         --cmdpos;
1109                 }
1110                 /* Spacebar invokes "lazy traversal" commands */
1111                 if ((ch == 32) && (cmdpos == 0)) {
1112                         this_lazy_cmd = next_lazy_cmd;
1113                         if (this_lazy_cmd == 13)
1114                                 next_lazy_cmd = 5;
1115                         if (this_lazy_cmd == 5)
1116                                 next_lazy_cmd = 13;
1117                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1118                                 if (cptr->c_cmdnum == this_lazy_cmd) {
1119                                         for (a = 0; a < 5; ++a)
1120                                                 if (cptr->c_keys[a][0] != 0)
1121                                                         scr_printf("%s ", cmd_expand(
1122                                                                                         cptr->c_keys[a], 0));
1123                                         scr_printf("\n");
1124                                         return (this_lazy_cmd);
1125                                 }
1126                         }
1127                         scr_printf("\n");
1128                         return (this_lazy_cmd);
1129                 }
1130                 /* Otherwise, process the command */
1131                 cmdbuf[cmdpos] = tolower(ch);
1132
1133                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1134                         if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1135
1136                                 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1137                                 cmdspaces[cmdpos] = strlen(
1138                                     cmd_expand(cptr->c_keys[cmdpos], 0));
1139                                 if (cmdpos < 4)
1140                                         if ((cptr->c_keys[cmdpos + 1]) != 0)
1141                                                 scr_putc(' ');
1142                                 ++cmdpos;
1143                         }
1144                 }
1145
1146                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1147                         if (cmdmatch(cmdbuf, cptr, 5)) {
1148                                 /* We've found our command. */
1149                                 if (requires_string(cptr, cmdpos)) {
1150                                         getline(argbuf, 32);
1151                                 } else {
1152                                         scr_printf("\n");
1153                                 }
1154
1155                                 /* If this command is one that changes rooms,
1156                                  * then the next lazy-command (space bar)
1157                                  * should be "read new" instead of "goto"
1158                                  */
1159                                 if ((cptr->c_cmdnum == 5)
1160                                     || (cptr->c_cmdnum == 6)
1161                                     || (cptr->c_cmdnum == 47)
1162                                     || (cptr->c_cmdnum == 52)
1163                                     || (cptr->c_cmdnum == 16)
1164                                     || (cptr->c_cmdnum == 20))
1165                                         next_lazy_cmd = 13;
1166
1167                                 /* If this command is "read new"
1168                                  * then the next lazy-command (space bar)
1169                                  * should be "goto"
1170                                  */
1171                                 if (cptr->c_cmdnum == 13)
1172                                         next_lazy_cmd = 5;
1173
1174                                 return (cptr->c_cmdnum);
1175
1176                         }
1177                 }
1178
1179                 if (ch == '?') {
1180                         pprintf("\rOne of ...                         \n");
1181                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1182                                 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1183                                         for (a = 0; a < 5; ++a) {
1184                                                 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1185                                         }
1186                                         pprintf("\n");
1187                                 }
1188                         }
1189
1190                         pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1191                         got = 0;
1192                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1193                                 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1194                                         for (a = 0; a < cmdpos; ++a) {
1195                                                 pprintf("%s ",
1196                                                        cmd_expand(cptr->c_keys[a], 0));
1197                                         }
1198                                         got = 1;
1199                                 }
1200                         }
1201                 }
1202         }
1203
1204 }
1205
1206
1207
1208
1209
1210 /*
1211  * set tty modes.  commands are:
1212  * 
1213  * 01- set to bbs mode
1214  * 2 - save current settings for later restoral
1215  * 3 - restore saved settings
1216  */
1217 #ifdef HAVE_TERMIOS_H
1218 void sttybbs(int cmd)
1219 {                               /* SysV version of sttybbs() */
1220         struct termios live;
1221         static struct termios saved_settings;
1222         static int last_cmd = 0;
1223
1224         if (cmd == SB_LAST)
1225                 cmd = last_cmd;
1226         else
1227                 last_cmd = cmd;
1228
1229         if ((cmd == 0) || (cmd == 1)) {
1230                 tcgetattr(0, &live);
1231                 live.c_iflag = ISTRIP | IXON | IXANY;
1232                 live.c_oflag = OPOST | ONLCR;
1233                 live.c_lflag = ISIG | NOFLSH;
1234
1235                 live.c_cc[VINTR] = 0;
1236                 live.c_cc[VQUIT] = 0;
1237
1238 #ifdef hpux
1239                 live.c_cc[VMIN] = 0;
1240                 live.c_cc[VTIME] = 0;
1241 #endif
1242
1243                 /* do we even need this stuff anymore? */
1244                 /* live.c_line=0; */
1245                 live.c_cc[VERASE] = 8;
1246                 live.c_cc[VKILL] = 24;
1247                 live.c_cc[VEOF] = 1;
1248                 live.c_cc[VEOL] = 255;
1249                 live.c_cc[VEOL2] = 0;
1250                 live.c_cc[VSTART] = 0;
1251                 tcsetattr(0, TCSADRAIN, &live);
1252         }
1253         if (cmd == 2) {
1254                 tcgetattr(0, &saved_settings);
1255         }
1256         if (cmd == 3) {
1257                 tcsetattr(0, TCSADRAIN, &saved_settings);
1258         }
1259
1260 }
1261 #else
1262 void sttybbs(int cmd)
1263 {                               /* BSD version of sttybbs() */
1264         struct sgttyb live;
1265         static struct sgttyb saved_settings;
1266         static int last_cmd = 0;
1267
1268         if (cmd == SB_LAST)
1269                 cmd = last_cmd;
1270         else
1271                 last_cmd = cmd;
1272
1273         if ((cmd == 0) || (cmd == 1)) {
1274                 gtty(0, &live);
1275                 live.sg_flags |= CBREAK;
1276                 live.sg_flags |= CRMOD;
1277                 live.sg_flags |= NL1;
1278                 live.sg_flags &= ~ECHO;
1279                 if (cmd == 1)
1280                         live.sg_flags |= NOFLSH;
1281                 stty(0, &live);
1282         }
1283         if (cmd == 2) {
1284                 gtty(0, &saved_settings);
1285         }
1286         if (cmd == 3) {
1287                 stty(0, &saved_settings);
1288         }
1289 }
1290 #endif
1291
1292
1293 /*
1294  * display_help()  -  help file viewer
1295  */
1296 void display_help(CtdlIPC *ipc, char *name)
1297 {
1298         formout(ipc, name);
1299 }
1300
1301
1302 /*
1303  * fmout() - Citadel text formatter and paginator
1304  */
1305 int fmout(
1306         int width,      /* screen width to use */
1307         FILE *fpin,     /* file to read from, or NULL to format given text */
1308         char *text,     /* text to be formatted (when fpin is NULL */
1309         FILE *fpout,    /* file to write to, or NULL to write to screen */
1310         char pagin,     /* nonzero if we should use the paginator */
1311         int height,     /* screen height to use */
1312         int starting_lp,/* starting value for lines_printed, -1 for global */
1313         int subst)      /* nonzero if we should use hypertext mode */
1314 {
1315         char *buffer = NULL;    /* The current message */
1316         char *word = NULL;      /* What we are about to actually print */
1317         char *e;                /* Pointer to position in text */
1318         char old = 0;           /* The previous character */
1319         int column = 0;         /* Current column */
1320         size_t i;               /* Generic counter */
1321
1322         /* Space for a single word, which can be at most screenwidth */
1323         word = (char *)calloc(1, width);
1324         if (!word) {
1325                 err_printf("Can't alloc memory to print message: %s!\n",
1326                                 strerror(errno));
1327                 logoff(NULL, 3);
1328         }
1329
1330         /* Read the entire message body into memory */
1331         if (fpin) {
1332                 buffer = load_message_from_file(fpin);
1333                 if (!buffer) {
1334                         err_printf("Can't print message: %s!\n",
1335                                         strerror(errno));
1336                         logoff(NULL, 3);
1337                 }
1338         } else {
1339                 buffer = text;
1340         }
1341         e = buffer;
1342
1343         if (starting_lp >= 0)
1344                 lines_printed = starting_lp;
1345
1346         /* Run the message body */
1347         while (*e) {
1348                 /* Catch characters that shouldn't be there at all */
1349                 if (*e == '\r') {
1350                         e++;
1351                         continue;
1352                 }
1353                 /* First, are we looking at a newline? */
1354                 if (*e == '\n') {
1355                         e++;
1356                         if (*e == ' ') {        /* Paragraph */
1357                                 if (fpout) {
1358                                         fprintf(fpout, "\n");
1359                                 } else {
1360                                         scr_printf("\n");
1361                                         ++lines_printed;
1362                                         lines_printed = checkpagin(lines_printed, pagin, height);
1363                                 }
1364                                 column = 0;
1365                         } else if (old != ' ') {/* Don't print two spaces */
1366                                 if (fpout) {
1367                                         fprintf(fpout, " ");
1368                                 } else {
1369                                         scr_printf(" ");
1370                                 }
1371                                 column++;
1372                         }
1373                         old = '\n';
1374                         continue;
1375                 }
1376                 /* Are we looking at a nonprintable? */
1377                 if ( (*e < 32) || (*e > 126) ) {
1378                         e++;
1379                         continue;
1380                 }
1381                 /* Or are we looking at a space? */
1382                 if (*e == ' ') {
1383                         e++;
1384                         if (column >= width - 1) {
1385                                 /* Are we in the rightmost column? */
1386                                 if (fpout) {
1387                                         fprintf(fpout, "\n");
1388                                 } else {
1389                                         scr_printf("\n");
1390                                         ++lines_printed;
1391                                         lines_printed = checkpagin(lines_printed, pagin, height);
1392                                 }
1393                                 column = 0;
1394                         } else if (!(column == 0 && old == ' ')) {
1395                                 /* Eat only the first space on a line */
1396                                 if (fpout) {
1397                                         fprintf(fpout, " ");
1398                                 } else {
1399                                         scr_printf(" ");
1400                                 }
1401                                 column++;
1402                         }
1403                         /* ONLY eat the FIRST space on a line */
1404                         old = ' ';
1405                         continue;
1406                 }
1407                 old = *e;
1408
1409                 /* Read a word, slightly messy */
1410                 i = 0;
1411                 while (e[i]) {
1412                         if (!isprint(e[i]) && !isspace(e[i]))
1413                                 e[i] = ' ';
1414                         if (isspace(e[i]))
1415                                 break;
1416                         i++;
1417                 }
1418
1419                 /* We should never see these, but... slightly messy */
1420                 if (e[i] == '\t' || e[i] == '\f' || e[i] == '\v')
1421                         e[i] = ' ';
1422
1423                 /* Break up really long words */
1424                 /* TODO: auto-hyphenation someday? */
1425                 if (i >= width) 
1426                         i = width - 1;
1427                 strncpy(word, e, i);
1428                 word[i] = 0;
1429
1430                 /* Decide where to print the word */
1431                 if (column + i >= width) {
1432                         /* Wrap to the next line */
1433                         if (fpout) {
1434                                 fprintf(fpout, "\n");
1435                         } else {
1436                                 scr_printf("\n");
1437                                 ++lines_printed;
1438                                 lines_printed = checkpagin(lines_printed, pagin, height);
1439                         }
1440                         column = 0;
1441                 }
1442
1443                 /* Print the word */
1444                 if (fpout) {
1445                         fprintf(fpout, "%s", word);
1446                 } else {
1447                         scr_printf("%s", word);
1448                 }
1449                 column += i;
1450                 e += i;         /* Start over with the whitepsace! */
1451         }
1452
1453         free(word);
1454         if (fpin)               /* We allocated this, remember? */
1455                 free(buffer);
1456
1457         /* Is this necessary?  It makes the output kind of spacey. */
1458         if (fpout) {
1459                 fprintf(fpout, "\n");
1460         } else {
1461                 scr_printf("\n");
1462                 ++lines_printed;
1463                 lines_printed = checkpagin(lines_printed, pagin, height);
1464         }
1465
1466         return sigcaught;
1467 }
1468
1469
1470 /*
1471  * support ANSI color if defined
1472  */
1473 void color(int colornum)
1474 {
1475         static int hold_color;
1476         static int current_color;
1477
1478         if (colornum == COLOR_PUSH) {
1479                 hold_color = current_color;
1480                 return;
1481         }
1482
1483         if (colornum == COLOR_POP) {
1484                 color(hold_color);
1485                 return;
1486         }
1487
1488         current_color = colornum;
1489         if (enable_color) {
1490 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
1491                 if (scr_color(colornum))
1492                         return;
1493 #endif
1494                 /* When switching to dim white, actually output an 'original
1495                  * pair' sequence -- this looks better on black-on-white
1496                  * terminals. - Changed to ORIGINAL_PAIR as this actually
1497                  * wound up looking horrible on black-on-white terminals, not
1498                  * to mention transparent terminals.
1499                  */
1500                 if (colornum == ORIGINAL_PAIR)
1501                         printf("\033[0;39;49m");
1502                 else
1503                         printf("\033[%d;3%d;4%dm", 
1504                                         (colornum & 8) ? 1 : 0,
1505                                         (colornum & 7),
1506                                         rc_color_use_bg);
1507
1508                 scr_flush();
1509         }
1510 }
1511
1512 void cls(int colornum)
1513 {
1514         if (enable_color) {
1515                 printf("\033[4%dm\033[2J\033[H\033[0m",
1516                                 colornum ? colornum : rc_color_use_bg);
1517                 scr_flush();
1518         }
1519 }
1520
1521
1522 /*
1523  * Detect whether ANSI color is available (answerback)
1524  */
1525 void send_ansi_detect(void)
1526 {
1527         if (rc_ansi_color == 2) {
1528                 printf("\033[c");
1529                 scr_flush();
1530                 time(&AnsiDetect);
1531         }
1532 }
1533
1534 void look_for_ansi(void)
1535 {
1536         fd_set rfds;
1537         struct timeval tv;
1538         char abuf[512];
1539         time_t now;
1540         int a;
1541
1542         if (rc_ansi_color == 0) {
1543                 enable_color = 0;
1544         } else if (rc_ansi_color == 1) {
1545                 enable_color = 1;
1546         } else if (rc_ansi_color == 2) {
1547
1548                 /* otherwise, do the auto-detect */
1549
1550                 strcpy(abuf, "");
1551
1552                 time(&now);
1553                 if ((now - AnsiDetect) < 2)
1554                         sleep(1);
1555
1556                 do {
1557                         FD_ZERO(&rfds);
1558                         FD_SET(0, &rfds);
1559                         tv.tv_sec = 0;
1560                         tv.tv_usec = 1;
1561
1562                         select(1, &rfds, NULL, NULL, &tv);
1563                         if (FD_ISSET(0, &rfds)) {
1564                                 abuf[strlen(abuf) + 1] = 0;
1565                                 read(0, &abuf[strlen(abuf)], 1);
1566                         }
1567                 } while (FD_ISSET(0, &rfds));
1568
1569                 for (a = 0; a < strlen(abuf); ++a) {
1570                         if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1571                             && (abuf[a + 2] == '?')) {
1572                                 enable_color = 1;
1573                         }
1574                 }
1575         }
1576 }
1577
1578
1579 /*
1580  * Display key options (highlight hotkeys inside angle brackets)
1581  */
1582 void keyopt(char *buf) {
1583         int i;
1584
1585         color(DIM_WHITE);
1586         for (i=0; i<strlen(buf); ++i) {
1587                 if (buf[i]=='<') {
1588                         scr_putc(buf[i]);
1589                         color(BRIGHT_MAGENTA);
1590                 } else {
1591                         if (buf[i]=='>') {
1592                                 color(DIM_WHITE);
1593                         }
1594                         scr_putc(buf[i]);
1595                 }
1596         }
1597         color(DIM_WHITE);
1598 }
1599
1600
1601
1602 /*
1603  * Present a key-menu line choice type of thing
1604  */
1605 char keymenu(char *menuprompt, char *menustring) {
1606         int i, c, a;
1607         int choices;
1608         int do_prompt = 0;
1609         char buf[1024];
1610         int ch;
1611         int display_prompt = 1;
1612
1613         choices = num_tokens(menustring, '|');
1614
1615         if (menuprompt != NULL) do_prompt = 1;
1616         if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1617
1618         while (1) {
1619                 if (display_prompt) {
1620                         if (do_prompt) {
1621                                 scr_printf("%s ", menuprompt);
1622                         } 
1623                         else {
1624                                 for (i=0; i<choices; ++i) {
1625                                         extract(buf, menustring, i);
1626                                         keyopt(buf);
1627                                         scr_printf(" ");
1628                                 }
1629                         }
1630                         scr_printf(" -> ");
1631                         display_prompt = 0;
1632                 }
1633                 ch = lkey();
1634         
1635                 if ( (do_prompt) && (ch=='?') ) {
1636                         scr_printf("\rOne of...                               ");
1637                         scr_printf("                                      \n");
1638                         for (i=0; i<choices; ++i) {
1639                                 extract(buf, menustring, i);
1640                                 scr_printf("   ");
1641                                 keyopt(buf);
1642                                 scr_printf("\n");
1643                         }
1644                         scr_printf("\n");
1645                         display_prompt = 1;
1646                 }
1647
1648                 for (i=0; i<choices; ++i) {
1649                         extract(buf, menustring, i);
1650                         for (c=1; c<strlen(buf); ++c) {
1651                                 if ( (ch == tolower(buf[c]))
1652                                    && (buf[c-1]=='<')
1653                                    && (buf[c+1]=='>') ) {
1654                                         for (a=0; a<strlen(buf); ++a) {
1655                                                 if ( (a!=(c-1)) && (a!=(c+1))) {
1656                                                         scr_putc(buf[a]);
1657                                                 }
1658                                         }
1659                                         scr_printf("\n\n");
1660                                         return ch;
1661                                 }
1662                         }
1663                 }
1664         }
1665 }