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