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;
80 int rc_prompt_control = 0;
81 time_t rc_idle_threshold = (time_t)900;
82 char urls[MAXURLS][SIZ];
84 char rc_gotmail_cmd[SIZ];
87 int next_lazy_cmd = 5;
89 int lines_printed = 0; /* line count for paginator */
90 extern int screenwidth, screenheight;
92 extern CtdlIPC *ipc_for_signal_handlers; /* KLUDGE cover your eyes */
94 struct citcmd *cmdlist = NULL;
97 /* these variables are local to this module */
98 char keepalives_enabled = KA_YES; /* send NOOPs to server when idle */
99 int ok_to_interrupt = 0; /* print express msgs asynchronously */
100 time_t AnsiDetect; /* when did we send the detect code? */
101 int enable_color = 0; /* nonzero for ANSI color */
107 * If an interesting key has been pressed, return its value, otherwise 0
109 char was_a_key_pressed(void) {
119 retval = select(1, &rfds, NULL, NULL, &tv);
121 /* Careful! Disable keepalives during keyboard polling; we're probably
122 * in the middle of a data transfer from the server, in which case
123 * sending a NOOP would throw the client protocol out of sync.
125 if (FD_ISSET(0, &rfds)) {
126 set_keepalives(KA_NO);
127 the_character = inkey();
128 set_keepalives(KA_YES);
133 return(the_character);
141 * Check to see if we need to pause at the end of a screen.
142 * If we do, we have to switch to half keepalives during the pause because
143 * we are probably in the middle of a server operation and the NOOP command
144 * would confuse everything.
146 int checkpagin(int lp, unsigned int pagin, unsigned int height)
150 if (sigcaught) return(lp);
151 thekey = was_a_key_pressed();
152 if (thekey == 'q' || thekey == 'Q' || thekey == 's' || thekey == 'S')
154 if (thekey == 'n' || thekey == 'N')
156 if ( (thekey == NEXT_KEY) || (thekey == STOP_KEY)) sigcaught = thekey;
157 if (sigcaught) return(lp);
159 if (!pagin) return(0);
160 if (lp>=(height-1)) {
161 set_keepalives(KA_HALF);
163 set_keepalives(KA_YES);
173 * pprintf() ... paginated version of printf()
175 void pprintf(const char *format, ...) {
177 static char buf[4096]; /* static for performance, change if needed */
180 /* If sigcaught is nonzero, a keypress has interrupted this and we
181 * should just drain output.
183 if (sigcaught) return;
185 /* Otherwise, start spewing... */
186 va_start(arg_ptr, format);
187 vsnprintf(buf, sizeof(buf), format, arg_ptr);
190 for (i=0; i<strlen(buf); ++i) {
194 lines_printed = checkpagin(lines_printed,
195 (userflags & US_PAGINATOR),
204 * print_express() - print express messages if there are any
206 void print_express(void)
215 char *listing = NULL;
216 int r; /* IPC result code */
218 if (express_msgs == 0)
224 if (strlen(rc_exp_cmd) == 0) {
229 while (express_msgs != 0) {
230 r = CtdlIPCGetInstantMessage(ipc_for_signal_handlers, &listing, buf);
234 express_msgs = extract_int(buf, 0);
235 timestamp = extract_long(buf, 1);
236 flags = extract_int(buf, 2);
237 extract(sender, buf, 3);
238 extract(node, buf, 4);
239 strcpy(last_paged, sender);
241 stamp = localtime(×tamp);
243 /* If the page is a Logoff Request, honor it. */
249 if (strlen(rc_exp_cmd) > 0) {
250 outpipe = popen(rc_exp_cmd, "w");
251 if (outpipe != NULL) {
252 /* Header derived from flags */
255 "Please log off now, as requested ");
257 fprintf(outpipe, "Broadcast message ");
259 fprintf(outpipe, "Chat request ");
261 fprintf(outpipe, "Message ");
262 /* Timestamp. Can this be improved? */
263 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)
264 fprintf(outpipe, "at 12:%02d%cm",
266 stamp->tm_hour ? 'p' : 'a');
267 else if (stamp->tm_hour > 12) /* pm */
268 fprintf(outpipe, "at %d:%02dpm",
272 fprintf(outpipe, "at %d:%02dam",
273 stamp->tm_hour, stamp->tm_min);
274 fprintf(outpipe, " from %s", sender);
275 if (strncmp(serv_info.serv_nodename, node, 32))
276 fprintf(outpipe, " @%s", node);
277 fprintf(outpipe, ":\n%s\n", listing);
279 if (express_msgs == 0)
284 /* fall back to built-in express message display */
288 /* Header derived from flags */
290 scr_printf("Please log off now, as requested ");
292 scr_printf("Broadcast message ");
294 scr_printf("Chat request ");
296 scr_printf("Message ");
298 /* Timestamp. Can this be improved? */
299 if (stamp->tm_hour == 0 || stamp->tm_hour == 12)/* 12am/12pm */
300 scr_printf("at 12:%02d%cm", stamp->tm_min,
301 stamp->tm_hour ? 'p' : 'a');
302 else if (stamp->tm_hour > 12) /* pm */
303 scr_printf("at %d:%02dpm",
304 stamp->tm_hour - 12, stamp->tm_min);
306 scr_printf("at %d:%02dam", stamp->tm_hour, stamp->tm_min);
309 scr_printf(" from %s", sender);
311 /* Remote node, if any */
312 if (strncmp(serv_info.serv_nodename, node, 32))
313 scr_printf(" @%s", node);
317 fmout(screenwidth, NULL, listing, NULL, 1, screenheight, -1, 0);
320 /* when running in curses mode, the scroll bar in most
321 xterm-style programs becomes useless, so it makes sense to
322 pause after a screenful of pages if the user has been idle
323 for a while. However, this is annoying to some of the users
324 who aren't in curses mode and tend to leave their clients
325 idle. keepalives become disabled, resulting in getting booted
326 when coming back to the idle session. but they probably have
327 a working scrollback in their terminal, so disable it in this
330 if (!is_curses_enabled())
333 scr_printf("\n---\n");
340 void set_keepalives(int s)
342 keepalives_enabled = (char) s;
346 * This loop handles the "keepalive" messages sent to the server when idling.
349 static time_t idlet = 0;
350 static void really_do_keepalive(void) {
351 int r; /* IPC response code */
355 /* If full keepalives are enabled, send a NOOP to the server and
356 * wait for a response.
358 if (keepalives_enabled == KA_YES) {
359 r = CtdlIPCNoop(ipc_for_signal_handlers);
360 if (express_msgs > 0) {
361 if (ok_to_interrupt == 1) {
362 scr_printf("\r%64s\r", "");
364 scr_printf("%s%c ", room_name,
365 room_prompt(room_flags));
371 /* If half keepalives are enabled, send a QNOP to the server (if the
372 * server supports it) and then do nothing.
374 if ( (keepalives_enabled == KA_HALF)
375 && (serv_info.serv_supports_qnop > 0) ) {
376 CtdlIPC_putline(ipc_for_signal_handlers, "QNOP");
380 /* threaded nonblocking keepalive stuff starts here. I'm going for a simple
381 encapsulated interface; in theory there should be no need to touch these
382 globals outside of the async_ka_* functions. */
384 #ifdef THREADED_CLIENT
385 static pthread_t ka_thr_handle;
386 static int ka_thr_active = 0;
387 static int async_ka_enabled = 0;
389 static void *ka_thread(void *arg)
391 really_do_keepalive();
392 pthread_detach(ka_thr_handle);
397 /* start up a thread to handle a keepalive in the background */
398 static void async_ka_exec(void)
400 if (!ka_thr_active) {
402 if (pthread_create(&ka_thr_handle, NULL, ka_thread, NULL)) {
403 perror("pthread_create");
408 #endif /* THREADED_CLIENT */
410 /* I changed this from static to not because I need to call it from
411 screen.c, either that or make something in screen.c not static.
412 Fix it how you like. Why all the staticness? stu */
414 void do_keepalive(void)
419 if ((now - idlet) < ((long) S_KEEPALIVE))
422 /* Do a space-backspace to keep telnet sessions from idling out */
423 scr_printf(" %c", 8);
426 #ifdef THREADED_CLIENT
427 if (async_ka_enabled)
431 really_do_keepalive();
435 /* Now the actual async-keepalve API that we expose to higher levels:
436 async_ka_start() and async_ka_end(). These do nothing when we don't have
437 threading enabled, so we avoid sprinkling ifdef's throughout the code. */
439 /* wait for a background keepalive to complete. this must be done before
440 attempting any further server requests! */
441 void async_ka_end(void)
443 #ifdef THREADED_CLIENT
445 pthread_join(ka_thr_handle, NULL);
451 /* tell do_keepalive() that keepalives are asynchronous. */
452 void async_ka_start(void)
454 #ifdef THREADED_CLIENT
461 { /* get a character from the keyboard, with */
462 int a; /* the watchdog timer in effect if necessary */
472 /* This loop waits for keyboard input. If the keepalive
473 * timer expires, it sends a keepalive to the server if
474 * necessary and then waits again.
477 scr_set_windowsize();
479 scr_set_windowsize();
483 tv.tv_sec = S_KEEPALIVE;
486 select(1, &rfds, NULL, NULL, &tv);
487 } while (!FD_ISSET(0, &rfds));
489 /* At this point, there's input, so fetch it.
490 * (There's a hole in the bucket...)
492 a = scr_getc(SCR_NOBLOCK);
499 if (((a != 23) && (a != 4) && (a != 10) && (a != 8) && (a != NEXT_KEY) && (a != STOP_KEY))
500 && ((a < 32) || (a > 126)))
503 #ifndef DISABLE_CURSES
504 #if defined(HAVE_CURSES_H) || defined(HAVE_NCURSES_H)
516 { /* Returns 1 for yes, 0 for no */
532 /* Returns 1 for yes, 0 for no, arg is default value */
555 /* Gets a line from the terminal */
556 /* string == Pointer to string buffer */
557 /* lim == Maximum length - if negative, no-show */
558 void getline(char *string, int lim)
572 if ((a == 8 || a == 23) && (strlen(string) == 0))
574 if ((a != 10) && (a != 8) && (strlen(string) == lim))
576 if ((a == 8) && (string[0] != 0)) {
577 string[strlen(string) - 1] = 0;
578 scr_putc(8); scr_putc(32); scr_putc(8);
581 if ((a == 23) && (string[0] != 0)) {
583 string[strlen(string) - 1] = 0;
584 scr_putc(8); scr_putc(32); scr_putc(8);
585 } while (strlen(string) && string[strlen(string) - 1] != ' ');
607 * strprompt() - prompt for a string, print the existing value and
608 * allow the user to press return to keep it...
610 void strprompt(char *prompt, char *str, int len)
617 scr_printf("%s ", prompt);
620 color(BRIGHT_MAGENTA);
623 scr_printf("%s", str);
626 for (i=0; i<strlen(str); ++i) {
643 * boolprompt() - prompt for a yes/no, print the existing value and
644 * allow the user to press return to keep it...
646 int boolprompt(char *prompt, int prev_val)
651 scr_printf("%s ", prompt);
654 color(BRIGHT_MAGENTA);
655 scr_printf("%s", (prev_val ? "Yes" : "No"));
659 r = (yesno_d(prev_val));
665 * intprompt() - like strprompt(), except for an integer
666 * (note that it RETURNS the new value!)
668 int intprompt(char *prompt, int ival, int imin, int imax)
676 snprintf(buf, sizeof buf, "%d", i);
677 strprompt(prompt, buf, 15);
679 for (p=0; p<strlen(buf); ++p) {
680 if ( (!isdigit(buf[p]))
681 && ( (buf[p]!='-') || (p!=0) ) )
685 scr_printf("*** Must be no less than %d.\n", imin);
687 scr_printf("*** Must be no more than %d.\n", imax);
688 } while ((i < imin) || (i > imax));
693 * newprompt() - prompt for a string with no existing value
694 * (clears out string buffer first)
696 void newprompt(char *prompt, char *str, int len)
698 color(BRIGHT_MAGENTA);
699 scr_printf("%s", prompt);
707 { /* returns a lower case value */
716 * parse the citadel.rc file
718 void load_command_set(void)
723 struct citcmd *lastcmd = NULL;
728 /* first, set up some defaults for non-required variables */
730 strcpy(editor_path, "");
731 strcpy(printcmd, "");
732 strcpy(rc_username, "");
733 strcpy(rc_password, "");
736 rc_allow_attachments = 0;
737 rc_remember_passwords = 0;
738 strcpy(rc_exp_cmd, "");
739 rc_display_message_numbers = 0;
740 rc_force_mail_prompts = 0;
743 strcpy(rc_url_cmd, "");
744 strcpy(rc_gotmail_cmd, "");
746 rc_encrypt = RC_DEFAULT;
748 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
749 rc_screen = RC_DEFAULT;
751 rc_alt_semantics = 0;
753 /* now try to open the citadel.rc file */
756 if (getenv("HOME") != NULL) {
757 snprintf(buf, sizeof buf, "%s/.citadelrc", getenv("HOME"));
758 ccfile = fopen(buf, "r");
760 if (ccfile == NULL) {
761 snprintf(buf, sizeof buf, "%s/citadel.rc", BBSDIR);
762 ccfile = fopen(buf, "r");
764 if (ccfile == NULL) {
765 ccfile = fopen("/etc/citadel.rc", "r");
767 if (ccfile == NULL) {
768 ccfile = fopen("./citadel.rc", "r");
770 if (ccfile == NULL) {
771 perror("commands: cannot open citadel.rc");
774 while (fgets(buf, sizeof buf, ccfile) != NULL) {
775 while ((strlen(buf) > 0) ? (isspace(buf[strlen(buf) - 1])) : 0)
776 buf[strlen(buf) - 1] = 0;
778 if (!strncasecmp(buf, "encrypt=", 8)) {
779 if (!strcasecmp(&buf[8], "yes")) {
783 fprintf(stderr, "citadel.rc requires encryption support but citadel is not compiled with OpenSSL");
788 else if (!strcasecmp(&buf[8], "no")) {
791 else if (!strcasecmp(&buf[8], "default")) {
792 rc_encrypt = RC_DEFAULT;
797 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
798 if (!strncasecmp(buf, "fullscreen=", 11)) {
799 if (!strcasecmp(&buf[11], "yes"))
801 else if (!strcasecmp(&buf[11], "no"))
806 if (!strncasecmp(buf, "editor=", 7))
807 strcpy(editor_path, &buf[7]);
809 if (!strncasecmp(buf, "printcmd=", 9))
810 strcpy(printcmd, &buf[9]);
812 if (!strncasecmp(buf, "expcmd=", 7))
813 strcpy(rc_exp_cmd, &buf[7]);
815 if (!strncasecmp(buf, "local_screen_dimensions=", 24))
816 have_xterm = (char) atoi(&buf[24]);
818 if (!strncasecmp(buf, "use_floors=", 11)) {
819 if (!strcasecmp(&buf[11], "yes"))
820 rc_floor_mode = RC_YES;
821 if (!strcasecmp(&buf[11], "no"))
822 rc_floor_mode = RC_NO;
823 if (!strcasecmp(&buf[11], "default"))
824 rc_floor_mode = RC_DEFAULT;
826 if (!strncasecmp(buf, "beep=", 5)) {
827 rc_exp_beep = atoi(&buf[5]);
829 if (!strncasecmp(buf, "allow_attachments=", 18)) {
830 rc_allow_attachments = atoi(&buf[18]);
832 if (!strncasecmp(buf, "idle_threshold=", 15)) {
833 rc_idle_threshold = atoi(&buf[15]);
835 if (!strncasecmp(buf, "remember_passwords=", 19)) {
836 rc_remember_passwords = atoi(&buf[19]);
838 if (!strncasecmp(buf, "display_message_numbers=", 24)) {
839 rc_display_message_numbers = atoi(&buf[24]);
841 if (!strncasecmp(buf, "force_mail_prompts=", 19)) {
842 rc_force_mail_prompts = atoi(&buf[19]);
844 if (!strncasecmp(buf, "ansi_color=", 11)) {
845 if (!strncasecmp(&buf[11], "on", 2))
847 if (!strncasecmp(&buf[11], "auto", 4))
848 rc_ansi_color = 2; /* autodetect */
849 if (!strncasecmp(&buf[11], "user", 4))
850 rc_ansi_color = 3; /* user config */
852 if (!strncasecmp(buf, "use_background=", 15)) {
853 if (!strncasecmp(&buf[15], "on", 2))
856 if (!strncasecmp(buf, "prompt_control=", 15)) {
857 if (!strncasecmp(&buf[15], "on", 2))
858 rc_prompt_control = 1;
859 if (!strncasecmp(&buf[15], "user", 4))
860 rc_prompt_control = 3; /* user config */
862 if (!strncasecmp(buf, "username=", 9))
863 strcpy(rc_username, &buf[9]);
865 if (!strncasecmp(buf, "password=", 9))
866 strcpy(rc_password, &buf[9]);
868 if (!strncasecmp(buf, "urlcmd=", 7))
869 strcpy(rc_url_cmd, &buf[7]);
871 if (!strncasecmp(buf, "gotmailcmd=", 11))
872 strcpy(rc_gotmail_cmd, &buf[11]);
874 if (!strncasecmp(buf, "alternate_semantics=", 20)) {
875 if (!strncasecmp(&buf[11], "yes", 3))
876 rc_alt_semantics = 1;
877 if (!strncasecmp(&buf[11], "no", 2))
878 rc_alt_semantics = 0;
880 if (!strncasecmp(buf, "cmd=", 4)) {
881 strcpy(buf, &buf[4]);
883 cptr = (struct citcmd *) malloc(sizeof(struct citcmd));
885 cptr->c_cmdnum = atoi(buf);
886 for (d = strlen(buf); d >= 0; --d)
889 strcpy(buf, &buf[b + 1]);
891 cptr->c_axlevel = atoi(buf);
892 for (d = strlen(buf); d >= 0; --d)
895 strcpy(buf, &buf[b + 1]);
897 for (a = 0; a < 5; ++a)
898 cptr->c_keys[a][0] = 0;
902 buf[strlen(buf) + 1] = 0;
903 while (strlen(buf) > 0) {
905 for (d = strlen(buf); d >= 0; --d)
908 strncpy(cptr->c_keys[a], buf, b);
909 cptr->c_keys[a][b] = 0;
911 strcpy(buf, &buf[b + 1]);
921 lastcmd->next = cptr;
931 * return the key associated with a command
933 char keycmd(char *cmdstr)
937 for (a = 0; a < strlen(cmdstr); ++a)
938 if (cmdstr[a] == '&')
939 return (tolower(cmdstr[a + 1]));
945 * Output the string from a key command without the ampersand
946 * "mode" should be set to 0 for normal or 1 for <C>ommand key highlighting
948 char *cmd_expand(char *strbuf, int mode)
956 for (a = 0; a < strlen(exp); ++a) {
957 if (strbuf[a] == '&') {
960 strcpy(&exp[a], &exp[a + 1]);
964 strcpy(buf, &exp[a + 2]);
970 if (!strncmp(&exp[a], "^r", 2)) {
972 strcpy(&exp[a], room_name);
973 strcat(exp, &buf[a + 2]);
975 if (!strncmp(&exp[a], "^c", 2)) {
977 strcpy(&exp[a + 1], &exp[a + 2]);
987 * Comparison function to determine if entered commands match a
988 * command loaded from the config file.
990 int cmdmatch(char *cmdbuf, struct citcmd *cptr, int ncomp)
1001 for (a = 0; a < ncomp; ++a) {
1002 if ((tolower(cmdbuf[a]) != keycmd(cptr->c_keys[a]))
1003 || (cptr->c_axlevel > cmdax))
1011 * This function returns 1 if a given command requires a string input
1013 int requires_string(struct citcmd *cptr, int ncomp)
1018 strcpy(buf, cptr->c_keys[ncomp - 1]);
1019 for (a = 0; a < strlen(buf); ++a) {
1028 * Input a command at the main prompt.
1029 * This function returns an integer command number. If the command prompts
1030 * for a string then it is placed in the supplied buffer.
1032 int getcmd(CtdlIPC *ipc, char *argbuf)
1041 struct citcmd *cptr;
1044 * Starting a new command now, so set sigcaught to 0. This variable
1045 * is set to nonzero (usually NEXT_KEY or STOP_KEY) if a command has
1046 * been interrupted by a keypress.
1050 /* Switch color support on or off if we're in user mode */
1051 if (rc_ansi_color == 3) {
1052 if (userflags & US_COLOR)
1057 /* if we're running in idiot mode, display a cute little menu */
1058 IFNEXPERT formout(ipc, "mainmenu");
1063 for (a = 0; a < 5; ++a)
1065 /* now the room prompt... */
1066 ok_to_interrupt = 1;
1067 color(BRIGHT_WHITE);
1068 scr_printf("\n%s", room_name);
1070 scr_printf("%c ", room_prompt(room_flags));
1075 ok_to_interrupt = 0;
1077 /* Handle the backspace key, but only if there's something
1078 * to backspace over...
1080 if ((ch == 8) && (cmdpos > 0)) {
1081 back(cmdspaces[cmdpos - 1] + 1);
1085 /* Spacebar invokes "lazy traversal" commands */
1086 if ((ch == 32) && (cmdpos == 0)) {
1087 this_lazy_cmd = next_lazy_cmd;
1088 if (this_lazy_cmd == 13)
1090 if (this_lazy_cmd == 5)
1092 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1093 if (cptr->c_cmdnum == this_lazy_cmd) {
1094 for (a = 0; a < 5; ++a)
1095 if (cptr->c_keys[a][0] != 0)
1096 scr_printf("%s ", cmd_expand(
1097 cptr->c_keys[a], 0));
1099 return (this_lazy_cmd);
1103 return (this_lazy_cmd);
1105 /* Otherwise, process the command */
1106 cmdbuf[cmdpos] = tolower(ch);
1108 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1109 if (cmdmatch(cmdbuf, cptr, cmdpos + 1)) {
1111 scr_printf("%s", cmd_expand(cptr->c_keys[cmdpos], 0));
1112 cmdspaces[cmdpos] = strlen(
1113 cmd_expand(cptr->c_keys[cmdpos], 0));
1115 if ((cptr->c_keys[cmdpos + 1]) != 0)
1121 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1122 if (cmdmatch(cmdbuf, cptr, 5)) {
1123 /* We've found our command. */
1124 if (requires_string(cptr, cmdpos)) {
1125 getline(argbuf, 32);
1130 /* If this command is one that changes rooms,
1131 * then the next lazy-command (space bar)
1132 * should be "read new" instead of "goto"
1134 if ((cptr->c_cmdnum == 5)
1135 || (cptr->c_cmdnum == 6)
1136 || (cptr->c_cmdnum == 47)
1137 || (cptr->c_cmdnum == 52)
1138 || (cptr->c_cmdnum == 16)
1139 || (cptr->c_cmdnum == 20))
1142 return (cptr->c_cmdnum);
1148 pprintf("\rOne of ... \n");
1149 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1150 if (cmdmatch(cmdbuf, cptr, cmdpos)) {
1151 for (a = 0; a < 5; ++a) {
1152 pprintf("%s ", cmd_expand(cptr->c_keys[a], 1));
1158 pprintf("\n%s%c ", room_name, room_prompt(room_flags));
1160 for (cptr = cmdlist; cptr != NULL; cptr = cptr->next) {
1161 if ((got == 0) && (cmdmatch(cmdbuf, cptr, cmdpos))) {
1162 for (a = 0; a < cmdpos; ++a) {
1164 cmd_expand(cptr->c_keys[a], 0));
1179 * set tty modes. commands are:
1181 * 01- set to bbs mode
1182 * 2 - save current settings for later restoral
1183 * 3 - restore saved settings
1185 #ifdef HAVE_TERMIOS_H
1186 void sttybbs(int cmd)
1187 { /* SysV version of sttybbs() */
1188 struct termios live;
1189 static struct termios saved_settings;
1190 static int last_cmd = 0;
1197 if ((cmd == 0) || (cmd == 1)) {
1198 tcgetattr(0, &live);
1199 live.c_iflag = ISTRIP | IXON | IXANY;
1200 live.c_oflag = OPOST | ONLCR;
1201 live.c_lflag = ISIG | NOFLSH;
1203 live.c_cc[VINTR] = 0;
1204 live.c_cc[VQUIT] = 0;
1207 live.c_cc[VMIN] = 0;
1208 live.c_cc[VTIME] = 0;
1211 /* do we even need this stuff anymore? */
1212 /* live.c_line=0; */
1213 live.c_cc[VERASE] = 8;
1214 live.c_cc[VKILL] = 24;
1215 live.c_cc[VEOF] = 1;
1216 live.c_cc[VEOL] = 255;
1217 live.c_cc[VEOL2] = 0;
1218 live.c_cc[VSTART] = 0;
1219 tcsetattr(0, TCSADRAIN, &live);
1222 tcgetattr(0, &saved_settings);
1225 tcsetattr(0, TCSADRAIN, &saved_settings);
1230 void sttybbs(int cmd)
1231 { /* BSD version of sttybbs() */
1233 static struct sgttyb saved_settings;
1234 static int last_cmd = 0;
1241 if ((cmd == 0) || (cmd == 1)) {
1243 live.sg_flags |= CBREAK;
1244 live.sg_flags |= CRMOD;
1245 live.sg_flags |= NL1;
1246 live.sg_flags &= ~ECHO;
1248 live.sg_flags |= NOFLSH;
1252 gtty(0, &saved_settings);
1255 stty(0, &saved_settings);
1262 * display_help() - help file viewer
1264 void display_help(CtdlIPC *ipc, char *name)
1271 * fmout() - Citadel text formatter and paginator
1274 int width, /* screen width to use */
1275 FILE *fpin, /* file to read from, or NULL to format given text */
1276 char *text, /* Text to be formatted (when fpin is NULL) */
1277 FILE *fpout, /* File to write to, or NULL to write to screen */
1278 char pagin, /* nonzero if we should use the paginator */
1279 int height, /* screen height to use */
1280 int starting_lp,/* starting value for lines_printed, -1 for global */
1281 int subst) /* nonzero if we should use hypertext mode */
1290 num_urls = 0; /* Start with a clean slate of embedded URL's */
1292 if (starting_lp >= 0) {
1293 lines_printed = starting_lp;
1298 c = 1; /* c is the current pos */
1299 e = text; /* e is pointer to current pos */
1301 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1302 if (fpin != NULL) { /* read from file */
1305 if (eof_flag == 0) {
1307 buffer[strlen(buffer) + 1] = 0;
1308 buffer[strlen(buffer)] = a;
1310 } else { /* read from text */
1313 while (isspace(buffer[strlen(buffer) - 1]))
1314 buffer[strlen(buffer) - 1] = 0;
1315 buffer[strlen(buffer) + 1] = 0;
1316 buffer[strlen(buffer)] = 10;
1318 if (eof_flag == 0) {
1320 buffer[strlen(buffer) + 1] = 0;
1321 buffer[strlen(buffer)] = a;
1326 if ( (!strncasecmp(buffer, "http://", 7))
1327 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1328 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1329 for (a=0; a<strlen(urls[num_urls]); ++a) {
1330 b = urls[num_urls][a];
1331 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1332 || (b==13) || (b==9) || (b=='\"') )
1333 urls[num_urls][a] = 0;
1338 buffer[strlen(buffer) + 1] = 0;
1340 strcpy(buffer, &buffer[1]);
1347 if (a == 10) scr_printf("<a==10>");
1348 if (a == 13) scr_printf("<a==13>");
1350 if ((a == 13) || (a == 10)) {
1351 if ((old != 13) && (old != 10))
1353 if ((old == 13) || (old == 10))
1356 if (((old == 13) || (old == 10)) && (isspace(real))) {
1358 fprintf(fpout, "\n");
1362 lines_printed = checkpagin(lines_printed, pagin, height);
1370 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1372 fprintf(fpout, "\n%s", aaa);
1374 scr_printf("\n%s", aaa);
1376 lines_printed = checkpagin(lines_printed, pagin, height);
1386 if ((strlen(aaa) + c) > (width - 1)) {
1389 fprintf(fpout, "\n");
1393 lines_printed = checkpagin(lines_printed, pagin, height);
1397 fprintf(fpout, "%s ", aaa);
1399 scr_printf("%s ", aaa);
1402 c = c + strlen(aaa);
1406 if ((a == 13) || (a == 10)) {
1408 fprintf(fpout, "%s\n", aaa);
1410 scr_printf("%s\n", aaa);
1412 lines_printed = checkpagin(lines_printed, pagin, height);
1415 if (sigcaught) goto FMTEND;
1421 /* keypress caught; drain the server */
1424 fprintf(fpout, "\n");
1428 lines_printed = checkpagin(lines_printed, pagin, height);
1435 * fmout() - Citadel text formatter and paginator
1438 int width, /* screen width to use */
1439 FILE *fpin, /* file to read from, or NULL to format given text */
1440 char *text, /* text to be formatted (when fpin is NULL */
1441 FILE *fpout, /* file to write to, or NULL to write to screen */
1442 char pagin, /* nonzero if we should use the paginator */
1443 int height, /* screen height to use */
1444 int starting_lp,/* starting value for lines_printed, -1 for global */
1445 int subst) /* nonzero if we should use hypertext mode */
1447 char *buffer = NULL; /* The current message */
1448 char *word = NULL; /* What we are about to actually print */
1449 char *e; /* Pointer to position in text */
1450 char old = 0; /* The previous character */
1451 int column = 0; /* Current column */
1452 size_t i; /* Generic counter */
1454 num_urls = 0; /* Start with a clean slate of embedded URL's */
1456 word = (char *)calloc(1, width);
1458 err_printf("Can't alloc memory to print message: %s!\n",
1466 fseek(fpin, 0, SEEK_END);
1469 buffer = (char *)calloc(1, i + 1);
1471 err_printf("Can't alloc memory for message: %s!\n",
1478 g = fread(buffer + got, i - got, 1, fpin);
1479 if (g < 1) { /* error reading or eof */
1491 if (starting_lp >= 0)
1492 lines_printed = starting_lp;
1495 /* First, are we looking at a newline? */
1500 fprintf(fpout, "\n");
1504 lines_printed = checkpagin(lines_printed, pagin, height);
1507 } else if (old != ' ') {
1509 fprintf(fpout, " ");
1517 /* Or are we looking at a space? */
1520 if (column >= width - 1) {
1522 fprintf(fpout, "\n");
1526 lines_printed = checkpagin(lines_printed, pagin, height);
1529 } else if (!(column == 0 && old == ' ')) {
1531 fprintf(fpout, " ");
1540 /* Read a word, slightly messy */
1543 if (!isprint(e[i]) && !isspace(e[i]))
1550 /* We should never see these, but... slightly messy */
1551 if (e[i] == '\t' || e[i] == '\f' || e[i] == '\v')
1555 * Check for and copy URLs
1556 * We will get the entire URL even if it's longer than the
1557 * screen width, as long as the server didn't break it up
1559 if (!strncasecmp(e, "http://", 7) ||
1560 !strncasecmp(e, "ftp://", 6)) {
1563 strncpy(urls[num_urls], e, i);
1564 urls[num_urls][i] = 0;
1565 for (j = 0; j < strlen(e); j++) {
1568 c = urls[num_urls][j];
1569 if (c == '>' || c == '\"' || c == ')' ||
1570 c == ' ' || c == '\n') {
1571 urls[num_urls][j] = 0;
1578 /* Break up really long words */
1579 /* TODO: auto-hyphenation someday? */
1582 strncpy(word, e, i);
1585 /* Decide where to print the word */
1586 if (column + i >= width) {
1588 fprintf(fpout, "\n");
1592 lines_printed = checkpagin(lines_printed, pagin, height);
1597 /* Print the word */
1599 fprintf(fpout, "%s", word);
1601 scr_printf("%s", word);
1604 e += i; /* Start with the whitepsace! */
1608 if (fpin) /* We allocated this, remember? */
1615 * support ANSI color if defined
1617 void color(int colornum)
1619 static int hold_color;
1620 static int current_color;
1622 if (colornum == COLOR_PUSH) {
1623 hold_color = current_color;
1627 if (colornum == COLOR_POP) {
1632 current_color = colornum;
1634 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
1635 if (scr_color(colornum))
1638 /* When switching to dim white, actually output an 'original
1639 * pair' sequence -- this looks better on black-on-white
1640 * terminals. - Changed to ORIGINAL_PAIR as this actually
1641 * wound up looking horrible on black-on-white terminals, not
1642 * to mention transparent terminals.
1644 if (colornum == ORIGINAL_PAIR)
1645 printf("\033[0;39;49m");
1647 printf("\033[%d;3%d;4%dm",
1648 (colornum & 8) ? 1 : 0,
1656 void cls(int colornum)
1659 printf("\033[4%dm\033[2J\033[H\033[0m",
1660 colornum ? colornum : rc_color_use_bg);
1667 * Detect whether ANSI color is available (answerback)
1669 void send_ansi_detect(void)
1671 if (rc_ansi_color == 2) {
1678 void look_for_ansi(void)
1686 if (rc_ansi_color == 0) {
1688 } else if (rc_ansi_color == 1) {
1690 } else if (rc_ansi_color == 2) {
1692 /* otherwise, do the auto-detect */
1697 if ((now - AnsiDetect) < 2)
1706 select(1, &rfds, NULL, NULL, &tv);
1707 if (FD_ISSET(0, &rfds)) {
1708 abuf[strlen(abuf) + 1] = 0;
1709 read(0, &abuf[strlen(abuf)], 1);
1711 } while (FD_ISSET(0, &rfds));
1713 for (a = 0; a < strlen(abuf); ++a) {
1714 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1715 && (abuf[a + 2] == '?')) {
1724 * Display key options (highlight hotkeys inside angle brackets)
1726 void keyopt(char *buf) {
1730 for (i=0; i<strlen(buf); ++i) {
1733 color(BRIGHT_MAGENTA);
1747 * Present a key-menu line choice type of thing
1749 char keymenu(char *menuprompt, char *menustring) {
1755 int display_prompt = 1;
1757 choices = num_tokens(menustring, '|');
1759 if (menuprompt != NULL) do_prompt = 1;
1760 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1763 if (display_prompt) {
1765 scr_printf("%s ", menuprompt);
1768 for (i=0; i<choices; ++i) {
1769 extract(buf, menustring, i);
1779 if ( (do_prompt) && (ch=='?') ) {
1780 scr_printf("\rOne of... ");
1782 for (i=0; i<choices; ++i) {
1783 extract(buf, menustring, i);
1792 for (i=0; i<choices; ++i) {
1793 extract(buf, menustring, i);
1794 for (c=1; c<strlen(buf); ++c) {
1795 if ( (ch == tolower(buf[c]))
1797 && (buf[c+1]=='>') ) {
1798 for (a=0; a<strlen(buf); ++a) {
1799 if ( (a!=(c-1)) && (a!=(c+1))) {