diff options
author | Mattias Andrée <m@maandree.se> | 2025-02-08 12:52:16 +0100 |
---|---|---|
committer | Mattias Andrée <m@maandree.se> | 2025-02-08 12:52:16 +0100 |
commit | 1adad6b0b13c9e437a7fcddd973e43b23abebdc0 (patch) | |
tree | c607c7b82272368cb10e1f1283e6d9e906dd3dea | |
parent | m (diff) | |
download | makeenv-1adad6b0b13c9e437a7fcddd973e43b23abebdc0.tar.gz makeenv-1adad6b0b13c9e437a7fcddd973e43b23abebdc0.tar.bz2 makeenv-1adad6b0b13c9e437a7fcddd973e43b23abebdc0.tar.xz |
m + place -- appropriately + add documentation
Signed-off-by: Mattias Andrée <m@maandree.se>
-rw-r--r-- | LICENSE | 2 | ||||
-rw-r--r-- | makeenv.c | 193 |
2 files changed, 184 insertions, 11 deletions
@@ -1,6 +1,6 @@ ISC License -© 2024 Mattias Andrée <m@maandree.se> +© 2024, 2025 Mattias Andrée <m@maandree.se> Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above @@ -8,18 +8,62 @@ #include <unistd.h> +/** + * The name of the process, used in error messages + */ static const char *argv0 = "makeenv"; + +/** + * Options found in .makeenv and in the command line + */ static const char **options = NULL; + +/** + * Macros found in .makeenv and in the command line + */ static const char **macros = NULL; + +/** + * Targets found in .makeenv and in the command line + */ static const char **targets = NULL; + +/** + * Number of elements in `.options` + */ static size_t noptions = 0; + +/** + * Number of elements in `.macros` + */ static size_t nmacros = 0; + +/** + * Number of elements in `.targets` + */ static size_t ntargets = 0; + +/** + * Number of elements allocated to `.options` + */ static size_t options_size = 0; + +/** + * Number of elements allocated to `.macros` + */ static size_t macros_size = 0; + +/** + * Number of elements allocated to `.targets` + */ static size_t targets_size = 0; +/** + * Add an option to the list of options + * + * @param s The option to add + */ static void put_option(const char *s) { @@ -35,10 +79,15 @@ put_option(const char *s) } +/** + * Add an option to the list of options + * + * @param c The letter of the option to add, for example 'x' for "-x" + */ static void put_option_short(int c) { - char *opt = malloc(3); + char *opt = malloc(sizeof("-x")); if (!opt) { fprintf(stderr, "%s: failed to allocate enough memory to load .makeenv\n", argv0); exit(125); @@ -50,11 +99,16 @@ put_option_short(int c) } +/** + * Add an option to the list of options + * + * @param s The option to add, it will be prefixed with a dash ("-") + */ static void put_option_prefix_dash(const char *s) { size_t len = strlen(s); - char *arg = malloc(len + 2); + char *arg = malloc(sizeof("-") + len); if (!arg) { fprintf(stderr, "%s: failed to allocate enough memory to load .makeenv\n", argv0); exit(125); @@ -64,6 +118,11 @@ put_option_prefix_dash(const char *s) } +/** + * Add all options from a list to the list of options + * + * @parm s The list of options to add; elements are separated by spaces + */ static void put_options(char *s) { @@ -81,6 +140,15 @@ put_options(char *s) } +/** + * Add a macro to the list of macros and to environ(3) + * + * This function should not be called directly when adding + * macros from the command line; use `put_operand` instead + * as it will detect and warn about mixing targets and macros + * + * @param s The macro to add + */ static void put_macro(const char *s) { @@ -100,6 +168,15 @@ put_macro(const char *s) } +/** + * Add a target to the list of targets + * + * This function should not be called directly when adding + * targets from the command line; use `put_operand` instead + * as it will detect and warn about mixing targets and macros + * + * @param s The target to add + */ static void put_target(const char *s) { @@ -115,6 +192,11 @@ put_target(const char *s) } +/** + * Add all targets from a list to the list of targets + * + * @param s The list of targets to add; elements are separated by spaces + */ static void put_targets(char *s) { @@ -131,6 +213,13 @@ put_targets(char *s) put_target(word); } + +/** + * Add an operand to either the list of macros or the list of targets, + * depending on the classification of the operand + * + * @param s The operand to add + */ static void put_operand(char *s) { @@ -163,7 +252,13 @@ put_operand(char *s) } - +/** + * Check if a string appears as a word in another string + * + * @param set The string to search in; words are separated by spaces + * @param sought The string to search for + * @return 1 if the string is found, 0 otherwise + */ #if defined(__GNUC__) __attribute__((__pure__)) #endif @@ -181,10 +276,29 @@ contains(const char *set, const char *sought) } +/** + * Create the argument list for for make(1) + * + * The arguments will be listed in the order required by make(1posix): + * targets follow macros, however also, with POSIX.1-2017, Section 12.2, + * Utility Syntax Guidelines, except for Guideline 9 enforced: operands + * (macros and targets) follow options. The list will also begin with + * and unset element where the process name for make(1) can be placed. + * + * The function will insert a "--" argument to separate options from + * operands, to ensure it is put in a position, which does not cause + * any problems, due to argument reordering, when the user has added + * a "--" argument. The "--" argument will be added regardless of + * whether the user added a "--" argument; in fact any "--" argument + * from the user shall be skipped unless it can be determined that + * it is a target ranther than and option-list ender. + * + * @return `NULL`-terminated list of arguments + */ static const char ** create_cmdline(void) { - size_t i, j = 1, n = noptions + nmacros + ntargets + 2; + size_t i, j = 1, n = noptions + nmacros + ntargets + 3; const char **args = malloc(n * sizeof(char *)); if (!args) { fprintf(stderr, "%s: failed to allocate enough memory to execute make\n", argv0); @@ -192,6 +306,7 @@ create_cmdline(void) } for (i = 0; i < noptions; i++) args[j++] = options[i]; + args[j++] = "--"; for (i = 0; i < nmacros; i++) args[j++] = macros[i]; for (i = 0; i < ntargets; i++) @@ -201,6 +316,12 @@ create_cmdline(void) } +/** + * Trim leading and trailing whitespace from a string + * + * @param s The string to trim + * @return The trimmed string + */ static char * trim(char *s) { @@ -215,6 +336,11 @@ trim(char *s) } +/** + * Trim whitespace around an equals sign in a string + * + * @param s The string to trim, will be updated + */ static void trim_around_equals(char *s) { @@ -233,6 +359,13 @@ trim_around_equals(char *s) } +/** + * Get an environment variable or a default value + * + * @param var The name of the environment variable + * @param def The default value to return if the environment variable is not set + * @return The value of the environment variable, or the default value if unset + */ static const char * get(const char *var, const char *def) { @@ -268,6 +401,7 @@ main(int argc, char *argv[]) argv0 = *argv++; (void) argc; + /* Open .makeenv */ fd = open(".makeenv", O_RDONLY); if (fd < 0) { if (errno == ENOENT) @@ -276,6 +410,7 @@ main(int argc, char *argv[]) return 125; } + /* Read .makeenv */ for (;;) { if (envlen == envsize) { envsize += 8096; @@ -295,11 +430,13 @@ main(int argc, char *argv[]) envlen += (size_t)r; } + /* If .makeenv is empty, use make(1) without any additional arguments */ if (!envlen) { close(fd); goto exec; } + /* Ensure .makeenv is LF-terminated */ env = realloc(env, envlen + 1); if (!env) { fprintf(stderr, "%s: failed to allocate enough memory to load .makeenv\n", argv0); @@ -307,6 +444,10 @@ main(int argc, char *argv[]) } env[envlen++] = '\n'; + /* Close .makeenv */ + close(fd); + + /* Add options, macros, and targets from .makeenv */ line = env; has_equals = 0; for (i = 0; i < envlen; i++) { @@ -334,8 +475,7 @@ main(int argc, char *argv[]) } } - close(fd); - + /* Get option syntax from the environment */ unarged_opts = get("MAKEENV_OPTS_NO_ARG", "eiknpqrSst"); arged_opts = get("MAKEENV_OPTS_ARG", "fW"); optatarged_opts = get("MAKEENV_OPTS_OPT_ATTACHED_ARG", ""); @@ -344,27 +484,37 @@ main(int argc, char *argv[]) arged_longopts = get("MAKEENV_LONG_OPTS_ARG", ""); optarged_longopts = get("MAKEENV_LONG_OPTS_OPT_ARG", ""); + /* Add arguments from the command line */ for (; *argv; argv++) { + /* Stop processing options when "--" is found */ if (!strcmp(*argv, "--")) { - put_operand(*argv++); break; - } else if ((*argv)[0] != '-' || !(*argv)[1]) { + } + + /* Add operands listed before "--", but do not stop processing subsequent arguments as options */ + if ((*argv)[0] != '-' || !(*argv)[1]) { /* do not break, as make(1) allows mixing options and operands */ put_operand(*argv); operand_found = 1; continue; } + /* Warn when reordering operands to be placed after options */ if (operand_found && !warned_reordered) { warned_reordered = 1; fprintf(stderr, "%s: warning: reordering operands to after options\n", argv0); } + /* Add options */ if ((*argv)[1] == '-') { + /* Long option */ arg = *argv; if (strchr(arg, '=') || contains(unarged_longopts, arg)) { + /* The option either has an attached argument (so it's always safe to + * add) or it is recognised to never have an argument */ put_option(arg); } else if (contains(arged_longopts, arg)) { + /* Long option with dettached argument */ put_option(arg); if (!argv[1]) { fprintf(stderr, "%s: argument for option %s missing\n", argv0, arg); @@ -372,24 +522,32 @@ main(int argc, char *argv[]) } put_option(*++argv); } else if (contains(optarged_longopts, arg)) { + /* Long option with either dettached argument or no argument */ put_option(arg); - if (argv[1] && argv[1][0] != '-') + if (argv[1] && argv[1][0] != '-') { + /* The option has a detached argument */ put_option(*++argv); + } } else { fprintf(stderr, "%s: option %s not recognised\n", argv0, arg); return 125; } } else { + /* Short options */ arg = &(*argv)[1]; while (*arg) { if (strchr(unarged_opts, *arg)) { + /* Option cannot have an argument */ put_option_short(*arg++); } else if (strchr(arged_opts, *arg)) { + /* Option must have an argument */ if (arg[1]) { + /* Argument is attached to option */ put_option_short(*arg++); put_option(arg); break; } else if (argv[1]) { + /* Argument is dettached from option*/ put_option_short(*arg++); put_option(*++argv); break; @@ -398,15 +556,27 @@ main(int argc, char *argv[]) return 125; } } else if (strchr(optatarged_opts, *arg)) { + /* Option may have an attached argument, but it cannot have a dettached argument */ put_option_prefix_dash(arg); break; } else if (strchr(optarged_opts, *arg)) { + /* Option may have an attached or dettached argument */ if (arg[1]) { + /* Argument is attached to option */ put_option_prefix_dash(arg); } else { + /* Either there is no argument, or it is dettached from the option */ put_option_short(*arg++); - if (argv[1] && argv[1][0] != '-') + if (argv[1] && argv[1][0] != '-') { + /* Argument exist and is dettached. We assume that if the next + * argument in the command line is the option's argument unless + * it starts with '-'; however some implementations can be more + * intelligent about determining what is an option argument and + * what is an operands. This means that in some cases makeenv(1) + * would require the user to attach the argument even when their + * implementation of make(1) does not require this. */ put_option(*++argv); + } } break; } else { @@ -417,12 +587,15 @@ main(int argc, char *argv[]) } } + /* Add operands listed after "--" */ for (; *argv; argv++) put_operand(*argv); + /* Create the arguments for make(1); the zeroth argument will be unset and to be filled in later */ args = create_cmdline(); exec: + /* Execute make(1) */ args[0] = get("MAKEENV_MAKE", get("MAKE", "make")); execvp(args[0], (void *)args); exitstatus = errno == ENOENT ? 127 : 126; |