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);
1223 void sttybbs(int cmd)
1224 { /* BSD version of sttybbs() */
1226 static struct sgttyb saved_settings;
1227 static int last_cmd = 0;
1234 if ((cmd == 0) || (cmd == 1)) {
1236 live.sg_flags |= CBREAK;
1237 live.sg_flags |= CRMOD;
1238 live.sg_flags |= NL1;
1239 live.sg_flags &= ~ECHO;
1241 live.sg_flags |= NOFLSH;
1245 gtty(0, &saved_settings);
1248 stty(0, &saved_settings);
1255 * display_help() - help file viewer
1257 void display_help(CtdlIPC *ipc, char *name)
1264 * fmout() - Citadel text formatter and paginator
1267 int width, /* screen width to use */
1268 FILE *fpin, /* file to read from, or NULL to format given text */
1269 char *text, /* Text to be formatted (when fpin is NULL) */
1270 FILE *fpout, /* File to write to, or NULL to write to screen */
1271 char pagin, /* nonzero if we should use the paginator */
1272 int height, /* screen height to use */
1273 int starting_lp,/* starting value for lines_printed, -1 for global */
1274 int subst) /* nonzero if we should use hypertext mode */
1283 num_urls = 0; /* Start with a clean slate of embedded URL's */
1285 if (starting_lp >= 0) {
1286 lines_printed = starting_lp;
1291 c = 1; /* c is the current pos */
1292 e = text; /* e is pointer to current pos */
1294 FMTA: while ((eof_flag == 0) && (strlen(buffer) < 126)) {
1295 if (fpin != NULL) { /* read from file */
1298 if (eof_flag == 0) {
1300 buffer[strlen(buffer) + 1] = 0;
1301 buffer[strlen(buffer)] = a;
1303 } else { /* read from text */
1306 while (isspace(buffer[strlen(buffer) - 1]))
1307 buffer[strlen(buffer) - 1] = 0;
1308 buffer[strlen(buffer) + 1] = 0;
1309 buffer[strlen(buffer)] = 10;
1311 if (eof_flag == 0) {
1313 buffer[strlen(buffer) + 1] = 0;
1314 buffer[strlen(buffer)] = a;
1319 if ( (!strncasecmp(buffer, "http://", 7))
1320 || (!strncasecmp(buffer, "ftp://", 6)) ) {
1321 safestrncpy(urls[num_urls], buffer, (SIZ-1));
1322 for (a=0; a<strlen(urls[num_urls]); ++a) {
1323 b = urls[num_urls][a];
1324 if ( (b==' ') || (b==')') || (b=='>') || (b==10)
1325 || (b==13) || (b==9) || (b=='\"') )
1326 urls[num_urls][a] = 0;
1331 buffer[strlen(buffer) + 1] = 0;
1333 strcpy(buffer, &buffer[1]);
1340 if (((a == 13) || (a == 10)) && (old != 13) && (old != 10))
1342 if (((old == 13) || (old == 10)) && (isspace(real))) {
1344 fprintf(fpout, "\n");
1348 lines_printed = checkpagin(lines_printed, pagin, height);
1356 if (((strlen(aaa) + c) > (width - 1)) && (strlen(aaa) > (width - 1))) {
1358 fprintf(fpout, "\n%s", aaa);
1360 scr_printf("\n%s", aaa);
1362 lines_printed = checkpagin(lines_printed, pagin, height);
1372 if ((strlen(aaa) + c) > (width - 1)) {
1375 fprintf(fpout, "\n");
1379 lines_printed = checkpagin(lines_printed, pagin, height);
1383 fprintf(fpout, "%s ", aaa);
1385 scr_printf("%s ", aaa);
1388 c = c + strlen(aaa);
1392 if ((a == 13) || (a == 10)) {
1394 fprintf(fpout, "%s\n", aaa);
1396 scr_printf("%s\n", aaa);
1398 lines_printed = checkpagin(lines_printed, pagin, height);
1401 if (sigcaught) goto OOPS;
1407 /* keypress caught; drain the server */
1409 CtdlIPC_getline(ipc, aaa);
1410 } while (strcmp(aaa, "000")); */
1414 fprintf(fpout, "\n");
1418 lines_printed = checkpagin(lines_printed, pagin, height);
1425 * support ANSI color if defined
1427 void color(int colornum)
1429 static int is_bold = 0;
1430 static int hold_color, current_color;
1432 if (colornum == COLOR_PUSH) {
1433 hold_color = current_color;
1437 if (colornum == COLOR_POP) {
1442 current_color = colornum;
1444 #if defined(HAVE_CURSES_H) && !defined(DISABLE_CURSES)
1445 if (scr_color(colornum))
1448 /* When switching to dim white, actually output an 'original
1449 * pair' sequence -- this looks better on black-on-white
1450 * terminals. - Changed to ORIGINAL_PAIR as this actually
1451 * wound up looking horrible on black-on-white terminals, not
1452 * to mention transparent terminals.
1454 if (colornum == ORIGINAL_PAIR)
1455 printf("\033[39;49m");
1457 printf("\033[3%d;40m", (colornum & 7));
1459 if ((colornum >= 8) && (is_bold == 0)) {
1462 } else if ((colornum < 8) && (is_bold == 1)) {
1470 void cls(int colornum)
1473 printf("\033[4%dm\033[2J\033[H\033[0m", colornum);
1480 * Detect whether ANSI color is available (answerback)
1482 void send_ansi_detect(void)
1484 if (rc_ansi_color == 2) {
1491 void look_for_ansi(void)
1499 if (rc_ansi_color == 0) {
1501 } else if (rc_ansi_color == 1) {
1503 } else if (rc_ansi_color == 2) {
1505 /* otherwise, do the auto-detect */
1510 if ((now - AnsiDetect) < 2)
1519 select(1, &rfds, NULL, NULL, &tv);
1520 if (FD_ISSET(0, &rfds)) {
1521 abuf[strlen(abuf) + 1] = 0;
1522 read(0, &abuf[strlen(abuf)], 1);
1524 } while (FD_ISSET(0, &rfds));
1526 for (a = 0; a < strlen(abuf); ++a) {
1527 if ((abuf[a] == 27) && (abuf[a + 1] == '[')
1528 && (abuf[a + 2] == '?')) {
1537 * Display key options (highlight hotkeys inside angle brackets)
1539 void keyopt(char *buf) {
1543 for (i=0; i<strlen(buf); ++i) {
1546 color(BRIGHT_MAGENTA);
1560 * Present a key-menu line choice type of thing
1562 char keymenu(char *menuprompt, char *menustring) {
1568 int display_prompt = 1;
1570 choices = num_tokens(menustring, '|');
1572 if (menuprompt != NULL) do_prompt = 1;
1573 if (menuprompt != NULL) if (strlen(menuprompt)==0) do_prompt = 0;
1576 if (display_prompt) {
1578 scr_printf("%s ", menuprompt);
1581 for (i=0; i<choices; ++i) {
1582 extract(buf, menustring, i);
1592 if ( (do_prompt) && (ch=='?') ) {
1593 scr_printf("\rOne of... ");
1595 for (i=0; i<choices; ++i) {
1596 extract(buf, menustring, i);
1605 for (i=0; i<choices; ++i) {
1606 extract(buf, menustring, i);
1607 for (c=1; c<strlen(buf); ++c) {
1608 if ( (ch == tolower(buf[c]))
1610 && (buf[c+1]=='>') ) {
1611 for (a=0; a<strlen(buf); ++a) {
1612 if ( (a!=(c-1)) && (a!=(c+1))) {