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..9dd6d9b9 --- /dev/null +++ b/client/birdc.c @@ -0,0 +1,223 @@ +/* + * BIRD Client - Readline variant I/O + * + * (c) 1999--2004 Martin Mares + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#include +#include +#include + +#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 ? LINES : 25; + term_cls = COLS ? COLS : 80; + + 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..c41b046c --- /dev/null +++ b/client/birdcl.c @@ -0,0 +1,165 @@ +/* + * BIRD Client - Light variant I/O + * + * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek + * + * Can be freely distributed and used under the terms of the GNU GPL. + */ + +#include +#include +#include +#include + +#include +#include + +#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; + } + else + { + term_lns = 25; + term_cls = 80; + } +} + +void +cleanup(void) +{ + if (more_active) + more_end(); +} diff --git a/client/client.c b/client/client.c index d8f0060c..61caf38b 100644 --- a/client/client.c +++ b/client/client.c @@ -2,22 +2,32 @@ * BIRD Client * * (c) 1999--2004 Martin Mares + * (c) 2013 Tomas Hlavacek * * 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 #include #include #include -#include #include #include #include #include -#include -#include -#include #include "nest/bird.h" #include "lib/resource.h" @@ -25,34 +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 int restricted; 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 ] [-v] [-r]\n"); + fprintf(stderr, "Usage: %s [-s ] [-v] [-r]\n", name); exit(1); } @@ -74,7 +81,7 @@ parse_args(int argc, char **argv) restricted = 1; break; default: - usage(); + usage(argv[0]); } /* If some arguments are not options, we take it as commands */ @@ -97,17 +104,14 @@ parse_args(int argc, char **argv) tmp[-1] = 0; once = 1; + interactive = 0; } } + /*** Input ***/ -static void server_send(char *); - -/* 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) @@ -125,182 +129,31 @@ 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); + server_send(cmd); } void -input_start_list(void) /* Leave the currently edited line and make space for listing */ +submit_command(char *cmd_raw) { - _rl_move_vert(_rl_vis_botlin); -#ifdef HAVE_RL_CRLF - rl_crlf(); -#endif -} + char *cmd = cmd_expand(cmd_raw); -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; -} - -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(); -} - -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) -{ - if (input_initialized) - { - input_initialized = 0; - input_hide(); - rl_callback_handler_remove(); - } -} - -void -update_state(void) -{ - if (nstate == cstate) + if (!cmd) return; + if (!handle_internal_command(cmd)) + submit_server_command(cmd); + + free(cmd); +} + +static void +init_commands(void) +{ if (restricted) { submit_server_command("restrict"); @@ -317,40 +170,35 @@ 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(); - } - - if (nstate != STATE_PROMPT) - input_hide(); - - cstate = nstate; + input_init(); + 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': @@ -362,6 +210,7 @@ more(void) printf(" \015"); fflush(stdout); + more_end(); } @@ -388,6 +237,7 @@ server_connect(void) die("fcntl: %m"); } + #define PRINTF(LEN, PARGS...) do { if (!skip_input) len = printf(PARGS); } while(0) static void @@ -396,36 +246,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(); } } @@ -470,19 +316,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); @@ -494,16 +344,16 @@ select_loop(void) die("select: %m"); } + if (FD_ISSET(0, &select_fds)) + { + input_read(); + continue; + } + if (FD_ISSET(server_fd, &select_fds)) { server_read(); - update_state(); - } - - if (FD_ISSET(0, &select_fds)) - { - rl_callback_read_char(); - update_state(); + continue; } } } @@ -561,14 +411,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); diff --git a/configure.in b/configure.in index 272a2431..96f2a50e 100644 --- a/configure.in +++ b/configure.in @@ -236,7 +236,7 @@ fi CLIENT= CLIENT_LIBS= if test "$enable_client" = yes ; then - CLIENT=client + CLIENT=birdc AC_CHECK_LIB(history, add_history, CLIENT_LIBS="-lhistory") AC_CHECK_LIB(ncurses, tgetent, USE_TERMCAP_LIB=-lncurses, AC_CHECK_LIB(curses, tgetent, USE_TERMCAP_LIB=-lcurses, diff --git a/doc/bird.sgml b/doc/bird.sgml index fab49105..18f3601b 100644 --- a/doc/bird.sgml +++ b/doc/bird.sgml @@ -623,7 +623,13 @@ codes along with the messages. You do not necessarily need to use -- the format of communication between BIRD and There is also lightweight variant of BIRD client called +Many commands have the Here is a brief list of supported functions: diff --git a/tools/Makefile.in b/tools/Makefile.in index 728e5797..feb83b9f 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -3,22 +3,31 @@ include Rules -.PHONY: all daemon client subdir depend clean distclean tags docs userdocs progdocs +.PHONY: all daemon birdc birdcl subdir depend clean distclean tags docs userdocs progdocs -all: sysdep/paths.h .dep-stamp subdir daemon @CLIENT@ +all: sysdep/paths.h .dep-stamp subdir daemon birdcl @CLIENT@ daemon: $(exedir)/bird -client: $(exedir)/birdc +birdc: $(exedir)/birdc + +birdcl: $(exedir)/birdcl bird-dep := $(addsuffix /all.o, $(static-dirs)) conf/all.o lib/birdlib.a $(bird-dep): sysdep/paths.h .dep-stamp subdir -birdc-dep := client/all.o lib/birdlib.a +birdc-dep := client/birdc.o client/all.o lib/birdlib.a $(birdc-dep): sysdep/paths.h .dep-stamp subdir +birdcl-dep := client/birdcl.o client/all.o lib/birdlib.a + +$(birdcl-dep): sysdep/paths.h .dep-stamp subdir + + +export client := @CLIENT@ + depend: sysdep/paths.h .dir-stamp set -e ; for a in $(dynamic-dirs) ; do $(MAKE) -C $$a $@ ; done set -e ; for a in $(static-dirs) $(client-dirs) ; do $(MAKE) -C $$a -f $(srcdir_abs)/$$a/Makefile $@ ; done @@ -33,6 +42,9 @@ $(exedir)/bird: $(bird-dep) $(exedir)/birdc: $(birdc-dep) $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) $(CLIENT_LIBS) +$(exedir)/birdcl: $(birdcl-dep) + $(CC) $(LDFLAGS) -o $@ $^ $(LIBS) + .dir-stamp: sysdep/paths.h mkdir -p $(static-dirs) $(client-dirs) $(doc-dirs) touch .dir-stamp @@ -58,6 +70,7 @@ tags: install: all $(INSTALL) -d $(DESTDIR)/$(sbindir) $(DESTDIR)/$(sysconfdir) $(DESTDIR)/@runtimedir@ $(INSTALL_PROGRAM) -s $(exedir)/bird $(DESTDIR)/$(sbindir)/bird@SUFFIX@ + $(INSTALL_PROGRAM) -s $(exedir)/birdcl $(DESTDIR)/$(sbindir)/birdcl@SUFFIX@ if test -n "@CLIENT@" ; then \ $(INSTALL_PROGRAM) -s $(exedir)/birdc $(DESTDIR)/$(sbindir)/birdc@SUFFIX@ ; \ fi @@ -74,7 +87,7 @@ install-docs: clean: find . -name "*.[oa]" -o -name core -o -name depend -o -name "*.html" | xargs rm -f rm -f conf/cf-lex.c conf/cf-parse.* conf/commands.h conf/keywords.h - rm -f $(exedir)/bird $(exedir)/birdc $(exedir)/bird.ctl $(exedir)/bird6.ctl .dep-stamp + rm -f $(exedir)/bird $(exedir)/birdcl $(exedir)/birdc $(exedir)/bird.ctl $(exedir)/bird6.ctl .dep-stamp distclean: clean rm -f config.* configure sysdep/autoconf.h sysdep/paths.h Makefile Rules diff --git a/tools/Rules.in b/tools/Rules.in index fc06aeb1..ca930ec8 100644 --- a/tools/Rules.in +++ b/tools/Rules.in @@ -11,7 +11,7 @@ static-dirs := nest filter $(addprefix proto/,$(protocols)) static-dir-paths := $(addprefix $(srcdir)/,$(static-dirs)) dynamic-dirs := lib conf dynamic-dir-paths := $(dynamic-dirs) -client-dirs := @CLIENT@ +client-dirs := client client-dir-paths := $(client-dirs) doc-dirs := doc doc-dir-paths := $(doc-dirs) @@ -75,8 +75,12 @@ endif %.o: $(src-path)%.c $(CC) $(CFLAGS) -o $@ -c $< +ifndef source-dep +source-dep := $(source) +endif + depend: - $(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source)) >depend + $(CC) $(CPPFLAGS) -MM $(addprefix $(src-path),$(source-dep)) >depend ifneq ($(wildcard depend),) include depend