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];
83 char rc_gotmail_cmd[SIZ];
86 int next_lazy_cmd = 5;
88 int lines_printed = 0; /* line count for paginator */
89 extern int screenwidth, screenheight;
92 struct citcmd *cmdlist = NULL;
95 /* these variables are local to this module */
96 char keepalives_enabled = KA_YES; /* send NOOPs to server when idle */
97 int ok_to_interrupt = 0; /* print express msgs asynchronously */
98 time_t AnsiDetect; /* when did we send the detect code? */
99 int enable_color = 0; /* nonzero for ANSI color */
105 * If an interesting key has been pressed, return its value, otherwise 0
107 char was_a_key_pressed(void) {
117 retval = select(1, &rfds, NULL, NULL, &tv);
119 /* Careful! Disable keepalives during keyboard polling; we're probably
120 * in the middle of a data transfer from the server, in which case
121 * sending a NOOP would throw the client protocol out of sync.
123 if (FD_ISSET(0, &rfds)) {
124 set_keepalives(KA_NO);
125 the_character = inkey();
126 set_keepalives(KA_YES);
131 return(the_character);
139 * Check to see if we need to pause at the end of a screen.
140 * If we do, we have to switch to half keepalives during the pause because
141 * we are probably in the middle of a server operation and the NOOP command
142 * would confuse everything.
144 int checkpagin(int lp, int pagin, int height)
148 if (sigcaught) return(lp);
149 thekey = was_a_key_pressed();
150 if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
152 if (thekey == 'n' || thekey == 'N')
154 if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
155 if (sigcaught) return(lp);
157 if (!pagin) return(0);
158 if (lp>=(height-1)) {
159 set_keepalives(KA_HALF);
161 set_keepalives(KA_YES);
171 * pprintf() ... paginated version of printf()
173 void pprintf(const char *format, ...) {
175 static char buf[4096]; /* static for performance, change if needed */
178 /* If sigcaught is nonzero, a keypress has interrupted this and we
179 * should just drain output.
181 if (sigcaught) return;
183 /* Otherwise, start spewing... */
184 va_start(arg_ptr, format);
185 vsnprintf(buf, sizeof(buf), format, arg_ptr);
188 for (i=0; i<strlen(buf); ++i) {
192 lines_printed = checkpagin(lines_printed,
193 (userflags & US_PAGINATOR),
202 * print_express() - print express messages if there are any
204 void print_express(void)
213 char *listing = NULL;
214 int r; /* IPC result code */
216 if (express_msgs == 0)
222 if (strlen(rc_exp_cmd) == 0) {
227 while (express_msgs != 0) {
228 r = CtdlIPCGetInstantMessage(&listing, buf);
232 express_msgs = extract_int(buf, 0);
233 timestamp = extract_long(buf, 1);
234 flags = extract_int(buf, 2);
235 extract(sender, buf, 3);
236 extract(node, buf, 4);
237 strcpy(last_paged, sender);
239 stamp = localtime(×tamp);
241 /* If the page is a Logoff Request, honor it. */
247 if (strlen(rc_exp_cmd) > 0) {
248 outpipe = popen(rc_exp_cmd, "w");
249 if (outpipe != NULL) {
250 /* Header derived from flags */
253 "Please log off now, as requested ");
255 fprintf(outpipe, "Broadcast message ");
257 fprintf(outpipe, "Chat request ");
259 fprintf(outpipe, "Message ");
260 /* Timestamp. Can this be improved? */
261 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
262 fprintf(outpipe, "at 12:%02d%cm",
264 stamp->tm_hour ? 'p' : 'a');
265 else if (stamp->tm_hour > 12) /* pm */
266 fprintf(outpipe, "at %d:%02dpm",
270 fprintf(outpipe, "at %d:%02dam",
271 stamp->tm_hour, stamp->tm_min);
272 fprintf(outpipe, " from %s", sender);
273 if (strncmp(serv_info.serv_nodename, node, 32))
274 fprintf(outpipe, " @%s", node);
275 fprintf(outpipe, ":\n%s\n", listing);
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, listing, NULL, 1, screenheight, -1, 0);
318 /* when running in curses mode, the scroll bar in most
319 xterm-style programs becomes useless, so it makes sense to
320 pause after a screenful of pages if the user has been idle
321 for a while. However, this is annoying to some of the users
322 who aren't in curses mode and tend to leave their clients
323 idle. keepalives become disabled, resulting in getting booted
324 when coming back to the idle session. but they probably have
325 a working scrollback in their terminal, so disable it in this
328 if (!is_curses_enabled())
331 scr_printf("\n---\n");
338 void set_keepalives(int s)
340 keepalives_enabled = (char) s;
344 * This loop handles the "keepalive" messages sent to the server when idling.
347 static time_t idlet = 0;
348 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, "");
739 strcpy(rc_gotmail_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, "gotmailcmd=", 11))
863 strcpy(rc_gotmail_cmd, &buf[11]);
865 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
866 if (!strncasecmp(&buf[11], "yes", 3))
867 rc_alt_semantics = 1;
868 if (!strncasecmp(&buf[11], "no", 2))
869 rc_alt_semantics = 0;
871 if (!strncasecmp(buf, "cmd=", 4)) {
872 strcpy(buf, &buf[4]);
874 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
876 cptr->c_cmdnum = atoi(buf);
877 for (d = strlen(buf); d >= 0; --d)
880 strcpy(buf, &buf[b + 1]);
882 cptr->c_axlevel = atoi(buf);
883 for (d = strlen(buf); d >= 0; --d)
886 strcpy(buf, &buf[b + 1]);
888 for (a = 0; a < 5; ++a)
889 cptr->c_keys[a][0] = 0;
893 buf[strlen(buf) + 1] = 0;
894 while (strlen(buf) > 0) {
896 for (d = strlen(buf); d >= 0; --d)
899 strncpy(cptr->c_keys[a], buf, b);
900 cptr->c_keys[a][b] = 0;
902 strcpy(buf, &buf[b + 1]);
912 lastcmd->next = cptr;
922 * return the key associated with a command
924 char keycmd(char *cmdstr)
928 for (a = 0; a < strlen(cmdstr); ++a)
929 if (cmdstr[a] == '&')
930 return (tolower(cmdstr[a + 1]));
936 * Output the string from a key command without the ampersand
937 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
939 char *cmd_expand(char *strbuf, int mode)
947 for (a = 0; a < strlen(exp); ++a) {
948 if (strbuf[a] == '&') {
951 strcpy(&exp[a], &exp[a + 1]);
955 strcpy(buf, &exp[a + 2]);
961 if (!strncmp(&exp[a], "^r", 2)) {
963 strcpy(&exp[a], room_name);
964 strcat(exp, &buf[a + 2]);
966 if (!strncmp(&exp[a], "^c", 2)) {
968 strcpy(&exp[a + 1], &exp[a + 2]);
978 * Comparison function to determine if entered commands match a
979 * command loaded from the config file.
981 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
992 for (a = 0; a < ncomp; ++a) {
993 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
994 || (cptr->c_axlevel > cmdax))
1002 * This function returns 1 if a given command requires a string input
1004 int requires_string(struct citcmd *cptr, int ncomp)
1009 strcpy(buf, cptr->c_keys[ncomp - 1]);
1010 for (a = 0; a < strlen(buf); ++a) {
1019 * Input a command at the main prompt.
1020 * This function returns an integer command number. If the command prompts
1021 * for a string then it is placed in the supplied buffer.
1023 int getcmd(char *argbuf)
1032 struct citcmd *cptr;
1035 * Starting a new command now, so set sigcaught to 0. This variable
1036 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1037 * been interrupted by a keypress.
1041 /* Switch color support on or off if we're in user mode */
1042 if (rc_ansi_color == 3) {
1043 if (userflags & US_COLOR)
1048 /* if we're running in idiot mode, display a cute little menu */
1049 IFNEXPERT formout("mainmenu");
1051 print_express(); /* print express messages if there are any */
1054 for (a = 0; a < 5; ++a)
1056 /* now the room prompt... */
1057 ok_to_interrupt = 1;
1058 color(BRIGHT_WHITE);
1059 scr_printf("\n%s", room_name);
1061 scr_printf("%c ", room_prompt(room_flags));
1066 ok_to_interrupt = 0;
1068 /* Handle the backspace key, but only if there's something
1069 * to backspace over...
1071 if ((ch == 8) && (cmdpos > 0)) {
1072 back(cmdspaces[cmdpos - 1] + 1);
1076 /* Spacebar invokes "lazy traversal" commands */
1077 if ((ch == 32) && (cmdpos == 0)) {
1078 this_lazy_cmd = next_lazy_cmd;
1079 if (this_lazy_cmd == 13)
1081 if (this_lazy_cmd == 5)
1083 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1084 if (cptr->c_cmdnum == this_lazy_cmd) {
1085 for (a = 0; a < 5; ++a)
1086 if (cptr->c_keys[a][0] != 0)
1087 scr_printf("%s ", cmd_expand(
1088 cptr->c_keys[a], 0));
1090 return (this_lazy_cmd);
1094 return (this_lazy_cmd);
1096 /* Otherwise, process the command */
1097 cmdbuf[cmdpos] = tolower(ch);
1099 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1100 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1102 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1103 cmdspaces[cmdpos] = strlen(
1104 cmd_expand(cptr->c_keys[cmdpos], 0));
1106 if ((cptr->c_keys[cmdpos + 1]) != 0)
1112 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1113 if (cmdmatch(cmdbuf, cptr, 5)) {
1114 /* We've found our command. */
1115 if (requires_string(cptr, cmdpos)) {
1116 getline(argbuf, 32);
1121 /* If this command is one that changes rooms,
1122 * then the next lazy-command (space bar)
1123 * should be "read new" instead of "goto"
1125 if ((cptr->c_cmdnum == 5)
1126 || (cptr->c_cmdnum == 6)
1127 || (cptr->c_cmdnum == 47)
1128 || (cptr->c_cmdnum == 52)
1129 || (cptr->c_cmdnum == 16)
1130 || (cptr->c_cmdnum == 20))
1133 return (cptr->c_cmdnum);
1139 pprintf("\rOne of ... \n");
1140 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1141 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1142 for (a = 0; a < 5; ++a) {
1143 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1149 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1151 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1152 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1153 for (a = 0; a < cmdpos; ++a) {
1155 cmd_expand(cptr->c_keys[a], 0));
1170 * set tty modes. commands are:
1172 * 01- set to bbs mode
1173 * 2 - save current settings for later restoral
1174 * 3 - restore saved settings
1176 #ifdef HAVE_TERMIOS_H
1177 void sttybbs(int cmd)
1178 { /* SysV version of sttybbs() */
1179 struct termios live;
1180 static struct termios saved_settings;
1181 static int last_cmd = 0;
1188 if ((cmd == 0) || (cmd == 1)) {
1189 tcgetattr(0, &live);
1190 live.c_iflag = ISTRIP | IXON | IXANY;
1191 live.c_oflag = OPOST | ONLCR;
1192 live.c_lflag = ISIG | NOFLSH;
1194 live.c_cc[VINTR] = (-1);
1195 live.c_cc[VQUIT] = (-1);
1198 live.c_cc[VMIN] = 0;
1199 live.c_cc[VTIME] = 0;
1202 /* do we even need this stuff anymore? */
1203 /* live.c_line=0; */
1204 live.c_cc[VERASE] = 8;
1205 live.c_cc[VKILL] = 24;
1206 live.c_cc[VEOF] = 1;
1207 live.c_cc[VEOL] = 255;
1208 live.c_cc[VEOL2] = 0;
1209 live.c_cc[VSTART] = 0;
1210 tcsetattr(0, TCSADRAIN, &live);
1213 tcgetattr(0, &saved_settings);
1216 tcsetattr(0, TCSADRAIN, &saved_settings);
1220 void sttybbs(int cmd)
1221 { /* BSD version of sttybbs() */
1223 static struct sgttyb saved_settings;
1224 static int last_cmd = 0;
1231 if ((cmd == 0) || (cmd == 1)) {
1233 live.sg_flags |= CBREAK;
1234 live.sg_flags |= CRMOD;
1235 live.sg_flags |= NL1;
1236 live.sg_flags &= ~ECHO;
1238 live.sg_flags |= NOFLSH;
1242 gtty(0, &saved_settings);
1245 stty(0, &saved_settings);
1252 * display_help() - help file viewer
1254 void display_help(char *name)
1261 * fmout() - Citadel text formatter and paginator
1264 int width, /* screen width to use */
1265 FILE *fpin, /* file to read from, or NULL to format given text */
1266 char *text, /* Text to be formatted (when fpin is NULL) */
1267 FILE *fpout, /* File to write to, or NULL to write to screen */
1268 char pagin, /* nonzero if we should use the paginator */
1269 int height, /* screen height to use */
1270 int starting_lp,/* starting value for lines_printed, -1 for global */
1271 char subst) /* nonzero if we should use hypertext mode */
1280 num_urls = 0; /* Start with a clean slate of embedded URL's */
1282 if (starting_lp >= 0) {
1283 lines_printed = starting_lp;
1288 c = 1; /* c is the current pos */
1289 e = text; /* e is pointer to current pos */
1291 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1292 if (fpin != NULL) { /* read from file */
1295 if (eof_flag == 0) {
1297 buffer[strlen(buffer) + 1] = 0;
1298 buffer[strlen(buffer)] = a;
1300 } else { /* read from text */
1303 while (isspace(buffer[strlen(buffer) - 1]))
1304 buffer[strlen(buffer) - 1] = 0;
1305 buffer[strlen(buffer) + 1] = 0;
1306 buffer[strlen(buffer)] = 10;
1308 if (eof_flag == 0) {
1310 buffer[strlen(buffer) + 1] = 0;
1311 buffer[strlen(buffer)] = a;
1316 if ( (!strncasecmp(buffer, "http://", 7))
1317 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1318 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1319 for (a=0; a<strlen(urls[num_urls]); ++a) {
1320 b = urls[num_urls][a];
1321 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1322 || (b==13) || (b==9) || (b=='\"') )
1323 urls[num_urls][a] = 0;
1328 buffer[strlen(buffer) + 1] = 0;
1330 strcpy(buffer, &buffer[1]);
1337 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1339 if (((old == 13) || (old == 10)) && (isspace(real))) {
1341 fprintf(fpout, "\n");
1345 lines_printed = checkpagin(lines_printed, pagin, height);
1353 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1355 fprintf(fpout, "\n%s", aaa);
1357 scr_printf("\n%s", aaa);
1359 lines_printed = checkpagin(lines_printed, pagin, height);
1369 if ((strlen(aaa) + c) > (width - 1)) {
1372 fprintf(fpout, "\n");
1376 lines_printed = checkpagin(lines_printed, pagin, height);
1380 fprintf(fpout, "%s ", aaa);
1382 scr_printf("%s ", aaa);
1385 c = c + strlen(aaa);
1389 if ((a == 13) || (a == 10)) {
1391 fprintf(fpout, "%s\n", aaa);
1393 scr_printf("%s\n", aaa);
1395 lines_printed = checkpagin(lines_printed, pagin, height);
1398 if (sigcaught) goto OOPS;
1404 /* keypress caught; drain the server */
1407 } while (strcmp(aaa, "000")); */
1411 fprintf(fpout, "\n");
1415 lines_printed = checkpagin(lines_printed, pagin, height);
1422 * support ANSI color if defined
1424 void color(int colornum)
1426 static int is_bold = 0;
1427 static int hold_color, current_color;
1429 if (colornum == COLOR_PUSH) {
1430 hold_color = current_color;
1434 if (colornum == COLOR_POP) {
1439 current_color = colornum;
1441 #ifdef HAVE_CURSES_H
1442 if (scr_color(colornum))
1445 /* When switching to dim white, actually output an 'original
1446 * pair' sequence -- this looks better on black-on-white
1449 if (colornum == DIM_WHITE)
1450 printf("\033[39;49m");
1452 printf("\033[3%d;40m", (colornum & 7));
1454 if ((colornum >= 8) && (is_bold == 0)) {
1457 } else if ((colornum < 8) && (is_bold == 1)) {
1465 void cls(int colornum)
1468 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1475 * Detect whether ANSI color is available (answerback)
1477 void send_ansi_detect(void)
1479 if (rc_ansi_color == 2) {
1486 void look_for_ansi(void)
1494 if (rc_ansi_color == 0) {
1496 } else if (rc_ansi_color == 1) {
1498 } else if (rc_ansi_color == 2) {
1500 /* otherwise, do the auto-detect */
1505 if ((now - AnsiDetect) < 2)
1514 select(1, &rfds, NULL, NULL, &tv);
1515 if (FD_ISSET(0, &rfds)) {
1516 abuf[strlen(abuf) + 1] = 0;
1517 read(0, &abuf[strlen(abuf)], 1);
1519 } while (FD_ISSET(0, &rfds));
1521 for (a = 0; a < strlen(abuf); ++a) {
1522 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1523 && (abuf[a + 2] == '?')) {
1532 * Display key options (highlight hotkeys inside angle brackets)
1534 void keyopt(char *buf) {
1538 for (i=0; i<strlen(buf); ++i) {
1541 color(BRIGHT_MAGENTA);
1555 * Present a key-menu line choice type of thing
1557 char keymenu(char *menuprompt, char *menustring) {
1563 int display_prompt = 1;
1565 choices = num_tokens(menustring, '|');
1567 if (menuprompt != NULL) do_prompt = 1;
1568 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1571 if (display_prompt) {
1573 scr_printf("%s ", menuprompt);
1576 for (i=0; i<choices; ++i) {
1577 extract(buf, menustring, i);
1587 if ( (do_prompt) && (ch=='?') ) {
1588 scr_printf("\rOne of... ");
1590 for (i=0; i<choices; ++i) {
1591 extract(buf, menustring, i);
1600 for (i=0; i<choices; ++i) {
1601 extract(buf, menustring, i);
1602 for (c=1; c<strlen(buf); ++c) {
1603 if ( (ch == tolower(buf[c]))
1605 && (buf[c+1]=='>') ) {
1606 for (a=0; a<strlen(buf); ++a) {
1607 if ( (a!=(c-1)) && (a!=(c+1))) {