* strlen holy war: loops. in loops it's very evil. the easy ones go away now.
[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; !IsEmptyStr(&buf[i]); ++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; !IsEmptyStr(&str[i]); ++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; !IsEmptyStr(&buf[p]); ++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; !IsEmptyStr(&cmdstr[a]); ++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         char *ptr;
983
984         strcpy(exp, strbuf);
985
986         ptr = exp;
987         while (!IsEmptyStr(ptr)){
988                 if (*ptr == '&') {
989
990                         /* dont echo these non mnemonic command keys */
991                         int noecho = *(ptr+1) == '<' || *(ptr+1) == '>' || 
992                                 *(ptr+1) == '+' || *(ptr+1) == '-';
993
994                         if (mode == 0) {
995                                 strcpy(ptr, ptr + 1 + noecho);
996                         }
997                         if (mode == 1) {
998                                 *ptr = '<';
999                                 strcpy(buf, ptr + 2);
1000                                 *(ptr + 2) = '>';
1001                                 *(ptr+ 3) = 0;
1002                                 strcat(exp, buf);
1003                         }
1004                 }
1005                 if (!strncmp(ptr, "^r", 2)) {
1006                         strcpy(buf, exp);
1007                         strcpy(ptr, room_name);
1008                         strcat(exp, &buf[ptr - exp + 2]);
1009                 }
1010                 if (!strncmp(ptr, "^c", 2)) {
1011                         *ptr = ',';
1012                         strcpy(ptr + 1], ptr + 2]);
1013                 }
1014         }
1015
1016         return (exp);
1017 }
1018
1019
1020
1021 /*
1022  * Comparison function to determine if entered commands match a
1023  * command loaded from the config file.
1024  */
1025 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
1026 {
1027         int a;
1028         int cmdax;
1029
1030         cmdax = 0;
1031         if (is_room_aide)
1032                 cmdax = 1;
1033         if (axlevel >= 6)
1034                 cmdax = 2;
1035
1036         for (a = 0; a < ncomp; ++a) {
1037                 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
1038                     || (cptr->c_axlevel > cmdax))
1039                         return (0);
1040         }
1041         return (1);
1042 }
1043
1044
1045 /*
1046  * This function returns 1 if a given command requires a string input
1047  */
1048 int requires_string(struct citcmd *cptr, int ncomp)
1049 {
1050         int a;
1051         char buf[64];
1052
1053         strcpy(buf, cptr->c_keys[ncomp - 1]);
1054         for (a = 0; !IsEmptyStr(&buf[a]); ++a) {
1055                 if (buf[a] == ':')
1056                         return (1);
1057         }
1058         return (0);
1059 }
1060
1061
1062 /*
1063  * Input a command at the main prompt.
1064  * This function returns an integer command number.  If the command prompts
1065  * for a string then it is placed in the supplied buffer.
1066  */
1067 int getcmd(CtdlIPC *ipc, char *argbuf)
1068 {
1069         char cmdbuf[5];
1070         int cmdspaces[5];
1071         int cmdpos;
1072         int ch;
1073         int a;
1074         int got;
1075         int this_lazy_cmd;
1076         struct citcmd *cptr;
1077
1078         /*
1079          * Starting a new command now, so set sigcaught to 0.  This variable
1080          * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1081          * been interrupted by a keypress.
1082          */
1083         sigcaught = 0;
1084
1085         /* Switch color support on or off if we're in user mode */
1086         if (rc_ansi_color == 3) {
1087                 if (userflags & US_COLOR)
1088                         enable_color = 1;
1089                 else
1090                         enable_color = 0;
1091         }
1092         /* if we're running in idiot mode, display a cute little menu */
1093         IFNEXPERT formout(ipc, "mainmenu");
1094
1095         print_instant();
1096         strcpy(argbuf, "");
1097         cmdpos = 0;
1098         for (a = 0; a < 5; ++a)
1099                 cmdbuf[a] = 0;
1100         /* now the room prompt... */
1101         ok_to_interrupt = 1;
1102         color(BRIGHT_WHITE);
1103         scr_printf("\n%s", room_name);
1104         color(DIM_WHITE);
1105         scr_printf("%c ", room_prompt(room_flags));
1106         scr_flush();
1107
1108         while (1) {
1109                 ch = inkey();
1110                 ok_to_interrupt = 0;
1111
1112                 /* Handle the backspace key, but only if there's something
1113                  * to backspace over...
1114                  */
1115                 if ((ch == 8) && (cmdpos > 0)) {
1116                         back(cmdspaces[cmdpos - 1] + 1);
1117                         cmdbuf[cmdpos] = 0;
1118                         --cmdpos;
1119                 }
1120                 /* Spacebar invokes "lazy traversal" commands */
1121                 if ((ch == 32) && (cmdpos == 0)) {
1122                         this_lazy_cmd = next_lazy_cmd;
1123                         if (this_lazy_cmd == 13)
1124                                 next_lazy_cmd = 5;
1125                         if (this_lazy_cmd == 5)
1126                                 next_lazy_cmd = 13;
1127                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1128                                 if (cptr->c_cmdnum == this_lazy_cmd) {
1129                                         for (a = 0; a < 5; ++a)
1130                                                 if (cptr->c_keys[a][0] != 0)
1131                                                         scr_printf("%s ", cmd_expand(
1132                                                                                         cptr->c_keys[a], 0));
1133                                         scr_printf("\n");
1134                                         return (this_lazy_cmd);
1135                                 }
1136                         }
1137                         scr_printf("\n");
1138                         return (this_lazy_cmd);
1139                 }
1140                 /* Otherwise, process the command */
1141                 cmdbuf[cmdpos] = tolower(ch);
1142
1143                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1144                         if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1145
1146                                 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1147                                 cmdspaces[cmdpos] = strlen(
1148                                     cmd_expand(cptr->c_keys[cmdpos], 0));
1149                                 if (cmdpos < 4)
1150                                         if ((cptr->c_keys[cmdpos + 1]) != 0)
1151                                                 scr_putc(' ');
1152                                 ++cmdpos;
1153                         }
1154                 }
1155
1156                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1157                         if (cmdmatch(cmdbuf, cptr, 5)) {
1158                                 /* We've found our command. */
1159                                 if (requires_string(cptr, cmdpos)) {
1160                                         ctdl_getline(argbuf, 64);
1161                                 } else {
1162                                         scr_printf("\n");
1163                                 }
1164
1165                                 /* If this command is one that changes rooms,
1166                                  * then the next lazy-command (space bar)
1167                                  * should be "read new" instead of "goto"
1168                                  */
1169                                 if ((cptr->c_cmdnum == 5)
1170                                     || (cptr->c_cmdnum == 6)
1171                                     || (cptr->c_cmdnum == 47)
1172                                     || (cptr->c_cmdnum == 52)
1173                                     || (cptr->c_cmdnum == 16)
1174                                     || (cptr->c_cmdnum == 20))
1175                                         next_lazy_cmd = 13;
1176
1177                                 /* If this command is "read new"
1178                                  * then the next lazy-command (space bar)
1179                                  * should be "goto"
1180                                  */
1181                                 if (cptr->c_cmdnum == 13)
1182                                         next_lazy_cmd = 5;
1183
1184                                 return (cptr->c_cmdnum);
1185
1186                         }
1187                 }
1188
1189                 if (ch == '?') {
1190                         pprintf("\rOne of ...                         \n");
1191                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1192                                 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1193                                         for (a = 0; a < 5; ++a) {
1194                                            keyopt(cmd_expand(cptr->c_keys[a], 1));
1195                                    pprintf(" ");
1196                                         }
1197                                         pprintf("\n");
1198                                 }
1199                         }
1200                 sigcaught = 0;
1201
1202                         pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1203                         got = 0;
1204                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1205                                 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1206                                         for (a = 0; a < cmdpos; ++a) {
1207                                                 pprintf("%s ",
1208                                                        cmd_expand(cptr->c_keys[a], 0));
1209                                         }
1210                                         got = 1;
1211                                 }
1212                         }
1213                 }
1214         }
1215
1216 }
1217
1218
1219
1220
1221
1222 /*
1223  * set tty modes.  commands are:
1224  * 
1225  * 01- set to Citadel mode
1226  * 2 - save current settings for later restoral
1227  * 3 - restore saved settings
1228  */
1229 #ifdef HAVE_TERMIOS_H
1230 void stty_ctdl(int cmd)
1231 {                               /* SysV version of stty_ctdl() */
1232         struct termios live;
1233         static struct termios saved_settings;
1234         static int last_cmd = 0;
1235
1236         if (cmd == SB_LAST)
1237                 cmd = last_cmd;
1238         else
1239                 last_cmd = cmd;
1240
1241         if ((cmd == 0) || (cmd == 1)) {
1242                 tcgetattr(0, &live);
1243                 live.c_iflag = ISTRIP | IXON | IXANY;
1244                 live.c_oflag = OPOST | ONLCR;
1245                 live.c_lflag = ISIG | NOFLSH;
1246
1247                 live.c_cc[VINTR] = 0;
1248                 live.c_cc[VQUIT] = 0;
1249
1250 #ifdef hpux
1251                 live.c_cc[VMIN] = 0;
1252                 live.c_cc[VTIME] = 0;
1253 #endif
1254
1255                 /* do we even need this stuff anymore? */
1256                 /* live.c_line=0; */
1257                 live.c_cc[VERASE] = 8;
1258                 live.c_cc[VKILL] = 24;
1259                 live.c_cc[VEOF] = 1;
1260                 live.c_cc[VEOL] = 255;
1261                 live.c_cc[VEOL2] = 0;
1262                 live.c_cc[VSTART] = 0;
1263                 tcsetattr(0, TCSADRAIN, &live);
1264         }
1265         if (cmd == 2) {
1266                 tcgetattr(0, &saved_settings);
1267         }
1268         if (cmd == 3) {
1269                 tcsetattr(0, TCSADRAIN, &saved_settings);
1270         }
1271
1272 }
1273 #else
1274 void stty_ctdl(int cmd)
1275 {                               /* BSD version of stty_ctdl() */
1276         struct sgttyb live;
1277         static struct sgttyb saved_settings;
1278         static int last_cmd = 0;
1279
1280         if (cmd == SB_LAST)
1281                 cmd = last_cmd;
1282         else
1283                 last_cmd = cmd;
1284
1285         if ((cmd == 0) || (cmd == 1)) {
1286                 gtty(0, &live);
1287                 live.sg_flags |= CBREAK;
1288                 live.sg_flags |= CRMOD;
1289                 live.sg_flags |= NL1;
1290                 live.sg_flags &= ~ECHO;
1291                 if (cmd == 1)
1292                         live.sg_flags |= NOFLSH;
1293                 stty(0, &live);
1294         }
1295         if (cmd == 2) {
1296                 gtty(0, &saved_settings);
1297         }
1298         if (cmd == 3) {
1299                 stty(0, &saved_settings);
1300         }
1301 }
1302 #endif
1303
1304
1305 /*
1306  * display_help()  -  help file viewer
1307  */
1308 void display_help(CtdlIPC *ipc, char *name)
1309 {
1310         formout(ipc, name);
1311 }
1312
1313
1314 /*
1315  * fmout() - Citadel text formatter and paginator
1316  */
1317 int fmout(
1318         int width,      /* screen width to use */
1319         FILE *fpin,     /* file to read from, or NULL to format given text */
1320         char *text,     /* text to be formatted (when fpin is NULL */
1321         FILE *fpout,    /* file to write to, or NULL to write to screen */
1322         char pagin,     /* nonzero if we should use the paginator */
1323         int height,     /* screen height to use */
1324         int starting_lp,/* starting value for lines_printed, -1 for global */
1325         int subst)      /* nonzero if we should use hypertext mode */
1326 {
1327         char *buffer = NULL;    /* The current message */
1328         char *word = NULL;      /* What we are about to actually print */
1329         char *e;                /* Pointer to position in text */
1330         char old = 0;           /* The previous character */
1331         int column = 0;         /* Current column */
1332         size_t i;               /* Generic counter */
1333
1334         /* Space for a single word, which can be at most screenwidth */
1335         word = (char *)calloc(1, width);
1336         if (!word) {
1337                 err_printf("Can't alloc memory to print message: %s!\n",
1338                                 strerror(errno));
1339                 logoff(NULL, 3);
1340         }
1341
1342         /* Read the entire message body into memory */
1343         if (fpin) {
1344                 buffer = load_message_from_file(fpin);
1345                 if (!buffer) {
1346                         err_printf("Can't print message: %s!\n",
1347                                         strerror(errno));
1348                         logoff(NULL, 3);
1349                 }
1350         } else {
1351                 buffer = text;
1352         }
1353         e = buffer;
1354
1355         if (starting_lp >= 0)
1356                 lines_printed = starting_lp;
1357
1358         /* Run the message body */
1359         while (*e) {
1360                 /* Catch characters that shouldn't be there at all */
1361                 if (*e == '\r') {
1362                         e++;
1363                         continue;
1364                 }
1365                 /* First, are we looking at a newline? */
1366                 if (*e == '\n') {
1367                         e++;
1368                         if (*e == ' ') {        /* Paragraph */
1369                                 if (fpout) {
1370                                         fprintf(fpout, "\n");
1371                                 } else {
1372                                         scr_printf("\n");
1373                                         ++lines_printed;
1374                                         lines_printed = checkpagin(lines_printed, pagin, height);
1375                                 }
1376                                 column = 0;
1377                         } else if (old != ' ') {/* Don't print two spaces */
1378                                 if (fpout) {
1379                                         fprintf(fpout, " ");
1380                                 } else {
1381                                         scr_printf(" ");
1382                                 }
1383                                 column++;
1384                         }
1385                         old = '\n';
1386                         continue;
1387                 }
1388
1389                 /* Are we looking at a nonprintable?
1390                  * (This section is now commented out because we could be displaying
1391                  * a character set like UTF-8 or ISO-8859-1.)
1392                 if ( (*e < 32) || (*e > 126) ) {
1393                         e++;
1394                         continue;
1395                 } */
1396
1397                 /* Or are we looking at a space? */
1398                 if (*e == ' ') {
1399                         e++;
1400                         if (column >= width - 1) {
1401                                 /* Are we in the rightmost column? */
1402                                 if (fpout) {
1403                                         fprintf(fpout, "\n");
1404                                 } else {
1405                                         scr_printf("\n");
1406                                         ++lines_printed;
1407                                         lines_printed = checkpagin(lines_printed, pagin, height);
1408                                 }
1409                                 column = 0;
1410                         } else if (!(column == 0 && old == ' ')) {
1411                                 /* Eat only the first space on a line */
1412                                 if (fpout) {
1413                                         fprintf(fpout, " ");
1414                                 } else {
1415                                         scr_printf(" ");
1416                                 }
1417                                 column++;
1418                         }
1419                         /* ONLY eat the FIRST space on a line */
1420                         old = ' ';
1421                         continue;
1422                 }
1423                 old = *e;
1424
1425                 /* Read a word, slightly messy */
1426                 i = 0;
1427                 while (e[i]) {
1428                         if (!isprint(e[i]) && !isspace(e[i]))
1429                                 e[i] = ' ';
1430                         if (isspace(e[i]))
1431                                 break;
1432                         i++;
1433                 }
1434
1435                 /* We should never see these, but... slightly messy */
1436                 if (e[i] == '\t' || e[i] == '\f' || e[i] == '\v')
1437                         e[i] = ' ';
1438
1439                 /* Break up really long words */
1440                 /* TODO: auto-hyphenation someday? */
1441                 if (i >= width) 
1442                         i = width - 1;
1443                 strncpy(word, e, i);
1444                 word[i] = 0;
1445
1446                 /* Decide where to print the word */
1447                 if (column + i >= width) {
1448                         /* Wrap to the next line */
1449                         if (fpout) {
1450                                 fprintf(fpout, "\n");
1451                         } else {
1452                                 scr_printf("\n");
1453                                 ++lines_printed;
1454                                 lines_printed = checkpagin(lines_printed, pagin, height);
1455                         }
1456                         column = 0;
1457                 }
1458
1459                 /* Print the word */
1460                 if (fpout) {
1461                         fprintf(fpout, "%s", word);
1462                 } else {
1463                         scr_printf("%s", word);
1464                 }
1465                 column += i;
1466                 e += i;         /* Start over with the whitepsace! */
1467         }
1468
1469         free(word);
1470         if (fpin)               /* We allocated this, remember? */
1471                 free(buffer);
1472
1473         /* Is this necessary?  It makes the output kind of spacey. */
1474         if (fpout) {
1475                 fprintf(fpout, "\n");
1476         } else {
1477                 scr_printf("\n");
1478                 ++lines_printed;
1479                 lines_printed = checkpagin(lines_printed, pagin, height);
1480         }
1481
1482         return sigcaught;
1483 }
1484
1485
1486 /*
1487  * support ANSI color if defined
1488  */
1489 void color(int colornum)
1490 {
1491         static int hold_color;
1492         static int current_color;
1493
1494         if (colornum == COLOR_PUSH) {
1495                 hold_color = current_color;
1496                 return;
1497         }
1498
1499         if (colornum == COLOR_POP) {
1500                 color(hold_color);
1501                 return;
1502         }
1503
1504         current_color = colornum;
1505         if (enable_color) {
1506 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
1507                 if (scr_color(colornum))
1508                         return;
1509 #endif
1510                 /* When switching to dim white, actually output an 'original
1511                  * pair' sequence -- this looks better on black-on-white
1512                  * terminals. - Changed to ORIGINAL_PAIR as this actually
1513                  * wound up looking horrible on black-on-white terminals, not
1514                  * to mention transparent terminals.
1515                  */
1516                 if (colornum == ORIGINAL_PAIR)
1517                         printf("\033[0;39;49m");
1518                 else
1519                         printf("\033[%d;3%d;4%dm", 
1520                                         (colornum & 8) ? 1 : 0,
1521                                         (colornum & 7),
1522                                         rc_color_use_bg);
1523
1524                 scr_flush();
1525         }
1526 }
1527
1528 void cls(int colornum)
1529 {
1530         if (enable_color) {
1531                 printf("\033[4%dm\033[2J\033[H\033[0m",
1532                                 colornum ? colornum : rc_color_use_bg);
1533                 scr_flush();
1534         }
1535 }
1536
1537
1538 /*
1539  * Detect whether ANSI color is available (answerback)
1540  */
1541 void send_ansi_detect(void)
1542 {
1543         if (rc_ansi_color == 2) {
1544                 printf("\033[c");
1545                 scr_flush();
1546                 time(&AnsiDetect);
1547         }
1548 }
1549
1550 void look_for_ansi(void)
1551 {
1552         fd_set rfds;
1553         struct timeval tv;
1554         char abuf[512];
1555         time_t now;
1556         int a;
1557
1558         if (rc_ansi_color == 0) {
1559                 enable_color = 0;
1560         } else if (rc_ansi_color == 1) {
1561                 enable_color = 1;
1562         } else if (rc_ansi_color == 2) {
1563
1564                 /* otherwise, do the auto-detect */
1565
1566                 strcpy(abuf, "");
1567
1568                 time(&now);
1569                 if ((now - AnsiDetect) < 2)
1570                         sleep(1);
1571
1572                 do {
1573                         FD_ZERO(&rfds);
1574                         FD_SET(0, &rfds);
1575                         tv.tv_sec = 0;
1576                         tv.tv_usec = 1;
1577
1578                         select(1, &rfds, NULL, NULL, &tv);
1579                         if (FD_ISSET(0, &rfds)) {
1580                                 abuf[strlen(abuf) + 1] = 0;
1581                                 read(0, &abuf[strlen(abuf)], 1);
1582                         }
1583                 } while (FD_ISSET(0, &rfds));
1584
1585                 for (a = 0; !IsEmptyStr(&abuf[a]); ++a) {
1586                         if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1587                             && (abuf[a + 2] == '?')) {
1588                                 enable_color = 1;
1589                         }
1590                 }
1591         }
1592 }
1593
1594
1595 /*
1596  * Display key options (highlight hotkeys inside angle brackets)
1597  */
1598 void keyopt(char *buf) {
1599         int i;
1600
1601         color(DIM_WHITE);
1602         for (i=0; !IsEmptyStr(&buf[i]); ++i) {
1603                 if (buf[i]=='<') {
1604                         pprintf("%c", buf[i]);
1605                         color(BRIGHT_MAGENTA);
1606                 } else {
1607                         if (buf[i]=='>'&& buf[i+1] != '>') {
1608                                 color(DIM_WHITE);
1609                         }
1610                         pprintf("%c", buf[i]);
1611                 }
1612         }
1613         color(DIM_WHITE);
1614 }
1615
1616
1617
1618 /*
1619  * Present a key-menu line choice type of thing
1620  */
1621 char keymenu(char *menuprompt, char *menustring) {
1622         int i, c, a;
1623         int choices;
1624         int do_prompt = 0;
1625         char buf[1024];
1626         int ch;
1627         int display_prompt = 1;
1628
1629         choices = num_tokens(menustring, '|');
1630
1631         if (menuprompt != NULL) do_prompt = 1;
1632         if (menuprompt != NULL) if (IsEmptyStr(menuprompt)) do_prompt = 0;
1633
1634         while (1) {
1635                 if (display_prompt) {
1636                         if (do_prompt) {
1637                                 scr_printf("%s ", menuprompt);
1638                         } 
1639                         else {
1640                                 for (i=0; i<choices; ++i) {
1641                                         extract_token(buf, menustring, i, '|', sizeof buf);
1642                                         keyopt(buf);
1643                                         scr_printf(" ");
1644                                 }
1645                         }
1646                         scr_printf("-> ");
1647                         display_prompt = 0;
1648                 }
1649                 ch = lkey();
1650         
1651                 if ( (do_prompt) && (ch=='?') ) {
1652                         scr_printf("\rOne of...                               ");
1653                         scr_printf("                                      \n");
1654                         for (i=0; i<choices; ++i) {
1655                                 extract_token(buf, menustring, i, '|', sizeof buf);
1656                                 scr_printf("   ");
1657                                 keyopt(buf);
1658                                 scr_printf("\n");
1659                         }
1660                         scr_printf("\n");
1661                         display_prompt = 1;
1662                 }
1663
1664                 for (i=0; i<choices; ++i) {
1665                         extract_token(buf, menustring, i, '|', sizeof buf);
1666                         for (c=1; !IsEmptyStr(&buf[c]); ++c) {
1667                                 if ( (ch == tolower(buf[c]))
1668                                    && (buf[c-1]=='<')
1669                                    && (buf[c+1]=='>') ) {
1670                                         for (a=0; !IsEmptyStr(&buf[a]); ++a) {
1671                                                 if ( (a!=(c-1)) && (a!=(c+1))) {
1672                                                         scr_putc(buf[a]);
1673                                                 }
1674                                         }
1675                                         scr_printf("\n");
1676                                         return ch;
1677                                 }
1678                         }
1679                 }
1680         }
1681 }