From d2f89995950260decef32ef904ecce9867103d3a Mon Sep 17 00:00:00 2001 From: Emmanuel Lepage Vallee Date: Sun, 2 Feb 2020 05:15:36 -0500 Subject: [PATCH] init: Support loading awesome with shebang (#!) executables. This commits re-use the modeline code from the previous commit to support Lua scripts starting with #!. Previously, it was non-trivial to add support since most *nix OS wont parse the command line arguments correctly. Since we now have a state machine good enough for 95%+ of the use case, it is easy to support them. --- awesome.c | 7 +++++-- options.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++++++ options.h | 1 + 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/awesome.c b/awesome.c index ffdeb92f3..ba9c81517 100644 --- a/awesome.c +++ b/awesome.c @@ -589,8 +589,11 @@ main(int argc, char **argv) /* Text won't be printed correctly otherwise */ setlocale(LC_CTYPE, ""); - /* check args */ - char *confpath = options_check_args(argc, argv, &default_init_flags, &searchpath); + char *confpath = options_detect_shebang(argc, argv); + + /* if no shebang is detected, check the args. Shebang (#!) args are parsed later */ + if (!confpath) + confpath = options_check_args(argc, argv, &default_init_flags, &searchpath); /* Get XDG basedir data */ if(!xdgInitHandle(&xdg)) diff --git a/options.c b/options.c index 3a8378043..380c7b523 100644 --- a/options.c +++ b/options.c @@ -24,6 +24,7 @@ #include #include +#include #include #define KEY_VALUE_BUF_MAX 64 @@ -267,6 +268,56 @@ options_init_config(char *execpath, char *configpath, int *init_flags, string_ar return state == MODELINE_STATE_COMPLETE; } + +char * +options_detect_shebang(int argc, char **argv) +{ + /* There is no cross-platform ways to check if it is *really* called by a + * shebang. There is a couple Linux specific hacks which work with the + * most common C libraries, but they wont work on *BSD. + * + * On some platforms, the argv is going to be parsed by the OS, in other + * they will be concatenated in one big string. There is some ambiguities + * caused by that. For example, `awesome -s foo` and and `#!/bin/awesome -s` + * are both technically valid if `foo` is a directory in the first and + * lua file (without extension) in the second. While `-s` with a file + * wont work, it is hard to know by looking at the string. + * + * The trick to avoid any ambiguity is to just read the file and see if + * the args match. `options_init_config` will be called later and the args + * will be parsed properly. + */ + + /* On WSL and some other *nix this isn't true, but it is true often enough */ + if (argc > 3 || argc == 1) + return NULL; + + /* Check if it is executable */ + struct stat inf; + if (stat(argv[argc-1], &inf) || !(inf.st_mode & S_IXUSR)) + return NULL; + + FILE *fp = fopen(argv[argc-1], "r"); + + if (!fp) + return NULL; + + char buf[3]; + + if (!fgets(buf, 2, fp)) { + fclose(fp); + return NULL; + } + + fclose(fp); + + if (!strcmp(buf, "#!")) + return NULL; + + /* Ok, good enough, this is a shebang script, assume it called `awesome` */ + return a_strdup(argv[argc-1]); +} + /** Print help and exit(2) with given exit_code. * \param exit_code The exit code. */ diff --git a/options.h b/options.h index a71fca8d7..422507541 100644 --- a/options.h +++ b/options.h @@ -34,5 +34,6 @@ typedef enum { INIT_FLAG_FORCE_CMD_ARGS = 0x1 << 5, } awesome_init_config_t; +char *options_detect_shebang(int argc, char **argv); bool options_init_config(char *execpath, char *configpath, int *init_flags, string_array_t *paths); char *options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths);