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