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