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 #ifndef DISABLE_CURSES
503 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
515 { /* Returns 1 for yes, 0 for no */
531 /* Returns 1 for yes, 0 for no, arg is default value */
554 /* Gets a line from the terminal */
555 /* string == Pointer to string buffer */
556 /* lim == Maximum length - if negative, no-show */
557 void getline(char *string, int lim)
571 if ((a == 8 || a == 23) && (strlen(string) == 0))
573 if ((a != 10) && (a != 8) && (strlen(string) == lim))
575 if ((a == 8) && (string[0] != 0)) {
576 string[strlen(string) - 1] = 0;
577 scr_putc(8); scr_putc(32); scr_putc(8);
580 if ((a == 23) && (string[0] != 0)) {
582 string[strlen(string) - 1] = 0;
583 scr_putc(8); scr_putc(32); scr_putc(8);
584 } while (strlen(string) && string[strlen(string) - 1] != ' ');
606 * strprompt() - prompt for a string, print the existing value and
607 * allow the user to press return to keep it...
609 void strprompt(char *prompt, char *str, int len)
616 scr_printf("%s ", prompt);
619 color(BRIGHT_MAGENTA);
622 scr_printf("%s", str);
625 for (i=0; i<strlen(str); ++i) {
642 * boolprompt() - prompt for a yes/no, print the existing value and
643 * allow the user to press return to keep it...
645 int boolprompt(char *prompt, int prev_val)
650 scr_printf("%s ", prompt);
653 color(BRIGHT_MAGENTA);
654 scr_printf("%s", (prev_val ? "Yes" : "No"));
658 r = (yesno_d(prev_val));
664 * intprompt() - like strprompt(), except for an integer
665 * (note that it RETURNS the new value!)
667 int intprompt(char *prompt, int ival, int imin, int imax)
675 snprintf(buf, sizeof buf, "%d", i);
676 strprompt(prompt, buf, 15);
678 for (p=0; p<strlen(buf); ++p) {
679 if ( (!isdigit(buf[p]))
680 && ( (buf[p]!='-') || (p!=0) ) )
684 scr_printf("*** Must be no less than %d.\n", imin);
686 scr_printf("*** Must be no more than %d.\n", imax);
687 } while ((i < imin) || (i > imax));
692 * newprompt() - prompt for a string with no existing value
693 * (clears out string buffer first)
695 void newprompt(char *prompt, char *str, int len)
697 color(BRIGHT_MAGENTA);
698 scr_printf("%s", prompt);
706 { /* returns a lower case value */
715 * parse the citadel.rc file
717 void load_command_set(void)
722 struct citcmd *lastcmd = NULL;
727 /* first, set up some defaults for non-required variables */
729 strcpy(editor_path, "");
730 strcpy(printcmd, "");
731 strcpy(rc_username, "");
732 strcpy(rc_password, "");
735 rc_allow_attachments = 0;
736 rc_remember_passwords = 0;
737 strcpy(rc_exp_cmd, "");
738 rc_display_message_numbers = 0;
739 rc_force_mail_prompts = 0;
741 strcpy(rc_url_cmd, "");
742 strcpy(rc_gotmail_cmd, "");
744 rc_encrypt = RC_DEFAULT;
746 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
747 rc_screen = RC_DEFAULT;
749 rc_alt_semantics = 0;
751 /* now try to open the citadel.rc file */
754 if (getenv("HOME") != NULL) {
755 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
756 ccfile = fopen(buf, "r");
758 if (ccfile == NULL) {
759 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
760 ccfile = fopen(buf, "r");
762 if (ccfile == NULL) {
763 ccfile = fopen("/etc/citadel.rc", "r");
765 if (ccfile == NULL) {
766 ccfile = fopen("./citadel.rc", "r");
768 if (ccfile == NULL) {
769 perror("commands: cannot open citadel.rc");
772 while (fgets(buf, sizeof buf, ccfile) != NULL) {
773 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
774 buf[strlen(buf) - 1] = 0;
776 if (!strncasecmp(buf, "encrypt=", 8)) {
777 if (!strcasecmp(&buf[8], "yes")) {
781 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
786 else if (!strcasecmp(&buf[8], "no")) {
789 else if (!strcasecmp(&buf[8], "default")) {
790 rc_encrypt = RC_DEFAULT;
795 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
796 if (!strncasecmp(buf, "fullscreen=", 11)) {
797 if (!strcasecmp(&buf[11], "yes"))
799 else if (!strcasecmp(&buf[11], "no"))
804 if (!strncasecmp(buf, "editor=", 7))
805 strcpy(editor_path, &buf[7]);
807 if (!strncasecmp(buf, "printcmd=", 9))
808 strcpy(printcmd, &buf[9]);
810 if (!strncasecmp(buf, "expcmd=", 7))
811 strcpy(rc_exp_cmd, &buf[7]);
813 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
814 have_xterm = (char) atoi(&buf[24]);
816 if (!strncasecmp(buf, "use_floors=", 11)) {
817 if (!strcasecmp(&buf[11], "yes"))
818 rc_floor_mode = RC_YES;
819 if (!strcasecmp(&buf[11], "no"))
820 rc_floor_mode = RC_NO;
821 if (!strcasecmp(&buf[11], "default"))
822 rc_floor_mode = RC_DEFAULT;
824 if (!strncasecmp(buf, "beep=", 5)) {
825 rc_exp_beep = atoi(&buf[5]);
827 if (!strncasecmp(buf, "allow_attachments=", 18)) {
828 rc_allow_attachments = atoi(&buf[18]);
830 if (!strncasecmp(buf, "idle_threshold=", 15)) {
831 rc_idle_threshold = atoi(&buf[15]);
833 if (!strncasecmp(buf, "remember_passwords=", 19)) {
834 rc_remember_passwords = atoi(&buf[19]);
836 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
837 rc_display_message_numbers = atoi(&buf[24]);
839 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
840 rc_force_mail_prompts = atoi(&buf[19]);
842 if (!strncasecmp(buf, "ansi_color=", 11)) {
843 if (!strncasecmp(&buf[11], "on", 2))
845 if (!strncasecmp(&buf[11], "auto", 4))
846 rc_ansi_color = 2; /* autodetect */
847 if (!strncasecmp(&buf[11], "user", 4))
848 rc_ansi_color = 3; /* user config */
850 if (!strncasecmp(buf, "prompt_control=", 15)) {
851 if (!strncasecmp(&buf[15], "on", 2))
852 rc_prompt_control = 1;
853 if (!strncasecmp(&buf[15], "user", 4))
854 rc_prompt_control = 3; /* user config */
856 if (!strncasecmp(buf, "username=", 9))
857 strcpy(rc_username, &buf[9]);
859 if (!strncasecmp(buf, "password=", 9))
860 strcpy(rc_password, &buf[9]);
862 if (!strncasecmp(buf, "urlcmd=", 7))
863 strcpy(rc_url_cmd, &buf[7]);
865 if (!strncasecmp(buf, "gotmailcmd=", 11))
866 strcpy(rc_gotmail_cmd, &buf[11]);
868 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
869 if (!strncasecmp(&buf[11], "yes", 3))
870 rc_alt_semantics = 1;
871 if (!strncasecmp(&buf[11], "no", 2))
872 rc_alt_semantics = 0;
874 if (!strncasecmp(buf, "cmd=", 4)) {
875 strcpy(buf, &buf[4]);
877 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
879 cptr->c_cmdnum = atoi(buf);
880 for (d = strlen(buf); d >= 0; --d)
883 strcpy(buf, &buf[b + 1]);
885 cptr->c_axlevel = atoi(buf);
886 for (d = strlen(buf); d >= 0; --d)
889 strcpy(buf, &buf[b + 1]);
891 for (a = 0; a < 5; ++a)
892 cptr->c_keys[a][0] = 0;
896 buf[strlen(buf) + 1] = 0;
897 while (strlen(buf) > 0) {
899 for (d = strlen(buf); d >= 0; --d)
902 strncpy(cptr->c_keys[a], buf, b);
903 cptr->c_keys[a][b] = 0;
905 strcpy(buf, &buf[b + 1]);
915 lastcmd->next = cptr;
925 * return the key associated with a command
927 char keycmd(char *cmdstr)
931 for (a = 0; a < strlen(cmdstr); ++a)
932 if (cmdstr[a] == '&')
933 return (tolower(cmdstr[a + 1]));
939 * Output the string from a key command without the ampersand
940 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
942 char *cmd_expand(char *strbuf, int mode)
950 for (a = 0; a < strlen(exp); ++a) {
951 if (strbuf[a] == '&') {
954 strcpy(&exp[a], &exp[a + 1]);
958 strcpy(buf, &exp[a + 2]);
964 if (!strncmp(&exp[a], "^r", 2)) {
966 strcpy(&exp[a], room_name);
967 strcat(exp, &buf[a + 2]);
969 if (!strncmp(&exp[a], "^c", 2)) {
971 strcpy(&exp[a + 1], &exp[a + 2]);
981 * Comparison function to determine if entered commands match a
982 * command loaded from the config file.
984 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
995 for (a = 0; a < ncomp; ++a) {
996 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
997 || (cptr->c_axlevel > cmdax))
1005 * This function returns 1 if a given command requires a string input
1007 int requires_string(struct citcmd *cptr, int ncomp)
1012 strcpy(buf, cptr->c_keys[ncomp - 1]);
1013 for (a = 0; a < strlen(buf); ++a) {
1022 * Input a command at the main prompt.
1023 * This function returns an integer command number. If the command prompts
1024 * for a string then it is placed in the supplied buffer.
1026 int getcmd(CtdlIPC *ipc, char *argbuf)
1035 struct citcmd *cptr;
1038 * Starting a new command now, so set sigcaught to 0. This variable
1039 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1040 * been interrupted by a keypress.
1044 /* Switch color support on or off if we're in user mode */
1045 if (rc_ansi_color == 3) {
1046 if (userflags & US_COLOR)
1051 /* if we're running in idiot mode, display a cute little menu */
1052 IFNEXPERT formout(ipc, "mainmenu");
1057 for (a = 0; a < 5; ++a)
1059 /* now the room prompt... */
1060 ok_to_interrupt = 1;
1061 color(BRIGHT_WHITE);
1062 scr_printf("\n%s", room_name);
1064 scr_printf("%c ", room_prompt(room_flags));
1069 ok_to_interrupt = 0;
1071 /* Handle the backspace key, but only if there's something
1072 * to backspace over...
1074 if ((ch == 8) && (cmdpos > 0)) {
1075 back(cmdspaces[cmdpos - 1] + 1);
1079 /* Spacebar invokes "lazy traversal" commands */
1080 if ((ch == 32) && (cmdpos == 0)) {
1081 this_lazy_cmd = next_lazy_cmd;
1082 if (this_lazy_cmd == 13)
1084 if (this_lazy_cmd == 5)
1086 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1087 if (cptr->c_cmdnum == this_lazy_cmd) {
1088 for (a = 0; a < 5; ++a)
1089 if (cptr->c_keys[a][0] != 0)
1090 scr_printf("%s ", cmd_expand(
1091 cptr->c_keys[a], 0));
1093 return (this_lazy_cmd);
1097 return (this_lazy_cmd);
1099 /* Otherwise, process the command */
1100 cmdbuf[cmdpos] = tolower(ch);
1102 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1103 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1105 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1106 cmdspaces[cmdpos] = strlen(
1107 cmd_expand(cptr->c_keys[cmdpos], 0));
1109 if ((cptr->c_keys[cmdpos + 1]) != 0)
1115 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1116 if (cmdmatch(cmdbuf, cptr, 5)) {
1117 /* We've found our command. */
1118 if (requires_string(cptr, cmdpos)) {
1119 getline(argbuf, 32);
1124 /* If this command is one that changes rooms,
1125 * then the next lazy-command (space bar)
1126 * should be "read new" instead of "goto"
1128 if ((cptr->c_cmdnum == 5)
1129 || (cptr->c_cmdnum == 6)
1130 || (cptr->c_cmdnum == 47)
1131 || (cptr->c_cmdnum == 52)
1132 || (cptr->c_cmdnum == 16)
1133 || (cptr->c_cmdnum == 20))
1136 return (cptr->c_cmdnum);
1142 pprintf("\rOne of ... \n");
1143 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1144 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1145 for (a = 0; a < 5; ++a) {
1146 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1152 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1154 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1155 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1156 for (a = 0; a < cmdpos; ++a) {
1158 cmd_expand(cptr->c_keys[a], 0));
1173 * set tty modes. commands are:
1175 * 01- set to bbs mode
1176 * 2 - save current settings for later restoral
1177 * 3 - restore saved settings
1179 #ifdef HAVE_TERMIOS_H
1180 void sttybbs(int cmd)
1181 { /* SysV version of sttybbs() */
1182 struct termios live;
1183 static struct termios saved_settings;
1184 static int last_cmd = 0;
1191 if ((cmd == 0) || (cmd == 1)) {
1192 tcgetattr(0, &live);
1193 live.c_iflag = ISTRIP | IXON | IXANY;
1194 live.c_oflag = OPOST | ONLCR;
1195 live.c_lflag = ISIG | NOFLSH;
1197 live.c_cc[VINTR] = 0;
1198 live.c_cc[VQUIT] = 0;
1201 live.c_cc[VMIN] = 0;
1202 live.c_cc[VTIME] = 0;
1205 /* do we even need this stuff anymore? */
1206 /* live.c_line=0; */
1207 live.c_cc[VERASE] = 8;
1208 live.c_cc[VKILL] = 24;
1209 live.c_cc[VEOF] = 1;
1210 live.c_cc[VEOL] = 255;
1211 live.c_cc[VEOL2] = 0;
1212 live.c_cc[VSTART] = 0;
1213 tcsetattr(0, TCSADRAIN, &live);
1216 tcgetattr(0, &saved_settings);
1219 tcsetattr(0, TCSADRAIN, &saved_settings);
1224 void sttybbs(int cmd)
1225 { /* BSD version of sttybbs() */
1227 static struct sgttyb saved_settings;
1228 static int last_cmd = 0;
1235 if ((cmd == 0) || (cmd == 1)) {
1237 live.sg_flags |= CBREAK;
1238 live.sg_flags |= CRMOD;
1239 live.sg_flags |= NL1;
1240 live.sg_flags &= ~ECHO;
1242 live.sg_flags |= NOFLSH;
1246 gtty(0, &saved_settings);
1249 stty(0, &saved_settings);
1256 * display_help() - help file viewer
1258 void display_help(CtdlIPC *ipc, char *name)
1265 * fmout() - Citadel text formatter and paginator
1268 int width, /* screen width to use */
1269 FILE *fpin, /* file to read from, or NULL to format given text */
1270 char *text, /* Text to be formatted (when fpin is NULL) */
1271 FILE *fpout, /* File to write to, or NULL to write to screen */
1272 char pagin, /* nonzero if we should use the paginator */
1273 int height, /* screen height to use */
1274 int starting_lp,/* starting value for lines_printed, -1 for global */
1275 int subst) /* nonzero if we should use hypertext mode */
1284 num_urls = 0; /* Start with a clean slate of embedded URL's */
1286 if (starting_lp >= 0) {
1287 lines_printed = starting_lp;
1292 c = 1; /* c is the current pos */
1293 e = text; /* e is pointer to current pos */
1295 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1296 if (fpin != NULL) { /* read from file */
1299 if (eof_flag == 0) {
1301 buffer[strlen(buffer) + 1] = 0;
1302 buffer[strlen(buffer)] = a;
1304 } else { /* read from text */
1307 while (isspace(buffer[strlen(buffer) - 1]))
1308 buffer[strlen(buffer) - 1] = 0;
1309 buffer[strlen(buffer) + 1] = 0;
1310 buffer[strlen(buffer)] = 10;
1312 if (eof_flag == 0) {
1314 buffer[strlen(buffer) + 1] = 0;
1315 buffer[strlen(buffer)] = a;
1320 if ( (!strncasecmp(buffer, "http://", 7))
1321 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1322 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1323 for (a=0; a<strlen(urls[num_urls]); ++a) {
1324 b = urls[num_urls][a];
1325 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1326 || (b==13) || (b==9) || (b=='\"') )
1327 urls[num_urls][a] = 0;
1332 buffer[strlen(buffer) + 1] = 0;
1334 strcpy(buffer, &buffer[1]);
1341 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1343 if (((old == 13) || (old == 10)) && (isspace(real))) {
1345 fprintf(fpout, "\n");
1349 lines_printed = checkpagin(lines_printed, pagin, height);
1357 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1359 fprintf(fpout, "\n%s", aaa);
1361 scr_printf("\n%s", aaa);
1363 lines_printed = checkpagin(lines_printed, pagin, height);
1373 if ((strlen(aaa) + c) > (width - 1)) {
1376 fprintf(fpout, "\n");
1380 lines_printed = checkpagin(lines_printed, pagin, height);
1384 fprintf(fpout, "%s ", aaa);
1386 scr_printf("%s ", aaa);
1389 c = c + strlen(aaa);
1393 if ((a == 13) || (a == 10)) {
1395 fprintf(fpout, "%s\n", aaa);
1397 scr_printf("%s\n", aaa);
1399 lines_printed = checkpagin(lines_printed, pagin, height);
1402 if (sigcaught) goto FMTEND;
1408 /* keypress caught; drain the server */
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 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
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
1447 * terminals. - Changed to ORIGINAL_PAIR as this actually
1448 * wound up looking horrible on black-on-white terminals, not
1449 * to mention transparent terminals.
1451 if (colornum == ORIGINAL_PAIR)
1452 printf("\033[39;49m");
1454 printf("\033[3%d;40m", (colornum & 7));
1456 if ((colornum >= 8) && (is_bold == 0)) {
1459 } else if ((colornum < 8) && (is_bold == 1)) {
1467 void cls(int colornum)
1470 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1477 * Detect whether ANSI color is available (answerback)
1479 void send_ansi_detect(void)
1481 if (rc_ansi_color == 2) {
1488 void look_for_ansi(void)
1496 if (rc_ansi_color == 0) {
1498 } else if (rc_ansi_color == 1) {
1500 } else if (rc_ansi_color == 2) {
1502 /* otherwise, do the auto-detect */
1507 if ((now - AnsiDetect) < 2)
1516 select(1, &rfds, NULL, NULL, &tv);
1517 if (FD_ISSET(0, &rfds)) {
1518 abuf[strlen(abuf) + 1] = 0;
1519 read(0, &abuf[strlen(abuf)], 1);
1521 } while (FD_ISSET(0, &rfds));
1523 for (a = 0; a < strlen(abuf); ++a) {
1524 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1525 && (abuf[a + 2] == '?')) {
1534 * Display key options (highlight hotkeys inside angle brackets)
1536 void keyopt(char *buf) {
1540 for (i=0; i<strlen(buf); ++i) {
1543 color(BRIGHT_MAGENTA);
1557 * Present a key-menu line choice type of thing
1559 char keymenu(char *menuprompt, char *menustring) {
1565 int display_prompt = 1;
1567 choices = num_tokens(menustring, '|');
1569 if (menuprompt != NULL) do_prompt = 1;
1570 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1573 if (display_prompt) {
1575 scr_printf("%s ", menuprompt);
1578 for (i=0; i<choices; ++i) {
1579 extract(buf, menustring, i);
1589 if ( (do_prompt) && (ch=='?') ) {
1590 scr_printf("\rOne of... ");
1592 for (i=0; i<choices; ++i) {
1593 extract(buf, menustring, i);
1602 for (i=0; i<choices; ++i) {
1603 extract(buf, menustring, i);
1604 for (c=1; c<strlen(buf); ++c) {
1605 if ( (ch == tolower(buf[c]))
1607 && (buf[c+1]=='>') ) {
1608 for (a=0; a<strlen(buf); ++a) {
1609 if ( (a!=(c-1)) && (a!=(c+1))) {