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