diff options
Diffstat (limited to 'miscutils')
| -rw-r--r-- | miscutils/bbconfig.c | 1 | ||||
| -rw-r--r-- | miscutils/bc.c | 18 | ||||
| -rw-r--r-- | miscutils/dc.c | 2 | ||||
| -rw-r--r-- | miscutils/drop.c | 220 | ||||
| -rw-r--r-- | miscutils/iconv.c | 1771 | ||||
| -rw-r--r-- | miscutils/inotifyd.c | 211 | ||||
| -rw-r--r-- | miscutils/jn.c | 37 | ||||
| -rw-r--r-- | miscutils/less.c | 99 | ||||
| -rw-r--r-- | miscutils/make.c | 3563 | ||||
| -rw-r--r-- | miscutils/man.c | 31 | ||||
| -rw-r--r-- | miscutils/time.c | 51 | ||||
| -rw-r--r-- | miscutils/ts.c | 8 |
12 files changed, 6008 insertions, 4 deletions
diff --git a/miscutils/bbconfig.c b/miscutils/bbconfig.c index fe02516a8..077e03c5d 100644 --- a/miscutils/bbconfig.c +++ b/miscutils/bbconfig.c | |||
| @@ -35,6 +35,7 @@ | |||
| 35 | #include "libbb.h" | 35 | #include "libbb.h" |
| 36 | #include "bbconfigopts.h" | 36 | #include "bbconfigopts.h" |
| 37 | #if ENABLE_FEATURE_COMPRESS_BBCONFIG | 37 | #if ENABLE_FEATURE_COMPRESS_BBCONFIG |
| 38 | #define BB_ARCHIVE_PUBLIC | ||
| 38 | # include "bb_archive.h" | 39 | # include "bb_archive.h" |
| 39 | # include "bbconfigopts_bz2.h" | 40 | # include "bbconfigopts_bz2.h" |
| 40 | #endif | 41 | #endif |
diff --git a/miscutils/bc.c b/miscutils/bc.c index 28bc40c8b..31485ae9c 100644 --- a/miscutils/bc.c +++ b/miscutils/bc.c | |||
| @@ -203,6 +203,9 @@ | |||
| 203 | 203 | ||
| 204 | #include "libbb.h" | 204 | #include "libbb.h" |
| 205 | #include "common_bufsiz.h" | 205 | #include "common_bufsiz.h" |
| 206 | #if ENABLE_PLATFORM_MINGW32 | ||
| 207 | # include "BB_VER.h" | ||
| 208 | #endif | ||
| 206 | 209 | ||
| 207 | #if !ENABLE_BC && !ENABLE_FEATURE_DC_BIG | 210 | #if !ENABLE_BC && !ENABLE_FEATURE_DC_BIG |
| 208 | # include "dc.c" | 211 | # include "dc.c" |
| @@ -7466,6 +7469,17 @@ static unsigned xc_vm_envLen(const char *var) | |||
| 7466 | return len; | 7469 | return len; |
| 7467 | } | 7470 | } |
| 7468 | 7471 | ||
| 7472 | #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_BC_INTERACTIVE | ||
| 7473 | static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) | ||
| 7474 | { | ||
| 7475 | if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { | ||
| 7476 | bb_got_signal = SIGINT; | ||
| 7477 | return TRUE; | ||
| 7478 | } | ||
| 7479 | return FALSE; | ||
| 7480 | } | ||
| 7481 | #endif | ||
| 7482 | |||
| 7469 | static int xc_vm_init(const char *env_len) | 7483 | static int xc_vm_init(const char *env_len) |
| 7470 | { | 7484 | { |
| 7471 | G.prog.len = xc_vm_envLen(env_len); | 7485 | G.prog.len = xc_vm_envLen(env_len); |
| @@ -7491,7 +7505,11 @@ static int xc_vm_init(const char *env_len) | |||
| 7491 | // from stdin is not interrupted by ^C either, | 7505 | // from stdin is not interrupted by ^C either, |
| 7492 | // it restarts, thus fgetc() does not return on ^C. | 7506 | // it restarts, thus fgetc() does not return on ^C. |
| 7493 | // (This problem manifests only if line editing is disabled) | 7507 | // (This problem manifests only if line editing is disabled) |
| 7508 | # if !ENABLE_PLATFORM_MINGW32 | ||
| 7494 | signal_SA_RESTART_empty_mask(SIGINT, record_signo); | 7509 | signal_SA_RESTART_empty_mask(SIGINT, record_signo); |
| 7510 | # else | ||
| 7511 | SetConsoleCtrlHandler(ctrl_handler, TRUE); | ||
| 7512 | # endif | ||
| 7495 | 7513 | ||
| 7496 | // Without SA_RESTART, this exhibits a bug: | 7514 | // Without SA_RESTART, this exhibits a bug: |
| 7497 | // "while (1) print 1" and try ^C-ing it. | 7515 | // "while (1) print 1" and try ^C-ing it. |
diff --git a/miscutils/dc.c b/miscutils/dc.c index 42baa67ad..d6369fd15 100644 --- a/miscutils/dc.c +++ b/miscutils/dc.c | |||
| @@ -17,7 +17,7 @@ typedef unsigned long data_t; | |||
| 17 | #define DATA_FMT "l" | 17 | #define DATA_FMT "l" |
| 18 | #else | 18 | #else |
| 19 | typedef unsigned long long data_t; | 19 | typedef unsigned long long data_t; |
| 20 | #define DATA_FMT "ll" | 20 | #define DATA_FMT LL_FMT |
| 21 | #endif | 21 | #endif |
| 22 | 22 | ||
| 23 | struct globals { | 23 | struct globals { |
diff --git a/miscutils/drop.c b/miscutils/drop.c new file mode 100644 index 000000000..bef1fa52b --- /dev/null +++ b/miscutils/drop.c | |||
| @@ -0,0 +1,220 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * drop - run a command without elevated privileges. | ||
| 4 | * | ||
| 5 | * Copyright (c) 2023 Ronald M Yorston | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | //config:config DROP | ||
| 10 | //config: bool "drop" | ||
| 11 | //config: default y | ||
| 12 | //config: depends on PLATFORM_MINGW32 && SH_IS_ASH | ||
| 13 | //config: help | ||
| 14 | //config: Run a command without elevated privileges | ||
| 15 | |||
| 16 | //config:config CDROP | ||
| 17 | //config: bool "cdrop" | ||
| 18 | //config: default y | ||
| 19 | //config: depends on PLATFORM_MINGW32 | ||
| 20 | //config: help | ||
| 21 | //config: Run a command without elevated privileges using cmd.exe | ||
| 22 | |||
| 23 | //config:config PDROP | ||
| 24 | //config: bool "pdrop" | ||
| 25 | //config: default y | ||
| 26 | //config: depends on PLATFORM_MINGW32 | ||
| 27 | //config: help | ||
| 28 | //config: Run a command without elevated privileges using PowerShell | ||
| 29 | |||
| 30 | //applet:IF_DROP(APPLET(drop, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 31 | //applet:IF_CDROP(APPLET_ODDNAME(cdrop, drop, BB_DIR_USR_BIN, BB_SUID_DROP, cdrop)) | ||
| 32 | //applet:IF_PDROP(APPLET_ODDNAME(pdrop, drop, BB_DIR_USR_BIN, BB_SUID_DROP, pdrop)) | ||
| 33 | |||
| 34 | //kbuild:lib-$(CONFIG_DROP) += drop.o | ||
| 35 | //kbuild:lib-$(CONFIG_CDROP) += drop.o | ||
| 36 | //kbuild:lib-$(CONFIG_PDROP) += drop.o | ||
| 37 | |||
| 38 | //usage:#define drop_trivial_usage | ||
| 39 | //usage: "[COMMAND [ARG...] | [-s SHELL] -c CMD_STRING [ARG...]]" | ||
| 40 | //usage:#define drop_full_usage "\n\n" | ||
| 41 | //usage: "Drop elevated privileges and run a command. If no command\n" | ||
| 42 | //usage: "is provided run a shell (by default, the BusyBox shell).\n" | ||
| 43 | |||
| 44 | //usage:#define cdrop_trivial_usage | ||
| 45 | //usage: "[COMMAND [ARG...] | -c CMD_STRING [ARG...]]" | ||
| 46 | //usage:#define cdrop_full_usage "\n\n" | ||
| 47 | //usage: "Drop elevated privileges and run a command. If no command\n" | ||
| 48 | //usage: "is provided run cmd.exe.\n" | ||
| 49 | |||
| 50 | //usage:#define pdrop_trivial_usage | ||
| 51 | //usage: "[COMMAND [ARG...] | -c CMD_STRING [ARG...]]" | ||
| 52 | //usage:#define pdrop_full_usage "\n\n" | ||
| 53 | //usage: "Drop elevated privileges and run a command. If no command\n" | ||
| 54 | //usage: "is provided run PowerShell.\n" | ||
| 55 | |||
| 56 | #include "libbb.h" | ||
| 57 | #include <winsafer.h> | ||
| 58 | #include <lazyload.h> | ||
| 59 | #include "NUM_APPLETS.h" | ||
| 60 | |||
| 61 | // Set an environment variable to the name of the unprivileged user, | ||
| 62 | // but only if it was previously unset or contained "root". | ||
| 63 | static void setenv_name(const char *key) | ||
| 64 | { | ||
| 65 | const char *name = get_user_name(); | ||
| 66 | const char *oldname = getenv(key); | ||
| 67 | |||
| 68 | if (name && (!oldname || strcmp(oldname, "root") == 0)) { | ||
| 69 | setenv(key, name, 1); | ||
| 70 | } | ||
| 71 | } | ||
| 72 | |||
| 73 | int drop_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 74 | int drop_main(int argc UNUSED_PARAM, char **argv) | ||
| 75 | { | ||
| 76 | SAFER_LEVEL_HANDLE safer; | ||
| 77 | HANDLE token; | ||
| 78 | STARTUPINFO si; | ||
| 79 | PROCESS_INFORMATION pi; | ||
| 80 | TOKEN_MANDATORY_LABEL TIL; | ||
| 81 | // Medium integrity level S-1-16-8192 | ||
| 82 | unsigned char medium[12] = { | ||
| 83 | 0x01, 0x01, 0x00, 0x00, | ||
| 84 | 0x00, 0x00, 0x00, 0x10, | ||
| 85 | 0x00, 0x20, 0x00, 0x00 | ||
| 86 | }; | ||
| 87 | DWORD code; | ||
| 88 | // This shouldn't be necessary but without it the binary complains | ||
| 89 | // it can't find CreateProcessAsUserA on older versions of Windows. | ||
| 90 | DECLARE_PROC_ADDR(BOOL, CreateProcessAsUserA, HANDLE, LPCSTR, LPSTR, | ||
| 91 | LPSECURITY_ATTRIBUTES, LPSECURITY_ATTRIBUTES, BOOL, DWORD, | ||
| 92 | LPVOID, LPCSTR, LPSTARTUPINFOA, LPPROCESS_INFORMATION); | ||
| 93 | |||
| 94 | if (!INIT_PROC_ADDR(advapi32.dll, CreateProcessAsUserA)) | ||
| 95 | bb_simple_error_msg_and_die("not supported"); | ||
| 96 | |||
| 97 | /* | ||
| 98 | * Run a shell using a token with reduced privilege. Hints from: | ||
| 99 | * | ||
| 100 | * https://stackoverflow.com/questions/17765568/ | ||
| 101 | */ | ||
| 102 | if (SaferCreateLevel(SAFER_SCOPEID_USER, SAFER_LEVELID_NORMALUSER, | ||
| 103 | SAFER_LEVEL_OPEN, &safer, NULL) && | ||
| 104 | SaferComputeTokenFromLevel(safer, NULL, &token, 0, NULL)) { | ||
| 105 | |||
| 106 | // Set medium integrity | ||
| 107 | TIL.Label.Sid = (PSID)medium; | ||
| 108 | TIL.Label.Attributes = SE_GROUP_INTEGRITY; | ||
| 109 | if (SetTokenInformation(token, TokenIntegrityLevel, &TIL, | ||
| 110 | sizeof(TOKEN_MANDATORY_LABEL))) { | ||
| 111 | char *opt_command = NULL; | ||
| 112 | char *opt_shell = NULL; | ||
| 113 | char **a; | ||
| 114 | const char *opt, *arg; | ||
| 115 | char *exe, *cmd, *q; | ||
| 116 | |||
| 117 | if (*applet_name == 'd') | ||
| 118 | getopt32(argv, "c:s:", &opt_command, &opt_shell); | ||
| 119 | else | ||
| 120 | getopt32(argv, "c:", &opt_command); | ||
| 121 | a = argv + optind; | ||
| 122 | opt = "-c"; | ||
| 123 | |||
| 124 | if (*a == NULL || opt_command) { | ||
| 125 | switch (*applet_name) { | ||
| 126 | #if ENABLE_PDROP | ||
| 127 | case 'p': | ||
| 128 | arg = "powershell.exe"; | ||
| 129 | exe = find_first_executable(arg); | ||
| 130 | break; | ||
| 131 | #endif | ||
| 132 | #if ENABLE_CDROP | ||
| 133 | case 'c': | ||
| 134 | opt = "/c"; | ||
| 135 | arg = "cmd.exe"; | ||
| 136 | exe = find_first_executable(arg); | ||
| 137 | break; | ||
| 138 | #endif | ||
| 139 | #if ENABLE_DROP | ||
| 140 | case 'd': | ||
| 141 | if (opt_shell) { | ||
| 142 | arg = bb_basename(opt_shell); | ||
| 143 | if (strcasecmp(arg, "cmd.exe") == 0) | ||
| 144 | opt = "/c"; | ||
| 145 | exe = file_is_win32_exe(opt_shell); | ||
| 146 | } else { | ||
| 147 | arg = "sh"; | ||
| 148 | exe = xstrdup(bb_busybox_exec_path); | ||
| 149 | } | ||
| 150 | break; | ||
| 151 | #endif | ||
| 152 | default: | ||
| 153 | // Never executed, just to silence warnings. | ||
| 154 | arg = argv[0]; | ||
| 155 | exe = NULL; | ||
| 156 | break; | ||
| 157 | } | ||
| 158 | } else { | ||
| 159 | arg = *a++; | ||
| 160 | |||
| 161 | #if ENABLE_FEATURE_PREFER_APPLETS && NUM_APPLETS > 1 | ||
| 162 | if (!has_path(arg) && find_applet_by_name(arg) >= 0) { | ||
| 163 | exe = xstrdup(bb_busybox_exec_path); | ||
| 164 | } else | ||
| 165 | #endif | ||
| 166 | if (has_path(arg)) { | ||
| 167 | exe = file_is_win32_exe(arg); | ||
| 168 | } else { | ||
| 169 | exe = find_first_executable(arg); | ||
| 170 | } | ||
| 171 | } | ||
| 172 | |||
| 173 | if (exe == NULL) { | ||
| 174 | xfunc_error_retval = 127; | ||
| 175 | bb_error_msg_and_die("can't find '%s'", arg); | ||
| 176 | } | ||
| 177 | |||
| 178 | slash_to_bs(exe); | ||
| 179 | cmd = quote_arg(arg); | ||
| 180 | if (opt_command) { | ||
| 181 | cmd = xappendword(cmd, opt); | ||
| 182 | q = quote_arg(opt_command); | ||
| 183 | cmd = xappendword(cmd, q); | ||
| 184 | free(q); | ||
| 185 | } | ||
| 186 | |||
| 187 | // Build the command line | ||
| 188 | while (*a) { | ||
| 189 | q = quote_arg(*a++); | ||
| 190 | cmd = xappendword(cmd, q); | ||
| 191 | free(q); | ||
| 192 | } | ||
| 193 | |||
| 194 | ZeroMemory(&si, sizeof(STARTUPINFO)); | ||
| 195 | si.cb = sizeof(STARTUPINFO); | ||
| 196 | si.hStdInput = GetStdHandle(STD_INPUT_HANDLE); | ||
| 197 | si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); | ||
| 198 | si.hStdError = GetStdHandle(STD_ERROR_HANDLE); | ||
| 199 | si.dwFlags = STARTF_USESTDHANDLES; | ||
| 200 | |||
| 201 | setenv_name("USER"); | ||
| 202 | setenv_name("LOGNAME"); | ||
| 203 | |||
| 204 | if (!CreateProcessAsUserA(token, exe, cmd, NULL, NULL, TRUE, | ||
| 205 | 0, NULL, NULL, &si, &pi)) { | ||
| 206 | xfunc_error_retval = 126; | ||
| 207 | bb_error_msg_and_die("can't execute '%s'", exe); | ||
| 208 | } | ||
| 209 | |||
| 210 | kill_child_ctrl_handler(pi.dwProcessId); | ||
| 211 | SetConsoleCtrlHandler(kill_child_ctrl_handler, TRUE); | ||
| 212 | WaitForSingleObject(pi.hProcess, INFINITE); | ||
| 213 | if (GetExitCodeProcess(pi.hProcess, &code)) { | ||
| 214 | return exit_code_to_posix(code); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | return EXIT_FAILURE; | ||
| 220 | } | ||
diff --git a/miscutils/iconv.c b/miscutils/iconv.c new file mode 100644 index 000000000..bedbb718d --- /dev/null +++ b/miscutils/iconv.c | |||
| @@ -0,0 +1,1771 @@ | |||
| 1 | /* | ||
| 2 | * iconv implementation using Win32 API to convert. | ||
| 3 | * | ||
| 4 | * This file is placed in the public domain. | ||
| 5 | */ | ||
| 6 | |||
| 7 | /* | ||
| 8 | * This code was obtained from: | ||
| 9 | * | ||
| 10 | * https://github.com/win-iconv/win-iconv | ||
| 11 | * | ||
| 12 | * Modified for busybox-w32 by Ronald M Yorston. These modifications | ||
| 13 | * are also dedicated to the public domain. | ||
| 14 | */ | ||
| 15 | |||
| 16 | //config:config ICONV | ||
| 17 | //config: bool "iconv (11.4 kb)" | ||
| 18 | //config: default y | ||
| 19 | //config: depends on PLATFORM_MINGW32 | ||
| 20 | //config: help | ||
| 21 | //config: 'iconv' converts text between character encodings. | ||
| 22 | |||
| 23 | //applet:IF_ICONV(APPLET(iconv, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 24 | |||
| 25 | //kbuild:lib-$(CONFIG_ICONV) += iconv.o | ||
| 26 | |||
| 27 | //usage:#define iconv_trivial_usage | ||
| 28 | //usage: "[-lc] [-o outfile] [-f from-enc] [-t to-enc] [FILE]..." | ||
| 29 | //usage:#define iconv_full_usage "\n\n" | ||
| 30 | //usage: "Convert text between character encodings\n" | ||
| 31 | //usage: "\n -l List all known character encodings" | ||
| 32 | //usage: "\n -c Silently discard characters that cannot be converted" | ||
| 33 | //usage: "\n -o Use outfile for output" | ||
| 34 | //usage: "\n -f Use from-enc for input characters" | ||
| 35 | //usage: "\n -t Use to-enc for output characters" | ||
| 36 | |||
| 37 | #include "libbb.h" | ||
| 38 | |||
| 39 | /* WORKAROUND: */ | ||
| 40 | #define GetProcAddressA GetProcAddress | ||
| 41 | |||
| 42 | #define MB_CHAR_MAX 16 | ||
| 43 | |||
| 44 | #define UNICODE_MODE_BOM_DONE 1 | ||
| 45 | #define UNICODE_MODE_SWAPPED 2 | ||
| 46 | |||
| 47 | #define FLAG_USE_BOM 1 | ||
| 48 | #define FLAG_TRANSLIT 2 /* //TRANSLIT */ | ||
| 49 | #define FLAG_IGNORE 4 /* //IGNORE */ | ||
| 50 | |||
| 51 | typedef unsigned char uchar; | ||
| 52 | typedef unsigned short ushort; | ||
| 53 | typedef unsigned int uint; | ||
| 54 | |||
| 55 | typedef void* iconv_t; | ||
| 56 | |||
| 57 | static iconv_t iconv_open(const char *tocode, const char *fromcode); | ||
| 58 | static int iconv_close(iconv_t cd); | ||
| 59 | static size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft); | ||
| 60 | |||
| 61 | typedef struct compat_t compat_t; | ||
| 62 | typedef struct csconv_t csconv_t; | ||
| 63 | typedef struct rec_iconv_t rec_iconv_t; | ||
| 64 | |||
| 65 | typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | ||
| 66 | typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | ||
| 67 | typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize); | ||
| 68 | typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize); | ||
| 69 | |||
| 70 | #define COMPAT_IN 1 | ||
| 71 | #define COMPAT_OUT 2 | ||
| 72 | |||
| 73 | /* unicode mapping for compatibility with other conversion table. */ | ||
| 74 | struct compat_t { | ||
| 75 | uint in; | ||
| 76 | uint out; | ||
| 77 | uint flag; | ||
| 78 | }; | ||
| 79 | |||
| 80 | struct csconv_t { | ||
| 81 | int codepage; | ||
| 82 | int flags; | ||
| 83 | f_mbtowc mbtowc; | ||
| 84 | f_wctomb wctomb; | ||
| 85 | f_mblen mblen; | ||
| 86 | f_flush flush; | ||
| 87 | DWORD mode; | ||
| 88 | compat_t *compat; | ||
| 89 | }; | ||
| 90 | |||
| 91 | struct rec_iconv_t { | ||
| 92 | iconv_t cd; | ||
| 93 | csconv_t from; | ||
| 94 | csconv_t to; | ||
| 95 | }; | ||
| 96 | |||
| 97 | static int load_mlang(void); | ||
| 98 | static int make_csconv(const char *name, csconv_t *cv); | ||
| 99 | static int name_to_codepage(const char *name); | ||
| 100 | static uint utf16_to_ucs4(const ushort *wbuf); | ||
| 101 | static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize); | ||
| 102 | static int mbtowc_flags(int codepage); | ||
| 103 | static int must_use_null_useddefaultchar(int codepage); | ||
| 104 | static int seterror(int err); | ||
| 105 | |||
| 106 | static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); | ||
| 107 | static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); | ||
| 108 | static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize); | ||
| 109 | static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize); | ||
| 110 | static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize); | ||
| 111 | |||
| 112 | static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | ||
| 113 | static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | ||
| 114 | static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | ||
| 115 | static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | ||
| 116 | static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | ||
| 117 | static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | ||
| 118 | static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | ||
| 119 | static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | ||
| 120 | static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize); | ||
| 121 | static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize); | ||
| 122 | static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize); | ||
| 123 | |||
| 124 | #define CP_ALIAS_LIST \ | ||
| 125 | CP_ALIAS(65001, "CP65001") \ | ||
| 126 | CP_ALIAS(65001, "UTF8") \ | ||
| 127 | CP_ALIAS(65001, "UTF-8") \ | ||
| 128 | \ | ||
| 129 | CP_ALIAS(1200, "CP1200") \ | ||
| 130 | CP_ALIAS(1200, "UTF16LE") \ | ||
| 131 | CP_ALIAS(1200, "UTF-16LE") \ | ||
| 132 | CP_ALIAS(1200, "UCS2LE") \ | ||
| 133 | CP_ALIAS(1200, "UCS-2LE") \ | ||
| 134 | CP_ALIAS(1200, "UCS-2-INTERNAL") \ | ||
| 135 | \ | ||
| 136 | CP_ALIAS(1201, "CP1201") \ | ||
| 137 | CP_ALIAS(1201, "UTF16BE") \ | ||
| 138 | CP_ALIAS(1201, "UTF-16BE") \ | ||
| 139 | CP_ALIAS(1201, "UCS2BE") \ | ||
| 140 | CP_ALIAS(1201, "UCS-2BE") \ | ||
| 141 | CP_ALIAS(1201, "unicodeFFFE") \ | ||
| 142 | \ | ||
| 143 | CP_ALIAS(12000, "CP12000") \ | ||
| 144 | CP_ALIAS(12000, "UTF32LE") \ | ||
| 145 | CP_ALIAS(12000, "UTF-32LE") \ | ||
| 146 | CP_ALIAS(12000, "UCS4LE") \ | ||
| 147 | CP_ALIAS(12000, "UCS-4LE") \ | ||
| 148 | \ | ||
| 149 | CP_ALIAS(12001, "CP12001") \ | ||
| 150 | CP_ALIAS(12001, "UTF32BE") \ | ||
| 151 | CP_ALIAS(12001, "UTF-32BE") \ | ||
| 152 | CP_ALIAS(12001, "UCS4BE") \ | ||
| 153 | CP_ALIAS(12001, "UCS-4BE") \ | ||
| 154 | \ | ||
| 155 | /* Default is little endian, because the platform is */ \ | ||
| 156 | CP_ALIAS(1200, "UTF16") \ | ||
| 157 | CP_ALIAS(1200, "UTF-16") \ | ||
| 158 | CP_ALIAS(1200, "UCS2") \ | ||
| 159 | CP_ALIAS(1200, "UCS-2") \ | ||
| 160 | CP_ALIAS(12000, "UTF32") \ | ||
| 161 | CP_ALIAS(12000, "UTF-32") \ | ||
| 162 | CP_ALIAS(12000, "UCS4") \ | ||
| 163 | CP_ALIAS(12000, "UCS-4") \ | ||
| 164 | \ | ||
| 165 | /* copy from libiconv `iconv -l` */ \ | ||
| 166 | /* !IsValidCodePage(367) */ \ | ||
| 167 | CP_ALIAS(20127, "ANSI_X3.4-1968") \ | ||
| 168 | CP_ALIAS(20127, "ANSI_X3.4-1986") \ | ||
| 169 | CP_ALIAS(20127, "ASCII") \ | ||
| 170 | CP_ALIAS(20127, "CP367") \ | ||
| 171 | CP_ALIAS(20127, "IBM367") \ | ||
| 172 | CP_ALIAS(20127, "ISO-IR-6") \ | ||
| 173 | CP_ALIAS(20127, "ISO646-US") \ | ||
| 174 | CP_ALIAS(20127, "ISO_646.IRV:1991") \ | ||
| 175 | CP_ALIAS(20127, "US") \ | ||
| 176 | CP_ALIAS(20127, "US-ASCII") \ | ||
| 177 | CP_ALIAS(20127, "CSASCII") \ | ||
| 178 | \ | ||
| 179 | /* !IsValidCodePage(819) */ \ | ||
| 180 | CP_ALIAS(1252, "CP819") \ | ||
| 181 | CP_ALIAS(1252, "IBM819") \ | ||
| 182 | CP_ALIAS(28591, "ISO-8859-1") \ | ||
| 183 | CP_ALIAS(28591, "ISO-IR-100") \ | ||
| 184 | CP_ALIAS(28591, "ISO8859-1") \ | ||
| 185 | CP_ALIAS(28591, "ISO_8859-1") \ | ||
| 186 | CP_ALIAS(28591, "ISO_8859-1:1987") \ | ||
| 187 | CP_ALIAS(28591, "L1") \ | ||
| 188 | CP_ALIAS(28591, "LATIN1") \ | ||
| 189 | CP_ALIAS(28591, "CSISOLATIN1") \ | ||
| 190 | \ | ||
| 191 | CP_ALIAS(1250, "CP1250") \ | ||
| 192 | CP_ALIAS(1250, "MS-EE") \ | ||
| 193 | CP_ALIAS(1250, "WINDOWS-1250") \ | ||
| 194 | \ | ||
| 195 | CP_ALIAS(1251, "CP1251") \ | ||
| 196 | CP_ALIAS(1251, "MS-CYRL") \ | ||
| 197 | CP_ALIAS(1251, "WINDOWS-1251") \ | ||
| 198 | \ | ||
| 199 | CP_ALIAS(1252, "CP1252") \ | ||
| 200 | CP_ALIAS(1252, "MS-ANSI") \ | ||
| 201 | CP_ALIAS(1252, "WINDOWS-1252") \ | ||
| 202 | \ | ||
| 203 | CP_ALIAS(1253, "CP1253") \ | ||
| 204 | CP_ALIAS(1253, "MS-GREEK") \ | ||
| 205 | CP_ALIAS(1253, "WINDOWS-1253") \ | ||
| 206 | \ | ||
| 207 | CP_ALIAS(1254, "CP1254") \ | ||
| 208 | CP_ALIAS(1254, "MS-TURK") \ | ||
| 209 | CP_ALIAS(1254, "WINDOWS-1254") \ | ||
| 210 | \ | ||
| 211 | CP_ALIAS(1255, "CP1255") \ | ||
| 212 | CP_ALIAS(1255, "MS-HEBR") \ | ||
| 213 | CP_ALIAS(1255, "WINDOWS-1255") \ | ||
| 214 | \ | ||
| 215 | CP_ALIAS(1256, "CP1256") \ | ||
| 216 | CP_ALIAS(1256, "MS-ARAB") \ | ||
| 217 | CP_ALIAS(1256, "WINDOWS-1256") \ | ||
| 218 | \ | ||
| 219 | CP_ALIAS(1257, "CP1257") \ | ||
| 220 | CP_ALIAS(1257, "WINBALTRIM") \ | ||
| 221 | CP_ALIAS(1257, "WINDOWS-1257") \ | ||
| 222 | \ | ||
| 223 | CP_ALIAS(1258, "CP1258") \ | ||
| 224 | CP_ALIAS(1258, "WINDOWS-1258") \ | ||
| 225 | \ | ||
| 226 | CP_ALIAS(850, "850") \ | ||
| 227 | CP_ALIAS(850, "CP850") \ | ||
| 228 | CP_ALIAS(850, "IBM850") \ | ||
| 229 | CP_ALIAS(850, "CSPC850MULTILINGUAL") \ | ||
| 230 | \ | ||
| 231 | /* !IsValidCodePage(862) */ \ | ||
| 232 | CP_ALIAS(862, "862") \ | ||
| 233 | CP_ALIAS(862, "CP862") \ | ||
| 234 | CP_ALIAS(862, "IBM862") \ | ||
| 235 | CP_ALIAS(862, "CSPC862LATINHEBREW") \ | ||
| 236 | \ | ||
| 237 | CP_ALIAS(866, "866") \ | ||
| 238 | CP_ALIAS(866, "CP866") \ | ||
| 239 | CP_ALIAS(866, "IBM866") \ | ||
| 240 | CP_ALIAS(866, "CSIBM866") \ | ||
| 241 | \ | ||
| 242 | /* !IsValidCodePage(154) */ \ | ||
| 243 | CP_ALIAS(154, "CP154") \ | ||
| 244 | CP_ALIAS(154, "CYRILLIC-ASIAN") \ | ||
| 245 | CP_ALIAS(154, "PT154") \ | ||
| 246 | CP_ALIAS(154, "PTCP154") \ | ||
| 247 | CP_ALIAS(154, "CSPTCP154") \ | ||
| 248 | \ | ||
| 249 | /* !IsValidCodePage(1133) */ \ | ||
| 250 | CP_ALIAS(1133, "CP1133") \ | ||
| 251 | CP_ALIAS(1133, "IBM-CP1133") \ | ||
| 252 | \ | ||
| 253 | CP_ALIAS(874, "CP874") \ | ||
| 254 | CP_ALIAS(874, "WINDOWS-874") \ | ||
| 255 | \ | ||
| 256 | /* !IsValidCodePage(51932) */ \ | ||
| 257 | CP_ALIAS(51932, "CP51932") \ | ||
| 258 | CP_ALIAS(51932, "MS51932") \ | ||
| 259 | CP_ALIAS(51932, "WINDOWS-51932") \ | ||
| 260 | CP_ALIAS(51932, "EUC-JP") \ | ||
| 261 | \ | ||
| 262 | CP_ALIAS(932, "CP932") \ | ||
| 263 | CP_ALIAS(932, "MS932") \ | ||
| 264 | CP_ALIAS(932, "SHIFFT_JIS") \ | ||
| 265 | CP_ALIAS(932, "SHIFFT_JIS-MS") \ | ||
| 266 | CP_ALIAS(932, "SJIS") \ | ||
| 267 | CP_ALIAS(932, "SJIS-MS") \ | ||
| 268 | CP_ALIAS(932, "SJIS-OPEN") \ | ||
| 269 | CP_ALIAS(932, "SJIS-WIN") \ | ||
| 270 | CP_ALIAS(932, "WINDOWS-31J") \ | ||
| 271 | CP_ALIAS(932, "WINDOWS-932") \ | ||
| 272 | CP_ALIAS(932, "CSWINDOWS31J") \ | ||
| 273 | \ | ||
| 274 | CP_ALIAS(50221, "CP50221") \ | ||
| 275 | CP_ALIAS(50221, "ISO-2022-JP") \ | ||
| 276 | CP_ALIAS(50221, "ISO-2022-JP-MS") \ | ||
| 277 | CP_ALIAS(50221, "ISO2022-JP") \ | ||
| 278 | CP_ALIAS(50221, "ISO2022-JP-MS") \ | ||
| 279 | CP_ALIAS(50221, "MS50221") \ | ||
| 280 | CP_ALIAS(50221, "WINDOWS-50221") \ | ||
| 281 | \ | ||
| 282 | CP_ALIAS(936, "CP936") \ | ||
| 283 | CP_ALIAS(936, "GBK") \ | ||
| 284 | CP_ALIAS(936, "MS936") \ | ||
| 285 | CP_ALIAS(936, "WINDOWS-936") \ | ||
| 286 | \ | ||
| 287 | CP_ALIAS(950, "CP950") \ | ||
| 288 | CP_ALIAS(950, "BIG5") \ | ||
| 289 | CP_ALIAS(950, "BIG5HKSCS") \ | ||
| 290 | CP_ALIAS(950, "BIG5-HKSCS") \ | ||
| 291 | \ | ||
| 292 | CP_ALIAS(949, "CP949") \ | ||
| 293 | CP_ALIAS(949, "UHC") \ | ||
| 294 | CP_ALIAS(949, "EUC-KR") \ | ||
| 295 | \ | ||
| 296 | CP_ALIAS(1361, "CP1361") \ | ||
| 297 | CP_ALIAS(1361, "JOHAB") \ | ||
| 298 | \ | ||
| 299 | CP_ALIAS(437, "437") \ | ||
| 300 | CP_ALIAS(437, "CP437") \ | ||
| 301 | CP_ALIAS(437, "IBM437") \ | ||
| 302 | CP_ALIAS(437, "CSPC8CODEPAGE437") \ | ||
| 303 | \ | ||
| 304 | CP_ALIAS(737, "CP737") \ | ||
| 305 | \ | ||
| 306 | CP_ALIAS(775, "CP775") \ | ||
| 307 | CP_ALIAS(775, "IBM775") \ | ||
| 308 | CP_ALIAS(775, "CSPC775BALTIC") \ | ||
| 309 | \ | ||
| 310 | CP_ALIAS(852, "852") \ | ||
| 311 | CP_ALIAS(852, "CP852") \ | ||
| 312 | CP_ALIAS(852, "IBM852") \ | ||
| 313 | CP_ALIAS(852, "CSPCP852") \ | ||
| 314 | \ | ||
| 315 | /* !IsValidCodePage(853) */ \ | ||
| 316 | CP_ALIAS(853, "CP853") \ | ||
| 317 | \ | ||
| 318 | CP_ALIAS(855, "855") \ | ||
| 319 | CP_ALIAS(855, "CP855") \ | ||
| 320 | CP_ALIAS(855, "IBM855") \ | ||
| 321 | CP_ALIAS(855, "CSIBM855") \ | ||
| 322 | \ | ||
| 323 | CP_ALIAS(857, "857") \ | ||
| 324 | CP_ALIAS(857, "CP857") \ | ||
| 325 | CP_ALIAS(857, "IBM857") \ | ||
| 326 | CP_ALIAS(857, "CSIBM857") \ | ||
| 327 | \ | ||
| 328 | /* !IsValidCodePage(858) */ \ | ||
| 329 | CP_ALIAS(858, "CP858") \ | ||
| 330 | \ | ||
| 331 | CP_ALIAS(860, "860") \ | ||
| 332 | CP_ALIAS(860, "CP860") \ | ||
| 333 | CP_ALIAS(860, "IBM860") \ | ||
| 334 | CP_ALIAS(860, "CSIBM860") \ | ||
| 335 | \ | ||
| 336 | CP_ALIAS(861, "861") \ | ||
| 337 | CP_ALIAS(861, "CP-IS") \ | ||
| 338 | CP_ALIAS(861, "CP861") \ | ||
| 339 | CP_ALIAS(861, "IBM861") \ | ||
| 340 | CP_ALIAS(861, "CSIBM861") \ | ||
| 341 | \ | ||
| 342 | CP_ALIAS(863, "863") \ | ||
| 343 | CP_ALIAS(863, "CP863") \ | ||
| 344 | CP_ALIAS(863, "IBM863") \ | ||
| 345 | CP_ALIAS(863, "CSIBM863") \ | ||
| 346 | \ | ||
| 347 | CP_ALIAS(864, "CP864") \ | ||
| 348 | CP_ALIAS(864, "IBM864") \ | ||
| 349 | CP_ALIAS(864, "CSIBM864") \ | ||
| 350 | \ | ||
| 351 | CP_ALIAS(865, "865") \ | ||
| 352 | CP_ALIAS(865, "CP865") \ | ||
| 353 | CP_ALIAS(865, "IBM865") \ | ||
| 354 | CP_ALIAS(865, "CSIBM865") \ | ||
| 355 | \ | ||
| 356 | CP_ALIAS(869, "869") \ | ||
| 357 | CP_ALIAS(869, "CP-GR") \ | ||
| 358 | CP_ALIAS(869, "CP869") \ | ||
| 359 | CP_ALIAS(869, "IBM869") \ | ||
| 360 | CP_ALIAS(869, "CSIBM869") \ | ||
| 361 | \ | ||
| 362 | /* !IsValidCodePage(1152) */ \ | ||
| 363 | CP_ALIAS(1125, "CP1125") \ | ||
| 364 | \ | ||
| 365 | /* \ | ||
| 366 | * Code Page Identifiers \ | ||
| 367 | * http://msdn2.microsoft.com/en-us/library/ms776446.aspx \ | ||
| 368 | */ \ | ||
| 369 | CP_ALIAS(37, "IBM037") /* IBM EBCDIC US-Canada */ \ | ||
| 370 | CP_ALIAS(437, "IBM437") /* OEM United States */ \ | ||
| 371 | CP_ALIAS(500, "IBM500") /* IBM EBCDIC International */ \ | ||
| 372 | CP_ALIAS(708, "ASMO-708") /* Arabic (ASMO 708) */ \ | ||
| 373 | /* 709 Arabic (ASMO-449+, BCON V4) */ \ | ||
| 374 | /* 710 Arabic - Transparent Arabic */ \ | ||
| 375 | CP_ALIAS(720, "DOS-720") /* Arabic (Transparent ASMO); Arabic (DOS) */ \ | ||
| 376 | CP_ALIAS(737, "ibm737") /* OEM Greek (formerly 437G); Greek (DOS) */ \ | ||
| 377 | CP_ALIAS(775, "ibm775") /* OEM Baltic; Baltic (DOS) */ \ | ||
| 378 | CP_ALIAS(850, "ibm850") /* OEM Multilingual Latin 1; Western European (DOS) */ \ | ||
| 379 | CP_ALIAS(852, "ibm852") /* OEM Latin 2; Central European (DOS) */ \ | ||
| 380 | CP_ALIAS(855, "IBM855") /* OEM Cyrillic (primarily Russian) */ \ | ||
| 381 | CP_ALIAS(857, "ibm857") /* OEM Turkish; Turkish (DOS) */ \ | ||
| 382 | CP_ALIAS(858, "IBM00858") /* OEM Multilingual Latin 1 + Euro symbol */ \ | ||
| 383 | CP_ALIAS(860, "IBM860") /* OEM Portuguese; Portuguese (DOS) */ \ | ||
| 384 | CP_ALIAS(861, "ibm861") /* OEM Icelandic; Icelandic (DOS) */ \ | ||
| 385 | CP_ALIAS(862, "DOS-862") /* OEM Hebrew; Hebrew (DOS) */ \ | ||
| 386 | CP_ALIAS(863, "IBM863") /* OEM French Canadian; French Canadian (DOS) */ \ | ||
| 387 | CP_ALIAS(864, "IBM864") /* OEM Arabic; Arabic (864) */ \ | ||
| 388 | CP_ALIAS(865, "IBM865") /* OEM Nordic; Nordic (DOS) */ \ | ||
| 389 | CP_ALIAS(866, "cp866") /* OEM Russian; Cyrillic (DOS) */ \ | ||
| 390 | CP_ALIAS(869, "ibm869") /* OEM Modern Greek; Greek, Modern (DOS) */ \ | ||
| 391 | CP_ALIAS(870, "IBM870") /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */ \ | ||
| 392 | CP_ALIAS(874, "windows-874") /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */ \ | ||
| 393 | CP_ALIAS(875, "cp875") /* IBM EBCDIC Greek Modern */ \ | ||
| 394 | CP_ALIAS(932, "shift_jis") /* ANSI/OEM Japanese; Japanese (Shift-JIS) */ \ | ||
| 395 | CP_ALIAS(932, "shift-jis") /* alternative name for it */ \ | ||
| 396 | CP_ALIAS(936, "gb2312") /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */ \ | ||
| 397 | CP_ALIAS(949, "ks_c_5601-1987") /* ANSI/OEM Korean (Unified Hangul Code) */ \ | ||
| 398 | CP_ALIAS(950, "big5") /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */ \ | ||
| 399 | CP_ALIAS(950, "big5hkscs") /* ANSI/OEM Traditional Chinese (Hong Kong SAR); Chinese Traditional (Big5-HKSCS) */ \ | ||
| 400 | CP_ALIAS(950, "big5-hkscs") /* alternative name for it */ \ | ||
| 401 | CP_ALIAS(1026, "IBM1026") /* IBM EBCDIC Turkish (Latin 5) */ \ | ||
| 402 | CP_ALIAS(1047, "IBM01047") /* IBM EBCDIC Latin 1/Open System */ \ | ||
| 403 | CP_ALIAS(1140, "IBM01140") /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */ \ | ||
| 404 | CP_ALIAS(1141, "IBM01141") /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */ \ | ||
| 405 | CP_ALIAS(1142, "IBM01142") /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */ \ | ||
| 406 | CP_ALIAS(1143, "IBM01143") /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */ \ | ||
| 407 | CP_ALIAS(1144, "IBM01144") /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */ \ | ||
| 408 | CP_ALIAS(1145, "IBM01145") /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */ \ | ||
| 409 | CP_ALIAS(1146, "IBM01146") /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */ \ | ||
| 410 | CP_ALIAS(1147, "IBM01147") /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */ \ | ||
| 411 | CP_ALIAS(1148, "IBM01148") /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */ \ | ||
| 412 | CP_ALIAS(1149, "IBM01149") /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */ \ | ||
| 413 | CP_ALIAS(1250, "windows-1250") /* ANSI Central European; Central European (Windows) */ \ | ||
| 414 | CP_ALIAS(1251, "windows-1251") /* ANSI Cyrillic; Cyrillic (Windows) */ \ | ||
| 415 | CP_ALIAS(1252, "windows-1252") /* ANSI Latin 1; Western European (Windows) */ \ | ||
| 416 | CP_ALIAS(1253, "windows-1253") /* ANSI Greek; Greek (Windows) */ \ | ||
| 417 | CP_ALIAS(1254, "windows-1254") /* ANSI Turkish; Turkish (Windows) */ \ | ||
| 418 | CP_ALIAS(1255, "windows-1255") /* ANSI Hebrew; Hebrew (Windows) */ \ | ||
| 419 | CP_ALIAS(1256, "windows-1256") /* ANSI Arabic; Arabic (Windows) */ \ | ||
| 420 | CP_ALIAS(1257, "windows-1257") /* ANSI Baltic; Baltic (Windows) */ \ | ||
| 421 | CP_ALIAS(1258, "windows-1258") /* ANSI/OEM Vietnamese; Vietnamese (Windows) */ \ | ||
| 422 | CP_ALIAS(1361, "Johab") /* Korean (Johab) */ \ | ||
| 423 | CP_ALIAS(10000, "macintosh") /* MAC Roman; Western European (Mac) */ \ | ||
| 424 | CP_ALIAS(10001, "x-mac-japanese") /* Japanese (Mac) */ \ | ||
| 425 | CP_ALIAS(10002, "x-mac-chinesetrad") /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */ \ | ||
| 426 | CP_ALIAS(10003, "x-mac-korean") /* Korean (Mac) */ \ | ||
| 427 | CP_ALIAS(10004, "x-mac-arabic") /* Arabic (Mac) */ \ | ||
| 428 | CP_ALIAS(10005, "x-mac-hebrew") /* Hebrew (Mac) */ \ | ||
| 429 | CP_ALIAS(10006, "x-mac-greek") /* Greek (Mac) */ \ | ||
| 430 | CP_ALIAS(10007, "x-mac-cyrillic") /* Cyrillic (Mac) */ \ | ||
| 431 | CP_ALIAS(10008, "x-mac-chinesesimp") /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */ \ | ||
| 432 | CP_ALIAS(10010, "x-mac-romanian") /* Romanian (Mac) */ \ | ||
| 433 | CP_ALIAS(10017, "x-mac-ukrainian") /* Ukrainian (Mac) */ \ | ||
| 434 | CP_ALIAS(10021, "x-mac-thai") /* Thai (Mac) */ \ | ||
| 435 | CP_ALIAS(10029, "x-mac-ce") /* MAC Latin 2; Central European (Mac) */ \ | ||
| 436 | CP_ALIAS(10079, "x-mac-icelandic") /* Icelandic (Mac) */ \ | ||
| 437 | CP_ALIAS(10081, "x-mac-turkish") /* Turkish (Mac) */ \ | ||
| 438 | CP_ALIAS(10082, "x-mac-croatian") /* Croatian (Mac) */ \ | ||
| 439 | CP_ALIAS(20000, "x-Chinese_CNS") /* CNS Taiwan; Chinese Traditional (CNS) */ \ | ||
| 440 | CP_ALIAS(20001, "x-cp20001") /* TCA Taiwan */ \ | ||
| 441 | CP_ALIAS(20002, "x_Chinese-Eten") /* Eten Taiwan; Chinese Traditional (Eten) */ \ | ||
| 442 | CP_ALIAS(20003, "x-cp20003") /* IBM5550 Taiwan */ \ | ||
| 443 | CP_ALIAS(20004, "x-cp20004") /* TeleText Taiwan */ \ | ||
| 444 | CP_ALIAS(20005, "x-cp20005") /* Wang Taiwan */ \ | ||
| 445 | CP_ALIAS(20105, "x-IA5") /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */ \ | ||
| 446 | CP_ALIAS(20106, "x-IA5-German") /* IA5 German (7-bit) */ \ | ||
| 447 | CP_ALIAS(20107, "x-IA5-Swedish") /* IA5 Swedish (7-bit) */ \ | ||
| 448 | CP_ALIAS(20108, "x-IA5-Norwegian") /* IA5 Norwegian (7-bit) */ \ | ||
| 449 | CP_ALIAS(20127, "us-ascii") /* US-ASCII (7-bit) */ \ | ||
| 450 | CP_ALIAS(20261, "x-cp20261") /* T.61 */ \ | ||
| 451 | CP_ALIAS(20269, "x-cp20269") /* ISO 6937 Non-Spacing Accent */ \ | ||
| 452 | CP_ALIAS(20273, "IBM273") /* IBM EBCDIC Germany */ \ | ||
| 453 | CP_ALIAS(20277, "IBM277") /* IBM EBCDIC Denmark-Norway */ \ | ||
| 454 | CP_ALIAS(20278, "IBM278") /* IBM EBCDIC Finland-Sweden */ \ | ||
| 455 | CP_ALIAS(20280, "IBM280") /* IBM EBCDIC Italy */ \ | ||
| 456 | CP_ALIAS(20284, "IBM284") /* IBM EBCDIC Latin America-Spain */ \ | ||
| 457 | CP_ALIAS(20285, "IBM285") /* IBM EBCDIC United Kingdom */ \ | ||
| 458 | CP_ALIAS(20290, "IBM290") /* IBM EBCDIC Japanese Katakana Extended */ \ | ||
| 459 | CP_ALIAS(20297, "IBM297") /* IBM EBCDIC France */ \ | ||
| 460 | CP_ALIAS(20420, "IBM420") /* IBM EBCDIC Arabic */ \ | ||
| 461 | CP_ALIAS(20423, "IBM423") /* IBM EBCDIC Greek */ \ | ||
| 462 | CP_ALIAS(20424, "IBM424") /* IBM EBCDIC Hebrew */ \ | ||
| 463 | CP_ALIAS(20833, "x-EBCDIC-KoreanExtended") /* IBM EBCDIC Korean Extended */ \ | ||
| 464 | CP_ALIAS(20838, "IBM-Thai") /* IBM EBCDIC Thai */ \ | ||
| 465 | CP_ALIAS(20866, "koi8-r") /* Russian (KOI8-R); Cyrillic (KOI8-R) */ \ | ||
| 466 | CP_ALIAS(20871, "IBM871") /* IBM EBCDIC Icelandic */ \ | ||
| 467 | CP_ALIAS(20880, "IBM880") /* IBM EBCDIC Cyrillic Russian */ \ | ||
| 468 | CP_ALIAS(20905, "IBM905") /* IBM EBCDIC Turkish */ \ | ||
| 469 | CP_ALIAS(20924, "IBM00924") /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */ \ | ||
| 470 | CP_ALIAS(20932, "EUC-JP") /* Japanese (JIS 0208-1990 and 0121-1990) */ \ | ||
| 471 | CP_ALIAS(20936, "x-cp20936") /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */ \ | ||
| 472 | CP_ALIAS(20949, "x-cp20949") /* Korean Wansung */ \ | ||
| 473 | CP_ALIAS(21025, "cp1025") /* IBM EBCDIC Cyrillic Serbian-Bulgarian */ \ | ||
| 474 | /* 21027 (deprecated) */ \ | ||
| 475 | CP_ALIAS(21866, "koi8-u") /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */ \ | ||
| 476 | CP_ALIAS(28591, "iso-8859-1") /* ISO 8859-1 Latin 1; Western European (ISO) */ \ | ||
| 477 | CP_ALIAS(28591, "iso8859-1") /* ISO 8859-1 Latin 1; Western European (ISO) */ \ | ||
| 478 | CP_ALIAS(28591, "iso_8859-1") \ | ||
| 479 | CP_ALIAS(28591, "iso_8859_1") \ | ||
| 480 | CP_ALIAS(28592, "iso-8859-2") /* ISO 8859-2 Central European; Central European (ISO) */ \ | ||
| 481 | CP_ALIAS(28592, "iso8859-2") /* ISO 8859-2 Central European; Central European (ISO) */ \ | ||
| 482 | CP_ALIAS(28592, "iso_8859-2") \ | ||
| 483 | CP_ALIAS(28592, "iso_8859_2") \ | ||
| 484 | CP_ALIAS(28593, "iso-8859-3") /* ISO 8859-3 Latin 3 */ \ | ||
| 485 | CP_ALIAS(28593, "iso8859-3") /* ISO 8859-3 Latin 3 */ \ | ||
| 486 | CP_ALIAS(28593, "iso_8859-3") \ | ||
| 487 | CP_ALIAS(28593, "iso_8859_3") \ | ||
| 488 | CP_ALIAS(28594, "iso-8859-4") /* ISO 8859-4 Baltic */ \ | ||
| 489 | CP_ALIAS(28594, "iso8859-4") /* ISO 8859-4 Baltic */ \ | ||
| 490 | CP_ALIAS(28594, "iso_8859-4") \ | ||
| 491 | CP_ALIAS(28594, "iso_8859_4") \ | ||
| 492 | CP_ALIAS(28595, "iso-8859-5") /* ISO 8859-5 Cyrillic */ \ | ||
| 493 | CP_ALIAS(28595, "iso8859-5") /* ISO 8859-5 Cyrillic */ \ | ||
| 494 | CP_ALIAS(28595, "iso_8859-5") \ | ||
| 495 | CP_ALIAS(28595, "iso_8859_5") \ | ||
| 496 | CP_ALIAS(28596, "iso-8859-6") /* ISO 8859-6 Arabic */ \ | ||
| 497 | CP_ALIAS(28596, "iso8859-6") /* ISO 8859-6 Arabic */ \ | ||
| 498 | CP_ALIAS(28596, "iso_8859-6") \ | ||
| 499 | CP_ALIAS(28596, "iso_8859_6") \ | ||
| 500 | CP_ALIAS(28597, "iso-8859-7") /* ISO 8859-7 Greek */ \ | ||
| 501 | CP_ALIAS(28597, "iso8859-7") /* ISO 8859-7 Greek */ \ | ||
| 502 | CP_ALIAS(28597, "iso_8859-7") \ | ||
| 503 | CP_ALIAS(28597, "iso_8859_7") \ | ||
| 504 | CP_ALIAS(28598, "iso-8859-8") /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ \ | ||
| 505 | CP_ALIAS(28598, "iso8859-8") /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ \ | ||
| 506 | CP_ALIAS(28598, "iso_8859-8") \ | ||
| 507 | CP_ALIAS(28598, "iso_8859_8") \ | ||
| 508 | CP_ALIAS(28599, "iso-8859-9") /* ISO 8859-9 Turkish */ \ | ||
| 509 | CP_ALIAS(28599, "iso8859-9") /* ISO 8859-9 Turkish */ \ | ||
| 510 | CP_ALIAS(28599, "iso_8859-9") \ | ||
| 511 | CP_ALIAS(28599, "iso_8859_9") \ | ||
| 512 | CP_ALIAS(28603, "iso-8859-13") /* ISO 8859-13 Estonian */ \ | ||
| 513 | CP_ALIAS(28603, "iso8859-13") /* ISO 8859-13 Estonian */ \ | ||
| 514 | CP_ALIAS(28603, "iso_8859-13") \ | ||
| 515 | CP_ALIAS(28603, "iso_8859_13") \ | ||
| 516 | CP_ALIAS(28605, "iso-8859-15") /* ISO 8859-15 Latin 9 */ \ | ||
| 517 | CP_ALIAS(28605, "iso8859-15") /* ISO 8859-15 Latin 9 */ \ | ||
| 518 | CP_ALIAS(28605, "iso_8859-15") \ | ||
| 519 | CP_ALIAS(28605, "iso_8859_15") \ | ||
| 520 | CP_ALIAS(29001, "x-Europa") /* Europa 3 */ \ | ||
| 521 | CP_ALIAS(38598, "iso-8859-8-i") /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ \ | ||
| 522 | CP_ALIAS(38598, "iso8859-8-i") /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ \ | ||
| 523 | CP_ALIAS(38598, "iso_8859-8-i") \ | ||
| 524 | CP_ALIAS(38598, "iso_8859_8-i") \ | ||
| 525 | CP_ALIAS(50220, "iso-2022-jp") /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */ \ | ||
| 526 | CP_ALIAS(50221, "csISO2022JP") /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */ \ | ||
| 527 | CP_ALIAS(50222, "iso-2022-jp") /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */ \ | ||
| 528 | CP_ALIAS(50225, "iso-2022-kr") /* ISO 2022 Korean */ \ | ||
| 529 | CP_ALIAS(50225, "iso2022-kr") /* ISO 2022 Korean */ \ | ||
| 530 | CP_ALIAS(50227, "x-cp50227") /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */ \ | ||
| 531 | /* 50229 ISO 2022 Traditional Chinese */ \ | ||
| 532 | /* 50930 EBCDIC Japanese (Katakana) Extended */ \ | ||
| 533 | /* 50931 EBCDIC US-Canada and Japanese */ \ | ||
| 534 | /* 50933 EBCDIC Korean Extended and Korean */ \ | ||
| 535 | /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */ \ | ||
| 536 | /* 50936 EBCDIC Simplified Chinese */ \ | ||
| 537 | /* 50937 EBCDIC US-Canada and Traditional Chinese */ \ | ||
| 538 | /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */ \ | ||
| 539 | CP_ALIAS(51932, "euc-jp") /* EUC Japanese */ \ | ||
| 540 | CP_ALIAS(51936, "EUC-CN") /* EUC Simplified Chinese; Chinese Simplified (EUC) */ \ | ||
| 541 | CP_ALIAS(51949, "euc-kr") /* EUC Korean */ \ | ||
| 542 | /* 51950 EUC Traditional Chinese */ \ | ||
| 543 | CP_ALIAS(52936, "hz-gb-2312") /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */ \ | ||
| 544 | CP_ALIAS(54936, "GB18030") /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */ \ | ||
| 545 | CP_ALIAS(57002, "x-iscii-de") /* ISCII Devanagari */ \ | ||
| 546 | CP_ALIAS(57003, "x-iscii-be") /* ISCII Bengali */ \ | ||
| 547 | CP_ALIAS(57004, "x-iscii-ta") /* ISCII Tamil */ \ | ||
| 548 | CP_ALIAS(57005, "x-iscii-te") /* ISCII Telugu */ \ | ||
| 549 | CP_ALIAS(57006, "x-iscii-as") /* ISCII Assamese */ \ | ||
| 550 | CP_ALIAS(57007, "x-iscii-or") /* ISCII Oriya */ \ | ||
| 551 | CP_ALIAS(57008, "x-iscii-ka") /* ISCII Kannada */ \ | ||
| 552 | CP_ALIAS(57009, "x-iscii-ma") /* ISCII Malayalam */ \ | ||
| 553 | CP_ALIAS(57010, "x-iscii-gu") /* ISCII Gujarati */ \ | ||
| 554 | CP_ALIAS(57011, "x-iscii-pa") /* ISCII Punjabi */ | ||
| 555 | |||
| 556 | #define CP_ALIAS(codepage, alias) codepage, | ||
| 557 | static const int cp_codepage[] = { | ||
| 558 | CP_ALIAS_LIST | ||
| 559 | }; | ||
| 560 | #undef CP_ALIAS | ||
| 561 | |||
| 562 | #define CP_ALIAS(codepage, alias) alias"\0" | ||
| 563 | static const char cp_alias[] ALIGN1 = | ||
| 564 | CP_ALIAS_LIST; | ||
| 565 | #undef CP_ALIAS | ||
| 566 | |||
| 567 | /* | ||
| 568 | * SJIS SHIFTJIS table CP932 table | ||
| 569 | * ---- --------------------------- -------------------------------- | ||
| 570 | * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS | ||
| 571 | * 7E U+203E OVERLINE U+007E TILDE | ||
| 572 | * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR | ||
| 573 | * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS | ||
| 574 | * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE | ||
| 575 | * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO | ||
| 576 | * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS | ||
| 577 | * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN | ||
| 578 | * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN | ||
| 579 | * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN | ||
| 580 | * | ||
| 581 | * EUC-JP and ISO-2022-JP should be compatible with CP932. | ||
| 582 | * | ||
| 583 | * Kernel and MLang have different Unicode mapping table. Make sure | ||
| 584 | * which API is used. | ||
| 585 | */ | ||
| 586 | static compat_t cp932_compat[] = { | ||
| 587 | {0x00A5, 0x005C, COMPAT_OUT}, | ||
| 588 | {0x203E, 0x007E, COMPAT_OUT}, | ||
| 589 | {0x2014, 0x2015, COMPAT_OUT}, | ||
| 590 | {0x301C, 0xFF5E, COMPAT_OUT}, | ||
| 591 | {0x2016, 0x2225, COMPAT_OUT}, | ||
| 592 | {0x2212, 0xFF0D, COMPAT_OUT}, | ||
| 593 | {0x00A2, 0xFFE0, COMPAT_OUT}, | ||
| 594 | {0x00A3, 0xFFE1, COMPAT_OUT}, | ||
| 595 | {0x00AC, 0xFFE2, COMPAT_OUT}, | ||
| 596 | {0, 0, 0} | ||
| 597 | }; | ||
| 598 | |||
| 599 | static compat_t cp20932_compat[] = { | ||
| 600 | {0x00A5, 0x005C, COMPAT_OUT}, | ||
| 601 | {0x203E, 0x007E, COMPAT_OUT}, | ||
| 602 | {0x2014, 0x2015, COMPAT_OUT}, | ||
| 603 | {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN}, | ||
| 604 | {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN}, | ||
| 605 | {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN}, | ||
| 606 | {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN}, | ||
| 607 | {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN}, | ||
| 608 | {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN}, | ||
| 609 | {0, 0, 0} | ||
| 610 | }; | ||
| 611 | |||
| 612 | static compat_t *cp51932_compat = cp932_compat; | ||
| 613 | |||
| 614 | /* cp20932_compat for kernel. cp932_compat for mlang. */ | ||
| 615 | static compat_t *cp5022x_compat = cp932_compat; | ||
| 616 | |||
| 617 | typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)( | ||
| 618 | LPDWORD lpdwMode, | ||
| 619 | DWORD dwSrcEncoding, | ||
| 620 | LPCSTR lpSrcStr, | ||
| 621 | LPINT lpnMultiCharCount, | ||
| 622 | LPWSTR lpDstStr, | ||
| 623 | LPINT lpnWideCharCount | ||
| 624 | ); | ||
| 625 | |||
| 626 | typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)( | ||
| 627 | LPDWORD lpdwMode, | ||
| 628 | DWORD dwEncoding, | ||
| 629 | LPCWSTR lpSrcStr, | ||
| 630 | LPINT lpnWideCharCount, | ||
| 631 | LPSTR lpDstStr, | ||
| 632 | LPINT lpnMultiCharCount | ||
| 633 | ); | ||
| 634 | |||
| 635 | static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode; | ||
| 636 | static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte; | ||
| 637 | |||
| 638 | static int | ||
| 639 | load_mlang(void) | ||
| 640 | { | ||
| 641 | HMODULE h; | ||
| 642 | if (ConvertINetMultiByteToUnicode != NULL) | ||
| 643 | return TRUE; | ||
| 644 | h = LoadLibrary(TEXT("mlang.dll")); | ||
| 645 | if (!h) | ||
| 646 | return FALSE; | ||
| 647 | ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddressA(h, "ConvertINetMultiByteToUnicode"); | ||
| 648 | ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddressA(h, "ConvertINetUnicodeToMultiByte"); | ||
| 649 | return TRUE; | ||
| 650 | } | ||
| 651 | |||
| 652 | static iconv_t | ||
| 653 | iconv_open(const char *tocode, const char *fromcode) | ||
| 654 | { | ||
| 655 | rec_iconv_t *cd; | ||
| 656 | |||
| 657 | cd = (rec_iconv_t *)xzalloc(sizeof(rec_iconv_t)); | ||
| 658 | |||
| 659 | /* reset the errno to prevent reporting wrong error code. | ||
| 660 | * 0 for unsorted error. */ | ||
| 661 | errno = 0; | ||
| 662 | if (make_csconv(fromcode, &cd->from) && make_csconv(tocode, &cd->to)) { | ||
| 663 | cd->cd = (iconv_t)cd; | ||
| 664 | return (iconv_t)cd; | ||
| 665 | } | ||
| 666 | |||
| 667 | free(cd); | ||
| 668 | return (iconv_t)(-1); | ||
| 669 | } | ||
| 670 | |||
| 671 | static int | ||
| 672 | iconv_close(iconv_t _cd) | ||
| 673 | { | ||
| 674 | free(_cd); | ||
| 675 | return 0; | ||
| 676 | } | ||
| 677 | |||
| 678 | static size_t | ||
| 679 | iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft) | ||
| 680 | { | ||
| 681 | rec_iconv_t *cd = (rec_iconv_t *)_cd; | ||
| 682 | ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */ | ||
| 683 | int insize; | ||
| 684 | int outsize; | ||
| 685 | int wsize; | ||
| 686 | DWORD frommode; | ||
| 687 | DWORD tomode; | ||
| 688 | uint wc; | ||
| 689 | compat_t *cp; | ||
| 690 | int i; | ||
| 691 | |||
| 692 | if (inbuf == NULL || *inbuf == NULL) | ||
| 693 | { | ||
| 694 | if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL) | ||
| 695 | { | ||
| 696 | tomode = cd->to.mode; | ||
| 697 | outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft); | ||
| 698 | if (outsize == -1) | ||
| 699 | { | ||
| 700 | if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG) | ||
| 701 | { | ||
| 702 | outsize = 0; | ||
| 703 | } | ||
| 704 | else | ||
| 705 | { | ||
| 706 | cd->to.mode = tomode; | ||
| 707 | return (size_t)(-1); | ||
| 708 | } | ||
| 709 | } | ||
| 710 | *outbuf += outsize; | ||
| 711 | *outbytesleft -= outsize; | ||
| 712 | } | ||
| 713 | cd->from.mode = 0; | ||
| 714 | cd->to.mode = 0; | ||
| 715 | return 0; | ||
| 716 | } | ||
| 717 | |||
| 718 | while (*inbytesleft != 0) | ||
| 719 | { | ||
| 720 | frommode = cd->from.mode; | ||
| 721 | tomode = cd->to.mode; | ||
| 722 | wsize = MB_CHAR_MAX; | ||
| 723 | |||
| 724 | insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize); | ||
| 725 | if (insize == -1) | ||
| 726 | { | ||
| 727 | if (cd->to.flags & FLAG_IGNORE) | ||
| 728 | { | ||
| 729 | cd->from.mode = frommode; | ||
| 730 | insize = 1; | ||
| 731 | wsize = 0; | ||
| 732 | } | ||
| 733 | else | ||
| 734 | { | ||
| 735 | cd->from.mode = frommode; | ||
| 736 | return (size_t)(-1); | ||
| 737 | } | ||
| 738 | } | ||
| 739 | |||
| 740 | if (wsize == 0) | ||
| 741 | { | ||
| 742 | *inbuf += insize; | ||
| 743 | *inbytesleft -= insize; | ||
| 744 | continue; | ||
| 745 | } | ||
| 746 | |||
| 747 | if (cd->from.compat != NULL) | ||
| 748 | { | ||
| 749 | wc = utf16_to_ucs4(wbuf); | ||
| 750 | cp = cd->from.compat; | ||
| 751 | for (i = 0; cp[i].in != 0; ++i) | ||
| 752 | { | ||
| 753 | if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc) | ||
| 754 | { | ||
| 755 | ucs4_to_utf16(cp[i].in, wbuf, &wsize); | ||
| 756 | break; | ||
| 757 | } | ||
| 758 | } | ||
| 759 | } | ||
| 760 | |||
| 761 | if (cd->to.compat != NULL) | ||
| 762 | { | ||
| 763 | wc = utf16_to_ucs4(wbuf); | ||
| 764 | cp = cd->to.compat; | ||
| 765 | for (i = 0; cp[i].in != 0; ++i) | ||
| 766 | { | ||
| 767 | if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc) | ||
| 768 | { | ||
| 769 | ucs4_to_utf16(cp[i].out, wbuf, &wsize); | ||
| 770 | break; | ||
| 771 | } | ||
| 772 | } | ||
| 773 | } | ||
| 774 | |||
| 775 | outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft); | ||
| 776 | if (outsize == -1) | ||
| 777 | { | ||
| 778 | if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG) | ||
| 779 | { | ||
| 780 | cd->to.mode = tomode; | ||
| 781 | outsize = 0; | ||
| 782 | } | ||
| 783 | else | ||
| 784 | { | ||
| 785 | cd->from.mode = frommode; | ||
| 786 | cd->to.mode = tomode; | ||
| 787 | return (size_t)(-1); | ||
| 788 | } | ||
| 789 | } | ||
| 790 | |||
| 791 | *inbuf += insize; | ||
| 792 | *outbuf += outsize; | ||
| 793 | *inbytesleft -= insize; | ||
| 794 | *outbytesleft -= outsize; | ||
| 795 | } | ||
| 796 | |||
| 797 | return 0; | ||
| 798 | } | ||
| 799 | |||
| 800 | static int | ||
| 801 | make_csconv(const char *_name, csconv_t *cv) | ||
| 802 | { | ||
| 803 | CPINFO cpinfo; | ||
| 804 | int use_compat = TRUE; | ||
| 805 | int flag = 0; | ||
| 806 | char *name; | ||
| 807 | char *p, *s; | ||
| 808 | |||
| 809 | name = xstrdup(_name); | ||
| 810 | |||
| 811 | /* check for option "enc_name//opt1//opt2" */ | ||
| 812 | while ((p = strrstr(name, "//")) != NULL) | ||
| 813 | { | ||
| 814 | for (s = p + 2; *s; ++s) | ||
| 815 | *s = tolower(*s); | ||
| 816 | switch (index_in_strings("nocompat\0translit\0ignore\0", p + 2)) { | ||
| 817 | case 0: | ||
| 818 | use_compat = FALSE; | ||
| 819 | break; | ||
| 820 | case 1: | ||
| 821 | flag |= FLAG_TRANSLIT; | ||
| 822 | break; | ||
| 823 | case 2: | ||
| 824 | flag |= FLAG_IGNORE; | ||
| 825 | break; | ||
| 826 | } | ||
| 827 | *p = 0; | ||
| 828 | } | ||
| 829 | |||
| 830 | cv->mode = 0; | ||
| 831 | cv->flags = flag; | ||
| 832 | cv->mblen = NULL; | ||
| 833 | cv->flush = NULL; | ||
| 834 | cv->compat = NULL; | ||
| 835 | cv->codepage = name_to_codepage(name); | ||
| 836 | if (cv->codepage == 1200 || cv->codepage == 1201) | ||
| 837 | { | ||
| 838 | cv->mbtowc = utf16_mbtowc; | ||
| 839 | cv->wctomb = utf16_wctomb; | ||
| 840 | if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0 || | ||
| 841 | _stricmp(name, "UCS-2") == 0 || _stricmp(name, "UCS2") == 0 || | ||
| 842 | _stricmp(name,"UCS-2-INTERNAL") == 0) | ||
| 843 | cv->flags |= FLAG_USE_BOM; | ||
| 844 | } | ||
| 845 | else if (cv->codepage == 12000 || cv->codepage == 12001) | ||
| 846 | { | ||
| 847 | cv->mbtowc = utf32_mbtowc; | ||
| 848 | cv->wctomb = utf32_wctomb; | ||
| 849 | if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0 || | ||
| 850 | _stricmp(name, "UCS-4") == 0 || _stricmp(name, "UCS4") == 0) | ||
| 851 | cv->flags |= FLAG_USE_BOM; | ||
| 852 | } | ||
| 853 | else if (cv->codepage == 65001) | ||
| 854 | { | ||
| 855 | cv->mbtowc = kernel_mbtowc; | ||
| 856 | cv->wctomb = kernel_wctomb; | ||
| 857 | cv->mblen = utf8_mblen; | ||
| 858 | } | ||
| 859 | else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang()) | ||
| 860 | { | ||
| 861 | cv->mbtowc = iso2022jp_mbtowc; | ||
| 862 | cv->wctomb = iso2022jp_wctomb; | ||
| 863 | cv->flush = iso2022jp_flush; | ||
| 864 | } | ||
| 865 | else if (cv->codepage == 51932 && load_mlang()) | ||
| 866 | { | ||
| 867 | cv->mbtowc = mlang_mbtowc; | ||
| 868 | cv->wctomb = mlang_wctomb; | ||
| 869 | cv->mblen = eucjp_mblen; | ||
| 870 | } | ||
| 871 | else if (IsValidCodePage(cv->codepage) | ||
| 872 | && GetCPInfo(cv->codepage, &cpinfo) != 0) | ||
| 873 | { | ||
| 874 | cv->mbtowc = kernel_mbtowc; | ||
| 875 | cv->wctomb = kernel_wctomb; | ||
| 876 | if (cpinfo.MaxCharSize == 1) | ||
| 877 | cv->mblen = sbcs_mblen; | ||
| 878 | else if (cpinfo.MaxCharSize == 2) | ||
| 879 | cv->mblen = dbcs_mblen; | ||
| 880 | else | ||
| 881 | cv->mblen = mbcs_mblen; | ||
| 882 | } | ||
| 883 | else | ||
| 884 | { | ||
| 885 | /* not supported */ | ||
| 886 | free(name); | ||
| 887 | errno = EINVAL; | ||
| 888 | return FALSE; | ||
| 889 | } | ||
| 890 | |||
| 891 | if (use_compat) | ||
| 892 | { | ||
| 893 | switch (cv->codepage) | ||
| 894 | { | ||
| 895 | case 932: cv->compat = cp932_compat; break; | ||
| 896 | case 20932: cv->compat = cp20932_compat; break; | ||
| 897 | case 51932: cv->compat = cp51932_compat; break; | ||
| 898 | case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break; | ||
| 899 | } | ||
| 900 | } | ||
| 901 | |||
| 902 | free(name); | ||
| 903 | |||
| 904 | return TRUE; | ||
| 905 | } | ||
| 906 | |||
| 907 | static int | ||
| 908 | name_to_codepage(const char *name) | ||
| 909 | { | ||
| 910 | int i; | ||
| 911 | const char *alias; | ||
| 912 | |||
| 913 | if (*name == '\0' || strcmp(name, "char") == 0) | ||
| 914 | return GetACP(); | ||
| 915 | else if (strcmp(name, "wchar_t") == 0) | ||
| 916 | return 1200; | ||
| 917 | else if (_strnicmp(name, "cp", 2) == 0) | ||
| 918 | return atoi(name + 2); /* CP123 */ | ||
| 919 | else if ('0' <= name[0] && name[0] <= '9') | ||
| 920 | return atoi(name); /* 123 */ | ||
| 921 | else if (_strnicmp(name, "xx", 2) == 0) | ||
| 922 | return atoi(name + 2); /* XX123 for debug */ | ||
| 923 | |||
| 924 | i = 0; | ||
| 925 | alias = cp_alias; | ||
| 926 | while (*alias) { | ||
| 927 | if (_stricmp(alias, name) == 0) { | ||
| 928 | return cp_codepage[i]; | ||
| 929 | } | ||
| 930 | alias += strlen(alias) + 1; | ||
| 931 | ++i; | ||
| 932 | } | ||
| 933 | return -1; | ||
| 934 | } | ||
| 935 | |||
| 936 | /* | ||
| 937 | * http://www.faqs.org/rfcs/rfc2781.html | ||
| 938 | */ | ||
| 939 | static uint | ||
| 940 | utf16_to_ucs4(const ushort *wbuf) | ||
| 941 | { | ||
| 942 | uint wc = wbuf[0]; | ||
| 943 | if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) | ||
| 944 | wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000; | ||
| 945 | return wc; | ||
| 946 | } | ||
| 947 | |||
| 948 | static void | ||
| 949 | ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize) | ||
| 950 | { | ||
| 951 | if (wc < 0x10000) | ||
| 952 | { | ||
| 953 | wbuf[0] = wc; | ||
| 954 | *wbufsize = 1; | ||
| 955 | } | ||
| 956 | else | ||
| 957 | { | ||
| 958 | wc -= 0x10000; | ||
| 959 | wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF); | ||
| 960 | wbuf[1] = 0xDC00 | (wc & 0x3FF); | ||
| 961 | *wbufsize = 2; | ||
| 962 | } | ||
| 963 | } | ||
| 964 | |||
| 965 | /* | ||
| 966 | * Check if codepage is one of those for which the dwFlags parameter | ||
| 967 | * to MultiByteToWideChar() must be zero. Return zero or | ||
| 968 | * MB_ERR_INVALID_CHARS. The docs in Platform SDK for Windows | ||
| 969 | * Server 2003 R2 claims that also codepage 65001 is one of these, but | ||
| 970 | * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave | ||
| 971 | * out 65001 (UTF-8), and that indeed seems to be the case on XP, it | ||
| 972 | * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting | ||
| 973 | * from UTF-8. | ||
| 974 | */ | ||
| 975 | static int | ||
| 976 | mbtowc_flags(int codepage) | ||
| 977 | { | ||
| 978 | return (codepage == 50220 || codepage == 50221 || | ||
| 979 | codepage == 50222 || codepage == 50225 || | ||
| 980 | codepage == 50227 || codepage == 50229 || | ||
| 981 | codepage == 52936 || codepage == 54936 || | ||
| 982 | (codepage >= 57002 && codepage <= 57011) || | ||
| 983 | codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS; | ||
| 984 | } | ||
| 985 | |||
| 986 | /* | ||
| 987 | * Check if codepage is one those for which the lpUsedDefaultChar | ||
| 988 | * parameter to WideCharToMultiByte() must be NULL. The docs in | ||
| 989 | * Platform SDK for Windows Server 2003 R2 claims that this is the | ||
| 990 | * list below, while the MSDN docs for MSVS2008 claim that it is only | ||
| 991 | * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform | ||
| 992 | * SDK seems to be correct, at least for XP. | ||
| 993 | */ | ||
| 994 | static int | ||
| 995 | must_use_null_useddefaultchar(int codepage) | ||
| 996 | { | ||
| 997 | return (codepage == 65000 || codepage == 65001 || | ||
| 998 | codepage == 50220 || codepage == 50221 || | ||
| 999 | codepage == 50222 || codepage == 50225 || | ||
| 1000 | codepage == 50227 || codepage == 50229 || | ||
| 1001 | codepage == 52936 || codepage == 54936 || | ||
| 1002 | (codepage >= 57002 && codepage <= 57011) || | ||
| 1003 | codepage == 42); | ||
| 1004 | } | ||
| 1005 | |||
| 1006 | static int | ||
| 1007 | seterror(int err) | ||
| 1008 | { | ||
| 1009 | errno = err; | ||
| 1010 | return -1; | ||
| 1011 | } | ||
| 1012 | |||
| 1013 | static int | ||
| 1014 | sbcs_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf UNUSED_PARAM, | ||
| 1015 | int bufsize UNUSED_PARAM) | ||
| 1016 | { | ||
| 1017 | return 1; | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | static int | ||
| 1021 | dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) | ||
| 1022 | { | ||
| 1023 | int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1; | ||
| 1024 | if (bufsize < len) | ||
| 1025 | return seterror(EINVAL); | ||
| 1026 | return len; | ||
| 1027 | } | ||
| 1028 | |||
| 1029 | static int | ||
| 1030 | mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize) | ||
| 1031 | { | ||
| 1032 | int len = 0; | ||
| 1033 | |||
| 1034 | if (cv->codepage == 54936) { | ||
| 1035 | if (buf[0] <= 0x7F) | ||
| 1036 | len = 1; | ||
| 1037 | else if (buf[0] >= 0x81 && buf[0] <= 0xFE && | ||
| 1038 | bufsize >= 2 && | ||
| 1039 | ((buf[1] >= 0x40 && buf[1] <= 0x7E) || | ||
| 1040 | (buf[1] >= 0x80 && buf[1] <= 0xFE))) | ||
| 1041 | len = 2; | ||
| 1042 | else if (buf[0] >= 0x81 && buf[0] <= 0xFE && | ||
| 1043 | bufsize >= 4 && | ||
| 1044 | buf[1] >= 0x30 && buf[1] <= 0x39) | ||
| 1045 | len = 4; | ||
| 1046 | else | ||
| 1047 | return seterror(EINVAL); | ||
| 1048 | return len; | ||
| 1049 | } | ||
| 1050 | else | ||
| 1051 | return seterror(EINVAL); | ||
| 1052 | } | ||
| 1053 | |||
| 1054 | static int | ||
| 1055 | utf8_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize) | ||
| 1056 | { | ||
| 1057 | int len = 0; | ||
| 1058 | |||
| 1059 | if (buf[0] < 0x80) len = 1; | ||
| 1060 | else if ((buf[0] & 0xE0) == 0xC0) len = 2; | ||
| 1061 | else if ((buf[0] & 0xF0) == 0xE0) len = 3; | ||
| 1062 | else if ((buf[0] & 0xF8) == 0xF0) len = 4; | ||
| 1063 | else if ((buf[0] & 0xFC) == 0xF8) len = 5; | ||
| 1064 | else if ((buf[0] & 0xFE) == 0xFC) len = 6; | ||
| 1065 | |||
| 1066 | if (len == 0) | ||
| 1067 | return seterror(EILSEQ); | ||
| 1068 | else if (bufsize < len) | ||
| 1069 | return seterror(EINVAL); | ||
| 1070 | return len; | ||
| 1071 | } | ||
| 1072 | |||
| 1073 | static int | ||
| 1074 | eucjp_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize) | ||
| 1075 | { | ||
| 1076 | if (buf[0] < 0x80) /* ASCII */ | ||
| 1077 | return 1; | ||
| 1078 | else if (buf[0] == 0x8E) /* JIS X 0201 */ | ||
| 1079 | { | ||
| 1080 | if (bufsize < 2) | ||
| 1081 | return seterror(EINVAL); | ||
| 1082 | else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF)) | ||
| 1083 | return seterror(EILSEQ); | ||
| 1084 | return 2; | ||
| 1085 | } | ||
| 1086 | else if (buf[0] == 0x8F) /* JIS X 0212 */ | ||
| 1087 | { | ||
| 1088 | if (bufsize < 3) | ||
| 1089 | return seterror(EINVAL); | ||
| 1090 | else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE) | ||
| 1091 | || !(0xA1 <= buf[2] && buf[2] <= 0xFE)) | ||
| 1092 | return seterror(EILSEQ); | ||
| 1093 | return 3; | ||
| 1094 | } | ||
| 1095 | else /* JIS X 0208 */ | ||
| 1096 | { | ||
| 1097 | if (bufsize < 2) | ||
| 1098 | return seterror(EINVAL); | ||
| 1099 | else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE) | ||
| 1100 | || !(0xA1 <= buf[1] && buf[1] <= 0xFE)) | ||
| 1101 | return seterror(EILSEQ); | ||
| 1102 | return 2; | ||
| 1103 | } | ||
| 1104 | } | ||
| 1105 | |||
| 1106 | static int | ||
| 1107 | kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | ||
| 1108 | { | ||
| 1109 | int len; | ||
| 1110 | |||
| 1111 | len = cv->mblen(cv, buf, bufsize); | ||
| 1112 | if (len == -1) | ||
| 1113 | return -1; | ||
| 1114 | /* If converting from ASCII, reject 8bit | ||
| 1115 | * chars. MultiByteToWideChar() doesn't. Note that for ASCII we | ||
| 1116 | * know that the mblen function is sbcs_mblen() so len is 1. | ||
| 1117 | */ | ||
| 1118 | if (cv->codepage == 20127 && buf[0] >= 0x80) | ||
| 1119 | return seterror(EILSEQ); | ||
| 1120 | *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage), | ||
| 1121 | (const char *)buf, len, (wchar_t *)wbuf, *wbufsize); | ||
| 1122 | if (*wbufsize == 0) | ||
| 1123 | return seterror(EILSEQ); | ||
| 1124 | return len; | ||
| 1125 | } | ||
| 1126 | |||
| 1127 | static int | ||
| 1128 | kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | ||
| 1129 | { | ||
| 1130 | BOOL usedDefaultChar = 0; | ||
| 1131 | BOOL *p = NULL; | ||
| 1132 | int flags = 0; | ||
| 1133 | int len; | ||
| 1134 | |||
| 1135 | if (bufsize == 0) | ||
| 1136 | return seterror(E2BIG); | ||
| 1137 | if (!must_use_null_useddefaultchar(cv->codepage)) | ||
| 1138 | { | ||
| 1139 | p = &usedDefaultChar; | ||
| 1140 | #ifdef WC_NO_BEST_FIT_CHARS | ||
| 1141 | if (!(cv->flags & FLAG_TRANSLIT)) | ||
| 1142 | flags |= WC_NO_BEST_FIT_CHARS; | ||
| 1143 | #endif | ||
| 1144 | } | ||
| 1145 | len = WideCharToMultiByte(cv->codepage, flags, | ||
| 1146 | (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p); | ||
| 1147 | if (len == 0) | ||
| 1148 | { | ||
| 1149 | if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) | ||
| 1150 | return seterror(E2BIG); | ||
| 1151 | return seterror(EILSEQ); | ||
| 1152 | } | ||
| 1153 | else if (usedDefaultChar && !(cv->flags & FLAG_TRANSLIT)) | ||
| 1154 | return seterror(EILSEQ); | ||
| 1155 | else if (cv->mblen(cv, buf, len) != len) /* validate result */ | ||
| 1156 | return seterror(EILSEQ); | ||
| 1157 | return len; | ||
| 1158 | } | ||
| 1159 | |||
| 1160 | /* | ||
| 1161 | * It seems that the mode (cv->mode) is fixnum. | ||
| 1162 | * For example, when converting iso-2022-jp(cp50221) to unicode: | ||
| 1163 | * in ascii sequence: mode=0xC42C0000 | ||
| 1164 | * in jisx0208 sequence: mode=0xC42C0001 | ||
| 1165 | * "C42C" is same for each convert session. | ||
| 1166 | * It should be: ((codepage-1)<<16)|state | ||
| 1167 | */ | ||
| 1168 | static int | ||
| 1169 | mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | ||
| 1170 | { | ||
| 1171 | int len; | ||
| 1172 | int insize; | ||
| 1173 | HRESULT hr; | ||
| 1174 | |||
| 1175 | len = cv->mblen(cv, buf, bufsize); | ||
| 1176 | if (len == -1) | ||
| 1177 | return -1; | ||
| 1178 | insize = len; | ||
| 1179 | hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage, | ||
| 1180 | (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize); | ||
| 1181 | if (hr != S_OK || insize != len) | ||
| 1182 | return seterror(EILSEQ); | ||
| 1183 | return len; | ||
| 1184 | } | ||
| 1185 | |||
| 1186 | static int | ||
| 1187 | mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | ||
| 1188 | { | ||
| 1189 | char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */ | ||
| 1190 | int tmpsize = MB_CHAR_MAX; | ||
| 1191 | int insize = wbufsize; | ||
| 1192 | HRESULT hr; | ||
| 1193 | |||
| 1194 | hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage, | ||
| 1195 | (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize); | ||
| 1196 | if (hr != S_OK || insize != wbufsize) | ||
| 1197 | return seterror(EILSEQ); | ||
| 1198 | else if (bufsize < tmpsize) | ||
| 1199 | return seterror(E2BIG); | ||
| 1200 | else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize) | ||
| 1201 | return seterror(EILSEQ); | ||
| 1202 | memcpy(buf, tmpbuf, tmpsize); | ||
| 1203 | return tmpsize; | ||
| 1204 | } | ||
| 1205 | |||
| 1206 | static int | ||
| 1207 | utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | ||
| 1208 | { | ||
| 1209 | int codepage = cv->codepage; | ||
| 1210 | |||
| 1211 | /* swap endian: 1200 <-> 1201 */ | ||
| 1212 | if (cv->mode & UNICODE_MODE_SWAPPED) | ||
| 1213 | codepage ^= 1; | ||
| 1214 | |||
| 1215 | if (bufsize < 2) | ||
| 1216 | return seterror(EINVAL); | ||
| 1217 | if (codepage == 1200) /* little endian */ | ||
| 1218 | wbuf[0] = (buf[1] << 8) | buf[0]; | ||
| 1219 | else if (codepage == 1201) /* big endian */ | ||
| 1220 | wbuf[0] = (buf[0] << 8) | buf[1]; | ||
| 1221 | |||
| 1222 | if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) | ||
| 1223 | { | ||
| 1224 | cv->mode |= UNICODE_MODE_BOM_DONE; | ||
| 1225 | if (wbuf[0] == 0xFFFE) | ||
| 1226 | { | ||
| 1227 | cv->mode |= UNICODE_MODE_SWAPPED; | ||
| 1228 | *wbufsize = 0; | ||
| 1229 | return 2; | ||
| 1230 | } | ||
| 1231 | else if (wbuf[0] == 0xFEFF) | ||
| 1232 | { | ||
| 1233 | *wbufsize = 0; | ||
| 1234 | return 2; | ||
| 1235 | } | ||
| 1236 | } | ||
| 1237 | |||
| 1238 | if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF) | ||
| 1239 | return seterror(EILSEQ); | ||
| 1240 | if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) | ||
| 1241 | { | ||
| 1242 | if (bufsize < 4) | ||
| 1243 | return seterror(EINVAL); | ||
| 1244 | if (codepage == 1200) /* little endian */ | ||
| 1245 | wbuf[1] = (buf[3] << 8) | buf[2]; | ||
| 1246 | else if (codepage == 1201) /* big endian */ | ||
| 1247 | wbuf[1] = (buf[2] << 8) | buf[3]; | ||
| 1248 | if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF)) | ||
| 1249 | return seterror(EILSEQ); | ||
| 1250 | *wbufsize = 2; | ||
| 1251 | return 4; | ||
| 1252 | } | ||
| 1253 | *wbufsize = 1; | ||
| 1254 | return 2; | ||
| 1255 | } | ||
| 1256 | |||
| 1257 | static int | ||
| 1258 | utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | ||
| 1259 | { | ||
| 1260 | if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) | ||
| 1261 | { | ||
| 1262 | int r; | ||
| 1263 | |||
| 1264 | cv->mode |= UNICODE_MODE_BOM_DONE; | ||
| 1265 | if (bufsize < 2) | ||
| 1266 | return seterror(E2BIG); | ||
| 1267 | if (cv->codepage == 1200) /* little endian */ | ||
| 1268 | memcpy(buf, "\xFF\xFE", 2); | ||
| 1269 | else if (cv->codepage == 1201) /* big endian */ | ||
| 1270 | memcpy(buf, "\xFE\xFF", 2); | ||
| 1271 | |||
| 1272 | r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2); | ||
| 1273 | if (r == -1) | ||
| 1274 | return -1; | ||
| 1275 | return r + 2; | ||
| 1276 | } | ||
| 1277 | |||
| 1278 | if (bufsize < 2) | ||
| 1279 | return seterror(E2BIG); | ||
| 1280 | if (cv->codepage == 1200) /* little endian */ | ||
| 1281 | { | ||
| 1282 | buf[0] = (wbuf[0] & 0x00FF); | ||
| 1283 | buf[1] = (wbuf[0] & 0xFF00) >> 8; | ||
| 1284 | } | ||
| 1285 | else if (cv->codepage == 1201) /* big endian */ | ||
| 1286 | { | ||
| 1287 | buf[0] = (wbuf[0] & 0xFF00) >> 8; | ||
| 1288 | buf[1] = (wbuf[0] & 0x00FF); | ||
| 1289 | } | ||
| 1290 | if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF) | ||
| 1291 | { | ||
| 1292 | if (bufsize < 4) | ||
| 1293 | return seterror(E2BIG); | ||
| 1294 | if (cv->codepage == 1200) /* little endian */ | ||
| 1295 | { | ||
| 1296 | buf[2] = (wbuf[1] & 0x00FF); | ||
| 1297 | buf[3] = (wbuf[1] & 0xFF00) >> 8; | ||
| 1298 | } | ||
| 1299 | else if (cv->codepage == 1201) /* big endian */ | ||
| 1300 | { | ||
| 1301 | buf[2] = (wbuf[1] & 0xFF00) >> 8; | ||
| 1302 | buf[3] = (wbuf[1] & 0x00FF); | ||
| 1303 | } | ||
| 1304 | return 4; | ||
| 1305 | } | ||
| 1306 | return 2; | ||
| 1307 | } | ||
| 1308 | |||
| 1309 | static int | ||
| 1310 | utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | ||
| 1311 | { | ||
| 1312 | int codepage = cv->codepage; | ||
| 1313 | uint wc = 0xD800; | ||
| 1314 | |||
| 1315 | /* swap endian: 12000 <-> 12001 */ | ||
| 1316 | if (cv->mode & UNICODE_MODE_SWAPPED) | ||
| 1317 | codepage ^= 1; | ||
| 1318 | |||
| 1319 | if (bufsize < 4) | ||
| 1320 | return seterror(EINVAL); | ||
| 1321 | if (codepage == 12000) /* little endian */ | ||
| 1322 | wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0]; | ||
| 1323 | else if (codepage == 12001) /* big endian */ | ||
| 1324 | wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3]; | ||
| 1325 | |||
| 1326 | if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) | ||
| 1327 | { | ||
| 1328 | cv->mode |= UNICODE_MODE_BOM_DONE; | ||
| 1329 | if (wc == 0xFFFE0000) | ||
| 1330 | { | ||
| 1331 | cv->mode |= UNICODE_MODE_SWAPPED; | ||
| 1332 | *wbufsize = 0; | ||
| 1333 | return 4; | ||
| 1334 | } | ||
| 1335 | else if (wc == 0x0000FEFF) | ||
| 1336 | { | ||
| 1337 | *wbufsize = 0; | ||
| 1338 | return 4; | ||
| 1339 | } | ||
| 1340 | } | ||
| 1341 | |||
| 1342 | if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc) | ||
| 1343 | return seterror(EILSEQ); | ||
| 1344 | ucs4_to_utf16(wc, wbuf, wbufsize); | ||
| 1345 | return 4; | ||
| 1346 | } | ||
| 1347 | |||
| 1348 | static int | ||
| 1349 | utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | ||
| 1350 | { | ||
| 1351 | uint wc; | ||
| 1352 | |||
| 1353 | if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE)) | ||
| 1354 | { | ||
| 1355 | int r; | ||
| 1356 | |||
| 1357 | cv->mode |= UNICODE_MODE_BOM_DONE; | ||
| 1358 | if (bufsize < 4) | ||
| 1359 | return seterror(E2BIG); | ||
| 1360 | if (cv->codepage == 12000) /* little endian */ | ||
| 1361 | memcpy(buf, "\xFF\xFE\x00\x00", 4); | ||
| 1362 | else if (cv->codepage == 12001) /* big endian */ | ||
| 1363 | memcpy(buf, "\x00\x00\xFE\xFF", 4); | ||
| 1364 | |||
| 1365 | r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4); | ||
| 1366 | if (r == -1) | ||
| 1367 | return -1; | ||
| 1368 | return r + 4; | ||
| 1369 | } | ||
| 1370 | |||
| 1371 | if (bufsize < 4) | ||
| 1372 | return seterror(E2BIG); | ||
| 1373 | wc = utf16_to_ucs4(wbuf); | ||
| 1374 | if (cv->codepage == 12000) /* little endian */ | ||
| 1375 | { | ||
| 1376 | buf[0] = wc & 0x000000FF; | ||
| 1377 | buf[1] = (wc & 0x0000FF00) >> 8; | ||
| 1378 | buf[2] = (wc & 0x00FF0000) >> 16; | ||
| 1379 | buf[3] = (wc & 0xFF000000) >> 24; | ||
| 1380 | } | ||
| 1381 | else if (cv->codepage == 12001) /* big endian */ | ||
| 1382 | { | ||
| 1383 | buf[0] = (wc & 0xFF000000) >> 24; | ||
| 1384 | buf[1] = (wc & 0x00FF0000) >> 16; | ||
| 1385 | buf[2] = (wc & 0x0000FF00) >> 8; | ||
| 1386 | buf[3] = wc & 0x000000FF; | ||
| 1387 | } | ||
| 1388 | return 4; | ||
| 1389 | } | ||
| 1390 | |||
| 1391 | /* | ||
| 1392 | * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) | ||
| 1393 | * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow | ||
| 1394 | * 1 byte Kana) | ||
| 1395 | * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte | ||
| 1396 | * Kana - SO/SI) | ||
| 1397 | * | ||
| 1398 | * MultiByteToWideChar() and WideCharToMultiByte() behave differently | ||
| 1399 | * depending on Windows version. On XP, WideCharToMultiByte() doesn't | ||
| 1400 | * terminate result sequence with ascii escape. But Vista does. | ||
| 1401 | * Use MLang instead. | ||
| 1402 | */ | ||
| 1403 | |||
| 1404 | #define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift)) | ||
| 1405 | #define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF) | ||
| 1406 | #define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF) | ||
| 1407 | |||
| 1408 | #define ISO2022_SI 0 | ||
| 1409 | #define ISO2022_SO 1 | ||
| 1410 | |||
| 1411 | /* shift in */ | ||
| 1412 | static const char iso2022_SI_seq[] = "\x0F"; | ||
| 1413 | /* shift out */ | ||
| 1414 | static const char iso2022_SO_seq[] = "\x0E"; | ||
| 1415 | |||
| 1416 | typedef struct iso2022_esc_t iso2022_esc_t; | ||
| 1417 | struct iso2022_esc_t { | ||
| 1418 | const char *esc; | ||
| 1419 | int esc_len; | ||
| 1420 | int len; | ||
| 1421 | int cs; | ||
| 1422 | }; | ||
| 1423 | |||
| 1424 | #define ISO2022JP_CS_ASCII 0 | ||
| 1425 | #define ISO2022JP_CS_JISX0201_ROMAN 1 | ||
| 1426 | #define ISO2022JP_CS_JISX0201_KANA 2 | ||
| 1427 | #define ISO2022JP_CS_JISX0208_1978 3 | ||
| 1428 | #define ISO2022JP_CS_JISX0208_1983 4 | ||
| 1429 | #define ISO2022JP_CS_JISX0212 5 | ||
| 1430 | |||
| 1431 | static iso2022_esc_t iso2022jp_esc[] = { | ||
| 1432 | {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII}, | ||
| 1433 | {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN}, | ||
| 1434 | {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA}, | ||
| 1435 | {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */ | ||
| 1436 | {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983}, | ||
| 1437 | {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212}, | ||
| 1438 | {NULL, 0, 0, 0} | ||
| 1439 | }; | ||
| 1440 | |||
| 1441 | static int | ||
| 1442 | iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize) | ||
| 1443 | { | ||
| 1444 | iso2022_esc_t *iesc = iso2022jp_esc; | ||
| 1445 | char tmp[MB_CHAR_MAX]; | ||
| 1446 | int insize; | ||
| 1447 | HRESULT hr; | ||
| 1448 | DWORD dummy = 0; | ||
| 1449 | int len; | ||
| 1450 | int esc_len; | ||
| 1451 | int cs; | ||
| 1452 | int shift; | ||
| 1453 | int i; | ||
| 1454 | |||
| 1455 | if (buf[0] == 0x1B) | ||
| 1456 | { | ||
| 1457 | for (i = 0; iesc[i].esc != NULL; ++i) | ||
| 1458 | { | ||
| 1459 | esc_len = iesc[i].esc_len; | ||
| 1460 | if (bufsize < esc_len) | ||
| 1461 | { | ||
| 1462 | if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0) | ||
| 1463 | return seterror(EINVAL); | ||
| 1464 | } | ||
| 1465 | else | ||
| 1466 | { | ||
| 1467 | if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0) | ||
| 1468 | { | ||
| 1469 | cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI); | ||
| 1470 | *wbufsize = 0; | ||
| 1471 | return esc_len; | ||
| 1472 | } | ||
| 1473 | } | ||
| 1474 | } | ||
| 1475 | /* not supported escape sequence */ | ||
| 1476 | return seterror(EILSEQ); | ||
| 1477 | } | ||
| 1478 | else if (buf[0] == iso2022_SO_seq[0]) | ||
| 1479 | { | ||
| 1480 | cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO); | ||
| 1481 | *wbufsize = 0; | ||
| 1482 | return 1; | ||
| 1483 | } | ||
| 1484 | else if (buf[0] == iso2022_SI_seq[0]) | ||
| 1485 | { | ||
| 1486 | cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI); | ||
| 1487 | *wbufsize = 0; | ||
| 1488 | return 1; | ||
| 1489 | } | ||
| 1490 | |||
| 1491 | cs = ISO2022_MODE_CS(cv->mode); | ||
| 1492 | shift = ISO2022_MODE_SHIFT(cv->mode); | ||
| 1493 | |||
| 1494 | /* reset the mode for informal sequence */ | ||
| 1495 | if (buf[0] < 0x20) | ||
| 1496 | { | ||
| 1497 | cs = ISO2022JP_CS_ASCII; | ||
| 1498 | shift = ISO2022_SI; | ||
| 1499 | } | ||
| 1500 | |||
| 1501 | len = iesc[cs].len; | ||
| 1502 | if (bufsize < len) | ||
| 1503 | return seterror(EINVAL); | ||
| 1504 | for (i = 0; i < len; ++i) | ||
| 1505 | if (!(buf[i] < 0x80)) | ||
| 1506 | return seterror(EILSEQ); | ||
| 1507 | esc_len = iesc[cs].esc_len; | ||
| 1508 | memcpy(tmp, iesc[cs].esc, esc_len); | ||
| 1509 | if (shift == ISO2022_SO) | ||
| 1510 | { | ||
| 1511 | memcpy(tmp + esc_len, iso2022_SO_seq, 1); | ||
| 1512 | esc_len += 1; | ||
| 1513 | } | ||
| 1514 | memcpy(tmp + esc_len, buf, len); | ||
| 1515 | |||
| 1516 | if ((cv->codepage == 50220 || cv->codepage == 50221 | ||
| 1517 | || cv->codepage == 50222) && shift == ISO2022_SO) | ||
| 1518 | { | ||
| 1519 | /* XXX: shift-out cannot be used for mbtowc (both kernel and | ||
| 1520 | * mlang) */ | ||
| 1521 | esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len; | ||
| 1522 | memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len); | ||
| 1523 | memcpy(tmp + esc_len, buf, len); | ||
| 1524 | } | ||
| 1525 | |||
| 1526 | insize = len + esc_len; | ||
| 1527 | hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage, | ||
| 1528 | (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize); | ||
| 1529 | if (hr != S_OK || insize != len + esc_len) | ||
| 1530 | return seterror(EILSEQ); | ||
| 1531 | |||
| 1532 | /* Check for conversion error. Assuming defaultChar is 0x3F. */ | ||
| 1533 | /* ascii should be converted from ascii */ | ||
| 1534 | if (wbuf[0] == buf[0] | ||
| 1535 | && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) | ||
| 1536 | return seterror(EILSEQ); | ||
| 1537 | |||
| 1538 | /* reset the mode for informal sequence */ | ||
| 1539 | if (cv->mode != ISO2022_MODE(cs, shift)) | ||
| 1540 | cv->mode = ISO2022_MODE(cs, shift); | ||
| 1541 | |||
| 1542 | return len; | ||
| 1543 | } | ||
| 1544 | |||
| 1545 | static int | ||
| 1546 | iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize) | ||
| 1547 | { | ||
| 1548 | iso2022_esc_t *iesc = iso2022jp_esc; | ||
| 1549 | char tmp[MB_CHAR_MAX]; | ||
| 1550 | int tmpsize = MB_CHAR_MAX; | ||
| 1551 | int insize = wbufsize; | ||
| 1552 | HRESULT hr; | ||
| 1553 | DWORD dummy = 0; | ||
| 1554 | int len; | ||
| 1555 | int esc_len; | ||
| 1556 | int cs; | ||
| 1557 | int shift; | ||
| 1558 | int i; | ||
| 1559 | |||
| 1560 | /* | ||
| 1561 | * MultiByte = [escape sequence] + character + [escape sequence] | ||
| 1562 | * | ||
| 1563 | * Whether trailing escape sequence is added depends on which API is | ||
| 1564 | * used (kernel or MLang, and its version). | ||
| 1565 | */ | ||
| 1566 | hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage, | ||
| 1567 | (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize); | ||
| 1568 | if (hr != S_OK || insize != wbufsize) | ||
| 1569 | return seterror(EILSEQ); | ||
| 1570 | else if (bufsize < tmpsize) | ||
| 1571 | return seterror(E2BIG); | ||
| 1572 | |||
| 1573 | if (tmpsize == 1) | ||
| 1574 | { | ||
| 1575 | cs = ISO2022JP_CS_ASCII; | ||
| 1576 | esc_len = 0; | ||
| 1577 | } | ||
| 1578 | else | ||
| 1579 | { | ||
| 1580 | for (i = 1; iesc[i].esc != NULL; ++i) | ||
| 1581 | { | ||
| 1582 | esc_len = iesc[i].esc_len; | ||
| 1583 | if (strncmp(tmp, iesc[i].esc, esc_len) == 0) | ||
| 1584 | { | ||
| 1585 | cs = iesc[i].cs; | ||
| 1586 | break; | ||
| 1587 | } | ||
| 1588 | } | ||
| 1589 | if (iesc[i].esc == NULL) | ||
| 1590 | /* not supported escape sequence */ | ||
| 1591 | return seterror(EILSEQ); | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | shift = ISO2022_SI; | ||
| 1595 | if (tmp[esc_len] == iso2022_SO_seq[0]) | ||
| 1596 | { | ||
| 1597 | shift = ISO2022_SO; | ||
| 1598 | esc_len += 1; | ||
| 1599 | } | ||
| 1600 | |||
| 1601 | len = iesc[cs].len; | ||
| 1602 | |||
| 1603 | /* Check for converting error. Assuming defaultChar is 0x3F. */ | ||
| 1604 | /* ascii should be converted from ascii */ | ||
| 1605 | if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80)) | ||
| 1606 | return seterror(EILSEQ); | ||
| 1607 | else if (tmpsize < esc_len + len) | ||
| 1608 | return seterror(EILSEQ); | ||
| 1609 | |||
| 1610 | if (cv->mode == ISO2022_MODE(cs, shift)) | ||
| 1611 | { | ||
| 1612 | /* remove escape sequence */ | ||
| 1613 | if (esc_len != 0) | ||
| 1614 | memmove(tmp, tmp + esc_len, len); | ||
| 1615 | esc_len = 0; | ||
| 1616 | } | ||
| 1617 | else | ||
| 1618 | { | ||
| 1619 | if (cs == ISO2022JP_CS_ASCII) | ||
| 1620 | { | ||
| 1621 | esc_len = iesc[ISO2022JP_CS_ASCII].esc_len; | ||
| 1622 | memmove(tmp + esc_len, tmp, len); | ||
| 1623 | memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len); | ||
| 1624 | } | ||
| 1625 | if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO) | ||
| 1626 | { | ||
| 1627 | /* shift-in before changing to other mode */ | ||
| 1628 | memmove(tmp + 1, tmp, len + esc_len); | ||
| 1629 | memcpy(tmp, iso2022_SI_seq, 1); | ||
| 1630 | esc_len += 1; | ||
| 1631 | } | ||
| 1632 | } | ||
| 1633 | |||
| 1634 | if (bufsize < len + esc_len) | ||
| 1635 | return seterror(E2BIG); | ||
| 1636 | memcpy(buf, tmp, len + esc_len); | ||
| 1637 | cv->mode = ISO2022_MODE(cs, shift); | ||
| 1638 | return len + esc_len; | ||
| 1639 | } | ||
| 1640 | |||
| 1641 | static int | ||
| 1642 | iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize) | ||
| 1643 | { | ||
| 1644 | iso2022_esc_t *iesc = iso2022jp_esc; | ||
| 1645 | int esc_len; | ||
| 1646 | |||
| 1647 | if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI)) | ||
| 1648 | { | ||
| 1649 | esc_len = 0; | ||
| 1650 | if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) | ||
| 1651 | esc_len += 1; | ||
| 1652 | if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) | ||
| 1653 | esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; | ||
| 1654 | if (bufsize < esc_len) | ||
| 1655 | return seterror(E2BIG); | ||
| 1656 | |||
| 1657 | esc_len = 0; | ||
| 1658 | if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI) | ||
| 1659 | { | ||
| 1660 | memcpy(buf, iso2022_SI_seq, 1); | ||
| 1661 | esc_len += 1; | ||
| 1662 | } | ||
| 1663 | if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII) | ||
| 1664 | { | ||
| 1665 | memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc, | ||
| 1666 | iesc[ISO2022JP_CS_ASCII].esc_len); | ||
| 1667 | esc_len += iesc[ISO2022JP_CS_ASCII].esc_len; | ||
| 1668 | } | ||
| 1669 | return esc_len; | ||
| 1670 | } | ||
| 1671 | return 0; | ||
| 1672 | } | ||
| 1673 | |||
| 1674 | static void process_file(iconv_t cd, FILE *in, FILE *out) | ||
| 1675 | { | ||
| 1676 | char inbuf[BUFSIZ]; | ||
| 1677 | char outbuf[BUFSIZ]; | ||
| 1678 | const char *pin; | ||
| 1679 | char *pout; | ||
| 1680 | size_t inbytesleft; | ||
| 1681 | size_t outbytesleft; | ||
| 1682 | size_t rest = 0; | ||
| 1683 | size_t r; | ||
| 1684 | |||
| 1685 | while ((inbytesleft=fread(inbuf+rest, 1, sizeof(inbuf)-rest, in)) != 0 | ||
| 1686 | || rest != 0) { | ||
| 1687 | inbytesleft += rest; | ||
| 1688 | pin = inbuf; | ||
| 1689 | pout = outbuf; | ||
| 1690 | outbytesleft = sizeof(outbuf); | ||
| 1691 | r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft); | ||
| 1692 | fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out); | ||
| 1693 | if (r == (size_t)(-1) && errno != E2BIG && | ||
| 1694 | (errno != EINVAL || feof(in))) | ||
| 1695 | bb_perror_msg_and_die("conversion error"); | ||
| 1696 | memmove(inbuf, pin, inbytesleft); | ||
| 1697 | rest = inbytesleft; | ||
| 1698 | if (rest == 0 && feof(in)) | ||
| 1699 | break; | ||
| 1700 | } | ||
| 1701 | pout = outbuf; | ||
| 1702 | outbytesleft = sizeof(outbuf); | ||
| 1703 | r = iconv(cd, NULL, NULL, &pout, &outbytesleft); | ||
| 1704 | fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out); | ||
| 1705 | if (r == (size_t)(-1)) | ||
| 1706 | bb_perror_msg_and_die("conversion error"); | ||
| 1707 | } | ||
| 1708 | |||
| 1709 | enum { | ||
| 1710 | OPT_f = (1 << 0), | ||
| 1711 | OPT_t = (1 << 1), | ||
| 1712 | OPT_l = (1 << 2), | ||
| 1713 | OPT_c = (1 << 3), | ||
| 1714 | OPT_o = (1 << 4), | ||
| 1715 | }; | ||
| 1716 | |||
| 1717 | int iconv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 1718 | int iconv_main(int argc, char **argv) | ||
| 1719 | { | ||
| 1720 | const char *fromcode = "", *tocode = "", *outfile; | ||
| 1721 | char *tmpname = NULL; | ||
| 1722 | int i, opt; | ||
| 1723 | iconv_t cd; | ||
| 1724 | FILE *in; | ||
| 1725 | FILE *out = stdout; | ||
| 1726 | |||
| 1727 | opt = getopt32(argv, "f:t:lco:", &fromcode, &tocode, &outfile); | ||
| 1728 | |||
| 1729 | if (opt & OPT_l) { | ||
| 1730 | const char *alias = cp_alias; | ||
| 1731 | while (*alias) { | ||
| 1732 | printf("%s\n", alias); | ||
| 1733 | alias += strlen(alias) + 1; | ||
| 1734 | } | ||
| 1735 | return 0; | ||
| 1736 | } | ||
| 1737 | |||
| 1738 | if (opt & OPT_o) { | ||
| 1739 | tmpname = xasprintf("%sXXXXXX", outfile); | ||
| 1740 | mktemp(tmpname); | ||
| 1741 | out = xfopen(tmpname, "wb"); | ||
| 1742 | } | ||
| 1743 | |||
| 1744 | if (opt & OPT_c) | ||
| 1745 | tocode = xasprintf("%s//IGNORE", tocode); | ||
| 1746 | |||
| 1747 | cd = iconv_open(tocode, fromcode); | ||
| 1748 | if (cd == (iconv_t)(-1)) | ||
| 1749 | bb_perror_msg_and_die("iconv_open error"); | ||
| 1750 | |||
| 1751 | if (optind == argc) | ||
| 1752 | argv[argc++] = (char *)"-"; | ||
| 1753 | |||
| 1754 | for (i=optind; i<argc; ++i) { | ||
| 1755 | if (argv[i][0] == '-' && argv[i][1] == '\0') | ||
| 1756 | in = stdin; | ||
| 1757 | else | ||
| 1758 | in = xfopen(argv[optind], "rb"); | ||
| 1759 | process_file(cd, in, out); | ||
| 1760 | fclose(in); | ||
| 1761 | } | ||
| 1762 | |||
| 1763 | if (tmpname) { | ||
| 1764 | fclose(out); | ||
| 1765 | xrename(tmpname, outfile); | ||
| 1766 | } | ||
| 1767 | |||
| 1768 | if (ENABLE_FEATURE_CLEAN_UP) | ||
| 1769 | iconv_close(cd); | ||
| 1770 | return 0; | ||
| 1771 | } | ||
diff --git a/miscutils/inotifyd.c b/miscutils/inotifyd.c index 8bff86ae5..fdd04c292 100644 --- a/miscutils/inotifyd.c +++ b/miscutils/inotifyd.c | |||
| @@ -45,8 +45,11 @@ | |||
| 45 | //usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run." | 45 | //usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run." |
| 46 | //usage: "\nIf PROG is -, events are sent to stdout." | 46 | //usage: "\nIf PROG is -, events are sent to stdout." |
| 47 | //usage: "\nEvents:" | 47 | //usage: "\nEvents:" |
| 48 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
| 48 | //usage: "\n a File is accessed" | 49 | //usage: "\n a File is accessed" |
| 50 | //usage: ) | ||
| 49 | //usage: "\n c File is modified" | 51 | //usage: "\n c File is modified" |
| 52 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
| 50 | //usage: "\n e Metadata changed" | 53 | //usage: "\n e Metadata changed" |
| 51 | //usage: "\n w Writable file is closed" | 54 | //usage: "\n w Writable file is closed" |
| 52 | //usage: "\n 0 Unwritable file is closed" | 55 | //usage: "\n 0 Unwritable file is closed" |
| @@ -55,8 +58,11 @@ | |||
| 55 | //usage: "\n M File is moved" | 58 | //usage: "\n M File is moved" |
| 56 | //usage: "\n u Backing fs is unmounted" | 59 | //usage: "\n u Backing fs is unmounted" |
| 57 | //usage: "\n o Event queue overflowed" | 60 | //usage: "\n o Event queue overflowed" |
| 61 | //usage: ) | ||
| 58 | //usage: "\n x File can't be watched anymore" | 62 | //usage: "\n x File can't be watched anymore" |
| 63 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
| 59 | //usage: "\nIf watching a directory:" | 64 | //usage: "\nIf watching a directory:" |
| 65 | //usage: ) | ||
| 60 | //usage: "\n y Subfile is moved into dir" | 66 | //usage: "\n y Subfile is moved into dir" |
| 61 | //usage: "\n m Subfile is moved out of dir" | 67 | //usage: "\n m Subfile is moved out of dir" |
| 62 | //usage: "\n n Subfile is created" | 68 | //usage: "\n n Subfile is created" |
| @@ -69,6 +75,7 @@ | |||
| 69 | #include "common_bufsiz.h" | 75 | #include "common_bufsiz.h" |
| 70 | #include <sys/inotify.h> | 76 | #include <sys/inotify.h> |
| 71 | 77 | ||
| 78 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 72 | static const char mask_names[] ALIGN1 = | 79 | static const char mask_names[] ALIGN1 = |
| 73 | "a" // 0x00000001 File was accessed | 80 | "a" // 0x00000001 File was accessed |
| 74 | "c" // 0x00000002 File was modified | 81 | "c" // 0x00000002 File was modified |
| @@ -222,3 +229,207 @@ int inotifyd_main(int argc, char **argv) | |||
| 222 | done: | 229 | done: |
| 223 | return bb_got_signal; | 230 | return bb_got_signal; |
| 224 | } | 231 | } |
| 232 | #else /* ENABLE_PLATFORM_MINGW32 */ | ||
| 233 | /* | ||
| 234 | * Order is important: the indices match the values taken by the | ||
| 235 | * Action member of the FILE_NOTIFY_INFORMATION structure, including | ||
| 236 | * the undocumented zero value when the directory itself is deleted. | ||
| 237 | */ | ||
| 238 | static const char mask_names[] ALIGN1 = | ||
| 239 | "x" // File is no longer watched (usually deleted) | ||
| 240 | "n" // Subfile was created | ||
| 241 | "d" // Subfile was deleted | ||
| 242 | "c" // File was modified | ||
| 243 | "m" // File was moved from X | ||
| 244 | "y" // File was moved to Y | ||
| 245 | ; | ||
| 246 | |||
| 247 | enum { | ||
| 248 | MASK_BITS = sizeof(mask_names) - 1 | ||
| 249 | }; | ||
| 250 | |||
| 251 | static const unsigned mask_values[] = { | ||
| 252 | 0x000, // File is no longer watched (usually deleted) | ||
| 253 | 0x003, // Subfile was created | ||
| 254 | 0x003, // Subfile was deleted | ||
| 255 | 0x1fc, // File was modified (everything except create/delete/move) | ||
| 256 | 0x003, // File was moved from X | ||
| 257 | 0x003, // File was moved to Y | ||
| 258 | }; | ||
| 259 | |||
| 260 | struct watch { | ||
| 261 | HANDLE hdir; | ||
| 262 | HANDLE hevent; | ||
| 263 | DWORD mask; // notification filter | ||
| 264 | DWORD bits; // events to report | ||
| 265 | OVERLAPPED overlap; | ||
| 266 | const char *dirname; | ||
| 267 | char buf[2048]; | ||
| 268 | }; | ||
| 269 | |||
| 270 | static void run_agent(const char *agent, FILE_NOTIFY_INFORMATION *info, | ||
| 271 | struct watch *w) | ||
| 272 | { | ||
| 273 | int len; | ||
| 274 | char filename[MAX_PATH]; | ||
| 275 | char event[2]; | ||
| 276 | const char *args[5]; | ||
| 277 | |||
| 278 | memset(filename, 0, sizeof(filename)); | ||
| 279 | len = WideCharToMultiByte(CP_ACP, 0, info->FileName, | ||
| 280 | info->FileNameLength/2, filename, sizeof(filename), | ||
| 281 | NULL, NULL); | ||
| 282 | |||
| 283 | if (info->Action >= 0 && info->Action < 6 && | ||
| 284 | ((1 << info->Action) & w->bits)) { | ||
| 285 | event[0] = mask_names[info->Action]; | ||
| 286 | event[1] = '\0'; | ||
| 287 | |||
| 288 | if (LONE_CHAR(agent, '-')) { | ||
| 289 | /* "inotifyd - FILE": built-in echo */ | ||
| 290 | printf(len ? "%s\t%s\t%s\n" : "%s\t%s\n", | ||
| 291 | event, w->dirname, filename); | ||
| 292 | fflush(stdout); | ||
| 293 | } | ||
| 294 | else { | ||
| 295 | args[0] = agent; | ||
| 296 | args[1] = event; | ||
| 297 | args[2] = w->dirname; | ||
| 298 | args[3] = len ? filename : NULL; | ||
| 299 | args[4] = NULL; | ||
| 300 | spawn_and_wait((char **)args); | ||
| 301 | } | ||
| 302 | } | ||
| 303 | } | ||
| 304 | |||
| 305 | static BOOL start_watch(struct watch *w) | ||
| 306 | { | ||
| 307 | DWORD nret; | ||
| 308 | |||
| 309 | memset(w->buf, 0, sizeof(w->buf)); | ||
| 310 | memset(&w->overlap, 0, sizeof(OVERLAPPED)); | ||
| 311 | w->overlap.hEvent = w->hevent; | ||
| 312 | ResetEvent(w->hevent); | ||
| 313 | |||
| 314 | return ReadDirectoryChangesW(w->hdir, w->buf, sizeof(w->buf), | ||
| 315 | FALSE, w->mask, &nret, &w->overlap, NULL); | ||
| 316 | } | ||
| 317 | |||
| 318 | int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 319 | int inotifyd_main(int argc, char **argv) | ||
| 320 | { | ||
| 321 | int n; | ||
| 322 | unsigned mask, bits; | ||
| 323 | const char *agent; | ||
| 324 | HANDLE *hevent; | ||
| 325 | struct watch *watch; | ||
| 326 | |||
| 327 | // sanity check: agent and at least one watch must be given | ||
| 328 | if (!argv[1] || !argv[2]) | ||
| 329 | bb_show_usage(); | ||
| 330 | |||
| 331 | argv++; | ||
| 332 | agent = *argv; | ||
| 333 | argc -= 2; // number of files we watch | ||
| 334 | |||
| 335 | watch = (struct watch *)xzalloc(argc * sizeof(struct watch)); | ||
| 336 | hevent = (HANDLE *)xzalloc(argc * sizeof(HANDLE)); | ||
| 337 | |||
| 338 | // setup watches | ||
| 339 | for (n = 0; *++argv; ++n) { | ||
| 340 | char *masks; | ||
| 341 | |||
| 342 | masks = strrchr(*argv, ':'); | ||
| 343 | // don't confuse a drive prefix with a mask | ||
| 344 | if (masks && masks != (*argv)+1) | ||
| 345 | *masks = '\0'; | ||
| 346 | |||
| 347 | mask = 0x01ff; // assuming we want all notifications | ||
| 348 | bits = 0x3f; // assuming we want to report everything | ||
| 349 | // if mask is specified -> | ||
| 350 | if (masks && *masks == '\0') { | ||
| 351 | // convert names to notification filter and report bitmask | ||
| 352 | mask = bits = 0; | ||
| 353 | while (*++masks) { | ||
| 354 | const char *found; | ||
| 355 | found = memchr(mask_names, *masks, MASK_BITS); | ||
| 356 | if (found) { | ||
| 357 | mask |= mask_values[(found - mask_names)]; | ||
| 358 | bits |= (1 << (found - mask_names)); | ||
| 359 | } | ||
| 360 | } | ||
| 361 | } | ||
| 362 | |||
| 363 | if (mask == 0) | ||
| 364 | bb_error_msg_and_die("%s: invalid mask\n", *argv); | ||
| 365 | |||
| 366 | if (!is_directory(*argv, FALSE)) | ||
| 367 | bb_error_msg_and_die("%s: not a directory", *argv); | ||
| 368 | |||
| 369 | watch[n].hdir = CreateFile(*argv, GENERIC_READ|FILE_LIST_DIRECTORY, | ||
| 370 | FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE, | ||
| 371 | NULL, OPEN_EXISTING, | ||
| 372 | FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL); | ||
| 373 | if (watch[n].hdir == INVALID_HANDLE_VALUE) | ||
| 374 | break; | ||
| 375 | |||
| 376 | watch[n].dirname = *argv; | ||
| 377 | watch[n].mask = mask; | ||
| 378 | watch[n].bits = bits; | ||
| 379 | watch[n].hevent = hevent[n] = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
| 380 | |||
| 381 | if (!start_watch(watch+n)) | ||
| 382 | break; | ||
| 383 | } | ||
| 384 | |||
| 385 | if (*argv != NULL) { | ||
| 386 | errno = err_win_to_posix(); | ||
| 387 | bb_perror_msg_and_die("add watch (%s) failed", *argv); | ||
| 388 | } | ||
| 389 | |||
| 390 | while (1) { | ||
| 391 | DWORD status; | ||
| 392 | |||
| 393 | status = WaitForMultipleObjects(n, hevent, FALSE, INFINITE); | ||
| 394 | if (WAIT_OBJECT_0 <= status && status < WAIT_OBJECT_0 + n) { | ||
| 395 | FILE_NOTIFY_INFORMATION *info; | ||
| 396 | int index = status - WAIT_OBJECT_0; | ||
| 397 | int offset = 0; | ||
| 398 | struct watch *w = watch + index; | ||
| 399 | int got_zero = 0; | ||
| 400 | |||
| 401 | do { | ||
| 402 | info = (FILE_NOTIFY_INFORMATION *)(w->buf + offset); | ||
| 403 | got_zero += (info->Action == 0); | ||
| 404 | run_agent(agent, info, w); | ||
| 405 | offset += info->NextEntryOffset; | ||
| 406 | } while (info->NextEntryOffset); | ||
| 407 | |||
| 408 | if (!start_watch(w)) { | ||
| 409 | // directory was deleted? | ||
| 410 | int i, count; | ||
| 411 | |||
| 412 | if (!got_zero) { | ||
| 413 | // we haven't seen an 'x' event, fake one | ||
| 414 | memset(info, 0, sizeof(FILE_NOTIFY_INFORMATION)); | ||
| 415 | run_agent(agent, info, w); | ||
| 416 | } | ||
| 417 | |||
| 418 | // mark watch as dead, terminate if all are dead | ||
| 419 | w->mask = 0; | ||
| 420 | for (count = i = 0; i<n; ++i) | ||
| 421 | if (watch[i].mask) | ||
| 422 | ++count; | ||
| 423 | if (count == 0) | ||
| 424 | break; | ||
| 425 | } | ||
| 426 | } | ||
| 427 | else { | ||
| 428 | errno = err_win_to_posix(); | ||
| 429 | bb_perror_msg_and_die("watch failed"); | ||
| 430 | } | ||
| 431 | } | ||
| 432 | |||
| 433 | return EXIT_SUCCESS; | ||
| 434 | } | ||
| 435 | #endif | ||
diff --git a/miscutils/jn.c b/miscutils/jn.c new file mode 100644 index 000000000..db6a3e6d9 --- /dev/null +++ b/miscutils/jn.c | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | /* | ||
| 2 | * directory junction creation for busybox | ||
| 3 | * | ||
| 4 | * Copyright (C) 2017 Denys Vlasenko <vda.linux@googlemail.com> | ||
| 5 | * Copyright (C) 2022 Ron Yorston <rmy@pobox.com> | ||
| 6 | * | ||
| 7 | * Licensed under GPLv2, see file LICENSE in this source tree. | ||
| 8 | */ | ||
| 9 | //config:config JN | ||
| 10 | //config: bool "jn (3.2 kb)" | ||
| 11 | //config: default y | ||
| 12 | //config: depends on PLATFORM_MINGW32 | ||
| 13 | //config: help | ||
| 14 | //config: Creates a directory junction. | ||
| 15 | |||
| 16 | //applet:IF_JN(APPLET_NOEXEC(jn, jn, BB_DIR_USR_BIN, BB_SUID_DROP, jn)) | ||
| 17 | |||
| 18 | //kbuild:lib-$(CONFIG_JN) += jn.o | ||
| 19 | |||
| 20 | //usage:#define jn_trivial_usage | ||
| 21 | //usage: "DIR JUNC" | ||
| 22 | //usage:#define jn_full_usage "\n\n" | ||
| 23 | //usage: "Create directory junction JUNC to DIR" | ||
| 24 | |||
| 25 | #include "libbb.h" | ||
| 26 | |||
| 27 | int jn_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 28 | int jn_main(int argc UNUSED_PARAM, char **argv) | ||
| 29 | { | ||
| 30 | getopt32(argv, "^" "" "\0" "=2"); | ||
| 31 | argv += optind; | ||
| 32 | if (create_junction(argv[0], argv[1]) != 0) { | ||
| 33 | bb_perror_msg_and_die("can't create junction '%s' to '%s'", | ||
| 34 | argv[1], argv[0]); | ||
| 35 | } | ||
| 36 | return EXIT_SUCCESS; | ||
| 37 | } | ||
diff --git a/miscutils/less.c b/miscutils/less.c index 2204b1cec..5a8fc5a74 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
| @@ -145,6 +145,10 @@ | |||
| 145 | 145 | ||
| 146 | #include <sched.h> /* sched_yield() */ | 146 | #include <sched.h> /* sched_yield() */ |
| 147 | 147 | ||
| 148 | #if ENABLE_PLATFORM_MINGW32 | ||
| 149 | #include <conio.h> | ||
| 150 | #endif | ||
| 151 | |||
| 148 | #include "libbb.h" | 152 | #include "libbb.h" |
| 149 | #include "common_bufsiz.h" | 153 | #include "common_bufsiz.h" |
| 150 | #if ENABLE_FEATURE_LESS_REGEXP | 154 | #if ENABLE_FEATURE_LESS_REGEXP |
| @@ -330,7 +334,11 @@ static void restore_tty(void) | |||
| 330 | set_tty_cooked(); | 334 | set_tty_cooked(); |
| 331 | if (!(G.kbd_fd_orig_flags & O_NONBLOCK)) | 335 | if (!(G.kbd_fd_orig_flags & O_NONBLOCK)) |
| 332 | ndelay_off(kbd_fd); | 336 | ndelay_off(kbd_fd); |
| 337 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 333 | clear_line(); | 338 | clear_line(); |
| 339 | #else | ||
| 340 | printf(ESC"[?1049l"); | ||
| 341 | #endif | ||
| 334 | } | 342 | } |
| 335 | 343 | ||
| 336 | static NOINLINE void less_exit(void) | 344 | static NOINLINE void less_exit(void) |
| @@ -578,6 +586,11 @@ static void read_lines(void) | |||
| 578 | last_line_pos = 0; | 586 | last_line_pos = 0; |
| 579 | break; | 587 | break; |
| 580 | } | 588 | } |
| 589 | #if ENABLE_PLATFORM_MINGW32 | ||
| 590 | if (c == '\r') { | ||
| 591 | continue; | ||
| 592 | } | ||
| 593 | #endif | ||
| 581 | /* NUL is substituted by '\n'! */ | 594 | /* NUL is substituted by '\n'! */ |
| 582 | if (c == '\0') c = '\n'; | 595 | if (c == '\0') c = '\n'; |
| 583 | *p++ = c; | 596 | *p++ = c; |
| @@ -674,7 +687,12 @@ static void update_num_lines(void) | |||
| 674 | /* only do this for regular files */ | 687 | /* only do this for regular files */ |
| 675 | if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) { | 688 | if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) { |
| 676 | count = 0; | 689 | count = 0; |
| 690 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 677 | fd = open("/proc/self/fd/0", O_RDONLY); | 691 | fd = open("/proc/self/fd/0", O_RDONLY); |
| 692 | #else | ||
| 693 | /* don't even try to access /proc on WIN32 */ | ||
| 694 | fd = -1; | ||
| 695 | #endif | ||
| 678 | if (fd < 0 && num_lines == REOPEN_AND_COUNT) { | 696 | if (fd < 0 && num_lines == REOPEN_AND_COUNT) { |
| 679 | /* "filename" is valid only if REOPEN_AND_COUNT */ | 697 | /* "filename" is valid only if REOPEN_AND_COUNT */ |
| 680 | fd = open(filename, O_RDONLY); | 698 | fd = open(filename, O_RDONLY); |
| @@ -857,7 +875,12 @@ static void print_found(const char *line) | |||
| 857 | match_status = 1; | 875 | match_status = 1; |
| 858 | } | 876 | } |
| 859 | 877 | ||
| 878 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 860 | printf("%s%s\n", growline ? growline : "", str); | 879 | printf("%s%s\n", growline ? growline : "", str); |
| 880 | #else | ||
| 881 | /* skip newline, we use explicit positioning on WIN32 */ | ||
| 882 | printf("%s%s", growline ? growline : "", str); | ||
| 883 | #endif | ||
| 861 | free(growline); | 884 | free(growline); |
| 862 | } | 885 | } |
| 863 | #else | 886 | #else |
| @@ -893,7 +916,12 @@ static void print_ascii(const char *str) | |||
| 893 | *p = '\0'; | 916 | *p = '\0'; |
| 894 | print_hilite(buf); | 917 | print_hilite(buf); |
| 895 | } | 918 | } |
| 919 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 896 | puts(str); | 920 | puts(str); |
| 921 | #else | ||
| 922 | /* skip newline, we use explicit positioning on WIN32 */ | ||
| 923 | printf("%s", str); | ||
| 924 | #endif | ||
| 897 | } | 925 | } |
| 898 | 926 | ||
| 899 | /* Print the buffer */ | 927 | /* Print the buffer */ |
| @@ -903,6 +931,10 @@ static void buffer_print(void) | |||
| 903 | 931 | ||
| 904 | move_cursor(0, 0); | 932 | move_cursor(0, 0); |
| 905 | for (i = 0; i <= max_displayed_line; i++) { | 933 | for (i = 0; i <= max_displayed_line; i++) { |
| 934 | #if ENABLE_PLATFORM_MINGW32 | ||
| 935 | /* make sure we're on the right line */ | ||
| 936 | move_cursor(i+1, 0); | ||
| 937 | #endif | ||
| 906 | printf(CLEAR_2_EOL); | 938 | printf(CLEAR_2_EOL); |
| 907 | if (option_mask32 & FLAG_N) | 939 | if (option_mask32 & FLAG_N) |
| 908 | print_lineno(buffer[i]); | 940 | print_lineno(buffer[i]); |
| @@ -1090,10 +1122,17 @@ static void reinitialize(void) | |||
| 1090 | if (G.winsize_err) | 1122 | if (G.winsize_err) |
| 1091 | printf(ESC"[999;999H" ESC"[6n"); | 1123 | printf(ESC"[999;999H" ESC"[6n"); |
| 1092 | #endif | 1124 | #endif |
| 1125 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1126 | printf(ESC"[?1049h"); | ||
| 1127 | #endif | ||
| 1093 | buffer_fill_and_print(); | 1128 | buffer_fill_and_print(); |
| 1094 | } | 1129 | } |
| 1095 | 1130 | ||
| 1131 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1132 | static int64_t unix_getch_nowait(void) | ||
| 1133 | #else | ||
| 1096 | static int64_t getch_nowait(void) | 1134 | static int64_t getch_nowait(void) |
| 1135 | #endif | ||
| 1097 | { | 1136 | { |
| 1098 | int rd; | 1137 | int rd; |
| 1099 | int64_t key64; | 1138 | int64_t key64; |
| @@ -1155,6 +1194,50 @@ static int64_t getch_nowait(void) | |||
| 1155 | return key64; | 1194 | return key64; |
| 1156 | } | 1195 | } |
| 1157 | 1196 | ||
| 1197 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1198 | static int64_t getch_nowait(void) | ||
| 1199 | { | ||
| 1200 | int64_t c; | ||
| 1201 | |||
| 1202 | if (terminal_mode(FALSE) & VT_INPUT) | ||
| 1203 | return unix_getch_nowait(); | ||
| 1204 | |||
| 1205 | retry: | ||
| 1206 | c = _getch(); | ||
| 1207 | if (c == 0 || c == 0xe0) { | ||
| 1208 | switch (_getch()) { | ||
| 1209 | case 0x48: | ||
| 1210 | c = KEYCODE_UP; | ||
| 1211 | break; | ||
| 1212 | case 0x50: | ||
| 1213 | c = KEYCODE_DOWN; | ||
| 1214 | break; | ||
| 1215 | case 0x49: | ||
| 1216 | c = KEYCODE_PAGEUP; | ||
| 1217 | break; | ||
| 1218 | case 0x51: | ||
| 1219 | c = KEYCODE_PAGEDOWN; | ||
| 1220 | break; | ||
| 1221 | case 0x47: | ||
| 1222 | c = KEYCODE_HOME; | ||
| 1223 | break; | ||
| 1224 | case 0x4f: | ||
| 1225 | c = KEYCODE_END; | ||
| 1226 | break; | ||
| 1227 | default: | ||
| 1228 | goto retry; | ||
| 1229 | } | ||
| 1230 | } | ||
| 1231 | |||
| 1232 | /* Position cursor if line input is done */ | ||
| 1233 | if (less_gets_pos >= 0) | ||
| 1234 | move_cursor(max_displayed_line + 2, less_gets_pos + 1); | ||
| 1235 | fflush_all(); | ||
| 1236 | |||
| 1237 | return c; | ||
| 1238 | } | ||
| 1239 | #endif | ||
| 1240 | |||
| 1158 | /* Grab a character from input without requiring the return key. | 1241 | /* Grab a character from input without requiring the return key. |
| 1159 | * May return KEYCODE_xxx values. | 1242 | * May return KEYCODE_xxx values. |
| 1160 | * Note that this function works best with raw input. */ | 1243 | * Note that this function works best with raw input. */ |
| @@ -1794,11 +1877,13 @@ static void keypress_process(int keypress) | |||
| 1794 | number_process(keypress); | 1877 | number_process(keypress); |
| 1795 | } | 1878 | } |
| 1796 | 1879 | ||
| 1880 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1797 | static void sig_catcher(int sig) | 1881 | static void sig_catcher(int sig) |
| 1798 | { | 1882 | { |
| 1799 | restore_tty(); | 1883 | restore_tty(); |
| 1800 | kill_myself_with_sig(sig); /* does not return */ | 1884 | kill_myself_with_sig(sig); /* does not return */ |
| 1801 | } | 1885 | } |
| 1886 | #endif | ||
| 1802 | 1887 | ||
| 1803 | #if ENABLE_FEATURE_LESS_WINCH | 1888 | #if ENABLE_FEATURE_LESS_WINCH |
| 1804 | static void sigwinch_handler(int sig UNUSED_PARAM) | 1889 | static void sigwinch_handler(int sig UNUSED_PARAM) |
| @@ -1810,7 +1895,11 @@ static void sigwinch_handler(int sig UNUSED_PARAM) | |||
| 1810 | int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1895 | int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| 1811 | int less_main(int argc, char **argv) | 1896 | int less_main(int argc, char **argv) |
| 1812 | { | 1897 | { |
| 1898 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1813 | char *tty_name; | 1899 | char *tty_name; |
| 1900 | #else | ||
| 1901 | HANDLE h; | ||
| 1902 | #endif | ||
| 1814 | int tty_fd; | 1903 | int tty_fd; |
| 1815 | 1904 | ||
| 1816 | INIT_G(); | 1905 | INIT_G(); |
| @@ -1869,6 +1958,7 @@ int less_main(int argc, char **argv) | |||
| 1869 | if (option_mask32 & FLAG_TILDE) | 1958 | if (option_mask32 & FLAG_TILDE) |
| 1870 | empty_line_marker = ""; | 1959 | empty_line_marker = ""; |
| 1871 | 1960 | ||
| 1961 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 1872 | /* Some versions of less can survive w/o controlling tty, | 1962 | /* Some versions of less can survive w/o controlling tty, |
| 1873 | * try to do the same. This also allows to specify an alternative | 1963 | * try to do the same. This also allows to specify an alternative |
| 1874 | * tty via "less 1<>TTY". | 1964 | * tty via "less 1<>TTY". |
| @@ -1894,6 +1984,15 @@ int less_main(int argc, char **argv) | |||
| 1894 | } | 1984 | } |
| 1895 | G.kbd_fd_orig_flags = ndelay_on(tty_fd); | 1985 | G.kbd_fd_orig_flags = ndelay_on(tty_fd); |
| 1896 | kbd_fd = tty_fd; /* save in a global */ | 1986 | kbd_fd = tty_fd; /* save in a global */ |
| 1987 | #else | ||
| 1988 | h = CreateFileA("CONIN$", GENERIC_READ | GENERIC_WRITE, | ||
| 1989 | FILE_SHARE_READ, NULL, OPEN_EXISTING, | ||
| 1990 | FILE_ATTRIBUTE_NORMAL, NULL); | ||
| 1991 | if (h == INVALID_HANDLE_VALUE) | ||
| 1992 | bb_simple_error_msg_and_die("unable to open console"); | ||
| 1993 | |||
| 1994 | kbd_fd = tty_fd = _open_osfhandle((intptr_t)h, O_BINARY); | ||
| 1995 | #endif | ||
| 1897 | 1996 | ||
| 1898 | get_termios_and_make_raw(tty_fd, &term_less, &term_orig, TERMIOS_RAW_CRNL_INPUT); | 1997 | get_termios_and_make_raw(tty_fd, &term_less, &term_orig, TERMIOS_RAW_CRNL_INPUT); |
| 1899 | 1998 | ||
diff --git a/miscutils/make.c b/miscutils/make.c new file mode 100644 index 000000000..8026917b3 --- /dev/null +++ b/miscutils/make.c | |||
| @@ -0,0 +1,3563 @@ | |||
| 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 PDPMAKE | ||
| 16 | //config: bool "pdpmake (18 kb)" | ||
| 17 | //config: default n | ||
| 18 | //config: help | ||
| 19 | //config: Alias for "make" | ||
| 20 | //config: | ||
| 21 | //config:config FEATURE_MAKE_POSIX | ||
| 22 | //config: bool "Runtime enforcement of POSIX" | ||
| 23 | //config: default n | ||
| 24 | //config: depends on MAKE || PDPMAKE | ||
| 25 | //config: help | ||
| 26 | //config: Allow strict enforcement of POSIX compliance at runtime by: | ||
| 27 | //config: - .POSIX special target in makefile | ||
| 28 | //config: - '--posix' command line option | ||
| 29 | //config: - PDPMAKE_POSIXLY_CORRECT environment variable | ||
| 30 | //config: Enable this if you want to check whether your makefiles are | ||
| 31 | //config: POSIX compliant. This adds about 1.7 kb. | ||
| 32 | //config: | ||
| 33 | //config:choice | ||
| 34 | //config: prompt "Default POSIX level to enforce" | ||
| 35 | //config: depends on FEATURE_MAKE_POSIX | ||
| 36 | //config: default FEATURE_MAKE_POSIX_2017 | ||
| 37 | //config: | ||
| 38 | //config:config FEATURE_MAKE_POSIX_2017 | ||
| 39 | //config: bool "2017" | ||
| 40 | //config: | ||
| 41 | //config:config FEATURE_MAKE_POSIX_2024 | ||
| 42 | //config: bool "2024" | ||
| 43 | //config: | ||
| 44 | //config:endchoice | ||
| 45 | |||
| 46 | //applet:IF_MAKE(APPLET(make, BB_DIR_USR_BIN, BB_SUID_DROP)) | ||
| 47 | //applet:IF_PDPMAKE(APPLET_ODDNAME(pdpmake, make, BB_DIR_USR_BIN, BB_SUID_DROP, make)) | ||
| 48 | |||
| 49 | //kbuild:lib-$(CONFIG_MAKE) += make.o | ||
| 50 | //kbuild:lib-$(CONFIG_PDPMAKE) += make.o | ||
| 51 | |||
| 52 | //usage:#define make_trivial_usage | ||
| 53 | //usage: IF_FEATURE_MAKE_POSIX( | ||
| 54 | //usage: "[--posix] [-C DIR] [-f FILE] [-j NUM] [-x PRAG] [-eiknpqrsSt] [MACRO[:[:[:]]]=VAL]... [TARGET]..." | ||
| 55 | //usage: ) | ||
| 56 | //usage: IF_NOT_FEATURE_MAKE_POSIX( | ||
| 57 | //usage: "[-C DIR] [-f FILE] [-j NUM] [-eiknpqrsSt] [MACRO[:[:[:]]]=VAL]... [TARGET]..." | ||
| 58 | //usage: ) | ||
| 59 | //usage:#define make_full_usage "\n\n" | ||
| 60 | //usage: "Maintain files based on their dependencies\n" | ||
| 61 | //usage: IF_FEATURE_MAKE_POSIX( | ||
| 62 | //usage: "\n --posix Enforce POSIX mode" | ||
| 63 | //usage: ) | ||
| 64 | //usage: "\n -C DIR Change to DIR" | ||
| 65 | //usage: "\n -f FILE Makefile" | ||
| 66 | //usage: "\n -j NUM Jobs to run in parallel (not implemented)" | ||
| 67 | //usage: IF_FEATURE_MAKE_POSIX( | ||
| 68 | //usage: "\n -x PRAG Make POSIX mode less strict" | ||
| 69 | //usage: ) | ||
| 70 | //usage: "\n -e Environment variables override macros in makefiles" | ||
| 71 | //usage: "\n -i Ignore exit status" | ||
| 72 | //usage: "\n -k Continue on error" | ||
| 73 | //usage: "\n -n Dry run" | ||
| 74 | //usage: "\n -p Print macros and targets" | ||
| 75 | //usage: "\n -q Query target; exit status 1 if not up to date" | ||
| 76 | //usage: "\n -r Don't use built-in rules" | ||
| 77 | //usage: "\n -s Make silently" | ||
| 78 | //usage: "\n -S Stop on error" | ||
| 79 | //usage: "\n -t Touch files instead of making them" | ||
| 80 | //usage: IF_FEATURE_MAKE_POSIX( | ||
| 81 | //usage: "\n\nThis build supports: non-POSIX extensions, POSIX 2024, POSIX 2017" | ||
| 82 | //usage: ) | ||
| 83 | //usage: IF_FEATURE_MAKE_POSIX_2017( | ||
| 84 | //usage: "\nIn strict POSIX mode the 2017 standard is enforced by default" | ||
| 85 | //usage: ) | ||
| 86 | //usage: IF_FEATURE_MAKE_POSIX_2024( | ||
| 87 | //usage: "\nIn strict POSIX mode the 2024 standard is enforced by default" | ||
| 88 | //usage: ) | ||
| 89 | |||
| 90 | #include "libbb.h" | ||
| 91 | #include "bb_archive.h" | ||
| 92 | #include "common_bufsiz.h" | ||
| 93 | #include <glob.h> | ||
| 94 | |||
| 95 | // Supported POSIX levels | ||
| 96 | #define STD_POSIX_2017 0 | ||
| 97 | #define STD_POSIX_2024 1 | ||
| 98 | |||
| 99 | #define POSIX_2017 (posix && posix_level == STD_POSIX_2017) | ||
| 100 | |||
| 101 | #if ENABLE_FEATURE_MAKE_POSIX_2017 | ||
| 102 | # define DEFAULT_POSIX_LEVEL STD_POSIX_2017 | ||
| 103 | #else | ||
| 104 | # define DEFAULT_POSIX_LEVEL STD_POSIX_2024 | ||
| 105 | #endif | ||
| 106 | |||
| 107 | #define OPTSTR1 "eij:+knqrsSt" | ||
| 108 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 109 | #define OPTSTR2 "pf:*C:*x:*" | ||
| 110 | #else | ||
| 111 | #define OPTSTR2 "pf:*C:*" | ||
| 112 | #endif | ||
| 113 | |||
| 114 | enum { | ||
| 115 | OPTBIT_e = 0, | ||
| 116 | OPTBIT_i, | ||
| 117 | OPTBIT_j, | ||
| 118 | OPTBIT_k, | ||
| 119 | OPTBIT_n, | ||
| 120 | OPTBIT_q, | ||
| 121 | OPTBIT_r, | ||
| 122 | OPTBIT_s, | ||
| 123 | OPTBIT_S, | ||
| 124 | OPTBIT_t, | ||
| 125 | OPTBIT_p, | ||
| 126 | OPTBIT_f, | ||
| 127 | OPTBIT_C, | ||
| 128 | IF_FEATURE_MAKE_POSIX(OPTBIT_x,) | ||
| 129 | OPTBIT_precious, | ||
| 130 | OPTBIT_phony, | ||
| 131 | OPTBIT_include, | ||
| 132 | OPTBIT_make, | ||
| 133 | |||
| 134 | OPT_e = (1 << OPTBIT_e), | ||
| 135 | OPT_i = (1 << OPTBIT_i), | ||
| 136 | OPT_j = (1 << OPTBIT_j), | ||
| 137 | OPT_k = (1 << OPTBIT_k), | ||
| 138 | OPT_n = (1 << OPTBIT_n), | ||
| 139 | OPT_q = (1 << OPTBIT_q), | ||
| 140 | OPT_r = (1 << OPTBIT_r), | ||
| 141 | OPT_s = (1 << OPTBIT_s), | ||
| 142 | OPT_S = (1 << OPTBIT_S), | ||
| 143 | OPT_t = (1 << OPTBIT_t), | ||
| 144 | // These options aren't allowed in MAKEFLAGS | ||
| 145 | OPT_p = (1 << OPTBIT_p), | ||
| 146 | OPT_f = (1 << OPTBIT_f), | ||
| 147 | OPT_C = (1 << OPTBIT_C), | ||
| 148 | OPT_x = IF_FEATURE_MAKE_POSIX((1 << OPTBIT_x)) + 0, | ||
| 149 | // The following aren't command line options and must be last | ||
| 150 | OPT_precious = (1 << OPTBIT_precious), | ||
| 151 | OPT_phony = (1 << OPTBIT_phony), | ||
| 152 | OPT_include = (1 << OPTBIT_include), | ||
| 153 | OPT_make = (1 << OPTBIT_make), | ||
| 154 | }; | ||
| 155 | |||
| 156 | // Options in OPTSTR1 that aren't included in MAKEFLAGS | ||
| 157 | #define OPT_MASK (~OPT_S) | ||
| 158 | |||
| 159 | #define useenv (opts & OPT_e) | ||
| 160 | #define ignore (opts & OPT_i) | ||
| 161 | #define errcont (opts & OPT_k) | ||
| 162 | #define dryrun (opts & OPT_n) | ||
| 163 | #define print (opts & OPT_p) | ||
| 164 | #define quest (opts & OPT_q) | ||
| 165 | #define norules (opts & OPT_r) | ||
| 166 | #define silent (opts & OPT_s) | ||
| 167 | #define dotouch (opts & OPT_t) | ||
| 168 | #define precious (opts & OPT_precious) | ||
| 169 | #define doinclude (opts & OPT_include) | ||
| 170 | #define domake (opts & OPT_make) | ||
| 171 | |||
| 172 | // A name. This represents a file, either to be made, or pre-existing. | ||
| 173 | struct name { | ||
| 174 | struct name *n_next; // Next in the list of names | ||
| 175 | char *n_name; // Called | ||
| 176 | struct rule *n_rule; // Rules to build this (prerequisites/commands) | ||
| 177 | struct timespec n_tim; // Modification time of this name | ||
| 178 | uint16_t n_flag; // Info about the name | ||
| 179 | }; | ||
| 180 | |||
| 181 | #define N_DOING 0x01 // Name in process of being built | ||
| 182 | #define N_DONE 0x02 // Name looked at | ||
| 183 | #define N_TARGET 0x04 // Name is a target | ||
| 184 | #define N_PRECIOUS 0x08 // Target is precious | ||
| 185 | #define N_DOUBLE 0x10 // Double-colon target | ||
| 186 | #define N_SILENT 0x20 // Build target silently | ||
| 187 | #define N_IGNORE 0x40 // Ignore build errors | ||
| 188 | #define N_SPECIAL 0x80 // Special target | ||
| 189 | #define N_MARK 0x100 // Mark for deduplication | ||
| 190 | #define N_PHONY 0x200 // Name is a phony target | ||
| 191 | #define N_INFERENCE 0x400 // Inference rule | ||
| 192 | |||
| 193 | // List of rules to build a target | ||
| 194 | struct rule { | ||
| 195 | struct rule *r_next; // Next rule | ||
| 196 | struct depend *r_dep; // Prerequisites for this rule | ||
| 197 | struct cmd *r_cmd; // Commands for this rule | ||
| 198 | }; | ||
| 199 | |||
| 200 | // NOTE: the layout of the following two structures must be compatible. | ||
| 201 | // Also, their first two members must be compatible with llist_t. | ||
| 202 | |||
| 203 | // List of prerequisites for a rule | ||
| 204 | struct depend { | ||
| 205 | struct depend *d_next; // Next prerequisite | ||
| 206 | struct name *d_name; // Name of prerequisite | ||
| 207 | int d_refcnt; // Reference count | ||
| 208 | }; | ||
| 209 | |||
| 210 | // List of commands for a rule | ||
| 211 | struct cmd { | ||
| 212 | struct cmd *c_next; // Next command line | ||
| 213 | char *c_cmd; // Text of command line | ||
| 214 | int c_refcnt; // Reference count | ||
| 215 | const char *c_makefile; // Makefile in which command was defined | ||
| 216 | int c_dispno; // Line number within makefile | ||
| 217 | }; | ||
| 218 | |||
| 219 | // Macro storage | ||
| 220 | struct macro { | ||
| 221 | struct macro *m_next; // Next variable | ||
| 222 | char *m_name; // Its name | ||
| 223 | char *m_val; // Its value | ||
| 224 | bool m_immediate; // Immediate-expansion macro set using ::= | ||
| 225 | bool m_flag; // Infinite loop check | ||
| 226 | uint8_t m_level; // Level at which macro was created | ||
| 227 | }; | ||
| 228 | |||
| 229 | // Flags passed to setmacro() | ||
| 230 | #define M_IMMEDIATE 0x08 // immediate-expansion macro is being defined | ||
| 231 | #define M_VALID 0x10 // assert macro name is valid | ||
| 232 | #define M_ENVIRON 0x20 // macro imported from environment | ||
| 233 | |||
| 234 | // Constants for PRAGMA. Order must match strings in set_pragma(). | ||
| 235 | enum { | ||
| 236 | BIT_MACRO_NAME = 0, | ||
| 237 | BIT_TARGET_NAME, | ||
| 238 | BIT_COMMAND_COMMENT, | ||
| 239 | BIT_EMPTY_SUFFIX, | ||
| 240 | #if ENABLE_PLATFORM_MINGW32 | ||
| 241 | BIT_WINDOWS, | ||
| 242 | #endif | ||
| 243 | BIT_POSIX_2017, | ||
| 244 | BIT_POSIX_2024, | ||
| 245 | BIT_POSIX_202X, | ||
| 246 | |||
| 247 | P_MACRO_NAME = (1 << BIT_MACRO_NAME), | ||
| 248 | P_TARGET_NAME = (1 << BIT_TARGET_NAME), | ||
| 249 | P_COMMAND_COMMENT = (1 << BIT_COMMAND_COMMENT), | ||
| 250 | P_EMPTY_SUFFIX = (1 << BIT_EMPTY_SUFFIX), | ||
| 251 | #if ENABLE_PLATFORM_MINGW32 | ||
| 252 | P_WINDOWS = (1 << BIT_WINDOWS), | ||
| 253 | #endif | ||
| 254 | }; | ||
| 255 | |||
| 256 | // Status of make() | ||
| 257 | #define MAKE_FAILURE 0x01 | ||
| 258 | #define MAKE_DIDSOMETHING 0x02 | ||
| 259 | |||
| 260 | // Return TRUE if c is allowed in a POSIX 2017 macro or target name | ||
| 261 | #define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_') | ||
| 262 | // Return TRUE if c is in the POSIX 'portable filename character set' | ||
| 263 | #define isfname(c) (ispname(c) || c == '-') | ||
| 264 | |||
| 265 | #define HTABSIZE 39 | ||
| 266 | |||
| 267 | struct globals { | ||
| 268 | uint32_t opts; | ||
| 269 | const char *makefile; | ||
| 270 | llist_t *makefiles; | ||
| 271 | llist_t *dirs; | ||
| 272 | struct name *namehead[HTABSIZE]; | ||
| 273 | struct macro *macrohead[HTABSIZE]; | ||
| 274 | struct name *firstname; | ||
| 275 | struct name *target; | ||
| 276 | time_t ar_mtime; | ||
| 277 | int lineno; // Physical line number in file | ||
| 278 | int dispno; // Line number for display purposes | ||
| 279 | struct cmd *curr_cmd; | ||
| 280 | const char *rulepos; | ||
| 281 | int rule_idx; | ||
| 282 | #define IF_MAX 10 | ||
| 283 | uint8_t clevel; | ||
| 284 | uint8_t cstate[IF_MAX + 1]; | ||
| 285 | int numjobs; | ||
| 286 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 287 | bool posix; | ||
| 288 | bool seen_first; | ||
| 289 | llist_t *pragmas; | ||
| 290 | unsigned char pragma; | ||
| 291 | unsigned char posix_level; | ||
| 292 | #endif | ||
| 293 | } FIX_ALIASING; | ||
| 294 | |||
| 295 | #define G (*(struct globals*)bb_common_bufsiz1) | ||
| 296 | #define INIT_G() do { \ | ||
| 297 | setup_common_bufsiz(); \ | ||
| 298 | } while (0) | ||
| 299 | |||
| 300 | #define opts (G.opts) | ||
| 301 | #define makefile (G.makefile) | ||
| 302 | #define makefiles (G.makefiles) | ||
| 303 | #define dirs (G.dirs) | ||
| 304 | #define namehead (G.namehead) | ||
| 305 | #define macrohead (G.macrohead) | ||
| 306 | #define firstname (G.firstname) | ||
| 307 | #define target (G.target) | ||
| 308 | #define ar_mtime (G.ar_mtime) | ||
| 309 | #define lineno (G.lineno) | ||
| 310 | #define dispno (G.dispno) | ||
| 311 | #define curr_cmd (G.curr_cmd) | ||
| 312 | #define rulepos (G.rulepos) | ||
| 313 | #define rule_idx (G.rule_idx) | ||
| 314 | #define clevel (G.clevel) | ||
| 315 | #define cstate (G.cstate) | ||
| 316 | #define numjobs (G.numjobs) | ||
| 317 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 318 | #define posix (G.posix) | ||
| 319 | #define seen_first (G.seen_first) | ||
| 320 | #define pragmas (G.pragmas) | ||
| 321 | #define pragma (G.pragma) | ||
| 322 | #define posix_level (G.posix_level) | ||
| 323 | #else | ||
| 324 | #define posix 0 | ||
| 325 | #define pragma 0 | ||
| 326 | #define posix_level DEFAULT_POSIX_LEVEL | ||
| 327 | #endif | ||
| 328 | |||
| 329 | static int make(struct name *np, int level); | ||
| 330 | static struct name *dyndep(struct name *np, struct rule *infrule, | ||
| 331 | const char **ptsuff); | ||
| 332 | |||
| 333 | /* | ||
| 334 | * Utility functions. | ||
| 335 | */ | ||
| 336 | |||
| 337 | /* | ||
| 338 | * Print message, with makefile and line number if possible. | ||
| 339 | */ | ||
| 340 | static void | ||
| 341 | vwarning(FILE *stream, const char *msg, va_list list) | ||
| 342 | { | ||
| 343 | const char *m = NULL; | ||
| 344 | int d = 0; | ||
| 345 | |||
| 346 | if (curr_cmd) { | ||
| 347 | m = curr_cmd->c_makefile; | ||
| 348 | d = curr_cmd->c_dispno; | ||
| 349 | } else if (makefile) { | ||
| 350 | m = makefile; | ||
| 351 | d = dispno; | ||
| 352 | } | ||
| 353 | |||
| 354 | fprintf(stream, "%s: ", applet_name); | ||
| 355 | if (m) | ||
| 356 | fprintf(stream, "(%s:%d): ", m, d); | ||
| 357 | vfprintf(stream, msg, list); | ||
| 358 | fputc('\n', stream); | ||
| 359 | } | ||
| 360 | |||
| 361 | /* | ||
| 362 | * Diagnostic handler. Print message to standard error. | ||
| 363 | */ | ||
| 364 | static void | ||
| 365 | diagnostic(const char *msg, ...) | ||
| 366 | { | ||
| 367 | va_list list; | ||
| 368 | |||
| 369 | va_start(list, msg); | ||
| 370 | vwarning(stderr, msg, list); | ||
| 371 | va_end(list); | ||
| 372 | } | ||
| 373 | |||
| 374 | /* | ||
| 375 | * Error handler. Print message and exit. | ||
| 376 | */ | ||
| 377 | static void error(const char *msg, ...) NORETURN; | ||
| 378 | static void | ||
| 379 | error(const char *msg, ...) | ||
| 380 | { | ||
| 381 | va_list list; | ||
| 382 | |||
| 383 | va_start(list, msg); | ||
| 384 | vwarning(stderr, msg, list); | ||
| 385 | va_end(list); | ||
| 386 | exit(2); | ||
| 387 | } | ||
| 388 | |||
| 389 | static void error_unexpected(const char *s) NORETURN; | ||
| 390 | static void | ||
| 391 | error_unexpected(const char *s) | ||
| 392 | { | ||
| 393 | error("unexpected %s", s); | ||
| 394 | } | ||
| 395 | |||
| 396 | static void error_in_inference_rule(const char *s) NORETURN; | ||
| 397 | static void | ||
| 398 | error_in_inference_rule(const char *s) | ||
| 399 | { | ||
| 400 | error("%s in inference rule", s); | ||
| 401 | } | ||
| 402 | |||
| 403 | static void | ||
| 404 | error_not_allowed(const char *s, const char *t) | ||
| 405 | { | ||
| 406 | error("%s not allowed for %s", s, t); | ||
| 407 | } | ||
| 408 | |||
| 409 | static void | ||
| 410 | warning(const char *msg, ...) | ||
| 411 | { | ||
| 412 | va_list list; | ||
| 413 | |||
| 414 | va_start(list, msg); | ||
| 415 | vwarning(stdout, msg, list); | ||
| 416 | va_end(list); | ||
| 417 | } | ||
| 418 | |||
| 419 | static char * | ||
| 420 | auto_concat(const char *s1, const char *s2) | ||
| 421 | { | ||
| 422 | return auto_string(xasprintf("%s%s", s1, s2)); | ||
| 423 | } | ||
| 424 | |||
| 425 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 426 | /* | ||
| 427 | * Append a word to a space-separated string of words. The first | ||
| 428 | * call should use a NULL pointer for str, subsequent calls should | ||
| 429 | * pass an allocated string which will be freed. | ||
| 430 | */ | ||
| 431 | static char * | ||
| 432 | xappendword(const char *str, const char *word) | ||
| 433 | { | ||
| 434 | char *newstr = str ? xasprintf("%s %s", str, word) : xstrdup(word); | ||
| 435 | free((void *)str); | ||
| 436 | return newstr; | ||
| 437 | } | ||
| 438 | #endif | ||
| 439 | |||
| 440 | static unsigned int | ||
| 441 | getbucket(const char *name) | ||
| 442 | { | ||
| 443 | unsigned int hashval = 0; | ||
| 444 | const unsigned char *p = (unsigned char *)name; | ||
| 445 | |||
| 446 | while (*p) | ||
| 447 | hashval ^= (hashval << 5) + (hashval >> 2) + *p++; | ||
| 448 | return hashval % HTABSIZE; | ||
| 449 | } | ||
| 450 | |||
| 451 | /* | ||
| 452 | * Add a prerequisite to the end of the supplied list. | ||
| 453 | */ | ||
| 454 | static void | ||
| 455 | newdep(struct depend **dphead, struct name *np) | ||
| 456 | { | ||
| 457 | while (*dphead) | ||
| 458 | dphead = &(*dphead)->d_next; | ||
| 459 | *dphead = xzalloc(sizeof(struct depend)); | ||
| 460 | /*(*dphead)->d_next = NULL; - xzalloc did it */ | ||
| 461 | (*dphead)->d_name = np; | ||
| 462 | /*(*dphead)->d_refcnt = 0; */ | ||
| 463 | } | ||
| 464 | |||
| 465 | static void | ||
| 466 | freedeps(struct depend *dp) | ||
| 467 | { | ||
| 468 | if (dp && --dp->d_refcnt <= 0) | ||
| 469 | llist_free((llist_t *)dp, NULL); | ||
| 470 | } | ||
| 471 | |||
| 472 | /* | ||
| 473 | * Add a command to the end of the supplied list of commands. | ||
| 474 | */ | ||
| 475 | static void | ||
| 476 | newcmd(struct cmd **cphead, char *str) | ||
| 477 | { | ||
| 478 | while (isspace(*str)) | ||
| 479 | str++; | ||
| 480 | |||
| 481 | while (*cphead) | ||
| 482 | cphead = &(*cphead)->c_next; | ||
| 483 | *cphead = xzalloc(sizeof(struct cmd)); | ||
| 484 | /*(*cphead)->c_next = NULL; - xzalloc did it */ | ||
| 485 | (*cphead)->c_cmd = xstrdup(str); | ||
| 486 | /*(*cphead)->c_refcnt = 0; */ | ||
| 487 | (*cphead)->c_makefile = xstrdup(makefile); | ||
| 488 | (*cphead)->c_dispno = dispno; | ||
| 489 | } | ||
| 490 | |||
| 491 | static void | ||
| 492 | freecmds(struct cmd *cp) | ||
| 493 | { | ||
| 494 | struct cmd *nextcp; | ||
| 495 | |||
| 496 | if (cp && --cp->c_refcnt <= 0) { | ||
| 497 | for (; cp; cp = nextcp) { | ||
| 498 | nextcp = cp->c_next; | ||
| 499 | free(cp->c_cmd); | ||
| 500 | free((void *)cp->c_makefile); | ||
| 501 | free(cp); | ||
| 502 | } | ||
| 503 | } | ||
| 504 | } | ||
| 505 | |||
| 506 | static struct name * | ||
| 507 | findname(const char *name) | ||
| 508 | { | ||
| 509 | struct name *np = namehead[getbucket(name)]; | ||
| 510 | return (struct name *)llist_find_str((llist_t *)np, name); | ||
| 511 | } | ||
| 512 | |||
| 513 | static int | ||
| 514 | check_name(const char *name) | ||
| 515 | { | ||
| 516 | const char *s; | ||
| 517 | |||
| 518 | #if ENABLE_PLATFORM_MINGW32 | ||
| 519 | if (!posix || (pragma & P_WINDOWS)) { | ||
| 520 | if (isalpha(name[0]) && name[1] == ':' && name[2] == '/') { | ||
| 521 | name += 3; | ||
| 522 | } | ||
| 523 | } | ||
| 524 | #endif | ||
| 525 | if (!posix) { | ||
| 526 | for (s = name; *s; ++s) { | ||
| 527 | if (*s == '=') | ||
| 528 | return FALSE; | ||
| 529 | } | ||
| 530 | return TRUE; | ||
| 531 | } | ||
| 532 | |||
| 533 | for (s = name; *s; ++s) { | ||
| 534 | if ((pragma & P_TARGET_NAME) || !POSIX_2017 ? | ||
| 535 | !(isfname(*s) || *s == '/') : !ispname(*s)) | ||
| 536 | return FALSE; | ||
| 537 | } | ||
| 538 | return TRUE; | ||
| 539 | } | ||
| 540 | |||
| 541 | static char *splitlib(const char *name, char **member); | ||
| 542 | |||
| 543 | static int | ||
| 544 | is_valid_target(const char *name) | ||
| 545 | { | ||
| 546 | char *archive, *member = NULL; | ||
| 547 | int ret; | ||
| 548 | |||
| 549 | /* Names of the form 'lib(member)' are referred to as 'expressions' | ||
| 550 | * in POSIX and are subjected to special treatment. The 'lib' | ||
| 551 | * and 'member' elements must each be a valid target name. */ | ||
| 552 | archive = splitlib(name, &member); | ||
| 553 | ret = check_name(archive) && (member == NULL || check_name(member)); | ||
| 554 | free(archive); | ||
| 555 | |||
| 556 | return ret; | ||
| 557 | } | ||
| 558 | |||
| 559 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 560 | static int | ||
| 561 | potentially_valid_target(const char *name) | ||
| 562 | { | ||
| 563 | int ret = FALSE; | ||
| 564 | |||
| 565 | if (!(pragma & P_TARGET_NAME)) { | ||
| 566 | pragma |= P_TARGET_NAME; | ||
| 567 | ret = is_valid_target(name); | ||
| 568 | pragma &= ~P_TARGET_NAME; | ||
| 569 | } | ||
| 570 | return ret; | ||
| 571 | } | ||
| 572 | #endif | ||
| 573 | |||
| 574 | /* | ||
| 575 | * Intern a name. Return a pointer to the name struct | ||
| 576 | */ | ||
| 577 | static struct name * | ||
| 578 | newname(const char *name) | ||
| 579 | { | ||
| 580 | struct name *np = findname(name); | ||
| 581 | |||
| 582 | if (np == NULL) { | ||
| 583 | unsigned int bucket; | ||
| 584 | |||
| 585 | if (!is_valid_target(name)) | ||
| 586 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 587 | error("invalid target name '%s'%s", name, | ||
| 588 | potentially_valid_target(name) ? | ||
| 589 | ": allow with pragma target_name" : ""); | ||
| 590 | #else | ||
| 591 | error("invalid target name '%s'", name); | ||
| 592 | #endif | ||
| 593 | |||
| 594 | bucket = getbucket(name); | ||
| 595 | np = xzalloc(sizeof(struct name)); | ||
| 596 | np->n_next = namehead[bucket]; | ||
| 597 | namehead[bucket] = np; | ||
| 598 | np->n_name = xstrdup(name); | ||
| 599 | /*np->n_rule = NULL; - xzalloc did it */ | ||
| 600 | /*np->n_tim = (struct timespec){0, 0}; */ | ||
| 601 | /*np->n_flag = 0; */ | ||
| 602 | } | ||
| 603 | return np; | ||
| 604 | } | ||
| 605 | |||
| 606 | /* | ||
| 607 | * Return the commands on the first rule that has them or NULL. | ||
| 608 | */ | ||
| 609 | static struct cmd * | ||
| 610 | getcmd(struct name *np) | ||
| 611 | { | ||
| 612 | struct rule *rp; | ||
| 613 | |||
| 614 | if (np == NULL) | ||
| 615 | return NULL; | ||
| 616 | |||
| 617 | for (rp = np->n_rule; rp; rp = rp->r_next) | ||
| 618 | if (rp->r_cmd) | ||
| 619 | return rp->r_cmd; | ||
| 620 | return NULL; | ||
| 621 | } | ||
| 622 | |||
| 623 | #if ENABLE_FEATURE_CLEAN_UP | ||
| 624 | static void | ||
| 625 | freenames(void) | ||
| 626 | { | ||
| 627 | int i; | ||
| 628 | struct name *np, *nextnp; | ||
| 629 | |||
| 630 | for (i = 0; i < HTABSIZE; i++) { | ||
| 631 | for (np = namehead[i]; np; np = nextnp) { | ||
| 632 | nextnp = np->n_next; | ||
| 633 | free(np->n_name); | ||
| 634 | freerules(np->n_rule); | ||
| 635 | free(np); | ||
| 636 | } | ||
| 637 | } | ||
| 638 | } | ||
| 639 | #endif | ||
| 640 | |||
| 641 | static void | ||
| 642 | freerules(struct rule *rp) | ||
| 643 | { | ||
| 644 | struct rule *nextrp; | ||
| 645 | |||
| 646 | for (; rp; rp = nextrp) { | ||
| 647 | nextrp = rp->r_next; | ||
| 648 | freedeps(rp->r_dep); | ||
| 649 | freecmds(rp->r_cmd); | ||
| 650 | free(rp); | ||
| 651 | } | ||
| 652 | } | ||
| 653 | |||
| 654 | static void * | ||
| 655 | inc_ref(void *vp) | ||
| 656 | { | ||
| 657 | if (vp) { | ||
| 658 | struct depend *dp = vp; | ||
| 659 | if (dp->d_refcnt == INT_MAX) | ||
| 660 | bb_die_memory_exhausted(); | ||
| 661 | dp->d_refcnt++; | ||
| 662 | } | ||
| 663 | return vp; | ||
| 664 | } | ||
| 665 | |||
| 666 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 667 | // Order must match constants above. | ||
| 668 | // POSIX levels must be last and in increasing order | ||
| 669 | static const char *p_name = | ||
| 670 | "macro_name\0" | ||
| 671 | "target_name\0" | ||
| 672 | "command_comment\0" | ||
| 673 | "empty_suffix\0" | ||
| 674 | #if ENABLE_PLATFORM_MINGW32 | ||
| 675 | "windows\0" | ||
| 676 | #endif | ||
| 677 | "posix_2017\0" | ||
| 678 | "posix_2024\0" | ||
| 679 | "posix_202x\0"; | ||
| 680 | |||
| 681 | static void | ||
| 682 | set_pragma(const char *name) | ||
| 683 | { | ||
| 684 | int idx = index_in_strings(p_name, name); | ||
| 685 | |||
| 686 | if (idx != -1) { | ||
| 687 | if (idx >= BIT_POSIX_2017) { | ||
| 688 | // POSIX level is stored in a separate variable. | ||
| 689 | // No bits in 'pragma' are used. | ||
| 690 | if (posix_level == DEFAULT_POSIX_LEVEL) { | ||
| 691 | posix_level = idx - BIT_POSIX_2017; | ||
| 692 | if (posix_level > STD_POSIX_2024) | ||
| 693 | posix_level = STD_POSIX_2024; | ||
| 694 | } else if (posix_level != idx - BIT_POSIX_2017) | ||
| 695 | warning("unable to change POSIX level"); | ||
| 696 | } else { | ||
| 697 | pragma |= 1 << idx; | ||
| 698 | } | ||
| 699 | return; | ||
| 700 | } | ||
| 701 | warning("invalid pragma '%s'", name); | ||
| 702 | } | ||
| 703 | |||
| 704 | static void | ||
| 705 | pragmas_to_env(void) | ||
| 706 | { | ||
| 707 | int i; | ||
| 708 | char *val = NULL; | ||
| 709 | |||
| 710 | for (i = 0; i < BIT_POSIX_2017; ++i) { | ||
| 711 | if ((pragma & (1 << i))) | ||
| 712 | val = xappendword(val, nth_string(p_name, i)); | ||
| 713 | } | ||
| 714 | |||
| 715 | if (posix_level != DEFAULT_POSIX_LEVEL) | ||
| 716 | val = xappendword(val, | ||
| 717 | nth_string(p_name, BIT_POSIX_2017 + posix_level)); | ||
| 718 | |||
| 719 | if (val) { | ||
| 720 | setenv("PDPMAKE_PRAGMAS", val, 1); | ||
| 721 | free(val); | ||
| 722 | } | ||
| 723 | } | ||
| 724 | #endif | ||
| 725 | |||
| 726 | /* | ||
| 727 | * Add a new rule to a target. This checks to see if commands already | ||
| 728 | * exist for the target. If flag is TRUE the target can have multiple | ||
| 729 | * rules with commands (double-colon rules). | ||
| 730 | * | ||
| 731 | * i) If the name is a special target and there are no prerequisites | ||
| 732 | * or commands to be added remove all prerequisites and commands. | ||
| 733 | * This is necessary when clearing a built-in inference rule. | ||
| 734 | * ii) If name is a special target and has commands, replace them. | ||
| 735 | * This is for redefining commands for an inference rule. | ||
| 736 | */ | ||
| 737 | static void | ||
| 738 | addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag) | ||
| 739 | { | ||
| 740 | struct rule *rp; | ||
| 741 | struct rule **rpp; | ||
| 742 | struct cmd *old_cp; | ||
| 743 | |||
| 744 | // Can't mix single-colon and double-colon rules | ||
| 745 | if (!posix && (np->n_flag & N_TARGET)) { | ||
| 746 | if (!(np->n_flag & N_DOUBLE) != !flag) // like xor | ||
| 747 | error("inconsistent rules for target %s", np->n_name); | ||
| 748 | } | ||
| 749 | |||
| 750 | // Clear out prerequisites and commands | ||
| 751 | if ((np->n_flag & N_SPECIAL) && !dp && !cp) { | ||
| 752 | if (strcmp(np->n_name, ".PHONY") == 0) | ||
| 753 | return; | ||
| 754 | freerules(np->n_rule); | ||
| 755 | np->n_rule = NULL; | ||
| 756 | return; | ||
| 757 | } | ||
| 758 | |||
| 759 | if (cp && !(np->n_flag & N_DOUBLE) && (old_cp = getcmd(np))) { | ||
| 760 | // Handle the inference rule redefinition case | ||
| 761 | // .DEFAULT rule can also be redefined (as an extension). | ||
| 762 | if ((np->n_flag & N_INFERENCE) | ||
| 763 | && !(posix && (np->n_flag & N_SPECIAL)) | ||
| 764 | ) { | ||
| 765 | freerules(np->n_rule); | ||
| 766 | np->n_rule = NULL; | ||
| 767 | } else { | ||
| 768 | // We're adding commands to a single colon rule which | ||
| 769 | // already has some. Clear the old ones first. | ||
| 770 | warning("overriding rule for target %s", np->n_name); | ||
| 771 | curr_cmd = old_cp; | ||
| 772 | warning("previous rule for target %s", np->n_name); | ||
| 773 | curr_cmd = NULL; | ||
| 774 | |||
| 775 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 776 | freecmds(rp->r_cmd); | ||
| 777 | rp->r_cmd = NULL; | ||
| 778 | } | ||
| 779 | } | ||
| 780 | } | ||
| 781 | |||
| 782 | rpp = &np->n_rule; | ||
| 783 | while (*rpp) | ||
| 784 | rpp = &(*rpp)->r_next; | ||
| 785 | |||
| 786 | *rpp = rp = xzalloc(sizeof(struct rule)); | ||
| 787 | /*rp->r_next = NULL; - xzalloc did it */ | ||
| 788 | rp->r_dep = inc_ref(dp); | ||
| 789 | rp->r_cmd = inc_ref(cp); | ||
| 790 | |||
| 791 | np->n_flag |= N_TARGET; | ||
| 792 | if (flag) | ||
| 793 | np->n_flag |= N_DOUBLE; | ||
| 794 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 795 | if (strcmp(np->n_name, ".PRAGMA") == 0) { | ||
| 796 | for (; dp; dp = dp->d_next) { | ||
| 797 | set_pragma(dp->d_name->n_name); | ||
| 798 | } | ||
| 799 | pragmas_to_env(); | ||
| 800 | } | ||
| 801 | #endif | ||
| 802 | } | ||
| 803 | |||
| 804 | /* | ||
| 805 | * Macro control for make | ||
| 806 | */ | ||
| 807 | static struct macro * | ||
| 808 | getmp(const char *name) | ||
| 809 | { | ||
| 810 | struct macro *mp = macrohead[getbucket(name)]; | ||
| 811 | return (struct macro *)llist_find_str((llist_t *)mp, name); | ||
| 812 | } | ||
| 813 | |||
| 814 | static int | ||
| 815 | is_valid_macro(const char *name) | ||
| 816 | { | ||
| 817 | const char *s; | ||
| 818 | for (s = name; *s; ++s) { | ||
| 819 | // In POSIX mode only a limited set of characters are guaranteed | ||
| 820 | // to be allowed in macro names. | ||
| 821 | if (posix) { | ||
| 822 | // Find the appropriate character set | ||
| 823 | if ((pragma & P_MACRO_NAME) || !POSIX_2017 ? | ||
| 824 | !isfname(*s) : !ispname(*s)) | ||
| 825 | return FALSE; | ||
| 826 | } | ||
| 827 | // As an extension allow anything that can get through the | ||
| 828 | // input parser, apart from the following. | ||
| 829 | if (*s == '=' || isblank(*s) || iscntrl(*s)) | ||
| 830 | return FALSE; | ||
| 831 | } | ||
| 832 | return TRUE; | ||
| 833 | } | ||
| 834 | |||
| 835 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 836 | static int | ||
| 837 | potentially_valid_macro(const char *name) | ||
| 838 | { | ||
| 839 | int ret = FALSE; | ||
| 840 | |||
| 841 | if (!(pragma & P_MACRO_NAME)) { | ||
| 842 | pragma |= P_MACRO_NAME; | ||
| 843 | ret = is_valid_macro(name); | ||
| 844 | pragma &= ~P_MACRO_NAME; | ||
| 845 | } | ||
| 846 | return ret; | ||
| 847 | } | ||
| 848 | #endif | ||
| 849 | |||
| 850 | static void | ||
| 851 | setmacro(const char *name, const char *val, int level) | ||
| 852 | { | ||
| 853 | struct macro *mp; | ||
| 854 | bool valid = level & M_VALID; | ||
| 855 | bool from_env = level & M_ENVIRON; | ||
| 856 | bool immediate = level & M_IMMEDIATE; | ||
| 857 | |||
| 858 | level &= ~(M_IMMEDIATE | M_VALID | M_ENVIRON); | ||
| 859 | mp = getmp(name); | ||
| 860 | if (mp) { | ||
| 861 | // Don't replace existing macro from a lower level | ||
| 862 | if (level > mp->m_level) | ||
| 863 | return; | ||
| 864 | |||
| 865 | // Replace existing macro | ||
| 866 | free(mp->m_val); | ||
| 867 | } else { | ||
| 868 | // If not defined, allocate space for new | ||
| 869 | unsigned int bucket; | ||
| 870 | |||
| 871 | if (!valid && !is_valid_macro(name)) { | ||
| 872 | // Silently drop invalid names from the environment | ||
| 873 | if (from_env) | ||
| 874 | return; | ||
| 875 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 876 | error("invalid macro name '%s'%s", name, | ||
| 877 | potentially_valid_macro(name) ? | ||
| 878 | ": allow with pragma macro_name" : ""); | ||
| 879 | #else | ||
| 880 | error("invalid macro name '%s'", name); | ||
| 881 | #endif | ||
| 882 | } | ||
| 883 | |||
| 884 | bucket = getbucket(name); | ||
| 885 | mp = xzalloc(sizeof(struct macro)); | ||
| 886 | mp->m_next = macrohead[bucket]; | ||
| 887 | macrohead[bucket] = mp; | ||
| 888 | /* mp->m_flag = FALSE; - xzalloc did it */ | ||
| 889 | mp->m_name = xstrdup(name); | ||
| 890 | } | ||
| 891 | mp->m_immediate = immediate; | ||
| 892 | mp->m_level = level; | ||
| 893 | mp->m_val = xstrdup(val ? val : ""); | ||
| 894 | } | ||
| 895 | |||
| 896 | #if ENABLE_FEATURE_CLEAN_UP | ||
| 897 | static void | ||
| 898 | freemacros(void) | ||
| 899 | { | ||
| 900 | int i; | ||
| 901 | struct macro *mp, *nextmp; | ||
| 902 | |||
| 903 | for (i = 0; i < HTABSIZE; i++) { | ||
| 904 | for (mp = macrohead[i]; mp; mp = nextmp) { | ||
| 905 | nextmp = mp->m_next; | ||
| 906 | free(mp->m_name); | ||
| 907 | free(mp->m_val); | ||
| 908 | free(mp); | ||
| 909 | } | ||
| 910 | } | ||
| 911 | } | ||
| 912 | #endif | ||
| 913 | |||
| 914 | /* | ||
| 915 | * Get modification time of file or archive member | ||
| 916 | */ | ||
| 917 | static void FAST_FUNC | ||
| 918 | record_mtime(const file_header_t *file_header) | ||
| 919 | { | ||
| 920 | ar_mtime = file_header->mtime; | ||
| 921 | } | ||
| 922 | |||
| 923 | static time_t | ||
| 924 | artime(const char *archive, const char *member) | ||
| 925 | { | ||
| 926 | archive_handle_t *archive_handle; | ||
| 927 | |||
| 928 | ar_mtime = 0; | ||
| 929 | archive_handle = init_handle(); | ||
| 930 | archive_handle->src_fd = open(archive, O_RDONLY); | ||
| 931 | if (archive_handle->src_fd != -1) { | ||
| 932 | archive_handle->action_header = record_mtime; | ||
| 933 | archive_handle->filter = filter_accept_list; | ||
| 934 | llist_add_to_end(&archive_handle->accept, (void *)member); | ||
| 935 | unpack_ar_archive(archive_handle); | ||
| 936 | close(archive_handle->src_fd); | ||
| 937 | } | ||
| 938 | |||
| 939 | #if ENABLE_FEATURE_AR_LONG_FILENAMES | ||
| 940 | free(archive_handle->ar__long_names); | ||
| 941 | #endif | ||
| 942 | llist_free(archive_handle->accept, NULL); | ||
| 943 | free(archive_handle->file_header); | ||
| 944 | free(archive_handle); | ||
| 945 | |||
| 946 | return ar_mtime; | ||
| 947 | } | ||
| 948 | |||
| 949 | /* | ||
| 950 | * If the name is of the form 'libname(member.o)' split it into its | ||
| 951 | * name and member parts and set the member pointer to point to the | ||
| 952 | * latter. Otherwise just take a copy of the name and don't alter | ||
| 953 | * the member pointer. | ||
| 954 | * | ||
| 955 | * In either case the return value is an allocated string which must | ||
| 956 | * be freed by the caller. | ||
| 957 | */ | ||
| 958 | static char * | ||
| 959 | splitlib(const char *name, char **member) | ||
| 960 | { | ||
| 961 | char *s, *t; | ||
| 962 | size_t len; | ||
| 963 | |||
| 964 | t = xstrdup(name); | ||
| 965 | s = strchr(t, '('); | ||
| 966 | if (s) { | ||
| 967 | // We have 'libname(member.o)' | ||
| 968 | *s++ = '\0'; | ||
| 969 | len = strlen(s); | ||
| 970 | if (len <= 1 || s[len - 1] != ')' || *t == '\0') | ||
| 971 | error("invalid name '%s'", name); | ||
| 972 | s[len - 1] = '\0'; | ||
| 973 | *member = s; | ||
| 974 | } | ||
| 975 | return t; | ||
| 976 | } | ||
| 977 | |||
| 978 | /* | ||
| 979 | * Get the modification time of a file. Set it to 0 if the file | ||
| 980 | * doesn't exist. | ||
| 981 | */ | ||
| 982 | static void | ||
| 983 | modtime(struct name *np) | ||
| 984 | { | ||
| 985 | char *name, *member = NULL; | ||
| 986 | struct stat info; | ||
| 987 | |||
| 988 | name = splitlib(np->n_name, &member); | ||
| 989 | if (member) { | ||
| 990 | // Looks like library(member) | ||
| 991 | np->n_tim.tv_sec = artime(name, member); | ||
| 992 | np->n_tim.tv_nsec = 0; | ||
| 993 | } else if (stat(name, &info) < 0) { | ||
| 994 | if (errno != ENOENT) | ||
| 995 | bb_perror_msg("can't open %s", name); | ||
| 996 | np->n_tim.tv_sec = 0; | ||
| 997 | np->n_tim.tv_nsec = 0; | ||
| 998 | } else { | ||
| 999 | np->n_tim.tv_sec = info.st_mtim.tv_sec; | ||
| 1000 | np->n_tim.tv_nsec = info.st_mtim.tv_nsec; | ||
| 1001 | } | ||
| 1002 | free(name); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | /* | ||
| 1006 | * Control of the implicit suffix rules | ||
| 1007 | */ | ||
| 1008 | |||
| 1009 | /* | ||
| 1010 | * Return a pointer to the suffix of a name (which may be the | ||
| 1011 | * terminating NUL if there's no suffix). | ||
| 1012 | */ | ||
| 1013 | static char * | ||
| 1014 | suffix(const char *name) | ||
| 1015 | { | ||
| 1016 | char *p = strrchr(name, '.'); | ||
| 1017 | return p ? p : (char *)name + strlen(name); | ||
| 1018 | } | ||
| 1019 | |||
| 1020 | /* | ||
| 1021 | * Search for an inference rule to convert some suffix ('psuff') | ||
| 1022 | * to the target suffix 'tsuff'. The basename of the prerequisite | ||
| 1023 | * is 'base'. | ||
| 1024 | */ | ||
| 1025 | static struct name * | ||
| 1026 | dyndep0(char *base, const char *tsuff, struct rule *infrule) | ||
| 1027 | { | ||
| 1028 | char *psuff; | ||
| 1029 | struct name *xp; // Suffixes | ||
| 1030 | struct name *sp; // Suffix rule | ||
| 1031 | struct rule *rp; | ||
| 1032 | struct depend *dp; | ||
| 1033 | bool chain = FALSE; | ||
| 1034 | |||
| 1035 | xp = newname(".SUFFIXES"); | ||
| 1036 | retry: | ||
| 1037 | for (rp = xp->n_rule; rp; rp = rp->r_next) { | ||
| 1038 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 1039 | // Generate new suffix rule to try | ||
| 1040 | psuff = dp->d_name->n_name; | ||
| 1041 | sp = findname(auto_concat(psuff, tsuff)); | ||
| 1042 | if (sp && sp->n_rule) { | ||
| 1043 | struct name *ip; | ||
| 1044 | int got_ip; | ||
| 1045 | |||
| 1046 | // Has rule already been used in this chain? | ||
| 1047 | if ((sp->n_flag & N_MARK)) | ||
| 1048 | continue; | ||
| 1049 | // Generate a name for an implicit prerequisite | ||
| 1050 | ip = newname(auto_concat(base, psuff)); | ||
| 1051 | if ((ip->n_flag & N_DOING)) | ||
| 1052 | continue; | ||
| 1053 | |||
| 1054 | if (!ip->n_tim.tv_sec) | ||
| 1055 | modtime(ip); | ||
| 1056 | |||
| 1057 | if (!chain) { | ||
| 1058 | got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); | ||
| 1059 | } else { | ||
| 1060 | sp->n_flag |= N_MARK; | ||
| 1061 | got_ip = dyndep(ip, NULL, NULL) != NULL; | ||
| 1062 | sp->n_flag &= ~N_MARK; | ||
| 1063 | } | ||
| 1064 | |||
| 1065 | if (got_ip) { | ||
| 1066 | // Prerequisite exists or we know how to make it | ||
| 1067 | if (infrule) { | ||
| 1068 | dp = NULL; | ||
| 1069 | newdep(&dp, ip); | ||
| 1070 | infrule->r_dep = dp; | ||
| 1071 | infrule->r_cmd = sp->n_rule->r_cmd; | ||
| 1072 | } | ||
| 1073 | return ip; | ||
| 1074 | } | ||
| 1075 | } | ||
| 1076 | } | ||
| 1077 | } | ||
| 1078 | // If we didn't find an existing file or an explicit rule try | ||
| 1079 | // again, this time looking for a chained inference rule. | ||
| 1080 | if (!posix && !chain) { | ||
| 1081 | chain = TRUE; | ||
| 1082 | goto retry; | ||
| 1083 | } | ||
| 1084 | return NULL; | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | /* | ||
| 1088 | * If 'name' ends with 'suffix' return an allocated string containing | ||
| 1089 | * the name with the suffix removed, else return NULL. | ||
| 1090 | */ | ||
| 1091 | static char * | ||
| 1092 | has_suffix(const char *name, const char *suffix) | ||
| 1093 | { | ||
| 1094 | ssize_t delta = strlen(name) - strlen(suffix); | ||
| 1095 | char *base = NULL; | ||
| 1096 | |||
| 1097 | if (delta > 0 && strcmp(name + delta, suffix) == 0) { | ||
| 1098 | base = xstrdup(name); | ||
| 1099 | base[delta] = '\0'; | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | return base; | ||
| 1103 | } | ||
| 1104 | |||
| 1105 | /* | ||
| 1106 | * Dynamic dependency. This routine applies the suffix rules | ||
| 1107 | * to try and find a source and a set of rules for a missing | ||
| 1108 | * target. NULL is returned on failure. On success the name of | ||
| 1109 | * the implicit prerequisite is returned and the rule used is | ||
| 1110 | * placed in the infrule structure provided by the caller. | ||
| 1111 | */ | ||
| 1112 | static struct name * | ||
| 1113 | dyndep(struct name *np, struct rule *infrule, const char **ptsuff) | ||
| 1114 | { | ||
| 1115 | const char *tsuff; | ||
| 1116 | char *base, *name, *member; | ||
| 1117 | struct name *pp = NULL; // Implicit prerequisite | ||
| 1118 | |||
| 1119 | member = NULL; | ||
| 1120 | name = splitlib(np->n_name, &member); | ||
| 1121 | |||
| 1122 | // POSIX only allows inference rules with one or two periods. | ||
| 1123 | // As an extension this restriction is lifted, but not for | ||
| 1124 | // targets of the form lib.a(member.o). | ||
| 1125 | if (!posix && member == NULL) { | ||
| 1126 | struct name *xp = newname(".SUFFIXES"); | ||
| 1127 | int found_suffix = FALSE; | ||
| 1128 | |||
| 1129 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { | ||
| 1130 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 1131 | tsuff = dp->d_name->n_name; | ||
| 1132 | base = has_suffix(name, tsuff); | ||
| 1133 | if (base) { | ||
| 1134 | found_suffix = TRUE; | ||
| 1135 | pp = dyndep0(base, tsuff, infrule); | ||
| 1136 | free(base); | ||
| 1137 | if (pp) { | ||
| 1138 | goto done; | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | } | ||
| 1142 | } | ||
| 1143 | |||
| 1144 | if (!found_suffix) { | ||
| 1145 | // The name didn't have a known suffix. Try single-suffix rule. | ||
| 1146 | tsuff = ""; | ||
| 1147 | pp = dyndep0(name, tsuff, infrule); | ||
| 1148 | if (pp) { | ||
| 1149 | done: | ||
| 1150 | if (ptsuff) { | ||
| 1151 | *ptsuff = tsuff; | ||
| 1152 | } | ||
| 1153 | } | ||
| 1154 | } | ||
| 1155 | } else { | ||
| 1156 | tsuff = xstrdup(suffix(name)); | ||
| 1157 | base = member ? member : name; | ||
| 1158 | *suffix(base) = '\0'; | ||
| 1159 | |||
| 1160 | pp = dyndep0(base, tsuff, infrule); | ||
| 1161 | free((void *)tsuff); | ||
| 1162 | } | ||
| 1163 | free(name); | ||
| 1164 | |||
| 1165 | return pp; | ||
| 1166 | } | ||
| 1167 | |||
| 1168 | #define RULES \ | ||
| 1169 | ".c.o:\n" \ | ||
| 1170 | " $(CC) $(CFLAGS) -c $<\n" \ | ||
| 1171 | ".y.o:\n" \ | ||
| 1172 | " $(YACC) $(YFLAGS) $<\n" \ | ||
| 1173 | " $(CC) $(CFLAGS) -c y.tab.c\n" \ | ||
| 1174 | " rm -f y.tab.c\n" \ | ||
| 1175 | " mv y.tab.o $@\n" \ | ||
| 1176 | ".y.c:\n" \ | ||
| 1177 | " $(YACC) $(YFLAGS) $<\n" \ | ||
| 1178 | " mv y.tab.c $@\n" \ | ||
| 1179 | ".l.o:\n" \ | ||
| 1180 | " $(LEX) $(LFLAGS) $<\n" \ | ||
| 1181 | " $(CC) $(CFLAGS) -c lex.yy.c\n" \ | ||
| 1182 | " rm -f lex.yy.c\n" \ | ||
| 1183 | " mv lex.yy.o $@\n" \ | ||
| 1184 | ".l.c:\n" \ | ||
| 1185 | " $(LEX) $(LFLAGS) $<\n" \ | ||
| 1186 | " mv lex.yy.c $@\n" \ | ||
| 1187 | ".c.a:\n" \ | ||
| 1188 | " $(CC) -c $(CFLAGS) $<\n" \ | ||
| 1189 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ | ||
| 1190 | " rm -f $*.o\n" \ | ||
| 1191 | ".c:\n" \ | ||
| 1192 | " $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<\n" \ | ||
| 1193 | ".sh:\n" \ | ||
| 1194 | " cp $< $@\n" \ | ||
| 1195 | " chmod a+x $@\n" | ||
| 1196 | |||
| 1197 | #define RULES_2017 \ | ||
| 1198 | ".SUFFIXES:.o .c .y .l .a .sh .f\n" \ | ||
| 1199 | ".f.o:\n" \ | ||
| 1200 | " $(FC) $(FFLAGS) -c $<\n" \ | ||
| 1201 | ".f.a:\n" \ | ||
| 1202 | " $(FC) -c $(FFLAGS) $<\n" \ | ||
| 1203 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ | ||
| 1204 | " rm -f $*.o\n" \ | ||
| 1205 | ".f:\n" \ | ||
| 1206 | " $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<\n" | ||
| 1207 | |||
| 1208 | #define RULES_2024 \ | ||
| 1209 | ".SUFFIXES:.o .c .y .l .a .sh\n" | ||
| 1210 | |||
| 1211 | #define MACROS \ | ||
| 1212 | "CFLAGS=-O1\n" \ | ||
| 1213 | "YACC=yacc\n" \ | ||
| 1214 | "YFLAGS=\n" \ | ||
| 1215 | "LEX=lex\n" \ | ||
| 1216 | "LFLAGS=\n" \ | ||
| 1217 | "AR=ar\n" \ | ||
| 1218 | "ARFLAGS=-rv\n" \ | ||
| 1219 | "LDFLAGS=\n" | ||
| 1220 | |||
| 1221 | #define MACROS_2017 \ | ||
| 1222 | "CC=c99\n" \ | ||
| 1223 | "FC=fort77\n" \ | ||
| 1224 | "FFLAGS=-O1\n" \ | ||
| 1225 | |||
| 1226 | #define MACROS_2024 \ | ||
| 1227 | "CC=c17\n" | ||
| 1228 | |||
| 1229 | #define MACROS_EXT \ | ||
| 1230 | "CC=cc\n" | ||
| 1231 | |||
| 1232 | /* | ||
| 1233 | * Read the built-in rules using a fake fgets-like interface. | ||
| 1234 | */ | ||
| 1235 | static char * | ||
| 1236 | getrules(char *s, int size) | ||
| 1237 | { | ||
| 1238 | char *r = s; | ||
| 1239 | |||
| 1240 | if (rulepos == NULL || *rulepos == '\0') { | ||
| 1241 | if (rule_idx == 0) { | ||
| 1242 | rulepos = MACROS; | ||
| 1243 | rule_idx++; | ||
| 1244 | } else if (rule_idx == 1) { | ||
| 1245 | if (POSIX_2017) | ||
| 1246 | rulepos = MACROS_2017; | ||
| 1247 | else if (posix) | ||
| 1248 | rulepos = MACROS_2024; | ||
| 1249 | else | ||
| 1250 | rulepos = MACROS_EXT; | ||
| 1251 | rule_idx++; | ||
| 1252 | } else if (!norules) { | ||
| 1253 | if (rule_idx == 2) { | ||
| 1254 | rulepos = POSIX_2017 ? RULES_2017 : RULES_2024; | ||
| 1255 | rule_idx++; | ||
| 1256 | } else if (rule_idx == 3) { | ||
| 1257 | rulepos = RULES; | ||
| 1258 | rule_idx++; | ||
| 1259 | } | ||
| 1260 | } | ||
| 1261 | } | ||
| 1262 | |||
| 1263 | if (*rulepos == '\0') | ||
| 1264 | return NULL; | ||
| 1265 | |||
| 1266 | while (--size) { | ||
| 1267 | if ((*r++ = *rulepos++) == '\n') | ||
| 1268 | break; | ||
| 1269 | } | ||
| 1270 | *r = '\0'; | ||
| 1271 | return s; | ||
| 1272 | } | ||
| 1273 | |||
| 1274 | /* | ||
| 1275 | * Parse a makefile | ||
| 1276 | */ | ||
| 1277 | |||
| 1278 | /* | ||
| 1279 | * Return a pointer to the next blank-delimited word or NULL if | ||
| 1280 | * there are none left. | ||
| 1281 | */ | ||
| 1282 | static char * | ||
| 1283 | gettok(char **ptr) | ||
| 1284 | { | ||
| 1285 | char *p; | ||
| 1286 | |||
| 1287 | while (isblank(**ptr)) // Skip blanks | ||
| 1288 | (*ptr)++; | ||
| 1289 | |||
| 1290 | if (**ptr == '\0') // Nothing after blanks | ||
| 1291 | return NULL; | ||
| 1292 | |||
| 1293 | p = *ptr; // Word starts here | ||
| 1294 | |||
| 1295 | while (**ptr != '\0' && !isblank(**ptr)) | ||
| 1296 | (*ptr)++; // Find end of word | ||
| 1297 | |||
| 1298 | // Terminate token and move on unless already at end of string | ||
| 1299 | if (**ptr != '\0') | ||
| 1300 | *(*ptr)++ = '\0'; | ||
| 1301 | |||
| 1302 | return(p); | ||
| 1303 | } | ||
| 1304 | |||
| 1305 | /* | ||
| 1306 | * Skip over (possibly adjacent or nested) macro expansions. | ||
| 1307 | */ | ||
| 1308 | static char * | ||
| 1309 | skip_macro(const char *s) | ||
| 1310 | { | ||
| 1311 | while (*s && s[0] == '$') { | ||
| 1312 | if (s[1] == '(' || s[1] == '{') { | ||
| 1313 | char end = *++s == '(' ? ')' : '}'; | ||
| 1314 | while (*s && *s != end) | ||
| 1315 | s = skip_macro(s + 1); | ||
| 1316 | if (*s == end) | ||
| 1317 | ++s; | ||
| 1318 | } else if (s[1] != '\0') { | ||
| 1319 | s += 2; | ||
| 1320 | } else { | ||
| 1321 | break; | ||
| 1322 | } | ||
| 1323 | } | ||
| 1324 | return (char *)s; | ||
| 1325 | } | ||
| 1326 | |||
| 1327 | /* | ||
| 1328 | * Process each whitespace-separated word in the input string: | ||
| 1329 | * | ||
| 1330 | * - replace paths with their directory or filename part | ||
| 1331 | * - replace prefixes and suffixes | ||
| 1332 | * | ||
| 1333 | * Returns an allocated string or NULL if the input is unmodified. | ||
| 1334 | */ | ||
| 1335 | static char * | ||
| 1336 | modify_words(const char *val, int modifier, size_t lenf, size_t lenr, | ||
| 1337 | const char *find_pref, const char *repl_pref, | ||
| 1338 | const char *find_suff, const char *repl_suff) | ||
| 1339 | { | ||
| 1340 | char *s, *copy, *word, *sep, *buf = NULL; | ||
| 1341 | size_t find_pref_len = 0, find_suff_len = 0; | ||
| 1342 | |||
| 1343 | if (!modifier && lenf == 0 && lenr == 0) | ||
| 1344 | return buf; | ||
| 1345 | |||
| 1346 | if (find_pref) { | ||
| 1347 | // get length of find prefix, e.g: src/ | ||
| 1348 | find_pref_len = strlen(find_pref); | ||
| 1349 | // get length of find suffix, e.g: .c | ||
| 1350 | find_suff_len = lenf - find_pref_len - 1; | ||
| 1351 | } | ||
| 1352 | |||
| 1353 | s = copy = xstrdup(val); | ||
| 1354 | while ((word = gettok(&s)) != NULL) { | ||
| 1355 | if (modifier) { | ||
| 1356 | sep = strrchr(word, '/'); | ||
| 1357 | if (modifier == 'D') { | ||
| 1358 | if (!sep) { | ||
| 1359 | word[0] = '.'; // no '/', return "." | ||
| 1360 | sep = word + 1; | ||
| 1361 | } else if (sep == word) { | ||
| 1362 | // '/' at start of word, return "/" | ||
| 1363 | sep = word + 1; | ||
| 1364 | } | ||
| 1365 | // else terminate at separator | ||
| 1366 | *sep = '\0'; | ||
| 1367 | } else if (/* modifier == 'F' && */ sep) { | ||
| 1368 | word = sep + 1; | ||
| 1369 | } | ||
| 1370 | } | ||
| 1371 | if (find_pref != NULL || lenf != 0 || lenr != 0) { | ||
| 1372 | size_t lenw = strlen(word); | ||
| 1373 | // This code implements pattern macro expansions: | ||
| 1374 | // https://austingroupbugs.net/view.php?id=519 | ||
| 1375 | // | ||
| 1376 | // find: <prefix>%<suffix> | ||
| 1377 | // example: src/%.c | ||
| 1378 | // | ||
| 1379 | // For a pattern of the form: | ||
| 1380 | // $(string1:[op]%[os]=[np][%][ns]) | ||
| 1381 | // lenf is the length of [op]%[os]. So lenf >= 1. | ||
| 1382 | if (find_pref != NULL && lenw + 1 >= lenf) { | ||
| 1383 | // If prefix and suffix of word match find_pref and | ||
| 1384 | // find_suff, then do substitution. | ||
| 1385 | if (strncmp(word, find_pref, find_pref_len) == 0 && | ||
| 1386 | strcmp(word + lenw - find_suff_len, find_suff) == 0) { | ||
| 1387 | // replace: <prefix>[%<suffix>] | ||
| 1388 | // example: build/%.o or build/all.o (notice no %) | ||
| 1389 | // If repl_suff is NULL, replace whole word with repl_pref. | ||
| 1390 | if (!repl_suff) { | ||
| 1391 | word = xstrdup(repl_pref); | ||
| 1392 | } else { | ||
| 1393 | word[lenw - find_suff_len] = '\0'; | ||
| 1394 | word = xasprintf("%s%s%s", repl_pref, | ||
| 1395 | word + find_pref_len, repl_suff); | ||
| 1396 | } | ||
| 1397 | word = auto_string(word); | ||
| 1398 | } | ||
| 1399 | } else if (lenw >= lenf && | ||
| 1400 | strcmp(word + lenw - lenf, find_suff) == 0) { | ||
| 1401 | word[lenw - lenf] = '\0'; | ||
| 1402 | word = auto_concat(word, repl_suff); | ||
| 1403 | } | ||
| 1404 | } | ||
| 1405 | buf = xappendword(buf, word); | ||
| 1406 | } | ||
| 1407 | free(copy); | ||
| 1408 | return buf; | ||
| 1409 | } | ||
| 1410 | |||
| 1411 | /* | ||
| 1412 | * Return a pointer to the next instance of a given character. Macro | ||
| 1413 | * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't | ||
| 1414 | * detected as separators in macro definitions. Some other situations | ||
| 1415 | * also require skipping the internals of a macro expansion. | ||
| 1416 | */ | ||
| 1417 | static char * | ||
| 1418 | find_char(const char *str, int c) | ||
| 1419 | { | ||
| 1420 | const char *s; | ||
| 1421 | |||
| 1422 | for (s = skip_macro(str); *s; s = skip_macro(s + 1)) { | ||
| 1423 | if (*s == c) | ||
| 1424 | return (char *)s; | ||
| 1425 | } | ||
| 1426 | return NULL; | ||
| 1427 | } | ||
| 1428 | |||
| 1429 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1430 | /* | ||
| 1431 | * Check for a target rule by searching for a colon that isn't | ||
| 1432 | * part of a Windows path. Return a pointer to the colon or NULL. | ||
| 1433 | */ | ||
| 1434 | static char * | ||
| 1435 | find_colon(char *p) | ||
| 1436 | { | ||
| 1437 | char *q; | ||
| 1438 | |||
| 1439 | for (q = p; (q = strchr(q, ':')); ++q) { | ||
| 1440 | if (posix && !(pragma & P_WINDOWS)) | ||
| 1441 | break; | ||
| 1442 | if (q == p || !isalpha(q[-1]) || q[1] != '/') | ||
| 1443 | break; | ||
| 1444 | } | ||
| 1445 | return q; | ||
| 1446 | } | ||
| 1447 | #else | ||
| 1448 | # define find_colon(s) strchr(s, ':') | ||
| 1449 | #endif | ||
| 1450 | |||
| 1451 | /* | ||
| 1452 | * Recursively expand any macros in str to an allocated string. | ||
| 1453 | */ | ||
| 1454 | static char * | ||
| 1455 | expand_macros(const char *str, int except_dollar) | ||
| 1456 | { | ||
| 1457 | char *exp, *newexp, *s, *t, *p, *q, *name; | ||
| 1458 | char *find, *replace, *modified; | ||
| 1459 | char *expval, *expfind, *find_suff, *repl_suff; | ||
| 1460 | char *find_pref = NULL, *repl_pref = NULL; | ||
| 1461 | size_t lenf, lenr; | ||
| 1462 | char modifier; | ||
| 1463 | struct macro *mp; | ||
| 1464 | |||
| 1465 | exp = xstrdup(str); | ||
| 1466 | for (t = exp; *t; t++) { | ||
| 1467 | if (*t == '$') { | ||
| 1468 | if (t[1] == '\0') { | ||
| 1469 | break; | ||
| 1470 | } | ||
| 1471 | if (t[1] == '$' && except_dollar) { | ||
| 1472 | t++; | ||
| 1473 | continue; | ||
| 1474 | } | ||
| 1475 | // Need to expand a macro. Find its extent (s to t inclusive) | ||
| 1476 | // and take a copy of its content. | ||
| 1477 | s = t; | ||
| 1478 | t++; | ||
| 1479 | if (*t == '{' || *t == '(') { | ||
| 1480 | t = find_char(t, *t == '{' ? '}' : ')'); | ||
| 1481 | if (t == NULL) | ||
| 1482 | error("unterminated variable '%s'", s); | ||
| 1483 | name = xstrndup(s + 2, t - s - 2); | ||
| 1484 | } else { | ||
| 1485 | name = xzalloc(2); | ||
| 1486 | name[0] = *t; | ||
| 1487 | /*name[1] = '\0'; - xzalloc did it */ | ||
| 1488 | } | ||
| 1489 | |||
| 1490 | // Only do suffix replacement or pattern macro expansion | ||
| 1491 | // if both ':' and '=' are found, plus a '%' for the latter. | ||
| 1492 | // Suffix replacement is indicated by | ||
| 1493 | // find_pref == NULL && (lenf != 0 || lenr != 0); | ||
| 1494 | // pattern macro expansion by find_pref != NULL. | ||
| 1495 | expfind = NULL; | ||
| 1496 | find_suff = repl_suff = NULL; | ||
| 1497 | lenf = lenr = 0; | ||
| 1498 | if ((find = find_char(name, ':'))) { | ||
| 1499 | *find++ = '\0'; | ||
| 1500 | expfind = expand_macros(find, FALSE); | ||
| 1501 | if ((replace = find_char(expfind, '='))) { | ||
| 1502 | *replace++ = '\0'; | ||
| 1503 | lenf = strlen(expfind); | ||
| 1504 | if (!POSIX_2017 && (find_suff = strchr(expfind, '%'))) { | ||
| 1505 | find_pref = expfind; | ||
| 1506 | repl_pref = replace; | ||
| 1507 | *find_suff++ = '\0'; | ||
| 1508 | if ((repl_suff = strchr(replace, '%'))) | ||
| 1509 | *repl_suff++ = '\0'; | ||
| 1510 | } else { | ||
| 1511 | if (posix && !(pragma & P_EMPTY_SUFFIX) && lenf == 0) | ||
| 1512 | error("empty suffix%s", | ||
| 1513 | !ENABLE_FEATURE_MAKE_POSIX ? "" : | ||
| 1514 | ": allow with pragma empty_suffix"); | ||
| 1515 | find_suff = expfind; | ||
| 1516 | repl_suff = replace; | ||
| 1517 | lenr = strlen(repl_suff); | ||
| 1518 | } | ||
| 1519 | } | ||
| 1520 | } | ||
| 1521 | |||
| 1522 | p = q = name; | ||
| 1523 | // If not in POSIX mode expand macros in the name. | ||
| 1524 | if (!POSIX_2017) { | ||
| 1525 | char *expname = expand_macros(name, FALSE); | ||
| 1526 | free(name); | ||
| 1527 | name = expname; | ||
| 1528 | } else { | ||
| 1529 | // Skip over nested expansions in name | ||
| 1530 | do { | ||
| 1531 | *q++ = *p; | ||
| 1532 | } while ((p = skip_macro(p + 1)) && *p); | ||
| 1533 | } | ||
| 1534 | |||
| 1535 | // The internal macros support 'D' and 'F' modifiers | ||
| 1536 | modifier = '\0'; | ||
| 1537 | switch (name[0]) { | ||
| 1538 | case '^': | ||
| 1539 | case '+': | ||
| 1540 | if (POSIX_2017) | ||
| 1541 | break; | ||
| 1542 | // fall through | ||
| 1543 | case '@': case '%': case '?': case '<': case '*': | ||
| 1544 | if ((name[1] == 'D' || name[1] == 'F') && name[2] == '\0') { | ||
| 1545 | modifier = name[1]; | ||
| 1546 | name[1] = '\0'; | ||
| 1547 | } | ||
| 1548 | break; | ||
| 1549 | } | ||
| 1550 | |||
| 1551 | modified = NULL; | ||
| 1552 | if ((mp = getmp(name))) { | ||
| 1553 | // Recursive expansion | ||
| 1554 | if (mp->m_flag) | ||
| 1555 | error("recursive macro %s", name); | ||
| 1556 | // Note if we've expanded $(MAKE) | ||
| 1557 | if (strcmp(name, "MAKE") == 0) | ||
| 1558 | opts |= OPT_make; | ||
| 1559 | mp->m_flag = TRUE; | ||
| 1560 | expval = expand_macros(mp->m_val, FALSE); | ||
| 1561 | mp->m_flag = FALSE; | ||
| 1562 | modified = modify_words(expval, modifier, lenf, lenr, | ||
| 1563 | find_pref, repl_pref, find_suff, repl_suff); | ||
| 1564 | if (modified) | ||
| 1565 | free(expval); | ||
| 1566 | else | ||
| 1567 | modified = expval; | ||
| 1568 | } | ||
| 1569 | free(name); | ||
| 1570 | free(expfind); | ||
| 1571 | |||
| 1572 | if (modified && *modified) { | ||
| 1573 | // The text to be replaced by the macro expansion is | ||
| 1574 | // from s to t inclusive. | ||
| 1575 | *s = '\0'; | ||
| 1576 | newexp = xasprintf("%s%s%s", exp, modified, t + 1); | ||
| 1577 | t = newexp + (s - exp) + strlen(modified) - 1; | ||
| 1578 | free(exp); | ||
| 1579 | exp = newexp; | ||
| 1580 | } else { | ||
| 1581 | // Macro wasn't expanded or expanded to nothing. | ||
| 1582 | // Close the space occupied by the macro reference. | ||
| 1583 | q = t + 1; | ||
| 1584 | t = s - 1; | ||
| 1585 | while ((*s++ = *q++)) | ||
| 1586 | continue; | ||
| 1587 | } | ||
| 1588 | free(modified); | ||
| 1589 | } | ||
| 1590 | } | ||
| 1591 | return exp; | ||
| 1592 | } | ||
| 1593 | |||
| 1594 | /* | ||
| 1595 | * Process a non-command line | ||
| 1596 | */ | ||
| 1597 | static void | ||
| 1598 | process_line(char *s) | ||
| 1599 | { | ||
| 1600 | char *t; | ||
| 1601 | |||
| 1602 | // Strip comment | ||
| 1603 | // don't treat '#' in macro expansion as a comment | ||
| 1604 | // nor '#' outside macro expansion preceded by backslash | ||
| 1605 | if (!posix) { | ||
| 1606 | char *u = s; | ||
| 1607 | while ((t = find_char(u, '#')) && t > u && t[-1] == '\\') { | ||
| 1608 | for (u = t; *u; ++u) { | ||
| 1609 | u[-1] = u[0]; | ||
| 1610 | } | ||
| 1611 | *u = '\0'; | ||
| 1612 | u = t; | ||
| 1613 | } | ||
| 1614 | } else | ||
| 1615 | t = strchr(s, '#'); | ||
| 1616 | if (t) | ||
| 1617 | *t = '\0'; | ||
| 1618 | |||
| 1619 | // Replace escaped newline and any leading white space on the | ||
| 1620 | // following line with a single space. Stop processing at a | ||
| 1621 | // non-escaped newline. | ||
| 1622 | for (t = s; *s && *s != '\n'; ) { | ||
| 1623 | if (s[0] == '\\' && s[1] == '\n') { | ||
| 1624 | s += 2; | ||
| 1625 | while (isspace(*s)) | ||
| 1626 | ++s; | ||
| 1627 | *t++ = ' '; | ||
| 1628 | } else { | ||
| 1629 | *t++ = *s++; | ||
| 1630 | } | ||
| 1631 | } | ||
| 1632 | *t = '\0'; | ||
| 1633 | } | ||
| 1634 | |||
| 1635 | enum { | ||
| 1636 | INITIAL = 0, | ||
| 1637 | SKIP_LINE = 1 << 0, | ||
| 1638 | EXPECT_ELSE = 1 << 1, | ||
| 1639 | GOT_MATCH = 1 << 2 | ||
| 1640 | }; | ||
| 1641 | |||
| 1642 | #define IFDEF 0 | ||
| 1643 | #define IFNDEF 1 | ||
| 1644 | #define IFEQ 2 | ||
| 1645 | #define IFNEQ 3 | ||
| 1646 | #define ELSE 0 | ||
| 1647 | #define ENDIF 1 | ||
| 1648 | |||
| 1649 | /* | ||
| 1650 | * Extract strings following ifeq/ifneq and compare them. | ||
| 1651 | * Return -1 on error. | ||
| 1652 | */ | ||
| 1653 | static int | ||
| 1654 | compare_strings(char *arg1) | ||
| 1655 | { | ||
| 1656 | char *arg2, *end, term, *t1, *t2; | ||
| 1657 | int ret; | ||
| 1658 | |||
| 1659 | // Get first string terminator. | ||
| 1660 | if (arg1[0] == '(') | ||
| 1661 | term = ','; | ||
| 1662 | else if (arg1[0] == '"' || arg1[0] == '\'') | ||
| 1663 | term = arg1[0]; | ||
| 1664 | else | ||
| 1665 | return -1; | ||
| 1666 | |||
| 1667 | arg2 = find_char(++arg1, term); | ||
| 1668 | if (arg2 == NULL) | ||
| 1669 | return -1; | ||
| 1670 | *arg2++ = '\0'; | ||
| 1671 | |||
| 1672 | // Get second string terminator. | ||
| 1673 | if (term == ',') { | ||
| 1674 | term = ')'; | ||
| 1675 | } else { | ||
| 1676 | // Skip spaces between quoted strings. | ||
| 1677 | while (isspace(arg2[0])) | ||
| 1678 | arg2++; | ||
| 1679 | if (arg2[0] == '"' || arg2[0] == '\'') | ||
| 1680 | term = arg2[0]; | ||
| 1681 | else | ||
| 1682 | return -1; | ||
| 1683 | ++arg2; | ||
| 1684 | } | ||
| 1685 | |||
| 1686 | end = find_char(arg2, term); | ||
| 1687 | if (end == NULL) | ||
| 1688 | return -1; | ||
| 1689 | *end++ = '\0'; | ||
| 1690 | |||
| 1691 | if (gettok(&end) != NULL) { | ||
| 1692 | warning("unexpected text"); | ||
| 1693 | } | ||
| 1694 | |||
| 1695 | t1 = expand_macros(arg1, FALSE); | ||
| 1696 | t2 = expand_macros(arg2, FALSE); | ||
| 1697 | |||
| 1698 | ret = strcmp(t1, t2) == 0; | ||
| 1699 | free(t1); | ||
| 1700 | free(t2); | ||
| 1701 | return ret; | ||
| 1702 | } | ||
| 1703 | |||
| 1704 | /* | ||
| 1705 | * Process conditional directives and return TRUE if the current line | ||
| 1706 | * should be skipped. | ||
| 1707 | */ | ||
| 1708 | static int | ||
| 1709 | skip_line(const char *str1) | ||
| 1710 | { | ||
| 1711 | char *copy, *q, *token; | ||
| 1712 | bool new_level = TRUE; | ||
| 1713 | // Default is to return skip flag for current level | ||
| 1714 | int ret = cstate[clevel] & SKIP_LINE; | ||
| 1715 | int key; | ||
| 1716 | |||
| 1717 | q = copy = xstrdup(str1); | ||
| 1718 | process_line(copy); | ||
| 1719 | if ((token = gettok(&q)) != NULL) { | ||
| 1720 | switch (index_in_strings("else\0endif\0", token)) { | ||
| 1721 | case ENDIF: | ||
| 1722 | if (gettok(&q) != NULL) | ||
| 1723 | error_unexpected("text"); | ||
| 1724 | if (clevel == 0) | ||
| 1725 | error_unexpected(token); | ||
| 1726 | --clevel; | ||
| 1727 | ret = TRUE; | ||
| 1728 | goto end; | ||
| 1729 | case ELSE: | ||
| 1730 | if (!(cstate[clevel] & EXPECT_ELSE)) | ||
| 1731 | error_unexpected(token); | ||
| 1732 | |||
| 1733 | // If an earlier condition matched we'll now skip lines. | ||
| 1734 | // If not we don't, though an 'else if' may override this. | ||
| 1735 | if ((cstate[clevel] & GOT_MATCH)) | ||
| 1736 | cstate[clevel] |= SKIP_LINE; | ||
| 1737 | else | ||
| 1738 | cstate[clevel] &= ~SKIP_LINE; | ||
| 1739 | |||
| 1740 | token = gettok(&q); | ||
| 1741 | if (token == NULL) { | ||
| 1742 | // Simple else with no conditional directive | ||
| 1743 | cstate[clevel] &= ~EXPECT_ELSE; | ||
| 1744 | ret = TRUE; | ||
| 1745 | goto end; | ||
| 1746 | } else { | ||
| 1747 | // A conditional directive is now required ('else if'). | ||
| 1748 | new_level = FALSE; | ||
| 1749 | } | ||
| 1750 | } | ||
| 1751 | |||
| 1752 | key = index_in_strings("ifdef\0ifndef\0ifeq\0ifneq\0", token); | ||
| 1753 | if (key != -1) { | ||
| 1754 | int match; | ||
| 1755 | |||
| 1756 | if (key == IFDEF || key == IFNDEF) { | ||
| 1757 | // ifdef/ifndef: find out if macro is defined. | ||
| 1758 | char *name = gettok(&q); | ||
| 1759 | if (name != NULL && gettok(&q) == NULL) { | ||
| 1760 | char *t = expand_macros(name, FALSE); | ||
| 1761 | struct macro *mp = getmp(t); | ||
| 1762 | match = mp != NULL && mp->m_val[0] != '\0'; | ||
| 1763 | free(t); | ||
| 1764 | } else { | ||
| 1765 | match = -1; | ||
| 1766 | } | ||
| 1767 | } else { | ||
| 1768 | // ifeq/ifneq: compare strings. | ||
| 1769 | match = compare_strings(q); | ||
| 1770 | } | ||
| 1771 | |||
| 1772 | if (match >= 0) { | ||
| 1773 | if (new_level) { | ||
| 1774 | // Start a new level. | ||
| 1775 | if (clevel == IF_MAX) | ||
| 1776 | error("nesting too deep"); | ||
| 1777 | ++clevel; | ||
| 1778 | cstate[clevel] = EXPECT_ELSE | SKIP_LINE; | ||
| 1779 | // If we were skipping lines at the previous level | ||
| 1780 | // we need to continue doing that unconditionally | ||
| 1781 | // at the new level. | ||
| 1782 | if ((cstate[clevel - 1] & SKIP_LINE)) | ||
| 1783 | cstate[clevel] |= GOT_MATCH; | ||
| 1784 | } | ||
| 1785 | |||
| 1786 | if (!(cstate[clevel] & GOT_MATCH)) { | ||
| 1787 | if (key == IFNDEF || key == IFNEQ) | ||
| 1788 | match = !match; | ||
| 1789 | if (match) { | ||
| 1790 | cstate[clevel] &= ~SKIP_LINE; | ||
| 1791 | cstate[clevel] |= GOT_MATCH; | ||
| 1792 | } | ||
| 1793 | } | ||
| 1794 | } else { | ||
| 1795 | error("invalid condition"); | ||
| 1796 | } | ||
| 1797 | ret = TRUE; | ||
| 1798 | } else if (!new_level) { | ||
| 1799 | error("missing conditional"); | ||
| 1800 | } | ||
| 1801 | } | ||
| 1802 | end: | ||
| 1803 | free(copy); | ||
| 1804 | return ret; | ||
| 1805 | } | ||
| 1806 | |||
| 1807 | /* | ||
| 1808 | * If fd is NULL read the built-in rules. Otherwise read from the | ||
| 1809 | * specified file descriptor. | ||
| 1810 | */ | ||
| 1811 | static char * | ||
| 1812 | make_fgets(char *s, int size, FILE *fd) | ||
| 1813 | { | ||
| 1814 | return fd ? fgets(s, size, fd) : getrules(s, size); | ||
| 1815 | } | ||
| 1816 | |||
| 1817 | /* | ||
| 1818 | * Read a newline-terminated line into an allocated string. | ||
| 1819 | * Backslash-escaped newlines don't terminate the line. | ||
| 1820 | * Ignore comment lines. Return NULL on EOF. | ||
| 1821 | */ | ||
| 1822 | static char * | ||
| 1823 | readline(FILE *fd, int want_command) | ||
| 1824 | { | ||
| 1825 | char *p, *str = NULL; | ||
| 1826 | int pos = 0; | ||
| 1827 | int len = 0; | ||
| 1828 | |||
| 1829 | for (;;) { | ||
| 1830 | // We need room for at least one character and a NUL terminator | ||
| 1831 | if (len - pos > 1 && | ||
| 1832 | make_fgets(str + pos, len - pos, fd) == NULL) { | ||
| 1833 | if (pos) | ||
| 1834 | return str; | ||
| 1835 | free(str); | ||
| 1836 | return NULL; // EOF | ||
| 1837 | } | ||
| 1838 | |||
| 1839 | if (len - pos < 2 || (p = strchr(str + pos, '\n')) == NULL) { | ||
| 1840 | // Need more room | ||
| 1841 | if (len) | ||
| 1842 | pos = len - 1; | ||
| 1843 | len += 256; | ||
| 1844 | str = xrealloc(str, len); | ||
| 1845 | continue; | ||
| 1846 | } | ||
| 1847 | lineno++; | ||
| 1848 | |||
| 1849 | #if ENABLE_PLATFORM_MINGW32 | ||
| 1850 | // Remove CR before LF | ||
| 1851 | if (p != str && p[-1] == '\r') { | ||
| 1852 | p[-1] = '\n'; | ||
| 1853 | *p-- = '\0'; | ||
| 1854 | } | ||
| 1855 | #endif | ||
| 1856 | // Keep going if newline has been escaped | ||
| 1857 | if (p != str && p[-1] == '\\') { | ||
| 1858 | pos = p - str + 1; | ||
| 1859 | continue; | ||
| 1860 | } | ||
| 1861 | dispno = lineno; | ||
| 1862 | |||
| 1863 | // Check for lines that are conditionally skipped. | ||
| 1864 | if (posix || !skip_line(str)) { | ||
| 1865 | if (want_command && *str == '\t') | ||
| 1866 | return str; | ||
| 1867 | |||
| 1868 | // Check for comment lines | ||
| 1869 | p = str; | ||
| 1870 | while (isblank(*p)) | ||
| 1871 | p++; | ||
| 1872 | |||
| 1873 | if (*p != '\n' && (posix ? *str != '#' : *p != '#')) | ||
| 1874 | return str; | ||
| 1875 | } | ||
| 1876 | |||
| 1877 | pos = 0; | ||
| 1878 | } | ||
| 1879 | } | ||
| 1880 | |||
| 1881 | /* | ||
| 1882 | * Return a pointer to the suffix name if the argument is a known suffix | ||
| 1883 | * or NULL if it isn't. | ||
| 1884 | */ | ||
| 1885 | static const char * | ||
| 1886 | is_suffix(const char *s) | ||
| 1887 | { | ||
| 1888 | struct name *np; | ||
| 1889 | struct rule *rp; | ||
| 1890 | struct depend *dp; | ||
| 1891 | |||
| 1892 | np = newname(".SUFFIXES"); | ||
| 1893 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 1894 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 1895 | if (strcmp(s, dp->d_name->n_name) == 0) { | ||
| 1896 | return dp->d_name->n_name; | ||
| 1897 | } | ||
| 1898 | } | ||
| 1899 | } | ||
| 1900 | return NULL; | ||
| 1901 | } | ||
| 1902 | |||
| 1903 | /* | ||
| 1904 | * Return TRUE if the argument is formed by concatenating two | ||
| 1905 | * known suffixes. | ||
| 1906 | */ | ||
| 1907 | static int | ||
| 1908 | is_inference_target(const char *s) | ||
| 1909 | { | ||
| 1910 | struct name *np; | ||
| 1911 | struct rule *rp1, *rp2; | ||
| 1912 | struct depend *dp1, *dp2; | ||
| 1913 | |||
| 1914 | np = newname(".SUFFIXES"); | ||
| 1915 | for (rp1 = np->n_rule; rp1; rp1 = rp1->r_next) { | ||
| 1916 | for (dp1 = rp1->r_dep; dp1; dp1 = dp1->d_next) { | ||
| 1917 | const char *suff1 = dp1->d_name->n_name; | ||
| 1918 | size_t len = strlen(suff1); | ||
| 1919 | |||
| 1920 | if (strncmp(s, suff1, len) == 0) { | ||
| 1921 | for (rp2 = np->n_rule; rp2; rp2 = rp2->r_next) { | ||
| 1922 | for (dp2 = rp2->r_dep; dp2; dp2 = dp2->d_next) { | ||
| 1923 | const char *suff2 = dp2->d_name->n_name; | ||
| 1924 | if (strcmp(s + len, suff2) == 0) { | ||
| 1925 | return TRUE; | ||
| 1926 | } | ||
| 1927 | } | ||
| 1928 | } | ||
| 1929 | } | ||
| 1930 | } | ||
| 1931 | } | ||
| 1932 | return FALSE; | ||
| 1933 | } | ||
| 1934 | |||
| 1935 | enum { | ||
| 1936 | T_NORMAL = 0, | ||
| 1937 | T_SPECIAL = (1 << 0), | ||
| 1938 | T_INFERENCE = (1 << 1), // Inference rule | ||
| 1939 | T_NOPREREQ = (1 << 2), // If set must not have prerequisites | ||
| 1940 | T_COMMAND = (1 << 3), // If set must have commands, if unset must not | ||
| 1941 | }; | ||
| 1942 | |||
| 1943 | /* | ||
| 1944 | * Determine if the argument is a special target and return a set | ||
| 1945 | * of flags indicating its properties. | ||
| 1946 | */ | ||
| 1947 | static int | ||
| 1948 | target_type(char *s) | ||
| 1949 | { | ||
| 1950 | int ret; | ||
| 1951 | static const char *s_name = | ||
| 1952 | ".DEFAULT\0" | ||
| 1953 | ".POSIX\0" | ||
| 1954 | ".IGNORE\0" | ||
| 1955 | ".PRECIOUS\0" | ||
| 1956 | ".SILENT\0" | ||
| 1957 | ".SUFFIXES\0" | ||
| 1958 | ".PHONY\0" | ||
| 1959 | ".NOTPARALLEL\0" | ||
| 1960 | ".WAIT\0" | ||
| 1961 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 1962 | ".PRAGMA\0" | ||
| 1963 | #endif | ||
| 1964 | ; | ||
| 1965 | |||
| 1966 | static const uint8_t s_type[] = { | ||
| 1967 | T_SPECIAL | T_NOPREREQ | T_COMMAND, | ||
| 1968 | T_SPECIAL | T_NOPREREQ, | ||
| 1969 | T_SPECIAL, | ||
| 1970 | T_SPECIAL, | ||
| 1971 | T_SPECIAL, | ||
| 1972 | T_SPECIAL, | ||
| 1973 | T_SPECIAL, | ||
| 1974 | T_SPECIAL | T_NOPREREQ, | ||
| 1975 | T_SPECIAL | T_NOPREREQ, | ||
| 1976 | T_SPECIAL, | ||
| 1977 | }; | ||
| 1978 | |||
| 1979 | // Check for one of the known special targets | ||
| 1980 | ret = index_in_strings(s_name, s); | ||
| 1981 | if (ret >= 0) | ||
| 1982 | return s_type[ret]; | ||
| 1983 | |||
| 1984 | // Check for an inference rule | ||
| 1985 | ret = T_NORMAL; | ||
| 1986 | if (!posix) { | ||
| 1987 | if (is_suffix(s) || is_inference_target(s)) { | ||
| 1988 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | ||
| 1989 | } | ||
| 1990 | } else { | ||
| 1991 | // In POSIX inference rule targets must contain one or two dots | ||
| 1992 | char *sfx = suffix(s); | ||
| 1993 | if (*s == '.' && is_suffix(sfx)) { | ||
| 1994 | if (s == sfx) { // Single suffix rule | ||
| 1995 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | ||
| 1996 | } else { | ||
| 1997 | // Suffix is valid, check that prefix is too | ||
| 1998 | *sfx = '\0'; | ||
| 1999 | if (is_suffix(s)) | ||
| 2000 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | ||
| 2001 | *sfx = '.'; | ||
| 2002 | } | ||
| 2003 | } | ||
| 2004 | } | ||
| 2005 | return ret; | ||
| 2006 | } | ||
| 2007 | |||
| 2008 | static int | ||
| 2009 | ends_with_bracket(const char *s) | ||
| 2010 | { | ||
| 2011 | return last_char_is(s, ')') != NULL; | ||
| 2012 | } | ||
| 2013 | |||
| 2014 | /* | ||
| 2015 | * Process a command line | ||
| 2016 | */ | ||
| 2017 | static char * | ||
| 2018 | process_command(char *s) | ||
| 2019 | { | ||
| 2020 | char *t, *u; | ||
| 2021 | int len; | ||
| 2022 | char *outside; | ||
| 2023 | |||
| 2024 | if (!(pragma & P_COMMAND_COMMENT) && posix) { | ||
| 2025 | // POSIX strips comments from command lines | ||
| 2026 | t = strchr(s, '#'); | ||
| 2027 | if (t) { | ||
| 2028 | *t = '\0'; | ||
| 2029 | warning("comment in command removed: keep with pragma command_comment"); | ||
| 2030 | } | ||
| 2031 | } | ||
| 2032 | |||
| 2033 | len = strlen(s) + 1; | ||
| 2034 | outside = xzalloc(len); | ||
| 2035 | for (t = skip_macro(s); *t; t = skip_macro(t + 1)) { | ||
| 2036 | outside[t - s] = 1; | ||
| 2037 | } | ||
| 2038 | |||
| 2039 | // Process escaped newlines. Stop at first non-escaped newline. | ||
| 2040 | for (t = u = s; *u && *u != '\n'; ) { | ||
| 2041 | if (u[0] == '\\' && u[1] == '\n') { | ||
| 2042 | if (POSIX_2017 || outside[u - s]) { | ||
| 2043 | // Outside macro: remove tab following escaped newline. | ||
| 2044 | *t++ = *u++; | ||
| 2045 | *t++ = *u++; | ||
| 2046 | u += (*u == '\t'); | ||
| 2047 | } else { | ||
| 2048 | // Inside macro: replace escaped newline and any leading | ||
| 2049 | // whitespace on the following line with a single space. | ||
| 2050 | u += 2; | ||
| 2051 | while (isspace(*u)) | ||
| 2052 | ++u; | ||
| 2053 | *t++ = ' '; | ||
| 2054 | } | ||
| 2055 | } else { | ||
| 2056 | *t++ = *u++; | ||
| 2057 | } | ||
| 2058 | } | ||
| 2059 | *t = '\0'; | ||
| 2060 | free(outside); | ||
| 2061 | return s; | ||
| 2062 | } | ||
| 2063 | |||
| 2064 | static char * | ||
| 2065 | run_command(const char *cmd) | ||
| 2066 | { | ||
| 2067 | FILE *fd; | ||
| 2068 | char *s, *val = NULL; | ||
| 2069 | char buf[256]; | ||
| 2070 | size_t len = 0, nread; | ||
| 2071 | |||
| 2072 | if ((fd = popen(cmd, "r")) == NULL) | ||
| 2073 | return val; | ||
| 2074 | |||
| 2075 | for (;;) { | ||
| 2076 | nread = fread(buf, 1, sizeof(buf), fd); | ||
| 2077 | if (nread == 0) | ||
| 2078 | break; | ||
| 2079 | |||
| 2080 | val = xrealloc(val, len + nread + 1); | ||
| 2081 | memcpy(val + len, buf, nread); | ||
| 2082 | len += nread; | ||
| 2083 | val[len] = '\0'; | ||
| 2084 | } | ||
| 2085 | pclose(fd); | ||
| 2086 | |||
| 2087 | if (val == NULL) | ||
| 2088 | return NULL; | ||
| 2089 | |||
| 2090 | // Strip leading whitespace in POSIX 2024 mode | ||
| 2091 | if (posix) { | ||
| 2092 | s = val; | ||
| 2093 | while (isspace(*s)) { | ||
| 2094 | ++s; | ||
| 2095 | --len; | ||
| 2096 | } | ||
| 2097 | |||
| 2098 | if (len == 0) { | ||
| 2099 | free(val); | ||
| 2100 | return NULL; | ||
| 2101 | } | ||
| 2102 | memmove(val, s, len + 1); | ||
| 2103 | } | ||
| 2104 | |||
| 2105 | #if ENABLE_PLATFORM_MINGW32 | ||
| 2106 | len = remove_cr(val, len + 1) - 1; | ||
| 2107 | if (len == 0) { | ||
| 2108 | free(val); | ||
| 2109 | return NULL; | ||
| 2110 | } | ||
| 2111 | #endif | ||
| 2112 | |||
| 2113 | // Remove one newline from the end (BSD compatibility) | ||
| 2114 | if (val[len - 1] == '\n') | ||
| 2115 | val[len - 1] = '\0'; | ||
| 2116 | // Other newlines are changed to spaces | ||
| 2117 | for (s = val; *s; ++s) { | ||
| 2118 | if (*s == '\n') | ||
| 2119 | *s = ' '; | ||
| 2120 | } | ||
| 2121 | return val; | ||
| 2122 | } | ||
| 2123 | |||
| 2124 | /* | ||
| 2125 | * Check for an unescaped wildcard character | ||
| 2126 | */ | ||
| 2127 | static int wildchar(const char *p) | ||
| 2128 | { | ||
| 2129 | while (*p) { | ||
| 2130 | switch (*p) { | ||
| 2131 | case '?': | ||
| 2132 | case '*': | ||
| 2133 | case '[': | ||
| 2134 | return 1; | ||
| 2135 | case '\\': | ||
| 2136 | if (p[1] != '\0') | ||
| 2137 | ++p; | ||
| 2138 | break; | ||
| 2139 | } | ||
| 2140 | ++p; | ||
| 2141 | } | ||
| 2142 | return 0; | ||
| 2143 | } | ||
| 2144 | |||
| 2145 | /* | ||
| 2146 | * Expand any wildcards in a pattern. Return TRUE if a match is | ||
| 2147 | * found, in which case the caller should call globfree() on the | ||
| 2148 | * glob_t structure. | ||
| 2149 | */ | ||
| 2150 | static int | ||
| 2151 | wildcard(char *p, glob_t *gd) | ||
| 2152 | { | ||
| 2153 | int ret; | ||
| 2154 | char *s; | ||
| 2155 | |||
| 2156 | // Don't call glob() if there are no wildcards. | ||
| 2157 | if (!wildchar(p)) { | ||
| 2158 | nomatch: | ||
| 2159 | // Remove backslashes from the name. | ||
| 2160 | for (s = p; *p; ++p) { | ||
| 2161 | if (*p == '\\' && p[1] != '\0') | ||
| 2162 | continue; | ||
| 2163 | *s++ = *p; | ||
| 2164 | } | ||
| 2165 | *s = '\0'; | ||
| 2166 | return 0; | ||
| 2167 | } | ||
| 2168 | |||
| 2169 | memset(gd, 0, sizeof(*gd)); | ||
| 2170 | ret = glob(p, GLOB_NOSORT, NULL, gd); | ||
| 2171 | if (ret == GLOB_NOMATCH) { | ||
| 2172 | globfree(gd); | ||
| 2173 | goto nomatch; | ||
| 2174 | } else if (ret != 0) { | ||
| 2175 | error("glob error for '%s'", p); | ||
| 2176 | } | ||
| 2177 | return 1; | ||
| 2178 | } | ||
| 2179 | |||
| 2180 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 2181 | static void | ||
| 2182 | pragmas_from_env(void) | ||
| 2183 | { | ||
| 2184 | char *p, *q, *var; | ||
| 2185 | const char *env = getenv("PDPMAKE_PRAGMAS"); | ||
| 2186 | |||
| 2187 | if (env == NULL) | ||
| 2188 | return; | ||
| 2189 | |||
| 2190 | q = var = xstrdup(env); | ||
| 2191 | while ((p = gettok(&q)) != NULL) | ||
| 2192 | set_pragma(p); | ||
| 2193 | free(var); | ||
| 2194 | } | ||
| 2195 | #endif | ||
| 2196 | |||
| 2197 | /* | ||
| 2198 | * Determine if a line is a target rule with an inline command. | ||
| 2199 | * Return a pointer to the semicolon separator if it is, else NULL. | ||
| 2200 | */ | ||
| 2201 | static char * | ||
| 2202 | inline_command(char *line) | ||
| 2203 | { | ||
| 2204 | char *p = find_char(line, ':'); | ||
| 2205 | |||
| 2206 | if (p) | ||
| 2207 | p = strchr(p, ';'); | ||
| 2208 | return p; | ||
| 2209 | } | ||
| 2210 | |||
| 2211 | /* | ||
| 2212 | * Parse input from the makefile and construct a tree structure of it. | ||
| 2213 | */ | ||
| 2214 | static void | ||
| 2215 | input(FILE *fd, int ilevel) | ||
| 2216 | { | ||
| 2217 | char *p, *q, *s, *a, *str, *expanded, *copy; | ||
| 2218 | char *str1, *str2; | ||
| 2219 | struct name *np; | ||
| 2220 | struct depend *dp; | ||
| 2221 | struct cmd *cp; | ||
| 2222 | int startno, count; | ||
| 2223 | bool semicolon_cmd, seen_inference; | ||
| 2224 | uint8_t old_clevel = clevel; | ||
| 2225 | bool dbl; | ||
| 2226 | char *lib = NULL; | ||
| 2227 | glob_t gd; | ||
| 2228 | int nfile, i; | ||
| 2229 | char **files; | ||
| 2230 | bool minus; | ||
| 2231 | |||
| 2232 | lineno = 0; | ||
| 2233 | str1 = readline(fd, FALSE); | ||
| 2234 | while (str1) { | ||
| 2235 | str2 = NULL; | ||
| 2236 | |||
| 2237 | // Newlines and comments are handled differently in command lines | ||
| 2238 | // and other types of line. Take a copy of the current line before | ||
| 2239 | // processing it as a non-command line in case it contains a | ||
| 2240 | // rule with a command line. That is, a line of the form: | ||
| 2241 | // | ||
| 2242 | // target: prereq; command | ||
| 2243 | // | ||
| 2244 | copy = xstrdup(str1); | ||
| 2245 | process_line(str1); | ||
| 2246 | str = str1; | ||
| 2247 | |||
| 2248 | // Check for an include line | ||
| 2249 | if (!posix) | ||
| 2250 | while (isblank(*str)) | ||
| 2251 | ++str; | ||
| 2252 | minus = !POSIX_2017 && *str == '-'; | ||
| 2253 | p = str + minus; | ||
| 2254 | if (strncmp(p, "include", 7) == 0 && isblank(p[7])) { | ||
| 2255 | const char *old_makefile = makefile; | ||
| 2256 | int old_lineno = lineno; | ||
| 2257 | |||
| 2258 | if (ilevel > 16) | ||
| 2259 | error("too many includes"); | ||
| 2260 | |||
| 2261 | count = 0; | ||
| 2262 | q = expanded = expand_macros(p + 7, FALSE); | ||
| 2263 | while ((p = gettok(&q)) != NULL) { | ||
| 2264 | FILE *ifd; | ||
| 2265 | |||
| 2266 | ++count; | ||
| 2267 | if (!POSIX_2017) { | ||
| 2268 | // Try to create include file or bring it up-to-date | ||
| 2269 | opts |= OPT_include; | ||
| 2270 | make(newname(p), 1); | ||
| 2271 | opts &= ~OPT_include; | ||
| 2272 | } | ||
| 2273 | if ((ifd = fopen(p, "r")) == NULL) { | ||
| 2274 | if (!minus) | ||
| 2275 | error("can't open include file '%s'", p); | ||
| 2276 | } else { | ||
| 2277 | makefile = p; | ||
| 2278 | input(ifd, ilevel + 1); | ||
| 2279 | fclose(ifd); | ||
| 2280 | makefile = old_makefile; | ||
| 2281 | lineno = old_lineno; | ||
| 2282 | } | ||
| 2283 | if (POSIX_2017) | ||
| 2284 | break; | ||
| 2285 | } | ||
| 2286 | if (POSIX_2017) { | ||
| 2287 | // In POSIX 2017 zero or more than one include file is | ||
| 2288 | // unspecified behaviour. | ||
| 2289 | if (p == NULL || gettok(&q)) { | ||
| 2290 | error("one include file per line"); | ||
| 2291 | } | ||
| 2292 | } else if (count == 0) { | ||
| 2293 | // In POSIX 2024 no include file is unspecified behaviour. | ||
| 2294 | if (posix) | ||
| 2295 | error("no include file"); | ||
| 2296 | } | ||
| 2297 | goto end_loop; | ||
| 2298 | } | ||
| 2299 | |||
| 2300 | // Check for a macro definition | ||
| 2301 | str = str1; | ||
| 2302 | // POSIX 2024 seems to allow a tab as the first character of | ||
| 2303 | // a macro definition, though most implementations don't. | ||
| 2304 | if (POSIX_2017 && *str == '\t') | ||
| 2305 | error("command not allowed here"); | ||
| 2306 | if (find_char(str, '=') != NULL) { | ||
| 2307 | int level = (useenv || fd == NULL) ? 4 : 3; | ||
| 2308 | // Use a copy of the line: we might need the original | ||
| 2309 | // if this turns out to be a target rule. | ||
| 2310 | char *copy2 = xstrdup(str); | ||
| 2311 | char *newq = NULL; | ||
| 2312 | char eq = '\0'; | ||
| 2313 | q = find_char(copy2, '='); // q can't be NULL | ||
| 2314 | |||
| 2315 | if (q - 1 > copy2) { | ||
| 2316 | switch (q[-1]) { | ||
| 2317 | case ':': | ||
| 2318 | // '::=' and ':::=' are from POSIX 2024. | ||
| 2319 | if (!POSIX_2017 && q - 2 > copy2 && q[-2] == ':') { | ||
| 2320 | if (q - 3 > copy2 && q[-3] == ':') { | ||
| 2321 | eq = 'B'; // BSD-style ':=' | ||
| 2322 | q[-3] = '\0'; | ||
| 2323 | } else { | ||
| 2324 | eq = ':'; // GNU-style ':=' | ||
| 2325 | q[-2] = '\0'; | ||
| 2326 | } | ||
| 2327 | break; | ||
| 2328 | } | ||
| 2329 | // ':=' is a non-POSIX extension. | ||
| 2330 | if (posix) | ||
| 2331 | break; | ||
| 2332 | goto set_eq; | ||
| 2333 | case '+': | ||
| 2334 | case '?': | ||
| 2335 | case '!': | ||
| 2336 | // '+=', '?=' and '!=' are from POSIX 2024. | ||
| 2337 | if (POSIX_2017) | ||
| 2338 | break; | ||
| 2339 | set_eq: | ||
| 2340 | eq = q[-1]; | ||
| 2341 | q[-1] = '\0'; | ||
| 2342 | break; | ||
| 2343 | } | ||
| 2344 | } | ||
| 2345 | *q++ = '\0'; // Separate name and value | ||
| 2346 | while (isblank(*q)) | ||
| 2347 | q++; | ||
| 2348 | if ((p = strrchr(q, '\n')) != NULL) | ||
| 2349 | *p = '\0'; | ||
| 2350 | |||
| 2351 | // Expand left-hand side of assignment | ||
| 2352 | p = expanded = expand_macros(copy2, FALSE); | ||
| 2353 | if ((a = gettok(&p)) == NULL) | ||
| 2354 | error("invalid macro assignment"); | ||
| 2355 | |||
| 2356 | // If the expanded LHS contains ':' and ';' it can't be a | ||
| 2357 | // macro assignment but it might be a target rule. | ||
| 2358 | if ((s = strchr(a, ':')) != NULL && strchr(s, ';') != NULL) { | ||
| 2359 | free(expanded); | ||
| 2360 | free(copy2); | ||
| 2361 | goto try_target; | ||
| 2362 | } | ||
| 2363 | |||
| 2364 | if (gettok(&p)) | ||
| 2365 | error("invalid macro assignment"); | ||
| 2366 | |||
| 2367 | if (eq == ':') { | ||
| 2368 | // GNU-style ':='. Expand right-hand side of assignment. | ||
| 2369 | // Macro is of type immediate-expansion. | ||
| 2370 | q = newq = expand_macros(q, FALSE); | ||
| 2371 | level |= M_IMMEDIATE; | ||
| 2372 | } | ||
| 2373 | else if (eq == 'B') { | ||
| 2374 | // BSD-style ':='. Expand right-hand side of assignment, | ||
| 2375 | // though not '$$'. Macro is of type delayed-expansion. | ||
| 2376 | q = newq = expand_macros(q, TRUE); | ||
| 2377 | } else if (eq == '?' && getmp(a) != NULL) { | ||
| 2378 | // Skip assignment if macro is already set | ||
| 2379 | goto end_loop; | ||
| 2380 | } else if (eq == '+') { | ||
| 2381 | // Append to current value | ||
| 2382 | struct macro *mp = getmp(a); | ||
| 2383 | char *rhs; | ||
| 2384 | newq = mp && mp->m_val[0] ? xstrdup(mp->m_val) : NULL; | ||
| 2385 | if (mp && mp->m_immediate) { | ||
| 2386 | // Expand right-hand side of assignment (GNU make | ||
| 2387 | // compatibility) | ||
| 2388 | rhs = expand_macros(q, FALSE); | ||
| 2389 | level |= M_IMMEDIATE; | ||
| 2390 | } else { | ||
| 2391 | rhs = q; | ||
| 2392 | } | ||
| 2393 | newq = xappendword(newq, rhs); | ||
| 2394 | if (rhs != q) | ||
| 2395 | free(rhs); | ||
| 2396 | q = newq; | ||
| 2397 | } else if (eq == '!') { | ||
| 2398 | char *cmd = expand_macros(q, FALSE); | ||
| 2399 | q = newq = run_command(cmd); | ||
| 2400 | free(cmd); | ||
| 2401 | } | ||
| 2402 | setmacro(a, q, level); | ||
| 2403 | free(newq); | ||
| 2404 | free(copy2); | ||
| 2405 | goto end_loop; | ||
| 2406 | } | ||
| 2407 | |||
| 2408 | // If we get here it must be a target rule | ||
| 2409 | try_target: | ||
| 2410 | if (*str == '\t') // Command without target | ||
| 2411 | error("command not allowed here"); | ||
| 2412 | p = expanded = expand_macros(str, FALSE); | ||
| 2413 | |||
| 2414 | // Look for colon separator | ||
| 2415 | q = find_colon(p); | ||
| 2416 | if (q == NULL) | ||
| 2417 | error("expected separator"); | ||
| 2418 | |||
| 2419 | *q++ = '\0'; // Separate targets and prerequisites | ||
| 2420 | |||
| 2421 | // Double colon | ||
| 2422 | dbl = !posix && *q == ':'; | ||
| 2423 | if (dbl) | ||
| 2424 | q++; | ||
| 2425 | |||
| 2426 | // Look for semicolon separator | ||
| 2427 | cp = NULL; | ||
| 2428 | s = strchr(q, ';'); | ||
| 2429 | if (s) { | ||
| 2430 | // Retrieve command from original or expanded copy of line | ||
| 2431 | char *copy3 = expand_macros(copy, FALSE); | ||
| 2432 | if ((p = inline_command(copy)) || (p = inline_command(copy3))) | ||
| 2433 | newcmd(&cp, process_command(p + 1)); | ||
| 2434 | free(copy3); | ||
| 2435 | *s = '\0'; | ||
| 2436 | } | ||
| 2437 | semicolon_cmd = cp != NULL && cp->c_cmd[0] != '\0'; | ||
| 2438 | |||
| 2439 | // Create list of prerequisites | ||
| 2440 | dp = NULL; | ||
| 2441 | while (((p = gettok(&q)) != NULL)) { | ||
| 2442 | char *newp = NULL; | ||
| 2443 | |||
| 2444 | if (!posix) { | ||
| 2445 | // Allow prerequisites of form library(member1 member2). | ||
| 2446 | // Leading and trailing spaces in the brackets are skipped. | ||
| 2447 | if (!lib) { | ||
| 2448 | s = strchr(p, '('); | ||
| 2449 | if (s && !ends_with_bracket(s) && strchr(q, ')')) { | ||
| 2450 | // Looks like an unterminated archive member | ||
| 2451 | // with a terminator later on the line. | ||
| 2452 | lib = p; | ||
| 2453 | if (s[1] != '\0') { | ||
| 2454 | p = newp = auto_concat(lib, ")"); | ||
| 2455 | s[1] = '\0'; | ||
| 2456 | } else { | ||
| 2457 | continue; | ||
| 2458 | } | ||
| 2459 | } | ||
| 2460 | } else if (ends_with_bracket(p)) { | ||
| 2461 | if (*p != ')') | ||
| 2462 | p = newp = auto_concat(lib, p); | ||
| 2463 | lib = NULL; | ||
| 2464 | if (newp == NULL) | ||
| 2465 | continue; | ||
| 2466 | } else { | ||
| 2467 | p = newp = auto_string(xasprintf("%s%s)", lib, p)); | ||
| 2468 | } | ||
| 2469 | } | ||
| 2470 | |||
| 2471 | // If not in POSIX mode expand wildcards in the name. | ||
| 2472 | nfile = 1; | ||
| 2473 | files = &p; | ||
| 2474 | if (!posix && wildcard(p, &gd)) { | ||
| 2475 | nfile = gd.gl_pathc; | ||
| 2476 | files = gd.gl_pathv; | ||
| 2477 | } | ||
| 2478 | for (i = 0; i < nfile; ++i) { | ||
| 2479 | if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0) | ||
| 2480 | continue; | ||
| 2481 | np = newname(files[i]); | ||
| 2482 | newdep(&dp, np); | ||
| 2483 | } | ||
| 2484 | if (files != &p) | ||
| 2485 | globfree(&gd); | ||
| 2486 | free(newp); | ||
| 2487 | } | ||
| 2488 | lib = NULL; | ||
| 2489 | |||
| 2490 | // Create list of commands | ||
| 2491 | startno = dispno; | ||
| 2492 | while ((str2 = readline(fd, TRUE)) && *str2 == '\t') { | ||
| 2493 | newcmd(&cp, process_command(str2)); | ||
| 2494 | free(str2); | ||
| 2495 | } | ||
| 2496 | dispno = startno; | ||
| 2497 | |||
| 2498 | // Create target names and attach rule to them | ||
| 2499 | q = expanded; | ||
| 2500 | count = 0; | ||
| 2501 | seen_inference = FALSE; | ||
| 2502 | while ((p = gettok(&q)) != NULL) { | ||
| 2503 | // If not in POSIX mode expand wildcards in the name. | ||
| 2504 | nfile = 1; | ||
| 2505 | files = &p; | ||
| 2506 | if (!posix && wildcard(p, &gd)) { | ||
| 2507 | nfile = gd.gl_pathc; | ||
| 2508 | files = gd.gl_pathv; | ||
| 2509 | } | ||
| 2510 | for (i = 0; i < nfile; ++i) { | ||
| 2511 | int ttype = target_type(files[i]); | ||
| 2512 | |||
| 2513 | np = newname(files[i]); | ||
| 2514 | if (ttype != T_NORMAL) { | ||
| 2515 | // Enforce prerequisites/commands in POSIX mode | ||
| 2516 | if (posix) { | ||
| 2517 | if ((ttype & T_NOPREREQ) && dp) | ||
| 2518 | error_not_allowed("prerequisites", p); | ||
| 2519 | if ((ttype & T_INFERENCE)) { | ||
| 2520 | if (semicolon_cmd) | ||
| 2521 | error_in_inference_rule("'; command'"); | ||
| 2522 | seen_inference = TRUE; | ||
| 2523 | } | ||
| 2524 | if ((ttype & T_COMMAND) && !cp && | ||
| 2525 | !((ttype & T_INFERENCE) && !semicolon_cmd)) | ||
| 2526 | error("commands required for %s", p); | ||
| 2527 | if (!(ttype & T_COMMAND) && cp) | ||
| 2528 | error_not_allowed("commands", p); | ||
| 2529 | } | ||
| 2530 | |||
| 2531 | if ((ttype & T_INFERENCE)) { | ||
| 2532 | np->n_flag |= N_INFERENCE; | ||
| 2533 | } else if (strcmp(p, ".DEFAULT") == 0) { | ||
| 2534 | // .DEFAULT rule is a special case | ||
| 2535 | np->n_flag |= N_SPECIAL | N_INFERENCE; | ||
| 2536 | } else { | ||
| 2537 | np->n_flag |= N_SPECIAL; | ||
| 2538 | } | ||
| 2539 | } else if (!firstname) { | ||
| 2540 | firstname = np; | ||
| 2541 | } | ||
| 2542 | addrule(np, dp, cp, dbl); | ||
| 2543 | count++; | ||
| 2544 | } | ||
| 2545 | if (files != &p) | ||
| 2546 | globfree(&gd); | ||
| 2547 | } | ||
| 2548 | if (posix && seen_inference && count != 1) | ||
| 2549 | error_in_inference_rule("multiple targets"); | ||
| 2550 | |||
| 2551 | // Prerequisites and commands will be unused if there were | ||
| 2552 | // no targets. Avoid leaking memory. | ||
| 2553 | if (count == 0) { | ||
| 2554 | freedeps(dp); | ||
| 2555 | freecmds(cp); | ||
| 2556 | } | ||
| 2557 | |||
| 2558 | end_loop: | ||
| 2559 | free(str1); | ||
| 2560 | dispno = lineno; | ||
| 2561 | str1 = str2 ? str2 : readline(fd, FALSE); | ||
| 2562 | free(copy); | ||
| 2563 | free(expanded); | ||
| 2564 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 2565 | if (!seen_first && fd) { | ||
| 2566 | if (findname(".POSIX")) { | ||
| 2567 | // The first non-comment line from a real makefile | ||
| 2568 | // defined the .POSIX special target. | ||
| 2569 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); | ||
| 2570 | posix = TRUE; | ||
| 2571 | } | ||
| 2572 | seen_first = TRUE; | ||
| 2573 | } | ||
| 2574 | #endif | ||
| 2575 | } | ||
| 2576 | // Conditionals aren't allowed to span files | ||
| 2577 | if (clevel != old_clevel) | ||
| 2578 | error("invalid conditional"); | ||
| 2579 | } | ||
| 2580 | |||
| 2581 | static void | ||
| 2582 | remove_target(void) | ||
| 2583 | { | ||
| 2584 | if (!dryrun && !print && !precious && | ||
| 2585 | target && !(target->n_flag & (N_PRECIOUS | N_PHONY)) && | ||
| 2586 | unlink(target->n_name) == 0) { | ||
| 2587 | diagnostic("'%s' removed", target->n_name); | ||
| 2588 | } | ||
| 2589 | } | ||
| 2590 | |||
| 2591 | /* | ||
| 2592 | * Update the modification time of a file to now. | ||
| 2593 | */ | ||
| 2594 | static void | ||
| 2595 | touch(struct name *np) | ||
| 2596 | { | ||
| 2597 | if (dryrun || !silent) | ||
| 2598 | printf("touch %s\n", np->n_name); | ||
| 2599 | |||
| 2600 | if (!dryrun) { | ||
| 2601 | const struct timespec timebuf[2] = {{0, UTIME_NOW}, {0, UTIME_NOW}}; | ||
| 2602 | |||
| 2603 | if (utimensat(AT_FDCWD, np->n_name, timebuf, 0) < 0) { | ||
| 2604 | if (errno == ENOENT) { | ||
| 2605 | int fd = open(np->n_name, O_RDWR | O_CREAT, 0666); | ||
| 2606 | if (fd >= 0) { | ||
| 2607 | close(fd); | ||
| 2608 | return; | ||
| 2609 | } | ||
| 2610 | } | ||
| 2611 | warning("touch %s failed", np->n_name); | ||
| 2612 | } | ||
| 2613 | } | ||
| 2614 | } | ||
| 2615 | |||
| 2616 | /* | ||
| 2617 | * Do commands to make a target | ||
| 2618 | */ | ||
| 2619 | static int | ||
| 2620 | docmds(struct name *np, struct cmd *cp) | ||
| 2621 | { | ||
| 2622 | int estat = 0; | ||
| 2623 | char *q, *command; | ||
| 2624 | |||
| 2625 | for (; cp; cp = cp->c_next) { | ||
| 2626 | uint32_t ssilent, signore, sdomake; | ||
| 2627 | |||
| 2628 | // Location of command in makefile (for use in error messages) | ||
| 2629 | curr_cmd = cp; | ||
| 2630 | opts &= ~OPT_make; // We want to know if $(MAKE) is expanded | ||
| 2631 | q = command = expand_macros(cp->c_cmd, FALSE); | ||
| 2632 | ssilent = silent || (np->n_flag & N_SILENT) || dotouch; | ||
| 2633 | signore = ignore || (np->n_flag & N_IGNORE); | ||
| 2634 | sdomake = (!dryrun || doinclude || domake) && !dotouch; | ||
| 2635 | for (;;) { | ||
| 2636 | if (*q == '@') // Specific silent | ||
| 2637 | ssilent = TRUE + 1; | ||
| 2638 | else if (*q == '-') // Specific ignore | ||
| 2639 | signore = TRUE; | ||
| 2640 | else if (*q == '+') // Specific domake | ||
| 2641 | sdomake = TRUE + 1; | ||
| 2642 | else | ||
| 2643 | break; | ||
| 2644 | do { | ||
| 2645 | q++; | ||
| 2646 | } while (isblank(*q)); | ||
| 2647 | } | ||
| 2648 | |||
| 2649 | if (sdomake > TRUE) { | ||
| 2650 | // '+' must not override '@' or .SILENT | ||
| 2651 | if (ssilent != TRUE + 1 && !(np->n_flag & N_SILENT)) | ||
| 2652 | ssilent = FALSE; | ||
| 2653 | } else if (!sdomake) | ||
| 2654 | ssilent = dotouch; | ||
| 2655 | |||
| 2656 | if (!ssilent && *q != '\0') { // Ignore empty commands | ||
| 2657 | puts(q); | ||
| 2658 | fflush_all(); | ||
| 2659 | } | ||
| 2660 | |||
| 2661 | if (quest && sdomake != TRUE + 1) { | ||
| 2662 | // MAKE_FAILURE means rebuild is needed | ||
| 2663 | estat |= MAKE_FAILURE | MAKE_DIDSOMETHING; | ||
| 2664 | continue; | ||
| 2665 | } | ||
| 2666 | |||
| 2667 | if (sdomake && *q != '\0') { // Ignore empty commands | ||
| 2668 | // Get the shell to execute it | ||
| 2669 | int status; | ||
| 2670 | char *cmd = !signore && posix ? auto_concat("set -e;", q) : q; | ||
| 2671 | |||
| 2672 | target = np; | ||
| 2673 | status = system(cmd); | ||
| 2674 | // If this command was being run to create an include file | ||
| 2675 | // or bring it up-to-date errors should be ignored and a | ||
| 2676 | // failure status returned. | ||
| 2677 | if (status == -1 && !doinclude) { | ||
| 2678 | error("couldn't execute '%s'", q); | ||
| 2679 | } else if (status != 0 && !signore) { | ||
| 2680 | if (!posix && WIFSIGNALED(status)) | ||
| 2681 | remove_target(); | ||
| 2682 | if (doinclude) { | ||
| 2683 | warning("failed to build '%s'", np->n_name); | ||
| 2684 | } else { | ||
| 2685 | const char *err_type = NULL; | ||
| 2686 | int err_value = 1; | ||
| 2687 | |||
| 2688 | if (WIFEXITED(status)) { | ||
| 2689 | err_type = "exit"; | ||
| 2690 | err_value = WEXITSTATUS(status); | ||
| 2691 | } else if (WIFSIGNALED(status)) { | ||
| 2692 | err_type = "signal"; | ||
| 2693 | err_value = WTERMSIG(status); | ||
| 2694 | } | ||
| 2695 | |||
| 2696 | if (!quest || err_value == 127) { | ||
| 2697 | if (err_type) | ||
| 2698 | diagnostic("failed to build '%s' %s %d", | ||
| 2699 | np->n_name, err_type, err_value); | ||
| 2700 | else | ||
| 2701 | diagnostic("failed to build '%s'", np->n_name); | ||
| 2702 | } | ||
| 2703 | |||
| 2704 | if (errcont) { | ||
| 2705 | estat |= MAKE_FAILURE; | ||
| 2706 | free(command); | ||
| 2707 | break; | ||
| 2708 | } | ||
| 2709 | exit(2); | ||
| 2710 | } | ||
| 2711 | } | ||
| 2712 | } | ||
| 2713 | if (sdomake || dryrun) | ||
| 2714 | estat = MAKE_DIDSOMETHING; | ||
| 2715 | free(command); | ||
| 2716 | } | ||
| 2717 | |||
| 2718 | if (dotouch && !(np->n_flag & N_PHONY) && !(estat & MAKE_DIDSOMETHING)) { | ||
| 2719 | touch(np); | ||
| 2720 | estat = MAKE_DIDSOMETHING; | ||
| 2721 | } | ||
| 2722 | |||
| 2723 | curr_cmd = NULL; | ||
| 2724 | return estat; | ||
| 2725 | } | ||
| 2726 | |||
| 2727 | /* | ||
| 2728 | * Remove the suffix from a name, either the one provided in 'tsuff' | ||
| 2729 | * or, if 'tsuff' is NULL, one of the known suffixes. | ||
| 2730 | */ | ||
| 2731 | static char * | ||
| 2732 | remove_suffix(const char *name, const char *tsuff) | ||
| 2733 | { | ||
| 2734 | char *base = NULL; | ||
| 2735 | |||
| 2736 | if (tsuff != NULL) { | ||
| 2737 | base = has_suffix(name, tsuff); | ||
| 2738 | } else { | ||
| 2739 | struct name *xp = newname(".SUFFIXES"); | ||
| 2740 | for (struct rule *rp = xp->n_rule; rp; rp = rp->r_next) { | ||
| 2741 | for (struct depend *dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2742 | base = has_suffix(name, dp->d_name->n_name); | ||
| 2743 | if (base) { | ||
| 2744 | return base; | ||
| 2745 | } | ||
| 2746 | } | ||
| 2747 | } | ||
| 2748 | } | ||
| 2749 | return base; | ||
| 2750 | } | ||
| 2751 | |||
| 2752 | static int | ||
| 2753 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | ||
| 2754 | char *dedup, struct name *implicit, const char *tsuff) | ||
| 2755 | { | ||
| 2756 | char *name, *member = NULL, *base = NULL, *prereq = NULL; | ||
| 2757 | |||
| 2758 | name = splitlib(np->n_name, &member); | ||
| 2759 | setmacro("?", oodate, 0 | M_VALID); | ||
| 2760 | if (!POSIX_2017) { | ||
| 2761 | setmacro("+", allsrc, 0 | M_VALID); | ||
| 2762 | setmacro("^", dedup, 0 | M_VALID); | ||
| 2763 | } | ||
| 2764 | setmacro("%", member, 0 | M_VALID); | ||
| 2765 | setmacro("@", name, 0 | M_VALID); | ||
| 2766 | if (implicit || !posix) { | ||
| 2767 | char *s; | ||
| 2768 | |||
| 2769 | // As an extension, if we're not dealing with an implicit | ||
| 2770 | // prerequisite set $< to the first out-of-date prerequisite. | ||
| 2771 | if (implicit == NULL) { | ||
| 2772 | if (oodate) { | ||
| 2773 | s = strchr(oodate, ' '); | ||
| 2774 | if (s) | ||
| 2775 | *s = '\0'; | ||
| 2776 | prereq = oodate; | ||
| 2777 | } | ||
| 2778 | } else | ||
| 2779 | prereq = implicit->n_name; | ||
| 2780 | |||
| 2781 | if (!posix && member == NULL) { | ||
| 2782 | // As an extension remove a suffix that doesn't necessarily | ||
| 2783 | // start with a period from a target, but not for targets | ||
| 2784 | // of the form lib.a(member.o). | ||
| 2785 | base = remove_suffix(name, tsuff); | ||
| 2786 | if (base) { | ||
| 2787 | free(name); | ||
| 2788 | name = base; | ||
| 2789 | } | ||
| 2790 | } else { | ||
| 2791 | base = member ? member : name; | ||
| 2792 | s = suffix(base); | ||
| 2793 | // As an extension, if we're not dealing with an implicit | ||
| 2794 | // prerequisite and the target ends with a known suffix, | ||
| 2795 | // remove it and set $* to the stem, else to an empty string. | ||
| 2796 | if (implicit == NULL && !is_suffix(s)) | ||
| 2797 | base = NULL; | ||
| 2798 | else | ||
| 2799 | *s = '\0'; | ||
| 2800 | } | ||
| 2801 | } | ||
| 2802 | setmacro("<", prereq, 0 | M_VALID); | ||
| 2803 | setmacro("*", base, 0 | M_VALID); | ||
| 2804 | free(name); | ||
| 2805 | |||
| 2806 | return docmds(np, cp); | ||
| 2807 | } | ||
| 2808 | |||
| 2809 | /* | ||
| 2810 | * Determine if the modification time of a target, t, is less than | ||
| 2811 | * that of a prerequisite, p. If the tv_nsec member of either is | ||
| 2812 | * exactly 0 we assume (possibly incorrectly) that the time resolution | ||
| 2813 | * is 1 second and only compare tv_sec values. | ||
| 2814 | */ | ||
| 2815 | static int | ||
| 2816 | timespec_le(const struct timespec *t, const struct timespec *p) | ||
| 2817 | { | ||
| 2818 | if (t->tv_nsec == 0 || p->tv_nsec == 0) | ||
| 2819 | return t->tv_sec <= p->tv_sec; | ||
| 2820 | else if (t->tv_sec < p->tv_sec) | ||
| 2821 | return TRUE; | ||
| 2822 | else if (t->tv_sec == p->tv_sec) | ||
| 2823 | return t->tv_nsec <= p->tv_nsec; | ||
| 2824 | return FALSE; | ||
| 2825 | } | ||
| 2826 | |||
| 2827 | /* | ||
| 2828 | * Return the greater of two struct timespecs | ||
| 2829 | */ | ||
| 2830 | static const struct timespec * | ||
| 2831 | timespec_max(const struct timespec *t, const struct timespec *p) | ||
| 2832 | { | ||
| 2833 | return timespec_le(t, p) ? p : t; | ||
| 2834 | } | ||
| 2835 | |||
| 2836 | /* | ||
| 2837 | * Recursive routine to make a target. | ||
| 2838 | */ | ||
| 2839 | static int | ||
| 2840 | make(struct name *np, int level) | ||
| 2841 | { | ||
| 2842 | struct depend *dp; | ||
| 2843 | struct rule *rp; | ||
| 2844 | struct name *impdep = NULL; // implicit prerequisite | ||
| 2845 | struct rule infrule; | ||
| 2846 | struct cmd *sc_cmd = NULL; // commands for single-colon rule | ||
| 2847 | char *oodate = NULL; | ||
| 2848 | char *allsrc = NULL; | ||
| 2849 | char *dedup = NULL; | ||
| 2850 | const char *tsuff = NULL; | ||
| 2851 | struct timespec dtim = {1, 0}; | ||
| 2852 | int estat = 0; | ||
| 2853 | |||
| 2854 | if (np->n_flag & N_DONE) | ||
| 2855 | return 0; | ||
| 2856 | if (np->n_flag & N_DOING) | ||
| 2857 | error("circular dependency for %s", np->n_name); | ||
| 2858 | np->n_flag |= N_DOING; | ||
| 2859 | |||
| 2860 | if (!np->n_tim.tv_sec) | ||
| 2861 | modtime(np); // Get modtime of this file | ||
| 2862 | |||
| 2863 | if (!(np->n_flag & N_DOUBLE)) { | ||
| 2864 | // Find the commands needed for a single-colon rule, using | ||
| 2865 | // an inference rule or .DEFAULT rule if necessary (but, | ||
| 2866 | // as an extension, not for phony targets) | ||
| 2867 | sc_cmd = getcmd(np); | ||
| 2868 | if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) { | ||
| 2869 | impdep = dyndep(np, &infrule, &tsuff); | ||
| 2870 | if (impdep) { | ||
| 2871 | sc_cmd = infrule.r_cmd; | ||
| 2872 | addrule(np, infrule.r_dep, NULL, FALSE); | ||
| 2873 | } | ||
| 2874 | } | ||
| 2875 | |||
| 2876 | // As a last resort check for a default rule | ||
| 2877 | if (!(np->n_flag & N_TARGET) && np->n_tim.tv_sec == 0) { | ||
| 2878 | if (posix || !(np->n_flag & N_PHONY)) | ||
| 2879 | sc_cmd = getcmd(findname(".DEFAULT")); | ||
| 2880 | if (!sc_cmd) { | ||
| 2881 | if (doinclude) | ||
| 2882 | return 1; | ||
| 2883 | error("don't know how to make %s", np->n_name); | ||
| 2884 | } | ||
| 2885 | impdep = np; | ||
| 2886 | } | ||
| 2887 | } else { | ||
| 2888 | // If any double-colon rule has no commands we need | ||
| 2889 | // an inference rule. | ||
| 2890 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2891 | if (!rp->r_cmd) { | ||
| 2892 | // Phony targets don't need an inference rule. | ||
| 2893 | if (!posix && (np->n_flag & N_PHONY)) | ||
| 2894 | continue; | ||
| 2895 | impdep = dyndep(np, &infrule, &tsuff); | ||
| 2896 | if (!impdep) { | ||
| 2897 | if (doinclude) | ||
| 2898 | return 1; | ||
| 2899 | error("don't know how to make %s", np->n_name); | ||
| 2900 | } | ||
| 2901 | break; | ||
| 2902 | } | ||
| 2903 | } | ||
| 2904 | } | ||
| 2905 | |||
| 2906 | // Reset flag to detect duplicate prerequisites | ||
| 2907 | if (!(np->n_flag & N_DOUBLE)) { | ||
| 2908 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2909 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2910 | dp->d_name->n_flag &= ~N_MARK; | ||
| 2911 | } | ||
| 2912 | } | ||
| 2913 | } | ||
| 2914 | |||
| 2915 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 2916 | struct name *locdep = NULL; | ||
| 2917 | |||
| 2918 | // Each double-colon rule is handled separately. | ||
| 2919 | if ((np->n_flag & N_DOUBLE)) { | ||
| 2920 | // If the rule has no commands use the inference rule. | ||
| 2921 | // Unless there isn't one, as allowed for phony targets. | ||
| 2922 | if (!rp->r_cmd) { | ||
| 2923 | if (impdep) { | ||
| 2924 | locdep = impdep; | ||
| 2925 | infrule.r_dep->d_next = rp->r_dep; | ||
| 2926 | rp->r_dep = infrule.r_dep; | ||
| 2927 | rp->r_cmd = infrule.r_cmd; | ||
| 2928 | } | ||
| 2929 | } | ||
| 2930 | // A rule with no prerequisities is executed unconditionally. | ||
| 2931 | if (!rp->r_dep) | ||
| 2932 | dtim = np->n_tim; | ||
| 2933 | // Reset flag to detect duplicate prerequisites | ||
| 2934 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2935 | dp->d_name->n_flag &= ~N_MARK; | ||
| 2936 | } | ||
| 2937 | } | ||
| 2938 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 2939 | // Make prerequisite | ||
| 2940 | estat |= make(dp->d_name, level + 1); | ||
| 2941 | |||
| 2942 | // Make strings of out-of-date prerequisites (for $?), | ||
| 2943 | // all prerequisites (for $+) and deduplicated prerequisites | ||
| 2944 | // (for $^). | ||
| 2945 | if (timespec_le(&np->n_tim, &dp->d_name->n_tim)) { | ||
| 2946 | if (posix || !(dp->d_name->n_flag & N_MARK)) | ||
| 2947 | oodate = xappendword(oodate, dp->d_name->n_name); | ||
| 2948 | } | ||
| 2949 | allsrc = xappendword(allsrc, dp->d_name->n_name); | ||
| 2950 | if (!(dp->d_name->n_flag & N_MARK)) | ||
| 2951 | dedup = xappendword(dedup, dp->d_name->n_name); | ||
| 2952 | dp->d_name->n_flag |= N_MARK; | ||
| 2953 | dtim = *timespec_max(&dtim, &dp->d_name->n_tim); | ||
| 2954 | } | ||
| 2955 | if ((np->n_flag & N_DOUBLE)) { | ||
| 2956 | if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { | ||
| 2957 | if (!(estat & MAKE_FAILURE)) { | ||
| 2958 | estat |= make1(np, rp->r_cmd, oodate, allsrc, | ||
| 2959 | dedup, locdep, tsuff); | ||
| 2960 | dtim = (struct timespec){1, 0}; | ||
| 2961 | } | ||
| 2962 | free(oodate); | ||
| 2963 | oodate = NULL; | ||
| 2964 | } | ||
| 2965 | free(allsrc); | ||
| 2966 | free(dedup); | ||
| 2967 | allsrc = dedup = NULL; | ||
| 2968 | if (locdep) { | ||
| 2969 | rp->r_dep = rp->r_dep->d_next; | ||
| 2970 | rp->r_cmd = NULL; | ||
| 2971 | } | ||
| 2972 | } | ||
| 2973 | } | ||
| 2974 | if ((np->n_flag & N_DOUBLE) && impdep) | ||
| 2975 | free(infrule.r_dep); | ||
| 2976 | |||
| 2977 | np->n_flag |= N_DONE; | ||
| 2978 | np->n_flag &= ~N_DOING; | ||
| 2979 | |||
| 2980 | if (!(np->n_flag & N_DOUBLE) && | ||
| 2981 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { | ||
| 2982 | if (!(estat & MAKE_FAILURE)) { | ||
| 2983 | if (sc_cmd) | ||
| 2984 | estat |= make1(np, sc_cmd, oodate, allsrc, dedup, | ||
| 2985 | impdep, tsuff); | ||
| 2986 | else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) | ||
| 2987 | warning("nothing to be done for %s", np->n_name); | ||
| 2988 | } else if (!doinclude && !quest) { | ||
| 2989 | diagnostic("'%s' not built due to errors", np->n_name); | ||
| 2990 | } | ||
| 2991 | free(oodate); | ||
| 2992 | } | ||
| 2993 | |||
| 2994 | if (estat & MAKE_DIDSOMETHING) { | ||
| 2995 | modtime(np); | ||
| 2996 | if (!np->n_tim.tv_sec) | ||
| 2997 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | ||
| 2998 | } else if (!quest && level == 0 && !timespec_le(&np->n_tim, &dtim)) | ||
| 2999 | printf("%s: '%s' is up to date\n", applet_name, np->n_name); | ||
| 3000 | |||
| 3001 | free(allsrc); | ||
| 3002 | free(dedup); | ||
| 3003 | return estat; | ||
| 3004 | } | ||
| 3005 | |||
| 3006 | /* | ||
| 3007 | * Check structures for make. | ||
| 3008 | */ | ||
| 3009 | |||
| 3010 | static void | ||
| 3011 | print_name(struct name *np) | ||
| 3012 | { | ||
| 3013 | if (np == firstname) | ||
| 3014 | printf("# default target\n"); | ||
| 3015 | printf("%s:", np->n_name); | ||
| 3016 | if ((np->n_flag & N_DOUBLE)) | ||
| 3017 | putchar(':'); | ||
| 3018 | } | ||
| 3019 | |||
| 3020 | static void | ||
| 3021 | print_prerequisites(struct rule *rp) | ||
| 3022 | { | ||
| 3023 | struct depend *dp; | ||
| 3024 | |||
| 3025 | for (dp = rp->r_dep; dp; dp = dp->d_next) | ||
| 3026 | printf(" %s", dp->d_name->n_name); | ||
| 3027 | } | ||
| 3028 | |||
| 3029 | static void | ||
| 3030 | print_commands(struct rule *rp) | ||
| 3031 | { | ||
| 3032 | struct cmd *cp; | ||
| 3033 | |||
| 3034 | for (cp = rp->r_cmd; cp; cp = cp->c_next) | ||
| 3035 | printf("\t%s\n", cp->c_cmd); | ||
| 3036 | } | ||
| 3037 | |||
| 3038 | static void | ||
| 3039 | print_details(void) | ||
| 3040 | { | ||
| 3041 | int i; | ||
| 3042 | struct macro *mp; | ||
| 3043 | struct name *np; | ||
| 3044 | struct rule *rp; | ||
| 3045 | |||
| 3046 | for (i = 0; i < HTABSIZE; i++) | ||
| 3047 | for (mp = macrohead[i]; mp; mp = mp->m_next) | ||
| 3048 | printf("%s = %s\n", mp->m_name, mp->m_val); | ||
| 3049 | putchar('\n'); | ||
| 3050 | |||
| 3051 | for (i = 0; i < HTABSIZE; i++) { | ||
| 3052 | for (np = namehead[i]; np; np = np->n_next) { | ||
| 3053 | if (!(np->n_flag & N_DOUBLE)) { | ||
| 3054 | print_name(np); | ||
| 3055 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 3056 | print_prerequisites(rp); | ||
| 3057 | } | ||
| 3058 | putchar('\n'); | ||
| 3059 | |||
| 3060 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 3061 | print_commands(rp); | ||
| 3062 | } | ||
| 3063 | putchar('\n'); | ||
| 3064 | } else { | ||
| 3065 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 3066 | print_name(np); | ||
| 3067 | print_prerequisites(rp); | ||
| 3068 | putchar('\n'); | ||
| 3069 | |||
| 3070 | print_commands(rp); | ||
| 3071 | putchar('\n'); | ||
| 3072 | } | ||
| 3073 | } | ||
| 3074 | } | ||
| 3075 | } | ||
| 3076 | } | ||
| 3077 | |||
| 3078 | /* | ||
| 3079 | * Process options from an argv array. If from_env is non-zero we're | ||
| 3080 | * handling options from MAKEFLAGS so skip '-C', '-f', '-p' and '-x'. | ||
| 3081 | */ | ||
| 3082 | static uint32_t | ||
| 3083 | process_options(char **argv, int from_env) | ||
| 3084 | { | ||
| 3085 | uint32_t flags; | ||
| 3086 | |||
| 3087 | flags = getopt32(argv, "^" OPTSTR1 OPTSTR2 "\0k-S:S-k", | ||
| 3088 | &numjobs, &makefiles, &dirs | ||
| 3089 | IF_FEATURE_MAKE_POSIX(, &pragmas)); | ||
| 3090 | if (from_env && (flags & (OPT_C | OPT_f | OPT_p | OPT_x))) | ||
| 3091 | error("invalid MAKEFLAGS"); | ||
| 3092 | if (posix && (flags & OPT_C)) | ||
| 3093 | error("-C not allowed"); | ||
| 3094 | |||
| 3095 | return flags; | ||
| 3096 | } | ||
| 3097 | |||
| 3098 | /* | ||
| 3099 | * Split the contents of MAKEFLAGS into an argv array. If the return | ||
| 3100 | * value (call it fargv) isn't NULL the caller should free fargv[1] and | ||
| 3101 | * fargv. | ||
| 3102 | */ | ||
| 3103 | static char ** | ||
| 3104 | expand_makeflags(void) | ||
| 3105 | { | ||
| 3106 | const char *m, *makeflags = getenv("MAKEFLAGS"); | ||
| 3107 | char *p, *argstr; | ||
| 3108 | int argc; | ||
| 3109 | char **argv; | ||
| 3110 | |||
| 3111 | if (makeflags == NULL) | ||
| 3112 | return NULL; | ||
| 3113 | |||
| 3114 | while (isblank(*makeflags)) | ||
| 3115 | makeflags++; | ||
| 3116 | |||
| 3117 | if (*makeflags == '\0') | ||
| 3118 | return NULL; | ||
| 3119 | |||
| 3120 | p = argstr = xzalloc(strlen(makeflags) + 2); | ||
| 3121 | |||
| 3122 | // If MAKEFLAGS doesn't start with a hyphen, doesn't look like | ||
| 3123 | // a macro definition and only contains valid option characters, | ||
| 3124 | // add a hyphen. | ||
| 3125 | argc = 3; | ||
| 3126 | if (makeflags[0] != '-' && strchr(makeflags, '=') == NULL) { | ||
| 3127 | if (strspn(makeflags, OPTSTR1) != strlen(makeflags)) | ||
| 3128 | error("invalid MAKEFLAGS"); | ||
| 3129 | *p++ = '-'; | ||
| 3130 | } else { | ||
| 3131 | // MAKEFLAGS may need to be split, estimate size of argv array. | ||
| 3132 | for (m = makeflags; *m; ++m) { | ||
| 3133 | if (isblank(*m)) | ||
| 3134 | argc++; | ||
| 3135 | } | ||
| 3136 | } | ||
| 3137 | |||
| 3138 | argv = xzalloc(argc * sizeof(char *)); | ||
| 3139 | argc = 0; | ||
| 3140 | argv[argc++] = (char *)applet_name; | ||
| 3141 | argv[argc++] = argstr; | ||
| 3142 | |||
| 3143 | // Copy MAKEFLAGS into argstr, splitting at non-escaped blanks. | ||
| 3144 | m = makeflags; | ||
| 3145 | do { | ||
| 3146 | if (*m == '\\' && m[1] != '\0') | ||
| 3147 | m++; // Skip backslash, copy next character unconditionally. | ||
| 3148 | else if (isblank(*m)) { | ||
| 3149 | // Terminate current argument and start a new one. | ||
| 3150 | /* *p = '\0'; - xzalloc did it */ | ||
| 3151 | argv[argc++] = ++p; | ||
| 3152 | do { | ||
| 3153 | m++; | ||
| 3154 | } while (isblank(*m)); | ||
| 3155 | continue; | ||
| 3156 | } | ||
| 3157 | *p++ = *m++; | ||
| 3158 | } while (*m != '\0'); | ||
| 3159 | /* *p = '\0'; - xzalloc did it */ | ||
| 3160 | /* argv[argc] = NULL; - and this */ | ||
| 3161 | |||
| 3162 | return argv; | ||
| 3163 | } | ||
| 3164 | |||
| 3165 | // These macros require special treatment | ||
| 3166 | #define SPECIAL_MACROS "MAKEFLAGS\0SHELL\0CURDIR\0" | ||
| 3167 | #define MAKEFLAGS 0 | ||
| 3168 | #define SHELL 1 | ||
| 3169 | #define CURDIR 2 | ||
| 3170 | |||
| 3171 | /* | ||
| 3172 | * Instantiate all macros in an argv-style array of pointers. Stop | ||
| 3173 | * processing at the first string that doesn't contain an equal sign. | ||
| 3174 | * As an extension, target arguments on the command line (level 1) | ||
| 3175 | * are skipped and will be processed later. | ||
| 3176 | */ | ||
| 3177 | static char ** | ||
| 3178 | process_macros(char **argv, int level) | ||
| 3179 | { | ||
| 3180 | char *equal; | ||
| 3181 | |||
| 3182 | for (; *argv; argv++) { | ||
| 3183 | char *colon = NULL; | ||
| 3184 | int idx, immediate = 0; | ||
| 3185 | int except_dollar = FALSE; | ||
| 3186 | |||
| 3187 | if (!(equal = strchr(*argv, '='))) { | ||
| 3188 | // Skip targets on the command line | ||
| 3189 | if (!posix && level == 1) | ||
| 3190 | continue; | ||
| 3191 | else | ||
| 3192 | // Stop at first target | ||
| 3193 | break; | ||
| 3194 | } | ||
| 3195 | |||
| 3196 | if (equal - 1 > *argv && equal[-1] == ':') { | ||
| 3197 | if (equal - 2 > *argv && equal[-2] == ':') { | ||
| 3198 | if (POSIX_2017) | ||
| 3199 | error("invalid macro assignment"); | ||
| 3200 | if (equal - 3 > *argv && equal[-3] == ':') { | ||
| 3201 | // BSD-style ':='. Expand RHS, but not '$$', | ||
| 3202 | // resulting macro is delayed expansion. | ||
| 3203 | colon = equal - 3; | ||
| 3204 | except_dollar = TRUE; | ||
| 3205 | } else { | ||
| 3206 | // GNU-style ':='. Expand RHS, including '$$', | ||
| 3207 | // resulting macro is immediate expansion. | ||
| 3208 | colon = equal - 2; | ||
| 3209 | immediate = M_IMMEDIATE; | ||
| 3210 | } | ||
| 3211 | *colon = '\0'; | ||
| 3212 | } else { | ||
| 3213 | if (posix) | ||
| 3214 | error("invalid macro assignment"); | ||
| 3215 | colon = equal - 1; | ||
| 3216 | immediate = M_IMMEDIATE; | ||
| 3217 | *colon = '\0'; | ||
| 3218 | } | ||
| 3219 | } else | ||
| 3220 | *equal = '\0'; | ||
| 3221 | |||
| 3222 | /* We want to process _most_ macro assignments. | ||
| 3223 | * There are exceptions for particular values from the | ||
| 3224 | * environment. */ | ||
| 3225 | idx = index_in_strings(SPECIAL_MACROS, *argv); | ||
| 3226 | if (!((level & M_ENVIRON) && | ||
| 3227 | (idx == MAKEFLAGS || idx == SHELL || | ||
| 3228 | (idx == CURDIR && !useenv && !POSIX_2017)))) { | ||
| 3229 | if (colon) { | ||
| 3230 | char *exp = expand_macros(equal + 1, except_dollar); | ||
| 3231 | setmacro(*argv, exp, level | immediate); | ||
| 3232 | free(exp); | ||
| 3233 | } else | ||
| 3234 | setmacro(*argv, equal + 1, level); | ||
| 3235 | } | ||
| 3236 | |||
| 3237 | if (colon) | ||
| 3238 | *colon = ':'; | ||
| 3239 | else | ||
| 3240 | *equal = '='; | ||
| 3241 | } | ||
| 3242 | return argv; | ||
| 3243 | } | ||
| 3244 | |||
| 3245 | /* | ||
| 3246 | * Update the MAKEFLAGS macro and environment variable to include any | ||
| 3247 | * command line options that don't have their default value (apart from | ||
| 3248 | * -f, -p and -S). Also add any macros defined on the command line or | ||
| 3249 | * by the MAKEFLAGS environment variable (apart from MAKEFLAGS itself). | ||
| 3250 | * Add macros that were defined on the command line to the environment. | ||
| 3251 | */ | ||
| 3252 | static void | ||
| 3253 | update_makeflags(void) | ||
| 3254 | { | ||
| 3255 | int i; | ||
| 3256 | char optbuf[] = "-?"; | ||
| 3257 | char *makeflags = NULL; | ||
| 3258 | char *macro, *s; | ||
| 3259 | const char *t; | ||
| 3260 | struct macro *mp; | ||
| 3261 | |||
| 3262 | t = OPTSTR1; | ||
| 3263 | for (i = 0; *t; t++) { | ||
| 3264 | if (*t == ':' || *t == '+') | ||
| 3265 | continue; | ||
| 3266 | if ((opts & OPT_MASK & (1 << i))) { | ||
| 3267 | optbuf[1] = *t; | ||
| 3268 | makeflags = xappendword(makeflags, optbuf); | ||
| 3269 | if (*t == 'j') { | ||
| 3270 | s = auto_string(xasprintf("%d", numjobs)); | ||
| 3271 | makeflags = xappendword(makeflags, s); | ||
| 3272 | } | ||
| 3273 | } | ||
| 3274 | i++; | ||
| 3275 | } | ||
| 3276 | |||
| 3277 | for (i = 0; i < HTABSIZE; ++i) { | ||
| 3278 | for (mp = macrohead[i]; mp; mp = mp->m_next) { | ||
| 3279 | if (mp->m_level == 1 || mp->m_level == 2) { | ||
| 3280 | int idx = index_in_strings(SPECIAL_MACROS, mp->m_name); | ||
| 3281 | if (idx == MAKEFLAGS) | ||
| 3282 | continue; | ||
| 3283 | macro = xzalloc(strlen(mp->m_name) + 2 * strlen(mp->m_val) + 1); | ||
| 3284 | s = stpcpy(macro, mp->m_name); | ||
| 3285 | *s++ = '='; | ||
| 3286 | for (t = mp->m_val; *t; t++) { | ||
| 3287 | if (*t == '\\' || isblank(*t)) | ||
| 3288 | *s++ = '\\'; | ||
| 3289 | *s++ = *t; | ||
| 3290 | } | ||
| 3291 | /* *s = '\0'; - xzalloc did it */ | ||
| 3292 | |||
| 3293 | makeflags = xappendword(makeflags, macro); | ||
| 3294 | free(macro); | ||
| 3295 | |||
| 3296 | // Add command line macro definitions to the environment | ||
| 3297 | if (mp->m_level == 1 && idx != SHELL) | ||
| 3298 | setenv(mp->m_name, mp->m_val, 1); | ||
| 3299 | } | ||
| 3300 | } | ||
| 3301 | } | ||
| 3302 | |||
| 3303 | if (makeflags) { | ||
| 3304 | setmacro("MAKEFLAGS", makeflags, 0); | ||
| 3305 | setenv("MAKEFLAGS", makeflags, 1); | ||
| 3306 | free(makeflags); | ||
| 3307 | } | ||
| 3308 | } | ||
| 3309 | |||
| 3310 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 3311 | static void | ||
| 3312 | make_handler(int sig) | ||
| 3313 | { | ||
| 3314 | signal(sig, SIG_DFL); | ||
| 3315 | remove_target(); | ||
| 3316 | kill(getpid(), sig); | ||
| 3317 | } | ||
| 3318 | |||
| 3319 | static void | ||
| 3320 | init_signal(int sig) | ||
| 3321 | { | ||
| 3322 | struct sigaction sa, new_action; | ||
| 3323 | |||
| 3324 | sigemptyset(&new_action.sa_mask); | ||
| 3325 | new_action.sa_flags = 0; | ||
| 3326 | new_action.sa_handler = make_handler; | ||
| 3327 | |||
| 3328 | sigaction(sig, NULL, &sa); | ||
| 3329 | if (sa.sa_handler != SIG_IGN) | ||
| 3330 | sigaction_set(sig, &new_action); | ||
| 3331 | } | ||
| 3332 | #endif | ||
| 3333 | |||
| 3334 | /* | ||
| 3335 | * If the global option flag associated with a special target hasn't | ||
| 3336 | * been set mark all prerequisites of the target with a flag. If the | ||
| 3337 | * target had no prerequisites set the global option flag. | ||
| 3338 | */ | ||
| 3339 | static void | ||
| 3340 | mark_special(const char *special, uint32_t oflag, uint16_t nflag) | ||
| 3341 | { | ||
| 3342 | struct name *np; | ||
| 3343 | struct rule *rp; | ||
| 3344 | struct depend *dp; | ||
| 3345 | int marked = FALSE; | ||
| 3346 | |||
| 3347 | if (!(opts & oflag) && (np = findname(special))) { | ||
| 3348 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
| 3349 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
| 3350 | dp->d_name->n_flag |= nflag; | ||
| 3351 | marked = TRUE; | ||
| 3352 | } | ||
| 3353 | } | ||
| 3354 | |||
| 3355 | if (!marked) | ||
| 3356 | opts |= oflag; | ||
| 3357 | } | ||
| 3358 | } | ||
| 3359 | |||
| 3360 | int make_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
| 3361 | int make_main(int argc UNUSED_PARAM, char **argv) | ||
| 3362 | { | ||
| 3363 | const char *path, *newpath = NULL; | ||
| 3364 | char **fargv, **fargv0; | ||
| 3365 | const char *dir, *file; | ||
| 3366 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 3367 | const char *prag; | ||
| 3368 | #endif | ||
| 3369 | int estat; | ||
| 3370 | bool found_target; | ||
| 3371 | FILE *ifd; | ||
| 3372 | |||
| 3373 | INIT_G(); | ||
| 3374 | |||
| 3375 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 3376 | if (argv[1] && strcmp(argv[1], "--posix") == 0) { | ||
| 3377 | argv[1] = argv[0]; | ||
| 3378 | ++argv; | ||
| 3379 | --argc; | ||
| 3380 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); | ||
| 3381 | posix = TRUE; | ||
| 3382 | } else { | ||
| 3383 | posix = getenv("PDPMAKE_POSIXLY_CORRECT") != NULL; | ||
| 3384 | } | ||
| 3385 | posix_level = DEFAULT_POSIX_LEVEL; | ||
| 3386 | pragmas_from_env(); | ||
| 3387 | #endif | ||
| 3388 | |||
| 3389 | if (!POSIX_2017) { | ||
| 3390 | path = argv[0]; | ||
| 3391 | #if ENABLE_PLATFORM_MINGW32 | ||
| 3392 | if (has_path(argv[0])) { | ||
| 3393 | // Add extension if necessary, else realpath() will fail | ||
| 3394 | char *p = alloc_ext_space(argv[0]); | ||
| 3395 | add_win32_extension(p); | ||
| 3396 | path = newpath = xmalloc_realpath(p); | ||
| 3397 | free(p); | ||
| 3398 | if (!path) { | ||
| 3399 | if (unix_path(argv[0])) | ||
| 3400 | path = argv[0]; | ||
| 3401 | else | ||
| 3402 | bb_perror_msg("can't resolve path for %s", argv[0]); | ||
| 3403 | } | ||
| 3404 | } | ||
| 3405 | #else | ||
| 3406 | if (argv[0][0] != '/' && strchr(argv[0], '/')) { | ||
| 3407 | // Make relative path absolute | ||
| 3408 | path = newpath = realpath(argv[0], NULL); | ||
| 3409 | if (!path) { | ||
| 3410 | bb_perror_msg("can't resolve path for %s", argv[0]); | ||
| 3411 | } | ||
| 3412 | } | ||
| 3413 | #endif | ||
| 3414 | } else { | ||
| 3415 | path = "make"; | ||
| 3416 | } | ||
| 3417 | |||
| 3418 | // Process options from MAKEFLAGS | ||
| 3419 | fargv = fargv0 = expand_makeflags(); | ||
| 3420 | if (fargv0) { | ||
| 3421 | opts = process_options(fargv, TRUE); | ||
| 3422 | fargv = fargv0 + optind; | ||
| 3423 | // Reset getopt(3) so we can call it again | ||
| 3424 | GETOPT_RESET(); | ||
| 3425 | } | ||
| 3426 | |||
| 3427 | // Process options from the command line | ||
| 3428 | opts |= process_options(argv, FALSE); | ||
| 3429 | argv += optind; | ||
| 3430 | |||
| 3431 | while ((dir = llist_pop(&dirs))) { | ||
| 3432 | if (chdir(dir) == -1) { | ||
| 3433 | bb_perror_msg("can't chdir to %s", dir); | ||
| 3434 | } | ||
| 3435 | } | ||
| 3436 | |||
| 3437 | #if ENABLE_FEATURE_MAKE_POSIX | ||
| 3438 | while ((prag = llist_pop(&pragmas))) | ||
| 3439 | set_pragma(prag); | ||
| 3440 | pragmas_to_env(); | ||
| 3441 | #endif | ||
| 3442 | |||
| 3443 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 3444 | init_signal(SIGHUP); | ||
| 3445 | init_signal(SIGTERM); | ||
| 3446 | #endif | ||
| 3447 | |||
| 3448 | setmacro("$", "$", 0 | M_VALID); | ||
| 3449 | |||
| 3450 | // Process macro definitions from the command line | ||
| 3451 | if (!posix) | ||
| 3452 | process_macros(argv, 1); | ||
| 3453 | else | ||
| 3454 | // In POSIX mode macros must appear before targets. | ||
| 3455 | // argv should now point to targets only. | ||
| 3456 | argv = process_macros(argv, 1); | ||
| 3457 | |||
| 3458 | // Process macro definitions from MAKEFLAGS | ||
| 3459 | if (fargv) { | ||
| 3460 | process_macros(fargv, 2); | ||
| 3461 | free(fargv0[1]); | ||
| 3462 | free(fargv0); | ||
| 3463 | } | ||
| 3464 | |||
| 3465 | // Process macro definitions from the environment | ||
| 3466 | process_macros(environ, 3 | M_ENVIRON); | ||
| 3467 | |||
| 3468 | // Update MAKEFLAGS and environment | ||
| 3469 | update_makeflags(); | ||
| 3470 | |||
| 3471 | // Read built-in rules | ||
| 3472 | input(NULL, 0); | ||
| 3473 | |||
| 3474 | setmacro("SHELL", DEFAULT_SHELL, 4); | ||
| 3475 | setmacro("MAKE", path, 4); | ||
| 3476 | if (!POSIX_2017) { | ||
| 3477 | char *cwd = xrealloc_getcwd_or_warn(NULL); | ||
| 3478 | |||
| 3479 | if (cwd) { | ||
| 3480 | if (!useenv) { | ||
| 3481 | // Export cwd to environment, if necessary | ||
| 3482 | char *envcwd = getenv("CURDIR"); | ||
| 3483 | if (envcwd && strcmp(cwd, envcwd) != 0) | ||
| 3484 | setenv("CURDIR", cwd, 1); | ||
| 3485 | } | ||
| 3486 | setmacro("CURDIR", cwd, 4); | ||
| 3487 | } | ||
| 3488 | free(cwd); | ||
| 3489 | } | ||
| 3490 | free((void *)newpath); | ||
| 3491 | |||
| 3492 | if (!makefiles) { // Look for a default Makefile | ||
| 3493 | if (!posix && (ifd = fopen("PDPmakefile", "r")) != NULL) | ||
| 3494 | makefile = "PDPmakefile"; | ||
| 3495 | else if ((ifd = fopen("PDPmakefile" + 3, "r")) != NULL) | ||
| 3496 | makefile = "PDPmakefile" + 3; | ||
| 3497 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 3498 | else if ((ifd = fopen("Makefile", "r")) != NULL) | ||
| 3499 | makefile = "Makefile"; | ||
| 3500 | #endif | ||
| 3501 | else | ||
| 3502 | error("no makefile found"); | ||
| 3503 | goto read_makefile; | ||
| 3504 | } | ||
| 3505 | |||
| 3506 | while ((file = llist_pop(&makefiles))) { | ||
| 3507 | if (strcmp(file, "-") == 0) { // Can use stdin as makefile | ||
| 3508 | ifd = stdin; | ||
| 3509 | makefile = "stdin"; | ||
| 3510 | } else { | ||
| 3511 | ifd = xfopen_for_read(file); | ||
| 3512 | makefile = file; | ||
| 3513 | } | ||
| 3514 | read_makefile: | ||
| 3515 | input(ifd, 0); | ||
| 3516 | fclose(ifd); | ||
| 3517 | makefile = NULL; | ||
| 3518 | } | ||
| 3519 | |||
| 3520 | if (print) | ||
| 3521 | print_details(); | ||
| 3522 | |||
| 3523 | mark_special(".SILENT", OPT_s, N_SILENT); | ||
| 3524 | mark_special(".IGNORE", OPT_i, N_IGNORE); | ||
| 3525 | mark_special(".PRECIOUS", OPT_precious, N_PRECIOUS); | ||
| 3526 | if (!POSIX_2017) | ||
| 3527 | mark_special(".PHONY", OPT_phony, N_PHONY); | ||
| 3528 | |||
| 3529 | if (posix) { | ||
| 3530 | // In POSIX mode only targets should now be in argv. | ||
| 3531 | found_target = FALSE; | ||
| 3532 | for (char **a = argv; *a; a++) { | ||
| 3533 | if (!strchr(*a, '=')) | ||
| 3534 | found_target = TRUE; | ||
| 3535 | else if (found_target) | ||
| 3536 | error("macro assignments must precede targets"); | ||
| 3537 | } | ||
| 3538 | } | ||
| 3539 | |||
| 3540 | estat = 0; | ||
| 3541 | found_target = FALSE; | ||
| 3542 | for (; *argv; argv++) { | ||
| 3543 | // Skip macro assignments. | ||
| 3544 | if (strchr(*argv, '=')) | ||
| 3545 | continue; | ||
| 3546 | found_target = TRUE; | ||
| 3547 | estat |= make(newname(*argv), 0); | ||
| 3548 | } | ||
| 3549 | if (!found_target) { | ||
| 3550 | if (!firstname) | ||
| 3551 | error("no targets defined"); | ||
| 3552 | estat = make(firstname, 0); | ||
| 3553 | } | ||
| 3554 | |||
| 3555 | #if ENABLE_FEATURE_CLEAN_UP | ||
| 3556 | freenames(); | ||
| 3557 | freemacros(); | ||
| 3558 | llist_free(makefiles, NULL); | ||
| 3559 | llist_free(dirs, NULL); | ||
| 3560 | #endif | ||
| 3561 | |||
| 3562 | return estat & MAKE_FAILURE; | ||
| 3563 | } | ||
diff --git a/miscutils/man.c b/miscutils/man.c index 6fa1fbfdc..38c1b9aa3 100644 --- a/miscutils/man.c +++ b/miscutils/man.c | |||
| @@ -199,8 +199,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path) | |||
| 199 | if (path) while (*path) { | 199 | if (path) while (*path) { |
| 200 | char *next_path; | 200 | char *next_path; |
| 201 | char **path_element; | 201 | char **path_element; |
| 202 | 202 | next_path = strchr(path, PATH_SEP); | |
| 203 | next_path = strchr(path, ':'); | ||
| 204 | if (next_path) { | 203 | if (next_path) { |
| 205 | if (next_path == path) /* "::"? */ | 204 | if (next_path == path) /* "::"? */ |
| 206 | goto next; | 205 | goto next; |
| @@ -223,7 +222,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path) | |||
| 223 | if (!next_path) | 222 | if (!next_path) |
| 224 | break; | 223 | break; |
| 225 | /* "path" may be a result of getenv(), be nice and don't mangle it */ | 224 | /* "path" may be a result of getenv(), be nice and don't mangle it */ |
| 226 | *next_path = ':'; | 225 | *next_path = PATH_SEP; |
| 227 | next: | 226 | next: |
| 228 | path = next_path + 1; | 227 | path = next_path + 1; |
| 229 | } | 228 | } |
| @@ -260,11 +259,24 @@ int man_main(int argc UNUSED_PARAM, char **argv) | |||
| 260 | int count_mp; | 259 | int count_mp; |
| 261 | int opt, not_found; | 260 | int opt, not_found; |
| 262 | char *token[2]; | 261 | char *token[2]; |
| 262 | #if ENABLE_PLATFORM_MINGW32 | ||
| 263 | char **ptr; | ||
| 264 | char *relpath; | ||
| 265 | const char *mpl[] = { "/usr/man", "/usr/share/man", NULL, NULL }; | ||
| 266 | #endif | ||
| 263 | 267 | ||
| 264 | INIT_G(); | 268 | INIT_G(); |
| 265 | 269 | ||
| 266 | opt = getopt32(argv, "^+" "aw" "\0" "-1"/*at least one arg*/); | 270 | opt = getopt32(argv, "^+" "aw" "\0" "-1"/*at least one arg*/); |
| 267 | argv += optind; | 271 | argv += optind; |
| 272 | #if ENABLE_PLATFORM_MINGW32 | ||
| 273 | /* add system drive prefix to filenames, if necessary */ | ||
| 274 | for (ptr = argv; *ptr; ++ptr) { | ||
| 275 | if (strchr(*ptr, '/') || strchr(*ptr, '\\')) | ||
| 276 | *ptr = xabsolute_path(*ptr); | ||
| 277 | } | ||
| 278 | chdir_system_drive(); | ||
| 279 | #endif | ||
| 268 | 280 | ||
| 269 | conf_sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); | 281 | conf_sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); |
| 270 | 282 | ||
| @@ -302,11 +314,24 @@ int man_main(int argc UNUSED_PARAM, char **argv) | |||
| 302 | } | 314 | } |
| 303 | config_close(parser); | 315 | config_close(parser); |
| 304 | 316 | ||
| 317 | #if ENABLE_PLATFORM_MINGW32 | ||
| 318 | /* allow man pages to be stored relative to the executable */ | ||
| 319 | relpath = exe_relative_path("man"); | ||
| 320 | |||
| 321 | if (!man_path_list) { | ||
| 322 | mpl[2] = relpath; | ||
| 323 | man_path_list = (char**)mpl; | ||
| 324 | } | ||
| 325 | else { | ||
| 326 | man_path_list = add_MANPATH(man_path_list, &count_mp, relpath); | ||
| 327 | } | ||
| 328 | #else | ||
| 305 | if (!man_path_list) { | 329 | if (!man_path_list) { |
| 306 | static const char *const mpl[] ALIGN_PTR = { "/usr/man", "/usr/share/man", NULL }; | 330 | static const char *const mpl[] ALIGN_PTR = { "/usr/man", "/usr/share/man", NULL }; |
| 307 | man_path_list = (char**)mpl; | 331 | man_path_list = (char**)mpl; |
| 308 | /*count_mp = 2; - not used below anyway */ | 332 | /*count_mp = 2; - not used below anyway */ |
| 309 | } | 333 | } |
| 334 | #endif | ||
| 310 | 335 | ||
| 311 | { | 336 | { |
| 312 | /* environment overrides setting from man.config */ | 337 | /* environment overrides setting from man.config */ |
diff --git a/miscutils/time.c b/miscutils/time.c index 77d35a832..9a1039be9 100644 --- a/miscutils/time.c +++ b/miscutils/time.c | |||
| @@ -22,10 +22,17 @@ | |||
| 22 | //kbuild:lib-$(CONFIG_TIME) += time.o | 22 | //kbuild:lib-$(CONFIG_TIME) += time.o |
| 23 | 23 | ||
| 24 | //usage:#define time_trivial_usage | 24 | //usage:#define time_trivial_usage |
| 25 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
| 25 | //usage: "[-vpa] [-o FILE] PROG ARGS" | 26 | //usage: "[-vpa] [-o FILE] PROG ARGS" |
| 27 | //usage: ) | ||
| 28 | //usage: IF_PLATFORM_MINGW32( | ||
| 29 | //usage: "[-pa] [-f FMT] [-o FILE] PROG ARGS" | ||
| 30 | //usage: ) | ||
| 26 | //usage:#define time_full_usage "\n\n" | 31 | //usage:#define time_full_usage "\n\n" |
| 27 | //usage: "Run PROG, display resource usage when it exits\n" | 32 | //usage: "Run PROG, display resource usage when it exits\n" |
| 33 | //usage: IF_NOT_PLATFORM_MINGW32( | ||
| 28 | //usage: "\n -v Verbose" | 34 | //usage: "\n -v Verbose" |
| 35 | //usage: ) | ||
| 29 | //usage: "\n -p POSIX output format" | 36 | //usage: "\n -p POSIX output format" |
| 30 | //usage: "\n -f FMT Custom format" | 37 | //usage: "\n -f FMT Custom format" |
| 31 | //usage: "\n -o FILE Write result to FILE" | 38 | //usage: "\n -o FILE Write result to FILE" |
| @@ -57,6 +64,7 @@ static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T"; | |||
| 57 | /* The output format for the -p option .*/ | 64 | /* The output format for the -p option .*/ |
| 58 | static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S"; | 65 | static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S"; |
| 59 | 66 | ||
| 67 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 60 | /* Format string for printing all statistics verbosely. | 68 | /* Format string for printing all statistics verbosely. |
| 61 | Keep this output to 24 lines so users on terminals can see it all.*/ | 69 | Keep this output to 24 lines so users on terminals can see it all.*/ |
| 62 | static const char long_format[] ALIGN1 = | 70 | static const char long_format[] ALIGN1 = |
| @@ -83,6 +91,7 @@ static const char long_format[] ALIGN1 = | |||
| 83 | "\tSignals delivered: %k\n" | 91 | "\tSignals delivered: %k\n" |
| 84 | "\tPage size (bytes): %Z\n" | 92 | "\tPage size (bytes): %Z\n" |
| 85 | "\tExit status: %x"; | 93 | "\tExit status: %x"; |
| 94 | #endif | ||
| 86 | 95 | ||
| 87 | /* Wait for and fill in data on child process PID. | 96 | /* Wait for and fill in data on child process PID. |
| 88 | Return 0 on error, 1 if ok. */ | 97 | Return 0 on error, 1 if ok. */ |
| @@ -93,7 +102,11 @@ static void resuse_end(pid_t pid, resource_t *resp) | |||
| 93 | 102 | ||
| 94 | /* Ignore signals, but don't ignore the children. When wait3 | 103 | /* Ignore signals, but don't ignore the children. When wait3 |
| 95 | * returns the child process, set the time the command finished. */ | 104 | * returns the child process, set the time the command finished. */ |
| 105 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 96 | while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) { | 106 | while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) { |
| 107 | #else | ||
| 108 | while ((caught=mingw_wait3(pid, &resp->waitstatus, 0, &resp->ru)) != pid) { | ||
| 109 | #endif | ||
| 97 | if (caught == -1 && errno != EINTR) { | 110 | if (caught == -1 && errno != EINTR) { |
| 98 | bb_simple_perror_msg("wait"); | 111 | bb_simple_perror_msg("wait"); |
| 99 | return; | 112 | return; |
| @@ -189,9 +202,11 @@ static unsigned long ptok(const unsigned pagesize, const unsigned long pages) | |||
| 189 | 202 | ||
| 190 | static void summarize(const char *fmt, char **command, resource_t *resp) | 203 | static void summarize(const char *fmt, char **command, resource_t *resp) |
| 191 | { | 204 | { |
| 205 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 192 | unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */ | 206 | unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */ |
| 193 | unsigned cpu_ticks; /* Same, in "CPU ticks" */ | 207 | unsigned cpu_ticks; /* Same, in "CPU ticks" */ |
| 194 | unsigned pagesize = bb_getpagesize(); | 208 | unsigned pagesize = bb_getpagesize(); |
| 209 | #endif | ||
| 195 | 210 | ||
| 196 | /* Impossible: we do not use WUNTRACED flag in wait()... | 211 | /* Impossible: we do not use WUNTRACED flag in wait()... |
| 197 | if (WIFSTOPPED(resp->waitstatus)) | 212 | if (WIFSTOPPED(resp->waitstatus)) |
| @@ -205,6 +220,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 205 | printf("Command exited with non-zero status %u\n", | 220 | printf("Command exited with non-zero status %u\n", |
| 206 | WEXITSTATUS(resp->waitstatus)); | 221 | WEXITSTATUS(resp->waitstatus)); |
| 207 | 222 | ||
| 223 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 208 | vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000 | 224 | vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000 |
| 209 | + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000; | 225 | + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000; |
| 210 | 226 | ||
| @@ -215,6 +231,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 215 | cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000; | 231 | cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000; |
| 216 | #endif | 232 | #endif |
| 217 | if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */ | 233 | if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */ |
| 234 | #endif | ||
| 218 | 235 | ||
| 219 | while (*fmt) { | 236 | while (*fmt) { |
| 220 | /* Handle leading literal part */ | 237 | /* Handle leading literal part */ |
| @@ -242,6 +259,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 242 | case 'C': /* The command that got timed. */ | 259 | case 'C': /* The command that got timed. */ |
| 243 | printargv(command); | 260 | printargv(command); |
| 244 | break; | 261 | break; |
| 262 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 245 | case 'D': /* Average unshared data size. */ | 263 | case 'D': /* Average unshared data size. */ |
| 246 | /* (linux kernel sets ru_idrss/isrss/ixrss to 0, | 264 | /* (linux kernel sets ru_idrss/isrss/ixrss to 0, |
| 247 | * docs say the value is in kbytes, so ptok() is wrong) */ | 265 | * docs say the value is in kbytes, so ptok() is wrong) */ |
| @@ -251,6 +269,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 251 | ) / cpu_ticks | 269 | ) / cpu_ticks |
| 252 | ); | 270 | ); |
| 253 | break; | 271 | break; |
| 272 | #endif | ||
| 254 | case 'E': { /* Elapsed real (wall clock) time. */ | 273 | case 'E': { /* Elapsed real (wall clock) time. */ |
| 255 | unsigned seconds = resp->elapsed_ms / 1000; | 274 | unsigned seconds = resp->elapsed_ms / 1000; |
| 256 | if (seconds >= 3600) /* One hour -> h:m:s. */ | 275 | if (seconds >= 3600) /* One hour -> h:m:s. */ |
| @@ -265,6 +284,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 265 | (unsigned)(resp->elapsed_ms / 10) % 100); | 284 | (unsigned)(resp->elapsed_ms / 10) % 100); |
| 266 | break; | 285 | break; |
| 267 | } | 286 | } |
| 287 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 268 | case 'F': /* Major page faults. */ | 288 | case 'F': /* Major page faults. */ |
| 269 | printf("%lu", resp->ru.ru_majflt); | 289 | printf("%lu", resp->ru.ru_majflt); |
| 270 | break; | 290 | break; |
| @@ -297,6 +317,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 297 | case 'R': /* Minor page faults (reclaims). */ | 317 | case 'R': /* Minor page faults (reclaims). */ |
| 298 | printf("%lu", resp->ru.ru_minflt); | 318 | printf("%lu", resp->ru.ru_minflt); |
| 299 | break; | 319 | break; |
| 320 | #endif | ||
| 300 | case 'S': /* System time. */ | 321 | case 'S': /* System time. */ |
| 301 | printf("%u.%02u", | 322 | printf("%u.%02u", |
| 302 | (unsigned)resp->ru.ru_stime.tv_sec, | 323 | (unsigned)resp->ru.ru_stime.tv_sec, |
| @@ -331,6 +352,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 331 | (unsigned)(resp->ru.ru_utime.tv_sec % 60), | 352 | (unsigned)(resp->ru.ru_utime.tv_sec % 60), |
| 332 | (unsigned)(resp->ru.ru_utime.tv_usec / 10000)); | 353 | (unsigned)(resp->ru.ru_utime.tv_usec / 10000)); |
| 333 | break; | 354 | break; |
| 355 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 334 | case 'W': /* Times swapped out. */ | 356 | case 'W': /* Times swapped out. */ |
| 335 | printf("%lu", resp->ru.ru_nswap); | 357 | printf("%lu", resp->ru.ru_nswap); |
| 336 | break; | 358 | break; |
| @@ -343,11 +365,13 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 343 | case 'c': /* Involuntary context switches. */ | 365 | case 'c': /* Involuntary context switches. */ |
| 344 | printf("%lu", resp->ru.ru_nivcsw); | 366 | printf("%lu", resp->ru.ru_nivcsw); |
| 345 | break; | 367 | break; |
| 368 | #endif | ||
| 346 | case 'e': /* Elapsed real time in seconds. */ | 369 | case 'e': /* Elapsed real time in seconds. */ |
| 347 | printf("%u.%02u", | 370 | printf("%u.%02u", |
| 348 | (unsigned)resp->elapsed_ms / 1000, | 371 | (unsigned)resp->elapsed_ms / 1000, |
| 349 | (unsigned)(resp->elapsed_ms / 10) % 100); | 372 | (unsigned)(resp->elapsed_ms / 10) % 100); |
| 350 | break; | 373 | break; |
| 374 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 351 | case 'k': /* Signals delivered. */ | 375 | case 'k': /* Signals delivered. */ |
| 352 | printf("%lu", resp->ru.ru_nsignals); | 376 | printf("%lu", resp->ru.ru_nsignals); |
| 353 | break; | 377 | break; |
| @@ -366,6 +390,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 366 | case 'w': /* Voluntary context switches. */ | 390 | case 'w': /* Voluntary context switches. */ |
| 367 | printf("%lu", resp->ru.ru_nvcsw); | 391 | printf("%lu", resp->ru.ru_nvcsw); |
| 368 | break; | 392 | break; |
| 393 | #endif | ||
| 369 | case 'x': /* Exit status. */ | 394 | case 'x': /* Exit status. */ |
| 370 | printf("%u", WEXITSTATUS(resp->waitstatus)); | 395 | printf("%u", WEXITSTATUS(resp->waitstatus)); |
| 371 | break; | 396 | break; |
| @@ -409,6 +434,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp) | |||
| 409 | static void run_command(char *const *cmd, resource_t *resp) | 434 | static void run_command(char *const *cmd, resource_t *resp) |
| 410 | { | 435 | { |
| 411 | pid_t pid; | 436 | pid_t pid; |
| 437 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 412 | void (*interrupt_signal)(int); | 438 | void (*interrupt_signal)(int); |
| 413 | void (*quit_signal)(int); | 439 | void (*quit_signal)(int); |
| 414 | 440 | ||
| @@ -429,6 +455,13 @@ static void run_command(char *const *cmd, resource_t *resp) | |||
| 429 | /* Re-enable signals. */ | 455 | /* Re-enable signals. */ |
| 430 | signal(SIGINT, interrupt_signal); | 456 | signal(SIGINT, interrupt_signal); |
| 431 | signal(SIGQUIT, quit_signal); | 457 | signal(SIGQUIT, quit_signal); |
| 458 | #else | ||
| 459 | resp->elapsed_ms = monotonic_ms(); | ||
| 460 | if ((pid=spawn((char **)cmd)) == -1) | ||
| 461 | bb_perror_msg_and_die("can't execute %s", cmd[0]); | ||
| 462 | |||
| 463 | resuse_end(pid, resp); | ||
| 464 | #endif | ||
| 432 | } | 465 | } |
| 433 | 466 | ||
| 434 | int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 467 | int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
| @@ -442,20 +475,35 @@ int time_main(int argc UNUSED_PARAM, char **argv) | |||
| 442 | int opt; | 475 | int opt; |
| 443 | int ex; | 476 | int ex; |
| 444 | enum { | 477 | enum { |
| 478 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 445 | OPT_v = (1 << 0), | 479 | OPT_v = (1 << 0), |
| 446 | OPT_p = (1 << 1), | 480 | OPT_p = (1 << 1), |
| 447 | OPT_a = (1 << 2), | 481 | OPT_a = (1 << 2), |
| 448 | OPT_o = (1 << 3), | 482 | OPT_o = (1 << 3), |
| 449 | OPT_f = (1 << 4), | 483 | OPT_f = (1 << 4), |
| 484 | #else | ||
| 485 | OPT_p = (1 << 0), | ||
| 486 | OPT_a = (1 << 1), | ||
| 487 | OPT_o = (1 << 2), | ||
| 488 | OPT_f = (1 << 3), | ||
| 489 | #endif | ||
| 450 | }; | 490 | }; |
| 451 | 491 | ||
| 452 | /* "+": stop on first non-option */ | 492 | /* "+": stop on first non-option */ |
| 493 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 453 | opt = getopt32(argv, "^+" "vpao:f:" "\0" "-1"/*at least one arg*/, | 494 | opt = getopt32(argv, "^+" "vpao:f:" "\0" "-1"/*at least one arg*/, |
| 454 | &output_filename, &output_format | 495 | &output_filename, &output_format |
| 455 | ); | 496 | ); |
| 497 | #else | ||
| 498 | opt = getopt32(argv, "^+" "pao:f:" "\0" "-1"/*at least one arg*/, | ||
| 499 | &output_filename, &output_format | ||
| 500 | ); | ||
| 501 | #endif | ||
| 456 | argv += optind; | 502 | argv += optind; |
| 503 | #if !ENABLE_PLATFORM_MINGW32 | ||
| 457 | if (opt & OPT_v) | 504 | if (opt & OPT_v) |
| 458 | output_format = long_format; | 505 | output_format = long_format; |
| 506 | #endif | ||
| 459 | if (opt & OPT_p) | 507 | if (opt & OPT_p) |
| 460 | output_format = posix_format; | 508 | output_format = posix_format; |
| 461 | output_fd = STDERR_FILENO; | 509 | output_fd = STDERR_FILENO; |
| @@ -476,6 +524,9 @@ int time_main(int argc UNUSED_PARAM, char **argv) | |||
| 476 | 524 | ||
| 477 | /* Cheat. printf's are shorter :) */ | 525 | /* Cheat. printf's are shorter :) */ |
| 478 | xdup2(output_fd, STDOUT_FILENO); | 526 | xdup2(output_fd, STDOUT_FILENO); |
| 527 | #if ENABLE_PLATFORM_MINGW32 | ||
| 528 | setvbuf(stdout, NULL, _IOFBF, 256); | ||
| 529 | #endif | ||
| 479 | summarize(output_format, argv, &res); | 530 | summarize(output_format, argv, &res); |
| 480 | 531 | ||
| 481 | ex = WEXITSTATUS(res.waitstatus); | 532 | ex = WEXITSTATUS(res.waitstatus); |
diff --git a/miscutils/ts.c b/miscutils/ts.c index c5c1879df..fb669b858 100644 --- a/miscutils/ts.c +++ b/miscutils/ts.c | |||
| @@ -25,6 +25,9 @@ int ts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | |||
| 25 | int ts_main(int argc UNUSED_PARAM, char **argv) | 25 | int ts_main(int argc UNUSED_PARAM, char **argv) |
| 26 | { | 26 | { |
| 27 | struct timeval base; | 27 | struct timeval base; |
| 28 | #if ENABLE_PLATFORM_MINGW32 | ||
| 29 | time_t t; | ||
| 30 | #endif | ||
| 28 | unsigned opt; | 31 | unsigned opt; |
| 29 | char *frac; | 32 | char *frac; |
| 30 | char *fmt_dt2str; | 33 | char *fmt_dt2str; |
| @@ -73,7 +76,12 @@ int ts_main(int argc UNUSED_PARAM, char **argv) | |||
| 73 | if (opt & 1) /* -i */ | 76 | if (opt & 1) /* -i */ |
| 74 | base = ts1; | 77 | base = ts1; |
| 75 | } | 78 | } |
| 79 | #if ENABLE_PLATFORM_MINGW32 | ||
| 80 | t = ts.tv_sec; | ||
| 81 | localtime_r(&t, &tm_time); | ||
| 82 | #else | ||
| 76 | localtime_r(&ts.tv_sec, &tm_time); | 83 | localtime_r(&ts.tv_sec, &tm_time); |
| 84 | #endif | ||
| 77 | strftime(date_buf, COMMON_BUFSIZE, fmt_dt2str, &tm_time); | 85 | strftime(date_buf, COMMON_BUFSIZE, fmt_dt2str, &tm_time); |
| 78 | if (!frac) { | 86 | if (!frac) { |
| 79 | printf("%s %s", date_buf, line); | 87 | printf("%s %s", date_buf, line); |
