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) {
349 int r; /* IPC response code */
353 /* If full keepalives are enabled, send a NOOP to the server and
354 * wait for a response.
356 if (keepalives_enabled == KA_YES) {
358 if (express_msgs > 0) {
359 if (ok_to_interrupt == 1) {
360 scr_printf("\r%64s\r", "");
362 scr_printf("%s%c ", room_name,
363 room_prompt(room_flags));
369 /* If half keepalives are enabled, send a QNOP to the server (if the
370 * server supports it) and then do nothing.
372 if ( (keepalives_enabled == KA_HALF)
373 && (serv_info.serv_supports_qnop > 0) ) {
378 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
379 encapsulated interface; in theory there should be no need to touch these
380 globals outside of the async_ka_* functions. */
382 #ifdef THREADED_CLIENT
383 static pthread_t ka_thr_handle;
384 static int ka_thr_active = 0;
385 static int async_ka_enabled = 0;
387 static void *ka_thread(void *arg)
389 really_do_keepalive();
390 pthread_detach(ka_thr_handle);
395 /* start up a thread to handle a keepalive in the background */
396 static void async_ka_exec(void)
398 if (!ka_thr_active) {
400 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
401 perror("pthread_create");
406 #endif /* THREADED_CLIENT */
408 /* I changed this from static to not because I need to call it from
409 screen.c, either that or make something in screen.c not static.
410 Fix it how you like. Why all the staticness? stu */
412 void do_keepalive(void)
417 if ((now - idlet) < ((long) S_KEEPALIVE))
420 /* Do a space-backspace to keep telnet sessions from idling out */
421 scr_printf(" %c", 8);
424 #ifdef THREADED_CLIENT
425 if (async_ka_enabled)
429 really_do_keepalive();
433 /* Now the actual async-keepalve API that we expose to higher levels:
434 async_ka_start() and async_ka_end(). These do nothing when we don't have
435 threading enabled, so we avoid sprinkling ifdef's throughout the code. */
437 /* wait for a background keepalive to complete. this must be done before
438 attempting any further server requests! */
439 void async_ka_end(void)
441 #ifdef THREADED_CLIENT
443 pthread_join(ka_thr_handle, NULL);
449 /* tell do_keepalive() that keepalives are asynchronous. */
450 void async_ka_start(void)
452 #ifdef THREADED_CLIENT
459 { /* get a character from the keyboard, with */
460 int a; /* the watchdog timer in effect if necessary */
470 /* This loop waits for keyboard input. If the keepalive
471 * timer expires, it sends a keepalive to the server if
472 * necessary and then waits again.
475 scr_set_windowsize();
477 scr_set_windowsize();
481 tv.tv_sec = S_KEEPALIVE;
484 select(1, &rfds, NULL, NULL, &tv);
485 } while (!FD_ISSET(0, &rfds));
487 /* At this point, there's input, so fetch it.
488 * (There's a hole in the bucket...)
490 a = scr_getc(SCR_NOBLOCK);
497 if (((a != 23) && (a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
498 && ((a < 32) || (a > 126)))
501 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
512 { /* Returns 1 for yes, 0 for no */
528 /* Returns 1 for yes, 0 for no, arg is default value */
551 /* Gets a line from the terminal */
552 /* string == Pointer to string buffer */
553 /* lim == Maximum length - if negative, no-show */
554 void getline(char *string, int lim)
568 if ((a == 8 || a == 23) && (strlen(string) == 0))
570 if ((a != 10) && (a != 8) && (strlen(string) == lim))
572 if ((a == 8) && (string[0] != 0)) {
573 string[strlen(string) - 1] = 0;
574 scr_putc(8); scr_putc(32); scr_putc(8);
577 if ((a == 23) && (string[0] != 0)) {
579 string[strlen(string) - 1] = 0;
580 scr_putc(8); scr_putc(32); scr_putc(8);
581 } while (strlen(string) && string[strlen(string) - 1] != ' ');
603 * strprompt() - prompt for a string, print the existing value and
604 * allow the user to press return to keep it...
606 void strprompt(char *prompt, char *str, int len)
613 scr_printf("%s ", prompt);
616 color(BRIGHT_MAGENTA);
619 scr_printf("%s", str);
622 for (i=0; i<strlen(str); ++i) {
639 * boolprompt() - prompt for a yes/no, print the existing value and
640 * allow the user to press return to keep it...
642 int boolprompt(char *prompt, int prev_val)
647 scr_printf("%s ", prompt);
650 color(BRIGHT_MAGENTA);
651 scr_printf("%s", (prev_val ? "Yes" : "No"));
655 r = (yesno_d(prev_val));
661 * intprompt() - like strprompt(), except for an integer
662 * (note that it RETURNS the new value!)
664 int intprompt(char *prompt, int ival, int imin, int imax)
672 snprintf(buf, sizeof buf, "%d", i);
673 strprompt(prompt, buf, 15);
675 for (p=0; p<strlen(buf); ++p) {
676 if ( (!isdigit(buf[p]))
677 && ( (buf[p]!='-') || (p!=0) ) )
681 scr_printf("*** Must be no less than %d.\n", imin);
683 scr_printf("*** Must be no more than %d.\n", imax);
684 } while ((i < imin) || (i > imax));
689 * newprompt() - prompt for a string with no existing value
690 * (clears out string buffer first)
692 void newprompt(char *prompt, char *str, int len)
694 color(BRIGHT_MAGENTA);
695 scr_printf("%s", prompt);
703 { /* returns a lower case value */
712 * parse the citadel.rc file
714 void load_command_set(void)
719 struct citcmd *lastcmd = NULL;
724 /* first, set up some defaults for non-required variables */
726 strcpy(editor_path, "");
727 strcpy(printcmd, "");
728 strcpy(rc_username, "");
729 strcpy(rc_password, "");
732 rc_allow_attachments = 0;
733 rc_remember_passwords = 0;
734 strcpy(rc_exp_cmd, "");
735 rc_display_message_numbers = 0;
736 rc_force_mail_prompts = 0;
738 strcpy(rc_url_cmd, "");
740 rc_encrypt = RC_DEFAULT;
743 rc_screen = RC_DEFAULT;
745 rc_alt_semantics = 0;
747 /* now try to open the citadel.rc file */
750 if (getenv("HOME") != NULL) {
751 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
752 ccfile = fopen(buf, "r");
754 if (ccfile == NULL) {
755 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
756 ccfile = fopen(buf, "r");
758 if (ccfile == NULL) {
759 ccfile = fopen("/etc/citadel.rc", "r");
761 if (ccfile == NULL) {
762 ccfile = fopen("./citadel.rc", "r");
764 if (ccfile == NULL) {
765 perror("commands: cannot open citadel.rc");
768 while (fgets(buf, sizeof buf, ccfile) != NULL) {
769 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
770 buf[strlen(buf) - 1] = 0;
772 if (!strncasecmp(buf, "encrypt=", 8)) {
773 if (!strcasecmp(&buf[8], "yes")) {
777 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
782 else if (!strcasecmp(&buf[8], "no")) {
785 else if (!strcasecmp(&buf[8], "default")) {
786 rc_encrypt = RC_DEFAULT;
792 if (!strncasecmp(buf, "fullscreen=", 11)) {
793 if (!strcasecmp(&buf[11], "yes"))
795 else if (!strcasecmp(&buf[11], "no"))
800 if (!strncasecmp(buf, "editor=", 7))
801 strcpy(editor_path, &buf[7]);
803 if (!strncasecmp(buf, "printcmd=", 9))
804 strcpy(printcmd, &buf[9]);
806 if (!strncasecmp(buf, "expcmd=", 7))
807 strcpy(rc_exp_cmd, &buf[7]);
809 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
810 have_xterm = (char) atoi(&buf[24]);
812 if (!strncasecmp(buf, "use_floors=", 11)) {
813 if (!strcasecmp(&buf[11], "yes"))
814 rc_floor_mode = RC_YES;
815 if (!strcasecmp(&buf[11], "no"))
816 rc_floor_mode = RC_NO;
817 if (!strcasecmp(&buf[11], "default"))
818 rc_floor_mode = RC_DEFAULT;
820 if (!strncasecmp(buf, "beep=", 5)) {
821 rc_exp_beep = atoi(&buf[5]);
823 if (!strncasecmp(buf, "allow_attachments=", 18)) {
824 rc_allow_attachments = atoi(&buf[18]);
826 if (!strncasecmp(buf, "idle_threshold=", 15)) {
827 rc_idle_threshold = atoi(&buf[15]);
829 if (!strncasecmp(buf, "remember_passwords=", 19)) {
830 rc_remember_passwords = atoi(&buf[19]);
832 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
833 rc_display_message_numbers = atoi(&buf[24]);
835 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
836 rc_force_mail_prompts = atoi(&buf[19]);
838 if (!strncasecmp(buf, "ansi_color=", 11)) {
839 if (!strncasecmp(&buf[11], "on", 2))
841 if (!strncasecmp(&buf[11], "auto", 4))
842 rc_ansi_color = 2; /* autodetect */
843 if (!strncasecmp(&buf[11], "user", 4))
844 rc_ansi_color = 3; /* user config */
846 if (!strncasecmp(buf, "prompt_control=", 15)) {
847 if (!strncasecmp(&buf[15], "on", 2))
848 rc_prompt_control = 1;
849 if (!strncasecmp(&buf[15], "user", 4))
850 rc_prompt_control = 3; /* user config */
852 if (!strncasecmp(buf, "username=", 9))
853 strcpy(rc_username, &buf[9]);
855 if (!strncasecmp(buf, "password=", 9))
856 strcpy(rc_password, &buf[9]);
858 if (!strncasecmp(buf, "urlcmd=", 7))
859 strcpy(rc_url_cmd, &buf[7]);
861 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
862 if (!strncasecmp(&buf[11], "yes", 3))
863 rc_alt_semantics = 1;
864 if (!strncasecmp(&buf[11], "no", 2))
865 rc_alt_semantics = 0;
867 if (!strncasecmp(buf, "cmd=", 4)) {
868 strcpy(buf, &buf[4]);
870 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
872 cptr->c_cmdnum = atoi(buf);
873 for (d = strlen(buf); d >= 0; --d)
876 strcpy(buf, &buf[b + 1]);
878 cptr->c_axlevel = atoi(buf);
879 for (d = strlen(buf); d >= 0; --d)
882 strcpy(buf, &buf[b + 1]);
884 for (a = 0; a < 5; ++a)
885 cptr->c_keys[a][0] = 0;
889 buf[strlen(buf) + 1] = 0;
890 while (strlen(buf) > 0) {
892 for (d = strlen(buf); d >= 0; --d)
895 strncpy(cptr->c_keys[a], buf, b);
896 cptr->c_keys[a][b] = 0;
898 strcpy(buf, &buf[b + 1]);
908 lastcmd->next = cptr;
918 * return the key associated with a command
920 char keycmd(char *cmdstr)
924 for (a = 0; a < strlen(cmdstr); ++a)
925 if (cmdstr[a] == '&')
926 return (tolower(cmdstr[a + 1]));
932 * Output the string from a key command without the ampersand
933 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
935 char *cmd_expand(char *strbuf, int mode)
943 for (a = 0; a < strlen(exp); ++a) {
944 if (strbuf[a] == '&') {
947 strcpy(&exp[a], &exp[a + 1]);
951 strcpy(buf, &exp[a + 2]);
957 if (!strncmp(&exp[a], "^r", 2)) {
959 strcpy(&exp[a], room_name);
960 strcat(exp, &buf[a + 2]);
962 if (!strncmp(&exp[a], "^c", 2)) {
964 strcpy(&exp[a + 1], &exp[a + 2]);
974 * Comparison function to determine if entered commands match a
975 * command loaded from the config file.
977 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
988 for (a = 0; a < ncomp; ++a) {
989 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
990 || (cptr->c_axlevel > cmdax))
998 * This function returns 1 if a given command requires a string input
1000 int requires_string(struct citcmd *cptr, int ncomp)
1005 strcpy(buf, cptr->c_keys[ncomp - 1]);
1006 for (a = 0; a < strlen(buf); ++a) {
1015 * Input a command at the main prompt.
1016 * This function returns an integer command number. If the command prompts
1017 * for a string then it is placed in the supplied buffer.
1019 int getcmd(char *argbuf)
1028 struct citcmd *cptr;
1031 * Starting a new command now, so set sigcaught to 0. This variable
1032 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1033 * been interrupted by a keypress.
1037 /* Switch color support on or off if we're in user mode */
1038 if (rc_ansi_color == 3) {
1039 if (userflags & US_COLOR)
1044 /* if we're running in idiot mode, display a cute little menu */
1045 IFNEXPERT formout("mainmenu");
1047 print_express(); /* print express messages if there are any */
1050 for (a = 0; a < 5; ++a)
1052 /* now the room prompt... */
1053 ok_to_interrupt = 1;
1054 color(BRIGHT_WHITE);
1055 scr_printf("\n%s", room_name);
1057 scr_printf("%c ", room_prompt(room_flags));
1062 ok_to_interrupt = 0;
1064 /* Handle the backspace key, but only if there's something
1065 * to backspace over...
1067 if ((ch == 8) && (cmdpos > 0)) {
1068 back(cmdspaces[cmdpos - 1] + 1);
1072 /* Spacebar invokes "lazy traversal" commands */
1073 if ((ch == 32) && (cmdpos == 0)) {
1074 this_lazy_cmd = next_lazy_cmd;
1075 if (this_lazy_cmd == 13)
1077 if (this_lazy_cmd == 5)
1079 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1080 if (cptr->c_cmdnum == this_lazy_cmd) {
1081 for (a = 0; a < 5; ++a)
1082 if (cptr->c_keys[a][0] != 0)
1083 scr_printf("%s ", cmd_expand(
1084 cptr->c_keys[a], 0));
1086 return (this_lazy_cmd);
1090 return (this_lazy_cmd);
1092 /* Otherwise, process the command */
1093 cmdbuf[cmdpos] = tolower(ch);
1095 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1096 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1098 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1099 cmdspaces[cmdpos] = strlen(
1100 cmd_expand(cptr->c_keys[cmdpos], 0));
1102 if ((cptr->c_keys[cmdpos + 1]) != 0)
1108 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1109 if (cmdmatch(cmdbuf, cptr, 5)) {
1110 /* We've found our command. */
1111 if (requires_string(cptr, cmdpos)) {
1112 getline(argbuf, 32);
1117 /* If this command is one that changes rooms,
1118 * then the next lazy-command (space bar)
1119 * should be "read new" instead of "goto"
1121 if ((cptr->c_cmdnum == 5)
1122 || (cptr->c_cmdnum == 6)
1123 || (cptr->c_cmdnum == 47)
1124 || (cptr->c_cmdnum == 52)
1125 || (cptr->c_cmdnum == 16)
1126 || (cptr->c_cmdnum == 20))
1129 return (cptr->c_cmdnum);
1135 pprintf("\rOne of ... \n");
1136 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1137 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1138 for (a = 0; a < 5; ++a) {
1139 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1145 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1147 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1148 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1149 for (a = 0; a < cmdpos; ++a) {
1151 cmd_expand(cptr->c_keys[a], 0));
1166 * set tty modes. commands are:
1168 * 01- set to bbs mode
1169 * 2 - save current settings for later restoral
1170 * 3 - restore saved settings
1172 #ifdef HAVE_TERMIOS_H
1173 void sttybbs(int cmd)
1174 { /* SysV version of sttybbs() */
1175 struct termios live;
1176 static struct termios saved_settings;
1177 static int last_cmd = 0;
1184 if ((cmd == 0) || (cmd == 1)) {
1185 tcgetattr(0, &live);
1186 live.c_iflag = ISTRIP | IXON | IXANY;
1187 live.c_oflag = OPOST | ONLCR;
1188 live.c_lflag = ISIG | NOFLSH;
1190 live.c_cc[VINTR] = (-1);
1191 live.c_cc[VQUIT] = (-1);
1194 live.c_cc[VMIN] = 0;
1195 live.c_cc[VTIME] = 0;
1198 /* do we even need this stuff anymore? */
1199 /* live.c_line=0; */
1200 live.c_cc[VERASE] = 8;
1201 live.c_cc[VKILL] = 24;
1202 live.c_cc[VEOF] = 1;
1203 live.c_cc[VEOL] = 255;
1204 live.c_cc[VEOL2] = 0;
1205 live.c_cc[VSTART] = 0;
1206 tcsetattr(0, TCSADRAIN, &live);
1209 tcgetattr(0, &saved_settings);
1212 tcsetattr(0, TCSADRAIN, &saved_settings);
1216 void sttybbs(int cmd)
1217 { /* BSD version of sttybbs() */
1219 static struct sgttyb saved_settings;
1220 static int last_cmd = 0;
1227 if ((cmd == 0) || (cmd == 1)) {
1229 live.sg_flags |= CBREAK;
1230 live.sg_flags |= CRMOD;
1231 live.sg_flags |= NL1;
1232 live.sg_flags &= ~ECHO;
1234 live.sg_flags |= NOFLSH;
1238 gtty(0, &saved_settings);
1241 stty(0, &saved_settings);
1248 * display_help() - help file viewer
1250 void display_help(char *name)
1257 * fmout() - Citadel text formatter and paginator
1260 int width, /* screen width to use */
1261 FILE *fpin, /* file to read from, or NULL to format given text */
1262 char *text, /* Text to be formatted (when fpin is NULL) */
1263 FILE *fpout, /* File to write to, or NULL to write to screen */
1264 char pagin, /* nonzero if we should use the paginator */
1265 int height, /* screen height to use */
1266 int starting_lp,/* starting value for lines_printed, -1 for global */
1267 char subst) /* nonzero if we should use hypertext mode */
1276 num_urls = 0; /* Start with a clean slate of embedded URL's */
1278 if (starting_lp >= 0) {
1279 lines_printed = starting_lp;
1284 c = 1; /* c is the current pos */
1285 e = text; /* e is pointer to current pos */
1287 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1288 if (fpin != NULL) { /* read from file */
1291 if (eof_flag == 0) {
1293 buffer[strlen(buffer) + 1] = 0;
1294 buffer[strlen(buffer)] = a;
1296 } else { /* read from text */
1299 while (isspace(buffer[strlen(buffer) - 1]))
1300 buffer[strlen(buffer) - 1] = 0;
1301 buffer[strlen(buffer) + 1] = 0;
1302 buffer[strlen(buffer)] = 10;
1304 if (eof_flag == 0) {
1306 buffer[strlen(buffer) + 1] = 0;
1307 buffer[strlen(buffer)] = a;
1312 if ( (!strncasecmp(buffer, "http://", 7))
1313 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1314 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1315 for (a=0; a<strlen(urls[num_urls]); ++a) {
1316 b = urls[num_urls][a];
1317 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1318 || (b==13) || (b==9) || (b=='\"') )
1319 urls[num_urls][a] = 0;
1324 buffer[strlen(buffer) + 1] = 0;
1326 strcpy(buffer, &buffer[1]);
1333 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1335 if (((old == 13) || (old == 10)) && (isspace(real))) {
1337 fprintf(fpout, "\n");
1341 lines_printed = checkpagin(lines_printed, pagin, height);
1349 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1351 fprintf(fpout, "\n%s", aaa);
1353 scr_printf("\n%s", aaa);
1355 lines_printed = checkpagin(lines_printed, pagin, height);
1365 if ((strlen(aaa) + c) > (width - 1)) {
1368 fprintf(fpout, "\n");
1372 lines_printed = checkpagin(lines_printed, pagin, height);
1376 fprintf(fpout, "%s ", aaa);
1378 scr_printf("%s ", aaa);
1381 c = c + strlen(aaa);
1385 if ((a == 13) || (a == 10)) {
1387 fprintf(fpout, "%s\n", aaa);
1389 scr_printf("%s\n", aaa);
1391 lines_printed = checkpagin(lines_printed, pagin, height);
1394 if (sigcaught) goto OOPS;
1400 /* keypress caught; drain the server */
1403 } while (strcmp(aaa, "000")); */
1407 fprintf(fpout, "\n");
1411 lines_printed = checkpagin(lines_printed, pagin, height);
1418 * support ANSI color if defined
1420 void color(int colornum)
1422 static int is_bold = 0;
1423 static int hold_color, current_color;
1425 if (colornum == COLOR_PUSH) {
1426 hold_color = current_color;
1430 if (colornum == COLOR_POP) {
1435 current_color = colornum;
1437 #ifdef HAVE_CURSES_H
1438 if (scr_color(colornum))
1441 /* When switching to dim white, actually output an 'original
1442 * pair' sequence -- this looks better on black-on-white
1445 if (colornum == DIM_WHITE)
1446 printf("\033[39;49m");
1448 printf("\033[3%d;40m", (colornum & 7));
1450 if ((colornum >= 8) && (is_bold == 0)) {
1453 } else if ((colornum < 8) && (is_bold == 1)) {
1461 void cls(int colornum)
1464 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1471 * Detect whether ANSI color is available (answerback)
1473 void send_ansi_detect(void)
1475 if (rc_ansi_color == 2) {
1482 void look_for_ansi(void)
1490 if (rc_ansi_color == 0) {
1492 } else if (rc_ansi_color == 1) {
1494 } else if (rc_ansi_color == 2) {
1496 /* otherwise, do the auto-detect */
1501 if ((now - AnsiDetect) < 2)
1510 select(1, &rfds, NULL, NULL, &tv);
1511 if (FD_ISSET(0, &rfds)) {
1512 abuf[strlen(abuf) + 1] = 0;
1513 read(0, &abuf[strlen(abuf)], 1);
1515 } while (FD_ISSET(0, &rfds));
1517 for (a = 0; a < strlen(abuf); ++a) {
1518 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1519 && (abuf[a + 2] == '?')) {
1528 * Display key options (highlight hotkeys inside angle brackets)
1530 void keyopt(char *buf) {
1534 for (i=0; i<strlen(buf); ++i) {
1537 color(BRIGHT_MAGENTA);
1551 * Present a key-menu line choice type of thing
1553 char keymenu(char *menuprompt, char *menustring) {
1559 int display_prompt = 1;
1561 choices = num_tokens(menustring, '|');
1563 if (menuprompt != NULL) do_prompt = 1;
1564 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1567 if (display_prompt) {
1569 scr_printf("%s ", menuprompt);
1572 for (i=0; i<choices; ++i) {
1573 extract(buf, menustring, i);
1583 if ( (do_prompt) && (ch=='?') ) {
1584 scr_printf("\rOne of... ");
1586 for (i=0; i<choices; ++i) {
1587 extract(buf, menustring, i);
1596 for (i=0; i<choices; ++i) {
1597 extract(buf, menustring, i);
1598 for (c=1; c<strlen(buf); ++c) {
1599 if ( (ch == tolower(buf[c]))
1601 && (buf[c+1]=='>') ) {
1602 for (a=0; a<strlen(buf); ++a) {
1603 if ( (a!=(c-1)) && (a!=(c+1))) {