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