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