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