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