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.
This commit is contained in:
Emmanuel Lepage Vallee 2020-02-02 05:15:36 -05:00
parent f1aedb237a
commit d2f8999595
3 changed files with 57 additions and 2 deletions

View File

@ -589,8 +589,11 @@ main(int argc, char **argv)
/* Text won't be printed correctly otherwise */ /* Text won't be printed correctly otherwise */
setlocale(LC_CTYPE, ""); setlocale(LC_CTYPE, "");
/* check args */ char *confpath = options_detect_shebang(argc, argv);
char *confpath = options_check_args(argc, argv, &default_init_flags, &searchpath);
/* 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 */ /* Get XDG basedir data */
if(!xdgInitHandle(&xdg)) if(!xdgInitHandle(&xdg))

View File

@ -24,6 +24,7 @@
#include <unistd.h> #include <unistd.h>
#include <stdio.h> #include <stdio.h>
#include <sys/stat.h>
#include <getopt.h> #include <getopt.h>
#define KEY_VALUE_BUF_MAX 64 #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; 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. /** Print help and exit(2) with given exit_code.
* \param exit_code The exit code. * \param exit_code The exit code.
*/ */

View File

@ -34,5 +34,6 @@ typedef enum {
INIT_FLAG_FORCE_CMD_ARGS = 0x1 << 5, INIT_FLAG_FORCE_CMD_ARGS = 0x1 << 5,
} awesome_init_config_t; } 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); 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); char *options_check_args(int argc, char **argv, int *init_flags, string_array_t *paths);