diff --git a/Makefile b/Makefile index ee1eb6e..b236368 100644 --- a/Makefile +++ b/Makefile @@ -1,9 +1,13 @@ +export DESTDIR ?= $(HOME)/.local + .PHONY: build build: $(MAKE) -C src build .PHONY: install -install: build - install src/cli.exe $(HOME)/.local/bin/displayswitcheroo2 - install -D data/displayswitcheroo/list.lua $(HOME)/.local/share/displayswitcheroo/list.lua - install -D data/displayswitcheroo/displayswitcheroo.lua $(HOME)/.local/share/displayswitcheroo/displayswitcheroo.lua +install: + ./install.sh + +.PHONY: clean +clean: + $(MAKE) -C src clean diff --git a/install.sh b/install.sh new file mode 100755 index 0000000..3c082f1 --- /dev/null +++ b/install.sh @@ -0,0 +1,42 @@ +#!/bin/bash + +set -o nounset -o pipefail -o errexit + +SCRIPT_DIR=$(readlink -f "$0" | xargs dirname) + +usage() { + cat <&2 +Usage: $(basename "$0") [options] +Options: + -h show this message + -d DESTDIR set DESTDIR + -u user installation: DESTDIR=~/.local + -s systemwide installation: DESTDIR=/usr + -a APP application name +EOF + exit "${1-0}" +} + +DESTDIR=${DESTDIR-$HOME/.local} +APP=${APP-displayswitcheroo} +while getopts "dusah-" OPT; do + case $OPT in + d) DESTDIR=$OPTARG ;; + u) DESTDIR=$HOME/.local ;; + s) DESTDIR=/usr ;; + a) APP=$OPTARG ;; + h) usage ;; + -) break ;; + ?) usage 2 ;; + esac +done +shift $((OPTIND-1)) + +make -C "$SCRIPT_DIR" clean build EXTRA_CFLAGS="-DXDG_APP='\"$APP\"'" + +mkdir -pm 0700 "$DESTDIR/bin" +install -v "$SCRIPT_DIR/src/cli.exe" "$DESTDIR/bin/$APP" + +mkdir -pm 0700 "$DESTDIR/share/$APP" +install -v -D "$SCRIPT_DIR/data/displayswitcheroo/list.lua" "$DESTDIR/share/$APP/list.lua" +install -v -D "$SCRIPT_DIR/data/displayswitcheroo/displayswitcheroo.lua" "$DESTDIR/share/$APP/$APP.lua" diff --git a/src/Makefile b/src/Makefile index 8d14094..c583f13 100644 --- a/src/Makefile +++ b/src/Makefile @@ -23,7 +23,7 @@ build: cli.exe x11.so SRC = x11.c edid.c r.c cli.exe: cli.c version.c wait.c $(SRC) - $(CC) $(CFLAGS) $(READLINE_CFLAGS) -o$@ $^ $(LDFLAGS) $(READLINE_LDFLAGS) + $(CC) $(CFLAGS) $(EXTRA_CFLAGS) $(READLINE_CFLAGS) -o$@ $^ $(LDFLAGS) $(READLINE_LDFLAGS) $(EXTRA_LDFLAGS) x11.so: $(SRC) $(CC) -rdynamic -fPIC -shared $(CFLAGS) -o$@ $^ $(LDFLAGS) diff --git a/src/cli.c b/src/cli.c index 150c8c2..51ed3a3 100644 --- a/src/cli.c +++ b/src/cli.c @@ -166,11 +166,93 @@ static int add_xdg_to_search_paths(lua_State* L) luaR_return(L, 0); } +static lua_State* new_lua_state(void) +{ + lua_State* L = luaL_newstate(); + CHECK_NOT(L, NULL, "unable to create Lua state"); + + openlibs(L); + add_xdg_to_search_paths(L); + + return L; +} + +static const char* resolve_script(const char* script) +{ + if(script == NULL) return NULL; + + debug("trying to resolve as path: %s", script); + struct stat st; + if(stat(script, &st) == 0) { + return script; + } + + // config + debug("trying to resolve relative to XDG config dirs: %s", script); + const char* p = xdg_resolves(xdg, XDG_CONFIG, script, NULL); + if(p) return p; + + static char buf[NAME_MAX]; + int r = snprintf(LIT(buf), "%s.lua", script); + if(r >= sizeof(buf)) failwith("buffer overflow"); + debug("trying to resolve relative to XDG config dirs: %s", buf); + p = xdg_resolves(xdg, XDG_CONFIG, buf, NULL); + if(p) return p; + + + // data + debug("trying to resolve relative to XDG data dirs: %s", script); + p = xdg_resolves(xdg, XDG_DATA, script, NULL); + if(p) return p; + + r = snprintf(LIT(buf), "%s.lua", script); + if(r >= sizeof(buf)) failwith("buffer overflow"); + + debug("trying to resolve relative to XDG data dirs: %s", buf); + p = xdg_resolves(xdg, XDG_DATA, buf, NULL); + if(p) return p; + + // path + debug("trying to resolve as path: %s", buf); + if(stat(buf, &st) == 0) { + return buf; + } + + dprintf(2, "unable to resolve script: %s\n", script); + exit(1); +} + +static char* collect_fd(int fd, size_t* len) +{ + size_t l = 0, L = 4096; + char* buf = malloc(L); + CHECK_MALLOC(buf); + + for(;;) { + int r = read(fd, &buf[l], L-l); + CHECK(r, "read(%d)", 0); + l += r; + if(r == 0) { + if(len) { + *len = l; + } + return buf; + } + + if(l >= L) { + L <<= 1; + buf = realloc(buf, L); + CHECK_MALLOC(buf); + } + } +} + struct options { const char* script; int interact; int wait; int once; + int global; }; static void print_usage(int fd) @@ -181,6 +263,7 @@ static void print_usage(int fd) dprintf(fd, " -i enter interactive mode after executing SCRIPT\n"); dprintf(fd, " -w wait for output connect/disconnects\n"); dprintf(fd, " -1 run once before waiting\n"); + dprintf(fd, " -g keep global state between runs\n"); dprintf(fd, " -h print this message\n"); dprintf(fd, " -v print version information\n"); } @@ -192,7 +275,7 @@ static void parse_options(struct options* o, int argc, char* argv[]) memset(o, 0, sizeof(*o)); int res; - while((res = getopt(argc, argv, "iw1hv")) != -1) { + while((res = getopt(argc, argv, "iw1ghv")) != -1) { switch(res) { case 'i': o->interact = 1; @@ -203,6 +286,9 @@ static void parse_options(struct options* o, int argc, char* argv[]) case '1': o->once = 1; break; + case 'g': + o->global = 1; + break; case 'v': print_version(progname); exit(0); @@ -218,69 +304,36 @@ static void parse_options(struct options* o, int argc, char* argv[]) } } -static const char* resolve_script(const struct options* o) -{ - if(o->script == NULL) return NULL; - - debug("trying to resolve as path: %s", o->script); - struct stat st; - if(stat(o->script, &st) == 0) { - return o->script; - } - - // config - debug("trying to resolve relative to XDG config dirs: %s", o->script); - const char* p = xdg_resolves(xdg, XDG_CONFIG, o->script, NULL); - if(p) return p; - - static char buf[NAME_MAX]; - int r = snprintf(LIT(buf), "%s.lua", o->script); - if(r >= sizeof(buf)) failwith("buffer overflow"); - debug("trying to resolve relative to XDG config dirs: %s", buf); - p = xdg_resolves(xdg, XDG_CONFIG, buf, NULL); - if(p) return p; - - - // data - debug("trying to resolve relative to XDG data dirs: %s", o->script); - p = xdg_resolves(xdg, XDG_DATA, o->script, NULL); - if(p) return p; - - r = snprintf(LIT(buf), "%s.lua", o->script); - if(r >= sizeof(buf)) failwith("buffer overflow"); +struct state { + struct options o; - debug("trying to resolve relative to XDG data dirs: %s", buf); - p = xdg_resolves(xdg, XDG_DATA, buf, NULL); - if(p) return p; + const char* script; - // path - debug("trying to resolve as path: %s", buf); - if(stat(buf, &st) == 0) { - return buf; - } + char* stdin_buf; + size_t stdin_len; - dprintf(2, "unable to resolve script: %s\n", o->script); - exit(1); -} + lua_State* L0; +}; -void run(const struct options* o) +static void run(struct state* st) { - const char* script = resolve_script(o); - if(script) { - debug("resolved script: %s", script); - } - - lua_State* L = luaL_newstate(); - CHECK_NOT(L, NULL, "unable to create Lua state"); + lua_State* L = st->L0 ? st->L0 : new_lua_state(); - openlibs(L); - add_xdg_to_search_paths(L); + if(st->script || st->stdin_buf) { + int r; + if(st->script) { + info("running script: %s", st->script); + r = luaL_dofile(L, st->script); + } else { + info("running script from stdin"); + r = luaL_loadbuffer(L, st->stdin_buf, st->stdin_len, "stdin"); + if(r == LUA_OK) { + r = lua_pcall(L, 0, LUA_MULTRET, 0); + } + } - if(script) { - info("running script: %s", script); - int r = luaL_dofile(L, script); if(r == LUA_OK) { - if(o->interact) { + if(st->o.interact) { run_repl(L); } } else { @@ -292,23 +345,36 @@ void run(const struct options* o) run_repl(L); } - lua_close(L); + if(!st->L0) { + lua_close(L); + } } int main(int argc, char* argv[]) { - struct options o; - parse_options(&o, argc, argv); + struct state st; + memset(&st, 0, sizeof(st)); + parse_options(&st.o, argc, argv); + + st.L0 = st.o.global ? new_lua_state() : NULL; xdg_init(); - if(o.wait) { - if(o.once) { - run(&o); + if(st.o.script) { + if(strcmp(st.o.script, "-") == 0) { + st.stdin_buf = collect_fd(0, &st.stdin_len); + } else { + st.script = resolve_script(st.o.script); + } + } + + if(st.o.wait) { + if(st.o.once) { + run(&st); } - run_wait_loop((void(*)(void*))run, &o); + run_wait_loop((void(*)(void*))run, &st); } else { - run(&o); + run(&st); } xdg_deinit(); diff --git a/tools/common.makefile b/tools/common.makefile index 616353c..ce1c34e 100644 --- a/tools/common.makefile +++ b/tools/common.makefile @@ -6,9 +6,12 @@ VERSION ?= $(TOOLS)/version CC = gcc PKG_CONFIG ?= pkg-config -CFLAGS ?= -Wall -Werror -O2 +CFLAGS ?= -Wall -Werror -O1 LDFLAGS ?= +EXTRA_CFLAGS ?= +EXTRA_LDFLAGS ?= + LOG_LEVEL ?= INFO CFLAGS += -DLOG_LEVEL=LOG_$(LOG_LEVEL)