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