diff options
| author | Ron Yorston <rmy@pobox.com> | 2022-08-01 12:45:10 +0100 |
|---|---|---|
| committer | Ron Yorston <rmy@pobox.com> | 2022-08-01 12:51:37 +0100 |
| commit | 67a630e5af1ace1dd528ea9652ee69102b3136c3 (patch) | |
| tree | c918ed81ad1791c415a811d63d2f8771a7dd6ef7 | |
| parent | b0f279a48f5f7e57b6f6e941e4b59e9a1bc54548 (diff) | |
| download | busybox-w32-67a630e5af1ace1dd528ea9652ee69102b3136c3.tar.gz busybox-w32-67a630e5af1ace1dd528ea9652ee69102b3136c3.tar.bz2 busybox-w32-67a630e5af1ace1dd528ea9652ee69102b3136c3.zip | |
make: new applet
This is an experimental implementation of make for busybox-w32,
based on my public domain POSIX make:
https://frippery.org/make/
(GitHub issue #44)
| -rw-r--r-- | archival/ar.c | 4 | ||||
| -rw-r--r-- | archival/libarchive/Kbuild.src | 1 | ||||
| -rw-r--r-- | archival/libarchive/unpack_ar_archive.c | 8 | ||||
| -rw-r--r-- | configs/mingw32_defconfig | 4 | ||||
| -rw-r--r-- | configs/mingw64_defconfig | 4 | ||||
| -rw-r--r-- | miscutils/make.c | 2621 | ||||
| -rwxr-xr-x | testsuite/make.tests | 413 | ||||
| -rw-r--r-- | win32/Kbuild | 1 | ||||
| -rw-r--r-- | win32/glob.c | 343 | ||||
| -rw-r--r-- | win32/glob.h | 89 |
10 files changed, 3482 insertions, 6 deletions
diff --git a/archival/ar.c b/archival/ar.c index beccab217..b5565c936 100644 --- a/archival/ar.c +++ b/archival/ar.c | |||
| @@ -30,12 +30,12 @@ | |||
| 30 | //config:config FEATURE_AR_LONG_FILENAMES | 30 | //config:config FEATURE_AR_LONG_FILENAMES |
| 31 | //config: bool "Support long filenames (not needed for debs)" | 31 | //config: bool "Support long filenames (not needed for debs)" |
| 32 | //config: default y | 32 | //config: default y |
| 33 | //config: depends on AR | 33 | //config: depends on AR || MAKE |
| 34 | //config: help | 34 | //config: help |
| 35 | //config: By default the ar format can only store the first 15 characters | 35 | //config: By default the ar format can only store the first 15 characters |
| 36 | //config: of the filename, this option removes that limitation. | 36 | //config: of the filename, this option removes that limitation. |
| 37 | //config: It supports the GNU ar long filename method which moves multiple long | 37 | //config: It supports the GNU ar long filename method which moves multiple long |
| 38 | //config: filenames into a the data section of a new ar entry. | 38 | //config: filenames into the data section of a new ar entry. |
| 39 | //config: | 39 | //config: |
| 40 | //config:config FEATURE_AR_CREATE | 40 | //config:config FEATURE_AR_CREATE |
| 41 | //config: bool "Support archive creation" | 41 | //config: bool "Support archive creation" |
diff --git a/archival/libarchive/Kbuild.src b/archival/libarchive/Kbuild.src index d2f284b08..1c74250a2 100644 --- a/archival/libarchive/Kbuild.src +++ b/archival/libarchive/Kbuild.src | |||
| @@ -47,6 +47,7 @@ lib-$(CONFIG_DPKG_DEB) += $(DPKG_FILES) | |||
| 47 | 47 | ||
| 48 | lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o | 48 | lib-$(CONFIG_AR) += get_header_ar.o unpack_ar_archive.o |
| 49 | lib-$(CONFIG_CPIO) += get_header_cpio.o | 49 | lib-$(CONFIG_CPIO) += get_header_cpio.o |
| 50 | lib-$(CONFIG_MAKE) += get_header_ar.o unpack_ar_archive.o | ||
| 50 | lib-$(CONFIG_TAR) += get_header_tar.o unsafe_prefix.o | 51 | lib-$(CONFIG_TAR) += get_header_tar.o unsafe_prefix.o |
| 51 | lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o | 52 | lib-$(CONFIG_FEATURE_TAR_TO_COMMAND) += data_extract_to_command.o |
| 52 | lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o | 53 | lib-$(CONFIG_LZOP) += lzo1x_1.o lzo1x_1o.o lzo1x_d.o |
diff --git a/archival/libarchive/unpack_ar_archive.c b/archival/libarchive/unpack_ar_archive.c index 125d424c9..923a0b2ab 100644 --- a/archival/libarchive/unpack_ar_archive.c +++ b/archival/libarchive/unpack_ar_archive.c | |||
| @@ -16,6 +16,10 @@ void FAST_FUNC unpack_ar_archive(archive_handle_t *ar_archive) | |||
| 16 | } | 16 | } |
| 17 | ar_archive->offset += AR_MAGIC_LEN; | 17 | ar_archive->offset += AR_MAGIC_LEN; |
| 18 | 18 | ||
| 19 | while (get_header_ar(ar_archive) == EXIT_SUCCESS) | 19 | while (get_header_ar(ar_archive) == EXIT_SUCCESS) { |
| 20 | continue; | 20 | #if ENABLE_MAKE |
| 21 | free(ar_archive->file_header->name); | ||
| 22 | ar_archive->file_header->name = NULL; | ||
| 23 | #endif | ||
| 24 | } | ||
| 21 | } | 25 | } |
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig index 78e359500..81bbda4dd 100644 --- a/configs/mingw32_defconfig +++ b/configs/mingw32_defconfig | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | # | 1 | # |
| 2 | # Automatically generated make config: don't edit | 2 | # Automatically generated make config: don't edit |
| 3 | # Busybox version: 1.36.0.git | 3 | # Busybox version: 1.36.0.git |
| 4 | # Thu May 12 08:13:00 2022 | 4 | # Thu Jul 7 08:08:14 2022 |
| 5 | # | 5 | # |
| 6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
| 7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
| @@ -831,6 +831,8 @@ CONFIG_FEATURE_LESS_LINENUMS=y | |||
| 831 | CONFIG_FEATURE_LESS_RAW=y | 831 | CONFIG_FEATURE_LESS_RAW=y |
| 832 | CONFIG_FEATURE_LESS_ENV=y | 832 | CONFIG_FEATURE_LESS_ENV=y |
| 833 | # CONFIG_LSSCSI is not set | 833 | # CONFIG_LSSCSI is not set |
| 834 | CONFIG_MAKE=y | ||
| 835 | CONFIG_FEATURE_MAKE_POSIX=y | ||
| 834 | # CONFIG_MAKEDEVS is not set | 836 | # CONFIG_MAKEDEVS is not set |
| 835 | # CONFIG_FEATURE_MAKEDEVS_LEAF is not set | 837 | # CONFIG_FEATURE_MAKEDEVS_LEAF is not set |
| 836 | # CONFIG_FEATURE_MAKEDEVS_TABLE is not set | 838 | # CONFIG_FEATURE_MAKEDEVS_TABLE is not set |
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig index c88da8007..0e54eb78d 100644 --- a/configs/mingw64_defconfig +++ b/configs/mingw64_defconfig | |||
| @@ -1,7 +1,7 @@ | |||
| 1 | # | 1 | # |
| 2 | # Automatically generated make config: don't edit | 2 | # Automatically generated make config: don't edit |
| 3 | # Busybox version: 1.36.0.git | 3 | # Busybox version: 1.36.0.git |
| 4 | # Thu May 12 08:13:00 2022 | 4 | # Thu Jul 7 08:08:14 2022 |
| 5 | # | 5 | # |
| 6 | CONFIG_HAVE_DOT_CONFIG=y | 6 | CONFIG_HAVE_DOT_CONFIG=y |
| 7 | # CONFIG_PLATFORM_POSIX is not set | 7 | # CONFIG_PLATFORM_POSIX is not set |
| @@ -831,6 +831,8 @@ CONFIG_FEATURE_LESS_LINENUMS=y | |||
| 831 | CONFIG_FEATURE_LESS_RAW=y | 831 | CONFIG_FEATURE_LESS_RAW=y |
| 832 | CONFIG_FEATURE_LESS_ENV=y | 832 | CONFIG_FEATURE_LESS_ENV=y |
| 833 | # CONFIG_LSSCSI is not set | 833 | # CONFIG_LSSCSI is not set |
| 834 | CONFIG_MAKE=y | ||
| 835 | CONFIG_FEATURE_MAKE_POSIX=y | ||
| 834 | # CONFIG_MAKEDEVS is not set | 836 | # CONFIG_MAKEDEVS is not set |
| 835 | # CONFIG_FEATURE_MAKEDEVS_LEAF is not set | 837 | # CONFIG_FEATURE_MAKEDEVS_LEAF is not set |
| 836 | # CONFIG_FEATURE_MAKEDEVS_TABLE is not set | 838 | # CONFIG_FEATURE_MAKEDEVS_TABLE is not set |
diff --git a/miscutils/make.c b/miscutils/make.c new file mode 100644 index 000000000..b92819266 --- /dev/null +++ b/miscutils/make.c | |||
| @@ -0,0 +1,2621 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * make implementation for BusyBox | ||
| 4 | * | ||
| 5 | * Based on public domain POSIX make: https://frippery.org/make | ||
| 6 | */ | ||
| 7 | //config:config MAKE | ||
| 8 | //config: bool "make (18 kb)" | ||
| 9 | //config: default n | ||
| 10 | //config: help | ||
| 11 | //config: The make command can be used to maintain files that depend on | ||
| 12 | //config: other files. Normally it's used to build programs from source | ||
| 13 | //config: code but it can be used in other situations too. | ||
| 14 | //config: | ||
| 15 | //config:config FEATURE_MAKE_POSIX | ||
| 16 | //config: bool "Runtime enforcement of POSIX" | ||
| 17 | //config: default n | ||
| 18 | //config: depends on MAKE | ||
| 19 | //config: help | ||
| 20 | //config: Allow strict enforcement of POSIX mode at runtime by: | ||
| 21 | //config: - .POSIX special target in makefile | ||
| 22 | //config: - '--posix' command line option | ||
| 23 | //config: - PDPMAKE_POSIXLY_CORRECT environment variable | ||
| 24 | //config: Enable this if you want to check whether your makefiles are | ||
| 25 | //config: POSIX compliant. This adds about 500 bytes. | ||
| 26 | |||
| 27 | //applet:IF_MAKE(APPLET(make, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 28 | |||
| 29 | //kbuild:lib-$(CONFIG_MAKE) += make.o | ||
| 30 | |||
| 31 | //usage:#define make_trivial_usage | ||
| 32 | //usage: IF_FEATURE_MAKE_POSIX( | ||
| 33 | //usage: "[--posix] [-C DIR] [-f FILE] [j NUM] [-eiknpqrsSt] [MACRO[::]=VAL]... [TARGET]..." | ||
| 34 | //usage: ) | ||
| 35 | //usage: IF_NOT_FEATURE_MAKE_POSIX( | ||
| 36 | //usage: "[-C DIR] [-f FILE] [j NUM] [-eiknpqrsSt] [MACRO[::]=VAL]... [TARGET]..." | ||
| 37 | //usage: ) | ||
| 38 | //usage:#define make_full_usage "\n\n" | ||
| 39 | //usage: "Maintain files based on their dependencies\n" | ||
| 40 | //usage: IF_FEATURE_MAKE_POSIX( | ||
| 41 | //usage: "\n --posix Enforce POSIX mode" | ||
| 42 | //usage: ) | ||
| 43 | //usage: "\n -C DIR Change to DIR" | ||
| 44 | //usage: "\n -f FILE Makefile" | ||
| 45 | //usage: "\n -j NUM Jobs to run in parallel (not implemented)" | ||
| 46 | //usage: "\n -e Environment variables override macros in makefiles" | ||
| 47 | //usage: "\n -i Ignore exit status" | ||
| 48 | //usage: "\n -k Continue on error" | ||
| 49 | //usage: "\n -n Dry run" | ||
| 50 | //usage: "\n -p Print macros and targets" | ||
| 51 | //usage: "\n -q Query target; exit status 1 if not up to date" | ||
| 52 | //usage: "\n -r Don't use built-in rules" | ||
| 53 | //usage: "\n -s Make silently" | ||
| 54 | //usage: "\n -S Stop on error" | ||
| 55 | //usage: "\n -t Touch files instead of making them" | ||
| 56 | |||
| 57 | #include "libbb.h" | ||
| 58 | #include "bb_archive.h" | ||
| 59 | #include "common_bufsiz.h" | ||
| 60 | #include <glob.h> | ||
| 61 | |||
| 62 | #define OPTSTR1 "eij:+knqrsSt" | ||
| 63 | #define OPTSTR2 "pf:*C:*" | ||
| 64 | |||
| 65 | enum { | ||
| 66 | OPT_e = (1 << 0), | ||
| 67 | OPT_i = (1 << 1), | ||
| 68 | OPT_j = (1 << 2), | ||
| 69 | OPT_k = (1 << 3), | ||
| 70 | OPT_n = (1 << 4), | ||
| 71 | OPT_q = (1 << 5), | ||
| 72 | OPT_r = (1 << 6), | ||
| 73 | OPT_s = (1 << 7), | ||
| 74 | OPT_S = (1 << 8), | ||
| 75 | OPT_t = (1 << 9), | ||
| 76 | // These options aren't allowed in MAKEFLAGS | ||
| 77 | OPT_p = (1 << 10), | ||
| 78 | OPT_f = (1 << 11), | ||
| 79 | OPT_C = (1 << 12), | ||
| 80 | // The following aren't command line options and must be last | ||
| 81 | OPT_precious = (1 << 13), | ||
| 82 | OPT_phony = (1 << 14), | ||
| 83 | OPT_include = (1 << 15), | ||
| 84 | OPT_make = (1 << 16), | ||
| 85 | }; | ||
| 86 | |||
| 87 | // Options in OPTSTR1 that aren't included in MAKEFLAGS | ||
| 88 | #define OPT_MASK (~OPT_S) | ||
| 89 | |||
| 90 | #define useenv (opts & OPT_e) | ||
| 91 | #define ignore (opts & OPT_i) | ||
| 92 | #define errcont (opts & OPT_k) | ||
| 93 | #define dryrun (opts & OPT_n) | ||
| 94 | #define print (opts & OPT_p) | ||
| 95 | #define quest (opts & OPT_q) | ||
| 96 | #define norules (opts & OPT_r) | ||
| 97 | #define silent (opts & OPT_s) | ||
| 98 | #define dotouch (opts & OPT_t) | ||
| 99 | #define precious (opts & OPT_precious) | ||
| 100 | #define doinclude (opts & OPT_include) | ||
| 101 | #define domake (opts & OPT_make) | ||
| 102 | |||
| 103 | // A name. This represents a file, either to be made, or pre-existing. | ||
| 104 | struct name { | ||
| 105 | struct name *n_next; // Next in the list of names | ||
| 106 | char *n_name; // Called | ||
| 107 | struct rule *n_rule; // Rules to build this (prerequisites/commands) | ||
| 108 | struct timespec n_tim; // Modification time of this name | ||
| 109 | uint16_t n_flag; // Info about the name | ||
| 110 | }; | ||
| 111 | |||
| 112 | #define N_DOING 0x01 // Name in process of being built | ||
| 113 | #define N_DONE 0x02 // Name looked at | ||
| 114 | #define N_TARGET 0x04 // Name is a target | ||
| 115 | #define N_PRECIOUS 0x08 // Target is precious | ||
| 116 | #define N_DOUBLE 0x10 // Double-colon target | ||
| 117 | #define N_SILENT 0x20 // Build target silently | ||
| 118 | #define N_IGNORE 0x40 // Ignore build errors | ||
| 119 | #define N_SPECIAL 0x80 // Special target | ||
| 120 | #define N_MARK 0x100 // Mark for deduplication | ||
| 121 | #define N_PHONY 0x200 // Name is a phony target | ||
| 122 | |||
| 123 | // List of rules to build a target | ||
| 124 | struct rule { | ||
| 125 | struct rule *r_next; // Next rule | ||
| 126 | struct depend *r_dep; // Prerequisites for this rule | ||
| 127 | struct cmd *r_cmd; // Commands for this rule | ||
| 128 | }; | ||
| 129 | |||
| 130 | // NOTE: the layout of the following two structures must be compatible. | ||
| 131 | // Also, their first two members must be compatible with llist_t. | ||
| 132 | |||
| 133 | // List of prerequisites for a rule | ||
| 134 | struct depend { | ||
| 135 | struct depend *d_next; // Next prerequisite | ||
| 136 | struct name *d_name; // Name of prerequisite | ||
| 137 | int d_refcnt; // Reference count | ||
| 138 | }; | ||
| 139 | |||
| 140 | // List of commands for a rule | ||
| 141 | struct cmd { | ||
| 142 | struct cmd *c_next; // Next command line | ||
| 143 | char *c_cmd; // Text of command line | ||
| 144 | int c_refcnt; // Reference count | ||
| 145 | }; | ||
| 146 | |||
| 147 | // Macro storage | ||
| 148 | struct macro { | ||
| 149 | struct macro *m_next; // Next variable | ||
| 150 | char *m_name; // Its name | ||
| 151 | char *m_val; // Its value | ||
| 152 | bool m_immediate; // Immediate-expansion macro set using ::= | ||
| 153 | bool m_flag; // Infinite loop check | ||
| 154 | uint8_t m_level; // Level at which macro was created | ||
| 155 | }; | ||
| 156 | |||
| 157 | // Flags passed to setmacro() | ||
| 158 | #define M_IMMEDIATE 8 // immediate-expansion macro is being defined | ||
| 159 | #define M_VALID 16 // assert macro name is valid | ||
| 160 | |||
| 161 | #define HTABSIZE 39 | ||
| 162 | |||
| 163 | struct globals { | ||
| 164 | uint32_t opts; | ||
| 165 | const char *makefile; | ||
| 166 | llist_t *makefiles; | ||
| 167 | llist_t *dirs; | ||
| 168 | struct name *namehead[HTABSIZE]; | ||
| 169 | struct macro *macrohead[HTABSIZE]; | ||
| 170 | struct name *firstname; | ||
| 171 | struct name *target; | ||
| 172 | time_t ar_mtime; | ||
| 173 | int lineno; // Physical line number in file | ||
| 174 | int dispno; // Line number for display purposes | ||
| 175 | const char *rulepos; | ||
| 176 | #define IF_MAX 10 | ||
| 177 | uint8_t clevel; | ||
| 178 | uint8_t cstate[IF_MAX + 1]; | ||
| 179 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 180 | bool posix; | ||
| 181 | bool seen_first; | ||
| 182 | #endif | ||
| 183 | int numjobs; | ||
| 184 | } FIX_ALIASING; | ||
| 185 | |||
| 186 | #define G (*(struct globals*)bb_common_bufsiz1) | ||
| 187 | #define INIT_G() do { \ | ||
| 188 | setup_common_bufsiz(); \ | ||
| 189 | } while (0) | ||
| 190 | |||
| 191 | #define opts (G.opts) | ||
| 192 | #define makefile (G.makefile) | ||
| 193 | #define makefiles (G.makefiles) | ||
| 194 | #define dirs (G.dirs) | ||
| 195 | #define namehead (G.namehead) | ||
| 196 | #define macrohead (G.macrohead) | ||
| 197 | #define firstname (G.firstname) | ||
| 198 | #define target (G.target) | ||
| 199 | #define ar_mtime (G.ar_mtime) | ||
| 200 | #define lineno (G.lineno) | ||
| 201 | #define dispno (G.dispno) | ||
| 202 | #define rulepos (G.rulepos) | ||
| 203 | #define clevel (G.clevel) | ||
| 204 | #define cstate (G.cstate) | ||
| 205 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 206 | #define posix (G.posix) | ||
| 207 | #define seen_first (G.seen_first) | ||
| 208 | #else | ||
| 209 | #define posix 0 | ||
| 210 | #endif | ||
| 211 | #define numjobs (G.numjobs) | ||
| 212 | |||
| 213 | static int make(struct name *np, int level); | ||
| 214 | |||
| 215 | // Return TRUE if c is allowed in a POSIX 2017 macro or target name | ||
| 216 | #define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_') | ||
| 217 | // Return TRUE if c is in the POSIX 'portable filename character set' | ||
| 218 | #define isfname(c) (ispname(c) || c == '-') | ||
| 219 | |||
| 220 | /* | ||
| 221 | * Utility functions. | ||
| 222 | */ | ||
| 223 | |||
| 224 | /* | ||
| 225 | * Error handler. Print message, with line number, and exit. | ||
| 226 | */ | ||
| 227 | static void error(const char *msg, ...) NORETURN; | ||
| 228 | static void | ||
| 229 | error(const char *msg, ...) | ||
| 230 | { | ||
| 231 | va_list list; | ||
| 232 | |||
| 233 | if (makefile) { | ||
| 234 | const char *num = itoa(dispno); | ||
| 235 | char *s = malloc(strlen(makefile) + strlen(num) + 2); | ||
| 236 | if (s) { | ||
| 237 | sprintf(s, "%s:%s", makefile, num); | ||
| 238 | applet_name = s; | ||
| 239 | } | ||
| 240 | } | ||
| 241 | va_start(list, msg); | ||
| 242 | bb_verror_msg(msg, list, NULL); | ||
| 243 | va_end(list); | ||
| 244 | exit(2); | ||
| 245 | } | ||
| 246 | |||
| 247 | static void error_unexpected(const char *s) NORETURN; | ||
| 248 | static void | ||
| 249 | error_unexpected(const char *s) | ||
| 250 | { | ||
| 251 | error("unexpected %s", s); | ||
| 252 | } | ||
| 253 | |||
| 254 | static void error_in_inference_rule(const char *s) NORETURN; | ||
| 255 | static void | ||
| 256 | error_in_inference_rule(const char *s) | ||
| 257 | { | ||
| 258 | error("%s in inference rule", s); | ||
| 259 | } | ||
| 260 | |||
| 261 | static char * | ||
| 262 | auto_concat(const char *s1, const char *s2) | ||
| 263 | { | ||
| 264 | return auto_string(xasprintf("%s%s", s1, s2)); | ||
| 265 | } | ||
| 266 | |||
| 267 | /* | ||
| 268 | * Append a word to a space-separated string of words. The first | ||
| 269 | * call should use a NULL pointer for str, subsequent calls should | ||
| 270 | * pass an allocated string which will be freed. | ||
| 271 | */ | ||
| 272 | static char * | ||
| 273 | xappendword(const char *str, const char *word) | ||
| 274 | { | ||
| 275 | char *newstr = str ? xasprintf("%s %s", str, word) : xstrdup(word); | ||
| 276 | free((void *)str); | ||
| 277 | return newstr; | ||
| 278 | } | ||
| 279 | |||
| 280 | static unsigned int | ||
| 281 | getbucket(const char *name) | ||
| 282 | { | ||
| 283 | unsigned int hashval = 0; | ||
| 284 | const unsigned char *p = (unsigned char *)name; | ||
| 285 | |||
| 286 | while (*p) | ||
| 287 | hashval ^= (hashval << 5) + (hashval >> 2) + *p++; | ||
| 288 | return hashval % HTABSIZE; | ||
| 289 | } | ||
| 290 | |||
| 291 | /* | ||
| 292 | * Add a prerequisite to the end of the supplied list. | ||
| 293 | */ | ||
| 294 | static void | ||
| 295 | newdep(struct depend **dphead, struct name *np) | ||
| 296 | { | ||
| 297 | while (*dphead) | ||
| 298 | dphead = &(*dphead)->d_next; | ||
| 299 | *dphead = xzalloc(sizeof(struct depend)); | ||
| 300 | /*(*dphead)->d_next = NULL; - xzalloc did it */ | ||
| 301 | (*dphead)->d_name = np; | ||
| 302 | /*(*dphead)->d_refcnt = 0; */ | ||
| 303 | } | ||
| 304 | |||
| 305 | static void | ||
| 306 | freedeps(struct depend *dp) | ||
| 307 | { | ||
| 308 | if (dp && --dp->d_refcnt <= 0) | ||
| 309 | llist_free((llist_t *)dp, NULL); | ||
| 310 | } | ||
| 311 | |||
| 312 | /* | ||
| 313 | * Add a command to the end of the supplied list of commands. | ||
| 314 | */ | ||
| 315 | static void | ||
| 316 | newcmd(struct cmd **cphead, char *str) | ||
| 317 | { | ||
| 318 | while (isspace(*str)) | ||
| 319 | str++; | ||
| 320 | |||
| 321 | if (*str == '\0') // No command, leave current head unchanged | ||
| 322 | return; | ||
| 323 | |||
| 324 | while (*cphead) | ||
| 325 | cphead = &(*cphead)->c_next; | ||
| 326 | *cphead = xzalloc(sizeof(struct cmd)); | ||
| 327 | /*(*cphead)->c_next = NULL; - xzalloc did it */ | ||
| 328 | (*cphead)->c_cmd = xstrdup(str); | ||
| 329 | /*(*cphead)->c_refcnt = 0; */ | ||
| 330 | } | ||
| 331 | |||
| 332 | static void | ||
| 333 | freecmds(struct cmd *cp) | ||
| 334 | { | ||
| 335 | if (cp && --cp->c_refcnt <= 0) | ||
| 336 | llist_free((llist_t *)cp, free); | ||
| 337 | } | ||
| 338 | |||
| 339 | static struct name * | ||
| 340 | findname(const char *name) | ||
| 341 | { | ||
| 342 | struct name *np = namehead[getbucket(name)]; | ||
| 343 | return (struct name *)llist_find_str((llist_t *)np, name); | ||
| 344 | } | ||
| 345 | |||
| 346 | static int | ||
| 347 | is_valid_target(const char *name) | ||
| 348 | { | ||
| 349 | const char *s; | ||
| 350 | for (s = name; *s; ++s) { | ||
| 351 | if (posix && !ispname(*s)) | ||
| 352 | return FALSE; | ||
| 353 | } | ||
| 354 | return TRUE; | ||
| 355 | } | ||
| 356 | |||
| 357 | /* | ||
| 358 | * Intern a name. Return a pointer to the name struct | ||
| 359 | */ | ||
| 360 | static struct name * | ||
| 361 | newname(const char *name) | ||
| 362 | { | ||
| 363 | struct name *np = findname(name); | ||
| 364 | |||
| 365 | if (np == NULL) { | ||
| 366 | unsigned int bucket; | ||
| 367 | |||
| 368 | if (!is_valid_target(name)) | ||
| 369 | error("invalid target name '%s'", name); | ||
| 370 | |||
| 371 | bucket = getbucket(name); | ||
| 372 | np = xzalloc(sizeof(struct name)); | ||
| 373 | np->n_next = namehead[bucket]; | ||
| 374 | namehead[bucket] = np; | ||
| 375 | np->n_name = xstrdup(name); | ||
| 376 | /*np->n_rule = NULL; - xzalloc did it */ | ||
| 377 | /*np->n_tim = (struct timespec){0, 0}; */ | ||
| 378 | /*np->n_flag = 0; */ | ||
| 379 | } | ||
| 380 | return np; | ||
| 381 | } | ||
| 382 | |||
| 383 | /* | ||
| 384 | * Return the commands on the first rule that has them or NULL. | ||
| 385 | */ | ||
| 386 | static struct cmd * | ||
| 387 | getcmd(struct name *np) | ||
| 388 | { | ||
| 389 | struct rule *rp; | ||
| 390 | |||
| 391 | if (np == NULL) | ||
| 392 | return NULL; | ||
| 393 | |||
| 394 | for (rp = np->n_rule; rp; rp = rp->r_next) | ||
| 395 | if (rp->r_cmd) | ||
| 396 | return rp->r_cmd; | ||
| 397 | return NULL; | ||
| 398 | } | ||
| 399 | |||
| 400 | #if ENABLE_FEATURE_CLEAN_UP | ||
| 401 | static void | ||
| 402 | freenames(void) | ||
| 403 | { | ||
| 404 | int i; | ||
| 405 | struct name *np, *nextnp; | ||
| 406 | |||
| 407 | for (i = 0; i < HTABSIZE; i++) { | ||
| 408 | for (np = namehead[i]; np; np = nextnp) { | ||
| 409 | nextnp = np->n_next; | ||
| 410 | free(np->n_name); | ||
| 411 | freerules(np->n_rule); | ||
| 412 | free(np); | ||
| 413 | } | ||
| 414 | } | ||
| 415 | } | ||
| 416 | #endif | ||
| 417 | |||
| 418 | static void | ||
| 419 | freerules(struct rule *rp) | ||
| 420 | { | ||
| 421 | struct rule *nextrp; | ||
| 422 | |||
| 423 | for (; rp; rp = nextrp) { | ||
| 424 | nextrp = rp->r_next; | ||
| 425 | freedeps(rp->r_dep); | ||
| 426 | freecmds(rp->r_cmd); | ||
| 427 | free(rp); | ||
| 428 | } | ||
| 429 | } | ||
| 430 | |||
| 431 | static void * | ||
| 432 | inc_ref(void *vp) | ||
| 433 | { | ||
| 434 | if (vp) { | ||
| 435 | struct depend *dp = vp; | ||
| 436 | if (dp->d_refcnt == INT_MAX) | ||
| 437 | bb_die_memory_exhausted(); | ||
| 438 | dp->d_refcnt++; | ||
| 439 | } | ||
| 440 | return vp; | ||
| 441 | } | ||
| 442 | |||
| 443 | /* | ||
| 444 | * Add a new rule to a target. This checks to see if commands already | ||
| 445 | * exist for the target. If flag is TRUE the target can have multiple | ||
| 446 | * rules with commands (double-colon rules). | ||
| 447 | * | ||
| 448 | * i) If the name is a special target and there are no prerequisites | ||
| 449 | * or commands to be added remove all prerequisites and commands. | ||
| 450 | * This is necessary when clearing a built-in inference rule. | ||
| 451 | * ii) If name is a special target and has commands, replace them. | ||
| 452 | * This is for redefining commands for an inference rule. | ||
| 453 | */ | ||
| 454 | static void | ||
| 455 | addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag) | ||
| 456 | { | ||
| 457 | struct rule *rp; | ||
| 458 | struct rule **rpp; | ||
| 459 | |||
| 460 | // Can't mix single-colon and double-colon rules | ||
| 461 | if (!posix && (np->n_flag & N_TARGET)) { | ||
| 462 | if (!(np->n_flag & N_DOUBLE) != !flag) // like xor | ||
| 463 | error("inconsistent rules for target %s", np->n_name); | ||
| 464 | } | ||
| 465 | |||
| 466 | // Clear out prerequisites and commands | ||
| 467 | if ((np->n_flag & N_SPECIAL) && !dp && !cp) { | ||
| 468 | if (strcmp(np->n_name, ".PHONY") == 0) | ||
| 469 | return; | ||
| 470 | freerules(np->n_rule); | ||
| 471 | np->n_rule = NULL; | ||
| 472 | return; | ||
| 473 | } | ||
| 474 | |||
| 475 | if (cp && !(np->n_flag & N_DOUBLE) && getcmd(np)) { | ||
| 476 | // Handle the inference rule redefinition case | ||
| 477 | if ((np->n_flag & N_SPECIAL) && !dp) { | ||
| 478 | freerules(np->n_rule); | ||
| 479 | np->n_rule = NULL; | ||
| 480 | } else { | ||
| 481 | error("commands defined twice for target %s", np->n_name); | ||
| 482 | } | ||
| 483 | } | ||
| 484 | |||
| 485 | rpp = &np->n_rule; | ||
| 486 | while (*rpp) | ||
| 487 | rpp = &(*rpp)->r_next; | ||
| 488 | |||
| 489 | *rpp = rp = xzalloc(sizeof(struct rule)); | ||
| 490 | /*rp->r_next = NULL; - xzalloc did it */ | ||
| 491 | rp->r_dep = inc_ref(dp); | ||
| 492 | rp->r_cmd = inc_ref(cp); | ||
| 493 | |||
| 494 | np->n_flag |= N_TARGET; | ||
| 495 | if (flag) | ||
| 496 | np->n_flag |= N_DOUBLE; | ||
| 497 | } | ||
| 498 | |||
| 499 | /* | ||
| 500 | * Macro control for make | ||
| 501 | */ | ||
| 502 | static struct macro * | ||
| 503 | getmp(const char *name) | ||
| 504 | { | ||
| 505 | struct macro *mp = macrohead[getbucket(name)]; | ||
| 506 | return (struct macro *)llist_find_str((llist_t *)mp, name); | ||
| 507 | } | ||
| 508 | |||
| 509 | static int | ||
| 510 | is_valid_macro(const char *name) | ||
| 511 | { | ||
| 512 | const char *s; | ||
| 513 | for (s = name; *s; ++s) { | ||
| 514 | // In POSIX mode only a limited set of characters are guaranteed | ||
| 515 | // to be allowed in macro names. | ||
| 516 | if (posix && !isfname(*s)) | ||
| 517 | return FALSE; | ||
| 518 | // As an extension allow anything that can get through the | ||
| 519 | // input parser, apart from the following. | ||
| 520 | if (*s == '=' || isblank(*s) || iscntrl(*s)) | ||
| 521 | return FALSE; | ||
| 522 | } | ||
| 523 | return TRUE; | ||
| 524 | } | ||
| 525 | |||
| 526 | static void | ||
| 527 | setmacro(const char *name, const char *val, int level) | ||
| 528 | { | ||
| 529 | struct macro *mp; | ||
| 530 | bool valid = level & M_VALID; | ||
| 531 | bool immediate = level & M_IMMEDIATE; | ||
| 532 | |||
| 533 | level &= ~(M_IMMEDIATE | M_VALID); | ||
| 534 | mp = getmp(name); | ||
| 535 | if (mp) { | ||
| 536 | // Don't replace existing macro from a lower level | ||
| 537 | if (level > mp->m_level) | ||
| 538 | return; | ||
| 539 | |||
| 540 | // Replace existing macro | ||
| 541 | free(mp->m_val); | ||
| 542 | } else { | ||
| 543 | // If not defined, allocate space for new | ||
| 544 | unsigned int bucket; | ||
| 545 | |||
| 546 | if (!valid && !is_valid_macro(name)) | ||
| 547 | error("invalid macro name '%s'", name); | ||
| 548 | |||
| 549 | bucket = getbucket(name); | ||
| 550 | mp = xzalloc(sizeof(struct macro)); | ||
| 551 | mp->m_next = macrohead[bucket]; | ||
| 552 | macrohead[bucket] = mp; | ||
| 553 | /* mp->m_flag = FALSE; - xzalloc did it */ | ||
| 554 | mp->m_name = xstrdup(name); | ||
| 555 | } | ||
| 556 | mp->m_immediate = immediate; | ||
| 557 | mp->m_level = level; | ||
| 558 | mp->m_val = xstrdup(val ? val : ""); | ||
| 559 | } | ||
| 560 | |||
| 561 | #if ENABLE_FEATURE_CLEAN_UP | ||
| 562 | static void | ||
| 563 | freemacros(void) | ||
| 564 | { | ||
| 565 | int i; | ||
| 566 | struct macro *mp, *nextmp; | ||
| 567 | |||
| 568 | for (i = 0; i < HTABSIZE; i++) { | ||
| 569 | for (mp = macrohead[i]; mp; mp = nextmp) { | ||
| 570 | nextmp = mp->m_next; | ||
| 571 | free(mp->m_name); | ||
| 572 | free(mp->m_val); | ||
| 573 | free(mp); | ||
| 574 | } | ||
| 575 | } | ||
| 576 | } | ||
| 577 | #endif | ||
| 578 | |||
| 579 | /* | ||
| 580 | * Get modification time of file or archive member | ||
| 581 | */ | ||
| 582 | static void FAST_FUNC | ||
| 583 | record_mtime(const file_header_t *file_header) | ||
| 584 | { | ||
| 585 | ar_mtime = file_header->mtime; | ||
| 586 | } | ||
| 587 | |||
| 588 | static time_t | ||
| 589 | artime(const char *archive, const char *member) | ||
| 590 | { | ||
| 591 | archive_handle_t *archive_handle; | ||
| 592 | |||
| 593 | ar_mtime = 0; | ||
| 594 | archive_handle = init_handle(); | ||
| 595 | archive_handle->src_fd = open(archive, O_RDONLY); | ||
| 596 | if (archive_handle->src_fd != -1) { | ||
| 597 | archive_handle->action_header = record_mtime; | ||
| 598 | archive_handle->filter = filter_accept_list; | ||
| 599 | llist_add_to_end(&archive_handle->accept, (void *)member); | ||
| 600 | unpack_ar_archive(archive_handle); | ||
| 601 | close(archive_handle->src_fd); | ||
| 602 | } | ||
| 603 | |||
| 604 | #if ENABLE_FEATURE_AR_LONG_FILENAMES | ||
| 605 | free(archive_handle->ar__long_names); | ||
| 606 | #endif | ||
| 607 | llist_free(archive_handle->accept, NULL); | ||
| 608 | free(archive_handle->file_header); | ||
| 609 | free(archive_handle); | ||
| 610 | |||
| 611 | return ar_mtime; | ||
| 612 | } | ||
| 613 | |||
| 614 | /* | ||
| 615 | * If the name is of the form 'libname(member.o)' split it into its | ||
| 616 | * name and member parts and set the member pointer to point to the | ||
| 617 | * latter. Otherwise just take a copy of the name and don't alter | ||
| 618 | * the member pointer. | ||
| 619 | * | ||
| 620 | * In either case the return value is an allocated string which must | ||
| 621 | * be freed by the caller. | ||
| 622 | */ | ||
| 623 | static char * | ||
| 624 | splitlib(const char *name, char **member) | ||
| 625 | { | ||
| 626 | char *s, *t; | ||
| 627 | size_t len; | ||
| 628 | |||
| 629 | t = xstrdup(name); | ||
| 630 | s = strchr(t, '('); | ||
| 631 | if (s) { | ||
| 632 | // We have 'libname(member.o)' | ||
| 633 | *s++ = '\0'; | ||
| 634 | len = strlen(s); | ||
| 635 | if (len <= 1 || s[len - 1] != ')' || *t == '\0') | ||
| 636 | error("invalid name '%s'", name); | ||
| 637 | s[len - 1] = '\0'; | ||
| 638 | *member = s; | ||
| 639 | } | ||
| 640 | return t; | ||
| 641 | } | ||
| 642 | |||
| 643 | /* | ||
| 644 | * Get the modification time of a file. Set it to 0 if the file | ||
| 645 | * doesn't exist. | ||
| 646 | */ | ||
| 647 | static void | ||
| 648 | modtime(struct name *np) | ||
| 649 | { | ||
| 650 | char *name, *member = NULL; | ||
| 651 | struct stat info; | ||
| 652 | |||
| 653 | name = splitlib(np->n_name, &member); | ||
| 654 | if (member) { | ||
| 655 | // Looks like library(member) | ||
| 656 | np->n_tim.tv_sec = artime(name, member); | ||
| 657 | np->n_tim.tv_nsec = 0; | ||
| 658 | } else if (stat(name, &info) < 0) { | ||
| 659 | if (errno != ENOENT) | ||
| 660 | bb_perror_msg("can't open %s", name); | ||
| 661 | np->n_tim.tv_sec = 0; | ||
| 662 | np->n_tim.tv_nsec = 0; | ||
| 663 | } else { | ||
| 664 | np->n_tim.tv_sec = info.st_mtim.tv_sec; | ||
| 665 | np->n_tim.tv_nsec = info.st_mtim.tv_nsec; | ||
| 666 | } | ||
| 667 | free(name); | ||
| 668 | } | ||
| 669 | |||
| 670 | /* | ||
| 671 | * Control of the implicit suffix rules | ||
| 672 | */ | ||
| 673 | |||
| 674 | /* | ||
| 675 | * Return a pointer to the suffix of a name (which may be the | ||
| 676 | * terminating NUL if there's no suffix). | ||
| 677 | */ | ||
| 678 | static char * | ||
| 679 | suffix(const char *name) | ||
| 680 | { | ||
| 681 | char *p = strrchr(name, '.'); | ||
| 682 | return p ? p : (char *)name + strlen(name); | ||
| 683 | } | ||
| 684 | |||
| 685 | /* | ||
| 686 | * Dynamic dependency. This routine applies the suffix rules | ||
| 687 | * to try and find a source and a set of rules for a missing | ||
| 688 | * target. NULL is returned on failure. On success the name of | ||
| 689 | * the implicit prerequisite is returned and the details are | ||
| 690 | * placed in the imprule structure provided by the caller. | ||
| 691 | */ | ||
| 692 | static struct name * | ||
| 693 | dyndep(struct name *np, struct rule *imprule) | ||
| 694 | { | ||
| 695 | char *suff, *newsuff; | ||
| 696 | char *base, *name, *member; | ||
| 697 | struct name *xp; // Suffixes | ||
| 698 | struct name *sp; // Suffix rule | ||
| 699 | struct name *pp = NULL; // Implicit prerequisite | ||
| 700 | struct rule *rp; | ||
| 701 | struct depend *dp; | ||
| 702 | bool chain = FALSE; | ||
| 703 | |||
| 704 | member = NULL; | ||
| 705 | name = splitlib(np->n_name, &member); | ||
| 706 | |||
| 707 | suff = xstrdup(suffix(name)); | ||
| 708 | base = member ? member : name; | ||
| 709 | *suffix(base) = '\0'; | ||
| 710 | |||
| 711 | xp = newname(".SUFFIXES"); | ||
| 712 | retry: | ||
| 713 | for (rp = xp->n_rule; rp; rp = rp->r_next) { | ||
| 714 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 715 | // Generate new suffix rule to try | ||
| 716 | newsuff = dp->d_name->n_name; | ||
| 717 | sp = findname(auto_concat(newsuff, suff)); | ||
| 718 | if (sp && sp->n_rule) { | ||
| 719 | // Generate a name for an implicit prerequisite | ||
| 720 | pp = newname(auto_concat(base, newsuff)); | ||
| 721 | if (!pp->n_tim.tv_sec) | ||
| 722 | modtime(pp); | ||
| 723 | if ((!chain && (pp->n_tim.tv_sec || getcmd(pp))) || | ||
| 724 | (chain && dyndep(pp, NULL))) { | ||
| 725 | // Prerequisite exists or we know how to make it | ||
| 726 | if (imprule) { | ||
| 727 | dp = NULL; | ||
| 728 | newdep(&dp, pp); | ||
| 729 | imprule->r_dep = dp; | ||
| 730 | imprule->r_cmd = sp->n_rule->r_cmd; | ||
| 731 | } | ||
| 732 | goto finish; | ||
| 733 | } | ||
| 734 | pp = NULL; | ||
| 735 | } | ||
| 736 | } | ||
| 737 | } | ||
| 738 | // If we didn't find an existing file or an explicit rule try | ||
| 739 | // again, this time looking for a chained inference rule. | ||
| 740 | if (!posix && !chain) { | ||
| 741 | chain = TRUE; | ||
| 742 | goto retry; | ||
| 743 | } | ||
| 744 | finish: | ||
| 745 | free(suff); | ||
| 746 | free(name); | ||
| 747 | return pp; | ||
| 748 | } | ||
| 749 | |||
| 750 | #define RULES \ | ||
| 751 | ".SUFFIXES:.o .c .y .l .a .sh .f\n" \ | ||
| 752 | ".c.o:\n" \ | ||
| 753 | " $(CC) $(CFLAGS) -c $<\n" \ | ||
| 754 | ".f.o:\n" \ | ||
| 755 | " $(FC) $(FFLAGS) -c $<\n" \ | ||
| 756 | ".y.o:\n" \ | ||
| 757 | " $(YACC) $(YFLAGS) $<\n" \ | ||
| 758 | " $(CC) $(CFLAGS) -c y.tab.c\n" \ | ||
| 759 | " rm -f y.tab.c\n" \ | ||
| 760 | " mv y.tab.o $@\n" \ | ||
| 761 | ".y.c:\n" \ | ||
| 762 | " $(YACC) $(YFLAGS) $<\n" \ | ||
| 763 | " mv y.tab.c $@\n" \ | ||
| 764 | ".l.o:\n" \ | ||
| 765 | " $(LEX) $(LFLAGS) $<\n" \ | ||
| 766 | " $(CC) $(CFLAGS) -c lex.yy.c\n" \ | ||
| 767 | " rm -f lex.yy.c\n" \ | ||
| 768 | " mv lex.yy.o $@\n" \ | ||
| 769 | ".l.c:\n" \ | ||
| 770 | " $(LEX) $(LFLAGS) $<\n" \ | ||
| 771 | " mv lex.yy.c $@\n" \ | ||
| 772 | ".c.a:\n" \ | ||
| 773 | " $(CC) -c $(CFLAGS) $<\n" \ | ||
| 774 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ | ||
| 775 | " rm -f $*.o\n" \ | ||
| 776 | ".f.a:\n" \ | ||
| 777 | " $(FC) -c $(FFLAGS) $<\n" \ | ||
| 778 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ | ||
| 779 | " rm -f $*.o\n" \ | ||
| 780 | ".c:\n" \ | ||
| 781 | " $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<\n" \ | ||
| 782 | ".f:\n" \ | ||
| 783 | " $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<\n" \ | ||
| 784 | ".sh:\n" \ | ||
| 785 | " cp $< $@\n" \ | ||
| 786 | " chmod a+x $@\n" | ||
| 787 | |||
| 788 | #define MACROS \ | ||
| 789 | "CC=c99\n" \ | ||
| 790 | "CFLAGS=-O1\n" \ | ||
| 791 | "FC=fort77\n" \ | ||
| 792 | "FFLAGS=-O1\n" \ | ||
| 793 | "YACC=yacc\n" \ | ||
| 794 | "YFLAGS=\n" \ | ||
| 795 | "LEX=lex\n" \ | ||
| 796 | "LFLAGS=\n" \ | ||
| 797 | "AR=ar\n" \ | ||
| 798 | "ARFLAGS=-rv\n" \ | ||
| 799 | "LDFLAGS=\n" | ||
| 800 | |||
| 801 | /* | ||
| 802 | * Read the built-in rules using a fake fgets-like interface. | ||
| 803 | */ | ||
| 804 | static char * | ||
| 805 | getrules(char *s, int size) | ||
| 806 | { | ||
| 807 | char *r = s; | ||
| 808 | |||
| 809 | if (rulepos == NULL) | ||
| 810 | rulepos = (RULES MACROS) + (norules ? sizeof(RULES) - 1 : 0); | ||
| 811 | |||
| 812 | if (*rulepos == '\0') | ||
| 813 | return NULL; | ||
| 814 | |||
| 815 | while (--size) { | ||
| 816 | if ((*r++ = *rulepos++) == '\n') | ||
| 817 | break; | ||
| 818 | } | ||
| 819 | *r = '\0'; | ||
| 820 | return s; | ||
| 821 | } | ||
| 822 | |||
| 823 | /* | ||
| 824 | * Parse a makefile | ||
| 825 | */ | ||
| 826 | |||
| 827 | /* | ||
| 828 | * Return a pointer to the next blank-delimited word or NULL if | ||
| 829 | * there are none left. | ||
| 830 | */ | ||
| 831 | static char * | ||
| 832 | gettok(char **ptr) | ||
| 833 | { | ||
| 834 | char *p; | ||
| 835 | |||
| 836 | while (isblank(**ptr)) // Skip blanks | ||
| 837 | (*ptr)++; | ||
| 838 | |||
| 839 | if (**ptr == '\0') // Nothing after blanks | ||
| 840 | return NULL; | ||
| 841 | |||
| 842 | p = *ptr; // Word starts here | ||
| 843 | |||
| 844 | while (**ptr != '\0' && !isblank(**ptr)) | ||
| 845 | (*ptr)++; // Find end of word | ||
| 846 | |||
| 847 | // Terminate token and move on unless already at end of string | ||
| 848 | if (**ptr != '\0') | ||
| 849 | *(*ptr)++ = '\0'; | ||
| 850 | |||
| 851 | return(p); | ||
| 852 | } | ||
| 853 | |||
| 854 | /* | ||
| 855 | * Skip over (possibly adjacent or nested) macro expansions. | ||
| 856 | */ | ||
| 857 | static char * | ||
| 858 | skip_macro(const char *s) | ||
| 859 | { | ||
| 860 | while (*s && s[0] == '$') { | ||
| 861 | if (s[1] == '(' || s[1] == '{') { | ||
| 862 | char end = *++s == '(' ? ')' : '}'; | ||
| 863 | while (*s && *s != end) | ||
| 864 | s = skip_macro(s + 1); | ||
| 865 | if (*s == end) | ||
| 866 | ++s; | ||
| 867 | } else if (s[1] != '\0') { | ||
| 868 | s += 2; | ||
| 869 | } else { | ||
| 870 | break; | ||
| 871 | } | ||
| 872 | } | ||
| 873 | return (char *)s; | ||
| 874 | } | ||
| 875 | |||
| 876 | /* | ||
| 877 | * Process each whitespace-separated word in the input string: | ||
| 878 | * | ||
| 879 | * - replace paths with their directory or filename part | ||
| 880 | * - replace prefixes and suffixes | ||
| 881 | * | ||
| 882 | * Returns an allocated string or NULL if the input is unmodified. | ||
| 883 | */ | ||
| 884 | static char * | ||
| 885 | modify_words(const char *val, int modifier, size_t lenf, | ||
| 886 | const char *find_pref, const char *repl_pref, | ||
| 887 | const char *find_suff, const char *repl_suff) | ||
| 888 | { | ||
| 889 | char *s, *copy, *word, *sep, *buf = NULL; | ||
| 890 | size_t find_pref_len = 0, find_suff_len = 0; | ||
| 891 | |||
| 892 | if (!modifier && !lenf) | ||
| 893 | return buf; | ||
| 894 | |||
| 895 | if (find_pref) { | ||
| 896 | // get length of find prefix, e.g: src/ | ||
| 897 | find_pref_len = strlen(find_pref); | ||
| 898 | // get length of find suffix, e.g: .c | ||
| 899 | find_suff_len = lenf - find_pref_len - 1; | ||
| 900 | } | ||
| 901 | |||
| 902 | s = copy = xstrdup(val); | ||
| 903 | while ((word = gettok(&s)) != NULL) { | ||
| 904 | if (modifier) { | ||
| 905 | sep = strrchr(word, '/'); | ||
| 906 | if (modifier == 'D') { | ||
| 907 | if (!sep) { | ||
| 908 | word[0] = '.'; // no '/', return "." | ||
| 909 | sep = word + 1; | ||
| 910 | } else if (sep == word) { | ||
| 911 | // '/' at start of word, return "/" | ||
| 912 | sep = word + 1; | ||
| 913 | } | ||
| 914 | // else terminate at separator | ||
| 915 | *sep = '\0'; | ||
| 916 | } else if (/* modifier == 'F' && */ sep) { | ||
| 917 | word = sep + 1; | ||
| 918 | } | ||
| 919 | } | ||
| 920 | if (lenf) { | ||
| 921 | size_t lenw = strlen(word); | ||
| 922 | // This code implements pattern macro expansions: | ||
| 923 | // https://austingroupbugs.net/view.php?id=519 | ||
| 924 | // | ||
| 925 | // find: <prefix>%<suffix> | ||
| 926 | // example: src/%.c | ||
| 927 | if (lenw >= lenf - 1 && find_pref) { | ||
| 928 | // If prefix and suffix of word match find_pref and | ||
| 929 | // find_suff, then do substitution. | ||
| 930 | if (strncmp(word, find_pref, find_pref_len) == 0 && | ||
| 931 | strcmp(word + lenw - find_suff_len, find_suff) == 0) { | ||
| 932 | // replace: <prefix>[%<suffix>] | ||
| 933 | // example: build/%.o or build/all.o (notice no %) | ||
| 934 | // If repl_suff is NULL, replace whole word with repl_pref. | ||
| 935 | if (!repl_suff) { | ||
| 936 | word = xstrdup(repl_pref); | ||
| 937 | } else { | ||
| 938 | word[lenw - find_suff_len] = '\0'; | ||
| 939 | word = xasprintf("%s%s%s", repl_pref, | ||
| 940 | word + find_pref_len, repl_suff); | ||
| 941 | } | ||
| 942 | word = auto_string(word); | ||
| 943 | } | ||
| 944 | } else if (lenw >= lenf && | ||
| 945 | strcmp(word + lenw - lenf, find_suff) == 0) { | ||
| 946 | word[lenw - lenf] = '\0'; | ||
| 947 | word = auto_concat(word, repl_suff); | ||
| 948 | } | ||
| 949 | } | ||
| 950 | buf = xappendword(buf, word); | ||
| 951 | } | ||
| 952 | free(copy); | ||
| 953 | return buf; | ||
| 954 | } | ||
| 955 | |||
| 956 | /* | ||
| 957 | * Return a pointer to the next instance of a given character. Macro | ||
| 958 | * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't | ||
| 959 | * detected as separators for rules or macro definitions. | ||
| 960 | */ | ||
| 961 | static char * | ||
| 962 | find_char(const char *str, int c) | ||
| 963 | { | ||
| 964 | const char *s; | ||
| 965 | |||
| 966 | for (s = skip_macro(str); *s; s = skip_macro(s + 1)) { | ||
| 967 | if (*s == c) | ||
| 968 | return (char *)s; | ||
| 969 | } | ||
| 970 | return NULL; | ||
| 971 | } | ||
| 972 | |||
| 973 | /* | ||
| 974 | * Recursively expand any macros in str to an allocated string. | ||
| 975 | */ | ||
| 976 | static char * | ||
| 977 | expand_macros(const char *str, int except_dollar) | ||
| 978 | { | ||
| 979 | char *exp, *newexp, *s, *t, *p, *q, *name; | ||
| 980 | char *find, *replace, *modified; | ||
| 981 | char *expval, *expfind, *find_suff, *repl_suff; | ||
| 982 | char *find_pref = NULL, *repl_pref = NULL; | ||
| 983 | size_t lenf; | ||
| 984 | char modifier; | ||
| 985 | struct macro *mp; | ||
| 986 | |||
| 987 | exp = xstrdup(str); | ||
| 988 | for (t = exp; *t; t++) { | ||
| 989 | if (*t == '$') { | ||
| 990 | if (t[1] == '\0') { | ||
| 991 | break; | ||
| 992 | } | ||
| 993 | if (t[1] == '$' && except_dollar) { | ||
| 994 | t++; | ||
| 995 | continue; | ||
| 996 | } | ||
| 997 | // Need to expand a macro. Find its extent (s to t inclusive) | ||
| 998 | // and take a copy of its content. | ||
| 999 | s = t; | ||
| 1000 | t++; | ||
| 1001 | if (*t == '{' || *t == '(') { | ||
| 1002 | t = find_char(t, *t == '{' ? '}' : ')'); | ||
| 1003 | if (t == NULL) | ||
| 1004 | error("unterminated variable '%s'", s); | ||
| 1005 | name = xstrndup(s + 2, t - s - 2); | ||
| 1006 | } else { | ||
| 1007 | name = xzalloc(2); | ||
| 1008 | name[0] = *t; | ||
| 1009 | /*name[1] = '\0'; - xzalloc did it */ | ||
| 1010 | } | ||
| 1011 | |||
| 1012 | // Only do suffix replacement or pattern macro expansion | ||
| 1013 | // if both ':' and '=' are found. This is indicated by | ||
| 1014 | // lenf != 0. | ||
| 1015 | expfind = NULL; | ||
| 1016 | find_suff = repl_suff = NULL; | ||
| 1017 | lenf = 0; | ||
| 1018 | if ((find = find_char(name, ':'))) { | ||
| 1019 | *find++ = '\0'; | ||
| 1020 | expfind = expand_macros(find, FALSE); | ||
| 1021 | if ((replace = find_char(expfind, '='))) { | ||
| 1022 | *replace++ = '\0'; | ||
| 1023 | lenf = strlen(expfind); | ||
| 1024 | if (!posix && (find_suff = strchr(expfind, '%'))) { | ||
| 1025 | find_pref = expfind; | ||
| 1026 | repl_pref = replace; | ||
| 1027 | *find_suff++ = '\0'; | ||
| 1028 | if ((repl_suff = strchr(replace, '%'))) | ||
| 1029 | *repl_suff++ = '\0'; | ||
| 1030 | } else { | ||
| 1031 | find_suff = expfind; | ||
| 1032 | repl_suff = replace; | ||
| 1033 | } | ||
| 1034 | } | ||
| 1035 | } | ||
| 1036 | |||
| 1037 | p = q = name; | ||
| 1038 | // If not in POSIX mode expand macros in the name. | ||
| 1039 | if (!posix) { | ||
| 1040 | char *expname = expand_macros(name, FALSE); | ||
| 1041 | free(name); | ||
| 1042 | name = expname; | ||
| 1043 | } else { | ||
| 1044 | // Skip over nested expansions in name | ||
| 1045 | do { | ||
| 1046 | *q++ = *p; | ||
| 1047 | } while ((p = skip_macro(p + 1)) && *p); | ||
| 1048 | } | ||
| 1049 | |||
| 1050 | // The internal macros support 'D' and 'F' modifiers | ||
| 1051 | modifier = '\0'; | ||
| 1052 | switch (name[0]) { | ||
| 1053 | case '^': | ||
| 1054 | if (posix) | ||
| 1055 | break; | ||
| 1056 | // fall through | ||
| 1057 | case '@': case '%': case '?': case '<': case '*': | ||
| 1058 | if ((name[1] == 'D' || name[1] == 'F') && name[2] == '\0') { | ||
| 1059 | modifier = name[1]; | ||
| 1060 | name[1] = '\0'; | ||
| 1061 | } | ||
| 1062 | break; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | modified = NULL; | ||
| 1066 | if ((mp = getmp(name))) { | ||
| 1067 | // Recursive expansion | ||
| 1068 | if (mp->m_flag) | ||
| 1069 | error("recursive macro %s", name); | ||
| 1070 | // Note if we've expanded $(MAKE) | ||
| 1071 | if (strcmp(name, "MAKE") == 0) | ||
| 1072 | opts |= OPT_make; | ||
| 1073 | mp->m_flag = TRUE; | ||
| 1074 | expval = expand_macros(mp->m_val, FALSE); | ||
| 1075 | mp->m_flag = FALSE; | ||
| 1076 | modified = modify_words(expval, modifier, lenf, | ||
| 1077 | find_pref, repl_pref, find_suff, repl_suff); | ||
| 1078 | if (modified) | ||
| 1079 | free(expval); | ||
| 1080 | else | ||
| 1081 | modified = expval; | ||
| 1082 | } | ||
| 1083 | free(name); | ||
| 1084 | free(expfind); | ||
| 1085 | |||
| 1086 | if (modified && *modified) { | ||
| 1087 | // The text to be replaced by the macro expansion is | ||
| 1088 | // from s to t inclusive. | ||
| 1089 | *s = '\0'; | ||
| 1090 | newexp = xasprintf("%s%s%s", exp, modified, t + 1); | ||
| 1091 | t = newexp + (s - exp) + strlen(modified) - 1; | ||
| 1092 | free(exp); | ||
| 1093 | exp = newexp; | ||
| 1094 | } else { | ||
| 1095 | // Macro wasn't expanded or expanded to nothing. | ||
| 1096 | // Close the space occupied by the macro reference. | ||
| 1097 | q = t + 1; | ||
| 1098 | t = s - 1; | ||
| 1099 | while ((*s++ = *q++)) | ||
| 1100 | continue; | ||
| 1101 | } | ||
| 1102 | free(modified); | ||
| 1103 | } | ||
| 1104 | } | ||
| 1105 | return exp; | ||
| 1106 | } | ||
| 1107 | |||
| 1108 | /* | ||
| 1109 | * Process a non-command line | ||
| 1110 | */ | ||
| 1111 | static char * | ||
| 1112 | process_line(char *s) | ||
| 1113 | { | ||
| 1114 | char *r, *t; | ||
| 1115 | |||
| 1116 | // Skip leading blanks | ||
| 1117 | while (isblank(*s)) | ||
| 1118 | s++; | ||
| 1119 | r = s; | ||
| 1120 | |||
| 1121 | // Strip comment | ||
| 1122 | t = strchr(s, '#'); | ||
| 1123 | if (t) | ||
| 1124 | *t = '\0'; | ||
| 1125 | |||
| 1126 | // Replace escaped newline and any leading white space on the | ||
| 1127 | // following line with a single space. Stop processing at a | ||
| 1128 | // non-escaped newline. | ||
| 1129 | for (t = s; *s && *s != '\n'; ) { | ||
| 1130 | if (s[0] == '\\' && s[1] == '\n') { | ||
| 1131 | s += 2; | ||
| 1132 | while (isspace(*s)) | ||
| 1133 | ++s; | ||
| 1134 | *t++ = ' '; | ||
| 1135 | } else { | ||
| 1136 | *t++ = *s++; | ||
| 1137 | } | ||
| 1138 | } | ||
| 1139 | *t = '\0'; | ||
| 1140 | |||
| 1141 | return r; | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | enum { | ||
| 1145 | INITIAL = 0, | ||
| 1146 | SKIP_LINE = 1 << 0, | ||
| 1147 | EXPECT_ELSE = 1 << 1, | ||
| 1148 | GOT_MATCH = 1 << 2 | ||
| 1149 | }; | ||
| 1150 | |||
| 1151 | #define IFDEF 0 | ||
| 1152 | #define IFNDEF 1 | ||
| 1153 | #define ELSE 0 | ||
| 1154 | #define ENDIF 1 | ||
| 1155 | |||
| 1156 | /* | ||
| 1157 | * Process conditional directives and return TRUE if the current line | ||
| 1158 | * should be skipped. | ||
| 1159 | */ | ||
| 1160 | static int | ||
| 1161 | skip_line(const char *str1) | ||
| 1162 | { | ||
| 1163 | char *copy, *q, *token, *next_token; | ||
| 1164 | bool new_level = TRUE; | ||
| 1165 | // Default is to return skip flag for current level | ||
| 1166 | int ret = cstate[clevel] & SKIP_LINE; | ||
| 1167 | int key; | ||
| 1168 | |||
| 1169 | if (*str1 == '\t') | ||
| 1170 | return ret; | ||
| 1171 | |||
| 1172 | copy = xstrdup(str1); | ||
| 1173 | q = process_line(copy); | ||
| 1174 | if ((token = gettok(&q)) != NULL) { | ||
| 1175 | next_token = gettok(&q); | ||
| 1176 | |||
| 1177 | switch (index_in_strings("else\0endif\0", token)) { | ||
| 1178 | case ENDIF: | ||
| 1179 | if (next_token != NULL) | ||
| 1180 | error_unexpected("text"); | ||
| 1181 | if (clevel == 0) | ||
| 1182 | error_unexpected(token); | ||
| 1183 | --clevel; | ||
| 1184 | ret = TRUE; | ||
| 1185 | goto end; | ||
| 1186 | case ELSE: | ||
| 1187 | if (!(cstate[clevel] & EXPECT_ELSE)) | ||
| 1188 | error_unexpected(token); | ||
| 1189 | |||
| 1190 | // If an earlier condition matched we'll now skip lines. | ||
| 1191 | // If not we don't, though an 'else if' may override this. | ||
| 1192 | if ((cstate[clevel] & GOT_MATCH)) | ||
| 1193 | cstate[clevel] |= SKIP_LINE; | ||
| 1194 | else | ||
| 1195 | cstate[clevel] &= ~SKIP_LINE; | ||
| 1196 | |||
| 1197 | if (next_token == NULL) { | ||
| 1198 | // Simple else with no conditional directive | ||
| 1199 | cstate[clevel] &= ~EXPECT_ELSE; | ||
| 1200 | ret = TRUE; | ||
| 1201 | goto end; | ||
| 1202 | } else { | ||
| 1203 | // A conditional directive is now required ('else if'). | ||
| 1204 | token = next_token; | ||
| 1205 | next_token = gettok(&q); | ||
| 1206 | new_level = FALSE; | ||
| 1207 | } | ||
| 1208 | break; | ||
| 1209 | } | ||
| 1210 | |||
| 1211 | key = index_in_strings("ifdef\0ifndef\0", token); | ||
| 1212 | if (key != -1) { | ||
| 1213 | if (next_token != NULL && gettok(&q) == NULL) { | ||
| 1214 | if (new_level) { | ||
| 1215 | // Start a new level. | ||
| 1216 | if (clevel == IF_MAX) | ||
| 1217 | error("nesting too deep"); | ||
| 1218 | ++clevel; | ||
| 1219 | cstate[clevel] = EXPECT_ELSE | SKIP_LINE; | ||
| 1220 | // If we were skipping lines at the previous level | ||
| 1221 | // we need to continue doing that unconditionally | ||
| 1222 | // at the new level. | ||
| 1223 | if ((cstate[clevel - 1] & SKIP_LINE)) | ||
| 1224 | cstate[clevel] |= GOT_MATCH; | ||
| 1225 | } | ||
| 1226 | |||
| 1227 | if (!(cstate[clevel] & GOT_MATCH)) { | ||
| 1228 | char *t = expand_macros(next_token, FALSE); | ||
| 1229 | struct macro *mp = getmp(t); | ||
| 1230 | int match = mp != NULL && mp->m_val[0] != '\0'; | ||
| 1231 | |||
| 1232 | if (key == IFNDEF) | ||
| 1233 | match = !match; | ||
| 1234 | if (match) { | ||
| 1235 | cstate[clevel] &= ~SKIP_LINE; | ||
| 1236 | cstate[clevel] |= GOT_MATCH; | ||
| 1237 | } | ||
| 1238 | free(t); | ||
| 1239 | } | ||
| 1240 | } else { | ||
| 1241 | error("invalid condition"); | ||
| 1242 | } | ||
| 1243 | ret = TRUE; | ||
| 1244 | } else if (!new_level) { | ||
| 1245 | error("missing conditional"); | ||
| 1246 | } | ||
| 1247 | } | ||
| 1248 | end: | ||
| 1249 | free(copy); | ||
| 1250 | return ret; | ||
| 1251 | } | ||
| 1252 | |||
| 1253 | /* | ||
| 1254 | * If fd is NULL read the built-in rules. Otherwise read from the | ||
| 1255 | * specified file descriptor. | ||
| 1256 | */ | ||
| 1257 | static char * | ||
| 1258 | make_fgets(char *s, int size, FILE *fd) | ||
| 1259 | { | ||
| 1260 | return fd ? fgets(s, size, fd) : getrules(s, size); | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | /* | ||
| 1264 | * Read a newline-terminated line into an allocated string. | ||
| 1265 | * Backslash-escaped newlines don't terminate the line. | ||
| 1266 | * Ignore comment lines. Return NULL on EOF. | ||
| 1267 | */ | ||
| 1268 | static char * | ||
| 1269 | readline(FILE *fd) | ||
| 1270 | { | ||
| 1271 | char *p, *str; | ||
| 1272 | int pos = 0; | ||
| 1273 | int len = 256; | ||
| 1274 | |||
| 1275 | str = xmalloc(len); | ||
| 1276 | |||
| 1277 | for (;;) { | ||
| 1278 | if (make_fgets(str + pos, len - pos, fd) == NULL) { | ||
| 1279 | if (pos) | ||
| 1280 | return str; | ||
| 1281 | free(str); | ||
| 1282 | return NULL; // EOF | ||
| 1283 | } | ||
| 1284 | |||
| 1285 | if ((p = strchr(str + pos, '\n')) == NULL) { | ||
| 1286 | // Need more room | ||
| 1287 | pos = len - 1; | ||
| 1288 | len += 256; | ||
| 1289 | str = xrealloc(str, len); | ||
| 1290 | continue; | ||
| 1291 | } | ||
| 1292 | lineno++; | ||
| 1293 | |||
| 1294 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1295 | // Remove CR before LF | ||
| 1296 | if (p != str && p[-1] == '\r') { | ||
| 1297 | p[-1] = '\n'; | ||
| 1298 | *p-- = '\0'; | ||
| 1299 | } | ||
| 1300 | #endif | ||
| 1301 | // Keep going if newline has been escaped | ||
| 1302 | if (p != str && p[-1] == '\\') { | ||
| 1303 | pos = p - str + 1; | ||
| 1304 | continue; | ||
| 1305 | } | ||
| 1306 | dispno = lineno; | ||
| 1307 | |||
| 1308 | // Check for comment lines and lines that are conditionally skipped. | ||
| 1309 | p = str; | ||
| 1310 | while (isblank(*p)) | ||
| 1311 | p++; | ||
| 1312 | |||
| 1313 | if (*p != '\n' && *str != '#' && (posix || !skip_line(str))) | ||
| 1314 | return str; | ||
| 1315 | |||
| 1316 | pos = 0; | ||
| 1317 | } | ||
| 1318 | } | ||
| 1319 | |||
| 1320 | /* | ||
| 1321 | * Return TRUE if the argument is a known suffix. | ||
| 1322 | */ | ||
| 1323 | static int | ||
| 1324 | is_suffix(const char *s) | ||
| 1325 | { | ||
| 1326 | struct name *np; | ||
| 1327 | struct rule *rp; | ||
| 1328 | struct depend *dp; | ||
| 1329 | |||
| 1330 | np = newname(".SUFFIXES"); | ||
| 1331 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 1332 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 1333 | if (strcmp(s, dp->d_name->n_name) == 0) { | ||
| 1334 | return TRUE; | ||
| 1335 | } | ||
| 1336 | } | ||
| 1337 | } | ||
| 1338 | return FALSE; | ||
| 1339 | } | ||
| 1340 | |||
| 1341 | #define T_NORMAL 0 | ||
| 1342 | #define T_SPECIAL 1 | ||
| 1343 | #define T_INFERENCE 2 | ||
| 1344 | /* | ||
| 1345 | * Determine if the argument is a special target and return a set | ||
| 1346 | * of flags indicating its properties. | ||
| 1347 | */ | ||
| 1348 | static int | ||
| 1349 | target_type(char *s) | ||
| 1350 | { | ||
| 1351 | char *sfx; | ||
| 1352 | int ret; | ||
| 1353 | static const char *s_name[] = { | ||
| 1354 | ".DEFAULT", | ||
| 1355 | ".POSIX", | ||
| 1356 | ".IGNORE", | ||
| 1357 | ".PRECIOUS", | ||
| 1358 | ".SILENT", | ||
| 1359 | ".SUFFIXES", | ||
| 1360 | ".PHONY", | ||
| 1361 | }; | ||
| 1362 | |||
| 1363 | if (*s != '.') | ||
| 1364 | return T_NORMAL; | ||
| 1365 | |||
| 1366 | // Check for one of the known special targets | ||
| 1367 | for (ret = 0; ret < ARRAY_SIZE(s_name); ret++) | ||
| 1368 | if (strcmp(s_name[ret], s) == 0) | ||
| 1369 | return T_SPECIAL; | ||
| 1370 | |||
| 1371 | // Check for an inference rule | ||
| 1372 | ret = T_NORMAL; | ||
| 1373 | sfx = suffix(s); | ||
| 1374 | if (is_suffix(sfx)) { | ||
| 1375 | if (s == sfx) { // Single suffix rule | ||
| 1376 | ret = T_INFERENCE; | ||
| 1377 | } else { | ||
| 1378 | // Suffix is valid, check that prefix is too | ||
| 1379 | *sfx = '\0'; | ||
| 1380 | if (is_suffix(s)) | ||
| 1381 | ret = T_INFERENCE; | ||
| 1382 | *sfx = '.'; | ||
| 1383 | } | ||
| 1384 | } | ||
| 1385 | return ret; | ||
| 1386 | } | ||
| 1387 | |||
| 1388 | static int | ||
| 1389 | ends_with_bracket(const char *s) | ||
| 1390 | { | ||
| 1391 | return last_char_is(s, ')') != NULL; | ||
| 1392 | } | ||
| 1393 | |||
| 1394 | /* | ||
| 1395 | * Process a command line | ||
| 1396 | */ | ||
| 1397 | static char * | ||
| 1398 | process_command(char *s) | ||
| 1399 | { | ||
| 1400 | char *t, *u; | ||
| 1401 | |||
| 1402 | // Remove tab following escaped newline. Stop processing at a | ||
| 1403 | // non-escaped newline. | ||
| 1404 | for (t = u = s; *u && *u != '\n'; u++) { | ||
| 1405 | if (u[0] == '\\' && u[1] == '\n' && u[2] == '\t') { | ||
| 1406 | *t++ = *u++; | ||
| 1407 | *t++ = *u++; | ||
| 1408 | } else { | ||
| 1409 | *t++ = *u; | ||
| 1410 | } | ||
| 1411 | } | ||
| 1412 | *t = '\0'; | ||
| 1413 | return s; | ||
| 1414 | } | ||
| 1415 | |||
| 1416 | static char * | ||
| 1417 | run_command(const char *cmd) | ||
| 1418 | { | ||
| 1419 | FILE *fd; | ||
| 1420 | char *s, *val = NULL; | ||
| 1421 | char buf[256]; | ||
| 1422 | size_t len = 0, nread; | ||
| 1423 | |||
| 1424 | if ((fd = popen(cmd, "r")) == NULL) | ||
| 1425 | return val; | ||
| 1426 | |||
| 1427 | for (;;) { | ||
| 1428 | nread = fread(buf, 1, sizeof(buf), fd); | ||
| 1429 | if (nread == 0) | ||
| 1430 | break; | ||
| 1431 | |||
| 1432 | val = xrealloc(val, len + nread + 1); | ||
| 1433 | memcpy(val + len, buf, nread); | ||
| 1434 | len += nread; | ||
| 1435 | val[len] = '\0'; | ||
| 1436 | } | ||
| 1437 | pclose(fd); | ||
| 1438 | |||
| 1439 | if (val) { | ||
| 1440 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1441 | len = remove_cr(val, len + 1) - 1; | ||
| 1442 | if (len == 0) { | ||
| 1443 | free(val); | ||
| 1444 | return NULL; | ||
| 1445 | } | ||
| 1446 | #endif | ||
| 1447 | // Remove one newline from the end (BSD compatibility) | ||
| 1448 | if (val[len - 1] == '\n') | ||
| 1449 | val[len - 1] = '\0'; | ||
| 1450 | // Other newlines are changed to spaces | ||
| 1451 | for (s = val; *s; ++s) { | ||
| 1452 | if (*s == '\n') | ||
| 1453 | *s = ' '; | ||
| 1454 | } | ||
| 1455 | } | ||
| 1456 | return val; | ||
| 1457 | } | ||
| 1458 | |||
| 1459 | /* | ||
| 1460 | * Check for an unescaped wildcard character | ||
| 1461 | */ | ||
| 1462 | static int wildchar(const char *p) | ||
| 1463 | { | ||
| 1464 | while (*p) { | ||
| 1465 | switch (*p) { | ||
| 1466 | case '?': | ||
| 1467 | case '*': | ||
| 1468 | case '[': | ||
| 1469 | return 1; | ||
| 1470 | case '\\': | ||
| 1471 | if (p[1] != '\0') | ||
| 1472 | ++p; | ||
| 1473 | break; | ||
| 1474 | } | ||
| 1475 | ++p; | ||
| 1476 | } | ||
| 1477 | return 0; | ||
| 1478 | } | ||
| 1479 | |||
| 1480 | /* | ||
| 1481 | * Expand any wildcards in a pattern. Return TRUE if a match is | ||
| 1482 | * found, in which case the caller should call globfree() on the | ||
| 1483 | * glob_t structure. | ||
| 1484 | */ | ||
| 1485 | static int | ||
| 1486 | wildcard(char *p, glob_t *gd) | ||
| 1487 | { | ||
| 1488 | int ret; | ||
| 1489 | char *s; | ||
| 1490 | |||
| 1491 | // Don't call glob() if there are no wildcards. | ||
| 1492 | if (!wildchar(p)) { | ||
| 1493 | nomatch: | ||
| 1494 | // Remove backslashes from the name. | ||
| 1495 | for (s = p; *p; ++p) { | ||
| 1496 | if (*p == '\\' && p[1] != '\0') | ||
| 1497 | continue; | ||
| 1498 | *s++ = *p; | ||
| 1499 | } | ||
| 1500 | *s = '\0'; | ||
| 1501 | return 0; | ||
| 1502 | } | ||
| 1503 | |||
| 1504 | memset(gd, 0, sizeof(*gd)); | ||
| 1505 | ret = glob(p, GLOB_NOSORT, NULL, gd); | ||
| 1506 | if (ret == GLOB_NOMATCH) { | ||
| 1507 | globfree(gd); | ||
| 1508 | goto nomatch; | ||
| 1509 | } else if (ret != 0) { | ||
| 1510 | error("glob error for '%s'", p); | ||
| 1511 | } | ||
| 1512 | return 1; | ||
| 1513 | } | ||
| 1514 | |||
| 1515 | /* | ||
| 1516 | * Parse input from the makefile and construct a tree structure of it. | ||
| 1517 | */ | ||
| 1518 | static void | ||
| 1519 | input(FILE *fd, int ilevel) | ||
| 1520 | { | ||
| 1521 | char *p, *q, *s, *a, *str, *expanded, *copy; | ||
| 1522 | char *str1, *str2; | ||
| 1523 | struct name *np; | ||
| 1524 | struct depend *dp; | ||
| 1525 | struct cmd *cp; | ||
| 1526 | int startno, count; | ||
| 1527 | bool semicolon_cmd, seen_inference; | ||
| 1528 | uint8_t old_clevel = clevel; | ||
| 1529 | bool dbl; | ||
| 1530 | char *lib = NULL; | ||
| 1531 | glob_t gd; | ||
| 1532 | int nfile, i; | ||
| 1533 | char **files; | ||
| 1534 | bool minus; | ||
| 1535 | |||
| 1536 | lineno = 0; | ||
| 1537 | str1 = readline(fd); | ||
| 1538 | while (str1) { | ||
| 1539 | str2 = NULL; | ||
| 1540 | if (*str1 == '\t') // Command without target | ||
| 1541 | error("command not allowed here"); | ||
| 1542 | |||
| 1543 | // Newlines and comments are handled differently in command lines | ||
| 1544 | // and other types of line. Take a copy of the current line before | ||
| 1545 | // processing it as a non-command line in case it contains a | ||
| 1546 | // rule with a command line. That is, a line of the form: | ||
| 1547 | // | ||
| 1548 | // target: prereq; command | ||
| 1549 | // | ||
| 1550 | copy = xstrdup(str1); | ||
| 1551 | str = process_line(str1); | ||
| 1552 | |||
| 1553 | // Check for an include line | ||
| 1554 | minus = !posix && *str == '-'; | ||
| 1555 | p = str + minus; | ||
| 1556 | if (strncmp(p, "include", 7) == 0 && isblank(p[7])) { | ||
| 1557 | const char *old_makefile = makefile; | ||
| 1558 | int old_lineno = lineno; | ||
| 1559 | |||
| 1560 | if (ilevel > 16) | ||
| 1561 | error("too many includes"); | ||
| 1562 | |||
| 1563 | q = expanded = expand_macros(p + 7, FALSE); | ||
| 1564 | while ((p = gettok(&q)) != NULL) { | ||
| 1565 | FILE *ifd; | ||
| 1566 | |||
| 1567 | if (!posix) { | ||
| 1568 | // Try to create include file or bring it up-to-date | ||
| 1569 | opts |= OPT_include; | ||
| 1570 | make(newname(p), 1); | ||
| 1571 | opts &= ~OPT_include; | ||
| 1572 | } | ||
| 1573 | if ((ifd = fopen(p, "r")) == NULL) { | ||
| 1574 | if (!minus) | ||
| 1575 | error("can't open include file '%s'", p); | ||
| 1576 | } else { | ||
| 1577 | makefile = p; | ||
| 1578 | input(ifd, ilevel + 1); | ||
| 1579 | fclose(ifd); | ||
| 1580 | } | ||
| 1581 | if (posix) | ||
| 1582 | break; | ||
| 1583 | } | ||
| 1584 | if (posix && (p == NULL || gettok(&q))) | ||
| 1585 | error("one include file per line"); | ||
| 1586 | |||
| 1587 | makefile = old_makefile; | ||
| 1588 | lineno = old_lineno; | ||
| 1589 | goto end_loop; | ||
| 1590 | } | ||
| 1591 | |||
| 1592 | // Check for a macro definition | ||
| 1593 | q = find_char(str, '='); | ||
| 1594 | if (q != NULL) { | ||
| 1595 | int level = (useenv || fd == NULL) ? 4 : 3; | ||
| 1596 | char *newq = NULL; | ||
| 1597 | char eq = '\0'; | ||
| 1598 | |||
| 1599 | if (q - 1 > str) { | ||
| 1600 | switch (q[-1]) { | ||
| 1601 | case ':': | ||
| 1602 | // '::=' and ':::=' are from POSIX 202X. | ||
| 1603 | if (!posix && q - 2 > str && q[-2] == ':') { | ||
| 1604 | if (q - 3 > str && q[-3] == ':') { | ||
| 1605 | eq = 'B'; // BSD-style ':=' | ||
| 1606 | q[-3] = '\0'; | ||
| 1607 | } else { | ||
| 1608 | eq = ':'; // GNU-style ':=' | ||
| 1609 | q[-2] = '\0'; | ||
| 1610 | } | ||
| 1611 | break; | ||
| 1612 | } | ||
| 1613 | case '!': | ||
| 1614 | // ':=' and '!=' are non-POSIX extensions. | ||
| 1615 | case '+': | ||
| 1616 | case '?': | ||
| 1617 | // '+=' and '?=' are from POSIX 202X. | ||
| 1618 | if (posix) | ||
| 1619 | break; | ||
| 1620 | eq = q[-1]; | ||
| 1621 | q[-1] = '\0'; | ||
| 1622 | break; | ||
| 1623 | } | ||
| 1624 | } | ||
| 1625 | *q++ = '\0'; // Separate name and value | ||
| 1626 | while (isblank(*q)) | ||
| 1627 | q++; | ||
| 1628 | if ((p = strrchr(q, '\n')) != NULL) | ||
| 1629 | *p = '\0'; | ||
| 1630 | |||
| 1631 | // Expand left-hand side of assignment | ||
| 1632 | p = expanded = expand_macros(str, FALSE); | ||
| 1633 | if ((a = gettok(&p)) == NULL || gettok(&p)) | ||
| 1634 | error("invalid macro assignment"); | ||
| 1635 | |||
| 1636 | if (eq == ':') { | ||
| 1637 | // GNU-style ':='. Expand right-hand side of assignment. | ||
| 1638 | // Macro is of type immediate-expansion. | ||
| 1639 | q = newq = expand_macros(q, FALSE); | ||
| 1640 | level |= M_IMMEDIATE; | ||
| 1641 | } | ||
| 1642 | else if (eq == 'B') { | ||
| 1643 | // BSD-style ':='. Expand right-hand side of assignment, | ||
| 1644 | // though not '$$'. Macro is of type delayed-expansion. | ||
| 1645 | q = newq = expand_macros(q, TRUE); | ||
| 1646 | } else if (eq == '?' && getmp(a) != NULL) { | ||
| 1647 | // Skip assignment if macro is already set | ||
| 1648 | goto end_loop; | ||
| 1649 | } else if (eq == '+') { | ||
| 1650 | // Append to current value | ||
| 1651 | struct macro *mp = getmp(a); | ||
| 1652 | char *rhs; | ||
| 1653 | newq = mp && mp->m_val[0] ? xstrdup(mp->m_val) : NULL; | ||
| 1654 | if (mp && mp->m_immediate) { | ||
| 1655 | // Expand right-hand side of assignment (GNU make | ||
| 1656 | // compatibility) | ||
| 1657 | rhs = expand_macros(q, FALSE); | ||
| 1658 | level |= M_IMMEDIATE; | ||
| 1659 | } else { | ||
| 1660 | rhs = q; | ||
| 1661 | } | ||
| 1662 | newq = xappendword(newq, rhs); | ||
| 1663 | if (rhs != q) | ||
| 1664 | free(rhs); | ||
| 1665 | q = newq; | ||
| 1666 | } | ||
| 1667 | else if (eq == '!') { | ||
| 1668 | char *cmd = expand_macros(q, FALSE); | ||
| 1669 | q = newq = run_command(cmd); | ||
| 1670 | free(cmd); | ||
| 1671 | } | ||
| 1672 | setmacro(a, q, level); | ||
| 1673 | free(newq); | ||
| 1674 | goto end_loop; | ||
| 1675 | } | ||
| 1676 | |||
| 1677 | // If we get here it must be a target rule | ||
| 1678 | p = expanded = expand_macros(str, FALSE); | ||
| 1679 | |||
| 1680 | // Look for colon separator | ||
| 1681 | q = find_char(p, ':'); | ||
| 1682 | if (q == NULL) | ||
| 1683 | error("expected separator"); | ||
| 1684 | |||
| 1685 | *q++ = '\0'; // Separate targets and prerequisites | ||
| 1686 | |||
| 1687 | // Double colon | ||
| 1688 | dbl = !posix && *q == ':'; | ||
| 1689 | if (dbl) | ||
| 1690 | q++; | ||
| 1691 | |||
| 1692 | // Look for semicolon separator | ||
| 1693 | cp = NULL; | ||
| 1694 | s = strchr(q, ';'); | ||
| 1695 | if (s) { | ||
| 1696 | *s = '\0'; | ||
| 1697 | // Retrieve command from copy of line | ||
| 1698 | if ((p = find_char(copy, ':')) && (p = strchr(p, ';'))) | ||
| 1699 | newcmd(&cp, process_command(p + 1)); | ||
| 1700 | } | ||
| 1701 | semicolon_cmd = cp != NULL; | ||
| 1702 | |||
| 1703 | // Create list of prerequisites | ||
| 1704 | dp = NULL; | ||
| 1705 | while (((p = gettok(&q)) != NULL)) { | ||
| 1706 | char *newp = NULL; | ||
| 1707 | |||
| 1708 | if (!posix) { | ||
| 1709 | // Allow prerequisites of form library(member1 member2). | ||
| 1710 | // Leading and trailing spaces in the brackets are skipped. | ||
| 1711 | if (!lib) { | ||
| 1712 | s = strchr(p, '('); | ||
| 1713 | if (s && !ends_with_bracket(s) && strchr(q, ')')) { | ||
| 1714 | // Looks like an unterminated archive member | ||
| 1715 | // with a terminator later on the line. | ||
| 1716 | lib = p; | ||
| 1717 | if (s[1] != '\0') { | ||
| 1718 | p = newp = auto_concat(lib, ")"); | ||
| 1719 | s[1] = '\0'; | ||
| 1720 | } else { | ||
| 1721 | continue; | ||
| 1722 | } | ||
| 1723 | } | ||
| 1724 | } else if (ends_with_bracket(p)) { | ||
| 1725 | if (*p != ')') | ||
| 1726 | p = newp = auto_concat(lib, p); | ||
| 1727 | lib = NULL; | ||
| 1728 | if (newp == NULL) | ||
| 1729 | continue; | ||
| 1730 | } else { | ||
| 1731 | p = newp = auto_string(xasprintf("%s%s)", lib, p)); | ||
| 1732 | } | ||
| 1733 | } | ||
| 1734 | |||
| 1735 | // If not in POSIX mode expand wildcards in the name. | ||
| 1736 | nfile = 1; | ||
| 1737 | files = &p; | ||
| 1738 | if (!posix && wildcard(p, &gd)) { | ||
| 1739 | nfile = gd.gl_pathc; | ||
| 1740 | files = gd.gl_pathv; | ||
| 1741 | } | ||
| 1742 | for (i = 0; i < nfile; ++i) { | ||
| 1743 | np = newname(files[i]); | ||
| 1744 | newdep(&dp, np); | ||
| 1745 | } | ||
| 1746 | if (files != &p) | ||
| 1747 | globfree(&gd); | ||
| 1748 | free(newp); | ||
| 1749 | } | ||
| 1750 | lib = NULL; | ||
| 1751 | |||
| 1752 | // Create list of commands | ||
| 1753 | startno = dispno; | ||
| 1754 | while ((str2 = readline(fd)) && *str2 == '\t') { | ||
| 1755 | newcmd(&cp, process_command(str2)); | ||
| 1756 | free(str2); | ||
| 1757 | } | ||
| 1758 | dispno = startno; | ||
| 1759 | |||
| 1760 | // Create target names and attach rule to them | ||
| 1761 | q = expanded; | ||
| 1762 | count = 0; | ||
| 1763 | seen_inference = FALSE; | ||
| 1764 | while ((p = gettok(&q)) != NULL) { | ||
| 1765 | // If not in POSIX mode expand wildcards in the name. | ||
| 1766 | nfile = 1; | ||
| 1767 | files = &p; | ||
| 1768 | if (!posix && wildcard(p, &gd)) { | ||
| 1769 | nfile = gd.gl_pathc; | ||
| 1770 | files = gd.gl_pathv; | ||
| 1771 | } | ||
| 1772 | for (i = 0; i < nfile; ++i) { | ||
| 1773 | int ttype = target_type(files[i]); | ||
| 1774 | |||
| 1775 | np = newname(files[i]); | ||
| 1776 | if (ttype != T_NORMAL) { | ||
| 1777 | if (ttype == T_INFERENCE && posix) { | ||
| 1778 | if (semicolon_cmd) | ||
| 1779 | error_in_inference_rule("'; command'"); | ||
| 1780 | seen_inference = TRUE; | ||
| 1781 | } | ||
| 1782 | np->n_flag |= N_SPECIAL; | ||
| 1783 | } else if (!firstname) { | ||
| 1784 | firstname = np; | ||
| 1785 | } | ||
| 1786 | addrule(np, dp, cp, dbl); | ||
| 1787 | count++; | ||
| 1788 | } | ||
| 1789 | if (files != &p) | ||
| 1790 | globfree(&gd); | ||
| 1791 | } | ||
| 1792 | if (seen_inference && count != 1) | ||
| 1793 | error_in_inference_rule("multiple targets"); | ||
| 1794 | |||
| 1795 | // Prerequisites and commands will be unused if there were | ||
| 1796 | // no targets. Avoid leaking memory. | ||
| 1797 | if (count == 0) { | ||
| 1798 | freedeps(dp); | ||
| 1799 | freecmds(cp); | ||
| 1800 | } | ||
| 1801 | end_loop: | ||
| 1802 | free(str1); | ||
| 1803 | dispno = lineno; | ||
| 1804 | str1 = str2 ? str2 : readline(fd); | ||
| 1805 | free(copy); | ||
| 1806 | free(expanded); | ||
| 1807 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 1808 | if (!seen_first && fd) { | ||
| 1809 | if (findname(".POSIX")) { | ||
| 1810 | // The first non-comment line from a real makefile | ||
| 1811 | // defined the .POSIX special target. | ||
| 1812 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); | ||
| 1813 | posix = TRUE; | ||
| 1814 | } | ||
| 1815 | seen_first = TRUE; | ||
| 1816 | } | ||
| 1817 | #endif | ||
| 1818 | } | ||
| 1819 | // Conditionals aren't allowed to span files | ||
| 1820 | if (clevel != old_clevel) | ||
| 1821 | error("invalid conditional"); | ||
| 1822 | } | ||
| 1823 | |||
| 1824 | static void | ||
| 1825 | remove_target(void) | ||
| 1826 | { | ||
| 1827 | if (!dryrun && !print && !precious && | ||
| 1828 | target && !(target->n_flag & (N_PRECIOUS | N_PHONY)) && | ||
| 1829 | unlink(target->n_name) == 0) { | ||
| 1830 | bb_error_msg("'%s' removed", target->n_name); | ||
| 1831 | } | ||
| 1832 | } | ||
| 1833 | |||
| 1834 | /* | ||
| 1835 | * Do commands to make a target | ||
| 1836 | */ | ||
| 1837 | static int | ||
| 1838 | docmds(struct name *np, struct cmd *cp) | ||
| 1839 | { | ||
| 1840 | int estat = 0; // 0 exit status is success | ||
| 1841 | char *q, *command; | ||
| 1842 | |||
| 1843 | for (; cp; cp = cp->c_next) { | ||
| 1844 | uint8_t ssilent, signore, sdomake; | ||
| 1845 | |||
| 1846 | opts &= ~OPT_make; // We want to know if $(MAKE) is expanded | ||
| 1847 | q = command = expand_macros(cp->c_cmd, FALSE); | ||
| 1848 | ssilent = silent || (np->n_flag & N_SILENT) || dotouch; | ||
| 1849 | signore = ignore || (np->n_flag & N_IGNORE); | ||
| 1850 | sdomake = (!dryrun || doinclude || domake) && !dotouch; | ||
| 1851 | for (;;) { | ||
| 1852 | if (*q == '@') // Specific silent | ||
| 1853 | ssilent = TRUE + 1; | ||
| 1854 | else if (*q == '-') // Specific ignore | ||
| 1855 | signore = TRUE; | ||
| 1856 | else if (*q == '+') // Specific domake | ||
| 1857 | sdomake = TRUE + 1; | ||
| 1858 | else | ||
| 1859 | break; | ||
| 1860 | q++; | ||
| 1861 | } | ||
| 1862 | |||
| 1863 | if (sdomake > TRUE) { | ||
| 1864 | // '+' must not override '@' or .SILENT | ||
| 1865 | if (ssilent != TRUE + 1 && !(np->n_flag & N_SILENT)) | ||
| 1866 | ssilent = FALSE; | ||
| 1867 | } else if (!sdomake) | ||
| 1868 | ssilent = dotouch; | ||
| 1869 | |||
| 1870 | if (!ssilent) | ||
| 1871 | puts(q); | ||
| 1872 | |||
| 1873 | if (sdomake) { | ||
| 1874 | // Get the shell to execute it | ||
| 1875 | int status; | ||
| 1876 | char *cmd = !signore ? auto_concat("set -e;", q) : q; | ||
| 1877 | |||
| 1878 | target = np; | ||
| 1879 | status = system(cmd); | ||
| 1880 | target = NULL; | ||
| 1881 | // If this command was being run to create an include file | ||
| 1882 | // or bring it up-to-date errors should be ignored and a | ||
| 1883 | // failure status returned. | ||
| 1884 | if (status == -1 && !doinclude) { | ||
| 1885 | error("couldn't execute '%s'", q); | ||
| 1886 | } else if (status != 0 && !signore) { | ||
| 1887 | if (!doinclude) | ||
| 1888 | bb_error_msg("failed to build '%s'", np->n_name); | ||
| 1889 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1890 | if (status == SIGINT || status == SIGQUIT) | ||
| 1891 | #endif | ||
| 1892 | remove_target(); | ||
| 1893 | if (errcont || doinclude) | ||
| 1894 | estat = 1; // 1 exit status is failure | ||
| 1895 | else | ||
| 1896 | exit(status); | ||
| 1897 | } | ||
| 1898 | } | ||
| 1899 | free(command); | ||
| 1900 | } | ||
| 1901 | return estat; | ||
| 1902 | } | ||
| 1903 | |||
| 1904 | /* | ||
| 1905 | * Update the modification time of a file to now. | ||
| 1906 | */ | ||
| 1907 | static void | ||
| 1908 | touch(struct name *np) | ||
| 1909 | { | ||
| 1910 | if (dryrun || !silent) | ||
| 1911 | printf("touch %s\n", np->n_name); | ||
| 1912 | |||
| 1913 | if (!dryrun) { | ||
| 1914 | const struct timespec timebuf[2] = {{0, UTIME_NOW}, {0, UTIME_NOW}}; | ||
| 1915 | |||
| 1916 | if (utimensat(AT_FDCWD, np->n_name, timebuf, 0) < 0) { | ||
| 1917 | if (errno == ENOENT) { | ||
| 1918 | int fd = open(np->n_name, O_RDWR | O_CREAT, 0666); | ||
| 1919 | if (fd >= 0) { | ||
| 1920 | close(fd); | ||
| 1921 | return; | ||
| 1922 | } | ||
| 1923 | } | ||
| 1924 | bb_perror_msg("touch %s failed", np->n_name); | ||
| 1925 | } | ||
| 1926 | } | ||
| 1927 | } | ||
| 1928 | |||
| 1929 | static int | ||
| 1930 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | ||
| 1931 | struct name *implicit) | ||
| 1932 | { | ||
| 1933 | int estat = 0; // 0 exit status is success | ||
| 1934 | char *name, *member = NULL, *base; | ||
| 1935 | |||
| 1936 | name = splitlib(np->n_name, &member); | ||
| 1937 | setmacro("?", oodate, 0 | M_VALID); | ||
| 1938 | if (!posix) | ||
| 1939 | setmacro("^", allsrc, 0 | M_VALID); | ||
| 1940 | setmacro("%", member, 0 | M_VALID); | ||
| 1941 | setmacro("@", name, 0 | M_VALID); | ||
| 1942 | if (implicit) { | ||
| 1943 | setmacro("<", implicit->n_name, 0 | M_VALID); | ||
| 1944 | base = member ? member : name; | ||
| 1945 | *suffix(base) = '\0'; | ||
| 1946 | setmacro("*", base, 0 | M_VALID); | ||
| 1947 | } | ||
| 1948 | free(name); | ||
| 1949 | |||
| 1950 | estat = docmds(np, cp); | ||
| 1951 | if (dotouch && !(np->n_flag & N_PHONY)) | ||
| 1952 | touch(np); | ||
| 1953 | |||
| 1954 | return estat; | ||
| 1955 | } | ||
| 1956 | |||
| 1957 | /* | ||
| 1958 | * Determine if the modification time of a target, t, is less than | ||
| 1959 | * that of a prerequisite, p. If the tv_nsec member of either is | ||
| 1960 | * exactly 0 we assume (possibly incorrectly) that the time resolution | ||
| 1961 | * is 1 second and only compare tv_sec values. | ||
| 1962 | */ | ||
| 1963 | static int | ||
| 1964 | timespec_le(const struct timespec *t, const struct timespec *p) | ||
| 1965 | { | ||
| 1966 | if (t->tv_nsec == 0 || p->tv_nsec == 0) | ||
| 1967 | return t->tv_sec <= p->tv_sec; | ||
| 1968 | else if (t->tv_sec < p->tv_sec) | ||
| 1969 | return TRUE; | ||
| 1970 | else if (t->tv_sec == p->tv_sec) | ||
| 1971 | return t->tv_nsec <= p->tv_nsec; | ||
| 1972 | return FALSE; | ||
| 1973 | } | ||
| 1974 | |||
| 1975 | /* | ||
| 1976 | * Return the greater of two struct timespecs | ||
| 1977 | */ | ||
| 1978 | static const struct timespec * | ||
| 1979 | timespec_max(const struct timespec *t, const struct timespec *p) | ||
| 1980 | { | ||
| 1981 | return timespec_le(t, p) ? p : t; | ||
| 1982 | } | ||
| 1983 | |||
| 1984 | /* | ||
| 1985 | * Recursive routine to make a target. | ||
| 1986 | */ | ||
| 1987 | static int | ||
| 1988 | make(struct name *np, int level) | ||
| 1989 | { | ||
| 1990 | struct depend *dp; | ||
| 1991 | struct rule *rp; | ||
| 1992 | struct name *impdep = NULL; // implicit prerequisite | ||
| 1993 | struct rule imprule; | ||
| 1994 | struct cmd *sc_cmd = NULL; // commands for single-colon rule | ||
| 1995 | char *oodate = NULL; | ||
| 1996 | char *allsrc = NULL; | ||
| 1997 | struct timespec dtim = {1, 0}; | ||
| 1998 | bool didsomething = 0; | ||
| 1999 | bool estat = 0; // 0 exit status is success | ||
| 2000 | |||
| 2001 | if (np->n_flag & N_DONE) | ||
| 2002 | return 0; | ||
| 2003 | if (np->n_flag & N_DOING) | ||
| 2004 | error("circular dependency for %s", np->n_name); | ||
| 2005 | np->n_flag |= N_DOING; | ||
| 2006 | |||
| 2007 | if (!np->n_tim.tv_sec) | ||
| 2008 | modtime(np); // Get modtime of this file | ||
| 2009 | |||
| 2010 | if (!(np->n_flag & N_DOUBLE)) { | ||
| 2011 | // Find the commands needed for a single-colon rule, using | ||
| 2012 | // an inference rule or .DEFAULT rule if necessary | ||
| 2013 | sc_cmd = getcmd(np); | ||
| 2014 | if (!sc_cmd) { | ||
| 2015 | impdep = dyndep(np, &imprule); | ||
| 2016 | if (impdep) { | ||
| 2017 | sc_cmd = imprule.r_cmd; | ||
| 2018 | addrule(np, imprule.r_dep, NULL, FALSE); | ||
| 2019 | } | ||
| 2020 | } | ||
| 2021 | |||
| 2022 | // As a last resort check for a default rule | ||
| 2023 | if (!(np->n_flag & N_TARGET) && np->n_tim.tv_sec == 0) { | ||
| 2024 | sc_cmd = getcmd(findname(".DEFAULT")); | ||
| 2025 | if (!sc_cmd) { | ||
| 2026 | if (doinclude) | ||
| 2027 | return 1; | ||
| 2028 | error("don't know how to make %s", np->n_name); | ||
| 2029 | } | ||
| 2030 | impdep = np; | ||
| 2031 | } | ||
| 2032 | } | ||
| 2033 | else { | ||
| 2034 | // If any double-colon rule has no commands we need | ||
| 2035 | // an inference rule | ||
| 2036 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2037 | if (!rp->r_cmd) { | ||
| 2038 | impdep = dyndep(np, &imprule); | ||
| 2039 | if (!impdep) { | ||
| 2040 | if (doinclude) | ||
| 2041 | return 1; | ||
| 2042 | error("don't know how to make %s", np->n_name); | ||
| 2043 | } | ||
| 2044 | break; | ||
| 2045 | } | ||
| 2046 | } | ||
| 2047 | } | ||
| 2048 | |||
| 2049 | // Reset flag to detect duplicate prerequisites | ||
| 2050 | if (!quest && !(np->n_flag & N_DOUBLE)) { | ||
| 2051 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2052 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2053 | dp->d_name->n_flag &= ~N_MARK; | ||
| 2054 | } | ||
| 2055 | } | ||
| 2056 | } | ||
| 2057 | |||
| 2058 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2059 | struct name *locdep = NULL; | ||
| 2060 | |||
| 2061 | // Each double-colon rule is handled separately. | ||
| 2062 | if ((np->n_flag & N_DOUBLE)) { | ||
| 2063 | // If the rule has no commands use the inference rule. | ||
| 2064 | if (!rp->r_cmd) { | ||
| 2065 | locdep = impdep; | ||
| 2066 | imprule.r_dep->d_next = rp->r_dep; | ||
| 2067 | rp->r_dep = imprule.r_dep; | ||
| 2068 | rp->r_cmd = imprule.r_cmd; | ||
| 2069 | } | ||
| 2070 | // A rule with no prerequisities is executed unconditionally. | ||
| 2071 | if (!rp->r_dep) | ||
| 2072 | dtim = np->n_tim; | ||
| 2073 | // Reset flag to detect duplicate prerequisites | ||
| 2074 | if (!quest) { | ||
| 2075 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2076 | dp->d_name->n_flag &= ~N_MARK; | ||
| 2077 | } | ||
| 2078 | } | ||
| 2079 | } | ||
| 2080 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2081 | // Make prerequisite | ||
| 2082 | estat |= make(dp->d_name, level + 1); | ||
| 2083 | |||
| 2084 | // Make strings of out-of-date prerequisites (for $?) | ||
| 2085 | // and all prerequisites (for $^). But not if we were | ||
| 2086 | // invoked with -q. | ||
| 2087 | if (!quest | ||
| 2088 | // Skip duplicate entries. | ||
| 2089 | && (posix || !(dp->d_name->n_flag & N_MARK)) | ||
| 2090 | ) { | ||
| 2091 | if (timespec_le(&np->n_tim, &dp->d_name->n_tim)) { | ||
| 2092 | oodate = xappendword(oodate, dp->d_name->n_name); | ||
| 2093 | } | ||
| 2094 | allsrc = xappendword(allsrc, dp->d_name->n_name); | ||
| 2095 | dp->d_name->n_flag |= N_MARK; | ||
| 2096 | } | ||
| 2097 | dtim = *timespec_max(&dtim, &dp->d_name->n_tim); | ||
| 2098 | } | ||
| 2099 | if ((np->n_flag & N_DOUBLE)) { | ||
| 2100 | if (!quest && ((np->n_flag & N_PHONY) || | ||
| 2101 | timespec_le(&np->n_tim, &dtim))) { | ||
| 2102 | if (estat == 0) { | ||
| 2103 | estat = make1(np, rp->r_cmd, oodate, allsrc, locdep); | ||
| 2104 | dtim = (struct timespec){1, 0}; | ||
| 2105 | didsomething = 1; | ||
| 2106 | } | ||
| 2107 | free(oodate); | ||
| 2108 | oodate = NULL; | ||
| 2109 | } | ||
| 2110 | free(allsrc); | ||
| 2111 | allsrc = NULL; | ||
| 2112 | if (locdep) { | ||
| 2113 | rp->r_dep = rp->r_dep->d_next; | ||
| 2114 | rp->r_cmd = NULL; | ||
| 2115 | } | ||
| 2116 | } | ||
| 2117 | } | ||
| 2118 | if ((np->n_flag & N_DOUBLE) && impdep) | ||
| 2119 | free(imprule.r_dep); | ||
| 2120 | |||
| 2121 | np->n_flag |= N_DONE; | ||
| 2122 | np->n_flag &= ~N_DOING; | ||
| 2123 | |||
| 2124 | if (quest) { | ||
| 2125 | if (timespec_le(&np->n_tim, &dtim)) { | ||
| 2126 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | ||
| 2127 | return 1; // 1 means rebuild is needed | ||
| 2128 | } | ||
| 2129 | } else if (!(np->n_flag & N_DOUBLE) && | ||
| 2130 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { | ||
| 2131 | if (estat == 0) { | ||
| 2132 | if (!sc_cmd) { | ||
| 2133 | if (!doinclude) | ||
| 2134 | bb_error_msg("nothing to be done for %s", np->n_name); | ||
| 2135 | } else { | ||
| 2136 | estat = make1(np, sc_cmd, oodate, allsrc, impdep); | ||
| 2137 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | ||
| 2138 | } | ||
| 2139 | } else if (!doinclude) { | ||
| 2140 | bb_error_msg("'%s' not built due to errors", np->n_name); | ||
| 2141 | } | ||
| 2142 | free(oodate); | ||
| 2143 | } else if (didsomething) { | ||
| 2144 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | ||
| 2145 | } else if (level == 0) { | ||
| 2146 | printf("%s: '%s' is up to date\n", applet_name, np->n_name); | ||
| 2147 | } | ||
| 2148 | free(allsrc); | ||
| 2149 | return estat; | ||
| 2150 | } | ||
| 2151 | |||
| 2152 | /* | ||
| 2153 | * Check structures for make. | ||
| 2154 | */ | ||
| 2155 | |||
| 2156 | static void | ||
| 2157 | print_name(struct name *np) | ||
| 2158 | { | ||
| 2159 | if (np == firstname) | ||
| 2160 | printf("# default target\n"); | ||
| 2161 | printf("%s:", np->n_name); | ||
| 2162 | if ((np->n_flag & N_DOUBLE)) | ||
| 2163 | putchar(':'); | ||
| 2164 | } | ||
| 2165 | |||
| 2166 | static void | ||
| 2167 | print_prerequisites(struct rule *rp) | ||
| 2168 | { | ||
| 2169 | struct depend *dp; | ||
| 2170 | |||
| 2171 | for (dp = rp->r_dep; dp; dp = dp->d_next) | ||
| 2172 | printf(" %s", dp->d_name->n_name); | ||
| 2173 | } | ||
| 2174 | |||
| 2175 | static void | ||
| 2176 | print_commands(struct rule *rp) | ||
| 2177 | { | ||
| 2178 | struct cmd *cp; | ||
| 2179 | |||
| 2180 | for (cp = rp->r_cmd; cp; cp = cp->c_next) | ||
| 2181 | printf("\t%s\n", cp->c_cmd); | ||
| 2182 | } | ||
| 2183 | |||
| 2184 | static void | ||
| 2185 | print_details(void) | ||
| 2186 | { | ||
| 2187 | int i; | ||
| 2188 | struct macro *mp; | ||
| 2189 | struct name *np; | ||
| 2190 | struct rule *rp; | ||
| 2191 | |||
| 2192 | for (i = 0; i < HTABSIZE; i++) | ||
| 2193 | for (mp = macrohead[i]; mp; mp = mp->m_next) | ||
| 2194 | printf("%s = %s\n", mp->m_name, mp->m_val); | ||
| 2195 | putchar('\n'); | ||
| 2196 | |||
| 2197 | for (i = 0; i < HTABSIZE; i++) { | ||
| 2198 | for (np = namehead[i]; np; np = np->n_next) { | ||
| 2199 | if (!(np->n_flag & N_DOUBLE)) { | ||
| 2200 | print_name(np); | ||
| 2201 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2202 | print_prerequisites(rp); | ||
| 2203 | } | ||
| 2204 | putchar('\n'); | ||
| 2205 | |||
| 2206 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2207 | print_commands(rp); | ||
| 2208 | } | ||
| 2209 | putchar('\n'); | ||
| 2210 | } else { | ||
| 2211 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2212 | print_name(np); | ||
| 2213 | print_prerequisites(rp); | ||
| 2214 | putchar('\n'); | ||
| 2215 | |||
| 2216 | print_commands(rp); | ||
| 2217 | putchar('\n'); | ||
| 2218 | } | ||
| 2219 | } | ||
| 2220 | } | ||
| 2221 | } | ||
| 2222 | } | ||
| 2223 | |||
| 2224 | /* | ||
| 2225 | * Process options from an argv array. If from_env is non-zero we're | ||
| 2226 | * handling options from MAKEFLAGS so skip '-C', '-f' and '-p'. | ||
| 2227 | */ | ||
| 2228 | static uint32_t | ||
| 2229 | process_options(char **argv, int from_env) | ||
| 2230 | { | ||
| 2231 | uint32_t flags; | ||
| 2232 | |||
| 2233 | flags = getopt32(argv, "^" OPTSTR1 OPTSTR2 "\0k-S:S-k", | ||
| 2234 | &numjobs, &makefiles, &dirs); | ||
| 2235 | if (from_env && (flags & (OPT_C | OPT_f | OPT_p))) | ||
| 2236 | error("invalid MAKEFLAGS"); | ||
| 2237 | if (posix && (flags & OPT_C)) | ||
| 2238 | error("-C not allowed"); | ||
| 2239 | |||
| 2240 | return flags; | ||
| 2241 | } | ||
| 2242 | |||
| 2243 | /* | ||
| 2244 | * Split the contents of MAKEFLAGS into an argv array. If the return | ||
| 2245 | * value (call it fargv) isn't NULL the caller should free fargv[1] and | ||
| 2246 | * fargv. | ||
| 2247 | */ | ||
| 2248 | static char ** | ||
| 2249 | expand_makeflags(void) | ||
| 2250 | { | ||
| 2251 | const char *m, *makeflags = getenv("MAKEFLAGS"); | ||
| 2252 | char *p, *argstr; | ||
| 2253 | int argc; | ||
| 2254 | char **argv; | ||
| 2255 | |||
| 2256 | if (makeflags == NULL) | ||
| 2257 | return NULL; | ||
| 2258 | |||
| 2259 | while (isblank(*makeflags)) | ||
| 2260 | makeflags++; | ||
| 2261 | |||
| 2262 | if (*makeflags == '\0') | ||
| 2263 | return NULL; | ||
| 2264 | |||
| 2265 | p = argstr = xzalloc(strlen(makeflags) + 2); | ||
| 2266 | |||
| 2267 | // If MAKEFLAGS doesn't start with a hyphen, doesn't look like | ||
| 2268 | // a macro definition and only contains valid option characters, | ||
| 2269 | // add a hyphen. | ||
| 2270 | argc = 3; | ||
| 2271 | if (makeflags[0] != '-' && strchr(makeflags, '=') == NULL) { | ||
| 2272 | if (strspn(makeflags, OPTSTR1) != strlen(makeflags)) | ||
| 2273 | error("invalid MAKEFLAGS"); | ||
| 2274 | *p++ = '-'; | ||
| 2275 | } else { | ||
| 2276 | // MAKEFLAGS may need to be split, estimate size of argv array. | ||
| 2277 | for (m = makeflags; *m; ++m) { | ||
| 2278 | if (isblank(*m)) | ||
| 2279 | argc++; | ||
| 2280 | } | ||
| 2281 | } | ||
| 2282 | |||
| 2283 | argv = xzalloc(argc * sizeof(char *)); | ||
| 2284 | argc = 0; | ||
| 2285 | argv[argc++] = (char *)applet_name; | ||
| 2286 | argv[argc++] = argstr; | ||
| 2287 | |||
| 2288 | // Copy MAKEFLAGS into argstr, splitting at non-escaped blanks. | ||
| 2289 | m = makeflags; | ||
| 2290 | do { | ||
| 2291 | if (*m == '\\' && m[1] != '\0') | ||
| 2292 | m++; // Skip backslash, copy next character unconditionally. | ||
| 2293 | else if (isblank(*m)) { | ||
| 2294 | // Terminate current argument and start a new one. | ||
| 2295 | /* *p = '\0'; - xzalloc did it */ | ||
| 2296 | argv[argc++] = ++p; | ||
| 2297 | do { | ||
| 2298 | m++; | ||
| 2299 | } while (isblank(*m)); | ||
| 2300 | continue; | ||
| 2301 | } | ||
| 2302 | *p++ = *m++; | ||
| 2303 | } while (*m != '\0'); | ||
| 2304 | /* *p = '\0'; - xzalloc did it */ | ||
| 2305 | /* argv[argc] = NULL; - and this */ | ||
| 2306 | |||
| 2307 | return argv; | ||
| 2308 | } | ||
| 2309 | |||
| 2310 | // These macros require special treatment | ||
| 2311 | #define MAKEFLAGS_SHELL "MAKEFLAGS\0SHELL\0" | ||
| 2312 | #define MAKEFLAGS 0 | ||
| 2313 | #define SHELL 1 | ||
| 2314 | |||
| 2315 | /* | ||
| 2316 | * Instantiate all macros in an argv-style array of pointers. Stop | ||
| 2317 | * processing at the first string that doesn't contain an equal sign. | ||
| 2318 | */ | ||
| 2319 | static char ** | ||
| 2320 | process_macros(char **argv, int level) | ||
| 2321 | { | ||
| 2322 | char *p; | ||
| 2323 | |||
| 2324 | while (*argv && (p = strchr(*argv, '=')) != NULL) { | ||
| 2325 | int immediate = 0; | ||
| 2326 | |||
| 2327 | if (p - 2 > *argv && p[-1] == ':' && p[-2] == ':') { | ||
| 2328 | if (posix) | ||
| 2329 | error("invalid macro assignment"); | ||
| 2330 | immediate = M_IMMEDIATE; | ||
| 2331 | p[-2] = '\0'; | ||
| 2332 | } else | ||
| 2333 | *p = '\0'; | ||
| 2334 | if (level != 3 || index_in_strings(MAKEFLAGS_SHELL, *argv) < 0) { | ||
| 2335 | if (immediate) { | ||
| 2336 | char *exp = expand_macros(p + 1, FALSE); | ||
| 2337 | setmacro(*argv, exp, level | immediate); | ||
| 2338 | free(exp); | ||
| 2339 | } else { | ||
| 2340 | setmacro(*argv, p + 1, level); | ||
| 2341 | } | ||
| 2342 | } | ||
| 2343 | *p = '='; | ||
| 2344 | if (immediate) | ||
| 2345 | p[-2] = ':'; | ||
| 2346 | |||
| 2347 | argv++; | ||
| 2348 | } | ||
| 2349 | return argv; | ||
| 2350 | } | ||
| 2351 | |||
| 2352 | /* | ||
| 2353 | * Update the MAKEFLAGS macro and environment variable to include any | ||
| 2354 | * command line options that don't have their default value (apart from | ||
| 2355 | * -f, -p and -S). Also add any macros defined on the command line or | ||
| 2356 | * by the MAKEFLAGS environment variable (apart from MAKEFLAGS itself). | ||
| 2357 | * Add macros that were defined on the command line to the environment. | ||
| 2358 | */ | ||
| 2359 | static void | ||
| 2360 | update_makeflags(void) | ||
| 2361 | { | ||
| 2362 | int i; | ||
| 2363 | char optbuf[] = "-?"; | ||
| 2364 | char *makeflags = NULL; | ||
| 2365 | char *macro, *s; | ||
| 2366 | const char *t; | ||
| 2367 | struct macro *mp; | ||
| 2368 | |||
| 2369 | t = OPTSTR1; | ||
| 2370 | for (i = 0; *t; t++) { | ||
| 2371 | if (*t == ':' || *t == '+') | ||
| 2372 | continue; | ||
| 2373 | if ((opts & OPT_MASK & (1 << i))) { | ||
| 2374 | optbuf[1] = *t; | ||
| 2375 | makeflags = xappendword(makeflags, optbuf); | ||
| 2376 | if (*t == 'j') { | ||
| 2377 | s = auto_string(xasprintf("%d", numjobs)); | ||
| 2378 | makeflags = xappendword(makeflags, s); | ||
| 2379 | } | ||
| 2380 | } | ||
| 2381 | i++; | ||
| 2382 | } | ||
| 2383 | |||
| 2384 | for (i = 0; i < HTABSIZE; ++i) { | ||
| 2385 | for (mp = macrohead[i]; mp; mp = mp->m_next) { | ||
| 2386 | if (mp->m_level == 1 || mp->m_level == 2) { | ||
| 2387 | int idx = index_in_strings(MAKEFLAGS_SHELL, mp->m_name); | ||
| 2388 | if (idx == MAKEFLAGS) | ||
| 2389 | continue; | ||
| 2390 | macro = xzalloc(strlen(mp->m_name) + 2 * strlen(mp->m_val) + 1); | ||
| 2391 | s = stpcpy(macro, mp->m_name); | ||
| 2392 | *s++ = '='; | ||
| 2393 | for (t = mp->m_val; *t; t++) { | ||
| 2394 | if (*t == '\\' || isblank(*t)) | ||
| 2395 | *s++ = '\\'; | ||
| 2396 | *s++ = *t; | ||
| 2397 | } | ||
| 2398 | /* *s = '\0'; - xzalloc did it */ | ||
| 2399 | |||
| 2400 | makeflags = xappendword(makeflags, macro); | ||
| 2401 | free(macro); | ||
| 2402 | |||
| 2403 | // Add command line macro definitions to the environment | ||
| 2404 | if (mp->m_level == 1 && idx != SHELL) | ||
| 2405 | setenv(mp->m_name, mp->m_val, 1); | ||
| 2406 | } | ||
| 2407 | } | ||
| 2408 | } | ||
| 2409 | |||
| 2410 | if (makeflags) { | ||
| 2411 | setmacro("MAKEFLAGS", makeflags, 0); | ||
| 2412 | setenv("MAKEFLAGS", makeflags, 1); | ||
| 2413 | free(makeflags); | ||
| 2414 | } | ||
| 2415 | } | ||
| 2416 | |||
| 2417 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 2418 | static void | ||
| 2419 | make_handler(int sig) | ||
| 2420 | { | ||
| 2421 | signal(sig, SIG_DFL); | ||
| 2422 | remove_target(); | ||
| 2423 | kill(getpid(), sig); | ||
| 2424 | } | ||
| 2425 | |||
| 2426 | static void | ||
| 2427 | init_signal(int sig) | ||
| 2428 | { | ||
| 2429 | struct sigaction sa, new_action; | ||
| 2430 | |||
| 2431 | sigemptyset(&new_action.sa_mask); | ||
| 2432 | new_action.sa_flags = 0; | ||
| 2433 | new_action.sa_handler = make_handler; | ||
| 2434 | |||
| 2435 | sigaction(sig, NULL, &sa); | ||
| 2436 | if (sa.sa_handler != SIG_IGN) | ||
| 2437 | sigaction_set(sig, &new_action); | ||
| 2438 | } | ||
| 2439 | #endif | ||
| 2440 | |||
| 2441 | /* | ||
| 2442 | * If the global option flag associated with a special target hasn't | ||
| 2443 | * been set mark all prerequisites of the target with a flag. If the | ||
| 2444 | * target had no prerequisites set the global option flag. | ||
| 2445 | */ | ||
| 2446 | static void | ||
| 2447 | mark_special(const char *special, uint32_t oflag, uint16_t nflag) | ||
| 2448 | { | ||
| 2449 | struct name *np; | ||
| 2450 | struct rule *rp; | ||
| 2451 | struct depend *dp; | ||
| 2452 | int marked = FALSE; | ||
| 2453 | |||
| 2454 | if (!(opts & oflag) && (np = findname(special))) { | ||
| 2455 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2456 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2457 | dp->d_name->n_flag |= nflag; | ||
| 2458 | marked = TRUE; | ||
| 2459 | } | ||
| 2460 | } | ||
| 2461 | |||
| 2462 | if (!marked) | ||
| 2463 | opts |= oflag; | ||
| 2464 | } | ||
| 2465 | } | ||
| 2466 | |||
| 2467 | int make_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 2468 | int make_main(int argc UNUSED_PARAM, char **argv) | ||
| 2469 | { | ||
| 2470 | const char *path, *newpath = NULL; | ||
| 2471 | char **fargv, **fargv0; | ||
| 2472 | const char *dir, *file; | ||
| 2473 | char def_make[] = "makefile"; | ||
| 2474 | int estat; | ||
| 2475 | FILE *ifd; | ||
| 2476 | |||
| 2477 | INIT_G(); | ||
| 2478 | |||
| 2479 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 2480 | if (argv[1] && strcmp(argv[1], "--posix") == 0) { | ||
| 2481 | argv[1] = argv[0]; | ||
| 2482 | ++argv; | ||
| 2483 | --argc; | ||
| 2484 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); | ||
| 2485 | posix = TRUE; | ||
| 2486 | } else { | ||
| 2487 | posix = getenv("PDPMAKE_POSIXLY_CORRECT") != NULL; | ||
| 2488 | } | ||
| 2489 | #endif | ||
| 2490 | |||
| 2491 | if (!posix) { | ||
| 2492 | path = argv[0]; | ||
| 2493 | #if ENABLE_PLATFORM_MINGW32 | ||
| 2494 | if (has_path(argv[0])) { | ||
| 2495 | // Add extension if necessary, else realpath() will fail | ||
| 2496 | char *p = xmalloc(strlen(argv[0]) + 5); | ||
| 2497 | add_win32_extension(strcpy(p, argv[0])); | ||
| 2498 | path = newpath = xmalloc_realpath(p); | ||
| 2499 | free(p); | ||
| 2500 | if (!path) { | ||
| 2501 | bb_perror_msg("can't resolve path for %s", argv[0]); | ||
| 2502 | } | ||
| 2503 | } | ||
| 2504 | #else | ||
| 2505 | if (argv[0][0] != '/' && strchr(argv[0], '/')) { | ||
| 2506 | // Make relative path absolute | ||
| 2507 | path = newpath = realpath(argv[0], NULL); | ||
| 2508 | if (!path) { | ||
| 2509 | bb_perror_msg("can't resolve path for %s", argv[0]); | ||
| 2510 | } | ||
| 2511 | } | ||
| 2512 | #endif | ||
| 2513 | } else { | ||
| 2514 | path = "make"; | ||
| 2515 | } | ||
| 2516 | |||
| 2517 | // Process options from MAKEFLAGS | ||
| 2518 | fargv = fargv0 = expand_makeflags(); | ||
| 2519 | if (fargv0) { | ||
| 2520 | opts = process_options(fargv, TRUE); | ||
| 2521 | fargv = fargv0 + optind; | ||
| 2522 | } | ||
| 2523 | // Reset getopt(3) so we can call it again | ||
| 2524 | GETOPT_RESET(); | ||
| 2525 | |||
| 2526 | // Process options from the command line | ||
| 2527 | opts |= process_options(argv, FALSE); | ||
| 2528 | argv += optind; | ||
| 2529 | |||
| 2530 | while ((dir = llist_pop(&dirs))) { | ||
| 2531 | if (chdir(dir) == -1) { | ||
| 2532 | bb_perror_msg("can't chdir to %s", dir); | ||
| 2533 | } | ||
| 2534 | } | ||
| 2535 | |||
| 2536 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 2537 | init_signal(SIGHUP); | ||
| 2538 | init_signal(SIGTERM); | ||
| 2539 | #endif | ||
| 2540 | |||
| 2541 | setmacro("$", "$", 0 | M_VALID); | ||
| 2542 | |||
| 2543 | // Process macro definitions from the command line | ||
| 2544 | argv = process_macros(argv, 1); | ||
| 2545 | |||
| 2546 | // Process macro definitions from MAKEFLAGS | ||
| 2547 | if (fargv) { | ||
| 2548 | process_macros(fargv, 2); | ||
| 2549 | free(fargv0[1]); | ||
| 2550 | free(fargv0); | ||
| 2551 | } | ||
| 2552 | |||
| 2553 | // Process macro definitions from the environment | ||
| 2554 | process_macros(environ, 3 | M_VALID); | ||
| 2555 | |||
| 2556 | // Update MAKEFLAGS and environment | ||
| 2557 | update_makeflags(); | ||
| 2558 | |||
| 2559 | // Read built-in rules | ||
| 2560 | input(NULL, 0); | ||
| 2561 | |||
| 2562 | setmacro("SHELL", "/bin/sh", 4); | ||
| 2563 | setmacro("MAKE", path, 4); | ||
| 2564 | free((void *)newpath); | ||
| 2565 | |||
| 2566 | if (!makefiles) { // Look for a default Makefile | ||
| 2567 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 2568 | for (; def_make[0] >= 'M'; def_make[0] -= 0x20) { | ||
| 2569 | #else | ||
| 2570 | { | ||
| 2571 | #endif | ||
| 2572 | if ((ifd = fopen(def_make, "r")) != NULL) { | ||
| 2573 | makefile = def_make; | ||
| 2574 | goto read_makefile; | ||
| 2575 | } | ||
| 2576 | } | ||
| 2577 | error("no makefile found"); | ||
| 2578 | } | ||
| 2579 | |||
| 2580 | while ((file = llist_pop(&makefiles))) { | ||
| 2581 | if (strcmp(file, "-") == 0) { // Can use stdin as makefile | ||
| 2582 | ifd = stdin; | ||
| 2583 | makefile = "stdin"; | ||
| 2584 | } else { | ||
| 2585 | ifd = xfopen_for_read(file); | ||
| 2586 | makefile = file; | ||
| 2587 | } | ||
| 2588 | read_makefile: | ||
| 2589 | input(ifd, 0); | ||
| 2590 | fclose(ifd); | ||
| 2591 | makefile = NULL; | ||
| 2592 | } | ||
| 2593 | |||
| 2594 | if (print) | ||
| 2595 | print_details(); | ||
| 2596 | |||
| 2597 | mark_special(".SILENT", OPT_s, N_SILENT); | ||
| 2598 | mark_special(".IGNORE", OPT_i, N_IGNORE); | ||
| 2599 | mark_special(".PRECIOUS", OPT_precious, N_PRECIOUS); | ||
| 2600 | if (!posix) | ||
| 2601 | mark_special(".PHONY", OPT_phony, N_PHONY); | ||
| 2602 | |||
| 2603 | estat = 0; | ||
| 2604 | if (*argv == NULL) { | ||
| 2605 | if (!firstname) | ||
| 2606 | error("no targets defined"); | ||
| 2607 | estat = make(firstname, 0); | ||
| 2608 | } else { | ||
| 2609 | while (*argv != NULL) | ||
| 2610 | estat |= make(newname(*argv++), 0); | ||
| 2611 | } | ||
| 2612 | |||
| 2613 | #if ENABLE_FEATURE_CLEAN_UP | ||
| 2614 | freenames(); | ||
| 2615 | freemacros(); | ||
| 2616 | llist_free(makefiles, NULL); | ||
| 2617 | llist_free(dirs, NULL); | ||
| 2618 | #endif | ||
| 2619 | |||
| 2620 | return estat; | ||
| 2621 | } | ||
diff --git a/testsuite/make.tests b/testsuite/make.tests new file mode 100755 index 000000000..75091b0f7 --- /dev/null +++ b/testsuite/make.tests | |||
| @@ -0,0 +1,413 @@ | |||
| 1 | #!/bin/sh | ||
| 2 | |||
| 3 | . ./testing.sh | ||
| 4 | |||
| 5 | # testing "test name" "command" "expected result" "file input" "stdin" | ||
| 6 | |||
| 7 | testing "Basic makefile" \ | ||
| 8 | "make -f -" "target\n" "" ' | ||
| 9 | target: | ||
| 10 | @echo target | ||
| 11 | ' | ||
| 12 | |||
| 13 | # .DEFAULT rules with no commands or some prerequisites are ignored. | ||
| 14 | # .DEFAULT rules with commands can be redefined. | ||
| 15 | testing ".DEFAULT rule" \ | ||
| 16 | "make -f - default" "default2\n" "" ' | ||
| 17 | .DEFAULT: ignored | ||
| 18 | .DEFAULT: | ||
| 19 | @echo default1 | ||
| 20 | .DEFAULT: | ||
| 21 | @echo default2 | ||
| 22 | target: | ||
| 23 | ' | ||
| 24 | |||
| 25 | # Macros should be expanded before suffix substitution. The suffixes | ||
| 26 | # can be obtained by macro expansion. | ||
| 27 | testing "Macro expansion and suffix substitution" \ | ||
| 28 | "make -f -" "src1.o src2.o\n" "" ' | ||
| 29 | DOTC = .c | ||
| 30 | DOTO = .o | ||
| 31 | SRC1 = src1.c | ||
| 32 | SRCS = $(SRC1) src2.c | ||
| 33 | target: | ||
| 34 | @echo $(SRCS:$(DOTC)=$(DOTO)) | ||
| 35 | ' | ||
| 36 | |||
| 37 | # Indeed, everything after the <colon> can be obtained by macro | ||
| 38 | # macro expansion. | ||
| 39 | testing "Macro expansion and suffix substitution 2" \ | ||
| 40 | "make -f -" "src1.o src2.o\n" "" ' | ||
| 41 | DOTS = .c=.o | ||
| 42 | SRC1 = src1.c | ||
| 43 | SRCS = $(SRC1) src2.c | ||
| 44 | target: | ||
| 45 | @echo $(SRCS:$(DOTS)) | ||
| 46 | ' | ||
| 47 | |||
| 48 | # It should be possible for an inference rule to determine that a | ||
| 49 | # prerequisite can be created using an explicit rule. | ||
| 50 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 51 | testing "Inference rule with explicit rule for prerequisite" \ | ||
| 52 | "make -f -" "touch x.p\ncat x.p >x.q\n" "" ' | ||
| 53 | .SUFFIXES: .p .q | ||
| 54 | x.q: | ||
| 55 | x.p: | ||
| 56 | touch $@ | ||
| 57 | .p.q: | ||
| 58 | cat $< >$@ | ||
| 59 | ' | ||
| 60 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 61 | |||
| 62 | # A macro created using ::= remembers it's of type immediate-expansion. | ||
| 63 | # Immediate expansion also occurs when += is used to append to such a macro. | ||
| 64 | testing "Appending to immediate-expansion macro" \ | ||
| 65 | "make -f -" \ | ||
| 66 | "hello 1 2 3\nhello 4 4\n" "" ' | ||
| 67 | world = 1 | ||
| 68 | hello ::= hello $(world) | ||
| 69 | world = 2 | ||
| 70 | hello += $(world) | ||
| 71 | world = 3 | ||
| 72 | hello += $(world) | ||
| 73 | world = 4 | ||
| 74 | |||
| 75 | world = 1 | ||
| 76 | reset ::= hello $(world) | ||
| 77 | world = 2 | ||
| 78 | # No longer immediate-expansion | ||
| 79 | reset = hello $(world) | ||
| 80 | world = 3 | ||
| 81 | reset += $(world) | ||
| 82 | world = 4 | ||
| 83 | |||
| 84 | target: | ||
| 85 | @echo $(hello) | ||
| 86 | @echo $(reset) | ||
| 87 | ' | ||
| 88 | |||
| 89 | # basic pattern macro expansion | ||
| 90 | testing "Basic pattern macro expansion" \ | ||
| 91 | "make -f -" \ | ||
| 92 | "obj/util.o obj/main.o\n" "" ' | ||
| 93 | SRC = src/util.c src/main.c | ||
| 94 | OBJ = $(SRC:src/%.c=obj/%.o) | ||
| 95 | |||
| 96 | target: | ||
| 97 | @echo $(OBJ) | ||
| 98 | ' | ||
| 99 | |||
| 100 | # pattern macro expansion; match any value | ||
| 101 | testing "Pattern macro expansion; match any value" \ | ||
| 102 | "make -f -" \ | ||
| 103 | "any_value.o\n" "" ' | ||
| 104 | SRC = any_value | ||
| 105 | OBJ = $(SRC:%=%.o) | ||
| 106 | |||
| 107 | target: | ||
| 108 | @echo $(OBJ) | ||
| 109 | ' | ||
| 110 | |||
| 111 | # pattern macro expansion with empty rvalue | ||
| 112 | testing "Pattern macro expansion with empty rvalue" \ | ||
| 113 | "make -f -" \ | ||
| 114 | "\n" "" ' | ||
| 115 | SRC = util.c main.c | ||
| 116 | OBJ = $(SRC:%.c=) | ||
| 117 | |||
| 118 | target: | ||
| 119 | @echo $(OBJ) | ||
| 120 | ' | ||
| 121 | |||
| 122 | # pattern macro expansion with multiple <percent> in rvalue | ||
| 123 | # POSIX requires the first <percent> to be expanded, others | ||
| 124 | # may or may not be expanded. Permit either case. | ||
| 125 | testing "Pattern macro expansion with multiple <percent> in rvalue" \ | ||
| 126 | "make -f - | sed 's/mainmainmain/main%%/'" \ | ||
| 127 | "main%%\n" "" ' | ||
| 128 | SRC = main.c | ||
| 129 | OBJ = $(SRC:%.c=%%%) | ||
| 130 | |||
| 131 | target: | ||
| 132 | @echo $(OBJ) | ||
| 133 | ' | ||
| 134 | |||
| 135 | # pattern macro expansion; zero match | ||
| 136 | testing "Pattern macro expansion; zero match" \ | ||
| 137 | "make -f -" \ | ||
| 138 | "nsnp\n" "" ' | ||
| 139 | WORD = osop | ||
| 140 | REPL = $(WORD:os%op=ns%np) | ||
| 141 | |||
| 142 | target: | ||
| 143 | @echo $(REPL) | ||
| 144 | ' | ||
| 145 | |||
| 146 | # Check that MAKE will contain argv[0], e.g make in this case | ||
| 147 | testing "Basic MAKE macro expansion" \ | ||
| 148 | "make -f -" \ | ||
| 149 | "make\n" "" ' | ||
| 150 | target: | ||
| 151 | @echo $(MAKE) | ||
| 152 | ' | ||
| 153 | |||
| 154 | # Check that MAKE defined as environment variable will overwrite default MAKE | ||
| 155 | testing "MAKE macro expansion; overwrite with env macro" \ | ||
| 156 | "MAKE=hello make -f -" \ | ||
| 157 | "hello\n" "" ' | ||
| 158 | target: | ||
| 159 | @echo $(MAKE) | ||
| 160 | ' | ||
| 161 | |||
| 162 | # Check that MAKE defined on the command-line will overwrite MAKE defined in | ||
| 163 | # Makefile | ||
| 164 | testing "MAKE macro expansion; overwrite with command-line macro" \ | ||
| 165 | "make -f - MAKE=hello" \ | ||
| 166 | "hello\n" "" ' | ||
| 167 | MAKE = test | ||
| 168 | |||
| 169 | target: | ||
| 170 | @echo $(MAKE) | ||
| 171 | ' | ||
| 172 | |||
| 173 | # POSIX draft states that if make was invoked using relative path, MAKE must | ||
| 174 | # contain absolute path, not just argv[0] | ||
| 175 | testing "MAKE macro expansion; turn relative path into absolute" \ | ||
| 176 | "../runtest-tempdir-links/make -f -" \ | ||
| 177 | "ok\n" "" ' | ||
| 178 | target: | ||
| 179 | @test -e "$(MAKE)" && test "$(MAKE)" = "$$(env which make)" && echo ok | ||
| 180 | ' | ||
| 181 | |||
| 182 | # $? contains prerequisites newer than target, file2 in this case | ||
| 183 | # $^ has all prerequisites, file1 and file2 | ||
| 184 | touch -t 202206171200 file1 | ||
| 185 | touch -t 202206171201 target | ||
| 186 | touch -t 202206171202 file2 | ||
| 187 | testing "Compare \$? and \$^ internal macros" \ | ||
| 188 | "make -f -" \ | ||
| 189 | "file2\nfile1 file2\n" "" ' | ||
| 190 | target: file1 file2 | ||
| 191 | @echo $? | ||
| 192 | @echo $^ | ||
| 193 | ' | ||
| 194 | rm -f target file1 file2 | ||
| 195 | |||
| 196 | # Phony targets are executed (once) even if a matching file exists. | ||
| 197 | # A .PHONY target with no prerequisites is ignored. | ||
| 198 | touch -t 202206171201 target | ||
| 199 | testing "Phony target" \ | ||
| 200 | "make -f -" \ | ||
| 201 | "phony\n" "" ' | ||
| 202 | .PHONY: target | ||
| 203 | .PHONY: | ||
| 204 | target: | ||
| 205 | @echo phony | ||
| 206 | ' | ||
| 207 | rm -f target | ||
| 208 | |||
| 209 | # Phony targets aren't touched with -t | ||
| 210 | testing "Phony target not touched" \ | ||
| 211 | "make -t -f - >/dev/null && test -f target && echo target" \ | ||
| 212 | "" "" ' | ||
| 213 | .PHONY: target | ||
| 214 | target: | ||
| 215 | @: | ||
| 216 | ' | ||
| 217 | rm -f target | ||
| 218 | |||
| 219 | # Include files are created or brought up-to-date | ||
| 220 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 221 | testing "Create include file" \ | ||
| 222 | "make -f -" \ | ||
| 223 | "made\n" "" ' | ||
| 224 | target: | ||
| 225 | @echo $(VAR) | ||
| 226 | mk: | ||
| 227 | @echo "VAR = made" >mk | ||
| 228 | include mk | ||
| 229 | ' | ||
| 230 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 231 | |||
| 232 | # Include files are created or brought up-to-date even when the -n | ||
| 233 | # option is given. | ||
| 234 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 235 | testing "Create include file even with -n" \ | ||
| 236 | "make -n -f -" \ | ||
| 237 | "echo made\n" "" ' | ||
| 238 | target: | ||
| 239 | @echo $(VAR) | ||
| 240 | mk: | ||
| 241 | @echo "VAR = made" >mk | ||
| 242 | include mk | ||
| 243 | ' | ||
| 244 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 245 | |||
| 246 | # Failure to create an include file isn't an error. (Provided the | ||
| 247 | # include line is ignoring non-existent files.) | ||
| 248 | testing "Failure to create include file is OK" \ | ||
| 249 | "make -f -" \ | ||
| 250 | "OK\n" "" ' | ||
| 251 | target: | ||
| 252 | @echo OK | ||
| 253 | mk: | ||
| 254 | @: | ||
| 255 | -include mk | ||
| 256 | ' | ||
| 257 | |||
| 258 | # Nested macro expansion is allowed. This should be compatible | ||
| 259 | # with other implementations. | ||
| 260 | testing "Nested macro expansion" \ | ||
| 261 | "make -f -" "0 bc\n1 d\n2\n3\n4 bcd\n5 bcd\n" "" ' | ||
| 262 | a = b | ||
| 263 | b = c | ||
| 264 | c = d | ||
| 265 | $(a:.q=.v)$(b:.z=.v) = bc | ||
| 266 | bcd = bcd | ||
| 267 | target: | ||
| 268 | @echo 0 $(bc) | ||
| 269 | @echo 1 $($($(a))) | ||
| 270 | @echo 2 $($(a) $(b) $(c)) | ||
| 271 | @echo 3 $($a $b $c) | ||
| 272 | @echo 4 $($(a)$(b)$(c)) | ||
| 273 | @echo 5 $($a$b$c) | ||
| 274 | ' | ||
| 275 | |||
| 276 | testing "Double-colon rule" \ | ||
| 277 | "make -f -" "target1\ntarget2\n" "" ' | ||
| 278 | target:: | ||
| 279 | @echo target1 | ||
| 280 | target:: | ||
| 281 | @echo target2 | ||
| 282 | ' | ||
| 283 | |||
| 284 | # There was a bug whereby the modification time of a file created by | ||
| 285 | # double-colon rules wasn't correctly updated. This test checks that | ||
| 286 | # the bug is now fixed. | ||
| 287 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 288 | touch -t 202206171200 file1 | ||
| 289 | touch -t 202206171201 intermediate | ||
| 290 | touch -t 202206171202 target | ||
| 291 | touch -t 202206171203 file2 | ||
| 292 | testing "Target depends on prerequisite updated by double-colon rule" \ | ||
| 293 | "make -f -" \ | ||
| 294 | "file2\n" "" ' | ||
| 295 | target: intermediate | ||
| 296 | @cat intermediate | ||
| 297 | intermediate:: file1 | ||
| 298 | @echo file1 >>intermediate | ||
| 299 | intermediate:: file2 | ||
| 300 | @echo file2 >>intermediate | ||
| 301 | ' | ||
| 302 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 303 | |||
| 304 | # Use chained inference rules to determine prerequisites. | ||
| 305 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 306 | touch target.p | ||
| 307 | testing "Chained inference rules" \ | ||
| 308 | "make -s -f - target.s" \ | ||
| 309 | "target.q\ntarget.r\ntarget.s\n" "" ' | ||
| 310 | .SUFFIXES: .p .q .r .s | ||
| 311 | .p.q: | ||
| 312 | @cp $< $*.q | ||
| 313 | @echo $*.q | ||
| 314 | .q.r: | ||
| 315 | @cp $< $*.r | ||
| 316 | @echo $*.r | ||
| 317 | .r.s: | ||
| 318 | @cp $< $*.s | ||
| 319 | @echo $*.s | ||
| 320 | ' | ||
| 321 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 322 | |||
| 323 | # Assign the output of a shell command to a macro. | ||
| 324 | testing "Shell assignment" \ | ||
| 325 | "make -f -" \ | ||
| 326 | "1 2 3 4\n" "" ' | ||
| 327 | hello != echo 1; echo 2; echo 3; echo; echo | ||
| 328 | |||
| 329 | target: | ||
| 330 | @echo "$(hello) 4" | ||
| 331 | ' | ||
| 332 | |||
| 333 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 334 | # make supports *, ? and [] wildcards in targets and prerequisites | ||
| 335 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 336 | touch -t 202206171201 t1a t2aa t3b | ||
| 337 | touch s1a s2aa s3b | ||
| 338 | testing "Expand wildcards in filenames" \ | ||
| 339 | "make -f - t1a t2aa t3b" \ | ||
| 340 | "t1a s1a s2aa s3b\nt2aa s1a s2aa s3b\nt3b s1a s2aa s3b\n" "" ' | ||
| 341 | t1? t2* t3[abc]: s1? s2* s3[abc] | ||
| 342 | @echo $@ $? | ||
| 343 | ' | ||
| 344 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 345 | |||
| 346 | # Skip duplicate entries in $? and $^ | ||
| 347 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 348 | touch -t 202206171200 file1 file3 | ||
| 349 | touch -t 202206171201 target | ||
| 350 | touch -t 202206171202 file2 | ||
| 351 | testing "Skip duplicate entries in \$? and \$^" \ | ||
| 352 | "make -f -" \ | ||
| 353 | "file2\nfile1 file2 file3\n" "" ' | ||
| 354 | target: file1 file2 file2 file3 file3 | ||
| 355 | @echo $? | ||
| 356 | @echo $^ | ||
| 357 | ' | ||
| 358 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 359 | |||
| 360 | # Skip duplicate entries in $? and $^, with each double-colon rule | ||
| 361 | # handled separately | ||
| 362 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 363 | touch -t 202206171200 file1 file3 | ||
| 364 | touch -t 202206171201 target | ||
| 365 | touch -t 202206171202 file2 | ||
| 366 | testing "Skip duplicate entries: double-colon rules" \ | ||
| 367 | "make -f -" \ | ||
| 368 | "file2\nfile1 file3 file2\nfile2\nfile2 file3\n" "" ' | ||
| 369 | target:: file1 file3 file1 file2 file3 | ||
| 370 | @echo $? | ||
| 371 | @echo $^ | ||
| 372 | target:: file2 file3 file3 | ||
| 373 | @echo $? | ||
| 374 | @echo $^ | ||
| 375 | ' | ||
| 376 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 377 | |||
| 378 | # Skip duplicate entries in $? and $^, with each double-colon rule | ||
| 379 | # handled separately. No prerequisites out-of-date in the first. | ||
| 380 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 381 | touch -t 202206171200 file1 file3 | ||
| 382 | touch -t 202206171201 target | ||
| 383 | touch -t 202206171202 file2 | ||
| 384 | testing "Skip duplicate entries: double-colon rules, only second invoked" \ | ||
| 385 | "make -f -" \ | ||
| 386 | "file2\nfile2 file3\n" "" ' | ||
| 387 | target:: file1 file3 file1 file3 | ||
| 388 | @echo $? | ||
| 389 | @echo $^ | ||
| 390 | target:: file2 file3 file3 | ||
| 391 | @echo $? | ||
| 392 | @echo $^ | ||
| 393 | ' | ||
| 394 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
| 395 | |||
| 396 | # Double-colon rules didn't work properly if their target was phony: | ||
| 397 | # - they didn't ignore the presence of a file matching the target name; | ||
| 398 | # - they were also invoked as if they were a single-colon rule. | ||
| 399 | mkdir make.tempdir && cd make.tempdir || exit 1 | ||
| 400 | touch -t 202206171200 file1 | ||
| 401 | touch -t 202206171201 target | ||
| 402 | testing "Phony target of double-colon rule" \ | ||
| 403 | "make -f - 2>&1" \ | ||
| 404 | "unconditional\nconditional\n" "" ' | ||
| 405 | .PHONY: target | ||
| 406 | target:: | ||
| 407 | @echo unconditional | ||
| 408 | target:: file1 | ||
| 409 | @echo conditional | ||
| 410 | file1: | ||
| 411 | @touch file1 | ||
| 412 | ' | ||
| 413 | cd .. || exit 1; rm -rf make.tempdir 2>/dev/null | ||
diff --git a/win32/Kbuild b/win32/Kbuild index 9f486d5e9..10614461a 100644 --- a/win32/Kbuild +++ b/win32/Kbuild | |||
| @@ -8,6 +8,7 @@ lib-$(CONFIG_PLATFORM_MINGW32) += dirent.o | |||
| 8 | lib-$(CONFIG_PLATFORM_MINGW32) += env.o | 8 | lib-$(CONFIG_PLATFORM_MINGW32) += env.o |
| 9 | lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o | 9 | lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o |
| 10 | lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o | 10 | lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o |
| 11 | lib-$(CONFIG_MAKE) += glob.o | ||
| 11 | lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o | 12 | lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o |
| 12 | lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o | 13 | lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o |
| 13 | lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o | 14 | lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o |
diff --git a/win32/glob.c b/win32/glob.c new file mode 100644 index 000000000..1cc6483e7 --- /dev/null +++ b/win32/glob.c | |||
| @@ -0,0 +1,343 @@ | |||
| 1 | /* | ||
| 2 | glob from musl (https://www.musl-libc.org/). | ||
| 3 | |||
| 4 | MIT licensed: | ||
| 5 | |||
| 6 | ---------------------------------------------------------------------- | ||
| 7 | Copyright © 2005-2020 Rich Felker, et al. | ||
| 8 | |||
| 9 | Permission is hereby granted, free of charge, to any person obtaining | ||
| 10 | a copy of this software and associated documentation files (the | ||
| 11 | "Software"), to deal in the Software without restriction, including | ||
| 12 | without limitation the rights to use, copy, modify, merge, publish, | ||
| 13 | distribute, sublicense, and/or sell copies of the Software, and to | ||
| 14 | permit persons to whom the Software is furnished to do so, subject to | ||
| 15 | the following conditions: | ||
| 16 | |||
| 17 | The above copyright notice and this permission notice shall be | ||
| 18 | included in all copies or substantial portions of the Software. | ||
| 19 | |||
| 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| 23 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
| 25 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
| 26 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 27 | ---------------------------------------------------------------------- | ||
| 28 | */ | ||
| 29 | #include "libbb.h" | ||
| 30 | #include <glob.h> | ||
| 31 | #include <fnmatch.h> | ||
| 32 | |||
| 33 | struct match | ||
| 34 | { | ||
| 35 | struct match *next; | ||
| 36 | char name[]; | ||
| 37 | }; | ||
| 38 | |||
| 39 | static int append(struct match **tail, const char *name, size_t len, int mark) | ||
| 40 | { | ||
| 41 | struct match *new = malloc(sizeof(struct match) + len + 2); | ||
| 42 | if (!new) return -1; | ||
| 43 | (*tail)->next = new; | ||
| 44 | new->next = NULL; | ||
| 45 | memcpy(new->name, name, len+1); | ||
| 46 | if (mark && len && name[len-1]!='/') { | ||
| 47 | new->name[len] = '/'; | ||
| 48 | new->name[len+1] = 0; | ||
| 49 | } | ||
| 50 | *tail = new; | ||
| 51 | return 0; | ||
| 52 | } | ||
| 53 | |||
| 54 | static int do_glob(char *buf, size_t pos, int type, char *pat, int flags, int (*errfunc)(const char *path, int err), struct match **tail) | ||
| 55 | { | ||
| 56 | ptrdiff_t i, j; | ||
| 57 | int in_bracket, overflow; | ||
| 58 | char *p2, saved_sep; | ||
| 59 | int readerr, old_errno; | ||
| 60 | DIR *dir; | ||
| 61 | struct dirent *de; | ||
| 62 | |||
| 63 | /* If GLOB_MARK is unused, we don't care about type. */ | ||
| 64 | if (!type && !(flags & GLOB_MARK)) type = DT_REG; | ||
| 65 | |||
| 66 | /* Special-case the remaining pattern being all slashes, in | ||
| 67 | * which case we can use caller-passed type if it's a dir. */ | ||
| 68 | if (*pat && type!=DT_DIR) type = 0; | ||
| 69 | while (pos+1 < PATH_MAX && *pat=='/') buf[pos++] = *pat++; | ||
| 70 | |||
| 71 | /* Consume maximal [escaped-]literal prefix of pattern, copying | ||
| 72 | * and un-escaping it to the running buffer as we go. */ | ||
| 73 | i=0; j=0; | ||
| 74 | in_bracket = 0; overflow = 0; | ||
| 75 | for (; pat[i]!='*' && pat[i]!='?' && (!in_bracket || pat[i]!=']'); i++) { | ||
| 76 | if (!pat[i]) { | ||
| 77 | if (overflow) return 0; | ||
| 78 | pat += i; | ||
| 79 | pos += j; | ||
| 80 | i = j = 0; | ||
| 81 | break; | ||
| 82 | } else if (pat[i] == '[') { | ||
| 83 | in_bracket = 1; | ||
| 84 | } else if (pat[i] == '\\' && !(flags & GLOB_NOESCAPE)) { | ||
| 85 | /* Backslashes inside a bracket are (at least by | ||
| 86 | * our interpretation) non-special, so if next | ||
| 87 | * char is ']' we have a complete expression. */ | ||
| 88 | if (in_bracket && pat[i+1]==']') break; | ||
| 89 | /* Unpaired final backslash never matches. */ | ||
| 90 | if (!pat[i+1]) return 0; | ||
| 91 | i++; | ||
| 92 | } | ||
| 93 | if (pat[i] == '/') { | ||
| 94 | if (overflow) return 0; | ||
| 95 | in_bracket = 0; | ||
| 96 | pat += i+1; | ||
| 97 | i = -1; | ||
| 98 | pos += j+1; | ||
| 99 | j = -1; | ||
| 100 | } | ||
| 101 | /* Only store a character if it fits in the buffer, but if | ||
| 102 | * a potential bracket expression is open, the overflow | ||
| 103 | * must be remembered and handled later only if the bracket | ||
| 104 | * is unterminated (and thereby a literal), so as not to | ||
| 105 | * disallow long bracket expressions with short matches. */ | ||
| 106 | if (pos+(j+1) < PATH_MAX) { | ||
| 107 | buf[pos+j++] = pat[i]; | ||
| 108 | } else if (in_bracket) { | ||
| 109 | overflow = 1; | ||
| 110 | } else { | ||
| 111 | return 0; | ||
| 112 | } | ||
| 113 | /* If we consume any new components, the caller-passed type | ||
| 114 | * or dummy type from above is no longer valid. */ | ||
| 115 | type = 0; | ||
| 116 | } | ||
| 117 | buf[pos] = 0; | ||
| 118 | if (!*pat) { | ||
| 119 | /* If we consumed any components above, or if GLOB_MARK is | ||
| 120 | * requested and we don't yet know if the match is a dir, | ||
| 121 | * we must confirm the file exists and/or determine its type. | ||
| 122 | * | ||
| 123 | * If marking dirs, symlink type is inconclusive; we need the | ||
| 124 | * type for the symlink target, and therefore must try stat | ||
| 125 | * first unless type is known not to be a symlink. Otherwise, | ||
| 126 | * or if that fails, use lstat for determining existence to | ||
| 127 | * avoid false negatives in the case of broken symlinks. */ | ||
| 128 | struct stat st; | ||
| 129 | if ((flags & GLOB_MARK) && (!type||type==DT_LNK) && !stat(buf, &st)) { | ||
| 130 | if (S_ISDIR(st.st_mode)) type = DT_DIR; | ||
| 131 | else type = DT_REG; | ||
| 132 | } | ||
| 133 | if (!type && lstat(buf, &st)) { | ||
| 134 | if (errno!=ENOENT && (errfunc(buf, errno) || (flags & GLOB_ERR))) | ||
| 135 | return GLOB_ABORTED; | ||
| 136 | return 0; | ||
| 137 | } | ||
| 138 | if (append(tail, buf, pos, (flags & GLOB_MARK) && type==DT_DIR)) | ||
| 139 | return GLOB_NOSPACE; | ||
| 140 | return 0; | ||
| 141 | } | ||
| 142 | p2 = strchr(pat, '/'); | ||
| 143 | saved_sep = '/'; | ||
| 144 | /* Check if the '/' was escaped and, if so, remove the escape char | ||
| 145 | * so that it will not be unpaired when passed to fnmatch. */ | ||
| 146 | if (p2 && !(flags & GLOB_NOESCAPE)) { | ||
| 147 | char *p; | ||
| 148 | for (p=p2; p>pat && p[-1]=='\\'; p--); | ||
| 149 | if ((p2-p)%2) { | ||
| 150 | p2--; | ||
| 151 | saved_sep = '\\'; | ||
| 152 | } | ||
| 153 | } | ||
| 154 | dir = opendir(pos ? buf : "."); | ||
| 155 | if (!dir) { | ||
| 156 | if (errfunc(buf, errno) || (flags & GLOB_ERR)) | ||
| 157 | return GLOB_ABORTED; | ||
| 158 | return 0; | ||
| 159 | } | ||
| 160 | old_errno = errno; | ||
| 161 | while (errno=0, de=readdir(dir)) { | ||
| 162 | size_t l; | ||
| 163 | int fnm_flags, r; | ||
| 164 | |||
| 165 | /* Quickly skip non-directories when there's pattern left. */ | ||
| 166 | if (p2 && de->d_type && de->d_type!=DT_DIR && de->d_type!=DT_LNK) | ||
| 167 | continue; | ||
| 168 | |||
| 169 | l = strlen(de->d_name); | ||
| 170 | if (l >= PATH_MAX-pos) continue; | ||
| 171 | |||
| 172 | if (p2) *p2 = 0; | ||
| 173 | |||
| 174 | fnm_flags= ((flags & GLOB_NOESCAPE) ? FNM_NOESCAPE : 0) | ||
| 175 | | ((!(flags & GLOB_PERIOD)) ? FNM_PERIOD : 0); | ||
| 176 | |||
| 177 | if (fnmatch(pat, de->d_name, fnm_flags)) | ||
| 178 | continue; | ||
| 179 | |||
| 180 | /* With GLOB_PERIOD, don't allow matching . or .. unless | ||
| 181 | * fnmatch would match them with FNM_PERIOD rules in effect. */ | ||
| 182 | if (p2 && (flags & GLOB_PERIOD) && de->d_name[0]=='.' | ||
| 183 | && (!de->d_name[1] || (de->d_name[1]=='.' && !de->d_name[2])) | ||
| 184 | && fnmatch(pat, de->d_name, fnm_flags | FNM_PERIOD)) | ||
| 185 | continue; | ||
| 186 | |||
| 187 | memcpy(buf+pos, de->d_name, l+1); | ||
| 188 | if (p2) *p2 = saved_sep; | ||
| 189 | r = do_glob(buf, pos+l, de->d_type, p2 ? p2 : (char *)"", flags, errfunc, tail); | ||
| 190 | if (r) { | ||
| 191 | closedir(dir); | ||
| 192 | return r; | ||
| 193 | } | ||
| 194 | } | ||
| 195 | readerr = errno; | ||
| 196 | if (p2) *p2 = saved_sep; | ||
| 197 | closedir(dir); | ||
| 198 | if (readerr && (errfunc(buf, errno) || (flags & GLOB_ERR))) | ||
| 199 | return GLOB_ABORTED; | ||
| 200 | errno = old_errno; | ||
| 201 | return 0; | ||
| 202 | } | ||
| 203 | |||
| 204 | static int ignore_err(const char *path UNUSED_PARAM, int err UNUSED_PARAM) | ||
| 205 | { | ||
| 206 | return 0; | ||
| 207 | } | ||
| 208 | |||
| 209 | static void freelist(struct match *head) | ||
| 210 | { | ||
| 211 | struct match *match, *next; | ||
| 212 | for (match=head->next; match; match=next) { | ||
| 213 | next = match->next; | ||
| 214 | free(match); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | |||
| 218 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 219 | static int sort(const void *a, const void *b) | ||
| 220 | { | ||
| 221 | return strcmp(*(const char **)a, *(const char **)b); | ||
| 222 | } | ||
| 223 | |||
| 224 | static int expand_tilde(char **pat, char *buf, size_t *pos) | ||
| 225 | { | ||
| 226 | char *p = *pat + 1; | ||
| 227 | size_t i = 0; | ||
| 228 | |||
| 229 | char delim, *name_end = __strchrnul(p, '/'); | ||
| 230 | if ((delim = *name_end)) *name_end++ = 0; | ||
| 231 | *pat = name_end; | ||
| 232 | |||
| 233 | char *home = *p ? NULL : getenv("HOME"); | ||
| 234 | if (!home) { | ||
| 235 | struct passwd pw, *res; | ||
| 236 | switch (*p ? getpwnam_r(p, &pw, buf, PATH_MAX, &res) | ||
| 237 | : getpwuid_r(getuid(), &pw, buf, PATH_MAX, &res)) { | ||
| 238 | case ENOMEM: | ||
| 239 | return GLOB_NOSPACE; | ||
| 240 | case 0: | ||
| 241 | if (!res) | ||
| 242 | default: | ||
| 243 | return GLOB_NOMATCH; | ||
| 244 | } | ||
| 245 | home = pw.pw_dir; | ||
| 246 | } | ||
| 247 | while (i < PATH_MAX - 2 && *home) | ||
| 248 | buf[i++] = *home++; | ||
| 249 | if (*home) | ||
| 250 | return GLOB_NOMATCH; | ||
| 251 | if ((buf[i] = delim)) | ||
| 252 | buf[++i] = 0; | ||
| 253 | *pos = i; | ||
| 254 | return 0; | ||
| 255 | } | ||
| 256 | #endif | ||
| 257 | |||
| 258 | int glob(const char *restrict pat, int flags, int (*errfunc)(const char *path, int err), glob_t *restrict g) | ||
| 259 | { | ||
| 260 | struct match head = { .next = NULL }, *tail = &head; | ||
| 261 | size_t cnt, i; | ||
| 262 | size_t offs = (flags & GLOB_DOOFFS) ? g->gl_offs : 0; | ||
| 263 | int error = 0; | ||
| 264 | char buf[PATH_MAX]; | ||
| 265 | |||
| 266 | if (!errfunc) errfunc = ignore_err; | ||
| 267 | |||
| 268 | if (!(flags & GLOB_APPEND)) { | ||
| 269 | g->gl_offs = offs; | ||
| 270 | g->gl_pathc = 0; | ||
| 271 | g->gl_pathv = NULL; | ||
| 272 | } | ||
| 273 | |||
| 274 | if (*pat) { | ||
| 275 | char *p = strdup(pat); | ||
| 276 | size_t pos = 0; | ||
| 277 | char *s = p; | ||
| 278 | if (!p) return GLOB_NOSPACE; | ||
| 279 | buf[0] = 0; | ||
| 280 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 281 | if ((flags & (GLOB_TILDE | GLOB_TILDE_CHECK)) && *p == '~') | ||
| 282 | error = expand_tilde(&s, buf, &pos); | ||
| 283 | if (!error) | ||
| 284 | #endif | ||
| 285 | error = do_glob(buf, pos, 0, s, flags, errfunc, &tail); | ||
| 286 | free(p); | ||
| 287 | } | ||
| 288 | |||
| 289 | if (error == GLOB_NOSPACE) { | ||
| 290 | freelist(&head); | ||
| 291 | return error; | ||
| 292 | } | ||
| 293 | |||
| 294 | for (cnt=0, tail=head.next; tail; tail=tail->next, cnt++); | ||
| 295 | if (!cnt) { | ||
| 296 | if (flags & GLOB_NOCHECK) { | ||
| 297 | tail = &head; | ||
| 298 | if (append(&tail, pat, strlen(pat), 0)) | ||
| 299 | return GLOB_NOSPACE; | ||
| 300 | cnt++; | ||
| 301 | } else | ||
| 302 | return GLOB_NOMATCH; | ||
| 303 | } | ||
| 304 | |||
| 305 | if (flags & GLOB_APPEND) { | ||
| 306 | char **pathv = realloc(g->gl_pathv, (offs + g->gl_pathc + cnt + 1) * sizeof(char *)); | ||
| 307 | if (!pathv) { | ||
| 308 | freelist(&head); | ||
| 309 | return GLOB_NOSPACE; | ||
| 310 | } | ||
| 311 | g->gl_pathv = pathv; | ||
| 312 | offs += g->gl_pathc; | ||
| 313 | } else { | ||
| 314 | g->gl_pathv = malloc((offs + cnt + 1) * sizeof(char *)); | ||
| 315 | if (!g->gl_pathv) { | ||
| 316 | freelist(&head); | ||
| 317 | return GLOB_NOSPACE; | ||
| 318 | } | ||
| 319 | for (i=0; i<offs; i++) | ||
| 320 | g->gl_pathv[i] = NULL; | ||
| 321 | } | ||
| 322 | for (i=0, tail=head.next; i<cnt; tail=tail->next, i++) | ||
| 323 | g->gl_pathv[offs + i] = tail->name; | ||
| 324 | g->gl_pathv[offs + i] = NULL; | ||
| 325 | g->gl_pathc += cnt; | ||
| 326 | |||
| 327 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 328 | if (!(flags & GLOB_NOSORT)) | ||
| 329 | qsort(g->gl_pathv+offs, cnt, sizeof(char *), sort); | ||
| 330 | #endif | ||
| 331 | |||
| 332 | return error; | ||
| 333 | } | ||
| 334 | |||
| 335 | void globfree(glob_t *g) | ||
| 336 | { | ||
| 337 | size_t i; | ||
| 338 | for (i=0; i<g->gl_pathc; i++) | ||
| 339 | free(g->gl_pathv[g->gl_offs + i] - offsetof(struct match, name)); | ||
| 340 | free(g->gl_pathv); | ||
| 341 | g->gl_pathc = 0; | ||
| 342 | g->gl_pathv = NULL; | ||
| 343 | } | ||
diff --git a/win32/glob.h b/win32/glob.h new file mode 100644 index 000000000..a8141b8bf --- /dev/null +++ b/win32/glob.h | |||
| @@ -0,0 +1,89 @@ | |||
| 1 | /* | ||
| 2 | glob from musl (https://www.musl-libc.org/). | ||
| 3 | |||
| 4 | MIT licensed: | ||
| 5 | |||
| 6 | ---------------------------------------------------------------------- | ||
| 7 | Copyright © 2005-2020 Rich Felker, et al. | ||
| 8 | |||
| 9 | Permission is hereby granted, free of charge, to any person obtaining | ||
| 10 | a copy of this software and associated documentation files (the | ||
| 11 | "Software"), to deal in the Software without restriction, including | ||
| 12 | without limitation the rights to use, copy, modify, merge, publish, | ||
| 13 | distribute, sublicense, and/or sell copies of the Software, and to | ||
| 14 | permit persons to whom the Software is furnished to do so, subject to | ||
| 15 | the following conditions: | ||
| 16 | |||
| 17 | The above copyright notice and this permission notice shall be | ||
| 18 | included in all copies or substantial portions of the Software. | ||
| 19 | |||
| 20 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | ||
| 21 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | ||
| 22 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | ||
| 23 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | ||
| 24 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | ||
| 25 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE | ||
| 26 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | ||
| 27 | ---------------------------------------------------------------------- | ||
| 28 | */ | ||
| 29 | #ifndef _GLOB_H | ||
| 30 | #define _GLOB_H | ||
| 31 | |||
| 32 | #ifdef __cplusplus | ||
| 33 | extern "C" { | ||
| 34 | #endif | ||
| 35 | |||
| 36 | typedef struct { | ||
| 37 | size_t gl_pathc; | ||
| 38 | char **gl_pathv; | ||
| 39 | size_t gl_offs; | ||
| 40 | int __dummy1; | ||
| 41 | void *__dummy2[5]; | ||
| 42 | } glob_t; | ||
| 43 | |||
| 44 | int glob(const char *__restrict, int, int (*)(const char *, int), glob_t *__restrict); | ||
| 45 | void globfree(glob_t *); | ||
| 46 | |||
| 47 | #if ENABLE_PLATFORM_MINGW32 | ||
| 48 | // Set some flags to zero so the compiler can exclude unused code. | ||
| 49 | #define GLOB_ERR 0 | ||
| 50 | #define GLOB_MARK 0 | ||
| 51 | #define GLOB_NOSORT 0x04 | ||
| 52 | #define GLOB_DOOFFS 0 | ||
| 53 | #define GLOB_NOCHECK 0x10 | ||
| 54 | #define GLOB_APPEND 0 | ||
| 55 | #define GLOB_NOESCAPE 0x40 | ||
| 56 | #define GLOB_PERIOD 0 | ||
| 57 | |||
| 58 | #define GLOB_TILDE 0 | ||
| 59 | #define GLOB_TILDE_CHECK 0 | ||
| 60 | #else | ||
| 61 | #define GLOB_ERR 0x01 | ||
| 62 | #define GLOB_MARK 0x02 | ||
| 63 | #define GLOB_NOSORT 0x04 | ||
| 64 | #define GLOB_DOOFFS 0x08 | ||
| 65 | #define GLOB_NOCHECK 0x10 | ||
| 66 | #define GLOB_APPEND 0x20 | ||
| 67 | #define GLOB_NOESCAPE 0x40 | ||
| 68 | #define GLOB_PERIOD 0x80 | ||
| 69 | |||
| 70 | #define GLOB_TILDE 0x1000 | ||
| 71 | #define GLOB_TILDE_CHECK 0x4000 | ||
| 72 | #endif | ||
| 73 | |||
| 74 | #define GLOB_NOSPACE 1 | ||
| 75 | #define GLOB_ABORTED 2 | ||
| 76 | #define GLOB_NOMATCH 3 | ||
| 77 | #define GLOB_NOSYS 4 | ||
| 78 | |||
| 79 | #if defined(_LARGEFILE64_SOURCE) || defined(_GNU_SOURCE) | ||
| 80 | #define glob64 glob | ||
| 81 | #define globfree64 globfree | ||
| 82 | #define glob64_t glob_t | ||
| 83 | #endif | ||
| 84 | |||
| 85 | #ifdef __cplusplus | ||
| 86 | } | ||
| 87 | #endif | ||
| 88 | |||
| 89 | #endif | ||
