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