]> code.citadel.org Git - citadel.git/blob - citadel/commands.c
* Changed some of the rev-level sensitive stuff to look at the actual version
[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 #include <sys/time.h>
18
19 #ifdef HAVE_TERMIOS_H
20 #include <termios.h>
21 #else
22 #include <sgtty.h>
23 #endif
24
25 #ifdef HAVE_SYS_SELECT_H
26 #include <sys/select.h>
27 #endif
28
29 #ifdef THREADED_CLIENT
30 #include <pthread.h>
31 #endif
32
33 #include <signal.h>
34 #include <errno.h>
35 #include <stdarg.h>
36 #include "citadel.h"
37 #include "commands.h"
38 #include "messages.h"
39 #include "citadel_decls.h"
40 #include "routines.h"
41 #include "routines2.h"
42 #include "tools.h"
43 #include "rooms.h"
44 #ifndef HAVE_SNPRINTF
45 #include "snprintf.h"
46 #endif
47
48 struct citcmd {
49         struct citcmd *next;
50         int c_cmdnum;
51         int c_axlevel;
52         char c_keys[5][64];
53 };
54
55 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
56
57
58 int rc_exp_beep;
59 char rc_exp_cmd[256];
60 int rc_allow_attachments;
61 int rc_display_message_numbers;
62 int rc_force_mail_prompts;
63 int rc_remember_passwords;
64 int rc_ansi_color;
65 int num_urls = 0;
66 char urls[MAXURLS][256];
67 char rc_url_cmd[256];
68
69 char *gl_string;
70 int next_lazy_cmd = 5;
71
72 int lines_printed = 0;          /* line count for paginator */
73 extern int screenwidth, screenheight;
74
75 struct citcmd *cmdlist = NULL;
76
77
78 /* these variables are local to this module */
79 char keepalives_enabled = KA_YES;       /* send NOOPs to server when idle */
80 int ok_to_interrupt = 0;                /* print express msgs asynchronously */
81 time_t AnsiDetect;                      /* when did we send the detect code? */
82 int enable_color = 0;                   /* nonzero for ANSI color */
83
84
85
86
87
88 /*
89  * Check to see if we need to pause at the end of a screen.
90  * If we do, we have to disable server keepalives during the pause because
91  * we are probably in the middle of a server operation and the NOOP command
92  * would confuse everything.
93  */
94 int checkpagin(int lp, int pagin, int height)
95 {
96         if (!pagin) return(0);
97         if (lp>=(height-1)) {
98                 set_keepalives(KA_NO);
99                 hit_any_key();
100                 set_keepalives(KA_YES);
101                 return(0);
102                 }
103         return(lp);
104         }
105
106
107
108
109 /*
110  * pprintf()  ...   paginated version of printf()
111  */
112 void pprintf(const char *format, ...) {   
113         va_list arg_ptr;
114         static char buf[4096];  /* static for performance, change if needed */
115         int i;
116   
117         va_start(arg_ptr, format);   
118         vsprintf(buf, format, arg_ptr);   
119         va_end(arg_ptr);   
120
121         for (i=0; i<strlen(buf); ++i) {
122                 putc(buf[i], stdout);
123                 if (buf[i]==10) {
124                         ++lines_printed;
125                         lines_printed = checkpagin(lines_printed,
126                                 (userflags & US_PAGINATOR),
127                                 screenheight);
128                 }
129         }
130 }   
131
132
133
134 /*
135  * print_express()  -  print express messages if there are any
136  */
137 void print_express(void)
138 {
139         char buf[256];
140         FILE *outpipe;
141
142         if (express_msgs == 0)
143                 return;
144         express_msgs = 0;
145         serv_puts("PEXP");
146         serv_gets(buf);
147         if (buf[0] != '1')
148                 return;
149
150         if (strlen(rc_exp_cmd) > 0) {
151                 outpipe = popen(rc_exp_cmd, "w");
152                 if (outpipe != NULL) {
153                         while (serv_gets(buf), strcmp(buf, "000")) {
154                                 fprintf(outpipe, "%s\n", buf);
155                         }
156                         pclose(outpipe);
157                         return;
158                 }
159         }
160         /* fall back to built-in express message display */
161         if (rc_exp_beep) {
162                 putc(7, stdout);
163         }
164         color(BRIGHT_RED);
165         printf("\r---\n");
166         while (serv_gets(buf), strcmp(buf, "000")) {
167                 printf("%s\n", buf);
168         }
169         printf("---\n");
170         color(BRIGHT_WHITE);
171 }
172
173
174 void set_keepalives(int s)
175 {
176         keepalives_enabled = (char) s;
177 }
178
179 /* 
180  * This loop handles the "keepalive" messages sent to the server when idling.
181  */
182
183 static time_t idlet = 0;
184 static void really_do_keepalive(void) {
185         char buf[256];
186
187         time(&idlet);
188         if (keepalives_enabled != KA_NO) {
189                 serv_puts("NOOP");
190                 if (keepalives_enabled == KA_YES) {
191                         serv_gets(buf);
192                         if (buf[3] == '*') {
193                                 express_msgs = 1;
194                                 if (ok_to_interrupt == 1) {
195                                         printf("\r%64s\r", "");
196                                         print_express();
197                                         printf("%s%c ", room_name,
198                                                room_prompt(room_flags));
199                                         fflush(stdout);
200                                 }
201                         }
202                 }
203         }
204 }
205
206 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
207    encapsulated interface; in theory there should be no need to touch these
208    globals outside of the async_ka_* functions. */
209
210 #ifdef THREADED_CLIENT
211 static pthread_t ka_thr_handle;
212 static int ka_thr_active = 0;
213 static int async_ka_enabled = 0;
214
215 static void *ka_thread(void *arg)
216 {
217         really_do_keepalive();
218         pthread_detach(ka_thr_handle);
219         ka_thr_active = 0;
220         return NULL;
221 }
222
223 /* start up a thread to handle a keepalive in the background */
224 static void async_ka_exec(void)
225 {
226         if (!ka_thr_active) {
227                 ka_thr_active = 1;
228                 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
229                         perror("pthread_create");
230                         exit(1);
231                 }
232         }
233 }
234 #endif /* THREADED_CLIENT */
235
236 static void do_keepalive(void)
237 {
238         time_t now;
239
240         time(&now);
241         if ((now - idlet) < ((long) S_KEEPALIVE))
242                 return;
243
244         /* Do a space-backspace to keep telnet sessions from idling out */
245         printf(" %c", 8);
246         fflush(stdout);
247
248 #ifdef THREADED_CLIENT
249         if (async_ka_enabled)
250                 async_ka_exec();
251         else
252 #endif
253                 really_do_keepalive();
254 }
255
256
257 /* Now the actual async-keepalve API that we expose to higher levels:
258    async_ka_start() and async_ka_end(). These do nothing when we don't have
259    threading enabled, so we avoid sprinkling ifdef's throughout the code. */
260
261 /* wait for a background keepalive to complete. this must be done before
262    attempting any further server requests! */
263 void async_ka_end(void)
264 {
265 #ifdef THREADED_CLIENT
266         if (ka_thr_active)
267                 pthread_join(ka_thr_handle, NULL);
268
269         async_ka_enabled--;
270 #endif
271 }
272
273 /* tell do_keepalive() that keepalives are asynchronous. */
274 void async_ka_start(void)
275 {
276 #ifdef THREADED_CLIENT
277         async_ka_enabled++;
278 #endif
279 }
280
281
282 int inkey(void)
283 {                               /* get a character from the keyboard, with   */
284         int a;                  /* the watchdog timer in effect if necessary */
285         fd_set rfds;
286         struct timeval tv;
287         time_t start_time, now;
288         char inbuf[2];
289
290         fflush(stdout);
291         lines_printed = 0;
292         time(&start_time);
293
294         do {
295
296                 /* This loop waits for keyboard input.  If the keepalive
297                  * timer expires, it sends a keepalive to the server if
298                  * necessary and then waits again.
299                  */
300                 do {
301                         do_keepalive();
302
303                         FD_ZERO(&rfds);
304                         FD_SET(0, &rfds);
305                         tv.tv_sec = S_KEEPALIVE;
306                         tv.tv_usec = 0;
307
308                         time(&now);
309                         if (((now - start_time) > SLEEPING)
310                             && (SLEEPING != 0) && (getppid() == 1)) {
311                                 printf("Sleeping? Call again.\n");
312                                 logoff(SIGALRM);
313                         }
314                         select(1, &rfds, NULL, NULL, &tv);
315                 } while (!FD_ISSET(0, &rfds));
316
317
318
319
320                 /* At this point, there's input, so fetch it.
321                  * (There's a hole in the bucket...)
322                  */
323                 read(0, inbuf, 1);
324                 a = inbuf[0];
325                 if (a == 127)
326                         a = 8;
327                 if (a > 126)
328                         a = 0;
329                 if (a == 10)
330                         a = 13;
331                 if (((a != 4) && (a != 13) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
332                     && ((a < 32) || (a > 126)))
333                         a = 0;
334         } while (a == 0);
335         return (a);
336 }
337
338
339 int yesno(void)
340 {                               /* Returns 1 for yes, 0 for no */
341         int a;
342         while (1) {
343                 a = inkey();
344                 a = tolower(a);
345                 if (a == 'y') {
346                         printf("Yes\n");
347                         return (1);
348                 }
349                 if (a == 'n') {
350                         printf("No\n");
351                         return (0);
352                 }
353         }
354 }
355
356 /* Returns 1 for yes, 0 for no, arg is default value */
357 int yesno_d(int d)
358 {
359         int a;
360         while (1) {
361                 a = inkey();
362                 a = tolower(a);
363                 if (a == 13)
364                         a = (d ? 'y' : 'n');
365                 if (a == 'y') {
366                         printf("Yes\n");
367                         return (1);
368                 }
369                 if (a == 'n') {
370                         printf("No\n");
371                         return (0);
372                 }
373         }
374 }
375
376
377
378
379 /* Gets a line from the terminal */
380 /* string == Pointer to string buffer */
381 /* lim == Maximum length - if negative, no-show */
382 void getline(char *string, int lim) 
383 {
384         int a, b;
385         char flag = 0;
386
387         if (lim < 0) {
388                 lim = (0 - lim);
389                 flag = 1;
390         }
391         strcpy(string, "");
392         gl_string = string;
393         async_ka_start();
394       GLA:a = inkey();
395         a = (a & 127);
396         if ((a == 8) && (strlen(string) == 0))
397                 goto GLA;
398         if ((a != 13) && (a != 8) && (strlen(string) == lim))
399                 goto GLA;
400         if ((a == 8) && (string[0] != 0)) {
401                 string[strlen(string) - 1] = 0;
402                 putc(8, stdout);
403                 putc(32, stdout);
404                 putc(8, stdout);
405                 goto GLA;
406         }
407         if ((a == 13) || (a == 10)) {
408                 putc(13, stdout);
409                 putc(10, stdout);
410                 async_ka_end();
411                 return;
412         }
413         if (a < 32)
414                 a = '.';
415         b = strlen(string);
416         string[b] = a;
417         string[b + 1] = 0;
418         if (flag == 0)
419                 putc(a, stdout);
420         if (flag == 1)
421                 putc('*', stdout);
422         goto GLA;
423 }
424
425
426 /*
427  * strprompt()  -  prompt for a string, print the existing value and
428  *                 allow the user to press return to keep it...
429  */
430 void strprompt(char *prompt, char *str, int len)
431 {
432         char buf[128];
433         print_express();
434         color(DIM_WHITE);
435         printf("%s ", prompt);
436         color(DIM_MAGENTA);
437         printf("[");
438         color(BRIGHT_MAGENTA);
439         printf("%s", str);
440         color(DIM_MAGENTA);
441         printf("]");
442         color(DIM_WHITE);
443         printf(": ");
444         color(BRIGHT_CYAN);
445         getline(buf, len);
446         if (buf[0] != 0)
447                 strcpy(str, buf);
448         color(DIM_WHITE);
449 }
450
451 /*
452  * boolprompt()  -  prompt for a yes/no, print the existing value and
453  *                  allow the user to press return to keep it...
454  */
455 int boolprompt(char *prompt, int prev_val)
456 {
457         int r;
458
459         color(DIM_WHITE);
460         printf("%s ", prompt);
461         color(DIM_MAGENTA);
462         printf(" [");
463         color(BRIGHT_MAGENTA);
464         printf("%s", (prev_val ? "Yes" : "No"));
465         color(DIM_MAGENTA);
466         printf("]: ");
467         color(BRIGHT_CYAN);
468         r = (yesno_d(prev_val));
469         color(DIM_WHITE);
470         return r;
471 }
472
473 /* 
474  * intprompt()  -  like strprompt(), except for an integer
475  *                 (note that it RETURNS the new value!)
476  */
477 int intprompt(char *prompt, int ival, int imin, int imax)
478 {
479         char buf[16];
480         int i;
481         int p;
482
483         do {
484                 i = ival;
485                 snprintf(buf, sizeof buf, "%d", i);
486                 strprompt(prompt, buf, 15);
487                 i = atoi(buf);
488                 for (p=0; p<strlen(buf); ++p) {
489                         if ( (!isdigit(buf[p]))
490                            && ( (buf[p]!='-') || (p!=0) )  )
491                                 i = imin - 1;
492                 }
493                 if (i < imin)
494                         printf("*** Must be no less than %d.\n", imin);
495                 if (i > imax)
496                         printf("*** Must be no more than %d.\n", imax);
497         } while ((i < imin) || (i > imax));
498         return (i);
499 }
500
501 /* 
502  * newprompt()  -  prompt for a string with no existing value
503  *                 (clears out string buffer first)
504  */
505 void newprompt(char *prompt, char *str, int len)
506 {
507         color(BRIGHT_MAGENTA);
508         printf("%s", prompt);
509         color(DIM_MAGENTA);
510         getline(str, len);
511         color(DIM_WHITE);
512 }
513
514
515 int lkey(void)
516 {                               /* returns a lower case value */
517         int a;
518         a = inkey();
519         if (isupper(a))
520                 a = tolower(a);
521         return (a);
522 }
523
524 /*
525  * parse the citadel.rc file
526  */
527 void load_command_set(void)
528 {
529         FILE *ccfile;
530         char buf[256];
531         struct citcmd *cptr;
532         struct citcmd *lastcmd = NULL;
533         int a, d;
534         int b = 0;
535
536
537         /* first, set up some defaults for non-required variables */
538
539         strcpy(editor_path, "");
540         strcpy(printcmd, "");
541         strcpy(rc_username, "");
542         strcpy(rc_password, "");
543         rc_floor_mode = 0;
544         rc_exp_beep = 1;
545         rc_allow_attachments = 0;
546         rc_remember_passwords = 0;
547         strcpy(rc_exp_cmd, "");
548         rc_display_message_numbers = 0;
549         rc_force_mail_prompts = 0;
550         rc_ansi_color = 0;
551         strcpy(rc_url_cmd, "");
552
553         /* now try to open the citadel.rc file */
554
555         ccfile = NULL;
556         if (getenv("HOME") != NULL) {
557                 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
558                 ccfile = fopen(buf, "r");
559         }
560         if (ccfile == NULL) {
561                 ccfile = fopen("/usr/local/lib/citadel.rc", "r");
562         }
563         if (ccfile == NULL) {
564                 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
565                 ccfile = fopen(buf, "r");
566         }
567         if (ccfile == NULL) {
568                 ccfile = fopen("./citadel.rc", "r");
569         }
570         if (ccfile == NULL) {
571                 perror("commands: cannot open citadel.rc");
572                 logoff(errno);
573         }
574         while (fgets(buf, 256, ccfile) != NULL) {
575                 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
576                         buf[strlen(buf) - 1] = 0;
577
578                 if (!strncasecmp(buf, "editor=", 7))
579                         strcpy(editor_path, &buf[7]);
580
581                 if (!strncasecmp(buf, "printcmd=", 9))
582                         strcpy(printcmd, &buf[9]);
583
584                 if (!strncasecmp(buf, "expcmd=", 7))
585                         strcpy(rc_exp_cmd, &buf[7]);
586
587                 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
588                         have_xterm = (char) atoi(&buf[24]);
589
590                 if (!strncasecmp(buf, "use_floors=", 11)) {
591                         if (!strcasecmp(&buf[11], "yes"))
592                                 rc_floor_mode = RC_YES;
593                         if (!strcasecmp(&buf[11], "no"))
594                                 rc_floor_mode = RC_NO;
595                         if (!strcasecmp(&buf[11], "default"))
596                                 rc_floor_mode = RC_DEFAULT;
597                 }
598                 if (!strncasecmp(buf, "beep=", 5)) {
599                         rc_exp_beep = atoi(&buf[5]);
600                 }
601                 if (!strncasecmp(buf, "allow_attachments=", 18)) {
602                         rc_allow_attachments = atoi(&buf[18]);
603                 }
604                 if (!strncasecmp(buf, "remember_passwords=", 19)) {
605                         rc_remember_passwords = atoi(&buf[19]);
606                 }
607                 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
608                         rc_display_message_numbers = atoi(&buf[24]);
609                 }
610                 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
611                         rc_force_mail_prompts = atoi(&buf[19]);
612                 }
613                 if (!strncasecmp(buf, "ansi_color=", 11)) {
614                         if (!strncasecmp(&buf[11], "on", 2))
615                                 rc_ansi_color = 1;
616                         if (!strncasecmp(&buf[11], "auto", 4))
617                                 rc_ansi_color = 2;      /* autodetect */
618                         if (!strncasecmp(&buf[11], "user", 4))
619                                 rc_ansi_color = 3;      /* user config */
620                 }
621                 if (!strncasecmp(buf, "username=", 9))
622                         strcpy(rc_username, &buf[9]);
623
624                 if (!strncasecmp(buf, "password=", 9))
625                         strcpy(rc_password, &buf[9]);
626
627                 if (!strncasecmp(buf, "urlcmd=", 7))
628                         strcpy(rc_url_cmd, &buf[7]);
629
630                 if (!strncasecmp(buf, "cmd=", 4)) {
631                         strcpy(buf, &buf[4]);
632
633                         cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
634
635                         cptr->c_cmdnum = atoi(buf);
636                         for (d = strlen(buf); d >= 0; --d)
637                                 if (buf[d] == ',')
638                                         b = d;
639                         strcpy(buf, &buf[b + 1]);
640
641                         cptr->c_axlevel = atoi(buf);
642                         for (d = strlen(buf); d >= 0; --d)
643                                 if (buf[d] == ',')
644                                         b = d;
645                         strcpy(buf, &buf[b + 1]);
646
647                         for (a = 0; a < 5; ++a)
648                                 cptr->c_keys[a][0] = 0;
649
650                         a = 0;
651                         b = 0;
652                         buf[strlen(buf) + 1] = 0;
653                         while (strlen(buf) > 0) {
654                                 b = strlen(buf);
655                                 for (d = strlen(buf); d >= 0; --d)
656                                         if (buf[d] == ',')
657                                                 b = d;
658                                 strncpy(cptr->c_keys[a], buf, b);
659                                 cptr->c_keys[a][b] = 0;
660                                 if (buf[b] == ',')
661                                         strcpy(buf, &buf[b + 1]);
662                                 else
663                                         strcpy(buf, "");
664                                 ++a;
665                         }
666
667                         cptr->next = NULL;
668                         if (cmdlist == NULL)
669                                 cmdlist = cptr;
670                         else
671                                 lastcmd->next = cptr;
672                         lastcmd = cptr;
673                 }
674         }
675         fclose(ccfile);
676 }
677
678
679
680 /*
681  * return the key associated with a command
682  */
683 char keycmd(char *cmdstr)
684 {
685         int a;
686
687         for (a = 0; a < strlen(cmdstr); ++a)
688                 if (cmdstr[a] == '&')
689                         return (tolower(cmdstr[a + 1]));
690         return (0);
691 }
692
693
694 /*
695  * Output the string from a key command without the ampersand
696  * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
697  */
698 char *cmd_expand(char *strbuf, int mode)
699 {
700         int a;
701         static char exp[64];
702         char buf[256];
703
704         strcpy(exp, strbuf);
705
706         for (a = 0; a < strlen(exp); ++a) {
707                 if (strbuf[a] == '&') {
708
709                         if (mode == 0) {
710                                 strcpy(&exp[a], &exp[a + 1]);
711                         }
712                         if (mode == 1) {
713                                 exp[a] = '<';
714                                 strcpy(buf, &exp[a + 2]);
715                                 exp[a + 2] = '>';
716                                 exp[a + 3] = 0;
717                                 strcat(exp, buf);
718                         }
719                 }
720                 if (!strncmp(&exp[a], "^r", 2)) {
721                         strcpy(buf, exp);
722                         strcpy(&exp[a], room_name);
723                         strcat(exp, &buf[a + 2]);
724                 }
725                 if (!strncmp(&exp[a], "^c", 2)) {
726                         exp[a] = ',';
727                         strcpy(&exp[a + 1], &exp[a + 2]);
728                 }
729         }
730
731         return (exp);
732 }
733
734
735
736 /*
737  * Comparison function to determine if entered commands match a
738  * command loaded from the config file.
739  */
740 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
741 {
742         int a;
743         int cmdax;
744
745         cmdax = 0;
746         if (is_room_aide)
747                 cmdax = 1;
748         if (axlevel >= 6)
749                 cmdax = 2;
750
751         for (a = 0; a < ncomp; ++a) {
752                 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
753                     || (cptr->c_axlevel > cmdax))
754                         return (0);
755         }
756         return (1);
757 }
758
759
760 /*
761  * This function returns 1 if a given command requires a string input
762  */
763 int requires_string(struct citcmd *cptr, int ncomp)
764 {
765         int a;
766         char buf[64];
767
768         strcpy(buf, cptr->c_keys[ncomp - 1]);
769         for (a = 0; a < strlen(buf); ++a) {
770                 if (buf[a] == ':')
771                         return (1);
772         }
773         return (0);
774 }
775
776
777 /*
778  * Input a command at the main prompt.
779  * This function returns an integer command number.  If the command prompts
780  * for a string then it is placed in the supplied buffer.
781  */
782 int getcmd(char *argbuf)
783 {
784         char cmdbuf[5];
785         int cmdspaces[5];
786         int cmdpos;
787         int ch;
788         int a;
789         int got;
790         int this_lazy_cmd;
791         struct citcmd *cptr;
792
793         /* Switch color support on or off if we're in user mode */
794         if (rc_ansi_color == 3) {
795                 if (userflags & US_COLOR)
796                         enable_color = 1;
797                 else
798                         enable_color = 0;
799         }
800         /* if we're running in idiot mode, display a cute little menu */
801         IFNEXPERT formout("mainmenu");
802
803         print_express();        /* print express messages if there are any */
804         strcpy(argbuf, "");
805         cmdpos = 0;
806         for (a = 0; a < 5; ++a)
807                 cmdbuf[a] = 0;
808         /* now the room prompt... */
809         ok_to_interrupt = 1;
810         color(BRIGHT_WHITE);
811         printf("\n%s", room_name);
812         color(DIM_WHITE);
813         printf("%c ", room_prompt(room_flags));
814         fflush(stdout);
815
816         while (1) {
817                 ch = inkey();
818                 ok_to_interrupt = 0;
819
820                 /* Handle the backspace key, but only if there's something
821                  * to backspace over...
822                  */
823                 if ((ch == 8) && (cmdpos > 0)) {
824                         back(cmdspaces[cmdpos - 1] + 1);
825                         cmdbuf[cmdpos] = 0;
826                         --cmdpos;
827                 }
828                 /* Spacebar invokes "lazy traversal" commands */
829                 if ((ch == 32) && (cmdpos == 0)) {
830                         this_lazy_cmd = next_lazy_cmd;
831                         if (this_lazy_cmd == 13)
832                                 next_lazy_cmd = 5;
833                         if (this_lazy_cmd == 5)
834                                 next_lazy_cmd = 13;
835                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
836                                 if (cptr->c_cmdnum == this_lazy_cmd) {
837                                         for (a = 0; a < 5; ++a)
838                                                 if (cptr->c_keys[a][0] != 0)
839                                                         printf("%s ", cmd_expand(
840                                                                                         cptr->c_keys[a], 0));
841                                         printf("\n");
842                                         return (this_lazy_cmd);
843                                 }
844                         }
845                         printf("\n");
846                         return (this_lazy_cmd);
847                 }
848                 /* Otherwise, process the command */
849                 cmdbuf[cmdpos] = tolower(ch);
850
851                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
852                         if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
853
854                                 printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
855                                 cmdspaces[cmdpos] = strlen(
856                                     cmd_expand(cptr->c_keys[cmdpos], 0));
857                                 if (cmdpos < 4)
858                                         if ((cptr->c_keys[cmdpos + 1]) != 0)
859                                                 putc(' ', stdout);
860                                 ++cmdpos;
861                         }
862                 }
863
864                 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
865                         if (cmdmatch(cmdbuf, cptr, 5)) {
866                                 /* We've found our command. */
867                                 if (requires_string(cptr, cmdpos)) {
868                                         getline(argbuf, 32);
869                                 } else {
870                                         printf("\n");
871                                 }
872
873                                 /* If this command is one that changes rooms,
874                                  * then the next lazy-command (space bar)
875                                  * should be "read new" instead of "goto"
876                                  */
877                                 if ((cptr->c_cmdnum == 5)
878                                     || (cptr->c_cmdnum == 6)
879                                     || (cptr->c_cmdnum == 47)
880                                     || (cptr->c_cmdnum == 52)
881                                     || (cptr->c_cmdnum == 16)
882                                     || (cptr->c_cmdnum == 20))
883                                         next_lazy_cmd = 13;
884
885                                 return (cptr->c_cmdnum);
886
887                         }
888                 }
889
890                 if (ch == '?') {
891                         printf("\rOne of ...                         \n");
892                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
893                                 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
894                                         for (a = 0; a < 5; ++a) {
895                                                 printf("%s ", cmd_expand(cptr->c_keys[a], 1));
896                                         }
897                                         printf("\n");
898                                 }
899                         }
900
901                         printf("\n%s%c ", room_name, room_prompt(room_flags));
902                         got = 0;
903                         for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
904                                 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
905                                         for (a = 0; a < cmdpos; ++a) {
906                                                 printf("%s ",
907                                                        cmd_expand(cptr->c_keys[a], 0));
908                                         }
909                                         got = 1;
910                                 }
911                         }
912                 }
913         }
914
915 }
916
917
918
919
920
921 /*
922  * set tty modes.  commands are:
923  * 
924  * 0 - set to bbs mode, intr/quit disabled
925  * 1 - set to bbs mode, intr/quit enabled
926  * 2 - save current settings for later restoral
927  * 3 - restore saved settings
928  */
929 #ifdef HAVE_TERMIOS_H
930 void sttybbs(int cmd)
931 {                               /* SysV version of sttybbs() */
932         struct termios live;
933         static struct termios saved_settings;
934         static int last_cmd = 0;
935
936         if (cmd == SB_LAST)
937                 cmd = last_cmd;
938         else
939                 last_cmd = cmd;
940
941         if ((cmd == 0) || (cmd == 1)) {
942                 tcgetattr(0, &live);
943                 live.c_iflag = ISTRIP | IXON | IXANY;
944                 live.c_oflag = OPOST | ONLCR;
945                 live.c_lflag = ISIG | NOFLSH;
946
947                 if (cmd == SB_YES_INTR) {
948                         live.c_cc[VINTR] = NEXT_KEY;
949                         live.c_cc[VQUIT] = STOP_KEY;
950                         signal(SIGINT, *sighandler);
951                         signal(SIGQUIT, *sighandler);
952                 } else {
953                         signal(SIGINT, SIG_IGN);
954                         signal(SIGQUIT, SIG_IGN);
955                         live.c_cc[VINTR] = (-1);
956                         live.c_cc[VQUIT] = (-1);
957                 }
958
959                 /* do we even need this stuff anymore? */
960                 /* live.c_line=0; */
961                 live.c_cc[VERASE] = 8;
962                 live.c_cc[VKILL] = 24;
963                 live.c_cc[VEOF] = 1;
964                 live.c_cc[VEOL] = 255;
965                 live.c_cc[VEOL2] = 0;
966                 live.c_cc[VSTART] = 0;
967                 tcsetattr(0, TCSADRAIN, &live);
968         }
969         if (cmd == 2) {
970                 tcgetattr(0, &saved_settings);
971         }
972         if (cmd == 3) {
973                 tcsetattr(0, TCSADRAIN, &saved_settings);
974         }
975 }
976 #else
977 void sttybbs(int cmd)
978 {                               /* BSD version of sttybbs() */
979         struct sgttyb live;
980         static struct sgttyb saved_settings;
981
982         if ((cmd == 0) || (cmd == 1)) {
983                 gtty(0, &live);
984                 live.sg_flags |= CBREAK;
985                 live.sg_flags |= CRMOD;
986                 live.sg_flags |= NL1;
987                 live.sg_flags &= ~ECHO;
988                 if (cmd == 1)
989                         live.sg_flags |= NOFLSH;
990                 stty(0, &live);
991         }
992         if (cmd == 2) {
993                 gtty(0, &saved_settings);
994         }
995         if (cmd == 3) {
996                 stty(0, &saved_settings);
997         }
998 }
999 #endif
1000
1001
1002 /*
1003  * display_help()  -  help file viewer
1004  */
1005 void display_help(char *name)
1006 {
1007         formout(name);
1008 }
1009
1010
1011 /*
1012  * fmout()  -  Citadel text formatter and paginator
1013  */
1014 int fmout(int width, FILE * fp, char pagin, int height, int starting_lp, char subst)
1015                         /* screen width to use */
1016                         /* file to read from, or NULL to read from server */
1017                         /* nonzero if we should use the paginator */
1018                         /* screen height to use */
1019                         /* starting value for lines_printed, -1 for global */
1020                         /* nonzero if we should use hypertext mode */
1021 {
1022         int a, b, c, d, old;
1023         int real = (-1);
1024         char aaa[140];
1025         char buffer[512];
1026         int eof_flag = 0;
1027
1028         num_urls = 0;   /* Start with a clean slate of embedded URL's */
1029
1030         if (starting_lp >= 0) {
1031                 lines_printed = starting_lp;
1032         }
1033         strcpy(aaa, "");
1034         old = 255;
1035         strcpy(buffer, "");
1036         c = 1;                  /* c is the current pos */
1037
1038         sigcaught = 0;
1039         sttybbs(1);
1040
1041 FMTA:   while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1042         
1043                 if (sigcaught)
1044                         goto OOPS;
1045                 if (fp != NULL) {       /* read from file */
1046                         if (feof(fp))
1047                                 eof_flag = 1;
1048                         if (eof_flag == 0) {
1049                                 a = getc(fp);
1050                                 buffer[strlen(buffer) + 1] = 0;
1051                                 buffer[strlen(buffer)] = a;
1052                         }
1053                 } else {        /* read from server */
1054                         d = strlen(buffer);
1055                         serv_gets(&buffer[d]);
1056                         while ((!isspace(buffer[d])) && (isspace(buffer[strlen(buffer) - 1])))
1057                                 buffer[strlen(buffer) - 1] = 0;
1058                         if (!strcmp(&buffer[d], "000")) {
1059                                 buffer[d] = 0;
1060                                 eof_flag = 1;
1061                                 while (isspace(buffer[strlen(buffer) - 1]))
1062                                         buffer[strlen(buffer) - 1] = 0;
1063                         }
1064                         d = strlen(buffer);
1065                         buffer[d] = 10;
1066                         buffer[d + 1] = 0;
1067                 }
1068         }
1069
1070         if ( (!strncasecmp(buffer, "http://", 7))
1071            || (!strncasecmp(buffer, "ftp://", 6)) ) {
1072                 safestrncpy(urls[num_urls], buffer, 255);
1073                 for (a=0; a<strlen(urls[num_urls]); ++a) {
1074                         b = urls[num_urls][a];
1075                         if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1076                            || (b==13) || (b==9) || (b=='\"') )
1077                                 urls[num_urls][a] = 0;
1078                 }
1079                 ++num_urls;
1080         }
1081
1082         buffer[strlen(buffer) + 1] = 0;
1083         a = buffer[0];
1084         strcpy(buffer, &buffer[1]);
1085
1086         old = real;
1087         real = a;
1088         if (a <= 0)
1089                 goto FMTEND;
1090
1091         if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1092                 a = 32;
1093         if (((old == 13) || (old == 10)) && (isspace(real))) {
1094                 printf("\n");
1095                 ++lines_printed;
1096                 lines_printed = checkpagin(lines_printed, pagin, height);
1097                 c = 1;
1098         }
1099         if (a > 126)
1100                 goto FMTA;
1101
1102         if (a > 32) {
1103                 if (((strlen(aaa) + c) > (width - 5)) && (strlen(aaa) > (width - 5))) {
1104                         printf("\n%s", aaa);
1105                         c = strlen(aaa);
1106                         aaa[0] = 0;
1107                         ++lines_printed;
1108                         lines_printed = checkpagin(lines_printed, pagin, height);
1109                 }
1110                 b = strlen(aaa);
1111                 aaa[b] = a;
1112                 aaa[b + 1] = 0;
1113         }
1114         if (a == 32) {
1115                 if ((strlen(aaa) + c) > (width - 5)) {
1116                         c = 1;
1117                         printf("\n");
1118                         ++lines_printed;
1119                         lines_printed = checkpagin(lines_printed, pagin, height);
1120                 }
1121                 printf("%s ", aaa);
1122                 ++c;
1123                 c = c + strlen(aaa);
1124                 strcpy(aaa, "");
1125                 goto FMTA;
1126         }
1127         if ((a == 13) || (a == 10)) {
1128                 printf("%s\n", aaa);
1129                 c = 1;
1130                 ++lines_printed;
1131                 lines_printed = checkpagin(lines_printed, pagin, height);
1132                 strcpy(aaa, "");
1133                 goto FMTA;
1134         }
1135         goto FMTA;
1136
1137         /* signal caught; drain the server */
1138       OOPS:do {
1139                 serv_gets(aaa);
1140         } while (strcmp(aaa, "000"));
1141
1142       FMTEND:printf("\n");
1143         ++lines_printed;
1144         lines_printed = checkpagin(lines_printed, pagin, height);
1145         return (sigcaught);
1146 }
1147
1148
1149 /*
1150  * support ANSI color if defined
1151  */
1152 void color(int colornum)
1153 {
1154         static int is_bold = 0;
1155         static int hold_color, current_color;
1156
1157         if (colornum == COLOR_PUSH) {
1158                 hold_color = current_color;
1159                 return;
1160         }
1161
1162         if (colornum == COLOR_POP) {
1163                 color(hold_color);
1164                 return;
1165         }
1166
1167         current_color = colornum;
1168         if (enable_color) {
1169                 /* When switching to dim white, actually output an 'original
1170                  * pair' sequence -- this looks better on black-on-white
1171                  * terminals.
1172                  */
1173                 if (colornum == DIM_WHITE)
1174                         printf("\033[39;49m");
1175                 else
1176                         printf("\033[3%d;40m", (colornum & 7));
1177
1178                 if ((colornum >= 8) && (is_bold == 0)) {
1179                         printf("\033[1m");
1180                         is_bold = 1;
1181                 } else if ((colornum < 8) && (is_bold == 1)) {
1182                         printf("\033[0m");
1183                         is_bold = 0;
1184                 }
1185                 fflush(stdout);
1186         }
1187 }
1188
1189 void cls(int colornum)
1190 {
1191         if (enable_color) {
1192                 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1193                 fflush(stdout);
1194         }
1195 }
1196
1197
1198 /*
1199  * Detect whether ANSI color is available (answerback)
1200  */
1201 void send_ansi_detect(void)
1202 {
1203         if (rc_ansi_color == 2) {
1204                 printf("\033[c");
1205                 fflush(stdout);
1206                 time(&AnsiDetect);
1207         }
1208 }
1209
1210 void look_for_ansi(void)
1211 {
1212         fd_set rfds;
1213         struct timeval tv;
1214         char abuf[512];
1215         time_t now;
1216         int a;
1217
1218         if (rc_ansi_color == 0) {
1219                 enable_color = 0;
1220         } else if (rc_ansi_color == 1) {
1221                 enable_color = 1;
1222         } else if (rc_ansi_color == 2) {
1223
1224                 /* otherwise, do the auto-detect */
1225
1226                 strcpy(abuf, "");
1227
1228                 time(&now);
1229                 if ((now - AnsiDetect) < 2)
1230                         sleep(1);
1231
1232                 do {
1233                         FD_ZERO(&rfds);
1234                         FD_SET(0, &rfds);
1235                         tv.tv_sec = 0;
1236                         tv.tv_usec = 1;
1237
1238                         select(1, &rfds, NULL, NULL, &tv);
1239                         if (FD_ISSET(0, &rfds)) {
1240                                 abuf[strlen(abuf) + 1] = 0;
1241                                 read(0, &abuf[strlen(abuf)], 1);
1242                         }
1243                 } while (FD_ISSET(0, &rfds));
1244
1245                 for (a = 0; a < strlen(abuf); ++a) {
1246                         if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1247                             && (abuf[a + 2] == '?')) {
1248                                 enable_color = 1;
1249                         }
1250                 }
1251         }
1252 }
1253
1254
1255 /*
1256  * Display key options (highlight hotkeys inside angle brackets)
1257  */
1258 void keyopt(char *buf) {
1259         int i;
1260
1261         color(DIM_WHITE);
1262         for (i=0; i<strlen(buf); ++i) {
1263                 if (buf[i]=='<') {
1264                         putc(buf[i], stdout);
1265                         color(BRIGHT_MAGENTA);
1266                 } else {
1267                         if (buf[i]=='>') {
1268                                 color(DIM_WHITE);
1269                         }
1270                         putc(buf[i], stdout);
1271                 }
1272         }
1273         color(DIM_WHITE);
1274 }
1275
1276
1277
1278 /*
1279  * Present a key-menu line choice type of thing
1280  */
1281 char keymenu(char *menuprompt, char *menustring) {
1282         int i, c, a;
1283         int choices;
1284         int do_prompt = 0;
1285         char buf[256];
1286         int ch;
1287         int display_prompt = 1;
1288
1289         choices = num_tokens(menustring, '|');
1290
1291         if (menuprompt != NULL) do_prompt = 1;
1292         if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1293
1294         while (1) {
1295                 if (display_prompt) {
1296                         if (do_prompt) {
1297                                 printf("%s ", menuprompt);
1298                         } 
1299                         else {
1300                                 for (i=0; i<choices; ++i) {
1301                                         extract(buf, menustring, i);
1302                                         keyopt(buf);
1303                                         printf(" ");
1304                                 }
1305                         }
1306                         printf(" -> ");
1307                         display_prompt = 0;
1308                 }
1309                 ch = lkey();
1310         
1311                 if ( (do_prompt) && (ch=='?') ) {
1312                         printf("\rOne of...                               ");
1313                         printf("                                      \n");
1314                         for (i=0; i<choices; ++i) {
1315                                 extract(buf, menustring, i);
1316                                 printf("   ");
1317                                 keyopt(buf);
1318                                 printf("\n");
1319                         }
1320                         printf("\n");
1321                         display_prompt = 1;
1322                 }
1323
1324                 for (i=0; i<choices; ++i) {
1325                         extract(buf, menustring, i);
1326                         for (c=1; c<strlen(buf); ++c) {
1327                                 if ( (ch == tolower(buf[c]))
1328                                    && (buf[c-1]=='<')
1329                                    && (buf[c+1]=='>') ) {
1330                                         for (a=0; a<strlen(buf); ++a) {
1331                                                 if ( (a!=(c-1)) && (a!=(c+1))) {
1332                                                         putc(buf[a], stdout);
1333                                                 }
1334                                         }
1335                                         printf("\n\n");
1336                                         return ch;
1337                                 }
1338                         }
1339                 }
1340         }
1341 }