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) {
348 int r; /* IPC response code */
352 /* If full keepalives are enabled, send a NOOP to the server and
353 * wait for a response.
355 if (keepalives_enabled == KA_YES) {
357 if (express_msgs > 0) {
358 if (ok_to_interrupt == 1) {
359 scr_printf("\r%64s\r", "");
361 scr_printf("%s%c ", room_name,
362 room_prompt(room_flags));
368 /* If half keepalives are enabled, send a QNOP to the server (if the
369 * server supports it) and then do nothing.
371 if ( (keepalives_enabled == KA_HALF)
372 && (serv_info.serv_supports_qnop > 0) ) {
377 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
378 encapsulated interface; in theory there should be no need to touch these
379 globals outside of the async_ka_* functions. */
381 #ifdef THREADED_CLIENT
382 static pthread_t ka_thr_handle;
383 static int ka_thr_active = 0;
384 static int async_ka_enabled = 0;
386 static void *ka_thread(void *arg)
388 really_do_keepalive();
389 pthread_detach(ka_thr_handle);
394 /* start up a thread to handle a keepalive in the background */
395 static void async_ka_exec(void)
397 if (!ka_thr_active) {
399 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
400 perror("pthread_create");
405 #endif /* THREADED_CLIENT */
407 /* I changed this from static to not because I need to call it from
408 screen.c, either that or make something in screen.c not static.
409 Fix it how you like. Why all the staticness? stu */
411 void do_keepalive(void)
416 if ((now - idlet) < ((long) S_KEEPALIVE))
419 /* Do a space-backspace to keep telnet sessions from idling out */
420 scr_printf(" %c", 8);
423 #ifdef THREADED_CLIENT
424 if (async_ka_enabled)
428 really_do_keepalive();
432 /* Now the actual async-keepalve API that we expose to higher levels:
433 async_ka_start() and async_ka_end(). These do nothing when we don't have
434 threading enabled, so we avoid sprinkling ifdef's throughout the code. */
436 /* wait for a background keepalive to complete. this must be done before
437 attempting any further server requests! */
438 void async_ka_end(void)
440 #ifdef THREADED_CLIENT
442 pthread_join(ka_thr_handle, NULL);
448 /* tell do_keepalive() that keepalives are asynchronous. */
449 void async_ka_start(void)
451 #ifdef THREADED_CLIENT
458 { /* get a character from the keyboard, with */
459 int a; /* the watchdog timer in effect if necessary */
469 /* This loop waits for keyboard input. If the keepalive
470 * timer expires, it sends a keepalive to the server if
471 * necessary and then waits again.
474 scr_set_windowsize();
476 scr_set_windowsize();
480 tv.tv_sec = S_KEEPALIVE;
483 select(1, &rfds, NULL, NULL, &tv);
484 } while (!FD_ISSET(0, &rfds));
486 /* At this point, there's input, so fetch it.
487 * (There's a hole in the bucket...)
489 a = scr_getc(SCR_NOBLOCK);
496 if (((a != 23) && (a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
497 && ((a < 32) || (a > 126)))
500 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
511 { /* Returns 1 for yes, 0 for no */
527 /* Returns 1 for yes, 0 for no, arg is default value */
550 /* Gets a line from the terminal */
551 /* string == Pointer to string buffer */
552 /* lim == Maximum length - if negative, no-show */
553 void getline(char *string, int lim)
567 if ((a == 8 || a == 23) && (strlen(string) == 0))
569 if ((a != 10) && (a != 8) && (strlen(string) == lim))
571 if ((a == 8) && (string[0] != 0)) {
572 string[strlen(string) - 1] = 0;
573 scr_putc(8); scr_putc(32); scr_putc(8);
576 if ((a == 23) && (string[0] != 0)) {
578 string[strlen(string) - 1] = 0;
579 scr_putc(8); scr_putc(32); scr_putc(8);
580 } while (strlen(string) && string[strlen(string) - 1] != ' ');
602 * strprompt() - prompt for a string, print the existing value and
603 * allow the user to press return to keep it...
605 void strprompt(char *prompt, char *str, int len)
612 scr_printf("%s ", prompt);
615 color(BRIGHT_MAGENTA);
618 scr_printf("%s", str);
621 for (i=0; i<strlen(str); ++i) {
638 * boolprompt() - prompt for a yes/no, print the existing value and
639 * allow the user to press return to keep it...
641 int boolprompt(char *prompt, int prev_val)
646 scr_printf("%s ", prompt);
649 color(BRIGHT_MAGENTA);
650 scr_printf("%s", (prev_val ? "Yes" : "No"));
654 r = (yesno_d(prev_val));
660 * intprompt() - like strprompt(), except for an integer
661 * (note that it RETURNS the new value!)
663 int intprompt(char *prompt, int ival, int imin, int imax)
671 snprintf(buf, sizeof buf, "%d", i);
672 strprompt(prompt, buf, 15);
674 for (p=0; p<strlen(buf); ++p) {
675 if ( (!isdigit(buf[p]))
676 && ( (buf[p]!='-') || (p!=0) ) )
680 scr_printf("*** Must be no less than %d.\n", imin);
682 scr_printf("*** Must be no more than %d.\n", imax);
683 } while ((i < imin) || (i > imax));
688 * newprompt() - prompt for a string with no existing value
689 * (clears out string buffer first)
691 void newprompt(char *prompt, char *str, int len)
693 color(BRIGHT_MAGENTA);
694 scr_printf("%s", prompt);
702 { /* returns a lower case value */
711 * parse the citadel.rc file
713 void load_command_set(void)
718 struct citcmd *lastcmd = NULL;
723 /* first, set up some defaults for non-required variables */
725 strcpy(editor_path, "");
726 strcpy(printcmd, "");
727 strcpy(rc_username, "");
728 strcpy(rc_password, "");
731 rc_allow_attachments = 0;
732 rc_remember_passwords = 0;
733 strcpy(rc_exp_cmd, "");
734 rc_display_message_numbers = 0;
735 rc_force_mail_prompts = 0;
737 strcpy(rc_url_cmd, "");
739 rc_encrypt = RC_DEFAULT;
742 rc_screen = RC_DEFAULT;
744 rc_alt_semantics = 0;
746 /* now try to open the citadel.rc file */
749 if (getenv("HOME") != NULL) {
750 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
751 ccfile = fopen(buf, "r");
753 if (ccfile == NULL) {
754 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
755 ccfile = fopen(buf, "r");
757 if (ccfile == NULL) {
758 ccfile = fopen("/etc/citadel.rc", "r");
760 if (ccfile == NULL) {
761 ccfile = fopen("./citadel.rc", "r");
763 if (ccfile == NULL) {
764 perror("commands: cannot open citadel.rc");
767 while (fgets(buf, sizeof buf, ccfile) != NULL) {
768 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
769 buf[strlen(buf) - 1] = 0;
771 if (!strncasecmp(buf, "encrypt=", 8)) {
772 if (!strcasecmp(&buf[8], "yes")) {
776 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
781 else if (!strcasecmp(&buf[8], "no")) {
784 else if (!strcasecmp(&buf[8], "default")) {
785 rc_encrypt = RC_DEFAULT;
791 if (!strncasecmp(buf, "fullscreen=", 11)) {
792 if (!strcasecmp(&buf[11], "yes"))
794 else if (!strcasecmp(&buf[11], "no"))
799 if (!strncasecmp(buf, "editor=", 7))
800 strcpy(editor_path, &buf[7]);
802 if (!strncasecmp(buf, "printcmd=", 9))
803 strcpy(printcmd, &buf[9]);
805 if (!strncasecmp(buf, "expcmd=", 7))
806 strcpy(rc_exp_cmd, &buf[7]);
808 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
809 have_xterm = (char) atoi(&buf[24]);
811 if (!strncasecmp(buf, "use_floors=", 11)) {
812 if (!strcasecmp(&buf[11], "yes"))
813 rc_floor_mode = RC_YES;
814 if (!strcasecmp(&buf[11], "no"))
815 rc_floor_mode = RC_NO;
816 if (!strcasecmp(&buf[11], "default"))
817 rc_floor_mode = RC_DEFAULT;
819 if (!strncasecmp(buf, "beep=", 5)) {
820 rc_exp_beep = atoi(&buf[5]);
822 if (!strncasecmp(buf, "allow_attachments=", 18)) {
823 rc_allow_attachments = atoi(&buf[18]);
825 if (!strncasecmp(buf, "idle_threshold=", 15)) {
826 rc_idle_threshold = atoi(&buf[15]);
828 if (!strncasecmp(buf, "remember_passwords=", 19)) {
829 rc_remember_passwords = atoi(&buf[19]);
831 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
832 rc_display_message_numbers = atoi(&buf[24]);
834 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
835 rc_force_mail_prompts = atoi(&buf[19]);
837 if (!strncasecmp(buf, "ansi_color=", 11)) {
838 if (!strncasecmp(&buf[11], "on", 2))
840 if (!strncasecmp(&buf[11], "auto", 4))
841 rc_ansi_color = 2; /* autodetect */
842 if (!strncasecmp(&buf[11], "user", 4))
843 rc_ansi_color = 3; /* user config */
845 if (!strncasecmp(buf, "prompt_control=", 15)) {
846 if (!strncasecmp(&buf[15], "on", 2))
847 rc_prompt_control = 1;
848 if (!strncasecmp(&buf[15], "user", 4))
849 rc_prompt_control = 3; /* user config */
851 if (!strncasecmp(buf, "username=", 9))
852 strcpy(rc_username, &buf[9]);
854 if (!strncasecmp(buf, "password=", 9))
855 strcpy(rc_password, &buf[9]);
857 if (!strncasecmp(buf, "urlcmd=", 7))
858 strcpy(rc_url_cmd, &buf[7]);
860 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
861 if (!strncasecmp(&buf[11], "yes", 3))
862 rc_alt_semantics = 1;
863 if (!strncasecmp(&buf[11], "no", 2))
864 rc_alt_semantics = 0;
866 if (!strncasecmp(buf, "cmd=", 4)) {
867 strcpy(buf, &buf[4]);
869 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
871 cptr->c_cmdnum = atoi(buf);
872 for (d = strlen(buf); d >= 0; --d)
875 strcpy(buf, &buf[b + 1]);
877 cptr->c_axlevel = atoi(buf);
878 for (d = strlen(buf); d >= 0; --d)
881 strcpy(buf, &buf[b + 1]);
883 for (a = 0; a < 5; ++a)
884 cptr->c_keys[a][0] = 0;
888 buf[strlen(buf) + 1] = 0;
889 while (strlen(buf) > 0) {
891 for (d = strlen(buf); d >= 0; --d)
894 strncpy(cptr->c_keys[a], buf, b);
895 cptr->c_keys[a][b] = 0;
897 strcpy(buf, &buf[b + 1]);
907 lastcmd->next = cptr;
917 * return the key associated with a command
919 char keycmd(char *cmdstr)
923 for (a = 0; a < strlen(cmdstr); ++a)
924 if (cmdstr[a] == '&')
925 return (tolower(cmdstr[a + 1]));
931 * Output the string from a key command without the ampersand
932 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
934 char *cmd_expand(char *strbuf, int mode)
942 for (a = 0; a < strlen(exp); ++a) {
943 if (strbuf[a] == '&') {
946 strcpy(&exp[a], &exp[a + 1]);
950 strcpy(buf, &exp[a + 2]);
956 if (!strncmp(&exp[a], "^r", 2)) {
958 strcpy(&exp[a], room_name);
959 strcat(exp, &buf[a + 2]);
961 if (!strncmp(&exp[a], "^c", 2)) {
963 strcpy(&exp[a + 1], &exp[a + 2]);
973 * Comparison function to determine if entered commands match a
974 * command loaded from the config file.
976 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
987 for (a = 0; a < ncomp; ++a) {
988 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
989 || (cptr->c_axlevel > cmdax))
997 * This function returns 1 if a given command requires a string input
999 int requires_string(struct citcmd *cptr, int ncomp)
1004 strcpy(buf, cptr->c_keys[ncomp - 1]);
1005 for (a = 0; a < strlen(buf); ++a) {
1014 * Input a command at the main prompt.
1015 * This function returns an integer command number. If the command prompts
1016 * for a string then it is placed in the supplied buffer.
1018 int getcmd(char *argbuf)
1027 struct citcmd *cptr;
1030 * Starting a new command now, so set sigcaught to 0. This variable
1031 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1032 * been interrupted by a keypress.
1036 /* Switch color support on or off if we're in user mode */
1037 if (rc_ansi_color == 3) {
1038 if (userflags & US_COLOR)
1043 /* if we're running in idiot mode, display a cute little menu */
1044 IFNEXPERT formout("mainmenu");
1046 print_express(); /* print express messages if there are any */
1049 for (a = 0; a < 5; ++a)
1051 /* now the room prompt... */
1052 ok_to_interrupt = 1;
1053 color(BRIGHT_WHITE);
1054 scr_printf("\n%s", room_name);
1056 scr_printf("%c ", room_prompt(room_flags));
1061 ok_to_interrupt = 0;
1063 /* Handle the backspace key, but only if there's something
1064 * to backspace over...
1066 if ((ch == 8) && (cmdpos > 0)) {
1067 back(cmdspaces[cmdpos - 1] + 1);
1071 /* Spacebar invokes "lazy traversal" commands */
1072 if ((ch == 32) && (cmdpos == 0)) {
1073 this_lazy_cmd = next_lazy_cmd;
1074 if (this_lazy_cmd == 13)
1076 if (this_lazy_cmd == 5)
1078 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1079 if (cptr->c_cmdnum == this_lazy_cmd) {
1080 for (a = 0; a < 5; ++a)
1081 if (cptr->c_keys[a][0] != 0)
1082 scr_printf("%s ", cmd_expand(
1083 cptr->c_keys[a], 0));
1085 return (this_lazy_cmd);
1089 return (this_lazy_cmd);
1091 /* Otherwise, process the command */
1092 cmdbuf[cmdpos] = tolower(ch);
1094 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1095 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1097 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1098 cmdspaces[cmdpos] = strlen(
1099 cmd_expand(cptr->c_keys[cmdpos], 0));
1101 if ((cptr->c_keys[cmdpos + 1]) != 0)
1107 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1108 if (cmdmatch(cmdbuf, cptr, 5)) {
1109 /* We've found our command. */
1110 if (requires_string(cptr, cmdpos)) {
1111 getline(argbuf, 32);
1116 /* If this command is one that changes rooms,
1117 * then the next lazy-command (space bar)
1118 * should be "read new" instead of "goto"
1120 if ((cptr->c_cmdnum == 5)
1121 || (cptr->c_cmdnum == 6)
1122 || (cptr->c_cmdnum == 47)
1123 || (cptr->c_cmdnum == 52)
1124 || (cptr->c_cmdnum == 16)
1125 || (cptr->c_cmdnum == 20))
1128 return (cptr->c_cmdnum);
1134 pprintf("\rOne of ... \n");
1135 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1136 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1137 for (a = 0; a < 5; ++a) {
1138 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1144 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1146 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1147 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1148 for (a = 0; a < cmdpos; ++a) {
1150 cmd_expand(cptr->c_keys[a], 0));
1165 * set tty modes. commands are:
1167 * 01- set to bbs mode
1168 * 2 - save current settings for later restoral
1169 * 3 - restore saved settings
1171 #ifdef HAVE_TERMIOS_H
1172 void sttybbs(int cmd)
1173 { /* SysV version of sttybbs() */
1174 struct termios live;
1175 static struct termios saved_settings;
1176 static int last_cmd = 0;
1183 if ((cmd == 0) || (cmd == 1)) {
1184 tcgetattr(0, &live);
1185 live.c_iflag = ISTRIP | IXON | IXANY;
1186 live.c_oflag = OPOST | ONLCR;
1187 live.c_lflag = ISIG | NOFLSH;
1189 live.c_cc[VINTR] = (-1);
1190 live.c_cc[VQUIT] = (-1);
1193 live.c_cc[VMIN] = 0;
1194 live.c_cc[VTIME] = 0;
1197 /* do we even need this stuff anymore? */
1198 /* live.c_line=0; */
1199 live.c_cc[VERASE] = 8;
1200 live.c_cc[VKILL] = 24;
1201 live.c_cc[VEOF] = 1;
1202 live.c_cc[VEOL] = 255;
1203 live.c_cc[VEOL2] = 0;
1204 live.c_cc[VSTART] = 0;
1205 tcsetattr(0, TCSADRAIN, &live);
1208 tcgetattr(0, &saved_settings);
1211 tcsetattr(0, TCSADRAIN, &saved_settings);
1215 void sttybbs(int cmd)
1216 { /* BSD version of sttybbs() */
1218 static struct sgttyb saved_settings;
1219 static int last_cmd = 0;
1226 if ((cmd == 0) || (cmd == 1)) {
1228 live.sg_flags |= CBREAK;
1229 live.sg_flags |= CRMOD;
1230 live.sg_flags |= NL1;
1231 live.sg_flags &= ~ECHO;
1233 live.sg_flags |= NOFLSH;
1237 gtty(0, &saved_settings);
1240 stty(0, &saved_settings);
1247 * display_help() - help file viewer
1249 void display_help(char *name)
1256 * fmout() - Citadel text formatter and paginator
1259 int width, /* screen width to use */
1260 FILE *fpin, /* file to read from, or NULL to format given text */
1261 char *text, /* Text to be formatted (when fpin is NULL) */
1262 FILE *fpout, /* File to write to, or NULL to write to screen */
1263 char pagin, /* nonzero if we should use the paginator */
1264 int height, /* screen height to use */
1265 int starting_lp,/* starting value for lines_printed, -1 for global */
1266 char subst) /* nonzero if we should use hypertext mode */
1275 num_urls = 0; /* Start with a clean slate of embedded URL's */
1277 if (starting_lp >= 0) {
1278 lines_printed = starting_lp;
1283 c = 1; /* c is the current pos */
1284 e = text; /* e is pointer to current pos */
1286 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1287 if (fpin != NULL) { /* read from file */
1290 if (eof_flag == 0) {
1292 buffer[strlen(buffer) + 1] = 0;
1293 buffer[strlen(buffer)] = a;
1295 } else { /* read from text */
1298 while (isspace(buffer[strlen(buffer) - 1]))
1299 buffer[strlen(buffer) - 1] = 0;
1300 buffer[strlen(buffer) + 1] = 0;
1301 buffer[strlen(buffer)] = 10;
1303 if (eof_flag == 0) {
1305 buffer[strlen(buffer) + 1] = 0;
1306 buffer[strlen(buffer)] = a;
1311 if ( (!strncasecmp(buffer, "http://", 7))
1312 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1313 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1314 for (a=0; a<strlen(urls[num_urls]); ++a) {
1315 b = urls[num_urls][a];
1316 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1317 || (b==13) || (b==9) || (b=='\"') )
1318 urls[num_urls][a] = 0;
1323 buffer[strlen(buffer) + 1] = 0;
1325 strcpy(buffer, &buffer[1]);
1332 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1334 if (((old == 13) || (old == 10)) && (isspace(real))) {
1336 fprintf(fpout, "\n");
1340 lines_printed = checkpagin(lines_printed, pagin, height);
1348 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1350 fprintf(fpout, "\n%s", aaa);
1352 scr_printf("\n%s", aaa);
1354 lines_printed = checkpagin(lines_printed, pagin, height);
1364 if ((strlen(aaa) + c) > (width - 1)) {
1367 fprintf(fpout, "\n");
1371 lines_printed = checkpagin(lines_printed, pagin, height);
1375 fprintf(fpout, "%s ", aaa);
1377 scr_printf("%s ", aaa);
1380 c = c + strlen(aaa);
1384 if ((a == 13) || (a == 10)) {
1386 fprintf(fpout, "%s\n", aaa);
1388 scr_printf("%s\n", aaa);
1390 lines_printed = checkpagin(lines_printed, pagin, height);
1393 if (sigcaught) goto OOPS;
1399 /* keypress caught; drain the server */
1402 } while (strcmp(aaa, "000")); */
1406 fprintf(fpout, "\n");
1410 lines_printed = checkpagin(lines_printed, pagin, height);
1417 * support ANSI color if defined
1419 void color(int colornum)
1421 static int is_bold = 0;
1422 static int hold_color, current_color;
1424 if (colornum == COLOR_PUSH) {
1425 hold_color = current_color;
1429 if (colornum == COLOR_POP) {
1434 current_color = colornum;
1436 #ifdef HAVE_CURSES_H
1437 if (scr_color(colornum))
1440 /* When switching to dim white, actually output an 'original
1441 * pair' sequence -- this looks better on black-on-white
1444 if (colornum == DIM_WHITE)
1445 printf("\033[39;49m");
1447 printf("\033[3%d;40m", (colornum & 7));
1449 if ((colornum >= 8) && (is_bold == 0)) {
1452 } else if ((colornum < 8) && (is_bold == 1)) {
1460 void cls(int colornum)
1463 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1470 * Detect whether ANSI color is available (answerback)
1472 void send_ansi_detect(void)
1474 if (rc_ansi_color == 2) {
1481 void look_for_ansi(void)
1489 if (rc_ansi_color == 0) {
1491 } else if (rc_ansi_color == 1) {
1493 } else if (rc_ansi_color == 2) {
1495 /* otherwise, do the auto-detect */
1500 if ((now - AnsiDetect) < 2)
1509 select(1, &rfds, NULL, NULL, &tv);
1510 if (FD_ISSET(0, &rfds)) {
1511 abuf[strlen(abuf) + 1] = 0;
1512 read(0, &abuf[strlen(abuf)], 1);
1514 } while (FD_ISSET(0, &rfds));
1516 for (a = 0; a < strlen(abuf); ++a) {
1517 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1518 && (abuf[a + 2] == '?')) {
1527 * Display key options (highlight hotkeys inside angle brackets)
1529 void keyopt(char *buf) {
1533 for (i=0; i<strlen(buf); ++i) {
1536 color(BRIGHT_MAGENTA);
1550 * Present a key-menu line choice type of thing
1552 char keymenu(char *menuprompt, char *menustring) {
1558 int display_prompt = 1;
1560 choices = num_tokens(menustring, '|');
1562 if (menuprompt != NULL) do_prompt = 1;
1563 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1566 if (display_prompt) {
1568 scr_printf("%s ", menuprompt);
1571 for (i=0; i<choices; ++i) {
1572 extract(buf, menustring, i);
1582 if ( (do_prompt) && (ch=='?') ) {
1583 scr_printf("\rOne of... ");
1585 for (i=0; i<choices; ++i) {
1586 extract(buf, menustring, i);
1595 for (i=0; i<choices; ++i) {
1596 extract(buf, menustring, i);
1597 for (c=1; c<strlen(buf); ++c) {
1598 if ( (ch == tolower(buf[c]))
1600 && (buf[c+1]=='>') ) {
1601 for (a=0; a<strlen(buf); ++a) {
1602 if ( (a!=(c-1)) && (a!=(c+1))) {