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