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 int rc_idle_threshold = 900;
80 char urls[MAXURLS][SIZ];
84 int next_lazy_cmd = 5;
86 int lines_printed = 0; /* line count for paginator */
87 extern int screenwidth, screenheight;
90 struct citcmd *cmdlist = NULL;
93 /* these variables are local to this module */
94 char keepalives_enabled = KA_YES; /* send NOOPs to server when idle */
95 int ok_to_interrupt = 0; /* print express msgs asynchronously */
96 time_t AnsiDetect; /* when did we send the detect code? */
97 int enable_color = 0; /* nonzero for ANSI color */
103 * If an interesting key has been pressed, return its value, otherwise 0
105 char was_a_key_pressed(void) {
115 retval = select(1, &rfds, NULL, NULL, &tv);
117 /* Careful! Disable keepalives during keyboard polling; we're probably
118 * in the middle of a data transfer from the server, in which case
119 * sending a NOOP would throw the client protocol out of sync.
121 if (FD_ISSET(0, &rfds)) {
122 set_keepalives(KA_NO);
123 the_character = inkey();
124 set_keepalives(KA_YES);
129 return(the_character);
137 * Check to see if we need to pause at the end of a screen.
138 * If we do, we have to disable server keepalives during the pause because
139 * we are probably in the middle of a server operation and the NOOP command
140 * would confuse everything.
142 int checkpagin(int lp, int pagin, int height)
146 if (sigcaught) return(lp);
147 thekey = was_a_key_pressed();
148 if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
150 if (thekey == 'n' || thekey == 'N')
152 if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
153 if (sigcaught) return(lp);
155 if (!pagin) return(0);
156 if (lp>=(height-1)) {
157 set_keepalives(KA_NO);
159 set_keepalives(KA_YES);
169 * pprintf() ... paginated version of printf()
171 void pprintf(const char *format, ...) {
173 static char buf[4096]; /* static for performance, change if needed */
176 /* If sigcaught is nonzero, a keypress has interrupted this and we
177 * should just drain output.
179 if (sigcaught) return;
181 /* Otherwise, start spewing... */
182 va_start(arg_ptr, format);
183 vsnprintf(buf, sizeof(buf), format, arg_ptr);
186 for (i=0; i<strlen(buf); ++i) {
190 lines_printed = checkpagin(lines_printed,
191 (userflags & US_PAGINATOR),
200 * print_express() - print express messages if there are any
202 void print_express(void)
212 if (express_msgs == 0)
218 if (strlen(rc_exp_cmd) == 0) {
223 while (express_msgs != 0) {
229 express_msgs = extract_int(&buf[4], 0);
230 timestamp = extract_long(&buf[4], 1);
231 flags = extract_int(&buf[4], 2);
232 extract(sender, &buf[4], 3);
233 extract(node, &buf[4], 4);
234 strcpy(last_paged, sender);
236 stamp = localtime(×tamp);
238 /* If the page is a Logoff Request, honor it. */
244 if (strlen(rc_exp_cmd) > 0) {
245 outpipe = popen(rc_exp_cmd, "w");
246 if (outpipe != NULL) {
247 /* Header derived from flags */
250 "Please log off now, as requested ");
252 fprintf(outpipe, "Broadcast message ");
254 fprintf(outpipe, "Chat request ");
256 fprintf(outpipe, "Message ");
257 /* Timestamp. Can this be improved? */
258 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
259 fprintf(outpipe, "at 12:%02d%cm",
261 stamp->tm_hour ? 'p' : 'a');
262 else if (stamp->tm_hour > 12) /* pm */
263 fprintf(outpipe, "at %d:%02dpm",
267 fprintf(outpipe, "at %d:%02dam",
268 stamp->tm_hour, stamp->tm_min);
269 fprintf(outpipe, " from %s", sender);
270 if (strncmp(serv_info.serv_nodename, node, 32))
271 fprintf(outpipe, " @%s", node);
272 fprintf(outpipe, ":\n");
273 while (serv_gets(buf), strcmp(buf, "000")) {
274 fprintf(outpipe, "%s\n", buf);
277 if (express_msgs == 0)
282 /* fall back to built-in express message display */
286 /* Header derived from flags */
288 scr_printf("Please log off now, as requested ");
290 scr_printf("Broadcast message ");
292 scr_printf("Chat request ");
294 scr_printf("Message ");
296 /* Timestamp. Can this be improved? */
297 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)/* 12am/12pm */
298 scr_printf("at 12:%02d%cm", stamp->tm_min,
299 stamp->tm_hour ? 'p' : 'a');
300 else if (stamp->tm_hour > 12) /* pm */
301 scr_printf("at %d:%02dpm",
302 stamp->tm_hour - 12, stamp->tm_min);
304 scr_printf("at %d:%02dam", stamp->tm_hour, stamp->tm_min);
307 scr_printf(" from %s", sender);
309 /* Remote node, if any */
310 if (strncmp(serv_info.serv_nodename, node, 32))
311 scr_printf(" @%s", node);
315 fmout(screenwidth, NULL, NULL, 1, screenheight, -1, 0);
317 /* when running in curses mode, the scroll bar in most
318 xterm-style programs becomes useless, so it makes sense to
319 pause after a screenful of pages if the user has been idle
320 for a while. However, this is annoying to some of the users
321 who aren't in curses mode and tend to leave their clients
322 idle. keepalives become disabled, resulting in getting booted
323 when coming back to the idle session. but they probably have
324 a working scrollback in their terminal, so disable it in this
327 if (!is_curses_enabled())
330 scr_printf("\n---\n");
337 void set_keepalives(int s)
339 keepalives_enabled = (char) s;
343 * This loop handles the "keepalive" messages sent to the server when idling.
346 static time_t idlet = 0;
347 static void really_do_keepalive(void) {
351 if (keepalives_enabled == KA_YES) {
356 if (ok_to_interrupt == 1) {
357 scr_printf("\r%64s\r", "");
359 scr_printf("%s%c ", room_name,
360 room_prompt(room_flags));
367 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
368 encapsulated interface; in theory there should be no need to touch these
369 globals outside of the async_ka_* functions. */
371 #ifdef THREADED_CLIENT
372 static pthread_t ka_thr_handle;
373 static int ka_thr_active = 0;
374 static int async_ka_enabled = 0;
376 static void *ka_thread(void *arg)
378 really_do_keepalive();
379 pthread_detach(ka_thr_handle);
384 /* start up a thread to handle a keepalive in the background */
385 static void async_ka_exec(void)
387 if (!ka_thr_active) {
389 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
390 perror("pthread_create");
395 #endif /* THREADED_CLIENT */
397 /* I changed this from static to not because I need to call it from
398 screen.c, either that or make something in screen.c not static.
399 Fix it how you like. Why all the staticness? stu */
401 void do_keepalive(void)
406 if ((now - idlet) < ((long) S_KEEPALIVE))
409 /* Do a space-backspace to keep telnet sessions from idling out */
410 scr_printf(" %c", 8);
413 #ifdef THREADED_CLIENT
414 if (async_ka_enabled)
418 really_do_keepalive();
422 /* Now the actual async-keepalve API that we expose to higher levels:
423 async_ka_start() and async_ka_end(). These do nothing when we don't have
424 threading enabled, so we avoid sprinkling ifdef's throughout the code. */
426 /* wait for a background keepalive to complete. this must be done before
427 attempting any further server requests! */
428 void async_ka_end(void)
430 #ifdef THREADED_CLIENT
432 pthread_join(ka_thr_handle, NULL);
438 /* tell do_keepalive() that keepalives are asynchronous. */
439 void async_ka_start(void)
441 #ifdef THREADED_CLIENT
448 { /* get a character from the keyboard, with */
449 int a; /* the watchdog timer in effect if necessary */
459 /* This loop waits for keyboard input. If the keepalive
460 * timer expires, it sends a keepalive to the server if
461 * necessary and then waits again.
464 scr_set_windowsize();
466 scr_set_windowsize();
470 tv.tv_sec = S_KEEPALIVE;
473 select(1, &rfds, NULL, NULL, &tv);
474 } while (!FD_ISSET(0, &rfds));
476 /* At this point, there's input, so fetch it.
477 * (There's a hole in the bucket...)
479 a = scr_getc(SCR_NOBLOCK);
486 if (((a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
487 && ((a < 32) || (a > 126)))
490 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
501 { /* Returns 1 for yes, 0 for no */
517 /* Returns 1 for yes, 0 for no, arg is default value */
540 /* Gets a line from the terminal */
541 /* string == Pointer to string buffer */
542 /* lim == Maximum length - if negative, no-show */
543 void getline(char *string, int lim)
557 if ((a == 8) && (strlen(string) == 0))
559 if ((a != 10) && (a != 8) && (strlen(string) == lim))
561 if ((a == 8) && (string[0] != 0)) {
562 string[strlen(string) - 1] = 0;
587 * strprompt() - prompt for a string, print the existing value and
588 * allow the user to press return to keep it...
590 void strprompt(char *prompt, char *str, int len)
597 scr_printf("%s ", prompt);
600 color(BRIGHT_MAGENTA);
603 scr_printf("%s", str);
606 for (i=0; i<strlen(str); ++i) {
623 * boolprompt() - prompt for a yes/no, print the existing value and
624 * allow the user to press return to keep it...
626 int boolprompt(char *prompt, int prev_val)
631 scr_printf("%s ", prompt);
634 color(BRIGHT_MAGENTA);
635 scr_printf("%s", (prev_val ? "Yes" : "No"));
639 r = (yesno_d(prev_val));
645 * intprompt() - like strprompt(), except for an integer
646 * (note that it RETURNS the new value!)
648 int intprompt(char *prompt, int ival, int imin, int imax)
656 snprintf(buf, sizeof buf, "%d", i);
657 strprompt(prompt, buf, 15);
659 for (p=0; p<strlen(buf); ++p) {
660 if ( (!isdigit(buf[p]))
661 && ( (buf[p]!='-') || (p!=0) ) )
665 scr_printf("*** Must be no less than %d.\n", imin);
667 scr_printf("*** Must be no more than %d.\n", imax);
668 } while ((i < imin) || (i > imax));
673 * newprompt() - prompt for a string with no existing value
674 * (clears out string buffer first)
676 void newprompt(char *prompt, char *str, int len)
678 color(BRIGHT_MAGENTA);
679 scr_printf("%s", prompt);
687 { /* returns a lower case value */
696 * parse the citadel.rc file
698 void load_command_set(void)
703 struct citcmd *lastcmd = NULL;
708 /* first, set up some defaults for non-required variables */
710 strcpy(editor_path, "");
711 strcpy(printcmd, "");
712 strcpy(rc_username, "");
713 strcpy(rc_password, "");
716 rc_allow_attachments = 0;
717 rc_remember_passwords = 0;
718 strcpy(rc_exp_cmd, "");
719 rc_display_message_numbers = 0;
720 rc_force_mail_prompts = 0;
722 strcpy(rc_url_cmd, "");
724 rc_encrypt = RC_DEFAULT;
727 rc_screen = RC_DEFAULT;
729 rc_alt_semantics = 0;
731 /* now try to open the citadel.rc file */
734 if (getenv("HOME") != NULL) {
735 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
736 ccfile = fopen(buf, "r");
738 if (ccfile == NULL) {
739 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
740 ccfile = fopen(buf, "r");
742 if (ccfile == NULL) {
743 ccfile = fopen("/etc/citadel.rc", "r");
745 if (ccfile == NULL) {
746 ccfile = fopen("./citadel.rc", "r");
748 if (ccfile == NULL) {
749 perror("commands: cannot open citadel.rc");
752 while (fgets(buf, sizeof buf, ccfile) != NULL) {
753 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
754 buf[strlen(buf) - 1] = 0;
756 if (!strncasecmp(buf, "encrypt=", 8)) {
757 if (!strcasecmp(&buf[8], "yes")) {
761 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
766 else if (!strcasecmp(&buf[8], "no")) {
769 else if (!strcasecmp(&buf[8], "default")) {
770 rc_encrypt = RC_DEFAULT;
776 if (!strncasecmp(buf, "fullscreen=", 11)) {
777 if (!strcasecmp(&buf[11], "yes"))
779 else if (!strcasecmp(&buf[11], "no"))
784 if (!strncasecmp(buf, "editor=", 7))
785 strcpy(editor_path, &buf[7]);
787 if (!strncasecmp(buf, "printcmd=", 9))
788 strcpy(printcmd, &buf[9]);
790 if (!strncasecmp(buf, "expcmd=", 7))
791 strcpy(rc_exp_cmd, &buf[7]);
793 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
794 have_xterm = (char) atoi(&buf[24]);
796 if (!strncasecmp(buf, "use_floors=", 11)) {
797 if (!strcasecmp(&buf[11], "yes"))
798 rc_floor_mode = RC_YES;
799 if (!strcasecmp(&buf[11], "no"))
800 rc_floor_mode = RC_NO;
801 if (!strcasecmp(&buf[11], "default"))
802 rc_floor_mode = RC_DEFAULT;
804 if (!strncasecmp(buf, "beep=", 5)) {
805 rc_exp_beep = atoi(&buf[5]);
807 if (!strncasecmp(buf, "allow_attachments=", 18)) {
808 rc_allow_attachments = atoi(&buf[18]);
810 if (!strncasecmp(buf, "idle_threshold=", 14)) {
811 rc_idle_threshold = atoi(&buf[14]);
813 if (!strncasecmp(buf, "remember_passwords=", 19)) {
814 rc_remember_passwords = atoi(&buf[19]);
816 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
817 rc_display_message_numbers = atoi(&buf[24]);
819 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
820 rc_force_mail_prompts = atoi(&buf[19]);
822 if (!strncasecmp(buf, "ansi_color=", 11)) {
823 if (!strncasecmp(&buf[11], "on", 2))
825 if (!strncasecmp(&buf[11], "auto", 4))
826 rc_ansi_color = 2; /* autodetect */
827 if (!strncasecmp(&buf[11], "user", 4))
828 rc_ansi_color = 3; /* user config */
830 if (!strncasecmp(buf, "prompt_control=", 15)) {
831 if (!strncasecmp(&buf[15], "on", 2))
832 rc_prompt_control = 1;
833 if (!strncasecmp(&buf[15], "user", 4))
834 rc_prompt_control = 3; /* user config */
836 if (!strncasecmp(buf, "username=", 9))
837 strcpy(rc_username, &buf[9]);
839 if (!strncasecmp(buf, "password=", 9))
840 strcpy(rc_password, &buf[9]);
842 if (!strncasecmp(buf, "urlcmd=", 7))
843 strcpy(rc_url_cmd, &buf[7]);
845 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
846 if (!strncasecmp(&buf[11], "yes", 3))
847 rc_alt_semantics = 1;
848 if (!strncasecmp(&buf[11], "no", 2))
849 rc_alt_semantics = 0;
851 if (!strncasecmp(buf, "cmd=", 4)) {
852 strcpy(buf, &buf[4]);
854 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
856 cptr->c_cmdnum = atoi(buf);
857 for (d = strlen(buf); d >= 0; --d)
860 strcpy(buf, &buf[b + 1]);
862 cptr->c_axlevel = atoi(buf);
863 for (d = strlen(buf); d >= 0; --d)
866 strcpy(buf, &buf[b + 1]);
868 for (a = 0; a < 5; ++a)
869 cptr->c_keys[a][0] = 0;
873 buf[strlen(buf) + 1] = 0;
874 while (strlen(buf) > 0) {
876 for (d = strlen(buf); d >= 0; --d)
879 strncpy(cptr->c_keys[a], buf, b);
880 cptr->c_keys[a][b] = 0;
882 strcpy(buf, &buf[b + 1]);
892 lastcmd->next = cptr;
902 * return the key associated with a command
904 char keycmd(char *cmdstr)
908 for (a = 0; a < strlen(cmdstr); ++a)
909 if (cmdstr[a] == '&')
910 return (tolower(cmdstr[a + 1]));
916 * Output the string from a key command without the ampersand
917 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
919 char *cmd_expand(char *strbuf, int mode)
927 for (a = 0; a < strlen(exp); ++a) {
928 if (strbuf[a] == '&') {
931 strcpy(&exp[a], &exp[a + 1]);
935 strcpy(buf, &exp[a + 2]);
941 if (!strncmp(&exp[a], "^r", 2)) {
943 strcpy(&exp[a], room_name);
944 strcat(exp, &buf[a + 2]);
946 if (!strncmp(&exp[a], "^c", 2)) {
948 strcpy(&exp[a + 1], &exp[a + 2]);
958 * Comparison function to determine if entered commands match a
959 * command loaded from the config file.
961 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
972 for (a = 0; a < ncomp; ++a) {
973 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
974 || (cptr->c_axlevel > cmdax))
982 * This function returns 1 if a given command requires a string input
984 int requires_string(struct citcmd *cptr, int ncomp)
989 strcpy(buf, cptr->c_keys[ncomp - 1]);
990 for (a = 0; a < strlen(buf); ++a) {
999 * Input a command at the main prompt.
1000 * This function returns an integer command number. If the command prompts
1001 * for a string then it is placed in the supplied buffer.
1003 int getcmd(char *argbuf)
1012 struct citcmd *cptr;
1015 * Starting a new command now, so set sigcaught to 0. This variable
1016 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1017 * been interrupted by a keypress.
1021 /* Switch color support on or off if we're in user mode */
1022 if (rc_ansi_color == 3) {
1023 if (userflags & US_COLOR)
1028 /* if we're running in idiot mode, display a cute little menu */
1029 IFNEXPERT formout("mainmenu");
1031 print_express(); /* print express messages if there are any */
1034 for (a = 0; a < 5; ++a)
1036 /* now the room prompt... */
1037 ok_to_interrupt = 1;
1038 color(BRIGHT_WHITE);
1039 scr_printf("\n%s", room_name);
1041 scr_printf("%c ", room_prompt(room_flags));
1046 ok_to_interrupt = 0;
1048 /* Handle the backspace key, but only if there's something
1049 * to backspace over...
1051 if ((ch == 8) && (cmdpos > 0)) {
1052 back(cmdspaces[cmdpos - 1] + 1);
1056 /* Spacebar invokes "lazy traversal" commands */
1057 if ((ch == 32) && (cmdpos == 0)) {
1058 this_lazy_cmd = next_lazy_cmd;
1059 if (this_lazy_cmd == 13)
1061 if (this_lazy_cmd == 5)
1063 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1064 if (cptr->c_cmdnum == this_lazy_cmd) {
1065 for (a = 0; a < 5; ++a)
1066 if (cptr->c_keys[a][0] != 0)
1067 scr_printf("%s ", cmd_expand(
1068 cptr->c_keys[a], 0));
1070 return (this_lazy_cmd);
1074 return (this_lazy_cmd);
1076 /* Otherwise, process the command */
1077 cmdbuf[cmdpos] = tolower(ch);
1079 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1080 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1082 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1083 cmdspaces[cmdpos] = strlen(
1084 cmd_expand(cptr->c_keys[cmdpos], 0));
1086 if ((cptr->c_keys[cmdpos + 1]) != 0)
1092 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1093 if (cmdmatch(cmdbuf, cptr, 5)) {
1094 /* We've found our command. */
1095 if (requires_string(cptr, cmdpos)) {
1096 getline(argbuf, 32);
1101 /* If this command is one that changes rooms,
1102 * then the next lazy-command (space bar)
1103 * should be "read new" instead of "goto"
1105 if ((cptr->c_cmdnum == 5)
1106 || (cptr->c_cmdnum == 6)
1107 || (cptr->c_cmdnum == 47)
1108 || (cptr->c_cmdnum == 52)
1109 || (cptr->c_cmdnum == 16)
1110 || (cptr->c_cmdnum == 20))
1113 return (cptr->c_cmdnum);
1119 pprintf("\rOne of ... \n");
1120 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1121 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1122 for (a = 0; a < 5; ++a) {
1123 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1129 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1131 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1132 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1133 for (a = 0; a < cmdpos; ++a) {
1135 cmd_expand(cptr->c_keys[a], 0));
1150 * set tty modes. commands are:
1152 * 01- set to bbs mode
1153 * 2 - save current settings for later restoral
1154 * 3 - restore saved settings
1156 #ifdef HAVE_TERMIOS_H
1157 void sttybbs(int cmd)
1158 { /* SysV version of sttybbs() */
1159 struct termios live;
1160 static struct termios saved_settings;
1161 static int last_cmd = 0;
1168 if ((cmd == 0) || (cmd == 1)) {
1169 tcgetattr(0, &live);
1170 live.c_iflag = ISTRIP | IXON | IXANY;
1171 live.c_oflag = OPOST | ONLCR;
1172 live.c_lflag = ISIG | NOFLSH;
1174 live.c_cc[VINTR] = (-1);
1175 live.c_cc[VQUIT] = (-1);
1178 live.c_cc[VMIN] = 0;
1179 live.c_cc[VTIME] = 0;
1182 /* do we even need this stuff anymore? */
1183 /* live.c_line=0; */
1184 live.c_cc[VERASE] = 8;
1185 live.c_cc[VKILL] = 24;
1186 live.c_cc[VEOF] = 1;
1187 live.c_cc[VEOL] = 255;
1188 live.c_cc[VEOL2] = 0;
1189 live.c_cc[VSTART] = 0;
1190 tcsetattr(0, TCSADRAIN, &live);
1193 tcgetattr(0, &saved_settings);
1196 tcsetattr(0, TCSADRAIN, &saved_settings);
1200 void sttybbs(int cmd)
1201 { /* BSD version of sttybbs() */
1203 static struct sgttyb saved_settings;
1204 static int last_cmd = 0;
1211 if ((cmd == 0) || (cmd == 1)) {
1213 live.sg_flags |= CBREAK;
1214 live.sg_flags |= CRMOD;
1215 live.sg_flags |= NL1;
1216 live.sg_flags &= ~ECHO;
1218 live.sg_flags |= NOFLSH;
1222 gtty(0, &saved_settings);
1225 stty(0, &saved_settings);
1232 * display_help() - help file viewer
1234 void display_help(char *name)
1241 * fmout() - Citadel text formatter and paginator
1244 int width, /* screen width to use */
1245 FILE *fpin, /* file to read from, or NULL to read from server */
1246 FILE *fpout, /* File to write to, or NULL to write to screen */
1247 char pagin, /* nonzero if we should use the paginator */
1248 int height, /* screen height to use */
1249 int starting_lp,/* starting value for lines_printed, -1 for global */
1250 char subst) /* nonzero if we should use hypertext mode */
1252 int a, b, c, d, old;
1258 num_urls = 0; /* Start with a clean slate of embedded URL's */
1260 if (starting_lp >= 0) {
1261 lines_printed = starting_lp;
1266 c = 1; /* c is the current pos */
1268 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1269 if (fpin != NULL) { /* read from file */
1272 if (eof_flag == 0) {
1274 buffer[strlen(buffer) + 1] = 0;
1275 buffer[strlen(buffer)] = a;
1277 } else { /* read from server */
1279 serv_gets(&buffer[d]);
1280 while ((!isspace(buffer[d])) && (isspace(buffer[strlen(buffer) - 1])))
1281 buffer[strlen(buffer) - 1] = 0;
1282 if (!strcmp(&buffer[d], "000")) {
1285 while (isspace(buffer[strlen(buffer) - 1]))
1286 buffer[strlen(buffer) - 1] = 0;
1294 if ( (!strncasecmp(buffer, "http://", 7))
1295 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1296 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1297 for (a=0; a<strlen(urls[num_urls]); ++a) {
1298 b = urls[num_urls][a];
1299 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1300 || (b==13) || (b==9) || (b=='\"') )
1301 urls[num_urls][a] = 0;
1306 buffer[strlen(buffer) + 1] = 0;
1308 strcpy(buffer, &buffer[1]);
1315 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1317 if (((old == 13) || (old == 10)) && (isspace(real))) {
1319 fprintf(fpout, "\n");
1323 lines_printed = checkpagin(lines_printed, pagin, height);
1331 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1333 fprintf(fpout, "\n%s", aaa);
1335 scr_printf("\n%s", aaa);
1337 lines_printed = checkpagin(lines_printed, pagin, height);
1347 if ((strlen(aaa) + c) > (width - 1)) {
1350 fprintf(fpout, "\n");
1354 lines_printed = checkpagin(lines_printed, pagin, height);
1358 fprintf(fpout, "%s ", aaa);
1360 scr_printf("%s ", aaa);
1363 c = c + strlen(aaa);
1367 if ((a == 13) || (a == 10)) {
1369 fprintf(fpout, "%s\n", aaa);
1371 scr_printf("%s\n", aaa);
1373 lines_printed = checkpagin(lines_printed, pagin, height);
1376 if (sigcaught) goto OOPS;
1382 /* keypress caught; drain the server */
1385 } while (strcmp(aaa, "000"));
1389 fprintf(fpout, "\n");
1393 lines_printed = checkpagin(lines_printed, pagin, height);
1400 * support ANSI color if defined
1402 void color(int colornum)
1404 static int is_bold = 0;
1405 static int hold_color, current_color;
1407 if (colornum == COLOR_PUSH) {
1408 hold_color = current_color;
1412 if (colornum == COLOR_POP) {
1417 current_color = colornum;
1419 #ifdef HAVE_CURSES_H
1420 if (scr_color(colornum))
1423 /* When switching to dim white, actually output an 'original
1424 * pair' sequence -- this looks better on black-on-white
1427 if (colornum == DIM_WHITE)
1428 printf("\033[39;49m");
1430 printf("\033[3%d;40m", (colornum & 7));
1432 if ((colornum >= 8) && (is_bold == 0)) {
1435 } else if ((colornum < 8) && (is_bold == 1)) {
1443 void cls(int colornum)
1446 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1453 * Detect whether ANSI color is available (answerback)
1455 void send_ansi_detect(void)
1457 if (rc_ansi_color == 2) {
1464 void look_for_ansi(void)
1472 if (rc_ansi_color == 0) {
1474 } else if (rc_ansi_color == 1) {
1476 } else if (rc_ansi_color == 2) {
1478 /* otherwise, do the auto-detect */
1483 if ((now - AnsiDetect) < 2)
1492 select(1, &rfds, NULL, NULL, &tv);
1493 if (FD_ISSET(0, &rfds)) {
1494 abuf[strlen(abuf) + 1] = 0;
1495 read(0, &abuf[strlen(abuf)], 1);
1497 } while (FD_ISSET(0, &rfds));
1499 for (a = 0; a < strlen(abuf); ++a) {
1500 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1501 && (abuf[a + 2] == '?')) {
1510 * Display key options (highlight hotkeys inside angle brackets)
1512 void keyopt(char *buf) {
1516 for (i=0; i<strlen(buf); ++i) {
1519 color(BRIGHT_MAGENTA);
1533 * Present a key-menu line choice type of thing
1535 char keymenu(char *menuprompt, char *menustring) {
1541 int display_prompt = 1;
1543 choices = num_tokens(menustring, '|');
1545 if (menuprompt != NULL) do_prompt = 1;
1546 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1549 if (display_prompt) {
1551 scr_printf("%s ", menuprompt);
1554 for (i=0; i<choices; ++i) {
1555 extract(buf, menustring, i);
1565 if ( (do_prompt) && (ch=='?') ) {
1566 scr_printf("\rOne of... ");
1568 for (i=0; i<choices; ++i) {
1569 extract(buf, menustring, i);
1578 for (i=0; i<choices; ++i) {
1579 extract(buf, menustring, i);
1580 for (c=1; c<strlen(buf); ++c) {
1581 if ( (ch == tolower(buf[c]))
1583 && (buf[c+1]=='>') ) {
1584 for (a=0; a<strlen(buf); ++a) {
1585 if ( (a!=(c-1)) && (a!=(c+1))) {