diff options
Diffstat (limited to 'client')
-rw-r--r-- | client/Makefile | 8 | ||||
-rw-r--r-- | client/birdc.c | 223 | ||||
-rw-r--r-- | client/birdcl.c | 160 | ||||
-rw-r--r-- | client/client.c | 333 | ||||
-rw-r--r-- | client/client.h | 20 |
5 files changed, 508 insertions, 236 deletions
diff --git a/client/Makefile b/client/Makefile index 867476cc..a1578766 100644 --- a/client/Makefile +++ b/client/Makefile @@ -1,5 +1,11 @@ -source=client.c commands.c util.c +source=commands.c util.c client.c root-rel=../ dir-name=client +clients := $(client) birdcl + +source-dep := $(source) $(addsuffix .c,$(clients)) + +subdir: $(addsuffix .o,$(clients)) + include ../Rules diff --git a/client/birdc.c b/client/birdc.c new file mode 100644 index 00000000..bbe18331 --- /dev/null +++ b/client/birdc.c @@ -0,0 +1,223 @@ +/* + * BIRD Client - Readline variant I/O + * + * (c) 1999--2004 Martin Mares <mj@ucw.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <termios.h> + +#include <readline/readline.h> +#include <readline/history.h> +#include <curses.h> + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +static int input_hidden_end; +static int prompt_active; + +/*** Input ***/ + +/* HACK: libreadline internals we need to access */ +extern int _rl_vis_botlin; +extern void _rl_move_vert(int); +extern Function *rl_last_func; + +static void +add_history_dedup(char *cmd) +{ + /* Add history line if it differs from the last one */ + HIST_ENTRY *he = history_get(history_length); + if (!he || strcmp(he->line, cmd)) + add_history(cmd); +} + +static void +input_got_line(char *cmd_buffer) +{ + if (!cmd_buffer) + { + cleanup(); + exit(0); + } + + if (cmd_buffer[0]) + { + add_history_dedup(cmd_buffer); + submit_command(cmd_buffer); + } + + free(cmd_buffer); +} + +void +input_start_list(void) +{ + /* Leave the currently edited line and make space for listing */ + _rl_move_vert(_rl_vis_botlin); +#ifdef HAVE_RL_CRLF + rl_crlf(); +#endif +} + +void +input_stop_list(void) +{ + /* Reprint the currently edited line after listing */ + rl_on_new_line(); + rl_redisplay(); +} + +static int +input_complete(int arg UNUSED, int key UNUSED) +{ + static int complete_flag; + char buf[256]; + + if (rl_last_func != input_complete) + complete_flag = 0; + switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) + { + case 0: + complete_flag = 1; + break; + case 1: + rl_insert_text(buf); + break; + default: + complete_flag = 1; +#ifdef HAVE_RL_DING + rl_ding(); +#endif + } + return 0; +} + +static int +input_help(int arg, int key UNUSED) +{ + int i, in_string, in_bracket; + + if (arg != 1) + return rl_insert(arg, '?'); + + in_string = in_bracket = 0; + for (i = 0; i < rl_point; i++) + { + + if (rl_line_buffer[i] == '"') + in_string = ! in_string; + else if (! in_string) + { + if (rl_line_buffer[i] == '[') + in_bracket++; + else if (rl_line_buffer[i] == ']') + in_bracket--; + } + } + + /* `?' inside string or path -> insert */ + if (in_string || in_bracket) + return rl_insert(1, '?'); + + rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ + rl_insert_text("?"); + rl_redisplay(); + rl_end_undo_group(); + input_start_list(); + cmd_help(rl_line_buffer, rl_point); + rl_undo_command(1, 0); + input_stop_list(); + return 0; +} + +void +input_init(void) +{ + rl_readline_name = "birdc"; + rl_add_defun("bird-complete", input_complete, '\t'); + rl_add_defun("bird-help", input_help, '?'); + rl_callback_handler_install("bird> ", input_got_line); + + // rl_get_screen_size(); + term_lns = LINES; + term_cls = COLS; + + prompt_active = 1; + + // readline library does strange things when stdin is nonblocking. + // if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) + // die("fcntl: %m"); +} + +static void +input_reveal(void) +{ + /* need this, otherwise some lib seems to eat pending output when + the prompt is displayed */ + fflush(stdout); + tcdrain(STDOUT_FILENO); + + rl_end = input_hidden_end; + rl_expand_prompt("bird> "); + rl_forced_update_display(); + + prompt_active = 1; +} + +static void +input_hide(void) +{ + input_hidden_end = rl_end; + rl_end = 0; + rl_expand_prompt(""); + rl_redisplay(); + + prompt_active = 0; +} + +void +input_notify(int prompt) +{ + if (prompt == prompt_active) + return; + + if (prompt) + input_reveal(); + else + input_hide(); +} + +void +input_read(void) +{ + rl_callback_read_char(); +} + +void +more_begin(void) +{ +} + +void +more_end(void) +{ +} + +void +cleanup(void) +{ + if (init) + return; + + input_hide(); + rl_callback_handler_remove(); +} diff --git a/client/birdcl.c b/client/birdcl.c new file mode 100644 index 00000000..2d5e1067 --- /dev/null +++ b/client/birdcl.c @@ -0,0 +1,160 @@ +/* + * BIRD Client - Light variant I/O + * + * (c) 1999--2004 Martin Mares <mj@ucw.cz> + * (c) 2013 Tomas Hlavacek <tomas.hlavacek@nic.cz> + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <termios.h> + +#include <sys/ioctl.h> +#include <signal.h> + +#include "nest/bird.h" +#include "lib/resource.h" +#include "lib/string.h" +#include "client/client.h" +#include "sysdep/unix/unix.h" + +#define INPUT_BUF_LEN 2048 + +struct termios tty_save; + +void +input_start_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_stop_list(void) +{ + /* Empty in non-ncurses version. */ +} + +void +input_notify(int prompt) +{ + /* No ncurses -> no status to reveal/hide, print prompt manually. */ + if (!prompt) + return; + + printf("bird> "); + fflush(stdout); +} + + +static int +lastnb(char *str, int i) +{ + while (i--) + if ((str[i] != ' ') && (str[i] != '\t')) + return str[i]; + + return 0; +} + +void +input_read(void) +{ + char buf[INPUT_BUF_LEN]; + + if ((fgets(buf, INPUT_BUF_LEN, stdin) == NULL) || (buf[0] == 0)) + { + putchar('\n'); + cleanup(); + exit(0); + } + + int l = strlen(buf); + if ((l+1) == INPUT_BUF_LEN) + { + printf("Input too long.\n"); + return; + } + + if (buf[l-1] == '\n') + buf[--l] = '\0'; + + if (!interactive) + printf("%s\n", buf); + + if (l == 0) + return; + + if (lastnb(buf, l) == '?') + { + cmd_help(buf, strlen(buf)); + return; + } + + submit_command(buf); +} + +static struct termios stored_tty; +static int more_active = 0; + +void +more_begin(void) +{ + static struct termios tty; + + tty = stored_tty; + tty.c_lflag &= (~ECHO); + tty.c_lflag &= (~ICANON); + + if (tcsetattr (0, TCSANOW, &tty) < 0) + die("tcsetattr: %m"); + + more_active = 1; +} + +void +more_end(void) +{ + more_active = 0; + + if (tcsetattr (0, TCSANOW, &stored_tty) < 0) + die("tcsetattr: %m"); +} + +static void +sig_handler(int signal) +{ + cleanup(); + exit(0); +} + +void +input_init(void) +{ + if (!interactive) + return; + + if (tcgetattr(0, &stored_tty) < 0) + die("tcgetattr: %m"); + + if (signal(SIGINT, sig_handler) == SIG_IGN) + signal(SIGINT, SIG_IGN); + if (signal(SIGTERM, sig_handler) == SIG_IGN) + signal(SIGTERM, SIG_IGN); + + struct winsize tws; + if (ioctl(0, TIOCGWINSZ, &tws) == 0) + { + term_lns = tws.ws_row; + term_cls = tws.ws_col; + } +} + +void +cleanup(void) +{ + if (more_active) + more_end(); +} diff --git a/client/client.c b/client/client.c index 8711cf0a..a9d0096d 100644 --- a/client/client.c +++ b/client/client.c @@ -2,22 +2,32 @@ * BIRD Client * * (c) 1999--2004 Martin Mares <mj@ucw.cz> + * (c) 2013 Tomas Hlavacek <tmshlvck@gmail.com> * * Can be freely distributed and used under the terms of the GNU GPL. */ +/** + * DOC: BIRD client + * + * There are two variants of BIRD client: regular and light. regular + * variant depends on readline and ncurses libraries, while light + * variant uses just libc. Most of the code and the main() is common + * for both variants (in client.c file) and just a few functions are + * different (in birdc.c for regular and birdcl.c for light). Two + * binaries are generated by linking common object files like client.o + * (which is compiled from client.c just once) with either birdc.o or + * birdcl.o for each variant. + */ + #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <unistd.h> -#include <termios.h> #include <errno.h> #include <sys/socket.h> #include <sys/un.h> #include <sys/types.h> -#include <readline/readline.h> -#include <readline/history.h> -#include <curses.h> #include "nest/bird.h" #include "lib/resource.h" @@ -25,33 +35,31 @@ #include "client/client.h" #include "sysdep/unix/unix.h" +#define SERVER_READ_BUF_LEN 4096 + static char *opt_list = "s:vr"; -static int verbose; +static int verbose, restricted, once; static char *init_cmd; -static int once; static char *server_path = PATH_CONTROL_SOCKET; static int server_fd; -static byte server_read_buf[4096]; +static byte server_read_buf[SERVER_READ_BUF_LEN]; static byte *server_read_pos = server_read_buf; -#define STATE_PROMPT 0 -#define STATE_CMD_SERVER 1 -#define STATE_CMD_USER 2 +int init = 1; /* During intial sequence */ +int busy = 1; /* Executing BIRD command */ +int interactive; /* Whether stdin is terminal */ -static int input_initialized; -static int input_hidden_end; -static int cstate = STATE_CMD_SERVER; -static int nstate = STATE_CMD_SERVER; +static int num_lines, skip_input; +int term_lns, term_cls; -static int num_lines, skip_input, interactive; /*** Parsing of arguments ***/ static void -usage(void) +usage(char *name) { - fprintf(stderr, "Usage: birdc [-s <control-socket>] [-v] [-r]\n"); + fprintf(stderr, "Usage: %s [-s <control-socket>] [-v] [-r]\n", name); exit(1); } @@ -70,10 +78,10 @@ parse_args(int argc, char **argv) verbose++; break; case 'r': - init_cmd = "restrict"; + restricted = 1; break; default: - usage(); + usage(argv[0]); } /* If some arguments are not options, we take it as commands */ @@ -83,9 +91,6 @@ parse_args(int argc, char **argv) int i; int len = 0; - if (init_cmd) - usage(); - for (i = optind; i < argc; i++) len += strlen(argv[i]) + 1; @@ -99,17 +104,14 @@ parse_args(int argc, char **argv) tmp[-1] = 0; once = 1; + interactive = 0; } } -/*** Input ***/ -static void server_send(char *); +/*** Input ***/ -/* HACK: libreadline internals we need to access */ -extern int _rl_vis_botlin; -extern void _rl_move_vert(int); -extern Function *rl_last_func; +static void server_send(char *cmd); static int handle_internal_command(char *cmd) @@ -127,181 +129,37 @@ handle_internal_command(char *cmd) return 0; } -void +static void submit_server_command(char *cmd) { - server_send(cmd); - nstate = STATE_CMD_SERVER; + busy = 1; num_lines = 2; -} - -static void -add_history_dedup(char *cmd) -{ - /* Add history line if it differs from the last one */ - HIST_ENTRY *he = history_get(history_length); - if (!he || strcmp(he->line, cmd)) - add_history(cmd); -} - -static void -got_line(char *cmd_buffer) -{ - char *cmd; - - if (!cmd_buffer) - { - cleanup(); - exit(0); - } - if (cmd_buffer[0]) - { - cmd = cmd_expand(cmd_buffer); - if (cmd) - { - add_history_dedup(cmd); - - if (!handle_internal_command(cmd)) - submit_server_command(cmd); - - free(cmd); - } - else - add_history_dedup(cmd_buffer); - } - free(cmd_buffer); -} - -void -input_start_list(void) /* Leave the currently edited line and make space for listing */ -{ - _rl_move_vert(_rl_vis_botlin); -#ifdef HAVE_RL_CRLF - rl_crlf(); -#endif + server_send(cmd); } void -input_stop_list(void) /* Reprint the currently edited line after listing */ -{ - rl_on_new_line(); - rl_redisplay(); -} - -static int -input_complete(int arg UNUSED, int key UNUSED) +submit_command(char *cmd_raw) { - static int complete_flag; - char buf[256]; + char *cmd = cmd_expand(cmd_raw); - if (rl_last_func != input_complete) - complete_flag = 0; - switch (cmd_complete(rl_line_buffer, rl_point, buf, complete_flag)) - { - case 0: - complete_flag = 1; - break; - case 1: - rl_insert_text(buf); - break; - default: - complete_flag = 1; -#ifdef HAVE_RL_DING - rl_ding(); -#endif - } - return 0; -} - -static int -input_help(int arg, int key UNUSED) -{ - int i, in_string, in_bracket; - - if (arg != 1) - return rl_insert(arg, '?'); - - in_string = in_bracket = 0; - for (i = 0; i < rl_point; i++) - { - - if (rl_line_buffer[i] == '"') - in_string = ! in_string; - else if (! in_string) - { - if (rl_line_buffer[i] == '[') - in_bracket++; - else if (rl_line_buffer[i] == ']') - in_bracket--; - } - } + if (!cmd) + return; - /* `?' inside string or path -> insert */ - if (in_string || in_bracket) - return rl_insert(1, '?'); - - rl_begin_undo_group(); /* HACK: We want to display `?' at point position */ - rl_insert_text("?"); - rl_redisplay(); - rl_end_undo_group(); - input_start_list(); - cmd_help(rl_line_buffer, rl_point); - rl_undo_command(1, 0); - input_stop_list(); - return 0; -} + if (!handle_internal_command(cmd)) + submit_server_command(cmd); -static void -input_init(void) -{ - rl_readline_name = "birdc"; - rl_add_defun("bird-complete", input_complete, '\t'); - rl_add_defun("bird-help", input_help, '?'); - rl_callback_handler_install("bird> ", got_line); - input_initialized = 1; -// readline library does strange things when stdin is nonblocking. -// if (fcntl(0, F_SETFL, O_NONBLOCK) < 0) -// die("fcntl: %m"); -} - -static void -input_hide(void) -{ - input_hidden_end = rl_end; - rl_end = 0; - rl_expand_prompt(""); - rl_redisplay(); + free(cmd); } static void -input_reveal(void) -{ - /* need this, otherwise some lib seems to eat pending output when - the prompt is displayed */ - fflush(stdout); - tcdrain(fileno(stdout)); - - rl_end = input_hidden_end; - rl_expand_prompt("bird> "); - rl_forced_update_display(); -} - -void -cleanup(void) +init_commands(void) { - if (input_initialized) + if (restricted) { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); + submit_server_command("restrict"); + restricted = 0; + return; } -} - -void -update_state(void) -{ - if (nstate == cstate) - return; if (init_cmd) { @@ -312,40 +170,39 @@ update_state(void) return; } - if (!init_cmd && once) + if (once) { /* Initial command is finished and we want to exit */ cleanup(); exit(0); } - if (nstate == STATE_PROMPT) - { - if (input_initialized) - input_reveal(); - else - input_init(); - } + input_init(); - if (nstate != STATE_PROMPT) - input_hide(); + term_lns = (term_lns > 0) ? term_lns : 25; + term_cls = (term_cls > 0) ? term_cls : 80; - cstate = nstate; + init = 0; } + +/*** Output ***/ + void more(void) { + more_begin(); printf("--More--\015"); fflush(stdout); redo: switch (getchar()) { - case 32: + case ' ': num_lines = 2; break; - case 13: + case '\n': + case '\r': num_lines--; break; case 'q': @@ -357,6 +214,7 @@ more(void) printf(" \015"); fflush(stdout); + more_end(); } @@ -383,6 +241,7 @@ server_connect(void) die("fcntl: %m"); } + #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) static void @@ -391,36 +250,32 @@ server_got_reply(char *x) int code; int len = 0; - if (*x == '+') /* Async reply */ + if (*x == '+') /* Async reply */ PRINTF(len, ">>> %s\n", x+1); - else if (x[0] == ' ') /* Continuation */ + else if (x[0] == ' ') /* Continuation */ PRINTF(len, "%s%s\n", verbose ? " " : "", x+1); else if (strlen(x) > 4 && - sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && - (x[4] == ' ' || x[4] == '-')) + sscanf(x, "%d", &code) == 1 && code >= 0 && code < 10000 && + (x[4] == ' ' || x[4] == '-')) { if (code) - PRINTF(len, "%s\n", verbose ? x : x+5); + PRINTF(len, "%s\n", verbose ? x : x+5); + if (x[4] == ' ') { - nstate = STATE_PROMPT; - skip_input = 0; - return; + busy = 0; + skip_input = 0; + return; } } else PRINTF(len, "??? <%s>\n", x); - if (skip_input) - return; - - if (interactive && input_initialized && (len > 0)) + if (interactive && busy && !skip_input && !init && (len > 0)) { - int lns = LINES ? LINES : 25; - int cls = COLS ? COLS : 80; - num_lines += (len + cls - 1) / cls; /* Divide and round up */ - if ((num_lines >= lns) && (cstate == STATE_CMD_SERVER)) - more(); + num_lines += (len + term_cls - 1) / term_cls; /* Divide and round up */ + if (num_lines >= term_lns) + more(); } } @@ -465,19 +320,23 @@ server_read(void) } } -static fd_set select_fds; - static void select_loop(void) { int rv; while (1) { + if (init && !busy) + init_commands(); + + if (!init) + input_notify(!busy); + + fd_set select_fds; FD_ZERO(&select_fds); - if (cstate != STATE_CMD_USER) - FD_SET(server_fd, &select_fds); - if (cstate != STATE_CMD_SERVER) + FD_SET(server_fd, &select_fds); + if (!busy) FD_SET(0, &select_fds); rv = select(server_fd+1, &select_fds, NULL, NULL, NULL); @@ -489,16 +348,16 @@ select_loop(void) die("select: %m"); } - if (FD_ISSET(server_fd, &select_fds)) + if (FD_ISSET(0, &select_fds)) { - server_read(); - update_state(); + input_read(); + continue; } - if (FD_ISSET(0, &select_fds)) + if (FD_ISSET(server_fd, &select_fds)) { - rl_callback_read_char(); - update_state(); + server_read(); + continue; } } } @@ -556,14 +415,22 @@ server_send(char *cmd) } } + +/* XXXX + + get_term_size(); + + if (tcgetattr(0, &tty_save) != 0) + { + perror("tcgetattr error"); + return(EXIT_FAILURE); + } + } + + */ int main(int argc, char **argv) { -#ifdef HAVE_LIBDMALLOC - if (!getenv("DMALLOC_OPTIONS")) - dmalloc_debug(0x2f03d00); -#endif - interactive = isatty(0); parse_args(argc, argv); cmd_build_tree(); diff --git a/client/client.h b/client/client.h index 64de97ec..b194a772 100644 --- a/client/client.h +++ b/client/client.h @@ -6,15 +6,31 @@ * Can be freely distributed and used under the terms of the GNU GPL. */ -/* client.c */ -void cleanup(void); +extern int init, busy, interactive; +extern int term_lns, term_cls; + +/* birdc.c / birdcl.c */ + void input_start_list(void); void input_stop_list(void); +void input_init(void); +void input_notify(int prompt); +void input_read(void); + +void more_begin(void); +void more_end(void); + +void cleanup(void); + /* commands.c */ void cmd_build_tree(void); void cmd_help(char *cmd, int len); int cmd_complete(char *cmd, int len, char *buf, int again); char *cmd_expand(char *cmd); + +/* client.c */ + +void submit_command(char *cmd_raw); |