aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMattias Andrée <m@maandree.se>2025-02-08 12:52:16 +0100
committerMattias Andrée <m@maandree.se>2025-02-08 12:52:16 +0100
commit1adad6b0b13c9e437a7fcddd973e43b23abebdc0 (patch)
treec607c7b82272368cb10e1f1283e6d9e906dd3dea
parentm (diff)
downloadmakeenv-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--LICENSE2
-rw-r--r--makeenv.c193
2 files changed, 184 insertions, 11 deletions
diff --git a/LICENSE b/LICENSE
index f930ea6..46cd895 100644
--- a/LICENSE
+++ b/LICENSE
@@ -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
diff --git a/makeenv.c b/makeenv.c
index 8f76f5b..df940be 100644
--- a/makeenv.c
+++ b/makeenv.c
@@ -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;