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