#include #include #include #undef NORETURN #undef PATH_SEP #include static VALUE shellwords; struct cmd_struct { const char *cmd; int (*fn)(int, const char **, const char *); int option; }; #define RUN_SETUP (1 << 0) #define RUN_SETUP_GENTLY (1 << 1) #define USE_PAGER (1 << 2) #define NEED_WORK_TREE (1 << 3) static struct cmd_struct commands[] = { { "rev-parse", cmd_rev_parse }, { "show", cmd_show, RUN_SETUP }, }; static VALUE git_rb_backticks(int o_argc, VALUE *o_argv, VALUE ctx) { int argc, i, old; int pipefd[2]; const char **argv; char buf[0x1000]; VALUE command; int do_read; struct cmd_struct *cmd = NULL; const char *prefix = NULL; if (strncmp(RSTRING_PTR(o_argv[0]), "git ", 4)) { VALUE port, result; port = rb_funcall(rb_cIO, rb_intern("popen"), 1, o_argv[0]); result = rb_funcall(port, rb_intern("read"), 0); rb_funcall(result, rb_intern("chomp!"), 0); rb_io_close(port); return result; } command = rb_funcall(shellwords, rb_intern("shellsplit"), 1, o_argv[0]); rb_ary_shift(command); argc = RARRAY_LEN(command); argv = xcalloc(sizeof(*argv), argc); VALUE *rarray = RARRAY_PTR(command); for (i = 0; i < argc; i++) argv[i] = RSTRING_PTR(rarray[i]); old = dup(1); i = pipe(pipefd); dup2(pipefd[1], 1); close(pipefd[1]); for (i = 0; i < ARRAY_SIZE(commands); i++) { struct cmd_struct *p = &commands[i]; if (strcmp(p->cmd, argv[0])) continue; cmd = p; } if (!cmd) rb_raise(rb_eArgError, "unknown command: %s", argv[0]); if (cmd->option & RUN_SETUP) prefix = setup_git_directory(); i = cmd->fn(argc, argv, prefix); rb_last_status_set(i, getpid()); fflush(stdout); dup2(old, 1); i = read(pipefd[0], buf, sizeof(buf)); if (buf[i - 1] == '\n') i -= 1; buf[i] = '\0'; return rb_str_new(buf, i); } void Init_git(void) { rb_require("shellwords"); shellwords = rb_define_module("Shellwords"); rb_define_global_function("`", git_rb_backticks, -1); }