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"
55 #include "citadel_ipc.h"
68 #define IFNEXPERT if ((userflags&US_EXPERT)==0)
72 char rc_exp_cmd[1024];
73 int rc_allow_attachments;
74 int rc_display_message_numbers;
75 int rc_force_mail_prompts;
76 int rc_remember_passwords;
79 int rc_prompt_control = 0;
80 time_t rc_idle_threshold = (time_t)900;
81 char urls[MAXURLS][SIZ];
85 int next_lazy_cmd = 5;
87 int lines_printed = 0; /* line count for paginator */
88 extern int screenwidth, screenheight;
91 struct citcmd *cmdlist = NULL;
94 /* these variables are local to this module */
95 char keepalives_enabled = KA_YES; /* send NOOPs to server when idle */
96 int ok_to_interrupt = 0; /* print express msgs asynchronously */
97 time_t AnsiDetect; /* when did we send the detect code? */
98 int enable_color = 0; /* nonzero for ANSI color */
104 * If an interesting key has been pressed, return its value, otherwise 0
106 char was_a_key_pressed(void) {
116 retval = select(1, &rfds, NULL, NULL, &tv);
118 /* Careful! Disable keepalives during keyboard polling; we're probably
119 * in the middle of a data transfer from the server, in which case
120 * sending a NOOP would throw the client protocol out of sync.
122 if (FD_ISSET(0, &rfds)) {
123 set_keepalives(KA_NO);
124 the_character = inkey();
125 set_keepalives(KA_YES);
130 return(the_character);
138 * Check to see if we need to pause at the end of a screen.
139 * If we do, we have to switch to half keepalives during the pause because
140 * we are probably in the middle of a server operation and the NOOP command
141 * would confuse everything.
143 int checkpagin(int lp, int pagin, int height)
147 if (sigcaught) return(lp);
148 thekey = was_a_key_pressed();
149 if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
151 if (thekey == 'n' || thekey == 'N')
153 if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
154 if (sigcaught) return(lp);
156 if (!pagin) return(0);
157 if (lp>=(height-1)) {
158 set_keepalives(KA_HALF);
160 set_keepalives(KA_YES);
170 * pprintf() ... paginated version of printf()
172 void pprintf(const char *format, ...) {
174 static char buf[4096]; /* static for performance, change if needed */
177 /* If sigcaught is nonzero, a keypress has interrupted this and we
178 * should just drain output.
180 if (sigcaught) return;
182 /* Otherwise, start spewing... */
183 va_start(arg_ptr, format);
184 vsnprintf(buf, sizeof(buf), format, arg_ptr);
187 for (i=0; i<strlen(buf); ++i) {
191 lines_printed = checkpagin(lines_printed,
192 (userflags & US_PAGINATOR),
201 * print_express() - print express messages if there are any
203 void print_express(void)
212 char *listing = NULL;
213 int r; /* IPC result code */
215 if (express_msgs == 0)
221 if (strlen(rc_exp_cmd) == 0) {
226 while (express_msgs != 0) {
227 r = CtdlIPCGetInstantMessage(&listing, buf);
231 express_msgs = extract_int(buf, 0);
232 timestamp = extract_long(buf, 1);
233 flags = extract_int(buf, 2);
234 extract(sender, buf, 3);
235 extract(node, buf, 4);
236 strcpy(last_paged, sender);
238 stamp = localtime(×tamp);
240 /* If the page is a Logoff Request, honor it. */
246 if (strlen(rc_exp_cmd) > 0) {
247 outpipe = popen(rc_exp_cmd, "w");
248 if (outpipe != NULL) {
249 /* Header derived from flags */
252 "Please log off now, as requested ");
254 fprintf(outpipe, "Broadcast message ");
256 fprintf(outpipe, "Chat request ");
258 fprintf(outpipe, "Message ");
259 /* Timestamp. Can this be improved? */
260 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
261 fprintf(outpipe, "at 12:%02d%cm",
263 stamp->tm_hour ? 'p' : 'a');
264 else if (stamp->tm_hour > 12) /* pm */
265 fprintf(outpipe, "at %d:%02dpm",
269 fprintf(outpipe, "at %d:%02dam",
270 stamp->tm_hour, stamp->tm_min);
271 fprintf(outpipe, " from %s", sender);
272 if (strncmp(serv_info.serv_nodename, node, 32))
273 fprintf(outpipe, " @%s", node);
274 fprintf(outpipe, ":\n%s\n", listing);
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, listing, 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) {
352 /* If full keepalives are enabled, send a NOOP to the server and
353 * wait for a response.
355 if (keepalives_enabled == KA_YES) {
360 if (ok_to_interrupt == 1) {
361 scr_printf("\r%64s\r", "");
363 scr_printf("%s%c ", room_name,
364 room_prompt(room_flags));
370 /* If half keepalives are enabled, send a QNOP to the server (if the
371 * server supports it) and then do nothing.
373 if ( (keepalives_enabled == KA_HALF)
374 && (serv_info.serv_supports_qnop > 0) ) {
379 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
380 encapsulated interface; in theory there should be no need to touch these
381 globals outside of the async_ka_* functions. */
383 #ifdef THREADED_CLIENT
384 static pthread_t ka_thr_handle;
385 static int ka_thr_active = 0;
386 static int async_ka_enabled = 0;
388 static void *ka_thread(void *arg)
390 really_do_keepalive();
391 pthread_detach(ka_thr_handle);
396 /* start up a thread to handle a keepalive in the background */
397 static void async_ka_exec(void)
399 if (!ka_thr_active) {
401 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
402 perror("pthread_create");
407 #endif /* THREADED_CLIENT */
409 /* I changed this from static to not because I need to call it from
410 screen.c, either that or make something in screen.c not static.
411 Fix it how you like. Why all the staticness? stu */
413 void do_keepalive(void)
418 if ((now - idlet) < ((long) S_KEEPALIVE))
421 /* Do a space-backspace to keep telnet sessions from idling out */
422 scr_printf(" %c", 8);
425 #ifdef THREADED_CLIENT
426 if (async_ka_enabled)
430 really_do_keepalive();
434 /* Now the actual async-keepalve API that we expose to higher levels:
435 async_ka_start() and async_ka_end(). These do nothing when we don't have
436 threading enabled, so we avoid sprinkling ifdef's throughout the code. */
438 /* wait for a background keepalive to complete. this must be done before
439 attempting any further server requests! */
440 void async_ka_end(void)
442 #ifdef THREADED_CLIENT
444 pthread_join(ka_thr_handle, NULL);
450 /* tell do_keepalive() that keepalives are asynchronous. */
451 void async_ka_start(void)
453 #ifdef THREADED_CLIENT
460 { /* get a character from the keyboard, with */
461 int a; /* the watchdog timer in effect if necessary */
471 /* This loop waits for keyboard input. If the keepalive
472 * timer expires, it sends a keepalive to the server if
473 * necessary and then waits again.
476 scr_set_windowsize();
478 scr_set_windowsize();
482 tv.tv_sec = S_KEEPALIVE;
485 select(1, &rfds, NULL, NULL, &tv);
486 } while (!FD_ISSET(0, &rfds));
488 /* At this point, there's input, so fetch it.
489 * (There's a hole in the bucket...)
491 a = scr_getc(SCR_NOBLOCK);
498 if (((a != 23) && (a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
499 && ((a < 32) || (a > 126)))
502 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
513 { /* Returns 1 for yes, 0 for no */
529 /* Returns 1 for yes, 0 for no, arg is default value */
552 /* Gets a line from the terminal */
553 /* string == Pointer to string buffer */
554 /* lim == Maximum length - if negative, no-show */
555 void getline(char *string, int lim)
569 if ((a == 8 || a == 23) && (strlen(string) == 0))
571 if ((a != 10) && (a != 8) && (strlen(string) == lim))
573 if ((a == 8) && (string[0] != 0)) {
574 string[strlen(string) - 1] = 0;
575 scr_putc(8); scr_putc(32); scr_putc(8);
578 if ((a == 23) && (string[0] != 0)) {
580 string[strlen(string) - 1] = 0;
581 scr_putc(8); scr_putc(32); scr_putc(8);
582 } while (strlen(string) && string[strlen(string) - 1] != ' ');
604 * strprompt() - prompt for a string, print the existing value and
605 * allow the user to press return to keep it...
607 void strprompt(char *prompt, char *str, int len)
614 scr_printf("%s ", prompt);
617 color(BRIGHT_MAGENTA);
620 scr_printf("%s", str);
623 for (i=0; i<strlen(str); ++i) {
640 * boolprompt() - prompt for a yes/no, print the existing value and
641 * allow the user to press return to keep it...
643 int boolprompt(char *prompt, int prev_val)
648 scr_printf("%s ", prompt);
651 color(BRIGHT_MAGENTA);
652 scr_printf("%s", (prev_val ? "Yes" : "No"));
656 r = (yesno_d(prev_val));
662 * intprompt() - like strprompt(), except for an integer
663 * (note that it RETURNS the new value!)
665 int intprompt(char *prompt, int ival, int imin, int imax)
673 snprintf(buf, sizeof buf, "%d", i);
674 strprompt(prompt, buf, 15);
676 for (p=0; p<strlen(buf); ++p) {
677 if ( (!isdigit(buf[p]))
678 && ( (buf[p]!='-') || (p!=0) ) )
682 scr_printf("*** Must be no less than %d.\n", imin);
684 scr_printf("*** Must be no more than %d.\n", imax);
685 } while ((i < imin) || (i > imax));
690 * newprompt() - prompt for a string with no existing value
691 * (clears out string buffer first)
693 void newprompt(char *prompt, char *str, int len)
695 color(BRIGHT_MAGENTA);
696 scr_printf("%s", prompt);
704 { /* returns a lower case value */
713 * parse the citadel.rc file
715 void load_command_set(void)
720 struct citcmd *lastcmd = NULL;
725 /* first, set up some defaults for non-required variables */
727 strcpy(editor_path, "");
728 strcpy(printcmd, "");
729 strcpy(rc_username, "");
730 strcpy(rc_password, "");
733 rc_allow_attachments = 0;
734 rc_remember_passwords = 0;
735 strcpy(rc_exp_cmd, "");
736 rc_display_message_numbers = 0;
737 rc_force_mail_prompts = 0;
739 strcpy(rc_url_cmd, "");
741 rc_encrypt = RC_DEFAULT;
744 rc_screen = RC_DEFAULT;
746 rc_alt_semantics = 0;
748 /* now try to open the citadel.rc file */
751 if (getenv("HOME") != NULL) {
752 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
753 ccfile = fopen(buf, "r");
755 if (ccfile == NULL) {
756 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
757 ccfile = fopen(buf, "r");
759 if (ccfile == NULL) {
760 ccfile = fopen("/etc/citadel.rc", "r");
762 if (ccfile == NULL) {
763 ccfile = fopen("./citadel.rc", "r");
765 if (ccfile == NULL) {
766 perror("commands: cannot open citadel.rc");
769 while (fgets(buf, sizeof buf, ccfile) != NULL) {
770 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
771 buf[strlen(buf) - 1] = 0;
773 if (!strncasecmp(buf, "encrypt=", 8)) {
774 if (!strcasecmp(&buf[8], "yes")) {
778 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
783 else if (!strcasecmp(&buf[8], "no")) {
786 else if (!strcasecmp(&buf[8], "default")) {
787 rc_encrypt = RC_DEFAULT;
793 if (!strncasecmp(buf, "fullscreen=", 11)) {
794 if (!strcasecmp(&buf[11], "yes"))
796 else if (!strcasecmp(&buf[11], "no"))
801 if (!strncasecmp(buf, "editor=", 7))
802 strcpy(editor_path, &buf[7]);
804 if (!strncasecmp(buf, "printcmd=", 9))
805 strcpy(printcmd, &buf[9]);
807 if (!strncasecmp(buf, "expcmd=", 7))
808 strcpy(rc_exp_cmd, &buf[7]);
810 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
811 have_xterm = (char) atoi(&buf[24]);
813 if (!strncasecmp(buf, "use_floors=", 11)) {
814 if (!strcasecmp(&buf[11], "yes"))
815 rc_floor_mode = RC_YES;
816 if (!strcasecmp(&buf[11], "no"))
817 rc_floor_mode = RC_NO;
818 if (!strcasecmp(&buf[11], "default"))
819 rc_floor_mode = RC_DEFAULT;
821 if (!strncasecmp(buf, "beep=", 5)) {
822 rc_exp_beep = atoi(&buf[5]);
824 if (!strncasecmp(buf, "allow_attachments=", 18)) {
825 rc_allow_attachments = atoi(&buf[18]);
827 if (!strncasecmp(buf, "idle_threshold=", 15)) {
828 rc_idle_threshold = atoi(&buf[15]);
830 if (!strncasecmp(buf, "remember_passwords=", 19)) {
831 rc_remember_passwords = atoi(&buf[19]);
833 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
834 rc_display_message_numbers = atoi(&buf[24]);
836 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
837 rc_force_mail_prompts = atoi(&buf[19]);
839 if (!strncasecmp(buf, "ansi_color=", 11)) {
840 if (!strncasecmp(&buf[11], "on", 2))
842 if (!strncasecmp(&buf[11], "auto", 4))
843 rc_ansi_color = 2; /* autodetect */
844 if (!strncasecmp(&buf[11], "user", 4))
845 rc_ansi_color = 3; /* user config */
847 if (!strncasecmp(buf, "prompt_control=", 15)) {
848 if (!strncasecmp(&buf[15], "on", 2))
849 rc_prompt_control = 1;
850 if (!strncasecmp(&buf[15], "user", 4))
851 rc_prompt_control = 3; /* user config */
853 if (!strncasecmp(buf, "username=", 9))
854 strcpy(rc_username, &buf[9]);
856 if (!strncasecmp(buf, "password=", 9))
857 strcpy(rc_password, &buf[9]);
859 if (!strncasecmp(buf, "urlcmd=", 7))
860 strcpy(rc_url_cmd, &buf[7]);
862 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
863 if (!strncasecmp(&buf[11], "yes", 3))
864 rc_alt_semantics = 1;
865 if (!strncasecmp(&buf[11], "no", 2))
866 rc_alt_semantics = 0;
868 if (!strncasecmp(buf, "cmd=", 4)) {
869 strcpy(buf, &buf[4]);
871 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
873 cptr->c_cmdnum = atoi(buf);
874 for (d = strlen(buf); d >= 0; --d)
877 strcpy(buf, &buf[b + 1]);
879 cptr->c_axlevel = atoi(buf);
880 for (d = strlen(buf); d >= 0; --d)
883 strcpy(buf, &buf[b + 1]);
885 for (a = 0; a < 5; ++a)
886 cptr->c_keys[a][0] = 0;
890 buf[strlen(buf) + 1] = 0;
891 while (strlen(buf) > 0) {
893 for (d = strlen(buf); d >= 0; --d)
896 strncpy(cptr->c_keys[a], buf, b);
897 cptr->c_keys[a][b] = 0;
899 strcpy(buf, &buf[b + 1]);
909 lastcmd->next = cptr;
919 * return the key associated with a command
921 char keycmd(char *cmdstr)
925 for (a = 0; a < strlen(cmdstr); ++a)
926 if (cmdstr[a] == '&')
927 return (tolower(cmdstr[a + 1]));
933 * Output the string from a key command without the ampersand
934 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
936 char *cmd_expand(char *strbuf, int mode)
944 for (a = 0; a < strlen(exp); ++a) {
945 if (strbuf[a] == '&') {
948 strcpy(&exp[a], &exp[a + 1]);
952 strcpy(buf, &exp[a + 2]);
958 if (!strncmp(&exp[a], "^r", 2)) {
960 strcpy(&exp[a], room_name);
961 strcat(exp, &buf[a + 2]);
963 if (!strncmp(&exp[a], "^c", 2)) {
965 strcpy(&exp[a + 1], &exp[a + 2]);
975 * Comparison function to determine if entered commands match a
976 * command loaded from the config file.
978 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
989 for (a = 0; a < ncomp; ++a) {
990 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
991 || (cptr->c_axlevel > cmdax))
999 * This function returns 1 if a given command requires a string input
1001 int requires_string(struct citcmd *cptr, int ncomp)
1006 strcpy(buf, cptr->c_keys[ncomp - 1]);
1007 for (a = 0; a < strlen(buf); ++a) {
1016 * Input a command at the main prompt.
1017 * This function returns an integer command number. If the command prompts
1018 * for a string then it is placed in the supplied buffer.
1020 int getcmd(char *argbuf)
1029 struct citcmd *cptr;
1032 * Starting a new command now, so set sigcaught to 0. This variable
1033 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1034 * been interrupted by a keypress.
1038 /* Switch color support on or off if we're in user mode */
1039 if (rc_ansi_color == 3) {
1040 if (userflags & US_COLOR)
1045 /* if we're running in idiot mode, display a cute little menu */
1046 IFNEXPERT formout("mainmenu");
1048 print_express(); /* print express messages if there are any */
1051 for (a = 0; a < 5; ++a)
1053 /* now the room prompt... */
1054 ok_to_interrupt = 1;
1055 color(BRIGHT_WHITE);
1056 scr_printf("\n%s", room_name);
1058 scr_printf("%c ", room_prompt(room_flags));
1063 ok_to_interrupt = 0;
1065 /* Handle the backspace key, but only if there's something
1066 * to backspace over...
1068 if ((ch == 8) && (cmdpos > 0)) {
1069 back(cmdspaces[cmdpos - 1] + 1);
1073 /* Spacebar invokes "lazy traversal" commands */
1074 if ((ch == 32) && (cmdpos == 0)) {
1075 this_lazy_cmd = next_lazy_cmd;
1076 if (this_lazy_cmd == 13)
1078 if (this_lazy_cmd == 5)
1080 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1081 if (cptr->c_cmdnum == this_lazy_cmd) {
1082 for (a = 0; a < 5; ++a)
1083 if (cptr->c_keys[a][0] != 0)
1084 scr_printf("%s ", cmd_expand(
1085 cptr->c_keys[a], 0));
1087 return (this_lazy_cmd);
1091 return (this_lazy_cmd);
1093 /* Otherwise, process the command */
1094 cmdbuf[cmdpos] = tolower(ch);
1096 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1097 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1099 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1100 cmdspaces[cmdpos] = strlen(
1101 cmd_expand(cptr->c_keys[cmdpos], 0));
1103 if ((cptr->c_keys[cmdpos + 1]) != 0)
1109 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1110 if (cmdmatch(cmdbuf, cptr, 5)) {
1111 /* We've found our command. */
1112 if (requires_string(cptr, cmdpos)) {
1113 getline(argbuf, 32);
1118 /* If this command is one that changes rooms,
1119 * then the next lazy-command (space bar)
1120 * should be "read new" instead of "goto"
1122 if ((cptr->c_cmdnum == 5)
1123 || (cptr->c_cmdnum == 6)
1124 || (cptr->c_cmdnum == 47)
1125 || (cptr->c_cmdnum == 52)
1126 || (cptr->c_cmdnum == 16)
1127 || (cptr->c_cmdnum == 20))
1130 return (cptr->c_cmdnum);
1136 pprintf("\rOne of ... \n");
1137 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1138 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1139 for (a = 0; a < 5; ++a) {
1140 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1146 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1148 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1149 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1150 for (a = 0; a < cmdpos; ++a) {
1152 cmd_expand(cptr->c_keys[a], 0));
1167 * set tty modes. commands are:
1169 * 01- set to bbs mode
1170 * 2 - save current settings for later restoral
1171 * 3 - restore saved settings
1173 #ifdef HAVE_TERMIOS_H
1174 void sttybbs(int cmd)
1175 { /* SysV version of sttybbs() */
1176 struct termios live;
1177 static struct termios saved_settings;
1178 static int last_cmd = 0;
1185 if ((cmd == 0) || (cmd == 1)) {
1186 tcgetattr(0, &live);
1187 live.c_iflag = ISTRIP | IXON | IXANY;
1188 live.c_oflag = OPOST | ONLCR;
1189 live.c_lflag = ISIG | NOFLSH;
1191 live.c_cc[VINTR] = (-1);
1192 live.c_cc[VQUIT] = (-1);
1195 live.c_cc[VMIN] = 0;
1196 live.c_cc[VTIME] = 0;
1199 /* do we even need this stuff anymore? */
1200 /* live.c_line=0; */
1201 live.c_cc[VERASE] = 8;
1202 live.c_cc[VKILL] = 24;
1203 live.c_cc[VEOF] = 1;
1204 live.c_cc[VEOL] = 255;
1205 live.c_cc[VEOL2] = 0;
1206 live.c_cc[VSTART] = 0;
1207 tcsetattr(0, TCSADRAIN, &live);
1210 tcgetattr(0, &saved_settings);
1213 tcsetattr(0, TCSADRAIN, &saved_settings);
1217 void sttybbs(int cmd)
1218 { /* BSD version of sttybbs() */
1220 static struct sgttyb saved_settings;
1221 static int last_cmd = 0;
1228 if ((cmd == 0) || (cmd == 1)) {
1230 live.sg_flags |= CBREAK;
1231 live.sg_flags |= CRMOD;
1232 live.sg_flags |= NL1;
1233 live.sg_flags &= ~ECHO;
1235 live.sg_flags |= NOFLSH;
1239 gtty(0, &saved_settings);
1242 stty(0, &saved_settings);
1249 * display_help() - help file viewer
1251 void display_help(char *name)
1258 * fmout() - Citadel text formatter and paginator
1261 int width, /* screen width to use */
1262 FILE *fpin, /* file to read from, or NULL to format given text */
1263 char *text, /* Text to be formatted (when fpin is NULL) */
1264 FILE *fpout, /* File to write to, or NULL to write to screen */
1265 char pagin, /* nonzero if we should use the paginator */
1266 int height, /* screen height to use */
1267 int starting_lp,/* starting value for lines_printed, -1 for global */
1268 char subst) /* nonzero if we should use hypertext mode */
1277 num_urls = 0; /* Start with a clean slate of embedded URL's */
1279 if (starting_lp >= 0) {
1280 lines_printed = starting_lp;
1285 c = 1; /* c is the current pos */
1286 e = text; /* e is pointer to current pos */
1288 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1289 if (fpin != NULL) { /* read from file */
1292 if (eof_flag == 0) {
1294 buffer[strlen(buffer) + 1] = 0;
1295 buffer[strlen(buffer)] = a;
1297 } else { /* read from text */
1300 while (isspace(buffer[strlen(buffer) - 1]))
1301 buffer[strlen(buffer) - 1] = 0;
1302 buffer[strlen(buffer) + 1] = 0;
1303 buffer[strlen(buffer)] = 10;
1305 if (eof_flag == 0) {
1307 buffer[strlen(buffer) + 1] = 0;
1308 buffer[strlen(buffer)] = a;
1313 if ( (!strncasecmp(buffer, "http://", 7))
1314 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1315 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1316 for (a=0; a<strlen(urls[num_urls]); ++a) {
1317 b = urls[num_urls][a];
1318 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1319 || (b==13) || (b==9) || (b=='\"') )
1320 urls[num_urls][a] = 0;
1325 buffer[strlen(buffer) + 1] = 0;
1327 strcpy(buffer, &buffer[1]);
1334 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1336 if (((old == 13) || (old == 10)) && (isspace(real))) {
1338 fprintf(fpout, "\n");
1342 lines_printed = checkpagin(lines_printed, pagin, height);
1350 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1352 fprintf(fpout, "\n%s", aaa);
1354 scr_printf("\n%s", aaa);
1356 lines_printed = checkpagin(lines_printed, pagin, height);
1366 if ((strlen(aaa) + c) > (width - 1)) {
1369 fprintf(fpout, "\n");
1373 lines_printed = checkpagin(lines_printed, pagin, height);
1377 fprintf(fpout, "%s ", aaa);
1379 scr_printf("%s ", aaa);
1382 c = c + strlen(aaa);
1386 if ((a == 13) || (a == 10)) {
1388 fprintf(fpout, "%s\n", aaa);
1390 scr_printf("%s\n", aaa);
1392 lines_printed = checkpagin(lines_printed, pagin, height);
1395 if (sigcaught) goto OOPS;
1401 /* keypress caught; drain the server */
1404 } while (strcmp(aaa, "000")); */
1408 fprintf(fpout, "\n");
1412 lines_printed = checkpagin(lines_printed, pagin, height);
1419 * support ANSI color if defined
1421 void color(int colornum)
1423 static int is_bold = 0;
1424 static int hold_color, current_color;
1426 if (colornum == COLOR_PUSH) {
1427 hold_color = current_color;
1431 if (colornum == COLOR_POP) {
1436 current_color = colornum;
1438 #ifdef HAVE_CURSES_H
1439 if (scr_color(colornum))
1442 /* When switching to dim white, actually output an 'original
1443 * pair' sequence -- this looks better on black-on-white
1446 if (colornum == DIM_WHITE)
1447 printf("\033[39;49m");
1449 printf("\033[3%d;40m", (colornum & 7));
1451 if ((colornum >= 8) && (is_bold == 0)) {
1454 } else if ((colornum < 8) && (is_bold == 1)) {
1462 void cls(int colornum)
1465 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1472 * Detect whether ANSI color is available (answerback)
1474 void send_ansi_detect(void)
1476 if (rc_ansi_color == 2) {
1483 void look_for_ansi(void)
1491 if (rc_ansi_color == 0) {
1493 } else if (rc_ansi_color == 1) {
1495 } else if (rc_ansi_color == 2) {
1497 /* otherwise, do the auto-detect */
1502 if ((now - AnsiDetect) < 2)
1511 select(1, &rfds, NULL, NULL, &tv);
1512 if (FD_ISSET(0, &rfds)) {
1513 abuf[strlen(abuf) + 1] = 0;
1514 read(0, &abuf[strlen(abuf)], 1);
1516 } while (FD_ISSET(0, &rfds));
1518 for (a = 0; a < strlen(abuf); ++a) {
1519 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1520 && (abuf[a + 2] == '?')) {
1529 * Display key options (highlight hotkeys inside angle brackets)
1531 void keyopt(char *buf) {
1535 for (i=0; i<strlen(buf); ++i) {
1538 color(BRIGHT_MAGENTA);
1552 * Present a key-menu line choice type of thing
1554 char keymenu(char *menuprompt, char *menustring) {
1560 int display_prompt = 1;
1562 choices = num_tokens(menustring, '|');
1564 if (menuprompt != NULL) do_prompt = 1;
1565 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1568 if (display_prompt) {
1570 scr_printf("%s ", menuprompt);
1573 for (i=0; i<choices; ++i) {
1574 extract(buf, menustring, i);
1584 if ( (do_prompt) && (ch=='?') ) {
1585 scr_printf("\rOne of... ");
1587 for (i=0; i<choices; ++i) {
1588 extract(buf, menustring, i);
1597 for (i=0; i<choices; ++i) {
1598 extract(buf, menustring, i);
1599 for (c=1; c<strlen(buf); ++c) {
1600 if ( (ch == tolower(buf[c]))
1602 && (buf[c+1]=='>') ) {
1603 for (a=0; a<strlen(buf); ++a) {
1604 if ( (a!=(c-1)) && (a!=(c+1))) {