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 | 3382 | ||||
-rw-r--r-- | miscutils/man.c | 31 | ||||
-rw-r--r-- | miscutils/time.c | 51 | ||||
-rw-r--r-- | miscutils/ts.c | 8 |
12 files changed, 5827 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 8a0525cb7..467c76e2a 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..7316408bf --- /dev/null +++ b/miscutils/make.c | |||
@@ -0,0 +1,3382 @@ | |||
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 | #define HTABSIZE 39 | ||
261 | |||
262 | struct globals { | ||
263 | uint32_t opts; | ||
264 | const char *makefile; | ||
265 | llist_t *makefiles; | ||
266 | llist_t *dirs; | ||
267 | struct name *namehead[HTABSIZE]; | ||
268 | struct macro *macrohead[HTABSIZE]; | ||
269 | struct name *firstname; | ||
270 | struct name *target; | ||
271 | time_t ar_mtime; | ||
272 | int lineno; // Physical line number in file | ||
273 | int dispno; // Line number for display purposes | ||
274 | const char *rulepos; | ||
275 | int rule_idx; | ||
276 | #define IF_MAX 10 | ||
277 | uint8_t clevel; | ||
278 | uint8_t cstate[IF_MAX + 1]; | ||
279 | int numjobs; | ||
280 | #if ENABLE_FEATURE_MAKE_POSIX | ||
281 | bool posix; | ||
282 | bool seen_first; | ||
283 | llist_t *pragmas; | ||
284 | unsigned char pragma; | ||
285 | unsigned char posix_level; | ||
286 | #endif | ||
287 | } FIX_ALIASING; | ||
288 | |||
289 | #define G (*(struct globals*)bb_common_bufsiz1) | ||
290 | #define INIT_G() do { \ | ||
291 | setup_common_bufsiz(); \ | ||
292 | } while (0) | ||
293 | |||
294 | #define opts (G.opts) | ||
295 | #define makefile (G.makefile) | ||
296 | #define makefiles (G.makefiles) | ||
297 | #define dirs (G.dirs) | ||
298 | #define namehead (G.namehead) | ||
299 | #define macrohead (G.macrohead) | ||
300 | #define firstname (G.firstname) | ||
301 | #define target (G.target) | ||
302 | #define ar_mtime (G.ar_mtime) | ||
303 | #define lineno (G.lineno) | ||
304 | #define dispno (G.dispno) | ||
305 | #define rulepos (G.rulepos) | ||
306 | #define rule_idx (G.rule_idx) | ||
307 | #define clevel (G.clevel) | ||
308 | #define cstate (G.cstate) | ||
309 | #define numjobs (G.numjobs) | ||
310 | #if ENABLE_FEATURE_MAKE_POSIX | ||
311 | #define posix (G.posix) | ||
312 | #define seen_first (G.seen_first) | ||
313 | #define pragmas (G.pragmas) | ||
314 | #define pragma (G.pragma) | ||
315 | #define posix_level (G.posix_level) | ||
316 | #else | ||
317 | #define posix 0 | ||
318 | #define pragma 0 | ||
319 | #define posix_level DEFAULT_POSIX_LEVEL | ||
320 | #endif | ||
321 | |||
322 | static int make(struct name *np, int level); | ||
323 | |||
324 | // Return TRUE if c is allowed in a POSIX 2017 macro or target name | ||
325 | #define ispname(c) (isalpha(c) || isdigit(c) || c == '.' || c == '_') | ||
326 | // Return TRUE if c is in the POSIX 'portable filename character set' | ||
327 | #define isfname(c) (ispname(c) || c == '-') | ||
328 | |||
329 | /* | ||
330 | * Utility functions. | ||
331 | */ | ||
332 | |||
333 | /* | ||
334 | * Print message, with makefile and line number if possible. | ||
335 | */ | ||
336 | static void | ||
337 | vwarning(FILE *stream, const char *msg, va_list list) | ||
338 | { | ||
339 | fprintf(stream, "%s: ", applet_name); | ||
340 | if (makefile) | ||
341 | fprintf(stream, "(%s:%d): ", makefile, dispno); | ||
342 | vfprintf(stream, msg, list); | ||
343 | fputc('\n', stream); | ||
344 | } | ||
345 | |||
346 | /* | ||
347 | * Diagnostic handler. Print message to standard error. | ||
348 | */ | ||
349 | static void | ||
350 | diagnostic(const char *msg, ...) | ||
351 | { | ||
352 | va_list list; | ||
353 | |||
354 | va_start(list, msg); | ||
355 | vwarning(stderr, msg, list); | ||
356 | va_end(list); | ||
357 | } | ||
358 | |||
359 | /* | ||
360 | * Error handler. Print message and exit. | ||
361 | */ | ||
362 | static void error(const char *msg, ...) NORETURN; | ||
363 | static void | ||
364 | error(const char *msg, ...) | ||
365 | { | ||
366 | va_list list; | ||
367 | |||
368 | va_start(list, msg); | ||
369 | vwarning(stderr, msg, list); | ||
370 | va_end(list); | ||
371 | exit(2); | ||
372 | } | ||
373 | |||
374 | static void error_unexpected(const char *s) NORETURN; | ||
375 | static void | ||
376 | error_unexpected(const char *s) | ||
377 | { | ||
378 | error("unexpected %s", s); | ||
379 | } | ||
380 | |||
381 | static void error_in_inference_rule(const char *s) NORETURN; | ||
382 | static void | ||
383 | error_in_inference_rule(const char *s) | ||
384 | { | ||
385 | error("%s in inference rule", s); | ||
386 | } | ||
387 | |||
388 | static void | ||
389 | error_not_allowed(const char *s, const char *t) | ||
390 | { | ||
391 | error("%s not allowed for %s", s, t); | ||
392 | } | ||
393 | |||
394 | static void | ||
395 | warning(const char *msg, ...) | ||
396 | { | ||
397 | va_list list; | ||
398 | |||
399 | va_start(list, msg); | ||
400 | vwarning(stdout, msg, list); | ||
401 | va_end(list); | ||
402 | } | ||
403 | |||
404 | static char * | ||
405 | auto_concat(const char *s1, const char *s2) | ||
406 | { | ||
407 | return auto_string(xasprintf("%s%s", s1, s2)); | ||
408 | } | ||
409 | |||
410 | #if !ENABLE_PLATFORM_MINGW32 | ||
411 | /* | ||
412 | * Append a word to a space-separated string of words. The first | ||
413 | * call should use a NULL pointer for str, subsequent calls should | ||
414 | * pass an allocated string which will be freed. | ||
415 | */ | ||
416 | static char * | ||
417 | xappendword(const char *str, const char *word) | ||
418 | { | ||
419 | char *newstr = str ? xasprintf("%s %s", str, word) : xstrdup(word); | ||
420 | free((void *)str); | ||
421 | return newstr; | ||
422 | } | ||
423 | #endif | ||
424 | |||
425 | static unsigned int | ||
426 | getbucket(const char *name) | ||
427 | { | ||
428 | unsigned int hashval = 0; | ||
429 | const unsigned char *p = (unsigned char *)name; | ||
430 | |||
431 | while (*p) | ||
432 | hashval ^= (hashval << 5) + (hashval >> 2) + *p++; | ||
433 | return hashval % HTABSIZE; | ||
434 | } | ||
435 | |||
436 | /* | ||
437 | * Add a prerequisite to the end of the supplied list. | ||
438 | */ | ||
439 | static void | ||
440 | newdep(struct depend **dphead, struct name *np) | ||
441 | { | ||
442 | while (*dphead) | ||
443 | dphead = &(*dphead)->d_next; | ||
444 | *dphead = xzalloc(sizeof(struct depend)); | ||
445 | /*(*dphead)->d_next = NULL; - xzalloc did it */ | ||
446 | (*dphead)->d_name = np; | ||
447 | /*(*dphead)->d_refcnt = 0; */ | ||
448 | } | ||
449 | |||
450 | static void | ||
451 | freedeps(struct depend *dp) | ||
452 | { | ||
453 | if (dp && --dp->d_refcnt <= 0) | ||
454 | llist_free((llist_t *)dp, NULL); | ||
455 | } | ||
456 | |||
457 | /* | ||
458 | * Add a command to the end of the supplied list of commands. | ||
459 | */ | ||
460 | static void | ||
461 | newcmd(struct cmd **cphead, char *str) | ||
462 | { | ||
463 | while (isspace(*str)) | ||
464 | str++; | ||
465 | |||
466 | while (*cphead) | ||
467 | cphead = &(*cphead)->c_next; | ||
468 | *cphead = xzalloc(sizeof(struct cmd)); | ||
469 | /*(*cphead)->c_next = NULL; - xzalloc did it */ | ||
470 | (*cphead)->c_cmd = xstrdup(str); | ||
471 | /*(*cphead)->c_refcnt = 0; */ | ||
472 | if (makefile) | ||
473 | (*cphead)->c_makefile = xstrdup(makefile); | ||
474 | (*cphead)->c_dispno = dispno; | ||
475 | } | ||
476 | |||
477 | static void | ||
478 | freecmds(struct cmd *cp) | ||
479 | { | ||
480 | struct cmd *nextcp; | ||
481 | |||
482 | if (cp && --cp->c_refcnt <= 0) { | ||
483 | for (; cp; cp = nextcp) { | ||
484 | nextcp = cp->c_next; | ||
485 | free(cp->c_cmd); | ||
486 | free((void *)cp->c_makefile); | ||
487 | free(cp); | ||
488 | } | ||
489 | } | ||
490 | } | ||
491 | |||
492 | static struct name * | ||
493 | findname(const char *name) | ||
494 | { | ||
495 | struct name *np = namehead[getbucket(name)]; | ||
496 | return (struct name *)llist_find_str((llist_t *)np, name); | ||
497 | } | ||
498 | |||
499 | static int | ||
500 | check_name(const char *name) | ||
501 | { | ||
502 | const char *s; | ||
503 | |||
504 | #if ENABLE_PLATFORM_MINGW32 | ||
505 | if (!posix || (pragma & P_WINDOWS)) { | ||
506 | if (isalpha(name[0]) && name[1] == ':' && name[2] == '/') { | ||
507 | name += 3; | ||
508 | } | ||
509 | } | ||
510 | #endif | ||
511 | if (!posix) { | ||
512 | for (s = name; *s; ++s) { | ||
513 | if (*s == '=') | ||
514 | return FALSE; | ||
515 | } | ||
516 | return TRUE; | ||
517 | } | ||
518 | |||
519 | for (s = name; *s; ++s) { | ||
520 | if ((pragma & P_TARGET_NAME) || !POSIX_2017 ? | ||
521 | !(isfname(*s) || *s == '/') : !ispname(*s)) | ||
522 | return FALSE; | ||
523 | } | ||
524 | return TRUE; | ||
525 | } | ||
526 | |||
527 | static char *splitlib(const char *name, char **member); | ||
528 | |||
529 | static int | ||
530 | is_valid_target(const char *name) | ||
531 | { | ||
532 | char *archive, *member = NULL; | ||
533 | int ret; | ||
534 | |||
535 | /* Names of the form 'lib(member)' are referred to as 'expressions' | ||
536 | * in POSIX and are subjected to special treatment. The 'lib' | ||
537 | * and 'member' elements must each be a valid target name. */ | ||
538 | archive = splitlib(name, &member); | ||
539 | ret = check_name(archive) && (member == NULL || check_name(member)); | ||
540 | free(archive); | ||
541 | |||
542 | return ret; | ||
543 | } | ||
544 | |||
545 | #if ENABLE_FEATURE_MAKE_POSIX | ||
546 | static int | ||
547 | potentially_valid_target(const char *name) | ||
548 | { | ||
549 | int ret = FALSE; | ||
550 | |||
551 | if (!(pragma & P_TARGET_NAME)) { | ||
552 | pragma |= P_TARGET_NAME; | ||
553 | ret = is_valid_target(name); | ||
554 | pragma &= ~P_TARGET_NAME; | ||
555 | } | ||
556 | return ret; | ||
557 | } | ||
558 | #endif | ||
559 | |||
560 | /* | ||
561 | * Intern a name. Return a pointer to the name struct | ||
562 | */ | ||
563 | static struct name * | ||
564 | newname(const char *name) | ||
565 | { | ||
566 | struct name *np = findname(name); | ||
567 | |||
568 | if (np == NULL) { | ||
569 | unsigned int bucket; | ||
570 | |||
571 | if (!is_valid_target(name)) | ||
572 | #if ENABLE_FEATURE_MAKE_POSIX | ||
573 | error("invalid target name '%s'%s", name, | ||
574 | potentially_valid_target(name) ? | ||
575 | ": allow with pragma target_name" : ""); | ||
576 | #else | ||
577 | error("invalid target name '%s'", name); | ||
578 | #endif | ||
579 | |||
580 | bucket = getbucket(name); | ||
581 | np = xzalloc(sizeof(struct name)); | ||
582 | np->n_next = namehead[bucket]; | ||
583 | namehead[bucket] = np; | ||
584 | np->n_name = xstrdup(name); | ||
585 | /*np->n_rule = NULL; - xzalloc did it */ | ||
586 | /*np->n_tim = (struct timespec){0, 0}; */ | ||
587 | /*np->n_flag = 0; */ | ||
588 | } | ||
589 | return np; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * Return the commands on the first rule that has them or NULL. | ||
594 | */ | ||
595 | static struct cmd * | ||
596 | getcmd(struct name *np) | ||
597 | { | ||
598 | struct rule *rp; | ||
599 | |||
600 | if (np == NULL) | ||
601 | return NULL; | ||
602 | |||
603 | for (rp = np->n_rule; rp; rp = rp->r_next) | ||
604 | if (rp->r_cmd) | ||
605 | return rp->r_cmd; | ||
606 | return NULL; | ||
607 | } | ||
608 | |||
609 | #if ENABLE_FEATURE_CLEAN_UP | ||
610 | static void | ||
611 | freenames(void) | ||
612 | { | ||
613 | int i; | ||
614 | struct name *np, *nextnp; | ||
615 | |||
616 | for (i = 0; i < HTABSIZE; i++) { | ||
617 | for (np = namehead[i]; np; np = nextnp) { | ||
618 | nextnp = np->n_next; | ||
619 | free(np->n_name); | ||
620 | freerules(np->n_rule); | ||
621 | free(np); | ||
622 | } | ||
623 | } | ||
624 | } | ||
625 | #endif | ||
626 | |||
627 | static void | ||
628 | freerules(struct rule *rp) | ||
629 | { | ||
630 | struct rule *nextrp; | ||
631 | |||
632 | for (; rp; rp = nextrp) { | ||
633 | nextrp = rp->r_next; | ||
634 | freedeps(rp->r_dep); | ||
635 | freecmds(rp->r_cmd); | ||
636 | free(rp); | ||
637 | } | ||
638 | } | ||
639 | |||
640 | static void * | ||
641 | inc_ref(void *vp) | ||
642 | { | ||
643 | if (vp) { | ||
644 | struct depend *dp = vp; | ||
645 | if (dp->d_refcnt == INT_MAX) | ||
646 | bb_die_memory_exhausted(); | ||
647 | dp->d_refcnt++; | ||
648 | } | ||
649 | return vp; | ||
650 | } | ||
651 | |||
652 | #if ENABLE_FEATURE_MAKE_POSIX | ||
653 | // Order must match constants above. | ||
654 | // POSIX levels must be last and in increasing order | ||
655 | static const char *p_name = | ||
656 | "macro_name\0" | ||
657 | "target_name\0" | ||
658 | "command_comment\0" | ||
659 | "empty_suffix\0" | ||
660 | #if ENABLE_PLATFORM_MINGW32 | ||
661 | "windows\0" | ||
662 | #endif | ||
663 | "posix_2017\0" | ||
664 | "posix_2024\0" | ||
665 | "posix_202x\0"; | ||
666 | |||
667 | static void | ||
668 | set_pragma(const char *name) | ||
669 | { | ||
670 | int idx = index_in_strings(p_name, name); | ||
671 | |||
672 | if (idx != -1) { | ||
673 | if (idx >= BIT_POSIX_2017) { | ||
674 | // POSIX level is stored in a separate variable. | ||
675 | // No bits in 'pragma' are used. | ||
676 | if (posix_level == DEFAULT_POSIX_LEVEL) { | ||
677 | posix_level = idx - BIT_POSIX_2017; | ||
678 | if (posix_level > STD_POSIX_2024) | ||
679 | posix_level = STD_POSIX_2024; | ||
680 | } else if (posix_level != idx - BIT_POSIX_2017) | ||
681 | warning("unable to change POSIX level"); | ||
682 | } else { | ||
683 | pragma |= 1 << idx; | ||
684 | } | ||
685 | return; | ||
686 | } | ||
687 | warning("invalid pragma '%s'", name); | ||
688 | } | ||
689 | |||
690 | static void | ||
691 | pragmas_to_env(void) | ||
692 | { | ||
693 | int i; | ||
694 | char *val = NULL; | ||
695 | |||
696 | for (i = 0; i < BIT_POSIX_2017; ++i) { | ||
697 | if ((pragma & (1 << i))) | ||
698 | val = xappendword(val, nth_string(p_name, i)); | ||
699 | } | ||
700 | |||
701 | if (posix_level != DEFAULT_POSIX_LEVEL) | ||
702 | val = xappendword(val, | ||
703 | nth_string(p_name, BIT_POSIX_2017 + posix_level)); | ||
704 | |||
705 | if (val) { | ||
706 | setenv("PDPMAKE_PRAGMAS", val, 1); | ||
707 | free(val); | ||
708 | } | ||
709 | } | ||
710 | #endif | ||
711 | |||
712 | /* | ||
713 | * Add a new rule to a target. This checks to see if commands already | ||
714 | * exist for the target. If flag is TRUE the target can have multiple | ||
715 | * rules with commands (double-colon rules). | ||
716 | * | ||
717 | * i) If the name is a special target and there are no prerequisites | ||
718 | * or commands to be added remove all prerequisites and commands. | ||
719 | * This is necessary when clearing a built-in inference rule. | ||
720 | * ii) If name is a special target and has commands, replace them. | ||
721 | * This is for redefining commands for an inference rule. | ||
722 | */ | ||
723 | static void | ||
724 | addrule(struct name *np, struct depend *dp, struct cmd *cp, int flag) | ||
725 | { | ||
726 | struct rule *rp; | ||
727 | struct rule **rpp; | ||
728 | |||
729 | // Can't mix single-colon and double-colon rules | ||
730 | if (!posix && (np->n_flag & N_TARGET)) { | ||
731 | if (!(np->n_flag & N_DOUBLE) != !flag) // like xor | ||
732 | error("inconsistent rules for target %s", np->n_name); | ||
733 | } | ||
734 | |||
735 | // Clear out prerequisites and commands | ||
736 | if ((np->n_flag & N_SPECIAL) && !dp && !cp) { | ||
737 | if (strcmp(np->n_name, ".PHONY") == 0) | ||
738 | return; | ||
739 | freerules(np->n_rule); | ||
740 | np->n_rule = NULL; | ||
741 | return; | ||
742 | } | ||
743 | |||
744 | if (cp && !(np->n_flag & N_DOUBLE) && getcmd(np)) { | ||
745 | // Handle the inference rule redefinition case | ||
746 | // .DEFAULT rule can also be redefined (as an extension). | ||
747 | if ((np->n_flag & N_INFERENCE) | ||
748 | && !(posix && (np->n_flag & N_SPECIAL)) | ||
749 | ) { | ||
750 | freerules(np->n_rule); | ||
751 | np->n_rule = NULL; | ||
752 | } else { | ||
753 | error("commands defined twice for target %s", np->n_name); | ||
754 | } | ||
755 | } | ||
756 | |||
757 | rpp = &np->n_rule; | ||
758 | while (*rpp) | ||
759 | rpp = &(*rpp)->r_next; | ||
760 | |||
761 | *rpp = rp = xzalloc(sizeof(struct rule)); | ||
762 | /*rp->r_next = NULL; - xzalloc did it */ | ||
763 | rp->r_dep = inc_ref(dp); | ||
764 | rp->r_cmd = inc_ref(cp); | ||
765 | |||
766 | np->n_flag |= N_TARGET; | ||
767 | if (flag) | ||
768 | np->n_flag |= N_DOUBLE; | ||
769 | #if ENABLE_FEATURE_MAKE_POSIX | ||
770 | if (strcmp(np->n_name, ".PRAGMA") == 0) { | ||
771 | for (; dp; dp = dp->d_next) { | ||
772 | set_pragma(dp->d_name->n_name); | ||
773 | } | ||
774 | pragmas_to_env(); | ||
775 | } | ||
776 | #endif | ||
777 | } | ||
778 | |||
779 | /* | ||
780 | * Macro control for make | ||
781 | */ | ||
782 | static struct macro * | ||
783 | getmp(const char *name) | ||
784 | { | ||
785 | struct macro *mp = macrohead[getbucket(name)]; | ||
786 | return (struct macro *)llist_find_str((llist_t *)mp, name); | ||
787 | } | ||
788 | |||
789 | static int | ||
790 | is_valid_macro(const char *name) | ||
791 | { | ||
792 | const char *s; | ||
793 | for (s = name; *s; ++s) { | ||
794 | // In POSIX mode only a limited set of characters are guaranteed | ||
795 | // to be allowed in macro names. | ||
796 | if (posix) { | ||
797 | // Find the appropriate character set | ||
798 | if ((pragma & P_MACRO_NAME) || !POSIX_2017 ? | ||
799 | !isfname(*s) : !ispname(*s)) | ||
800 | return FALSE; | ||
801 | } | ||
802 | // As an extension allow anything that can get through the | ||
803 | // input parser, apart from the following. | ||
804 | if (*s == '=' || isblank(*s) || iscntrl(*s)) | ||
805 | return FALSE; | ||
806 | } | ||
807 | return TRUE; | ||
808 | } | ||
809 | |||
810 | #if ENABLE_FEATURE_MAKE_POSIX | ||
811 | static int | ||
812 | potentially_valid_macro(const char *name) | ||
813 | { | ||
814 | int ret = FALSE; | ||
815 | |||
816 | if (!(pragma & P_MACRO_NAME)) { | ||
817 | pragma |= P_MACRO_NAME; | ||
818 | ret = is_valid_macro(name); | ||
819 | pragma &= ~P_MACRO_NAME; | ||
820 | } | ||
821 | return ret; | ||
822 | } | ||
823 | #endif | ||
824 | |||
825 | static void | ||
826 | setmacro(const char *name, const char *val, int level) | ||
827 | { | ||
828 | struct macro *mp; | ||
829 | bool valid = level & M_VALID; | ||
830 | bool from_env = level & M_ENVIRON; | ||
831 | bool immediate = level & M_IMMEDIATE; | ||
832 | |||
833 | level &= ~(M_IMMEDIATE | M_VALID | M_ENVIRON); | ||
834 | mp = getmp(name); | ||
835 | if (mp) { | ||
836 | // Don't replace existing macro from a lower level | ||
837 | if (level > mp->m_level) | ||
838 | return; | ||
839 | |||
840 | // Replace existing macro | ||
841 | free(mp->m_val); | ||
842 | } else { | ||
843 | // If not defined, allocate space for new | ||
844 | unsigned int bucket; | ||
845 | |||
846 | if (!valid && !is_valid_macro(name)) { | ||
847 | // Silently drop invalid names from the environment | ||
848 | if (from_env) | ||
849 | return; | ||
850 | #if ENABLE_FEATURE_MAKE_POSIX | ||
851 | error("invalid macro name '%s'%s", name, | ||
852 | potentially_valid_macro(name) ? | ||
853 | ": allow with pragma macro_name" : ""); | ||
854 | #else | ||
855 | error("invalid macro name '%s'", name); | ||
856 | #endif | ||
857 | } | ||
858 | |||
859 | bucket = getbucket(name); | ||
860 | mp = xzalloc(sizeof(struct macro)); | ||
861 | mp->m_next = macrohead[bucket]; | ||
862 | macrohead[bucket] = mp; | ||
863 | /* mp->m_flag = FALSE; - xzalloc did it */ | ||
864 | mp->m_name = xstrdup(name); | ||
865 | } | ||
866 | mp->m_immediate = immediate; | ||
867 | mp->m_level = level; | ||
868 | mp->m_val = xstrdup(val ? val : ""); | ||
869 | } | ||
870 | |||
871 | #if ENABLE_FEATURE_CLEAN_UP | ||
872 | static void | ||
873 | freemacros(void) | ||
874 | { | ||
875 | int i; | ||
876 | struct macro *mp, *nextmp; | ||
877 | |||
878 | for (i = 0; i < HTABSIZE; i++) { | ||
879 | for (mp = macrohead[i]; mp; mp = nextmp) { | ||
880 | nextmp = mp->m_next; | ||
881 | free(mp->m_name); | ||
882 | free(mp->m_val); | ||
883 | free(mp); | ||
884 | } | ||
885 | } | ||
886 | } | ||
887 | #endif | ||
888 | |||
889 | /* | ||
890 | * Get modification time of file or archive member | ||
891 | */ | ||
892 | static void FAST_FUNC | ||
893 | record_mtime(const file_header_t *file_header) | ||
894 | { | ||
895 | ar_mtime = file_header->mtime; | ||
896 | } | ||
897 | |||
898 | static time_t | ||
899 | artime(const char *archive, const char *member) | ||
900 | { | ||
901 | archive_handle_t *archive_handle; | ||
902 | |||
903 | ar_mtime = 0; | ||
904 | archive_handle = init_handle(); | ||
905 | archive_handle->src_fd = open(archive, O_RDONLY); | ||
906 | if (archive_handle->src_fd != -1) { | ||
907 | archive_handle->action_header = record_mtime; | ||
908 | archive_handle->filter = filter_accept_list; | ||
909 | llist_add_to_end(&archive_handle->accept, (void *)member); | ||
910 | unpack_ar_archive(archive_handle); | ||
911 | close(archive_handle->src_fd); | ||
912 | } | ||
913 | |||
914 | #if ENABLE_FEATURE_AR_LONG_FILENAMES | ||
915 | free(archive_handle->ar__long_names); | ||
916 | #endif | ||
917 | llist_free(archive_handle->accept, NULL); | ||
918 | free(archive_handle->file_header); | ||
919 | free(archive_handle); | ||
920 | |||
921 | return ar_mtime; | ||
922 | } | ||
923 | |||
924 | /* | ||
925 | * If the name is of the form 'libname(member.o)' split it into its | ||
926 | * name and member parts and set the member pointer to point to the | ||
927 | * latter. Otherwise just take a copy of the name and don't alter | ||
928 | * the member pointer. | ||
929 | * | ||
930 | * In either case the return value is an allocated string which must | ||
931 | * be freed by the caller. | ||
932 | */ | ||
933 | static char * | ||
934 | splitlib(const char *name, char **member) | ||
935 | { | ||
936 | char *s, *t; | ||
937 | size_t len; | ||
938 | |||
939 | t = xstrdup(name); | ||
940 | s = strchr(t, '('); | ||
941 | if (s) { | ||
942 | // We have 'libname(member.o)' | ||
943 | *s++ = '\0'; | ||
944 | len = strlen(s); | ||
945 | if (len <= 1 || s[len - 1] != ')' || *t == '\0') | ||
946 | error("invalid name '%s'", name); | ||
947 | s[len - 1] = '\0'; | ||
948 | *member = s; | ||
949 | } | ||
950 | return t; | ||
951 | } | ||
952 | |||
953 | /* | ||
954 | * Get the modification time of a file. Set it to 0 if the file | ||
955 | * doesn't exist. | ||
956 | */ | ||
957 | static void | ||
958 | modtime(struct name *np) | ||
959 | { | ||
960 | char *name, *member = NULL; | ||
961 | struct stat info; | ||
962 | |||
963 | name = splitlib(np->n_name, &member); | ||
964 | if (member) { | ||
965 | // Looks like library(member) | ||
966 | np->n_tim.tv_sec = artime(name, member); | ||
967 | np->n_tim.tv_nsec = 0; | ||
968 | } else if (stat(name, &info) < 0) { | ||
969 | if (errno != ENOENT) | ||
970 | bb_perror_msg("can't open %s", name); | ||
971 | np->n_tim.tv_sec = 0; | ||
972 | np->n_tim.tv_nsec = 0; | ||
973 | } else { | ||
974 | np->n_tim.tv_sec = info.st_mtim.tv_sec; | ||
975 | np->n_tim.tv_nsec = info.st_mtim.tv_nsec; | ||
976 | } | ||
977 | free(name); | ||
978 | } | ||
979 | |||
980 | /* | ||
981 | * Control of the implicit suffix rules | ||
982 | */ | ||
983 | |||
984 | /* | ||
985 | * Return a pointer to the suffix of a name (which may be the | ||
986 | * terminating NUL if there's no suffix). | ||
987 | */ | ||
988 | static char * | ||
989 | suffix(const char *name) | ||
990 | { | ||
991 | char *p = strrchr(name, '.'); | ||
992 | return p ? p : (char *)name + strlen(name); | ||
993 | } | ||
994 | |||
995 | /* | ||
996 | * Dynamic dependency. This routine applies the suffix rules | ||
997 | * to try and find a source and a set of rules for a missing | ||
998 | * target. NULL is returned on failure. On success the name of | ||
999 | * the implicit prerequisite is returned and the details are | ||
1000 | * placed in the imprule structure provided by the caller. | ||
1001 | */ | ||
1002 | static struct name * | ||
1003 | dyndep(struct name *np, struct rule *imprule) | ||
1004 | { | ||
1005 | char *suff, *newsuff; | ||
1006 | char *base, *name, *member; | ||
1007 | struct name *xp; // Suffixes | ||
1008 | struct name *sp; // Suffix rule | ||
1009 | struct name *pp = NULL; // Implicit prerequisite | ||
1010 | struct rule *rp; | ||
1011 | struct depend *dp; | ||
1012 | bool chain = FALSE; | ||
1013 | |||
1014 | member = NULL; | ||
1015 | name = splitlib(np->n_name, &member); | ||
1016 | |||
1017 | suff = xstrdup(suffix(name)); | ||
1018 | base = member ? member : name; | ||
1019 | *suffix(base) = '\0'; | ||
1020 | |||
1021 | xp = newname(".SUFFIXES"); | ||
1022 | retry: | ||
1023 | for (rp = xp->n_rule; rp; rp = rp->r_next) { | ||
1024 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
1025 | // Generate new suffix rule to try | ||
1026 | newsuff = dp->d_name->n_name; | ||
1027 | sp = findname(auto_concat(newsuff, suff)); | ||
1028 | if (sp && sp->n_rule) { | ||
1029 | struct name *ip; | ||
1030 | int got_ip; | ||
1031 | |||
1032 | // Has rule already been used in this chain? | ||
1033 | if ((sp->n_flag & N_MARK)) | ||
1034 | continue; | ||
1035 | |||
1036 | // Generate a name for an implicit prerequisite | ||
1037 | ip = newname(auto_concat(base, newsuff)); | ||
1038 | if ((ip->n_flag & N_DOING)) | ||
1039 | continue; | ||
1040 | |||
1041 | if (!ip->n_tim.tv_sec) | ||
1042 | modtime(ip); | ||
1043 | |||
1044 | if (!chain) { | ||
1045 | got_ip = ip->n_tim.tv_sec || (ip->n_flag & N_TARGET); | ||
1046 | } else { | ||
1047 | sp->n_flag |= N_MARK; | ||
1048 | got_ip = dyndep(ip, NULL) != NULL; | ||
1049 | sp->n_flag &= ~N_MARK; | ||
1050 | } | ||
1051 | |||
1052 | if (got_ip) { | ||
1053 | // Prerequisite exists or we know how to make it | ||
1054 | if (imprule) { | ||
1055 | dp = NULL; | ||
1056 | newdep(&dp, ip); | ||
1057 | imprule->r_dep = dp; | ||
1058 | imprule->r_cmd = sp->n_rule->r_cmd; | ||
1059 | } | ||
1060 | pp = ip; | ||
1061 | goto finish; | ||
1062 | } | ||
1063 | } | ||
1064 | } | ||
1065 | } | ||
1066 | // If we didn't find an existing file or an explicit rule try | ||
1067 | // again, this time looking for a chained inference rule. | ||
1068 | if (!posix && !chain) { | ||
1069 | chain = TRUE; | ||
1070 | goto retry; | ||
1071 | } | ||
1072 | finish: | ||
1073 | free(suff); | ||
1074 | free(name); | ||
1075 | return pp; | ||
1076 | } | ||
1077 | |||
1078 | #define RULES \ | ||
1079 | ".c.o:\n" \ | ||
1080 | " $(CC) $(CFLAGS) -c $<\n" \ | ||
1081 | ".y.o:\n" \ | ||
1082 | " $(YACC) $(YFLAGS) $<\n" \ | ||
1083 | " $(CC) $(CFLAGS) -c y.tab.c\n" \ | ||
1084 | " rm -f y.tab.c\n" \ | ||
1085 | " mv y.tab.o $@\n" \ | ||
1086 | ".y.c:\n" \ | ||
1087 | " $(YACC) $(YFLAGS) $<\n" \ | ||
1088 | " mv y.tab.c $@\n" \ | ||
1089 | ".l.o:\n" \ | ||
1090 | " $(LEX) $(LFLAGS) $<\n" \ | ||
1091 | " $(CC) $(CFLAGS) -c lex.yy.c\n" \ | ||
1092 | " rm -f lex.yy.c\n" \ | ||
1093 | " mv lex.yy.o $@\n" \ | ||
1094 | ".l.c:\n" \ | ||
1095 | " $(LEX) $(LFLAGS) $<\n" \ | ||
1096 | " mv lex.yy.c $@\n" \ | ||
1097 | ".c.a:\n" \ | ||
1098 | " $(CC) -c $(CFLAGS) $<\n" \ | ||
1099 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ | ||
1100 | " rm -f $*.o\n" \ | ||
1101 | ".c:\n" \ | ||
1102 | " $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $<\n" \ | ||
1103 | ".sh:\n" \ | ||
1104 | " cp $< $@\n" \ | ||
1105 | " chmod a+x $@\n" | ||
1106 | |||
1107 | #define RULES_2017 \ | ||
1108 | ".SUFFIXES:.o .c .y .l .a .sh .f\n" \ | ||
1109 | ".f.o:\n" \ | ||
1110 | " $(FC) $(FFLAGS) -c $<\n" \ | ||
1111 | ".f.a:\n" \ | ||
1112 | " $(FC) -c $(FFLAGS) $<\n" \ | ||
1113 | " $(AR) $(ARFLAGS) $@ $*.o\n" \ | ||
1114 | " rm -f $*.o\n" \ | ||
1115 | ".f:\n" \ | ||
1116 | " $(FC) $(FFLAGS) $(LDFLAGS) -o $@ $<\n" | ||
1117 | |||
1118 | #define RULES_2024 \ | ||
1119 | ".SUFFIXES:.o .c .y .l .a .sh\n" | ||
1120 | |||
1121 | #define MACROS \ | ||
1122 | "CFLAGS=-O1\n" \ | ||
1123 | "YACC=yacc\n" \ | ||
1124 | "YFLAGS=\n" \ | ||
1125 | "LEX=lex\n" \ | ||
1126 | "LFLAGS=\n" \ | ||
1127 | "AR=ar\n" \ | ||
1128 | "ARFLAGS=-rv\n" \ | ||
1129 | "LDFLAGS=\n" | ||
1130 | |||
1131 | #define MACROS_2017 \ | ||
1132 | "CC=c99\n" \ | ||
1133 | "FC=fort77\n" \ | ||
1134 | "FFLAGS=-O1\n" \ | ||
1135 | |||
1136 | #define MACROS_2024 \ | ||
1137 | "CC=c17\n" | ||
1138 | |||
1139 | #define MACROS_EXT \ | ||
1140 | "CC=cc\n" | ||
1141 | |||
1142 | /* | ||
1143 | * Read the built-in rules using a fake fgets-like interface. | ||
1144 | */ | ||
1145 | static char * | ||
1146 | getrules(char *s, int size) | ||
1147 | { | ||
1148 | char *r = s; | ||
1149 | |||
1150 | if (rulepos == NULL || *rulepos == '\0') { | ||
1151 | if (rule_idx == 0) { | ||
1152 | rulepos = MACROS; | ||
1153 | rule_idx++; | ||
1154 | } else if (rule_idx == 1) { | ||
1155 | if (POSIX_2017) | ||
1156 | rulepos = MACROS_2017; | ||
1157 | else if (posix) | ||
1158 | rulepos = MACROS_2024; | ||
1159 | else | ||
1160 | rulepos = MACROS_EXT; | ||
1161 | rule_idx++; | ||
1162 | } else if (!norules) { | ||
1163 | if (rule_idx == 2) { | ||
1164 | rulepos = POSIX_2017 ? RULES_2017 : RULES_2024; | ||
1165 | rule_idx++; | ||
1166 | } else if (rule_idx == 3) { | ||
1167 | rulepos = RULES; | ||
1168 | rule_idx++; | ||
1169 | } | ||
1170 | } | ||
1171 | } | ||
1172 | |||
1173 | if (*rulepos == '\0') | ||
1174 | return NULL; | ||
1175 | |||
1176 | while (--size) { | ||
1177 | if ((*r++ = *rulepos++) == '\n') | ||
1178 | break; | ||
1179 | } | ||
1180 | *r = '\0'; | ||
1181 | return s; | ||
1182 | } | ||
1183 | |||
1184 | /* | ||
1185 | * Parse a makefile | ||
1186 | */ | ||
1187 | |||
1188 | /* | ||
1189 | * Return a pointer to the next blank-delimited word or NULL if | ||
1190 | * there are none left. | ||
1191 | */ | ||
1192 | static char * | ||
1193 | gettok(char **ptr) | ||
1194 | { | ||
1195 | char *p; | ||
1196 | |||
1197 | while (isblank(**ptr)) // Skip blanks | ||
1198 | (*ptr)++; | ||
1199 | |||
1200 | if (**ptr == '\0') // Nothing after blanks | ||
1201 | return NULL; | ||
1202 | |||
1203 | p = *ptr; // Word starts here | ||
1204 | |||
1205 | while (**ptr != '\0' && !isblank(**ptr)) | ||
1206 | (*ptr)++; // Find end of word | ||
1207 | |||
1208 | // Terminate token and move on unless already at end of string | ||
1209 | if (**ptr != '\0') | ||
1210 | *(*ptr)++ = '\0'; | ||
1211 | |||
1212 | return(p); | ||
1213 | } | ||
1214 | |||
1215 | /* | ||
1216 | * Skip over (possibly adjacent or nested) macro expansions. | ||
1217 | */ | ||
1218 | static char * | ||
1219 | skip_macro(const char *s) | ||
1220 | { | ||
1221 | while (*s && s[0] == '$') { | ||
1222 | if (s[1] == '(' || s[1] == '{') { | ||
1223 | char end = *++s == '(' ? ')' : '}'; | ||
1224 | while (*s && *s != end) | ||
1225 | s = skip_macro(s + 1); | ||
1226 | if (*s == end) | ||
1227 | ++s; | ||
1228 | } else if (s[1] != '\0') { | ||
1229 | s += 2; | ||
1230 | } else { | ||
1231 | break; | ||
1232 | } | ||
1233 | } | ||
1234 | return (char *)s; | ||
1235 | } | ||
1236 | |||
1237 | /* | ||
1238 | * Process each whitespace-separated word in the input string: | ||
1239 | * | ||
1240 | * - replace paths with their directory or filename part | ||
1241 | * - replace prefixes and suffixes | ||
1242 | * | ||
1243 | * Returns an allocated string or NULL if the input is unmodified. | ||
1244 | */ | ||
1245 | static char * | ||
1246 | modify_words(const char *val, int modifier, size_t lenf, size_t lenr, | ||
1247 | const char *find_pref, const char *repl_pref, | ||
1248 | const char *find_suff, const char *repl_suff) | ||
1249 | { | ||
1250 | char *s, *copy, *word, *sep, *buf = NULL; | ||
1251 | size_t find_pref_len = 0, find_suff_len = 0; | ||
1252 | |||
1253 | if (!modifier && lenf == 0 && lenr == 0) | ||
1254 | return buf; | ||
1255 | |||
1256 | if (find_pref) { | ||
1257 | // get length of find prefix, e.g: src/ | ||
1258 | find_pref_len = strlen(find_pref); | ||
1259 | // get length of find suffix, e.g: .c | ||
1260 | find_suff_len = lenf - find_pref_len - 1; | ||
1261 | } | ||
1262 | |||
1263 | s = copy = xstrdup(val); | ||
1264 | while ((word = gettok(&s)) != NULL) { | ||
1265 | if (modifier) { | ||
1266 | sep = strrchr(word, '/'); | ||
1267 | if (modifier == 'D') { | ||
1268 | if (!sep) { | ||
1269 | word[0] = '.'; // no '/', return "." | ||
1270 | sep = word + 1; | ||
1271 | } else if (sep == word) { | ||
1272 | // '/' at start of word, return "/" | ||
1273 | sep = word + 1; | ||
1274 | } | ||
1275 | // else terminate at separator | ||
1276 | *sep = '\0'; | ||
1277 | } else if (/* modifier == 'F' && */ sep) { | ||
1278 | word = sep + 1; | ||
1279 | } | ||
1280 | } | ||
1281 | if (find_pref != NULL || lenf != 0 || lenr != 0) { | ||
1282 | size_t lenw = strlen(word); | ||
1283 | // This code implements pattern macro expansions: | ||
1284 | // https://austingroupbugs.net/view.php?id=519 | ||
1285 | // | ||
1286 | // find: <prefix>%<suffix> | ||
1287 | // example: src/%.c | ||
1288 | // | ||
1289 | // For a pattern of the form: | ||
1290 | // $(string1:[op]%[os]=[np][%][ns]) | ||
1291 | // lenf is the length of [op]%[os]. So lenf >= 1. | ||
1292 | if (find_pref != NULL && lenw + 1 >= lenf) { | ||
1293 | // If prefix and suffix of word match find_pref and | ||
1294 | // find_suff, then do substitution. | ||
1295 | if (strncmp(word, find_pref, find_pref_len) == 0 && | ||
1296 | strcmp(word + lenw - find_suff_len, find_suff) == 0) { | ||
1297 | // replace: <prefix>[%<suffix>] | ||
1298 | // example: build/%.o or build/all.o (notice no %) | ||
1299 | // If repl_suff is NULL, replace whole word with repl_pref. | ||
1300 | if (!repl_suff) { | ||
1301 | word = xstrdup(repl_pref); | ||
1302 | } else { | ||
1303 | word[lenw - find_suff_len] = '\0'; | ||
1304 | word = xasprintf("%s%s%s", repl_pref, | ||
1305 | word + find_pref_len, repl_suff); | ||
1306 | } | ||
1307 | word = auto_string(word); | ||
1308 | } | ||
1309 | } else if (lenw >= lenf && | ||
1310 | strcmp(word + lenw - lenf, find_suff) == 0) { | ||
1311 | word[lenw - lenf] = '\0'; | ||
1312 | word = auto_concat(word, repl_suff); | ||
1313 | } | ||
1314 | } | ||
1315 | buf = xappendword(buf, word); | ||
1316 | } | ||
1317 | free(copy); | ||
1318 | return buf; | ||
1319 | } | ||
1320 | |||
1321 | /* | ||
1322 | * Return a pointer to the next instance of a given character. Macro | ||
1323 | * expansions are skipped so the ':' and '=' in $(VAR:.s1=.s2) aren't | ||
1324 | * detected as separators in macro definitions. Some other situations | ||
1325 | * also require skipping the internals of a macro expansion. | ||
1326 | */ | ||
1327 | static char * | ||
1328 | find_char(const char *str, int c) | ||
1329 | { | ||
1330 | const char *s; | ||
1331 | |||
1332 | for (s = skip_macro(str); *s; s = skip_macro(s + 1)) { | ||
1333 | if (*s == c) | ||
1334 | return (char *)s; | ||
1335 | } | ||
1336 | return NULL; | ||
1337 | } | ||
1338 | |||
1339 | #if ENABLE_PLATFORM_MINGW32 | ||
1340 | /* | ||
1341 | * Check for a target rule by searching for a colon that isn't | ||
1342 | * part of a Windows path. Return a pointer to the colon or NULL. | ||
1343 | */ | ||
1344 | static char * | ||
1345 | find_colon(char *p) | ||
1346 | { | ||
1347 | char *q; | ||
1348 | |||
1349 | for (q = p; (q = strchr(q, ':')); ++q) { | ||
1350 | if (posix && !(pragma & P_WINDOWS)) | ||
1351 | break; | ||
1352 | if (q == p || !isalpha(q[-1]) || q[1] != '/') | ||
1353 | break; | ||
1354 | } | ||
1355 | return q; | ||
1356 | } | ||
1357 | #else | ||
1358 | # define find_colon(s) strchr(s, ':') | ||
1359 | #endif | ||
1360 | |||
1361 | /* | ||
1362 | * Recursively expand any macros in str to an allocated string. | ||
1363 | */ | ||
1364 | static char * | ||
1365 | expand_macros(const char *str, int except_dollar) | ||
1366 | { | ||
1367 | char *exp, *newexp, *s, *t, *p, *q, *name; | ||
1368 | char *find, *replace, *modified; | ||
1369 | char *expval, *expfind, *find_suff, *repl_suff; | ||
1370 | char *find_pref = NULL, *repl_pref = NULL; | ||
1371 | size_t lenf, lenr; | ||
1372 | char modifier; | ||
1373 | struct macro *mp; | ||
1374 | |||
1375 | exp = xstrdup(str); | ||
1376 | for (t = exp; *t; t++) { | ||
1377 | if (*t == '$') { | ||
1378 | if (t[1] == '\0') { | ||
1379 | break; | ||
1380 | } | ||
1381 | if (t[1] == '$' && except_dollar) { | ||
1382 | t++; | ||
1383 | continue; | ||
1384 | } | ||
1385 | // Need to expand a macro. Find its extent (s to t inclusive) | ||
1386 | // and take a copy of its content. | ||
1387 | s = t; | ||
1388 | t++; | ||
1389 | if (*t == '{' || *t == '(') { | ||
1390 | t = find_char(t, *t == '{' ? '}' : ')'); | ||
1391 | if (t == NULL) | ||
1392 | error("unterminated variable '%s'", s); | ||
1393 | name = xstrndup(s + 2, t - s - 2); | ||
1394 | } else { | ||
1395 | name = xzalloc(2); | ||
1396 | name[0] = *t; | ||
1397 | /*name[1] = '\0'; - xzalloc did it */ | ||
1398 | } | ||
1399 | |||
1400 | // Only do suffix replacement or pattern macro expansion | ||
1401 | // if both ':' and '=' are found, plus a '%' for the latter. | ||
1402 | // Suffix replacement is indicated by | ||
1403 | // find_pref == NULL && (lenf != 0 || lenr != 0); | ||
1404 | // pattern macro expansion by find_pref != NULL. | ||
1405 | expfind = NULL; | ||
1406 | find_suff = repl_suff = NULL; | ||
1407 | lenf = lenr = 0; | ||
1408 | if ((find = find_char(name, ':'))) { | ||
1409 | *find++ = '\0'; | ||
1410 | expfind = expand_macros(find, FALSE); | ||
1411 | if ((replace = find_char(expfind, '='))) { | ||
1412 | *replace++ = '\0'; | ||
1413 | lenf = strlen(expfind); | ||
1414 | if (!POSIX_2017 && (find_suff = strchr(expfind, '%'))) { | ||
1415 | find_pref = expfind; | ||
1416 | repl_pref = replace; | ||
1417 | *find_suff++ = '\0'; | ||
1418 | if ((repl_suff = strchr(replace, '%'))) | ||
1419 | *repl_suff++ = '\0'; | ||
1420 | } else { | ||
1421 | if (posix && !(pragma & P_EMPTY_SUFFIX) && lenf == 0) | ||
1422 | error("empty suffix%s", | ||
1423 | !ENABLE_FEATURE_MAKE_POSIX ? "" : | ||
1424 | ": allow with pragma empty_suffix"); | ||
1425 | find_suff = expfind; | ||
1426 | repl_suff = replace; | ||
1427 | lenr = strlen(repl_suff); | ||
1428 | } | ||
1429 | } | ||
1430 | } | ||
1431 | |||
1432 | p = q = name; | ||
1433 | // If not in POSIX mode expand macros in the name. | ||
1434 | if (!POSIX_2017) { | ||
1435 | char *expname = expand_macros(name, FALSE); | ||
1436 | free(name); | ||
1437 | name = expname; | ||
1438 | } else { | ||
1439 | // Skip over nested expansions in name | ||
1440 | do { | ||
1441 | *q++ = *p; | ||
1442 | } while ((p = skip_macro(p + 1)) && *p); | ||
1443 | } | ||
1444 | |||
1445 | // The internal macros support 'D' and 'F' modifiers | ||
1446 | modifier = '\0'; | ||
1447 | switch (name[0]) { | ||
1448 | case '^': | ||
1449 | case '+': | ||
1450 | if (POSIX_2017) | ||
1451 | break; | ||
1452 | // fall through | ||
1453 | case '@': case '%': case '?': case '<': case '*': | ||
1454 | if ((name[1] == 'D' || name[1] == 'F') && name[2] == '\0') { | ||
1455 | modifier = name[1]; | ||
1456 | name[1] = '\0'; | ||
1457 | } | ||
1458 | break; | ||
1459 | } | ||
1460 | |||
1461 | modified = NULL; | ||
1462 | if ((mp = getmp(name))) { | ||
1463 | // Recursive expansion | ||
1464 | if (mp->m_flag) | ||
1465 | error("recursive macro %s", name); | ||
1466 | // Note if we've expanded $(MAKE) | ||
1467 | if (strcmp(name, "MAKE") == 0) | ||
1468 | opts |= OPT_make; | ||
1469 | mp->m_flag = TRUE; | ||
1470 | expval = expand_macros(mp->m_val, FALSE); | ||
1471 | mp->m_flag = FALSE; | ||
1472 | modified = modify_words(expval, modifier, lenf, lenr, | ||
1473 | find_pref, repl_pref, find_suff, repl_suff); | ||
1474 | if (modified) | ||
1475 | free(expval); | ||
1476 | else | ||
1477 | modified = expval; | ||
1478 | } | ||
1479 | free(name); | ||
1480 | free(expfind); | ||
1481 | |||
1482 | if (modified && *modified) { | ||
1483 | // The text to be replaced by the macro expansion is | ||
1484 | // from s to t inclusive. | ||
1485 | *s = '\0'; | ||
1486 | newexp = xasprintf("%s%s%s", exp, modified, t + 1); | ||
1487 | t = newexp + (s - exp) + strlen(modified) - 1; | ||
1488 | free(exp); | ||
1489 | exp = newexp; | ||
1490 | } else { | ||
1491 | // Macro wasn't expanded or expanded to nothing. | ||
1492 | // Close the space occupied by the macro reference. | ||
1493 | q = t + 1; | ||
1494 | t = s - 1; | ||
1495 | while ((*s++ = *q++)) | ||
1496 | continue; | ||
1497 | } | ||
1498 | free(modified); | ||
1499 | } | ||
1500 | } | ||
1501 | return exp; | ||
1502 | } | ||
1503 | |||
1504 | /* | ||
1505 | * Process a non-command line | ||
1506 | */ | ||
1507 | static void | ||
1508 | process_line(char *s) | ||
1509 | { | ||
1510 | char *t; | ||
1511 | |||
1512 | // Strip comment | ||
1513 | // don't treat '#' in macro expansion as a comment | ||
1514 | // nor '#' outside macro expansion preceded by backslash | ||
1515 | if (!posix) { | ||
1516 | char *u = s; | ||
1517 | while ((t = find_char(u, '#')) && t > u && t[-1] == '\\') { | ||
1518 | for (u = t; *u; ++u) { | ||
1519 | u[-1] = u[0]; | ||
1520 | } | ||
1521 | *u = '\0'; | ||
1522 | u = t; | ||
1523 | } | ||
1524 | } else | ||
1525 | t = strchr(s, '#'); | ||
1526 | if (t) | ||
1527 | *t = '\0'; | ||
1528 | |||
1529 | // Replace escaped newline and any leading white space on the | ||
1530 | // following line with a single space. Stop processing at a | ||
1531 | // non-escaped newline. | ||
1532 | for (t = s; *s && *s != '\n'; ) { | ||
1533 | if (s[0] == '\\' && s[1] == '\n') { | ||
1534 | s += 2; | ||
1535 | while (isspace(*s)) | ||
1536 | ++s; | ||
1537 | *t++ = ' '; | ||
1538 | } else { | ||
1539 | *t++ = *s++; | ||
1540 | } | ||
1541 | } | ||
1542 | *t = '\0'; | ||
1543 | } | ||
1544 | |||
1545 | enum { | ||
1546 | INITIAL = 0, | ||
1547 | SKIP_LINE = 1 << 0, | ||
1548 | EXPECT_ELSE = 1 << 1, | ||
1549 | GOT_MATCH = 1 << 2 | ||
1550 | }; | ||
1551 | |||
1552 | #define IFDEF 0 | ||
1553 | #define IFNDEF 1 | ||
1554 | #define IFEQ 2 | ||
1555 | #define IFNEQ 3 | ||
1556 | #define ELSE 0 | ||
1557 | #define ENDIF 1 | ||
1558 | |||
1559 | /* | ||
1560 | * Extract strings following ifeq/ifneq and compare them. | ||
1561 | * Return -1 on error. | ||
1562 | */ | ||
1563 | static int | ||
1564 | compare_strings(char *arg1) | ||
1565 | { | ||
1566 | char *arg2, *end, term, *t1, *t2; | ||
1567 | int ret; | ||
1568 | |||
1569 | // Get first string terminator. | ||
1570 | if (arg1[0] == '(') | ||
1571 | term = ','; | ||
1572 | else if (arg1[0] == '"' || arg1[0] == '\'') | ||
1573 | term = arg1[0]; | ||
1574 | else | ||
1575 | return -1; | ||
1576 | |||
1577 | arg2 = find_char(++arg1, term); | ||
1578 | if (arg2 == NULL) | ||
1579 | return -1; | ||
1580 | *arg2++ = '\0'; | ||
1581 | |||
1582 | // Get second string terminator. | ||
1583 | if (term == ',') { | ||
1584 | term = ')'; | ||
1585 | } else { | ||
1586 | // Skip spaces between quoted strings. | ||
1587 | while (isspace(arg2[0])) | ||
1588 | arg2++; | ||
1589 | if (arg2[0] == '"' || arg2[0] == '\'') | ||
1590 | term = arg2[0]; | ||
1591 | else | ||
1592 | return -1; | ||
1593 | ++arg2; | ||
1594 | } | ||
1595 | |||
1596 | end = find_char(arg2, term); | ||
1597 | if (end == NULL) | ||
1598 | return -1; | ||
1599 | *end++ = '\0'; | ||
1600 | |||
1601 | if (gettok(&end) != NULL) { | ||
1602 | warning("unexpected text"); | ||
1603 | } | ||
1604 | |||
1605 | t1 = expand_macros(arg1, FALSE); | ||
1606 | t2 = expand_macros(arg2, FALSE); | ||
1607 | |||
1608 | ret = strcmp(t1, t2) == 0; | ||
1609 | free(t1); | ||
1610 | free(t2); | ||
1611 | return ret; | ||
1612 | } | ||
1613 | |||
1614 | /* | ||
1615 | * Process conditional directives and return TRUE if the current line | ||
1616 | * should be skipped. | ||
1617 | */ | ||
1618 | static int | ||
1619 | skip_line(const char *str1) | ||
1620 | { | ||
1621 | char *copy, *q, *token; | ||
1622 | bool new_level = TRUE; | ||
1623 | // Default is to return skip flag for current level | ||
1624 | int ret = cstate[clevel] & SKIP_LINE; | ||
1625 | int key; | ||
1626 | |||
1627 | q = copy = xstrdup(str1); | ||
1628 | process_line(copy); | ||
1629 | if ((token = gettok(&q)) != NULL) { | ||
1630 | switch (index_in_strings("else\0endif\0", token)) { | ||
1631 | case ENDIF: | ||
1632 | if (gettok(&q) != NULL) | ||
1633 | error_unexpected("text"); | ||
1634 | if (clevel == 0) | ||
1635 | error_unexpected(token); | ||
1636 | --clevel; | ||
1637 | ret = TRUE; | ||
1638 | goto end; | ||
1639 | case ELSE: | ||
1640 | if (!(cstate[clevel] & EXPECT_ELSE)) | ||
1641 | error_unexpected(token); | ||
1642 | |||
1643 | // If an earlier condition matched we'll now skip lines. | ||
1644 | // If not we don't, though an 'else if' may override this. | ||
1645 | if ((cstate[clevel] & GOT_MATCH)) | ||
1646 | cstate[clevel] |= SKIP_LINE; | ||
1647 | else | ||
1648 | cstate[clevel] &= ~SKIP_LINE; | ||
1649 | |||
1650 | token = gettok(&q); | ||
1651 | if (token == NULL) { | ||
1652 | // Simple else with no conditional directive | ||
1653 | cstate[clevel] &= ~EXPECT_ELSE; | ||
1654 | ret = TRUE; | ||
1655 | goto end; | ||
1656 | } else { | ||
1657 | // A conditional directive is now required ('else if'). | ||
1658 | new_level = FALSE; | ||
1659 | } | ||
1660 | } | ||
1661 | |||
1662 | key = index_in_strings("ifdef\0ifndef\0ifeq\0ifneq\0", token); | ||
1663 | if (key != -1) { | ||
1664 | int match; | ||
1665 | |||
1666 | if (key == IFDEF || key == IFNDEF) { | ||
1667 | // ifdef/ifndef: find out if macro is defined. | ||
1668 | char *name = gettok(&q); | ||
1669 | if (name != NULL && gettok(&q) == NULL) { | ||
1670 | char *t = expand_macros(name, FALSE); | ||
1671 | struct macro *mp = getmp(t); | ||
1672 | match = mp != NULL && mp->m_val[0] != '\0'; | ||
1673 | free(t); | ||
1674 | } else { | ||
1675 | match = -1; | ||
1676 | } | ||
1677 | } else { | ||
1678 | // ifeq/ifneq: compare strings. | ||
1679 | match = compare_strings(q); | ||
1680 | } | ||
1681 | |||
1682 | if (match >= 0) { | ||
1683 | if (new_level) { | ||
1684 | // Start a new level. | ||
1685 | if (clevel == IF_MAX) | ||
1686 | error("nesting too deep"); | ||
1687 | ++clevel; | ||
1688 | cstate[clevel] = EXPECT_ELSE | SKIP_LINE; | ||
1689 | // If we were skipping lines at the previous level | ||
1690 | // we need to continue doing that unconditionally | ||
1691 | // at the new level. | ||
1692 | if ((cstate[clevel - 1] & SKIP_LINE)) | ||
1693 | cstate[clevel] |= GOT_MATCH; | ||
1694 | } | ||
1695 | |||
1696 | if (!(cstate[clevel] & GOT_MATCH)) { | ||
1697 | if (key == IFNDEF || key == IFNEQ) | ||
1698 | match = !match; | ||
1699 | if (match) { | ||
1700 | cstate[clevel] &= ~SKIP_LINE; | ||
1701 | cstate[clevel] |= GOT_MATCH; | ||
1702 | } | ||
1703 | } | ||
1704 | } else { | ||
1705 | error("invalid condition"); | ||
1706 | } | ||
1707 | ret = TRUE; | ||
1708 | } else if (!new_level) { | ||
1709 | error("missing conditional"); | ||
1710 | } | ||
1711 | } | ||
1712 | end: | ||
1713 | free(copy); | ||
1714 | return ret; | ||
1715 | } | ||
1716 | |||
1717 | /* | ||
1718 | * If fd is NULL read the built-in rules. Otherwise read from the | ||
1719 | * specified file descriptor. | ||
1720 | */ | ||
1721 | static char * | ||
1722 | make_fgets(char *s, int size, FILE *fd) | ||
1723 | { | ||
1724 | return fd ? fgets(s, size, fd) : getrules(s, size); | ||
1725 | } | ||
1726 | |||
1727 | /* | ||
1728 | * Read a newline-terminated line into an allocated string. | ||
1729 | * Backslash-escaped newlines don't terminate the line. | ||
1730 | * Ignore comment lines. Return NULL on EOF. | ||
1731 | */ | ||
1732 | static char * | ||
1733 | readline(FILE *fd, int want_command) | ||
1734 | { | ||
1735 | char *p, *str = NULL; | ||
1736 | int pos = 0; | ||
1737 | int len = 0; | ||
1738 | |||
1739 | for (;;) { | ||
1740 | // We need room for at least one character and a NUL terminator | ||
1741 | if (len - pos > 1 && | ||
1742 | make_fgets(str + pos, len - pos, fd) == NULL) { | ||
1743 | if (pos) | ||
1744 | return str; | ||
1745 | free(str); | ||
1746 | return NULL; // EOF | ||
1747 | } | ||
1748 | |||
1749 | if (len - pos < 2 || (p = strchr(str + pos, '\n')) == NULL) { | ||
1750 | // Need more room | ||
1751 | if (len) | ||
1752 | pos = len - 1; | ||
1753 | len += 256; | ||
1754 | str = xrealloc(str, len); | ||
1755 | continue; | ||
1756 | } | ||
1757 | lineno++; | ||
1758 | |||
1759 | #if ENABLE_PLATFORM_MINGW32 | ||
1760 | // Remove CR before LF | ||
1761 | if (p != str && p[-1] == '\r') { | ||
1762 | p[-1] = '\n'; | ||
1763 | *p-- = '\0'; | ||
1764 | } | ||
1765 | #endif | ||
1766 | // Keep going if newline has been escaped | ||
1767 | if (p != str && p[-1] == '\\') { | ||
1768 | pos = p - str + 1; | ||
1769 | continue; | ||
1770 | } | ||
1771 | dispno = lineno; | ||
1772 | |||
1773 | // Check for lines that are conditionally skipped. | ||
1774 | if (posix || !skip_line(str)) { | ||
1775 | if (want_command && *str == '\t') | ||
1776 | return str; | ||
1777 | |||
1778 | // Check for comment lines | ||
1779 | p = str; | ||
1780 | while (isblank(*p)) | ||
1781 | p++; | ||
1782 | |||
1783 | if (*p != '\n' && (posix ? *str != '#' : *p != '#')) | ||
1784 | return str; | ||
1785 | } | ||
1786 | |||
1787 | pos = 0; | ||
1788 | } | ||
1789 | } | ||
1790 | |||
1791 | /* | ||
1792 | * Return TRUE if the argument is a known suffix. | ||
1793 | */ | ||
1794 | static int | ||
1795 | is_suffix(const char *s) | ||
1796 | { | ||
1797 | struct name *np; | ||
1798 | struct rule *rp; | ||
1799 | struct depend *dp; | ||
1800 | |||
1801 | np = newname(".SUFFIXES"); | ||
1802 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
1803 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
1804 | if (strcmp(s, dp->d_name->n_name) == 0) { | ||
1805 | return TRUE; | ||
1806 | } | ||
1807 | } | ||
1808 | } | ||
1809 | return FALSE; | ||
1810 | } | ||
1811 | |||
1812 | enum { | ||
1813 | T_NORMAL = 0, | ||
1814 | T_SPECIAL = (1 << 0), | ||
1815 | T_INFERENCE = (1 << 1), // Inference rule | ||
1816 | T_NOPREREQ = (1 << 2), // If set must not have prerequisites | ||
1817 | T_COMMAND = (1 << 3), // If set must have commands, if unset must not | ||
1818 | }; | ||
1819 | |||
1820 | /* | ||
1821 | * Determine if the argument is a special target and return a set | ||
1822 | * of flags indicating its properties. | ||
1823 | */ | ||
1824 | static int | ||
1825 | target_type(char *s) | ||
1826 | { | ||
1827 | char *sfx; | ||
1828 | int ret; | ||
1829 | static const char *s_name = | ||
1830 | ".DEFAULT\0" | ||
1831 | ".POSIX\0" | ||
1832 | ".IGNORE\0" | ||
1833 | ".PRECIOUS\0" | ||
1834 | ".SILENT\0" | ||
1835 | ".SUFFIXES\0" | ||
1836 | ".PHONY\0" | ||
1837 | ".NOTPARALLEL\0" | ||
1838 | ".WAIT\0" | ||
1839 | #if ENABLE_FEATURE_MAKE_POSIX | ||
1840 | ".PRAGMA\0" | ||
1841 | #endif | ||
1842 | ; | ||
1843 | |||
1844 | static const uint8_t s_type[] = { | ||
1845 | T_SPECIAL | T_NOPREREQ | T_COMMAND, | ||
1846 | T_SPECIAL | T_NOPREREQ, | ||
1847 | T_SPECIAL, | ||
1848 | T_SPECIAL, | ||
1849 | T_SPECIAL, | ||
1850 | T_SPECIAL, | ||
1851 | T_SPECIAL, | ||
1852 | T_SPECIAL | T_NOPREREQ, | ||
1853 | T_SPECIAL | T_NOPREREQ, | ||
1854 | T_SPECIAL, | ||
1855 | }; | ||
1856 | |||
1857 | if (*s != '.') | ||
1858 | return T_NORMAL; | ||
1859 | |||
1860 | // Check for one of the known special targets | ||
1861 | ret = index_in_strings(s_name, s); | ||
1862 | if (ret >= 0) | ||
1863 | return s_type[ret]; | ||
1864 | |||
1865 | // Check for an inference rule | ||
1866 | ret = T_NORMAL; | ||
1867 | sfx = suffix(s); | ||
1868 | if (is_suffix(sfx)) { | ||
1869 | if (s == sfx) { // Single suffix rule | ||
1870 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | ||
1871 | } else { | ||
1872 | // Suffix is valid, check that prefix is too | ||
1873 | *sfx = '\0'; | ||
1874 | if (is_suffix(s)) | ||
1875 | ret = T_INFERENCE | T_NOPREREQ | T_COMMAND; | ||
1876 | *sfx = '.'; | ||
1877 | } | ||
1878 | } | ||
1879 | return ret; | ||
1880 | } | ||
1881 | |||
1882 | static int | ||
1883 | ends_with_bracket(const char *s) | ||
1884 | { | ||
1885 | return last_char_is(s, ')') != NULL; | ||
1886 | } | ||
1887 | |||
1888 | /* | ||
1889 | * Process a command line | ||
1890 | */ | ||
1891 | static char * | ||
1892 | process_command(char *s) | ||
1893 | { | ||
1894 | char *t, *u; | ||
1895 | int len; | ||
1896 | char *outside; | ||
1897 | |||
1898 | if (!(pragma & P_COMMAND_COMMENT) && posix) { | ||
1899 | // POSIX strips comments from command lines | ||
1900 | t = strchr(s, '#'); | ||
1901 | if (t) { | ||
1902 | *t = '\0'; | ||
1903 | warning("comment in command removed: keep with pragma command_comment"); | ||
1904 | } | ||
1905 | } | ||
1906 | |||
1907 | len = strlen(s) + 1; | ||
1908 | outside = xzalloc(len); | ||
1909 | for (t = skip_macro(s); *t; t = skip_macro(t + 1)) { | ||
1910 | outside[t - s] = 1; | ||
1911 | } | ||
1912 | |||
1913 | // Process escaped newlines. Stop at first non-escaped newline. | ||
1914 | for (t = u = s; *u && *u != '\n'; ) { | ||
1915 | if (u[0] == '\\' && u[1] == '\n') { | ||
1916 | if (POSIX_2017 || outside[u - s]) { | ||
1917 | // Outside macro: remove tab following escaped newline. | ||
1918 | *t++ = *u++; | ||
1919 | *t++ = *u++; | ||
1920 | u += (*u == '\t'); | ||
1921 | } else { | ||
1922 | // Inside macro: replace escaped newline and any leading | ||
1923 | // whitespace on the following line with a single space. | ||
1924 | u += 2; | ||
1925 | while (isspace(*u)) | ||
1926 | ++u; | ||
1927 | *t++ = ' '; | ||
1928 | } | ||
1929 | } else { | ||
1930 | *t++ = *u++; | ||
1931 | } | ||
1932 | } | ||
1933 | *t = '\0'; | ||
1934 | free(outside); | ||
1935 | return s; | ||
1936 | } | ||
1937 | |||
1938 | static char * | ||
1939 | run_command(const char *cmd) | ||
1940 | { | ||
1941 | FILE *fd; | ||
1942 | char *s, *val = NULL; | ||
1943 | char buf[256]; | ||
1944 | size_t len = 0, nread; | ||
1945 | |||
1946 | if ((fd = popen(cmd, "r")) == NULL) | ||
1947 | return val; | ||
1948 | |||
1949 | for (;;) { | ||
1950 | nread = fread(buf, 1, sizeof(buf), fd); | ||
1951 | if (nread == 0) | ||
1952 | break; | ||
1953 | |||
1954 | val = xrealloc(val, len + nread + 1); | ||
1955 | memcpy(val + len, buf, nread); | ||
1956 | len += nread; | ||
1957 | val[len] = '\0'; | ||
1958 | } | ||
1959 | pclose(fd); | ||
1960 | |||
1961 | if (val == NULL) | ||
1962 | return NULL; | ||
1963 | |||
1964 | // Strip leading whitespace in POSIX 2024 mode | ||
1965 | if (posix) { | ||
1966 | s = val; | ||
1967 | while (isspace(*s)) { | ||
1968 | ++s; | ||
1969 | --len; | ||
1970 | } | ||
1971 | |||
1972 | if (len == 0) { | ||
1973 | free(val); | ||
1974 | return NULL; | ||
1975 | } | ||
1976 | memmove(val, s, len + 1); | ||
1977 | } | ||
1978 | |||
1979 | #if ENABLE_PLATFORM_MINGW32 | ||
1980 | len = remove_cr(val, len + 1) - 1; | ||
1981 | if (len == 0) { | ||
1982 | free(val); | ||
1983 | return NULL; | ||
1984 | } | ||
1985 | #endif | ||
1986 | |||
1987 | // Remove one newline from the end (BSD compatibility) | ||
1988 | if (val[len - 1] == '\n') | ||
1989 | val[len - 1] = '\0'; | ||
1990 | // Other newlines are changed to spaces | ||
1991 | for (s = val; *s; ++s) { | ||
1992 | if (*s == '\n') | ||
1993 | *s = ' '; | ||
1994 | } | ||
1995 | return val; | ||
1996 | } | ||
1997 | |||
1998 | /* | ||
1999 | * Check for an unescaped wildcard character | ||
2000 | */ | ||
2001 | static int wildchar(const char *p) | ||
2002 | { | ||
2003 | while (*p) { | ||
2004 | switch (*p) { | ||
2005 | case '?': | ||
2006 | case '*': | ||
2007 | case '[': | ||
2008 | return 1; | ||
2009 | case '\\': | ||
2010 | if (p[1] != '\0') | ||
2011 | ++p; | ||
2012 | break; | ||
2013 | } | ||
2014 | ++p; | ||
2015 | } | ||
2016 | return 0; | ||
2017 | } | ||
2018 | |||
2019 | /* | ||
2020 | * Expand any wildcards in a pattern. Return TRUE if a match is | ||
2021 | * found, in which case the caller should call globfree() on the | ||
2022 | * glob_t structure. | ||
2023 | */ | ||
2024 | static int | ||
2025 | wildcard(char *p, glob_t *gd) | ||
2026 | { | ||
2027 | int ret; | ||
2028 | char *s; | ||
2029 | |||
2030 | // Don't call glob() if there are no wildcards. | ||
2031 | if (!wildchar(p)) { | ||
2032 | nomatch: | ||
2033 | // Remove backslashes from the name. | ||
2034 | for (s = p; *p; ++p) { | ||
2035 | if (*p == '\\' && p[1] != '\0') | ||
2036 | continue; | ||
2037 | *s++ = *p; | ||
2038 | } | ||
2039 | *s = '\0'; | ||
2040 | return 0; | ||
2041 | } | ||
2042 | |||
2043 | memset(gd, 0, sizeof(*gd)); | ||
2044 | ret = glob(p, GLOB_NOSORT, NULL, gd); | ||
2045 | if (ret == GLOB_NOMATCH) { | ||
2046 | globfree(gd); | ||
2047 | goto nomatch; | ||
2048 | } else if (ret != 0) { | ||
2049 | error("glob error for '%s'", p); | ||
2050 | } | ||
2051 | return 1; | ||
2052 | } | ||
2053 | |||
2054 | #if ENABLE_FEATURE_MAKE_POSIX | ||
2055 | static void | ||
2056 | pragmas_from_env(void) | ||
2057 | { | ||
2058 | char *p, *q, *var; | ||
2059 | const char *env = getenv("PDPMAKE_PRAGMAS"); | ||
2060 | |||
2061 | if (env == NULL) | ||
2062 | return; | ||
2063 | |||
2064 | q = var = xstrdup(env); | ||
2065 | while ((p = gettok(&q)) != NULL) | ||
2066 | set_pragma(p); | ||
2067 | free(var); | ||
2068 | } | ||
2069 | #endif | ||
2070 | |||
2071 | /* | ||
2072 | * Parse input from the makefile and construct a tree structure of it. | ||
2073 | */ | ||
2074 | static void | ||
2075 | input(FILE *fd, int ilevel) | ||
2076 | { | ||
2077 | char *p, *q, *s, *a, *str, *expanded, *copy; | ||
2078 | char *str1, *str2; | ||
2079 | struct name *np; | ||
2080 | struct depend *dp; | ||
2081 | struct cmd *cp; | ||
2082 | int startno, count; | ||
2083 | bool semicolon_cmd, seen_inference; | ||
2084 | uint8_t old_clevel = clevel; | ||
2085 | bool dbl; | ||
2086 | char *lib = NULL; | ||
2087 | glob_t gd; | ||
2088 | int nfile, i; | ||
2089 | char **files; | ||
2090 | bool minus; | ||
2091 | |||
2092 | lineno = 0; | ||
2093 | str1 = readline(fd, FALSE); | ||
2094 | while (str1) { | ||
2095 | str2 = NULL; | ||
2096 | |||
2097 | // Newlines and comments are handled differently in command lines | ||
2098 | // and other types of line. Take a copy of the current line before | ||
2099 | // processing it as a non-command line in case it contains a | ||
2100 | // rule with a command line. That is, a line of the form: | ||
2101 | // | ||
2102 | // target: prereq; command | ||
2103 | // | ||
2104 | copy = xstrdup(str1); | ||
2105 | process_line(str1); | ||
2106 | str = str1; | ||
2107 | |||
2108 | // Check for an include line | ||
2109 | if (!posix) | ||
2110 | while (isblank(*str)) | ||
2111 | ++str; | ||
2112 | minus = !POSIX_2017 && *str == '-'; | ||
2113 | p = str + minus; | ||
2114 | if (strncmp(p, "include", 7) == 0 && isblank(p[7])) { | ||
2115 | const char *old_makefile = makefile; | ||
2116 | int old_lineno = lineno; | ||
2117 | |||
2118 | if (ilevel > 16) | ||
2119 | error("too many includes"); | ||
2120 | |||
2121 | count = 0; | ||
2122 | q = expanded = expand_macros(p + 7, FALSE); | ||
2123 | while ((p = gettok(&q)) != NULL) { | ||
2124 | FILE *ifd; | ||
2125 | |||
2126 | ++count; | ||
2127 | if (!POSIX_2017) { | ||
2128 | // Try to create include file or bring it up-to-date | ||
2129 | opts |= OPT_include; | ||
2130 | make(newname(p), 1); | ||
2131 | opts &= ~OPT_include; | ||
2132 | } | ||
2133 | if ((ifd = fopen(p, "r")) == NULL) { | ||
2134 | if (!minus) | ||
2135 | error("can't open include file '%s'", p); | ||
2136 | } else { | ||
2137 | makefile = p; | ||
2138 | input(ifd, ilevel + 1); | ||
2139 | fclose(ifd); | ||
2140 | makefile = old_makefile; | ||
2141 | lineno = old_lineno; | ||
2142 | } | ||
2143 | if (POSIX_2017) | ||
2144 | break; | ||
2145 | } | ||
2146 | if (POSIX_2017) { | ||
2147 | // In POSIX 2017 zero or more than one include file is | ||
2148 | // unspecified behaviour. | ||
2149 | if (p == NULL || gettok(&q)) { | ||
2150 | error("one include file per line"); | ||
2151 | } | ||
2152 | } else if (count == 0) { | ||
2153 | // In POSIX 2024 no include file is unspecified behaviour. | ||
2154 | if (posix) | ||
2155 | error("no include file"); | ||
2156 | } | ||
2157 | goto end_loop; | ||
2158 | } | ||
2159 | |||
2160 | // Check for a macro definition | ||
2161 | str = str1; | ||
2162 | // POSIX 2024 seems to allow a tab as the first character of | ||
2163 | // a macro definition, though most implementations don't. | ||
2164 | if (POSIX_2017 && *str == '\t') | ||
2165 | error("command not allowed here"); | ||
2166 | if (find_char(str, '=') != NULL) { | ||
2167 | int level = (useenv || fd == NULL) ? 4 : 3; | ||
2168 | // Use a copy of the line: we might need the original | ||
2169 | // if this turns out to be a target rule. | ||
2170 | char *copy2 = xstrdup(str); | ||
2171 | char *newq = NULL; | ||
2172 | char eq = '\0'; | ||
2173 | q = find_char(copy2, '='); // q can't be NULL | ||
2174 | |||
2175 | if (q - 1 > copy2) { | ||
2176 | switch (q[-1]) { | ||
2177 | case ':': | ||
2178 | // '::=' and ':::=' are from POSIX 2024. | ||
2179 | if (!POSIX_2017 && q - 2 > copy2 && q[-2] == ':') { | ||
2180 | if (q - 3 > copy2 && q[-3] == ':') { | ||
2181 | eq = 'B'; // BSD-style ':=' | ||
2182 | q[-3] = '\0'; | ||
2183 | } else { | ||
2184 | eq = ':'; // GNU-style ':=' | ||
2185 | q[-2] = '\0'; | ||
2186 | } | ||
2187 | break; | ||
2188 | } | ||
2189 | // ':=' is a non-POSIX extension. | ||
2190 | if (posix) | ||
2191 | break; | ||
2192 | goto set_eq; | ||
2193 | case '+': | ||
2194 | case '?': | ||
2195 | case '!': | ||
2196 | // '+=', '?=' and '!=' are from POSIX 2024. | ||
2197 | if (POSIX_2017) | ||
2198 | break; | ||
2199 | set_eq: | ||
2200 | eq = q[-1]; | ||
2201 | q[-1] = '\0'; | ||
2202 | break; | ||
2203 | } | ||
2204 | } | ||
2205 | *q++ = '\0'; // Separate name and value | ||
2206 | while (isblank(*q)) | ||
2207 | q++; | ||
2208 | if ((p = strrchr(q, '\n')) != NULL) | ||
2209 | *p = '\0'; | ||
2210 | |||
2211 | // Expand left-hand side of assignment | ||
2212 | p = expanded = expand_macros(copy2, FALSE); | ||
2213 | if ((a = gettok(&p)) == NULL) | ||
2214 | error("invalid macro assignment"); | ||
2215 | |||
2216 | // If the expanded LHS contains ':' and ';' it can't be a | ||
2217 | // macro assignment but it might be a target rule. | ||
2218 | if ((s = strchr(a, ':')) != NULL && strchr(s, ';') != NULL) { | ||
2219 | free(expanded); | ||
2220 | free(copy2); | ||
2221 | goto try_target; | ||
2222 | } | ||
2223 | |||
2224 | if (gettok(&p)) | ||
2225 | error("invalid macro assignment"); | ||
2226 | |||
2227 | if (eq == ':') { | ||
2228 | // GNU-style ':='. Expand right-hand side of assignment. | ||
2229 | // Macro is of type immediate-expansion. | ||
2230 | q = newq = expand_macros(q, FALSE); | ||
2231 | level |= M_IMMEDIATE; | ||
2232 | } | ||
2233 | else if (eq == 'B') { | ||
2234 | // BSD-style ':='. Expand right-hand side of assignment, | ||
2235 | // though not '$$'. Macro is of type delayed-expansion. | ||
2236 | q = newq = expand_macros(q, TRUE); | ||
2237 | } else if (eq == '?' && getmp(a) != NULL) { | ||
2238 | // Skip assignment if macro is already set | ||
2239 | goto end_loop; | ||
2240 | } else if (eq == '+') { | ||
2241 | // Append to current value | ||
2242 | struct macro *mp = getmp(a); | ||
2243 | char *rhs; | ||
2244 | newq = mp && mp->m_val[0] ? xstrdup(mp->m_val) : NULL; | ||
2245 | if (mp && mp->m_immediate) { | ||
2246 | // Expand right-hand side of assignment (GNU make | ||
2247 | // compatibility) | ||
2248 | rhs = expand_macros(q, FALSE); | ||
2249 | level |= M_IMMEDIATE; | ||
2250 | } else { | ||
2251 | rhs = q; | ||
2252 | } | ||
2253 | newq = xappendword(newq, rhs); | ||
2254 | if (rhs != q) | ||
2255 | free(rhs); | ||
2256 | q = newq; | ||
2257 | } else if (eq == '!') { | ||
2258 | char *cmd = expand_macros(q, FALSE); | ||
2259 | q = newq = run_command(cmd); | ||
2260 | free(cmd); | ||
2261 | } | ||
2262 | setmacro(a, q, level); | ||
2263 | free(newq); | ||
2264 | free(copy2); | ||
2265 | goto end_loop; | ||
2266 | } | ||
2267 | |||
2268 | // If we get here it must be a target rule | ||
2269 | try_target: | ||
2270 | if (*str == '\t') // Command without target | ||
2271 | error("command not allowed here"); | ||
2272 | p = expanded = expand_macros(str, FALSE); | ||
2273 | |||
2274 | // Look for colon separator | ||
2275 | q = find_colon(p); | ||
2276 | if (q == NULL) | ||
2277 | error("expected separator"); | ||
2278 | |||
2279 | *q++ = '\0'; // Separate targets and prerequisites | ||
2280 | |||
2281 | // Double colon | ||
2282 | dbl = !posix && *q == ':'; | ||
2283 | if (dbl) | ||
2284 | q++; | ||
2285 | |||
2286 | // Look for semicolon separator | ||
2287 | cp = NULL; | ||
2288 | s = strchr(q, ';'); | ||
2289 | if (s) { | ||
2290 | // Retrieve command from expanded copy of line | ||
2291 | char *copy3 = expand_macros(copy, FALSE); | ||
2292 | if ((p = find_colon(copy3)) && (p = strchr(p, ';'))) | ||
2293 | newcmd(&cp, process_command(p + 1)); | ||
2294 | free(copy3); | ||
2295 | *s = '\0'; | ||
2296 | } | ||
2297 | semicolon_cmd = cp != NULL && cp->c_cmd[0] != '\0'; | ||
2298 | |||
2299 | // Create list of prerequisites | ||
2300 | dp = NULL; | ||
2301 | while (((p = gettok(&q)) != NULL)) { | ||
2302 | char *newp = NULL; | ||
2303 | |||
2304 | if (!posix) { | ||
2305 | // Allow prerequisites of form library(member1 member2). | ||
2306 | // Leading and trailing spaces in the brackets are skipped. | ||
2307 | if (!lib) { | ||
2308 | s = strchr(p, '('); | ||
2309 | if (s && !ends_with_bracket(s) && strchr(q, ')')) { | ||
2310 | // Looks like an unterminated archive member | ||
2311 | // with a terminator later on the line. | ||
2312 | lib = p; | ||
2313 | if (s[1] != '\0') { | ||
2314 | p = newp = auto_concat(lib, ")"); | ||
2315 | s[1] = '\0'; | ||
2316 | } else { | ||
2317 | continue; | ||
2318 | } | ||
2319 | } | ||
2320 | } else if (ends_with_bracket(p)) { | ||
2321 | if (*p != ')') | ||
2322 | p = newp = auto_concat(lib, p); | ||
2323 | lib = NULL; | ||
2324 | if (newp == NULL) | ||
2325 | continue; | ||
2326 | } else { | ||
2327 | p = newp = auto_string(xasprintf("%s%s)", lib, p)); | ||
2328 | } | ||
2329 | } | ||
2330 | |||
2331 | // If not in POSIX mode expand wildcards in the name. | ||
2332 | nfile = 1; | ||
2333 | files = &p; | ||
2334 | if (!posix && wildcard(p, &gd)) { | ||
2335 | nfile = gd.gl_pathc; | ||
2336 | files = gd.gl_pathv; | ||
2337 | } | ||
2338 | for (i = 0; i < nfile; ++i) { | ||
2339 | if (!POSIX_2017 && strcmp(files[i], ".WAIT") == 0) | ||
2340 | continue; | ||
2341 | np = newname(files[i]); | ||
2342 | newdep(&dp, np); | ||
2343 | } | ||
2344 | if (files != &p) | ||
2345 | globfree(&gd); | ||
2346 | free(newp); | ||
2347 | } | ||
2348 | lib = NULL; | ||
2349 | |||
2350 | // Create list of commands | ||
2351 | startno = dispno; | ||
2352 | while ((str2 = readline(fd, TRUE)) && *str2 == '\t') { | ||
2353 | newcmd(&cp, process_command(str2)); | ||
2354 | free(str2); | ||
2355 | } | ||
2356 | dispno = startno; | ||
2357 | |||
2358 | // Create target names and attach rule to them | ||
2359 | q = expanded; | ||
2360 | count = 0; | ||
2361 | seen_inference = FALSE; | ||
2362 | while ((p = gettok(&q)) != NULL) { | ||
2363 | // If not in POSIX mode expand wildcards in the name. | ||
2364 | nfile = 1; | ||
2365 | files = &p; | ||
2366 | if (!posix && wildcard(p, &gd)) { | ||
2367 | nfile = gd.gl_pathc; | ||
2368 | files = gd.gl_pathv; | ||
2369 | } | ||
2370 | for (i = 0; i < nfile; ++i) { | ||
2371 | int ttype = target_type(files[i]); | ||
2372 | |||
2373 | np = newname(files[i]); | ||
2374 | if (ttype != T_NORMAL) { | ||
2375 | // Enforce prerequisites/commands in POSIX mode | ||
2376 | if (posix) { | ||
2377 | if ((ttype & T_NOPREREQ) && dp) | ||
2378 | error_not_allowed("prerequisites", p); | ||
2379 | if ((ttype & T_INFERENCE)) { | ||
2380 | if (semicolon_cmd) | ||
2381 | error_in_inference_rule("'; command'"); | ||
2382 | seen_inference = TRUE; | ||
2383 | } | ||
2384 | if ((ttype & T_COMMAND) && !cp && | ||
2385 | !((ttype & T_INFERENCE) && !semicolon_cmd)) | ||
2386 | error("commands required for %s", p); | ||
2387 | if (!(ttype & T_COMMAND) && cp) | ||
2388 | error_not_allowed("commands", p); | ||
2389 | } | ||
2390 | |||
2391 | if ((ttype & T_INFERENCE)) { | ||
2392 | np->n_flag |= N_INFERENCE; | ||
2393 | } else if (strcmp(p, ".DEFAULT") == 0) { | ||
2394 | // .DEFAULT rule is a special case | ||
2395 | np->n_flag |= N_SPECIAL | N_INFERENCE; | ||
2396 | } else { | ||
2397 | np->n_flag |= N_SPECIAL; | ||
2398 | } | ||
2399 | } else if (!firstname) { | ||
2400 | firstname = np; | ||
2401 | } | ||
2402 | addrule(np, dp, cp, dbl); | ||
2403 | count++; | ||
2404 | } | ||
2405 | if (files != &p) | ||
2406 | globfree(&gd); | ||
2407 | } | ||
2408 | if (posix && seen_inference && count != 1) | ||
2409 | error_in_inference_rule("multiple targets"); | ||
2410 | |||
2411 | // Prerequisites and commands will be unused if there were | ||
2412 | // no targets. Avoid leaking memory. | ||
2413 | if (count == 0) { | ||
2414 | freedeps(dp); | ||
2415 | freecmds(cp); | ||
2416 | } | ||
2417 | |||
2418 | end_loop: | ||
2419 | free(str1); | ||
2420 | dispno = lineno; | ||
2421 | str1 = str2 ? str2 : readline(fd, FALSE); | ||
2422 | free(copy); | ||
2423 | free(expanded); | ||
2424 | #if ENABLE_FEATURE_MAKE_POSIX | ||
2425 | if (!seen_first && fd) { | ||
2426 | if (findname(".POSIX")) { | ||
2427 | // The first non-comment line from a real makefile | ||
2428 | // defined the .POSIX special target. | ||
2429 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); | ||
2430 | posix = TRUE; | ||
2431 | } | ||
2432 | seen_first = TRUE; | ||
2433 | } | ||
2434 | #endif | ||
2435 | } | ||
2436 | // Conditionals aren't allowed to span files | ||
2437 | if (clevel != old_clevel) | ||
2438 | error("invalid conditional"); | ||
2439 | } | ||
2440 | |||
2441 | static void | ||
2442 | remove_target(void) | ||
2443 | { | ||
2444 | if (!dryrun && !print && !precious && | ||
2445 | target && !(target->n_flag & (N_PRECIOUS | N_PHONY)) && | ||
2446 | unlink(target->n_name) == 0) { | ||
2447 | diagnostic("'%s' removed", target->n_name); | ||
2448 | } | ||
2449 | } | ||
2450 | |||
2451 | /* | ||
2452 | * Update the modification time of a file to now. | ||
2453 | */ | ||
2454 | static void | ||
2455 | touch(struct name *np) | ||
2456 | { | ||
2457 | if (dryrun || !silent) | ||
2458 | printf("touch %s\n", np->n_name); | ||
2459 | |||
2460 | if (!dryrun) { | ||
2461 | const struct timespec timebuf[2] = {{0, UTIME_NOW}, {0, UTIME_NOW}}; | ||
2462 | |||
2463 | if (utimensat(AT_FDCWD, np->n_name, timebuf, 0) < 0) { | ||
2464 | if (errno == ENOENT) { | ||
2465 | int fd = open(np->n_name, O_RDWR | O_CREAT, 0666); | ||
2466 | if (fd >= 0) { | ||
2467 | close(fd); | ||
2468 | return; | ||
2469 | } | ||
2470 | } | ||
2471 | warning("touch %s failed", np->n_name); | ||
2472 | } | ||
2473 | } | ||
2474 | } | ||
2475 | |||
2476 | /* | ||
2477 | * Do commands to make a target | ||
2478 | */ | ||
2479 | static int | ||
2480 | docmds(struct name *np, struct cmd *cp) | ||
2481 | { | ||
2482 | int estat = 0; | ||
2483 | char *q, *command; | ||
2484 | |||
2485 | for (; cp; cp = cp->c_next) { | ||
2486 | uint32_t ssilent, signore, sdomake; | ||
2487 | |||
2488 | // Location of command in makefile (for use in error messages) | ||
2489 | makefile = cp->c_makefile; | ||
2490 | dispno = cp->c_dispno; | ||
2491 | opts &= ~OPT_make; // We want to know if $(MAKE) is expanded | ||
2492 | q = command = expand_macros(cp->c_cmd, FALSE); | ||
2493 | ssilent = silent || (np->n_flag & N_SILENT) || dotouch; | ||
2494 | signore = ignore || (np->n_flag & N_IGNORE); | ||
2495 | sdomake = (!dryrun || doinclude || domake) && !dotouch; | ||
2496 | for (;;) { | ||
2497 | if (*q == '@') // Specific silent | ||
2498 | ssilent = TRUE + 1; | ||
2499 | else if (*q == '-') // Specific ignore | ||
2500 | signore = TRUE; | ||
2501 | else if (*q == '+') // Specific domake | ||
2502 | sdomake = TRUE + 1; | ||
2503 | else | ||
2504 | break; | ||
2505 | do { | ||
2506 | q++; | ||
2507 | } while (isblank(*q)); | ||
2508 | } | ||
2509 | |||
2510 | if (sdomake > TRUE) { | ||
2511 | // '+' must not override '@' or .SILENT | ||
2512 | if (ssilent != TRUE + 1 && !(np->n_flag & N_SILENT)) | ||
2513 | ssilent = FALSE; | ||
2514 | } else if (!sdomake) | ||
2515 | ssilent = dotouch; | ||
2516 | |||
2517 | if (!ssilent && *q != '\0') { // Ignore empty commands | ||
2518 | puts(q); | ||
2519 | fflush_all(); | ||
2520 | } | ||
2521 | |||
2522 | if (quest && sdomake != TRUE + 1) { | ||
2523 | // MAKE_FAILURE means rebuild is needed | ||
2524 | estat |= MAKE_FAILURE | MAKE_DIDSOMETHING; | ||
2525 | continue; | ||
2526 | } | ||
2527 | |||
2528 | if (sdomake && *q != '\0') { // Ignore empty commands | ||
2529 | // Get the shell to execute it | ||
2530 | int status; | ||
2531 | char *cmd = !signore && posix ? auto_concat("set -e;", q) : q; | ||
2532 | |||
2533 | target = np; | ||
2534 | status = system(cmd); | ||
2535 | // If this command was being run to create an include file | ||
2536 | // or bring it up-to-date errors should be ignored and a | ||
2537 | // failure status returned. | ||
2538 | if (status == -1 && !doinclude) { | ||
2539 | error("couldn't execute '%s'", q); | ||
2540 | } else if (status != 0 && !signore) { | ||
2541 | if (!posix && WIFSIGNALED(status)) | ||
2542 | remove_target(); | ||
2543 | if (doinclude) { | ||
2544 | warning("failed to build '%s'", np->n_name); | ||
2545 | } else { | ||
2546 | const char *err_type = NULL; | ||
2547 | int err_value = 1; | ||
2548 | |||
2549 | if (WIFEXITED(status)) { | ||
2550 | err_type = "exit"; | ||
2551 | err_value = WEXITSTATUS(status); | ||
2552 | } else if (WIFSIGNALED(status)) { | ||
2553 | err_type = "signal"; | ||
2554 | err_value = WTERMSIG(status); | ||
2555 | } | ||
2556 | |||
2557 | if (!quest || err_value == 127) { | ||
2558 | if (err_type) | ||
2559 | diagnostic("failed to build '%s' %s %d", | ||
2560 | np->n_name, err_type, err_value); | ||
2561 | else | ||
2562 | diagnostic("failed to build '%s'", np->n_name); | ||
2563 | } | ||
2564 | |||
2565 | if (errcont) { | ||
2566 | estat |= MAKE_FAILURE; | ||
2567 | free(command); | ||
2568 | break; | ||
2569 | } | ||
2570 | exit(2); | ||
2571 | } | ||
2572 | } | ||
2573 | } | ||
2574 | if (sdomake || dryrun) | ||
2575 | estat = MAKE_DIDSOMETHING; | ||
2576 | free(command); | ||
2577 | } | ||
2578 | |||
2579 | if (dotouch && !(np->n_flag & N_PHONY) && !(estat & MAKE_DIDSOMETHING)) { | ||
2580 | touch(np); | ||
2581 | estat = MAKE_DIDSOMETHING; | ||
2582 | } | ||
2583 | |||
2584 | makefile = NULL; | ||
2585 | return estat; | ||
2586 | } | ||
2587 | |||
2588 | static int | ||
2589 | make1(struct name *np, struct cmd *cp, char *oodate, char *allsrc, | ||
2590 | char *dedup, struct name *implicit) | ||
2591 | { | ||
2592 | char *name, *member = NULL, *base = NULL, *prereq = NULL; | ||
2593 | |||
2594 | name = splitlib(np->n_name, &member); | ||
2595 | setmacro("?", oodate, 0 | M_VALID); | ||
2596 | if (!POSIX_2017) { | ||
2597 | setmacro("+", allsrc, 0 | M_VALID); | ||
2598 | setmacro("^", dedup, 0 | M_VALID); | ||
2599 | } | ||
2600 | setmacro("%", member, 0 | M_VALID); | ||
2601 | setmacro("@", name, 0 | M_VALID); | ||
2602 | if (implicit || !posix) { | ||
2603 | char *s; | ||
2604 | |||
2605 | // As an extension, if we're not dealing with an implicit | ||
2606 | // rule set $< to the first out-of-date prerequisite. | ||
2607 | if (implicit == NULL) { | ||
2608 | if (oodate) { | ||
2609 | s = strchr(oodate, ' '); | ||
2610 | if (s) | ||
2611 | *s = '\0'; | ||
2612 | prereq = oodate; | ||
2613 | } | ||
2614 | } else | ||
2615 | prereq = implicit->n_name; | ||
2616 | |||
2617 | base = member ? member : name; | ||
2618 | s = suffix(base); | ||
2619 | // As an extension, if we're not dealing with an implicit | ||
2620 | // rule and the target ends with a known suffix, remove it | ||
2621 | // and set $* to the stem, else to an empty string. | ||
2622 | if (implicit == NULL && !is_suffix(s)) | ||
2623 | base = NULL; | ||
2624 | else | ||
2625 | *s = '\0'; | ||
2626 | } | ||
2627 | setmacro("<", prereq, 0 | M_VALID); | ||
2628 | setmacro("*", base, 0 | M_VALID); | ||
2629 | free(name); | ||
2630 | |||
2631 | return docmds(np, cp); | ||
2632 | } | ||
2633 | |||
2634 | /* | ||
2635 | * Determine if the modification time of a target, t, is less than | ||
2636 | * that of a prerequisite, p. If the tv_nsec member of either is | ||
2637 | * exactly 0 we assume (possibly incorrectly) that the time resolution | ||
2638 | * is 1 second and only compare tv_sec values. | ||
2639 | */ | ||
2640 | static int | ||
2641 | timespec_le(const struct timespec *t, const struct timespec *p) | ||
2642 | { | ||
2643 | if (t->tv_nsec == 0 || p->tv_nsec == 0) | ||
2644 | return t->tv_sec <= p->tv_sec; | ||
2645 | else if (t->tv_sec < p->tv_sec) | ||
2646 | return TRUE; | ||
2647 | else if (t->tv_sec == p->tv_sec) | ||
2648 | return t->tv_nsec <= p->tv_nsec; | ||
2649 | return FALSE; | ||
2650 | } | ||
2651 | |||
2652 | /* | ||
2653 | * Return the greater of two struct timespecs | ||
2654 | */ | ||
2655 | static const struct timespec * | ||
2656 | timespec_max(const struct timespec *t, const struct timespec *p) | ||
2657 | { | ||
2658 | return timespec_le(t, p) ? p : t; | ||
2659 | } | ||
2660 | |||
2661 | /* | ||
2662 | * Recursive routine to make a target. | ||
2663 | */ | ||
2664 | static int | ||
2665 | make(struct name *np, int level) | ||
2666 | { | ||
2667 | struct depend *dp; | ||
2668 | struct rule *rp; | ||
2669 | struct name *impdep = NULL; // implicit prerequisite | ||
2670 | struct rule imprule; | ||
2671 | struct cmd *sc_cmd = NULL; // commands for single-colon rule | ||
2672 | char *oodate = NULL; | ||
2673 | char *allsrc = NULL; | ||
2674 | char *dedup = NULL; | ||
2675 | struct timespec dtim = {1, 0}; | ||
2676 | int estat = 0; | ||
2677 | |||
2678 | if (np->n_flag & N_DONE) | ||
2679 | return 0; | ||
2680 | if (np->n_flag & N_DOING) | ||
2681 | error("circular dependency for %s", np->n_name); | ||
2682 | np->n_flag |= N_DOING; | ||
2683 | |||
2684 | if (!np->n_tim.tv_sec) | ||
2685 | modtime(np); // Get modtime of this file | ||
2686 | |||
2687 | if (!(np->n_flag & N_DOUBLE)) { | ||
2688 | // Find the commands needed for a single-colon rule, using | ||
2689 | // an inference rule or .DEFAULT rule if necessary (but, | ||
2690 | // as an extension, not for phony targets) | ||
2691 | sc_cmd = getcmd(np); | ||
2692 | if (!sc_cmd && (posix || !(np->n_flag & N_PHONY))) { | ||
2693 | impdep = dyndep(np, &imprule); | ||
2694 | if (impdep) { | ||
2695 | sc_cmd = imprule.r_cmd; | ||
2696 | addrule(np, imprule.r_dep, NULL, FALSE); | ||
2697 | } | ||
2698 | } | ||
2699 | |||
2700 | // As a last resort check for a default rule | ||
2701 | if (!(np->n_flag & N_TARGET) && np->n_tim.tv_sec == 0) { | ||
2702 | if (posix || !(np->n_flag & N_PHONY)) | ||
2703 | sc_cmd = getcmd(findname(".DEFAULT")); | ||
2704 | if (!sc_cmd) { | ||
2705 | if (doinclude) | ||
2706 | return 1; | ||
2707 | error("don't know how to make %s", np->n_name); | ||
2708 | } | ||
2709 | impdep = np; | ||
2710 | } | ||
2711 | } | ||
2712 | else { | ||
2713 | // If any double-colon rule has no commands we need | ||
2714 | // an inference rule (but, as an extension, not for phony targets) | ||
2715 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
2716 | if (!rp->r_cmd) { | ||
2717 | if (posix || !(np->n_flag & N_PHONY)) | ||
2718 | impdep = dyndep(np, &imprule); | ||
2719 | if (!impdep) { | ||
2720 | if (doinclude) | ||
2721 | return 1; | ||
2722 | error("don't know how to make %s", np->n_name); | ||
2723 | } | ||
2724 | break; | ||
2725 | } | ||
2726 | } | ||
2727 | } | ||
2728 | |||
2729 | // Reset flag to detect duplicate prerequisites | ||
2730 | if (!(np->n_flag & N_DOUBLE)) { | ||
2731 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
2732 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
2733 | dp->d_name->n_flag &= ~N_MARK; | ||
2734 | } | ||
2735 | } | ||
2736 | } | ||
2737 | |||
2738 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
2739 | struct name *locdep = NULL; | ||
2740 | |||
2741 | // Each double-colon rule is handled separately. | ||
2742 | if ((np->n_flag & N_DOUBLE)) { | ||
2743 | // If the rule has no commands use the inference rule. | ||
2744 | if (!rp->r_cmd) { | ||
2745 | locdep = impdep; | ||
2746 | imprule.r_dep->d_next = rp->r_dep; | ||
2747 | rp->r_dep = imprule.r_dep; | ||
2748 | rp->r_cmd = imprule.r_cmd; | ||
2749 | } | ||
2750 | // A rule with no prerequisities is executed unconditionally. | ||
2751 | if (!rp->r_dep) | ||
2752 | dtim = np->n_tim; | ||
2753 | // Reset flag to detect duplicate prerequisites | ||
2754 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
2755 | dp->d_name->n_flag &= ~N_MARK; | ||
2756 | } | ||
2757 | } | ||
2758 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
2759 | // Make prerequisite | ||
2760 | estat |= make(dp->d_name, level + 1); | ||
2761 | |||
2762 | // Make strings of out-of-date prerequisites (for $?), | ||
2763 | // all prerequisites (for $+) and deduplicated prerequisites | ||
2764 | // (for $^). | ||
2765 | if (timespec_le(&np->n_tim, &dp->d_name->n_tim)) { | ||
2766 | if (posix || !(dp->d_name->n_flag & N_MARK)) | ||
2767 | oodate = xappendword(oodate, dp->d_name->n_name); | ||
2768 | } | ||
2769 | allsrc = xappendword(allsrc, dp->d_name->n_name); | ||
2770 | if (!(dp->d_name->n_flag & N_MARK)) | ||
2771 | dedup = xappendword(dedup, dp->d_name->n_name); | ||
2772 | dp->d_name->n_flag |= N_MARK; | ||
2773 | dtim = *timespec_max(&dtim, &dp->d_name->n_tim); | ||
2774 | } | ||
2775 | if ((np->n_flag & N_DOUBLE)) { | ||
2776 | if (((np->n_flag & N_PHONY) || timespec_le(&np->n_tim, &dtim))) { | ||
2777 | if (!(estat & MAKE_FAILURE)) { | ||
2778 | estat |= make1(np, rp->r_cmd, oodate, allsrc, | ||
2779 | dedup, locdep); | ||
2780 | dtim = (struct timespec){1, 0}; | ||
2781 | } | ||
2782 | free(oodate); | ||
2783 | oodate = NULL; | ||
2784 | } | ||
2785 | free(allsrc); | ||
2786 | free(dedup); | ||
2787 | allsrc = dedup = NULL; | ||
2788 | if (locdep) { | ||
2789 | rp->r_dep = rp->r_dep->d_next; | ||
2790 | rp->r_cmd = NULL; | ||
2791 | } | ||
2792 | } | ||
2793 | } | ||
2794 | if ((np->n_flag & N_DOUBLE) && impdep) | ||
2795 | free(imprule.r_dep); | ||
2796 | |||
2797 | np->n_flag |= N_DONE; | ||
2798 | np->n_flag &= ~N_DOING; | ||
2799 | |||
2800 | if (!(np->n_flag & N_DOUBLE) && | ||
2801 | ((np->n_flag & N_PHONY) || (timespec_le(&np->n_tim, &dtim)))) { | ||
2802 | if (!(estat & MAKE_FAILURE)) { | ||
2803 | if (sc_cmd) | ||
2804 | estat |= make1(np, sc_cmd, oodate, allsrc, dedup, impdep); | ||
2805 | else if (!doinclude && level == 0 && !(estat & MAKE_DIDSOMETHING)) | ||
2806 | warning("nothing to be done for %s", np->n_name); | ||
2807 | } else if (!doinclude && !quest) { | ||
2808 | diagnostic("'%s' not built due to errors", np->n_name); | ||
2809 | } | ||
2810 | free(oodate); | ||
2811 | } | ||
2812 | |||
2813 | if (estat & MAKE_DIDSOMETHING) { | ||
2814 | modtime(np); | ||
2815 | if (!np->n_tim.tv_sec) | ||
2816 | clock_gettime(CLOCK_REALTIME, &np->n_tim); | ||
2817 | } else if (!quest && level == 0 && !timespec_le(&np->n_tim, &dtim)) | ||
2818 | printf("%s: '%s' is up to date\n", applet_name, np->n_name); | ||
2819 | |||
2820 | free(allsrc); | ||
2821 | free(dedup); | ||
2822 | return estat; | ||
2823 | } | ||
2824 | |||
2825 | /* | ||
2826 | * Check structures for make. | ||
2827 | */ | ||
2828 | |||
2829 | static void | ||
2830 | print_name(struct name *np) | ||
2831 | { | ||
2832 | if (np == firstname) | ||
2833 | printf("# default target\n"); | ||
2834 | printf("%s:", np->n_name); | ||
2835 | if ((np->n_flag & N_DOUBLE)) | ||
2836 | putchar(':'); | ||
2837 | } | ||
2838 | |||
2839 | static void | ||
2840 | print_prerequisites(struct rule *rp) | ||
2841 | { | ||
2842 | struct depend *dp; | ||
2843 | |||
2844 | for (dp = rp->r_dep; dp; dp = dp->d_next) | ||
2845 | printf(" %s", dp->d_name->n_name); | ||
2846 | } | ||
2847 | |||
2848 | static void | ||
2849 | print_commands(struct rule *rp) | ||
2850 | { | ||
2851 | struct cmd *cp; | ||
2852 | |||
2853 | for (cp = rp->r_cmd; cp; cp = cp->c_next) | ||
2854 | printf("\t%s\n", cp->c_cmd); | ||
2855 | } | ||
2856 | |||
2857 | static void | ||
2858 | print_details(void) | ||
2859 | { | ||
2860 | int i; | ||
2861 | struct macro *mp; | ||
2862 | struct name *np; | ||
2863 | struct rule *rp; | ||
2864 | |||
2865 | for (i = 0; i < HTABSIZE; i++) | ||
2866 | for (mp = macrohead[i]; mp; mp = mp->m_next) | ||
2867 | printf("%s = %s\n", mp->m_name, mp->m_val); | ||
2868 | putchar('\n'); | ||
2869 | |||
2870 | for (i = 0; i < HTABSIZE; i++) { | ||
2871 | for (np = namehead[i]; np; np = np->n_next) { | ||
2872 | if (!(np->n_flag & N_DOUBLE)) { | ||
2873 | print_name(np); | ||
2874 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
2875 | print_prerequisites(rp); | ||
2876 | } | ||
2877 | putchar('\n'); | ||
2878 | |||
2879 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
2880 | print_commands(rp); | ||
2881 | } | ||
2882 | putchar('\n'); | ||
2883 | } else { | ||
2884 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
2885 | print_name(np); | ||
2886 | print_prerequisites(rp); | ||
2887 | putchar('\n'); | ||
2888 | |||
2889 | print_commands(rp); | ||
2890 | putchar('\n'); | ||
2891 | } | ||
2892 | } | ||
2893 | } | ||
2894 | } | ||
2895 | } | ||
2896 | |||
2897 | /* | ||
2898 | * Process options from an argv array. If from_env is non-zero we're | ||
2899 | * handling options from MAKEFLAGS so skip '-C', '-f', '-p' and '-x'. | ||
2900 | */ | ||
2901 | static uint32_t | ||
2902 | process_options(char **argv, int from_env) | ||
2903 | { | ||
2904 | uint32_t flags; | ||
2905 | |||
2906 | flags = getopt32(argv, "^" OPTSTR1 OPTSTR2 "\0k-S:S-k", | ||
2907 | &numjobs, &makefiles, &dirs | ||
2908 | IF_FEATURE_MAKE_POSIX(, &pragmas)); | ||
2909 | if (from_env && (flags & (OPT_C | OPT_f | OPT_p | OPT_x))) | ||
2910 | error("invalid MAKEFLAGS"); | ||
2911 | if (posix && (flags & OPT_C)) | ||
2912 | error("-C not allowed"); | ||
2913 | |||
2914 | return flags; | ||
2915 | } | ||
2916 | |||
2917 | /* | ||
2918 | * Split the contents of MAKEFLAGS into an argv array. If the return | ||
2919 | * value (call it fargv) isn't NULL the caller should free fargv[1] and | ||
2920 | * fargv. | ||
2921 | */ | ||
2922 | static char ** | ||
2923 | expand_makeflags(void) | ||
2924 | { | ||
2925 | const char *m, *makeflags = getenv("MAKEFLAGS"); | ||
2926 | char *p, *argstr; | ||
2927 | int argc; | ||
2928 | char **argv; | ||
2929 | |||
2930 | if (makeflags == NULL) | ||
2931 | return NULL; | ||
2932 | |||
2933 | while (isblank(*makeflags)) | ||
2934 | makeflags++; | ||
2935 | |||
2936 | if (*makeflags == '\0') | ||
2937 | return NULL; | ||
2938 | |||
2939 | p = argstr = xzalloc(strlen(makeflags) + 2); | ||
2940 | |||
2941 | // If MAKEFLAGS doesn't start with a hyphen, doesn't look like | ||
2942 | // a macro definition and only contains valid option characters, | ||
2943 | // add a hyphen. | ||
2944 | argc = 3; | ||
2945 | if (makeflags[0] != '-' && strchr(makeflags, '=') == NULL) { | ||
2946 | if (strspn(makeflags, OPTSTR1) != strlen(makeflags)) | ||
2947 | error("invalid MAKEFLAGS"); | ||
2948 | *p++ = '-'; | ||
2949 | } else { | ||
2950 | // MAKEFLAGS may need to be split, estimate size of argv array. | ||
2951 | for (m = makeflags; *m; ++m) { | ||
2952 | if (isblank(*m)) | ||
2953 | argc++; | ||
2954 | } | ||
2955 | } | ||
2956 | |||
2957 | argv = xzalloc(argc * sizeof(char *)); | ||
2958 | argc = 0; | ||
2959 | argv[argc++] = (char *)applet_name; | ||
2960 | argv[argc++] = argstr; | ||
2961 | |||
2962 | // Copy MAKEFLAGS into argstr, splitting at non-escaped blanks. | ||
2963 | m = makeflags; | ||
2964 | do { | ||
2965 | if (*m == '\\' && m[1] != '\0') | ||
2966 | m++; // Skip backslash, copy next character unconditionally. | ||
2967 | else if (isblank(*m)) { | ||
2968 | // Terminate current argument and start a new one. | ||
2969 | /* *p = '\0'; - xzalloc did it */ | ||
2970 | argv[argc++] = ++p; | ||
2971 | do { | ||
2972 | m++; | ||
2973 | } while (isblank(*m)); | ||
2974 | continue; | ||
2975 | } | ||
2976 | *p++ = *m++; | ||
2977 | } while (*m != '\0'); | ||
2978 | /* *p = '\0'; - xzalloc did it */ | ||
2979 | /* argv[argc] = NULL; - and this */ | ||
2980 | |||
2981 | return argv; | ||
2982 | } | ||
2983 | |||
2984 | // These macros require special treatment | ||
2985 | #define SPECIAL_MACROS "MAKEFLAGS\0SHELL\0CURDIR\0" | ||
2986 | #define MAKEFLAGS 0 | ||
2987 | #define SHELL 1 | ||
2988 | #define CURDIR 2 | ||
2989 | |||
2990 | /* | ||
2991 | * Instantiate all macros in an argv-style array of pointers. Stop | ||
2992 | * processing at the first string that doesn't contain an equal sign. | ||
2993 | * As an extension, target arguments on the command line (level 1) | ||
2994 | * are skipped and will be processed later. | ||
2995 | */ | ||
2996 | static char ** | ||
2997 | process_macros(char **argv, int level) | ||
2998 | { | ||
2999 | char *equal; | ||
3000 | |||
3001 | for (; *argv; argv++) { | ||
3002 | char *colon = NULL; | ||
3003 | int idx, immediate = 0; | ||
3004 | int except_dollar = FALSE; | ||
3005 | |||
3006 | if (!(equal = strchr(*argv, '='))) { | ||
3007 | // Skip targets on the command line | ||
3008 | if (!posix && level == 1) | ||
3009 | continue; | ||
3010 | else | ||
3011 | // Stop at first target | ||
3012 | break; | ||
3013 | } | ||
3014 | |||
3015 | if (equal - 1 > *argv && equal[-1] == ':') { | ||
3016 | if (equal - 2 > *argv && equal[-2] == ':') { | ||
3017 | if (POSIX_2017) | ||
3018 | error("invalid macro assignment"); | ||
3019 | if (equal - 3 > *argv && equal[-3] == ':') { | ||
3020 | // BSD-style ':='. Expand RHS, but not '$$', | ||
3021 | // resulting macro is delayed expansion. | ||
3022 | colon = equal - 3; | ||
3023 | except_dollar = TRUE; | ||
3024 | } else { | ||
3025 | // GNU-style ':='. Expand RHS, including '$$', | ||
3026 | // resulting macro is immediate expansion. | ||
3027 | colon = equal - 2; | ||
3028 | immediate = M_IMMEDIATE; | ||
3029 | } | ||
3030 | *colon = '\0'; | ||
3031 | } else { | ||
3032 | if (posix) | ||
3033 | error("invalid macro assignment"); | ||
3034 | colon = equal - 1; | ||
3035 | immediate = M_IMMEDIATE; | ||
3036 | *colon = '\0'; | ||
3037 | } | ||
3038 | } else | ||
3039 | *equal = '\0'; | ||
3040 | |||
3041 | /* We want to process _most_ macro assignments. | ||
3042 | * There are exceptions for particular values from the | ||
3043 | * environment. */ | ||
3044 | idx = index_in_strings(SPECIAL_MACROS, *argv); | ||
3045 | if (!((level & M_ENVIRON) && | ||
3046 | (idx == MAKEFLAGS || idx == SHELL || | ||
3047 | (idx == CURDIR && !useenv && !POSIX_2017)))) { | ||
3048 | if (colon) { | ||
3049 | char *exp = expand_macros(equal + 1, except_dollar); | ||
3050 | setmacro(*argv, exp, level | immediate); | ||
3051 | free(exp); | ||
3052 | } else | ||
3053 | setmacro(*argv, equal + 1, level); | ||
3054 | } | ||
3055 | |||
3056 | if (colon) | ||
3057 | *colon = ':'; | ||
3058 | else | ||
3059 | *equal = '='; | ||
3060 | } | ||
3061 | return argv; | ||
3062 | } | ||
3063 | |||
3064 | /* | ||
3065 | * Update the MAKEFLAGS macro and environment variable to include any | ||
3066 | * command line options that don't have their default value (apart from | ||
3067 | * -f, -p and -S). Also add any macros defined on the command line or | ||
3068 | * by the MAKEFLAGS environment variable (apart from MAKEFLAGS itself). | ||
3069 | * Add macros that were defined on the command line to the environment. | ||
3070 | */ | ||
3071 | static void | ||
3072 | update_makeflags(void) | ||
3073 | { | ||
3074 | int i; | ||
3075 | char optbuf[] = "-?"; | ||
3076 | char *makeflags = NULL; | ||
3077 | char *macro, *s; | ||
3078 | const char *t; | ||
3079 | struct macro *mp; | ||
3080 | |||
3081 | t = OPTSTR1; | ||
3082 | for (i = 0; *t; t++) { | ||
3083 | if (*t == ':' || *t == '+') | ||
3084 | continue; | ||
3085 | if ((opts & OPT_MASK & (1 << i))) { | ||
3086 | optbuf[1] = *t; | ||
3087 | makeflags = xappendword(makeflags, optbuf); | ||
3088 | if (*t == 'j') { | ||
3089 | s = auto_string(xasprintf("%d", numjobs)); | ||
3090 | makeflags = xappendword(makeflags, s); | ||
3091 | } | ||
3092 | } | ||
3093 | i++; | ||
3094 | } | ||
3095 | |||
3096 | for (i = 0; i < HTABSIZE; ++i) { | ||
3097 | for (mp = macrohead[i]; mp; mp = mp->m_next) { | ||
3098 | if (mp->m_level == 1 || mp->m_level == 2) { | ||
3099 | int idx = index_in_strings(SPECIAL_MACROS, mp->m_name); | ||
3100 | if (idx == MAKEFLAGS) | ||
3101 | continue; | ||
3102 | macro = xzalloc(strlen(mp->m_name) + 2 * strlen(mp->m_val) + 1); | ||
3103 | s = stpcpy(macro, mp->m_name); | ||
3104 | *s++ = '='; | ||
3105 | for (t = mp->m_val; *t; t++) { | ||
3106 | if (*t == '\\' || isblank(*t)) | ||
3107 | *s++ = '\\'; | ||
3108 | *s++ = *t; | ||
3109 | } | ||
3110 | /* *s = '\0'; - xzalloc did it */ | ||
3111 | |||
3112 | makeflags = xappendword(makeflags, macro); | ||
3113 | free(macro); | ||
3114 | |||
3115 | // Add command line macro definitions to the environment | ||
3116 | if (mp->m_level == 1 && idx != SHELL) | ||
3117 | setenv(mp->m_name, mp->m_val, 1); | ||
3118 | } | ||
3119 | } | ||
3120 | } | ||
3121 | |||
3122 | if (makeflags) { | ||
3123 | setmacro("MAKEFLAGS", makeflags, 0); | ||
3124 | setenv("MAKEFLAGS", makeflags, 1); | ||
3125 | free(makeflags); | ||
3126 | } | ||
3127 | } | ||
3128 | |||
3129 | #if !ENABLE_PLATFORM_MINGW32 | ||
3130 | static void | ||
3131 | make_handler(int sig) | ||
3132 | { | ||
3133 | signal(sig, SIG_DFL); | ||
3134 | remove_target(); | ||
3135 | kill(getpid(), sig); | ||
3136 | } | ||
3137 | |||
3138 | static void | ||
3139 | init_signal(int sig) | ||
3140 | { | ||
3141 | struct sigaction sa, new_action; | ||
3142 | |||
3143 | sigemptyset(&new_action.sa_mask); | ||
3144 | new_action.sa_flags = 0; | ||
3145 | new_action.sa_handler = make_handler; | ||
3146 | |||
3147 | sigaction(sig, NULL, &sa); | ||
3148 | if (sa.sa_handler != SIG_IGN) | ||
3149 | sigaction_set(sig, &new_action); | ||
3150 | } | ||
3151 | #endif | ||
3152 | |||
3153 | /* | ||
3154 | * If the global option flag associated with a special target hasn't | ||
3155 | * been set mark all prerequisites of the target with a flag. If the | ||
3156 | * target had no prerequisites set the global option flag. | ||
3157 | */ | ||
3158 | static void | ||
3159 | mark_special(const char *special, uint32_t oflag, uint16_t nflag) | ||
3160 | { | ||
3161 | struct name *np; | ||
3162 | struct rule *rp; | ||
3163 | struct depend *dp; | ||
3164 | int marked = FALSE; | ||
3165 | |||
3166 | if (!(opts & oflag) && (np = findname(special))) { | ||
3167 | for (rp = np->n_rule; rp; rp = rp->r_next) { | ||
3168 | for (dp = rp->r_dep; dp; dp = dp->d_next) { | ||
3169 | dp->d_name->n_flag |= nflag; | ||
3170 | marked = TRUE; | ||
3171 | } | ||
3172 | } | ||
3173 | |||
3174 | if (!marked) | ||
3175 | opts |= oflag; | ||
3176 | } | ||
3177 | } | ||
3178 | |||
3179 | int make_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | ||
3180 | int make_main(int argc UNUSED_PARAM, char **argv) | ||
3181 | { | ||
3182 | const char *path, *newpath = NULL; | ||
3183 | char **fargv, **fargv0; | ||
3184 | const char *dir, *file; | ||
3185 | #if ENABLE_FEATURE_MAKE_POSIX | ||
3186 | const char *prag; | ||
3187 | #endif | ||
3188 | int estat; | ||
3189 | bool found_target; | ||
3190 | FILE *ifd; | ||
3191 | |||
3192 | INIT_G(); | ||
3193 | |||
3194 | #if ENABLE_FEATURE_MAKE_POSIX | ||
3195 | if (argv[1] && strcmp(argv[1], "--posix") == 0) { | ||
3196 | argv[1] = argv[0]; | ||
3197 | ++argv; | ||
3198 | --argc; | ||
3199 | setenv("PDPMAKE_POSIXLY_CORRECT", "", 1); | ||
3200 | posix = TRUE; | ||
3201 | } else { | ||
3202 | posix = getenv("PDPMAKE_POSIXLY_CORRECT") != NULL; | ||
3203 | } | ||
3204 | posix_level = DEFAULT_POSIX_LEVEL; | ||
3205 | pragmas_from_env(); | ||
3206 | #endif | ||
3207 | |||
3208 | if (!POSIX_2017) { | ||
3209 | path = argv[0]; | ||
3210 | #if ENABLE_PLATFORM_MINGW32 | ||
3211 | if (has_path(argv[0])) { | ||
3212 | // Add extension if necessary, else realpath() will fail | ||
3213 | char *p = alloc_ext_space(argv[0]); | ||
3214 | add_win32_extension(p); | ||
3215 | path = newpath = xmalloc_realpath(p); | ||
3216 | free(p); | ||
3217 | if (!path) { | ||
3218 | if (unix_path(argv[0])) | ||
3219 | path = argv[0]; | ||
3220 | else | ||
3221 | bb_perror_msg("can't resolve path for %s", argv[0]); | ||
3222 | } | ||
3223 | } | ||
3224 | #else | ||
3225 | if (argv[0][0] != '/' && strchr(argv[0], '/')) { | ||
3226 | // Make relative path absolute | ||
3227 | path = newpath = realpath(argv[0], NULL); | ||
3228 | if (!path) { | ||
3229 | bb_perror_msg("can't resolve path for %s", argv[0]); | ||
3230 | } | ||
3231 | } | ||
3232 | #endif | ||
3233 | } else { | ||
3234 | path = "make"; | ||
3235 | } | ||
3236 | |||
3237 | // Process options from MAKEFLAGS | ||
3238 | fargv = fargv0 = expand_makeflags(); | ||
3239 | if (fargv0) { | ||
3240 | opts = process_options(fargv, TRUE); | ||
3241 | fargv = fargv0 + optind; | ||
3242 | // Reset getopt(3) so we can call it again | ||
3243 | GETOPT_RESET(); | ||
3244 | } | ||
3245 | |||
3246 | // Process options from the command line | ||
3247 | opts |= process_options(argv, FALSE); | ||
3248 | argv += optind; | ||
3249 | |||
3250 | while ((dir = llist_pop(&dirs))) { | ||
3251 | if (chdir(dir) == -1) { | ||
3252 | bb_perror_msg("can't chdir to %s", dir); | ||
3253 | } | ||
3254 | } | ||
3255 | |||
3256 | #if ENABLE_FEATURE_MAKE_POSIX | ||
3257 | while ((prag = llist_pop(&pragmas))) | ||
3258 | set_pragma(prag); | ||
3259 | pragmas_to_env(); | ||
3260 | #endif | ||
3261 | |||
3262 | #if !ENABLE_PLATFORM_MINGW32 | ||
3263 | init_signal(SIGHUP); | ||
3264 | init_signal(SIGTERM); | ||
3265 | #endif | ||
3266 | |||
3267 | setmacro("$", "$", 0 | M_VALID); | ||
3268 | |||
3269 | // Process macro definitions from the command line | ||
3270 | if (!posix) | ||
3271 | process_macros(argv, 1); | ||
3272 | else | ||
3273 | // In POSIX mode macros must appear before targets. | ||
3274 | // argv should now point to targets only. | ||
3275 | argv = process_macros(argv, 1); | ||
3276 | |||
3277 | // Process macro definitions from MAKEFLAGS | ||
3278 | if (fargv) { | ||
3279 | process_macros(fargv, 2); | ||
3280 | free(fargv0[1]); | ||
3281 | free(fargv0); | ||
3282 | } | ||
3283 | |||
3284 | // Process macro definitions from the environment | ||
3285 | process_macros(environ, 3 | M_ENVIRON); | ||
3286 | |||
3287 | // Update MAKEFLAGS and environment | ||
3288 | update_makeflags(); | ||
3289 | |||
3290 | // Read built-in rules | ||
3291 | input(NULL, 0); | ||
3292 | |||
3293 | setmacro("SHELL", DEFAULT_SHELL, 4); | ||
3294 | setmacro("MAKE", path, 4); | ||
3295 | if (!POSIX_2017) { | ||
3296 | char *cwd = xrealloc_getcwd_or_warn(NULL); | ||
3297 | |||
3298 | if (cwd) { | ||
3299 | if (!useenv) { | ||
3300 | // Export cwd to environment, if necessary | ||
3301 | char *envcwd = getenv("CURDIR"); | ||
3302 | if (envcwd && strcmp(cwd, envcwd) != 0) | ||
3303 | setenv("CURDIR", cwd, 1); | ||
3304 | } | ||
3305 | setmacro("CURDIR", cwd, 4); | ||
3306 | } | ||
3307 | free(cwd); | ||
3308 | } | ||
3309 | free((void *)newpath); | ||
3310 | |||
3311 | if (!makefiles) { // Look for a default Makefile | ||
3312 | if (!posix && (ifd = fopen("PDPmakefile", "r")) != NULL) | ||
3313 | makefile = "PDPmakefile"; | ||
3314 | else if ((ifd = fopen("PDPmakefile" + 3, "r")) != NULL) | ||
3315 | makefile = "PDPmakefile" + 3; | ||
3316 | #if !ENABLE_PLATFORM_MINGW32 | ||
3317 | else if ((ifd = fopen("Makefile", "r")) != NULL) | ||
3318 | makefile = "Makefile"; | ||
3319 | #endif | ||
3320 | else | ||
3321 | error("no makefile found"); | ||
3322 | goto read_makefile; | ||
3323 | } | ||
3324 | |||
3325 | while ((file = llist_pop(&makefiles))) { | ||
3326 | if (strcmp(file, "-") == 0) { // Can use stdin as makefile | ||
3327 | ifd = stdin; | ||
3328 | makefile = "stdin"; | ||
3329 | } else { | ||
3330 | ifd = xfopen_for_read(file); | ||
3331 | makefile = file; | ||
3332 | } | ||
3333 | read_makefile: | ||
3334 | input(ifd, 0); | ||
3335 | fclose(ifd); | ||
3336 | makefile = NULL; | ||
3337 | } | ||
3338 | |||
3339 | if (print) | ||
3340 | print_details(); | ||
3341 | |||
3342 | mark_special(".SILENT", OPT_s, N_SILENT); | ||
3343 | mark_special(".IGNORE", OPT_i, N_IGNORE); | ||
3344 | mark_special(".PRECIOUS", OPT_precious, N_PRECIOUS); | ||
3345 | if (!POSIX_2017) | ||
3346 | mark_special(".PHONY", OPT_phony, N_PHONY); | ||
3347 | |||
3348 | if (posix) { | ||
3349 | // In POSIX mode only targets should now be in argv. | ||
3350 | found_target = FALSE; | ||
3351 | for (char **a = argv; *a; a++) { | ||
3352 | if (!strchr(*a, '=')) | ||
3353 | found_target = TRUE; | ||
3354 | else if (found_target) | ||
3355 | error("macro assignments must precede targets"); | ||
3356 | } | ||
3357 | } | ||
3358 | |||
3359 | estat = 0; | ||
3360 | found_target = FALSE; | ||
3361 | for (; *argv; argv++) { | ||
3362 | // Skip macro assignments. | ||
3363 | if (strchr(*argv, '=')) | ||
3364 | continue; | ||
3365 | found_target = TRUE; | ||
3366 | estat |= make(newname(*argv), 0); | ||
3367 | } | ||
3368 | if (!found_target) { | ||
3369 | if (!firstname) | ||
3370 | error("no targets defined"); | ||
3371 | estat = make(firstname, 0); | ||
3372 | } | ||
3373 | |||
3374 | #if ENABLE_FEATURE_CLEAN_UP | ||
3375 | freenames(); | ||
3376 | freemacros(); | ||
3377 | llist_free(makefiles, NULL); | ||
3378 | llist_free(dirs, NULL); | ||
3379 | #endif | ||
3380 | |||
3381 | return estat & MAKE_FAILURE; | ||
3382 | } | ||
diff --git a/miscutils/man.c b/miscutils/man.c index deaf9e5ab..3954455b4 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); |