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