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
47 #include "citadel_ipc.h"
50 #include "citadel_decls.h"
52 #include "routines2.h"
55 #include "client_chat.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;
91 extern CtdlIPC *ipc_for_signal_handlers; /* KLUDGE cover your eyes */
93 struct citcmd *cmdlist = NULL;
96 /* these variables are local to this module */
97 char keepalives_enabled = KA_YES; /* send NOOPs to server when idle */
98 int ok_to_interrupt = 0; /* print express msgs asynchronously */
99 time_t AnsiDetect; /* when did we send the detect code? */
100 int enable_color = 0; /* nonzero for ANSI color */
106 * If an interesting key has been pressed, return its value, otherwise 0
108 char was_a_key_pressed(void) {
118 retval = select(1, &rfds, NULL, NULL, &tv);
120 /* Careful! Disable keepalives during keyboard polling; we're probably
121 * in the middle of a data transfer from the server, in which case
122 * sending a NOOP would throw the client protocol out of sync.
124 if (FD_ISSET(0, &rfds)) {
125 set_keepalives(KA_NO);
126 the_character = inkey();
127 set_keepalives(KA_YES);
132 return(the_character);
140 * Check to see if we need to pause at the end of a screen.
141 * If we do, we have to switch to half keepalives during the pause because
142 * we are probably in the middle of a server operation and the NOOP command
143 * would confuse everything.
145 int checkpagin(int lp, unsigned int pagin, unsigned int height)
149 if (sigcaught) return(lp);
150 thekey = was_a_key_pressed();
151 if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
153 if (thekey == 'n' || thekey == 'N')
155 if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
156 if (sigcaught) return(lp);
158 if (!pagin) return(0);
159 if (lp>=(height-1)) {
160 set_keepalives(KA_HALF);
162 set_keepalives(KA_YES);
172 * pprintf() ... paginated version of printf()
174 void pprintf(const char *format, ...) {
176 static char buf[4096]; /* static for performance, change if needed */
179 /* If sigcaught is nonzero, a keypress has interrupted this and we
180 * should just drain output.
182 if (sigcaught) return;
184 /* Otherwise, start spewing... */
185 va_start(arg_ptr, format);
186 vsnprintf(buf, sizeof(buf), format, arg_ptr);
189 for (i=0; i<strlen(buf); ++i) {
193 lines_printed = checkpagin(lines_printed,
194 (userflags & US_PAGINATOR),
203 * print_express() - print express messages if there are any
205 void print_express(void)
214 char *listing = NULL;
215 int r; /* IPC result code */
217 if (express_msgs == 0)
223 if (strlen(rc_exp_cmd) == 0) {
228 while (express_msgs != 0) {
229 r = CtdlIPCGetInstantMessage(ipc_for_signal_handlers, &listing, buf);
233 express_msgs = extract_int(buf, 0);
234 timestamp = extract_long(buf, 1);
235 flags = extract_int(buf, 2);
236 extract(sender, buf, 3);
237 extract(node, buf, 4);
238 strcpy(last_paged, sender);
240 stamp = localtime(×tamp);
242 /* If the page is a Logoff Request, honor it. */
248 if (strlen(rc_exp_cmd) > 0) {
249 outpipe = popen(rc_exp_cmd, "w");
250 if (outpipe != NULL) {
251 /* Header derived from flags */
254 "Please log off now, as requested ");
256 fprintf(outpipe, "Broadcast message ");
258 fprintf(outpipe, "Chat request ");
260 fprintf(outpipe, "Message ");
261 /* Timestamp. Can this be improved? */
262 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
263 fprintf(outpipe, "at 12:%02d%cm",
265 stamp->tm_hour ? 'p' : 'a');
266 else if (stamp->tm_hour > 12) /* pm */
267 fprintf(outpipe, "at %d:%02dpm",
271 fprintf(outpipe, "at %d:%02dam",
272 stamp->tm_hour, stamp->tm_min);
273 fprintf(outpipe, " from %s", sender);
274 if (strncmp(serv_info.serv_nodename, node, 32))
275 fprintf(outpipe, " @%s", node);
276 fprintf(outpipe, ":\n%s\n", listing);
278 if (express_msgs == 0)
283 /* fall back to built-in express message display */
287 /* Header derived from flags */
289 scr_printf("Please log off now, as requested ");
291 scr_printf("Broadcast message ");
293 scr_printf("Chat request ");
295 scr_printf("Message ");
297 /* Timestamp. Can this be improved? */
298 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)/* 12am/12pm */
299 scr_printf("at 12:%02d%cm", stamp->tm_min,
300 stamp->tm_hour ? 'p' : 'a');
301 else if (stamp->tm_hour > 12) /* pm */
302 scr_printf("at %d:%02dpm",
303 stamp->tm_hour - 12, stamp->tm_min);
305 scr_printf("at %d:%02dam", stamp->tm_hour, stamp->tm_min);
308 scr_printf(" from %s", sender);
310 /* Remote node, if any */
311 if (strncmp(serv_info.serv_nodename, node, 32))
312 scr_printf(" @%s", node);
316 fmout(screenwidth, NULL, listing, NULL, 1, screenheight, -1, 0);
319 /* when running in curses mode, the scroll bar in most
320 xterm-style programs becomes useless, so it makes sense to
321 pause after a screenful of pages if the user has been idle
322 for a while. However, this is annoying to some of the users
323 who aren't in curses mode and tend to leave their clients
324 idle. keepalives become disabled, resulting in getting booted
325 when coming back to the idle session. but they probably have
326 a working scrollback in their terminal, so disable it in this
329 if (!is_curses_enabled())
332 scr_printf("\n---\n");
339 void set_keepalives(int s)
341 keepalives_enabled = (char) s;
345 * This loop handles the "keepalive" messages sent to the server when idling.
348 static time_t idlet = 0;
349 static void really_do_keepalive(void) {
350 int r; /* IPC response code */
354 /* If full keepalives are enabled, send a NOOP to the server and
355 * wait for a response.
357 if (keepalives_enabled == KA_YES) {
358 r = CtdlIPCNoop(ipc_for_signal_handlers);
359 if (express_msgs > 0) {
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) ) {
375 CtdlIPC_putline(ipc_for_signal_handlers, "QNOP");
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, "");
740 strcpy(rc_gotmail_cmd, "");
742 rc_encrypt = RC_DEFAULT;
745 rc_screen = RC_DEFAULT;
747 rc_alt_semantics = 0;
749 /* now try to open the citadel.rc file */
752 if (getenv("HOME") != NULL) {
753 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
754 ccfile = fopen(buf, "r");
756 if (ccfile == NULL) {
757 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
758 ccfile = fopen(buf, "r");
760 if (ccfile == NULL) {
761 ccfile = fopen("/etc/citadel.rc", "r");
763 if (ccfile == NULL) {
764 ccfile = fopen("./citadel.rc", "r");
766 if (ccfile == NULL) {
767 perror("commands: cannot open citadel.rc");
770 while (fgets(buf, sizeof buf, ccfile) != NULL) {
771 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
772 buf[strlen(buf) - 1] = 0;
774 if (!strncasecmp(buf, "encrypt=", 8)) {
775 if (!strcasecmp(&buf[8], "yes")) {
779 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
784 else if (!strcasecmp(&buf[8], "no")) {
787 else if (!strcasecmp(&buf[8], "default")) {
788 rc_encrypt = RC_DEFAULT;
794 if (!strncasecmp(buf, "fullscreen=", 11)) {
795 if (!strcasecmp(&buf[11], "yes"))
797 else if (!strcasecmp(&buf[11], "no"))
802 if (!strncasecmp(buf, "editor=", 7))
803 strcpy(editor_path, &buf[7]);
805 if (!strncasecmp(buf, "printcmd=", 9))
806 strcpy(printcmd, &buf[9]);
808 if (!strncasecmp(buf, "expcmd=", 7))
809 strcpy(rc_exp_cmd, &buf[7]);
811 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
812 have_xterm = (char) atoi(&buf[24]);
814 if (!strncasecmp(buf, "use_floors=", 11)) {
815 if (!strcasecmp(&buf[11], "yes"))
816 rc_floor_mode = RC_YES;
817 if (!strcasecmp(&buf[11], "no"))
818 rc_floor_mode = RC_NO;
819 if (!strcasecmp(&buf[11], "default"))
820 rc_floor_mode = RC_DEFAULT;
822 if (!strncasecmp(buf, "beep=", 5)) {
823 rc_exp_beep = atoi(&buf[5]);
825 if (!strncasecmp(buf, "allow_attachments=", 18)) {
826 rc_allow_attachments = atoi(&buf[18]);
828 if (!strncasecmp(buf, "idle_threshold=", 15)) {
829 rc_idle_threshold = atoi(&buf[15]);
831 if (!strncasecmp(buf, "remember_passwords=", 19)) {
832 rc_remember_passwords = atoi(&buf[19]);
834 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
835 rc_display_message_numbers = atoi(&buf[24]);
837 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
838 rc_force_mail_prompts = atoi(&buf[19]);
840 if (!strncasecmp(buf, "ansi_color=", 11)) {
841 if (!strncasecmp(&buf[11], "on", 2))
843 if (!strncasecmp(&buf[11], "auto", 4))
844 rc_ansi_color = 2; /* autodetect */
845 if (!strncasecmp(&buf[11], "user", 4))
846 rc_ansi_color = 3; /* user config */
848 if (!strncasecmp(buf, "prompt_control=", 15)) {
849 if (!strncasecmp(&buf[15], "on", 2))
850 rc_prompt_control = 1;
851 if (!strncasecmp(&buf[15], "user", 4))
852 rc_prompt_control = 3; /* user config */
854 if (!strncasecmp(buf, "username=", 9))
855 strcpy(rc_username, &buf[9]);
857 if (!strncasecmp(buf, "password=", 9))
858 strcpy(rc_password, &buf[9]);
860 if (!strncasecmp(buf, "urlcmd=", 7))
861 strcpy(rc_url_cmd, &buf[7]);
863 if (!strncasecmp(buf, "gotmailcmd=", 11))
864 strcpy(rc_gotmail_cmd, &buf[11]);
866 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
867 if (!strncasecmp(&buf[11], "yes", 3))
868 rc_alt_semantics = 1;
869 if (!strncasecmp(&buf[11], "no", 2))
870 rc_alt_semantics = 0;
872 if (!strncasecmp(buf, "cmd=", 4)) {
873 strcpy(buf, &buf[4]);
875 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
877 cptr->c_cmdnum = atoi(buf);
878 for (d = strlen(buf); d >= 0; --d)
881 strcpy(buf, &buf[b + 1]);
883 cptr->c_axlevel = atoi(buf);
884 for (d = strlen(buf); d >= 0; --d)
887 strcpy(buf, &buf[b + 1]);
889 for (a = 0; a < 5; ++a)
890 cptr->c_keys[a][0] = 0;
894 buf[strlen(buf) + 1] = 0;
895 while (strlen(buf) > 0) {
897 for (d = strlen(buf); d >= 0; --d)
900 strncpy(cptr->c_keys[a], buf, b);
901 cptr->c_keys[a][b] = 0;
903 strcpy(buf, &buf[b + 1]);
913 lastcmd->next = cptr;
923 * return the key associated with a command
925 char keycmd(char *cmdstr)
929 for (a = 0; a < strlen(cmdstr); ++a)
930 if (cmdstr[a] == '&')
931 return (tolower(cmdstr[a + 1]));
937 * Output the string from a key command without the ampersand
938 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
940 char *cmd_expand(char *strbuf, int mode)
948 for (a = 0; a < strlen(exp); ++a) {
949 if (strbuf[a] == '&') {
952 strcpy(&exp[a], &exp[a + 1]);
956 strcpy(buf, &exp[a + 2]);
962 if (!strncmp(&exp[a], "^r", 2)) {
964 strcpy(&exp[a], room_name);
965 strcat(exp, &buf[a + 2]);
967 if (!strncmp(&exp[a], "^c", 2)) {
969 strcpy(&exp[a + 1], &exp[a + 2]);
979 * Comparison function to determine if entered commands match a
980 * command loaded from the config file.
982 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
993 for (a = 0; a < ncomp; ++a) {
994 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
995 || (cptr->c_axlevel > cmdax))
1003 * This function returns 1 if a given command requires a string input
1005 int requires_string(struct citcmd *cptr, int ncomp)
1010 strcpy(buf, cptr->c_keys[ncomp - 1]);
1011 for (a = 0; a < strlen(buf); ++a) {
1020 * Input a command at the main prompt.
1021 * This function returns an integer command number. If the command prompts
1022 * for a string then it is placed in the supplied buffer.
1024 int getcmd(CtdlIPC *ipc, char *argbuf)
1033 struct citcmd *cptr;
1036 * Starting a new command now, so set sigcaught to 0. This variable
1037 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1038 * been interrupted by a keypress.
1042 /* Switch color support on or off if we're in user mode */
1043 if (rc_ansi_color == 3) {
1044 if (userflags & US_COLOR)
1049 /* if we're running in idiot mode, display a cute little menu */
1050 IFNEXPERT formout(ipc, "mainmenu");
1055 for (a = 0; a < 5; ++a)
1057 /* now the room prompt... */
1058 ok_to_interrupt = 1;
1059 color(BRIGHT_WHITE);
1060 scr_printf("\n%s", room_name);
1062 scr_printf("%c ", room_prompt(room_flags));
1067 ok_to_interrupt = 0;
1069 /* Handle the backspace key, but only if there's something
1070 * to backspace over...
1072 if ((ch == 8) && (cmdpos > 0)) {
1073 back(cmdspaces[cmdpos - 1] + 1);
1077 /* Spacebar invokes "lazy traversal" commands */
1078 if ((ch == 32) && (cmdpos == 0)) {
1079 this_lazy_cmd = next_lazy_cmd;
1080 if (this_lazy_cmd == 13)
1082 if (this_lazy_cmd == 5)
1084 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1085 if (cptr->c_cmdnum == this_lazy_cmd) {
1086 for (a = 0; a < 5; ++a)
1087 if (cptr->c_keys[a][0] != 0)
1088 scr_printf("%s ", cmd_expand(
1089 cptr->c_keys[a], 0));
1091 return (this_lazy_cmd);
1095 return (this_lazy_cmd);
1097 /* Otherwise, process the command */
1098 cmdbuf[cmdpos] = tolower(ch);
1100 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1101 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1103 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1104 cmdspaces[cmdpos] = strlen(
1105 cmd_expand(cptr->c_keys[cmdpos], 0));
1107 if ((cptr->c_keys[cmdpos + 1]) != 0)
1113 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1114 if (cmdmatch(cmdbuf, cptr, 5)) {
1115 /* We've found our command. */
1116 if (requires_string(cptr, cmdpos)) {
1117 getline(argbuf, 32);
1122 /* If this command is one that changes rooms,
1123 * then the next lazy-command (space bar)
1124 * should be "read new" instead of "goto"
1126 if ((cptr->c_cmdnum == 5)
1127 || (cptr->c_cmdnum == 6)
1128 || (cptr->c_cmdnum == 47)
1129 || (cptr->c_cmdnum == 52)
1130 || (cptr->c_cmdnum == 16)
1131 || (cptr->c_cmdnum == 20))
1134 return (cptr->c_cmdnum);
1140 pprintf("\rOne of ... \n");
1141 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1142 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1143 for (a = 0; a < 5; ++a) {
1144 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1150 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1152 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1153 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1154 for (a = 0; a < cmdpos; ++a) {
1156 cmd_expand(cptr->c_keys[a], 0));
1171 * set tty modes. commands are:
1173 * 01- set to bbs mode
1174 * 2 - save current settings for later restoral
1175 * 3 - restore saved settings
1177 #ifdef HAVE_TERMIOS_H
1178 void sttybbs(int cmd)
1179 { /* SysV version of sttybbs() */
1180 struct termios live;
1181 static struct termios saved_settings;
1182 static int last_cmd = 0;
1189 if ((cmd == 0) || (cmd == 1)) {
1190 tcgetattr(0, &live);
1191 live.c_iflag = ISTRIP | IXON | IXANY;
1192 live.c_oflag = OPOST | ONLCR;
1193 live.c_lflag = ISIG | NOFLSH;
1195 live.c_cc[VINTR] = 0;
1196 live.c_cc[VQUIT] = 0;
1199 live.c_cc[VMIN] = 0;
1200 live.c_cc[VTIME] = 0;
1203 /* do we even need this stuff anymore? */
1204 /* live.c_line=0; */
1205 live.c_cc[VERASE] = 8;
1206 live.c_cc[VKILL] = 24;
1207 live.c_cc[VEOF] = 1;
1208 live.c_cc[VEOL] = 255;
1209 live.c_cc[VEOL2] = 0;
1210 live.c_cc[VSTART] = 0;
1211 tcsetattr(0, TCSADRAIN, &live);
1214 tcgetattr(0, &saved_settings);
1217 tcsetattr(0, TCSADRAIN, &saved_settings);
1221 void sttybbs(int cmd)
1222 { /* BSD version of sttybbs() */
1224 static struct sgttyb saved_settings;
1225 static int last_cmd = 0;
1232 if ((cmd == 0) || (cmd == 1)) {
1234 live.sg_flags |= CBREAK;
1235 live.sg_flags |= CRMOD;
1236 live.sg_flags |= NL1;
1237 live.sg_flags &= ~ECHO;
1239 live.sg_flags |= NOFLSH;
1243 gtty(0, &saved_settings);
1246 stty(0, &saved_settings);
1253 * display_help() - help file viewer
1255 void display_help(CtdlIPC *ipc, char *name)
1262 * fmout() - Citadel text formatter and paginator
1265 int width, /* screen width to use */
1266 FILE *fpin, /* file to read from, or NULL to format given text */
1267 char *text, /* Text to be formatted (when fpin is NULL) */
1268 FILE *fpout, /* File to write to, or NULL to write to screen */
1269 char pagin, /* nonzero if we should use the paginator */
1270 int height, /* screen height to use */
1271 int starting_lp,/* starting value for lines_printed, -1 for global */
1272 int subst) /* nonzero if we should use hypertext mode */
1281 num_urls = 0; /* Start with a clean slate of embedded URL's */
1283 if (starting_lp >= 0) {
1284 lines_printed = starting_lp;
1289 c = 1; /* c is the current pos */
1290 e = text; /* e is pointer to current pos */
1292 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1293 if (fpin != NULL) { /* read from file */
1296 if (eof_flag == 0) {
1298 buffer[strlen(buffer) + 1] = 0;
1299 buffer[strlen(buffer)] = a;
1301 } else { /* read from text */
1304 while (isspace(buffer[strlen(buffer) - 1]))
1305 buffer[strlen(buffer) - 1] = 0;
1306 buffer[strlen(buffer) + 1] = 0;
1307 buffer[strlen(buffer)] = 10;
1309 if (eof_flag == 0) {
1311 buffer[strlen(buffer) + 1] = 0;
1312 buffer[strlen(buffer)] = a;
1317 if ( (!strncasecmp(buffer, "http://", 7))
1318 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1319 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1320 for (a=0; a<strlen(urls[num_urls]); ++a) {
1321 b = urls[num_urls][a];
1322 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1323 || (b==13) || (b==9) || (b=='\"') )
1324 urls[num_urls][a] = 0;
1329 buffer[strlen(buffer) + 1] = 0;
1331 strcpy(buffer, &buffer[1]);
1338 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1340 if (((old == 13) || (old == 10)) && (isspace(real))) {
1342 fprintf(fpout, "\n");
1346 lines_printed = checkpagin(lines_printed, pagin, height);
1354 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1356 fprintf(fpout, "\n%s", aaa);
1358 scr_printf("\n%s", aaa);
1360 lines_printed = checkpagin(lines_printed, pagin, height);
1370 if ((strlen(aaa) + c) > (width - 1)) {
1373 fprintf(fpout, "\n");
1377 lines_printed = checkpagin(lines_printed, pagin, height);
1381 fprintf(fpout, "%s ", aaa);
1383 scr_printf("%s ", aaa);
1386 c = c + strlen(aaa);
1390 if ((a == 13) || (a == 10)) {
1392 fprintf(fpout, "%s\n", aaa);
1394 scr_printf("%s\n", aaa);
1396 lines_printed = checkpagin(lines_printed, pagin, height);
1399 if (sigcaught) goto OOPS;
1405 /* keypress caught; drain the server */
1407 CtdlIPC_getline(ipc, aaa);
1408 } while (strcmp(aaa, "000")); */
1412 fprintf(fpout, "\n");
1416 lines_printed = checkpagin(lines_printed, pagin, height);
1423 * support ANSI color if defined
1425 void color(int colornum)
1427 static int is_bold = 0;
1428 static int hold_color, current_color;
1430 if (colornum == COLOR_PUSH) {
1431 hold_color = current_color;
1435 if (colornum == COLOR_POP) {
1440 current_color = colornum;
1442 #ifdef HAVE_CURSES_H
1443 if (scr_color(colornum))
1446 /* When switching to dim white, actually output an 'original
1447 * pair' sequence -- this looks better on black-on-white
1448 * terminals. - Changed to ORIGINAL_PAIR as this actually
1449 * wound up looking horrible on black-on-white terminals, not
1450 * to mention transparent terminals.
1452 if (colornum == ORIGINAL_PAIR)
1453 printf("\033[39;49m");
1455 printf("\033[3%d;40m", (colornum & 7));
1457 if ((colornum >= 8) && (is_bold == 0)) {
1460 } else if ((colornum < 8) && (is_bold == 1)) {
1468 void cls(int colornum)
1471 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1478 * Detect whether ANSI color is available (answerback)
1480 void send_ansi_detect(void)
1482 if (rc_ansi_color == 2) {
1489 void look_for_ansi(void)
1497 if (rc_ansi_color == 0) {
1499 } else if (rc_ansi_color == 1) {
1501 } else if (rc_ansi_color == 2) {
1503 /* otherwise, do the auto-detect */
1508 if ((now - AnsiDetect) < 2)
1517 select(1, &rfds, NULL, NULL, &tv);
1518 if (FD_ISSET(0, &rfds)) {
1519 abuf[strlen(abuf) + 1] = 0;
1520 read(0, &abuf[strlen(abuf)], 1);
1522 } while (FD_ISSET(0, &rfds));
1524 for (a = 0; a < strlen(abuf); ++a) {
1525 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1526 && (abuf[a + 2] == '?')) {
1535 * Display key options (highlight hotkeys inside angle brackets)
1537 void keyopt(char *buf) {
1541 for (i=0; i<strlen(buf); ++i) {
1544 color(BRIGHT_MAGENTA);
1558 * Present a key-menu line choice type of thing
1560 char keymenu(char *menuprompt, char *menustring) {
1566 int display_prompt = 1;
1568 choices = num_tokens(menustring, '|');
1570 if (menuprompt != NULL) do_prompt = 1;
1571 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1574 if (display_prompt) {
1576 scr_printf("%s ", menuprompt);
1579 for (i=0; i<choices; ++i) {
1580 extract(buf, menustring, i);
1590 if ( (do_prompt) && (ch=='?') ) {
1591 scr_printf("\rOne of... ");
1593 for (i=0; i<choices; ++i) {
1594 extract(buf, menustring, i);
1603 for (i=0; i<choices; ++i) {
1604 extract(buf, menustring, i);
1605 for (c=1; c<strlen(buf); ++c) {
1606 if ( (ch == tolower(buf[c]))
1608 && (buf[c+1]=='>') ) {
1609 for (a=0; a<strlen(buf); ++a) {
1610 if ( (a!=(c-1)) && (a!=(c+1))) {