4 * This file contains functions which implement parts of the
5 * text-mode user interface.
16 #include <sys/types.h>
18 #if TIME_WITH_SYS_TIME
19 # include <sys/time.h>
23 # include <sys/time.h>
35 #ifdef HAVE_SYS_SELECT_H
36 #include <sys/select.h>
39 #ifdef THREADED_CLIENT
49 #include "citadel_decls.h"
51 #include "routines2.h"
54 #include "client_chat.h"
67 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
71 char rc_exp_cmd[1024];
72 int rc_allow_attachments;
73 int rc_display_message_numbers;
74 int rc_force_mail_prompts;
75 int rc_remember_passwords;
78 int rc_prompt_control = 0;
79 char urls[MAXURLS][SIZ];
83 int next_lazy_cmd = 5;
85 int lines_printed = 0; /* line count for paginator */
86 extern int screenwidth, screenheight;
89 struct citcmd *cmdlist = NULL;
92 /* these variables are local to this module */
93 char keepalives_enabled = KA_YES; /* send NOOPs to server when idle */
94 int ok_to_interrupt = 0; /* print express msgs asynchronously */
95 time_t AnsiDetect; /* when did we send the detect code? */
96 int enable_color = 0; /* nonzero for ANSI color */
102 * If an interesting key has been pressed, return its value, otherwise 0
104 char was_a_key_pressed(void) {
114 retval = select(1, &rfds, NULL, NULL, &tv);
116 /* Careful! Disable keepalives during keyboard polling; we're probably
117 * in the middle of a data transfer from the server, in which case
118 * sending a NOOP would throw the client protocol out of sync.
120 if (FD_ISSET(0, &rfds)) {
121 set_keepalives(KA_NO);
122 the_character = inkey();
123 set_keepalives(KA_YES);
128 return(the_character);
136 * Check to see if we need to pause at the end of a screen.
137 * If we do, we have to disable server keepalives during the pause because
138 * we are probably in the middle of a server operation and the NOOP command
139 * would confuse everything.
141 int checkpagin(int lp, int pagin, int height)
145 if (sigcaught) return(lp);
146 thekey = was_a_key_pressed();
147 if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
149 if (thekey == 'n' || thekey == 'N')
151 if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
152 if (sigcaught) return(lp);
154 if (!pagin) return(0);
155 if (lp>=(height-1)) {
156 set_keepalives(KA_NO);
158 set_keepalives(KA_YES);
168 * pprintf() ... paginated version of printf()
170 void pprintf(const char *format, ...) {
172 static char buf[4096]; /* static for performance, change if needed */
175 /* If sigcaught is nonzero, a keypress has interrupted this and we
176 * should just drain output.
178 if (sigcaught) return;
180 /* Otherwise, start spewing... */
181 va_start(arg_ptr, format);
182 vsnprintf(buf, sizeof(buf), format, arg_ptr);
185 for (i=0; i<strlen(buf); ++i) {
189 lines_printed = checkpagin(lines_printed,
190 (userflags & US_PAGINATOR),
199 * print_express() - print express messages if there are any
201 void print_express(void)
211 if (express_msgs == 0)
217 if (strlen(rc_exp_cmd) == 0) {
222 while (express_msgs != 0) {
228 express_msgs = extract_int(&buf[4], 0);
229 timestamp = extract_long(&buf[4], 1);
230 flags = extract_int(&buf[4], 2);
231 extract(sender, &buf[4], 3);
232 extract(node, &buf[4], 4);
233 strcpy(last_paged, sender);
235 stamp = localtime(×tamp);
237 /* If the page is a Logoff Request, honor it. */
243 if (strlen(rc_exp_cmd) > 0) {
244 outpipe = popen(rc_exp_cmd, "w");
245 if (outpipe != NULL) {
246 /* Header derived from flags */
249 "Please log off now, as requested ");
251 fprintf(outpipe, "Broadcast message ");
253 fprintf(outpipe, "Chat request ");
255 fprintf(outpipe, "Message ");
256 /* Timestamp. Can this be improved? */
257 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
258 fprintf(outpipe, "at 12:%02d%cm",
260 stamp->tm_hour ? 'p' : 'a');
261 else if (stamp->tm_hour > 12) /* pm */
262 fprintf(outpipe, "at %d:%02dpm",
266 fprintf(outpipe, "at %d:%02dam",
267 stamp->tm_hour, stamp->tm_min);
268 fprintf(outpipe, " from %s", sender);
269 if (strncmp(serv_info.serv_nodename, node, 32))
270 fprintf(outpipe, " @%s", node);
271 fprintf(outpipe, ":\n");
272 while (serv_gets(buf), strcmp(buf, "000")) {
273 fprintf(outpipe, "%s\n", buf);
276 if (express_msgs == 0)
281 /* fall back to built-in express message display */
285 /* Header derived from flags */
287 scr_printf("Please log off now, as requested ");
289 scr_printf("Broadcast message ");
291 scr_printf("Chat request ");
293 scr_printf("Message ");
295 /* Timestamp. Can this be improved? */
296 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)/* 12am/12pm */
297 scr_printf("at 12:%02d%cm", stamp->tm_min,
298 stamp->tm_hour ? 'p' : 'a');
299 else if (stamp->tm_hour > 12) /* pm */
300 scr_printf("at %d:%02dpm",
301 stamp->tm_hour - 12, stamp->tm_min);
303 scr_printf("at %d:%02dam", stamp->tm_hour, stamp->tm_min);
306 scr_printf(" from %s", sender);
308 /* Remote node, if any */
309 if (strncmp(serv_info.serv_nodename, node, 32))
310 scr_printf(" @%s", node);
314 fmout(screenwidth, NULL, NULL, 1, screenheight, -1, 0);
316 /* when running in curses mode, the scroll bar in most
317 xterm-style programs becomes useless, so it makes sense to
318 pause after a screenful of pages if the user has been idle
319 for a while. However, this is annoying to some of the users
320 who aren't in curses mode and tend to leave their clients
321 idle. keepalives become disabled, resulting in getting booted
322 when coming back to the idle session. but they probably have
323 a working scrollback in their terminal, so disable it in this
326 if (!is_curses_enabled())
329 scr_printf("\n---\n");
336 void set_keepalives(int s)
338 keepalives_enabled = (char) s;
342 * This loop handles the "keepalive" messages sent to the server when idling.
345 static time_t idlet = 0;
346 static void really_do_keepalive(void) {
350 if (keepalives_enabled == KA_YES) {
355 if (ok_to_interrupt == 1) {
356 scr_printf("\r%64s\r", "");
358 scr_printf("%s%c ", room_name,
359 room_prompt(room_flags));
366 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
367 encapsulated interface; in theory there should be no need to touch these
368 globals outside of the async_ka_* functions. */
370 #ifdef THREADED_CLIENT
371 static pthread_t ka_thr_handle;
372 static int ka_thr_active = 0;
373 static int async_ka_enabled = 0;
375 static void *ka_thread(void *arg)
377 really_do_keepalive();
378 pthread_detach(ka_thr_handle);
383 /* start up a thread to handle a keepalive in the background */
384 static void async_ka_exec(void)
386 if (!ka_thr_active) {
388 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
389 perror("pthread_create");
394 #endif /* THREADED_CLIENT */
396 /* I changed this from static to not because I need to call it from
397 screen.c, either that or make something in screen.c not static.
398 Fix it how you like. Why all the staticness? stu */
400 void do_keepalive(void)
405 if ((now - idlet) < ((long) S_KEEPALIVE))
408 /* Do a space-backspace to keep telnet sessions from idling out */
409 scr_printf(" %c", 8);
412 #ifdef THREADED_CLIENT
413 if (async_ka_enabled)
417 really_do_keepalive();
421 /* Now the actual async-keepalve API that we expose to higher levels:
422 async_ka_start() and async_ka_end(). These do nothing when we don't have
423 threading enabled, so we avoid sprinkling ifdef's throughout the code. */
425 /* wait for a background keepalive to complete. this must be done before
426 attempting any further server requests! */
427 void async_ka_end(void)
429 #ifdef THREADED_CLIENT
431 pthread_join(ka_thr_handle, NULL);
437 /* tell do_keepalive() that keepalives are asynchronous. */
438 void async_ka_start(void)
440 #ifdef THREADED_CLIENT
447 { /* get a character from the keyboard, with */
448 int a; /* the watchdog timer in effect if necessary */
458 /* This loop waits for keyboard input. If the keepalive
459 * timer expires, it sends a keepalive to the server if
460 * necessary and then waits again.
463 scr_set_windowsize();
465 scr_set_windowsize();
469 tv.tv_sec = S_KEEPALIVE;
472 select(1, &rfds, NULL, NULL, &tv);
473 } while (!FD_ISSET(0, &rfds));
475 /* At this point, there's input, so fetch it.
476 * (There's a hole in the bucket...)
478 a = scr_getc(SCR_NOBLOCK);
485 if (((a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
486 && ((a < 32) || (a > 126)))
489 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
500 { /* Returns 1 for yes, 0 for no */
516 /* Returns 1 for yes, 0 for no, arg is default value */
539 /* Gets a line from the terminal */
540 /* string == Pointer to string buffer */
541 /* lim == Maximum length - if negative, no-show */
542 void getline(char *string, int lim)
556 if ((a == 8) && (strlen(string) == 0))
558 if ((a != 10) && (a != 8) && (strlen(string) == lim))
560 if ((a == 8) && (string[0] != 0)) {
561 string[strlen(string) - 1] = 0;
586 * strprompt() - prompt for a string, print the existing value and
587 * allow the user to press return to keep it...
589 void strprompt(char *prompt, char *str, int len)
596 scr_printf("%s ", prompt);
599 color(BRIGHT_MAGENTA);
602 scr_printf("%s", str);
605 for (i=0; i<strlen(str); ++i) {
622 * boolprompt() - prompt for a yes/no, print the existing value and
623 * allow the user to press return to keep it...
625 int boolprompt(char *prompt, int prev_val)
630 scr_printf("%s ", prompt);
633 color(BRIGHT_MAGENTA);
634 scr_printf("%s", (prev_val ? "Yes" : "No"));
638 r = (yesno_d(prev_val));
644 * intprompt() - like strprompt(), except for an integer
645 * (note that it RETURNS the new value!)
647 int intprompt(char *prompt, int ival, int imin, int imax)
655 snprintf(buf, sizeof buf, "%d", i);
656 strprompt(prompt, buf, 15);
658 for (p=0; p<strlen(buf); ++p) {
659 if ( (!isdigit(buf[p]))
660 && ( (buf[p]!='-') || (p!=0) ) )
664 scr_printf("*** Must be no less than %d.\n", imin);
666 scr_printf("*** Must be no more than %d.\n", imax);
667 } while ((i < imin) || (i > imax));
672 * newprompt() - prompt for a string with no existing value
673 * (clears out string buffer first)
675 void newprompt(char *prompt, char *str, int len)
677 color(BRIGHT_MAGENTA);
678 scr_printf("%s", prompt);
686 { /* returns a lower case value */
695 * parse the citadel.rc file
697 void load_command_set(void)
702 struct citcmd *lastcmd = NULL;
707 /* first, set up some defaults for non-required variables */
709 strcpy(editor_path, "");
710 strcpy(printcmd, "");
711 strcpy(rc_username, "");
712 strcpy(rc_password, "");
715 rc_allow_attachments = 0;
716 rc_remember_passwords = 0;
717 strcpy(rc_exp_cmd, "");
718 rc_display_message_numbers = 0;
719 rc_force_mail_prompts = 0;
721 strcpy(rc_url_cmd, "");
723 rc_encrypt = RC_DEFAULT;
726 rc_screen = RC_DEFAULT;
728 rc_alt_semantics = 0;
730 /* now try to open the citadel.rc file */
733 if (getenv("HOME") != NULL) {
734 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
735 ccfile = fopen(buf, "r");
737 if (ccfile == NULL) {
738 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
739 ccfile = fopen(buf, "r");
741 if (ccfile == NULL) {
742 ccfile = fopen("/etc/citadel.rc", "r");
744 if (ccfile == NULL) {
745 ccfile = fopen("./citadel.rc", "r");
747 if (ccfile == NULL) {
748 perror("commands: cannot open citadel.rc");
751 while (fgets(buf, sizeof buf, ccfile) != NULL) {
752 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
753 buf[strlen(buf) - 1] = 0;
755 if (!strncasecmp(buf, "encrypt=", 8)) {
756 if (!strcasecmp(&buf[8], "yes")) {
760 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
765 else if (!strcasecmp(&buf[8], "no")) {
768 else if (!strcasecmp(&buf[8], "default")) {
769 rc_encrypt = RC_DEFAULT;
775 if (!strncasecmp(buf, "fullscreen=", 11)) {
776 if (!strcasecmp(&buf[11], "yes"))
778 else if (!strcasecmp(&buf[11], "no"))
783 if (!strncasecmp(buf, "editor=", 7))
784 strcpy(editor_path, &buf[7]);
786 if (!strncasecmp(buf, "printcmd=", 9))
787 strcpy(printcmd, &buf[9]);
789 if (!strncasecmp(buf, "expcmd=", 7))
790 strcpy(rc_exp_cmd, &buf[7]);
792 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
793 have_xterm = (char) atoi(&buf[24]);
795 if (!strncasecmp(buf, "use_floors=", 11)) {
796 if (!strcasecmp(&buf[11], "yes"))
797 rc_floor_mode = RC_YES;
798 if (!strcasecmp(&buf[11], "no"))
799 rc_floor_mode = RC_NO;
800 if (!strcasecmp(&buf[11], "default"))
801 rc_floor_mode = RC_DEFAULT;
803 if (!strncasecmp(buf, "beep=", 5)) {
804 rc_exp_beep = atoi(&buf[5]);
806 if (!strncasecmp(buf, "allow_attachments=", 18)) {
807 rc_allow_attachments = atoi(&buf[18]);
809 if (!strncasecmp(buf, "remember_passwords=", 19)) {
810 rc_remember_passwords = atoi(&buf[19]);
812 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
813 rc_display_message_numbers = atoi(&buf[24]);
815 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
816 rc_force_mail_prompts = atoi(&buf[19]);
818 if (!strncasecmp(buf, "ansi_color=", 11)) {
819 if (!strncasecmp(&buf[11], "on", 2))
821 if (!strncasecmp(&buf[11], "auto", 4))
822 rc_ansi_color = 2; /* autodetect */
823 if (!strncasecmp(&buf[11], "user", 4))
824 rc_ansi_color = 3; /* user config */
826 if (!strncasecmp(buf, "prompt_control=", 15)) {
827 if (!strncasecmp(&buf[15], "on", 2))
828 rc_prompt_control = 1;
829 if (!strncasecmp(&buf[15], "user", 4))
830 rc_prompt_control = 3; /* user config */
832 if (!strncasecmp(buf, "username=", 9))
833 strcpy(rc_username, &buf[9]);
835 if (!strncasecmp(buf, "password=", 9))
836 strcpy(rc_password, &buf[9]);
838 if (!strncasecmp(buf, "urlcmd=", 7))
839 strcpy(rc_url_cmd, &buf[7]);
841 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
842 if (!strncasecmp(&buf[11], "yes", 3))
843 rc_alt_semantics = 1;
844 if (!strncasecmp(&buf[11], "no", 2))
845 rc_alt_semantics = 0;
847 if (!strncasecmp(buf, "cmd=", 4)) {
848 strcpy(buf, &buf[4]);
850 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
852 cptr->c_cmdnum = atoi(buf);
853 for (d = strlen(buf); d >= 0; --d)
856 strcpy(buf, &buf[b + 1]);
858 cptr->c_axlevel = atoi(buf);
859 for (d = strlen(buf); d >= 0; --d)
862 strcpy(buf, &buf[b + 1]);
864 for (a = 0; a < 5; ++a)
865 cptr->c_keys[a][0] = 0;
869 buf[strlen(buf) + 1] = 0;
870 while (strlen(buf) > 0) {
872 for (d = strlen(buf); d >= 0; --d)
875 strncpy(cptr->c_keys[a], buf, b);
876 cptr->c_keys[a][b] = 0;
878 strcpy(buf, &buf[b + 1]);
888 lastcmd->next = cptr;
898 * return the key associated with a command
900 char keycmd(char *cmdstr)
904 for (a = 0; a < strlen(cmdstr); ++a)
905 if (cmdstr[a] == '&')
906 return (tolower(cmdstr[a + 1]));
912 * Output the string from a key command without the ampersand
913 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
915 char *cmd_expand(char *strbuf, int mode)
923 for (a = 0; a < strlen(exp); ++a) {
924 if (strbuf[a] == '&') {
927 strcpy(&exp[a], &exp[a + 1]);
931 strcpy(buf, &exp[a + 2]);
937 if (!strncmp(&exp[a], "^r", 2)) {
939 strcpy(&exp[a], room_name);
940 strcat(exp, &buf[a + 2]);
942 if (!strncmp(&exp[a], "^c", 2)) {
944 strcpy(&exp[a + 1], &exp[a + 2]);
954 * Comparison function to determine if entered commands match a
955 * command loaded from the config file.
957 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
968 for (a = 0; a < ncomp; ++a) {
969 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
970 || (cptr->c_axlevel > cmdax))
978 * This function returns 1 if a given command requires a string input
980 int requires_string(struct citcmd *cptr, int ncomp)
985 strcpy(buf, cptr->c_keys[ncomp - 1]);
986 for (a = 0; a < strlen(buf); ++a) {
995 * Input a command at the main prompt.
996 * This function returns an integer command number. If the command prompts
997 * for a string then it is placed in the supplied buffer.
999 int getcmd(char *argbuf)
1008 struct citcmd *cptr;
1011 * Starting a new command now, so set sigcaught to 0. This variable
1012 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1013 * been interrupted by a keypress.
1017 /* Switch color support on or off if we're in user mode */
1018 if (rc_ansi_color == 3) {
1019 if (userflags & US_COLOR)
1024 /* if we're running in idiot mode, display a cute little menu */
1025 IFNEXPERT formout("mainmenu");
1027 print_express(); /* print express messages if there are any */
1030 for (a = 0; a < 5; ++a)
1032 /* now the room prompt... */
1033 ok_to_interrupt = 1;
1034 color(BRIGHT_WHITE);
1035 scr_printf("\n%s", room_name);
1037 scr_printf("%c ", room_prompt(room_flags));
1042 ok_to_interrupt = 0;
1044 /* Handle the backspace key, but only if there's something
1045 * to backspace over...
1047 if ((ch == 8) && (cmdpos > 0)) {
1048 back(cmdspaces[cmdpos - 1] + 1);
1052 /* Spacebar invokes "lazy traversal" commands */
1053 if ((ch == 32) && (cmdpos == 0)) {
1054 this_lazy_cmd = next_lazy_cmd;
1055 if (this_lazy_cmd == 13)
1057 if (this_lazy_cmd == 5)
1059 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1060 if (cptr->c_cmdnum == this_lazy_cmd) {
1061 for (a = 0; a < 5; ++a)
1062 if (cptr->c_keys[a][0] != 0)
1063 scr_printf("%s ", cmd_expand(
1064 cptr->c_keys[a], 0));
1066 return (this_lazy_cmd);
1070 return (this_lazy_cmd);
1072 /* Otherwise, process the command */
1073 cmdbuf[cmdpos] = tolower(ch);
1075 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1076 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1078 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1079 cmdspaces[cmdpos] = strlen(
1080 cmd_expand(cptr->c_keys[cmdpos], 0));
1082 if ((cptr->c_keys[cmdpos + 1]) != 0)
1088 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1089 if (cmdmatch(cmdbuf, cptr, 5)) {
1090 /* We've found our command. */
1091 if (requires_string(cptr, cmdpos)) {
1092 getline(argbuf, 32);
1097 /* If this command is one that changes rooms,
1098 * then the next lazy-command (space bar)
1099 * should be "read new" instead of "goto"
1101 if ((cptr->c_cmdnum == 5)
1102 || (cptr->c_cmdnum == 6)
1103 || (cptr->c_cmdnum == 47)
1104 || (cptr->c_cmdnum == 52)
1105 || (cptr->c_cmdnum == 16)
1106 || (cptr->c_cmdnum == 20))
1109 return (cptr->c_cmdnum);
1115 pprintf("\rOne of ... \n");
1116 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1117 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1118 for (a = 0; a < 5; ++a) {
1119 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1125 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1127 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1128 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1129 for (a = 0; a < cmdpos; ++a) {
1131 cmd_expand(cptr->c_keys[a], 0));
1146 * set tty modes. commands are:
1148 * 01- set to bbs mode
1149 * 2 - save current settings for later restoral
1150 * 3 - restore saved settings
1152 #ifdef HAVE_TERMIOS_H
1153 void sttybbs(int cmd)
1154 { /* SysV version of sttybbs() */
1155 struct termios live;
1156 static struct termios saved_settings;
1157 static int last_cmd = 0;
1164 if ((cmd == 0) || (cmd == 1)) {
1165 tcgetattr(0, &live);
1166 live.c_iflag = ISTRIP | IXON | IXANY;
1167 live.c_oflag = OPOST | ONLCR;
1168 live.c_lflag = ISIG | NOFLSH;
1170 live.c_cc[VINTR] = (-1);
1171 live.c_cc[VQUIT] = (-1);
1174 live.c_cc[VMIN] = 0;
1175 live.c_cc[VTIME] = 0;
1178 /* do we even need this stuff anymore? */
1179 /* live.c_line=0; */
1180 live.c_cc[VERASE] = 8;
1181 live.c_cc[VKILL] = 24;
1182 live.c_cc[VEOF] = 1;
1183 live.c_cc[VEOL] = 255;
1184 live.c_cc[VEOL2] = 0;
1185 live.c_cc[VSTART] = 0;
1186 tcsetattr(0, TCSADRAIN, &live);
1189 tcgetattr(0, &saved_settings);
1192 tcsetattr(0, TCSADRAIN, &saved_settings);
1196 void sttybbs(int cmd)
1197 { /* BSD version of sttybbs() */
1199 static struct sgttyb saved_settings;
1200 static int last_cmd = 0;
1207 if ((cmd == 0) || (cmd == 1)) {
1209 live.sg_flags |= CBREAK;
1210 live.sg_flags |= CRMOD;
1211 live.sg_flags |= NL1;
1212 live.sg_flags &= ~ECHO;
1214 live.sg_flags |= NOFLSH;
1218 gtty(0, &saved_settings);
1221 stty(0, &saved_settings);
1228 * display_help() - help file viewer
1230 void display_help(char *name)
1237 * fmout() - Citadel text formatter and paginator
1240 int width, /* screen width to use */
1241 FILE *fpin, /* file to read from, or NULL to read from server */
1242 FILE *fpout, /* File to write to, or NULL to write to screen */
1243 char pagin, /* nonzero if we should use the paginator */
1244 int height, /* screen height to use */
1245 int starting_lp,/* starting value for lines_printed, -1 for global */
1246 char subst) /* nonzero if we should use hypertext mode */
1248 int a, b, c, d, old;
1254 num_urls = 0; /* Start with a clean slate of embedded URL's */
1256 if (starting_lp >= 0) {
1257 lines_printed = starting_lp;
1262 c = 1; /* c is the current pos */
1264 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1265 if (fpin != NULL) { /* read from file */
1268 if (eof_flag == 0) {
1270 buffer[strlen(buffer) + 1] = 0;
1271 buffer[strlen(buffer)] = a;
1273 } else { /* read from server */
1275 serv_gets(&buffer[d]);
1276 while ((!isspace(buffer[d])) && (isspace(buffer[strlen(buffer) - 1])))
1277 buffer[strlen(buffer) - 1] = 0;
1278 if (!strcmp(&buffer[d], "000")) {
1281 while (isspace(buffer[strlen(buffer) - 1]))
1282 buffer[strlen(buffer) - 1] = 0;
1290 if ( (!strncasecmp(buffer, "http://", 7))
1291 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1292 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1293 for (a=0; a<strlen(urls[num_urls]); ++a) {
1294 b = urls[num_urls][a];
1295 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1296 || (b==13) || (b==9) || (b=='\"') )
1297 urls[num_urls][a] = 0;
1302 buffer[strlen(buffer) + 1] = 0;
1304 strcpy(buffer, &buffer[1]);
1311 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1313 if (((old == 13) || (old == 10)) && (isspace(real))) {
1315 fprintf(fpout, "\n");
1319 lines_printed = checkpagin(lines_printed, pagin, height);
1327 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1329 fprintf(fpout, "\n%s", aaa);
1331 scr_printf("\n%s", aaa);
1333 lines_printed = checkpagin(lines_printed, pagin, height);
1343 if ((strlen(aaa) + c) > (width - 1)) {
1346 fprintf(fpout, "\n");
1350 lines_printed = checkpagin(lines_printed, pagin, height);
1354 fprintf(fpout, "%s ", aaa);
1356 scr_printf("%s ", aaa);
1359 c = c + strlen(aaa);
1363 if ((a == 13) || (a == 10)) {
1365 fprintf(fpout, "%s\n", aaa);
1367 scr_printf("%s\n", aaa);
1369 lines_printed = checkpagin(lines_printed, pagin, height);
1372 if (sigcaught) goto OOPS;
1378 /* keypress caught; drain the server */
1381 } while (strcmp(aaa, "000"));
1385 fprintf(fpout, "\n");
1389 lines_printed = checkpagin(lines_printed, pagin, height);
1396 * support ANSI color if defined
1398 void color(int colornum)
1400 static int is_bold = 0;
1401 static int hold_color, current_color;
1403 if (colornum == COLOR_PUSH) {
1404 hold_color = current_color;
1408 if (colornum == COLOR_POP) {
1413 current_color = colornum;
1415 #ifdef HAVE_CURSES_H
1416 if (scr_color(colornum))
1419 /* When switching to dim white, actually output an 'original
1420 * pair' sequence -- this looks better on black-on-white
1423 if (colornum == DIM_WHITE)
1424 printf("\033[39;49m");
1426 printf("\033[3%d;40m", (colornum & 7));
1428 if ((colornum >= 8) && (is_bold == 0)) {
1431 } else if ((colornum < 8) && (is_bold == 1)) {
1439 void cls(int colornum)
1442 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1449 * Detect whether ANSI color is available (answerback)
1451 void send_ansi_detect(void)
1453 if (rc_ansi_color == 2) {
1460 void look_for_ansi(void)
1468 if (rc_ansi_color == 0) {
1470 } else if (rc_ansi_color == 1) {
1472 } else if (rc_ansi_color == 2) {
1474 /* otherwise, do the auto-detect */
1479 if ((now - AnsiDetect) < 2)
1488 select(1, &rfds, NULL, NULL, &tv);
1489 if (FD_ISSET(0, &rfds)) {
1490 abuf[strlen(abuf) + 1] = 0;
1491 read(0, &abuf[strlen(abuf)], 1);
1493 } while (FD_ISSET(0, &rfds));
1495 for (a = 0; a < strlen(abuf); ++a) {
1496 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1497 && (abuf[a + 2] == '?')) {
1506 * Display key options (highlight hotkeys inside angle brackets)
1508 void keyopt(char *buf) {
1512 for (i=0; i<strlen(buf); ++i) {
1515 color(BRIGHT_MAGENTA);
1529 * Present a key-menu line choice type of thing
1531 char keymenu(char *menuprompt, char *menustring) {
1537 int display_prompt = 1;
1539 choices = num_tokens(menustring, '|');
1541 if (menuprompt != NULL) do_prompt = 1;
1542 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1545 if (display_prompt) {
1547 scr_printf("%s ", menuprompt);
1550 for (i=0; i<choices; ++i) {
1551 extract(buf, menustring, i);
1561 if ( (do_prompt) && (ch=='?') ) {
1562 scr_printf("\rOne of... ");
1564 for (i=0; i<choices; ++i) {
1565 extract(buf, menustring, i);
1574 for (i=0; i<choices; ++i) {
1575 extract(buf, menustring, i);
1576 for (c=1; c<strlen(buf); ++c) {
1577 if ( (ch == tolower(buf[c]))
1579 && (buf[c+1]=='>') ) {
1580 for (a=0; a<strlen(buf); ++a) {
1581 if ( (a!=(c-1)) && (a!=(c+1))) {