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