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