]> code.citadel.org Git - citadel.git/blob - citadel/commands.c
30e9a148001debfd7e201e1797e7bf48e5667a92
[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 char rc_gotmail_cmd[SIZ];
84
85 char *gl_string;
86 int next_lazy_cmd = 5;
87
88 int lines_printed = 0;          /* line count for paginator */
89 extern int screenwidth, screenheight;
90 extern int termn8;
91
92 struct citcmd *cmdlist = NULL;
93
94
95 /* these variables are local to this module */
96 char keepalives_enabled = KA_YES;       /* send NOOPs to server when idle */
97 int ok_to_interrupt = 0;                /* print express msgs asynchronously */
98 time_t AnsiDetect;                      /* when did we send the detect code? */
99 int enable_color = 0;                   /* nonzero for ANSI color */
100
101
102
103
104 /*
105  * If an interesting key has been pressed, return its value, otherwise 0
106  */
107 char was_a_key_pressed(void) {
108         fd_set rfds;
109         struct timeval tv;
110         int the_character;
111         int retval;
112
113         FD_ZERO(&rfds);
114         FD_SET(0, &rfds);
115         tv.tv_sec = 0;
116         tv.tv_usec = 0;
117         retval = select(1, &rfds, NULL, NULL, &tv); 
118
119         /* Careful!  Disable keepalives during keyboard polling; we're probably
120          * in the middle of a data transfer from the server, in which case
121          * sending a NOOP would throw the client protocol out of sync.
122          */
123         if (FD_ISSET(0, &rfds)) {
124                 set_keepalives(KA_NO);
125                 the_character = inkey();
126                 set_keepalives(KA_YES);
127         }
128         else {
129                 the_character = 0;
130         }
131         return(the_character);
132 }
133
134
135
136
137
138 /*
139  * Check to see if we need to pause at the end of a screen.
140  * If we do, we have to switch to half keepalives during the pause because
141  * we are probably in the middle of a server operation and the NOOP command
142  * would confuse everything.
143  */
144 int checkpagin(int lp, int pagin, int height)
145 {
146         int thekey;
147
148         if (sigcaught) return(lp);
149         thekey = was_a_key_pressed();
150         if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
151                 thekey = STOP_KEY;
152         if (thekey == 'n' || thekey == 'N')
153                 thekey = NEXT_KEY;
154         if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
155         if (sigcaught) return(lp);
156
157         if (!pagin) return(0);
158         if (lp>=(height-1)) {
159                 set_keepalives(KA_HALF);
160                 hit_any_key();
161                 set_keepalives(KA_YES);
162                 return(0);
163         }
164         return(lp);
165 }
166
167
168
169
170 /*
171  * pprintf()  ...   paginated version of printf()
172  */
173 void pprintf(const char *format, ...) {   
174         va_list arg_ptr;
175         static char buf[4096];  /* static for performance, change if needed */
176         int i;
177
178         /* If sigcaught is nonzero, a keypress has interrupted this and we
179          * should just drain output.
180          */
181         if (sigcaught) return;
182  
183         /* Otherwise, start spewing... */ 
184         va_start(arg_ptr, format);   
185         vsnprintf(buf, sizeof(buf), format, arg_ptr);   
186         va_end(arg_ptr);   
187
188         for (i=0; i<strlen(buf); ++i) {
189                 scr_putc(buf[i]);
190                 if (buf[i]==10) {
191                         ++lines_printed;
192                         lines_printed = checkpagin(lines_printed,
193                                 (userflags & US_PAGINATOR),
194                                 screenheight);
195                 }
196         }
197 }   
198
199
200
201 /*
202  * print_express()  -  print express messages if there are any
203  */
204 void print_express(void)
205 {
206         char buf[1024];
207         FILE *outpipe;
208         time_t timestamp;
209         struct tm *stamp;
210         int flags = 0;
211         char sender[64];
212         char node[64];
213         char *listing = NULL;
214         int r;                  /* IPC result code */
215
216         if (express_msgs == 0)
217                 return;
218
219         if (rc_exp_beep) {
220                 scr_putc(7);
221         }
222         if (strlen(rc_exp_cmd) == 0) {
223                 color(BRIGHT_RED);
224                 scr_printf("\r---");
225         }
226         
227         while (express_msgs != 0) {
228                 r = CtdlIPCGetInstantMessage(&listing, buf);
229                 if (r / 100 != 1)
230                         return;
231         
232                 express_msgs = extract_int(buf, 0);
233                 timestamp = extract_long(buf, 1);
234                 flags = extract_int(buf, 2);
235                 extract(sender, buf, 3);
236                 extract(node, buf, 4);
237                 strcpy(last_paged, sender);
238         
239                 stamp = localtime(&timestamp);
240
241                 /* If the page is a Logoff Request, honor it. */
242                 if (flags & 2) {
243                         termn8 = 1;
244                         return;
245                 }
246         
247                 if (strlen(rc_exp_cmd) > 0) {
248                         outpipe = popen(rc_exp_cmd, "w");
249                         if (outpipe != NULL) {
250                                 /* Header derived from flags */
251                                 if (flags & 2)
252                                         fprintf(outpipe,
253                                                "Please log off now, as requested ");
254                                 else if (flags & 1)
255                                         fprintf(outpipe, "Broadcast message ");
256                                 else if (flags & 4)
257                                         fprintf(outpipe, "Chat request ");
258                                 else
259                                         fprintf(outpipe, "Message ");
260                                 /* Timestamp.  Can this be improved? */
261                                 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
262                                         fprintf(outpipe, "at 12:%02d%cm",
263                                                 stamp->tm_min, 
264                                                 stamp->tm_hour ? 'p' : 'a');
265                                 else if (stamp->tm_hour > 12)           /* pm */
266                                         fprintf(outpipe, "at %d:%02dpm",
267                                                 stamp->tm_hour - 12,
268                                                 stamp->tm_min);
269                                 else                                    /* am */
270                                         fprintf(outpipe, "at %d:%02dam",
271                                                 stamp->tm_hour, stamp->tm_min);
272                                 fprintf(outpipe, " from %s", sender);
273                                 if (strncmp(serv_info.serv_nodename, node, 32))
274                                         fprintf(outpipe, " @%s", node);
275                                 fprintf(outpipe, ":\n%s\n", listing);
276                                 pclose(outpipe);
277                                 if (express_msgs == 0)
278                                         return;
279                                 continue;
280                         }
281                 }
282                 /* fall back to built-in express message display */
283                 scr_printf("\n");
284                 lines_printed++;
285
286                 /* Header derived from flags */
287                 if (flags & 2)
288                         scr_printf("Please log off now, as requested ");
289                 else if (flags & 1)
290                         scr_printf("Broadcast message ");
291                 else if (flags & 4)
292                         scr_printf("Chat request ");
293                 else
294                         scr_printf("Message ");
295         
296                 /* Timestamp.  Can this be improved? */
297                 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)/* 12am/12pm */
298                         scr_printf("at 12:%02d%cm", stamp->tm_min, 
299                                 stamp->tm_hour ? 'p' : 'a');
300                 else if (stamp->tm_hour > 12)                   /* pm */
301                         scr_printf("at %d:%02dpm",
302                                 stamp->tm_hour - 12, stamp->tm_min);
303                 else                                            /* am */
304                         scr_printf("at %d:%02dam", stamp->tm_hour, stamp->tm_min);
305                 
306                 /* Sender */
307                 scr_printf(" from %s", sender);
308         
309                 /* Remote node, if any */
310                 if (strncmp(serv_info.serv_nodename, node, 32))
311                         scr_printf(" @%s", node);
312         
313                 scr_printf(":\n");
314                 lines_printed++;
315                 fmout(screenwidth, NULL, listing, NULL, 1, screenheight, -1, 0);
316                 free(listing);
317
318                 /* when running in curses mode, the scroll bar in most
319                    xterm-style programs becomes useless, so it makes sense to
320                    pause after a screenful of pages if the user has been idle
321                    for a while. However, this is annoying to some of the users
322                    who aren't in curses mode and tend to leave their clients
323                    idle. keepalives become disabled, resulting in getting booted
324                    when coming back to the idle session. but they probably have
325                    a working scrollback in their terminal, so disable it in this
326                    case:
327                  */
328                 if (!is_curses_enabled())
329                         lines_printed = 0;
330         }
331         scr_printf("\n---\n");
332         color(BRIGHT_WHITE);
333
334
335 }
336
337
338 void set_keepalives(int s)
339 {
340         keepalives_enabled = (char) s;
341 }
342
343 /* 
344  * This loop handles the "keepalive" messages sent to the server when idling.
345  */
346
347 static time_t idlet = 0;
348 static void really_do_keepalive(void) {
349         int r;                          /* IPC response code */
350
351         time(&idlet);
352
353         /* 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         strcpy(rc_gotmail_cmd, "");
740 #ifdef HAVE_OPENSSL
741         rc_encrypt = RC_DEFAULT;
742 #endif
743 #ifdef HAVE_CURSES_H
744         rc_screen = RC_DEFAULT;
745 #endif
746         rc_alt_semantics = 0;
747
748         /* now try to open the citadel.rc file */
749
750         ccfile = NULL;
751         if (getenv("HOME") != NULL) {
752                 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
753                 ccfile = fopen(buf, "r");
754         }
755         if (ccfile == NULL) {
756                 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
757                 ccfile = fopen(buf, "r");
758         }
759         if (ccfile == NULL) {
760                 ccfile = fopen("/etc/citadel.rc", "r");
761         }
762         if (ccfile == NULL) {
763                 ccfile = fopen("./citadel.rc", "r");
764         }
765         if (ccfile == NULL) {
766                 perror("commands: cannot open citadel.rc");
767                 logoff(errno);
768         }
769         while (fgets(buf, sizeof buf, ccfile) != NULL) {
770                 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
771                         buf[strlen(buf) - 1] = 0;
772
773                 if (!strncasecmp(buf, "encrypt=", 8)) {
774                         if (!strcasecmp(&buf[8], "yes")) {
775 #ifdef HAVE_OPENSSL
776                                 rc_encrypt = RC_YES;
777 #else
778                                 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
779                                 logoff(1);
780 #endif
781                         }
782 #ifdef HAVE_OPENSSL
783                         else if (!strcasecmp(&buf[8], "no")) {
784                                 rc_encrypt = RC_NO;
785                         }
786                         else if (!strcasecmp(&buf[8], "default")) {
787                                 rc_encrypt = RC_DEFAULT;
788                         }
789 #endif
790                 }
791
792 #ifdef HAVE_CURSES_H
793                 if (!strncasecmp(buf, "fullscreen=", 11)) {
794                         if (!strcasecmp(&buf[11], "yes"))
795                                 rc_screen = RC_YES;
796                         else if (!strcasecmp(&buf[11], "no"))
797                                 rc_screen = RC_NO;
798                 }
799 #endif
800
801                 if (!strncasecmp(buf, "editor=", 7))
802                         strcpy(editor_path, &buf[7]);
803
804                 if (!strncasecmp(buf, "printcmd=", 9))
805                         strcpy(printcmd, &buf[9]);
806
807                 if (!strncasecmp(buf, "expcmd=", 7))
808                         strcpy(rc_exp_cmd, &buf[7]);
809
810                 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
811                         have_xterm = (char) atoi(&buf[24]);
812
813                 if (!strncasecmp(buf, "use_floors=", 11)) {
814                         if (!strcasecmp(&buf[11], "yes"))
815                                 rc_floor_mode = RC_YES;
816                         if (!strcasecmp(&buf[11], "no"))
817                                 rc_floor_mode = RC_NO;
818                         if (!strcasecmp(&buf[11], "default"))
819                                 rc_floor_mode = RC_DEFAULT;
820                 }
821                 if (!strncasecmp(buf, "beep=", 5)) {
822                         rc_exp_beep = atoi(&buf[5]);
823                 }
824                 if (!strncasecmp(buf, "allow_attachments=", 18)) {
825                         rc_allow_attachments = atoi(&buf[18]);
826                 }
827                 if (!strncasecmp(buf, "idle_threshold=", 15)) {
828                         rc_idle_threshold = atoi(&buf[15]);
829                 }
830                 if (!strncasecmp(buf, "remember_passwords=", 19)) {
831                         rc_remember_passwords = atoi(&buf[19]);
832                 }
833                 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
834                         rc_display_message_numbers = atoi(&buf[24]);
835                 }
836                 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
837                         rc_force_mail_prompts = atoi(&buf[19]);
838                 }
839                 if (!strncasecmp(buf, "ansi_color=", 11)) {
840                         if (!strncasecmp(&buf[11], "on", 2))
841                                 rc_ansi_color = 1;
842                         if (!strncasecmp(&buf[11], "auto", 4))
843                                 rc_ansi_color = 2;      /* autodetect */
844                         if (!strncasecmp(&buf[11], "user", 4))
845                                 rc_ansi_color = 3;      /* user config */
846                 }
847                 if (!strncasecmp(buf, "prompt_control=", 15)) {
848                         if (!strncasecmp(&buf[15], "on", 2))
849                                 rc_prompt_control = 1;
850                         if (!strncasecmp(&buf[15], "user", 4))
851                                 rc_prompt_control = 3;  /* user config */
852                 }
853                 if (!strncasecmp(buf, "username=", 9))
854                         strcpy(rc_username, &buf[9]);
855
856                 if (!strncasecmp(buf, "password=", 9))
857                         strcpy(rc_password, &buf[9]);
858
859                 if (!strncasecmp(buf, "urlcmd=", 7))
860                         strcpy(rc_url_cmd, &buf[7]);
861
862                 if (!strncasecmp(buf, "gotmailcmd=", 11))
863                         strcpy(rc_gotmail_cmd, &buf[11]);
864
865                 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
866                         if (!strncasecmp(&buf[11], "yes", 3))
867                                 rc_alt_semantics = 1;
868                         if (!strncasecmp(&buf[11], "no", 2))
869                                 rc_alt_semantics = 0;
870                 }
871                 if (!strncasecmp(buf, "cmd=", 4)) {
872                         strcpy(buf, &buf[4]);
873
874                         cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
875
876                         cptr->c_cmdnum = atoi(buf);
877                         for (d = strlen(buf); d >= 0; --d)
878                                 if (buf[d] == ',')
879                                         b = d;
880                         strcpy(buf, &buf[b + 1]);
881
882                         cptr->c_axlevel = atoi(buf);
883                         for (d = strlen(buf); d >= 0; --d)
884                                 if (buf[d] == ',')
885                                         b = d;
886                         strcpy(buf, &buf[b + 1]);
887
888                         for (a = 0; a < 5; ++a)
889                                 cptr->c_keys[a][0] = 0;
890
891                         a = 0;
892                         b = 0;
893                         buf[strlen(buf) + 1] = 0;
894                         while (strlen(buf) > 0) {
895                                 b = strlen(buf);
896                                 for (d = strlen(buf); d >= 0; --d)
897                                         if (buf[d] == ',')
898                                                 b = d;
899                                 strncpy(cptr->c_keys[a], buf, b);
900                                 cptr->c_keys[a][b] = 0;
901                                 if (buf[b] == ',')
902                                         strcpy(buf, &buf[b + 1]);
903                                 else
904                                         strcpy(buf, "");
905                                 ++a;
906                         }
907
908                         cptr->next = NULL;
909                         if (cmdlist == NULL)
910                                 cmdlist = cptr;
911                         else
912                                 lastcmd->next = cptr;
913                         lastcmd = cptr;
914                 }
915         }
916         fclose(ccfile);
917 }
918
919
920
921 /*
922  * return the key associated with a command
923  */
924 char keycmd(char *cmdstr)
925 {
926         int a;
927
928         for (a = 0; a < strlen(cmdstr); ++a)
929                 if (cmdstr[a] == '&')
930                         return (tolower(cmdstr[a + 1]));
931         return (0);
932 }
933
934
935 /*
936  * Output the string from a key command without the ampersand
937  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
938  */
939 char *cmd_expand(char *strbuf, int mode)
940 {
941         int a;
942         static char exp[64];
943         char buf[1024];
944
945         strcpy(exp, strbuf);
946
947         for (a = 0; a < strlen(exp); ++a) {
948                 if (strbuf[a] == '&') {
949
950                         if (mode == 0) {
951                                 strcpy(&exp[a], &exp[a + 1]);
952                         }
953                         if (mode == 1) {
954                                 exp[a] = '<';
955                                 strcpy(buf, &exp[a + 2]);
956                                 exp[a + 2] = '>';
957                                 exp[a + 3] = 0;
958                                 strcat(exp, buf);
959                         }
960                 }
961                 if (!strncmp(&exp[a], "^r", 2)) {
962                         strcpy(buf, exp);
963                         strcpy(&exp[a], room_name);
964                         strcat(exp, &buf[a + 2]);
965                 }
966                 if (!strncmp(&exp[a], "^c", 2)) {
967                         exp[a] = ',';
968                         strcpy(&exp[a + 1], &exp[a + 2]);
969                 }
970         }
971
972         return (exp);
973 }
974
975
976
977 /*
978  * Comparison function to determine if entered commands match a
979  * command loaded from the config file.
980  */
981 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
982 {
983         int a;
984         int cmdax;
985
986         cmdax = 0;
987         if (is_room_aide)
988                 cmdax = 1;
989         if (axlevel >= 6)
990                 cmdax = 2;
991
992         for (a = 0; a < ncomp; ++a) {
993                 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
994                     || (cptr->c_axlevel > cmdax))
995                         return (0);
996         }
997         return (1);
998 }
999
1000
1001 /*
1002  * This function returns 1 if a given command requires a string input
1003  */
1004 int requires_string(struct citcmd *cptr, int ncomp)
1005 {
1006         int a;
1007         char buf[64];
1008
1009         strcpy(buf, cptr->c_keys[ncomp - 1]);
1010         for (a = 0; a < strlen(buf); ++a) {
1011                 if (buf[a] == ':')
1012                         return (1);
1013         }
1014         return (0);
1015 }
1016
1017
1018 /*
1019  * Input a command at the main prompt.
1020  * This function returns an integer command number.  If the command prompts
1021  * for a string then it is placed in the supplied buffer.
1022  */
1023 int getcmd(char *argbuf)
1024 {
1025         char cmdbuf[5];
1026         int cmdspaces[5];
1027         int cmdpos;
1028         int ch;
1029         int a;
1030         int got;
1031         int this_lazy_cmd;
1032         struct citcmd *cptr;
1033
1034         /*
1035          * Starting a new command now, so set sigcaught to 0.  This variable
1036          * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1037          * been interrupted by a keypress.
1038          */
1039         sigcaught = 0;
1040
1041         /* Switch color support on or off if we're in user mode */
1042         if (rc_ansi_color == 3) {
1043                 if (userflags & US_COLOR)
1044                         enable_color = 1;
1045                 else
1046                         enable_color = 0;
1047         }
1048         /* if we're running in idiot mode, display a cute little menu */
1049         IFNEXPERT formout("mainmenu");
1050
1051         print_express();        /* print express messages if there are any */
1052         strcpy(argbuf, "");
1053         cmdpos = 0;
1054         for (a = 0; a < 5; ++a)
1055                 cmdbuf[a] = 0;
1056         /* now the room prompt... */
1057         ok_to_interrupt = 1;
1058         color(BRIGHT_WHITE);
1059         scr_printf("\n%s", room_name);
1060         color(DIM_WHITE);
1061         scr_printf("%c ", room_prompt(room_flags));
1062         scr_flush();
1063
1064         while (1) {
1065                 ch = inkey();
1066                 ok_to_interrupt = 0;
1067
1068                 /* Handle the backspace key, but only if there's something
1069                  * to backspace over...
1070                  */
1071                 if ((ch == 8) && (cmdpos > 0)) {
1072                         back(cmdspaces[cmdpos - 1] + 1);
1073                         cmdbuf[cmdpos] = 0;
1074                         --cmdpos;
1075                 }
1076                 /* Spacebar invokes "lazy traversal" commands */
1077                 if ((ch == 32) && (cmdpos == 0)) {
1078                         this_lazy_cmd = next_lazy_cmd;
1079                         if (this_lazy_cmd == 13)
1080                                 next_lazy_cmd = 5;
1081                         if (this_lazy_cmd == 5)
1082                                 next_lazy_cmd = 13;
1083                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1084                                 if (cptr->c_cmdnum == this_lazy_cmd) {
1085                                         for (a = 0; a < 5; ++a)
1086                                                 if (cptr->c_keys[a][0] != 0)
1087                                                         scr_printf("%s ", cmd_expand(
1088                                                                                         cptr->c_keys[a], 0));
1089                                         scr_printf("\n");
1090                                         return (this_lazy_cmd);
1091                                 }
1092                         }
1093                         scr_printf("\n");
1094                         return (this_lazy_cmd);
1095                 }
1096                 /* Otherwise, process the command */
1097                 cmdbuf[cmdpos] = tolower(ch);
1098
1099                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1100                         if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1101
1102                                 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1103                                 cmdspaces[cmdpos] = strlen(
1104                                     cmd_expand(cptr->c_keys[cmdpos], 0));
1105                                 if (cmdpos < 4)
1106                                         if ((cptr->c_keys[cmdpos + 1]) != 0)
1107                                                 scr_putc(' ');
1108                                 ++cmdpos;
1109                         }
1110                 }
1111
1112                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1113                         if (cmdmatch(cmdbuf, cptr, 5)) {
1114                                 /* We've found our command. */
1115                                 if (requires_string(cptr, cmdpos)) {
1116                                         getline(argbuf, 32);
1117                                 } else {
1118                                         scr_printf("\n");
1119                                 }
1120
1121                                 /* If this command is one that changes rooms,
1122                                  * then the next lazy-command (space bar)
1123                                  * should be "read new" instead of "goto"
1124                                  */
1125                                 if ((cptr->c_cmdnum == 5)
1126                                     || (cptr->c_cmdnum == 6)
1127                                     || (cptr->c_cmdnum == 47)
1128                                     || (cptr->c_cmdnum == 52)
1129                                     || (cptr->c_cmdnum == 16)
1130                                     || (cptr->c_cmdnum == 20))
1131                                         next_lazy_cmd = 13;
1132
1133                                 return (cptr->c_cmdnum);
1134
1135                         }
1136                 }
1137
1138                 if (ch == '?') {
1139                         pprintf("\rOne of ...                         \n");
1140                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1141                                 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1142                                         for (a = 0; a < 5; ++a) {
1143                                                 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1144                                         }
1145                                         pprintf("\n");
1146                                 }
1147                         }
1148
1149                         pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1150                         got = 0;
1151                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1152                                 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1153                                         for (a = 0; a < cmdpos; ++a) {
1154                                                 pprintf("%s ",
1155                                                        cmd_expand(cptr->c_keys[a], 0));
1156                                         }
1157                                         got = 1;
1158                                 }
1159                         }
1160                 }
1161         }
1162
1163 }
1164
1165
1166
1167
1168
1169 /*
1170  * set tty modes.  commands are:
1171  * 
1172  * 01- set to bbs mode
1173  * 2 - save current settings for later restoral
1174  * 3 - restore saved settings
1175  */
1176 #ifdef HAVE_TERMIOS_H
1177 void sttybbs(int cmd)
1178 {                               /* SysV version of sttybbs() */
1179         struct termios live;
1180         static struct termios saved_settings;
1181         static int last_cmd = 0;
1182
1183         if (cmd == SB_LAST)
1184                 cmd = last_cmd;
1185         else
1186                 last_cmd = cmd;
1187
1188         if ((cmd == 0) || (cmd == 1)) {
1189                 tcgetattr(0, &live);
1190                 live.c_iflag = ISTRIP | IXON | IXANY;
1191                 live.c_oflag = OPOST | ONLCR;
1192                 live.c_lflag = ISIG | NOFLSH;
1193
1194                 live.c_cc[VINTR] = (-1);
1195                 live.c_cc[VQUIT] = (-1);
1196
1197 #ifdef hpux
1198                 live.c_cc[VMIN] = 0;
1199                 live.c_cc[VTIME] = 0;
1200 #endif
1201
1202                 /* do we even need this stuff anymore? */
1203                 /* live.c_line=0; */
1204                 live.c_cc[VERASE] = 8;
1205                 live.c_cc[VKILL] = 24;
1206                 live.c_cc[VEOF] = 1;
1207                 live.c_cc[VEOL] = 255;
1208                 live.c_cc[VEOL2] = 0;
1209                 live.c_cc[VSTART] = 0;
1210                 tcsetattr(0, TCSADRAIN, &live);
1211         }
1212         if (cmd == 2) {
1213                 tcgetattr(0, &saved_settings);
1214         }
1215         if (cmd == 3) {
1216                 tcsetattr(0, TCSADRAIN, &saved_settings);
1217         }
1218 }
1219 #else
1220 void sttybbs(int cmd)
1221 {                               /* BSD version of sttybbs() */
1222         struct sgttyb live;
1223         static struct sgttyb saved_settings;
1224         static int last_cmd = 0;
1225
1226         if (cmd == SB_LAST)
1227                 cmd = last_cmd;
1228         else
1229                 last_cmd = cmd;
1230
1231         if ((cmd == 0) || (cmd == 1)) {
1232                 gtty(0, &live);
1233                 live.sg_flags |= CBREAK;
1234                 live.sg_flags |= CRMOD;
1235                 live.sg_flags |= NL1;
1236                 live.sg_flags &= ~ECHO;
1237                 if (cmd == 1)
1238                         live.sg_flags |= NOFLSH;
1239                 stty(0, &live);
1240         }
1241         if (cmd == 2) {
1242                 gtty(0, &saved_settings);
1243         }
1244         if (cmd == 3) {
1245                 stty(0, &saved_settings);
1246         }
1247 }
1248 #endif
1249
1250
1251 /*
1252  * display_help()  -  help file viewer
1253  */
1254 void display_help(char *name)
1255 {
1256         formout(name);
1257 }
1258
1259
1260 /*
1261  * fmout()  -  Citadel text formatter and paginator
1262  */
1263 int fmout(
1264         int width,      /* screen width to use */
1265         FILE *fpin,     /* file to read from, or NULL to format given text */
1266         char *text,     /* Text to be formatted (when fpin is NULL) */
1267         FILE *fpout,    /* File to write to, or NULL to write to screen */
1268         char pagin,     /* nonzero if we should use the paginator */
1269         int height,     /* screen height to use */
1270         int starting_lp,/* starting value for lines_printed, -1 for global */
1271         char subst)     /* nonzero if we should use hypertext mode */
1272 {
1273         int a, b, c, old;
1274         int real = (-1);
1275         char aaa[140];
1276         char buffer[512];
1277         char *e;
1278         int eof_flag = 0;
1279
1280         num_urls = 0;   /* Start with a clean slate of embedded URL's */
1281
1282         if (starting_lp >= 0) {
1283                 lines_printed = starting_lp;
1284         }
1285         strcpy(aaa, "");
1286         old = 255;
1287         strcpy(buffer, "");
1288         c = 1;                  /* c is the current pos */
1289         e = text;               /* e is pointer to current pos */
1290
1291 FMTA:   while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1292                 if (fpin != NULL) {     /* read from file */
1293                         if (feof(fpin))
1294                                 eof_flag = 1;
1295                         if (eof_flag == 0) {
1296                                 a = getc(fpin);
1297                                 buffer[strlen(buffer) + 1] = 0;
1298                                 buffer[strlen(buffer)] = a;
1299                         }
1300                 } else {        /* read from text */
1301                         if (!*e) {
1302                                 eof_flag = 1;
1303                                 while (isspace(buffer[strlen(buffer) - 1]))
1304                                         buffer[strlen(buffer) - 1] = 0;
1305                                 buffer[strlen(buffer) + 1] = 0;
1306                                 buffer[strlen(buffer)] = 10;
1307                         }
1308                         if (eof_flag == 0) {
1309                                 a = *e++;
1310                                 buffer[strlen(buffer) + 1] = 0;
1311                                 buffer[strlen(buffer)] = a;
1312                         }
1313                 }
1314         }
1315
1316         if ( (!strncasecmp(buffer, "http://", 7))
1317            || (!strncasecmp(buffer, "ftp://", 6)) ) {
1318                 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1319                 for (a=0; a<strlen(urls[num_urls]); ++a) {
1320                         b = urls[num_urls][a];
1321                         if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1322                            || (b==13) || (b==9) || (b=='\"') )
1323                                 urls[num_urls][a] = 0;
1324                 }
1325                 ++num_urls;
1326         }
1327
1328         buffer[strlen(buffer) + 1] = 0;
1329         a = buffer[0];
1330         strcpy(buffer, &buffer[1]);
1331
1332         old = real;
1333         real = a;
1334         if (a <= 0)
1335                 goto FMTEND;
1336
1337         if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1338                 a = 32;
1339         if (((old == 13) || (old == 10)) && (isspace(real))) {
1340                 if (fpout) {
1341                         fprintf(fpout, "\n");
1342                 } else {
1343                         scr_printf("\n");
1344                         ++lines_printed;
1345                         lines_printed = checkpagin(lines_printed, pagin, height);
1346                 }
1347                 c = 1;
1348         }
1349         if (a > 126)
1350                 goto FMTA;
1351
1352         if (a > 32) {
1353                 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1354                         if (fpout) {
1355                                 fprintf(fpout, "\n%s", aaa);
1356                         } else {
1357                                 scr_printf("\n%s", aaa);
1358                                 ++lines_printed;
1359                                 lines_printed = checkpagin(lines_printed, pagin, height);
1360                         }
1361                         c = strlen(aaa);
1362                         aaa[0] = 0;
1363                 }
1364                 b = strlen(aaa);
1365                 aaa[b] = a;
1366                 aaa[b + 1] = 0;
1367         }
1368         if (a == 32) {
1369                 if ((strlen(aaa) + c) > (width - 1)) {
1370                         c = 1;
1371                         if (fpout) {
1372                                 fprintf(fpout, "\n");
1373                         } else {
1374                                 scr_printf("\n");
1375                                 ++lines_printed;
1376                                 lines_printed = checkpagin(lines_printed, pagin, height);
1377                         }
1378                 }
1379                 if (fpout) {
1380                         fprintf(fpout, "%s ", aaa);
1381                 } else {
1382                         scr_printf("%s ", aaa);
1383                 }
1384                 ++c;
1385                 c = c + strlen(aaa);
1386                 strcpy(aaa, "");
1387                 goto FMTA;
1388         }
1389         if ((a == 13) || (a == 10)) {
1390                 if (fpout) {
1391                         fprintf(fpout, "%s\n", aaa);
1392                 } else {
1393                         scr_printf("%s\n", aaa);
1394                         ++lines_printed;
1395                         lines_printed = checkpagin(lines_printed, pagin, height);
1396                 }
1397                 c = 1;
1398                 if (sigcaught) goto OOPS;
1399                 strcpy(aaa, "");
1400                 goto FMTA;
1401         }
1402         goto FMTA;
1403
1404         /* keypress caught; drain the server */
1405 OOPS:   /* do {
1406                 serv_gets(aaa);
1407         } while (strcmp(aaa, "000")); */
1408
1409 FMTEND:
1410         if (fpout) {
1411                 fprintf(fpout, "\n");
1412         } else {
1413                 scr_printf("\n");
1414                 ++lines_printed;
1415                 lines_printed = checkpagin(lines_printed, pagin, height);
1416         }
1417         return (sigcaught);
1418 }
1419
1420
1421 /*
1422  * support ANSI color if defined
1423  */
1424 void color(int colornum)
1425 {
1426         static int is_bold = 0;
1427         static int hold_color, current_color;
1428
1429         if (colornum == COLOR_PUSH) {
1430                 hold_color = current_color;
1431                 return;
1432         }
1433
1434         if (colornum == COLOR_POP) {
1435                 color(hold_color);
1436                 return;
1437         }
1438
1439         current_color = colornum;
1440         if (enable_color) {
1441 #ifdef HAVE_CURSES_H
1442                 if (scr_color(colornum))
1443                         return;
1444 #endif
1445                 /* When switching to dim white, actually output an 'original
1446                  * pair' sequence -- this looks better on black-on-white
1447                  * terminals.
1448                  */
1449                 if (colornum == DIM_WHITE)
1450                         printf("\033[39;49m");
1451                 else
1452                         printf("\033[3%d;40m", (colornum & 7));
1453
1454                 if ((colornum >= 8) && (is_bold == 0)) {
1455                         printf("\033[1m");
1456                         is_bold = 1;
1457                 } else if ((colornum < 8) && (is_bold == 1)) {
1458                         printf("\033[0m");
1459                         is_bold = 0;
1460                 }
1461                 scr_flush();
1462         }
1463 }
1464
1465 void cls(int colornum)
1466 {
1467         if (enable_color) {
1468                 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1469                 scr_flush();
1470         }
1471 }
1472
1473
1474 /*
1475  * Detect whether ANSI color is available (answerback)
1476  */
1477 void send_ansi_detect(void)
1478 {
1479         if (rc_ansi_color == 2) {
1480                 printf("\033[c");
1481                 scr_flush();
1482                 time(&AnsiDetect);
1483         }
1484 }
1485
1486 void look_for_ansi(void)
1487 {
1488         fd_set rfds;
1489         struct timeval tv;
1490         char abuf[512];
1491         time_t now;
1492         int a;
1493
1494         if (rc_ansi_color == 0) {
1495                 enable_color = 0;
1496         } else if (rc_ansi_color == 1) {
1497                 enable_color = 1;
1498         } else if (rc_ansi_color == 2) {
1499
1500                 /* otherwise, do the auto-detect */
1501
1502                 strcpy(abuf, "");
1503
1504                 time(&now);
1505                 if ((now - AnsiDetect) < 2)
1506                         sleep(1);
1507
1508                 do {
1509                         FD_ZERO(&rfds);
1510                         FD_SET(0, &rfds);
1511                         tv.tv_sec = 0;
1512                         tv.tv_usec = 1;
1513
1514                         select(1, &rfds, NULL, NULL, &tv);
1515                         if (FD_ISSET(0, &rfds)) {
1516                                 abuf[strlen(abuf) + 1] = 0;
1517                                 read(0, &abuf[strlen(abuf)], 1);
1518                         }
1519                 } while (FD_ISSET(0, &rfds));
1520
1521                 for (a = 0; a < strlen(abuf); ++a) {
1522                         if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1523                             && (abuf[a + 2] == '?')) {
1524                                 enable_color = 1;
1525                         }
1526                 }
1527         }
1528 }
1529
1530
1531 /*
1532  * Display key options (highlight hotkeys inside angle brackets)
1533  */
1534 void keyopt(char *buf) {
1535         int i;
1536
1537         color(DIM_WHITE);
1538         for (i=0; i<strlen(buf); ++i) {
1539                 if (buf[i]=='<') {
1540                         scr_putc(buf[i]);
1541                         color(BRIGHT_MAGENTA);
1542                 } else {
1543                         if (buf[i]=='>') {
1544                                 color(DIM_WHITE);
1545                         }
1546                         scr_putc(buf[i]);
1547                 }
1548         }
1549         color(DIM_WHITE);
1550 }
1551
1552
1553
1554 /*
1555  * Present a key-menu line choice type of thing
1556  */
1557 char keymenu(char *menuprompt, char *menustring) {
1558         int i, c, a;
1559         int choices;
1560         int do_prompt = 0;
1561         char buf[1024];
1562         int ch;
1563         int display_prompt = 1;
1564
1565         choices = num_tokens(menustring, '|');
1566
1567         if (menuprompt != NULL) do_prompt = 1;
1568         if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1569
1570         while (1) {
1571                 if (display_prompt) {
1572                         if (do_prompt) {
1573                                 scr_printf("%s ", menuprompt);
1574                         } 
1575                         else {
1576                                 for (i=0; i<choices; ++i) {
1577                                         extract(buf, menustring, i);
1578                                         keyopt(buf);
1579                                         scr_printf(" ");
1580                                 }
1581                         }
1582                         scr_printf(" -> ");
1583                         display_prompt = 0;
1584                 }
1585                 ch = lkey();
1586         
1587                 if ( (do_prompt) && (ch=='?') ) {
1588                         scr_printf("\rOne of...                               ");
1589                         scr_printf("                                      \n");
1590                         for (i=0; i<choices; ++i) {
1591                                 extract(buf, menustring, i);
1592                                 scr_printf("   ");
1593                                 keyopt(buf);
1594                                 scr_printf("\n");
1595                         }
1596                         scr_printf("\n");
1597                         display_prompt = 1;
1598                 }
1599
1600                 for (i=0; i<choices; ++i) {
1601                         extract(buf, menustring, i);
1602                         for (c=1; c<strlen(buf); ++c) {
1603                                 if ( (ch == tolower(buf[c]))
1604                                    && (buf[c-1]=='<')
1605                                    && (buf[c+1]=='>') ) {
1606                                         for (a=0; a<strlen(buf); ++a) {
1607                                                 if ( (a!=(c-1)) && (a!=(c+1))) {
1608                                                         scr_putc(buf[a]);
1609                                                 }
1610                                         }
1611                                         scr_printf("\n\n");
1612                                         return ch;
1613                                 }
1614                         }
1615                 }
1616         }
1617 }