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