diff options
130 files changed, 22848 insertions, 186 deletions
diff --git a/.gitignore b/.gitignore index be1d46199..2a7f2f79b 100644 --- a/.gitignore +++ b/.gitignore | |||
@@ -18,6 +18,7 @@ Config.in | |||
18 | # Normal output | 18 | # Normal output |
19 | # | 19 | # |
20 | /busybox | 20 | /busybox |
21 | /busybox.exe | ||
21 | /busybox_old | 22 | /busybox_old |
22 | /busybox_unstripped* | 23 | /busybox_unstripped* |
23 | 24 | ||
@@ -9,6 +9,20 @@ config HAVE_DOT_CONFIG | |||
9 | bool | 9 | bool |
10 | default y | 10 | default y |
11 | 11 | ||
12 | choice | ||
13 | prompt "Target platform" | ||
14 | default PLATFORM_POSIX | ||
15 | help | ||
16 | Target platform you are building busybox for | ||
17 | |||
18 | config PLATFORM_POSIX | ||
19 | bool "POSIX" | ||
20 | |||
21 | config PLATFORM_MINGW32 | ||
22 | bool "MS Windows (MinGW port)" | ||
23 | |||
24 | endchoice | ||
25 | |||
12 | menu "Settings" | 26 | menu "Settings" |
13 | 27 | ||
14 | config DESKTOP | 28 | config DESKTOP |
@@ -95,6 +109,28 @@ config LFS | |||
95 | programs that can benefit from large file support include dd, gzip, | 109 | programs that can benefit from large file support include dd, gzip, |
96 | cp, mount, tar. | 110 | cp, mount, tar. |
97 | 111 | ||
112 | config GLOBBING | ||
113 | bool "Allow busybox.exe to expand wildcards" | ||
114 | default n | ||
115 | depends on PLATFORM_MINGW32 | ||
116 | help | ||
117 | In Microsoft Windows expansion of wildcards on the command line | ||
118 | ('globbing') is handled by the C runtime while the BusyBox shell | ||
119 | does its own wildcard expansion. For best results when using the | ||
120 | shell globbing by the C runtime should be turned off. If you want | ||
121 | the BusyBox binary to handle wildcard expansion using the C runtime | ||
122 | set this to 'Y'. | ||
123 | |||
124 | config SAFE_ENV | ||
125 | bool "Manipulate the environment through safe API calls" | ||
126 | default n | ||
127 | depends on PLATFORM_MINGW32 | ||
128 | help | ||
129 | Enable this option to use safe API calls when clearing environment | ||
130 | variables. This is necessary if BusyBox is to run on ReactOS or | ||
131 | 64-bit Windows. The default is 'N', which must be used if BusyBox | ||
132 | is to run on Windows XP. | ||
133 | |||
98 | config PAM | 134 | config PAM |
99 | bool "Support PAM (Pluggable Authentication Modules)" | 135 | bool "Support PAM (Pluggable Authentication Modules)" |
100 | default n | 136 | default n |
@@ -312,6 +312,7 @@ AFLAGS_MODULE = $(MODFLAGS) | |||
312 | LDFLAGS_MODULE = -r | 312 | LDFLAGS_MODULE = -r |
313 | CFLAGS_KERNEL = | 313 | CFLAGS_KERNEL = |
314 | AFLAGS_KERNEL = | 314 | AFLAGS_KERNEL = |
315 | EXEEXT = | ||
315 | 316 | ||
316 | 317 | ||
317 | # Use LINUXINCLUDE when you must reference the include/ directory. | 318 | # Use LINUXINCLUDE when you must reference the include/ directory. |
@@ -491,6 +492,7 @@ libs-y := \ | |||
491 | sysklogd/ \ | 492 | sysklogd/ \ |
492 | util-linux/ \ | 493 | util-linux/ \ |
493 | util-linux/volume_id/ \ | 494 | util-linux/volume_id/ \ |
495 | win32/ \ | ||
494 | 496 | ||
495 | endif # KBUILD_EXTMOD | 497 | endif # KBUILD_EXTMOD |
496 | 498 | ||
@@ -531,7 +533,7 @@ endif | |||
531 | # command line. | 533 | # command line. |
532 | # This allow a user to issue only 'make' to build a kernel including modules | 534 | # This allow a user to issue only 'make' to build a kernel including modules |
533 | # Defaults busybox but it is usually overridden in the arch makefile | 535 | # Defaults busybox but it is usually overridden in the arch makefile |
534 | all: busybox doc | 536 | all: busybox$(EXEEXT) doc |
535 | 537 | ||
536 | # arch Makefile may override CC so keep this after arch Makefile is included | 538 | # arch Makefile may override CC so keep this after arch Makefile is included |
537 | #bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) | 539 | #bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) |
@@ -713,16 +715,16 @@ debug_kallsyms: .tmp_map$(last_kallsyms) | |||
713 | endif # ifdef CONFIG_KALLSYMS | 715 | endif # ifdef CONFIG_KALLSYMS |
714 | 716 | ||
715 | # busybox image - including updated kernel symbols | 717 | # busybox image - including updated kernel symbols |
716 | busybox_unstripped: $(busybox-all) FORCE | 718 | busybox_unstripped$(EXEEXT): $(busybox-all) FORCE |
717 | $(call if_changed_rule,busybox__) | 719 | $(call if_changed_rule,busybox__) |
718 | $(Q)rm -f .old_version | 720 | $(Q)rm -f .old_version |
719 | 721 | ||
720 | busybox: busybox_unstripped | 722 | busybox$(EXEEXT): busybox_unstripped$(EXEEXT) |
721 | ifeq ($(SKIP_STRIP),y) | 723 | ifeq ($(SKIP_STRIP),y) |
722 | $(Q)cp $< $@ | 724 | $(Q)cp $< $@ |
723 | else | 725 | else |
724 | $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \ | 726 | $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \ |
725 | busybox_unstripped -o $@ | 727 | busybox_unstripped$(EXEEXT) -o $@ |
726 | # strip is confused by PIE executable and does not set exec bits | 728 | # strip is confused by PIE executable and does not set exec bits |
727 | $(Q)chmod a+x $@ | 729 | $(Q)chmod a+x $@ |
728 | endif | 730 | endif |
@@ -961,7 +963,7 @@ endif # CONFIG_MODULES | |||
961 | 963 | ||
962 | # Directories & files removed with 'make clean' | 964 | # Directories & files removed with 'make clean' |
963 | CLEAN_DIRS += $(MODVERDIR) _install 0_lib | 965 | CLEAN_DIRS += $(MODVERDIR) _install 0_lib |
964 | CLEAN_FILES += busybox busybox_unstripped* busybox.links \ | 966 | CLEAN_FILES += busybox$(EXEEXT) busybox_unstripped* busybox.links \ |
965 | System.map .kernelrelease \ | 967 | System.map .kernelrelease \ |
966 | .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map | 968 | .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map |
967 | 969 | ||
diff --git a/Makefile.flags b/Makefile.flags index f3c897b06..6bb6a8fb1 100644 --- a/Makefile.flags +++ b/Makefile.flags | |||
@@ -15,7 +15,7 @@ CPPFLAGS += \ | |||
15 | -include include/autoconf.h \ | 15 | -include include/autoconf.h \ |
16 | -D_GNU_SOURCE -DNDEBUG \ | 16 | -D_GNU_SOURCE -DNDEBUG \ |
17 | $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \ | 17 | $(if $(CONFIG_LFS),-D_LARGEFILE_SOURCE -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64) \ |
18 | -D"BB_VER=KBUILD_STR($(BB_VER))" | 18 | -D"BB_VER=KBUILD_STR($(BB_VER))" -D"MINGW_VER=KBUILD_STR($(MINGW_VER))" |
19 | 19 | ||
20 | CFLAGS += $(call cc-option,-Wall,) | 20 | CFLAGS += $(call cc-option,-Wall,) |
21 | CFLAGS += $(call cc-option,-Wshadow,) | 21 | CFLAGS += $(call cc-option,-Wshadow,) |
@@ -123,6 +123,12 @@ CFLAGS += --sysroot=$(CONFIG_SYSROOT) | |||
123 | export SYSROOT=$(CONFIG_SYSROOT) | 123 | export SYSROOT=$(CONFIG_SYSROOT) |
124 | endif | 124 | endif |
125 | 125 | ||
126 | ifeq ($(CONFIG_PLATFORM_MINGW32),y) | ||
127 | CFLAGS += -Iwin32 -DHAVE_STRING_H=1 -DHAVE_CONFIG_H=0 -fno-builtin-stpcpy -fno-ident | ||
128 | EXEEXT = .exe | ||
129 | LDLIBS += userenv ws2_32 | ||
130 | endif | ||
131 | |||
126 | # Android has no separate crypt library | 132 | # Android has no separate crypt library |
127 | # gcc-4.2.1 fails if we try to feed C source on stdin: | 133 | # gcc-4.2.1 fails if we try to feed C source on stdin: |
128 | # echo 'int main(void){return 0;}' | $(CC) $(CFLAGS) -lcrypt -o /dev/null -xc - | 134 | # echo 'int main(void){return 0;}' | $(CC) $(CFLAGS) -lcrypt -o /dev/null -xc - |
@@ -1,3 +1,4 @@ | |||
1 | Please see README.md for Windows-specific info. | ||
1 | Please see the LICENSE file for details on copying and usage. | 2 | Please see the LICENSE file for details on copying and usage. |
2 | Please refer to the INSTALL file for instructions on how to build. | 3 | Please refer to the INSTALL file for instructions on how to build. |
3 | 4 | ||
diff --git a/README.md b/README.md new file mode 100644 index 000000000..06720791f --- /dev/null +++ b/README.md | |||
@@ -0,0 +1,23 @@ | |||
1 | ### Status | ||
2 | |||
3 | Things may work for you, or may not. Things may never work because of huge differences between Linux and Windows. Or things may work in future, if you report the problem to https://github.com/rmyorston/busybox-w32. If you don't have a GitHub account you can email me: rmy@pobox.com. | ||
4 | |||
5 | ### Building | ||
6 | |||
7 | You need a MinGW compiler and a POSIX environment (so that `make menuconfig` works). I cross-compile on Linux. On Fedora or RHEL/CentOS+EPEL installing mingw32-gcc (32-bit build) or mingw64-gcc (64-bit build) will pull in everything needed. | ||
8 | |||
9 | To start, run `make mingw32_defconfig` or `make mingw64_defconfig`. You can then customize your build with `make menuconfig`. | ||
10 | |||
11 | In particular you may need to adjust the compiler by going to Busybox Settings -> Build Options -> Cross Compiler Prefix | ||
12 | |||
13 | Then just `make`. | ||
14 | |||
15 | ### Limitations | ||
16 | |||
17 | - Use forward slashes in paths: Windows doesn't mind and the shell will be happier. | ||
18 | - Don't do wild things with Windows drive or UNC notation. | ||
19 | - Wildcard expansion is disabled by default, though it can be turned on at compile time. This only affects command line arguments to the binary: the BusyBox shell has full support for wildcards. | ||
20 | - Handling of users, groups and permissions is totally bogus. The system only admits to knowing about the current user and always returns the same hardcoded uid, gid and permission values. | ||
21 | - Some crufty old Windows code (Windows XP, cmd.exe) doesn't like forward slashes in environment variables. The -X shell option (which must be the first argument) prevents busybox-w32 from changing backslashes to forward slashes. If Windows programs don't run from the shell it's worth trying it. | ||
22 | - If you want to install 32-bit BusyBox in a system directory on a 64-bit version of Windows you should put it in `C:\Windows\SysWOW64`, not `C:\Windows\System32` as you might expect. On 64-bit systems the latter is for 64-bit binaries. | ||
23 | - ANSI escape sequences are emulated by converting to the equivalent in the Windows console API. Setting the environment variable `BB_SKIP_ANSI_EMULATION` will cause ANSI escapes to be passed to the console without emulation. This may be useful for Windows consoles that support ANSI escapes (e.g. ConEmu). | ||
diff --git a/applets/applet_tables.c b/applets/applet_tables.c index ef911a43b..9a2aa5329 100644 --- a/applets/applet_tables.c +++ b/applets/applet_tables.c | |||
@@ -179,7 +179,7 @@ int main(int argc, char **argv) | |||
179 | printf("};\n\n"); | 179 | printf("};\n\n"); |
180 | #endif | 180 | #endif |
181 | 181 | ||
182 | #if ENABLE_FEATURE_INSTALLER | 182 | #if ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32 |
183 | printf("const uint8_t applet_install_loc[] ALIGN1 = {\n"); | 183 | printf("const uint8_t applet_install_loc[] ALIGN1 = {\n"); |
184 | i = 0; | 184 | i = 0; |
185 | while (i < NUM_APPLETS) { | 185 | while (i < NUM_APPLETS) { |
diff --git a/archival/ar.c b/archival/ar.c index 2886d155b..ea36bda88 100644 --- a/archival/ar.c +++ b/archival/ar.c | |||
@@ -163,6 +163,7 @@ static int write_ar_archive(archive_handle_t *handle) | |||
163 | { | 163 | { |
164 | struct stat st; | 164 | struct stat st; |
165 | archive_handle_t *out_handle; | 165 | archive_handle_t *out_handle; |
166 | char *temp_fn = NULL; | ||
166 | 167 | ||
167 | xfstat(handle->src_fd, &st, handle->ar__name); | 168 | xfstat(handle->src_fd, &st, handle->ar__name); |
168 | 169 | ||
@@ -171,8 +172,14 @@ static int write_ar_archive(archive_handle_t *handle) | |||
171 | */ | 172 | */ |
172 | if (st.st_size != 0) { | 173 | if (st.st_size != 0) { |
173 | out_handle = init_handle(); | 174 | out_handle = init_handle(); |
175 | #if !ENABLE_PLATFORM_MINGW32 | ||
174 | xunlink(handle->ar__name); | 176 | xunlink(handle->ar__name); |
175 | out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC); | 177 | out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC); |
178 | #else | ||
179 | /* can't unlink open file, create temporary output file */ | ||
180 | temp_fn = xasprintf("%sXXXXXX", handle->ar__name); | ||
181 | out_handle->src_fd = xmkstemp(temp_fn); | ||
182 | #endif | ||
176 | out_handle->accept = handle->accept; | 183 | out_handle->accept = handle->accept; |
177 | } else { | 184 | } else { |
178 | out_handle = handle; | 185 | out_handle = handle; |
@@ -194,12 +201,19 @@ static int write_ar_archive(archive_handle_t *handle) | |||
194 | continue; | 201 | continue; |
195 | 202 | ||
196 | /* optional, since we exit right after we return */ | 203 | /* optional, since we exit right after we return */ |
197 | if (ENABLE_FEATURE_CLEAN_UP) { | 204 | if (ENABLE_FEATURE_CLEAN_UP || ENABLE_PLATFORM_MINGW32) { |
198 | close(handle->src_fd); | 205 | close(handle->src_fd); |
199 | if (out_handle->src_fd != handle->src_fd) | 206 | if (out_handle->src_fd != handle->src_fd) |
200 | close(out_handle->src_fd); | 207 | close(out_handle->src_fd); |
201 | } | 208 | } |
202 | 209 | ||
210 | #if ENABLE_PLATFORM_MINGW32 | ||
211 | if ( temp_fn != NULL ) { | ||
212 | xrename(temp_fn, handle->ar__name); | ||
213 | free(temp_fn); | ||
214 | } | ||
215 | #endif | ||
216 | |||
203 | return EXIT_SUCCESS; | 217 | return EXIT_SUCCESS; |
204 | } | 218 | } |
205 | #endif /* FEATURE_AR_CREATE */ | 219 | #endif /* FEATURE_AR_CREATE */ |
diff --git a/archival/bbunzip.c b/archival/bbunzip.c index d2b162d5e..f91dd25eb 100644 --- a/archival/bbunzip.c +++ b/archival/bbunzip.c | |||
@@ -191,6 +191,8 @@ int FAST_FUNC bbunpack(char **argv, | |||
191 | if (option_mask32 & OPT_KEEP) /* ... unless -k */ | 191 | if (option_mask32 & OPT_KEEP) /* ... unless -k */ |
192 | del = NULL; | 192 | del = NULL; |
193 | } | 193 | } |
194 | if (ENABLE_PLATFORM_MINGW32) | ||
195 | xclose(STDIN_FILENO); | ||
194 | if (del) | 196 | if (del) |
195 | xunlink(del); | 197 | xunlink(del); |
196 | free_name: | 198 | free_name: |
diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c index c7fa5b526..74d364379 100644 --- a/archival/libarchive/decompress_gunzip.c +++ b/archival/libarchive/decompress_gunzip.c | |||
@@ -1118,6 +1118,9 @@ static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY) | |||
1118 | return res; | 1118 | return res; |
1119 | } | 1119 | } |
1120 | 1120 | ||
1121 | #if ENABLE_PLATFORM_MINGW32 && __GNUC__ | ||
1122 | #pragma pack(2) | ||
1123 | #endif | ||
1121 | static int check_header_gzip(STATE_PARAM transformer_state_t *xstate) | 1124 | static int check_header_gzip(STATE_PARAM transformer_state_t *xstate) |
1122 | { | 1125 | { |
1123 | union { | 1126 | union { |
@@ -1189,6 +1192,9 @@ static int check_header_gzip(STATE_PARAM transformer_state_t *xstate) | |||
1189 | } | 1192 | } |
1190 | return 1; | 1193 | return 1; |
1191 | } | 1194 | } |
1195 | #if ENABLE_PLATFORM_MINGW32 && __GNUC__ | ||
1196 | #pragma pack() | ||
1197 | #endif | ||
1192 | 1198 | ||
1193 | IF_DESKTOP(long long) int FAST_FUNC | 1199 | IF_DESKTOP(long long) int FAST_FUNC |
1194 | unpack_gz_stream(transformer_state_t *xstate) | 1200 | unpack_gz_stream(transformer_state_t *xstate) |
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c index ac7e5db95..641256787 100644 --- a/archival/libarchive/open_transformer.c +++ b/archival/libarchive/open_transformer.c | |||
@@ -65,6 +65,7 @@ ssize_t FAST_FUNC xtransformer_write(transformer_state_t *xstate, const void *bu | |||
65 | return nwrote; | 65 | return nwrote; |
66 | } | 66 | } |
67 | 67 | ||
68 | #if !ENABLE_PLATFORM_MINGW32 | ||
68 | void check_errors_in_children(int signo) | 69 | void check_errors_in_children(int signo) |
69 | { | 70 | { |
70 | int status; | 71 | int status; |
@@ -151,6 +152,31 @@ void FAST_FUNC fork_transformer(int fd, const char *transform_prog) | |||
151 | close(fd_pipe.wr); /* don't want to write to the child */ | 152 | close(fd_pipe.wr); /* don't want to write to the child */ |
152 | xmove_fd(fd_pipe.rd, fd); | 153 | xmove_fd(fd_pipe.rd, fd); |
153 | } | 154 | } |
155 | #else /* ENABLE_PLATFORM_MINGW */ | ||
156 | void check_errors_in_children(int signo UNUSED_PARAM) | ||
157 | { | ||
158 | bb_got_signal = 0; | ||
159 | } | ||
160 | |||
161 | void FAST_FUNC fork_transformer(int fd, const char *transform_prog) | ||
162 | { | ||
163 | char *cmd; | ||
164 | int fd1; | ||
165 | |||
166 | if (find_applet_by_name(transform_prog) >= 0) { | ||
167 | cmd = xasprintf("%s --busybox %s -cf -", bb_busybox_exec_path, | ||
168 | transform_prog); | ||
169 | } | ||
170 | else { | ||
171 | cmd = xasprintf("%s -cf -", transform_prog); | ||
172 | } | ||
173 | if ( (fd1=mingw_popen_fd(cmd, "r", fd, NULL)) == -1 ) { | ||
174 | bb_perror_msg_and_die("can't execute '%s'", transform_prog); | ||
175 | } | ||
176 | free(cmd); | ||
177 | xmove_fd(fd1, fd); | ||
178 | } | ||
179 | #endif | ||
154 | 180 | ||
155 | 181 | ||
156 | #if SEAMLESS_COMPRESSION | 182 | #if SEAMLESS_COMPRESSION |
diff --git a/archival/tar.c b/archival/tar.c index 280ded4e1..d90a5dc4f 100644 --- a/archival/tar.c +++ b/archival/tar.c | |||
@@ -542,6 +542,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb | |||
542 | } | 542 | } |
543 | } | 543 | } |
544 | 544 | ||
545 | #if !ENABLE_PLATFORM_MINGW32 | ||
545 | /* It is a bad idea to store the archive we are in the process of creating, | 546 | /* It is a bad idea to store the archive we are in the process of creating, |
546 | * so check the device and inode to be sure that this particular file isn't | 547 | * so check the device and inode to be sure that this particular file isn't |
547 | * the new tarball */ | 548 | * the new tarball */ |
@@ -551,6 +552,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb | |||
551 | bb_error_msg("%s: file is the archive; skipping", fileName); | 552 | bb_error_msg("%s: file is the archive; skipping", fileName); |
552 | return TRUE; | 553 | return TRUE; |
553 | } | 554 | } |
555 | #endif | ||
554 | 556 | ||
555 | if (exclude_file(tbInfo->excludeList, header_name)) | 557 | if (exclude_file(tbInfo->excludeList, header_name)) |
556 | return SKIP; | 558 | return SKIP; |
@@ -608,6 +610,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb | |||
608 | } | 610 | } |
609 | 611 | ||
610 | #if SEAMLESS_COMPRESSION | 612 | #if SEAMLESS_COMPRESSION |
613 | #if !ENABLE_PLATFORM_MINGW32 | ||
611 | /* Don't inline: vfork scares gcc and pessimizes code */ | 614 | /* Don't inline: vfork scares gcc and pessimizes code */ |
612 | static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) | 615 | static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) |
613 | { | 616 | { |
@@ -667,6 +670,27 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) | |||
667 | bb_perror_msg_and_die("can't execute '%s'", gzip); | 670 | bb_perror_msg_and_die("can't execute '%s'", gzip); |
668 | } | 671 | } |
669 | } | 672 | } |
673 | #else | ||
674 | static pid_t vfork_compressor(int tar_fd, const char *gzip) | ||
675 | { | ||
676 | char *cmd; | ||
677 | int fd1; | ||
678 | pid_t pid; | ||
679 | |||
680 | if (find_applet_by_name(gzip) >= 0) { | ||
681 | cmd = xasprintf("%s --busybox %s -cf -", bb_busybox_exec_path, gzip); | ||
682 | } | ||
683 | else { | ||
684 | cmd = xasprintf("%s -cf -", gzip); | ||
685 | } | ||
686 | if ( (fd1=mingw_popen_fd(cmd, "w", tar_fd, &pid)) == -1 ) { | ||
687 | bb_perror_msg_and_die("can't execute '%s'", gzip); | ||
688 | } | ||
689 | free(cmd); | ||
690 | xmove_fd(fd1, tar_fd); | ||
691 | return pid; | ||
692 | } | ||
693 | #endif /* ENABLE_PLATFORM_MINGW32 */ | ||
670 | #endif /* SEAMLESS_COMPRESSION */ | 694 | #endif /* SEAMLESS_COMPRESSION */ |
671 | 695 | ||
672 | 696 | ||
@@ -682,6 +706,7 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, | |||
682 | { | 706 | { |
683 | int errorFlag = FALSE; | 707 | int errorFlag = FALSE; |
684 | struct TarBallInfo tbInfo; | 708 | struct TarBallInfo tbInfo; |
709 | IF_PLATFORM_MINGW32(pid_t pid = 0;) | ||
685 | 710 | ||
686 | tbInfo.hlInfoHead = NULL; | 711 | tbInfo.hlInfoHead = NULL; |
687 | tbInfo.tarFd = tar_fd; | 712 | tbInfo.tarFd = tar_fd; |
@@ -693,7 +718,7 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, | |||
693 | 718 | ||
694 | #if SEAMLESS_COMPRESSION | 719 | #if SEAMLESS_COMPRESSION |
695 | if (gzip) | 720 | if (gzip) |
696 | vfork_compressor(tbInfo.tarFd, gzip); | 721 | IF_PLATFORM_MINGW32(pid = )vfork_compressor(tbInfo.tarFd, gzip); |
697 | #endif | 722 | #endif |
698 | 723 | ||
699 | tbInfo.excludeList = exclude; | 724 | tbInfo.excludeList = exclude; |
@@ -729,7 +754,11 @@ static NOINLINE int writeTarFile(int tar_fd, int verboseFlag, | |||
729 | #if SEAMLESS_COMPRESSION | 754 | #if SEAMLESS_COMPRESSION |
730 | if (gzip) { | 755 | if (gzip) { |
731 | int status; | 756 | int status; |
757 | #if !ENABLE_PLATFORM_MINGW32 | ||
732 | if (safe_waitpid(-1, &status, 0) == -1) | 758 | if (safe_waitpid(-1, &status, 0) == -1) |
759 | #else | ||
760 | if (safe_waitpid(pid, &status, 0) == -1) | ||
761 | #endif | ||
733 | bb_perror_msg("waitpid"); | 762 | bb_perror_msg("waitpid"); |
734 | else if (!WIFEXITED(status) || WEXITSTATUS(status)) | 763 | else if (!WIFEXITED(status) || WEXITSTATUS(status)) |
735 | /* gzip was killed or has exited with nonzero! */ | 764 | /* gzip was killed or has exited with nonzero! */ |
diff --git a/archival/unzip.c b/archival/unzip.c index 8ed9ae7d5..f37ea3519 100644 --- a/archival/unzip.c +++ b/archival/unzip.c | |||
@@ -70,6 +70,9 @@ | |||
70 | 70 | ||
71 | #include "libbb.h" | 71 | #include "libbb.h" |
72 | #include "bb_archive.h" | 72 | #include "bb_archive.h" |
73 | #if ENABLE_PLATFORM_MINGW32 && __GNUC__ | ||
74 | #pragma pack(2) | ||
75 | #endif | ||
73 | 76 | ||
74 | #if 0 | 77 | #if 0 |
75 | # define dbg(...) bb_error_msg(__VA_ARGS__) | 78 | # define dbg(...) bb_error_msg(__VA_ARGS__) |
@@ -586,7 +589,7 @@ int unzip_main(int argc, char **argv) | |||
586 | } | 589 | } |
587 | } | 590 | } |
588 | 591 | ||
589 | #ifndef __GLIBC__ | 592 | #if !defined(__GLIBC__) && !ENABLE_PLATFORM_MINGW32 |
590 | /* | 593 | /* |
591 | * This code is needed for non-GNU getopt | 594 | * This code is needed for non-GNU getopt |
592 | * which doesn't understand "-" in option string. | 595 | * which doesn't understand "-" in option string. |
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig new file mode 100644 index 000000000..c5bc6eb9a --- /dev/null +++ b/configs/mingw32_defconfig | |||
@@ -0,0 +1,1131 @@ | |||
1 | # | ||
2 | # Automatically generated make config: don't edit | ||
3 | # Busybox version: 1.28.0.git | ||
4 | # Mon Jul 24 11:51:22 2017 | ||
5 | # | ||
6 | CONFIG_HAVE_DOT_CONFIG=y | ||
7 | # CONFIG_PLATFORM_POSIX is not set | ||
8 | CONFIG_PLATFORM_MINGW32=y | ||
9 | |||
10 | # | ||
11 | # Busybox Settings | ||
12 | # | ||
13 | CONFIG_DESKTOP=y | ||
14 | # CONFIG_EXTRA_COMPAT is not set | ||
15 | # CONFIG_INCLUDE_SUSv2 is not set | ||
16 | CONFIG_LONG_OPTS=y | ||
17 | CONFIG_SHOW_USAGE=y | ||
18 | CONFIG_FEATURE_VERBOSE_USAGE=y | ||
19 | CONFIG_FEATURE_COMPRESS_USAGE=y | ||
20 | CONFIG_LFS=y | ||
21 | # CONFIG_GLOBBING is not set | ||
22 | # CONFIG_SAFE_ENV is not set | ||
23 | # CONFIG_PAM is not set | ||
24 | # CONFIG_FEATURE_DEVPTS is not set | ||
25 | # CONFIG_FEATURE_UTMP is not set | ||
26 | # CONFIG_FEATURE_WTMP is not set | ||
27 | # CONFIG_FEATURE_PIDFILE is not set | ||
28 | CONFIG_PID_FILE_PATH="" | ||
29 | CONFIG_BUSYBOX=y | ||
30 | CONFIG_FEATURE_INSTALLER=y | ||
31 | # CONFIG_INSTALL_NO_USR is not set | ||
32 | # CONFIG_FEATURE_SUID is not set | ||
33 | # CONFIG_FEATURE_SUID_CONFIG is not set | ||
34 | # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set | ||
35 | CONFIG_FEATURE_PREFER_APPLETS=y | ||
36 | CONFIG_BUSYBOX_EXEC_PATH="" | ||
37 | # CONFIG_SELINUX is not set | ||
38 | # CONFIG_FEATURE_CLEAN_UP is not set | ||
39 | # CONFIG_FEATURE_SYSLOG is not set | ||
40 | # CONFIG_FEATURE_HAVE_RPC is not set | ||
41 | # CONFIG_PLATFORM_LINUX is not set | ||
42 | |||
43 | # | ||
44 | # Build Options | ||
45 | # | ||
46 | # CONFIG_STATIC is not set | ||
47 | # CONFIG_PIE is not set | ||
48 | # CONFIG_NOMMU is not set | ||
49 | # CONFIG_BUILD_LIBBUSYBOX is not set | ||
50 | # CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set | ||
51 | # CONFIG_FEATURE_INDIVIDUAL is not set | ||
52 | # CONFIG_FEATURE_SHARED_BUSYBOX is not set | ||
53 | CONFIG_CROSS_COMPILER_PREFIX="i686-w64-mingw32-" | ||
54 | CONFIG_SYSROOT="" | ||
55 | CONFIG_EXTRA_CFLAGS="" | ||
56 | CONFIG_EXTRA_LDFLAGS="" | ||
57 | CONFIG_EXTRA_LDLIBS="" | ||
58 | CONFIG_USE_PORTABLE_CODE=y | ||
59 | |||
60 | # | ||
61 | # Installation Options ("make install" behavior) | ||
62 | # | ||
63 | CONFIG_INSTALL_APPLET_SYMLINKS=y | ||
64 | # CONFIG_INSTALL_APPLET_HARDLINKS is not set | ||
65 | # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set | ||
66 | # CONFIG_INSTALL_APPLET_DONT is not set | ||
67 | # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set | ||
68 | # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set | ||
69 | # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set | ||
70 | CONFIG_PREFIX="" | ||
71 | |||
72 | # | ||
73 | # Debugging Options | ||
74 | # | ||
75 | CONFIG_DEBUG=y | ||
76 | # CONFIG_DEBUG_PESSIMIZE is not set | ||
77 | # CONFIG_DEBUG_SANITIZE is not set | ||
78 | # CONFIG_UNIT_TEST is not set | ||
79 | # CONFIG_WERROR is not set | ||
80 | CONFIG_NO_DEBUG_LIB=y | ||
81 | # CONFIG_DMALLOC is not set | ||
82 | # CONFIG_EFENCE is not set | ||
83 | |||
84 | # | ||
85 | # Library Tuning | ||
86 | # | ||
87 | # CONFIG_FEATURE_USE_BSS_TAIL is not set | ||
88 | CONFIG_FEATURE_RTMINMAX=y | ||
89 | CONFIG_FEATURE_BUFFERS_USE_MALLOC=y | ||
90 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set | ||
91 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set | ||
92 | CONFIG_PASSWORD_MINLEN=6 | ||
93 | CONFIG_MD5_SMALL=1 | ||
94 | CONFIG_SHA3_SMALL=1 | ||
95 | # CONFIG_FEATURE_FAST_TOP is not set | ||
96 | # CONFIG_FEATURE_ETC_NETWORKS is not set | ||
97 | CONFIG_FEATURE_EDITING=y | ||
98 | CONFIG_FEATURE_EDITING_MAX_LEN=1024 | ||
99 | CONFIG_FEATURE_EDITING_VI=y | ||
100 | CONFIG_FEATURE_EDITING_HISTORY=255 | ||
101 | CONFIG_FEATURE_EDITING_SAVEHISTORY=y | ||
102 | # CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set | ||
103 | CONFIG_FEATURE_REVERSE_SEARCH=y | ||
104 | CONFIG_FEATURE_TAB_COMPLETION=y | ||
105 | CONFIG_FEATURE_USERNAME_COMPLETION=y | ||
106 | CONFIG_FEATURE_EDITING_FANCY_PROMPT=y | ||
107 | # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set | ||
108 | # CONFIG_LOCALE_SUPPORT is not set | ||
109 | # CONFIG_UNICODE_SUPPORT is not set | ||
110 | # CONFIG_UNICODE_USING_LOCALE is not set | ||
111 | # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set | ||
112 | CONFIG_SUBST_WCHAR=0 | ||
113 | CONFIG_LAST_SUPPORTED_WCHAR=0 | ||
114 | # CONFIG_UNICODE_COMBINING_WCHARS is not set | ||
115 | # CONFIG_UNICODE_WIDE_WCHARS is not set | ||
116 | # CONFIG_UNICODE_BIDI_SUPPORT is not set | ||
117 | # CONFIG_UNICODE_NEUTRAL_TABLE is not set | ||
118 | # CONFIG_UNICODE_PRESERVE_BROKEN is not set | ||
119 | CONFIG_FEATURE_NON_POSIX_CP=y | ||
120 | # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set | ||
121 | # CONFIG_FEATURE_USE_SENDFILE is not set | ||
122 | CONFIG_FEATURE_COPYBUF_KB=4 | ||
123 | CONFIG_FEATURE_SKIP_ROOTFS=y | ||
124 | # CONFIG_MONOTONIC_SYSCALL is not set | ||
125 | CONFIG_IOCTL_HEX2STR_ERROR=y | ||
126 | # CONFIG_FEATURE_HWIB is not set | ||
127 | |||
128 | # | ||
129 | # Applets | ||
130 | # | ||
131 | |||
132 | # | ||
133 | # Archival Utilities | ||
134 | # | ||
135 | CONFIG_FEATURE_SEAMLESS_XZ=y | ||
136 | CONFIG_FEATURE_SEAMLESS_LZMA=y | ||
137 | CONFIG_FEATURE_SEAMLESS_BZ2=y | ||
138 | CONFIG_FEATURE_SEAMLESS_GZ=y | ||
139 | CONFIG_FEATURE_SEAMLESS_Z=y | ||
140 | CONFIG_AR=y | ||
141 | CONFIG_FEATURE_AR_LONG_FILENAMES=y | ||
142 | CONFIG_FEATURE_AR_CREATE=y | ||
143 | CONFIG_UNCOMPRESS=y | ||
144 | CONFIG_GUNZIP=y | ||
145 | CONFIG_ZCAT=y | ||
146 | CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y | ||
147 | CONFIG_BUNZIP2=y | ||
148 | CONFIG_BZCAT=y | ||
149 | CONFIG_UNLZMA=y | ||
150 | CONFIG_LZCAT=y | ||
151 | CONFIG_LZMA=y | ||
152 | CONFIG_UNXZ=y | ||
153 | CONFIG_XZCAT=y | ||
154 | CONFIG_XZ=y | ||
155 | CONFIG_BZIP2=y | ||
156 | CONFIG_FEATURE_BZIP2_DECOMPRESS=y | ||
157 | CONFIG_CPIO=y | ||
158 | CONFIG_FEATURE_CPIO_O=y | ||
159 | CONFIG_FEATURE_CPIO_P=y | ||
160 | # CONFIG_DPKG is not set | ||
161 | CONFIG_DPKG_DEB=y | ||
162 | CONFIG_GZIP=y | ||
163 | CONFIG_FEATURE_GZIP_LONG_OPTIONS=y | ||
164 | CONFIG_GZIP_FAST=2 | ||
165 | CONFIG_FEATURE_GZIP_LEVELS=y | ||
166 | CONFIG_FEATURE_GZIP_DECOMPRESS=y | ||
167 | CONFIG_LZOP=y | ||
168 | CONFIG_UNLZOP=y | ||
169 | CONFIG_LZOPCAT=y | ||
170 | # CONFIG_LZOP_COMPR_HIGH is not set | ||
171 | CONFIG_RPM2CPIO=y | ||
172 | # CONFIG_RPM is not set | ||
173 | CONFIG_TAR=y | ||
174 | CONFIG_FEATURE_TAR_LONG_OPTIONS=y | ||
175 | CONFIG_FEATURE_TAR_CREATE=y | ||
176 | CONFIG_FEATURE_TAR_AUTODETECT=y | ||
177 | CONFIG_FEATURE_TAR_FROM=y | ||
178 | CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y | ||
179 | # CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set | ||
180 | CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y | ||
181 | # CONFIG_FEATURE_TAR_TO_COMMAND is not set | ||
182 | # CONFIG_FEATURE_TAR_UNAME_GNAME is not set | ||
183 | CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y | ||
184 | # CONFIG_FEATURE_TAR_SELINUX is not set | ||
185 | CONFIG_UNZIP=y | ||
186 | CONFIG_FEATURE_UNZIP_CDF=y | ||
187 | CONFIG_FEATURE_UNZIP_BZIP2=y | ||
188 | CONFIG_FEATURE_UNZIP_LZMA=y | ||
189 | CONFIG_FEATURE_UNZIP_XZ=y | ||
190 | CONFIG_FEATURE_LZMA_FAST=y | ||
191 | |||
192 | # | ||
193 | # Coreutils | ||
194 | # | ||
195 | CONFIG_BASENAME=y | ||
196 | CONFIG_CAT=y | ||
197 | CONFIG_FEATURE_CATN=y | ||
198 | CONFIG_FEATURE_CATV=y | ||
199 | # CONFIG_CHGRP is not set | ||
200 | CONFIG_CHMOD=y | ||
201 | # CONFIG_CHOWN is not set | ||
202 | # CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set | ||
203 | # CONFIG_CHROOT is not set | ||
204 | CONFIG_CKSUM=y | ||
205 | CONFIG_COMM=y | ||
206 | CONFIG_CP=y | ||
207 | CONFIG_FEATURE_CP_LONG_OPTIONS=y | ||
208 | CONFIG_CUT=y | ||
209 | CONFIG_DATE=y | ||
210 | CONFIG_FEATURE_DATE_ISOFMT=y | ||
211 | # CONFIG_FEATURE_DATE_NANO is not set | ||
212 | CONFIG_FEATURE_DATE_COMPAT=y | ||
213 | CONFIG_DD=y | ||
214 | # CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set | ||
215 | # CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set | ||
216 | CONFIG_FEATURE_DD_IBS_OBS=y | ||
217 | CONFIG_FEATURE_DD_STATUS=y | ||
218 | CONFIG_DF=y | ||
219 | # CONFIG_FEATURE_DF_FANCY is not set | ||
220 | CONFIG_DIRNAME=y | ||
221 | CONFIG_DOS2UNIX=y | ||
222 | CONFIG_UNIX2DOS=y | ||
223 | CONFIG_DU=y | ||
224 | CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y | ||
225 | CONFIG_ECHO=y | ||
226 | CONFIG_FEATURE_FANCY_ECHO=y | ||
227 | CONFIG_ENV=y | ||
228 | CONFIG_FEATURE_ENV_LONG_OPTIONS=y | ||
229 | CONFIG_EXPAND=y | ||
230 | CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y | ||
231 | CONFIG_UNEXPAND=y | ||
232 | CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y | ||
233 | CONFIG_EXPR=y | ||
234 | CONFIG_EXPR_MATH_SUPPORT_64=y | ||
235 | CONFIG_FACTOR=y | ||
236 | CONFIG_FALSE=y | ||
237 | CONFIG_FOLD=y | ||
238 | # CONFIG_FSYNC is not set | ||
239 | CONFIG_HEAD=y | ||
240 | CONFIG_FEATURE_FANCY_HEAD=y | ||
241 | # CONFIG_HOSTID is not set | ||
242 | CONFIG_ID=y | ||
243 | CONFIG_GROUPS=y | ||
244 | # CONFIG_INSTALL is not set | ||
245 | # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set | ||
246 | CONFIG_LINK=y | ||
247 | CONFIG_LN=y | ||
248 | CONFIG_LOGNAME=y | ||
249 | CONFIG_LS=y | ||
250 | CONFIG_FEATURE_LS_FILETYPES=y | ||
251 | CONFIG_FEATURE_LS_FOLLOWLINKS=y | ||
252 | CONFIG_FEATURE_LS_RECURSIVE=y | ||
253 | CONFIG_FEATURE_LS_WIDTH=y | ||
254 | CONFIG_FEATURE_LS_SORTFILES=y | ||
255 | CONFIG_FEATURE_LS_TIMESTAMPS=y | ||
256 | CONFIG_FEATURE_LS_USERNAME=y | ||
257 | CONFIG_FEATURE_LS_COLOR=y | ||
258 | CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y | ||
259 | CONFIG_MD5SUM=y | ||
260 | CONFIG_SHA1SUM=y | ||
261 | CONFIG_SHA256SUM=y | ||
262 | CONFIG_SHA512SUM=y | ||
263 | CONFIG_SHA3SUM=y | ||
264 | |||
265 | # | ||
266 | # Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum | ||
267 | # | ||
268 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y | ||
269 | CONFIG_MKDIR=y | ||
270 | CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y | ||
271 | # CONFIG_MKFIFO is not set | ||
272 | # CONFIG_MKNOD is not set | ||
273 | CONFIG_MKTEMP=y | ||
274 | CONFIG_MV=y | ||
275 | CONFIG_FEATURE_MV_LONG_OPTIONS=y | ||
276 | # CONFIG_NICE is not set | ||
277 | CONFIG_NL=y | ||
278 | # CONFIG_NOHUP is not set | ||
279 | # CONFIG_NPROC is not set | ||
280 | CONFIG_OD=y | ||
281 | CONFIG_PASTE=y | ||
282 | CONFIG_PRINTENV=y | ||
283 | CONFIG_PRINTF=y | ||
284 | CONFIG_PWD=y | ||
285 | # CONFIG_READLINK is not set | ||
286 | # CONFIG_FEATURE_READLINK_FOLLOW is not set | ||
287 | # CONFIG_REALPATH is not set | ||
288 | CONFIG_RM=y | ||
289 | CONFIG_RMDIR=y | ||
290 | CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y | ||
291 | CONFIG_SEQ=y | ||
292 | # CONFIG_SHRED is not set | ||
293 | CONFIG_SHUF=y | ||
294 | CONFIG_SLEEP=y | ||
295 | CONFIG_FEATURE_FANCY_SLEEP=y | ||
296 | CONFIG_FEATURE_FLOAT_SLEEP=y | ||
297 | CONFIG_SORT=y | ||
298 | CONFIG_FEATURE_SORT_BIG=y | ||
299 | CONFIG_SPLIT=y | ||
300 | CONFIG_FEATURE_SPLIT_FANCY=y | ||
301 | CONFIG_STAT=y | ||
302 | CONFIG_FEATURE_STAT_FORMAT=y | ||
303 | CONFIG_FEATURE_STAT_FILESYSTEM=y | ||
304 | # CONFIG_STTY is not set | ||
305 | CONFIG_SUM=y | ||
306 | # CONFIG_SYNC is not set | ||
307 | # CONFIG_FEATURE_SYNC_FANCY is not set | ||
308 | CONFIG_TAC=y | ||
309 | CONFIG_TAIL=y | ||
310 | CONFIG_FEATURE_FANCY_TAIL=y | ||
311 | CONFIG_TEE=y | ||
312 | CONFIG_FEATURE_TEE_USE_BLOCK_IO=y | ||
313 | CONFIG_TEST=y | ||
314 | CONFIG_TEST1=y | ||
315 | CONFIG_TEST2=y | ||
316 | CONFIG_FEATURE_TEST_64=y | ||
317 | # CONFIG_TIMEOUT is not set | ||
318 | CONFIG_TOUCH=y | ||
319 | # CONFIG_FEATURE_TOUCH_NODEREF is not set | ||
320 | CONFIG_FEATURE_TOUCH_SUSV3=y | ||
321 | CONFIG_TR=y | ||
322 | CONFIG_FEATURE_TR_CLASSES=y | ||
323 | CONFIG_FEATURE_TR_EQUIV=y | ||
324 | CONFIG_TRUE=y | ||
325 | CONFIG_TRUNCATE=y | ||
326 | # CONFIG_TTY is not set | ||
327 | CONFIG_UNAME=y | ||
328 | CONFIG_UNAME_OSNAME="MS/Windows" | ||
329 | CONFIG_BB_ARCH=y | ||
330 | CONFIG_UNIQ=y | ||
331 | CONFIG_UNLINK=y | ||
332 | CONFIG_USLEEP=y | ||
333 | CONFIG_UUDECODE=y | ||
334 | CONFIG_BASE64=y | ||
335 | CONFIG_UUENCODE=y | ||
336 | CONFIG_WC=y | ||
337 | CONFIG_FEATURE_WC_LARGE=y | ||
338 | CONFIG_WHOAMI=y | ||
339 | # CONFIG_WHO is not set | ||
340 | # CONFIG_W is not set | ||
341 | # CONFIG_USERS is not set | ||
342 | CONFIG_YES=y | ||
343 | |||
344 | # | ||
345 | # Common options | ||
346 | # | ||
347 | CONFIG_FEATURE_VERBOSE=y | ||
348 | |||
349 | # | ||
350 | # Common options for cp and mv | ||
351 | # | ||
352 | # CONFIG_FEATURE_PRESERVE_HARDLINKS is not set | ||
353 | |||
354 | # | ||
355 | # Common options for df, du, ls | ||
356 | # | ||
357 | CONFIG_FEATURE_HUMAN_READABLE=y | ||
358 | |||
359 | # | ||
360 | # Console Utilities | ||
361 | # | ||
362 | # CONFIG_CHVT is not set | ||
363 | CONFIG_CLEAR=y | ||
364 | # CONFIG_DEALLOCVT is not set | ||
365 | # CONFIG_DUMPKMAP is not set | ||
366 | # CONFIG_FGCONSOLE is not set | ||
367 | # CONFIG_KBD_MODE is not set | ||
368 | # CONFIG_LOADFONT is not set | ||
369 | # CONFIG_SETFONT is not set | ||
370 | # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set | ||
371 | CONFIG_DEFAULT_SETFONT_DIR="" | ||
372 | # CONFIG_FEATURE_LOADFONT_PSF2 is not set | ||
373 | # CONFIG_FEATURE_LOADFONT_RAW is not set | ||
374 | # CONFIG_LOADKMAP is not set | ||
375 | # CONFIG_OPENVT is not set | ||
376 | # CONFIG_RESET is not set | ||
377 | # CONFIG_RESIZE is not set | ||
378 | # CONFIG_FEATURE_RESIZE_PRINT is not set | ||
379 | # CONFIG_SETCONSOLE is not set | ||
380 | # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set | ||
381 | # CONFIG_SETKEYCODES is not set | ||
382 | # CONFIG_SETLOGCONS is not set | ||
383 | # CONFIG_SHOWKEY is not set | ||
384 | |||
385 | # | ||
386 | # Debian Utilities | ||
387 | # | ||
388 | # CONFIG_PIPE_PROGRESS is not set | ||
389 | # CONFIG_RUN_PARTS is not set | ||
390 | # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set | ||
391 | # CONFIG_FEATURE_RUN_PARTS_FANCY is not set | ||
392 | # CONFIG_START_STOP_DAEMON is not set | ||
393 | # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set | ||
394 | # CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set | ||
395 | CONFIG_WHICH=y | ||
396 | |||
397 | # | ||
398 | # Editors | ||
399 | # | ||
400 | CONFIG_AWK=y | ||
401 | CONFIG_FEATURE_AWK_LIBM=y | ||
402 | CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y | ||
403 | CONFIG_CMP=y | ||
404 | CONFIG_DIFF=y | ||
405 | CONFIG_FEATURE_DIFF_LONG_OPTIONS=y | ||
406 | CONFIG_FEATURE_DIFF_DIR=y | ||
407 | CONFIG_ED=y | ||
408 | CONFIG_PATCH=y | ||
409 | CONFIG_SED=y | ||
410 | CONFIG_VI=y | ||
411 | CONFIG_FEATURE_VI_MAX_LEN=4096 | ||
412 | CONFIG_FEATURE_VI_8BIT=y | ||
413 | CONFIG_FEATURE_VI_COLON=y | ||
414 | CONFIG_FEATURE_VI_YANKMARK=y | ||
415 | CONFIG_FEATURE_VI_SEARCH=y | ||
416 | # CONFIG_FEATURE_VI_REGEX_SEARCH is not set | ||
417 | # CONFIG_FEATURE_VI_USE_SIGNALS is not set | ||
418 | CONFIG_FEATURE_VI_DOT_CMD=y | ||
419 | CONFIG_FEATURE_VI_READONLY=y | ||
420 | CONFIG_FEATURE_VI_SETOPTS=y | ||
421 | CONFIG_FEATURE_VI_SET=y | ||
422 | CONFIG_FEATURE_VI_WIN_RESIZE=y | ||
423 | CONFIG_FEATURE_VI_ASK_TERMINAL=y | ||
424 | CONFIG_FEATURE_VI_UNDO=y | ||
425 | CONFIG_FEATURE_VI_UNDO_QUEUE=y | ||
426 | CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256 | ||
427 | CONFIG_FEATURE_ALLOW_EXEC=y | ||
428 | |||
429 | # | ||
430 | # Finding Utilities | ||
431 | # | ||
432 | CONFIG_FIND=y | ||
433 | CONFIG_FEATURE_FIND_PRINT0=y | ||
434 | CONFIG_FEATURE_FIND_MTIME=y | ||
435 | CONFIG_FEATURE_FIND_MMIN=y | ||
436 | CONFIG_FEATURE_FIND_PERM=y | ||
437 | CONFIG_FEATURE_FIND_TYPE=y | ||
438 | # CONFIG_FEATURE_FIND_XDEV is not set | ||
439 | CONFIG_FEATURE_FIND_MAXDEPTH=y | ||
440 | CONFIG_FEATURE_FIND_NEWER=y | ||
441 | # CONFIG_FEATURE_FIND_INUM is not set | ||
442 | # CONFIG_FEATURE_FIND_EXEC is not set | ||
443 | # CONFIG_FEATURE_FIND_EXEC_PLUS is not set | ||
444 | # CONFIG_FEATURE_FIND_USER is not set | ||
445 | # CONFIG_FEATURE_FIND_GROUP is not set | ||
446 | CONFIG_FEATURE_FIND_NOT=y | ||
447 | CONFIG_FEATURE_FIND_DEPTH=y | ||
448 | CONFIG_FEATURE_FIND_PAREN=y | ||
449 | CONFIG_FEATURE_FIND_SIZE=y | ||
450 | CONFIG_FEATURE_FIND_PRUNE=y | ||
451 | CONFIG_FEATURE_FIND_DELETE=y | ||
452 | CONFIG_FEATURE_FIND_PATH=y | ||
453 | CONFIG_FEATURE_FIND_REGEX=y | ||
454 | # CONFIG_FEATURE_FIND_CONTEXT is not set | ||
455 | # CONFIG_FEATURE_FIND_LINKS is not set | ||
456 | CONFIG_GREP=y | ||
457 | CONFIG_EGREP=y | ||
458 | CONFIG_FGREP=y | ||
459 | CONFIG_FEATURE_GREP_CONTEXT=y | ||
460 | CONFIG_XARGS=y | ||
461 | CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y | ||
462 | CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y | ||
463 | CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y | ||
464 | CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y | ||
465 | CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y | ||
466 | |||
467 | # | ||
468 | # Init Utilities | ||
469 | # | ||
470 | # CONFIG_BOOTCHARTD is not set | ||
471 | # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set | ||
472 | # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set | ||
473 | # CONFIG_HALT is not set | ||
474 | # CONFIG_POWEROFF is not set | ||
475 | # CONFIG_REBOOT is not set | ||
476 | # CONFIG_FEATURE_CALL_TELINIT is not set | ||
477 | CONFIG_TELINIT_PATH="" | ||
478 | # CONFIG_INIT is not set | ||
479 | # CONFIG_LINUXRC is not set | ||
480 | # CONFIG_FEATURE_USE_INITTAB is not set | ||
481 | # CONFIG_FEATURE_KILL_REMOVED is not set | ||
482 | CONFIG_FEATURE_KILL_DELAY=0 | ||
483 | # CONFIG_FEATURE_INIT_SCTTY is not set | ||
484 | # CONFIG_FEATURE_INIT_SYSLOG is not set | ||
485 | # CONFIG_FEATURE_INIT_QUIET is not set | ||
486 | # CONFIG_FEATURE_INIT_COREDUMPS is not set | ||
487 | CONFIG_INIT_TERMINAL_TYPE="" | ||
488 | # CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set | ||
489 | |||
490 | # | ||
491 | # Login/Password Management Utilities | ||
492 | # | ||
493 | # CONFIG_FEATURE_SHADOWPASSWDS is not set | ||
494 | # CONFIG_USE_BB_PWD_GRP is not set | ||
495 | # CONFIG_USE_BB_SHADOW is not set | ||
496 | # CONFIG_USE_BB_CRYPT is not set | ||
497 | # CONFIG_USE_BB_CRYPT_SHA is not set | ||
498 | # CONFIG_ADDGROUP is not set | ||
499 | # CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set | ||
500 | # CONFIG_FEATURE_ADDUSER_TO_GROUP is not set | ||
501 | # CONFIG_ADD_SHELL is not set | ||
502 | # CONFIG_REMOVE_SHELL is not set | ||
503 | # CONFIG_ADDUSER is not set | ||
504 | # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set | ||
505 | # CONFIG_FEATURE_CHECK_NAMES is not set | ||
506 | CONFIG_LAST_ID=0 | ||
507 | CONFIG_FIRST_SYSTEM_ID=0 | ||
508 | CONFIG_LAST_SYSTEM_ID=0 | ||
509 | # CONFIG_CHPASSWD is not set | ||
510 | CONFIG_FEATURE_DEFAULT_PASSWD_ALGO="" | ||
511 | # CONFIG_CRYPTPW is not set | ||
512 | # CONFIG_MKPASSWD is not set | ||
513 | # CONFIG_DELUSER is not set | ||
514 | # CONFIG_DELGROUP is not set | ||
515 | # CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set | ||
516 | # CONFIG_GETTY is not set | ||
517 | # CONFIG_LOGIN is not set | ||
518 | # CONFIG_LOGIN_SESSION_AS_CHILD is not set | ||
519 | # CONFIG_LOGIN_SCRIPTS is not set | ||
520 | # CONFIG_FEATURE_NOLOGIN is not set | ||
521 | # CONFIG_FEATURE_SECURETTY is not set | ||
522 | # CONFIG_PASSWD is not set | ||
523 | # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set | ||
524 | # CONFIG_SU is not set | ||
525 | # CONFIG_FEATURE_SU_SYSLOG is not set | ||
526 | # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set | ||
527 | # CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set | ||
528 | # CONFIG_SULOGIN is not set | ||
529 | # CONFIG_VLOCK is not set | ||
530 | |||
531 | # | ||
532 | # Linux Ext2 FS Progs | ||
533 | # | ||
534 | # CONFIG_CHATTR is not set | ||
535 | # CONFIG_FSCK is not set | ||
536 | # CONFIG_LSATTR is not set | ||
537 | # CONFIG_TUNE2FS is not set | ||
538 | |||
539 | # | ||
540 | # Linux Module Utilities | ||
541 | # | ||
542 | # CONFIG_MODPROBE_SMALL is not set | ||
543 | # CONFIG_DEPMOD is not set | ||
544 | # CONFIG_INSMOD is not set | ||
545 | # CONFIG_LSMOD is not set | ||
546 | # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set | ||
547 | # CONFIG_MODINFO is not set | ||
548 | # CONFIG_MODPROBE is not set | ||
549 | # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set | ||
550 | # CONFIG_RMMOD is not set | ||
551 | |||
552 | # | ||
553 | # Options common to multiple modutils | ||
554 | # | ||
555 | # CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set | ||
556 | # CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set | ||
557 | # CONFIG_FEATURE_2_4_MODULES is not set | ||
558 | # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set | ||
559 | # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set | ||
560 | # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set | ||
561 | # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set | ||
562 | # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set | ||
563 | # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set | ||
564 | # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set | ||
565 | # CONFIG_FEATURE_MODUTILS_ALIAS is not set | ||
566 | # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set | ||
567 | CONFIG_DEFAULT_MODULES_DIR="" | ||
568 | CONFIG_DEFAULT_DEPMOD_FILE="" | ||
569 | |||
570 | # | ||
571 | # Linux System Utilities | ||
572 | # | ||
573 | # CONFIG_ACPID is not set | ||
574 | # CONFIG_FEATURE_ACPID_COMPAT is not set | ||
575 | # CONFIG_BLKDISCARD is not set | ||
576 | # CONFIG_BLKID is not set | ||
577 | # CONFIG_FEATURE_BLKID_TYPE is not set | ||
578 | # CONFIG_BLOCKDEV is not set | ||
579 | CONFIG_CAL=y | ||
580 | # CONFIG_CHRT is not set | ||
581 | # CONFIG_DMESG is not set | ||
582 | # CONFIG_FEATURE_DMESG_PRETTY is not set | ||
583 | # CONFIG_EJECT is not set | ||
584 | # CONFIG_FEATURE_EJECT_SCSI is not set | ||
585 | # CONFIG_FALLOCATE is not set | ||
586 | # CONFIG_FATATTR is not set | ||
587 | # CONFIG_FBSET is not set | ||
588 | # CONFIG_FEATURE_FBSET_FANCY is not set | ||
589 | # CONFIG_FEATURE_FBSET_READMODE is not set | ||
590 | # CONFIG_FDFORMAT is not set | ||
591 | # CONFIG_FDISK is not set | ||
592 | # CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set | ||
593 | # CONFIG_FEATURE_FDISK_WRITABLE is not set | ||
594 | # CONFIG_FEATURE_AIX_LABEL is not set | ||
595 | # CONFIG_FEATURE_SGI_LABEL is not set | ||
596 | # CONFIG_FEATURE_SUN_LABEL is not set | ||
597 | # CONFIG_FEATURE_OSF_LABEL is not set | ||
598 | # CONFIG_FEATURE_GPT_LABEL is not set | ||
599 | # CONFIG_FEATURE_FDISK_ADVANCED is not set | ||
600 | # CONFIG_FINDFS is not set | ||
601 | # CONFIG_FLOCK is not set | ||
602 | # CONFIG_FDFLUSH is not set | ||
603 | # CONFIG_FREERAMDISK is not set | ||
604 | # CONFIG_FSCK_MINIX is not set | ||
605 | # CONFIG_FSFREEZE is not set | ||
606 | # CONFIG_FSTRIM is not set | ||
607 | CONFIG_GETOPT=y | ||
608 | CONFIG_FEATURE_GETOPT_LONG=y | ||
609 | CONFIG_HEXDUMP=y | ||
610 | CONFIG_FEATURE_HEXDUMP_REVERSE=y | ||
611 | CONFIG_HD=y | ||
612 | CONFIG_XXD=y | ||
613 | # CONFIG_HWCLOCK is not set | ||
614 | # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set | ||
615 | # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set | ||
616 | # CONFIG_IONICE is not set | ||
617 | # CONFIG_IPCRM is not set | ||
618 | # CONFIG_IPCS is not set | ||
619 | # CONFIG_LAST is not set | ||
620 | # CONFIG_FEATURE_LAST_FANCY is not set | ||
621 | # CONFIG_LOSETUP is not set | ||
622 | # CONFIG_LSPCI is not set | ||
623 | # CONFIG_LSUSB is not set | ||
624 | # CONFIG_MDEV is not set | ||
625 | # CONFIG_FEATURE_MDEV_CONF is not set | ||
626 | # CONFIG_FEATURE_MDEV_RENAME is not set | ||
627 | # CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set | ||
628 | # CONFIG_FEATURE_MDEV_EXEC is not set | ||
629 | # CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set | ||
630 | # CONFIG_MESG is not set | ||
631 | # CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set | ||
632 | # CONFIG_MKE2FS is not set | ||
633 | # CONFIG_MKFS_EXT2 is not set | ||
634 | # CONFIG_MKFS_MINIX is not set | ||
635 | # CONFIG_FEATURE_MINIX2 is not set | ||
636 | # CONFIG_MKFS_REISER is not set | ||
637 | # CONFIG_MKDOSFS is not set | ||
638 | # CONFIG_MKFS_VFAT is not set | ||
639 | # CONFIG_MKSWAP is not set | ||
640 | # CONFIG_FEATURE_MKSWAP_UUID is not set | ||
641 | # CONFIG_MORE is not set | ||
642 | # CONFIG_MOUNT is not set | ||
643 | # CONFIG_FEATURE_MOUNT_FAKE is not set | ||
644 | # CONFIG_FEATURE_MOUNT_VERBOSE is not set | ||
645 | # CONFIG_FEATURE_MOUNT_HELPERS is not set | ||
646 | # CONFIG_FEATURE_MOUNT_LABEL is not set | ||
647 | # CONFIG_FEATURE_MOUNT_NFS is not set | ||
648 | # CONFIG_FEATURE_MOUNT_CIFS is not set | ||
649 | # CONFIG_FEATURE_MOUNT_FLAGS is not set | ||
650 | # CONFIG_FEATURE_MOUNT_FSTAB is not set | ||
651 | # CONFIG_FEATURE_MOUNT_OTHERTAB is not set | ||
652 | # CONFIG_MOUNTPOINT is not set | ||
653 | # CONFIG_NSENTER is not set | ||
654 | # CONFIG_FEATURE_NSENTER_LONG_OPTS is not set | ||
655 | # CONFIG_PIVOT_ROOT is not set | ||
656 | # CONFIG_RDATE is not set | ||
657 | # CONFIG_RDEV is not set | ||
658 | # CONFIG_READPROFILE is not set | ||
659 | # CONFIG_RENICE is not set | ||
660 | CONFIG_REV=y | ||
661 | # CONFIG_RTCWAKE is not set | ||
662 | # CONFIG_SCRIPT is not set | ||
663 | # CONFIG_SCRIPTREPLAY is not set | ||
664 | # CONFIG_SETARCH is not set | ||
665 | # CONFIG_LINUX32 is not set | ||
666 | # CONFIG_LINUX64 is not set | ||
667 | # CONFIG_SETPRIV is not set | ||
668 | # CONFIG_FEATURE_SETPRIV_DUMP is not set | ||
669 | # CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set | ||
670 | # CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set | ||
671 | # CONFIG_SETSID is not set | ||
672 | # CONFIG_SWAPON is not set | ||
673 | # CONFIG_FEATURE_SWAPON_DISCARD is not set | ||
674 | # CONFIG_FEATURE_SWAPON_PRI is not set | ||
675 | # CONFIG_SWAPOFF is not set | ||
676 | # CONFIG_SWITCH_ROOT is not set | ||
677 | # CONFIG_TASKSET is not set | ||
678 | # CONFIG_FEATURE_TASKSET_FANCY is not set | ||
679 | # CONFIG_UEVENT is not set | ||
680 | # CONFIG_UMOUNT is not set | ||
681 | # CONFIG_FEATURE_UMOUNT_ALL is not set | ||
682 | # CONFIG_UNSHARE is not set | ||
683 | # CONFIG_WALL is not set | ||
684 | # CONFIG_FEATURE_MOUNT_LOOP is not set | ||
685 | # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set | ||
686 | # CONFIG_FEATURE_MTAB_SUPPORT is not set | ||
687 | # CONFIG_VOLUMEID is not set | ||
688 | # CONFIG_FEATURE_VOLUMEID_BCACHE is not set | ||
689 | # CONFIG_FEATURE_VOLUMEID_BTRFS is not set | ||
690 | # CONFIG_FEATURE_VOLUMEID_CRAMFS is not set | ||
691 | # CONFIG_FEATURE_VOLUMEID_EXFAT is not set | ||
692 | # CONFIG_FEATURE_VOLUMEID_EXT is not set | ||
693 | # CONFIG_FEATURE_VOLUMEID_F2FS is not set | ||
694 | # CONFIG_FEATURE_VOLUMEID_FAT is not set | ||
695 | # CONFIG_FEATURE_VOLUMEID_HFS is not set | ||
696 | # CONFIG_FEATURE_VOLUMEID_ISO9660 is not set | ||
697 | # CONFIG_FEATURE_VOLUMEID_JFS is not set | ||
698 | # CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set | ||
699 | # CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set | ||
700 | # CONFIG_FEATURE_VOLUMEID_LUKS is not set | ||
701 | # CONFIG_FEATURE_VOLUMEID_NILFS is not set | ||
702 | # CONFIG_FEATURE_VOLUMEID_NTFS is not set | ||
703 | # CONFIG_FEATURE_VOLUMEID_OCFS2 is not set | ||
704 | # CONFIG_FEATURE_VOLUMEID_REISERFS is not set | ||
705 | # CONFIG_FEATURE_VOLUMEID_ROMFS is not set | ||
706 | # CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set | ||
707 | # CONFIG_FEATURE_VOLUMEID_SYSV is not set | ||
708 | # CONFIG_FEATURE_VOLUMEID_UBIFS is not set | ||
709 | # CONFIG_FEATURE_VOLUMEID_UDF is not set | ||
710 | # CONFIG_FEATURE_VOLUMEID_XFS is not set | ||
711 | |||
712 | # | ||
713 | # Miscellaneous Utilities | ||
714 | # | ||
715 | # CONFIG_ADJTIMEX is not set | ||
716 | # CONFIG_BBCONFIG is not set | ||
717 | # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set | ||
718 | # CONFIG_BEEP is not set | ||
719 | CONFIG_FEATURE_BEEP_FREQ=0 | ||
720 | CONFIG_FEATURE_BEEP_LENGTH_MS=0 | ||
721 | # CONFIG_CHAT is not set | ||
722 | # CONFIG_FEATURE_CHAT_NOFAIL is not set | ||
723 | # CONFIG_FEATURE_CHAT_TTY_HIFI is not set | ||
724 | # CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set | ||
725 | # CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set | ||
726 | # CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set | ||
727 | # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set | ||
728 | # CONFIG_FEATURE_CHAT_CLR_ABORT is not set | ||
729 | # CONFIG_CONSPY is not set | ||
730 | # CONFIG_CROND is not set | ||
731 | # CONFIG_FEATURE_CROND_D is not set | ||
732 | # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set | ||
733 | # CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set | ||
734 | CONFIG_FEATURE_CROND_DIR="" | ||
735 | # CONFIG_CRONTAB is not set | ||
736 | CONFIG_DC=y | ||
737 | CONFIG_FEATURE_DC_LIBM=y | ||
738 | # CONFIG_DEVFSD is not set | ||
739 | # CONFIG_DEVFSD_MODLOAD is not set | ||
740 | # CONFIG_DEVFSD_FG_NP is not set | ||
741 | # CONFIG_DEVFSD_VERBOSE is not set | ||
742 | # CONFIG_FEATURE_DEVFS is not set | ||
743 | # CONFIG_DEVMEM is not set | ||
744 | # CONFIG_FBSPLASH is not set | ||
745 | # CONFIG_FLASHCP is not set | ||
746 | # CONFIG_FLASH_ERASEALL is not set | ||
747 | # CONFIG_FLASH_LOCK is not set | ||
748 | # CONFIG_FLASH_UNLOCK is not set | ||
749 | # CONFIG_HDPARM is not set | ||
750 | # CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set | ||
751 | # CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set | ||
752 | # CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set | ||
753 | # CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set | ||
754 | # CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set | ||
755 | # CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set | ||
756 | # CONFIG_I2CGET is not set | ||
757 | # CONFIG_I2CSET is not set | ||
758 | # CONFIG_I2CDUMP is not set | ||
759 | # CONFIG_I2CDETECT is not set | ||
760 | # CONFIG_INOTIFYD is not set | ||
761 | CONFIG_LESS=y | ||
762 | CONFIG_FEATURE_LESS_MAXLINES=9999999 | ||
763 | CONFIG_FEATURE_LESS_BRACKETS=y | ||
764 | CONFIG_FEATURE_LESS_FLAGS=y | ||
765 | CONFIG_FEATURE_LESS_TRUNCATE=y | ||
766 | CONFIG_FEATURE_LESS_MARKS=y | ||
767 | CONFIG_FEATURE_LESS_REGEXP=y | ||
768 | # CONFIG_FEATURE_LESS_WINCH is not set | ||
769 | # CONFIG_FEATURE_LESS_ASK_TERMINAL is not set | ||
770 | CONFIG_FEATURE_LESS_DASHCMD=y | ||
771 | CONFIG_FEATURE_LESS_LINENUMS=y | ||
772 | # CONFIG_LSSCSI is not set | ||
773 | # CONFIG_MAKEDEVS is not set | ||
774 | # CONFIG_FEATURE_MAKEDEVS_LEAF is not set | ||
775 | # CONFIG_FEATURE_MAKEDEVS_TABLE is not set | ||
776 | CONFIG_MAN=y | ||
777 | # CONFIG_MICROCOM is not set | ||
778 | # CONFIG_MT is not set | ||
779 | # CONFIG_NANDWRITE is not set | ||
780 | # CONFIG_NANDDUMP is not set | ||
781 | # CONFIG_PARTPROBE is not set | ||
782 | # CONFIG_RAIDAUTORUN is not set | ||
783 | # CONFIG_READAHEAD is not set | ||
784 | # CONFIG_RFKILL is not set | ||
785 | # CONFIG_RUNLEVEL is not set | ||
786 | # CONFIG_RX is not set | ||
787 | # CONFIG_SETSERIAL is not set | ||
788 | CONFIG_STRINGS=y | ||
789 | # CONFIG_TIME is not set | ||
790 | # CONFIG_TTYSIZE is not set | ||
791 | # CONFIG_UBIRENAME is not set | ||
792 | # CONFIG_UBIATTACH is not set | ||
793 | # CONFIG_UBIDETACH is not set | ||
794 | # CONFIG_UBIMKVOL is not set | ||
795 | # CONFIG_UBIRMVOL is not set | ||
796 | # CONFIG_UBIRSVOL is not set | ||
797 | # CONFIG_UBIUPDATEVOL is not set | ||
798 | # CONFIG_VOLNAME is not set | ||
799 | # CONFIG_WATCHDOG is not set | ||
800 | |||
801 | # | ||
802 | # Networking Utilities | ||
803 | # | ||
804 | CONFIG_FEATURE_IPV6=y | ||
805 | # CONFIG_FEATURE_UNIX_LOCAL is not set | ||
806 | CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y | ||
807 | # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set | ||
808 | # CONFIG_ARP is not set | ||
809 | # CONFIG_ARPING is not set | ||
810 | # CONFIG_BRCTL is not set | ||
811 | # CONFIG_FEATURE_BRCTL_FANCY is not set | ||
812 | # CONFIG_FEATURE_BRCTL_SHOW is not set | ||
813 | # CONFIG_DNSD is not set | ||
814 | # CONFIG_ETHER_WAKE is not set | ||
815 | # CONFIG_FTPD is not set | ||
816 | # CONFIG_FEATURE_FTPD_WRITE is not set | ||
817 | # CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set | ||
818 | # CONFIG_FEATURE_FTPD_AUTHENTICATION is not set | ||
819 | CONFIG_FTPGET=y | ||
820 | CONFIG_FTPPUT=y | ||
821 | CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y | ||
822 | # CONFIG_HOSTNAME is not set | ||
823 | # CONFIG_DNSDOMAINNAME is not set | ||
824 | # CONFIG_HTTPD is not set | ||
825 | # CONFIG_FEATURE_HTTPD_RANGES is not set | ||
826 | # CONFIG_FEATURE_HTTPD_SETUID is not set | ||
827 | # CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set | ||
828 | # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set | ||
829 | # CONFIG_FEATURE_HTTPD_CGI is not set | ||
830 | # CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set | ||
831 | # CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set | ||
832 | # CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set | ||
833 | # CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set | ||
834 | # CONFIG_FEATURE_HTTPD_PROXY is not set | ||
835 | # CONFIG_FEATURE_HTTPD_GZIP is not set | ||
836 | # CONFIG_IFCONFIG is not set | ||
837 | # CONFIG_FEATURE_IFCONFIG_STATUS is not set | ||
838 | # CONFIG_FEATURE_IFCONFIG_SLIP is not set | ||
839 | # CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set | ||
840 | # CONFIG_FEATURE_IFCONFIG_HW is not set | ||
841 | # CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set | ||
842 | # CONFIG_IFENSLAVE is not set | ||
843 | # CONFIG_IFPLUGD is not set | ||
844 | # CONFIG_IFUP is not set | ||
845 | # CONFIG_IFDOWN is not set | ||
846 | CONFIG_IFUPDOWN_IFSTATE_PATH="" | ||
847 | # CONFIG_FEATURE_IFUPDOWN_IP is not set | ||
848 | # CONFIG_FEATURE_IFUPDOWN_IPV4 is not set | ||
849 | # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set | ||
850 | # CONFIG_FEATURE_IFUPDOWN_MAPPING is not set | ||
851 | # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set | ||
852 | # CONFIG_INETD is not set | ||
853 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set | ||
854 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set | ||
855 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set | ||
856 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set | ||
857 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set | ||
858 | # CONFIG_FEATURE_INETD_RPC is not set | ||
859 | # CONFIG_IP is not set | ||
860 | # CONFIG_IPADDR is not set | ||
861 | # CONFIG_IPLINK is not set | ||
862 | # CONFIG_IPROUTE is not set | ||
863 | # CONFIG_IPTUNNEL is not set | ||
864 | # CONFIG_IPRULE is not set | ||
865 | # CONFIG_IPNEIGH is not set | ||
866 | # CONFIG_FEATURE_IP_ADDRESS is not set | ||
867 | # CONFIG_FEATURE_IP_LINK is not set | ||
868 | # CONFIG_FEATURE_IP_ROUTE is not set | ||
869 | CONFIG_FEATURE_IP_ROUTE_DIR="" | ||
870 | # CONFIG_FEATURE_IP_TUNNEL is not set | ||
871 | # CONFIG_FEATURE_IP_RULE is not set | ||
872 | # CONFIG_FEATURE_IP_NEIGH is not set | ||
873 | # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set | ||
874 | CONFIG_IPCALC=y | ||
875 | CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y | ||
876 | CONFIG_FEATURE_IPCALC_FANCY=y | ||
877 | # CONFIG_FAKEIDENTD is not set | ||
878 | # CONFIG_NAMEIF is not set | ||
879 | # CONFIG_FEATURE_NAMEIF_EXTENDED is not set | ||
880 | # CONFIG_NBDCLIENT is not set | ||
881 | CONFIG_NC=y | ||
882 | CONFIG_NC_SERVER=y | ||
883 | # CONFIG_NC_EXTRA is not set | ||
884 | # CONFIG_NC_110_COMPAT is not set | ||
885 | # CONFIG_NETSTAT is not set | ||
886 | # CONFIG_FEATURE_NETSTAT_WIDE is not set | ||
887 | # CONFIG_FEATURE_NETSTAT_PRG is not set | ||
888 | # CONFIG_NSLOOKUP is not set | ||
889 | # CONFIG_NTPD is not set | ||
890 | # CONFIG_FEATURE_NTPD_SERVER is not set | ||
891 | # CONFIG_FEATURE_NTPD_CONF is not set | ||
892 | # CONFIG_PING is not set | ||
893 | # CONFIG_PING6 is not set | ||
894 | # CONFIG_FEATURE_FANCY_PING is not set | ||
895 | # CONFIG_PSCAN is not set | ||
896 | # CONFIG_ROUTE is not set | ||
897 | # CONFIG_SLATTACH is not set | ||
898 | # CONFIG_SSL_CLIENT is not set | ||
899 | # CONFIG_TCPSVD is not set | ||
900 | # CONFIG_UDPSVD is not set | ||
901 | # CONFIG_TELNET is not set | ||
902 | # CONFIG_FEATURE_TELNET_TTYPE is not set | ||
903 | # CONFIG_FEATURE_TELNET_AUTOLOGIN is not set | ||
904 | # CONFIG_FEATURE_TELNET_WIDTH is not set | ||
905 | # CONFIG_TELNETD is not set | ||
906 | # CONFIG_FEATURE_TELNETD_STANDALONE is not set | ||
907 | # CONFIG_FEATURE_TELNETD_INETD_WAIT is not set | ||
908 | # CONFIG_TFTP is not set | ||
909 | # CONFIG_TFTPD is not set | ||
910 | # CONFIG_FEATURE_TFTP_GET is not set | ||
911 | # CONFIG_FEATURE_TFTP_PUT is not set | ||
912 | # CONFIG_FEATURE_TFTP_BLOCKSIZE is not set | ||
913 | # CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set | ||
914 | # CONFIG_TFTP_DEBUG is not set | ||
915 | # CONFIG_TLS is not set | ||
916 | # CONFIG_TRACEROUTE is not set | ||
917 | # CONFIG_TRACEROUTE6 is not set | ||
918 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set | ||
919 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | ||
920 | # CONFIG_TUNCTL is not set | ||
921 | # CONFIG_FEATURE_TUNCTL_UG is not set | ||
922 | # CONFIG_VCONFIG is not set | ||
923 | CONFIG_WGET=y | ||
924 | CONFIG_FEATURE_WGET_LONG_OPTIONS=y | ||
925 | # CONFIG_FEATURE_WGET_STATUSBAR is not set | ||
926 | # CONFIG_FEATURE_WGET_AUTHENTICATION is not set | ||
927 | # CONFIG_FEATURE_WGET_TIMEOUT is not set | ||
928 | # CONFIG_FEATURE_WGET_HTTPS is not set | ||
929 | # CONFIG_FEATURE_WGET_OPENSSL is not set | ||
930 | CONFIG_WHOIS=y | ||
931 | # CONFIG_ZCIP is not set | ||
932 | # CONFIG_UDHCPC6 is not set | ||
933 | # CONFIG_FEATURE_UDHCPC6_RFC3646 is not set | ||
934 | # CONFIG_FEATURE_UDHCPC6_RFC4704 is not set | ||
935 | # CONFIG_FEATURE_UDHCPC6_RFC4833 is not set | ||
936 | # CONFIG_UDHCPD is not set | ||
937 | # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set | ||
938 | # CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set | ||
939 | CONFIG_DHCPD_LEASES_FILE="" | ||
940 | # CONFIG_DUMPLEASES is not set | ||
941 | # CONFIG_DHCPRELAY is not set | ||
942 | # CONFIG_UDHCPC is not set | ||
943 | # CONFIG_FEATURE_UDHCPC_ARPING is not set | ||
944 | # CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set | ||
945 | CONFIG_UDHCPC_DEFAULT_SCRIPT="" | ||
946 | # CONFIG_FEATURE_UDHCP_PORT is not set | ||
947 | CONFIG_UDHCP_DEBUG=0 | ||
948 | # CONFIG_FEATURE_UDHCP_RFC3397 is not set | ||
949 | # CONFIG_FEATURE_UDHCP_8021Q is not set | ||
950 | CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 | ||
951 | CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" | ||
952 | |||
953 | # | ||
954 | # Print Utilities | ||
955 | # | ||
956 | # CONFIG_LPD is not set | ||
957 | # CONFIG_LPR is not set | ||
958 | # CONFIG_LPQ is not set | ||
959 | |||
960 | # | ||
961 | # Mail Utilities | ||
962 | # | ||
963 | # CONFIG_MAKEMIME is not set | ||
964 | # CONFIG_POPMAILDIR is not set | ||
965 | # CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set | ||
966 | # CONFIG_REFORMIME is not set | ||
967 | # CONFIG_FEATURE_REFORMIME_COMPAT is not set | ||
968 | # CONFIG_SENDMAIL is not set | ||
969 | CONFIG_FEATURE_MIME_CHARSET="" | ||
970 | |||
971 | # | ||
972 | # Process Utilities | ||
973 | # | ||
974 | # CONFIG_FREE is not set | ||
975 | # CONFIG_FUSER is not set | ||
976 | # CONFIG_IOSTAT is not set | ||
977 | CONFIG_KILL=y | ||
978 | CONFIG_KILLALL=y | ||
979 | # CONFIG_KILLALL5 is not set | ||
980 | # CONFIG_LSOF is not set | ||
981 | # CONFIG_MPSTAT is not set | ||
982 | # CONFIG_NMETER is not set | ||
983 | CONFIG_PGREP=y | ||
984 | # CONFIG_PKILL is not set | ||
985 | CONFIG_PIDOF=y | ||
986 | CONFIG_FEATURE_PIDOF_SINGLE=y | ||
987 | CONFIG_FEATURE_PIDOF_OMIT=y | ||
988 | # CONFIG_PMAP is not set | ||
989 | # CONFIG_POWERTOP is not set | ||
990 | # CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set | ||
991 | CONFIG_PS=y | ||
992 | # CONFIG_FEATURE_PS_WIDE is not set | ||
993 | # CONFIG_FEATURE_PS_LONG is not set | ||
994 | # CONFIG_FEATURE_PS_TIME is not set | ||
995 | # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set | ||
996 | # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set | ||
997 | # CONFIG_PSTREE is not set | ||
998 | # CONFIG_PWDX is not set | ||
999 | # CONFIG_SMEMCAP is not set | ||
1000 | # CONFIG_BB_SYSCTL is not set | ||
1001 | # CONFIG_TOP is not set | ||
1002 | # CONFIG_FEATURE_TOP_INTERACTIVE is not set | ||
1003 | # CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set | ||
1004 | # CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set | ||
1005 | # CONFIG_FEATURE_TOP_SMP_CPU is not set | ||
1006 | # CONFIG_FEATURE_TOP_DECIMALS is not set | ||
1007 | # CONFIG_FEATURE_TOP_SMP_PROCESS is not set | ||
1008 | # CONFIG_FEATURE_TOPMEM is not set | ||
1009 | # CONFIG_UPTIME is not set | ||
1010 | # CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set | ||
1011 | CONFIG_WATCH=y | ||
1012 | # CONFIG_FEATURE_SHOW_THREADS is not set | ||
1013 | |||
1014 | # | ||
1015 | # Runit Utilities | ||
1016 | # | ||
1017 | # CONFIG_CHPST is not set | ||
1018 | # CONFIG_SETUIDGID is not set | ||
1019 | # CONFIG_ENVUIDGID is not set | ||
1020 | # CONFIG_ENVDIR is not set | ||
1021 | # CONFIG_SOFTLIMIT is not set | ||
1022 | # CONFIG_RUNSV is not set | ||
1023 | # CONFIG_RUNSVDIR is not set | ||
1024 | # CONFIG_FEATURE_RUNSVDIR_LOG is not set | ||
1025 | # CONFIG_SV is not set | ||
1026 | CONFIG_SV_DEFAULT_SERVICE_DIR="" | ||
1027 | # CONFIG_SVC is not set | ||
1028 | # CONFIG_SVLOGD is not set | ||
1029 | # CONFIG_CHCON is not set | ||
1030 | # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set | ||
1031 | # CONFIG_GETENFORCE is not set | ||
1032 | # CONFIG_GETSEBOOL is not set | ||
1033 | # CONFIG_LOAD_POLICY is not set | ||
1034 | # CONFIG_MATCHPATHCON is not set | ||
1035 | # CONFIG_RUNCON is not set | ||
1036 | # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set | ||
1037 | # CONFIG_SELINUXENABLED is not set | ||
1038 | # CONFIG_SESTATUS is not set | ||
1039 | # CONFIG_SETENFORCE is not set | ||
1040 | # CONFIG_SETFILES is not set | ||
1041 | # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set | ||
1042 | # CONFIG_RESTORECON is not set | ||
1043 | # CONFIG_SETSEBOOL is not set | ||
1044 | |||
1045 | # | ||
1046 | # Shells | ||
1047 | # | ||
1048 | CONFIG_SH_IS_ASH=y | ||
1049 | # CONFIG_SH_IS_HUSH is not set | ||
1050 | # CONFIG_SH_IS_NONE is not set | ||
1051 | CONFIG_BASH_IS_ASH=y | ||
1052 | # CONFIG_BASH_IS_HUSH is not set | ||
1053 | # CONFIG_BASH_IS_NONE is not set | ||
1054 | CONFIG_ASH=y | ||
1055 | CONFIG_ASH_OPTIMIZE_FOR_SIZE=y | ||
1056 | CONFIG_ASH_INTERNAL_GLOB=y | ||
1057 | CONFIG_ASH_BASH_COMPAT=y | ||
1058 | # CONFIG_ASH_JOB_CONTROL is not set | ||
1059 | CONFIG_ASH_ALIAS=y | ||
1060 | CONFIG_ASH_RANDOM_SUPPORT=y | ||
1061 | CONFIG_ASH_EXPAND_PRMT=y | ||
1062 | # CONFIG_ASH_IDLE_TIMEOUT is not set | ||
1063 | # CONFIG_ASH_MAIL is not set | ||
1064 | CONFIG_ASH_ECHO=y | ||
1065 | CONFIG_ASH_PRINTF=y | ||
1066 | CONFIG_ASH_TEST=y | ||
1067 | CONFIG_ASH_HELP=y | ||
1068 | CONFIG_ASH_GETOPTS=y | ||
1069 | CONFIG_ASH_CMDCMD=y | ||
1070 | CONFIG_ASH_NOCONSOLE=y | ||
1071 | # CONFIG_CTTYHACK is not set | ||
1072 | # CONFIG_HUSH is not set | ||
1073 | # CONFIG_HUSH_BASH_COMPAT is not set | ||
1074 | # CONFIG_HUSH_BRACE_EXPANSION is not set | ||
1075 | # CONFIG_HUSH_INTERACTIVE is not set | ||
1076 | # CONFIG_HUSH_SAVEHISTORY is not set | ||
1077 | # CONFIG_HUSH_JOB is not set | ||
1078 | # CONFIG_HUSH_TICK is not set | ||
1079 | # CONFIG_HUSH_IF is not set | ||
1080 | # CONFIG_HUSH_LOOPS is not set | ||
1081 | # CONFIG_HUSH_CASE is not set | ||
1082 | # CONFIG_HUSH_FUNCTIONS is not set | ||
1083 | # CONFIG_HUSH_LOCAL is not set | ||
1084 | # CONFIG_HUSH_RANDOM_SUPPORT is not set | ||
1085 | # CONFIG_HUSH_MODE_X is not set | ||
1086 | # CONFIG_HUSH_ECHO is not set | ||
1087 | # CONFIG_HUSH_PRINTF is not set | ||
1088 | # CONFIG_HUSH_TEST is not set | ||
1089 | # CONFIG_HUSH_HELP is not set | ||
1090 | # CONFIG_HUSH_EXPORT is not set | ||
1091 | # CONFIG_HUSH_EXPORT_N is not set | ||
1092 | # CONFIG_HUSH_READONLY is not set | ||
1093 | # CONFIG_HUSH_KILL is not set | ||
1094 | # CONFIG_HUSH_WAIT is not set | ||
1095 | # CONFIG_HUSH_TRAP is not set | ||
1096 | # CONFIG_HUSH_TYPE is not set | ||
1097 | # CONFIG_HUSH_READ is not set | ||
1098 | # CONFIG_HUSH_SET is not set | ||
1099 | # CONFIG_HUSH_UNSET is not set | ||
1100 | # CONFIG_HUSH_ULIMIT is not set | ||
1101 | # CONFIG_HUSH_UMASK is not set | ||
1102 | # CONFIG_HUSH_MEMLEAK is not set | ||
1103 | |||
1104 | # | ||
1105 | # Options common to all shells | ||
1106 | # | ||
1107 | CONFIG_FEATURE_SH_MATH=y | ||
1108 | CONFIG_FEATURE_SH_MATH_64=y | ||
1109 | CONFIG_FEATURE_SH_EXTRA_QUIET=y | ||
1110 | CONFIG_FEATURE_SH_STANDALONE=y | ||
1111 | CONFIG_FEATURE_SH_NOFORK=y | ||
1112 | # CONFIG_FEATURE_SH_READ_FRAC is not set | ||
1113 | CONFIG_FEATURE_SH_HISTFILESIZE=y | ||
1114 | |||
1115 | # | ||
1116 | # System Logging Utilities | ||
1117 | # | ||
1118 | # CONFIG_KLOGD is not set | ||
1119 | # CONFIG_FEATURE_KLOGD_KLOGCTL is not set | ||
1120 | # CONFIG_LOGGER is not set | ||
1121 | # CONFIG_LOGREAD is not set | ||
1122 | # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set | ||
1123 | # CONFIG_SYSLOGD is not set | ||
1124 | # CONFIG_FEATURE_ROTATE_LOGFILE is not set | ||
1125 | # CONFIG_FEATURE_REMOTE_LOG is not set | ||
1126 | # CONFIG_FEATURE_SYSLOGD_DUP is not set | ||
1127 | # CONFIG_FEATURE_SYSLOGD_CFG is not set | ||
1128 | CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 | ||
1129 | # CONFIG_FEATURE_IPC_SYSLOG is not set | ||
1130 | CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 | ||
1131 | # CONFIG_FEATURE_KMSG_SYSLOG is not set | ||
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig new file mode 100644 index 000000000..decb93015 --- /dev/null +++ b/configs/mingw64_defconfig | |||
@@ -0,0 +1,1131 @@ | |||
1 | # | ||
2 | # Automatically generated make config: don't edit | ||
3 | # Busybox version: 1.28.0.git | ||
4 | # Mon Jul 24 11:51:22 2017 | ||
5 | # | ||
6 | CONFIG_HAVE_DOT_CONFIG=y | ||
7 | # CONFIG_PLATFORM_POSIX is not set | ||
8 | CONFIG_PLATFORM_MINGW32=y | ||
9 | |||
10 | # | ||
11 | # Busybox Settings | ||
12 | # | ||
13 | CONFIG_DESKTOP=y | ||
14 | # CONFIG_EXTRA_COMPAT is not set | ||
15 | # CONFIG_INCLUDE_SUSv2 is not set | ||
16 | CONFIG_LONG_OPTS=y | ||
17 | CONFIG_SHOW_USAGE=y | ||
18 | CONFIG_FEATURE_VERBOSE_USAGE=y | ||
19 | CONFIG_FEATURE_COMPRESS_USAGE=y | ||
20 | CONFIG_LFS=y | ||
21 | # CONFIG_GLOBBING is not set | ||
22 | CONFIG_SAFE_ENV=y | ||
23 | # CONFIG_PAM is not set | ||
24 | # CONFIG_FEATURE_DEVPTS is not set | ||
25 | # CONFIG_FEATURE_UTMP is not set | ||
26 | # CONFIG_FEATURE_WTMP is not set | ||
27 | # CONFIG_FEATURE_PIDFILE is not set | ||
28 | CONFIG_PID_FILE_PATH="" | ||
29 | CONFIG_BUSYBOX=y | ||
30 | CONFIG_FEATURE_INSTALLER=y | ||
31 | # CONFIG_INSTALL_NO_USR is not set | ||
32 | # CONFIG_FEATURE_SUID is not set | ||
33 | # CONFIG_FEATURE_SUID_CONFIG is not set | ||
34 | # CONFIG_FEATURE_SUID_CONFIG_QUIET is not set | ||
35 | CONFIG_FEATURE_PREFER_APPLETS=y | ||
36 | CONFIG_BUSYBOX_EXEC_PATH="" | ||
37 | # CONFIG_SELINUX is not set | ||
38 | # CONFIG_FEATURE_CLEAN_UP is not set | ||
39 | # CONFIG_FEATURE_SYSLOG is not set | ||
40 | # CONFIG_FEATURE_HAVE_RPC is not set | ||
41 | # CONFIG_PLATFORM_LINUX is not set | ||
42 | |||
43 | # | ||
44 | # Build Options | ||
45 | # | ||
46 | # CONFIG_STATIC is not set | ||
47 | # CONFIG_PIE is not set | ||
48 | # CONFIG_NOMMU is not set | ||
49 | # CONFIG_BUILD_LIBBUSYBOX is not set | ||
50 | # CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set | ||
51 | # CONFIG_FEATURE_INDIVIDUAL is not set | ||
52 | # CONFIG_FEATURE_SHARED_BUSYBOX is not set | ||
53 | CONFIG_CROSS_COMPILER_PREFIX="x86_64-w64-mingw32-" | ||
54 | CONFIG_SYSROOT="" | ||
55 | CONFIG_EXTRA_CFLAGS="-funwind-tables -fasynchronous-unwind-tables" | ||
56 | CONFIG_EXTRA_LDFLAGS="" | ||
57 | CONFIG_EXTRA_LDLIBS="" | ||
58 | CONFIG_USE_PORTABLE_CODE=y | ||
59 | |||
60 | # | ||
61 | # Installation Options ("make install" behavior) | ||
62 | # | ||
63 | CONFIG_INSTALL_APPLET_SYMLINKS=y | ||
64 | # CONFIG_INSTALL_APPLET_HARDLINKS is not set | ||
65 | # CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set | ||
66 | # CONFIG_INSTALL_APPLET_DONT is not set | ||
67 | # CONFIG_INSTALL_SH_APPLET_SYMLINK is not set | ||
68 | # CONFIG_INSTALL_SH_APPLET_HARDLINK is not set | ||
69 | # CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set | ||
70 | CONFIG_PREFIX="" | ||
71 | |||
72 | # | ||
73 | # Debugging Options | ||
74 | # | ||
75 | CONFIG_DEBUG=y | ||
76 | # CONFIG_DEBUG_PESSIMIZE is not set | ||
77 | # CONFIG_DEBUG_SANITIZE is not set | ||
78 | # CONFIG_UNIT_TEST is not set | ||
79 | # CONFIG_WERROR is not set | ||
80 | CONFIG_NO_DEBUG_LIB=y | ||
81 | # CONFIG_DMALLOC is not set | ||
82 | # CONFIG_EFENCE is not set | ||
83 | |||
84 | # | ||
85 | # Library Tuning | ||
86 | # | ||
87 | # CONFIG_FEATURE_USE_BSS_TAIL is not set | ||
88 | CONFIG_FEATURE_RTMINMAX=y | ||
89 | CONFIG_FEATURE_BUFFERS_USE_MALLOC=y | ||
90 | # CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set | ||
91 | # CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set | ||
92 | CONFIG_PASSWORD_MINLEN=6 | ||
93 | CONFIG_MD5_SMALL=1 | ||
94 | CONFIG_SHA3_SMALL=1 | ||
95 | # CONFIG_FEATURE_FAST_TOP is not set | ||
96 | # CONFIG_FEATURE_ETC_NETWORKS is not set | ||
97 | CONFIG_FEATURE_EDITING=y | ||
98 | CONFIG_FEATURE_EDITING_MAX_LEN=1024 | ||
99 | CONFIG_FEATURE_EDITING_VI=y | ||
100 | CONFIG_FEATURE_EDITING_HISTORY=255 | ||
101 | CONFIG_FEATURE_EDITING_SAVEHISTORY=y | ||
102 | # CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set | ||
103 | CONFIG_FEATURE_REVERSE_SEARCH=y | ||
104 | CONFIG_FEATURE_TAB_COMPLETION=y | ||
105 | CONFIG_FEATURE_USERNAME_COMPLETION=y | ||
106 | CONFIG_FEATURE_EDITING_FANCY_PROMPT=y | ||
107 | # CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set | ||
108 | # CONFIG_LOCALE_SUPPORT is not set | ||
109 | # CONFIG_UNICODE_SUPPORT is not set | ||
110 | # CONFIG_UNICODE_USING_LOCALE is not set | ||
111 | # CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set | ||
112 | CONFIG_SUBST_WCHAR=0 | ||
113 | CONFIG_LAST_SUPPORTED_WCHAR=0 | ||
114 | # CONFIG_UNICODE_COMBINING_WCHARS is not set | ||
115 | # CONFIG_UNICODE_WIDE_WCHARS is not set | ||
116 | # CONFIG_UNICODE_BIDI_SUPPORT is not set | ||
117 | # CONFIG_UNICODE_NEUTRAL_TABLE is not set | ||
118 | # CONFIG_UNICODE_PRESERVE_BROKEN is not set | ||
119 | CONFIG_FEATURE_NON_POSIX_CP=y | ||
120 | # CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set | ||
121 | # CONFIG_FEATURE_USE_SENDFILE is not set | ||
122 | CONFIG_FEATURE_COPYBUF_KB=4 | ||
123 | CONFIG_FEATURE_SKIP_ROOTFS=y | ||
124 | # CONFIG_MONOTONIC_SYSCALL is not set | ||
125 | CONFIG_IOCTL_HEX2STR_ERROR=y | ||
126 | # CONFIG_FEATURE_HWIB is not set | ||
127 | |||
128 | # | ||
129 | # Applets | ||
130 | # | ||
131 | |||
132 | # | ||
133 | # Archival Utilities | ||
134 | # | ||
135 | CONFIG_FEATURE_SEAMLESS_XZ=y | ||
136 | CONFIG_FEATURE_SEAMLESS_LZMA=y | ||
137 | CONFIG_FEATURE_SEAMLESS_BZ2=y | ||
138 | CONFIG_FEATURE_SEAMLESS_GZ=y | ||
139 | CONFIG_FEATURE_SEAMLESS_Z=y | ||
140 | CONFIG_AR=y | ||
141 | CONFIG_FEATURE_AR_LONG_FILENAMES=y | ||
142 | CONFIG_FEATURE_AR_CREATE=y | ||
143 | CONFIG_UNCOMPRESS=y | ||
144 | CONFIG_GUNZIP=y | ||
145 | CONFIG_ZCAT=y | ||
146 | CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y | ||
147 | CONFIG_BUNZIP2=y | ||
148 | CONFIG_BZCAT=y | ||
149 | CONFIG_UNLZMA=y | ||
150 | CONFIG_LZCAT=y | ||
151 | CONFIG_LZMA=y | ||
152 | CONFIG_UNXZ=y | ||
153 | CONFIG_XZCAT=y | ||
154 | CONFIG_XZ=y | ||
155 | CONFIG_BZIP2=y | ||
156 | CONFIG_FEATURE_BZIP2_DECOMPRESS=y | ||
157 | CONFIG_CPIO=y | ||
158 | CONFIG_FEATURE_CPIO_O=y | ||
159 | CONFIG_FEATURE_CPIO_P=y | ||
160 | # CONFIG_DPKG is not set | ||
161 | CONFIG_DPKG_DEB=y | ||
162 | CONFIG_GZIP=y | ||
163 | CONFIG_FEATURE_GZIP_LONG_OPTIONS=y | ||
164 | CONFIG_GZIP_FAST=2 | ||
165 | CONFIG_FEATURE_GZIP_LEVELS=y | ||
166 | CONFIG_FEATURE_GZIP_DECOMPRESS=y | ||
167 | CONFIG_LZOP=y | ||
168 | CONFIG_UNLZOP=y | ||
169 | CONFIG_LZOPCAT=y | ||
170 | # CONFIG_LZOP_COMPR_HIGH is not set | ||
171 | CONFIG_RPM2CPIO=y | ||
172 | # CONFIG_RPM is not set | ||
173 | CONFIG_TAR=y | ||
174 | CONFIG_FEATURE_TAR_LONG_OPTIONS=y | ||
175 | CONFIG_FEATURE_TAR_CREATE=y | ||
176 | CONFIG_FEATURE_TAR_AUTODETECT=y | ||
177 | CONFIG_FEATURE_TAR_FROM=y | ||
178 | CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y | ||
179 | # CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set | ||
180 | CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y | ||
181 | # CONFIG_FEATURE_TAR_TO_COMMAND is not set | ||
182 | # CONFIG_FEATURE_TAR_UNAME_GNAME is not set | ||
183 | CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y | ||
184 | # CONFIG_FEATURE_TAR_SELINUX is not set | ||
185 | CONFIG_UNZIP=y | ||
186 | CONFIG_FEATURE_UNZIP_CDF=y | ||
187 | CONFIG_FEATURE_UNZIP_BZIP2=y | ||
188 | CONFIG_FEATURE_UNZIP_LZMA=y | ||
189 | CONFIG_FEATURE_UNZIP_XZ=y | ||
190 | CONFIG_FEATURE_LZMA_FAST=y | ||
191 | |||
192 | # | ||
193 | # Coreutils | ||
194 | # | ||
195 | CONFIG_BASENAME=y | ||
196 | CONFIG_CAT=y | ||
197 | CONFIG_FEATURE_CATN=y | ||
198 | CONFIG_FEATURE_CATV=y | ||
199 | # CONFIG_CHGRP is not set | ||
200 | CONFIG_CHMOD=y | ||
201 | # CONFIG_CHOWN is not set | ||
202 | # CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set | ||
203 | # CONFIG_CHROOT is not set | ||
204 | CONFIG_CKSUM=y | ||
205 | CONFIG_COMM=y | ||
206 | CONFIG_CP=y | ||
207 | CONFIG_FEATURE_CP_LONG_OPTIONS=y | ||
208 | CONFIG_CUT=y | ||
209 | CONFIG_DATE=y | ||
210 | CONFIG_FEATURE_DATE_ISOFMT=y | ||
211 | # CONFIG_FEATURE_DATE_NANO is not set | ||
212 | CONFIG_FEATURE_DATE_COMPAT=y | ||
213 | CONFIG_DD=y | ||
214 | # CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set | ||
215 | # CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set | ||
216 | CONFIG_FEATURE_DD_IBS_OBS=y | ||
217 | CONFIG_FEATURE_DD_STATUS=y | ||
218 | CONFIG_DF=y | ||
219 | # CONFIG_FEATURE_DF_FANCY is not set | ||
220 | CONFIG_DIRNAME=y | ||
221 | CONFIG_DOS2UNIX=y | ||
222 | CONFIG_UNIX2DOS=y | ||
223 | CONFIG_DU=y | ||
224 | CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y | ||
225 | CONFIG_ECHO=y | ||
226 | CONFIG_FEATURE_FANCY_ECHO=y | ||
227 | CONFIG_ENV=y | ||
228 | CONFIG_FEATURE_ENV_LONG_OPTIONS=y | ||
229 | CONFIG_EXPAND=y | ||
230 | CONFIG_FEATURE_EXPAND_LONG_OPTIONS=y | ||
231 | CONFIG_UNEXPAND=y | ||
232 | CONFIG_FEATURE_UNEXPAND_LONG_OPTIONS=y | ||
233 | CONFIG_EXPR=y | ||
234 | CONFIG_EXPR_MATH_SUPPORT_64=y | ||
235 | CONFIG_FACTOR=y | ||
236 | CONFIG_FALSE=y | ||
237 | CONFIG_FOLD=y | ||
238 | # CONFIG_FSYNC is not set | ||
239 | CONFIG_HEAD=y | ||
240 | CONFIG_FEATURE_FANCY_HEAD=y | ||
241 | # CONFIG_HOSTID is not set | ||
242 | CONFIG_ID=y | ||
243 | CONFIG_GROUPS=y | ||
244 | # CONFIG_INSTALL is not set | ||
245 | # CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set | ||
246 | CONFIG_LINK=y | ||
247 | CONFIG_LN=y | ||
248 | CONFIG_LOGNAME=y | ||
249 | CONFIG_LS=y | ||
250 | CONFIG_FEATURE_LS_FILETYPES=y | ||
251 | CONFIG_FEATURE_LS_FOLLOWLINKS=y | ||
252 | CONFIG_FEATURE_LS_RECURSIVE=y | ||
253 | CONFIG_FEATURE_LS_WIDTH=y | ||
254 | CONFIG_FEATURE_LS_SORTFILES=y | ||
255 | CONFIG_FEATURE_LS_TIMESTAMPS=y | ||
256 | CONFIG_FEATURE_LS_USERNAME=y | ||
257 | CONFIG_FEATURE_LS_COLOR=y | ||
258 | CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y | ||
259 | CONFIG_MD5SUM=y | ||
260 | CONFIG_SHA1SUM=y | ||
261 | CONFIG_SHA256SUM=y | ||
262 | CONFIG_SHA512SUM=y | ||
263 | CONFIG_SHA3SUM=y | ||
264 | |||
265 | # | ||
266 | # Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum | ||
267 | # | ||
268 | CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y | ||
269 | CONFIG_MKDIR=y | ||
270 | CONFIG_FEATURE_MKDIR_LONG_OPTIONS=y | ||
271 | # CONFIG_MKFIFO is not set | ||
272 | # CONFIG_MKNOD is not set | ||
273 | CONFIG_MKTEMP=y | ||
274 | CONFIG_MV=y | ||
275 | CONFIG_FEATURE_MV_LONG_OPTIONS=y | ||
276 | # CONFIG_NICE is not set | ||
277 | CONFIG_NL=y | ||
278 | # CONFIG_NOHUP is not set | ||
279 | # CONFIG_NPROC is not set | ||
280 | CONFIG_OD=y | ||
281 | CONFIG_PASTE=y | ||
282 | CONFIG_PRINTENV=y | ||
283 | CONFIG_PRINTF=y | ||
284 | CONFIG_PWD=y | ||
285 | # CONFIG_READLINK is not set | ||
286 | # CONFIG_FEATURE_READLINK_FOLLOW is not set | ||
287 | # CONFIG_REALPATH is not set | ||
288 | CONFIG_RM=y | ||
289 | CONFIG_RMDIR=y | ||
290 | CONFIG_FEATURE_RMDIR_LONG_OPTIONS=y | ||
291 | CONFIG_SEQ=y | ||
292 | # CONFIG_SHRED is not set | ||
293 | CONFIG_SHUF=y | ||
294 | CONFIG_SLEEP=y | ||
295 | CONFIG_FEATURE_FANCY_SLEEP=y | ||
296 | CONFIG_FEATURE_FLOAT_SLEEP=y | ||
297 | CONFIG_SORT=y | ||
298 | CONFIG_FEATURE_SORT_BIG=y | ||
299 | CONFIG_SPLIT=y | ||
300 | CONFIG_FEATURE_SPLIT_FANCY=y | ||
301 | CONFIG_STAT=y | ||
302 | CONFIG_FEATURE_STAT_FORMAT=y | ||
303 | CONFIG_FEATURE_STAT_FILESYSTEM=y | ||
304 | # CONFIG_STTY is not set | ||
305 | CONFIG_SUM=y | ||
306 | # CONFIG_SYNC is not set | ||
307 | # CONFIG_FEATURE_SYNC_FANCY is not set | ||
308 | CONFIG_TAC=y | ||
309 | CONFIG_TAIL=y | ||
310 | CONFIG_FEATURE_FANCY_TAIL=y | ||
311 | CONFIG_TEE=y | ||
312 | CONFIG_FEATURE_TEE_USE_BLOCK_IO=y | ||
313 | CONFIG_TEST=y | ||
314 | CONFIG_TEST1=y | ||
315 | CONFIG_TEST2=y | ||
316 | CONFIG_FEATURE_TEST_64=y | ||
317 | # CONFIG_TIMEOUT is not set | ||
318 | CONFIG_TOUCH=y | ||
319 | # CONFIG_FEATURE_TOUCH_NODEREF is not set | ||
320 | CONFIG_FEATURE_TOUCH_SUSV3=y | ||
321 | CONFIG_TR=y | ||
322 | CONFIG_FEATURE_TR_CLASSES=y | ||
323 | CONFIG_FEATURE_TR_EQUIV=y | ||
324 | CONFIG_TRUE=y | ||
325 | CONFIG_TRUNCATE=y | ||
326 | # CONFIG_TTY is not set | ||
327 | CONFIG_UNAME=y | ||
328 | CONFIG_UNAME_OSNAME="MS/Windows" | ||
329 | CONFIG_BB_ARCH=y | ||
330 | CONFIG_UNIQ=y | ||
331 | CONFIG_UNLINK=y | ||
332 | CONFIG_USLEEP=y | ||
333 | CONFIG_UUDECODE=y | ||
334 | CONFIG_BASE64=y | ||
335 | CONFIG_UUENCODE=y | ||
336 | CONFIG_WC=y | ||
337 | CONFIG_FEATURE_WC_LARGE=y | ||
338 | CONFIG_WHOAMI=y | ||
339 | # CONFIG_WHO is not set | ||
340 | # CONFIG_W is not set | ||
341 | # CONFIG_USERS is not set | ||
342 | CONFIG_YES=y | ||
343 | |||
344 | # | ||
345 | # Common options | ||
346 | # | ||
347 | CONFIG_FEATURE_VERBOSE=y | ||
348 | |||
349 | # | ||
350 | # Common options for cp and mv | ||
351 | # | ||
352 | # CONFIG_FEATURE_PRESERVE_HARDLINKS is not set | ||
353 | |||
354 | # | ||
355 | # Common options for df, du, ls | ||
356 | # | ||
357 | CONFIG_FEATURE_HUMAN_READABLE=y | ||
358 | |||
359 | # | ||
360 | # Console Utilities | ||
361 | # | ||
362 | # CONFIG_CHVT is not set | ||
363 | CONFIG_CLEAR=y | ||
364 | # CONFIG_DEALLOCVT is not set | ||
365 | # CONFIG_DUMPKMAP is not set | ||
366 | # CONFIG_FGCONSOLE is not set | ||
367 | # CONFIG_KBD_MODE is not set | ||
368 | # CONFIG_LOADFONT is not set | ||
369 | # CONFIG_SETFONT is not set | ||
370 | # CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set | ||
371 | CONFIG_DEFAULT_SETFONT_DIR="" | ||
372 | # CONFIG_FEATURE_LOADFONT_PSF2 is not set | ||
373 | # CONFIG_FEATURE_LOADFONT_RAW is not set | ||
374 | # CONFIG_LOADKMAP is not set | ||
375 | # CONFIG_OPENVT is not set | ||
376 | # CONFIG_RESET is not set | ||
377 | # CONFIG_RESIZE is not set | ||
378 | # CONFIG_FEATURE_RESIZE_PRINT is not set | ||
379 | # CONFIG_SETCONSOLE is not set | ||
380 | # CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set | ||
381 | # CONFIG_SETKEYCODES is not set | ||
382 | # CONFIG_SETLOGCONS is not set | ||
383 | # CONFIG_SHOWKEY is not set | ||
384 | |||
385 | # | ||
386 | # Debian Utilities | ||
387 | # | ||
388 | # CONFIG_PIPE_PROGRESS is not set | ||
389 | # CONFIG_RUN_PARTS is not set | ||
390 | # CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set | ||
391 | # CONFIG_FEATURE_RUN_PARTS_FANCY is not set | ||
392 | # CONFIG_START_STOP_DAEMON is not set | ||
393 | # CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set | ||
394 | # CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set | ||
395 | CONFIG_WHICH=y | ||
396 | |||
397 | # | ||
398 | # Editors | ||
399 | # | ||
400 | CONFIG_AWK=y | ||
401 | CONFIG_FEATURE_AWK_LIBM=y | ||
402 | CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y | ||
403 | CONFIG_CMP=y | ||
404 | CONFIG_DIFF=y | ||
405 | CONFIG_FEATURE_DIFF_LONG_OPTIONS=y | ||
406 | CONFIG_FEATURE_DIFF_DIR=y | ||
407 | CONFIG_ED=y | ||
408 | CONFIG_PATCH=y | ||
409 | CONFIG_SED=y | ||
410 | CONFIG_VI=y | ||
411 | CONFIG_FEATURE_VI_MAX_LEN=4096 | ||
412 | CONFIG_FEATURE_VI_8BIT=y | ||
413 | CONFIG_FEATURE_VI_COLON=y | ||
414 | CONFIG_FEATURE_VI_YANKMARK=y | ||
415 | CONFIG_FEATURE_VI_SEARCH=y | ||
416 | # CONFIG_FEATURE_VI_REGEX_SEARCH is not set | ||
417 | # CONFIG_FEATURE_VI_USE_SIGNALS is not set | ||
418 | CONFIG_FEATURE_VI_DOT_CMD=y | ||
419 | CONFIG_FEATURE_VI_READONLY=y | ||
420 | CONFIG_FEATURE_VI_SETOPTS=y | ||
421 | CONFIG_FEATURE_VI_SET=y | ||
422 | CONFIG_FEATURE_VI_WIN_RESIZE=y | ||
423 | CONFIG_FEATURE_VI_ASK_TERMINAL=y | ||
424 | CONFIG_FEATURE_VI_UNDO=y | ||
425 | CONFIG_FEATURE_VI_UNDO_QUEUE=y | ||
426 | CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256 | ||
427 | CONFIG_FEATURE_ALLOW_EXEC=y | ||
428 | |||
429 | # | ||
430 | # Finding Utilities | ||
431 | # | ||
432 | CONFIG_FIND=y | ||
433 | CONFIG_FEATURE_FIND_PRINT0=y | ||
434 | CONFIG_FEATURE_FIND_MTIME=y | ||
435 | CONFIG_FEATURE_FIND_MMIN=y | ||
436 | CONFIG_FEATURE_FIND_PERM=y | ||
437 | CONFIG_FEATURE_FIND_TYPE=y | ||
438 | # CONFIG_FEATURE_FIND_XDEV is not set | ||
439 | CONFIG_FEATURE_FIND_MAXDEPTH=y | ||
440 | CONFIG_FEATURE_FIND_NEWER=y | ||
441 | # CONFIG_FEATURE_FIND_INUM is not set | ||
442 | # CONFIG_FEATURE_FIND_EXEC is not set | ||
443 | # CONFIG_FEATURE_FIND_EXEC_PLUS is not set | ||
444 | # CONFIG_FEATURE_FIND_USER is not set | ||
445 | # CONFIG_FEATURE_FIND_GROUP is not set | ||
446 | CONFIG_FEATURE_FIND_NOT=y | ||
447 | CONFIG_FEATURE_FIND_DEPTH=y | ||
448 | CONFIG_FEATURE_FIND_PAREN=y | ||
449 | CONFIG_FEATURE_FIND_SIZE=y | ||
450 | CONFIG_FEATURE_FIND_PRUNE=y | ||
451 | CONFIG_FEATURE_FIND_DELETE=y | ||
452 | CONFIG_FEATURE_FIND_PATH=y | ||
453 | CONFIG_FEATURE_FIND_REGEX=y | ||
454 | # CONFIG_FEATURE_FIND_CONTEXT is not set | ||
455 | # CONFIG_FEATURE_FIND_LINKS is not set | ||
456 | CONFIG_GREP=y | ||
457 | CONFIG_EGREP=y | ||
458 | CONFIG_FGREP=y | ||
459 | CONFIG_FEATURE_GREP_CONTEXT=y | ||
460 | CONFIG_XARGS=y | ||
461 | CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y | ||
462 | CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y | ||
463 | CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y | ||
464 | CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y | ||
465 | CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y | ||
466 | |||
467 | # | ||
468 | # Init Utilities | ||
469 | # | ||
470 | # CONFIG_BOOTCHARTD is not set | ||
471 | # CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set | ||
472 | # CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set | ||
473 | # CONFIG_HALT is not set | ||
474 | # CONFIG_POWEROFF is not set | ||
475 | # CONFIG_REBOOT is not set | ||
476 | # CONFIG_FEATURE_CALL_TELINIT is not set | ||
477 | CONFIG_TELINIT_PATH="" | ||
478 | # CONFIG_INIT is not set | ||
479 | # CONFIG_LINUXRC is not set | ||
480 | # CONFIG_FEATURE_USE_INITTAB is not set | ||
481 | # CONFIG_FEATURE_KILL_REMOVED is not set | ||
482 | CONFIG_FEATURE_KILL_DELAY=0 | ||
483 | # CONFIG_FEATURE_INIT_SCTTY is not set | ||
484 | # CONFIG_FEATURE_INIT_SYSLOG is not set | ||
485 | # CONFIG_FEATURE_INIT_QUIET is not set | ||
486 | # CONFIG_FEATURE_INIT_COREDUMPS is not set | ||
487 | CONFIG_INIT_TERMINAL_TYPE="" | ||
488 | # CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set | ||
489 | |||
490 | # | ||
491 | # Login/Password Management Utilities | ||
492 | # | ||
493 | # CONFIG_FEATURE_SHADOWPASSWDS is not set | ||
494 | # CONFIG_USE_BB_PWD_GRP is not set | ||
495 | # CONFIG_USE_BB_SHADOW is not set | ||
496 | # CONFIG_USE_BB_CRYPT is not set | ||
497 | # CONFIG_USE_BB_CRYPT_SHA is not set | ||
498 | # CONFIG_ADDGROUP is not set | ||
499 | # CONFIG_FEATURE_ADDGROUP_LONG_OPTIONS is not set | ||
500 | # CONFIG_FEATURE_ADDUSER_TO_GROUP is not set | ||
501 | # CONFIG_ADD_SHELL is not set | ||
502 | # CONFIG_REMOVE_SHELL is not set | ||
503 | # CONFIG_ADDUSER is not set | ||
504 | # CONFIG_FEATURE_ADDUSER_LONG_OPTIONS is not set | ||
505 | # CONFIG_FEATURE_CHECK_NAMES is not set | ||
506 | CONFIG_LAST_ID=0 | ||
507 | CONFIG_FIRST_SYSTEM_ID=0 | ||
508 | CONFIG_LAST_SYSTEM_ID=0 | ||
509 | # CONFIG_CHPASSWD is not set | ||
510 | CONFIG_FEATURE_DEFAULT_PASSWD_ALGO="" | ||
511 | # CONFIG_CRYPTPW is not set | ||
512 | # CONFIG_MKPASSWD is not set | ||
513 | # CONFIG_DELUSER is not set | ||
514 | # CONFIG_DELGROUP is not set | ||
515 | # CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set | ||
516 | # CONFIG_GETTY is not set | ||
517 | # CONFIG_LOGIN is not set | ||
518 | # CONFIG_LOGIN_SESSION_AS_CHILD is not set | ||
519 | # CONFIG_LOGIN_SCRIPTS is not set | ||
520 | # CONFIG_FEATURE_NOLOGIN is not set | ||
521 | # CONFIG_FEATURE_SECURETTY is not set | ||
522 | # CONFIG_PASSWD is not set | ||
523 | # CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set | ||
524 | # CONFIG_SU is not set | ||
525 | # CONFIG_FEATURE_SU_SYSLOG is not set | ||
526 | # CONFIG_FEATURE_SU_CHECKS_SHELLS is not set | ||
527 | # CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set | ||
528 | # CONFIG_SULOGIN is not set | ||
529 | # CONFIG_VLOCK is not set | ||
530 | |||
531 | # | ||
532 | # Linux Ext2 FS Progs | ||
533 | # | ||
534 | # CONFIG_CHATTR is not set | ||
535 | # CONFIG_FSCK is not set | ||
536 | # CONFIG_LSATTR is not set | ||
537 | # CONFIG_TUNE2FS is not set | ||
538 | |||
539 | # | ||
540 | # Linux Module Utilities | ||
541 | # | ||
542 | # CONFIG_MODPROBE_SMALL is not set | ||
543 | # CONFIG_DEPMOD is not set | ||
544 | # CONFIG_INSMOD is not set | ||
545 | # CONFIG_LSMOD is not set | ||
546 | # CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set | ||
547 | # CONFIG_MODINFO is not set | ||
548 | # CONFIG_MODPROBE is not set | ||
549 | # CONFIG_FEATURE_MODPROBE_BLACKLIST is not set | ||
550 | # CONFIG_RMMOD is not set | ||
551 | |||
552 | # | ||
553 | # Options common to multiple modutils | ||
554 | # | ||
555 | # CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set | ||
556 | # CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set | ||
557 | # CONFIG_FEATURE_2_4_MODULES is not set | ||
558 | # CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set | ||
559 | # CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set | ||
560 | # CONFIG_FEATURE_INSMOD_LOADINKMEM is not set | ||
561 | # CONFIG_FEATURE_INSMOD_LOAD_MAP is not set | ||
562 | # CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set | ||
563 | # CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set | ||
564 | # CONFIG_FEATURE_INSMOD_TRY_MMAP is not set | ||
565 | # CONFIG_FEATURE_MODUTILS_ALIAS is not set | ||
566 | # CONFIG_FEATURE_MODUTILS_SYMBOLS is not set | ||
567 | CONFIG_DEFAULT_MODULES_DIR="" | ||
568 | CONFIG_DEFAULT_DEPMOD_FILE="" | ||
569 | |||
570 | # | ||
571 | # Linux System Utilities | ||
572 | # | ||
573 | # CONFIG_ACPID is not set | ||
574 | # CONFIG_FEATURE_ACPID_COMPAT is not set | ||
575 | # CONFIG_BLKDISCARD is not set | ||
576 | # CONFIG_BLKID is not set | ||
577 | # CONFIG_FEATURE_BLKID_TYPE is not set | ||
578 | # CONFIG_BLOCKDEV is not set | ||
579 | CONFIG_CAL=y | ||
580 | # CONFIG_CHRT is not set | ||
581 | # CONFIG_DMESG is not set | ||
582 | # CONFIG_FEATURE_DMESG_PRETTY is not set | ||
583 | # CONFIG_EJECT is not set | ||
584 | # CONFIG_FEATURE_EJECT_SCSI is not set | ||
585 | # CONFIG_FALLOCATE is not set | ||
586 | # CONFIG_FATATTR is not set | ||
587 | # CONFIG_FBSET is not set | ||
588 | # CONFIG_FEATURE_FBSET_FANCY is not set | ||
589 | # CONFIG_FEATURE_FBSET_READMODE is not set | ||
590 | # CONFIG_FDFORMAT is not set | ||
591 | # CONFIG_FDISK is not set | ||
592 | # CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set | ||
593 | # CONFIG_FEATURE_FDISK_WRITABLE is not set | ||
594 | # CONFIG_FEATURE_AIX_LABEL is not set | ||
595 | # CONFIG_FEATURE_SGI_LABEL is not set | ||
596 | # CONFIG_FEATURE_SUN_LABEL is not set | ||
597 | # CONFIG_FEATURE_OSF_LABEL is not set | ||
598 | # CONFIG_FEATURE_GPT_LABEL is not set | ||
599 | # CONFIG_FEATURE_FDISK_ADVANCED is not set | ||
600 | # CONFIG_FINDFS is not set | ||
601 | # CONFIG_FLOCK is not set | ||
602 | # CONFIG_FDFLUSH is not set | ||
603 | # CONFIG_FREERAMDISK is not set | ||
604 | # CONFIG_FSCK_MINIX is not set | ||
605 | # CONFIG_FSFREEZE is not set | ||
606 | # CONFIG_FSTRIM is not set | ||
607 | CONFIG_GETOPT=y | ||
608 | CONFIG_FEATURE_GETOPT_LONG=y | ||
609 | CONFIG_HEXDUMP=y | ||
610 | CONFIG_FEATURE_HEXDUMP_REVERSE=y | ||
611 | CONFIG_HD=y | ||
612 | CONFIG_XXD=y | ||
613 | # CONFIG_HWCLOCK is not set | ||
614 | # CONFIG_FEATURE_HWCLOCK_LONG_OPTIONS is not set | ||
615 | # CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set | ||
616 | # CONFIG_IONICE is not set | ||
617 | # CONFIG_IPCRM is not set | ||
618 | # CONFIG_IPCS is not set | ||
619 | # CONFIG_LAST is not set | ||
620 | # CONFIG_FEATURE_LAST_FANCY is not set | ||
621 | # CONFIG_LOSETUP is not set | ||
622 | # CONFIG_LSPCI is not set | ||
623 | # CONFIG_LSUSB is not set | ||
624 | # CONFIG_MDEV is not set | ||
625 | # CONFIG_FEATURE_MDEV_CONF is not set | ||
626 | # CONFIG_FEATURE_MDEV_RENAME is not set | ||
627 | # CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set | ||
628 | # CONFIG_FEATURE_MDEV_EXEC is not set | ||
629 | # CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set | ||
630 | # CONFIG_MESG is not set | ||
631 | # CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set | ||
632 | # CONFIG_MKE2FS is not set | ||
633 | # CONFIG_MKFS_EXT2 is not set | ||
634 | # CONFIG_MKFS_MINIX is not set | ||
635 | # CONFIG_FEATURE_MINIX2 is not set | ||
636 | # CONFIG_MKFS_REISER is not set | ||
637 | # CONFIG_MKDOSFS is not set | ||
638 | # CONFIG_MKFS_VFAT is not set | ||
639 | # CONFIG_MKSWAP is not set | ||
640 | # CONFIG_FEATURE_MKSWAP_UUID is not set | ||
641 | # CONFIG_MORE is not set | ||
642 | # CONFIG_MOUNT is not set | ||
643 | # CONFIG_FEATURE_MOUNT_FAKE is not set | ||
644 | # CONFIG_FEATURE_MOUNT_VERBOSE is not set | ||
645 | # CONFIG_FEATURE_MOUNT_HELPERS is not set | ||
646 | # CONFIG_FEATURE_MOUNT_LABEL is not set | ||
647 | # CONFIG_FEATURE_MOUNT_NFS is not set | ||
648 | # CONFIG_FEATURE_MOUNT_CIFS is not set | ||
649 | # CONFIG_FEATURE_MOUNT_FLAGS is not set | ||
650 | # CONFIG_FEATURE_MOUNT_FSTAB is not set | ||
651 | # CONFIG_FEATURE_MOUNT_OTHERTAB is not set | ||
652 | # CONFIG_MOUNTPOINT is not set | ||
653 | # CONFIG_NSENTER is not set | ||
654 | # CONFIG_FEATURE_NSENTER_LONG_OPTS is not set | ||
655 | # CONFIG_PIVOT_ROOT is not set | ||
656 | # CONFIG_RDATE is not set | ||
657 | # CONFIG_RDEV is not set | ||
658 | # CONFIG_READPROFILE is not set | ||
659 | # CONFIG_RENICE is not set | ||
660 | CONFIG_REV=y | ||
661 | # CONFIG_RTCWAKE is not set | ||
662 | # CONFIG_SCRIPT is not set | ||
663 | # CONFIG_SCRIPTREPLAY is not set | ||
664 | # CONFIG_SETARCH is not set | ||
665 | # CONFIG_LINUX32 is not set | ||
666 | # CONFIG_LINUX64 is not set | ||
667 | # CONFIG_SETPRIV is not set | ||
668 | # CONFIG_FEATURE_SETPRIV_DUMP is not set | ||
669 | # CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set | ||
670 | # CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set | ||
671 | # CONFIG_SETSID is not set | ||
672 | # CONFIG_SWAPON is not set | ||
673 | # CONFIG_FEATURE_SWAPON_DISCARD is not set | ||
674 | # CONFIG_FEATURE_SWAPON_PRI is not set | ||
675 | # CONFIG_SWAPOFF is not set | ||
676 | # CONFIG_SWITCH_ROOT is not set | ||
677 | # CONFIG_TASKSET is not set | ||
678 | # CONFIG_FEATURE_TASKSET_FANCY is not set | ||
679 | # CONFIG_UEVENT is not set | ||
680 | # CONFIG_UMOUNT is not set | ||
681 | # CONFIG_FEATURE_UMOUNT_ALL is not set | ||
682 | # CONFIG_UNSHARE is not set | ||
683 | # CONFIG_WALL is not set | ||
684 | # CONFIG_FEATURE_MOUNT_LOOP is not set | ||
685 | # CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set | ||
686 | # CONFIG_FEATURE_MTAB_SUPPORT is not set | ||
687 | # CONFIG_VOLUMEID is not set | ||
688 | # CONFIG_FEATURE_VOLUMEID_BCACHE is not set | ||
689 | # CONFIG_FEATURE_VOLUMEID_BTRFS is not set | ||
690 | # CONFIG_FEATURE_VOLUMEID_CRAMFS is not set | ||
691 | # CONFIG_FEATURE_VOLUMEID_EXFAT is not set | ||
692 | # CONFIG_FEATURE_VOLUMEID_EXT is not set | ||
693 | # CONFIG_FEATURE_VOLUMEID_F2FS is not set | ||
694 | # CONFIG_FEATURE_VOLUMEID_FAT is not set | ||
695 | # CONFIG_FEATURE_VOLUMEID_HFS is not set | ||
696 | # CONFIG_FEATURE_VOLUMEID_ISO9660 is not set | ||
697 | # CONFIG_FEATURE_VOLUMEID_JFS is not set | ||
698 | # CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set | ||
699 | # CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set | ||
700 | # CONFIG_FEATURE_VOLUMEID_LUKS is not set | ||
701 | # CONFIG_FEATURE_VOLUMEID_NILFS is not set | ||
702 | # CONFIG_FEATURE_VOLUMEID_NTFS is not set | ||
703 | # CONFIG_FEATURE_VOLUMEID_OCFS2 is not set | ||
704 | # CONFIG_FEATURE_VOLUMEID_REISERFS is not set | ||
705 | # CONFIG_FEATURE_VOLUMEID_ROMFS is not set | ||
706 | # CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set | ||
707 | # CONFIG_FEATURE_VOLUMEID_SYSV is not set | ||
708 | # CONFIG_FEATURE_VOLUMEID_UBIFS is not set | ||
709 | # CONFIG_FEATURE_VOLUMEID_UDF is not set | ||
710 | # CONFIG_FEATURE_VOLUMEID_XFS is not set | ||
711 | |||
712 | # | ||
713 | # Miscellaneous Utilities | ||
714 | # | ||
715 | # CONFIG_ADJTIMEX is not set | ||
716 | # CONFIG_BBCONFIG is not set | ||
717 | # CONFIG_FEATURE_COMPRESS_BBCONFIG is not set | ||
718 | # CONFIG_BEEP is not set | ||
719 | CONFIG_FEATURE_BEEP_FREQ=0 | ||
720 | CONFIG_FEATURE_BEEP_LENGTH_MS=0 | ||
721 | # CONFIG_CHAT is not set | ||
722 | # CONFIG_FEATURE_CHAT_NOFAIL is not set | ||
723 | # CONFIG_FEATURE_CHAT_TTY_HIFI is not set | ||
724 | # CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set | ||
725 | # CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set | ||
726 | # CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set | ||
727 | # CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set | ||
728 | # CONFIG_FEATURE_CHAT_CLR_ABORT is not set | ||
729 | # CONFIG_CONSPY is not set | ||
730 | # CONFIG_CROND is not set | ||
731 | # CONFIG_FEATURE_CROND_D is not set | ||
732 | # CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set | ||
733 | # CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set | ||
734 | CONFIG_FEATURE_CROND_DIR="" | ||
735 | # CONFIG_CRONTAB is not set | ||
736 | CONFIG_DC=y | ||
737 | CONFIG_FEATURE_DC_LIBM=y | ||
738 | # CONFIG_DEVFSD is not set | ||
739 | # CONFIG_DEVFSD_MODLOAD is not set | ||
740 | # CONFIG_DEVFSD_FG_NP is not set | ||
741 | # CONFIG_DEVFSD_VERBOSE is not set | ||
742 | # CONFIG_FEATURE_DEVFS is not set | ||
743 | # CONFIG_DEVMEM is not set | ||
744 | # CONFIG_FBSPLASH is not set | ||
745 | # CONFIG_FLASHCP is not set | ||
746 | # CONFIG_FLASH_ERASEALL is not set | ||
747 | # CONFIG_FLASH_LOCK is not set | ||
748 | # CONFIG_FLASH_UNLOCK is not set | ||
749 | # CONFIG_HDPARM is not set | ||
750 | # CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set | ||
751 | # CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set | ||
752 | # CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set | ||
753 | # CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set | ||
754 | # CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set | ||
755 | # CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set | ||
756 | # CONFIG_I2CGET is not set | ||
757 | # CONFIG_I2CSET is not set | ||
758 | # CONFIG_I2CDUMP is not set | ||
759 | # CONFIG_I2CDETECT is not set | ||
760 | # CONFIG_INOTIFYD is not set | ||
761 | CONFIG_LESS=y | ||
762 | CONFIG_FEATURE_LESS_MAXLINES=9999999 | ||
763 | CONFIG_FEATURE_LESS_BRACKETS=y | ||
764 | CONFIG_FEATURE_LESS_FLAGS=y | ||
765 | CONFIG_FEATURE_LESS_TRUNCATE=y | ||
766 | CONFIG_FEATURE_LESS_MARKS=y | ||
767 | CONFIG_FEATURE_LESS_REGEXP=y | ||
768 | # CONFIG_FEATURE_LESS_WINCH is not set | ||
769 | # CONFIG_FEATURE_LESS_ASK_TERMINAL is not set | ||
770 | CONFIG_FEATURE_LESS_DASHCMD=y | ||
771 | CONFIG_FEATURE_LESS_LINENUMS=y | ||
772 | # CONFIG_LSSCSI is not set | ||
773 | # CONFIG_MAKEDEVS is not set | ||
774 | # CONFIG_FEATURE_MAKEDEVS_LEAF is not set | ||
775 | # CONFIG_FEATURE_MAKEDEVS_TABLE is not set | ||
776 | CONFIG_MAN=y | ||
777 | # CONFIG_MICROCOM is not set | ||
778 | # CONFIG_MT is not set | ||
779 | # CONFIG_NANDWRITE is not set | ||
780 | # CONFIG_NANDDUMP is not set | ||
781 | # CONFIG_PARTPROBE is not set | ||
782 | # CONFIG_RAIDAUTORUN is not set | ||
783 | # CONFIG_READAHEAD is not set | ||
784 | # CONFIG_RFKILL is not set | ||
785 | # CONFIG_RUNLEVEL is not set | ||
786 | # CONFIG_RX is not set | ||
787 | # CONFIG_SETSERIAL is not set | ||
788 | CONFIG_STRINGS=y | ||
789 | # CONFIG_TIME is not set | ||
790 | # CONFIG_TTYSIZE is not set | ||
791 | # CONFIG_UBIRENAME is not set | ||
792 | # CONFIG_UBIATTACH is not set | ||
793 | # CONFIG_UBIDETACH is not set | ||
794 | # CONFIG_UBIMKVOL is not set | ||
795 | # CONFIG_UBIRMVOL is not set | ||
796 | # CONFIG_UBIRSVOL is not set | ||
797 | # CONFIG_UBIUPDATEVOL is not set | ||
798 | # CONFIG_VOLNAME is not set | ||
799 | # CONFIG_WATCHDOG is not set | ||
800 | |||
801 | # | ||
802 | # Networking Utilities | ||
803 | # | ||
804 | CONFIG_FEATURE_IPV6=y | ||
805 | # CONFIG_FEATURE_UNIX_LOCAL is not set | ||
806 | CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y | ||
807 | # CONFIG_VERBOSE_RESOLUTION_ERRORS is not set | ||
808 | # CONFIG_ARP is not set | ||
809 | # CONFIG_ARPING is not set | ||
810 | # CONFIG_BRCTL is not set | ||
811 | # CONFIG_FEATURE_BRCTL_FANCY is not set | ||
812 | # CONFIG_FEATURE_BRCTL_SHOW is not set | ||
813 | # CONFIG_DNSD is not set | ||
814 | # CONFIG_ETHER_WAKE is not set | ||
815 | # CONFIG_FTPD is not set | ||
816 | # CONFIG_FEATURE_FTPD_WRITE is not set | ||
817 | # CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set | ||
818 | # CONFIG_FEATURE_FTPD_AUTHENTICATION is not set | ||
819 | CONFIG_FTPGET=y | ||
820 | CONFIG_FTPPUT=y | ||
821 | CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y | ||
822 | # CONFIG_HOSTNAME is not set | ||
823 | # CONFIG_DNSDOMAINNAME is not set | ||
824 | # CONFIG_HTTPD is not set | ||
825 | # CONFIG_FEATURE_HTTPD_RANGES is not set | ||
826 | # CONFIG_FEATURE_HTTPD_SETUID is not set | ||
827 | # CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set | ||
828 | # CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set | ||
829 | # CONFIG_FEATURE_HTTPD_CGI is not set | ||
830 | # CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set | ||
831 | # CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set | ||
832 | # CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set | ||
833 | # CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set | ||
834 | # CONFIG_FEATURE_HTTPD_PROXY is not set | ||
835 | # CONFIG_FEATURE_HTTPD_GZIP is not set | ||
836 | # CONFIG_IFCONFIG is not set | ||
837 | # CONFIG_FEATURE_IFCONFIG_STATUS is not set | ||
838 | # CONFIG_FEATURE_IFCONFIG_SLIP is not set | ||
839 | # CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set | ||
840 | # CONFIG_FEATURE_IFCONFIG_HW is not set | ||
841 | # CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set | ||
842 | # CONFIG_IFENSLAVE is not set | ||
843 | # CONFIG_IFPLUGD is not set | ||
844 | # CONFIG_IFUP is not set | ||
845 | # CONFIG_IFDOWN is not set | ||
846 | CONFIG_IFUPDOWN_IFSTATE_PATH="" | ||
847 | # CONFIG_FEATURE_IFUPDOWN_IP is not set | ||
848 | # CONFIG_FEATURE_IFUPDOWN_IPV4 is not set | ||
849 | # CONFIG_FEATURE_IFUPDOWN_IPV6 is not set | ||
850 | # CONFIG_FEATURE_IFUPDOWN_MAPPING is not set | ||
851 | # CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set | ||
852 | # CONFIG_INETD is not set | ||
853 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set | ||
854 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set | ||
855 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set | ||
856 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set | ||
857 | # CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set | ||
858 | # CONFIG_FEATURE_INETD_RPC is not set | ||
859 | # CONFIG_IP is not set | ||
860 | # CONFIG_IPADDR is not set | ||
861 | # CONFIG_IPLINK is not set | ||
862 | # CONFIG_IPROUTE is not set | ||
863 | # CONFIG_IPTUNNEL is not set | ||
864 | # CONFIG_IPRULE is not set | ||
865 | # CONFIG_IPNEIGH is not set | ||
866 | # CONFIG_FEATURE_IP_ADDRESS is not set | ||
867 | # CONFIG_FEATURE_IP_LINK is not set | ||
868 | # CONFIG_FEATURE_IP_ROUTE is not set | ||
869 | CONFIG_FEATURE_IP_ROUTE_DIR="" | ||
870 | # CONFIG_FEATURE_IP_TUNNEL is not set | ||
871 | # CONFIG_FEATURE_IP_RULE is not set | ||
872 | # CONFIG_FEATURE_IP_NEIGH is not set | ||
873 | # CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set | ||
874 | CONFIG_IPCALC=y | ||
875 | CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y | ||
876 | CONFIG_FEATURE_IPCALC_FANCY=y | ||
877 | # CONFIG_FAKEIDENTD is not set | ||
878 | # CONFIG_NAMEIF is not set | ||
879 | # CONFIG_FEATURE_NAMEIF_EXTENDED is not set | ||
880 | # CONFIG_NBDCLIENT is not set | ||
881 | CONFIG_NC=y | ||
882 | CONFIG_NC_SERVER=y | ||
883 | # CONFIG_NC_EXTRA is not set | ||
884 | # CONFIG_NC_110_COMPAT is not set | ||
885 | # CONFIG_NETSTAT is not set | ||
886 | # CONFIG_FEATURE_NETSTAT_WIDE is not set | ||
887 | # CONFIG_FEATURE_NETSTAT_PRG is not set | ||
888 | # CONFIG_NSLOOKUP is not set | ||
889 | # CONFIG_NTPD is not set | ||
890 | # CONFIG_FEATURE_NTPD_SERVER is not set | ||
891 | # CONFIG_FEATURE_NTPD_CONF is not set | ||
892 | # CONFIG_PING is not set | ||
893 | # CONFIG_PING6 is not set | ||
894 | # CONFIG_FEATURE_FANCY_PING is not set | ||
895 | # CONFIG_PSCAN is not set | ||
896 | # CONFIG_ROUTE is not set | ||
897 | # CONFIG_SLATTACH is not set | ||
898 | # CONFIG_SSL_CLIENT is not set | ||
899 | # CONFIG_TCPSVD is not set | ||
900 | # CONFIG_UDPSVD is not set | ||
901 | # CONFIG_TELNET is not set | ||
902 | # CONFIG_FEATURE_TELNET_TTYPE is not set | ||
903 | # CONFIG_FEATURE_TELNET_AUTOLOGIN is not set | ||
904 | # CONFIG_FEATURE_TELNET_WIDTH is not set | ||
905 | # CONFIG_TELNETD is not set | ||
906 | # CONFIG_FEATURE_TELNETD_STANDALONE is not set | ||
907 | # CONFIG_FEATURE_TELNETD_INETD_WAIT is not set | ||
908 | # CONFIG_TFTP is not set | ||
909 | # CONFIG_TFTPD is not set | ||
910 | # CONFIG_FEATURE_TFTP_GET is not set | ||
911 | # CONFIG_FEATURE_TFTP_PUT is not set | ||
912 | # CONFIG_FEATURE_TFTP_BLOCKSIZE is not set | ||
913 | # CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set | ||
914 | # CONFIG_TFTP_DEBUG is not set | ||
915 | # CONFIG_TLS is not set | ||
916 | # CONFIG_TRACEROUTE is not set | ||
917 | # CONFIG_TRACEROUTE6 is not set | ||
918 | # CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set | ||
919 | # CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set | ||
920 | # CONFIG_TUNCTL is not set | ||
921 | # CONFIG_FEATURE_TUNCTL_UG is not set | ||
922 | # CONFIG_VCONFIG is not set | ||
923 | CONFIG_WGET=y | ||
924 | CONFIG_FEATURE_WGET_LONG_OPTIONS=y | ||
925 | # CONFIG_FEATURE_WGET_STATUSBAR is not set | ||
926 | # CONFIG_FEATURE_WGET_AUTHENTICATION is not set | ||
927 | # CONFIG_FEATURE_WGET_TIMEOUT is not set | ||
928 | # CONFIG_FEATURE_WGET_HTTPS is not set | ||
929 | # CONFIG_FEATURE_WGET_OPENSSL is not set | ||
930 | CONFIG_WHOIS=y | ||
931 | # CONFIG_ZCIP is not set | ||
932 | # CONFIG_UDHCPC6 is not set | ||
933 | # CONFIG_FEATURE_UDHCPC6_RFC3646 is not set | ||
934 | # CONFIG_FEATURE_UDHCPC6_RFC4704 is not set | ||
935 | # CONFIG_FEATURE_UDHCPC6_RFC4833 is not set | ||
936 | # CONFIG_UDHCPD is not set | ||
937 | # CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set | ||
938 | # CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set | ||
939 | CONFIG_DHCPD_LEASES_FILE="" | ||
940 | # CONFIG_DUMPLEASES is not set | ||
941 | # CONFIG_DHCPRELAY is not set | ||
942 | # CONFIG_UDHCPC is not set | ||
943 | # CONFIG_FEATURE_UDHCPC_ARPING is not set | ||
944 | # CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set | ||
945 | CONFIG_UDHCPC_DEFAULT_SCRIPT="" | ||
946 | # CONFIG_FEATURE_UDHCP_PORT is not set | ||
947 | CONFIG_UDHCP_DEBUG=0 | ||
948 | # CONFIG_FEATURE_UDHCP_RFC3397 is not set | ||
949 | # CONFIG_FEATURE_UDHCP_8021Q is not set | ||
950 | CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0 | ||
951 | CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS="" | ||
952 | |||
953 | # | ||
954 | # Print Utilities | ||
955 | # | ||
956 | # CONFIG_LPD is not set | ||
957 | # CONFIG_LPR is not set | ||
958 | # CONFIG_LPQ is not set | ||
959 | |||
960 | # | ||
961 | # Mail Utilities | ||
962 | # | ||
963 | # CONFIG_MAKEMIME is not set | ||
964 | # CONFIG_POPMAILDIR is not set | ||
965 | # CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set | ||
966 | # CONFIG_REFORMIME is not set | ||
967 | # CONFIG_FEATURE_REFORMIME_COMPAT is not set | ||
968 | # CONFIG_SENDMAIL is not set | ||
969 | CONFIG_FEATURE_MIME_CHARSET="" | ||
970 | |||
971 | # | ||
972 | # Process Utilities | ||
973 | # | ||
974 | # CONFIG_FREE is not set | ||
975 | # CONFIG_FUSER is not set | ||
976 | # CONFIG_IOSTAT is not set | ||
977 | CONFIG_KILL=y | ||
978 | CONFIG_KILLALL=y | ||
979 | # CONFIG_KILLALL5 is not set | ||
980 | # CONFIG_LSOF is not set | ||
981 | # CONFIG_MPSTAT is not set | ||
982 | # CONFIG_NMETER is not set | ||
983 | CONFIG_PGREP=y | ||
984 | # CONFIG_PKILL is not set | ||
985 | CONFIG_PIDOF=y | ||
986 | CONFIG_FEATURE_PIDOF_SINGLE=y | ||
987 | CONFIG_FEATURE_PIDOF_OMIT=y | ||
988 | # CONFIG_PMAP is not set | ||
989 | # CONFIG_POWERTOP is not set | ||
990 | # CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set | ||
991 | CONFIG_PS=y | ||
992 | # CONFIG_FEATURE_PS_WIDE is not set | ||
993 | # CONFIG_FEATURE_PS_LONG is not set | ||
994 | # CONFIG_FEATURE_PS_TIME is not set | ||
995 | # CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set | ||
996 | # CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set | ||
997 | # CONFIG_PSTREE is not set | ||
998 | # CONFIG_PWDX is not set | ||
999 | # CONFIG_SMEMCAP is not set | ||
1000 | # CONFIG_BB_SYSCTL is not set | ||
1001 | # CONFIG_TOP is not set | ||
1002 | # CONFIG_FEATURE_TOP_INTERACTIVE is not set | ||
1003 | # CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set | ||
1004 | # CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set | ||
1005 | # CONFIG_FEATURE_TOP_SMP_CPU is not set | ||
1006 | # CONFIG_FEATURE_TOP_DECIMALS is not set | ||
1007 | # CONFIG_FEATURE_TOP_SMP_PROCESS is not set | ||
1008 | # CONFIG_FEATURE_TOPMEM is not set | ||
1009 | # CONFIG_UPTIME is not set | ||
1010 | # CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set | ||
1011 | CONFIG_WATCH=y | ||
1012 | # CONFIG_FEATURE_SHOW_THREADS is not set | ||
1013 | |||
1014 | # | ||
1015 | # Runit Utilities | ||
1016 | # | ||
1017 | # CONFIG_CHPST is not set | ||
1018 | # CONFIG_SETUIDGID is not set | ||
1019 | # CONFIG_ENVUIDGID is not set | ||
1020 | # CONFIG_ENVDIR is not set | ||
1021 | # CONFIG_SOFTLIMIT is not set | ||
1022 | # CONFIG_RUNSV is not set | ||
1023 | # CONFIG_RUNSVDIR is not set | ||
1024 | # CONFIG_FEATURE_RUNSVDIR_LOG is not set | ||
1025 | # CONFIG_SV is not set | ||
1026 | CONFIG_SV_DEFAULT_SERVICE_DIR="" | ||
1027 | # CONFIG_SVC is not set | ||
1028 | # CONFIG_SVLOGD is not set | ||
1029 | # CONFIG_CHCON is not set | ||
1030 | # CONFIG_FEATURE_CHCON_LONG_OPTIONS is not set | ||
1031 | # CONFIG_GETENFORCE is not set | ||
1032 | # CONFIG_GETSEBOOL is not set | ||
1033 | # CONFIG_LOAD_POLICY is not set | ||
1034 | # CONFIG_MATCHPATHCON is not set | ||
1035 | # CONFIG_RUNCON is not set | ||
1036 | # CONFIG_FEATURE_RUNCON_LONG_OPTIONS is not set | ||
1037 | # CONFIG_SELINUXENABLED is not set | ||
1038 | # CONFIG_SESTATUS is not set | ||
1039 | # CONFIG_SETENFORCE is not set | ||
1040 | # CONFIG_SETFILES is not set | ||
1041 | # CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set | ||
1042 | # CONFIG_RESTORECON is not set | ||
1043 | # CONFIG_SETSEBOOL is not set | ||
1044 | |||
1045 | # | ||
1046 | # Shells | ||
1047 | # | ||
1048 | CONFIG_SH_IS_ASH=y | ||
1049 | # CONFIG_SH_IS_HUSH is not set | ||
1050 | # CONFIG_SH_IS_NONE is not set | ||
1051 | CONFIG_BASH_IS_ASH=y | ||
1052 | # CONFIG_BASH_IS_HUSH is not set | ||
1053 | # CONFIG_BASH_IS_NONE is not set | ||
1054 | CONFIG_ASH=y | ||
1055 | CONFIG_ASH_OPTIMIZE_FOR_SIZE=y | ||
1056 | CONFIG_ASH_INTERNAL_GLOB=y | ||
1057 | CONFIG_ASH_BASH_COMPAT=y | ||
1058 | # CONFIG_ASH_JOB_CONTROL is not set | ||
1059 | CONFIG_ASH_ALIAS=y | ||
1060 | CONFIG_ASH_RANDOM_SUPPORT=y | ||
1061 | CONFIG_ASH_EXPAND_PRMT=y | ||
1062 | # CONFIG_ASH_IDLE_TIMEOUT is not set | ||
1063 | # CONFIG_ASH_MAIL is not set | ||
1064 | CONFIG_ASH_ECHO=y | ||
1065 | CONFIG_ASH_PRINTF=y | ||
1066 | CONFIG_ASH_TEST=y | ||
1067 | CONFIG_ASH_HELP=y | ||
1068 | CONFIG_ASH_GETOPTS=y | ||
1069 | CONFIG_ASH_CMDCMD=y | ||
1070 | # CONFIG_ASH_NOCONSOLE is not set | ||
1071 | # CONFIG_CTTYHACK is not set | ||
1072 | # CONFIG_HUSH is not set | ||
1073 | # CONFIG_HUSH_BASH_COMPAT is not set | ||
1074 | # CONFIG_HUSH_BRACE_EXPANSION is not set | ||
1075 | # CONFIG_HUSH_INTERACTIVE is not set | ||
1076 | # CONFIG_HUSH_SAVEHISTORY is not set | ||
1077 | # CONFIG_HUSH_JOB is not set | ||
1078 | # CONFIG_HUSH_TICK is not set | ||
1079 | # CONFIG_HUSH_IF is not set | ||
1080 | # CONFIG_HUSH_LOOPS is not set | ||
1081 | # CONFIG_HUSH_CASE is not set | ||
1082 | # CONFIG_HUSH_FUNCTIONS is not set | ||
1083 | # CONFIG_HUSH_LOCAL is not set | ||
1084 | # CONFIG_HUSH_RANDOM_SUPPORT is not set | ||
1085 | # CONFIG_HUSH_MODE_X is not set | ||
1086 | # CONFIG_HUSH_ECHO is not set | ||
1087 | # CONFIG_HUSH_PRINTF is not set | ||
1088 | # CONFIG_HUSH_TEST is not set | ||
1089 | # CONFIG_HUSH_HELP is not set | ||
1090 | # CONFIG_HUSH_EXPORT is not set | ||
1091 | # CONFIG_HUSH_EXPORT_N is not set | ||
1092 | # CONFIG_HUSH_READONLY is not set | ||
1093 | # CONFIG_HUSH_KILL is not set | ||
1094 | # CONFIG_HUSH_WAIT is not set | ||
1095 | # CONFIG_HUSH_TRAP is not set | ||
1096 | # CONFIG_HUSH_TYPE is not set | ||
1097 | # CONFIG_HUSH_READ is not set | ||
1098 | # CONFIG_HUSH_SET is not set | ||
1099 | # CONFIG_HUSH_UNSET is not set | ||
1100 | # CONFIG_HUSH_ULIMIT is not set | ||
1101 | # CONFIG_HUSH_UMASK is not set | ||
1102 | # CONFIG_HUSH_MEMLEAK is not set | ||
1103 | |||
1104 | # | ||
1105 | # Options common to all shells | ||
1106 | # | ||
1107 | CONFIG_FEATURE_SH_MATH=y | ||
1108 | CONFIG_FEATURE_SH_MATH_64=y | ||
1109 | CONFIG_FEATURE_SH_EXTRA_QUIET=y | ||
1110 | CONFIG_FEATURE_SH_STANDALONE=y | ||
1111 | CONFIG_FEATURE_SH_NOFORK=y | ||
1112 | # CONFIG_FEATURE_SH_READ_FRAC is not set | ||
1113 | CONFIG_FEATURE_SH_HISTFILESIZE=y | ||
1114 | |||
1115 | # | ||
1116 | # System Logging Utilities | ||
1117 | # | ||
1118 | # CONFIG_KLOGD is not set | ||
1119 | # CONFIG_FEATURE_KLOGD_KLOGCTL is not set | ||
1120 | # CONFIG_LOGGER is not set | ||
1121 | # CONFIG_LOGREAD is not set | ||
1122 | # CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set | ||
1123 | # CONFIG_SYSLOGD is not set | ||
1124 | # CONFIG_FEATURE_ROTATE_LOGFILE is not set | ||
1125 | # CONFIG_FEATURE_REMOTE_LOG is not set | ||
1126 | # CONFIG_FEATURE_SYSLOGD_DUP is not set | ||
1127 | # CONFIG_FEATURE_SYSLOGD_CFG is not set | ||
1128 | CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0 | ||
1129 | # CONFIG_FEATURE_IPC_SYSLOG is not set | ||
1130 | CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0 | ||
1131 | # CONFIG_FEATURE_KMSG_SYSLOG is not set | ||
diff --git a/coreutils/dd.c b/coreutils/dd.c index 43545c010..f7f1c9564 100644 --- a/coreutils/dd.c +++ b/coreutils/dd.c | |||
@@ -293,6 +293,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) | |||
293 | #endif | 293 | #endif |
294 | }; | 294 | }; |
295 | smallint exitcode = EXIT_FAILURE; | 295 | smallint exitcode = EXIT_FAILURE; |
296 | int devzero = 0; | ||
296 | int i; | 297 | int i; |
297 | size_t ibs = 512; | 298 | size_t ibs = 512; |
298 | char *ibuf; | 299 | char *ibuf; |
@@ -419,7 +420,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv) | |||
419 | #endif | 420 | #endif |
420 | 421 | ||
421 | if (infile) { | 422 | if (infile) { |
422 | xmove_fd(xopen(infile, O_RDONLY), ifd); | 423 | if (ENABLE_PLATFORM_MINGW32 && !strcmp(infile, "/dev/zero")) { |
424 | G.flags |= FLAG_NOERROR; | ||
425 | devzero = 1; | ||
426 | } else { | ||
427 | xmove_fd(xopen(infile, O_RDONLY), ifd); | ||
428 | } | ||
423 | } else { | 429 | } else { |
424 | infile = bb_msg_standard_input; | 430 | infile = bb_msg_standard_input; |
425 | } | 431 | } |
@@ -446,7 +452,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv) | |||
446 | } else { | 452 | } else { |
447 | outfile = bb_msg_standard_output; | 453 | outfile = bb_msg_standard_output; |
448 | } | 454 | } |
449 | if (skip) { | 455 | if (skip && !devzero) { |
450 | size_t blocksz = (G.flags & FLAG_SKIP_BYTES) ? 1 : ibs; | 456 | size_t blocksz = (G.flags & FLAG_SKIP_BYTES) ? 1 : ibs; |
451 | if (lseek(ifd, skip * blocksz, SEEK_CUR) < 0) { | 457 | if (lseek(ifd, skip * blocksz, SEEK_CUR) < 0) { |
452 | do { | 458 | do { |
@@ -466,7 +472,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv) | |||
466 | while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { | 472 | while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { |
467 | ssize_t n; | 473 | ssize_t n; |
468 | 474 | ||
469 | n = safe_read(ifd, ibuf, ibs); | 475 | if (devzero) { |
476 | memset(ibuf, 0, ibs); | ||
477 | n = ibs; | ||
478 | } | ||
479 | else | ||
480 | n = safe_read(ifd, ibuf, ibs); | ||
470 | if (n == 0) | 481 | if (n == 0) |
471 | break; | 482 | break; |
472 | if (n < 0) { | 483 | if (n < 0) { |
@@ -542,7 +553,8 @@ int dd_main(int argc UNUSED_PARAM, char **argv) | |||
542 | if (write_and_stats(obuf, oc, obs, outfile)) | 553 | if (write_and_stats(obuf, oc, obs, outfile)) |
543 | goto out_status; | 554 | goto out_status; |
544 | } | 555 | } |
545 | if (close(ifd) < 0) { | 556 | |
557 | if (!devzero && close(ifd) < 0) { | ||
546 | die_infile: | 558 | die_infile: |
547 | bb_simple_perror_msg_and_die(infile); | 559 | bb_simple_perror_msg_and_die(infile); |
548 | } | 560 | } |
diff --git a/coreutils/expr.c b/coreutils/expr.c index a2bbfdd69..0cf2b9bd9 100644 --- a/coreutils/expr.c +++ b/coreutils/expr.c | |||
@@ -84,7 +84,7 @@ | |||
84 | #if ENABLE_EXPR_MATH_SUPPORT_64 | 84 | #if ENABLE_EXPR_MATH_SUPPORT_64 |
85 | typedef int64_t arith_t; | 85 | typedef int64_t arith_t; |
86 | 86 | ||
87 | #define PF_REZ "ll" | 87 | #define PF_REZ LL_FMT |
88 | #define PF_REZ_TYPE (long long) | 88 | #define PF_REZ_TYPE (long long) |
89 | #define STRTOL(s, e, b) strtoll(s, e, b) | 89 | #define STRTOL(s, e, b) strtoll(s, e, b) |
90 | #else | 90 | #else |
diff --git a/coreutils/factor.c b/coreutils/factor.c index 467e23a4d..ee3943a4a 100644 --- a/coreutils/factor.c +++ b/coreutils/factor.c | |||
@@ -161,7 +161,7 @@ static NOINLINE void factorize(wide_t N) | |||
161 | } | 161 | } |
162 | end: | 162 | end: |
163 | if (N > 1) | 163 | if (N > 1) |
164 | printf(" %llu", N); | 164 | printf(" %"LL_FMT"u", N); |
165 | bb_putchar('\n'); | 165 | bb_putchar('\n'); |
166 | } | 166 | } |
167 | 167 | ||
@@ -175,7 +175,7 @@ static void factorize_numstr(const char *numstr) | |||
175 | N = bb_strtoull(numstr, NULL, 10); | 175 | N = bb_strtoull(numstr, NULL, 10); |
176 | if (errno) | 176 | if (errno) |
177 | bb_show_usage(); | 177 | bb_show_usage(); |
178 | printf("%llu:", N); | 178 | printf("%"LL_FMT"u:", N); |
179 | factorize(N); | 179 | factorize(N); |
180 | } | 180 | } |
181 | 181 | ||
diff --git a/coreutils/ls.c b/coreutils/ls.c index 0fe0345b3..4c0944bb0 100644 --- a/coreutils/ls.c +++ b/coreutils/ls.c | |||
@@ -496,7 +496,7 @@ static NOINLINE unsigned display_single(const struct dnode *dn) | |||
496 | lpath = xmalloc_readlink_or_warn(dn->fullname); | 496 | lpath = xmalloc_readlink_or_warn(dn->fullname); |
497 | 497 | ||
498 | if (opt & OPT_i) /* show inode# */ | 498 | if (opt & OPT_i) /* show inode# */ |
499 | column += printf("%7llu ", (long long) dn->dn_ino); | 499 | column += printf("%7"LL_FMT"u ", (long long) dn->dn_ino); |
500 | //TODO: -h should affect -s too: | 500 | //TODO: -h should affect -s too: |
501 | if (opt & OPT_s) /* show allocated blocks */ | 501 | if (opt & OPT_s) /* show allocated blocks */ |
502 | column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); | 502 | column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); |
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c index fa0196ca4..513c8ef37 100644 --- a/coreutils/od_bloaty.c +++ b/coreutils/od_bloaty.c | |||
@@ -101,6 +101,13 @@ typedef long long llong; | |||
101 | # define LDBL_DIG DBL_DIG | 101 | # define LDBL_DIG DBL_DIG |
102 | #endif | 102 | #endif |
103 | 103 | ||
104 | #if ENABLE_PLATFORM_MINGW32 | ||
105 | /* symbol conflict */ | ||
106 | #define CHAR SIZE_CHAR | ||
107 | #define SHORT SIZE_SHORT | ||
108 | #define LONG SIZE_LONG | ||
109 | #define INT SIZE_INT | ||
110 | #endif | ||
104 | enum size_spec { | 111 | enum size_spec { |
105 | NO_SIZE, | 112 | NO_SIZE, |
106 | CHAR, | 113 | CHAR, |
diff --git a/coreutils/stat.c b/coreutils/stat.c index 3b85808b5..96efa1d5d 100644 --- a/coreutils/stat.c +++ b/coreutils/stat.c | |||
@@ -31,7 +31,6 @@ | |||
31 | //config: bool "Enable display of filesystem status (-f)" | 31 | //config: bool "Enable display of filesystem status (-f)" |
32 | //config: default y | 32 | //config: default y |
33 | //config: depends on STAT | 33 | //config: depends on STAT |
34 | //config: select PLATFORM_LINUX # statfs() | ||
35 | //config: help | 34 | //config: help |
36 | //config: Without this, stat will not support the '-f' option to display | 35 | //config: Without this, stat will not support the '-f' option to display |
37 | //config: information about filesystem status. | 36 | //config: information about filesystem status. |
diff --git a/coreutils/sum.c b/coreutils/sum.c index 487e93f4a..ee758877f 100644 --- a/coreutils/sum.c +++ b/coreutils/sum.c | |||
@@ -82,9 +82,9 @@ static unsigned sum_file(const char *file, unsigned type) | |||
82 | if (type >= SUM_SYSV) { | 82 | if (type >= SUM_SYSV) { |
83 | r = (s & 0xffff) + ((s & 0xffffffff) >> 16); | 83 | r = (s & 0xffff) + ((s & 0xffffffff) >> 16); |
84 | s = (r & 0xffff) + (r >> 16); | 84 | s = (r & 0xffff) + (r >> 16); |
85 | printf("%u %llu %s\n", s, (total_bytes + 511) / 512, file); | 85 | printf("%u %"LL_FMT"u %s\n", s, (total_bytes + 511) / 512, file); |
86 | } else | 86 | } else |
87 | printf("%05u %5llu %s\n", s, (total_bytes + 1023) / 1024, file); | 87 | printf("%05u %5"OFF_FMT"u %s\n", s, (total_bytes + 1023) / 1024, file); |
88 | return 1; | 88 | return 1; |
89 | #undef buf | 89 | #undef buf |
90 | } | 90 | } |
diff --git a/coreutils/test.c b/coreutils/test.c index a8286525a..ed708c6d3 100644 --- a/coreutils/test.c +++ b/coreutils/test.c | |||
@@ -637,6 +637,21 @@ static int filstat(char *nm, enum token mode) | |||
637 | return 0; | 637 | return 0; |
638 | } | 638 | } |
639 | 639 | ||
640 | #if ENABLE_PLATFORM_MINGW32 | ||
641 | if (mode == FILEX) { | ||
642 | char *p; | ||
643 | |||
644 | if (file_is_executable(nm)) { | ||
645 | return 1; | ||
646 | } | ||
647 | else if ((p=file_is_win32_executable(nm))) { | ||
648 | free(p); | ||
649 | return 1; | ||
650 | } | ||
651 | return 0; | ||
652 | } | ||
653 | #endif | ||
654 | |||
640 | if (stat(nm, &s) != 0) | 655 | if (stat(nm, &s) != 0) |
641 | return 0; | 656 | return 0; |
642 | if (mode == FILEXIST) | 657 | if (mode == FILEXIST) |
diff --git a/coreutils/yes.c b/coreutils/yes.c index 493b7b1c3..a5a444249 100644 --- a/coreutils/yes.c +++ b/coreutils/yes.c | |||
@@ -40,6 +40,10 @@ int yes_main(int argc UNUSED_PARAM, char **argv) | |||
40 | ++argv; | 40 | ++argv; |
41 | 41 | ||
42 | do { | 42 | do { |
43 | #if ENABLE_PLATFORM_MINGW32 | ||
44 | if (ferror(stdout) != 0) | ||
45 | break; | ||
46 | #endif | ||
43 | pp = argv; | 47 | pp = argv; |
44 | while (1) { | 48 | while (1) { |
45 | fputs(*pp, stdout); | 49 | fputs(*pp, stdout); |
diff --git a/debianutils/which.c b/debianutils/which.c index 3197ddac1..23a481438 100644 --- a/debianutils/which.c +++ b/debianutils/which.c | |||
@@ -43,17 +43,34 @@ int which_main(int argc UNUSED_PARAM, char **argv) | |||
43 | 43 | ||
44 | do { | 44 | do { |
45 | int missing = 1; | 45 | int missing = 1; |
46 | char *p; | ||
47 | |||
48 | #if ENABLE_FEATURE_SH_STANDALONE | ||
49 | if (find_applet_by_name(*argv) >= 0 || | ||
50 | is_prefixed_with(*argv, "busybox")) { | ||
51 | missing = 0; | ||
52 | puts(*argv); | ||
53 | if (!option_mask32) /* -a not set */ | ||
54 | break; | ||
55 | } | ||
56 | #endif | ||
46 | 57 | ||
47 | /* If file contains a slash don't use PATH */ | 58 | /* If file contains a slash don't use PATH */ |
48 | if (strchr(*argv, '/')) { | 59 | if (strchr(*argv, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(*argv, '\\'))) { |
49 | if (file_is_executable(*argv)) { | 60 | if (file_is_executable(*argv)) { |
50 | missing = 0; | 61 | missing = 0; |
51 | puts(*argv); | 62 | puts(*argv); |
52 | } | 63 | } |
64 | #if ENABLE_PLATFORM_MINGW32 | ||
65 | else if ((p=file_is_win32_executable(*argv)) != NULL) { | ||
66 | missing = 0; | ||
67 | puts(p); | ||
68 | free(p); | ||
69 | } | ||
70 | #endif | ||
53 | } else { | 71 | } else { |
54 | char *path; | 72 | char *path; |
55 | char *tmp; | 73 | char *tmp; |
56 | char *p; | ||
57 | 74 | ||
58 | path = tmp = xstrdup(env_path); | 75 | path = tmp = xstrdup(env_path); |
59 | while ((p = find_executable(*argv, &tmp)) != NULL) { | 76 | while ((p = find_executable(*argv, &tmp)) != NULL) { |
diff --git a/editors/awk.c b/editors/awk.c index cc17ad438..56688d72c 100644 --- a/editors/awk.c +++ b/editors/awk.c | |||
@@ -727,7 +727,11 @@ static char *skip_spaces(char *p) | |||
727 | if (*p == '\\' && p[1] == '\n') { | 727 | if (*p == '\\' && p[1] == '\n') { |
728 | p++; | 728 | p++; |
729 | t_lineno++; | 729 | t_lineno++; |
730 | #if !ENABLE_PLATFORM_MINGW32 | ||
730 | } else if (*p != ' ' && *p != '\t') { | 731 | } else if (*p != ' ' && *p != '\t') { |
732 | #else | ||
733 | } else if (*p != ' ' && *p != '\t' && *p != '\r') { | ||
734 | #endif | ||
731 | break; | 735 | break; |
732 | } | 736 | } |
733 | p++; | 737 | p++; |
@@ -2073,7 +2077,7 @@ static int fmt_num(char *b, int size, const char *format, double n, int int_as_i | |||
2073 | const char *s = format; | 2077 | const char *s = format; |
2074 | 2078 | ||
2075 | if (int_as_int && n == (long long)n) { | 2079 | if (int_as_int && n == (long long)n) { |
2076 | r = snprintf(b, size, "%lld", (long long)n); | 2080 | r = snprintf(b, size, "%"LL_FMT"d", (long long)n); |
2077 | } else { | 2081 | } else { |
2078 | do { c = *s; } while (c && *++s); | 2082 | do { c = *s; } while (c && *++s); |
2079 | if (strchr("diouxX", c)) { | 2083 | if (strchr("diouxX", c)) { |
diff --git a/editors/diff.c b/editors/diff.c index 03c13908e..62f558944 100644 --- a/editors/diff.c +++ b/editors/diff.c | |||
@@ -711,6 +711,10 @@ static int diffreg(char *file[2]) | |||
711 | FILE *fp[2]; | 711 | FILE *fp[2]; |
712 | bool binary = false, differ = false; | 712 | bool binary = false, differ = false; |
713 | int status = STATUS_SAME, i; | 713 | int status = STATUS_SAME, i; |
714 | #if ENABLE_PLATFORM_MINGW32 | ||
715 | char *tmpfile[2] = { NULL, NULL }; | ||
716 | char *tmpdir; | ||
717 | #endif | ||
714 | 718 | ||
715 | fp[0] = stdin; | 719 | fp[0] = stdin; |
716 | fp[1] = stdin; | 720 | fp[1] = stdin; |
@@ -732,10 +736,19 @@ static int diffreg(char *file[2]) | |||
732 | * When we meet non-seekable file, we must make a temp copy. | 736 | * When we meet non-seekable file, we must make a temp copy. |
733 | */ | 737 | */ |
734 | if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) { | 738 | if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) { |
739 | #if !ENABLE_PLATFORM_MINGW32 | ||
735 | char name[] = "/tmp/difXXXXXX"; | 740 | char name[] = "/tmp/difXXXXXX"; |
736 | int fd_tmp = xmkstemp(name); | 741 | int fd_tmp = xmkstemp(name); |
737 | 742 | ||
738 | unlink(name); | 743 | unlink(name); |
744 | #else | ||
745 | int fd_tmp; | ||
746 | |||
747 | if (!(tmpdir=getenv("TMPDIR"))) | ||
748 | goto out; | ||
749 | tmpfile[i] = xasprintf("%s/difXXXXXX", tmpdir); | ||
750 | fd_tmp = xmkstemp(tmpfile[i]); | ||
751 | #endif | ||
739 | if (bb_copyfd_eof(fd, fd_tmp) < 0) | 752 | if (bb_copyfd_eof(fd, fd_tmp) < 0) |
740 | xfunc_die(); | 753 | xfunc_die(); |
741 | if (fd != STDIN_FILENO) | 754 | if (fd != STDIN_FILENO) |
@@ -778,6 +791,14 @@ static int diffreg(char *file[2]) | |||
778 | out: | 791 | out: |
779 | fclose_if_not_stdin(fp[0]); | 792 | fclose_if_not_stdin(fp[0]); |
780 | fclose_if_not_stdin(fp[1]); | 793 | fclose_if_not_stdin(fp[1]); |
794 | #if ENABLE_PLATFORM_MINGW32 | ||
795 | for (i = 0; i < 2; i++) { | ||
796 | if (tmpfile[i]) { | ||
797 | unlink(tmpfile[i]); | ||
798 | free(tmpfile[i]); | ||
799 | } | ||
800 | } | ||
801 | #endif | ||
781 | 802 | ||
782 | return status; | 803 | return status; |
783 | } | 804 | } |
diff --git a/editors/sed.c b/editors/sed.c index bec20040a..e10078b7c 100644 --- a/editors/sed.c +++ b/editors/sed.c | |||
@@ -1016,6 +1016,11 @@ static char *get_next_line(char *gets_char, char *last_puts_char) | |||
1016 | char c = temp[len-1]; | 1016 | char c = temp[len-1]; |
1017 | if (c == '\n' || c == '\0') { | 1017 | if (c == '\n' || c == '\0') { |
1018 | temp[len-1] = '\0'; | 1018 | temp[len-1] = '\0'; |
1019 | #if ENABLE_PLATFORM_MINGW32 | ||
1020 | if (c == '\n' && len > 1 && temp[len-2] == '\r') { | ||
1021 | temp[len-2] = '\0'; | ||
1022 | } | ||
1023 | #endif | ||
1019 | gc = c; | 1024 | gc = c; |
1020 | if (c == '\0') { | 1025 | if (c == '\0') { |
1021 | int ch = fgetc(fp); | 1026 | int ch = fgetc(fp); |
diff --git a/editors/vi.c b/editors/vi.c index 116022c93..91e954a87 100644 --- a/editors/vi.c +++ b/editors/vi.c | |||
@@ -2800,6 +2800,14 @@ static void catch_sig(int sig) | |||
2800 | 2800 | ||
2801 | static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready | 2801 | static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready |
2802 | { | 2802 | { |
2803 | #if ENABLE_PLATFORM_MINGW32 | ||
2804 | HANDLE h = GetStdHandle(STD_INPUT_HANDLE); | ||
2805 | DWORD ret; | ||
2806 | |||
2807 | fflush(stdout); | ||
2808 | ret = WaitForSingleObject(h, hund*10); | ||
2809 | return ret != WAIT_TIMEOUT; | ||
2810 | #else | ||
2803 | struct pollfd pfd[1]; | 2811 | struct pollfd pfd[1]; |
2804 | 2812 | ||
2805 | if (hund != 0) | 2813 | if (hund != 0) |
@@ -2808,6 +2816,7 @@ static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready | |||
2808 | pfd[0].fd = STDIN_FILENO; | 2816 | pfd[0].fd = STDIN_FILENO; |
2809 | pfd[0].events = POLLIN; | 2817 | pfd[0].events = POLLIN; |
2810 | return safe_poll(pfd, 1, hund*10) > 0; | 2818 | return safe_poll(pfd, 1, hund*10) > 0; |
2819 | #endif | ||
2811 | } | 2820 | } |
2812 | 2821 | ||
2813 | //----- IO Routines -------------------------------------------- | 2822 | //----- IO Routines -------------------------------------------- |
@@ -2936,6 +2945,9 @@ static int file_insert(const char *fn, char *p, int initial) | |||
2936 | status_line_bold("'%s' is not a regular file", fn); | 2945 | status_line_bold("'%s' is not a regular file", fn); |
2937 | goto fi; | 2946 | goto fi; |
2938 | } | 2947 | } |
2948 | #if ENABLE_PLATFORM_MINGW32 | ||
2949 | _setmode(fd, _O_TEXT); | ||
2950 | #endif | ||
2939 | size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX); | 2951 | size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX); |
2940 | p += text_hole_make(p, size); | 2952 | p += text_hole_make(p, size); |
2941 | cnt = full_read(fd, p, size); | 2953 | cnt = full_read(fd, p, size); |
@@ -2944,8 +2956,25 @@ static int file_insert(const char *fn, char *p, int initial) | |||
2944 | p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert | 2956 | p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert |
2945 | } else if (cnt < size) { | 2957 | } else if (cnt < size) { |
2946 | // There was a partial read, shrink unused space | 2958 | // There was a partial read, shrink unused space |
2959 | #if ENABLE_PLATFORM_MINGW32 | ||
2960 | int i, newline; | ||
2961 | |||
2962 | newline = 0; | ||
2963 | for ( i=0; i<cnt; ++i ) { | ||
2964 | if ( p[i] == '\n' ) { | ||
2965 | ++newline; | ||
2966 | } | ||
2967 | } | ||
2968 | #endif | ||
2947 | p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); | 2969 | p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); |
2970 | #if ENABLE_PLATFORM_MINGW32 | ||
2971 | // on WIN32 a partial read might just mean CRs have been removed | ||
2972 | if ( cnt+newline != size ) { | ||
2973 | status_line_bold("can't read '%s'", fn); | ||
2974 | } | ||
2975 | #else | ||
2948 | status_line_bold("can't read '%s'", fn); | 2976 | status_line_bold("can't read '%s'", fn); |
2977 | #endif | ||
2949 | } | 2978 | } |
2950 | fi: | 2979 | fi: |
2951 | close(fd); | 2980 | close(fd); |
@@ -2967,6 +2996,9 @@ static int file_insert(const char *fn, char *p, int initial) | |||
2967 | static int file_write(char *fn, char *first, char *last) | 2996 | static int file_write(char *fn, char *first, char *last) |
2968 | { | 2997 | { |
2969 | int fd, cnt, charcnt; | 2998 | int fd, cnt, charcnt; |
2999 | #if ENABLE_PLATFORM_MINGW32 | ||
3000 | int i, newline; | ||
3001 | #endif | ||
2970 | 3002 | ||
2971 | if (fn == 0) { | 3003 | if (fn == 0) { |
2972 | status_line_bold("No current filename"); | 3004 | status_line_bold("No current filename"); |
@@ -2980,8 +3012,23 @@ static int file_write(char *fn, char *first, char *last) | |||
2980 | if (fd < 0) | 3012 | if (fd < 0) |
2981 | return -1; | 3013 | return -1; |
2982 | cnt = last - first + 1; | 3014 | cnt = last - first + 1; |
3015 | #if ENABLE_PLATFORM_MINGW32 | ||
3016 | /* write file in text mode; this makes it bigger so adjust | ||
3017 | * the truncation to match | ||
3018 | */ | ||
3019 | _setmode(fd, _O_TEXT); | ||
3020 | newline = 0; | ||
3021 | for ( i=0; i<cnt; ++i ) { | ||
3022 | if ( first[i] == '\n' ) { | ||
3023 | ++newline; | ||
3024 | } | ||
3025 | } | ||
3026 | charcnt = full_write(fd, first, cnt); | ||
3027 | ftruncate(fd, charcnt+newline); | ||
3028 | #else | ||
2983 | charcnt = full_write(fd, first, cnt); | 3029 | charcnt = full_write(fd, first, cnt); |
2984 | ftruncate(fd, charcnt); | 3030 | ftruncate(fd, charcnt); |
3031 | #endif | ||
2985 | if (charcnt == cnt) { | 3032 | if (charcnt == cnt) { |
2986 | // good write | 3033 | // good write |
2987 | //modified_count = FALSE; | 3034 | //modified_count = FALSE; |
@@ -3032,7 +3079,12 @@ static void go_bottom_and_clear_to_eol(void) | |||
3032 | //----- Erase from cursor to end of screen ----------------------- | 3079 | //----- Erase from cursor to end of screen ----------------------- |
3033 | static void clear_to_eos(void) | 3080 | static void clear_to_eos(void) |
3034 | { | 3081 | { |
3082 | #if !ENABLE_PLATFORM_MINGW32 | ||
3035 | write1(ESC_CLEAR2EOS); | 3083 | write1(ESC_CLEAR2EOS); |
3084 | #else | ||
3085 | /* in practice clear_to_eos() always clears the entire screen */ | ||
3086 | reset_screen(); | ||
3087 | #endif | ||
3036 | } | 3088 | } |
3037 | 3089 | ||
3038 | //----- Start standout mode ------------------------------------ | 3090 | //----- Start standout mode ------------------------------------ |
@@ -3578,12 +3630,7 @@ static void do_cmd(int c) | |||
3578 | break; | 3630 | break; |
3579 | case 12: // ctrl-L force redraw whole screen | 3631 | case 12: // ctrl-L force redraw whole screen |
3580 | case 18: // ctrl-R force redraw | 3632 | case 18: // ctrl-R force redraw |
3581 | place_cursor(0, 0); | 3633 | redraw(TRUE); // this will redraw the entire display |
3582 | clear_to_eos(); | ||
3583 | //mysleep(10); // why??? | ||
3584 | screen_erase(); // erase the internal screen buffer | ||
3585 | last_status_cksum = 0; // force status update | ||
3586 | refresh(TRUE); // this will redraw the entire display | ||
3587 | break; | 3634 | break; |
3588 | case 13: // Carriage Return ^M | 3635 | case 13: // Carriage Return ^M |
3589 | case '+': // +- goto next line | 3636 | case '+': // +- goto next line |
diff --git a/findutils/xargs.c b/findutils/xargs.c index 2d0d1c8b9..e5384d14c 100644 --- a/findutils/xargs.c +++ b/findutils/xargs.c | |||
@@ -65,6 +65,9 @@ | |||
65 | 65 | ||
66 | //kbuild:lib-$(CONFIG_XARGS) += xargs.o | 66 | //kbuild:lib-$(CONFIG_XARGS) += xargs.o |
67 | 67 | ||
68 | #if ENABLE_PLATFORM_MINGW32 | ||
69 | #include <conio.h> | ||
70 | #endif | ||
68 | #include "libbb.h" | 71 | #include "libbb.h" |
69 | #include "common_bufsiz.h" | 72 | #include "common_bufsiz.h" |
70 | 73 | ||
@@ -405,13 +408,23 @@ static int xargs_ask_confirmation(void) | |||
405 | FILE *tty_stream; | 408 | FILE *tty_stream; |
406 | int c, savec; | 409 | int c, savec; |
407 | 410 | ||
411 | #if !ENABLE_PLATFORM_MINGW32 | ||
408 | tty_stream = xfopen_for_read(CURRENT_TTY); | 412 | tty_stream = xfopen_for_read(CURRENT_TTY); |
413 | #endif | ||
409 | fputs(" ?...", stderr); | 414 | fputs(" ?...", stderr); |
410 | fflush_all(); | 415 | fflush_all(); |
416 | #if !ENABLE_PLATFORM_MINGW32 | ||
411 | c = savec = getc(tty_stream); | 417 | c = savec = getc(tty_stream); |
412 | while (c != EOF && c != '\n') | 418 | while (c != EOF && c != '\n') |
413 | c = getc(tty_stream); | 419 | c = getc(tty_stream); |
414 | fclose(tty_stream); | 420 | fclose(tty_stream); |
421 | #else | ||
422 | c = savec = getche(); | ||
423 | while (c != EOF && c != '\r') | ||
424 | c = getche(); | ||
425 | fputs("\n", stderr); | ||
426 | fflush_all(); | ||
427 | #endif | ||
415 | return (savec == 'y' || savec == 'Y'); | 428 | return (savec == 'y' || savec == 'Y'); |
416 | } | 429 | } |
417 | #else | 430 | #else |
diff --git a/include/bb_archive.h b/include/bb_archive.h index d3762415f..c118fa7ec 100644 --- a/include/bb_archive.h +++ b/include/bb_archive.h | |||
@@ -2,6 +2,16 @@ | |||
2 | #ifndef UNARCHIVE_H | 2 | #ifndef UNARCHIVE_H |
3 | #define UNARCHIVE_H 1 | 3 | #define UNARCHIVE_H 1 |
4 | 4 | ||
5 | #if !defined(BB_ARCHIVE_PUBLIC) && ENABLE_PLATFORM_MINGW32 | ||
6 | /* treat mingw as a non-MMU platform */ | ||
7 | #undef BB_MMU | ||
8 | #undef USE_FOR_NOMMU | ||
9 | #undef USE_FOR_MMU | ||
10 | #define BB_MMU 0 | ||
11 | #define USE_FOR_NOMMU(...) __VA_ARGS__ | ||
12 | #define USE_FOR_MMU(...) | ||
13 | #endif | ||
14 | |||
5 | PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | 15 | PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN |
6 | 16 | ||
7 | enum { | 17 | enum { |
diff --git a/include/libbb.h b/include/libbb.h index 9aba71949..61ceb6085 100644 --- a/include/libbb.h +++ b/include/libbb.h | |||
@@ -138,6 +138,12 @@ | |||
138 | # include <arpa/inet.h> | 138 | # include <arpa/inet.h> |
139 | #elif defined __APPLE__ | 139 | #elif defined __APPLE__ |
140 | # include <netinet/in.h> | 140 | # include <netinet/in.h> |
141 | #elif ENABLE_PLATFORM_MINGW32 | ||
142 | # ifndef WINVER | ||
143 | # define WINVER 0x0501 | ||
144 | # endif | ||
145 | # include <winsock2.h> | ||
146 | # include <ws2tcpip.h> | ||
141 | #else | 147 | #else |
142 | # include <arpa/inet.h> | 148 | # include <arpa/inet.h> |
143 | //This breaks on bionic: | 149 | //This breaks on bionic: |
@@ -166,7 +172,9 @@ | |||
166 | 172 | ||
167 | /* Some libc's forget to declare these, do it ourself */ | 173 | /* Some libc's forget to declare these, do it ourself */ |
168 | 174 | ||
175 | #if !ENABLE_PLATFORM_MINGW32 | ||
169 | extern char **environ; | 176 | extern char **environ; |
177 | #endif | ||
170 | /* klogctl is in libc's klog.h, but we cheat and not #include that */ | 178 | /* klogctl is in libc's klog.h, but we cheat and not #include that */ |
171 | int klogctl(int type, char *b, int len); | 179 | int klogctl(int type, char *b, int len); |
172 | #ifndef PATH_MAX | 180 | #ifndef PATH_MAX |
@@ -176,6 +184,9 @@ int klogctl(int type, char *b, int len); | |||
176 | # define BUFSIZ 4096 | 184 | # define BUFSIZ 4096 |
177 | #endif | 185 | #endif |
178 | 186 | ||
187 | #if ENABLE_PLATFORM_MINGW32 | ||
188 | # include "mingw.h" | ||
189 | #endif | ||
179 | 190 | ||
180 | /* Busybox does not use threads, we can speed up stdio. */ | 191 | /* Busybox does not use threads, we can speed up stdio. */ |
181 | #ifdef HAVE_UNLOCKED_STDIO | 192 | #ifdef HAVE_UNLOCKED_STDIO |
@@ -238,6 +249,13 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
238 | : ((T)1 << (sizeof(T)*8-1)) \ | 249 | : ((T)1 << (sizeof(T)*8-1)) \ |
239 | ) | 250 | ) |
240 | 251 | ||
252 | #if ENABLE_PLATFORM_MINGW32 && \ | ||
253 | (!defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO) | ||
254 | #define LL_FMT "I64" | ||
255 | #else | ||
256 | #define LL_FMT "ll" | ||
257 | #endif | ||
258 | |||
241 | /* Large file support */ | 259 | /* Large file support */ |
242 | /* Note that CONFIG_LFS=y forces bbox to be built with all common ops | 260 | /* Note that CONFIG_LFS=y forces bbox to be built with all common ops |
243 | * (stat, lseek etc) mapped to "largefile" variants by libc. | 261 | * (stat, lseek etc) mapped to "largefile" variants by libc. |
@@ -263,7 +281,7 @@ typedef unsigned long long uoff_t; | |||
263 | # define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX) | 281 | # define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX) |
264 | # define BB_STRTOOFF bb_strtoull | 282 | # define BB_STRTOOFF bb_strtoull |
265 | # define STRTOOFF strtoull | 283 | # define STRTOOFF strtoull |
266 | # define OFF_FMT "ll" | 284 | # define OFF_FMT LL_FMT |
267 | # endif | 285 | # endif |
268 | #else | 286 | #else |
269 | /* CONFIG_LFS is off */ | 287 | /* CONFIG_LFS is off */ |
@@ -510,6 +528,7 @@ enum { | |||
510 | + (1LL << SIGUSR2) | 528 | + (1LL << SIGUSR2) |
511 | + 0), | 529 | + 0), |
512 | }; | 530 | }; |
531 | #if !ENABLE_PLATFORM_MINGW32 | ||
513 | void bb_signals(int sigs, void (*f)(int)) FAST_FUNC; | 532 | void bb_signals(int sigs, void (*f)(int)) FAST_FUNC; |
514 | /* Unlike signal() and bb_signals, sets handler with sigaction() | 533 | /* Unlike signal() and bb_signals, sets handler with sigaction() |
515 | * and in a way that while signal handler is run, no other signals | 534 | * and in a way that while signal handler is run, no other signals |
@@ -527,6 +546,10 @@ void sig_unblock(int sig) FAST_FUNC; | |||
527 | int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC; | 546 | int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC; |
528 | /* SIG_BLOCK/SIG_UNBLOCK all signals: */ | 547 | /* SIG_BLOCK/SIG_UNBLOCK all signals: */ |
529 | int sigprocmask_allsigs(int how) FAST_FUNC; | 548 | int sigprocmask_allsigs(int how) FAST_FUNC; |
549 | #else | ||
550 | #define bb_signals(s, f) | ||
551 | #define kill_myself_with_sig(s) | ||
552 | #endif | ||
530 | /* Standard handler which just records signo */ | 553 | /* Standard handler which just records signo */ |
531 | extern smallint bb_got_signal; | 554 | extern smallint bb_got_signal; |
532 | void record_signo(int signo); /* not FAST_FUNC! */ | 555 | void record_signo(int signo); /* not FAST_FUNC! */ |
@@ -606,7 +629,7 @@ char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC; | |||
606 | int xsocket(int domain, int type, int protocol) FAST_FUNC; | 629 | int xsocket(int domain, int type, int protocol) FAST_FUNC; |
607 | void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC; | 630 | void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC; |
608 | void xlisten(int s, int backlog) FAST_FUNC; | 631 | void xlisten(int s, int backlog) FAST_FUNC; |
609 | void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) FAST_FUNC; | 632 | void xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen) FAST_FUNC; |
610 | ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, | 633 | ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, |
611 | socklen_t tolen) FAST_FUNC; | 634 | socklen_t tolen) FAST_FUNC; |
612 | 635 | ||
@@ -941,6 +964,9 @@ char *safe_gethostname(void) FAST_FUNC; | |||
941 | char* str_tolower(char *str) FAST_FUNC; | 964 | char* str_tolower(char *str) FAST_FUNC; |
942 | 965 | ||
943 | char *utoa(unsigned n) FAST_FUNC; | 966 | char *utoa(unsigned n) FAST_FUNC; |
967 | #if ENABLE_PLATFORM_MINGW32 | ||
968 | # define itoa bb_itoa | ||
969 | #endif | ||
944 | char *itoa(int n) FAST_FUNC; | 970 | char *itoa(int n) FAST_FUNC; |
945 | /* Returns a pointer past the formatted number, does NOT null-terminate */ | 971 | /* Returns a pointer past the formatted number, does NOT null-terminate */ |
946 | char *utoa_to_buf(unsigned n, char *buf, unsigned buflen) FAST_FUNC; | 972 | char *utoa_to_buf(unsigned n, char *buf, unsigned buflen) FAST_FUNC; |
@@ -1116,6 +1142,9 @@ int run_nofork_applet(int applet_no, char **argv) FAST_FUNC; | |||
1116 | extern int find_applet_by_name(const char *name) FAST_FUNC; | 1142 | extern int find_applet_by_name(const char *name) FAST_FUNC; |
1117 | extern void run_applet_no_and_exit(int a, const char *name, char **argv) NORETURN FAST_FUNC; | 1143 | extern void run_applet_no_and_exit(int a, const char *name, char **argv) NORETURN FAST_FUNC; |
1118 | #endif | 1144 | #endif |
1145 | #if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_NOFORK | ||
1146 | extern int long_running_applet(int applet_no) FAST_FUNC; | ||
1147 | #endif | ||
1119 | 1148 | ||
1120 | /* Helpers for daemonization. | 1149 | /* Helpers for daemonization. |
1121 | * | 1150 | * |
@@ -1199,7 +1228,7 @@ extern uint32_t getopt32(char **argv, const char *applet_opts, ...) FAST_FUNC; | |||
1199 | * By ~2008, OpenBSD 3.4 was changed to survive glibc-like optind = 0 | 1228 | * By ~2008, OpenBSD 3.4 was changed to survive glibc-like optind = 0 |
1200 | * (to interpret it as if optreset was set). | 1229 | * (to interpret it as if optreset was set). |
1201 | */ | 1230 | */ |
1202 | #ifdef __GLIBC__ | 1231 | #if defined(__GLIBC__) || ENABLE_PLATFORM_MINGW32 |
1203 | #define GETOPT_RESET() (optind = 0) | 1232 | #define GETOPT_RESET() (optind = 0) |
1204 | #else /* BSD style */ | 1233 | #else /* BSD style */ |
1205 | #define GETOPT_RESET() (optind = 1) | 1234 | #define GETOPT_RESET() (optind = 1) |
@@ -1776,6 +1805,9 @@ typedef struct procps_status_t { | |||
1776 | #if ENABLE_FEATURE_TOP_SMP_PROCESS | 1805 | #if ENABLE_FEATURE_TOP_SMP_PROCESS |
1777 | int last_seen_on_cpu; | 1806 | int last_seen_on_cpu; |
1778 | #endif | 1807 | #endif |
1808 | #if ENABLE_PLATFORM_MINGW32 | ||
1809 | HANDLE snapshot; | ||
1810 | #endif | ||
1779 | } procps_status_t; | 1811 | } procps_status_t; |
1780 | /* flag bits for procps_scan(xx, flags) calls */ | 1812 | /* flag bits for procps_scan(xx, flags) calls */ |
1781 | enum { | 1813 | enum { |
@@ -1814,7 +1846,11 @@ void free_procps_scan(procps_status_t* sp) FAST_FUNC; | |||
1814 | procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; | 1846 | procps_status_t* procps_scan(procps_status_t* sp, int flags) FAST_FUNC; |
1815 | /* Format cmdline (up to col chars) into char buf[size] */ | 1847 | /* Format cmdline (up to col chars) into char buf[size] */ |
1816 | /* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ | 1848 | /* Puts [comm] if cmdline is empty (-> process is a kernel thread) */ |
1849 | #if !ENABLE_PLATFORM_MINGW32 | ||
1817 | void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; | 1850 | void read_cmdline(char *buf, int size, unsigned pid, const char *comm) FAST_FUNC; |
1851 | #else | ||
1852 | #define read_cmdline(buf, size, pid, comm) snprintf(buf, size, "[%s]", comm) | ||
1853 | #endif | ||
1818 | pid_t *find_pid_by_name(const char* procName) FAST_FUNC; | 1854 | pid_t *find_pid_by_name(const char* procName) FAST_FUNC; |
1819 | pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; | 1855 | pid_t *pidlist_reverse(pid_t *pidList) FAST_FUNC; |
1820 | int starts_with_cpu(const char *str) FAST_FUNC; | 1856 | int starts_with_cpu(const char *str) FAST_FUNC; |
@@ -1958,7 +1994,11 @@ extern const char bb_path_wtmp_file[] ALIGN1; | |||
1958 | #define bb_path_motd_file "/etc/motd" | 1994 | #define bb_path_motd_file "/etc/motd" |
1959 | 1995 | ||
1960 | #define bb_dev_null "/dev/null" | 1996 | #define bb_dev_null "/dev/null" |
1997 | #if ENABLE_PLATFORM_MINGW32 | ||
1998 | #define bb_busybox_exec_path get_busybox_exec_path() | ||
1999 | #else | ||
1961 | extern const char bb_busybox_exec_path[] ALIGN1; | 2000 | extern const char bb_busybox_exec_path[] ALIGN1; |
2001 | #endif | ||
1962 | /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, | 2002 | /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, |
1963 | * but I want to save a few bytes here */ | 2003 | * but I want to save a few bytes here */ |
1964 | extern const char bb_PATH_root_path[] ALIGN1; /* "PATH=/sbin:/usr/sbin:/bin:/usr/bin" */ | 2004 | extern const char bb_PATH_root_path[] ALIGN1; /* "PATH=/sbin:/usr/sbin:/bin:/usr/bin" */ |
diff --git a/include/mingw.h b/include/mingw.h new file mode 100644 index 000000000..5f1074160 --- /dev/null +++ b/include/mingw.h | |||
@@ -0,0 +1,484 @@ | |||
1 | |||
2 | #define NOIMPL(name,...) static inline int name(__VA_ARGS__) { errno = ENOSYS; return -1; } | ||
3 | #define IMPL(name,ret,retval,...) static inline ret name(__VA_ARGS__) { return retval; } | ||
4 | |||
5 | /* | ||
6 | * sys/types.h | ||
7 | */ | ||
8 | typedef int gid_t; | ||
9 | typedef int uid_t; | ||
10 | #ifndef _WIN64 | ||
11 | typedef int pid_t; | ||
12 | #else | ||
13 | typedef __int64 pid_t; | ||
14 | #endif | ||
15 | |||
16 | #define DEFAULT_UID 1000 | ||
17 | #define DEFAULT_GID 1000 | ||
18 | |||
19 | /* | ||
20 | * arpa/inet.h | ||
21 | */ | ||
22 | static inline unsigned int git_ntohl(unsigned int x) { return (unsigned int)ntohl(x); } | ||
23 | #define ntohl git_ntohl | ||
24 | int inet_aton(const char *cp, struct in_addr *inp); | ||
25 | int inet_pton(int af, const char *src, void *dst); | ||
26 | |||
27 | /* | ||
28 | * fcntl.h | ||
29 | */ | ||
30 | #define F_DUPFD 0 | ||
31 | #define F_GETFD 1 | ||
32 | #define F_SETFD 2 | ||
33 | #define F_GETFL 3 | ||
34 | #define F_SETFL 3 | ||
35 | #define FD_CLOEXEC 0x1 | ||
36 | #define O_NONBLOCK 0 | ||
37 | #define O_NOFOLLOW 0 | ||
38 | |||
39 | /* | ||
40 | * grp.h | ||
41 | */ | ||
42 | |||
43 | struct group { | ||
44 | char *gr_name; | ||
45 | char *gr_passwd; | ||
46 | gid_t gr_gid; | ||
47 | char **gr_mem; | ||
48 | }; | ||
49 | IMPL(getgrnam,struct group *,NULL,const char *name UNUSED_PARAM); | ||
50 | struct group *getgrgid(gid_t gid); | ||
51 | NOIMPL(initgroups,const char *group UNUSED_PARAM,gid_t gid UNUSED_PARAM); | ||
52 | static inline void endgrent(void) {} | ||
53 | int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups); | ||
54 | |||
55 | /* | ||
56 | * limits.h | ||
57 | */ | ||
58 | #define NAME_MAX 255 | ||
59 | #define MAXSYMLINKS 20 | ||
60 | |||
61 | /* | ||
62 | * netdb.h | ||
63 | */ | ||
64 | |||
65 | typedef int sa_family_t; | ||
66 | |||
67 | /* | ||
68 | * linux/un.h | ||
69 | */ | ||
70 | struct sockaddr_un { | ||
71 | sa_family_t sun_family; | ||
72 | char sun_path[1]; /* to make compiler happy, don't bother */ | ||
73 | }; | ||
74 | |||
75 | /* | ||
76 | * pwd.h | ||
77 | */ | ||
78 | struct passwd { | ||
79 | char *pw_name; | ||
80 | char *pw_passwd; | ||
81 | char *pw_gecos; | ||
82 | char *pw_dir; | ||
83 | char *pw_shell; | ||
84 | uid_t pw_uid; | ||
85 | gid_t pw_gid; | ||
86 | }; | ||
87 | |||
88 | struct passwd *getpwnam(const char *name); | ||
89 | struct passwd *getpwuid(uid_t uid); | ||
90 | static inline void setpwent(void) {} | ||
91 | static inline void endpwent(void) {} | ||
92 | IMPL(getpwent_r,int,ENOENT,struct passwd *pwbuf UNUSED_PARAM,char *buf UNUSED_PARAM,size_t buflen UNUSED_PARAM,struct passwd **pwbufp UNUSED_PARAM); | ||
93 | IMPL(getpwent,struct passwd *,NULL,void) | ||
94 | |||
95 | /* | ||
96 | * signal.h | ||
97 | */ | ||
98 | #define SIGHUP 1 | ||
99 | #define SIGQUIT 3 | ||
100 | #define SIGKILL 9 | ||
101 | #define SIGUSR1 10 | ||
102 | #define SIGUSR2 12 | ||
103 | #define SIGPIPE 13 | ||
104 | #define SIGALRM 14 | ||
105 | #define SIGCHLD 17 | ||
106 | #define SIGCONT 18 | ||
107 | #define SIGSTOP 19 | ||
108 | #define SIGTSTP 20 | ||
109 | #define SIGTTIN 21 | ||
110 | #define SIGTTOU 22 | ||
111 | #define SIGXCPU 24 | ||
112 | #define SIGXFSZ 25 | ||
113 | #define SIGVTALRM 26 | ||
114 | #define SIGWINCH 28 | ||
115 | |||
116 | #define SIG_UNBLOCK 1 | ||
117 | |||
118 | typedef void (__cdecl *sighandler_t)(int); | ||
119 | struct sigaction { | ||
120 | sighandler_t sa_handler; | ||
121 | unsigned sa_flags; | ||
122 | int sa_mask; | ||
123 | }; | ||
124 | #define sigemptyset(x) (void)0 | ||
125 | #define SA_RESTART 0 | ||
126 | |||
127 | NOIMPL(sigaction,int sig UNUSED_PARAM, struct sigaction *in UNUSED_PARAM, struct sigaction *out UNUSED_PARAM); | ||
128 | NOIMPL(sigfillset,int *mask UNUSED_PARAM); | ||
129 | NOIMPL(FAST_FUNC sigprocmask_allsigs, int how UNUSED_PARAM); | ||
130 | NOIMPL(FAST_FUNC sigaction_set,int signo UNUSED_PARAM, const struct sigaction *sa UNUSED_PARAM); | ||
131 | |||
132 | /* | ||
133 | * stdio.h | ||
134 | */ | ||
135 | #undef fseeko | ||
136 | #define fseeko(f,o,w) fseek(f,o,w) | ||
137 | |||
138 | int fdprintf(int fd, const char *format, ...); | ||
139 | FILE* mingw_fopen(const char *filename, const char *mode); | ||
140 | int mingw_rename(const char*, const char*); | ||
141 | #define fopen mingw_fopen | ||
142 | #define rename mingw_rename | ||
143 | |||
144 | FILE *mingw_popen(const char *cmd, const char *mode); | ||
145 | int mingw_popen_fd(const char *cmd, const char *mode, int fd0, pid_t *pid); | ||
146 | int mingw_pclose(FILE *fd); | ||
147 | #undef popen | ||
148 | #undef pclose | ||
149 | #define popen mingw_popen | ||
150 | #define pclose mingw_pclose | ||
151 | |||
152 | #define setlinebuf(fd) setvbuf(fd, (char *) NULL, _IOLBF, 0) | ||
153 | |||
154 | /* | ||
155 | * ANSI emulation wrappers | ||
156 | */ | ||
157 | |||
158 | void move_cursor_row(int n); | ||
159 | void reset_screen(void); | ||
160 | int winansi_putchar(int c); | ||
161 | int winansi_puts(const char *s); | ||
162 | size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); | ||
163 | int winansi_fputs(const char *str, FILE *stream); | ||
164 | int winansi_vfprintf(FILE *stream, const char *format, va_list list); | ||
165 | int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2))); | ||
166 | int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3))); | ||
167 | int winansi_write(int fd, const void *buf, size_t count); | ||
168 | int winansi_read(int fd, void *buf, size_t count); | ||
169 | int winansi_getc(FILE *stream); | ||
170 | #define putchar winansi_putchar | ||
171 | #define puts winansi_puts | ||
172 | #define fwrite winansi_fwrite | ||
173 | #define fputs winansi_fputs | ||
174 | #define vfprintf(stream, ...) winansi_vfprintf(stream, __VA_ARGS__) | ||
175 | #define vprintf(...) winansi_vfprintf(stdout, __VA_ARGS__) | ||
176 | #define printf(...) winansi_printf(__VA_ARGS__) | ||
177 | #define fprintf(...) winansi_fprintf(__VA_ARGS__) | ||
178 | #define write winansi_write | ||
179 | #define read winansi_read | ||
180 | #define getc winansi_getc | ||
181 | |||
182 | int winansi_get_terminal_width_height(struct winsize *win); | ||
183 | |||
184 | /* | ||
185 | * stdlib.h | ||
186 | */ | ||
187 | #define WIFEXITED(x) ((unsigned)(x) < 259) /* STILL_ACTIVE */ | ||
188 | #define WEXITSTATUS(x) ((x) & 0xff) | ||
189 | #define WIFSIGNALED(x) ((unsigned)(x) > 259) | ||
190 | #define WTERMSIG(x) ((x) & 0x7f) | ||
191 | #define WCOREDUMP(x) 0 | ||
192 | |||
193 | int mingw_system(const char *cmd); | ||
194 | #define system mingw_system | ||
195 | |||
196 | int clearenv(void); | ||
197 | char *mingw_getenv(const char *name); | ||
198 | int mingw_putenv(const char *env); | ||
199 | char *mingw_mktemp(char *template); | ||
200 | int mkstemp(char *template); | ||
201 | char *realpath(const char *path, char *resolved_path); | ||
202 | int setenv(const char *name, const char *value, int replace); | ||
203 | #if ENABLE_SAFE_ENV | ||
204 | int unsetenv(const char *env); | ||
205 | #else | ||
206 | void unsetenv(const char *env); | ||
207 | #endif | ||
208 | |||
209 | #define getenv mingw_getenv | ||
210 | #if ENABLE_SAFE_ENV | ||
211 | #define putenv mingw_putenv | ||
212 | #endif | ||
213 | #define mktemp mingw_mktemp | ||
214 | |||
215 | /* | ||
216 | * string.h | ||
217 | */ | ||
218 | void *mempcpy(void *dest, const void *src, size_t n); | ||
219 | |||
220 | /* | ||
221 | * strings.h | ||
222 | */ | ||
223 | int ffs(int i); | ||
224 | |||
225 | /* | ||
226 | * sys/ioctl.h | ||
227 | */ | ||
228 | |||
229 | #define TIOCGWINSZ 0x5413 | ||
230 | |||
231 | int ioctl(int fd, int code, ...); | ||
232 | |||
233 | /* | ||
234 | * sys/socket.h | ||
235 | */ | ||
236 | #define hstrerror strerror | ||
237 | |||
238 | #define SHUT_WR SD_SEND | ||
239 | |||
240 | int mingw_socket(int domain, int type, int protocol); | ||
241 | int mingw_connect(int sockfd, const struct sockaddr *sa, size_t sz); | ||
242 | int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz); | ||
243 | int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen); | ||
244 | int mingw_shutdown(int sockfd, int how); | ||
245 | int mingw_listen(int sockfd, int backlog); | ||
246 | int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz); | ||
247 | int mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, | ||
248 | struct timeval *timeout); | ||
249 | |||
250 | NOIMPL(mingw_sendto,SOCKET s UNUSED_PARAM, const char *buf UNUSED_PARAM, int len UNUSED_PARAM, int flags UNUSED_PARAM, const struct sockaddr *sa UNUSED_PARAM, int salen UNUSED_PARAM); | ||
251 | |||
252 | #define socket mingw_socket | ||
253 | #define connect mingw_connect | ||
254 | #define sendto mingw_sendto | ||
255 | #define listen mingw_listen | ||
256 | #define bind mingw_bind | ||
257 | #define setsockopt mingw_setsockopt | ||
258 | #define shutdown mingw_shutdown | ||
259 | #define accept mingw_accept | ||
260 | #define select mingw_select | ||
261 | |||
262 | /* | ||
263 | * sys/stat.h | ||
264 | */ | ||
265 | #define S_ISUID 04000 | ||
266 | #define S_ISGID 02000 | ||
267 | #define S_ISVTX 01000 | ||
268 | #ifndef S_IRWXU | ||
269 | #define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR) | ||
270 | #endif | ||
271 | #define S_IRWXG (S_IRWXU >> 3) | ||
272 | #define S_IRWXO (S_IRWXG >> 3) | ||
273 | |||
274 | #define S_IFSOCK 0140000 | ||
275 | #define S_IFLNK 0120000 /* Symbolic link */ | ||
276 | #define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK) | ||
277 | #define S_ISSOCK(x) 0 | ||
278 | |||
279 | #define S_IRGRP (S_IRUSR >> 3) | ||
280 | #define S_IWGRP (S_IWUSR >> 3) | ||
281 | #define S_IXGRP (S_IXUSR >> 3) | ||
282 | #define S_IROTH (S_IRGRP >> 3) | ||
283 | #define S_IWOTH (S_IWGRP >> 3) | ||
284 | #define S_IXOTH (S_IXGRP >> 3) | ||
285 | |||
286 | IMPL(fchmod,int,0,int fildes UNUSED_PARAM, mode_t mode UNUSED_PARAM); | ||
287 | NOIMPL(fchown,int fd UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM); | ||
288 | int mingw_mkdir(const char *path, int mode); | ||
289 | int mingw_chmod(const char *path, int mode); | ||
290 | |||
291 | #define mkdir mingw_mkdir | ||
292 | #define chmod mingw_chmod | ||
293 | |||
294 | #if ENABLE_LFS && !defined(__MINGW64_VERSION_MAJOR) | ||
295 | # define off_t off64_t | ||
296 | #endif | ||
297 | |||
298 | typedef int nlink_t; | ||
299 | typedef int blksize_t; | ||
300 | typedef off_t blkcnt_t; | ||
301 | |||
302 | struct mingw_stat { | ||
303 | dev_t st_dev; | ||
304 | ino_t st_ino; | ||
305 | mode_t st_mode; | ||
306 | nlink_t st_nlink; | ||
307 | uid_t st_uid; | ||
308 | gid_t st_gid; | ||
309 | dev_t st_rdev; | ||
310 | off_t st_size; | ||
311 | time_t st_atime; | ||
312 | time_t st_mtime; | ||
313 | time_t st_ctime; | ||
314 | blksize_t st_blksize; | ||
315 | blkcnt_t st_blocks; | ||
316 | }; | ||
317 | |||
318 | int mingw_lstat(const char *file_name, struct mingw_stat *buf); | ||
319 | int mingw_stat(const char *file_name, struct mingw_stat *buf); | ||
320 | int mingw_fstat(int fd, struct mingw_stat *buf); | ||
321 | #undef lstat | ||
322 | #undef stat | ||
323 | #undef fstat | ||
324 | #define lstat mingw_lstat | ||
325 | #define stat mingw_stat | ||
326 | #define fstat mingw_fstat | ||
327 | |||
328 | /* | ||
329 | * sys/sysmacros.h | ||
330 | */ | ||
331 | #define makedev(a,b) 0*(a)*(b) /* avoid unused warning */ | ||
332 | #define minor(x) 0 | ||
333 | #define major(x) 0 | ||
334 | |||
335 | /* | ||
336 | * sys/time.h | ||
337 | */ | ||
338 | #ifndef _TIMESPEC_DEFINED | ||
339 | #define _TIMESPEC_DEFINED | ||
340 | struct timespec { | ||
341 | time_t tv_sec; | ||
342 | long int tv_nsec; | ||
343 | }; | ||
344 | #endif | ||
345 | |||
346 | int nanosleep(const struct timespec *req, struct timespec *rem); | ||
347 | |||
348 | /* | ||
349 | * sys/wait.h | ||
350 | */ | ||
351 | #define WNOHANG 1 | ||
352 | #define WUNTRACED 2 | ||
353 | int waitpid(pid_t pid, int *status, int options); | ||
354 | |||
355 | /* | ||
356 | * time.h | ||
357 | */ | ||
358 | struct tm *gmtime_r(const time_t *timep, struct tm *result); | ||
359 | struct tm *localtime_r(const time_t *timep, struct tm *result); | ||
360 | char *strptime(const char *s, const char *format, struct tm *tm); | ||
361 | size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm); | ||
362 | int stime(time_t *t); | ||
363 | |||
364 | #define strftime mingw_strftime | ||
365 | |||
366 | /* | ||
367 | * times.h | ||
368 | */ | ||
369 | #define clock_t long | ||
370 | |||
371 | struct tms { | ||
372 | clock_t tms_utime; /* user CPU time */ | ||
373 | clock_t tms_stime; /* system CPU time */ | ||
374 | clock_t tms_cutime; /* user CPU time of children */ | ||
375 | clock_t tms_cstime; /* system CPU time of children */ | ||
376 | }; | ||
377 | |||
378 | clock_t times(struct tms *buf); | ||
379 | |||
380 | /* | ||
381 | * unistd.h | ||
382 | */ | ||
383 | #define PIPE_BUF 8192 | ||
384 | |||
385 | #define _SC_CLK_TCK 2 | ||
386 | |||
387 | IMPL(alarm,unsigned int,0,unsigned int seconds UNUSED_PARAM); | ||
388 | IMPL(chown,int,0,const char *path UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM); | ||
389 | NOIMPL(chroot,const char *root UNUSED_PARAM); | ||
390 | NOIMPL(fchdir,int fd UNUSED_PARAM); | ||
391 | int mingw_dup2 (int fd, int fdto); | ||
392 | char *mingw_getcwd(char *pointer, int len); | ||
393 | off_t mingw_lseek(int fd, off_t offset, int whence); | ||
394 | |||
395 | |||
396 | IMPL(getgid,int,DEFAULT_GID,void); | ||
397 | int getgroups(int n, gid_t *groups); | ||
398 | IMPL(getppid,int,1,void); | ||
399 | IMPL(getegid,int,DEFAULT_GID,void); | ||
400 | IMPL(geteuid,int,DEFAULT_UID,void); | ||
401 | NOIMPL(getsid,pid_t pid UNUSED_PARAM); | ||
402 | IMPL(getuid,int,DEFAULT_UID,void); | ||
403 | int getlogin_r(char *buf, size_t len); | ||
404 | int fcntl(int fd, int cmd, ...); | ||
405 | #define fork() -1 | ||
406 | IMPL(fsync,int,0,int fd UNUSED_PARAM); | ||
407 | int kill(pid_t pid, int sig); | ||
408 | int link(const char *oldpath, const char *newpath); | ||
409 | NOIMPL(mknod,const char *name UNUSED_PARAM, mode_t mode UNUSED_PARAM, dev_t device UNUSED_PARAM); | ||
410 | int mingw_open (const char *filename, int oflags, ...); | ||
411 | int pipe(int filedes[2]); | ||
412 | NOIMPL(readlink,const char *path UNUSED_PARAM, char *buf UNUSED_PARAM, size_t bufsiz UNUSED_PARAM); | ||
413 | NOIMPL(setgid,gid_t gid UNUSED_PARAM); | ||
414 | NOIMPL(setegid,gid_t gid UNUSED_PARAM); | ||
415 | NOIMPL(setsid,void); | ||
416 | NOIMPL(setuid,uid_t gid UNUSED_PARAM); | ||
417 | NOIMPL(seteuid,uid_t gid UNUSED_PARAM); | ||
418 | unsigned int sleep(unsigned int seconds); | ||
419 | NOIMPL(symlink,const char *oldpath UNUSED_PARAM, const char *newpath UNUSED_PARAM); | ||
420 | static inline void sync(void) {} | ||
421 | long sysconf(int name); | ||
422 | NOIMPL(ttyname_r,int fd UNUSED_PARAM, char *buf UNUSED_PARAM, int sz UNUSED_PARAM); | ||
423 | int mingw_unlink(const char *pathname); | ||
424 | NOIMPL(vfork,void); | ||
425 | int mingw_access(const char *name, int mode); | ||
426 | int mingw_rmdir(const char *name); | ||
427 | |||
428 | #define dup2 mingw_dup2 | ||
429 | #define getcwd mingw_getcwd | ||
430 | #define lchown chown | ||
431 | #define open mingw_open | ||
432 | #define unlink mingw_unlink | ||
433 | #define rmdir mingw_rmdir | ||
434 | #undef lseek | ||
435 | #define lseek mingw_lseek | ||
436 | |||
437 | #undef access | ||
438 | #define access mingw_access | ||
439 | |||
440 | /* | ||
441 | * utime.h | ||
442 | */ | ||
443 | int utimes(const char *file_name, const struct timeval times[2]); | ||
444 | |||
445 | /* | ||
446 | * dirent.h | ||
447 | */ | ||
448 | DIR *mingw_opendir(const char *path); | ||
449 | #define opendir mingw_opendir | ||
450 | |||
451 | /* | ||
452 | * MinGW specific | ||
453 | */ | ||
454 | #define is_dir_sep(c) ((c) == '/' || (c) == '\\') | ||
455 | #define PRIuMAX "I64u" | ||
456 | |||
457 | pid_t FAST_FUNC mingw_spawn(char **argv); | ||
458 | intptr_t FAST_FUNC mingw_spawn_proc(char **argv); | ||
459 | int mingw_execv(const char *cmd, const char *const *argv); | ||
460 | int mingw_execvp(const char *cmd, const char *const *argv); | ||
461 | int mingw_execve(const char *cmd, const char *const *argv, const char *const *envp); | ||
462 | #define spawn mingw_spawn | ||
463 | #define execvp mingw_execvp | ||
464 | #define execve mingw_execve | ||
465 | #define execv mingw_execv | ||
466 | |||
467 | const char * next_path_sep(const char *path); | ||
468 | #define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':') | ||
469 | #define is_absolute_path(path) ((path)[0] == '/' || (path)[0] == '\\' || has_dos_drive_prefix(path)) | ||
470 | |||
471 | /* | ||
472 | * helpers | ||
473 | */ | ||
474 | |||
475 | char **copy_environ(const char *const *env); | ||
476 | void free_environ(char **env); | ||
477 | char **env_setenv(char **env, const char *name); | ||
478 | |||
479 | const char *get_busybox_exec_path(void); | ||
480 | void init_winsock(void); | ||
481 | |||
482 | char *file_is_win32_executable(const char *p); | ||
483 | |||
484 | int err_win_to_posix(DWORD winerr); | ||
diff --git a/include/platform.h b/include/platform.h index b81c59d4e..749169b0c 100644 --- a/include/platform.h +++ b/include/platform.h | |||
@@ -7,6 +7,15 @@ | |||
7 | #ifndef BB_PLATFORM_H | 7 | #ifndef BB_PLATFORM_H |
8 | #define BB_PLATFORM_H 1 | 8 | #define BB_PLATFORM_H 1 |
9 | 9 | ||
10 | #if ENABLE_PLATFORM_MINGW32 | ||
11 | # if !defined(__MINGW32__) /* HOSTCC is called */ | ||
12 | # undef ENABLE_PLATFORM_MINGW32 | ||
13 | # endif | ||
14 | #else | ||
15 | # if defined(__MINGW32__) | ||
16 | # error "You must select target platform MS Windows, or it won't build" | ||
17 | # endif | ||
18 | #endif | ||
10 | 19 | ||
11 | /* Convenience macros to test the version of gcc. */ | 20 | /* Convenience macros to test the version of gcc. */ |
12 | #undef __GNUC_PREREQ | 21 | #undef __GNUC_PREREQ |
@@ -131,7 +140,7 @@ | |||
131 | 140 | ||
132 | /* Make all declarations hidden (-fvisibility flag only affects definitions) */ | 141 | /* Make all declarations hidden (-fvisibility flag only affects definitions) */ |
133 | /* (don't include system headers after this until corresponding pop!) */ | 142 | /* (don't include system headers after this until corresponding pop!) */ |
134 | #if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__) | 143 | #if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__) && !ENABLE_PLATFORM_MINGW32 |
135 | # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)") | 144 | # define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)") |
136 | # define POP_SAVED_FUNCTION_VISIBILITY _Pragma("GCC visibility pop") | 145 | # define POP_SAVED_FUNCTION_VISIBILITY _Pragma("GCC visibility pop") |
137 | #else | 146 | #else |
@@ -160,6 +169,14 @@ | |||
160 | # define bswap_64 __bswap64 | 169 | # define bswap_64 __bswap64 |
161 | # define bswap_32 __bswap32 | 170 | # define bswap_32 __bswap32 |
162 | # define bswap_16 __bswap16 | 171 | # define bswap_16 __bswap16 |
172 | # define __BIG_ENDIAN__ (_BYTE_ORDER == _BIG_ENDIAN) | ||
173 | #elif ENABLE_PLATFORM_MINGW32 | ||
174 | # define __BIG_ENDIAN 0 | ||
175 | # define __LITTLE_ENDIAN 1 | ||
176 | # define __BYTE_ORDER __LITTLE_ENDIAN | ||
177 | # define bswap_16(x) ((((x) & 0xFF00) >> 8) | (((x) & 0xFF) << 8)) | ||
178 | # define bswap_32(x) ((bswap_16(((x) & 0xFFFF0000L) >> 16)) | (bswap_16((x) & 0xFFFFL) << 16)) | ||
179 | # define bswap_64(x) ((bswap_32(((x) & 0xFFFFFFFF00000000LL) >> 32)) | (bswap_32((x) & 0xFFFFFFFFLL) << 32)) | ||
163 | #else | 180 | #else |
164 | # include <byteswap.h> | 181 | # include <byteswap.h> |
165 | # include <endian.h> | 182 | # include <endian.h> |
@@ -419,6 +436,26 @@ typedef unsigned smalluint; | |||
419 | # endif | 436 | # endif |
420 | #endif | 437 | #endif |
421 | 438 | ||
439 | #if ENABLE_PLATFORM_MINGW32 | ||
440 | # undef HAVE_DPRINTF | ||
441 | # undef HAVE_GETLINE | ||
442 | # undef HAVE_MEMRCHR | ||
443 | # undef HAVE_MKDTEMP | ||
444 | # undef HAVE_SETBIT | ||
445 | # undef HAVE_STPCPY | ||
446 | # undef HAVE_STRCASESTR | ||
447 | # undef HAVE_STRCHRNUL | ||
448 | # undef HAVE_STRSEP | ||
449 | # undef HAVE_STRSIGNAL | ||
450 | # undef HAVE_STRVERSCMP | ||
451 | #if !defined(__MINGW64_VERSION_MAJOR) | ||
452 | # undef HAVE_VASPRINTF | ||
453 | #endif | ||
454 | # undef HAVE_UNLOCKED_STDIO | ||
455 | # undef HAVE_UNLOCKED_LINE_OPS | ||
456 | # undef HAVE_PRINTF_PERCENTM | ||
457 | #endif | ||
458 | |||
422 | #if defined(__WATCOMC__) | 459 | #if defined(__WATCOMC__) |
423 | # undef HAVE_DPRINTF | 460 | # undef HAVE_DPRINTF |
424 | # undef HAVE_GETLINE | 461 | # undef HAVE_GETLINE |
@@ -533,6 +570,7 @@ extern int dprintf(int fd, const char *format, ...); | |||
533 | #endif | 570 | #endif |
534 | 571 | ||
535 | #ifndef HAVE_MEMRCHR | 572 | #ifndef HAVE_MEMRCHR |
573 | #include <stddef.h> | ||
536 | extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; | 574 | extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; |
537 | #endif | 575 | #endif |
538 | 576 | ||
@@ -592,6 +630,7 @@ extern int usleep(unsigned) FAST_FUNC; | |||
592 | #endif | 630 | #endif |
593 | 631 | ||
594 | #ifndef HAVE_VASPRINTF | 632 | #ifndef HAVE_VASPRINTF |
633 | # include <stdarg.h> | ||
595 | extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; | 634 | extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; |
596 | #endif | 635 | #endif |
597 | 636 | ||
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src index 458973f17..6749cceda 100644 --- a/libbb/Kbuild.src +++ b/libbb/Kbuild.src | |||
@@ -37,24 +37,19 @@ lib-y += fgets_str.o | |||
37 | lib-y += find_pid_by_name.o | 37 | lib-y += find_pid_by_name.o |
38 | lib-y += find_root_device.o | 38 | lib-y += find_root_device.o |
39 | lib-y += full_write.o | 39 | lib-y += full_write.o |
40 | lib-y += get_console.o | ||
41 | lib-y += get_last_path_component.o | 40 | lib-y += get_last_path_component.o |
42 | lib-y += get_line_from_file.o | 41 | lib-y += get_line_from_file.o |
43 | lib-y += getopt32.o | 42 | lib-y += getopt32.o |
44 | lib-y += getpty.o | ||
45 | lib-y += get_volsize.o | 43 | lib-y += get_volsize.o |
46 | lib-y += herror_msg.o | 44 | lib-y += herror_msg.o |
47 | lib-y += human_readable.o | 45 | lib-y += human_readable.o |
48 | lib-y += inet_common.o | 46 | lib-y += inet_common.o |
49 | lib-y += inode_hash.o | 47 | lib-y += inode_hash.o |
50 | lib-y += isdirectory.o | 48 | lib-y += isdirectory.o |
51 | lib-y += kernel_version.o | ||
52 | lib-y += last_char_is.o | 49 | lib-y += last_char_is.o |
53 | lib-y += lineedit.o lineedit_ptr_hack.o | 50 | lib-y += lineedit.o lineedit_ptr_hack.o |
54 | lib-y += llist.o | 51 | lib-y += llist.o |
55 | lib-y += login.o | ||
56 | lib-y += make_directory.o | 52 | lib-y += make_directory.o |
57 | lib-y += makedev.o | ||
58 | lib-y += hash_md5_sha.o | 53 | lib-y += hash_md5_sha.o |
59 | # Alternative (disabled) MD5 implementation | 54 | # Alternative (disabled) MD5 implementation |
60 | #lib-y += hash_md5prime.o | 55 | #lib-y += hash_md5prime.o |
@@ -75,7 +70,6 @@ lib-y += progress.o | |||
75 | lib-y += ptr_to_globals.o | 70 | lib-y += ptr_to_globals.o |
76 | lib-y += read.o | 71 | lib-y += read.o |
77 | lib-y += read_printf.o | 72 | lib-y += read_printf.o |
78 | lib-y += read_key.o | ||
79 | lib-y += recursive_action.o | 73 | lib-y += recursive_action.o |
80 | lib-y += remove_file.o | 74 | lib-y += remove_file.o |
81 | lib-y += run_shell.o | 75 | lib-y += run_shell.o |
@@ -85,7 +79,6 @@ lib-y += safe_strncpy.o | |||
85 | lib-y += safe_write.o | 79 | lib-y += safe_write.o |
86 | lib-y += securetty.o | 80 | lib-y += securetty.o |
87 | lib-y += setup_environment.o | 81 | lib-y += setup_environment.o |
88 | lib-y += signals.o | ||
89 | lib-y += simplify_path.o | 82 | lib-y += simplify_path.o |
90 | lib-y += single_argv.o | 83 | lib-y += single_argv.o |
91 | lib-y += skip_whitespace.o | 84 | lib-y += skip_whitespace.o |
@@ -109,10 +102,20 @@ lib-y += xfuncs.o | |||
109 | lib-y += xfuncs_printf.o | 102 | lib-y += xfuncs_printf.o |
110 | lib-y += xfunc_die.o | 103 | lib-y += xfunc_die.o |
111 | lib-y += xgetcwd.o | 104 | lib-y += xgetcwd.o |
112 | lib-y += xgethostbyname.o | ||
113 | lib-y += xreadlink.o | 105 | lib-y += xreadlink.o |
114 | lib-y += xrealloc_vector.o | 106 | lib-y += xrealloc_vector.o |
115 | 107 | ||
108 | lib-$(CONFIG_PLATFORM_POSIX) += get_console.o | ||
109 | lib-$(CONFIG_PLATFORM_POSIX) += getpty.o | ||
110 | lib-$(CONFIG_PLATFORM_POSIX) += inet_common.o | ||
111 | lib-$(CONFIG_PLATFORM_POSIX) += kernel_version.o | ||
112 | lib-$(CONFIG_PLATFORM_POSIX) += login.o | ||
113 | lib-$(CONFIG_PLATFORM_POSIX) += makedev.o | ||
114 | lib-$(CONFIG_PLATFORM_POSIX) += read_key.o | ||
115 | lib-$(CONFIG_PLATFORM_POSIX) += signals.o | ||
116 | lib-$(CONFIG_PLATFORM_POSIX) += udp_io.o | ||
117 | lib-$(CONFIG_PLATFORM_POSIX) += xgethostbyname.o | ||
118 | |||
116 | lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o | 119 | lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o |
117 | 120 | ||
118 | lib-$(CONFIG_FEATURE_UTMP) += utmp.o | 121 | lib-$(CONFIG_FEATURE_UTMP) += utmp.o |
@@ -124,7 +127,7 @@ lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o | |||
124 | lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o | 127 | lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o |
125 | lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o | 128 | lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o |
126 | 129 | ||
127 | lib-$(CONFIG_NC) += udp_io.o | 130 | lib-$(CONFIG_NC_110_COMPAT) += udp_io.o |
128 | lib-$(CONFIG_DNSD) += udp_io.o | 131 | lib-$(CONFIG_DNSD) += udp_io.o |
129 | lib-$(CONFIG_NTPD) += udp_io.o | 132 | lib-$(CONFIG_NTPD) += udp_io.o |
130 | lib-$(CONFIG_TFTP) += udp_io.o | 133 | lib-$(CONFIG_TFTP) += udp_io.o |
diff --git a/libbb/appletlib.c b/libbb/appletlib.c index fa28d433b..401475f18 100644 --- a/libbb/appletlib.c +++ b/libbb/appletlib.c | |||
@@ -105,6 +105,7 @@ static const char usage_messages[] ALIGN1 = UNPACKED_USAGE; | |||
105 | #if ENABLE_FEATURE_COMPRESS_USAGE | 105 | #if ENABLE_FEATURE_COMPRESS_USAGE |
106 | 106 | ||
107 | static const char packed_usage[] ALIGN1 = { PACKED_USAGE }; | 107 | static const char packed_usage[] ALIGN1 = { PACKED_USAGE }; |
108 | #define BB_ARCHIVE_PUBLIC | ||
108 | # include "bb_archive.h" | 109 | # include "bb_archive.h" |
109 | static const char *unpack_usage_messages(void) | 110 | static const char *unpack_usage_messages(void) |
110 | { | 111 | { |
@@ -165,7 +166,7 @@ void FAST_FUNC bb_show_usage(void) | |||
165 | ap--; | 166 | ap--; |
166 | } | 167 | } |
167 | full_write2_str(bb_banner); | 168 | full_write2_str(bb_banner); |
168 | full_write2_str(" multi-call binary.\n"); | 169 | full_write2_str(" multi-call binary\n"); |
169 | if (*p == '\b') | 170 | if (*p == '\b') |
170 | full_write2_str("\nNo help available.\n\n"); | 171 | full_write2_str("\nNo help available.\n\n"); |
171 | else { | 172 | else { |
@@ -309,6 +310,22 @@ int FAST_FUNC find_applet_by_name(const char *name) | |||
309 | #endif | 310 | #endif |
310 | } | 311 | } |
311 | 312 | ||
313 | # if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_NOFORK | ||
314 | int FAST_FUNC long_running_applet(int applet_no) | ||
315 | { | ||
316 | int ret = 0; | ||
317 | |||
318 | #if defined(APPLET_NO_seq) | ||
319 | ret |= (applet_no == APPLET_NO_seq); | ||
320 | #endif | ||
321 | #if defined(APPLET_NO_yes) | ||
322 | ret |= (applet_no == APPLET_NO_yes); | ||
323 | #endif | ||
324 | |||
325 | return ret; | ||
326 | } | ||
327 | #endif | ||
328 | |||
312 | 329 | ||
313 | void lbb_prepare(const char *applet | 330 | void lbb_prepare(const char *applet |
314 | IF_FEATURE_INDIVIDUAL(, char **argv)) | 331 | IF_FEATURE_INDIVIDUAL(, char **argv)) |
@@ -325,6 +342,10 @@ void lbb_prepare(const char *applet | |||
325 | if (ENABLE_LOCALE_SUPPORT) | 342 | if (ENABLE_LOCALE_SUPPORT) |
326 | setlocale(LC_ALL, ""); | 343 | setlocale(LC_ALL, ""); |
327 | 344 | ||
345 | #if ENABLE_PLATFORM_MINGW32 | ||
346 | init_winsock(); | ||
347 | #endif | ||
348 | |||
328 | #if ENABLE_FEATURE_INDIVIDUAL | 349 | #if ENABLE_FEATURE_INDIVIDUAL |
329 | /* Redundant for busybox (run_applet_and_exit covers that case) | 350 | /* Redundant for busybox (run_applet_and_exit covers that case) |
330 | * but needed for "individual applet" mode */ | 351 | * but needed for "individual applet" mode */ |
@@ -715,6 +736,7 @@ static void check_suid(int applet_no) | |||
715 | 736 | ||
716 | 737 | ||
717 | # if ENABLE_FEATURE_INSTALLER | 738 | # if ENABLE_FEATURE_INSTALLER |
739 | # if !ENABLE_PLATFORM_MINGW32 | ||
718 | static const char usr_bin [] ALIGN1 = "/usr/bin/"; | 740 | static const char usr_bin [] ALIGN1 = "/usr/bin/"; |
719 | static const char usr_sbin[] ALIGN1 = "/usr/sbin/"; | 741 | static const char usr_sbin[] ALIGN1 = "/usr/sbin/"; |
720 | static const char *const install_dir[] = { | 742 | static const char *const install_dir[] = { |
@@ -759,6 +781,29 @@ static void install_links(const char *busybox, int use_symbolic_links, | |||
759 | continue; | 781 | continue; |
760 | } | 782 | } |
761 | } | 783 | } |
784 | # else /* ENABLE_PLATFORM_MINGW32 */ | ||
785 | static void install_links(const char *busybox, | ||
786 | int use_symbolic_links UNUSED_PARAM, char *custom_install_dir) | ||
787 | { | ||
788 | char *fpc; | ||
789 | const char *appname = applet_names; | ||
790 | int rc; | ||
791 | |||
792 | if (!is_directory(custom_install_dir, FALSE)) | ||
793 | bb_error_msg_and_die("'%s' is not a directory", custom_install_dir); | ||
794 | |||
795 | while (*appname) { | ||
796 | fpc = xasprintf("%s/%s.exe", custom_install_dir, appname); | ||
797 | rc = link(busybox, fpc); | ||
798 | if (rc != 0 && errno != EEXIST) { | ||
799 | bb_simple_perror_msg(fpc); | ||
800 | } | ||
801 | free(fpc); | ||
802 | while (*appname++ != '\0') | ||
803 | continue; | ||
804 | } | ||
805 | } | ||
806 | # endif | ||
762 | # elif ENABLE_BUSYBOX | 807 | # elif ENABLE_BUSYBOX |
763 | static void install_links(const char *busybox UNUSED_PARAM, | 808 | static void install_links(const char *busybox UNUSED_PARAM, |
764 | int use_symbolic_links UNUSED_PARAM, | 809 | int use_symbolic_links UNUSED_PARAM, |
@@ -798,16 +843,26 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
798 | 843 | ||
799 | dup2(1, 2); | 844 | dup2(1, 2); |
800 | full_write2_str(bb_banner); /* reuse const string */ | 845 | full_write2_str(bb_banner); /* reuse const string */ |
801 | full_write2_str(" multi-call binary.\n"); /* reuse */ | 846 | full_write2_str(" multi-call binary\n"); /* reuse */ |
847 | #if defined(MINGW_VER) | ||
848 | if (strlen(MINGW_VER)) { | ||
849 | full_write2_str(MINGW_VER "\n\n"); | ||
850 | } | ||
851 | #endif | ||
802 | full_write2_str( | 852 | full_write2_str( |
803 | "BusyBox is copyrighted by many authors between 1998-2015.\n" | 853 | "BusyBox is copyrighted by many authors between 1998-2015.\n" |
804 | "Licensed under GPLv2. See source distribution for detailed\n" | 854 | "Licensed under GPLv2. See source distribution for detailed\n" |
805 | "copyright notices.\n" | 855 | "copyright notices.\n" |
806 | "\n" | 856 | "\n" |
807 | "Usage: busybox [function [arguments]...]\n" | 857 | "Usage: busybox [function [arguments]...]\n" |
858 | IF_NOT_PLATFORM_MINGW32( | ||
808 | " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n" | 859 | " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n" |
860 | ) | ||
861 | IF_PLATFORM_MINGW32( | ||
862 | " or: busybox --list\n" | ||
863 | ) | ||
809 | IF_FEATURE_INSTALLER( | 864 | IF_FEATURE_INSTALLER( |
810 | " or: busybox --install [-s] [DIR]\n" | 865 | " or: busybox --install "IF_NOT_PLATFORM_MINGW32("[-s] ")"[DIR]\n" |
811 | ) | 866 | ) |
812 | " or: function [arguments]...\n" | 867 | " or: function [arguments]...\n" |
813 | "\n" | 868 | "\n" |
@@ -825,6 +880,11 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
825 | "\tTo run external program, use full path (/sbin/ip instead of ip).\n" | 880 | "\tTo run external program, use full path (/sbin/ip instead of ip).\n" |
826 | ) | 881 | ) |
827 | "\n" | 882 | "\n" |
883 | #if ENABLE_GLOBBING | ||
884 | "\tSupport for native Windows wildcards is enabled. In some\n" | ||
885 | "\tcases this may result in wildcards being processed twice.\n" | ||
886 | "\n" | ||
887 | #endif | ||
828 | "Currently defined functions:\n" | 888 | "Currently defined functions:\n" |
829 | ); | 889 | ); |
830 | col = 0; | 890 | col = 0; |
@@ -856,7 +916,7 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
856 | const char *a = applet_names; | 916 | const char *a = applet_names; |
857 | dup2(1, 2); | 917 | dup2(1, 2); |
858 | while (*a) { | 918 | while (*a) { |
859 | # if ENABLE_FEATURE_INSTALLER | 919 | # if ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32 |
860 | if (argv[1][6]) /* --list-full? */ | 920 | if (argv[1][6]) /* --list-full? */ |
861 | full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); | 921 | full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); |
862 | # endif | 922 | # endif |
@@ -870,6 +930,7 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
870 | } | 930 | } |
871 | 931 | ||
872 | if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { | 932 | if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { |
933 | #if !ENABLE_PLATFORM_MINGW32 | ||
873 | int use_symbolic_links; | 934 | int use_symbolic_links; |
874 | const char *busybox; | 935 | const char *busybox; |
875 | 936 | ||
@@ -890,6 +951,14 @@ int busybox_main(int argc UNUSED_PARAM, char **argv) | |||
890 | */ | 951 | */ |
891 | use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv); | 952 | use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv); |
892 | install_links(busybox, use_symbolic_links, argv[2]); | 953 | install_links(busybox, use_symbolic_links, argv[2]); |
954 | #else | ||
955 | /* busybox --install [DIR] | ||
956 | * where DIR is the directory to install to. If DIR is not | ||
957 | * provided put the links in the same directory as busybox. | ||
958 | */ | ||
959 | install_links(bb_busybox_exec_path, FALSE, argv[2] ? argv[2] : | ||
960 | dirname(xstrdup(bb_busybox_exec_path))); | ||
961 | #endif | ||
893 | return 0; | 962 | return 0; |
894 | } | 963 | } |
895 | 964 | ||
@@ -1031,6 +1100,18 @@ int main(int argc UNUSED_PARAM, char **argv) | |||
1031 | } | 1100 | } |
1032 | #endif | 1101 | #endif |
1033 | 1102 | ||
1103 | #if defined(__MINGW64_VERSION_MAJOR) | ||
1104 | if ( stdin ) { | ||
1105 | _setmode(fileno(stdin), _O_BINARY); | ||
1106 | } | ||
1107 | if ( stdout ) { | ||
1108 | _setmode(fileno(stdout), _O_BINARY); | ||
1109 | } | ||
1110 | if ( stderr ) { | ||
1111 | _setmode(fileno(stderr), _O_BINARY); | ||
1112 | } | ||
1113 | #endif | ||
1114 | |||
1034 | #if defined(SINGLE_APPLET_MAIN) | 1115 | #if defined(SINGLE_APPLET_MAIN) |
1035 | 1116 | ||
1036 | /* Only one applet is selected in .config */ | 1117 | /* Only one applet is selected in .config */ |
@@ -1062,6 +1143,24 @@ int main(int argc UNUSED_PARAM, char **argv) | |||
1062 | applet_name = argv[0]; | 1143 | applet_name = argv[0]; |
1063 | if (applet_name[0] == '-') | 1144 | if (applet_name[0] == '-') |
1064 | applet_name++; | 1145 | applet_name++; |
1146 | if (ENABLE_PLATFORM_MINGW32) { | ||
1147 | if ( argv[1] && argv[2] && strcmp(argv[1], "--busybox") == 0 ) { | ||
1148 | argv += 2; | ||
1149 | applet_name = argv[0]; | ||
1150 | } | ||
1151 | else { | ||
1152 | char *s = argv[0]; | ||
1153 | int i, len = strlen(s); | ||
1154 | |||
1155 | for ( i=0; i < len; ++i ) { | ||
1156 | s[i] = tolower(s[i]); | ||
1157 | } | ||
1158 | if (len > 4 && !strcmp(s+len-4, ".exe")) { | ||
1159 | len -= 4; | ||
1160 | s[len] = '\0'; | ||
1161 | } | ||
1162 | } | ||
1163 | } | ||
1065 | applet_name = bb_basename(applet_name); | 1164 | applet_name = bb_basename(applet_name); |
1066 | 1165 | ||
1067 | # if defined(__linux__) | 1166 | # if defined(__linux__) |
diff --git a/libbb/copy_file.c b/libbb/copy_file.c index 23c0f8320..cb6d12359 100644 --- a/libbb/copy_file.c +++ b/libbb/copy_file.c | |||
@@ -105,12 +105,15 @@ int FAST_FUNC copy_file(const char *source, const char *dest, int flags) | |||
105 | return -1; | 105 | return -1; |
106 | } | 106 | } |
107 | } else { | 107 | } else { |
108 | #if !ENABLE_PLATFORM_MINGW32 | ||
109 | /* MinGW does not have inode, and does not use device */ | ||
108 | if (source_stat.st_dev == dest_stat.st_dev | 110 | if (source_stat.st_dev == dest_stat.st_dev |
109 | && source_stat.st_ino == dest_stat.st_ino | 111 | && source_stat.st_ino == dest_stat.st_ino |
110 | ) { | 112 | ) { |
111 | bb_error_msg("'%s' and '%s' are the same file", source, dest); | 113 | bb_error_msg("'%s' and '%s' are the same file", source, dest); |
112 | return -1; | 114 | return -1; |
113 | } | 115 | } |
116 | #endif | ||
114 | dest_exists = 1; | 117 | dest_exists = 1; |
115 | } | 118 | } |
116 | 119 | ||
diff --git a/libbb/executable.c b/libbb/executable.c index 3a1d4ff44..5f0ff8c6e 100644 --- a/libbb/executable.c +++ b/libbb/executable.c | |||
@@ -28,6 +28,10 @@ int FAST_FUNC file_is_executable(const char *name) | |||
28 | * return NULL otherwise; (PATHp is undefined) | 28 | * return NULL otherwise; (PATHp is undefined) |
29 | * in all cases (*PATHp) contents will be trashed (s/:/NUL/). | 29 | * in all cases (*PATHp) contents will be trashed (s/:/NUL/). |
30 | */ | 30 | */ |
31 | #if !ENABLE_PLATFORM_MINGW32 | ||
32 | #define next_path_sep(s) strchr(s, ':') | ||
33 | #endif | ||
34 | |||
31 | char* FAST_FUNC find_executable(const char *filename, char **PATHp) | 35 | char* FAST_FUNC find_executable(const char *filename, char **PATHp) |
32 | { | 36 | { |
33 | /* About empty components in $PATH: | 37 | /* About empty components in $PATH: |
@@ -39,10 +43,13 @@ char* FAST_FUNC find_executable(const char *filename, char **PATHp) | |||
39 | * following the rest of the list. | 43 | * following the rest of the list. |
40 | */ | 44 | */ |
41 | char *p, *n; | 45 | char *p, *n; |
46 | #if ENABLE_PLATFORM_MINGW32 | ||
47 | char *w; | ||
48 | #endif | ||
42 | 49 | ||
43 | p = *PATHp; | 50 | p = *PATHp; |
44 | while (p) { | 51 | while (p) { |
45 | n = strchr(p, ':'); | 52 | n = (char*)next_path_sep(p); |
46 | if (n) | 53 | if (n) |
47 | *n++ = '\0'; | 54 | *n++ = '\0'; |
48 | p = concat_path_file( | 55 | p = concat_path_file( |
@@ -53,6 +60,13 @@ char* FAST_FUNC find_executable(const char *filename, char **PATHp) | |||
53 | *PATHp = n; | 60 | *PATHp = n; |
54 | return p; | 61 | return p; |
55 | } | 62 | } |
63 | #if ENABLE_PLATFORM_MINGW32 | ||
64 | else if ((w=file_is_win32_executable(p))) { | ||
65 | *PATHp = n; | ||
66 | free(p); | ||
67 | return w; | ||
68 | } | ||
69 | #endif | ||
56 | free(p); | 70 | free(p); |
57 | p = n; | 71 | p = n; |
58 | } /* on loop exit p == NULL */ | 72 | } /* on loop exit p == NULL */ |
diff --git a/libbb/find_mount_point.c b/libbb/find_mount_point.c index 9676b5f52..de314a3c7 100644 --- a/libbb/find_mount_point.c +++ b/libbb/find_mount_point.c | |||
@@ -24,10 +24,18 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too) | |||
24 | struct mntent *mountEntry; | 24 | struct mntent *mountEntry; |
25 | dev_t devno_of_name; | 25 | dev_t devno_of_name; |
26 | bool block_dev; | 26 | bool block_dev; |
27 | #if ENABLE_PLATFORM_MINGW32 | ||
28 | static char mnt_fsname[4]; | ||
29 | static char mnt_dir[4]; | ||
30 | static struct mntent my_mount_entry = { mnt_fsname, mnt_dir, "", "", 0, 0 }; | ||
31 | char *current, *path; | ||
32 | DWORD len; | ||
33 | #endif | ||
27 | 34 | ||
28 | if (stat(name, &s) != 0) | 35 | if (stat(name, &s) != 0) |
29 | return NULL; | 36 | return NULL; |
30 | 37 | ||
38 | #if !ENABLE_PLATFORM_MINGW32 | ||
31 | devno_of_name = s.st_dev; | 39 | devno_of_name = s.st_dev; |
32 | block_dev = 0; | 40 | block_dev = 0; |
33 | /* Why S_ISCHR? - UBI volumes use char devices, not block */ | 41 | /* Why S_ISCHR? - UBI volumes use char devices, not block */ |
@@ -64,6 +72,35 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too) | |||
64 | break; | 72 | break; |
65 | } | 73 | } |
66 | endmntent(mtab_fp); | 74 | endmntent(mtab_fp); |
75 | #else | ||
76 | mountEntry = NULL; | ||
77 | path = NULL; | ||
78 | current = NULL; | ||
79 | |||
80 | if ( isalpha(name[0]) && name[1] == ':' ) { | ||
81 | path = name; | ||
82 | } | ||
83 | else { | ||
84 | if ( (len=GetCurrentDirectory(0, NULL)) > 0 && | ||
85 | (current=malloc(len+1)) != NULL && | ||
86 | GetCurrentDirectory(len, current) ) { | ||
87 | path = current; | ||
88 | } | ||
89 | } | ||
90 | |||
91 | if ( path && isalpha(path[0]) && path[1] == ':' ) { | ||
92 | mnt_fsname[0] = path[0]; | ||
93 | mnt_fsname[1] = path[1]; | ||
94 | mnt_fsname[2] = '\0'; | ||
95 | mnt_dir[0] = path[0]; | ||
96 | mnt_dir[1] = path[1]; | ||
97 | mnt_dir[2] = '\\'; | ||
98 | mnt_dir[3] = '\0'; | ||
99 | |||
100 | mountEntry = &my_mount_entry; | ||
101 | } | ||
102 | free(current); | ||
103 | #endif | ||
67 | 104 | ||
68 | return mountEntry; | 105 | return mountEntry; |
69 | } | 106 | } |
diff --git a/libbb/find_pid_by_name.c b/libbb/find_pid_by_name.c index db823d05b..d4bea8ab5 100644 --- a/libbb/find_pid_by_name.c +++ b/libbb/find_pid_by_name.c | |||
@@ -56,6 +56,7 @@ static int comm_match(procps_status_t *p, const char *procName) | |||
56 | * This can be crazily_long_script_name.sh! | 56 | * This can be crazily_long_script_name.sh! |
57 | * The telltale sign is basename(argv[1]) == procName */ | 57 | * The telltale sign is basename(argv[1]) == procName */ |
58 | 58 | ||
59 | #if !ENABLE_PLATFORM_MINGW32 | ||
59 | if (!p->argv0) | 60 | if (!p->argv0) |
60 | return 0; | 61 | return 0; |
61 | 62 | ||
@@ -66,6 +67,7 @@ static int comm_match(procps_status_t *p, const char *procName) | |||
66 | 67 | ||
67 | if (strcmp(bb_basename(argv1), procName) != 0) | 68 | if (strcmp(bb_basename(argv1), procName) != 0) |
68 | return 0; | 69 | return 0; |
70 | #endif | ||
69 | 71 | ||
70 | return 1; | 72 | return 1; |
71 | } | 73 | } |
@@ -88,10 +90,12 @@ pid_t* FAST_FUNC find_pid_by_name(const char *procName) | |||
88 | pidList = xzalloc(sizeof(*pidList)); | 90 | pidList = xzalloc(sizeof(*pidList)); |
89 | while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN|PSSCAN_EXE))) { | 91 | while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN|PSSCAN_EXE))) { |
90 | if (comm_match(p, procName) | 92 | if (comm_match(p, procName) |
93 | #if !ENABLE_PLATFORM_MINGW32 | ||
91 | /* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/ | 94 | /* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/ |
92 | || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0) | 95 | || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0) |
93 | /* or we require /proc/PID/exe link to match */ | 96 | /* or we require /proc/PID/exe link to match */ |
94 | || (p->exe && strcmp(bb_basename(p->exe), procName) == 0) | 97 | || (p->exe && strcmp(bb_basename(p->exe), procName) == 0) |
98 | #endif | ||
95 | ) { | 99 | ) { |
96 | pidList = xrealloc_vector(pidList, 2, i); | 100 | pidList = xrealloc_vector(pidList, 2, i); |
97 | pidList[i++] = p->pid; | 101 | pidList[i++] = p->pid; |
diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c index 04fdf2a3e..15eb85ca8 100644 --- a/libbb/get_last_path_component.c +++ b/libbb/get_last_path_component.c | |||
@@ -13,6 +13,11 @@ const char* FAST_FUNC bb_basename(const char *name) | |||
13 | const char *cp = strrchr(name, '/'); | 13 | const char *cp = strrchr(name, '/'); |
14 | if (cp) | 14 | if (cp) |
15 | return cp + 1; | 15 | return cp + 1; |
16 | #if ENABLE_PLATFORM_MINGW32 | ||
17 | cp = strrchr(name, '\\'); | ||
18 | if (cp) | ||
19 | return cp + 1; | ||
20 | #endif | ||
16 | return name; | 21 | return name; |
17 | } | 22 | } |
18 | 23 | ||
@@ -26,8 +31,18 @@ char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path) | |||
26 | { | 31 | { |
27 | char *slash = strrchr(path, '/'); | 32 | char *slash = strrchr(path, '/'); |
28 | 33 | ||
34 | #if ENABLE_PLATFORM_MINGW32 | ||
35 | const char *start = has_dos_drive_prefix(path) ? path+2 : path; | ||
36 | |||
37 | if (!slash) | ||
38 | slash = strrchr(path, '\\'); | ||
39 | |||
40 | if (!slash || (slash == start && !slash[1])) | ||
41 | return (char*)path; | ||
42 | #else | ||
29 | if (!slash || (slash == path && !slash[1])) | 43 | if (!slash || (slash == path && !slash[1])) |
30 | return (char*)path; | 44 | return (char*)path; |
45 | #endif | ||
31 | 46 | ||
32 | return slash + 1; | 47 | return slash + 1; |
33 | } | 48 | } |
@@ -42,9 +57,20 @@ char* FAST_FUNC bb_get_last_path_component_strip(char *path) | |||
42 | { | 57 | { |
43 | char *slash = last_char_is(path, '/'); | 58 | char *slash = last_char_is(path, '/'); |
44 | 59 | ||
60 | #if ENABLE_PLATFORM_MINGW32 | ||
61 | const char *start = has_dos_drive_prefix(path) ? path+2 : path; | ||
62 | |||
63 | if (!slash) | ||
64 | slash = last_char_is(path, '\\'); | ||
65 | |||
66 | if (slash) | ||
67 | while ((*slash == '/' || *slash == '\\') && slash != start) | ||
68 | *slash-- = '\0'; | ||
69 | #else | ||
45 | if (slash) | 70 | if (slash) |
46 | while (*slash == '/' && slash != path) | 71 | while (*slash == '/' && slash != path) |
47 | *slash-- = '\0'; | 72 | *slash-- = '\0'; |
73 | #endif | ||
48 | 74 | ||
49 | return bb_get_last_path_component_nostrip(path); | 75 | return bb_get_last_path_component_nostrip(path); |
50 | } | 76 | } |
diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c index a98dd35eb..2038fac7d 100644 --- a/libbb/get_line_from_file.c +++ b/libbb/get_line_from_file.c | |||
@@ -57,6 +57,10 @@ char* FAST_FUNC xmalloc_fgetline(FILE *file) | |||
57 | 57 | ||
58 | if (i && c[--i] == '\n') | 58 | if (i && c[--i] == '\n') |
59 | c[i] = '\0'; | 59 | c[i] = '\0'; |
60 | #if ENABLE_PLATFORM_MINGW32 | ||
61 | if (i && c[--i] == '\r') | ||
62 | c[i] = '\0'; | ||
63 | #endif | ||
60 | 64 | ||
61 | return c; | 65 | return c; |
62 | } | 66 | } |
diff --git a/libbb/inode_hash.c b/libbb/inode_hash.c index f11c2afb2..64f43b885 100644 --- a/libbb/inode_hash.c +++ b/libbb/inode_hash.c | |||
@@ -59,6 +59,7 @@ char* FAST_FUNC is_in_ino_dev_hashtable(const struct stat *statbuf) | |||
59 | /* Add statbuf to statbuf hash table */ | 59 | /* Add statbuf to statbuf hash table */ |
60 | void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) | 60 | void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) |
61 | { | 61 | { |
62 | #if !ENABLE_PLATFORM_MINGW32 | ||
62 | int i; | 63 | int i; |
63 | ino_dev_hashtable_bucket_t *bucket; | 64 | ino_dev_hashtable_bucket_t *bucket; |
64 | 65 | ||
@@ -76,6 +77,7 @@ void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char * | |||
76 | i = hash_inode(statbuf->st_ino); | 77 | i = hash_inode(statbuf->st_ino); |
77 | bucket->next = ino_dev_hashtable[i]; | 78 | bucket->next = ino_dev_hashtable[i]; |
78 | ino_dev_hashtable[i] = bucket; | 79 | ino_dev_hashtable[i] = bucket; |
80 | #endif | ||
79 | } | 81 | } |
80 | 82 | ||
81 | #if ENABLE_DU || ENABLE_FEATURE_CLEAN_UP | 83 | #if ENABLE_DU || ENABLE_FEATURE_CLEAN_UP |
diff --git a/libbb/lineedit.c b/libbb/lineedit.c index e5721b063..051a39b2e 100644 --- a/libbb/lineedit.c +++ b/libbb/lineedit.c | |||
@@ -354,7 +354,7 @@ int adjust_width_and_validate_wc(unsigned *width_adj, int wc); | |||
354 | /* Put 'command_ps[cursor]', cursor++. | 354 | /* Put 'command_ps[cursor]', cursor++. |
355 | * Advance cursor on screen. If we reached right margin, scroll text up | 355 | * Advance cursor on screen. If we reached right margin, scroll text up |
356 | * and remove terminal margin effect by printing 'next_char' */ | 356 | * and remove terminal margin effect by printing 'next_char' */ |
357 | #define HACK_FOR_WRONG_WIDTH 1 | 357 | #define HACK_FOR_WRONG_WIDTH 1 && !ENABLE_PLATFORM_MINGW32 |
358 | static void put_cur_glyph_and_inc_cursor(void) | 358 | static void put_cur_glyph_and_inc_cursor(void) |
359 | { | 359 | { |
360 | CHAR_T c = command_ps[cursor]; | 360 | CHAR_T c = command_ps[cursor]; |
@@ -417,6 +417,42 @@ static void put_cur_glyph_and_inc_cursor(void) | |||
417 | } | 417 | } |
418 | } | 418 | } |
419 | 419 | ||
420 | #if ENABLE_PLATFORM_MINGW32 | ||
421 | static void inc_cursor(void) | ||
422 | { | ||
423 | CHAR_T c = command_ps[cursor]; | ||
424 | unsigned width = 0; | ||
425 | int ofs_to_right; | ||
426 | |||
427 | /* advance cursor */ | ||
428 | cursor++; | ||
429 | if (unicode_status == UNICODE_ON) { | ||
430 | IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;) | ||
431 | c = adjust_width_and_validate_wc(&cmdedit_x, c); | ||
432 | IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;) | ||
433 | } else { | ||
434 | cmdedit_x++; | ||
435 | } | ||
436 | |||
437 | ofs_to_right = cmdedit_x - cmdedit_termw; | ||
438 | if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) { | ||
439 | /* cursor remains on this line */ | ||
440 | printf(ESC"[1C"); | ||
441 | } | ||
442 | |||
443 | if (ofs_to_right >= 0) { | ||
444 | /* we go to the next line */ | ||
445 | printf(ESC"[1B"); | ||
446 | bb_putchar('\r'); | ||
447 | cmdedit_y++; | ||
448 | if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) { | ||
449 | width = 0; | ||
450 | } | ||
451 | cmdedit_x = width; | ||
452 | } | ||
453 | } | ||
454 | #endif | ||
455 | |||
420 | /* Move to end of line (by printing all chars till the end) */ | 456 | /* Move to end of line (by printing all chars till the end) */ |
421 | static void put_till_end_and_adv_cursor(void) | 457 | static void put_till_end_and_adv_cursor(void) |
422 | { | 458 | { |
@@ -469,6 +505,7 @@ static void input_backward(unsigned num) | |||
469 | 505 | ||
470 | if (cmdedit_x >= num) { | 506 | if (cmdedit_x >= num) { |
471 | cmdedit_x -= num; | 507 | cmdedit_x -= num; |
508 | #if !ENABLE_PLATFORM_MINGW32 | ||
472 | if (num <= 4) { | 509 | if (num <= 4) { |
473 | /* This is longer by 5 bytes on x86. | 510 | /* This is longer by 5 bytes on x86. |
474 | * Also gets miscompiled for ARM users | 511 | * Also gets miscompiled for ARM users |
@@ -481,6 +518,7 @@ static void input_backward(unsigned num) | |||
481 | } while (--num); | 518 | } while (--num); |
482 | return; | 519 | return; |
483 | } | 520 | } |
521 | #endif | ||
484 | printf(ESC"[%uD", num); | 522 | printf(ESC"[%uD", num); |
485 | return; | 523 | return; |
486 | } | 524 | } |
@@ -610,7 +648,11 @@ static void input_backspace(void) | |||
610 | static void input_forward(void) | 648 | static void input_forward(void) |
611 | { | 649 | { |
612 | if (cursor < command_len) | 650 | if (cursor < command_len) |
651 | #if !ENABLE_PLATFORM_MINGW32 | ||
613 | put_cur_glyph_and_inc_cursor(); | 652 | put_cur_glyph_and_inc_cursor(); |
653 | #else | ||
654 | inc_cursor(); | ||
655 | #endif | ||
614 | } | 656 | } |
615 | 657 | ||
616 | #if ENABLE_FEATURE_TAB_COMPLETION | 658 | #if ENABLE_FEATURE_TAB_COMPLETION |
@@ -718,7 +760,11 @@ static int path_parse(char ***p) | |||
718 | tmp = (char*)pth; | 760 | tmp = (char*)pth; |
719 | npth = 1; /* path component count */ | 761 | npth = 1; /* path component count */ |
720 | while (1) { | 762 | while (1) { |
763 | #if ENABLE_PLATFORM_MINGW32 | ||
764 | tmp = (char *)next_path_sep(tmp); | ||
765 | #else | ||
721 | tmp = strchr(tmp, ':'); | 766 | tmp = strchr(tmp, ':'); |
767 | #endif | ||
722 | if (!tmp) | 768 | if (!tmp) |
723 | break; | 769 | break; |
724 | tmp++; | 770 | tmp++; |
@@ -731,7 +777,11 @@ static int path_parse(char ***p) | |||
731 | res[0] = tmp = xstrdup(pth); | 777 | res[0] = tmp = xstrdup(pth); |
732 | npth = 1; | 778 | npth = 1; |
733 | while (1) { | 779 | while (1) { |
780 | #if ENABLE_PLATFORM_MINGW32 | ||
781 | tmp = (char *)next_path_sep(tmp); | ||
782 | #else | ||
734 | tmp = strchr(tmp, ':'); | 783 | tmp = strchr(tmp, ':'); |
784 | #endif | ||
735 | if (!tmp) | 785 | if (!tmp) |
736 | break; | 786 | break; |
737 | *tmp++ = '\0'; /* ':' -> '\0' */ | 787 | *tmp++ = '\0'; /* ':' -> '\0' */ |
@@ -817,6 +867,9 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type) | |||
817 | if (stat(found, &st) && lstat(found, &st)) | 867 | if (stat(found, &st) && lstat(found, &st)) |
818 | goto cont; /* hmm, remove in progress? */ | 868 | goto cont; /* hmm, remove in progress? */ |
819 | 869 | ||
870 | if (type == FIND_EXE_ONLY && !file_is_executable(found)) | ||
871 | goto cont; | ||
872 | |||
820 | /* Save only name */ | 873 | /* Save only name */ |
821 | len = strlen(name_found); | 874 | len = strlen(name_found); |
822 | found = xrealloc(found, len + 2); /* +2: for slash and NUL */ | 875 | found = xrealloc(found, len + 2); /* +2: for slash and NUL */ |
@@ -1898,7 +1951,16 @@ static void parse_and_put_prompt(const char *prmt_ptr) | |||
1898 | char *after_home_user; | 1951 | char *after_home_user; |
1899 | 1952 | ||
1900 | /* /home/user[/something] -> ~[/something] */ | 1953 | /* /home/user[/something] -> ~[/something] */ |
1954 | #if !ENABLE_PLATFORM_MINGW32 | ||
1901 | after_home_user = is_prefixed_with(cwd_buf, home_pwd_buf); | 1955 | after_home_user = is_prefixed_with(cwd_buf, home_pwd_buf); |
1956 | #else | ||
1957 | after_home_user = NULL; | ||
1958 | l = strlen(home_pwd_buf); | ||
1959 | if (l != 0 | ||
1960 | && strncasecmp(home_pwd_buf, cwd_buf, l) == 0) { | ||
1961 | after_home_user = cwd_buf + l; | ||
1962 | } | ||
1963 | #endif | ||
1902 | if (after_home_user | 1964 | if (after_home_user |
1903 | && (*after_home_user == '/' || *after_home_user == '\0') | 1965 | && (*after_home_user == '/' || *after_home_user == '\0') |
1904 | ) { | 1966 | ) { |
@@ -2272,9 +2334,16 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2272 | 2334 | ||
2273 | INIT_S(); | 2335 | INIT_S(); |
2274 | 2336 | ||
2337 | #if ENABLE_PLATFORM_MINGW32 | ||
2338 | memset(initial_settings.c_cc, 0, sizeof(initial_settings.c_cc)); | ||
2339 | initial_settings.c_cc[VINTR] = CTRL('C'); | ||
2340 | initial_settings.c_cc[VEOF] = CTRL('D'); | ||
2341 | if (!isatty(0) || !isatty(1)) { | ||
2342 | #else | ||
2275 | if (tcgetattr(STDIN_FILENO, &initial_settings) < 0 | 2343 | if (tcgetattr(STDIN_FILENO, &initial_settings) < 0 |
2276 | || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON | 2344 | || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON |
2277 | ) { | 2345 | ) { |
2346 | #endif | ||
2278 | /* Happens when e.g. stty -echo was run before. | 2347 | /* Happens when e.g. stty -echo was run before. |
2279 | * But if ICANON is not set, we don't come here. | 2348 | * But if ICANON is not set, we don't come here. |
2280 | * (example: interactive python ^Z-backgrounded, | 2349 | * (example: interactive python ^Z-backgrounded, |
@@ -2383,6 +2452,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman | |||
2383 | } | 2452 | } |
2384 | 2453 | ||
2385 | ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); | 2454 | ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); |
2455 | #if ENABLE_PLATFORM_MINGW32 | ||
2456 | /* scroll to cursor position on any keypress */ | ||
2457 | if (isatty(fileno(stdin)) && isatty(fileno(stdout))) | ||
2458 | move_cursor_row(0); | ||
2459 | #endif | ||
2386 | 2460 | ||
2387 | #if ENABLE_FEATURE_REVERSE_SEARCH | 2461 | #if ENABLE_FEATURE_REVERSE_SEARCH |
2388 | again: | 2462 | again: |
diff --git a/libbb/make_directory.c b/libbb/make_directory.c index a6b7c28df..3e171ff02 100644 --- a/libbb/make_directory.c +++ b/libbb/make_directory.c | |||
@@ -51,11 +51,44 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags) | |||
51 | } | 51 | } |
52 | 52 | ||
53 | org_mask = cur_mask = (mode_t)-1L; | 53 | org_mask = cur_mask = (mode_t)-1L; |
54 | #if ENABLE_PLATFORM_MINGW32 | ||
55 | /* normalise path separators, path is already assumed writable */ | ||
56 | for (s=path; *s; ++s) { | ||
57 | if (*s == '\\') { | ||
58 | *s = '/'; | ||
59 | } | ||
60 | } | ||
61 | #endif | ||
54 | s = path; | 62 | s = path; |
55 | while (1) { | 63 | while (1) { |
56 | c = '\0'; | 64 | c = '\0'; |
57 | 65 | ||
58 | if (flags & FILEUTILS_RECUR) { /* Get the parent */ | 66 | if (flags & FILEUTILS_RECUR) { /* Get the parent */ |
67 | #if ENABLE_PLATFORM_MINGW32 | ||
68 | if (s == path && *s && s[1] == ':') { | ||
69 | /* skip drive letter */ | ||
70 | s += 2; | ||
71 | } | ||
72 | else if (s == path && s[0] == '/' && s[1] == '/' ) { | ||
73 | /* skip UNC server and share */ | ||
74 | int count = 0; | ||
75 | s += 2; | ||
76 | while (*s) { | ||
77 | if (*s == '/') { | ||
78 | do { | ||
79 | ++s; | ||
80 | } while (*s == '/'); | ||
81 | if (++count == 2) { | ||
82 | --s; | ||
83 | break; | ||
84 | } | ||
85 | } | ||
86 | else { | ||
87 | ++s; | ||
88 | } | ||
89 | } | ||
90 | } | ||
91 | #endif | ||
59 | /* Bypass leading non-'/'s and then subsequent '/'s */ | 92 | /* Bypass leading non-'/'s and then subsequent '/'s */ |
60 | while (*s) { | 93 | while (*s) { |
61 | if (*s == '/') { | 94 | if (*s == '/') { |
diff --git a/libbb/messages.c b/libbb/messages.c index 27fd14ecc..3c0b921cf 100644 --- a/libbb/messages.c +++ b/libbb/messages.c | |||
@@ -33,7 +33,9 @@ const char bb_msg_standard_output[] ALIGN1 = "standard output"; | |||
33 | 33 | ||
34 | const char bb_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF"; | 34 | const char bb_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF"; |
35 | 35 | ||
36 | #if !ENABLE_PLATFORM_MINGW32 | ||
36 | const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH; | 37 | const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH; |
38 | #endif | ||
37 | const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; | 39 | const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; |
38 | /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, | 40 | /* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, |
39 | * but I want to save a few bytes here. Check libbb.h before changing! */ | 41 | * but I want to save a few bytes here. Check libbb.h before changing! */ |
diff --git a/libbb/printable_string.c b/libbb/printable_string.c index 077d58d32..e638a178e 100644 --- a/libbb/printable_string.c +++ b/libbb/printable_string.c | |||
@@ -42,7 +42,7 @@ const char* FAST_FUNC printable_string(uni_stat_t *stats, const char *str) | |||
42 | unsigned char c = *d; | 42 | unsigned char c = *d; |
43 | if (c == '\0') | 43 | if (c == '\0') |
44 | break; | 44 | break; |
45 | if (c < ' ' || c >= 0x7f) | 45 | if (c < ' ' || (c >= 0x7f && !ENABLE_PLATFORM_MINGW32)) |
46 | *d = '?'; | 46 | *d = '?'; |
47 | d++; | 47 | d++; |
48 | } | 48 | } |
diff --git a/libbb/procps.c b/libbb/procps.c index b52c0f51b..6f971a116 100644 --- a/libbb/procps.c +++ b/libbb/procps.c | |||
@@ -75,6 +75,7 @@ const char* FAST_FUNC get_cached_groupname(gid_t gid) | |||
75 | return get_cached(&groupname, gid, gid2group_utoa); | 75 | return get_cached(&groupname, gid, gid2group_utoa); |
76 | } | 76 | } |
77 | 77 | ||
78 | #if !ENABLE_PLATFORM_MINGW32 | ||
78 | 79 | ||
79 | #define PROCPS_BUFSIZE 1024 | 80 | #define PROCPS_BUFSIZE 1024 |
80 | 81 | ||
@@ -622,6 +623,8 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm) | |||
622 | } | 623 | } |
623 | } | 624 | } |
624 | 625 | ||
626 | #endif /* ENABLE_PLATFORM_MINGW32 */ | ||
627 | |||
625 | /* from kernel: | 628 | /* from kernel: |
626 | // pid comm S ppid pgid sid tty_nr tty_pgrp flg | 629 | // pid comm S ppid pgid sid tty_nr tty_pgrp flg |
627 | sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ | 630 | sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ |
diff --git a/libbb/read_printf.c b/libbb/read_printf.c index b6a17cc36..e47ac7afe 100644 --- a/libbb/read_printf.c +++ b/libbb/read_printf.c | |||
@@ -93,6 +93,11 @@ char* FAST_FUNC xmalloc_reads(int fd, size_t *maxsz_p) | |||
93 | break; | 93 | break; |
94 | p++; | 94 | p++; |
95 | } | 95 | } |
96 | #if ENABLE_PLATFORM_MINGW32 | ||
97 | if ( p != buf && *(p-1) == '\r' ) { | ||
98 | --p; | ||
99 | } | ||
100 | #endif | ||
96 | *p = '\0'; | 101 | *p = '\0'; |
97 | if (maxsz_p) | 102 | if (maxsz_p) |
98 | *maxsz_p = p - buf; | 103 | *maxsz_p = p - buf; |
diff --git a/libbb/setup_environment.c b/libbb/setup_environment.c index 4258656fe..944ac5538 100644 --- a/libbb/setup_environment.c +++ b/libbb/setup_environment.c | |||
@@ -30,6 +30,14 @@ | |||
30 | 30 | ||
31 | #include "libbb.h" | 31 | #include "libbb.h" |
32 | 32 | ||
33 | #if ENABLE_PLATFORM_MINGW32 | ||
34 | static void xsetenv_if_unset(const char *key, const char *value) | ||
35 | { | ||
36 | if (!getenv(key)) | ||
37 | xsetenv(key, value); | ||
38 | } | ||
39 | #endif | ||
40 | |||
33 | void FAST_FUNC setup_environment(const char *shell, int flags, const struct passwd *pw) | 41 | void FAST_FUNC setup_environment(const char *shell, int flags, const struct passwd *pw) |
34 | { | 42 | { |
35 | if (!shell || !shell[0]) | 43 | if (!shell || !shell[0]) |
@@ -61,6 +69,9 @@ void FAST_FUNC setup_environment(const char *shell, int flags, const struct pass | |||
61 | //xsetenv("HOME", pw->pw_dir); | 69 | //xsetenv("HOME", pw->pw_dir); |
62 | //xsetenv("SHELL", shell); | 70 | //xsetenv("SHELL", shell); |
63 | } else if (flags & SETUP_ENV_CHANGEENV) { | 71 | } else if (flags & SETUP_ENV_CHANGEENV) { |
72 | #if ENABLE_PLATFORM_MINGW32 | ||
73 | #define xsetenv(k, v) xsetenv_if_unset(k, v) | ||
74 | #endif | ||
64 | /* Set HOME, SHELL, and if not becoming a super-user, | 75 | /* Set HOME, SHELL, and if not becoming a super-user, |
65 | * USER and LOGNAME. */ | 76 | * USER and LOGNAME. */ |
66 | if (pw->pw_uid) { | 77 | if (pw->pw_uid) { |
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c index 576534ee5..4b3ed5a3b 100644 --- a/libbb/vfork_daemon_rexec.c +++ b/libbb/vfork_daemon_rexec.c | |||
@@ -18,6 +18,7 @@ | |||
18 | #include "busybox.h" /* uses applet tables */ | 18 | #include "busybox.h" /* uses applet tables */ |
19 | #include "NUM_APPLETS.h" | 19 | #include "NUM_APPLETS.h" |
20 | 20 | ||
21 | #if !ENABLE_PLATFORM_MINGW32 | ||
21 | /* This does a fork/exec in one call, using vfork(). Returns PID of new child, | 22 | /* This does a fork/exec in one call, using vfork(). Returns PID of new child, |
22 | * -1 for failure. Runs argv[0], searching path if that has no / in it. */ | 23 | * -1 for failure. Runs argv[0], searching path if that has no / in it. */ |
23 | pid_t FAST_FUNC spawn(char **argv) | 24 | pid_t FAST_FUNC spawn(char **argv) |
@@ -59,6 +60,7 @@ pid_t FAST_FUNC spawn(char **argv) | |||
59 | } | 60 | } |
60 | return pid; | 61 | return pid; |
61 | } | 62 | } |
63 | #endif | ||
62 | 64 | ||
63 | /* Die with an error message if we can't spawn a child process. */ | 65 | /* Die with an error message if we can't spawn a child process. */ |
64 | pid_t FAST_FUNC xspawn(char **argv) | 66 | pid_t FAST_FUNC xspawn(char **argv) |
@@ -164,6 +166,7 @@ int FAST_FUNC spawn_and_wait(char **argv) | |||
164 | if (APPLET_IS_NOFORK(a)) | 166 | if (APPLET_IS_NOFORK(a)) |
165 | return run_nofork_applet(a, argv); | 167 | return run_nofork_applet(a, argv); |
166 | # if BB_MMU /* NOEXEC needs fork(), thus this is done only on MMU machines: */ | 168 | # if BB_MMU /* NOEXEC needs fork(), thus this is done only on MMU machines: */ |
169 | # if !ENABLE_PLATFORM_MINGW32 /* and then only if not on Microsoft Windows */ | ||
167 | if (APPLET_IS_NOEXEC(a)) { | 170 | if (APPLET_IS_NOEXEC(a)) { |
168 | fflush_all(); | 171 | fflush_all(); |
169 | rc = fork(); | 172 | rc = fork(); |
@@ -182,6 +185,7 @@ int FAST_FUNC spawn_and_wait(char **argv) | |||
182 | /* xfunc_error_retval and applet_name are init by: */ | 185 | /* xfunc_error_retval and applet_name are init by: */ |
183 | run_applet_no_and_exit(a, argv[0], argv); | 186 | run_applet_no_and_exit(a, argv[0], argv); |
184 | } | 187 | } |
188 | # endif | ||
185 | # endif | 189 | # endif |
186 | } | 190 | } |
187 | #endif /* FEATURE_PREFER_APPLETS */ | 191 | #endif /* FEATURE_PREFER_APPLETS */ |
diff --git a/libbb/xatonum_template.c b/libbb/xatonum_template.c index e0471983c..0d5d35b47 100644 --- a/libbb/xatonum_template.c +++ b/libbb/xatonum_template.c | |||
@@ -67,7 +67,7 @@ unsigned type FAST_FUNC xstrtou(_range_sfx)(const char *numstr, int base, | |||
67 | if (r >= lower && r <= upper) | 67 | if (r >= lower && r <= upper) |
68 | return r; | 68 | return r; |
69 | range: | 69 | range: |
70 | bb_error_msg_and_die("number %s is not in %llu..%llu range", | 70 | bb_error_msg_and_die("number %s is not in %"LL_FMT"u..%"LL_FMT"u range", |
71 | numstr, (unsigned long long)lower, | 71 | numstr, (unsigned long long)lower, |
72 | (unsigned long long)upper); | 72 | (unsigned long long)upper); |
73 | inval: | 73 | inval: |
@@ -144,7 +144,8 @@ type FAST_FUNC xstrto(_range_sfx)(const char *numstr, int base, | |||
144 | } | 144 | } |
145 | 145 | ||
146 | if (r < lower || r > upper) { | 146 | if (r < lower || r > upper) { |
147 | bb_error_msg_and_die("number %s is not in %lld..%lld range", | 147 | bb_error_msg_and_die("number %s is not in " |
148 | "%"LL_FMT"d..%"LL_FMT"d range", | ||
148 | numstr, (long long)lower, (long long)upper); | 149 | numstr, (long long)lower, (long long)upper); |
149 | } | 150 | } |
150 | 151 | ||
diff --git a/libbb/xconnect.c b/libbb/xconnect.c index 3a0dc2653..ee54898e3 100644 --- a/libbb/xconnect.c +++ b/libbb/xconnect.c | |||
@@ -98,15 +98,15 @@ len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd) | |||
98 | return get_lsa(fd, getpeername); | 98 | return get_lsa(fd, getpeername); |
99 | } | 99 | } |
100 | 100 | ||
101 | void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) | 101 | void FAST_FUNC xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen) |
102 | { | 102 | { |
103 | if (connect(s, s_addr, addrlen) < 0) { | 103 | if (connect(s, saddr, addrlen) < 0) { |
104 | if (ENABLE_FEATURE_CLEAN_UP) | 104 | if (ENABLE_FEATURE_CLEAN_UP) |
105 | close(s); | 105 | close(s); |
106 | if (s_addr->sa_family == AF_INET) | 106 | if (saddr->sa_family == AF_INET) |
107 | bb_perror_msg_and_die("%s (%s)", | 107 | bb_perror_msg_and_die("%s (%s)", |
108 | "can't connect to remote host", | 108 | "can't connect to remote host", |
109 | inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); | 109 | inet_ntoa(((struct sockaddr_in *)saddr)->sin_addr)); |
110 | bb_perror_msg_and_die("can't connect to remote host"); | 110 | bb_perror_msg_and_die("can't connect to remote host"); |
111 | } | 111 | } |
112 | } | 112 | } |
@@ -353,6 +353,10 @@ int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type) | |||
353 | #if ENABLE_FEATURE_IPV6 | 353 | #if ENABLE_FEATURE_IPV6 |
354 | fd = socket(AF_INET6, sock_type, 0); | 354 | fd = socket(AF_INET6, sock_type, 0); |
355 | if (fd >= 0) { | 355 | if (fd >= 0) { |
356 | #if ENABLE_PLATFORM_MINGW32 | ||
357 | DWORD buffer = 0; | ||
358 | setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &buffer, sizeof(DWORD)); | ||
359 | #endif | ||
356 | family = AF_INET6; | 360 | family = AF_INET6; |
357 | goto done; | 361 | goto done; |
358 | } | 362 | } |
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c index 7d4cb60a5..2c5a9ef39 100644 --- a/libbb/xreadlink.c +++ b/libbb/xreadlink.c | |||
@@ -65,7 +65,7 @@ char* FAST_FUNC xmalloc_follow_symlinks(const char *path) | |||
65 | linkpath = xmalloc_readlink(buf); | 65 | linkpath = xmalloc_readlink(buf); |
66 | if (!linkpath) { | 66 | if (!linkpath) { |
67 | /* not a symlink, or doesn't exist */ | 67 | /* not a symlink, or doesn't exist */ |
68 | if (errno == EINVAL || errno == ENOENT) | 68 | if (errno == EINVAL || errno == ENOENT || (ENABLE_PLATFORM_MINGW32 && errno == ENOSYS)) |
69 | return buf; | 69 | return buf; |
70 | goto free_buf_ret_null; | 70 | goto free_buf_ret_null; |
71 | } | 71 | } |
diff --git a/miscutils/bbconfig.c b/miscutils/bbconfig.c index 045aca211..bcc9caa7c 100644 --- a/miscutils/bbconfig.c +++ b/miscutils/bbconfig.c | |||
@@ -34,6 +34,7 @@ | |||
34 | #include "libbb.h" | 34 | #include "libbb.h" |
35 | #include "bbconfigopts.h" | 35 | #include "bbconfigopts.h" |
36 | #if ENABLE_FEATURE_COMPRESS_BBCONFIG | 36 | #if ENABLE_FEATURE_COMPRESS_BBCONFIG |
37 | #define BB_ARCHIVE_PUBLIC | ||
37 | # include "bb_archive.h" | 38 | # include "bb_archive.h" |
38 | # include "bbconfigopts_bz2.h" | 39 | # include "bbconfigopts_bz2.h" |
39 | #endif | 40 | #endif |
diff --git a/miscutils/dc.c b/miscutils/dc.c index b922a7184..f752a1377 100644 --- a/miscutils/dc.c +++ b/miscutils/dc.c | |||
@@ -56,7 +56,7 @@ typedef unsigned long data_t; | |||
56 | #define DATA_FMT "l" | 56 | #define DATA_FMT "l" |
57 | #else | 57 | #else |
58 | typedef unsigned long long data_t; | 58 | typedef unsigned long long data_t; |
59 | #define DATA_FMT "ll" | 59 | #define DATA_FMT LL_FMT |
60 | #endif | 60 | #endif |
61 | 61 | ||
62 | 62 | ||
diff --git a/miscutils/less.c b/miscutils/less.c index c1d5e1b39..75ded97cf 100644 --- a/miscutils/less.c +++ b/miscutils/less.c | |||
@@ -127,6 +127,10 @@ | |||
127 | 127 | ||
128 | #include <sched.h> /* sched_yield() */ | 128 | #include <sched.h> /* sched_yield() */ |
129 | 129 | ||
130 | #if ENABLE_PLATFORM_MINGW32 | ||
131 | #include <conio.h> | ||
132 | #endif | ||
133 | |||
130 | #include "libbb.h" | 134 | #include "libbb.h" |
131 | #include "common_bufsiz.h" | 135 | #include "common_bufsiz.h" |
132 | #if ENABLE_FEATURE_LESS_REGEXP | 136 | #if ENABLE_FEATURE_LESS_REGEXP |
@@ -533,6 +537,11 @@ static void read_lines(void) | |||
533 | last_line_pos = 0; | 537 | last_line_pos = 0; |
534 | break; | 538 | break; |
535 | } | 539 | } |
540 | #if ENABLE_PLATFORM_MINGW32 | ||
541 | if (c == '\r') { | ||
542 | continue; | ||
543 | } | ||
544 | #endif | ||
536 | /* NUL is substituted by '\n'! */ | 545 | /* NUL is substituted by '\n'! */ |
537 | if (c == '\0') c = '\n'; | 546 | if (c == '\0') c = '\n'; |
538 | *p++ = c; | 547 | *p++ = c; |
@@ -629,7 +638,12 @@ static void update_num_lines(void) | |||
629 | /* only do this for regular files */ | 638 | /* only do this for regular files */ |
630 | if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) { | 639 | if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) { |
631 | count = 0; | 640 | count = 0; |
641 | #if !ENABLE_PLATFORM_MINGW32 | ||
632 | fd = open("/proc/self/fd/0", O_RDONLY); | 642 | fd = open("/proc/self/fd/0", O_RDONLY); |
643 | #else | ||
644 | /* don't even try to access /proc on WIN32 */ | ||
645 | fd = -1; | ||
646 | #endif | ||
633 | if (fd < 0 && num_lines == REOPEN_AND_COUNT) { | 647 | if (fd < 0 && num_lines == REOPEN_AND_COUNT) { |
634 | /* "filename" is valid only if REOPEN_AND_COUNT */ | 648 | /* "filename" is valid only if REOPEN_AND_COUNT */ |
635 | fd = open(filename, O_RDONLY); | 649 | fd = open(filename, O_RDONLY); |
@@ -812,7 +826,12 @@ static void print_found(const char *line) | |||
812 | match_status = 1; | 826 | match_status = 1; |
813 | } | 827 | } |
814 | 828 | ||
829 | #if !ENABLE_PLATFORM_MINGW32 | ||
815 | printf("%s%s\n", growline ? growline : "", str); | 830 | printf("%s%s\n", growline ? growline : "", str); |
831 | #else | ||
832 | /* skip newline, we use explicit positioning on WIN32 */ | ||
833 | printf("%s%s", growline ? growline : "", str); | ||
834 | #endif | ||
816 | free(growline); | 835 | free(growline); |
817 | } | 836 | } |
818 | #else | 837 | #else |
@@ -848,7 +867,12 @@ static void print_ascii(const char *str) | |||
848 | *p = '\0'; | 867 | *p = '\0'; |
849 | print_hilite(buf); | 868 | print_hilite(buf); |
850 | } | 869 | } |
870 | #if !ENABLE_PLATFORM_MINGW32 | ||
851 | puts(str); | 871 | puts(str); |
872 | #else | ||
873 | /* skip newline, we use explicit positioning on WIN32 */ | ||
874 | printf("%s", str); | ||
875 | #endif | ||
852 | } | 876 | } |
853 | 877 | ||
854 | /* Print the buffer */ | 878 | /* Print the buffer */ |
@@ -858,6 +882,10 @@ static void buffer_print(void) | |||
858 | 882 | ||
859 | move_cursor(0, 0); | 883 | move_cursor(0, 0); |
860 | for (i = 0; i <= max_displayed_line; i++) { | 884 | for (i = 0; i <= max_displayed_line; i++) { |
885 | #if ENABLE_PLATFORM_MINGW32 | ||
886 | /* make sure we're on the right line */ | ||
887 | move_cursor(i+1, 0); | ||
888 | #endif | ||
861 | printf(CLEAR_2_EOL); | 889 | printf(CLEAR_2_EOL); |
862 | if (option_mask32 & FLAG_N) | 890 | if (option_mask32 & FLAG_N) |
863 | print_lineno(buffer[i]); | 891 | print_lineno(buffer[i]); |
@@ -1044,9 +1072,13 @@ static void reinitialize(void) | |||
1044 | if (G.winsize_err) | 1072 | if (G.winsize_err) |
1045 | printf("\033[999;999H" "\033[6n"); | 1073 | printf("\033[999;999H" "\033[6n"); |
1046 | #endif | 1074 | #endif |
1075 | #if ENABLE_PLATFORM_MINGW32 | ||
1076 | reset_screen(); | ||
1077 | #endif | ||
1047 | buffer_fill_and_print(); | 1078 | buffer_fill_and_print(); |
1048 | } | 1079 | } |
1049 | 1080 | ||
1081 | #if !ENABLE_PLATFORM_MINGW32 | ||
1050 | static int64_t getch_nowait(void) | 1082 | static int64_t getch_nowait(void) |
1051 | { | 1083 | { |
1052 | int rd; | 1084 | int rd; |
@@ -1108,6 +1140,46 @@ static int64_t getch_nowait(void) | |||
1108 | set_tty_cooked(); | 1140 | set_tty_cooked(); |
1109 | return key64; | 1141 | return key64; |
1110 | } | 1142 | } |
1143 | #else | ||
1144 | static int64_t getch_nowait(void) | ||
1145 | { | ||
1146 | int64_t c; | ||
1147 | |||
1148 | retry: | ||
1149 | c = _getch(); | ||
1150 | if (c == 0 || c == 0xe0) { | ||
1151 | switch (_getch()) { | ||
1152 | case 0x48: | ||
1153 | c = KEYCODE_UP; | ||
1154 | break; | ||
1155 | case 0x50: | ||
1156 | c = KEYCODE_DOWN; | ||
1157 | break; | ||
1158 | case 0x49: | ||
1159 | c = KEYCODE_PAGEUP; | ||
1160 | break; | ||
1161 | case 0x51: | ||
1162 | c = KEYCODE_PAGEDOWN; | ||
1163 | break; | ||
1164 | case 0x47: | ||
1165 | c = KEYCODE_HOME; | ||
1166 | break; | ||
1167 | case 0x4f: | ||
1168 | c = KEYCODE_END; | ||
1169 | break; | ||
1170 | default: | ||
1171 | goto retry; | ||
1172 | } | ||
1173 | } | ||
1174 | |||
1175 | /* Position cursor if line input is done */ | ||
1176 | if (less_gets_pos >= 0) | ||
1177 | move_cursor(max_displayed_line + 2, less_gets_pos + 1); | ||
1178 | fflush_all(); | ||
1179 | |||
1180 | return c; | ||
1181 | } | ||
1182 | #endif | ||
1111 | 1183 | ||
1112 | /* Grab a character from input without requiring the return key. | 1184 | /* Grab a character from input without requiring the return key. |
1113 | * May return KEYCODE_xxx values. | 1185 | * May return KEYCODE_xxx values. |
@@ -1763,8 +1835,10 @@ static void sigwinch_handler(int sig UNUSED_PARAM) | |||
1763 | int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; | 1835 | int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; |
1764 | int less_main(int argc, char **argv) | 1836 | int less_main(int argc, char **argv) |
1765 | { | 1837 | { |
1838 | #if !ENABLE_PLATFORM_MINGW32 | ||
1766 | char *tty_name; | 1839 | char *tty_name; |
1767 | int tty_fd; | 1840 | int tty_fd; |
1841 | #endif | ||
1768 | 1842 | ||
1769 | INIT_G(); | 1843 | INIT_G(); |
1770 | 1844 | ||
@@ -1798,6 +1872,7 @@ int less_main(int argc, char **argv) | |||
1798 | if (option_mask32 & FLAG_TILDE) | 1872 | if (option_mask32 & FLAG_TILDE) |
1799 | empty_line_marker = ""; | 1873 | empty_line_marker = ""; |
1800 | 1874 | ||
1875 | #if !ENABLE_PLATFORM_MINGW32 | ||
1801 | /* Some versions of less can survive w/o controlling tty, | 1876 | /* Some versions of less can survive w/o controlling tty, |
1802 | * try to do the same. This also allows to specify an alternative | 1877 | * try to do the same. This also allows to specify an alternative |
1803 | * tty via "less 1<>TTY". | 1878 | * tty via "less 1<>TTY". |
@@ -1823,6 +1898,9 @@ int less_main(int argc, char **argv) | |||
1823 | } | 1898 | } |
1824 | G.kbd_fd_orig_flags = ndelay_on(tty_fd); | 1899 | G.kbd_fd_orig_flags = ndelay_on(tty_fd); |
1825 | kbd_fd = tty_fd; /* save in a global */ | 1900 | kbd_fd = tty_fd; /* save in a global */ |
1901 | #else | ||
1902 | kbd_fd = 0; | ||
1903 | #endif | ||
1826 | 1904 | ||
1827 | tcgetattr(kbd_fd, &term_orig); | 1905 | tcgetattr(kbd_fd, &term_orig); |
1828 | term_less = term_orig; | 1906 | term_less = term_orig; |
diff --git a/miscutils/man.c b/miscutils/man.c index a16202f25..f68784767 100644 --- a/miscutils/man.c +++ b/miscutils/man.c | |||
@@ -200,7 +200,11 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path) | |||
200 | char *next_path; | 200 | char *next_path; |
201 | char **path_element; | 201 | char **path_element; |
202 | 202 | ||
203 | #if ENABLE_PLATFORM_MINGW32 | ||
204 | next_path = next_path_sep(path); | ||
205 | #else | ||
203 | next_path = strchr(path, ':'); | 206 | next_path = strchr(path, ':'); |
207 | #endif | ||
204 | if (next_path) { | 208 | if (next_path) { |
205 | if (next_path == path) /* "::"? */ | 209 | if (next_path == path) /* "::"? */ |
206 | goto next; | 210 | goto next; |
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c index d70f4ca77..cb0a96b59 100644 --- a/networking/ftpgetput.c +++ b/networking/ftpgetput.c | |||
@@ -123,6 +123,9 @@ static int ftpcmd(const char *s1, const char *s2) | |||
123 | fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3), | 123 | fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3), |
124 | s1, s2); | 124 | s1, s2); |
125 | fflush(control_stream); | 125 | fflush(control_stream); |
126 | #if ENABLE_PLATFORM_MINGW32 | ||
127 | fseek(control_stream, 0L, SEEK_CUR); | ||
128 | #endif | ||
126 | } | 129 | } |
127 | 130 | ||
128 | do { | 131 | do { |
@@ -131,6 +134,9 @@ static int ftpcmd(const char *s1, const char *s2) | |||
131 | ftp_die(NULL); | 134 | ftp_die(NULL); |
132 | } | 135 | } |
133 | } while (!isdigit(buf[0]) || buf[3] != ' '); | 136 | } while (!isdigit(buf[0]) || buf[3] != ' '); |
137 | #if ENABLE_PLATFORM_MINGW32 | ||
138 | fseek(control_stream, 0L, SEEK_CUR); | ||
139 | #endif | ||
134 | 140 | ||
135 | buf[3] = '\0'; | 141 | buf[3] = '\0'; |
136 | n = xatou(buf); | 142 | n = xatou(buf); |
diff --git a/networking/nc.c b/networking/nc.c index a13d77a00..df073d7df 100644 --- a/networking/nc.c +++ b/networking/nc.c | |||
@@ -117,7 +117,7 @@ int nc_main(int argc, char **argv) | |||
117 | IF_NOT_NC_EXTRA (const) unsigned delay = 0; | 117 | IF_NOT_NC_EXTRA (const) unsigned delay = 0; |
118 | IF_NOT_NC_EXTRA (const int execparam = 0;) | 118 | IF_NOT_NC_EXTRA (const int execparam = 0;) |
119 | IF_NC_EXTRA (char **execparam = NULL;) | 119 | IF_NC_EXTRA (char **execparam = NULL;) |
120 | struct pollfd pfds[2]; | 120 | fd_set readfds, testfds; |
121 | int opt; /* must be signed (getopt returns -1) */ | 121 | int opt; /* must be signed (getopt returns -1) */ |
122 | 122 | ||
123 | if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { | 123 | if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { |
@@ -235,28 +235,29 @@ int nc_main(int argc, char **argv) | |||
235 | IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);) | 235 | IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);) |
236 | } | 236 | } |
237 | 237 | ||
238 | /* loop copying stdin to cfd, and cfd to stdout */ | 238 | /* Select loop copying stdin to cfd, and cfd to stdout */ |
239 | 239 | ||
240 | pfds[0].fd = STDIN_FILENO; | 240 | FD_ZERO(&readfds); |
241 | pfds[0].events = POLLIN; | 241 | FD_SET(cfd, &readfds); |
242 | pfds[1].fd = cfd; | 242 | FD_SET(STDIN_FILENO, &readfds); |
243 | pfds[1].events = POLLIN; | ||
244 | 243 | ||
245 | #define iobuf bb_common_bufsiz1 | 244 | #define iobuf bb_common_bufsiz1 |
246 | setup_common_bufsiz(); | 245 | setup_common_bufsiz(); |
247 | for (;;) { | 246 | for (;;) { |
248 | int fdidx; | 247 | int fd; |
249 | int ofd; | 248 | int ofd; |
250 | int nread; | 249 | int nread; |
251 | 250 | ||
252 | if (safe_poll(pfds, 2, -1) < 0) | 251 | testfds = readfds; |
253 | bb_perror_msg_and_die("poll"); | ||
254 | 252 | ||
255 | fdidx = 0; | 253 | if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0) |
254 | bb_perror_msg_and_die("select"); | ||
255 | |||
256 | fd = STDIN_FILENO; | ||
256 | while (1) { | 257 | while (1) { |
257 | if (pfds[fdidx].revents) { | 258 | if (FD_ISSET(fd, &testfds)) { |
258 | nread = safe_read(pfds[fdidx].fd, iobuf, COMMON_BUFSIZE); | 259 | nread = safe_read(fd, iobuf, COMMON_BUFSIZE); |
259 | if (fdidx != 0) { | 260 | if (fd == cfd) { |
260 | if (nread < 1) | 261 | if (nread < 1) |
261 | exit(EXIT_SUCCESS); | 262 | exit(EXIT_SUCCESS); |
262 | ofd = STDOUT_FILENO; | 263 | ofd = STDOUT_FILENO; |
@@ -265,7 +266,7 @@ int nc_main(int argc, char **argv) | |||
265 | /* Close outgoing half-connection so they get EOF, | 266 | /* Close outgoing half-connection so they get EOF, |
266 | * but leave incoming alone so we can see response */ | 267 | * but leave incoming alone so we can see response */ |
267 | shutdown(cfd, SHUT_WR); | 268 | shutdown(cfd, SHUT_WR); |
268 | pfds[0].fd = -1; | 269 | FD_CLR(STDIN_FILENO, &readfds); |
269 | } | 270 | } |
270 | ofd = cfd; | 271 | ofd = cfd; |
271 | } | 272 | } |
@@ -273,9 +274,9 @@ int nc_main(int argc, char **argv) | |||
273 | if (delay > 0) | 274 | if (delay > 0) |
274 | sleep(delay); | 275 | sleep(delay); |
275 | } | 276 | } |
276 | if (fdidx == 1) | 277 | if (fd == cfd) |
277 | break; | 278 | break; |
278 | fdidx++; | 279 | fd = cfd; |
279 | } | 280 | } |
280 | } | 281 | } |
281 | } | 282 | } |
diff --git a/networking/wget.c b/networking/wget.c index e47c9a51b..ab9bc1836 100644 --- a/networking/wget.c +++ b/networking/wget.c | |||
@@ -457,11 +457,17 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp) | |||
457 | fprintf(stderr, "--> %s%s\n\n", s1, s2); | 457 | fprintf(stderr, "--> %s%s\n\n", s1, s2); |
458 | fflush(fp); | 458 | fflush(fp); |
459 | log_io("> %s%s", s1, s2); | 459 | log_io("> %s%s", s1, s2); |
460 | #if ENABLE_PLATFORM_MINGW32 | ||
461 | fseek(fp, 0L, SEEK_CUR); | ||
462 | #endif | ||
460 | } | 463 | } |
461 | 464 | ||
462 | do { | 465 | do { |
463 | fgets_and_trim(fp, "%s\n"); | 466 | fgets_and_trim(fp, "%s\n"); |
464 | } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' '); | 467 | } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' '); |
468 | #if ENABLE_PLATFORM_MINGW32 | ||
469 | fseek(fp, 0L, SEEK_CUR); | ||
470 | #endif | ||
465 | 471 | ||
466 | G.wget_buf[3] = '\0'; | 472 | G.wget_buf[3] = '\0'; |
467 | result = xatoi_positive(G.wget_buf); | 473 | result = xatoi_positive(G.wget_buf); |
diff --git a/procps/iostat.c b/procps/iostat.c index 608d41364..6a39c324f 100644 --- a/procps/iostat.c +++ b/procps/iostat.c | |||
@@ -29,7 +29,7 @@ | |||
29 | #if 1 | 29 | #if 1 |
30 | typedef unsigned long long cputime_t; | 30 | typedef unsigned long long cputime_t; |
31 | typedef long long icputime_t; | 31 | typedef long long icputime_t; |
32 | # define FMT_DATA "ll" | 32 | # define FMT_DATA LL_FMT |
33 | # define CPUTIME_MAX (~0ULL) | 33 | # define CPUTIME_MAX (~0ULL) |
34 | #else | 34 | #else |
35 | typedef unsigned long cputime_t; | 35 | typedef unsigned long cputime_t; |
diff --git a/procps/mpstat.c b/procps/mpstat.c index 1eabd8e38..05a3f3ff3 100644 --- a/procps/mpstat.c +++ b/procps/mpstat.c | |||
@@ -49,7 +49,7 @@ | |||
49 | #if 1 | 49 | #if 1 |
50 | typedef unsigned long long data_t; | 50 | typedef unsigned long long data_t; |
51 | typedef long long idata_t; | 51 | typedef long long idata_t; |
52 | #define FMT_DATA "ll" | 52 | #define FMT_DATA LL_FMT |
53 | #define DATA_MAX ULLONG_MAX | 53 | #define DATA_MAX ULLONG_MAX |
54 | #else | 54 | #else |
55 | typedef unsigned long data_t; | 55 | typedef unsigned long data_t; |
diff --git a/procps/ps.c b/procps/ps.c index eb1946d27..3e83a3e03 100644 --- a/procps/ps.c +++ b/procps/ps.c | |||
@@ -286,6 +286,7 @@ static unsigned get_kernel_HZ(void) | |||
286 | 286 | ||
287 | /* Print value to buf, max size+1 chars (including trailing '\0') */ | 287 | /* Print value to buf, max size+1 chars (including trailing '\0') */ |
288 | 288 | ||
289 | #if !ENABLE_PLATFORM_MINGW32 | ||
289 | static void func_user(char *buf, int size, const procps_status_t *ps) | 290 | static void func_user(char *buf, int size, const procps_status_t *ps) |
290 | { | 291 | { |
291 | #if 1 | 292 | #if 1 |
@@ -309,12 +310,14 @@ static void func_group(char *buf, int size, const procps_status_t *ps) | |||
309 | { | 310 | { |
310 | safe_strncpy(buf, get_cached_groupname(ps->gid), size+1); | 311 | safe_strncpy(buf, get_cached_groupname(ps->gid), size+1); |
311 | } | 312 | } |
313 | #endif | ||
312 | 314 | ||
313 | static void func_comm(char *buf, int size, const procps_status_t *ps) | 315 | static void func_comm(char *buf, int size, const procps_status_t *ps) |
314 | { | 316 | { |
315 | safe_strncpy(buf, ps->comm, size+1); | 317 | safe_strncpy(buf, ps->comm, size+1); |
316 | } | 318 | } |
317 | 319 | ||
320 | #if !ENABLE_PLATFORM_MINGW32 | ||
318 | static void func_state(char *buf, int size, const procps_status_t *ps) | 321 | static void func_state(char *buf, int size, const procps_status_t *ps) |
319 | { | 322 | { |
320 | safe_strncpy(buf, ps->state, size+1); | 323 | safe_strncpy(buf, ps->state, size+1); |
@@ -324,12 +327,14 @@ static void func_args(char *buf, int size, const procps_status_t *ps) | |||
324 | { | 327 | { |
325 | read_cmdline(buf, size+1, ps->pid, ps->comm); | 328 | read_cmdline(buf, size+1, ps->pid, ps->comm); |
326 | } | 329 | } |
330 | #endif | ||
327 | 331 | ||
328 | static void func_pid(char *buf, int size, const procps_status_t *ps) | 332 | static void func_pid(char *buf, int size, const procps_status_t *ps) |
329 | { | 333 | { |
330 | sprintf(buf, "%*u", size, ps->pid); | 334 | sprintf(buf, "%*u", size, ps->pid); |
331 | } | 335 | } |
332 | 336 | ||
337 | #if !ENABLE_PLATFORM_MINGW32 | ||
333 | static void func_ppid(char *buf, int size, const procps_status_t *ps) | 338 | static void func_ppid(char *buf, int size, const procps_status_t *ps) |
334 | { | 339 | { |
335 | sprintf(buf, "%*u", size, ps->ppid); | 340 | sprintf(buf, "%*u", size, ps->ppid); |
@@ -371,6 +376,7 @@ static void func_tty(char *buf, int size, const procps_status_t *ps) | |||
371 | if (ps->tty_major) /* tty field of "0" means "no tty" */ | 376 | if (ps->tty_major) /* tty field of "0" means "no tty" */ |
372 | snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); | 377 | snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); |
373 | } | 378 | } |
379 | #endif | ||
374 | 380 | ||
375 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS | 381 | #if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS |
376 | 382 | ||
@@ -436,13 +442,19 @@ static void func_pcpu(char *buf, int size, const procps_status_t *ps) | |||
436 | 442 | ||
437 | static const ps_out_t out_spec[] = { | 443 | static const ps_out_t out_spec[] = { |
438 | /* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */ | 444 | /* Mandated by http://pubs.opengroup.org/onlinepubs/9699919799/utilities/ps.html: */ |
445 | #if !ENABLE_PLATFORM_MINGW32 | ||
439 | { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, | 446 | { 8 , "user" ,"USER" ,func_user ,PSSCAN_UIDGID }, |
440 | { 8 , "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID }, | 447 | { 8 , "group" ,"GROUP" ,func_group ,PSSCAN_UIDGID }, |
448 | #endif | ||
441 | { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM }, | 449 | { 16 , "comm" ,"COMMAND",func_comm ,PSSCAN_COMM }, |
450 | #if !ENABLE_PLATFORM_MINGW32 | ||
442 | { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, | 451 | { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, |
452 | #endif | ||
443 | { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, | 453 | { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, |
454 | #if !ENABLE_PLATFORM_MINGW32 | ||
444 | { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, | 455 | { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, |
445 | { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, | 456 | { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, |
457 | #endif | ||
446 | #if ENABLE_FEATURE_PS_TIME | 458 | #if ENABLE_FEATURE_PS_TIME |
447 | { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, | 459 | { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, |
448 | #endif | 460 | #endif |
@@ -455,12 +467,14 @@ static const ps_out_t out_spec[] = { | |||
455 | #if ENABLE_FEATURE_PS_TIME | 467 | #if ENABLE_FEATURE_PS_TIME |
456 | { 6 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, | 468 | { 6 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, |
457 | #endif | 469 | #endif |
470 | #if !ENABLE_PLATFORM_MINGW32 | ||
458 | { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, | 471 | { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, |
459 | { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, | 472 | { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, |
460 | /* Not mandated, but useful: */ | 473 | /* Not mandated, but useful: */ |
461 | { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID }, | 474 | { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID }, |
462 | { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE }, | 475 | { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE }, |
463 | { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, | 476 | { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, |
477 | #endif | ||
464 | #if ENABLE_SELINUX | 478 | #if ENABLE_SELINUX |
465 | { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, | 479 | { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, |
466 | #endif | 480 | #endif |
@@ -599,6 +613,8 @@ static void format_process(const procps_status_t *ps) | |||
599 | #if ENABLE_SELINUX | 613 | #if ENABLE_SELINUX |
600 | # define SELINUX_O_PREFIX "label," | 614 | # define SELINUX_O_PREFIX "label," |
601 | # define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args") | 615 | # define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args") |
616 | #elif ENABLE_PLATFORM_MINGW32 | ||
617 | # define DEFAULT_O_STR ("pid,comm") | ||
602 | #else | 618 | #else |
603 | # define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args") | 619 | # define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args") |
604 | #endif | 620 | #endif |
diff --git a/procps/smemcap.c b/procps/smemcap.c index c15fef1c3..17aeccd53 100644 --- a/procps/smemcap.c +++ b/procps/smemcap.c | |||
@@ -20,6 +20,7 @@ | |||
20 | //config: a memory usage statistic tool. | 20 | //config: a memory usage statistic tool. |
21 | 21 | ||
22 | #include "libbb.h" | 22 | #include "libbb.h" |
23 | #define BB_ARCHIVE_PUBLIC | ||
23 | #include "bb_archive.h" | 24 | #include "bb_archive.h" |
24 | 25 | ||
25 | struct fileblock { | 26 | struct fileblock { |
diff --git a/scripts/basic/docproc.c b/scripts/basic/docproc.c index 720098a23..bfc1a9844 100644 --- a/scripts/basic/docproc.c +++ b/scripts/basic/docproc.c | |||
@@ -38,7 +38,9 @@ | |||
38 | #include <unistd.h> | 38 | #include <unistd.h> |
39 | #include <limits.h> | 39 | #include <limits.h> |
40 | #include <sys/types.h> | 40 | #include <sys/types.h> |
41 | #ifndef __MINGW32__ | ||
41 | #include <sys/wait.h> | 42 | #include <sys/wait.h> |
43 | #endif | ||
42 | //bbox disabled: #include <alloca.h> | 44 | //bbox disabled: #include <alloca.h> |
43 | 45 | ||
44 | /* exitstatus is used to keep track of any failing calls to kernel-doc, | 46 | /* exitstatus is used to keep track of any failing calls to kernel-doc, |
@@ -78,12 +80,24 @@ void usage (void) | |||
78 | */ | 80 | */ |
79 | void exec_kernel_doc(char **svec) | 81 | void exec_kernel_doc(char **svec) |
80 | { | 82 | { |
83 | #ifndef __MINGW32__ | ||
81 | pid_t pid; | 84 | pid_t pid; |
82 | int ret; | 85 | int ret; |
86 | #endif | ||
83 | char *real_filename; | 87 | char *real_filename; |
84 | int rflen; | 88 | int rflen; |
85 | 89 | ||
86 | /* Make sure output generated so far are flushed */ | 90 | /* Make sure output generated so far are flushed */ |
91 | #ifdef __MINGW32__ | ||
92 | fflush(stdout); | ||
93 | rflen = strlen(getenv("SRCTREE")); | ||
94 | rflen += strlen(KERNELDOCPATH KERNELDOC); | ||
95 | real_filename = alloca(rflen + 1); | ||
96 | strcpy(real_filename, getenv("SRCTREE")); | ||
97 | strcat(real_filename, KERNELDOCPATH KERNELDOC); | ||
98 | fprintf(stderr, "NOTIMPL: exec %s\n", real_filename); | ||
99 | exit(1); | ||
100 | #else | ||
87 | fflush(stdout); | 101 | fflush(stdout); |
88 | switch(pid=fork()) { | 102 | switch(pid=fork()) { |
89 | case -1: | 103 | case -1: |
@@ -106,6 +120,7 @@ void exec_kernel_doc(char **svec) | |||
106 | exitstatus |= WEXITSTATUS(ret); | 120 | exitstatus |= WEXITSTATUS(ret); |
107 | else | 121 | else |
108 | exitstatus = 0xff; | 122 | exitstatus = 0xff; |
123 | #endif | ||
109 | } | 124 | } |
110 | 125 | ||
111 | /* Types used to create list of all exported symbols in a number of files */ | 126 | /* Types used to create list of all exported symbols in a number of files */ |
diff --git a/scripts/basic/fixdep.c b/scripts/basic/fixdep.c index 19f82df09..9f461a65b 100644 --- a/scripts/basic/fixdep.c +++ b/scripts/basic/fixdep.c | |||
@@ -104,7 +104,9 @@ | |||
104 | 104 | ||
105 | #include <sys/types.h> | 105 | #include <sys/types.h> |
106 | #include <sys/stat.h> | 106 | #include <sys/stat.h> |
107 | #ifndef __MINGW32__ | ||
107 | #include <sys/mman.h> | 108 | #include <sys/mman.h> |
109 | #endif | ||
108 | #include <unistd.h> | 110 | #include <unistd.h> |
109 | #include <fcntl.h> | 111 | #include <fcntl.h> |
110 | #include <string.h> | 112 | #include <string.h> |
@@ -112,7 +114,9 @@ | |||
112 | #include <stdio.h> | 114 | #include <stdio.h> |
113 | #include <limits.h> | 115 | #include <limits.h> |
114 | #include <ctype.h> | 116 | #include <ctype.h> |
117 | #ifndef __MINGW32__ | ||
115 | #include <arpa/inet.h> | 118 | #include <arpa/inet.h> |
119 | #endif | ||
116 | //bbox disabled: #include <alloca.h> | 120 | //bbox disabled: #include <alloca.h> |
117 | 121 | ||
118 | /* bbox: not needed | 122 | /* bbox: not needed |
@@ -122,6 +126,57 @@ | |||
122 | #define INT_FIG_ ntohl(0x4649475f) | 126 | #define INT_FIG_ ntohl(0x4649475f) |
123 | */ | 127 | */ |
124 | 128 | ||
129 | #ifndef O_BINARY | ||
130 | #define O_BINARY 0 | ||
131 | #endif | ||
132 | |||
133 | #ifdef __MINGW32__ | ||
134 | #define UNUSED __attribute__ ((__unused__)) | ||
135 | |||
136 | /* Workaround specifically for fixdep */ | ||
137 | #define PROT_READ 0 | ||
138 | #define MAP_PRIVATE 0 | ||
139 | void *mmap(void *start UNUSED, size_t size, int prot UNUSED, | ||
140 | int flags UNUSED, int fd, off_t offset UNUSED) | ||
141 | { | ||
142 | void *p; | ||
143 | void *curP; | ||
144 | ssize_t readB; | ||
145 | |||
146 | p = malloc(size); | ||
147 | if (!p) | ||
148 | return (void*)((long)-1); | ||
149 | |||
150 | curP = p; | ||
151 | |||
152 | while (size > 0) | ||
153 | { | ||
154 | readB = read(fd, curP, size); | ||
155 | |||
156 | if (readB == 0) | ||
157 | { | ||
158 | /* EOF reached */ | ||
159 | break; | ||
160 | } | ||
161 | else if (readB < 0) | ||
162 | { | ||
163 | perror("fixdep: read config"); | ||
164 | free(p); | ||
165 | return (void*)((long)-1); | ||
166 | } | ||
167 | |||
168 | size -= readB; | ||
169 | curP += readB; | ||
170 | } | ||
171 | |||
172 | return p; | ||
173 | } | ||
174 | void munmap(void *p, size_t size UNUSED) | ||
175 | { | ||
176 | free(p); | ||
177 | } | ||
178 | #endif | ||
179 | |||
125 | char *target; | 180 | char *target; |
126 | char *depfile; | 181 | char *depfile; |
127 | char *cmdline; | 182 | char *cmdline; |
@@ -286,7 +341,7 @@ void do_config_file(char *filename) | |||
286 | int fd; | 341 | int fd; |
287 | void *map; | 342 | void *map; |
288 | 343 | ||
289 | fd = open(filename, O_RDONLY); | 344 | fd = open(filename, O_RDONLY | O_BINARY); |
290 | if (fd < 0) { | 345 | if (fd < 0) { |
291 | fprintf(stderr, "fixdep: "); | 346 | fprintf(stderr, "fixdep: "); |
292 | perror(filename); | 347 | perror(filename); |
@@ -298,7 +353,7 @@ void do_config_file(char *filename) | |||
298 | return; | 353 | return; |
299 | } | 354 | } |
300 | map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | 355 | map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
301 | if ((long) map == -1) { | 356 | if ((intptr_t) map == -1) { |
302 | perror("fixdep: mmap"); | 357 | perror("fixdep: mmap"); |
303 | close(fd); | 358 | close(fd); |
304 | return; | 359 | return; |
@@ -334,10 +389,12 @@ void parse_dep_file(void *map, size_t len) | |||
334 | m++; | 389 | m++; |
335 | p = m; | 390 | p = m; |
336 | while (p < end && *p != ' ') p++; | 391 | while (p < end && *p != ' ') p++; |
392 | if (p == m) break; | ||
337 | if (p == end) { | 393 | if (p == end) { |
338 | do p--; while (!isalnum(*p)); | 394 | do p--; while (p != m && !isalnum(*p)); |
339 | p++; | 395 | p++; |
340 | } | 396 | } |
397 | if (p == m) break; | ||
341 | memcpy(s, m, p-m); s[p-m] = 0; | 398 | memcpy(s, m, p-m); s[p-m] = 0; |
342 | if (strrcmp(s, "include/autoconf.h") && | 399 | if (strrcmp(s, "include/autoconf.h") && |
343 | strrcmp(s, "arch/um/include/uml-config.h") && | 400 | strrcmp(s, "arch/um/include/uml-config.h") && |
@@ -345,6 +402,7 @@ void parse_dep_file(void *map, size_t len) | |||
345 | printf(" %s \\\n", s); | 402 | printf(" %s \\\n", s); |
346 | do_config_file(s); | 403 | do_config_file(s); |
347 | } | 404 | } |
405 | if (p == end) break; | ||
348 | m = p + 1; | 406 | m = p + 1; |
349 | } | 407 | } |
350 | printf("\n%s: $(deps_%s)\n\n", target, target); | 408 | printf("\n%s: $(deps_%s)\n\n", target, target); |
@@ -357,7 +415,7 @@ void print_deps(void) | |||
357 | int fd; | 415 | int fd; |
358 | void *map; | 416 | void *map; |
359 | 417 | ||
360 | fd = open(depfile, O_RDONLY); | 418 | fd = open(depfile, O_RDONLY | O_BINARY); |
361 | if (fd < 0) { | 419 | if (fd < 0) { |
362 | fprintf(stderr, "fixdep: "); | 420 | fprintf(stderr, "fixdep: "); |
363 | perror(depfile); | 421 | perror(depfile); |
@@ -370,7 +428,7 @@ void print_deps(void) | |||
370 | return; | 428 | return; |
371 | } | 429 | } |
372 | map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); | 430 | map = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0); |
373 | if ((long) map == -1) { | 431 | if ((intptr_t) map == -1) { |
374 | perror("fixdep: mmap"); | 432 | perror("fixdep: mmap"); |
375 | close(fd); | 433 | close(fd); |
376 | return; | 434 | return; |
diff --git a/scripts/basic/split-include.c b/scripts/basic/split-include.c index e328788e2..8127fe261 100644 --- a/scripts/basic/split-include.c +++ b/scripts/basic/split-include.c | |||
@@ -39,8 +39,6 @@ | |||
39 | exit(1); \ | 39 | exit(1); \ |
40 | } | 40 | } |
41 | 41 | ||
42 | |||
43 | |||
44 | int main(int argc, const char * argv []) | 42 | int main(int argc, const char * argv []) |
45 | { | 43 | { |
46 | const char * str_my_name; | 44 | const char * str_my_name; |
@@ -89,7 +87,11 @@ int main(int argc, const char * argv []) | |||
89 | /* Make output directory if needed. */ | 87 | /* Make output directory if needed. */ |
90 | if (stat(str_dir_config, &stat_buf) != 0) | 88 | if (stat(str_dir_config, &stat_buf) != 0) |
91 | { | 89 | { |
90 | #ifdef __MINGW32__ | ||
91 | if (mkdir(str_dir_config) != 0) | ||
92 | #else | ||
92 | if (mkdir(str_dir_config, 0755) != 0) | 93 | if (mkdir(str_dir_config, 0755) != 0) |
94 | #endif | ||
93 | ERROR_EXIT(str_dir_config); | 95 | ERROR_EXIT(str_dir_config); |
94 | } | 96 | } |
95 | 97 | ||
@@ -148,7 +150,12 @@ int main(int argc, const char * argv []) | |||
148 | { | 150 | { |
149 | ptarget[islash] = '\0'; | 151 | ptarget[islash] = '\0'; |
150 | if (stat(ptarget, &stat_buf) != 0 | 152 | if (stat(ptarget, &stat_buf) != 0 |
151 | && mkdir(ptarget, 0755) != 0) | 153 | #ifdef __MINGW32__ |
154 | && mkdir(ptarget) != 0 | ||
155 | #else | ||
156 | && mkdir(ptarget, 0755) != 0 | ||
157 | #endif | ||
158 | ) | ||
152 | ERROR_EXIT( ptarget ); | 159 | ERROR_EXIT( ptarget ); |
153 | ptarget[islash] = '/'; | 160 | ptarget[islash] = '/'; |
154 | } | 161 | } |
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c index ea2446a89..41ac23936 100644 --- a/scripts/kconfig/conf.c +++ b/scripts/kconfig/conf.c | |||
@@ -153,9 +153,14 @@ static void conf_askvalue(struct symbol *sym, const char *def) | |||
153 | break; | 153 | break; |
154 | } | 154 | } |
155 | case set_random: | 155 | case set_random: |
156 | #ifdef __MINGW32__ | ||
157 | fprintf(stderr, "set_random not supported\n"); | ||
158 | exit(1); | ||
159 | #else | ||
156 | do { | 160 | do { |
157 | val = (tristate)(random() % 3); | 161 | val = (tristate)(random() % 3); |
158 | } while (!sym_tristate_within_range(sym, val)); | 162 | } while (!sym_tristate_within_range(sym, val)); |
163 | #endif | ||
159 | switch (val) { | 164 | switch (val) { |
160 | case no: line[0] = 'n'; break; | 165 | case no: line[0] = 'n'; break; |
161 | case mod: line[0] = 'm'; break; | 166 | case mod: line[0] = 'm'; break; |
@@ -366,7 +371,12 @@ static int conf_choice(struct menu *menu) | |||
366 | continue; | 371 | continue; |
367 | break; | 372 | break; |
368 | case set_random: | 373 | case set_random: |
374 | #ifdef __MINGW32__ | ||
375 | fprintf(stderr, "set_random not supported\n"); | ||
376 | exit(1); | ||
377 | #else | ||
369 | def = (random() % cnt) + 1; | 378 | def = (random() % cnt) + 1; |
379 | #endif | ||
370 | case set_default: | 380 | case set_default: |
371 | case set_yes: | 381 | case set_yes: |
372 | case set_mod: | 382 | case set_mod: |
@@ -522,8 +532,13 @@ int main(int ac, char **av) | |||
522 | input_mode = set_yes; | 532 | input_mode = set_yes; |
523 | break; | 533 | break; |
524 | case 'r': | 534 | case 'r': |
535 | #ifdef __MINGW32__ | ||
536 | fprintf(stderr, "set_random not supported\n"); | ||
537 | exit(1); | ||
538 | #else | ||
525 | input_mode = set_random; | 539 | input_mode = set_random; |
526 | srandom(time(NULL)); | 540 | srandom(time(NULL)); |
541 | #endif | ||
527 | break; | 542 | break; |
528 | case 'h': | 543 | case 'h': |
529 | case '?': | 544 | case '?': |
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c index 8f4ecbd33..1d77aefb7 100644 --- a/scripts/kconfig/confdata.c +++ b/scripts/kconfig/confdata.c | |||
@@ -571,15 +571,24 @@ int conf_write(const char *name) | |||
571 | fclose(out); | 571 | fclose(out); |
572 | if (out_h) { | 572 | if (out_h) { |
573 | fclose(out_h); | 573 | fclose(out_h); |
574 | #ifdef __MINGW32__ | ||
575 | unlink("include/autoconf.h"); | ||
576 | #endif | ||
574 | rename(".tmpconfig.h", "include/autoconf.h"); | 577 | rename(".tmpconfig.h", "include/autoconf.h"); |
575 | } | 578 | } |
576 | if (!name || basename != conf_def_filename) { | 579 | if (!name || basename != conf_def_filename) { |
577 | if (!name) | 580 | if (!name) |
578 | name = conf_def_filename; | 581 | name = conf_def_filename; |
579 | sprintf(tmpname, "%s.old", name); | 582 | sprintf(tmpname, "%s.old", name); |
583 | #ifdef __MINGW32__ | ||
584 | unlink(tmpname); | ||
585 | #endif | ||
580 | rename(name, tmpname); | 586 | rename(name, tmpname); |
581 | } | 587 | } |
582 | sprintf(tmpname, "%s%s", dirname, basename); | 588 | sprintf(tmpname, "%s%s", dirname, basename); |
589 | #ifdef __MINGW32__ | ||
590 | unlink(tmpname); | ||
591 | #endif | ||
583 | if (rename(newname, tmpname)) | 592 | if (rename(newname, tmpname)) |
584 | return 1; | 593 | return 1; |
585 | 594 | ||
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h index 527f60c99..b88b89d2d 100644 --- a/scripts/kconfig/lkc.h +++ b/scripts/kconfig/lkc.h | |||
@@ -11,9 +11,9 @@ | |||
11 | #ifndef KBUILD_NO_NLS | 11 | #ifndef KBUILD_NO_NLS |
12 | # include <libintl.h> | 12 | # include <libintl.h> |
13 | #else | 13 | #else |
14 | # define gettext(Msgid) ((const char *) (Msgid)) | 14 | static inline const char *gettext(const char *txt) { return txt; } |
15 | # define textdomain(Domainname) ((const char *) (Domainname)) | 15 | static inline void textdomain(const char *domainname) {} |
16 | # define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) | 16 | static inline void bindtextdomain(const char *name, const char *dir) {} |
17 | #endif | 17 | #endif |
18 | 18 | ||
19 | #ifdef __cplusplus | 19 | #ifdef __cplusplus |
diff --git a/scripts/kconfig/mconf.c b/scripts/kconfig/mconf.c index 006d03708..d3ad12d90 100644 --- a/scripts/kconfig/mconf.c +++ b/scripts/kconfig/mconf.c | |||
@@ -12,8 +12,10 @@ | |||
12 | /* On Darwin, this may be needed to get SIGWINCH: */ | 12 | /* On Darwin, this may be needed to get SIGWINCH: */ |
13 | #define _DARWIN_C_SOURCE 1 | 13 | #define _DARWIN_C_SOURCE 1 |
14 | 14 | ||
15 | #ifndef __MINGW32__ | ||
15 | #include <sys/ioctl.h> | 16 | #include <sys/ioctl.h> |
16 | #include <sys/wait.h> | 17 | #include <sys/wait.h> |
18 | #endif | ||
17 | #include <ctype.h> | 19 | #include <ctype.h> |
18 | #include <errno.h> | 20 | #include <errno.h> |
19 | #include <fcntl.h> | 21 | #include <fcntl.h> |
@@ -23,7 +25,9 @@ | |||
23 | #include <stdlib.h> | 25 | #include <stdlib.h> |
24 | #include <string.h> | 26 | #include <string.h> |
25 | #include <strings.h> /* for strcasecmp */ | 27 | #include <strings.h> /* for strcasecmp */ |
28 | #ifndef __MINGW32__ | ||
26 | #include <termios.h> | 29 | #include <termios.h> |
30 | #endif | ||
27 | #include <unistd.h> | 31 | #include <unistd.h> |
28 | #include <locale.h> | 32 | #include <locale.h> |
29 | 33 | ||
@@ -266,11 +270,15 @@ static char input_buf[4096]; | |||
266 | static const char filename[] = ".config"; | 270 | static const char filename[] = ".config"; |
267 | static char *args[1024], **argptr = args; | 271 | static char *args[1024], **argptr = args; |
268 | static int indent; | 272 | static int indent; |
273 | #ifndef __MINGW32__ | ||
269 | static struct termios ios_org; | 274 | static struct termios ios_org; |
275 | #endif | ||
270 | static int rows = 0, cols = 0; | 276 | static int rows = 0, cols = 0; |
271 | static struct menu *current_menu; | 277 | static struct menu *current_menu; |
272 | static int child_count; | 278 | static int child_count; |
279 | #ifndef __MINGW32__ | ||
273 | static int do_resize; | 280 | static int do_resize; |
281 | #endif | ||
274 | static int single_menu_mode; | 282 | static int single_menu_mode; |
275 | 283 | ||
276 | static void conf(struct menu *menu); | 284 | static void conf(struct menu *menu); |
@@ -290,6 +298,9 @@ static int cprint(const char *fmt, ...); | |||
290 | 298 | ||
291 | static void init_wsize(void) | 299 | static void init_wsize(void) |
292 | { | 300 | { |
301 | #ifdef __MINGW32__ | ||
302 | fprintf(stderr, "Skipping attempt to change window size\n"); | ||
303 | #else | ||
293 | struct winsize ws; | 304 | struct winsize ws; |
294 | char *env; | 305 | char *env; |
295 | 306 | ||
@@ -321,6 +332,7 @@ static void init_wsize(void) | |||
321 | 332 | ||
322 | rows -= 4; | 333 | rows -= 4; |
323 | cols -= 5; | 334 | cols -= 5; |
335 | #endif | ||
324 | } | 336 | } |
325 | 337 | ||
326 | static void cprint_init(void) | 338 | static void cprint_init(void) |
@@ -457,6 +469,10 @@ static void winch_handler(int sig) | |||
457 | 469 | ||
458 | static int exec_conf(void) | 470 | static int exec_conf(void) |
459 | { | 471 | { |
472 | #ifdef __MINGW32__ | ||
473 | fprintf(stderr, "exec_conf not implemented\n"); | ||
474 | exit(1); | ||
475 | #else | ||
460 | int pipefd[2], stat, size; | 476 | int pipefd[2], stat, size; |
461 | sigset_t sset, osset; | 477 | sigset_t sset, osset; |
462 | 478 | ||
@@ -530,6 +546,7 @@ static int exec_conf(void) | |||
530 | sigprocmask(SIG_SETMASK, &osset, NULL); | 546 | sigprocmask(SIG_SETMASK, &osset, NULL); |
531 | 547 | ||
532 | return WEXITSTATUS(stat); | 548 | return WEXITSTATUS(stat); |
549 | #endif | ||
533 | } | 550 | } |
534 | 551 | ||
535 | static void search_conf(void) | 552 | static void search_conf(void) |
@@ -783,7 +800,7 @@ static void conf(struct menu *menu) | |||
783 | switch (type) { | 800 | switch (type) { |
784 | case 'm': | 801 | case 'm': |
785 | if (single_menu_mode) | 802 | if (single_menu_mode) |
786 | submenu->data = (void *) (long) !submenu->data; | 803 | submenu->data = (void *) (intptr_t) !submenu->data; |
787 | else | 804 | else |
788 | conf(submenu); | 805 | conf(submenu); |
789 | break; | 806 | break; |
@@ -1044,7 +1061,9 @@ static void conf_save(void) | |||
1044 | 1061 | ||
1045 | static void conf_cleanup(void) | 1062 | static void conf_cleanup(void) |
1046 | { | 1063 | { |
1064 | #ifndef __MINGW32__ | ||
1047 | tcsetattr(1, TCSAFLUSH, &ios_org); | 1065 | tcsetattr(1, TCSAFLUSH, &ios_org); |
1066 | #endif | ||
1048 | unlink(".help.tmp"); | 1067 | unlink(".help.tmp"); |
1049 | unlink("lxdialog.scrltmp"); | 1068 | unlink("lxdialog.scrltmp"); |
1050 | } | 1069 | } |
@@ -1073,7 +1092,9 @@ int main(int ac, char **av) | |||
1073 | single_menu_mode = 1; | 1092 | single_menu_mode = 1; |
1074 | } | 1093 | } |
1075 | 1094 | ||
1095 | #ifndef __MINGW32__ | ||
1076 | tcgetattr(1, &ios_org); | 1096 | tcgetattr(1, &ios_org); |
1097 | #endif | ||
1077 | atexit(conf_cleanup); | 1098 | atexit(conf_cleanup); |
1078 | init_wsize(); | 1099 | init_wsize(); |
1079 | conf(&rootmenu); | 1100 | conf(&rootmenu); |
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c index 3d7877afc..63199cd93 100644 --- a/scripts/kconfig/symbol.c +++ b/scripts/kconfig/symbol.c | |||
@@ -6,8 +6,10 @@ | |||
6 | #include <ctype.h> | 6 | #include <ctype.h> |
7 | #include <stdlib.h> | 7 | #include <stdlib.h> |
8 | #include <string.h> | 8 | #include <string.h> |
9 | #ifndef __MINGW32__ | ||
9 | #include <regex.h> | 10 | #include <regex.h> |
10 | #include <sys/utsname.h> | 11 | #include <sys/utsname.h> |
12 | #endif | ||
11 | 13 | ||
12 | #define LKC_DIRECT_LINK | 14 | #define LKC_DIRECT_LINK |
13 | #include "lkc.h" | 15 | #include "lkc.h" |
@@ -44,7 +46,9 @@ void sym_add_default(struct symbol *sym, const char *def) | |||
44 | void sym_init(void) | 46 | void sym_init(void) |
45 | { | 47 | { |
46 | struct symbol *sym; | 48 | struct symbol *sym; |
49 | #ifndef __MINGW32__ | ||
47 | struct utsname uts; | 50 | struct utsname uts; |
51 | #endif | ||
48 | char *p; | 52 | char *p; |
49 | static bool inited = false; | 53 | static bool inited = false; |
50 | 54 | ||
@@ -52,7 +56,9 @@ void sym_init(void) | |||
52 | return; | 56 | return; |
53 | inited = true; | 57 | inited = true; |
54 | 58 | ||
59 | #ifndef __MINGW32__ | ||
55 | uname(&uts); | 60 | uname(&uts); |
61 | #endif | ||
56 | 62 | ||
57 | sym = sym_lookup("ARCH", 0); | 63 | sym = sym_lookup("ARCH", 0); |
58 | sym->type = S_STRING; | 64 | sym->type = S_STRING; |
@@ -71,7 +77,11 @@ void sym_init(void) | |||
71 | sym = sym_lookup("UNAME_RELEASE", 0); | 77 | sym = sym_lookup("UNAME_RELEASE", 0); |
72 | sym->type = S_STRING; | 78 | sym->type = S_STRING; |
73 | sym->flags |= SYMBOL_AUTO; | 79 | sym->flags |= SYMBOL_AUTO; |
80 | #ifdef __MINGW32__ | ||
81 | sym_add_default(sym, "UNKNOWN"); | ||
82 | #else | ||
74 | sym_add_default(sym, uts.release); | 83 | sym_add_default(sym, uts.release); |
84 | #endif | ||
75 | } | 85 | } |
76 | 86 | ||
77 | enum symbol_type sym_get_type(struct symbol *sym) | 87 | enum symbol_type sym_get_type(struct symbol *sym) |
@@ -720,6 +730,10 @@ struct symbol *sym_find(const char *name) | |||
720 | 730 | ||
721 | struct symbol **sym_re_search(const char *pattern) | 731 | struct symbol **sym_re_search(const char *pattern) |
722 | { | 732 | { |
733 | #ifdef __MINGW32__ | ||
734 | fprintf(stderr, "NOTIMPL: sym_re_search\n"); | ||
735 | exit(1); | ||
736 | #else | ||
723 | struct symbol *sym, **sym_arr = NULL; | 737 | struct symbol *sym, **sym_arr = NULL; |
724 | int i, cnt, size; | 738 | int i, cnt, size; |
725 | regex_t re; | 739 | regex_t re; |
@@ -752,6 +766,7 @@ struct symbol **sym_re_search(const char *pattern) | |||
752 | regfree(&re); | 766 | regfree(&re); |
753 | 767 | ||
754 | return sym_arr; | 768 | return sym_arr; |
769 | #endif | ||
755 | } | 770 | } |
756 | 771 | ||
757 | 772 | ||
diff --git a/scripts/kconfig/zconf.hash.c_shipped b/scripts/kconfig/zconf.hash.c_shipped index 29d9cf6cc..6996aba7f 100644 --- a/scripts/kconfig/zconf.hash.c_shipped +++ b/scripts/kconfig/zconf.hash.c_shipped | |||
@@ -161,43 +161,43 @@ kconf_id_lookup (register const char *str, register unsigned int len) | |||
161 | static struct kconf_id wordlist[] = | 161 | static struct kconf_id wordlist[] = |
162 | { | 162 | { |
163 | {-1}, {-1}, | 163 | {-1}, {-1}, |
164 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM}, | 164 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str2, T_IF, TF_COMMAND|TF_PARAM}, |
165 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT}, | 165 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str3, T_TYPE, TF_COMMAND, S_INT}, |
166 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str4, T_HELP, TF_COMMAND}, | 166 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str4, T_HELP, TF_COMMAND}, |
167 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND}, | 167 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str5, T_ENDIF, TF_COMMAND}, |
168 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_SELECT, TF_COMMAND}, | 168 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str6, T_SELECT, TF_COMMAND}, |
169 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_ENDMENU, TF_COMMAND}, | 169 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str7, T_ENDMENU, TF_COMMAND}, |
170 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE}, | 170 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str8, T_TYPE, TF_COMMAND, S_TRISTATE}, |
171 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND}, | 171 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str9, T_ENDCHOICE, TF_COMMAND}, |
172 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_RANGE, TF_COMMAND}, | 172 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str10, T_RANGE, TF_COMMAND}, |
173 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_TYPE, TF_COMMAND, S_STRING}, | 173 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str11, T_TYPE, TF_COMMAND, S_STRING}, |
174 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, | 174 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str12, T_DEFAULT, TF_COMMAND, S_UNKNOWN}, |
175 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, | 175 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str13, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, |
176 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_MENU, TF_COMMAND}, | 176 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str14, T_MENU, TF_COMMAND}, |
177 | {-1}, | 177 | {-1}, |
178 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, | 178 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str16, T_DEFAULT, TF_COMMAND, S_BOOLEAN}, |
179 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_DEFAULT, TF_COMMAND, S_TRISTATE}, | 179 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str17, T_DEFAULT, TF_COMMAND, S_TRISTATE}, |
180 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_MAINMENU, TF_COMMAND}, | 180 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str18, T_MAINMENU, TF_COMMAND}, |
181 | {-1}, | 181 | {-1}, |
182 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20, T_MENUCONFIG, TF_COMMAND}, | 182 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str20, T_MENUCONFIG, TF_COMMAND}, |
183 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_CONFIG, TF_COMMAND}, | 183 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str21, T_CONFIG, TF_COMMAND}, |
184 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ON, TF_PARAM}, | 184 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str22, T_ON, TF_PARAM}, |
185 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_TYPE, TF_COMMAND, S_HEX}, | 185 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str23, T_TYPE, TF_COMMAND, S_HEX}, |
186 | {-1}, {-1}, | 186 | {-1}, {-1}, |
187 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_SOURCE, TF_COMMAND}, | 187 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str26, T_SOURCE, TF_COMMAND}, |
188 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_DEPENDS, TF_COMMAND}, | 188 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str27, T_DEPENDS, TF_COMMAND}, |
189 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPTIONAL, TF_COMMAND}, | 189 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str28, T_OPTIONAL, TF_COMMAND}, |
190 | {-1}, {-1}, | 190 | {-1}, {-1}, |
191 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND}, | 191 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str31, T_SELECT, TF_COMMAND}, |
192 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND}, | 192 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str32, T_COMMENT, TF_COMMAND}, |
193 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_REQUIRES, TF_COMMAND}, | 193 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str33, T_REQUIRES, TF_COMMAND}, |
194 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34, T_TYPE, TF_COMMAND, S_BOOLEAN}, | 194 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str34, T_TYPE, TF_COMMAND, S_BOOLEAN}, |
195 | {-1}, {-1}, | 195 | {-1}, {-1}, |
196 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37, T_TYPE, TF_COMMAND, S_BOOLEAN}, | 196 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str37, T_TYPE, TF_COMMAND, S_BOOLEAN}, |
197 | {-1}, {-1}, {-1}, | 197 | {-1}, {-1}, {-1}, |
198 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_CHOICE, TF_COMMAND}, | 198 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str41, T_CHOICE, TF_COMMAND}, |
199 | {-1}, {-1}, {-1}, {-1}, | 199 | {-1}, {-1}, {-1}, {-1}, |
200 | {(int)(long)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_PROMPT, TF_COMMAND} | 200 | {(int)(intptr_t)&((struct kconf_id_strings_t *)0)->kconf_id_strings_str46, T_PROMPT, TF_COMMAND} |
201 | }; | 201 | }; |
202 | 202 | ||
203 | if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) | 203 | if (len <= MAX_WORD_LENGTH && len >= MIN_WORD_LENGTH) |
diff --git a/scripts/kconfig/zconf.tab.c_shipped b/scripts/kconfig/zconf.tab.c_shipped index a27d256d6..863f375be 100644 --- a/scripts/kconfig/zconf.tab.c_shipped +++ b/scripts/kconfig/zconf.tab.c_shipped | |||
@@ -143,6 +143,7 @@ | |||
143 | #include <stdarg.h> | 143 | #include <stdarg.h> |
144 | #include <stdio.h> | 144 | #include <stdio.h> |
145 | #include <stdlib.h> | 145 | #include <stdlib.h> |
146 | #include <stdint.h> | ||
146 | #include <string.h> | 147 | #include <string.h> |
147 | #include <stdbool.h> | 148 | #include <stdbool.h> |
148 | 149 | ||
diff --git a/shell/ash.c b/shell/ash.c index 1deae7c2f..18e53a0da 100644 --- a/shell/ash.c +++ b/shell/ash.c | |||
@@ -15,6 +15,21 @@ | |||
15 | * | 15 | * |
16 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. | 16 | * Licensed under GPLv2 or later, see file LICENSE in this source tree. |
17 | */ | 17 | */ |
18 | |||
19 | /* | ||
20 | * MinGW notes | ||
21 | * | ||
22 | * - Environment variables from Windows will all be turned to uppercase. | ||
23 | * - PATH accepts both ; and : as separator, but can't be mixed | ||
24 | * - command without ".exe" extension is still understood as executable | ||
25 | * - shell scripts on the path are detected by the presence of '#!'; | ||
26 | * the path to the interpreter is ignored, PATH is searched to find it | ||
27 | * - both / and \ are supported in PATH. Usually you must use / | ||
28 | * - trap/job does not work | ||
29 | * - /dev/null is supported for redirection | ||
30 | * - fake $PPID | ||
31 | */ | ||
32 | |||
18 | //config:config ASH | 33 | //config:config ASH |
19 | //config: bool "ash (77 kb)" | 34 | //config: bool "ash (77 kb)" |
20 | //config: default y | 35 | //config: default y |
@@ -132,6 +147,18 @@ | |||
132 | //config: you to run the specified command or builtin, | 147 | //config: you to run the specified command or builtin, |
133 | //config: even when there is a function with the same name. | 148 | //config: even when there is a function with the same name. |
134 | //config: | 149 | //config: |
150 | //config: | ||
151 | //config:config ASH_NOCONSOLE | ||
152 | //config: bool "'noconsole' option" | ||
153 | //config: default y | ||
154 | //config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32 | ||
155 | //config: help | ||
156 | //config: Enable support for the 'noconsole' option, which attempts to | ||
157 | //config: hide the console normally associated with a command line | ||
158 | //config: application. This may be useful when running a shell script | ||
159 | //config: from a GUI application. Disable this if your platform doesn't | ||
160 | //config: support the required APIs. | ||
161 | //config: | ||
135 | //config:endif # ash options | 162 | //config:endif # ash options |
136 | 163 | ||
137 | //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) | 164 | //applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) |
@@ -240,10 +267,58 @@ typedef long arith_t; | |||
240 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ | 267 | # define PIPE_BUF 4096 /* amount of buffering in a pipe */ |
241 | #endif | 268 | #endif |
242 | 269 | ||
270 | #if !ENABLE_PLATFORM_MINGW32 | ||
271 | # define is_absolute_path(path) ((path)[0] == '/') | ||
272 | #endif | ||
273 | |||
243 | #if !BB_MMU | 274 | #if !BB_MMU |
244 | # error "Do not even bother, ash will not run on NOMMU machine" | 275 | # error "Do not even bother, ash will not run on NOMMU machine" |
245 | #endif | 276 | #endif |
246 | 277 | ||
278 | #if ENABLE_PLATFORM_MINGW32 | ||
279 | union node; | ||
280 | struct strlist; | ||
281 | struct job; | ||
282 | |||
283 | struct forkshell { | ||
284 | /* filled by forkshell_copy() */ | ||
285 | struct globals_var *gvp; | ||
286 | struct globals_misc *gmp; | ||
287 | struct tblentry **cmdtable; | ||
288 | /* struct alias **atab; */ | ||
289 | /* struct parsefile *g_parsefile; */ | ||
290 | HANDLE hMapFile; | ||
291 | void *old_base; | ||
292 | int nodeptr_offset; | ||
293 | int size; | ||
294 | |||
295 | /* type of forkshell */ | ||
296 | int fpid; | ||
297 | |||
298 | /* optional data, used by forkshell_child */ | ||
299 | int flags; | ||
300 | int fd[10]; | ||
301 | union node *n; | ||
302 | char **argv; | ||
303 | char *string; | ||
304 | struct strlist *strlist; | ||
305 | }; | ||
306 | |||
307 | enum { | ||
308 | FS_OPENHERE, | ||
309 | FS_EVALBACKCMD, | ||
310 | FS_EVALSUBSHELL, | ||
311 | FS_EVALPIPE, | ||
312 | FS_SHELLEXEC | ||
313 | }; | ||
314 | |||
315 | static struct forkshell* forkshell_prepare(struct forkshell *fs); | ||
316 | static void forkshell_init(const char *idstr); | ||
317 | static void forkshell_child(struct forkshell *fs); | ||
318 | static void sticky_free(void *p); | ||
319 | #define free(p) sticky_free(p) | ||
320 | static int spawn_forkshell(struct job *jp, struct forkshell *fs, int mode); | ||
321 | #endif | ||
247 | 322 | ||
248 | /* ============ Hash table sizes. Configurable. */ | 323 | /* ============ Hash table sizes. Configurable. */ |
249 | 324 | ||
@@ -276,6 +351,12 @@ static const char *const optletters_optnames[] = { | |||
276 | ,"\0" "nolog" | 351 | ,"\0" "nolog" |
277 | ,"\0" "debug" | 352 | ,"\0" "debug" |
278 | #endif | 353 | #endif |
354 | #if ENABLE_PLATFORM_MINGW32 | ||
355 | ,"X" "winxp" | ||
356 | #endif | ||
357 | #if ENABLE_ASH_NOCONSOLE | ||
358 | ,"\0" "noconsole" | ||
359 | #endif | ||
279 | }; | 360 | }; |
280 | 361 | ||
281 | #define optletters(n) optletters_optnames[n][0] | 362 | #define optletters(n) optletters_optnames[n][0] |
@@ -354,6 +435,12 @@ struct globals_misc { | |||
354 | # define nolog optlist[14 + BASH_PIPEFAIL] | 435 | # define nolog optlist[14 + BASH_PIPEFAIL] |
355 | # define debug optlist[15 + BASH_PIPEFAIL] | 436 | # define debug optlist[15 + BASH_PIPEFAIL] |
356 | #endif | 437 | #endif |
438 | #if ENABLE_PLATFORM_MINGW32 | ||
439 | # define winxp optlist[14 + ENABLE_ASH_BASH_COMPAT + 2*DEBUG] | ||
440 | #endif | ||
441 | #if ENABLE_ASH_NOCONSOLE | ||
442 | # define noconsole optlist[15 + ENABLE_ASH_BASH_COMPAT + 2*DEBUG] | ||
443 | #endif | ||
357 | 444 | ||
358 | /* trap handler commands */ | 445 | /* trap handler commands */ |
359 | /* | 446 | /* |
@@ -2434,10 +2521,22 @@ path_advance(const char **path, const char *name) | |||
2434 | if (*path == NULL) | 2521 | if (*path == NULL) |
2435 | return NULL; | 2522 | return NULL; |
2436 | start = *path; | 2523 | start = *path; |
2524 | #if ENABLE_PLATFORM_MINGW32 | ||
2525 | p = next_path_sep(start); | ||
2526 | q = strchr(start, '%'); | ||
2527 | if ((p && q && q < p) || (!p && q)) | ||
2528 | p = q; | ||
2529 | if (!p) | ||
2530 | for (p = start; *p; p++) | ||
2531 | continue; | ||
2532 | #else | ||
2437 | for (p = start; *p && *p != ':' && *p != '%'; p++) | 2533 | for (p = start; *p && *p != ':' && *p != '%'; p++) |
2438 | continue; | 2534 | continue; |
2535 | #endif | ||
2439 | len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ | 2536 | len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ |
2440 | while (stackblocksize() < len) | 2537 | |
2538 | /* preserve space for .exe too */ | ||
2539 | while (stackblocksize() < (ENABLE_PLATFORM_MINGW32 ? len+4 : len)) | ||
2441 | growstackblock(); | 2540 | growstackblock(); |
2442 | q = stackblock(); | 2541 | q = stackblock(); |
2443 | if (p != start) { | 2542 | if (p != start) { |
@@ -2448,10 +2547,19 @@ path_advance(const char **path, const char *name) | |||
2448 | pathopt = NULL; | 2547 | pathopt = NULL; |
2449 | if (*p == '%') { | 2548 | if (*p == '%') { |
2450 | pathopt = ++p; | 2549 | pathopt = ++p; |
2550 | #if ENABLE_PLATFORM_MINGW32 | ||
2551 | p = next_path_sep(start); | ||
2552 | |||
2553 | /* *p != ':' and '*' would suffice */ | ||
2554 | if (!p) | ||
2555 | p = pathopt - 1; | ||
2556 | #else | ||
2451 | while (*p && *p != ':') | 2557 | while (*p && *p != ':') |
2452 | p++; | 2558 | p++; |
2559 | #endif | ||
2453 | } | 2560 | } |
2454 | if (*p == ':') | 2561 | if (*p == ':' || |
2562 | (ENABLE_PLATFORM_MINGW32 && *p == ';')) | ||
2455 | *path = p + 1; | 2563 | *path = p + 1; |
2456 | else | 2564 | else |
2457 | *path = NULL; | 2565 | *path = NULL; |
@@ -2560,6 +2668,106 @@ cdopt(void) | |||
2560 | static const char * | 2668 | static const char * |
2561 | updatepwd(const char *dir) | 2669 | updatepwd(const char *dir) |
2562 | { | 2670 | { |
2671 | #if ENABLE_PLATFORM_MINGW32 | ||
2672 | #define is_path_sep(x) ((x) == '/' || (x) == '\\') | ||
2673 | #define is_unc_path(x) (is_path_sep(x[0]) && is_path_sep(x[1])) | ||
2674 | /* | ||
2675 | * Due to Windows drive notion, getting pwd is a completely | ||
2676 | * different thing. Handle it in a separate routine | ||
2677 | */ | ||
2678 | |||
2679 | char *new; | ||
2680 | char *p; | ||
2681 | char *cdcomppath; | ||
2682 | const char *lim; | ||
2683 | /* | ||
2684 | * There are five cases that make some kind of sense | ||
2685 | * absdrive + abspath: c:/path | ||
2686 | * absdrive + !abspath: c:path | ||
2687 | * !absdrive + abspath: /path | ||
2688 | * !absdrive + uncpath: //host/share | ||
2689 | * !absdrive + !abspath: path | ||
2690 | * | ||
2691 | * Damn DOS! | ||
2692 | * c:path behaviour is "undefined" | ||
2693 | * To properly handle this case, I have to keep track of cwd | ||
2694 | * of every drive, which is too painful to do. | ||
2695 | * So when c:path is given, I assume it's c:${curdir}path | ||
2696 | * with ${curdir} comes from the current drive | ||
2697 | */ | ||
2698 | int absdrive = *dir && dir[1] == ':'; | ||
2699 | int abspath = absdrive ? is_path_sep(dir[2]) : is_path_sep(*dir); | ||
2700 | |||
2701 | cdcomppath = sstrdup(dir); | ||
2702 | STARTSTACKSTR(new); | ||
2703 | if (!absdrive && curdir == nullstr) | ||
2704 | return 0; | ||
2705 | if (!abspath) { | ||
2706 | if (curdir == nullstr) | ||
2707 | return 0; | ||
2708 | new = stack_putstr(curdir, new); | ||
2709 | } | ||
2710 | new = makestrspace(strlen(dir) + 2, new); | ||
2711 | |||
2712 | if ( is_unc_path(dir) || (!absdrive && !abspath && is_unc_path(curdir)) ) { | ||
2713 | lim = (char *)stackblock() + 1; | ||
2714 | } | ||
2715 | else { | ||
2716 | char *drive = stackblock(); | ||
2717 | if (absdrive) { | ||
2718 | *drive = *dir; | ||
2719 | cdcomppath += 2; | ||
2720 | dir += 2; | ||
2721 | } else { | ||
2722 | *drive = *curdir; | ||
2723 | } | ||
2724 | drive[1] = ':'; /* in case of absolute drive+path */ | ||
2725 | |||
2726 | if (abspath) | ||
2727 | new = drive + 2; | ||
2728 | lim = drive + 3; | ||
2729 | } | ||
2730 | |||
2731 | if (!abspath) { | ||
2732 | if (!is_path_sep(new[-1])) | ||
2733 | USTPUTC('/', new); | ||
2734 | if (new > lim && is_path_sep(*lim)) | ||
2735 | lim++; | ||
2736 | } else { | ||
2737 | USTPUTC('/', new); | ||
2738 | cdcomppath ++; | ||
2739 | if (is_path_sep(dir[1]) && !is_path_sep(dir[2])) { | ||
2740 | USTPUTC('/', new); | ||
2741 | cdcomppath++; | ||
2742 | lim++; | ||
2743 | } | ||
2744 | } | ||
2745 | p = strtok(cdcomppath, "/\\"); | ||
2746 | while (p) { | ||
2747 | switch (*p) { | ||
2748 | case '.': | ||
2749 | if (p[1] == '.' && p[2] == '\0') { | ||
2750 | while (new > lim) { | ||
2751 | STUNPUTC(new); | ||
2752 | if (is_path_sep(new[-1])) | ||
2753 | break; | ||
2754 | } | ||
2755 | break; | ||
2756 | } | ||
2757 | if (p[1] == '\0') | ||
2758 | break; | ||
2759 | /* fall through */ | ||
2760 | default: | ||
2761 | new = stack_putstr(p, new); | ||
2762 | USTPUTC('/', new); | ||
2763 | } | ||
2764 | p = strtok(0, "/\\"); | ||
2765 | } | ||
2766 | if (new > lim) | ||
2767 | STUNPUTC(new); | ||
2768 | *new = 0; | ||
2769 | return stackblock(); | ||
2770 | #else | ||
2563 | char *new; | 2771 | char *new; |
2564 | char *p; | 2772 | char *p; |
2565 | char *cdcomppath; | 2773 | char *cdcomppath; |
@@ -2613,6 +2821,7 @@ updatepwd(const char *dir) | |||
2613 | STUNPUTC(new); | 2821 | STUNPUTC(new); |
2614 | *new = 0; | 2822 | *new = 0; |
2615 | return stackblock(); | 2823 | return stackblock(); |
2824 | #endif | ||
2616 | } | 2825 | } |
2617 | 2826 | ||
2618 | /* | 2827 | /* |
@@ -2707,7 +2916,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
2707 | } | 2916 | } |
2708 | if (!dest) | 2917 | if (!dest) |
2709 | dest = nullstr; | 2918 | dest = nullstr; |
2710 | if (*dest == '/') | 2919 | if (is_absolute_path(dest)) |
2711 | goto step6; | 2920 | goto step6; |
2712 | if (*dest == '.') { | 2921 | if (*dest == '.') { |
2713 | c = dest[1]; | 2922 | c = dest[1]; |
@@ -3409,6 +3618,9 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
3409 | */ | 3618 | */ |
3410 | struct procstat { | 3619 | struct procstat { |
3411 | pid_t ps_pid; /* process id */ | 3620 | pid_t ps_pid; /* process id */ |
3621 | #if ENABLE_PLATFORM_MINGW32 | ||
3622 | HANDLE ps_proc; | ||
3623 | #endif | ||
3412 | int ps_status; /* last process status from wait() */ | 3624 | int ps_status; /* last process status from wait() */ |
3413 | char *ps_cmd; /* text of command being run */ | 3625 | char *ps_cmd; /* text of command being run */ |
3414 | }; | 3626 | }; |
@@ -3437,7 +3649,9 @@ struct job { | |||
3437 | }; | 3649 | }; |
3438 | 3650 | ||
3439 | static struct job *makejob(/*union node *,*/ int); | 3651 | static struct job *makejob(/*union node *,*/ int); |
3652 | #if !ENABLE_PLATFORM_MINGW32 | ||
3440 | static int forkshell(struct job *, union node *, int); | 3653 | static int forkshell(struct job *, union node *, int); |
3654 | #endif | ||
3441 | static int waitforjob(struct job *); | 3655 | static int waitforjob(struct job *); |
3442 | 3656 | ||
3443 | #if !JOBS | 3657 | #if !JOBS |
@@ -3462,6 +3676,7 @@ ignoresig(int signo) | |||
3462 | sigmode[signo - 1] = S_HARD_IGN; | 3676 | sigmode[signo - 1] = S_HARD_IGN; |
3463 | } | 3677 | } |
3464 | 3678 | ||
3679 | #if !ENABLE_PLATFORM_MINGW32 | ||
3465 | /* | 3680 | /* |
3466 | * Only one usage site - in setsignal() | 3681 | * Only one usage site - in setsignal() |
3467 | */ | 3682 | */ |
@@ -3586,6 +3801,9 @@ setsignal(int signo) | |||
3586 | 3801 | ||
3587 | *t = new_act; | 3802 | *t = new_act; |
3588 | } | 3803 | } |
3804 | #else | ||
3805 | #define setsignal(s) | ||
3806 | #endif | ||
3589 | 3807 | ||
3590 | /* mode flags for set_curjob */ | 3808 | /* mode flags for set_curjob */ |
3591 | #define CUR_DELETE 2 | 3809 | #define CUR_DELETE 2 |
@@ -4083,6 +4301,98 @@ sprint_status48(char *s, int status, int sigonly) | |||
4083 | return col; | 4301 | return col; |
4084 | } | 4302 | } |
4085 | 4303 | ||
4304 | #if ENABLE_PLATFORM_MINGW32 | ||
4305 | |||
4306 | HANDLE hSIGINT; /* Ctrl-C is pressed */ | ||
4307 | |||
4308 | static BOOL WINAPI ctrl_handler(DWORD dwCtrlType) | ||
4309 | { | ||
4310 | if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) { | ||
4311 | SetEvent(hSIGINT); | ||
4312 | pending_int = 1; | ||
4313 | return TRUE; | ||
4314 | } | ||
4315 | return FALSE; | ||
4316 | } | ||
4317 | |||
4318 | /* | ||
4319 | * Windows does not know about parent-child relationship | ||
4320 | * They don't support waitpid(-1) | ||
4321 | */ | ||
4322 | static pid_t | ||
4323 | waitpid_child(int *status, int wait_flags) | ||
4324 | { | ||
4325 | pid_t *pidlist; | ||
4326 | HANDLE *proclist; | ||
4327 | int pid_nr = 0; | ||
4328 | pid_t pid; | ||
4329 | DWORD win_status, idx; | ||
4330 | struct job *jb; | ||
4331 | |||
4332 | for (jb = curjob; jb; jb = jb->prev_job) { | ||
4333 | if (jb->state != JOBDONE) | ||
4334 | pid_nr += jb->nprocs; | ||
4335 | } | ||
4336 | if ( pid_nr++ == 0 ) | ||
4337 | return -1; | ||
4338 | |||
4339 | pidlist = ckmalloc(sizeof(*pidlist)*pid_nr); | ||
4340 | proclist = ckmalloc(sizeof(*proclist)*pid_nr); | ||
4341 | |||
4342 | pidlist[0] = -1; | ||
4343 | proclist[0] = hSIGINT; | ||
4344 | pid_nr = 1; | ||
4345 | for (jb = curjob; jb; jb = jb->prev_job) { | ||
4346 | struct procstat *ps, *psend; | ||
4347 | if (jb->state == JOBDONE) | ||
4348 | continue; | ||
4349 | ps = jb->ps; | ||
4350 | psend = ps + jb->nprocs; | ||
4351 | while (ps < psend) { | ||
4352 | if (ps->ps_pid != -1 && ps->ps_proc != NULL) { | ||
4353 | pidlist[pid_nr] = ps->ps_pid; | ||
4354 | proclist[pid_nr++] = ps->ps_proc; | ||
4355 | } | ||
4356 | ps++; | ||
4357 | } | ||
4358 | } | ||
4359 | |||
4360 | if (pid_nr == 1) { | ||
4361 | free(pidlist); | ||
4362 | free(proclist); | ||
4363 | return -1; | ||
4364 | } | ||
4365 | |||
4366 | idx = WaitForMultipleObjects(pid_nr, proclist, FALSE, | ||
4367 | wait_flags|WNOHANG ? 1 : INFINITE); | ||
4368 | if (idx >= pid_nr) { | ||
4369 | free(pidlist); | ||
4370 | free(proclist); | ||
4371 | return -1; | ||
4372 | } | ||
4373 | if (!idx) { /* hSIGINT */ | ||
4374 | int i; | ||
4375 | ResetEvent(hSIGINT); | ||
4376 | for (i = 1; i < pid_nr; i++) | ||
4377 | TerminateProcess(proclist[i], 1); | ||
4378 | pid = pidlist[1]; | ||
4379 | free(pidlist); | ||
4380 | free(proclist); | ||
4381 | *status = 260; /* terminated by a signal */ | ||
4382 | return pid; | ||
4383 | } | ||
4384 | GetExitCodeProcess(proclist[idx], &win_status); | ||
4385 | pid = pidlist[idx]; | ||
4386 | free(pidlist); | ||
4387 | free(proclist); | ||
4388 | *status = (int)win_status; | ||
4389 | return pid; | ||
4390 | } | ||
4391 | #define waitpid(p, s, f) waitpid_child(s, f) | ||
4392 | #define wait_block_or_sig(s) waitpid_child(s, 0) | ||
4393 | |||
4394 | #else | ||
4395 | |||
4086 | static int | 4396 | static int |
4087 | wait_block_or_sig(int *status) | 4397 | wait_block_or_sig(int *status) |
4088 | { | 4398 | { |
@@ -4115,6 +4425,7 @@ wait_block_or_sig(int *status) | |||
4115 | 4425 | ||
4116 | return pid; | 4426 | return pid; |
4117 | } | 4427 | } |
4428 | #endif | ||
4118 | 4429 | ||
4119 | #define DOWAIT_NONBLOCK 0 | 4430 | #define DOWAIT_NONBLOCK 0 |
4120 | #define DOWAIT_BLOCK 1 | 4431 | #define DOWAIT_BLOCK 1 |
@@ -4183,6 +4494,11 @@ dowait(int block, struct job *job) | |||
4183 | jobno(jp), pid, ps->ps_status, status)); | 4494 | jobno(jp), pid, ps->ps_status, status)); |
4184 | ps->ps_status = status; | 4495 | ps->ps_status = status; |
4185 | thisjob = jp; | 4496 | thisjob = jp; |
4497 | #if ENABLE_PLATFORM_MINGW32 | ||
4498 | ps->ps_pid = -1; | ||
4499 | CloseHandle(ps->ps_proc); | ||
4500 | ps->ps_proc = NULL; | ||
4501 | #endif | ||
4186 | } | 4502 | } |
4187 | if (ps->ps_status == -1) | 4503 | if (ps->ps_status == -1) |
4188 | jobstate = JOBRUNNING; | 4504 | jobstate = JOBRUNNING; |
@@ -4865,6 +5181,7 @@ commandtext(union node *n) | |||
4865 | * | 5181 | * |
4866 | * Called with interrupts off. | 5182 | * Called with interrupts off. |
4867 | */ | 5183 | */ |
5184 | #if !ENABLE_PLATFORM_MINGW32 | ||
4868 | /* | 5185 | /* |
4869 | * Clear traps on a fork. | 5186 | * Clear traps on a fork. |
4870 | */ | 5187 | */ |
@@ -5014,16 +5331,24 @@ forkchild(struct job *jp, union node *n, int mode) | |||
5014 | freejob(jp); | 5331 | freejob(jp); |
5015 | jobless = 0; | 5332 | jobless = 0; |
5016 | } | 5333 | } |
5334 | #endif | ||
5017 | 5335 | ||
5018 | /* Called after fork(), in parent */ | 5336 | /* Called after fork(), in parent */ |
5019 | #if !JOBS | 5337 | #if !JOBS |
5020 | #define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) | 5338 | #define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) |
5021 | #endif | 5339 | #endif |
5022 | static void | 5340 | static void |
5341 | #if !ENABLE_PLATFORM_MINGW32 | ||
5023 | forkparent(struct job *jp, union node *n, int mode, pid_t pid) | 5342 | forkparent(struct job *jp, union node *n, int mode, pid_t pid) |
5343 | #else | ||
5344 | forkparent(struct job *jp, union node *n, int mode, HANDLE proc) | ||
5345 | #endif | ||
5024 | { | 5346 | { |
5347 | #if ENABLE_PLATFORM_MINGW32 | ||
5348 | pid_t pid = GetProcessId(proc); | ||
5349 | #endif | ||
5025 | TRACE(("In parent shell: child = %d\n", pid)); | 5350 | TRACE(("In parent shell: child = %d\n", pid)); |
5026 | if (!jp) { | 5351 | if (!jp && !ENABLE_PLATFORM_MINGW32) { /* FIXME not quite understand this */ |
5027 | /* jp is NULL when called by openhere() for heredoc support */ | 5352 | /* jp is NULL when called by openhere() for heredoc support */ |
5028 | while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) | 5353 | while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) |
5029 | continue; | 5354 | continue; |
@@ -5049,6 +5374,9 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid) | |||
5049 | if (jp) { | 5374 | if (jp) { |
5050 | struct procstat *ps = &jp->ps[jp->nprocs++]; | 5375 | struct procstat *ps = &jp->ps[jp->nprocs++]; |
5051 | ps->ps_pid = pid; | 5376 | ps->ps_pid = pid; |
5377 | #if ENABLE_PLATFORM_MINGW32 | ||
5378 | ps->ps_proc = proc; | ||
5379 | #endif | ||
5052 | ps->ps_status = -1; | 5380 | ps->ps_status = -1; |
5053 | ps->ps_cmd = nullstr; | 5381 | ps->ps_cmd = nullstr; |
5054 | #if JOBS | 5382 | #if JOBS |
@@ -5058,6 +5386,7 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid) | |||
5058 | } | 5386 | } |
5059 | } | 5387 | } |
5060 | 5388 | ||
5389 | #if !ENABLE_PLATFORM_MINGW32 | ||
5061 | /* jp and n are NULL when called by openhere() for heredoc support */ | 5390 | /* jp and n are NULL when called by openhere() for heredoc support */ |
5062 | static int | 5391 | static int |
5063 | forkshell(struct job *jp, union node *n, int mode) | 5392 | forkshell(struct job *jp, union node *n, int mode) |
@@ -5080,6 +5409,7 @@ forkshell(struct job *jp, union node *n, int mode) | |||
5080 | } | 5409 | } |
5081 | return pid; | 5410 | return pid; |
5082 | } | 5411 | } |
5412 | #endif | ||
5083 | 5413 | ||
5084 | /* | 5414 | /* |
5085 | * Wait for job to finish. | 5415 | * Wait for job to finish. |
@@ -5211,6 +5541,7 @@ openhere(union node *redir) | |||
5211 | { | 5541 | { |
5212 | int pip[2]; | 5542 | int pip[2]; |
5213 | size_t len = 0; | 5543 | size_t len = 0; |
5544 | IF_PLATFORM_MINGW32(struct forkshell fs); | ||
5214 | 5545 | ||
5215 | if (pipe(pip) < 0) | 5546 | if (pipe(pip) < 0) |
5216 | ash_msg_and_raise_error("pipe call failed"); | 5547 | ash_msg_and_raise_error("pipe call failed"); |
@@ -5221,6 +5552,15 @@ openhere(union node *redir) | |||
5221 | goto out; | 5552 | goto out; |
5222 | } | 5553 | } |
5223 | } | 5554 | } |
5555 | #if ENABLE_PLATFORM_MINGW32 | ||
5556 | memset(&fs, 0, sizeof(fs)); | ||
5557 | fs.fpid = FS_OPENHERE; | ||
5558 | fs.n = redir; | ||
5559 | fs.fd[0] = pip[0]; | ||
5560 | fs.fd[1] = pip[1]; | ||
5561 | if (spawn_forkshell(NULL, &fs, FORK_NOJOB) < 0) | ||
5562 | ash_msg_and_raise_error("unable to spawn shell"); | ||
5563 | #else | ||
5224 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { | 5564 | if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { |
5225 | /* child */ | 5565 | /* child */ |
5226 | close(pip[0]); | 5566 | close(pip[0]); |
@@ -5235,6 +5575,7 @@ openhere(union node *redir) | |||
5235 | expandhere(redir->nhere.doc, pip[1]); | 5575 | expandhere(redir->nhere.doc, pip[1]); |
5236 | _exit(EXIT_SUCCESS); | 5576 | _exit(EXIT_SUCCESS); |
5237 | } | 5577 | } |
5578 | #endif | ||
5238 | out: | 5579 | out: |
5239 | close(pip[1]); | 5580 | close(pip[1]); |
5240 | return pip[0]; | 5581 | return pip[0]; |
@@ -5261,6 +5602,31 @@ openredirect(union node *redir) | |||
5261 | * allocated space. Do it only when we know it is safe. | 5602 | * allocated space. Do it only when we know it is safe. |
5262 | */ | 5603 | */ |
5263 | fname = redir->nfile.expfname; | 5604 | fname = redir->nfile.expfname; |
5605 | #if ENABLE_PLATFORM_MINGW32 | ||
5606 | /* Support for /dev/null */ | ||
5607 | switch (redir->nfile.type) { | ||
5608 | case NFROM: | ||
5609 | if (!strcmp(fname, "/dev/null")) | ||
5610 | return open("nul",O_RDWR); | ||
5611 | if (!strncmp(fname, "/dev/", 5)) { | ||
5612 | ash_msg("Unhandled device %s\n", fname); | ||
5613 | return -1; | ||
5614 | } | ||
5615 | break; | ||
5616 | |||
5617 | case NFROMTO: | ||
5618 | case NTO: | ||
5619 | case NCLOBBER: | ||
5620 | case NAPPEND: | ||
5621 | if (!strcmp(fname, "/dev/null")) | ||
5622 | return open("nul",O_RDWR); | ||
5623 | if (!strncmp(fname, "/dev/", 5)) { | ||
5624 | ash_msg("Unhandled device %s\n", fname); | ||
5625 | return -1; | ||
5626 | } | ||
5627 | break; | ||
5628 | } | ||
5629 | #endif | ||
5264 | 5630 | ||
5265 | switch (redir->nfile.type) { | 5631 | switch (redir->nfile.type) { |
5266 | default: | 5632 | default: |
@@ -5312,6 +5678,9 @@ openredirect(union node *redir) | |||
5312 | f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); | 5678 | f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); |
5313 | if (f < 0) | 5679 | if (f < 0) |
5314 | goto ecreate; | 5680 | goto ecreate; |
5681 | #if ENABLE_PLATFORM_MINGW32 | ||
5682 | lseek(f, 0, SEEK_END); | ||
5683 | #endif | ||
5315 | break; | 5684 | break; |
5316 | } | 5685 | } |
5317 | 5686 | ||
@@ -6240,6 +6609,7 @@ struct backcmd { /* result of evalbackcmd */ | |||
6240 | int fd; /* file descriptor to read from */ | 6609 | int fd; /* file descriptor to read from */ |
6241 | int nleft; /* number of chars in buffer */ | 6610 | int nleft; /* number of chars in buffer */ |
6242 | char *buf; /* buffer */ | 6611 | char *buf; /* buffer */ |
6612 | IF_PLATFORM_MINGW32(struct forkshell fs); | ||
6243 | struct job *jp; /* job structure for command */ | 6613 | struct job *jp; /* job structure for command */ |
6244 | }; | 6614 | }; |
6245 | 6615 | ||
@@ -6271,6 +6641,7 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6271 | result->fd = -1; | 6641 | result->fd = -1; |
6272 | result->buf = NULL; | 6642 | result->buf = NULL; |
6273 | result->nleft = 0; | 6643 | result->nleft = 0; |
6644 | IF_PLATFORM_MINGW32(memset(&result->fs, 0, sizeof(result->fs))); | ||
6274 | result->jp = NULL; | 6645 | result->jp = NULL; |
6275 | if (n == NULL) { | 6646 | if (n == NULL) { |
6276 | goto out; | 6647 | goto out; |
@@ -6279,6 +6650,14 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6279 | if (pipe(pip) < 0) | 6650 | if (pipe(pip) < 0) |
6280 | ash_msg_and_raise_error("pipe call failed"); | 6651 | ash_msg_and_raise_error("pipe call failed"); |
6281 | jp = makejob(/*n,*/ 1); | 6652 | jp = makejob(/*n,*/ 1); |
6653 | #if ENABLE_PLATFORM_MINGW32 | ||
6654 | result->fs.fpid = FS_EVALBACKCMD; | ||
6655 | result->fs.n = n; | ||
6656 | result->fs.fd[0] = pip[0]; | ||
6657 | result->fs.fd[1] = pip[1]; | ||
6658 | if (spawn_forkshell(jp, &result->fs, FORK_NOJOB) < 0) | ||
6659 | ash_msg_and_raise_error("unable to spawn shell"); | ||
6660 | #else | ||
6282 | if (forkshell(jp, n, FORK_NOJOB) == 0) { | 6661 | if (forkshell(jp, n, FORK_NOJOB) == 0) { |
6283 | /* child */ | 6662 | /* child */ |
6284 | FORCE_INT_ON; | 6663 | FORCE_INT_ON; |
@@ -6301,6 +6680,7 @@ evalbackcmd(union node *n, struct backcmd *result) | |||
6301 | evaltreenr(n, EV_EXIT); | 6680 | evaltreenr(n, EV_EXIT); |
6302 | /* NOTREACHED */ | 6681 | /* NOTREACHED */ |
6303 | } | 6682 | } |
6683 | #endif | ||
6304 | /* parent */ | 6684 | /* parent */ |
6305 | close(pip[1]); | 6685 | close(pip[1]); |
6306 | result->fd = pip[0]; | 6686 | result->fd = pip[0]; |
@@ -6357,7 +6737,8 @@ expbackq(union node *cmd, int flag) | |||
6357 | 6737 | ||
6358 | /* Eat all trailing newlines */ | 6738 | /* Eat all trailing newlines */ |
6359 | dest = expdest; | 6739 | dest = expdest; |
6360 | for (; dest > (char *)stackblock() && dest[-1] == '\n';) | 6740 | for (; dest > (char *)stackblock() && (dest[-1] == '\n' || |
6741 | (ENABLE_PLATFORM_MINGW32 && dest[-1] == '\r'));) | ||
6361 | STUNPUTC(dest); | 6742 | STUNPUTC(dest); |
6362 | expdest = dest; | 6743 | expdest = dest; |
6363 | 6744 | ||
@@ -7856,7 +8237,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx) | |||
7856 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ | 8237 | int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ |
7857 | 8238 | ||
7858 | envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL); | 8239 | envp = listvars(VEXPORT, VUNSET, /*end:*/ NULL); |
7859 | if (strchr(prog, '/') != NULL | 8240 | if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\'))) |
7860 | #if ENABLE_FEATURE_SH_STANDALONE | 8241 | #if ENABLE_FEATURE_SH_STANDALONE |
7861 | || (applet_no = find_applet_by_name(prog)) >= 0 | 8242 | || (applet_no = find_applet_by_name(prog)) >= 0 |
7862 | #endif | 8243 | #endif |
@@ -8440,10 +8821,14 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
8440 | #endif | 8821 | #endif |
8441 | 8822 | ||
8442 | 8823 | ||
8443 | /*static int funcblocksize; // size of structures in function */ | 8824 | static int funcblocksize; /* size of structures in function */ |
8444 | /*static int funcstringsize; // size of strings in node */ | 8825 | static int funcstringsize; /* size of strings in node */ |
8445 | static void *funcblock; /* block to allocate function from */ | 8826 | static void *funcblock; /* block to allocate function from */ |
8446 | static char *funcstring_end; /* end of block to allocate strings from */ | 8827 | static char *funcstring; /* block to allocate strings from */ |
8828 | #if ENABLE_PLATFORM_MINGW32 | ||
8829 | static int nodeptrsize; | ||
8830 | static char **nodeptr; | ||
8831 | #endif | ||
8447 | 8832 | ||
8448 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { | 8833 | static const uint8_t nodesize[N_NUMBER] ALIGN1 = { |
8449 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), | 8834 | [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), |
@@ -8477,72 +8862,81 @@ static const uint8_t nodesize[N_NUMBER] ALIGN1 = { | |||
8477 | [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)), | 8862 | [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)), |
8478 | }; | 8863 | }; |
8479 | 8864 | ||
8480 | static int calcsize(int funcblocksize, union node *n); | 8865 | static void calcsize(union node *n); |
8481 | 8866 | ||
8482 | static int | 8867 | static void |
8483 | sizenodelist(int funcblocksize, struct nodelist *lp) | 8868 | sizenodelist(struct nodelist *lp) |
8484 | { | 8869 | { |
8485 | while (lp) { | 8870 | while (lp) { |
8486 | funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); | 8871 | funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); |
8487 | funcblocksize = calcsize(funcblocksize, lp->n); | 8872 | IF_PLATFORM_MINGW32(nodeptrsize += 2); |
8873 | calcsize(lp->n); | ||
8488 | lp = lp->next; | 8874 | lp = lp->next; |
8489 | } | 8875 | } |
8490 | return funcblocksize; | ||
8491 | } | 8876 | } |
8492 | 8877 | ||
8493 | static int | 8878 | static void |
8494 | calcsize(int funcblocksize, union node *n) | 8879 | calcsize(union node *n) |
8495 | { | 8880 | { |
8496 | if (n == NULL) | 8881 | if (n == NULL) |
8497 | return funcblocksize; | 8882 | return; |
8498 | funcblocksize += nodesize[n->type]; | 8883 | funcblocksize += nodesize[n->type]; |
8499 | switch (n->type) { | 8884 | switch (n->type) { |
8500 | case NCMD: | 8885 | case NCMD: |
8501 | funcblocksize = calcsize(funcblocksize, n->ncmd.redirect); | 8886 | calcsize(n->ncmd.redirect); |
8502 | funcblocksize = calcsize(funcblocksize, n->ncmd.args); | 8887 | calcsize(n->ncmd.args); |
8503 | funcblocksize = calcsize(funcblocksize, n->ncmd.assign); | 8888 | calcsize(n->ncmd.assign); |
8889 | IF_PLATFORM_MINGW32(nodeptrsize += 3); | ||
8504 | break; | 8890 | break; |
8505 | case NPIPE: | 8891 | case NPIPE: |
8506 | funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist); | 8892 | sizenodelist(n->npipe.cmdlist); |
8893 | IF_PLATFORM_MINGW32(nodeptrsize++); | ||
8507 | break; | 8894 | break; |
8508 | case NREDIR: | 8895 | case NREDIR: |
8509 | case NBACKGND: | 8896 | case NBACKGND: |
8510 | case NSUBSHELL: | 8897 | case NSUBSHELL: |
8511 | funcblocksize = calcsize(funcblocksize, n->nredir.redirect); | 8898 | calcsize(n->nredir.redirect); |
8512 | funcblocksize = calcsize(funcblocksize, n->nredir.n); | 8899 | calcsize(n->nredir.n); |
8900 | IF_PLATFORM_MINGW32(nodeptrsize += 2); | ||
8513 | break; | 8901 | break; |
8514 | case NAND: | 8902 | case NAND: |
8515 | case NOR: | 8903 | case NOR: |
8516 | case NSEMI: | 8904 | case NSEMI: |
8517 | case NWHILE: | 8905 | case NWHILE: |
8518 | case NUNTIL: | 8906 | case NUNTIL: |
8519 | funcblocksize = calcsize(funcblocksize, n->nbinary.ch2); | 8907 | calcsize(n->nbinary.ch2); |
8520 | funcblocksize = calcsize(funcblocksize, n->nbinary.ch1); | 8908 | calcsize(n->nbinary.ch1); |
8909 | IF_PLATFORM_MINGW32(nodeptrsize += 2); | ||
8521 | break; | 8910 | break; |
8522 | case NIF: | 8911 | case NIF: |
8523 | funcblocksize = calcsize(funcblocksize, n->nif.elsepart); | 8912 | calcsize(n->nif.elsepart); |
8524 | funcblocksize = calcsize(funcblocksize, n->nif.ifpart); | 8913 | calcsize(n->nif.ifpart); |
8525 | funcblocksize = calcsize(funcblocksize, n->nif.test); | 8914 | calcsize(n->nif.test); |
8915 | IF_PLATFORM_MINGW32(nodeptrsize += 3); | ||
8526 | break; | 8916 | break; |
8527 | case NFOR: | 8917 | case NFOR: |
8528 | funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */ | 8918 | funcstringsize += strlen(n->nfor.var) + 1; |
8529 | funcblocksize = calcsize(funcblocksize, n->nfor.body); | 8919 | calcsize(n->nfor.body); |
8530 | funcblocksize = calcsize(funcblocksize, n->nfor.args); | 8920 | calcsize(n->nfor.args); |
8921 | IF_PLATFORM_MINGW32(nodeptrsize += 3); | ||
8531 | break; | 8922 | break; |
8532 | case NCASE: | 8923 | case NCASE: |
8533 | funcblocksize = calcsize(funcblocksize, n->ncase.cases); | 8924 | calcsize(n->ncase.cases); |
8534 | funcblocksize = calcsize(funcblocksize, n->ncase.expr); | 8925 | calcsize(n->ncase.expr); |
8926 | IF_PLATFORM_MINGW32(nodeptrsize += 2); | ||
8535 | break; | 8927 | break; |
8536 | case NCLIST: | 8928 | case NCLIST: |
8537 | funcblocksize = calcsize(funcblocksize, n->nclist.body); | 8929 | calcsize(n->nclist.body); |
8538 | funcblocksize = calcsize(funcblocksize, n->nclist.pattern); | 8930 | calcsize(n->nclist.pattern); |
8539 | funcblocksize = calcsize(funcblocksize, n->nclist.next); | 8931 | calcsize(n->nclist.next); |
8932 | IF_PLATFORM_MINGW32(nodeptrsize += 3); | ||
8540 | break; | 8933 | break; |
8541 | case NDEFUN: | 8934 | case NDEFUN: |
8542 | case NARG: | 8935 | case NARG: |
8543 | funcblocksize = sizenodelist(funcblocksize, n->narg.backquote); | 8936 | sizenodelist(n->narg.backquote); |
8544 | funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */ | 8937 | funcstringsize += strlen(n->narg.text) + 1; |
8545 | funcblocksize = calcsize(funcblocksize, n->narg.next); | 8938 | calcsize(n->narg.next); |
8939 | IF_PLATFORM_MINGW32(nodeptrsize += 3); | ||
8546 | break; | 8940 | break; |
8547 | case NTO: | 8941 | case NTO: |
8548 | #if BASH_REDIR_OUTPUT | 8942 | #if BASH_REDIR_OUTPUT |
@@ -8552,35 +8946,55 @@ calcsize(int funcblocksize, union node *n) | |||
8552 | case NFROM: | 8946 | case NFROM: |
8553 | case NFROMTO: | 8947 | case NFROMTO: |
8554 | case NAPPEND: | 8948 | case NAPPEND: |
8555 | funcblocksize = calcsize(funcblocksize, n->nfile.fname); | 8949 | calcsize(n->nfile.fname); |
8556 | funcblocksize = calcsize(funcblocksize, n->nfile.next); | 8950 | calcsize(n->nfile.next); |
8951 | IF_PLATFORM_MINGW32(nodeptrsize += 2); | ||
8557 | break; | 8952 | break; |
8558 | case NTOFD: | 8953 | case NTOFD: |
8559 | case NFROMFD: | 8954 | case NFROMFD: |
8560 | funcblocksize = calcsize(funcblocksize, n->ndup.vname); | 8955 | calcsize(n->ndup.vname); |
8561 | funcblocksize = calcsize(funcblocksize, n->ndup.next); | 8956 | calcsize(n->ndup.next); |
8957 | IF_PLATFORM_MINGW32(nodeptrsize += 2); | ||
8562 | break; | 8958 | break; |
8563 | case NHERE: | 8959 | case NHERE: |
8564 | case NXHERE: | 8960 | case NXHERE: |
8565 | funcblocksize = calcsize(funcblocksize, n->nhere.doc); | 8961 | calcsize(n->nhere.doc); |
8566 | funcblocksize = calcsize(funcblocksize, n->nhere.next); | 8962 | calcsize(n->nhere.next); |
8963 | IF_PLATFORM_MINGW32(nodeptrsize += 2); | ||
8567 | break; | 8964 | break; |
8568 | case NNOT: | 8965 | case NNOT: |
8569 | funcblocksize = calcsize(funcblocksize, n->nnot.com); | 8966 | calcsize(n->nnot.com); |
8967 | IF_PLATFORM_MINGW32(nodeptrsize++); | ||
8570 | break; | 8968 | break; |
8571 | }; | 8969 | }; |
8572 | return funcblocksize; | ||
8573 | } | 8970 | } |
8574 | 8971 | ||
8575 | static char * | 8972 | static char * |
8576 | nodeckstrdup(char *s) | 8973 | nodeckstrdup(const char *s) |
8577 | { | 8974 | { |
8578 | funcstring_end -= SHELL_ALIGN(strlen(s) + 1); | 8975 | char *rtn = funcstring; |
8579 | return strcpy(funcstring_end, s); | 8976 | |
8977 | if (!s) | ||
8978 | return NULL; | ||
8979 | strcpy(funcstring, s); | ||
8980 | funcstring += strlen(s) + 1; | ||
8981 | return rtn; | ||
8580 | } | 8982 | } |
8581 | 8983 | ||
8582 | static union node *copynode(union node *); | 8984 | static union node *copynode(union node *); |
8583 | 8985 | ||
8986 | #if ENABLE_PLATFORM_MINGW32 | ||
8987 | # define SAVE_PTR(dst) {if (nodeptr) *nodeptr++ = (char *)&(dst);} | ||
8988 | # define SAVE_PTR2(dst1,dst2) {if (nodeptr) { *nodeptr++ = (char *)&(dst1);*nodeptr++ = (char *)&(dst2);}} | ||
8989 | # define SAVE_PTR3(dst1,dst2,dst3) {if (nodeptr) { *nodeptr++ = (char *)&(dst1);*nodeptr++ = (char *)&(dst2);*nodeptr++ = (char *)&(dst3);}} | ||
8990 | # define SAVE_PTR4(dst1,dst2,dst3,dst4) {if (nodeptr) { *nodeptr++ = (char *)&(dst1);*nodeptr++ = (char *)&(dst2);*nodeptr++ = (char *)&(dst3);*nodeptr++ = (char *)&(dst4);}} | ||
8991 | #else | ||
8992 | # define SAVE_PTR(dst) | ||
8993 | # define SAVE_PTR2(dst,dst2) | ||
8994 | # define SAVE_PTR3(dst,dst2,dst3) | ||
8995 | # define SAVE_PTR4(dst,dst2,dst3,dst4) | ||
8996 | #endif | ||
8997 | |||
8584 | static struct nodelist * | 8998 | static struct nodelist * |
8585 | copynodelist(struct nodelist *lp) | 8999 | copynodelist(struct nodelist *lp) |
8586 | { | 9000 | { |
@@ -8592,6 +9006,7 @@ copynodelist(struct nodelist *lp) | |||
8592 | *lpp = funcblock; | 9006 | *lpp = funcblock; |
8593 | funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); | 9007 | funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); |
8594 | (*lpp)->n = copynode(lp->n); | 9008 | (*lpp)->n = copynode(lp->n); |
9009 | SAVE_PTR2((*lpp)->n, (*lpp)->next); | ||
8595 | lp = lp->next; | 9010 | lp = lp->next; |
8596 | lpp = &(*lpp)->next; | 9011 | lpp = &(*lpp)->next; |
8597 | } | 9012 | } |
@@ -8614,16 +9029,19 @@ copynode(union node *n) | |||
8614 | new->ncmd.redirect = copynode(n->ncmd.redirect); | 9029 | new->ncmd.redirect = copynode(n->ncmd.redirect); |
8615 | new->ncmd.args = copynode(n->ncmd.args); | 9030 | new->ncmd.args = copynode(n->ncmd.args); |
8616 | new->ncmd.assign = copynode(n->ncmd.assign); | 9031 | new->ncmd.assign = copynode(n->ncmd.assign); |
9032 | SAVE_PTR3(new->ncmd.redirect,new->ncmd.args, new->ncmd.assign); | ||
8617 | break; | 9033 | break; |
8618 | case NPIPE: | 9034 | case NPIPE: |
8619 | new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); | 9035 | new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); |
8620 | new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; | 9036 | new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; |
9037 | SAVE_PTR(new->npipe.cmdlist); | ||
8621 | break; | 9038 | break; |
8622 | case NREDIR: | 9039 | case NREDIR: |
8623 | case NBACKGND: | 9040 | case NBACKGND: |
8624 | case NSUBSHELL: | 9041 | case NSUBSHELL: |
8625 | new->nredir.redirect = copynode(n->nredir.redirect); | 9042 | new->nredir.redirect = copynode(n->nredir.redirect); |
8626 | new->nredir.n = copynode(n->nredir.n); | 9043 | new->nredir.n = copynode(n->nredir.n); |
9044 | SAVE_PTR2(new->nredir.redirect,new->nredir.n); | ||
8627 | break; | 9045 | break; |
8628 | case NAND: | 9046 | case NAND: |
8629 | case NOR: | 9047 | case NOR: |
@@ -8632,31 +9050,37 @@ copynode(union node *n) | |||
8632 | case NUNTIL: | 9050 | case NUNTIL: |
8633 | new->nbinary.ch2 = copynode(n->nbinary.ch2); | 9051 | new->nbinary.ch2 = copynode(n->nbinary.ch2); |
8634 | new->nbinary.ch1 = copynode(n->nbinary.ch1); | 9052 | new->nbinary.ch1 = copynode(n->nbinary.ch1); |
9053 | SAVE_PTR2(new->nbinary.ch1,new->nbinary.ch2); | ||
8635 | break; | 9054 | break; |
8636 | case NIF: | 9055 | case NIF: |
8637 | new->nif.elsepart = copynode(n->nif.elsepart); | 9056 | new->nif.elsepart = copynode(n->nif.elsepart); |
8638 | new->nif.ifpart = copynode(n->nif.ifpart); | 9057 | new->nif.ifpart = copynode(n->nif.ifpart); |
8639 | new->nif.test = copynode(n->nif.test); | 9058 | new->nif.test = copynode(n->nif.test); |
9059 | SAVE_PTR3(new->nif.elsepart,new->nif.ifpart,new->nif.test); | ||
8640 | break; | 9060 | break; |
8641 | case NFOR: | 9061 | case NFOR: |
8642 | new->nfor.var = nodeckstrdup(n->nfor.var); | 9062 | new->nfor.var = nodeckstrdup(n->nfor.var); |
8643 | new->nfor.body = copynode(n->nfor.body); | 9063 | new->nfor.body = copynode(n->nfor.body); |
8644 | new->nfor.args = copynode(n->nfor.args); | 9064 | new->nfor.args = copynode(n->nfor.args); |
9065 | SAVE_PTR3(new->nfor.var,new->nfor.body,new->nfor.args); | ||
8645 | break; | 9066 | break; |
8646 | case NCASE: | 9067 | case NCASE: |
8647 | new->ncase.cases = copynode(n->ncase.cases); | 9068 | new->ncase.cases = copynode(n->ncase.cases); |
8648 | new->ncase.expr = copynode(n->ncase.expr); | 9069 | new->ncase.expr = copynode(n->ncase.expr); |
9070 | SAVE_PTR2(new->ncase.cases,new->ncase.expr); | ||
8649 | break; | 9071 | break; |
8650 | case NCLIST: | 9072 | case NCLIST: |
8651 | new->nclist.body = copynode(n->nclist.body); | 9073 | new->nclist.body = copynode(n->nclist.body); |
8652 | new->nclist.pattern = copynode(n->nclist.pattern); | 9074 | new->nclist.pattern = copynode(n->nclist.pattern); |
8653 | new->nclist.next = copynode(n->nclist.next); | 9075 | new->nclist.next = copynode(n->nclist.next); |
9076 | SAVE_PTR3(new->nclist.body,new->nclist.pattern,new->nclist.next); | ||
8654 | break; | 9077 | break; |
8655 | case NDEFUN: | 9078 | case NDEFUN: |
8656 | case NARG: | 9079 | case NARG: |
8657 | new->narg.backquote = copynodelist(n->narg.backquote); | 9080 | new->narg.backquote = copynodelist(n->narg.backquote); |
8658 | new->narg.text = nodeckstrdup(n->narg.text); | 9081 | new->narg.text = nodeckstrdup(n->narg.text); |
8659 | new->narg.next = copynode(n->narg.next); | 9082 | new->narg.next = copynode(n->narg.next); |
9083 | SAVE_PTR3(new->narg.backquote,new->narg.text,new->narg.next); | ||
8660 | break; | 9084 | break; |
8661 | case NTO: | 9085 | case NTO: |
8662 | #if BASH_REDIR_OUTPUT | 9086 | #if BASH_REDIR_OUTPUT |
@@ -8669,6 +9093,7 @@ copynode(union node *n) | |||
8669 | new->nfile.fname = copynode(n->nfile.fname); | 9093 | new->nfile.fname = copynode(n->nfile.fname); |
8670 | new->nfile.fd = n->nfile.fd; | 9094 | new->nfile.fd = n->nfile.fd; |
8671 | new->nfile.next = copynode(n->nfile.next); | 9095 | new->nfile.next = copynode(n->nfile.next); |
9096 | SAVE_PTR2(new->nfile.fname,new->nfile.next); | ||
8672 | break; | 9097 | break; |
8673 | case NTOFD: | 9098 | case NTOFD: |
8674 | case NFROMFD: | 9099 | case NFROMFD: |
@@ -8676,15 +9101,18 @@ copynode(union node *n) | |||
8676 | new->ndup.dupfd = n->ndup.dupfd; | 9101 | new->ndup.dupfd = n->ndup.dupfd; |
8677 | new->ndup.fd = n->ndup.fd; | 9102 | new->ndup.fd = n->ndup.fd; |
8678 | new->ndup.next = copynode(n->ndup.next); | 9103 | new->ndup.next = copynode(n->ndup.next); |
9104 | SAVE_PTR2(new->ndup.vname,new->ndup.next); | ||
8679 | break; | 9105 | break; |
8680 | case NHERE: | 9106 | case NHERE: |
8681 | case NXHERE: | 9107 | case NXHERE: |
8682 | new->nhere.doc = copynode(n->nhere.doc); | 9108 | new->nhere.doc = copynode(n->nhere.doc); |
8683 | new->nhere.fd = n->nhere.fd; | 9109 | new->nhere.fd = n->nhere.fd; |
8684 | new->nhere.next = copynode(n->nhere.next); | 9110 | new->nhere.next = copynode(n->nhere.next); |
9111 | SAVE_PTR2(new->nhere.doc,new->nhere.next); | ||
8685 | break; | 9112 | break; |
8686 | case NNOT: | 9113 | case NNOT: |
8687 | new->nnot.com = copynode(n->nnot.com); | 9114 | new->nnot.com = copynode(n->nnot.com); |
9115 | SAVE_PTR(new->nnot.com); | ||
8688 | break; | 9116 | break; |
8689 | }; | 9117 | }; |
8690 | new->type = n->type; | 9118 | new->type = n->type; |
@@ -8700,13 +9128,16 @@ copyfunc(union node *n) | |||
8700 | struct funcnode *f; | 9128 | struct funcnode *f; |
8701 | size_t blocksize; | 9129 | size_t blocksize; |
8702 | 9130 | ||
8703 | /*funcstringsize = 0;*/ | 9131 | funcblocksize = offsetof(struct funcnode, n); |
8704 | blocksize = offsetof(struct funcnode, n) + calcsize(0, n); | 9132 | funcstringsize = 0; |
8705 | f = ckzalloc(blocksize /* + funcstringsize */); | 9133 | calcsize(n); |
9134 | blocksize = funcblocksize; | ||
9135 | f = ckmalloc(blocksize + funcstringsize); | ||
8706 | funcblock = (char *) f + offsetof(struct funcnode, n); | 9136 | funcblock = (char *) f + offsetof(struct funcnode, n); |
8707 | funcstring_end = (char *) f + blocksize; | 9137 | funcstring = (char *) f + blocksize; |
9138 | IF_PLATFORM_MINGW32(nodeptr = NULL); | ||
8708 | copynode(n); | 9139 | copynode(n); |
8709 | /* f->count = 0; - ckzalloc did it */ | 9140 | f->count = 0; |
8710 | return f; | 9141 | return f; |
8711 | } | 9142 | } |
8712 | 9143 | ||
@@ -9042,6 +9473,7 @@ evalcase(union node *n, int flags) | |||
9042 | static int | 9473 | static int |
9043 | evalsubshell(union node *n, int flags) | 9474 | evalsubshell(union node *n, int flags) |
9044 | { | 9475 | { |
9476 | IF_PLATFORM_MINGW32(struct forkshell fs;) | ||
9045 | struct job *jp; | 9477 | struct job *jp; |
9046 | int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ | 9478 | int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ |
9047 | int status; | 9479 | int status; |
@@ -9053,12 +9485,22 @@ evalsubshell(union node *n, int flags) | |||
9053 | if (backgnd == FORK_FG) | 9485 | if (backgnd == FORK_FG) |
9054 | get_tty_state(); | 9486 | get_tty_state(); |
9055 | jp = makejob(/*n,*/ 1); | 9487 | jp = makejob(/*n,*/ 1); |
9488 | #if ENABLE_PLATFORM_MINGW32 | ||
9489 | memset(&fs, 0, sizeof(fs)); | ||
9490 | fs.fpid = FS_EVALSUBSHELL; | ||
9491 | fs.n = n; | ||
9492 | fs.flags = flags; | ||
9493 | if (spawn_forkshell(jp, &fs, backgnd) < 0) | ||
9494 | ash_msg_and_raise_error("unable to spawn shell"); | ||
9495 | if ( 0 ) { | ||
9496 | #else | ||
9056 | if (forkshell(jp, n, backgnd) == 0) { | 9497 | if (forkshell(jp, n, backgnd) == 0) { |
9057 | /* child */ | 9498 | /* child */ |
9058 | INT_ON; | 9499 | INT_ON; |
9059 | flags |= EV_EXIT; | 9500 | flags |= EV_EXIT; |
9060 | if (backgnd) | 9501 | if (backgnd) |
9061 | flags &= ~EV_TESTED; | 9502 | flags &= ~EV_TESTED; |
9503 | #endif | ||
9062 | nofork: | 9504 | nofork: |
9063 | redirect(n->nredir.redirect, 0); | 9505 | redirect(n->nredir.redirect, 0); |
9064 | evaltreenr(n->nredir.n, flags); | 9506 | evaltreenr(n->nredir.n, flags); |
@@ -9145,6 +9587,7 @@ expredir(union node *n) | |||
9145 | static int | 9587 | static int |
9146 | evalpipe(union node *n, int flags) | 9588 | evalpipe(union node *n, int flags) |
9147 | { | 9589 | { |
9590 | IF_PLATFORM_MINGW32(struct forkshell fs;) | ||
9148 | struct job *jp; | 9591 | struct job *jp; |
9149 | struct nodelist *lp; | 9592 | struct nodelist *lp; |
9150 | int pipelen; | 9593 | int pipelen; |
@@ -9171,6 +9614,17 @@ evalpipe(union node *n, int flags) | |||
9171 | ash_msg_and_raise_error("pipe call failed"); | 9614 | ash_msg_and_raise_error("pipe call failed"); |
9172 | } | 9615 | } |
9173 | } | 9616 | } |
9617 | #if ENABLE_PLATFORM_MINGW32 | ||
9618 | memset(&fs, 0, sizeof(fs)); | ||
9619 | fs.fpid = FS_EVALPIPE; | ||
9620 | fs.flags = flags; | ||
9621 | fs.n = lp->n; | ||
9622 | fs.fd[0] = pip[0]; | ||
9623 | fs.fd[1] = pip[1]; | ||
9624 | fs.fd[2] = prevfd; | ||
9625 | if (spawn_forkshell(jp, &fs, n->npipe.pipe_backgnd) < 0) | ||
9626 | ash_msg_and_raise_error("unable to spawn shell"); | ||
9627 | #else | ||
9174 | if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { | 9628 | if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { |
9175 | /* child */ | 9629 | /* child */ |
9176 | INT_ON; | 9630 | INT_ON; |
@@ -9188,6 +9642,7 @@ evalpipe(union node *n, int flags) | |||
9188 | evaltreenr(lp->n, flags); | 9642 | evaltreenr(lp->n, flags); |
9189 | /* never returns */ | 9643 | /* never returns */ |
9190 | } | 9644 | } |
9645 | #endif | ||
9191 | /* parent */ | 9646 | /* parent */ |
9192 | if (prevfd >= 0) | 9647 | if (prevfd >= 0) |
9193 | close(prevfd); | 9648 | close(prevfd); |
@@ -9717,6 +10172,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) | |||
9717 | * as POSIX mandates */ | 10172 | * as POSIX mandates */ |
9718 | return back_exitstatus; | 10173 | return back_exitstatus; |
9719 | } | 10174 | } |
10175 | |||
9720 | static int | 10176 | static int |
9721 | evalcommand(union node *cmd, int flags) | 10177 | evalcommand(union node *cmd, int flags) |
9722 | { | 10178 | { |
@@ -9915,7 +10371,15 @@ evalcommand(union node *cmd, int flags) | |||
9915 | */ | 10371 | */ |
9916 | /* find_command() encodes applet_no as (-2 - applet_no) */ | 10372 | /* find_command() encodes applet_no as (-2 - applet_no) */ |
9917 | int applet_no = (- cmdentry.u.index - 2); | 10373 | int applet_no = (- cmdentry.u.index - 2); |
9918 | if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) { | 10374 | if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no) |
10375 | #if ENABLE_PLATFORM_MINGW32 | ||
10376 | /* | ||
10377 | * Fork long-running nofork applets (e.g. yes) in interactive | ||
10378 | * sessions. Otherwise ctrl-c won't let the user kill them. | ||
10379 | */ | ||
10380 | && !(iflag && long_running_applet(applet_no)) | ||
10381 | #endif | ||
10382 | ) { | ||
9919 | listsetvar(varlist.list, VEXPORT|VSTACK); | 10383 | listsetvar(varlist.list, VEXPORT|VSTACK); |
9920 | /* run <applet>_main() */ | 10384 | /* run <applet>_main() */ |
9921 | status = run_nofork_applet(applet_no, argv); | 10385 | status = run_nofork_applet(applet_no, argv); |
@@ -9926,6 +10390,28 @@ evalcommand(union node *cmd, int flags) | |||
9926 | * in a script or a subshell does not need forking, | 10390 | * in a script or a subshell does not need forking, |
9927 | * we can just exec it. | 10391 | * we can just exec it. |
9928 | */ | 10392 | */ |
10393 | #if ENABLE_PLATFORM_MINGW32 | ||
10394 | if (!(flags & EV_EXIT) || trap[0]) { | ||
10395 | /* No, forking off a child is necessary */ | ||
10396 | struct forkshell fs; | ||
10397 | |||
10398 | INT_OFF; | ||
10399 | memset(&fs, 0, sizeof(fs)); | ||
10400 | fs.fpid = FS_SHELLEXEC; | ||
10401 | fs.argv = argv; | ||
10402 | fs.string = (char*)path; | ||
10403 | fs.fd[0] = cmdentry.u.index; | ||
10404 | fs.strlist = varlist.list; | ||
10405 | jp = makejob(/*cmd,*/ 1); | ||
10406 | if (spawn_forkshell(jp, &fs, FORK_FG) < 0) | ||
10407 | ash_msg_and_raise_error("unable to spawn shell"); | ||
10408 | status = waitforjob(jp); | ||
10409 | INT_ON; | ||
10410 | TRACE(("forked child exited with %d\n", exitstatus)); | ||
10411 | break; | ||
10412 | } | ||
10413 | /* goes through to shellexec() */ | ||
10414 | #else | ||
9929 | if (!(flags & EV_EXIT) || may_have_traps) { | 10415 | if (!(flags & EV_EXIT) || may_have_traps) { |
9930 | /* No, forking off a child is necessary */ | 10416 | /* No, forking off a child is necessary */ |
9931 | INT_OFF; | 10417 | INT_OFF; |
@@ -9942,6 +10428,7 @@ evalcommand(union node *cmd, int flags) | |||
9942 | FORCE_INT_ON; | 10428 | FORCE_INT_ON; |
9943 | /* fall through to exec'ing external program */ | 10429 | /* fall through to exec'ing external program */ |
9944 | } | 10430 | } |
10431 | #endif | ||
9945 | listsetvar(varlist.list, VEXPORT|VSTACK); | 10432 | listsetvar(varlist.list, VEXPORT|VSTACK); |
9946 | shellexec(argv[0], argv, path, cmdentry.u.index); | 10433 | shellexec(argv[0], argv, path, cmdentry.u.index); |
9947 | /* NOTREACHED */ | 10434 | /* NOTREACHED */ |
@@ -10325,7 +10812,7 @@ preadbuffer(void) | |||
10325 | more--; | 10812 | more--; |
10326 | 10813 | ||
10327 | c = *q; | 10814 | c = *q; |
10328 | if (c == '\0') { | 10815 | if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) { |
10329 | memmove(q, q + 1, more); | 10816 | memmove(q, q + 1, more); |
10330 | } else { | 10817 | } else { |
10331 | q++; | 10818 | q++; |
@@ -10487,6 +10974,7 @@ popallfiles(void) | |||
10487 | popfile(); | 10974 | popfile(); |
10488 | } | 10975 | } |
10489 | 10976 | ||
10977 | #if !ENABLE_PLATFORM_MINGW32 | ||
10490 | /* | 10978 | /* |
10491 | * Close the file(s) that the shell is reading commands from. Called | 10979 | * Close the file(s) that the shell is reading commands from. Called |
10492 | * after a fork is done. | 10980 | * after a fork is done. |
@@ -10500,6 +10988,7 @@ closescript(void) | |||
10500 | g_parsefile->pf_fd = 0; | 10988 | g_parsefile->pf_fd = 0; |
10501 | } | 10989 | } |
10502 | } | 10990 | } |
10991 | #endif | ||
10503 | 10992 | ||
10504 | /* | 10993 | /* |
10505 | * Like setinputfile, but takes an open file descriptor. Call this with | 10994 | * Like setinputfile, but takes an open file descriptor. Call this with |
@@ -12786,7 +13275,7 @@ find_dot_file(char *name) | |||
12786 | struct stat statb; | 13275 | struct stat statb; |
12787 | 13276 | ||
12788 | /* don't try this for absolute or relative paths */ | 13277 | /* don't try this for absolute or relative paths */ |
12789 | if (strchr(name, '/')) | 13278 | if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\'))) |
12790 | return name; | 13279 | return name; |
12791 | 13280 | ||
12792 | while ((fullname = path_advance(&path, name)) != NULL) { | 13281 | while ((fullname = path_advance(&path, name)) != NULL) { |
@@ -12897,17 +13386,22 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
12897 | struct tblentry *cmdp; | 13386 | struct tblentry *cmdp; |
12898 | int idx; | 13387 | int idx; |
12899 | int prev; | 13388 | int prev; |
12900 | char *fullname; | 13389 | char *fullname IF_PLATFORM_MINGW32(= NULL); |
12901 | struct stat statb; | 13390 | struct stat statb; |
12902 | int e; | 13391 | int e; |
12903 | int updatetbl; | 13392 | int updatetbl; |
13393 | IF_PLATFORM_MINGW32(int len;) | ||
12904 | struct builtincmd *bcmd; | 13394 | struct builtincmd *bcmd; |
12905 | 13395 | ||
12906 | /* If name contains a slash, don't use PATH or hash table */ | 13396 | /* If name contains a slash, don't use PATH or hash table */ |
12907 | if (strchr(name, '/') != NULL) { | 13397 | if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\'))) { |
12908 | entry->u.index = -1; | 13398 | entry->u.index = -1; |
12909 | if (act & DO_ABS) { | 13399 | if (act & DO_ABS) { |
12910 | while (stat(name, &statb) < 0) { | 13400 | while (stat(name, &statb) < 0 |
13401 | #if ENABLE_PLATFORM_MINGW32 | ||
13402 | && (fullname=file_is_win32_executable(name)) == NULL | ||
13403 | #endif | ||
13404 | ) { | ||
12911 | #ifdef SYSV | 13405 | #ifdef SYSV |
12912 | if (errno == EINTR) | 13406 | if (errno == EINTR) |
12913 | continue; | 13407 | continue; |
@@ -12915,6 +13409,9 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
12915 | entry->cmdtype = CMDUNKNOWN; | 13409 | entry->cmdtype = CMDUNKNOWN; |
12916 | return; | 13410 | return; |
12917 | } | 13411 | } |
13412 | #if ENABLE_PLATFORM_MINGW32 | ||
13413 | free(fullname); | ||
13414 | #endif | ||
12918 | } | 13415 | } |
12919 | entry->cmdtype = CMDNORMAL; | 13416 | entry->cmdtype = CMDNORMAL; |
12920 | return; | 13417 | return; |
@@ -13011,12 +13508,48 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13011 | } | 13508 | } |
13012 | } | 13509 | } |
13013 | /* if rehash, don't redo absolute path names */ | 13510 | /* if rehash, don't redo absolute path names */ |
13014 | if (fullname[0] == '/' && idx <= prev) { | 13511 | if (is_absolute_path(fullname) && idx <= prev) { |
13015 | if (idx < prev) | 13512 | if (idx < prev) |
13016 | continue; | 13513 | continue; |
13017 | TRACE(("searchexec \"%s\": no change\n", name)); | 13514 | TRACE(("searchexec \"%s\": no change\n", name)); |
13018 | goto success; | 13515 | goto success; |
13019 | } | 13516 | } |
13517 | #if ENABLE_PLATFORM_MINGW32 | ||
13518 | len = strlen(fullname); | ||
13519 | if (len > 4 && | ||
13520 | (!strcasecmp(fullname+len-4, ".exe") || | ||
13521 | !strcasecmp(fullname+len-4, ".com"))) { | ||
13522 | if (stat(fullname, &statb) < 0) { | ||
13523 | if (errno != ENOENT && errno != ENOTDIR) | ||
13524 | e = errno; | ||
13525 | goto loop; | ||
13526 | } | ||
13527 | } | ||
13528 | else { | ||
13529 | /* path_advance() has reserved space for .exe */ | ||
13530 | memcpy(fullname+len, ".exe", 5); | ||
13531 | if (stat(fullname, &statb) < 0) { | ||
13532 | if (errno != ENOENT && errno != ENOTDIR) | ||
13533 | e = errno; | ||
13534 | memcpy(fullname+len, ".com", 5); | ||
13535 | if (stat(fullname, &statb) < 0) { | ||
13536 | if (errno != ENOENT && errno != ENOTDIR) | ||
13537 | e = errno; | ||
13538 | fullname[len] = '\0'; | ||
13539 | if (stat(fullname, &statb) < 0) { | ||
13540 | if (errno != ENOENT && errno != ENOTDIR) | ||
13541 | e = errno; | ||
13542 | goto loop; | ||
13543 | } | ||
13544 | if (!file_is_executable(fullname)) { | ||
13545 | e = ENOEXEC; | ||
13546 | goto loop; | ||
13547 | } | ||
13548 | } | ||
13549 | } | ||
13550 | fullname[len] = '\0'; | ||
13551 | } | ||
13552 | #else | ||
13020 | while (stat(fullname, &statb) < 0) { | 13553 | while (stat(fullname, &statb) < 0) { |
13021 | #ifdef SYSV | 13554 | #ifdef SYSV |
13022 | if (errno == EINTR) | 13555 | if (errno == EINTR) |
@@ -13026,6 +13559,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path) | |||
13026 | e = errno; | 13559 | e = errno; |
13027 | goto loop; | 13560 | goto loop; |
13028 | } | 13561 | } |
13562 | #endif | ||
13029 | e = EACCES; /* if we fail, this will be the error */ | 13563 | e = EACCES; /* if we fail, this will be the error */ |
13030 | if (!S_ISREG(statb.st_mode)) | 13564 | if (!S_ISREG(statb.st_mode)) |
13031 | continue; | 13565 | continue; |
@@ -13542,7 +14076,11 @@ exitshell(void) | |||
13542 | } | 14076 | } |
13543 | 14077 | ||
13544 | static void | 14078 | static void |
14079 | #if ENABLE_PLATFORM_MINGW32 | ||
14080 | init(int xp) | ||
14081 | #else | ||
13545 | init(void) | 14082 | init(void) |
14083 | #endif | ||
13546 | { | 14084 | { |
13547 | /* we will never free this */ | 14085 | /* we will never free this */ |
13548 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); | 14086 | basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); |
@@ -13561,6 +14099,86 @@ init(void) | |||
13561 | struct stat st1, st2; | 14099 | struct stat st1, st2; |
13562 | 14100 | ||
13563 | initvar(); | 14101 | initvar(); |
14102 | |||
14103 | #if ENABLE_PLATFORM_MINGW32 | ||
14104 | /* | ||
14105 | * case insensitive env names from Windows world | ||
14106 | * | ||
14107 | * Some standard env names such as PATH is named Path and so on | ||
14108 | * ash itself is case sensitive, so "Path" will confuse it, as | ||
14109 | * MSVC getenv() is case insensitive. | ||
14110 | * | ||
14111 | * We may end up having both Path and PATH. Then Path will be chosen | ||
14112 | * because it appears first. | ||
14113 | */ | ||
14114 | for (envp = environ; envp && *envp; envp++) { | ||
14115 | if (strncasecmp(*envp, "PATH=", 5) == 0 && | ||
14116 | strncmp(*envp, "PATH=", 5) != 0) { | ||
14117 | break; | ||
14118 | } | ||
14119 | } | ||
14120 | |||
14121 | if (envp && *envp) { | ||
14122 | /* | ||
14123 | * If we get here it's because the environment contains a path | ||
14124 | * variable called something other than PATH. This suggests we | ||
14125 | * haven't been invoked from an earlier instance of BusyBox. | ||
14126 | */ | ||
14127 | char *start, *end, *s; | ||
14128 | struct passwd *pw; | ||
14129 | |||
14130 | for (envp = environ; envp && *envp; envp++) { | ||
14131 | if (!(end=strchr(*envp, '='))) | ||
14132 | continue; | ||
14133 | |||
14134 | /* make all variable names uppercase */ | ||
14135 | for (start = *envp;start < end;start++) | ||
14136 | *start = toupper(*start); | ||
14137 | |||
14138 | /* skip conversion of variables known to cause problems */ | ||
14139 | if ( strncmp(*envp, "SYSTEMROOT=", 11) == 0 || | ||
14140 | strncmp(*envp, "COMSPEC=", 8) == 0 ) { | ||
14141 | continue; | ||
14142 | } | ||
14143 | |||
14144 | /* convert backslashes to forward slashes in value */ | ||
14145 | if (!xp) { | ||
14146 | for ( s=end+1; *s; ++s ) { | ||
14147 | if ( *s == '\\' ) { | ||
14148 | *s = '/'; | ||
14149 | } | ||
14150 | } | ||
14151 | } | ||
14152 | |||
14153 | /* check for invalid characters in name */ | ||
14154 | for (start = *envp;start < end;start++) { | ||
14155 | if (!isdigit(*start) && !isalpha(*start) && *start != '_') { | ||
14156 | break; | ||
14157 | } | ||
14158 | } | ||
14159 | |||
14160 | if (start != end) { | ||
14161 | /* | ||
14162 | * Make a copy of the variable, replacing invalid | ||
14163 | * characters in the name with underscores. | ||
14164 | */ | ||
14165 | char *var = xstrdup(*envp); | ||
14166 | |||
14167 | for (start = var;*start != '=';start++) { | ||
14168 | if (!isdigit(*start) && !isalpha(*start)) { | ||
14169 | *start = '_'; | ||
14170 | } | ||
14171 | } | ||
14172 | setvareq(var, VEXPORT|VNOSAVE); | ||
14173 | } | ||
14174 | } | ||
14175 | |||
14176 | /* some initialisation normally performed at login */ | ||
14177 | pw = xgetpwuid(getuid()); | ||
14178 | setup_environment(pw->pw_shell, | ||
14179 | SETUP_ENV_CHANGEENV|SETUP_ENV_NO_CHDIR, pw); | ||
14180 | } | ||
14181 | #endif | ||
13564 | for (envp = environ; envp && *envp; envp++) { | 14182 | for (envp = environ; envp && *envp; envp++) { |
13565 | p = endofname(*envp); | 14183 | p = endofname(*envp); |
13566 | if (p != *envp && *p == '=') { | 14184 | if (p != *envp && *p == '=') { |
@@ -13772,17 +14390,44 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13772 | exception_handler = &jmploc; | 14390 | exception_handler = &jmploc; |
13773 | rootpid = getpid(); | 14391 | rootpid = getpid(); |
13774 | 14392 | ||
13775 | init(); | 14393 | init(IF_PLATFORM_MINGW32(argc >= 2 && strcmp(argv[1], "-X") == 0)); |
13776 | setstackmark(&smark); | 14394 | setstackmark(&smark); |
14395 | |||
14396 | #if ENABLE_PLATFORM_MINGW32 | ||
14397 | hSIGINT = CreateEvent(NULL, TRUE, FALSE, NULL); | ||
14398 | SetConsoleCtrlHandler(ctrl_handler, TRUE); | ||
14399 | |||
14400 | if (argc == 3 && !strcmp(argv[1], "--forkshell")) { | ||
14401 | forkshell_init(argv[2]); | ||
14402 | |||
14403 | /* NOTREACHED */ | ||
14404 | bb_error_msg_and_die("subshell ended unexpectedly"); | ||
14405 | } | ||
14406 | #endif | ||
13777 | login_sh = procargs(argv); | 14407 | login_sh = procargs(argv); |
13778 | #if DEBUG | 14408 | #if DEBUG |
13779 | TRACE(("Shell args: ")); | 14409 | TRACE(("Shell args: ")); |
13780 | trace_puts_args(argv); | 14410 | trace_puts_args(argv); |
13781 | #endif | 14411 | #endif |
13782 | 14412 | ||
14413 | #if ENABLE_ASH_NOCONSOLE | ||
14414 | if ( noconsole ) { | ||
14415 | DWORD dummy; | ||
14416 | |||
14417 | if ( GetConsoleProcessList(&dummy, 1) == 1 ) { | ||
14418 | ShowWindow(GetConsoleWindow(), SW_HIDE); | ||
14419 | } | ||
14420 | } | ||
14421 | #endif | ||
14422 | |||
13783 | if (login_sh) { | 14423 | if (login_sh) { |
13784 | const char *hp; | 14424 | const char *hp; |
13785 | 14425 | ||
14426 | #if ENABLE_PLATFORM_MINGW32 | ||
14427 | chdir(xgetpwuid(getuid())->pw_dir); | ||
14428 | setpwd(NULL, 0); | ||
14429 | #endif | ||
14430 | |||
13786 | state = 1; | 14431 | state = 1; |
13787 | read_profile("/etc/profile"); | 14432 | read_profile("/etc/profile"); |
13788 | state1: | 14433 | state1: |
@@ -13857,6 +14502,642 @@ int ash_main(int argc UNUSED_PARAM, char **argv) | |||
13857 | /* NOTREACHED */ | 14502 | /* NOTREACHED */ |
13858 | } | 14503 | } |
13859 | 14504 | ||
14505 | #if ENABLE_PLATFORM_MINGW32 | ||
14506 | static void | ||
14507 | forkshell_openhere(struct forkshell *fs) | ||
14508 | { | ||
14509 | union node *redir = fs->n; | ||
14510 | int pip[2]; | ||
14511 | |||
14512 | pip[0] = fs->fd[0]; | ||
14513 | pip[1] = fs->fd[1]; | ||
14514 | |||
14515 | TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); | ||
14516 | |||
14517 | close(pip[0]); | ||
14518 | ignoresig(SIGINT); //signal(SIGINT, SIG_IGN); | ||
14519 | ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN); | ||
14520 | ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN); | ||
14521 | ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN); | ||
14522 | signal(SIGPIPE, SIG_DFL); | ||
14523 | if (redir->type == NHERE) { | ||
14524 | size_t len = strlen(redir->nhere.doc->narg.text); | ||
14525 | full_write(pip[1], redir->nhere.doc->narg.text, len); | ||
14526 | } else /* NXHERE */ | ||
14527 | expandhere(redir->nhere.doc, pip[1]); | ||
14528 | _exit(EXIT_SUCCESS); | ||
14529 | } | ||
14530 | |||
14531 | static void | ||
14532 | forkshell_evalbackcmd(struct forkshell *fs) | ||
14533 | { | ||
14534 | union node *n = fs->n; | ||
14535 | int pip[2] = {fs->fd[0], fs->fd[1]}; | ||
14536 | |||
14537 | FORCE_INT_ON; | ||
14538 | close(pip[0]); | ||
14539 | if (pip[1] != 1) { | ||
14540 | /*close(1);*/ | ||
14541 | dup2_or_raise(pip[1], 1); | ||
14542 | close(pip[1]); | ||
14543 | } | ||
14544 | eflag = 0; | ||
14545 | evaltree(n, EV_EXIT); /* actually evaltreenr... */ | ||
14546 | /* NOTREACHED */ | ||
14547 | } | ||
14548 | |||
14549 | static void | ||
14550 | forkshell_evalsubshell(struct forkshell *fs) | ||
14551 | { | ||
14552 | union node *n = fs->n; | ||
14553 | int flags = fs->flags; | ||
14554 | |||
14555 | TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); | ||
14556 | INT_ON; | ||
14557 | flags |= EV_EXIT; | ||
14558 | expredir(n->nredir.redirect); | ||
14559 | redirect(n->nredir.redirect, 0); | ||
14560 | evaltreenr(n->nredir.n, flags); | ||
14561 | /* never returns */ | ||
14562 | } | ||
14563 | |||
14564 | static void | ||
14565 | forkshell_evalpipe(struct forkshell *fs) | ||
14566 | { | ||
14567 | union node *n = fs->n; | ||
14568 | int flags = fs->flags; | ||
14569 | int prevfd = fs->fd[2]; | ||
14570 | int pip[2] = {fs->fd[0], fs->fd[1]}; | ||
14571 | |||
14572 | TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__)); | ||
14573 | INT_ON; | ||
14574 | if (pip[1] >= 0) { | ||
14575 | close(pip[0]); | ||
14576 | } | ||
14577 | if (prevfd > 0) { | ||
14578 | dup2(prevfd, 0); | ||
14579 | close(prevfd); | ||
14580 | } | ||
14581 | if (pip[1] > 1) { | ||
14582 | dup2(pip[1], 1); | ||
14583 | close(pip[1]); | ||
14584 | } | ||
14585 | evaltreenr(n, flags); | ||
14586 | } | ||
14587 | |||
14588 | static void | ||
14589 | forkshell_shellexec(struct forkshell *fs) | ||
14590 | { | ||
14591 | int idx = fs->fd[0]; | ||
14592 | struct strlist *varlist = fs->strlist; | ||
14593 | char **argv = fs->argv; | ||
14594 | char *path = fs->string; | ||
14595 | |||
14596 | FORCE_INT_ON; | ||
14597 | listsetvar(varlist, VEXPORT|VSTACK); | ||
14598 | shellexec(argv[0], argv, path, idx); | ||
14599 | } | ||
14600 | |||
14601 | static void | ||
14602 | forkshell_child(struct forkshell *fs) | ||
14603 | { | ||
14604 | switch ( fs->fpid ) { | ||
14605 | case FS_OPENHERE: | ||
14606 | forkshell_openhere(fs); | ||
14607 | break; | ||
14608 | case FS_EVALBACKCMD: | ||
14609 | forkshell_evalbackcmd(fs); | ||
14610 | break; | ||
14611 | case FS_EVALSUBSHELL: | ||
14612 | forkshell_evalsubshell(fs); | ||
14613 | break; | ||
14614 | case FS_EVALPIPE: | ||
14615 | forkshell_evalpipe(fs); | ||
14616 | break; | ||
14617 | case FS_SHELLEXEC: | ||
14618 | forkshell_shellexec(fs); | ||
14619 | break; | ||
14620 | } | ||
14621 | } | ||
14622 | |||
14623 | /* | ||
14624 | * Reset the pointers to the builtin environment variables in the hash | ||
14625 | * table to point to varinit rather than the bogus copy created during | ||
14626 | * forkshell_prepare. | ||
14627 | */ | ||
14628 | static void | ||
14629 | reinitvar(void) | ||
14630 | { | ||
14631 | struct var *vp; | ||
14632 | struct var *end; | ||
14633 | struct var **vpp; | ||
14634 | struct var **old; | ||
14635 | |||
14636 | vp = varinit; | ||
14637 | end = vp + ARRAY_SIZE(varinit); | ||
14638 | do { | ||
14639 | vpp = hashvar(vp->var_text); | ||
14640 | if ( (old=findvar(vpp, vp->var_text)) != NULL ) { | ||
14641 | vp->next = (*old)->next; | ||
14642 | *old = vp; | ||
14643 | } | ||
14644 | } while (++vp < end); | ||
14645 | } | ||
14646 | |||
14647 | /* FIXME: should consider running forkparent() and forkchild() */ | ||
14648 | static int | ||
14649 | spawn_forkshell(struct job *jp, struct forkshell *fs, int mode) | ||
14650 | { | ||
14651 | struct forkshell *new; | ||
14652 | char buf[16]; | ||
14653 | const char *argv[] = { "sh", "--forkshell", NULL, NULL }; | ||
14654 | intptr_t ret; | ||
14655 | |||
14656 | new = forkshell_prepare(fs); | ||
14657 | sprintf(buf, "%x", (unsigned int)new->hMapFile); | ||
14658 | argv[2] = buf; | ||
14659 | ret = mingw_spawn_proc(argv); | ||
14660 | CloseHandle(new->hMapFile); | ||
14661 | UnmapViewOfFile(new); | ||
14662 | if (ret == -1) { | ||
14663 | free(jp); | ||
14664 | return -1; | ||
14665 | } | ||
14666 | forkparent(jp, fs->node, mode, (HANDLE)ret); | ||
14667 | return ret == -1 ? -1 : 0; | ||
14668 | } | ||
14669 | |||
14670 | /* | ||
14671 | * forkshell_prepare() and friends | ||
14672 | * | ||
14673 | * The sequence is as follows: | ||
14674 | * - funcblocksize, funcstringsize, nodeptrsize are initialized | ||
14675 | * - forkshell_size(fs) is called to calculate the exact memory needed | ||
14676 | * - a new struct is allocated | ||
14677 | * - funcblock, funcstring, nodeptr are initialized from the new block | ||
14678 | * - forkshell_copy(fs) is called to copy recursively everything over | ||
14679 | * it will record all pointers along the way, to nodeptr | ||
14680 | * | ||
14681 | * When this memory is mapped elsewhere, pointer fixup will be needed | ||
14682 | */ | ||
14683 | #define SLIST_SIZE_BEGIN(name,type) \ | ||
14684 | static void \ | ||
14685 | name(type *p) \ | ||
14686 | { \ | ||
14687 | while (p) { \ | ||
14688 | funcblocksize += sizeof(type); | ||
14689 | /* do something here with p */ | ||
14690 | #define SLIST_SIZE_END() \ | ||
14691 | nodeptrsize++; \ | ||
14692 | p = p->next; \ | ||
14693 | } \ | ||
14694 | } | ||
14695 | |||
14696 | #define SLIST_COPY_BEGIN(name,type) \ | ||
14697 | static type * \ | ||
14698 | name(type *vp) \ | ||
14699 | { \ | ||
14700 | type *start; \ | ||
14701 | type **vpp; \ | ||
14702 | vpp = &start; \ | ||
14703 | while (vp) { \ | ||
14704 | *vpp = funcblock; \ | ||
14705 | funcblock = (char *) funcblock + sizeof(type); | ||
14706 | /* do something here with vpp and vp */ | ||
14707 | #define SLIST_COPY_END() \ | ||
14708 | SAVE_PTR((*vpp)->next); \ | ||
14709 | vp = vp->next; \ | ||
14710 | vpp = &(*vpp)->next; \ | ||
14711 | } \ | ||
14712 | *vpp = NULL; \ | ||
14713 | return start; \ | ||
14714 | } | ||
14715 | |||
14716 | /* | ||
14717 | * struct var | ||
14718 | */ | ||
14719 | SLIST_SIZE_BEGIN(var_size,struct var) | ||
14720 | funcstringsize += strlen(p->var_text) + 1; | ||
14721 | nodeptrsize++; /* p->text */ | ||
14722 | SLIST_SIZE_END() | ||
14723 | |||
14724 | SLIST_COPY_BEGIN(var_copy,struct var) | ||
14725 | (*vpp)->var_text = nodeckstrdup(vp->var_text); | ||
14726 | (*vpp)->flags = vp->flags; | ||
14727 | /* | ||
14728 | * The only place that can set struct var#func is varinit[], | ||
14729 | * which will be fixed by forkshell_init() | ||
14730 | */ | ||
14731 | (*vpp)->var_func = NULL; | ||
14732 | SAVE_PTR((*vpp)->var_text); | ||
14733 | SLIST_COPY_END() | ||
14734 | |||
14735 | /* | ||
14736 | * struct strlist | ||
14737 | */ | ||
14738 | SLIST_SIZE_BEGIN(strlist_size,struct strlist) | ||
14739 | funcstringsize += strlen(p->text) + 1; | ||
14740 | nodeptrsize++; /* p->text */ | ||
14741 | SLIST_SIZE_END() | ||
14742 | |||
14743 | SLIST_COPY_BEGIN(strlist_copy,struct strlist) | ||
14744 | (*vpp)->text = nodeckstrdup(vp->text); | ||
14745 | SAVE_PTR((*vpp)->text); | ||
14746 | SLIST_COPY_END() | ||
14747 | |||
14748 | /* | ||
14749 | * struct tblentry | ||
14750 | */ | ||
14751 | static void | ||
14752 | tblentry_size(struct tblentry *tep) | ||
14753 | { | ||
14754 | while (tep) { | ||
14755 | funcblocksize += sizeof(struct tblentry) + strlen(tep->cmdname) + 1; | ||
14756 | /* CMDBUILTIN, e->param.cmd needs no pointer relocation */ | ||
14757 | if (tep->cmdtype == CMDFUNCTION) { | ||
14758 | funcblocksize += offsetof(struct funcnode, n); | ||
14759 | calcsize(&tep->param.func->n); | ||
14760 | nodeptrsize++; /* tep->param.func */ | ||
14761 | } | ||
14762 | nodeptrsize++; /* tep->next */ | ||
14763 | tep = tep->next; | ||
14764 | } | ||
14765 | } | ||
14766 | |||
14767 | static struct tblentry * | ||
14768 | tblentry_copy(struct tblentry *tep) | ||
14769 | { | ||
14770 | struct tblentry *start; | ||
14771 | struct tblentry **newp; | ||
14772 | int size; | ||
14773 | |||
14774 | newp = &start; | ||
14775 | while (tep) { | ||
14776 | *newp = funcblock; | ||
14777 | size = sizeof(struct tblentry) + strlen(tep->cmdname) + 1; | ||
14778 | |||
14779 | funcblock = (char *) funcblock + size; | ||
14780 | memcpy(*newp, tep, size); | ||
14781 | switch (tep->cmdtype) { | ||
14782 | case CMDBUILTIN: | ||
14783 | /* No pointer saving, this field must be fixed by forkshell_init() */ | ||
14784 | (*newp)->param.cmd = (const struct builtincmd *)(tep->param.cmd - builtintab); | ||
14785 | break; | ||
14786 | case CMDFUNCTION: | ||
14787 | (*newp)->param.func = funcblock; | ||
14788 | funcblock = (char *) funcblock + offsetof(struct funcnode, n); | ||
14789 | copynode(&tep->param.func->n); | ||
14790 | SAVE_PTR((*newp)->param.func); | ||
14791 | break; | ||
14792 | default: | ||
14793 | break; | ||
14794 | } | ||
14795 | SAVE_PTR((*newp)->next); | ||
14796 | tep = tep->next; | ||
14797 | newp = &(*newp)->next; | ||
14798 | } | ||
14799 | *newp = NULL; | ||
14800 | return start; | ||
14801 | } | ||
14802 | |||
14803 | static void | ||
14804 | cmdtable_size(struct tblentry **cmdtablep) | ||
14805 | { | ||
14806 | int i; | ||
14807 | nodeptrsize += CMDTABLESIZE; | ||
14808 | funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE; | ||
14809 | for (i = 0; i < CMDTABLESIZE; i++) | ||
14810 | tblentry_size(cmdtablep[i]); | ||
14811 | } | ||
14812 | |||
14813 | static struct tblentry ** | ||
14814 | cmdtable_copy(struct tblentry **cmdtablep) | ||
14815 | { | ||
14816 | struct tblentry **new = funcblock; | ||
14817 | int i; | ||
14818 | |||
14819 | funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE; | ||
14820 | for (i = 0; i < CMDTABLESIZE; i++) { | ||
14821 | new[i] = tblentry_copy(cmdtablep[i]); | ||
14822 | SAVE_PTR(new[i]); | ||
14823 | } | ||
14824 | return new; | ||
14825 | } | ||
14826 | |||
14827 | /* | ||
14828 | * char ** | ||
14829 | */ | ||
14830 | static void | ||
14831 | argv_size(char **p) | ||
14832 | { | ||
14833 | while (p && *p) { | ||
14834 | funcblocksize += sizeof(char *); | ||
14835 | funcstringsize += strlen(*p)+1; | ||
14836 | nodeptrsize++; | ||
14837 | p++; | ||
14838 | } | ||
14839 | funcblocksize += sizeof(char *); | ||
14840 | } | ||
14841 | |||
14842 | static char ** | ||
14843 | argv_copy(char **p) | ||
14844 | { | ||
14845 | char **new, **start = funcblock; | ||
14846 | |||
14847 | while (p && *p) { | ||
14848 | new = funcblock; | ||
14849 | funcblock = (char *) funcblock + sizeof(char *); | ||
14850 | *new = nodeckstrdup(*p); | ||
14851 | SAVE_PTR(*new); | ||
14852 | p++; | ||
14853 | new++; | ||
14854 | } | ||
14855 | new = funcblock; | ||
14856 | funcblock = (char *) funcblock + sizeof(char *); | ||
14857 | *new = NULL; | ||
14858 | return start; | ||
14859 | } | ||
14860 | |||
14861 | /* | ||
14862 | * struct redirtab | ||
14863 | */ | ||
14864 | static void | ||
14865 | redirtab_size(struct redirtab *rdtp) | ||
14866 | { | ||
14867 | while (rdtp) { | ||
14868 | funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count; | ||
14869 | rdtp = rdtp->next; | ||
14870 | nodeptrsize++; /* rdtp->next */ | ||
14871 | } | ||
14872 | } | ||
14873 | |||
14874 | static struct redirtab * | ||
14875 | redirtab_copy(struct redirtab *rdtp) | ||
14876 | { | ||
14877 | struct redirtab *start; | ||
14878 | struct redirtab **vpp; | ||
14879 | |||
14880 | vpp = &start; | ||
14881 | while (rdtp) { | ||
14882 | int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count; | ||
14883 | *vpp = funcblock; | ||
14884 | funcblock = (char *) funcblock + size; | ||
14885 | memcpy(*vpp, rdtp, size); | ||
14886 | SAVE_PTR((*vpp)->next); | ||
14887 | rdtp = rdtp->next; | ||
14888 | vpp = &(*vpp)->next; | ||
14889 | } | ||
14890 | *vpp = NULL; | ||
14891 | return start; | ||
14892 | } | ||
14893 | |||
14894 | #undef shellparam | ||
14895 | #undef redirlist | ||
14896 | #undef varinit | ||
14897 | #undef vartab | ||
14898 | static void | ||
14899 | globals_var_size(struct globals_var *gvp) | ||
14900 | { | ||
14901 | int i; | ||
14902 | |||
14903 | funcblocksize += sizeof(struct globals_var); | ||
14904 | argv_size(gvp->shellparam.p); | ||
14905 | redirtab_size(gvp->redirlist); | ||
14906 | for (i = 0; i < VTABSIZE; i++) | ||
14907 | var_size(gvp->vartab[i]); | ||
14908 | for (i = 0; i < ARRAY_SIZE(varinit_data); i++) | ||
14909 | var_size(gvp->varinit+i); | ||
14910 | nodeptrsize += 2 + VTABSIZE; /* gvp->redirlist, gvp->shellparam.p, vartab */ | ||
14911 | } | ||
14912 | |||
14913 | #undef preverrout_fd | ||
14914 | static struct globals_var * | ||
14915 | globals_var_copy(struct globals_var *gvp) | ||
14916 | { | ||
14917 | int i; | ||
14918 | struct globals_var *new; | ||
14919 | |||
14920 | new = funcblock; | ||
14921 | funcblock = (char *) funcblock + sizeof(struct globals_var); | ||
14922 | |||
14923 | /* shparam */ | ||
14924 | memcpy(&new->shellparam, &gvp->shellparam, sizeof(struct shparam)); | ||
14925 | new->shellparam.malloced = 0; | ||
14926 | new->shellparam.p = argv_copy(gvp->shellparam.p); | ||
14927 | SAVE_PTR(new->shellparam.p); | ||
14928 | |||
14929 | new->redirlist = redirtab_copy(gvp->redirlist); | ||
14930 | SAVE_PTR(new->redirlist); | ||
14931 | |||
14932 | new->preverrout_fd = gvp->preverrout_fd; | ||
14933 | for (i = 0; i < VTABSIZE; i++) { | ||
14934 | new->vartab[i] = var_copy(gvp->vartab[i]); | ||
14935 | SAVE_PTR(new->vartab[i]); | ||
14936 | } | ||
14937 | |||
14938 | /* Can't use var_copy because varinit is already allocated */ | ||
14939 | for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { | ||
14940 | new->varinit[i].next = NULL; | ||
14941 | new->varinit[i].var_text = nodeckstrdup(gvp->varinit[i].var_text); | ||
14942 | SAVE_PTR(new->varinit[i].var_text); | ||
14943 | new->varinit[i].flags = gvp->varinit[i].flags; | ||
14944 | new->varinit[i].var_func = gvp->varinit[i].var_func; | ||
14945 | } | ||
14946 | return new; | ||
14947 | } | ||
14948 | |||
14949 | #undef minusc | ||
14950 | #undef curdir | ||
14951 | #undef physdir | ||
14952 | #undef arg0 | ||
14953 | #undef nullstr | ||
14954 | static void | ||
14955 | globals_misc_size(struct globals_misc *p) | ||
14956 | { | ||
14957 | funcblocksize += sizeof(struct globals_misc); | ||
14958 | funcstringsize += p->minusc ? strlen(p->minusc) + 1 : 1; | ||
14959 | if (p->curdir != p->nullstr) | ||
14960 | funcstringsize += strlen(p->curdir) + 1; | ||
14961 | if (p->physdir != p->nullstr) | ||
14962 | funcstringsize += strlen(p->physdir) + 1; | ||
14963 | funcstringsize += strlen(p->arg0) + 1; | ||
14964 | nodeptrsize += 4; /* minusc, curdir, physdir, arg0 */ | ||
14965 | } | ||
14966 | |||
14967 | static struct globals_misc * | ||
14968 | globals_misc_copy(struct globals_misc *p) | ||
14969 | { | ||
14970 | struct globals_misc *new = funcblock; | ||
14971 | |||
14972 | funcblock = (char *) funcblock + sizeof(struct globals_misc); | ||
14973 | memcpy(new, p, sizeof(struct globals_misc)); | ||
14974 | |||
14975 | new->minusc = nodeckstrdup(p->minusc); | ||
14976 | new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr; | ||
14977 | new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr; | ||
14978 | new->arg0 = nodeckstrdup(p->arg0); | ||
14979 | SAVE_PTR4(new->minusc, new->curdir, new->physdir, new->arg0); | ||
14980 | return new; | ||
14981 | } | ||
14982 | |||
14983 | static void | ||
14984 | forkshell_size(struct forkshell *fs) | ||
14985 | { | ||
14986 | funcblocksize += sizeof(struct forkshell); | ||
14987 | globals_var_size(fs->gvp); | ||
14988 | globals_misc_size(fs->gmp); | ||
14989 | cmdtable_size(fs->cmdtable); | ||
14990 | /* optlist_transfer(sending, fd); */ | ||
14991 | /* misc_transfer(sending, fd); */ | ||
14992 | |||
14993 | calcsize(fs->n); | ||
14994 | argv_size(fs->argv); | ||
14995 | funcstringsize += (fs->string ? strlen(fs->string) : 0) + 1; | ||
14996 | strlist_size(fs->strlist); | ||
14997 | |||
14998 | nodeptrsize += 7; /* gvp, gmp, cmdtable, n, argv, string, strlist */ | ||
14999 | } | ||
15000 | |||
15001 | static struct forkshell * | ||
15002 | forkshell_copy(struct forkshell *fs) | ||
15003 | { | ||
15004 | struct forkshell *new; | ||
15005 | |||
15006 | new = funcblock; | ||
15007 | funcblock = (char *) funcblock + sizeof(struct forkshell); | ||
15008 | |||
15009 | memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */ | ||
15010 | new->gvp = globals_var_copy(fs->gvp); | ||
15011 | new->gmp = globals_misc_copy(fs->gmp); | ||
15012 | new->cmdtable = cmdtable_copy(fs->cmdtable); | ||
15013 | SAVE_PTR3(new->gvp, new->gmp, new->cmdtable); | ||
15014 | |||
15015 | new->n = copynode(fs->n); | ||
15016 | new->argv = argv_copy(fs->argv); | ||
15017 | new->string = nodeckstrdup(fs->string); | ||
15018 | new->strlist = strlist_copy(fs->strlist); | ||
15019 | SAVE_PTR4(new->n, new->argv, new->string, new->strlist); | ||
15020 | return new; | ||
15021 | } | ||
15022 | |||
15023 | static struct forkshell * | ||
15024 | forkshell_prepare(struct forkshell *fs) | ||
15025 | { | ||
15026 | struct forkshell *new; | ||
15027 | int size, nodeptr_offset; | ||
15028 | HANDLE h; | ||
15029 | SECURITY_ATTRIBUTES sa; | ||
15030 | |||
15031 | /* Calculate size of "new" */ | ||
15032 | fs->gvp = ash_ptr_to_globals_var; | ||
15033 | fs->gmp = ash_ptr_to_globals_misc; | ||
15034 | fs->cmdtable = cmdtable; | ||
15035 | |||
15036 | nodeptrsize = 1; /* NULL terminated */ | ||
15037 | funcblocksize = 0; | ||
15038 | funcstringsize = 0; | ||
15039 | forkshell_size(fs); | ||
15040 | size = funcblocksize + funcstringsize + nodeptrsize*sizeof(char *); | ||
15041 | |||
15042 | /* Allocate, initialize pointers */ | ||
15043 | memset(&sa, 0, sizeof(sa)); | ||
15044 | sa.nLength = sizeof(sa); | ||
15045 | sa.lpSecurityDescriptor = NULL; | ||
15046 | sa.bInheritHandle = TRUE; | ||
15047 | h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, size, NULL); | ||
15048 | new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0); | ||
15049 | /* new = ckmalloc(size); */ | ||
15050 | funcblock = new; | ||
15051 | funcstring = (char *) funcblock + funcblocksize; | ||
15052 | nodeptr = (char **)((char *)funcstring + funcstringsize); | ||
15053 | nodeptr_offset = (char *)nodeptr - (char *)new; | ||
15054 | |||
15055 | /* Now pack them all */ | ||
15056 | forkshell_copy(fs); | ||
15057 | |||
15058 | /* Finish it up */ | ||
15059 | *nodeptr = NULL; | ||
15060 | new->size = size; | ||
15061 | new->nodeptr_offset = nodeptr_offset; | ||
15062 | new->old_base = new; | ||
15063 | new->hMapFile = h; | ||
15064 | return new; | ||
15065 | } | ||
15066 | |||
15067 | #undef exception_handler | ||
15068 | #undef trap | ||
15069 | #undef trap_ptr | ||
15070 | static void *sticky_mem_start, *sticky_mem_end; | ||
15071 | static void | ||
15072 | forkshell_init(const char *idstr) | ||
15073 | { | ||
15074 | struct forkshell *fs; | ||
15075 | int map_handle; | ||
15076 | HANDLE h; | ||
15077 | struct globals_var **gvpp; | ||
15078 | struct globals_misc **gmpp; | ||
15079 | int i; | ||
15080 | char **ptr; | ||
15081 | |||
15082 | if (sscanf(idstr, "%x", &map_handle) != 1) | ||
15083 | bb_error_msg_and_die("invalid forkshell ID"); | ||
15084 | |||
15085 | h = (HANDLE)map_handle; | ||
15086 | fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0); | ||
15087 | if (!fs) | ||
15088 | bb_error_msg_and_die("Invalid forkshell memory"); | ||
15089 | |||
15090 | /* this memory can't be freed */ | ||
15091 | sticky_mem_start = fs; | ||
15092 | sticky_mem_end = (char *) fs + fs->size; | ||
15093 | |||
15094 | /* pointer fixup */ | ||
15095 | nodeptr = (char **)((char *)fs + fs->nodeptr_offset); | ||
15096 | for ( i=0; nodeptr[i]; ++i ) { | ||
15097 | ptr = (char **)((char *)fs + (nodeptr[i] - (char *)fs->old_base)); | ||
15098 | if (*ptr) | ||
15099 | *ptr = (char *)fs + (*ptr - (char *)fs->old_base); | ||
15100 | } | ||
15101 | |||
15102 | /* Now fix up stuff that can't be transferred */ | ||
15103 | for (i = 0; i < ARRAY_SIZE(varinit_data); i++) | ||
15104 | fs->gvp->varinit[i].var_func = varinit_data[i].var_func; | ||
15105 | for (i = 0; i < CMDTABLESIZE; i++) { | ||
15106 | struct tblentry *e = fs->cmdtable[i]; | ||
15107 | while (e) { | ||
15108 | if (e->cmdtype == CMDBUILTIN) | ||
15109 | e->param.cmd = builtintab + (int)e->param.cmd; | ||
15110 | e = e->next; | ||
15111 | } | ||
15112 | } | ||
15113 | fs->gmp->exception_handler = ash_ptr_to_globals_misc->exception_handler; | ||
15114 | for (i = 0; i < NSIG; i++) | ||
15115 | fs->gmp->trap[i] = ash_ptr_to_globals_misc->trap[i]; | ||
15116 | fs->gmp->trap_ptr = ash_ptr_to_globals_misc->trap_ptr; | ||
15117 | |||
15118 | /* Switch global variables */ | ||
15119 | gvpp = (struct globals_var **)&ash_ptr_to_globals_var; | ||
15120 | *gvpp = fs->gvp; | ||
15121 | gmpp = (struct globals_misc **)&ash_ptr_to_globals_misc; | ||
15122 | *gmpp = fs->gmp; | ||
15123 | cmdtable = fs->cmdtable; | ||
15124 | |||
15125 | CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */ | ||
15126 | |||
15127 | reinitvar(); | ||
15128 | |||
15129 | forkshell_child(fs); | ||
15130 | } | ||
15131 | |||
15132 | #undef free | ||
15133 | static void | ||
15134 | sticky_free(void *base) | ||
15135 | { | ||
15136 | if (base >= sticky_mem_start && base < sticky_mem_end) | ||
15137 | return; | ||
15138 | free(base); | ||
15139 | } | ||
15140 | #endif | ||
13860 | 15141 | ||
13861 | /*- | 15142 | /*- |
13862 | * Copyright (c) 1989, 1991, 1993, 1994 | 15143 | * Copyright (c) 1989, 1991, 1993, 1994 |
diff --git a/shell/math.h b/shell/math.h index 32e1ffe35..90eb49144 100644 --- a/shell/math.h +++ b/shell/math.h | |||
@@ -65,7 +65,7 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN | |||
65 | 65 | ||
66 | #if ENABLE_FEATURE_SH_MATH_64 | 66 | #if ENABLE_FEATURE_SH_MATH_64 |
67 | typedef long long arith_t; | 67 | typedef long long arith_t; |
68 | #define ARITH_FMT "%lld" | 68 | #define ARITH_FMT "%"LL_FMT"d" |
69 | #define strto_arith_t strtoull | 69 | #define strto_arith_t strtoull |
70 | #else | 70 | #else |
71 | typedef long arith_t; | 71 | typedef long arith_t; |
diff --git a/shell/shell_common.c b/shell/shell_common.c index a9f8d8413..750adc5d8 100644 --- a/shell/shell_common.c +++ b/shell/shell_common.c | |||
@@ -20,7 +20,11 @@ | |||
20 | #include "shell_common.h" | 20 | #include "shell_common.h" |
21 | #include <sys/resource.h> /* getrlimit */ | 21 | #include <sys/resource.h> /* getrlimit */ |
22 | 22 | ||
23 | #if !ENABLE_PLATFORM_MINGW32 | ||
23 | const char defifsvar[] ALIGN1 = "IFS= \t\n"; | 24 | const char defifsvar[] ALIGN1 = "IFS= \t\n"; |
25 | #else | ||
26 | const char defifsvar[] ALIGN1 = "IFS= \t\n\r"; | ||
27 | #endif | ||
24 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; | 28 | const char defoptindvar[] ALIGN1 = "OPTIND=1"; |
25 | 29 | ||
26 | 30 | ||
@@ -209,6 +213,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
209 | * regardless of SA_RESTART-ness of that signal! | 213 | * regardless of SA_RESTART-ness of that signal! |
210 | */ | 214 | */ |
211 | errno = 0; | 215 | errno = 0; |
216 | #if !ENABLE_PLATFORM_MINGW32 | ||
212 | pfd[0].events = POLLIN; | 217 | pfd[0].events = POLLIN; |
213 | if (poll(pfd, 1, timeout) <= 0) { | 218 | if (poll(pfd, 1, timeout) <= 0) { |
214 | /* timed out, or EINTR */ | 219 | /* timed out, or EINTR */ |
@@ -216,6 +221,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
216 | retval = (const char *)(uintptr_t)1; | 221 | retval = (const char *)(uintptr_t)1; |
217 | goto ret; | 222 | goto ret; |
218 | } | 223 | } |
224 | #endif | ||
219 | if (read(fd, &buffer[bufpos], 1) != 1) { | 225 | if (read(fd, &buffer[bufpos], 1) != 1) { |
220 | err = errno; | 226 | err = errno; |
221 | retval = (const char *)(uintptr_t)1; | 227 | retval = (const char *)(uintptr_t)1; |
@@ -223,7 +229,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
223 | } | 229 | } |
224 | 230 | ||
225 | c = buffer[bufpos]; | 231 | c = buffer[bufpos]; |
226 | if (c == '\0') | 232 | if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) |
227 | continue; | 233 | continue; |
228 | if (!(read_flags & BUILTIN_READ_RAW)) { | 234 | if (!(read_flags & BUILTIN_READ_RAW)) { |
229 | if (backslash) { | 235 | if (backslash) { |
@@ -298,6 +304,7 @@ shell_builtin_read(void FAST_FUNC (*setvar)(const char *name, const char *val), | |||
298 | 304 | ||
299 | /* ulimit builtin */ | 305 | /* ulimit builtin */ |
300 | 306 | ||
307 | #if !ENABLE_PLATFORM_MINGW32 | ||
301 | struct limits { | 308 | struct limits { |
302 | uint8_t cmd; /* RLIMIT_xxx fit into it */ | 309 | uint8_t cmd; /* RLIMIT_xxx fit into it */ |
303 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ | 310 | uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ |
@@ -514,3 +521,9 @@ shell_builtin_ulimit(char **argv) | |||
514 | 521 | ||
515 | return 0; | 522 | return 0; |
516 | } | 523 | } |
524 | #else | ||
525 | int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM) | ||
526 | { | ||
527 | return 1; | ||
528 | } | ||
529 | #endif | ||
diff --git a/util-linux/more.c b/util-linux/more.c index 926cf5f26..fafc80403 100644 --- a/util-linux/more.c +++ b/util-linux/more.c | |||
@@ -35,6 +35,9 @@ | |||
35 | //usage:#define more_example_usage | 35 | //usage:#define more_example_usage |
36 | //usage: "$ dmesg | more\n" | 36 | //usage: "$ dmesg | more\n" |
37 | 37 | ||
38 | #if ENABLE_PLATFORM_MINGW32 | ||
39 | #include <conio.h> | ||
40 | #endif | ||
38 | #include "libbb.h" | 41 | #include "libbb.h" |
39 | #include "common_bufsiz.h" | 42 | #include "common_bufsiz.h" |
40 | 43 | ||
@@ -95,9 +98,13 @@ int more_main(int argc UNUSED_PARAM, char **argv) | |||
95 | * is not a tty and turns into cat. This makes sense. */ | 98 | * is not a tty and turns into cat. This makes sense. */ |
96 | if (!isatty(STDOUT_FILENO)) | 99 | if (!isatty(STDOUT_FILENO)) |
97 | return bb_cat(argv); | 100 | return bb_cat(argv); |
101 | #if !ENABLE_PLATFORM_MINGW32 | ||
98 | tty = fopen_for_read(CURRENT_TTY); | 102 | tty = fopen_for_read(CURRENT_TTY); |
99 | if (!tty) | 103 | if (!tty) |
100 | return bb_cat(argv); | 104 | return bb_cat(argv); |
105 | #else | ||
106 | tty = stdin; | ||
107 | #endif | ||
101 | 108 | ||
102 | G.tty_fileno = fileno(tty); | 109 | G.tty_fileno = fileno(tty); |
103 | 110 | ||
@@ -151,8 +158,12 @@ int more_main(int argc UNUSED_PARAM, char **argv) | |||
151 | * to get input from the user. | 158 | * to get input from the user. |
152 | */ | 159 | */ |
153 | for (;;) { | 160 | for (;;) { |
161 | #if !ENABLE_PLATFORM_MINGW32 | ||
154 | fflush_all(); | 162 | fflush_all(); |
155 | input = getc(tty); | 163 | input = getc(tty); |
164 | #else | ||
165 | input = _getch(); | ||
166 | #endif | ||
156 | input = tolower(input); | 167 | input = tolower(input); |
157 | /* Erase the last message */ | 168 | /* Erase the last message */ |
158 | printf("\r%*s\r", len, ""); | 169 | printf("\r%*s\r", len, ""); |
@@ -166,6 +177,10 @@ int more_main(int argc UNUSED_PARAM, char **argv) | |||
166 | * commands, else we show help msg. */ | 177 | * commands, else we show help msg. */ |
167 | if (input == ' ' || input == '\n' || input == 'r') | 178 | if (input == ' ' || input == '\n' || input == 'r') |
168 | break; | 179 | break; |
180 | #if ENABLE_PLATFORM_MINGW32 | ||
181 | if (input == '\r') | ||
182 | break; | ||
183 | #endif | ||
169 | len = printf("(Enter:next line Space:next page Q:quit R:show the rest)"); | 184 | len = printf("(Enter:next line Space:next page Q:quit R:show the rest)"); |
170 | } | 185 | } |
171 | len = 0; | 186 | len = 0; |
@@ -200,6 +215,10 @@ int more_main(int argc UNUSED_PARAM, char **argv) | |||
200 | * will move us to a new line. */ | 215 | * will move us to a new line. */ |
201 | if (++lines >= G.terminal_height || input == '\n') | 216 | if (++lines >= G.terminal_height || input == '\n') |
202 | please_display_more_prompt = 1; | 217 | please_display_more_prompt = 1; |
218 | #if ENABLE_PLATFORM_MINGW32 | ||
219 | if (input == '\r') | ||
220 | please_display_more_prompt = 1; | ||
221 | #endif | ||
203 | len = 0; | 222 | len = 0; |
204 | } | 223 | } |
205 | if (c != '\n' && wrap) { | 224 | if (c != '\n' && wrap) { |
diff --git a/util-linux/rev.c b/util-linux/rev.c index 2bef9b9be..b0a0c01aa 100644 --- a/util-linux/rev.c +++ b/util-linux/rev.c | |||
@@ -31,7 +31,7 @@ | |||
31 | #endif | 31 | #endif |
32 | 32 | ||
33 | /* In-place invert */ | 33 | /* In-place invert */ |
34 | static void strrev(CHAR_T *s, int len) | 34 | static void bb_strrev(CHAR_T *s, int len) |
35 | { | 35 | { |
36 | int i; | 36 | int i; |
37 | 37 | ||
@@ -39,6 +39,10 @@ static void strrev(CHAR_T *s, int len) | |||
39 | len--; | 39 | len--; |
40 | if (len != 0 && s[len] == '\n') | 40 | if (len != 0 && s[len] == '\n') |
41 | len--; | 41 | len--; |
42 | #if ENABLE_PLATFORM_MINGW32 | ||
43 | if (len != 0 && s[len] == '\r') | ||
44 | len--; | ||
45 | #endif | ||
42 | } | 46 | } |
43 | 47 | ||
44 | for (i = 0; i < len; i++, len--) { | 48 | for (i = 0; i < len; i++, len--) { |
@@ -99,14 +103,14 @@ int rev_main(int argc UNUSED_PARAM, char **argv) | |||
99 | /* Convert to wchar_t (might error out!) */ | 103 | /* Convert to wchar_t (might error out!) */ |
100 | int len = mbstowcs(tmp, buf, bufsize); | 104 | int len = mbstowcs(tmp, buf, bufsize); |
101 | if (len >= 0) { | 105 | if (len >= 0) { |
102 | strrev(tmp, len); | 106 | bb_strrev(tmp, len); |
103 | /* Convert back to char */ | 107 | /* Convert back to char */ |
104 | wcstombs(buf, tmp, bufsize); | 108 | wcstombs(buf, tmp, bufsize); |
105 | } | 109 | } |
106 | free(tmp); | 110 | free(tmp); |
107 | } | 111 | } |
108 | #else | 112 | #else |
109 | strrev(buf, strlen(buf)); | 113 | bb_strrev(buf, strlen(buf)); |
110 | #endif | 114 | #endif |
111 | fputs(buf, stdout); | 115 | fputs(buf, stdout); |
112 | } | 116 | } |
diff --git a/win32/Kbuild b/win32/Kbuild new file mode 100644 index 000000000..ba361f1ca --- /dev/null +++ b/win32/Kbuild | |||
@@ -0,0 +1,25 @@ | |||
1 | # Makefile for busybox | ||
2 | # | ||
3 | # Licensed under the GPL v2, see the file LICENSE in this tarball. | ||
4 | |||
5 | lib-y:= | ||
6 | |||
7 | lib-$(CONFIG_PLATFORM_MINGW32) += env.o | ||
8 | lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o | ||
9 | lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o | ||
10 | lib-$(CONFIG_PLATFORM_MINGW32) += mingw.o | ||
11 | lib-$(CONFIG_PLATFORM_MINGW32) += process.o | ||
12 | lib-$(CONFIG_PLATFORM_MINGW32) += regex.o | ||
13 | lib-$(CONFIG_PLATFORM_MINGW32) += net.o | ||
14 | lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o | ||
15 | lib-$(CONFIG_PLATFORM_MINGW32) += poll.o | ||
16 | lib-$(CONFIG_PLATFORM_MINGW32) += select.o | ||
17 | lib-$(CONFIG_PLATFORM_MINGW32) += popen.o | ||
18 | lib-$(CONFIG_PLATFORM_MINGW32) += statfs.o | ||
19 | lib-$(CONFIG_PLATFORM_MINGW32) += mempcpy.o | ||
20 | lib-$(CONFIG_PLATFORM_MINGW32) += mntent.o | ||
21 | lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o | ||
22 | lib-$(CONFIG_PLATFORM_MINGW32) += system.o | ||
23 | lib-$(CONFIG_PLATFORM_MINGW32) += termios.o | ||
24 | lib-$(CONFIG_PLATFORM_MINGW32) += uname.o | ||
25 | lib-$(CONFIG_PLATFORM_MINGW32) += winansi.o | ||
diff --git a/win32/arpa/inet.h b/win32/arpa/inet.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/arpa/inet.h | |||
diff --git a/win32/env.c b/win32/env.c new file mode 100644 index 000000000..48b86c555 --- /dev/null +++ b/win32/env.c | |||
@@ -0,0 +1,189 @@ | |||
1 | #include "libbb.h" | ||
2 | |||
3 | char **copy_environ(const char *const *envp) | ||
4 | { | ||
5 | char **env; | ||
6 | int i = 0; | ||
7 | while (envp[i]) | ||
8 | i++; | ||
9 | env = xmalloc((i+1)*sizeof(*env)); | ||
10 | for (i = 0; envp[i]; i++) | ||
11 | env[i] = xstrdup(envp[i]); | ||
12 | env[i] = NULL; | ||
13 | return env; | ||
14 | } | ||
15 | |||
16 | void free_environ(char **env) | ||
17 | { | ||
18 | int i; | ||
19 | for (i = 0; env[i]; i++) | ||
20 | free(env[i]); | ||
21 | free(env); | ||
22 | } | ||
23 | |||
24 | static int lookup_env(char **env, const char *name, size_t nmln) | ||
25 | { | ||
26 | int i; | ||
27 | |||
28 | for (i = 0; env[i]; i++) { | ||
29 | if (0 == strncmp(env[i], name, nmln) | ||
30 | && '=' == env[i][nmln]) | ||
31 | /* matches */ | ||
32 | return i; | ||
33 | } | ||
34 | return -1; | ||
35 | } | ||
36 | |||
37 | #undef getenv | ||
38 | char *mingw_getenv(const char *name) | ||
39 | { | ||
40 | char *result = getenv(name); | ||
41 | if (!result && !strcmp(name, "TMPDIR")) { | ||
42 | /* on Windows it is TMP and TEMP */ | ||
43 | result = getenv("TMP"); | ||
44 | if (!result) | ||
45 | result = getenv("TEMP"); | ||
46 | } | ||
47 | return result; | ||
48 | } | ||
49 | |||
50 | int setenv(const char *name, const char *value, int replace) | ||
51 | { | ||
52 | int out; | ||
53 | size_t namelen, valuelen; | ||
54 | char *envstr; | ||
55 | |||
56 | if (!name || !value) return -1; | ||
57 | if (!replace) { | ||
58 | char *oldval = NULL; | ||
59 | oldval = getenv(name); | ||
60 | if (oldval) return 0; | ||
61 | } | ||
62 | |||
63 | namelen = strlen(name); | ||
64 | valuelen = strlen(value); | ||
65 | envstr = malloc((namelen + valuelen + 2)); | ||
66 | if (!envstr) return -1; | ||
67 | |||
68 | memcpy(envstr, name, namelen); | ||
69 | envstr[namelen] = '='; | ||
70 | memcpy(envstr + namelen + 1, value, valuelen); | ||
71 | envstr[namelen + valuelen + 1] = 0; | ||
72 | |||
73 | out = putenv(envstr); | ||
74 | /* putenv(3) makes the argument string part of the environment, | ||
75 | * and changing that string modifies the environment --- which | ||
76 | * means we do not own that storage anymore. Do not free | ||
77 | * envstr. | ||
78 | */ | ||
79 | |||
80 | return out; | ||
81 | } | ||
82 | |||
83 | /* | ||
84 | * If name contains '=', then sets the variable, otherwise it unsets it | ||
85 | */ | ||
86 | char **env_setenv(char **env, const char *name) | ||
87 | { | ||
88 | char *eq = strchrnul(name, '='); | ||
89 | int i = lookup_env(env, name, eq-name); | ||
90 | |||
91 | if (i < 0) { | ||
92 | if (*eq) { | ||
93 | for (i = 0; env[i]; i++) | ||
94 | ; | ||
95 | env = xrealloc(env, (i+2)*sizeof(*env)); | ||
96 | env[i] = xstrdup(name); | ||
97 | env[i+1] = NULL; | ||
98 | } | ||
99 | } | ||
100 | else { | ||
101 | free(env[i]); | ||
102 | if (*eq) | ||
103 | env[i] = xstrdup(name); | ||
104 | else { | ||
105 | for (; env[i]; i++) | ||
106 | env[i] = env[i+1]; | ||
107 | #if !ENABLE_SAFE_ENV | ||
108 | SetEnvironmentVariable(name, NULL); | ||
109 | #endif | ||
110 | } | ||
111 | } | ||
112 | return env; | ||
113 | } | ||
114 | |||
115 | #if ENABLE_SAFE_ENV | ||
116 | /* | ||
117 | * Removing an environment variable with WIN32 putenv requires an argument | ||
118 | * like "NAME="; glibc omits the '='. The implementations of unsetenv and | ||
119 | * clearenv allow for this. | ||
120 | * | ||
121 | * It isn't possible to create an environment variable with an empty value | ||
122 | * using WIN32 putenv. | ||
123 | */ | ||
124 | #undef putenv | ||
125 | int unsetenv(const char *env) | ||
126 | { | ||
127 | char *name; | ||
128 | int ret; | ||
129 | |||
130 | name = xmalloc(strlen(env)+2); | ||
131 | strcat(strcpy(name, env), "="); | ||
132 | ret = putenv(name); | ||
133 | free(name); | ||
134 | |||
135 | return ret; | ||
136 | } | ||
137 | |||
138 | int clearenv(void) | ||
139 | { | ||
140 | char *name, *s; | ||
141 | |||
142 | while ( environ && *environ ) { | ||
143 | if ( (s=strchr(*environ, '=')) != NULL ) { | ||
144 | name = xstrndup(*environ, s-*environ+1); | ||
145 | putenv(name); | ||
146 | free(name); | ||
147 | } | ||
148 | else { | ||
149 | return -1; | ||
150 | } | ||
151 | } | ||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | int mingw_putenv(const char *env) | ||
156 | { | ||
157 | char *s; | ||
158 | |||
159 | if ( (s=strchr(env, '=')) == NULL ) { | ||
160 | return unsetenv(env); | ||
161 | } | ||
162 | |||
163 | if ( s[1] != '\0' ) { | ||
164 | return putenv(env); | ||
165 | } | ||
166 | |||
167 | /* can't set empty value */ | ||
168 | return 0; | ||
169 | } | ||
170 | #else | ||
171 | void unsetenv(const char *env) | ||
172 | { | ||
173 | env_setenv(environ, env); | ||
174 | } | ||
175 | |||
176 | int clearenv(void) | ||
177 | { | ||
178 | char **env = environ; | ||
179 | if (!env) | ||
180 | return 0; | ||
181 | while (*env) { | ||
182 | free(*env); | ||
183 | env++; | ||
184 | } | ||
185 | free(env); | ||
186 | environ = NULL; | ||
187 | return 0; | ||
188 | } | ||
189 | #endif | ||
diff --git a/win32/fnmatch.c b/win32/fnmatch.c new file mode 100644 index 000000000..1f4ead5f9 --- /dev/null +++ b/win32/fnmatch.c | |||
@@ -0,0 +1,488 @@ | |||
1 | /* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc. | ||
2 | This file is part of the GNU C Library. | ||
3 | |||
4 | This library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Library General Public License as | ||
6 | published by the Free Software Foundation; either version 2 of the | ||
7 | License, or (at your option) any later version. | ||
8 | |||
9 | This library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Library General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public | ||
15 | License along with this library; see the file COPYING.LIB. If not, | ||
16 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
17 | Boston, MA 02111-1307, USA. */ | ||
18 | |||
19 | #if HAVE_CONFIG_H | ||
20 | # include <config.h> | ||
21 | #endif | ||
22 | |||
23 | /* Enable GNU extensions in fnmatch.h. */ | ||
24 | #ifndef _GNU_SOURCE | ||
25 | # define _GNU_SOURCE 1 | ||
26 | #endif | ||
27 | |||
28 | #include <errno.h> | ||
29 | #include <fnmatch.h> | ||
30 | #include <ctype.h> | ||
31 | |||
32 | #if HAVE_STRING_H || defined _LIBC | ||
33 | # include <string.h> | ||
34 | #else | ||
35 | # include <strings.h> | ||
36 | #endif | ||
37 | |||
38 | #if defined STDC_HEADERS || defined _LIBC | ||
39 | # include <stdlib.h> | ||
40 | #endif | ||
41 | |||
42 | /* For platform which support the ISO C amendement 1 functionality we | ||
43 | support user defined character classes. */ | ||
44 | #if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) | ||
45 | /* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */ | ||
46 | # include <wchar.h> | ||
47 | # include <wctype.h> | ||
48 | #endif | ||
49 | |||
50 | /* Comment out all this code if we are using the GNU C Library, and are not | ||
51 | actually compiling the library itself. This code is part of the GNU C | ||
52 | Library, but also included in many other GNU distributions. Compiling | ||
53 | and linking in this code is a waste when using the GNU C library | ||
54 | (especially if it is a shared library). Rather than having every GNU | ||
55 | program understand `configure --with-gnu-libc' and omit the object files, | ||
56 | it is simpler to just do this in the source for each such file. */ | ||
57 | |||
58 | #if defined _LIBC || !defined __GNU_LIBRARY__ | ||
59 | |||
60 | |||
61 | # if defined STDC_HEADERS || !defined isascii | ||
62 | # define ISASCII(c) 1 | ||
63 | # else | ||
64 | # define ISASCII(c) isascii(c) | ||
65 | # endif | ||
66 | |||
67 | # ifdef isblank | ||
68 | # define ISBLANK(c) (ISASCII (c) && isblank (c)) | ||
69 | # else | ||
70 | # define ISBLANK(c) ((c) == ' ' || (c) == '\t') | ||
71 | # endif | ||
72 | # ifdef isgraph | ||
73 | # define ISGRAPH(c) (ISASCII (c) && isgraph (c)) | ||
74 | # else | ||
75 | # define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c)) | ||
76 | # endif | ||
77 | |||
78 | # define ISPRINT(c) (ISASCII (c) && isprint (c)) | ||
79 | # define ISDIGIT(c) (ISASCII (c) && isdigit (c)) | ||
80 | # define ISALNUM(c) (ISASCII (c) && isalnum (c)) | ||
81 | # define ISALPHA(c) (ISASCII (c) && isalpha (c)) | ||
82 | # define ISCNTRL(c) (ISASCII (c) && iscntrl (c)) | ||
83 | # define ISLOWER(c) (ISASCII (c) && islower (c)) | ||
84 | # define ISPUNCT(c) (ISASCII (c) && ispunct (c)) | ||
85 | # define ISSPACE(c) (ISASCII (c) && isspace (c)) | ||
86 | # define ISUPPER(c) (ISASCII (c) && isupper (c)) | ||
87 | # define ISXDIGIT(c) (ISASCII (c) && isxdigit (c)) | ||
88 | |||
89 | # define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) | ||
90 | |||
91 | # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) | ||
92 | /* The GNU C library provides support for user-defined character classes | ||
93 | and the functions from ISO C amendement 1. */ | ||
94 | # ifdef CHARCLASS_NAME_MAX | ||
95 | # define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX | ||
96 | # else | ||
97 | /* This shouldn't happen but some implementation might still have this | ||
98 | problem. Use a reasonable default value. */ | ||
99 | # define CHAR_CLASS_MAX_LENGTH 256 | ||
100 | # endif | ||
101 | |||
102 | # ifdef _LIBC | ||
103 | # define IS_CHAR_CLASS(string) __wctype (string) | ||
104 | # else | ||
105 | # define IS_CHAR_CLASS(string) wctype (string) | ||
106 | # endif | ||
107 | # else | ||
108 | # define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */ | ||
109 | |||
110 | # define IS_CHAR_CLASS(string) \ | ||
111 | (STREQ (string, "alpha") || STREQ (string, "upper") \ | ||
112 | || STREQ (string, "lower") || STREQ (string, "digit") \ | ||
113 | || STREQ (string, "alnum") || STREQ (string, "xdigit") \ | ||
114 | || STREQ (string, "space") || STREQ (string, "print") \ | ||
115 | || STREQ (string, "punct") || STREQ (string, "graph") \ | ||
116 | || STREQ (string, "cntrl") || STREQ (string, "blank")) | ||
117 | # endif | ||
118 | |||
119 | /* Avoid depending on library functions or files | ||
120 | whose names are inconsistent. */ | ||
121 | |||
122 | # if !defined _LIBC && !defined getenv | ||
123 | extern char *getenv (); | ||
124 | # endif | ||
125 | |||
126 | # ifndef errno | ||
127 | extern int errno; | ||
128 | # endif | ||
129 | |||
130 | /* This function doesn't exist on most systems. */ | ||
131 | |||
132 | # if !defined HAVE___STRCHRNUL && !defined _LIBC | ||
133 | static char * | ||
134 | __strchrnul (s, c) | ||
135 | const char *s; | ||
136 | int c; | ||
137 | { | ||
138 | char *result = strchr (s, c); | ||
139 | if (result == NULL) | ||
140 | result = strchr (s, '\0'); | ||
141 | return result; | ||
142 | } | ||
143 | # endif | ||
144 | |||
145 | # ifndef internal_function | ||
146 | /* Inside GNU libc we mark some function in a special way. In other | ||
147 | environments simply ignore the marking. */ | ||
148 | # define internal_function | ||
149 | # endif | ||
150 | |||
151 | /* Match STRING against the filename pattern PATTERN, returning zero if | ||
152 | it matches, nonzero if not. */ | ||
153 | static int internal_fnmatch __P ((const char *pattern, const char *string, | ||
154 | int no_leading_period, int flags)) | ||
155 | internal_function; | ||
156 | static int | ||
157 | internal_function | ||
158 | internal_fnmatch (pattern, string, no_leading_period, flags) | ||
159 | const char *pattern; | ||
160 | const char *string; | ||
161 | int no_leading_period; | ||
162 | int flags; | ||
163 | { | ||
164 | register const char *p = pattern, *n = string; | ||
165 | register unsigned char c; | ||
166 | |||
167 | /* Note that this evaluates C many times. */ | ||
168 | # ifdef _LIBC | ||
169 | # define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c)) | ||
170 | # else | ||
171 | # define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c)) | ||
172 | # endif | ||
173 | |||
174 | while ((c = *p++) != '\0') | ||
175 | { | ||
176 | c = FOLD (c); | ||
177 | |||
178 | switch (c) | ||
179 | { | ||
180 | case '?': | ||
181 | if (*n == '\0') | ||
182 | return FNM_NOMATCH; | ||
183 | else if (*n == '/' && (flags & FNM_FILE_NAME)) | ||
184 | return FNM_NOMATCH; | ||
185 | else if (*n == '.' && no_leading_period | ||
186 | && (n == string | ||
187 | || (n[-1] == '/' && (flags & FNM_FILE_NAME)))) | ||
188 | return FNM_NOMATCH; | ||
189 | break; | ||
190 | |||
191 | case '\\': | ||
192 | if (!(flags & FNM_NOESCAPE)) | ||
193 | { | ||
194 | c = *p++; | ||
195 | if (c == '\0') | ||
196 | /* Trailing \ loses. */ | ||
197 | return FNM_NOMATCH; | ||
198 | c = FOLD (c); | ||
199 | } | ||
200 | if (FOLD ((unsigned char) *n) != c) | ||
201 | return FNM_NOMATCH; | ||
202 | break; | ||
203 | |||
204 | case '*': | ||
205 | if (*n == '.' && no_leading_period | ||
206 | && (n == string | ||
207 | || (n[-1] == '/' && (flags & FNM_FILE_NAME)))) | ||
208 | return FNM_NOMATCH; | ||
209 | |||
210 | for (c = *p++; c == '?' || c == '*'; c = *p++) | ||
211 | { | ||
212 | if (*n == '/' && (flags & FNM_FILE_NAME)) | ||
213 | /* A slash does not match a wildcard under FNM_FILE_NAME. */ | ||
214 | return FNM_NOMATCH; | ||
215 | else if (c == '?') | ||
216 | { | ||
217 | /* A ? needs to match one character. */ | ||
218 | if (*n == '\0') | ||
219 | /* There isn't another character; no match. */ | ||
220 | return FNM_NOMATCH; | ||
221 | else | ||
222 | /* One character of the string is consumed in matching | ||
223 | this ? wildcard, so *??? won't match if there are | ||
224 | less than three characters. */ | ||
225 | ++n; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | if (c == '\0') | ||
230 | /* The wildcard(s) is/are the last element of the pattern. | ||
231 | If the name is a file name and contains another slash | ||
232 | this does mean it cannot match. */ | ||
233 | return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL | ||
234 | ? FNM_NOMATCH : 0); | ||
235 | else | ||
236 | { | ||
237 | const char *endp; | ||
238 | |||
239 | endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0'); | ||
240 | |||
241 | if (c == '[') | ||
242 | { | ||
243 | int flags2 = ((flags & FNM_FILE_NAME) | ||
244 | ? flags : (flags & ~FNM_PERIOD)); | ||
245 | |||
246 | for (--p; n < endp; ++n) | ||
247 | if (internal_fnmatch (p, n, | ||
248 | (no_leading_period | ||
249 | && (n == string | ||
250 | || (n[-1] == '/' | ||
251 | && (flags | ||
252 | & FNM_FILE_NAME)))), | ||
253 | flags2) | ||
254 | == 0) | ||
255 | return 0; | ||
256 | } | ||
257 | else if (c == '/' && (flags & FNM_FILE_NAME)) | ||
258 | { | ||
259 | while (*n != '\0' && *n != '/') | ||
260 | ++n; | ||
261 | if (*n == '/' | ||
262 | && (internal_fnmatch (p, n + 1, flags & FNM_PERIOD, | ||
263 | flags) == 0)) | ||
264 | return 0; | ||
265 | } | ||
266 | else | ||
267 | { | ||
268 | int flags2 = ((flags & FNM_FILE_NAME) | ||
269 | ? flags : (flags & ~FNM_PERIOD)); | ||
270 | |||
271 | if (c == '\\' && !(flags & FNM_NOESCAPE)) | ||
272 | c = *p; | ||
273 | c = FOLD (c); | ||
274 | for (--p; n < endp; ++n) | ||
275 | if (FOLD ((unsigned char) *n) == c | ||
276 | && (internal_fnmatch (p, n, | ||
277 | (no_leading_period | ||
278 | && (n == string | ||
279 | || (n[-1] == '/' | ||
280 | && (flags | ||
281 | & FNM_FILE_NAME)))), | ||
282 | flags2) == 0)) | ||
283 | return 0; | ||
284 | } | ||
285 | } | ||
286 | |||
287 | /* If we come here no match is possible with the wildcard. */ | ||
288 | return FNM_NOMATCH; | ||
289 | |||
290 | case '[': | ||
291 | { | ||
292 | /* Nonzero if the sense of the character class is inverted. */ | ||
293 | static int posixly_correct; | ||
294 | register int not; | ||
295 | char cold; | ||
296 | |||
297 | if (posixly_correct == 0) | ||
298 | posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1; | ||
299 | |||
300 | if (*n == '\0') | ||
301 | return FNM_NOMATCH; | ||
302 | |||
303 | if (*n == '.' && no_leading_period && (n == string | ||
304 | || (n[-1] == '/' | ||
305 | && (flags | ||
306 | & FNM_FILE_NAME)))) | ||
307 | return FNM_NOMATCH; | ||
308 | |||
309 | if (*n == '/' && (flags & FNM_FILE_NAME)) | ||
310 | /* `/' cannot be matched. */ | ||
311 | return FNM_NOMATCH; | ||
312 | |||
313 | not = (*p == '!' || (posixly_correct < 0 && *p == '^')); | ||
314 | if (not) | ||
315 | ++p; | ||
316 | |||
317 | c = *p++; | ||
318 | for (;;) | ||
319 | { | ||
320 | unsigned char fn = FOLD ((unsigned char) *n); | ||
321 | |||
322 | if (!(flags & FNM_NOESCAPE) && c == '\\') | ||
323 | { | ||
324 | if (*p == '\0') | ||
325 | return FNM_NOMATCH; | ||
326 | c = FOLD ((unsigned char) *p); | ||
327 | ++p; | ||
328 | |||
329 | if (c == fn) | ||
330 | goto matched; | ||
331 | } | ||
332 | else if (c == '[' && *p == ':') | ||
333 | { | ||
334 | /* Leave room for the null. */ | ||
335 | char str[CHAR_CLASS_MAX_LENGTH + 1]; | ||
336 | size_t c1 = 0; | ||
337 | # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) | ||
338 | wctype_t wt; | ||
339 | # endif | ||
340 | const char *startp = p; | ||
341 | |||
342 | for (;;) | ||
343 | { | ||
344 | if (c1 == CHAR_CLASS_MAX_LENGTH) | ||
345 | /* The name is too long and therefore the pattern | ||
346 | is ill-formed. */ | ||
347 | return FNM_NOMATCH; | ||
348 | |||
349 | c = *++p; | ||
350 | if (c == ':' && p[1] == ']') | ||
351 | { | ||
352 | p += 2; | ||
353 | break; | ||
354 | } | ||
355 | if (c < 'a' || c >= 'z') | ||
356 | { | ||
357 | /* This cannot possibly be a character class name. | ||
358 | Match it as a normal range. */ | ||
359 | p = startp; | ||
360 | c = '['; | ||
361 | goto normal_bracket; | ||
362 | } | ||
363 | str[c1++] = c; | ||
364 | } | ||
365 | str[c1] = '\0'; | ||
366 | |||
367 | # if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H) | ||
368 | wt = IS_CHAR_CLASS (str); | ||
369 | if (wt == 0) | ||
370 | /* Invalid character class name. */ | ||
371 | return FNM_NOMATCH; | ||
372 | |||
373 | if (__iswctype (__btowc ((unsigned char) *n), wt)) | ||
374 | goto matched; | ||
375 | # else | ||
376 | if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n)) | ||
377 | || (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n)) | ||
378 | || (STREQ (str, "blank") && ISBLANK ((unsigned char) *n)) | ||
379 | || (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n)) | ||
380 | || (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n)) | ||
381 | || (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n)) | ||
382 | || (STREQ (str, "lower") && ISLOWER ((unsigned char) *n)) | ||
383 | || (STREQ (str, "print") && ISPRINT ((unsigned char) *n)) | ||
384 | || (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n)) | ||
385 | || (STREQ (str, "space") && ISSPACE ((unsigned char) *n)) | ||
386 | || (STREQ (str, "upper") && ISUPPER ((unsigned char) *n)) | ||
387 | || (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n))) | ||
388 | goto matched; | ||
389 | # endif | ||
390 | } | ||
391 | else if (c == '\0') | ||
392 | /* [ (unterminated) loses. */ | ||
393 | return FNM_NOMATCH; | ||
394 | else | ||
395 | { | ||
396 | normal_bracket: | ||
397 | if (FOLD (c) == fn) | ||
398 | goto matched; | ||
399 | |||
400 | cold = c; | ||
401 | c = *p++; | ||
402 | |||
403 | if (c == '-' && *p != ']') | ||
404 | { | ||
405 | /* It is a range. */ | ||
406 | unsigned char cend = *p++; | ||
407 | if (!(flags & FNM_NOESCAPE) && cend == '\\') | ||
408 | cend = *p++; | ||
409 | if (cend == '\0') | ||
410 | return FNM_NOMATCH; | ||
411 | |||
412 | if (cold <= fn && fn <= FOLD (cend)) | ||
413 | goto matched; | ||
414 | |||
415 | c = *p++; | ||
416 | } | ||
417 | } | ||
418 | |||
419 | if (c == ']') | ||
420 | break; | ||
421 | } | ||
422 | |||
423 | if (!not) | ||
424 | return FNM_NOMATCH; | ||
425 | break; | ||
426 | |||
427 | matched: | ||
428 | /* Skip the rest of the [...] that already matched. */ | ||
429 | while (c != ']') | ||
430 | { | ||
431 | if (c == '\0') | ||
432 | /* [... (unterminated) loses. */ | ||
433 | return FNM_NOMATCH; | ||
434 | |||
435 | c = *p++; | ||
436 | if (!(flags & FNM_NOESCAPE) && c == '\\') | ||
437 | { | ||
438 | if (*p == '\0') | ||
439 | return FNM_NOMATCH; | ||
440 | /* XXX 1003.2d11 is unclear if this is right. */ | ||
441 | ++p; | ||
442 | } | ||
443 | else if (c == '[' && *p == ':') | ||
444 | { | ||
445 | do | ||
446 | if (*++p == '\0') | ||
447 | return FNM_NOMATCH; | ||
448 | while (*p != ':' || p[1] == ']'); | ||
449 | p += 2; | ||
450 | c = *p; | ||
451 | } | ||
452 | } | ||
453 | if (not) | ||
454 | return FNM_NOMATCH; | ||
455 | } | ||
456 | break; | ||
457 | |||
458 | default: | ||
459 | if (c != FOLD ((unsigned char) *n)) | ||
460 | return FNM_NOMATCH; | ||
461 | } | ||
462 | |||
463 | ++n; | ||
464 | } | ||
465 | |||
466 | if (*n == '\0') | ||
467 | return 0; | ||
468 | |||
469 | if ((flags & FNM_LEADING_DIR) && *n == '/') | ||
470 | /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */ | ||
471 | return 0; | ||
472 | |||
473 | return FNM_NOMATCH; | ||
474 | |||
475 | # undef FOLD | ||
476 | } | ||
477 | |||
478 | |||
479 | int | ||
480 | fnmatch (pattern, string, flags) | ||
481 | const char *pattern; | ||
482 | const char *string; | ||
483 | int flags; | ||
484 | { | ||
485 | return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags); | ||
486 | } | ||
487 | |||
488 | #endif /* _LIBC or not __GNU_LIBRARY__. */ | ||
diff --git a/win32/fnmatch.h b/win32/fnmatch.h new file mode 100644 index 000000000..cc3ec3794 --- /dev/null +++ b/win32/fnmatch.h | |||
@@ -0,0 +1,84 @@ | |||
1 | /* Copyright (C) 1991, 92, 93, 96, 97, 98, 99 Free Software Foundation, Inc. | ||
2 | This file is part of the GNU C Library. | ||
3 | |||
4 | The GNU C Library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Library General Public License as | ||
6 | published by the Free Software Foundation; either version 2 of the | ||
7 | License, or (at your option) any later version. | ||
8 | |||
9 | The GNU C Library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Library General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Library General Public | ||
15 | License along with the GNU C Library; see the file COPYING.LIB. If not, | ||
16 | write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, | ||
17 | Boston, MA 02111-1307, USA. */ | ||
18 | |||
19 | #ifndef _FNMATCH_H | ||
20 | #define _FNMATCH_H 1 | ||
21 | |||
22 | #ifdef __cplusplus | ||
23 | extern "C" { | ||
24 | #endif | ||
25 | |||
26 | #if defined __cplusplus || (defined __STDC__ && __STDC__) || defined WINDOWS32 | ||
27 | # if !defined __GLIBC__ || !defined __P | ||
28 | # undef __P | ||
29 | # define __P(protos) protos | ||
30 | # endif | ||
31 | #else /* Not C++ or ANSI C. */ | ||
32 | # undef __P | ||
33 | # define __P(protos) () | ||
34 | /* We can get away without defining `const' here only because in this file | ||
35 | it is used only inside the prototype for `fnmatch', which is elided in | ||
36 | non-ANSI C where `const' is problematical. */ | ||
37 | #endif /* C++ or ANSI C. */ | ||
38 | |||
39 | #ifndef const | ||
40 | # if (defined __STDC__ && __STDC__) || defined __cplusplus | ||
41 | # define __const const | ||
42 | # else | ||
43 | # define __const | ||
44 | # endif | ||
45 | #endif | ||
46 | |||
47 | /* We #undef these before defining them because some losing systems | ||
48 | (HP-UX A.08.07 for example) define these in <unistd.h>. */ | ||
49 | #undef FNM_PATHNAME | ||
50 | #undef FNM_NOESCAPE | ||
51 | #undef FNM_PERIOD | ||
52 | |||
53 | /* Bits set in the FLAGS argument to `fnmatch'. */ | ||
54 | #define FNM_PATHNAME (1 << 0) /* No wildcard can ever match `/'. */ | ||
55 | #define FNM_NOESCAPE (1 << 1) /* Backslashes don't quote special chars. */ | ||
56 | #define FNM_PERIOD (1 << 2) /* Leading `.' is matched only explicitly. */ | ||
57 | |||
58 | #if !defined _POSIX_C_SOURCE || _POSIX_C_SOURCE < 2 || defined _GNU_SOURCE | ||
59 | # define FNM_FILE_NAME FNM_PATHNAME /* Preferred GNU name. */ | ||
60 | # define FNM_LEADING_DIR (1 << 3) /* Ignore `/...' after a match. */ | ||
61 | # define FNM_CASEFOLD (1 << 4) /* Compare without regard to case. */ | ||
62 | #endif | ||
63 | |||
64 | /* Value returned by `fnmatch' if STRING does not match PATTERN. */ | ||
65 | #define FNM_NOMATCH 1 | ||
66 | |||
67 | /* This value is returned if the implementation does not support | ||
68 | `fnmatch'. Since this is not the case here it will never be | ||
69 | returned but the conformance test suites still require the symbol | ||
70 | to be defined. */ | ||
71 | #ifdef _XOPEN_SOURCE | ||
72 | # define FNM_NOSYS (-1) | ||
73 | #endif | ||
74 | |||
75 | /* Match NAME against the filename pattern PATTERN, | ||
76 | returning zero if it matches, FNM_NOMATCH if not. */ | ||
77 | extern int fnmatch __P ((__const char *__pattern, __const char *__name, | ||
78 | int __flags)); | ||
79 | |||
80 | #ifdef __cplusplus | ||
81 | } | ||
82 | #endif | ||
83 | |||
84 | #endif /* fnmatch.h */ | ||
diff --git a/win32/grp.h b/win32/grp.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/grp.h | |||
diff --git a/win32/inet_pton.c b/win32/inet_pton.c new file mode 100644 index 000000000..ec87abec5 --- /dev/null +++ b/win32/inet_pton.c | |||
@@ -0,0 +1,255 @@ | |||
1 | /* inet_pton.c -- convert IPv4 and IPv6 addresses from text to binary form | ||
2 | |||
3 | Copyright (C) 2006, 2008-2015 Free Software Foundation, Inc. | ||
4 | |||
5 | This program is free software: you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 3 of the License, or | ||
8 | (at your option) any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License | ||
16 | along with this program. If not, see <http://www.gnu.org/licenses/>. */ | ||
17 | |||
18 | /* | ||
19 | * Copyright (c) 1996,1999 by Internet Software Consortium. | ||
20 | * | ||
21 | * Permission to use, copy, modify, and distribute this software for any | ||
22 | * purpose with or without fee is hereby granted, provided that the above | ||
23 | * copyright notice and this permission notice appear in all copies. | ||
24 | * | ||
25 | * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS | ||
26 | * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES | ||
27 | * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE | ||
28 | * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL | ||
29 | * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR | ||
30 | * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS | ||
31 | * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS | ||
32 | * SOFTWARE. | ||
33 | */ | ||
34 | |||
35 | #include "libbb.h" | ||
36 | |||
37 | /* Specification. */ | ||
38 | #include <arpa/inet.h> | ||
39 | |||
40 | # include <ctype.h> | ||
41 | # include <string.h> | ||
42 | # include <errno.h> | ||
43 | |||
44 | # define NS_INADDRSZ 4 | ||
45 | # define NS_IN6ADDRSZ 16 | ||
46 | # define NS_INT16SZ 2 | ||
47 | # define HAVE_IPV6 1 | ||
48 | |||
49 | /* | ||
50 | * WARNING: Don't even consider trying to compile this on a system where | ||
51 | * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX. | ||
52 | */ | ||
53 | |||
54 | static int inet_pton4 (const char *src, unsigned char *dst); | ||
55 | # if HAVE_IPV6 | ||
56 | static int inet_pton6 (const char *src, unsigned char *dst); | ||
57 | # endif | ||
58 | |||
59 | /* int | ||
60 | * inet_pton(af, src, dst) | ||
61 | * convert from presentation format (which usually means ASCII printable) | ||
62 | * to network format (which is usually some kind of binary format). | ||
63 | * return: | ||
64 | * 1 if the address was valid for the specified address family | ||
65 | * 0 if the address wasn't valid ('dst' is untouched in this case) | ||
66 | * -1 if some other error occurred ('dst' is untouched in this case, too) | ||
67 | * author: | ||
68 | * Paul Vixie, 1996. | ||
69 | */ | ||
70 | int | ||
71 | inet_pton (int af, const char *restrict src, void *restrict dst) | ||
72 | { | ||
73 | switch (af) | ||
74 | { | ||
75 | case AF_INET: | ||
76 | return (inet_pton4 (src, dst)); | ||
77 | |||
78 | # if HAVE_IPV6 | ||
79 | case AF_INET6: | ||
80 | return (inet_pton6 (src, dst)); | ||
81 | # endif | ||
82 | |||
83 | default: | ||
84 | errno = EAFNOSUPPORT; | ||
85 | return (-1); | ||
86 | } | ||
87 | /* NOTREACHED */ | ||
88 | } | ||
89 | |||
90 | /* int | ||
91 | * inet_pton4(src, dst) | ||
92 | * like inet_aton() but without all the hexadecimal, octal (with the | ||
93 | * exception of 0) and shorthand. | ||
94 | * return: | ||
95 | * 1 if 'src' is a valid dotted quad, else 0. | ||
96 | * notice: | ||
97 | * does not touch 'dst' unless it's returning 1. | ||
98 | * author: | ||
99 | * Paul Vixie, 1996. | ||
100 | */ | ||
101 | static int | ||
102 | inet_pton4 (const char *restrict src, unsigned char *restrict dst) | ||
103 | { | ||
104 | int saw_digit, octets, ch; | ||
105 | unsigned char tmp[NS_INADDRSZ], *tp; | ||
106 | |||
107 | saw_digit = 0; | ||
108 | octets = 0; | ||
109 | *(tp = tmp) = 0; | ||
110 | while ((ch = *src++) != '\0') | ||
111 | { | ||
112 | |||
113 | if (ch >= '0' && ch <= '9') | ||
114 | { | ||
115 | unsigned new = *tp * 10 + (ch - '0'); | ||
116 | |||
117 | if (saw_digit && *tp == 0) | ||
118 | return (0); | ||
119 | if (new > 255) | ||
120 | return (0); | ||
121 | *tp = new; | ||
122 | if (!saw_digit) | ||
123 | { | ||
124 | if (++octets > 4) | ||
125 | return (0); | ||
126 | saw_digit = 1; | ||
127 | } | ||
128 | } | ||
129 | else if (ch == '.' && saw_digit) | ||
130 | { | ||
131 | if (octets == 4) | ||
132 | return (0); | ||
133 | *++tp = 0; | ||
134 | saw_digit = 0; | ||
135 | } | ||
136 | else | ||
137 | return (0); | ||
138 | } | ||
139 | if (octets < 4) | ||
140 | return (0); | ||
141 | memcpy (dst, tmp, NS_INADDRSZ); | ||
142 | return (1); | ||
143 | } | ||
144 | |||
145 | # if HAVE_IPV6 | ||
146 | |||
147 | /* int | ||
148 | * inet_pton6(src, dst) | ||
149 | * convert presentation level address to network order binary form. | ||
150 | * return: | ||
151 | * 1 if 'src' is a valid [RFC1884 2.2] address, else 0. | ||
152 | * notice: | ||
153 | * (1) does not touch 'dst' unless it's returning 1. | ||
154 | * (2) :: in a full address is silently ignored. | ||
155 | * credit: | ||
156 | * inspired by Mark Andrews. | ||
157 | * author: | ||
158 | * Paul Vixie, 1996. | ||
159 | */ | ||
160 | static int | ||
161 | inet_pton6 (const char *restrict src, unsigned char *restrict dst) | ||
162 | { | ||
163 | static const char xdigits[] = "0123456789abcdef"; | ||
164 | unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp; | ||
165 | const char *curtok; | ||
166 | int ch, saw_xdigit; | ||
167 | unsigned val; | ||
168 | |||
169 | tp = memset (tmp, '\0', NS_IN6ADDRSZ); | ||
170 | endp = tp + NS_IN6ADDRSZ; | ||
171 | colonp = NULL; | ||
172 | /* Leading :: requires some special handling. */ | ||
173 | if (*src == ':') | ||
174 | if (*++src != ':') | ||
175 | return (0); | ||
176 | curtok = src; | ||
177 | saw_xdigit = 0; | ||
178 | val = 0; | ||
179 | while ((ch = tolower (*src++)) != '\0') | ||
180 | { | ||
181 | const char *pch; | ||
182 | |||
183 | pch = strchr (xdigits, ch); | ||
184 | if (pch != NULL) | ||
185 | { | ||
186 | val <<= 4; | ||
187 | val |= (pch - xdigits); | ||
188 | if (val > 0xffff) | ||
189 | return (0); | ||
190 | saw_xdigit = 1; | ||
191 | continue; | ||
192 | } | ||
193 | if (ch == ':') | ||
194 | { | ||
195 | curtok = src; | ||
196 | if (!saw_xdigit) | ||
197 | { | ||
198 | if (colonp) | ||
199 | return (0); | ||
200 | colonp = tp; | ||
201 | continue; | ||
202 | } | ||
203 | else if (*src == '\0') | ||
204 | { | ||
205 | return (0); | ||
206 | } | ||
207 | if (tp + NS_INT16SZ > endp) | ||
208 | return (0); | ||
209 | *tp++ = (u_char) (val >> 8) & 0xff; | ||
210 | *tp++ = (u_char) val & 0xff; | ||
211 | saw_xdigit = 0; | ||
212 | val = 0; | ||
213 | continue; | ||
214 | } | ||
215 | if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) && | ||
216 | inet_pton4 (curtok, tp) > 0) | ||
217 | { | ||
218 | tp += NS_INADDRSZ; | ||
219 | saw_xdigit = 0; | ||
220 | break; /* '\0' was seen by inet_pton4(). */ | ||
221 | } | ||
222 | return (0); | ||
223 | } | ||
224 | if (saw_xdigit) | ||
225 | { | ||
226 | if (tp + NS_INT16SZ > endp) | ||
227 | return (0); | ||
228 | *tp++ = (u_char) (val >> 8) & 0xff; | ||
229 | *tp++ = (u_char) val & 0xff; | ||
230 | } | ||
231 | if (colonp != NULL) | ||
232 | { | ||
233 | /* | ||
234 | * Since some memmove()'s erroneously fail to handle | ||
235 | * overlapping regions, we'll do the shift by hand. | ||
236 | */ | ||
237 | const int n = tp - colonp; | ||
238 | int i; | ||
239 | |||
240 | if (tp == endp) | ||
241 | return (0); | ||
242 | for (i = 1; i <= n; i++) | ||
243 | { | ||
244 | endp[-i] = colonp[n - i]; | ||
245 | colonp[n - i] = 0; | ||
246 | } | ||
247 | tp = endp; | ||
248 | } | ||
249 | if (tp != endp) | ||
250 | return (0); | ||
251 | memcpy (dst, tmp, NS_IN6ADDRSZ); | ||
252 | return (1); | ||
253 | } | ||
254 | |||
255 | # endif | ||
diff --git a/win32/ioctl.c b/win32/ioctl.c new file mode 100644 index 000000000..73ceeedec --- /dev/null +++ b/win32/ioctl.c | |||
@@ -0,0 +1,24 @@ | |||
1 | #include "libbb.h" | ||
2 | |||
3 | int ioctl(int fd UNUSED_PARAM, int code, ...) | ||
4 | { | ||
5 | va_list ap; | ||
6 | void *arg; | ||
7 | int ret = -1; | ||
8 | |||
9 | va_start(ap, code); | ||
10 | |||
11 | switch (code) { | ||
12 | case TIOCGWINSZ: | ||
13 | arg = va_arg(ap, void *); | ||
14 | ret = winansi_get_terminal_width_height((struct winsize *)arg); | ||
15 | break; | ||
16 | default: | ||
17 | ret = -1; | ||
18 | errno = EINVAL; | ||
19 | break; | ||
20 | } | ||
21 | |||
22 | va_end(ap); | ||
23 | return ret; | ||
24 | } | ||
diff --git a/win32/mempcpy.c b/win32/mempcpy.c new file mode 100644 index 000000000..732a6f4b2 --- /dev/null +++ b/win32/mempcpy.c | |||
@@ -0,0 +1,27 @@ | |||
1 | /* Copy memory area and return pointer after last written byte. | ||
2 | Copyright (C) 2003, 2007, 2009-2014 Free Software Foundation, Inc. | ||
3 | |||
4 | This program is free software; you can redistribute it and/or modify | ||
5 | it under the terms of the GNU General Public License as published by | ||
6 | the Free Software Foundation; either version 2, or (at your option) | ||
7 | any later version. | ||
8 | |||
9 | This program is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | GNU General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU General Public License | ||
15 | along with this program; if not, see <http://www.gnu.org/licenses/>. */ | ||
16 | |||
17 | #include "libbb.h" | ||
18 | /* Specification. */ | ||
19 | #include <string.h> | ||
20 | |||
21 | /* Copy N bytes of SRC to DEST, return pointer to bytes after the | ||
22 | last written byte. */ | ||
23 | void * | ||
24 | mempcpy (void *dest, const void *src, size_t n) | ||
25 | { | ||
26 | return (char *) memcpy (dest, src, n) + n; | ||
27 | } | ||
diff --git a/win32/mingw.c b/win32/mingw.c new file mode 100644 index 000000000..1170cd9d5 --- /dev/null +++ b/win32/mingw.c | |||
@@ -0,0 +1,1064 @@ | |||
1 | #include "libbb.h" | ||
2 | #include <userenv.h> | ||
3 | |||
4 | #if defined(__MINGW64_VERSION_MAJOR) | ||
5 | #if ENABLE_GLOBBING | ||
6 | int _dowildcard = -1; | ||
7 | #else | ||
8 | int _dowildcard = 0; | ||
9 | #endif | ||
10 | |||
11 | #undef _fmode | ||
12 | int _fmode = _O_BINARY; | ||
13 | #endif | ||
14 | |||
15 | #if !defined(__MINGW64_VERSION_MAJOR) | ||
16 | #if ENABLE_GLOBBING | ||
17 | int _CRT_glob = 1; | ||
18 | #else | ||
19 | int _CRT_glob = 0; | ||
20 | #endif | ||
21 | |||
22 | unsigned int _CRT_fmode = _O_BINARY; | ||
23 | #endif | ||
24 | |||
25 | smallint bb_got_signal; | ||
26 | |||
27 | int err_win_to_posix(DWORD winerr) | ||
28 | { | ||
29 | int error = ENOSYS; | ||
30 | switch(winerr) { | ||
31 | case ERROR_ACCESS_DENIED: error = EACCES; break; | ||
32 | case ERROR_ACCOUNT_DISABLED: error = EACCES; break; | ||
33 | case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break; | ||
34 | case ERROR_ALREADY_ASSIGNED: error = EBUSY; break; | ||
35 | case ERROR_ALREADY_EXISTS: error = EEXIST; break; | ||
36 | case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break; | ||
37 | case ERROR_BAD_COMMAND: error = EIO; break; | ||
38 | case ERROR_BAD_DEVICE: error = ENODEV; break; | ||
39 | case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break; | ||
40 | case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break; | ||
41 | case ERROR_BAD_FORMAT: error = ENOEXEC; break; | ||
42 | case ERROR_BAD_LENGTH: error = EINVAL; break; | ||
43 | case ERROR_BAD_PATHNAME: error = ENOENT; break; | ||
44 | case ERROR_BAD_PIPE: error = EPIPE; break; | ||
45 | case ERROR_BAD_UNIT: error = ENODEV; break; | ||
46 | case ERROR_BAD_USERNAME: error = EINVAL; break; | ||
47 | case ERROR_BROKEN_PIPE: error = EPIPE; break; | ||
48 | case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break; | ||
49 | case ERROR_BUSY: error = EBUSY; break; | ||
50 | case ERROR_BUSY_DRIVE: error = EBUSY; break; | ||
51 | case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break; | ||
52 | case ERROR_CANNOT_MAKE: error = EACCES; break; | ||
53 | case ERROR_CANTOPEN: error = EIO; break; | ||
54 | case ERROR_CANTREAD: error = EIO; break; | ||
55 | case ERROR_CANTWRITE: error = EIO; break; | ||
56 | case ERROR_CRC: error = EIO; break; | ||
57 | case ERROR_CURRENT_DIRECTORY: error = EACCES; break; | ||
58 | case ERROR_DEVICE_IN_USE: error = EBUSY; break; | ||
59 | case ERROR_DEV_NOT_EXIST: error = ENODEV; break; | ||
60 | case ERROR_DIRECTORY: error = EINVAL; break; | ||
61 | case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break; | ||
62 | case ERROR_DISK_CHANGE: error = EIO; break; | ||
63 | case ERROR_DISK_FULL: error = ENOSPC; break; | ||
64 | case ERROR_DRIVE_LOCKED: error = EBUSY; break; | ||
65 | case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break; | ||
66 | case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break; | ||
67 | case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break; | ||
68 | case ERROR_FILE_EXISTS: error = EEXIST; break; | ||
69 | case ERROR_FILE_INVALID: error = ENODEV; break; | ||
70 | case ERROR_FILE_NOT_FOUND: error = ENOENT; break; | ||
71 | case ERROR_GEN_FAILURE: error = EIO; break; | ||
72 | case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break; | ||
73 | case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break; | ||
74 | case ERROR_INVALID_ACCESS: error = EACCES; break; | ||
75 | case ERROR_INVALID_ADDRESS: error = EFAULT; break; | ||
76 | case ERROR_INVALID_BLOCK: error = EFAULT; break; | ||
77 | case ERROR_INVALID_DATA: error = EINVAL; break; | ||
78 | case ERROR_INVALID_DRIVE: error = ENODEV; break; | ||
79 | case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break; | ||
80 | case ERROR_INVALID_FLAGS: error = EINVAL; break; | ||
81 | case ERROR_INVALID_FUNCTION: error = ENOSYS; break; | ||
82 | case ERROR_INVALID_HANDLE: error = EBADF; break; | ||
83 | case ERROR_INVALID_LOGON_HOURS: error = EACCES; break; | ||
84 | case ERROR_INVALID_NAME: error = EINVAL; break; | ||
85 | case ERROR_INVALID_OWNER: error = EINVAL; break; | ||
86 | case ERROR_INVALID_PARAMETER: error = EINVAL; break; | ||
87 | case ERROR_INVALID_PASSWORD: error = EPERM; break; | ||
88 | case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break; | ||
89 | case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break; | ||
90 | case ERROR_INVALID_TARGET_HANDLE: error = EIO; break; | ||
91 | case ERROR_INVALID_WORKSTATION: error = EACCES; break; | ||
92 | case ERROR_IO_DEVICE: error = EIO; break; | ||
93 | case ERROR_IO_INCOMPLETE: error = EINTR; break; | ||
94 | case ERROR_LOCKED: error = EBUSY; break; | ||
95 | case ERROR_LOCK_VIOLATION: error = EACCES; break; | ||
96 | case ERROR_LOGON_FAILURE: error = EACCES; break; | ||
97 | case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break; | ||
98 | case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break; | ||
99 | case ERROR_MORE_DATA: error = EPIPE; break; | ||
100 | case ERROR_NEGATIVE_SEEK: error = ESPIPE; break; | ||
101 | case ERROR_NOACCESS: error = EFAULT; break; | ||
102 | case ERROR_NONE_MAPPED: error = EINVAL; break; | ||
103 | case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break; | ||
104 | case ERROR_NOT_READY: error = EAGAIN; break; | ||
105 | case ERROR_NOT_SAME_DEVICE: error = EXDEV; break; | ||
106 | case ERROR_NO_DATA: error = EPIPE; break; | ||
107 | case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break; | ||
108 | case ERROR_NO_PROC_SLOTS: error = EAGAIN; break; | ||
109 | case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break; | ||
110 | case ERROR_OPEN_FAILED: error = EIO; break; | ||
111 | case ERROR_OPEN_FILES: error = EBUSY; break; | ||
112 | case ERROR_OPERATION_ABORTED: error = EINTR; break; | ||
113 | case ERROR_OUTOFMEMORY: error = ENOMEM; break; | ||
114 | case ERROR_PASSWORD_EXPIRED: error = EACCES; break; | ||
115 | case ERROR_PATH_BUSY: error = EBUSY; break; | ||
116 | case ERROR_PATH_NOT_FOUND: error = ENOENT; break; | ||
117 | case ERROR_PIPE_BUSY: error = EBUSY; break; | ||
118 | case ERROR_PIPE_CONNECTED: error = EPIPE; break; | ||
119 | case ERROR_PIPE_LISTENING: error = EPIPE; break; | ||
120 | case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break; | ||
121 | case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break; | ||
122 | case ERROR_READ_FAULT: error = EIO; break; | ||
123 | case ERROR_SEEK: error = EIO; break; | ||
124 | case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break; | ||
125 | case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break; | ||
126 | case ERROR_SHARING_VIOLATION: error = EACCES; break; | ||
127 | case ERROR_STACK_OVERFLOW: error = ENOMEM; break; | ||
128 | case ERROR_SWAPERROR: error = ENOENT; break; | ||
129 | case ERROR_TOO_MANY_LINKS: error = EMLINK; break; | ||
130 | case ERROR_TOO_MANY_MODULES: error = EMFILE; break; | ||
131 | case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break; | ||
132 | case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break; | ||
133 | case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break; | ||
134 | case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break; | ||
135 | case ERROR_WRITE_FAULT: error = EIO; break; | ||
136 | case ERROR_WRITE_PROTECT: error = EROFS; break; | ||
137 | } | ||
138 | return error; | ||
139 | } | ||
140 | |||
141 | #undef open | ||
142 | int mingw_open (const char *filename, int oflags, ...) | ||
143 | { | ||
144 | va_list args; | ||
145 | unsigned mode; | ||
146 | int fd; | ||
147 | |||
148 | va_start(args, oflags); | ||
149 | mode = va_arg(args, int); | ||
150 | va_end(args); | ||
151 | |||
152 | if (oflags & O_NONBLOCK) { | ||
153 | oflags &= ~O_NONBLOCK; | ||
154 | } | ||
155 | if (filename && !strcmp(filename, "/dev/null")) | ||
156 | filename = "nul"; | ||
157 | fd = open(filename, oflags, mode); | ||
158 | if (fd < 0 && (oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) { | ||
159 | DWORD attrs = GetFileAttributes(filename); | ||
160 | if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY)) | ||
161 | errno = EISDIR; | ||
162 | } | ||
163 | return fd; | ||
164 | } | ||
165 | |||
166 | #undef fopen | ||
167 | FILE *mingw_fopen (const char *filename, const char *otype) | ||
168 | { | ||
169 | if (filename && !strcmp(filename, "/dev/null")) | ||
170 | filename = "nul"; | ||
171 | return fopen(filename, otype); | ||
172 | } | ||
173 | |||
174 | #undef dup2 | ||
175 | int mingw_dup2 (int fd, int fdto) | ||
176 | { | ||
177 | int ret = dup2(fd, fdto); | ||
178 | return ret != -1 ? fdto : -1; | ||
179 | } | ||
180 | |||
181 | /* | ||
182 | * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC. | ||
183 | * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch. | ||
184 | */ | ||
185 | static inline long long filetime_to_hnsec(const FILETIME *ft) | ||
186 | { | ||
187 | long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime; | ||
188 | /* Windows to Unix Epoch conversion */ | ||
189 | return winTime - 116444736000000000LL; | ||
190 | } | ||
191 | |||
192 | static inline time_t filetime_to_time_t(const FILETIME *ft) | ||
193 | { | ||
194 | return (time_t)(filetime_to_hnsec(ft) / 10000000); | ||
195 | } | ||
196 | |||
197 | static inline int file_attr_to_st_mode (DWORD attr) | ||
198 | { | ||
199 | int fMode = S_IREAD; | ||
200 | if (attr & FILE_ATTRIBUTE_DIRECTORY) | ||
201 | fMode |= S_IFDIR|S_IWRITE|S_IEXEC; | ||
202 | else | ||
203 | fMode |= S_IFREG; | ||
204 | if (!(attr & FILE_ATTRIBUTE_READONLY)) | ||
205 | fMode |= S_IWRITE; | ||
206 | return fMode; | ||
207 | } | ||
208 | |||
209 | static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata) | ||
210 | { | ||
211 | if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata)) | ||
212 | return 0; | ||
213 | |||
214 | switch (GetLastError()) { | ||
215 | case ERROR_ACCESS_DENIED: | ||
216 | case ERROR_SHARING_VIOLATION: | ||
217 | case ERROR_LOCK_VIOLATION: | ||
218 | case ERROR_SHARING_BUFFER_EXCEEDED: | ||
219 | return EACCES; | ||
220 | case ERROR_BUFFER_OVERFLOW: | ||
221 | return ENAMETOOLONG; | ||
222 | case ERROR_NOT_ENOUGH_MEMORY: | ||
223 | return ENOMEM; | ||
224 | default: | ||
225 | return ENOENT; | ||
226 | } | ||
227 | } | ||
228 | |||
229 | /* We keep the do_lstat code in a separate function to avoid recursion. | ||
230 | * When a path ends with a slash, the stat will fail with ENOENT. In | ||
231 | * this case, we strip the trailing slashes and stat again. | ||
232 | * | ||
233 | * If follow is true then act like stat() and report on the link | ||
234 | * target. Otherwise report on the link itself. | ||
235 | */ | ||
236 | static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf) | ||
237 | { | ||
238 | int err; | ||
239 | WIN32_FILE_ATTRIBUTE_DATA fdata; | ||
240 | mode_t usermode; | ||
241 | |||
242 | if (!(err = get_file_attr(file_name, &fdata))) { | ||
243 | int len = strlen(file_name); | ||
244 | |||
245 | buf->st_ino = 0; | ||
246 | buf->st_uid = DEFAULT_UID; | ||
247 | buf->st_gid = DEFAULT_GID; | ||
248 | buf->st_nlink = 1; | ||
249 | buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); | ||
250 | if (len > 4 && (!strcasecmp(file_name+len-4, ".exe") || | ||
251 | !strcasecmp(file_name+len-4, ".com"))) | ||
252 | buf->st_mode |= S_IEXEC; | ||
253 | buf->st_size = fdata.nFileSizeLow | | ||
254 | (((off64_t)fdata.nFileSizeHigh)<<32); | ||
255 | buf->st_dev = buf->st_rdev = 0; /* not used by Git */ | ||
256 | buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); | ||
257 | buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); | ||
258 | buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); | ||
259 | if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { | ||
260 | WIN32_FIND_DATAA findbuf; | ||
261 | HANDLE handle = FindFirstFileA(file_name, &findbuf); | ||
262 | if (handle != INVALID_HANDLE_VALUE) { | ||
263 | if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && | ||
264 | (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) { | ||
265 | if (follow) { | ||
266 | char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE]; | ||
267 | buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE); | ||
268 | } else { | ||
269 | buf->st_mode = S_IFLNK; | ||
270 | } | ||
271 | buf->st_mode |= S_IREAD; | ||
272 | if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)) | ||
273 | buf->st_mode |= S_IWRITE; | ||
274 | } | ||
275 | FindClose(handle); | ||
276 | } | ||
277 | } | ||
278 | usermode = buf->st_mode & S_IRWXU; | ||
279 | buf->st_mode |= (usermode >> 3) | ((usermode >> 6) & ~S_IWOTH); | ||
280 | |||
281 | /* | ||
282 | * Assume a block is 4096 bytes and calculate number of 512 byte | ||
283 | * sectors. | ||
284 | */ | ||
285 | buf->st_blksize = 4096; | ||
286 | buf->st_blocks = ((buf->st_size+4095)>>12)<<3; | ||
287 | return 0; | ||
288 | } | ||
289 | errno = err; | ||
290 | return -1; | ||
291 | } | ||
292 | |||
293 | /* We provide our own lstat/fstat functions, since the provided | ||
294 | * lstat/fstat functions are so slow. These stat functions are | ||
295 | * tailored for Git's usage (read: fast), and are not meant to be | ||
296 | * complete. Note that Git stat()s are redirected to mingw_lstat() | ||
297 | * too, since Windows doesn't really handle symlinks that well. | ||
298 | */ | ||
299 | static int do_stat_internal(int follow, const char *file_name, struct mingw_stat *buf) | ||
300 | { | ||
301 | int namelen; | ||
302 | char alt_name[PATH_MAX]; | ||
303 | |||
304 | if (!do_lstat(follow, file_name, buf)) | ||
305 | return 0; | ||
306 | |||
307 | /* if file_name ended in a '/', Windows returned ENOENT; | ||
308 | * try again without trailing slashes | ||
309 | */ | ||
310 | if (errno != ENOENT) | ||
311 | return -1; | ||
312 | |||
313 | namelen = strlen(file_name); | ||
314 | if (namelen && file_name[namelen-1] != '/') | ||
315 | return -1; | ||
316 | while (namelen && file_name[namelen-1] == '/') | ||
317 | --namelen; | ||
318 | if (!namelen || namelen >= PATH_MAX) | ||
319 | return -1; | ||
320 | |||
321 | memcpy(alt_name, file_name, namelen); | ||
322 | alt_name[namelen] = 0; | ||
323 | return do_lstat(follow, alt_name, buf); | ||
324 | } | ||
325 | |||
326 | int mingw_lstat(const char *file_name, struct mingw_stat *buf) | ||
327 | { | ||
328 | return do_stat_internal(0, file_name, buf); | ||
329 | } | ||
330 | int mingw_stat(const char *file_name, struct mingw_stat *buf) | ||
331 | { | ||
332 | return do_stat_internal(1, file_name, buf); | ||
333 | } | ||
334 | |||
335 | int mingw_fstat(int fd, struct mingw_stat *buf) | ||
336 | { | ||
337 | HANDLE fh = (HANDLE)_get_osfhandle(fd); | ||
338 | BY_HANDLE_FILE_INFORMATION fdata; | ||
339 | |||
340 | if (fh == INVALID_HANDLE_VALUE) { | ||
341 | errno = EBADF; | ||
342 | return -1; | ||
343 | } | ||
344 | /* direct non-file handles to MS's fstat() */ | ||
345 | if (GetFileType(fh) != FILE_TYPE_DISK) { | ||
346 | struct _stati64 buf64; | ||
347 | |||
348 | if ( _fstati64(fd, &buf64) != 0 ) { | ||
349 | return -1; | ||
350 | } | ||
351 | buf->st_dev = 0; | ||
352 | buf->st_ino = 0; | ||
353 | buf->st_mode = S_IREAD|S_IWRITE; | ||
354 | buf->st_nlink = 1; | ||
355 | buf->st_uid = DEFAULT_UID; | ||
356 | buf->st_gid = DEFAULT_GID; | ||
357 | buf->st_rdev = 0; | ||
358 | buf->st_size = buf64.st_size; | ||
359 | buf->st_atime = buf64.st_atime; | ||
360 | buf->st_mtime = buf64.st_mtime; | ||
361 | buf->st_ctime = buf64.st_ctime; | ||
362 | buf->st_blksize = 4096; | ||
363 | buf->st_blocks = ((buf64.st_size+4095)>>12)<<3; | ||
364 | } | ||
365 | |||
366 | if (GetFileInformationByHandle(fh, &fdata)) { | ||
367 | buf->st_ino = 0; | ||
368 | buf->st_uid = DEFAULT_UID; | ||
369 | buf->st_gid = DEFAULT_GID; | ||
370 | /* could use fdata.nNumberOfLinks but it's inconsistent with stat */ | ||
371 | buf->st_nlink = 1; | ||
372 | buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes); | ||
373 | buf->st_size = fdata.nFileSizeLow | | ||
374 | (((off64_t)fdata.nFileSizeHigh)<<32); | ||
375 | buf->st_dev = buf->st_rdev = 0; /* not used by Git */ | ||
376 | buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime)); | ||
377 | buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime)); | ||
378 | buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime)); | ||
379 | buf->st_blksize = 4096; | ||
380 | buf->st_blocks = ((buf->st_size+4095)>>12)<<3; | ||
381 | return 0; | ||
382 | } | ||
383 | errno = EBADF; | ||
384 | return -1; | ||
385 | } | ||
386 | |||
387 | static inline void timeval_to_filetime(const struct timeval tv, FILETIME *ft) | ||
388 | { | ||
389 | long long winTime = ((tv.tv_sec * 1000000LL) + tv.tv_usec) * 10LL + 116444736000000000LL; | ||
390 | ft->dwLowDateTime = winTime; | ||
391 | ft->dwHighDateTime = winTime >> 32; | ||
392 | } | ||
393 | |||
394 | int utimes(const char *file_name, const struct timeval tims[2]) | ||
395 | { | ||
396 | FILETIME mft, aft; | ||
397 | HANDLE fh; | ||
398 | DWORD flags, attrs; | ||
399 | int rc; | ||
400 | |||
401 | flags = FILE_ATTRIBUTE_NORMAL; | ||
402 | |||
403 | /* must have write permission */ | ||
404 | attrs = GetFileAttributes(file_name); | ||
405 | if ( attrs != INVALID_FILE_ATTRIBUTES ) { | ||
406 | if ( attrs & FILE_ATTRIBUTE_READONLY ) { | ||
407 | /* ignore errors here; open() will report them */ | ||
408 | SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY); | ||
409 | } | ||
410 | |||
411 | if ( attrs & FILE_ATTRIBUTE_DIRECTORY ) { | ||
412 | flags = FILE_FLAG_BACKUP_SEMANTICS; | ||
413 | } | ||
414 | } | ||
415 | |||
416 | fh = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE, | ||
417 | FILE_SHARE_READ|FILE_SHARE_WRITE, | ||
418 | NULL, OPEN_EXISTING, flags, NULL); | ||
419 | if ( fh == INVALID_HANDLE_VALUE ) { | ||
420 | errno = err_win_to_posix(GetLastError()); | ||
421 | rc = -1; | ||
422 | goto revert_attrs; | ||
423 | } | ||
424 | |||
425 | if (tims) { | ||
426 | timeval_to_filetime(tims[0], &aft); | ||
427 | timeval_to_filetime(tims[1], &mft); | ||
428 | } | ||
429 | else { | ||
430 | GetSystemTimeAsFileTime(&mft); | ||
431 | aft = mft; | ||
432 | } | ||
433 | if (!SetFileTime(fh, NULL, &aft, &mft)) { | ||
434 | errno = EINVAL; | ||
435 | rc = -1; | ||
436 | } else | ||
437 | rc = 0; | ||
438 | CloseHandle(fh); | ||
439 | |||
440 | revert_attrs: | ||
441 | if (attrs != INVALID_FILE_ATTRIBUTES && | ||
442 | (attrs & FILE_ATTRIBUTE_READONLY)) { | ||
443 | /* ignore errors again */ | ||
444 | SetFileAttributes(file_name, attrs); | ||
445 | } | ||
446 | return rc; | ||
447 | } | ||
448 | |||
449 | unsigned int sleep (unsigned int seconds) | ||
450 | { | ||
451 | Sleep(seconds*1000); | ||
452 | return 0; | ||
453 | } | ||
454 | |||
455 | int nanosleep(const struct timespec *req, struct timespec *rem) | ||
456 | { | ||
457 | if (req->tv_nsec < 0 || 1000000000 <= req->tv_nsec) { | ||
458 | errno = EINVAL; | ||
459 | return -1; | ||
460 | } | ||
461 | |||
462 | Sleep(req->tv_sec*1000 + req->tv_nsec/1000000); | ||
463 | |||
464 | /* Sleep is not interruptible. So there is no remaining delay. */ | ||
465 | if (rem != NULL) { | ||
466 | rem->tv_sec = 0; | ||
467 | rem->tv_nsec = 0; | ||
468 | } | ||
469 | |||
470 | return 0; | ||
471 | } | ||
472 | |||
473 | /* | ||
474 | * Windows' mktemp returns NULL on error whereas POSIX always returns the | ||
475 | * template and signals an error by making it an empty string. | ||
476 | */ | ||
477 | #undef mktemp | ||
478 | char *mingw_mktemp(char *template) | ||
479 | { | ||
480 | if ( mktemp(template) == NULL ) { | ||
481 | template[0] = '\0'; | ||
482 | } | ||
483 | |||
484 | return template; | ||
485 | } | ||
486 | |||
487 | int mkstemp(char *template) | ||
488 | { | ||
489 | char *filename = mktemp(template); | ||
490 | if (filename == NULL) | ||
491 | return -1; | ||
492 | return open(filename, O_RDWR | O_CREAT, 0600); | ||
493 | } | ||
494 | |||
495 | int gettimeofday(struct timeval *tv, void *tz UNUSED_PARAM) | ||
496 | { | ||
497 | FILETIME ft; | ||
498 | long long hnsec; | ||
499 | |||
500 | GetSystemTimeAsFileTime(&ft); | ||
501 | hnsec = filetime_to_hnsec(&ft); | ||
502 | tv->tv_sec = hnsec / 10000000; | ||
503 | tv->tv_usec = (hnsec % 10000000) / 10; | ||
504 | return 0; | ||
505 | } | ||
506 | |||
507 | int pipe(int filedes[2]) | ||
508 | { | ||
509 | if (_pipe(filedes, PIPE_BUF, 0) < 0) | ||
510 | return -1; | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | struct tm *gmtime_r(const time_t *timep, struct tm *result) | ||
515 | { | ||
516 | /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */ | ||
517 | memcpy(result, gmtime(timep), sizeof(struct tm)); | ||
518 | return result; | ||
519 | } | ||
520 | |||
521 | struct tm *localtime_r(const time_t *timep, struct tm *result) | ||
522 | { | ||
523 | /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */ | ||
524 | memcpy(result, localtime(timep), sizeof(struct tm)); | ||
525 | return result; | ||
526 | } | ||
527 | |||
528 | #undef getcwd | ||
529 | char *mingw_getcwd(char *pointer, int len) | ||
530 | { | ||
531 | int i; | ||
532 | char *ret = getcwd(pointer, len); | ||
533 | if (!ret) | ||
534 | return ret; | ||
535 | for (i = 0; ret[i]; i++) | ||
536 | if (ret[i] == '\\') | ||
537 | ret[i] = '/'; | ||
538 | return ret; | ||
539 | } | ||
540 | |||
541 | #undef rename | ||
542 | int mingw_rename(const char *pold, const char *pnew) | ||
543 | { | ||
544 | DWORD attrs; | ||
545 | |||
546 | /* | ||
547 | * Try native rename() first to get errno right. | ||
548 | * It is based on MoveFile(), which cannot overwrite existing files. | ||
549 | */ | ||
550 | if (!rename(pold, pnew)) | ||
551 | return 0; | ||
552 | if (errno != EEXIST) | ||
553 | return -1; | ||
554 | if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) | ||
555 | return 0; | ||
556 | /* TODO: translate more errors */ | ||
557 | if (GetLastError() == ERROR_ACCESS_DENIED && | ||
558 | (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) { | ||
559 | if (attrs & FILE_ATTRIBUTE_DIRECTORY) { | ||
560 | errno = EISDIR; | ||
561 | return -1; | ||
562 | } | ||
563 | if ((attrs & FILE_ATTRIBUTE_READONLY) && | ||
564 | SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) { | ||
565 | if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING)) | ||
566 | return 0; | ||
567 | /* revert file attributes on failure */ | ||
568 | SetFileAttributes(pnew, attrs); | ||
569 | } | ||
570 | } | ||
571 | errno = EACCES; | ||
572 | return -1; | ||
573 | } | ||
574 | |||
575 | static char *gethomedir(void) | ||
576 | { | ||
577 | static char buf[PATH_MAX]; | ||
578 | DWORD len = sizeof(buf); | ||
579 | HANDLE h; | ||
580 | char *s; | ||
581 | |||
582 | buf[0] = '\0'; | ||
583 | if ( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h) ) | ||
584 | return buf; | ||
585 | |||
586 | if ( !GetUserProfileDirectory(h, buf, &len) ) { | ||
587 | CloseHandle(h); | ||
588 | return buf; | ||
589 | } | ||
590 | |||
591 | CloseHandle(h); | ||
592 | |||
593 | for ( s=buf; *s; ++s ) { | ||
594 | if ( *s == '\\' ) { | ||
595 | *s = '/'; | ||
596 | } | ||
597 | } | ||
598 | |||
599 | return buf; | ||
600 | } | ||
601 | |||
602 | static char *get_user_name(void) | ||
603 | { | ||
604 | static char user_name[100] = ""; | ||
605 | char *s; | ||
606 | DWORD len = sizeof(user_name); | ||
607 | |||
608 | if ( user_name[0] != '\0' ) { | ||
609 | return user_name; | ||
610 | } | ||
611 | |||
612 | if ( !GetUserName(user_name, &len) ) { | ||
613 | return NULL; | ||
614 | } | ||
615 | |||
616 | for ( s=user_name; *s; ++s ) { | ||
617 | if ( *s == ' ' ) { | ||
618 | *s = '_'; | ||
619 | } | ||
620 | } | ||
621 | |||
622 | return user_name; | ||
623 | } | ||
624 | |||
625 | struct passwd *getpwnam(const char *name) | ||
626 | { | ||
627 | const char *myname; | ||
628 | |||
629 | if ( (myname=get_user_name()) != NULL && | ||
630 | strcmp(myname, name) == 0 ) { | ||
631 | return getpwuid(DEFAULT_UID); | ||
632 | } | ||
633 | |||
634 | return NULL; | ||
635 | } | ||
636 | |||
637 | struct passwd *getpwuid(uid_t uid UNUSED_PARAM) | ||
638 | { | ||
639 | static struct passwd p; | ||
640 | |||
641 | if ( (p.pw_name=get_user_name()) == NULL ) { | ||
642 | return NULL; | ||
643 | } | ||
644 | p.pw_passwd = (char *)"secret"; | ||
645 | p.pw_gecos = (char *)"unknown"; | ||
646 | p.pw_dir = gethomedir(); | ||
647 | p.pw_shell = NULL; | ||
648 | p.pw_uid = DEFAULT_UID; | ||
649 | p.pw_gid = DEFAULT_GID; | ||
650 | |||
651 | return &p; | ||
652 | } | ||
653 | |||
654 | struct group *getgrgid(gid_t gid UNUSED_PARAM) | ||
655 | { | ||
656 | static char *members[2] = { NULL, NULL }; | ||
657 | static struct group g; | ||
658 | |||
659 | if ( (g.gr_name=get_user_name()) == NULL ) { | ||
660 | return NULL; | ||
661 | } | ||
662 | g.gr_passwd = (char *)"secret"; | ||
663 | g.gr_gid = DEFAULT_GID; | ||
664 | members[0] = g.gr_name; | ||
665 | g.gr_mem = members; | ||
666 | |||
667 | return &g; | ||
668 | } | ||
669 | |||
670 | int getgrouplist(const char *user UNUSED_PARAM, gid_t group UNUSED_PARAM, | ||
671 | gid_t *groups, int *ngroups) | ||
672 | { | ||
673 | if ( *ngroups == 0 ) { | ||
674 | *ngroups = 1; | ||
675 | return -1; | ||
676 | } | ||
677 | |||
678 | *ngroups = 1; | ||
679 | groups[0] = DEFAULT_GID; | ||
680 | return 1; | ||
681 | } | ||
682 | |||
683 | int getgroups(int n, gid_t *groups) | ||
684 | { | ||
685 | if ( n == 0 ) { | ||
686 | return 1; | ||
687 | } | ||
688 | |||
689 | groups[0] = DEFAULT_GID; | ||
690 | return 1; | ||
691 | } | ||
692 | |||
693 | int getlogin_r(char *buf, size_t len) | ||
694 | { | ||
695 | char *name; | ||
696 | |||
697 | if ( (name=get_user_name()) == NULL ) { | ||
698 | return -1; | ||
699 | } | ||
700 | |||
701 | if ( strlen(name) >= len ) { | ||
702 | errno = ERANGE; | ||
703 | return -1; | ||
704 | } | ||
705 | |||
706 | strcpy(buf, name); | ||
707 | return 0; | ||
708 | } | ||
709 | |||
710 | long sysconf(int name) | ||
711 | { | ||
712 | if ( name == _SC_CLK_TCK ) { | ||
713 | return 100; | ||
714 | } | ||
715 | errno = EINVAL; | ||
716 | return -1; | ||
717 | } | ||
718 | |||
719 | clock_t times(struct tms *buf) | ||
720 | { | ||
721 | buf->tms_utime = 0; | ||
722 | buf->tms_stime = 0; | ||
723 | buf->tms_cutime = 0; | ||
724 | buf->tms_cstime = 0; | ||
725 | |||
726 | return 0; | ||
727 | } | ||
728 | |||
729 | int link(const char *oldpath, const char *newpath) | ||
730 | { | ||
731 | typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES); | ||
732 | static T create_hard_link = NULL; | ||
733 | if (!create_hard_link) { | ||
734 | create_hard_link = (T) GetProcAddress( | ||
735 | GetModuleHandle("kernel32.dll"), "CreateHardLinkA"); | ||
736 | if (!create_hard_link) | ||
737 | create_hard_link = (T)-1; | ||
738 | } | ||
739 | if (create_hard_link == (T)-1) { | ||
740 | errno = ENOSYS; | ||
741 | return -1; | ||
742 | } | ||
743 | if (!create_hard_link(newpath, oldpath, NULL)) { | ||
744 | errno = err_win_to_posix(GetLastError()); | ||
745 | return -1; | ||
746 | } | ||
747 | return 0; | ||
748 | } | ||
749 | |||
750 | char *realpath(const char *path, char *resolved_path) | ||
751 | { | ||
752 | /* FIXME: need normalization */ | ||
753 | return strcpy(resolved_path, path); | ||
754 | } | ||
755 | |||
756 | const char *get_busybox_exec_path(void) | ||
757 | { | ||
758 | static char path[PATH_MAX] = ""; | ||
759 | |||
760 | if (!*path) | ||
761 | GetModuleFileName(NULL, path, PATH_MAX); | ||
762 | return path; | ||
763 | } | ||
764 | |||
765 | #undef mkdir | ||
766 | int mingw_mkdir(const char *path, int mode UNUSED_PARAM) | ||
767 | { | ||
768 | int ret; | ||
769 | struct stat st; | ||
770 | int lerrno = 0; | ||
771 | |||
772 | if ( (ret=mkdir(path)) < 0 ) { | ||
773 | lerrno = errno; | ||
774 | if ( lerrno == EACCES && stat(path, &st) == 0 ) { | ||
775 | ret = 0; | ||
776 | lerrno = 0; | ||
777 | } | ||
778 | } | ||
779 | |||
780 | errno = lerrno; | ||
781 | return ret; | ||
782 | } | ||
783 | |||
784 | #undef chmod | ||
785 | int mingw_chmod(const char *path, int mode) | ||
786 | { | ||
787 | WIN32_FILE_ATTRIBUTE_DATA fdata; | ||
788 | |||
789 | if ( get_file_attr(path, &fdata) == 0 && | ||
790 | fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { | ||
791 | mode |= 0222; | ||
792 | } | ||
793 | |||
794 | return chmod(path, mode); | ||
795 | } | ||
796 | |||
797 | int fcntl(int fd, int cmd, ...) | ||
798 | { | ||
799 | va_list arg; | ||
800 | int result = -1; | ||
801 | char *fds; | ||
802 | int target, i, newfd; | ||
803 | |||
804 | va_start(arg, cmd); | ||
805 | |||
806 | switch (cmd) { | ||
807 | case F_GETFD: | ||
808 | case F_SETFD: | ||
809 | case F_GETFL: | ||
810 | /* | ||
811 | * Our fake F_GETFL won't matter if the return value is used as | ||
812 | * fcntl(fd, F_SETFL, ret|something); | ||
813 | * because F_SETFL isn't supported either. | ||
814 | */ | ||
815 | result = 0; | ||
816 | break; | ||
817 | case F_DUPFD: | ||
818 | target = va_arg(arg, int); | ||
819 | fds = xzalloc(target); | ||
820 | while ((newfd = dup(fd)) < target && newfd >= 0) { | ||
821 | fds[newfd] = 1; | ||
822 | } | ||
823 | for (i = 0; i < target; ++i) { | ||
824 | if (fds[i]) { | ||
825 | close(i); | ||
826 | } | ||
827 | } | ||
828 | free(fds); | ||
829 | result = newfd; | ||
830 | break; | ||
831 | default: | ||
832 | errno = ENOSYS; | ||
833 | break; | ||
834 | } | ||
835 | |||
836 | va_end(arg); | ||
837 | return result; | ||
838 | } | ||
839 | |||
840 | #undef unlink | ||
841 | int mingw_unlink(const char *pathname) | ||
842 | { | ||
843 | /* read-only files cannot be removed */ | ||
844 | chmod(pathname, 0666); | ||
845 | return unlink(pathname); | ||
846 | } | ||
847 | |||
848 | #undef strftime | ||
849 | size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm) | ||
850 | { | ||
851 | size_t ret; | ||
852 | char day[3]; | ||
853 | char *t; | ||
854 | char *fmt, *newfmt; | ||
855 | struct tm tm2; | ||
856 | int m; | ||
857 | |||
858 | /* | ||
859 | * Emulate the '%e' and '%s' formats that Windows' strftime lacks. | ||
860 | * Happily, the string that replaces '%e' is two characters long. | ||
861 | * '%s' is a bit more complicated. | ||
862 | */ | ||
863 | fmt = xstrdup(format); | ||
864 | for ( t=fmt; *t; ++t ) { | ||
865 | if ( *t == '%' ) { | ||
866 | if ( t[1] == 'e' ) { | ||
867 | if ( tm->tm_mday >= 0 && tm->tm_mday <= 99 ) { | ||
868 | sprintf(day, "%2d", tm->tm_mday); | ||
869 | } | ||
870 | else { | ||
871 | strcpy(day, " "); | ||
872 | } | ||
873 | memcpy(t++, day, 2); | ||
874 | } | ||
875 | else if ( t[1] == 's' ) { | ||
876 | *t = '\0'; | ||
877 | m = t - fmt; | ||
878 | tm2 = *tm; | ||
879 | newfmt = xasprintf("%s%d%s", fmt, (int)mktime(&tm2), t+2); | ||
880 | free(fmt); | ||
881 | t = newfmt + m + 1; | ||
882 | fmt = newfmt; | ||
883 | } | ||
884 | else if ( t[1] == 'z' ) { | ||
885 | char buffer[16] = ""; | ||
886 | |||
887 | *t = '\0'; | ||
888 | m = t - fmt; | ||
889 | _tzset(); | ||
890 | if ( tm->tm_isdst >= 0 ) { | ||
891 | int offset = (int)_timezone - (tm->tm_isdst > 0 ? 3600 : 0); | ||
892 | int hr, min; | ||
893 | |||
894 | if ( offset > 0 ) { | ||
895 | buffer[0] = '-'; | ||
896 | } | ||
897 | else { | ||
898 | buffer[0] = '+'; | ||
899 | offset = -offset; | ||
900 | } | ||
901 | |||
902 | hr = offset / 3600; | ||
903 | min = (offset % 3600) / 60; | ||
904 | sprintf(buffer+1, "%02d%02d", hr, min); | ||
905 | } | ||
906 | newfmt = xasprintf("%s%s%s", fmt, buffer, t+2); | ||
907 | free(fmt); | ||
908 | t = newfmt + m + 1; | ||
909 | fmt = newfmt; | ||
910 | } | ||
911 | else if ( t[1] != '\0' ) { | ||
912 | ++t; | ||
913 | } | ||
914 | } | ||
915 | } | ||
916 | |||
917 | ret = strftime(buf, max, fmt, tm); | ||
918 | free(fmt); | ||
919 | |||
920 | return ret; | ||
921 | } | ||
922 | |||
923 | int stime(time_t *t UNUSED_PARAM) | ||
924 | { | ||
925 | errno = EPERM; | ||
926 | return -1; | ||
927 | } | ||
928 | |||
929 | #undef access | ||
930 | int mingw_access(const char *name, int mode) | ||
931 | { | ||
932 | int ret; | ||
933 | struct stat s; | ||
934 | int fd, n, sig; | ||
935 | unsigned int offset; | ||
936 | unsigned char buf[1024]; | ||
937 | |||
938 | /* Windows can only handle test for existence, read or write */ | ||
939 | if (mode == F_OK || (mode & ~X_OK)) { | ||
940 | ret = _access(name, mode & ~X_OK); | ||
941 | if (ret < 0 || !(mode & X_OK)) { | ||
942 | return ret; | ||
943 | } | ||
944 | } | ||
945 | |||
946 | if (!mingw_stat(name, &s) && S_ISREG(s.st_mode)) { | ||
947 | |||
948 | /* stat marks .exe and .com files as executable */ | ||
949 | if ((s.st_mode&S_IEXEC)) { | ||
950 | return 0; | ||
951 | } | ||
952 | |||
953 | fd = open(name, O_RDONLY); | ||
954 | if (fd < 0) | ||
955 | return -1; | ||
956 | n = read(fd, buf, sizeof(buf)-1); | ||
957 | close(fd); | ||
958 | if (n < 4) /* at least '#!/x' and not error */ | ||
959 | return -1; | ||
960 | |||
961 | /* shell script */ | ||
962 | if (buf[0] == '#' && buf[1] == '!') { | ||
963 | return 0; | ||
964 | } | ||
965 | |||
966 | /* | ||
967 | * Poke about in file to see if it's a PE binary. I've just copied | ||
968 | * the magic from the file command. | ||
969 | */ | ||
970 | if (buf[0] == 'M' && buf[1] == 'Z') { | ||
971 | offset = (buf[0x19] << 8) + buf[0x18]; | ||
972 | if (offset > 0x3f) { | ||
973 | offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) + | ||
974 | (buf[0x3d] << 8) + buf[0x3c]; | ||
975 | if (offset < sizeof(buf)-100) { | ||
976 | if (memcmp(buf+offset, "PE\0\0", 4) == 0) { | ||
977 | sig = (buf[offset+25] << 8) + buf[offset+24]; | ||
978 | if (sig == 0x10b || sig == 0x20b) { | ||
979 | sig = (buf[offset+23] << 8) + buf[offset+22]; | ||
980 | if ((sig & 0x2000) != 0) { | ||
981 | /* DLL */ | ||
982 | return -1; | ||
983 | } | ||
984 | sig = buf[offset+92]; | ||
985 | return !(sig == 1 || sig == 2 || | ||
986 | sig == 3 || sig == 7); | ||
987 | } | ||
988 | } | ||
989 | } | ||
990 | } | ||
991 | } | ||
992 | } | ||
993 | |||
994 | return -1; | ||
995 | } | ||
996 | |||
997 | #undef rmdir | ||
998 | int mingw_rmdir(const char *path) | ||
999 | { | ||
1000 | /* read-only directories cannot be removed */ | ||
1001 | chmod(path, 0666); | ||
1002 | return rmdir(path); | ||
1003 | } | ||
1004 | |||
1005 | /* check if path can be made into an executable by adding a suffix; | ||
1006 | * return an allocated string containing the path if it can; | ||
1007 | * return NULL if not. | ||
1008 | * | ||
1009 | * if path already has a suffix don't even bother trying | ||
1010 | */ | ||
1011 | char *file_is_win32_executable(const char *p) | ||
1012 | { | ||
1013 | char *path; | ||
1014 | int len = strlen(p); | ||
1015 | |||
1016 | if (len > 4 && (!strcasecmp(p+len-4, ".exe") || | ||
1017 | !strcasecmp(p+len-4, ".com"))) { | ||
1018 | return NULL; | ||
1019 | } | ||
1020 | |||
1021 | if ( (path=malloc(len+5)) != NULL ) { | ||
1022 | memcpy(path, p, len); | ||
1023 | memcpy(path+len, ".exe", 5); | ||
1024 | if (file_is_executable(path)) { | ||
1025 | return path; | ||
1026 | } | ||
1027 | memcpy(path+len, ".com", 5); | ||
1028 | if (file_is_executable(path)) { | ||
1029 | return path; | ||
1030 | } | ||
1031 | free(path); | ||
1032 | } | ||
1033 | |||
1034 | return NULL; | ||
1035 | } | ||
1036 | |||
1037 | #undef opendir | ||
1038 | DIR *mingw_opendir(const char *path) | ||
1039 | { | ||
1040 | char name[4]; | ||
1041 | |||
1042 | if (isalpha(path[0]) && path[1] == ':' && path[2] == '\0') { | ||
1043 | strcpy(name, path); | ||
1044 | name[2] = '/'; | ||
1045 | name[3] = '\0'; | ||
1046 | path = name; | ||
1047 | } | ||
1048 | |||
1049 | return opendir(path); | ||
1050 | } | ||
1051 | |||
1052 | off_t mingw_lseek(int fd, off_t offset, int whence) | ||
1053 | { | ||
1054 | HANDLE h = (HANDLE)_get_osfhandle(fd); | ||
1055 | if (h == INVALID_HANDLE_VALUE) { | ||
1056 | errno = EBADF; | ||
1057 | return -1; | ||
1058 | } | ||
1059 | if (GetFileType(h) != FILE_TYPE_DISK) { | ||
1060 | errno = ESPIPE; | ||
1061 | return -1; | ||
1062 | } | ||
1063 | return _lseeki64(fd, offset, whence); | ||
1064 | } | ||
diff --git a/win32/mntent.c b/win32/mntent.c new file mode 100644 index 000000000..9b04a9c5e --- /dev/null +++ b/win32/mntent.c | |||
@@ -0,0 +1,69 @@ | |||
1 | /* | ||
2 | * A simple WIN32 implementation of mntent routines. It only handles | ||
3 | * fixed logical drives. | ||
4 | */ | ||
5 | #include "libbb.h" | ||
6 | |||
7 | struct mntdata { | ||
8 | DWORD flags; | ||
9 | int index; | ||
10 | }; | ||
11 | |||
12 | FILE *setmntent(const char *file UNUSED_PARAM, const char *mode UNUSED_PARAM) | ||
13 | { | ||
14 | struct mntdata *data; | ||
15 | |||
16 | if ( (data=malloc(sizeof(struct mntdata))) == NULL ) { | ||
17 | return NULL; | ||
18 | } | ||
19 | |||
20 | data->flags = GetLogicalDrives(); | ||
21 | data->index = -1; | ||
22 | |||
23 | return (FILE *)data; | ||
24 | } | ||
25 | |||
26 | struct mntent *getmntent(FILE *stream) | ||
27 | { | ||
28 | struct mntdata *data = (struct mntdata *)stream; | ||
29 | static char mnt_fsname[4]; | ||
30 | static char mnt_dir[4]; | ||
31 | static char mnt_type[100]; | ||
32 | static char mnt_opts[4]; | ||
33 | static struct mntent my_mount_entry = | ||
34 | { mnt_fsname, mnt_dir, mnt_type, mnt_opts, 0, 0 }; | ||
35 | struct mntent *entry; | ||
36 | |||
37 | entry = NULL; | ||
38 | while ( ++data->index < 26 ) { | ||
39 | if ( (data->flags & 1<<data->index) != 0 ) { | ||
40 | mnt_fsname[0] = 'A' + data->index; | ||
41 | mnt_fsname[1] = ':'; | ||
42 | mnt_fsname[2] = '\0'; | ||
43 | mnt_dir[0] = 'A' + data->index; | ||
44 | mnt_dir[1] = ':'; | ||
45 | mnt_dir[2] = '\\'; | ||
46 | mnt_dir[3] = '\0'; | ||
47 | mnt_type[0] = '\0'; | ||
48 | mnt_opts[0] = '\0'; | ||
49 | |||
50 | if ( GetDriveType(mnt_dir) == DRIVE_FIXED ) { | ||
51 | if ( !GetVolumeInformation(mnt_dir, NULL, 0, NULL, NULL, | ||
52 | NULL, mnt_type, 100) ) { | ||
53 | mnt_type[0] = '\0'; | ||
54 | } | ||
55 | |||
56 | entry = &my_mount_entry; | ||
57 | break; | ||
58 | } | ||
59 | } | ||
60 | } | ||
61 | |||
62 | return entry; | ||
63 | } | ||
64 | |||
65 | int endmntent(FILE *stream) | ||
66 | { | ||
67 | free(stream); | ||
68 | return 0; | ||
69 | } | ||
diff --git a/win32/mntent.h b/win32/mntent.h new file mode 100644 index 000000000..b035bfa9c --- /dev/null +++ b/win32/mntent.h | |||
@@ -0,0 +1,19 @@ | |||
1 | #ifndef MNTENT_H | ||
2 | #define MNTENT_H | ||
3 | |||
4 | #include <stdio.h> | ||
5 | |||
6 | struct mntent { | ||
7 | char *mnt_fsname; /* Device or server for filesystem. */ | ||
8 | char *mnt_dir; /* Directory mounted on. */ | ||
9 | char *mnt_type; /* Type of filesystem: ufs, nfs, etc. */ | ||
10 | char *mnt_opts; /* Comma-separated options for fs. */ | ||
11 | int mnt_freq; /* Dump frequency (in days). */ | ||
12 | int mnt_passno; /* Pass number for `fsck'. */ | ||
13 | }; | ||
14 | |||
15 | extern FILE *setmntent(const char *file, const char *mode); | ||
16 | extern struct mntent *getmntent(FILE *stream); | ||
17 | extern int endmntent(FILE *stream); | ||
18 | |||
19 | #endif | ||
diff --git a/win32/net.c b/win32/net.c new file mode 100644 index 000000000..2341119b0 --- /dev/null +++ b/win32/net.c | |||
@@ -0,0 +1,101 @@ | |||
1 | #include "libbb.h" | ||
2 | |||
3 | int inet_aton(const char *cp, struct in_addr *inp) | ||
4 | { | ||
5 | unsigned long val = inet_addr(cp); | ||
6 | |||
7 | if (val == INADDR_NONE) | ||
8 | return 0; | ||
9 | inp->S_un.S_addr = val; | ||
10 | return 1; | ||
11 | } | ||
12 | |||
13 | void init_winsock(void) | ||
14 | { | ||
15 | WSADATA wsa; | ||
16 | if (WSAStartup(MAKEWORD(2,2), &wsa)) | ||
17 | bb_error_msg_and_die("unable to initialize winsock subsystem, error %d", | ||
18 | WSAGetLastError()); | ||
19 | atexit((void(*)(void)) WSACleanup); /* may conflict with other atexit? */ | ||
20 | } | ||
21 | |||
22 | int mingw_socket(int domain, int type, int protocol) | ||
23 | { | ||
24 | int sockfd; | ||
25 | SOCKET s = WSASocket(domain, type, protocol, NULL, 0, 0); | ||
26 | if (s == INVALID_SOCKET) { | ||
27 | /* | ||
28 | * WSAGetLastError() values are regular BSD error codes | ||
29 | * biased by WSABASEERR. | ||
30 | * However, strerror() does not know about networking | ||
31 | * specific errors, which are values beginning at 38 or so. | ||
32 | * Therefore, we choose to leave the biased error code | ||
33 | * in errno so that _if_ someone looks up the code somewhere, | ||
34 | * then it is at least the number that are usually listed. | ||
35 | */ | ||
36 | errno = WSAGetLastError(); | ||
37 | return -1; | ||
38 | } | ||
39 | /* convert into a file descriptor */ | ||
40 | if ((sockfd = _open_osfhandle((intptr_t)s, O_RDWR|O_BINARY)) < 0) { | ||
41 | closesocket(s); | ||
42 | bb_error_msg("unable to make a socket file descriptor: %s", | ||
43 | strerror(errno)); | ||
44 | return -1; | ||
45 | } | ||
46 | return sockfd; | ||
47 | } | ||
48 | |||
49 | #undef connect | ||
50 | int mingw_connect(int sockfd, const struct sockaddr *sa, size_t sz) | ||
51 | { | ||
52 | SOCKET s = (SOCKET)_get_osfhandle(sockfd); | ||
53 | return connect(s, sa, sz); | ||
54 | } | ||
55 | |||
56 | #undef bind | ||
57 | int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz) | ||
58 | { | ||
59 | SOCKET s = (SOCKET)_get_osfhandle(sockfd); | ||
60 | return bind(s, sa, sz); | ||
61 | } | ||
62 | |||
63 | #undef setsockopt | ||
64 | int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen) | ||
65 | { | ||
66 | SOCKET s = (SOCKET)_get_osfhandle(sockfd); | ||
67 | return setsockopt(s, lvl, optname, (const char*)optval, optlen); | ||
68 | } | ||
69 | |||
70 | #undef shutdown | ||
71 | int mingw_shutdown(int sockfd, int how) | ||
72 | { | ||
73 | SOCKET s = (SOCKET)_get_osfhandle(sockfd); | ||
74 | return shutdown(s, how); | ||
75 | } | ||
76 | |||
77 | #undef listen | ||
78 | int mingw_listen(int sockfd, int backlog) | ||
79 | { | ||
80 | SOCKET s = (SOCKET)_get_osfhandle(sockfd); | ||
81 | return listen(s, backlog); | ||
82 | } | ||
83 | |||
84 | #undef accept | ||
85 | int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz) | ||
86 | { | ||
87 | int sockfd2; | ||
88 | |||
89 | SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1); | ||
90 | SOCKET s2 = accept(s1, sa, sz); | ||
91 | |||
92 | /* convert into a file descriptor */ | ||
93 | if ((sockfd2 = _open_osfhandle((intptr_t)s2, O_RDWR|O_BINARY)) < 0) { | ||
94 | int err = errno; | ||
95 | closesocket(s2); | ||
96 | bb_error_msg("unable to make a socket file descriptor: %s", | ||
97 | strerror(err)); | ||
98 | return -1; | ||
99 | } | ||
100 | return sockfd2; | ||
101 | } | ||
diff --git a/win32/net/if.h b/win32/net/if.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/net/if.h | |||
diff --git a/win32/netdb.h b/win32/netdb.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/netdb.h | |||
diff --git a/win32/netinet/in.h b/win32/netinet/in.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/netinet/in.h | |||
diff --git a/win32/paths.h b/win32/paths.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/paths.h | |||
diff --git a/win32/poll.c b/win32/poll.c new file mode 100644 index 000000000..403eaa7a3 --- /dev/null +++ b/win32/poll.c | |||
@@ -0,0 +1,606 @@ | |||
1 | /* Emulation for poll(2) | ||
2 | Contributed by Paolo Bonzini. | ||
3 | |||
4 | Copyright 2001-2003, 2006-2011 Free Software Foundation, Inc. | ||
5 | |||
6 | This file is part of gnulib. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License along | ||
19 | with this program; if not, write to the Free Software Foundation, | ||
20 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ | ||
21 | |||
22 | /* Tell gcc not to warn about the (nfd < 0) tests, below. */ | ||
23 | #if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__ | ||
24 | # pragma GCC diagnostic ignored "-Wtype-limits" | ||
25 | #endif | ||
26 | |||
27 | #include <malloc.h> | ||
28 | |||
29 | #include <sys/types.h> | ||
30 | |||
31 | /* Specification. */ | ||
32 | #include <poll.h> | ||
33 | |||
34 | #include <errno.h> | ||
35 | #include <limits.h> | ||
36 | #include <assert.h> | ||
37 | |||
38 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | ||
39 | # define WIN32_NATIVE | ||
40 | # if defined (_MSC_VER) | ||
41 | # define _WIN32_WINNT 0x0502 | ||
42 | # endif | ||
43 | # include <winsock2.h> | ||
44 | # include <windows.h> | ||
45 | # include <io.h> | ||
46 | # include <stdio.h> | ||
47 | # include <conio.h> | ||
48 | #else | ||
49 | # include <sys/time.h> | ||
50 | # include <sys/socket.h> | ||
51 | # include <sys/select.h> | ||
52 | # include <unistd.h> | ||
53 | #endif | ||
54 | |||
55 | #ifdef HAVE_SYS_IOCTL_H | ||
56 | # include <sys/ioctl.h> | ||
57 | #endif | ||
58 | #ifdef HAVE_SYS_FILIO_H | ||
59 | # include <sys/filio.h> | ||
60 | #endif | ||
61 | |||
62 | #include <time.h> | ||
63 | |||
64 | #ifndef INFTIM | ||
65 | # define INFTIM (-1) | ||
66 | #endif | ||
67 | |||
68 | /* BeOS does not have MSG_PEEK. */ | ||
69 | #ifndef MSG_PEEK | ||
70 | # define MSG_PEEK 0 | ||
71 | #endif | ||
72 | |||
73 | #ifdef WIN32_NATIVE | ||
74 | |||
75 | #define IsConsoleHandle(h) (((long) (h) & 3) == 3) | ||
76 | |||
77 | static BOOL | ||
78 | IsSocketHandle (HANDLE h) | ||
79 | { | ||
80 | WSANETWORKEVENTS ev; | ||
81 | |||
82 | if (IsConsoleHandle (h)) | ||
83 | return FALSE; | ||
84 | |||
85 | /* Under Wine, it seems that getsockopt returns 0 for pipes too. | ||
86 | WSAEnumNetworkEvents instead distinguishes the two correctly. */ | ||
87 | ev.lNetworkEvents = 0xDEADBEEF; | ||
88 | WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); | ||
89 | return ev.lNetworkEvents != 0xDEADBEEF; | ||
90 | } | ||
91 | |||
92 | /* Declare data structures for ntdll functions. */ | ||
93 | typedef struct _FILE_PIPE_LOCAL_INFORMATION { | ||
94 | ULONG NamedPipeType; | ||
95 | ULONG NamedPipeConfiguration; | ||
96 | ULONG MaximumInstances; | ||
97 | ULONG CurrentInstances; | ||
98 | ULONG InboundQuota; | ||
99 | ULONG ReadDataAvailable; | ||
100 | ULONG OutboundQuota; | ||
101 | ULONG WriteQuotaAvailable; | ||
102 | ULONG NamedPipeState; | ||
103 | ULONG NamedPipeEnd; | ||
104 | } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; | ||
105 | |||
106 | typedef struct _IO_STATUS_BLOCK | ||
107 | { | ||
108 | union { | ||
109 | DWORD Status; | ||
110 | PVOID Pointer; | ||
111 | } u; | ||
112 | ULONG_PTR Information; | ||
113 | } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | ||
114 | |||
115 | typedef enum _FILE_INFORMATION_CLASS { | ||
116 | FilePipeLocalInformation = 24 | ||
117 | } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; | ||
118 | |||
119 | typedef DWORD (WINAPI *PNtQueryInformationFile) | ||
120 | (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); | ||
121 | |||
122 | # ifndef PIPE_BUF | ||
123 | # define PIPE_BUF 512 | ||
124 | # endif | ||
125 | |||
126 | /* Compute revents values for file handle H. If some events cannot happen | ||
127 | for the handle, eliminate them from *P_SOUGHT. */ | ||
128 | |||
129 | static int | ||
130 | win32_compute_revents (HANDLE h, int *p_sought) | ||
131 | { | ||
132 | int i, ret, happened; | ||
133 | INPUT_RECORD *irbuffer; | ||
134 | DWORD avail, nbuffer; | ||
135 | BOOL bRet; | ||
136 | IO_STATUS_BLOCK iosb; | ||
137 | FILE_PIPE_LOCAL_INFORMATION fpli; | ||
138 | static PNtQueryInformationFile NtQueryInformationFile; | ||
139 | static BOOL once_only; | ||
140 | |||
141 | switch (GetFileType (h)) | ||
142 | { | ||
143 | case FILE_TYPE_PIPE: | ||
144 | if (!once_only) | ||
145 | { | ||
146 | NtQueryInformationFile = (PNtQueryInformationFile) | ||
147 | GetProcAddress (GetModuleHandle ("ntdll.dll"), | ||
148 | "NtQueryInformationFile"); | ||
149 | once_only = TRUE; | ||
150 | } | ||
151 | |||
152 | happened = 0; | ||
153 | if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) | ||
154 | { | ||
155 | if (avail) | ||
156 | happened |= *p_sought & (POLLIN | POLLRDNORM); | ||
157 | } | ||
158 | else if (GetLastError () == ERROR_BROKEN_PIPE) | ||
159 | happened |= POLLHUP; | ||
160 | |||
161 | else | ||
162 | { | ||
163 | /* It was the write-end of the pipe. Check if it is writable. | ||
164 | If NtQueryInformationFile fails, optimistically assume the pipe is | ||
165 | writable. This could happen on Win9x, where NtQueryInformationFile | ||
166 | is not available, or if we inherit a pipe that doesn't permit | ||
167 | FILE_READ_ATTRIBUTES access on the write end (I think this should | ||
168 | not happen since WinXP SP2; WINE seems fine too). Otherwise, | ||
169 | ensure that enough space is available for atomic writes. */ | ||
170 | memset (&iosb, 0, sizeof (iosb)); | ||
171 | memset (&fpli, 0, sizeof (fpli)); | ||
172 | |||
173 | if (!NtQueryInformationFile | ||
174 | || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), | ||
175 | FilePipeLocalInformation) | ||
176 | || fpli.WriteQuotaAvailable >= PIPE_BUF | ||
177 | || (fpli.OutboundQuota < PIPE_BUF && | ||
178 | fpli.WriteQuotaAvailable == fpli.OutboundQuota)) | ||
179 | happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); | ||
180 | } | ||
181 | return happened; | ||
182 | |||
183 | case FILE_TYPE_CHAR: | ||
184 | ret = WaitForSingleObject (h, 0); | ||
185 | if (!IsConsoleHandle (h)) | ||
186 | return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0; | ||
187 | |||
188 | nbuffer = avail = 0; | ||
189 | bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); | ||
190 | if (bRet) | ||
191 | { | ||
192 | /* Input buffer. */ | ||
193 | *p_sought &= POLLIN | POLLRDNORM; | ||
194 | if (nbuffer == 0) | ||
195 | return POLLHUP; | ||
196 | if (!*p_sought) | ||
197 | return 0; | ||
198 | |||
199 | irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); | ||
200 | bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); | ||
201 | if (!bRet || avail == 0) | ||
202 | return POLLHUP; | ||
203 | |||
204 | for (i = 0; i < avail; i++) | ||
205 | if (irbuffer[i].EventType == KEY_EVENT) | ||
206 | return *p_sought; | ||
207 | return 0; | ||
208 | } | ||
209 | else | ||
210 | { | ||
211 | /* Screen buffer. */ | ||
212 | *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND; | ||
213 | return *p_sought; | ||
214 | } | ||
215 | |||
216 | default: | ||
217 | ret = WaitForSingleObject (h, 0); | ||
218 | if (ret == WAIT_OBJECT_0) | ||
219 | return *p_sought & ~(POLLPRI | POLLRDBAND); | ||
220 | |||
221 | return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND); | ||
222 | } | ||
223 | } | ||
224 | |||
225 | /* Convert fd_sets returned by select into revents values. */ | ||
226 | |||
227 | static int | ||
228 | win32_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents) | ||
229 | { | ||
230 | int happened = 0; | ||
231 | |||
232 | if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT) | ||
233 | happened |= (POLLIN | POLLRDNORM) & sought; | ||
234 | |||
235 | else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) | ||
236 | { | ||
237 | int r, error; | ||
238 | |||
239 | char data[64]; | ||
240 | WSASetLastError (0); | ||
241 | r = recv (h, data, sizeof (data), MSG_PEEK); | ||
242 | error = WSAGetLastError (); | ||
243 | WSASetLastError (0); | ||
244 | |||
245 | if (r > 0 || error == WSAENOTCONN) | ||
246 | happened |= (POLLIN | POLLRDNORM) & sought; | ||
247 | |||
248 | /* Distinguish hung-up sockets from other errors. */ | ||
249 | else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET | ||
250 | || error == WSAECONNABORTED || error == WSAENETRESET) | ||
251 | happened |= POLLHUP; | ||
252 | |||
253 | else | ||
254 | happened |= POLLERR; | ||
255 | } | ||
256 | |||
257 | if (lNetworkEvents & (FD_WRITE | FD_CONNECT)) | ||
258 | happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; | ||
259 | |||
260 | if (lNetworkEvents & FD_OOB) | ||
261 | happened |= (POLLPRI | POLLRDBAND) & sought; | ||
262 | |||
263 | return happened; | ||
264 | } | ||
265 | |||
266 | #else /* !MinGW */ | ||
267 | |||
268 | /* Convert select(2) returned fd_sets into poll(2) revents values. */ | ||
269 | static int | ||
270 | compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds) | ||
271 | { | ||
272 | int happened = 0; | ||
273 | if (FD_ISSET (fd, rfds)) | ||
274 | { | ||
275 | int r; | ||
276 | int socket_errno; | ||
277 | |||
278 | # if defined __MACH__ && defined __APPLE__ | ||
279 | /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK | ||
280 | for some kinds of descriptors. Detect if this descriptor is a | ||
281 | connected socket, a server socket, or something else using a | ||
282 | 0-byte recv, and use ioctl(2) to detect POLLHUP. */ | ||
283 | r = recv (fd, NULL, 0, MSG_PEEK); | ||
284 | socket_errno = (r < 0) ? errno : 0; | ||
285 | if (r == 0 || socket_errno == ENOTSOCK) | ||
286 | ioctl (fd, FIONREAD, &r); | ||
287 | # else | ||
288 | char data[64]; | ||
289 | r = recv (fd, data, sizeof (data), MSG_PEEK); | ||
290 | socket_errno = (r < 0) ? errno : 0; | ||
291 | # endif | ||
292 | if (r == 0) | ||
293 | happened |= POLLHUP; | ||
294 | |||
295 | /* If the event happened on an unconnected server socket, | ||
296 | that's fine. */ | ||
297 | else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN)) | ||
298 | happened |= (POLLIN | POLLRDNORM) & sought; | ||
299 | |||
300 | /* Distinguish hung-up sockets from other errors. */ | ||
301 | else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET | ||
302 | || socket_errno == ECONNABORTED || socket_errno == ENETRESET) | ||
303 | happened |= POLLHUP; | ||
304 | |||
305 | else | ||
306 | happened |= POLLERR; | ||
307 | } | ||
308 | |||
309 | if (FD_ISSET (fd, wfds)) | ||
310 | happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought; | ||
311 | |||
312 | if (FD_ISSET (fd, efds)) | ||
313 | happened |= (POLLPRI | POLLRDBAND) & sought; | ||
314 | |||
315 | return happened; | ||
316 | } | ||
317 | #endif /* !MinGW */ | ||
318 | |||
319 | int | ||
320 | poll (struct pollfd *pfd, nfds_t nfd, int timeout) | ||
321 | { | ||
322 | #ifndef WIN32_NATIVE | ||
323 | fd_set rfds, wfds, efds; | ||
324 | struct timeval tv; | ||
325 | struct timeval *ptv; | ||
326 | int maxfd, rc; | ||
327 | nfds_t i; | ||
328 | |||
329 | # ifdef _SC_OPEN_MAX | ||
330 | static int sc_open_max = -1; | ||
331 | |||
332 | if (nfd < 0 | ||
333 | || (nfd > sc_open_max | ||
334 | && (sc_open_max != -1 | ||
335 | || nfd > (sc_open_max = sysconf (_SC_OPEN_MAX))))) | ||
336 | { | ||
337 | errno = EINVAL; | ||
338 | return -1; | ||
339 | } | ||
340 | # else /* !_SC_OPEN_MAX */ | ||
341 | # ifdef OPEN_MAX | ||
342 | if (nfd < 0 || nfd > OPEN_MAX) | ||
343 | { | ||
344 | errno = EINVAL; | ||
345 | return -1; | ||
346 | } | ||
347 | # endif /* OPEN_MAX -- else, no check is needed */ | ||
348 | # endif /* !_SC_OPEN_MAX */ | ||
349 | |||
350 | /* EFAULT is not necessary to implement, but let's do it in the | ||
351 | simplest case. */ | ||
352 | if (!pfd) | ||
353 | { | ||
354 | errno = EFAULT; | ||
355 | return -1; | ||
356 | } | ||
357 | |||
358 | /* convert timeout number into a timeval structure */ | ||
359 | if (timeout == 0) | ||
360 | { | ||
361 | ptv = &tv; | ||
362 | ptv->tv_sec = 0; | ||
363 | ptv->tv_usec = 0; | ||
364 | } | ||
365 | else if (timeout > 0) | ||
366 | { | ||
367 | ptv = &tv; | ||
368 | ptv->tv_sec = timeout / 1000; | ||
369 | ptv->tv_usec = (timeout % 1000) * 1000; | ||
370 | } | ||
371 | else if (timeout == INFTIM) | ||
372 | /* wait forever */ | ||
373 | ptv = NULL; | ||
374 | else | ||
375 | { | ||
376 | errno = EINVAL; | ||
377 | return -1; | ||
378 | } | ||
379 | |||
380 | /* create fd sets and determine max fd */ | ||
381 | maxfd = -1; | ||
382 | FD_ZERO (&rfds); | ||
383 | FD_ZERO (&wfds); | ||
384 | FD_ZERO (&efds); | ||
385 | for (i = 0; i < nfd; i++) | ||
386 | { | ||
387 | if (pfd[i].fd < 0) | ||
388 | continue; | ||
389 | |||
390 | if (pfd[i].events & (POLLIN | POLLRDNORM)) | ||
391 | FD_SET (pfd[i].fd, &rfds); | ||
392 | |||
393 | /* see select(2): "the only exceptional condition detectable | ||
394 | is out-of-band data received on a socket", hence we push | ||
395 | POLLWRBAND events onto wfds instead of efds. */ | ||
396 | if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND)) | ||
397 | FD_SET (pfd[i].fd, &wfds); | ||
398 | if (pfd[i].events & (POLLPRI | POLLRDBAND)) | ||
399 | FD_SET (pfd[i].fd, &efds); | ||
400 | if (pfd[i].fd >= maxfd | ||
401 | && (pfd[i].events & (POLLIN | POLLOUT | POLLPRI | ||
402 | | POLLRDNORM | POLLRDBAND | ||
403 | | POLLWRNORM | POLLWRBAND))) | ||
404 | { | ||
405 | maxfd = pfd[i].fd; | ||
406 | if (maxfd > FD_SETSIZE) | ||
407 | { | ||
408 | errno = EOVERFLOW; | ||
409 | return -1; | ||
410 | } | ||
411 | } | ||
412 | } | ||
413 | |||
414 | /* examine fd sets */ | ||
415 | rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv); | ||
416 | if (rc < 0) | ||
417 | return rc; | ||
418 | |||
419 | /* establish results */ | ||
420 | rc = 0; | ||
421 | for (i = 0; i < nfd; i++) | ||
422 | if (pfd[i].fd < 0) | ||
423 | pfd[i].revents = 0; | ||
424 | else | ||
425 | { | ||
426 | int happened = compute_revents (pfd[i].fd, pfd[i].events, | ||
427 | &rfds, &wfds, &efds); | ||
428 | if (happened) | ||
429 | { | ||
430 | pfd[i].revents = happened; | ||
431 | rc++; | ||
432 | } | ||
433 | } | ||
434 | |||
435 | return rc; | ||
436 | #else | ||
437 | static struct timeval tv0; | ||
438 | static HANDLE hEvent; | ||
439 | WSANETWORKEVENTS ev; | ||
440 | HANDLE h, handle_array[FD_SETSIZE + 2]; | ||
441 | DWORD ret, wait_timeout, nhandles; | ||
442 | fd_set rfds, wfds, xfds; | ||
443 | BOOL poll_again; | ||
444 | MSG msg; | ||
445 | int rc = 0; | ||
446 | nfds_t i; | ||
447 | |||
448 | if (nfd < 0 || timeout < -1) | ||
449 | { | ||
450 | errno = EINVAL; | ||
451 | return -1; | ||
452 | } | ||
453 | |||
454 | if (!hEvent) | ||
455 | hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); | ||
456 | |||
457 | restart: | ||
458 | handle_array[0] = hEvent; | ||
459 | nhandles = 1; | ||
460 | FD_ZERO (&rfds); | ||
461 | FD_ZERO (&wfds); | ||
462 | FD_ZERO (&xfds); | ||
463 | |||
464 | /* Classify socket handles and create fd sets. */ | ||
465 | for (i = 0; i < nfd; i++) | ||
466 | { | ||
467 | int sought = pfd[i].events; | ||
468 | pfd[i].revents = 0; | ||
469 | if (pfd[i].fd < 0) | ||
470 | continue; | ||
471 | if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND | ||
472 | | POLLPRI | POLLRDBAND))) | ||
473 | continue; | ||
474 | |||
475 | h = (HANDLE) _get_osfhandle (pfd[i].fd); | ||
476 | assert (h != NULL); | ||
477 | if (IsSocketHandle (h)) | ||
478 | { | ||
479 | int requested = FD_CLOSE; | ||
480 | |||
481 | /* see above; socket handles are mapped onto select. */ | ||
482 | if (sought & (POLLIN | POLLRDNORM)) | ||
483 | { | ||
484 | requested |= FD_READ | FD_ACCEPT; | ||
485 | FD_SET ((SOCKET) h, &rfds); | ||
486 | } | ||
487 | if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND)) | ||
488 | { | ||
489 | requested |= FD_WRITE | FD_CONNECT; | ||
490 | FD_SET ((SOCKET) h, &wfds); | ||
491 | } | ||
492 | if (sought & (POLLPRI | POLLRDBAND)) | ||
493 | { | ||
494 | requested |= FD_OOB; | ||
495 | FD_SET ((SOCKET) h, &xfds); | ||
496 | } | ||
497 | |||
498 | if (requested) | ||
499 | WSAEventSelect ((SOCKET) h, hEvent, requested); | ||
500 | } | ||
501 | else | ||
502 | { | ||
503 | /* Poll now. If we get an event, do not poll again. Also, | ||
504 | screen buffer handles are waitable, and they'll block until | ||
505 | a character is available. win32_compute_revents eliminates | ||
506 | bits for the "wrong" direction. */ | ||
507 | pfd[i].revents = win32_compute_revents (h, &sought); | ||
508 | if (sought) | ||
509 | handle_array[nhandles++] = h; | ||
510 | if (pfd[i].revents) | ||
511 | timeout = 0; | ||
512 | } | ||
513 | } | ||
514 | |||
515 | if (select (0, &rfds, &wfds, &xfds, &tv0) > 0) | ||
516 | { | ||
517 | /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but | ||
518 | no need to call select again. */ | ||
519 | poll_again = FALSE; | ||
520 | wait_timeout = 0; | ||
521 | } | ||
522 | else | ||
523 | { | ||
524 | poll_again = TRUE; | ||
525 | if (timeout == INFTIM) | ||
526 | wait_timeout = INFINITE; | ||
527 | else | ||
528 | wait_timeout = timeout; | ||
529 | } | ||
530 | |||
531 | for (;;) | ||
532 | { | ||
533 | ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, | ||
534 | wait_timeout, QS_ALLINPUT); | ||
535 | |||
536 | if (ret == WAIT_OBJECT_0 + nhandles) | ||
537 | { | ||
538 | /* new input of some other kind */ | ||
539 | BOOL bRet; | ||
540 | while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) | ||
541 | { | ||
542 | TranslateMessage (&msg); | ||
543 | DispatchMessage (&msg); | ||
544 | } | ||
545 | } | ||
546 | else | ||
547 | break; | ||
548 | } | ||
549 | |||
550 | if (poll_again) | ||
551 | select (0, &rfds, &wfds, &xfds, &tv0); | ||
552 | |||
553 | /* Place a sentinel at the end of the array. */ | ||
554 | handle_array[nhandles] = NULL; | ||
555 | nhandles = 1; | ||
556 | for (i = 0; i < nfd; i++) | ||
557 | { | ||
558 | int happened; | ||
559 | |||
560 | if (pfd[i].fd < 0) | ||
561 | continue; | ||
562 | if (!(pfd[i].events & (POLLIN | POLLRDNORM | | ||
563 | POLLOUT | POLLWRNORM | POLLWRBAND))) | ||
564 | continue; | ||
565 | |||
566 | h = (HANDLE) _get_osfhandle (pfd[i].fd); | ||
567 | if (h != handle_array[nhandles]) | ||
568 | { | ||
569 | /* It's a socket. */ | ||
570 | WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); | ||
571 | WSAEventSelect ((SOCKET) h, 0, 0); | ||
572 | |||
573 | /* If we're lucky, WSAEnumNetworkEvents already provided a way | ||
574 | to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */ | ||
575 | if (FD_ISSET ((SOCKET) h, &rfds) | ||
576 | && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT))) | ||
577 | ev.lNetworkEvents |= FD_READ | FD_ACCEPT; | ||
578 | if (FD_ISSET ((SOCKET) h, &wfds)) | ||
579 | ev.lNetworkEvents |= FD_WRITE | FD_CONNECT; | ||
580 | if (FD_ISSET ((SOCKET) h, &xfds)) | ||
581 | ev.lNetworkEvents |= FD_OOB; | ||
582 | |||
583 | happened = win32_compute_revents_socket ((SOCKET) h, pfd[i].events, | ||
584 | ev.lNetworkEvents); | ||
585 | } | ||
586 | else | ||
587 | { | ||
588 | /* Not a socket. */ | ||
589 | int sought = pfd[i].events; | ||
590 | happened = win32_compute_revents (h, &sought); | ||
591 | nhandles++; | ||
592 | } | ||
593 | |||
594 | if ((pfd[i].revents |= happened) != 0) | ||
595 | rc++; | ||
596 | } | ||
597 | |||
598 | if (!rc && timeout == INFTIM) | ||
599 | { | ||
600 | SwitchToThread(); | ||
601 | goto restart; | ||
602 | } | ||
603 | |||
604 | return rc; | ||
605 | #endif | ||
606 | } | ||
diff --git a/win32/poll.h b/win32/poll.h new file mode 100644 index 000000000..b7aa59d97 --- /dev/null +++ b/win32/poll.h | |||
@@ -0,0 +1,53 @@ | |||
1 | /* Header for poll(2) emulation | ||
2 | Contributed by Paolo Bonzini. | ||
3 | |||
4 | Copyright 2001, 2002, 2003, 2007, 2009, 2010 Free Software Foundation, Inc. | ||
5 | |||
6 | This file is part of gnulib. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License along | ||
19 | with this program; if not, write to the Free Software Foundation, | ||
20 | Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ | ||
21 | |||
22 | #ifndef _GL_POLL_H | ||
23 | #define _GL_POLL_H | ||
24 | |||
25 | /* fake a poll(2) environment */ | ||
26 | #define POLLIN 0x0001 /* any readable data available */ | ||
27 | #define POLLPRI 0x0002 /* OOB/Urgent readable data */ | ||
28 | #define POLLOUT 0x0004 /* file descriptor is writeable */ | ||
29 | #define POLLERR 0x0008 /* some poll error occurred */ | ||
30 | #define POLLHUP 0x0010 /* file descriptor was "hung up" */ | ||
31 | #define POLLNVAL 0x0020 /* requested events "invalid" */ | ||
32 | #define POLLRDNORM 0x0040 | ||
33 | #define POLLRDBAND 0x0080 | ||
34 | #define POLLWRNORM 0x0100 | ||
35 | #define POLLWRBAND 0x0200 | ||
36 | |||
37 | struct pollfd | ||
38 | { | ||
39 | int fd; /* which file descriptor to poll */ | ||
40 | short events; /* events we are interested in */ | ||
41 | short revents; /* events found on return */ | ||
42 | }; | ||
43 | |||
44 | typedef unsigned long nfds_t; | ||
45 | |||
46 | extern int poll (struct pollfd *pfd, nfds_t nfd, int timeout); | ||
47 | |||
48 | /* Define INFTIM only if doing so conforms to POSIX. */ | ||
49 | #if !defined (_POSIX_C_SOURCE) && !defined (_XOPEN_SOURCE) | ||
50 | #define INFTIM (-1) | ||
51 | #endif | ||
52 | |||
53 | #endif /* _GL_POLL_H */ | ||
diff --git a/win32/popen.c b/win32/popen.c new file mode 100644 index 000000000..6b8e52ca9 --- /dev/null +++ b/win32/popen.c | |||
@@ -0,0 +1,318 @@ | |||
1 | #include <fcntl.h> | ||
2 | #include "libbb.h" | ||
3 | |||
4 | typedef struct { | ||
5 | PROCESS_INFORMATION piProcInfo; | ||
6 | HANDLE pipe[2]; | ||
7 | char mode; | ||
8 | int fd; | ||
9 | } pipe_data; | ||
10 | |||
11 | static pipe_data *pipes = NULL; | ||
12 | static int num_pipes = 0; | ||
13 | |||
14 | static int mingw_pipe(HANDLE *readwrite) | ||
15 | { | ||
16 | SECURITY_ATTRIBUTES sa; | ||
17 | |||
18 | sa.nLength = sizeof(sa); /* Length in bytes */ | ||
19 | sa.bInheritHandle = 1; /* the child must inherit these handles */ | ||
20 | sa.lpSecurityDescriptor = NULL; | ||
21 | |||
22 | if ( !CreatePipe (&readwrite[0], &readwrite[1], &sa, 1 << 13) ) { | ||
23 | return -1; | ||
24 | } | ||
25 | |||
26 | return 0; | ||
27 | } | ||
28 | |||
29 | static pipe_data *find_pipe(void) | ||
30 | { | ||
31 | int i; | ||
32 | pipe_data *p = NULL; | ||
33 | |||
34 | /* find an unused pipe structure */ | ||
35 | for ( i=0; i<num_pipes; ++i ) { | ||
36 | if ( pipes[i].mode == '\0' ) { | ||
37 | p = pipes+i; | ||
38 | break; | ||
39 | } | ||
40 | } | ||
41 | |||
42 | if ( p == NULL ) { | ||
43 | /* need to extend array */ | ||
44 | if ( (p=realloc(pipes, sizeof(pipe_data)*(num_pipes+10))) == NULL ) { | ||
45 | return NULL; | ||
46 | } | ||
47 | |||
48 | pipes = p; | ||
49 | for ( i=0; i<10; ++i ) { | ||
50 | memset(pipes+num_pipes+i, 0, sizeof(pipe_data)); | ||
51 | } | ||
52 | p = pipes + num_pipes; | ||
53 | num_pipes += 10; | ||
54 | } | ||
55 | |||
56 | p->pipe[0] = INVALID_HANDLE_VALUE; | ||
57 | p->pipe[1] = INVALID_HANDLE_VALUE; | ||
58 | |||
59 | return p; | ||
60 | } | ||
61 | |||
62 | FILE *mingw_popen(const char *cmd, const char *mode) | ||
63 | { | ||
64 | pipe_data *p; | ||
65 | FILE *fptr = NULL; | ||
66 | STARTUPINFO siStartInfo; | ||
67 | int success; | ||
68 | int fd; | ||
69 | int len, count; | ||
70 | int ip, ic; | ||
71 | char *cmd_buff = NULL; | ||
72 | const char *s; | ||
73 | char *t; | ||
74 | |||
75 | if ( cmd == NULL || *cmd == '\0' || mode == NULL || | ||
76 | (*mode != 'r' && *mode != 'w') ) { | ||
77 | return NULL; | ||
78 | } | ||
79 | |||
80 | /* find an unused pipe structure */ | ||
81 | if ( (p=find_pipe()) == NULL ) { | ||
82 | return NULL; | ||
83 | } | ||
84 | |||
85 | /* count double quotes */ | ||
86 | count = 0; | ||
87 | for ( s=cmd; *s; ++s ) { | ||
88 | if ( *s == '"' ) { | ||
89 | ++count; | ||
90 | } | ||
91 | } | ||
92 | |||
93 | len = strlen(cmd) + 10 + count; | ||
94 | if ( (cmd_buff=malloc(len)) == NULL ) { | ||
95 | return NULL; | ||
96 | } | ||
97 | |||
98 | /* escape double quotes */ | ||
99 | strcpy(cmd_buff, "sh -c \""); | ||
100 | for ( s=cmd,t=cmd_buff+strlen(cmd_buff); *s; ++s ) { | ||
101 | if ( *s == '"' ) { | ||
102 | *t++ = '\\'; | ||
103 | } | ||
104 | *t++ = *s; | ||
105 | } | ||
106 | *t++ = '"'; | ||
107 | *t = '\0'; | ||
108 | |||
109 | /* Create the pipe */ | ||
110 | if ( mingw_pipe(p->pipe) == -1 ) { | ||
111 | goto finito; | ||
112 | } | ||
113 | |||
114 | /* index of parent end of pipe */ | ||
115 | ip = !(*mode == 'r'); | ||
116 | /* index of child end of pipe */ | ||
117 | ic = (*mode == 'r'); | ||
118 | |||
119 | /* Make the parent end of the pipe non-inheritable */ | ||
120 | SetHandleInformation(p->pipe[ip], HANDLE_FLAG_INHERIT, 0); | ||
121 | |||
122 | /* Now create the child process */ | ||
123 | ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); | ||
124 | siStartInfo.cb = sizeof(STARTUPINFO); | ||
125 | if ( *mode == 'r' ) { | ||
126 | siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE); | ||
127 | siStartInfo.hStdOutput = p->pipe[ic]; | ||
128 | } | ||
129 | else { | ||
130 | siStartInfo.hStdInput = p->pipe[ic]; | ||
131 | siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE); | ||
132 | } | ||
133 | siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); | ||
134 | siStartInfo.wShowWindow = SW_HIDE; | ||
135 | siStartInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; | ||
136 | |||
137 | success = CreateProcess(NULL, | ||
138 | (LPTSTR)cmd_buff, /* command line */ | ||
139 | NULL, /* process security attributes */ | ||
140 | NULL, /* primary thread security attributes */ | ||
141 | TRUE, /* handles are inherited */ | ||
142 | 0, /* creation flags */ | ||
143 | NULL, /* use parent's environment */ | ||
144 | NULL, /* use parent's current directory */ | ||
145 | &siStartInfo, /* STARTUPINFO pointer */ | ||
146 | &p->piProcInfo); /* receives PROCESS_INFORMATION */ | ||
147 | |||
148 | if ( !success ) { | ||
149 | goto finito; | ||
150 | } | ||
151 | |||
152 | /* close child end of pipe */ | ||
153 | CloseHandle(p->pipe[ic]); | ||
154 | p->pipe[ic] = INVALID_HANDLE_VALUE; | ||
155 | |||
156 | if ( *mode == 'r' ) { | ||
157 | fd = _open_osfhandle((intptr_t)p->pipe[ip], _O_RDONLY|_O_BINARY); | ||
158 | fptr = _fdopen(fd, "rb"); | ||
159 | } | ||
160 | else { | ||
161 | fd = _open_osfhandle((intptr_t)p->pipe[ip], _O_WRONLY|_O_BINARY); | ||
162 | fptr = _fdopen(fd, "wb"); | ||
163 | } | ||
164 | |||
165 | finito: | ||
166 | if ( !fptr ) { | ||
167 | if ( p->pipe[0] != INVALID_HANDLE_VALUE ) { | ||
168 | CloseHandle(p->pipe[0]); | ||
169 | } | ||
170 | if ( p->pipe[1] != INVALID_HANDLE_VALUE ) { | ||
171 | CloseHandle(p->pipe[1]); | ||
172 | } | ||
173 | } | ||
174 | else { | ||
175 | p->mode = *mode; | ||
176 | p->fd = fd; | ||
177 | } | ||
178 | free(cmd_buff); | ||
179 | |||
180 | return fptr; | ||
181 | } | ||
182 | |||
183 | /* | ||
184 | * Open a pipe to a command where the file descriptor fd0 is used | ||
185 | * as input to the command (read mode) or as the destination of the | ||
186 | * output from the command (write mode). The pid of the command is | ||
187 | * returned in the variable pid, which can be NULL. | ||
188 | */ | ||
189 | int mingw_popen_fd(const char *cmd, const char *mode, int fd0, pid_t *pid) | ||
190 | { | ||
191 | pipe_data *p; | ||
192 | STARTUPINFO siStartInfo; | ||
193 | int success; | ||
194 | int fd = -1; | ||
195 | int ip, ic; | ||
196 | |||
197 | if ( cmd == NULL || *cmd == '\0' || mode == NULL || | ||
198 | (*mode != 'r' && *mode != 'w') ) { | ||
199 | return -1; | ||
200 | } | ||
201 | |||
202 | /* find an unused pipe structure */ | ||
203 | if ( (p=find_pipe()) == NULL ) { | ||
204 | return -1; | ||
205 | } | ||
206 | |||
207 | /* Create the pipe */ | ||
208 | if ( mingw_pipe(p->pipe) == -1 ) { | ||
209 | goto finito; | ||
210 | } | ||
211 | |||
212 | /* index of parent end of pipe */ | ||
213 | ip = !(*mode == 'r'); | ||
214 | /* index of child end of pipe */ | ||
215 | ic = (*mode == 'r'); | ||
216 | |||
217 | /* Make the parent end of the pipe non-inheritable */ | ||
218 | SetHandleInformation(p->pipe[ip], HANDLE_FLAG_INHERIT, 0); | ||
219 | |||
220 | /* Now create the child process */ | ||
221 | ZeroMemory(&siStartInfo, sizeof(STARTUPINFO)); | ||
222 | siStartInfo.cb = sizeof(STARTUPINFO); | ||
223 | if ( *mode == 'r' ) { | ||
224 | siStartInfo.hStdInput = (HANDLE)_get_osfhandle(fd0); | ||
225 | siStartInfo.hStdOutput = p->pipe[ic]; | ||
226 | } | ||
227 | else { | ||
228 | siStartInfo.hStdInput = p->pipe[ic]; | ||
229 | siStartInfo.hStdOutput = (HANDLE)_get_osfhandle(fd0); | ||
230 | } | ||
231 | siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE); | ||
232 | siStartInfo.wShowWindow = SW_HIDE; | ||
233 | siStartInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW; | ||
234 | |||
235 | success = CreateProcess(NULL, | ||
236 | (LPTSTR)cmd, /* command line */ | ||
237 | NULL, /* process security attributes */ | ||
238 | NULL, /* primary thread security attributes */ | ||
239 | TRUE, /* handles are inherited */ | ||
240 | 0, /* creation flags */ | ||
241 | NULL, /* use parent's environment */ | ||
242 | NULL, /* use parent's current directory */ | ||
243 | &siStartInfo, /* STARTUPINFO pointer */ | ||
244 | &p->piProcInfo); /* receives PROCESS_INFORMATION */ | ||
245 | |||
246 | if ( !success ) { | ||
247 | goto finito; | ||
248 | } | ||
249 | |||
250 | /* close child end of pipe */ | ||
251 | CloseHandle(p->pipe[ic]); | ||
252 | p->pipe[ic] = INVALID_HANDLE_VALUE; | ||
253 | |||
254 | if ( *mode == 'r' ) { | ||
255 | fd = _open_osfhandle((intptr_t)p->pipe[ip], _O_RDONLY|_O_BINARY); | ||
256 | } | ||
257 | else { | ||
258 | fd = _open_osfhandle((intptr_t)p->pipe[ip], _O_WRONLY|_O_BINARY); | ||
259 | } | ||
260 | |||
261 | finito: | ||
262 | if ( fd == -1 ) { | ||
263 | if ( p->pipe[0] != INVALID_HANDLE_VALUE ) { | ||
264 | CloseHandle(p->pipe[0]); | ||
265 | } | ||
266 | if ( p->pipe[1] != INVALID_HANDLE_VALUE ) { | ||
267 | CloseHandle(p->pipe[1]); | ||
268 | } | ||
269 | } | ||
270 | else { | ||
271 | p->mode = *mode; | ||
272 | p->fd = fd; | ||
273 | if ( pid ) { | ||
274 | *pid = (pid_t)p->piProcInfo.dwProcessId; | ||
275 | } | ||
276 | } | ||
277 | |||
278 | return fd; | ||
279 | } | ||
280 | |||
281 | int mingw_pclose(FILE *fp) | ||
282 | { | ||
283 | int i, ip, fd; | ||
284 | pipe_data *p = NULL; | ||
285 | DWORD ret; | ||
286 | |||
287 | if ( fp == NULL ) { | ||
288 | return -1; | ||
289 | } | ||
290 | |||
291 | /* find struct containing fd */ | ||
292 | fd = fileno(fp); | ||
293 | for ( i=0; i<num_pipes; ++i ) { | ||
294 | if ( pipes[i].mode && pipes[i].fd == fd ) { | ||
295 | p = pipes+i; | ||
296 | break; | ||
297 | } | ||
298 | } | ||
299 | |||
300 | if ( p == NULL ) { | ||
301 | /* no pipe data, maybe fd isn't a pipe? */ | ||
302 | return -1; | ||
303 | } | ||
304 | |||
305 | fclose(fp); | ||
306 | |||
307 | ip = !(p->mode == 'r'); | ||
308 | CloseHandle(p->pipe[ip]); | ||
309 | |||
310 | ret = WaitForSingleObject(p->piProcInfo.hProcess, INFINITE); | ||
311 | |||
312 | CloseHandle(p->piProcInfo.hProcess); | ||
313 | CloseHandle(p->piProcInfo.hThread); | ||
314 | |||
315 | p->mode = '\0'; | ||
316 | |||
317 | return (ret == WAIT_OBJECT_0) ? 0 : -1; | ||
318 | } | ||
diff --git a/win32/process.c b/win32/process.c new file mode 100644 index 000000000..968ea9afd --- /dev/null +++ b/win32/process.c | |||
@@ -0,0 +1,425 @@ | |||
1 | #include "libbb.h" | ||
2 | #include <tlhelp32.h> | ||
3 | |||
4 | int waitpid(pid_t pid, int *status, int options) | ||
5 | { | ||
6 | HANDLE proc; | ||
7 | intptr_t ret; | ||
8 | |||
9 | /* Windows does not understand parent-child */ | ||
10 | if (pid > 0 && options == 0) { | ||
11 | if ( (proc=OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION, | ||
12 | FALSE, pid)) != NULL ) { | ||
13 | ret = _cwait(status, (intptr_t)proc, 0); | ||
14 | CloseHandle(proc); | ||
15 | return ret == -1 ? -1 : pid; | ||
16 | } | ||
17 | } | ||
18 | errno = EINVAL; | ||
19 | return -1; | ||
20 | } | ||
21 | |||
22 | const char * | ||
23 | next_path_sep(const char *path) | ||
24 | { | ||
25 | static const char *from = NULL, *to; | ||
26 | static int has_semicolon; | ||
27 | int len = strlen(path); | ||
28 | |||
29 | if (!from || !(path >= from && path+len <= to)) { | ||
30 | from = path; | ||
31 | to = from+len; | ||
32 | has_semicolon = strchr(path, ';') != NULL; | ||
33 | } | ||
34 | |||
35 | /* Semicolons take precedence, it's Windows PATH */ | ||
36 | if (has_semicolon) | ||
37 | return strchr(path, ';'); | ||
38 | /* PATH=C:, not really a separator */ | ||
39 | return strchr(has_dos_drive_prefix(path) ? path+2 : path, ':'); | ||
40 | } | ||
41 | |||
42 | #define MAX_OPT 10 | ||
43 | |||
44 | static const char * | ||
45 | parse_interpreter(const char *cmd, char ***opts, int *nopts) | ||
46 | { | ||
47 | static char buf[100], *opt[MAX_OPT]; | ||
48 | char *p, *s, *t; | ||
49 | int n, fd; | ||
50 | |||
51 | *nopts = 0; | ||
52 | *opts = opt; | ||
53 | |||
54 | /* don't even try a .exe */ | ||
55 | n = strlen(cmd); | ||
56 | if (n >= 4 && | ||
57 | (!strcasecmp(cmd+n-4, ".exe") || | ||
58 | !strcasecmp(cmd+n-4, ".com"))) | ||
59 | return NULL; | ||
60 | |||
61 | fd = open(cmd, O_RDONLY); | ||
62 | if (fd < 0) | ||
63 | return NULL; | ||
64 | n = read(fd, buf, sizeof(buf)-1); | ||
65 | close(fd); | ||
66 | if (n < 4) /* at least '#!/x' and not error */ | ||
67 | return NULL; | ||
68 | |||
69 | /* | ||
70 | * See http://www.in-ulm.de/~mascheck/various/shebang/ for trivia | ||
71 | * relating to '#!'. | ||
72 | */ | ||
73 | if (buf[0] != '#' || buf[1] != '!') | ||
74 | return NULL; | ||
75 | buf[n] = '\0'; | ||
76 | p = strchr(buf, '\n'); | ||
77 | if (!p) | ||
78 | return NULL; | ||
79 | *p = '\0'; | ||
80 | |||
81 | /* remove trailing whitespace */ | ||
82 | while ( isspace(*--p) ) { | ||
83 | *p = '\0'; | ||
84 | } | ||
85 | |||
86 | /* skip whitespace after '#!' */ | ||
87 | for ( s=buf+2; *s && isspace(*s); ++s ) { | ||
88 | } | ||
89 | |||
90 | /* move to end of interpreter path (which may not contain spaces) */ | ||
91 | for ( ; *s && !isspace(*s); ++s ) { | ||
92 | } | ||
93 | |||
94 | n = 0; | ||
95 | if ( *s != '\0' ) { | ||
96 | /* there are options */ | ||
97 | *s++ = '\0'; | ||
98 | |||
99 | while ( (t=strtok(s, " \t")) && n < MAX_OPT ) { | ||
100 | s = NULL; | ||
101 | opt[n++] = t; | ||
102 | } | ||
103 | } | ||
104 | |||
105 | /* find interpreter name */ | ||
106 | if (!(p = strrchr(buf+2, '/'))) | ||
107 | return NULL; | ||
108 | |||
109 | *nopts = n; | ||
110 | *opts = opt; | ||
111 | |||
112 | return p+1; | ||
113 | } | ||
114 | |||
115 | /* | ||
116 | * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx | ||
117 | * (Parsing C++ Command-Line Arguments) | ||
118 | */ | ||
119 | static char * | ||
120 | quote_arg(const char *arg) | ||
121 | { | ||
122 | int len = 0, n = 0; | ||
123 | int force_quotes = 0; | ||
124 | char *q, *d; | ||
125 | const char *p = arg; | ||
126 | |||
127 | /* empty arguments must be quoted */ | ||
128 | if (!*p) { | ||
129 | force_quotes = 1; | ||
130 | } | ||
131 | |||
132 | while (*p) { | ||
133 | if (isspace(*p)) { | ||
134 | /* arguments containing whitespace must be quoted */ | ||
135 | force_quotes = 1; | ||
136 | } | ||
137 | else if (*p == '"') { | ||
138 | /* double quotes in arguments need to be escaped */ | ||
139 | n++; | ||
140 | } | ||
141 | else if (*p == '\\') { | ||
142 | /* count contiguous backslashes */ | ||
143 | int count = 0; | ||
144 | while (*p == '\\') { | ||
145 | count++; | ||
146 | p++; | ||
147 | len++; | ||
148 | } | ||
149 | |||
150 | /* | ||
151 | * Only escape backslashes before explicit double quotes or | ||
152 | * or where the backslashes are at the end of an argument | ||
153 | * that is scheduled to be quoted. | ||
154 | */ | ||
155 | if (*p == '"' || (force_quotes && *p == '\0')) { | ||
156 | n += count*2 + 1; | ||
157 | } | ||
158 | |||
159 | if (*p == '\0') { | ||
160 | break; | ||
161 | } | ||
162 | continue; | ||
163 | } | ||
164 | len++; | ||
165 | p++; | ||
166 | } | ||
167 | |||
168 | if (!force_quotes && n == 0) { | ||
169 | return (char*)arg; | ||
170 | } | ||
171 | |||
172 | /* insert double quotes and backslashes where necessary */ | ||
173 | d = q = xmalloc(len+n+3); | ||
174 | if (force_quotes) { | ||
175 | *d++ = '"'; | ||
176 | } | ||
177 | |||
178 | while (*arg) { | ||
179 | if (*arg == '"') { | ||
180 | *d++ = '\\'; | ||
181 | } | ||
182 | else if (*arg == '\\') { | ||
183 | int count = 0; | ||
184 | while (*arg == '\\') { | ||
185 | count++; | ||
186 | *d++ = *arg++; | ||
187 | } | ||
188 | |||
189 | if (*arg == '"' || (force_quotes && *arg == '\0')) { | ||
190 | while (count-- > 0) { | ||
191 | *d++ = '\\'; | ||
192 | } | ||
193 | if (*arg == '"') { | ||
194 | *d++ = '\\'; | ||
195 | } | ||
196 | } | ||
197 | } | ||
198 | if (*arg != '\0') { | ||
199 | *d++ = *arg++; | ||
200 | } | ||
201 | } | ||
202 | if (force_quotes) { | ||
203 | *d++ = '"'; | ||
204 | } | ||
205 | *d = '\0'; | ||
206 | |||
207 | return q; | ||
208 | } | ||
209 | |||
210 | static intptr_t | ||
211 | spawnveq(int mode, const char *path, const char *const *argv, const char *const *env) | ||
212 | { | ||
213 | char **new_argv; | ||
214 | int i, argc = 0; | ||
215 | intptr_t ret; | ||
216 | |||
217 | if (!argv) { | ||
218 | const char *empty_argv[] = { path, NULL }; | ||
219 | return spawnve(mode, path, empty_argv, env); | ||
220 | } | ||
221 | |||
222 | |||
223 | while (argv[argc]) | ||
224 | argc++; | ||
225 | |||
226 | new_argv = malloc(sizeof(*argv)*(argc+1)); | ||
227 | for (i = 0;i < argc;i++) | ||
228 | new_argv[i] = quote_arg(argv[i]); | ||
229 | new_argv[argc] = NULL; | ||
230 | ret = spawnve(mode, path, (const char *const *)new_argv, env); | ||
231 | for (i = 0;i < argc;i++) | ||
232 | if (new_argv[i] != argv[i]) | ||
233 | free(new_argv[i]); | ||
234 | free(new_argv); | ||
235 | return ret; | ||
236 | } | ||
237 | |||
238 | #if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE | ||
239 | static intptr_t | ||
240 | mingw_spawn_applet(int mode, | ||
241 | const char *const *argv, | ||
242 | const char *const *envp) | ||
243 | { | ||
244 | return spawnveq(mode, bb_busybox_exec_path, argv, envp); | ||
245 | } | ||
246 | #endif | ||
247 | |||
248 | static intptr_t | ||
249 | mingw_spawn_interpreter(int mode, const char *prog, const char *const *argv, const char *const *envp) | ||
250 | { | ||
251 | intptr_t ret; | ||
252 | char **opts; | ||
253 | int nopts; | ||
254 | const char *interpr = parse_interpreter(prog, &opts, &nopts); | ||
255 | const char **new_argv; | ||
256 | int argc = 0; | ||
257 | |||
258 | if (!interpr) | ||
259 | return spawnveq(mode, prog, argv, envp); | ||
260 | |||
261 | |||
262 | while (argv[argc]) | ||
263 | argc++; | ||
264 | new_argv = malloc(sizeof(*argv)*(argc+nopts+2)); | ||
265 | memcpy(new_argv+1, opts, sizeof(*opts)*nopts); | ||
266 | memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc); | ||
267 | new_argv[nopts+1] = prog; /* pass absolute path */ | ||
268 | |||
269 | #if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE | ||
270 | if (find_applet_by_name(interpr) >= 0) { | ||
271 | new_argv[0] = interpr; | ||
272 | ret = mingw_spawn_applet(mode, new_argv, envp); | ||
273 | } else | ||
274 | #endif | ||
275 | { | ||
276 | char *path = xstrdup(getenv("PATH")); | ||
277 | char *tmp = path; | ||
278 | char *iprog = find_executable(interpr, &tmp); | ||
279 | free(path); | ||
280 | if (!iprog) { | ||
281 | free(new_argv); | ||
282 | errno = ENOENT; | ||
283 | return -1; | ||
284 | } | ||
285 | new_argv[0] = iprog; | ||
286 | ret = spawnveq(mode, iprog, new_argv, envp); | ||
287 | free(iprog); | ||
288 | } | ||
289 | |||
290 | free(new_argv); | ||
291 | return ret; | ||
292 | } | ||
293 | |||
294 | static intptr_t | ||
295 | mingw_spawn_1(int mode, const char *cmd, const char *const *argv, const char *const *envp) | ||
296 | { | ||
297 | intptr_t ret; | ||
298 | |||
299 | #if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE | ||
300 | if (find_applet_by_name(cmd) >= 0) | ||
301 | return mingw_spawn_applet(mode, argv, envp); | ||
302 | else | ||
303 | #endif | ||
304 | if (strchr(cmd, '/') || strchr(cmd, '\\')) | ||
305 | return mingw_spawn_interpreter(mode, cmd, argv, envp); | ||
306 | else { | ||
307 | char *tmp, *path = getenv("PATH"); | ||
308 | char *prog; | ||
309 | |||
310 | if (!path) { | ||
311 | errno = ENOENT; | ||
312 | return -1; | ||
313 | } | ||
314 | |||
315 | /* executable_exists() does not return new file name */ | ||
316 | tmp = path = xstrdup(path); | ||
317 | prog = find_executable(cmd, &tmp); | ||
318 | free(path); | ||
319 | if (!prog) { | ||
320 | errno = ENOENT; | ||
321 | return -1; | ||
322 | } | ||
323 | ret = mingw_spawn_interpreter(mode, prog, argv, envp); | ||
324 | free(prog); | ||
325 | } | ||
326 | return ret; | ||
327 | } | ||
328 | |||
329 | pid_t FAST_FUNC | ||
330 | mingw_spawn(char **argv) | ||
331 | { | ||
332 | intptr_t ret; | ||
333 | |||
334 | ret = mingw_spawn_1(P_NOWAIT, argv[0], (const char *const *)argv, | ||
335 | (const char *const *)environ); | ||
336 | |||
337 | return ret == -1 ? -1 : GetProcessId((HANDLE)ret); | ||
338 | } | ||
339 | |||
340 | intptr_t FAST_FUNC | ||
341 | mingw_spawn_proc(char **argv) | ||
342 | { | ||
343 | return mingw_spawn_1(P_NOWAIT, argv[0], (const char *const *)argv, | ||
344 | (const char *const *)environ); | ||
345 | } | ||
346 | |||
347 | int | ||
348 | mingw_execvp(const char *cmd, const char *const *argv) | ||
349 | { | ||
350 | int ret = (int)mingw_spawn_1(P_WAIT, cmd, argv, (const char *const *)environ); | ||
351 | if (ret != -1) | ||
352 | exit(ret); | ||
353 | return ret; | ||
354 | } | ||
355 | |||
356 | int | ||
357 | mingw_execve(const char *cmd, const char *const *argv, const char *const *envp) | ||
358 | { | ||
359 | int ret; | ||
360 | int mode = P_WAIT; | ||
361 | |||
362 | ret = (int)mingw_spawn_interpreter(mode, cmd, argv, envp); | ||
363 | if (ret != -1) | ||
364 | exit(ret); | ||
365 | return ret; | ||
366 | } | ||
367 | |||
368 | int | ||
369 | mingw_execv(const char *cmd, const char *const *argv) | ||
370 | { | ||
371 | return mingw_execve(cmd, argv, (const char *const *)environ); | ||
372 | } | ||
373 | |||
374 | /* POSIX version in libbb/procps.c */ | ||
375 | procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags UNUSED_PARAM) | ||
376 | { | ||
377 | PROCESSENTRY32 pe; | ||
378 | |||
379 | pe.dwSize = sizeof(pe); | ||
380 | if (!sp) { | ||
381 | sp = xzalloc(sizeof(struct procps_status_t)); | ||
382 | sp->snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); | ||
383 | if (sp->snapshot == INVALID_HANDLE_VALUE) { | ||
384 | free(sp); | ||
385 | return NULL; | ||
386 | } | ||
387 | if (!Process32First(sp->snapshot, &pe)) { | ||
388 | CloseHandle(sp->snapshot); | ||
389 | free(sp); | ||
390 | return NULL; | ||
391 | } | ||
392 | } | ||
393 | else { | ||
394 | if (!Process32Next(sp->snapshot, &pe)) { | ||
395 | CloseHandle(sp->snapshot); | ||
396 | free(sp); | ||
397 | return NULL; | ||
398 | } | ||
399 | } | ||
400 | |||
401 | sp->pid = pe.th32ProcessID; | ||
402 | safe_strncpy(sp->comm, pe.szExeFile, COMM_LEN); | ||
403 | return sp; | ||
404 | } | ||
405 | |||
406 | int kill(pid_t pid, int sig) | ||
407 | { | ||
408 | HANDLE h; | ||
409 | |||
410 | if (pid > 0 && sig == SIGTERM) { | ||
411 | if ((h=OpenProcess(PROCESS_TERMINATE, FALSE, pid)) != NULL && | ||
412 | TerminateProcess(h, 0)) { | ||
413 | CloseHandle(h); | ||
414 | return 0; | ||
415 | } | ||
416 | |||
417 | errno = err_win_to_posix(GetLastError()); | ||
418 | if (h != NULL) | ||
419 | CloseHandle(h); | ||
420 | return -1; | ||
421 | } | ||
422 | |||
423 | errno = EINVAL; | ||
424 | return -1; | ||
425 | } | ||
diff --git a/win32/pwd.h b/win32/pwd.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/pwd.h | |||
diff --git a/win32/regcomp.c b/win32/regcomp.c new file mode 100644 index 000000000..dca7e6ef3 --- /dev/null +++ b/win32/regcomp.c | |||
@@ -0,0 +1,3886 @@ | |||
1 | /* Extended regular expression matching and search library. | ||
2 | Copyright (C) 2002-2007,2009,2010 Free Software Foundation, Inc. | ||
3 | This file is part of the GNU C Library. | ||
4 | Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. | ||
5 | |||
6 | The GNU C Library is free software; you can redistribute it and/or | ||
7 | modify it under the terms of the GNU Lesser General Public | ||
8 | License as published by the Free Software Foundation; either | ||
9 | version 2.1 of the License, or (at your option) any later version. | ||
10 | |||
11 | The GNU C Library is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | Lesser General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Lesser General Public | ||
17 | License along with the GNU C Library; if not, write to the Free | ||
18 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | 02110-1301 USA. */ | ||
20 | |||
21 | #define UNUSED_PARAM __attribute__ ((__unused__)) | ||
22 | |||
23 | static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern, | ||
24 | size_t length, reg_syntax_t syntax); | ||
25 | static void re_compile_fastmap_iter (regex_t *bufp, | ||
26 | const re_dfastate_t *init_state, | ||
27 | char *fastmap); | ||
28 | static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len); | ||
29 | #ifdef RE_ENABLE_I18N | ||
30 | static void free_charset (re_charset_t *cset); | ||
31 | #endif /* RE_ENABLE_I18N */ | ||
32 | static void free_workarea_compile (regex_t *preg); | ||
33 | static reg_errcode_t create_initial_state (re_dfa_t *dfa); | ||
34 | #ifdef RE_ENABLE_I18N | ||
35 | static void optimize_utf8 (re_dfa_t *dfa); | ||
36 | #endif | ||
37 | static reg_errcode_t analyze (regex_t *preg); | ||
38 | static reg_errcode_t preorder (bin_tree_t *root, | ||
39 | reg_errcode_t (fn (void *, bin_tree_t *)), | ||
40 | void *extra); | ||
41 | static reg_errcode_t postorder (bin_tree_t *root, | ||
42 | reg_errcode_t (fn (void *, bin_tree_t *)), | ||
43 | void *extra); | ||
44 | static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node); | ||
45 | static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node); | ||
46 | static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg, | ||
47 | bin_tree_t *node); | ||
48 | static reg_errcode_t calc_first (void *extra, bin_tree_t *node); | ||
49 | static reg_errcode_t calc_next (void *extra, bin_tree_t *node); | ||
50 | static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node); | ||
51 | static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint); | ||
52 | static int search_duplicated_node (const re_dfa_t *dfa, int org_node, | ||
53 | unsigned int constraint); | ||
54 | static reg_errcode_t calc_eclosure (re_dfa_t *dfa); | ||
55 | static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, | ||
56 | int node, int root); | ||
57 | static reg_errcode_t calc_inveclosure (re_dfa_t *dfa); | ||
58 | static int fetch_number (re_string_t *input, re_token_t *token, | ||
59 | reg_syntax_t syntax); | ||
60 | static int peek_token (re_token_t *token, re_string_t *input, | ||
61 | reg_syntax_t syntax) internal_function; | ||
62 | static bin_tree_t *parse (re_string_t *regexp, regex_t *preg, | ||
63 | reg_syntax_t syntax, reg_errcode_t *err); | ||
64 | static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg, | ||
65 | re_token_t *token, reg_syntax_t syntax, | ||
66 | int nest, reg_errcode_t *err); | ||
67 | static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg, | ||
68 | re_token_t *token, reg_syntax_t syntax, | ||
69 | int nest, reg_errcode_t *err); | ||
70 | static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg, | ||
71 | re_token_t *token, reg_syntax_t syntax, | ||
72 | int nest, reg_errcode_t *err); | ||
73 | static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg, | ||
74 | re_token_t *token, reg_syntax_t syntax, | ||
75 | int nest, reg_errcode_t *err); | ||
76 | static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp, | ||
77 | re_dfa_t *dfa, re_token_t *token, | ||
78 | reg_syntax_t syntax, reg_errcode_t *err); | ||
79 | static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, | ||
80 | re_token_t *token, reg_syntax_t syntax, | ||
81 | reg_errcode_t *err); | ||
82 | static reg_errcode_t parse_bracket_element (bracket_elem_t *elem, | ||
83 | re_string_t *regexp, | ||
84 | re_token_t *token, int token_len, | ||
85 | re_dfa_t *dfa, | ||
86 | reg_syntax_t syntax, | ||
87 | int accept_hyphen); | ||
88 | static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem, | ||
89 | re_string_t *regexp, | ||
90 | re_token_t *token); | ||
91 | #ifdef RE_ENABLE_I18N | ||
92 | static reg_errcode_t build_equiv_class (bitset_t sbcset, | ||
93 | re_charset_t *mbcset, | ||
94 | int *equiv_class_alloc, | ||
95 | const unsigned char *name); | ||
96 | static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, | ||
97 | bitset_t sbcset, | ||
98 | re_charset_t *mbcset, | ||
99 | int *char_class_alloc, | ||
100 | const char *class_name, | ||
101 | reg_syntax_t syntax); | ||
102 | #else /* not RE_ENABLE_I18N */ | ||
103 | static reg_errcode_t build_equiv_class (bitset_t sbcset, | ||
104 | const unsigned char *name); | ||
105 | static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans, | ||
106 | bitset_t sbcset, | ||
107 | const char *class_name, | ||
108 | reg_syntax_t syntax); | ||
109 | #endif /* not RE_ENABLE_I18N */ | ||
110 | static bin_tree_t *build_charclass_op (re_dfa_t *dfa, | ||
111 | RE_TRANSLATE_TYPE trans, | ||
112 | const char *class_name, | ||
113 | const char *extra, | ||
114 | int non_match, reg_errcode_t *err); | ||
115 | static bin_tree_t *create_tree (re_dfa_t *dfa, | ||
116 | bin_tree_t *left, bin_tree_t *right, | ||
117 | re_token_type_t type); | ||
118 | static bin_tree_t *create_token_tree (re_dfa_t *dfa, | ||
119 | bin_tree_t *left, bin_tree_t *right, | ||
120 | const re_token_t *token); | ||
121 | static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa); | ||
122 | static void free_token (re_token_t *node); | ||
123 | static reg_errcode_t free_tree (void *extra, bin_tree_t *node); | ||
124 | static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node); | ||
125 | |||
126 | /* This table gives an error message for each of the error codes listed | ||
127 | in regex.h. Obviously the order here has to be same as there. | ||
128 | POSIX doesn't require that we do anything for REG_NOERROR, | ||
129 | but why not be nice? */ | ||
130 | |||
131 | const char __re_error_msgid[] attribute_hidden = | ||
132 | { | ||
133 | #define REG_NOERROR_IDX 0 | ||
134 | gettext_noop ("Success") /* REG_NOERROR */ | ||
135 | "\0" | ||
136 | #define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success") | ||
137 | gettext_noop ("No match") /* REG_NOMATCH */ | ||
138 | "\0" | ||
139 | #define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match") | ||
140 | gettext_noop ("Invalid regular expression") /* REG_BADPAT */ | ||
141 | "\0" | ||
142 | #define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression") | ||
143 | gettext_noop ("Invalid collation character") /* REG_ECOLLATE */ | ||
144 | "\0" | ||
145 | #define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character") | ||
146 | gettext_noop ("Invalid character class name") /* REG_ECTYPE */ | ||
147 | "\0" | ||
148 | #define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name") | ||
149 | gettext_noop ("Trailing backslash") /* REG_EESCAPE */ | ||
150 | "\0" | ||
151 | #define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash") | ||
152 | gettext_noop ("Invalid back reference") /* REG_ESUBREG */ | ||
153 | "\0" | ||
154 | #define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference") | ||
155 | gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */ | ||
156 | "\0" | ||
157 | #define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^") | ||
158 | gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */ | ||
159 | "\0" | ||
160 | #define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(") | ||
161 | gettext_noop ("Unmatched \\{") /* REG_EBRACE */ | ||
162 | "\0" | ||
163 | #define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{") | ||
164 | gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */ | ||
165 | "\0" | ||
166 | #define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}") | ||
167 | gettext_noop ("Invalid range end") /* REG_ERANGE */ | ||
168 | "\0" | ||
169 | #define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end") | ||
170 | gettext_noop ("Memory exhausted") /* REG_ESPACE */ | ||
171 | "\0" | ||
172 | #define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted") | ||
173 | gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */ | ||
174 | "\0" | ||
175 | #define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression") | ||
176 | gettext_noop ("Premature end of regular expression") /* REG_EEND */ | ||
177 | "\0" | ||
178 | #define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression") | ||
179 | gettext_noop ("Regular expression too big") /* REG_ESIZE */ | ||
180 | "\0" | ||
181 | #define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big") | ||
182 | gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */ | ||
183 | }; | ||
184 | |||
185 | const size_t __re_error_msgid_idx[] attribute_hidden = | ||
186 | { | ||
187 | REG_NOERROR_IDX, | ||
188 | REG_NOMATCH_IDX, | ||
189 | REG_BADPAT_IDX, | ||
190 | REG_ECOLLATE_IDX, | ||
191 | REG_ECTYPE_IDX, | ||
192 | REG_EESCAPE_IDX, | ||
193 | REG_ESUBREG_IDX, | ||
194 | REG_EBRACK_IDX, | ||
195 | REG_EPAREN_IDX, | ||
196 | REG_EBRACE_IDX, | ||
197 | REG_BADBR_IDX, | ||
198 | REG_ERANGE_IDX, | ||
199 | REG_ESPACE_IDX, | ||
200 | REG_BADRPT_IDX, | ||
201 | REG_EEND_IDX, | ||
202 | REG_ESIZE_IDX, | ||
203 | REG_ERPAREN_IDX | ||
204 | }; | ||
205 | |||
206 | /* Entry points for GNU code. */ | ||
207 | |||
208 | |||
209 | #ifdef ZOS_USS | ||
210 | |||
211 | /* For ZOS USS we must define btowc */ | ||
212 | |||
213 | wchar_t | ||
214 | btowc (int c) | ||
215 | { | ||
216 | wchar_t wtmp[2]; | ||
217 | char tmp[2]; | ||
218 | |||
219 | tmp[0] = c; | ||
220 | tmp[1] = 0; | ||
221 | |||
222 | mbtowc (wtmp, tmp, 1); | ||
223 | return wtmp[0]; | ||
224 | } | ||
225 | #endif | ||
226 | |||
227 | /* re_compile_pattern is the GNU regular expression compiler: it | ||
228 | compiles PATTERN (of length LENGTH) and puts the result in BUFP. | ||
229 | Returns 0 if the pattern was valid, otherwise an error string. | ||
230 | |||
231 | Assumes the `allocated' (and perhaps `buffer') and `translate' fields | ||
232 | are set in BUFP on entry. */ | ||
233 | |||
234 | const char * | ||
235 | re_compile_pattern (const char *pattern, | ||
236 | size_t length, | ||
237 | struct re_pattern_buffer *bufp) | ||
238 | { | ||
239 | reg_errcode_t ret; | ||
240 | |||
241 | /* And GNU code determines whether or not to get register information | ||
242 | by passing null for the REGS argument to re_match, etc., not by | ||
243 | setting no_sub, unless RE_NO_SUB is set. */ | ||
244 | bufp->no_sub = !!(re_syntax_options & RE_NO_SUB); | ||
245 | |||
246 | /* Match anchors at newline. */ | ||
247 | bufp->newline_anchor = 1; | ||
248 | |||
249 | ret = re_compile_internal (bufp, pattern, length, re_syntax_options); | ||
250 | |||
251 | if (!ret) | ||
252 | return NULL; | ||
253 | return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); | ||
254 | } | ||
255 | #ifdef _LIBC | ||
256 | weak_alias (__re_compile_pattern, re_compile_pattern) | ||
257 | #endif | ||
258 | |||
259 | /* Set by `re_set_syntax' to the current regexp syntax to recognize. Can | ||
260 | also be assigned to arbitrarily: each pattern buffer stores its own | ||
261 | syntax, so it can be changed between regex compilations. */ | ||
262 | /* This has no initializer because initialized variables in Emacs | ||
263 | become read-only after dumping. */ | ||
264 | reg_syntax_t re_syntax_options; | ||
265 | |||
266 | |||
267 | /* Specify the precise syntax of regexps for compilation. This provides | ||
268 | for compatibility for various utilities which historically have | ||
269 | different, incompatible syntaxes. | ||
270 | |||
271 | The argument SYNTAX is a bit mask comprised of the various bits | ||
272 | defined in regex.h. We return the old syntax. */ | ||
273 | |||
274 | reg_syntax_t | ||
275 | re_set_syntax (reg_syntax_t syntax) | ||
276 | { | ||
277 | reg_syntax_t ret = re_syntax_options; | ||
278 | |||
279 | re_syntax_options = syntax; | ||
280 | return ret; | ||
281 | } | ||
282 | #ifdef _LIBC | ||
283 | weak_alias (__re_set_syntax, re_set_syntax) | ||
284 | #endif | ||
285 | |||
286 | int | ||
287 | re_compile_fastmap (struct re_pattern_buffer *bufp) | ||
288 | { | ||
289 | re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; | ||
290 | char *fastmap = bufp->fastmap; | ||
291 | |||
292 | memset (fastmap, '\0', sizeof (char) * SBC_MAX); | ||
293 | re_compile_fastmap_iter (bufp, dfa->init_state, fastmap); | ||
294 | if (dfa->init_state != dfa->init_state_word) | ||
295 | re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap); | ||
296 | if (dfa->init_state != dfa->init_state_nl) | ||
297 | re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap); | ||
298 | if (dfa->init_state != dfa->init_state_begbuf) | ||
299 | re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap); | ||
300 | bufp->fastmap_accurate = 1; | ||
301 | return 0; | ||
302 | } | ||
303 | #ifdef _LIBC | ||
304 | weak_alias (__re_compile_fastmap, re_compile_fastmap) | ||
305 | #endif | ||
306 | |||
307 | static inline void | ||
308 | __attribute ((always_inline)) | ||
309 | re_set_fastmap (char *fastmap, int icase, int ch) | ||
310 | { | ||
311 | fastmap[ch] = 1; | ||
312 | if (icase) | ||
313 | fastmap[tolower (ch)] = 1; | ||
314 | } | ||
315 | |||
316 | /* Helper function for re_compile_fastmap. | ||
317 | Compile fastmap for the initial_state INIT_STATE. */ | ||
318 | |||
319 | static void | ||
320 | re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state, | ||
321 | char *fastmap) | ||
322 | { | ||
323 | volatile re_dfa_t *dfa = (re_dfa_t *) bufp->buffer; | ||
324 | int node_cnt; | ||
325 | int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE)); | ||
326 | for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt) | ||
327 | { | ||
328 | int node = init_state->nodes.elems[node_cnt]; | ||
329 | re_token_type_t type = dfa->nodes[node].type; | ||
330 | |||
331 | if (type == CHARACTER) | ||
332 | { | ||
333 | re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c); | ||
334 | #ifdef RE_ENABLE_I18N | ||
335 | if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) | ||
336 | { | ||
337 | unsigned char *buf = re_malloc (unsigned char, dfa->mb_cur_max), *p; | ||
338 | wchar_t wc; | ||
339 | mbstate_t state; | ||
340 | |||
341 | p = buf; | ||
342 | *p++ = dfa->nodes[node].opr.c; | ||
343 | while (++node < dfa->nodes_len | ||
344 | && dfa->nodes[node].type == CHARACTER | ||
345 | && dfa->nodes[node].mb_partial) | ||
346 | *p++ = dfa->nodes[node].opr.c; | ||
347 | memset (&state, '\0', sizeof (state)); | ||
348 | if (__mbrtowc (&wc, (const char *) buf, p - buf, | ||
349 | &state) == p - buf | ||
350 | && (__wcrtomb ((char *) buf, towlower (wc), &state) | ||
351 | != (size_t) -1)) | ||
352 | re_set_fastmap (fastmap, 0, buf[0]); | ||
353 | re_free (buf); | ||
354 | } | ||
355 | #endif | ||
356 | } | ||
357 | else if (type == SIMPLE_BRACKET) | ||
358 | { | ||
359 | int i, ch; | ||
360 | for (i = 0, ch = 0; i < BITSET_WORDS; ++i) | ||
361 | { | ||
362 | int j; | ||
363 | bitset_word_t w = dfa->nodes[node].opr.sbcset[i]; | ||
364 | for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) | ||
365 | if (w & ((bitset_word_t) 1 << j)) | ||
366 | re_set_fastmap (fastmap, icase, ch); | ||
367 | } | ||
368 | } | ||
369 | #ifdef RE_ENABLE_I18N | ||
370 | else if (type == COMPLEX_BRACKET) | ||
371 | { | ||
372 | re_charset_t *cset = dfa->nodes[node].opr.mbcset; | ||
373 | int i; | ||
374 | |||
375 | # ifdef _LIBC | ||
376 | /* See if we have to try all bytes which start multiple collation | ||
377 | elements. | ||
378 | e.g. In da_DK, we want to catch 'a' since "aa" is a valid | ||
379 | collation element, and don't catch 'b' since 'b' is | ||
380 | the only collation element which starts from 'b' (and | ||
381 | it is caught by SIMPLE_BRACKET). */ | ||
382 | if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0 | ||
383 | && (cset->ncoll_syms || cset->nranges)) | ||
384 | { | ||
385 | const int32_t *table = (const int32_t *) | ||
386 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); | ||
387 | for (i = 0; i < SBC_MAX; ++i) | ||
388 | if (table[i] < 0) | ||
389 | re_set_fastmap (fastmap, icase, i); | ||
390 | } | ||
391 | # endif /* _LIBC */ | ||
392 | |||
393 | /* See if we have to start the match at all multibyte characters, | ||
394 | i.e. where we would not find an invalid sequence. This only | ||
395 | applies to multibyte character sets; for single byte character | ||
396 | sets, the SIMPLE_BRACKET again suffices. */ | ||
397 | if (dfa->mb_cur_max > 1 | ||
398 | && (cset->nchar_classes || cset->non_match || cset->nranges | ||
399 | # ifdef _LIBC | ||
400 | || cset->nequiv_classes | ||
401 | # endif /* _LIBC */ | ||
402 | )) | ||
403 | { | ||
404 | unsigned char c = 0; | ||
405 | do | ||
406 | { | ||
407 | mbstate_t mbs; | ||
408 | memset (&mbs, 0, sizeof (mbs)); | ||
409 | if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2) | ||
410 | re_set_fastmap (fastmap, false, (int) c); | ||
411 | } | ||
412 | while (++c != 0); | ||
413 | } | ||
414 | |||
415 | else | ||
416 | { | ||
417 | /* ... Else catch all bytes which can start the mbchars. */ | ||
418 | for (i = 0; i < cset->nmbchars; ++i) | ||
419 | { | ||
420 | char buf[256]; | ||
421 | mbstate_t state; | ||
422 | memset (&state, '\0', sizeof (state)); | ||
423 | if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1) | ||
424 | re_set_fastmap (fastmap, icase, *(unsigned char *) buf); | ||
425 | if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1) | ||
426 | { | ||
427 | if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state) | ||
428 | != (size_t) -1) | ||
429 | re_set_fastmap (fastmap, false, *(unsigned char *) buf); | ||
430 | } | ||
431 | } | ||
432 | } | ||
433 | } | ||
434 | #endif /* RE_ENABLE_I18N */ | ||
435 | else if (type == OP_PERIOD | ||
436 | #ifdef RE_ENABLE_I18N | ||
437 | || type == OP_UTF8_PERIOD | ||
438 | #endif /* RE_ENABLE_I18N */ | ||
439 | || type == END_OF_RE) | ||
440 | { | ||
441 | memset (fastmap, '\1', sizeof (char) * SBC_MAX); | ||
442 | if (type == END_OF_RE) | ||
443 | bufp->can_be_null = 1; | ||
444 | return; | ||
445 | } | ||
446 | } | ||
447 | } | ||
448 | |||
449 | /* Entry point for POSIX code. */ | ||
450 | /* regcomp takes a regular expression as a string and compiles it. | ||
451 | |||
452 | PREG is a regex_t *. We do not expect any fields to be initialized, | ||
453 | since POSIX says we shouldn't. Thus, we set | ||
454 | |||
455 | `buffer' to the compiled pattern; | ||
456 | `used' to the length of the compiled pattern; | ||
457 | `syntax' to RE_SYNTAX_POSIX_EXTENDED if the | ||
458 | REG_EXTENDED bit in CFLAGS is set; otherwise, to | ||
459 | RE_SYNTAX_POSIX_BASIC; | ||
460 | `newline_anchor' to REG_NEWLINE being set in CFLAGS; | ||
461 | `fastmap' to an allocated space for the fastmap; | ||
462 | `fastmap_accurate' to zero; | ||
463 | `re_nsub' to the number of subexpressions in PATTERN. | ||
464 | |||
465 | PATTERN is the address of the pattern string. | ||
466 | |||
467 | CFLAGS is a series of bits which affect compilation. | ||
468 | |||
469 | If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we | ||
470 | use POSIX basic syntax. | ||
471 | |||
472 | If REG_NEWLINE is set, then . and [^...] don't match newline. | ||
473 | Also, regexec will try a match beginning after every newline. | ||
474 | |||
475 | If REG_ICASE is set, then we considers upper- and lowercase | ||
476 | versions of letters to be equivalent when matching. | ||
477 | |||
478 | If REG_NOSUB is set, then when PREG is passed to regexec, that | ||
479 | routine will report only success or failure, and nothing about the | ||
480 | registers. | ||
481 | |||
482 | It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for | ||
483 | the return codes and their meanings.) */ | ||
484 | |||
485 | int | ||
486 | regcomp (regex_t *__restrict preg, | ||
487 | const char *__restrict pattern, | ||
488 | int cflags) | ||
489 | { | ||
490 | reg_errcode_t ret; | ||
491 | reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED | ||
492 | : RE_SYNTAX_POSIX_BASIC); | ||
493 | |||
494 | preg->buffer = NULL; | ||
495 | preg->allocated = 0; | ||
496 | preg->used = 0; | ||
497 | |||
498 | /* Try to allocate space for the fastmap. */ | ||
499 | preg->fastmap = re_malloc (char, SBC_MAX); | ||
500 | if (BE (preg->fastmap == NULL, 0)) | ||
501 | return REG_ESPACE; | ||
502 | |||
503 | syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0; | ||
504 | |||
505 | /* If REG_NEWLINE is set, newlines are treated differently. */ | ||
506 | if (cflags & REG_NEWLINE) | ||
507 | { /* REG_NEWLINE implies neither . nor [^...] match newline. */ | ||
508 | syntax &= ~RE_DOT_NEWLINE; | ||
509 | syntax |= RE_HAT_LISTS_NOT_NEWLINE; | ||
510 | /* It also changes the matching behavior. */ | ||
511 | preg->newline_anchor = 1; | ||
512 | } | ||
513 | else | ||
514 | preg->newline_anchor = 0; | ||
515 | preg->no_sub = !!(cflags & REG_NOSUB); | ||
516 | preg->translate = NULL; | ||
517 | |||
518 | ret = re_compile_internal (preg, pattern, strlen (pattern), syntax); | ||
519 | |||
520 | /* POSIX doesn't distinguish between an unmatched open-group and an | ||
521 | unmatched close-group: both are REG_EPAREN. */ | ||
522 | if (ret == REG_ERPAREN) | ||
523 | ret = REG_EPAREN; | ||
524 | |||
525 | /* We have already checked preg->fastmap != NULL. */ | ||
526 | if (BE (ret == REG_NOERROR, 1)) | ||
527 | /* Compute the fastmap now, since regexec cannot modify the pattern | ||
528 | buffer. This function never fails in this implementation. */ | ||
529 | (void) re_compile_fastmap (preg); | ||
530 | else | ||
531 | { | ||
532 | /* Some error occurred while compiling the expression. */ | ||
533 | re_free (preg->fastmap); | ||
534 | preg->fastmap = NULL; | ||
535 | } | ||
536 | |||
537 | return (int) ret; | ||
538 | } | ||
539 | #ifdef _LIBC | ||
540 | weak_alias (__regcomp, regcomp) | ||
541 | #endif | ||
542 | |||
543 | /* Returns a message corresponding to an error code, ERRCODE, returned | ||
544 | from either regcomp or regexec. We don't use PREG here. */ | ||
545 | |||
546 | size_t | ||
547 | regerror(int errcode, UNUSED_PARAM const regex_t *__restrict preg, | ||
548 | char *__restrict errbuf, size_t errbuf_size) | ||
549 | { | ||
550 | const char *msg; | ||
551 | size_t msg_size; | ||
552 | |||
553 | if (BE (errcode < 0 | ||
554 | || errcode >= (int) (sizeof (__re_error_msgid_idx) | ||
555 | / sizeof (__re_error_msgid_idx[0])), 0)) | ||
556 | /* Only error codes returned by the rest of the code should be passed | ||
557 | to this routine. If we are given anything else, or if other regex | ||
558 | code generates an invalid error code, then the program has a bug. | ||
559 | Dump core so we can fix it. */ | ||
560 | abort (); | ||
561 | |||
562 | msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]); | ||
563 | |||
564 | msg_size = strlen (msg) + 1; /* Includes the null. */ | ||
565 | |||
566 | if (BE (errbuf_size != 0, 1)) | ||
567 | { | ||
568 | if (BE (msg_size > errbuf_size, 0)) | ||
569 | { | ||
570 | memcpy (errbuf, msg, errbuf_size - 1); | ||
571 | errbuf[errbuf_size - 1] = 0; | ||
572 | } | ||
573 | else | ||
574 | memcpy (errbuf, msg, msg_size); | ||
575 | } | ||
576 | |||
577 | return msg_size; | ||
578 | } | ||
579 | #ifdef _LIBC | ||
580 | weak_alias (__regerror, regerror) | ||
581 | #endif | ||
582 | |||
583 | |||
584 | #ifdef RE_ENABLE_I18N | ||
585 | /* This static array is used for the map to single-byte characters when | ||
586 | UTF-8 is used. Otherwise we would allocate memory just to initialize | ||
587 | it the same all the time. UTF-8 is the preferred encoding so this is | ||
588 | a worthwhile optimization. */ | ||
589 | #if __GNUC__ >= 3 | ||
590 | static const bitset_t utf8_sb_map = { | ||
591 | /* Set the first 128 bits. */ | ||
592 | [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX | ||
593 | }; | ||
594 | #else /* ! (__GNUC__ >= 3) */ | ||
595 | static bitset_t utf8_sb_map; | ||
596 | #endif /* __GNUC__ >= 3 */ | ||
597 | #endif /* RE_ENABLE_I18N */ | ||
598 | |||
599 | |||
600 | static void | ||
601 | free_dfa_content (re_dfa_t *dfa) | ||
602 | { | ||
603 | int i, j; | ||
604 | |||
605 | if (dfa->nodes) | ||
606 | for (i = 0; i < dfa->nodes_len; ++i) | ||
607 | free_token (dfa->nodes + i); | ||
608 | re_free (dfa->nexts); | ||
609 | for (i = 0; i < dfa->nodes_len; ++i) | ||
610 | { | ||
611 | if (dfa->eclosures != NULL) | ||
612 | re_node_set_free (dfa->eclosures + i); | ||
613 | if (dfa->inveclosures != NULL) | ||
614 | re_node_set_free (dfa->inveclosures + i); | ||
615 | if (dfa->edests != NULL) | ||
616 | re_node_set_free (dfa->edests + i); | ||
617 | } | ||
618 | re_free (dfa->edests); | ||
619 | re_free (dfa->eclosures); | ||
620 | re_free (dfa->inveclosures); | ||
621 | re_free (dfa->nodes); | ||
622 | |||
623 | if (dfa->state_table) | ||
624 | for (i = 0; i <= dfa->state_hash_mask; ++i) | ||
625 | { | ||
626 | struct re_state_table_entry *entry = dfa->state_table + i; | ||
627 | for (j = 0; j < entry->num; ++j) | ||
628 | { | ||
629 | re_dfastate_t *state = entry->array[j]; | ||
630 | free_state (state); | ||
631 | } | ||
632 | re_free (entry->array); | ||
633 | } | ||
634 | re_free (dfa->state_table); | ||
635 | #ifdef RE_ENABLE_I18N | ||
636 | if (dfa->sb_char != utf8_sb_map) | ||
637 | re_free (dfa->sb_char); | ||
638 | #endif | ||
639 | re_free (dfa->subexp_map); | ||
640 | #ifdef DEBUG | ||
641 | re_free (dfa->re_str); | ||
642 | #endif | ||
643 | |||
644 | re_free (dfa); | ||
645 | } | ||
646 | |||
647 | |||
648 | /* Free dynamically allocated space used by PREG. */ | ||
649 | |||
650 | void | ||
651 | regfree (regex_t *preg) | ||
652 | { | ||
653 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
654 | if (BE (dfa != NULL, 1)) | ||
655 | free_dfa_content (dfa); | ||
656 | preg->buffer = NULL; | ||
657 | preg->allocated = 0; | ||
658 | |||
659 | re_free (preg->fastmap); | ||
660 | preg->fastmap = NULL; | ||
661 | |||
662 | re_free (preg->translate); | ||
663 | preg->translate = NULL; | ||
664 | } | ||
665 | #ifdef _LIBC | ||
666 | weak_alias (__regfree, regfree) | ||
667 | #endif | ||
668 | |||
669 | /* Entry points compatible with 4.2 BSD regex library. We don't define | ||
670 | them unless specifically requested. */ | ||
671 | |||
672 | #if defined _REGEX_RE_COMP || defined _LIBC | ||
673 | |||
674 | /* BSD has one and only one pattern buffer. */ | ||
675 | static struct re_pattern_buffer re_comp_buf; | ||
676 | |||
677 | char * | ||
678 | # ifdef _LIBC | ||
679 | /* Make these definitions weak in libc, so POSIX programs can redefine | ||
680 | these names if they don't use our functions, and still use | ||
681 | regcomp/regexec above without link errors. */ | ||
682 | weak_function | ||
683 | # endif | ||
684 | re_comp (s) | ||
685 | const char *s; | ||
686 | { | ||
687 | reg_errcode_t ret; | ||
688 | char *fastmap; | ||
689 | |||
690 | if (!s) | ||
691 | { | ||
692 | if (!re_comp_buf.buffer) | ||
693 | return gettext ("No previous regular expression"); | ||
694 | return 0; | ||
695 | } | ||
696 | |||
697 | if (re_comp_buf.buffer) | ||
698 | { | ||
699 | fastmap = re_comp_buf.fastmap; | ||
700 | re_comp_buf.fastmap = NULL; | ||
701 | __regfree (&re_comp_buf); | ||
702 | memset (&re_comp_buf, '\0', sizeof (re_comp_buf)); | ||
703 | re_comp_buf.fastmap = fastmap; | ||
704 | } | ||
705 | |||
706 | if (re_comp_buf.fastmap == NULL) | ||
707 | { | ||
708 | re_comp_buf.fastmap = (char *) malloc (SBC_MAX); | ||
709 | if (re_comp_buf.fastmap == NULL) | ||
710 | return (char *) gettext (__re_error_msgid | ||
711 | + __re_error_msgid_idx[(int) REG_ESPACE]); | ||
712 | } | ||
713 | |||
714 | /* Since `re_exec' always passes NULL for the `regs' argument, we | ||
715 | don't need to initialize the pattern buffer fields which affect it. */ | ||
716 | |||
717 | /* Match anchors at newlines. */ | ||
718 | re_comp_buf.newline_anchor = 1; | ||
719 | |||
720 | ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options); | ||
721 | |||
722 | if (!ret) | ||
723 | return NULL; | ||
724 | |||
725 | /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */ | ||
726 | return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]); | ||
727 | } | ||
728 | |||
729 | #ifdef _LIBC | ||
730 | libc_freeres_fn (free_mem) | ||
731 | { | ||
732 | __regfree (&re_comp_buf); | ||
733 | } | ||
734 | #endif | ||
735 | |||
736 | #endif /* _REGEX_RE_COMP */ | ||
737 | |||
738 | /* Internal entry point. | ||
739 | Compile the regular expression PATTERN, whose length is LENGTH. | ||
740 | SYNTAX indicate regular expression's syntax. */ | ||
741 | |||
742 | static reg_errcode_t | ||
743 | re_compile_internal (regex_t *preg, const char * pattern, size_t length, | ||
744 | reg_syntax_t syntax) | ||
745 | { | ||
746 | reg_errcode_t err = REG_NOERROR; | ||
747 | re_dfa_t *dfa; | ||
748 | re_string_t regexp; | ||
749 | |||
750 | /* Initialize the pattern buffer. */ | ||
751 | preg->fastmap_accurate = 0; | ||
752 | preg->syntax = syntax; | ||
753 | preg->not_bol = preg->not_eol = 0; | ||
754 | preg->used = 0; | ||
755 | preg->re_nsub = 0; | ||
756 | preg->can_be_null = 0; | ||
757 | preg->regs_allocated = REGS_UNALLOCATED; | ||
758 | |||
759 | /* Initialize the dfa. */ | ||
760 | dfa = (re_dfa_t *) preg->buffer; | ||
761 | if (BE (preg->allocated < sizeof (re_dfa_t), 0)) | ||
762 | { | ||
763 | /* If zero allocated, but buffer is non-null, try to realloc | ||
764 | enough space. This loses if buffer's address is bogus, but | ||
765 | that is the user's responsibility. If ->buffer is NULL this | ||
766 | is a simple allocation. */ | ||
767 | dfa = re_realloc (preg->buffer, re_dfa_t, 1); | ||
768 | if (dfa == NULL) | ||
769 | return REG_ESPACE; | ||
770 | preg->allocated = sizeof (re_dfa_t); | ||
771 | preg->buffer = (unsigned char *) dfa; | ||
772 | } | ||
773 | preg->used = sizeof (re_dfa_t); | ||
774 | |||
775 | err = init_dfa (dfa, length); | ||
776 | if (BE (err != REG_NOERROR, 0)) | ||
777 | { | ||
778 | free_dfa_content (dfa); | ||
779 | preg->buffer = NULL; | ||
780 | preg->allocated = 0; | ||
781 | return err; | ||
782 | } | ||
783 | #ifdef DEBUG | ||
784 | /* Note: length+1 will not overflow since it is checked in init_dfa. */ | ||
785 | dfa->re_str = re_malloc (char, length + 1); | ||
786 | strncpy (dfa->re_str, pattern, length + 1); | ||
787 | #endif | ||
788 | |||
789 | __libc_lock_init (dfa->lock); | ||
790 | |||
791 | err = re_string_construct (®exp, pattern, length, preg->translate, | ||
792 | syntax & RE_ICASE, dfa); | ||
793 | if (BE (err != REG_NOERROR, 0)) | ||
794 | { | ||
795 | re_compile_internal_free_return: | ||
796 | free_workarea_compile (preg); | ||
797 | re_string_destruct (®exp); | ||
798 | free_dfa_content (dfa); | ||
799 | preg->buffer = NULL; | ||
800 | preg->allocated = 0; | ||
801 | return err; | ||
802 | } | ||
803 | |||
804 | /* Parse the regular expression, and build a structure tree. */ | ||
805 | preg->re_nsub = 0; | ||
806 | dfa->str_tree = parse (®exp, preg, syntax, &err); | ||
807 | if (BE (dfa->str_tree == NULL, 0)) | ||
808 | goto re_compile_internal_free_return; | ||
809 | |||
810 | /* Analyze the tree and create the nfa. */ | ||
811 | err = analyze (preg); | ||
812 | if (BE (err != REG_NOERROR, 0)) | ||
813 | goto re_compile_internal_free_return; | ||
814 | |||
815 | #ifdef RE_ENABLE_I18N | ||
816 | /* If possible, do searching in single byte encoding to speed things up. */ | ||
817 | if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL) | ||
818 | optimize_utf8 (dfa); | ||
819 | #endif | ||
820 | |||
821 | /* Then create the initial state of the dfa. */ | ||
822 | err = create_initial_state (dfa); | ||
823 | |||
824 | /* Release work areas. */ | ||
825 | free_workarea_compile (preg); | ||
826 | re_string_destruct (®exp); | ||
827 | |||
828 | if (BE (err != REG_NOERROR, 0)) | ||
829 | { | ||
830 | free_dfa_content (dfa); | ||
831 | preg->buffer = NULL; | ||
832 | preg->allocated = 0; | ||
833 | } | ||
834 | |||
835 | return err; | ||
836 | } | ||
837 | |||
838 | /* Initialize DFA. We use the length of the regular expression PAT_LEN | ||
839 | as the initial length of some arrays. */ | ||
840 | |||
841 | static reg_errcode_t | ||
842 | init_dfa (re_dfa_t *dfa, size_t pat_len) | ||
843 | { | ||
844 | unsigned int table_size; | ||
845 | #ifndef _LIBC | ||
846 | const char *codeset_name; | ||
847 | #endif | ||
848 | |||
849 | memset (dfa, '\0', sizeof (re_dfa_t)); | ||
850 | |||
851 | /* Force allocation of str_tree_storage the first time. */ | ||
852 | dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; | ||
853 | |||
854 | /* Avoid overflows. */ | ||
855 | if (pat_len == SIZE_MAX) | ||
856 | return REG_ESPACE; | ||
857 | |||
858 | dfa->nodes_alloc = pat_len + 1; | ||
859 | dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc); | ||
860 | |||
861 | /* table_size = 2 ^ ceil(log pat_len) */ | ||
862 | for (table_size = 1; ; table_size <<= 1) | ||
863 | if (table_size > pat_len) | ||
864 | break; | ||
865 | |||
866 | dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size); | ||
867 | dfa->state_hash_mask = table_size - 1; | ||
868 | |||
869 | dfa->mb_cur_max = MB_CUR_MAX; | ||
870 | #ifdef _LIBC | ||
871 | if (dfa->mb_cur_max == 6 | ||
872 | && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0) | ||
873 | dfa->is_utf8 = 1; | ||
874 | dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII) | ||
875 | != 0); | ||
876 | #else | ||
877 | # ifdef HAVE_LANGINFO_CODESET | ||
878 | codeset_name = nl_langinfo (CODESET); | ||
879 | # else | ||
880 | codeset_name = getenv ("LC_ALL"); | ||
881 | if (codeset_name == NULL || codeset_name[0] == '\0') | ||
882 | codeset_name = getenv ("LC_CTYPE"); | ||
883 | if (codeset_name == NULL || codeset_name[0] == '\0') | ||
884 | codeset_name = getenv ("LANG"); | ||
885 | if (codeset_name == NULL) | ||
886 | codeset_name = ""; | ||
887 | else if (strchr (codeset_name, '.') != NULL) | ||
888 | codeset_name = strchr (codeset_name, '.') + 1; | ||
889 | # endif | ||
890 | |||
891 | /* strcasecmp isn't a standard interface. brute force check */ | ||
892 | #if 0 | ||
893 | if (strcasecmp (codeset_name, "UTF-8") == 0 | ||
894 | || strcasecmp (codeset_name, "UTF8") == 0) | ||
895 | dfa->is_utf8 = 1; | ||
896 | #else | ||
897 | if ( (codeset_name[0] == 'U' || codeset_name[0] == 'u') | ||
898 | && (codeset_name[1] == 'T' || codeset_name[1] == 't') | ||
899 | && (codeset_name[2] == 'F' || codeset_name[2] == 'f') | ||
900 | && (codeset_name[3] == '-' | ||
901 | ? codeset_name[4] == '8' && codeset_name[5] == '\0' | ||
902 | : codeset_name[3] == '8' && codeset_name[4] == '\0')) | ||
903 | dfa->is_utf8 = 1; | ||
904 | #endif | ||
905 | |||
906 | /* We check exhaustively in the loop below if this charset is a | ||
907 | superset of ASCII. */ | ||
908 | dfa->map_notascii = 0; | ||
909 | #endif | ||
910 | |||
911 | #ifdef RE_ENABLE_I18N | ||
912 | if (dfa->mb_cur_max > 1) | ||
913 | { | ||
914 | if (dfa->is_utf8) | ||
915 | { | ||
916 | #if !defined(__GNUC__) || __GNUC__ < 3 | ||
917 | static short utf8_sb_map_inited = 0; | ||
918 | |||
919 | if (! utf8_sb_map_inited) | ||
920 | { | ||
921 | int i; | ||
922 | |||
923 | utf8_sb_map_inited = 0; | ||
924 | for (i = 0; i <= 0x80 / BITSET_WORD_BITS - 1; i++) | ||
925 | utf8_sb_map[i] = BITSET_WORD_MAX; | ||
926 | } | ||
927 | #endif | ||
928 | dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map; | ||
929 | } | ||
930 | else | ||
931 | { | ||
932 | int i, j, ch; | ||
933 | |||
934 | dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); | ||
935 | if (BE (dfa->sb_char == NULL, 0)) | ||
936 | return REG_ESPACE; | ||
937 | |||
938 | /* Set the bits corresponding to single byte chars. */ | ||
939 | for (i = 0, ch = 0; i < BITSET_WORDS; ++i) | ||
940 | for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) | ||
941 | { | ||
942 | wint_t wch = __btowc (ch); | ||
943 | if (wch != WEOF) | ||
944 | dfa->sb_char[i] |= (bitset_word_t) 1 << j; | ||
945 | # ifndef _LIBC | ||
946 | if (isascii (ch) && wch != ch) | ||
947 | dfa->map_notascii = 1; | ||
948 | # endif | ||
949 | } | ||
950 | } | ||
951 | } | ||
952 | #endif | ||
953 | |||
954 | if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0)) | ||
955 | return REG_ESPACE; | ||
956 | return REG_NOERROR; | ||
957 | } | ||
958 | |||
959 | /* Initialize WORD_CHAR table, which indicate which character is | ||
960 | "word". In this case "word" means that it is the word construction | ||
961 | character used by some operators like "\<", "\>", etc. */ | ||
962 | |||
963 | static void | ||
964 | internal_function | ||
965 | init_word_char (re_dfa_t *dfa) | ||
966 | { | ||
967 | int i, j, ch; | ||
968 | dfa->word_ops_used = 1; | ||
969 | for (i = 0, ch = 0; i < BITSET_WORDS; ++i) | ||
970 | for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch) | ||
971 | if (isalnum (ch) || ch == '_') | ||
972 | dfa->word_char[i] |= (bitset_word_t) 1 << j; | ||
973 | } | ||
974 | |||
975 | /* Free the work area which are only used while compiling. */ | ||
976 | |||
977 | static void | ||
978 | free_workarea_compile (regex_t *preg) | ||
979 | { | ||
980 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
981 | bin_tree_storage_t *storage, *next; | ||
982 | for (storage = dfa->str_tree_storage; storage; storage = next) | ||
983 | { | ||
984 | next = storage->next; | ||
985 | re_free (storage); | ||
986 | } | ||
987 | dfa->str_tree_storage = NULL; | ||
988 | dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE; | ||
989 | dfa->str_tree = NULL; | ||
990 | re_free (dfa->org_indices); | ||
991 | dfa->org_indices = NULL; | ||
992 | } | ||
993 | |||
994 | /* Create initial states for all contexts. */ | ||
995 | |||
996 | static reg_errcode_t | ||
997 | create_initial_state (re_dfa_t *dfa) | ||
998 | { | ||
999 | int first, i; | ||
1000 | reg_errcode_t err; | ||
1001 | re_node_set init_nodes; | ||
1002 | |||
1003 | /* Initial states have the epsilon closure of the node which is | ||
1004 | the first node of the regular expression. */ | ||
1005 | first = dfa->str_tree->first->node_idx; | ||
1006 | dfa->init_node = first; | ||
1007 | err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first); | ||
1008 | if (BE (err != REG_NOERROR, 0)) | ||
1009 | return err; | ||
1010 | |||
1011 | /* The back-references which are in initial states can epsilon transit, | ||
1012 | since in this case all of the subexpressions can be null. | ||
1013 | Then we add epsilon closures of the nodes which are the next nodes of | ||
1014 | the back-references. */ | ||
1015 | if (dfa->nbackref > 0) | ||
1016 | for (i = 0; i < init_nodes.nelem; ++i) | ||
1017 | { | ||
1018 | int node_idx = init_nodes.elems[i]; | ||
1019 | re_token_type_t type = dfa->nodes[node_idx].type; | ||
1020 | |||
1021 | int clexp_idx; | ||
1022 | if (type != OP_BACK_REF) | ||
1023 | continue; | ||
1024 | for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx) | ||
1025 | { | ||
1026 | re_token_t *clexp_node; | ||
1027 | clexp_node = dfa->nodes + init_nodes.elems[clexp_idx]; | ||
1028 | if (clexp_node->type == OP_CLOSE_SUBEXP | ||
1029 | && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx) | ||
1030 | break; | ||
1031 | } | ||
1032 | if (clexp_idx == init_nodes.nelem) | ||
1033 | continue; | ||
1034 | |||
1035 | if (type == OP_BACK_REF) | ||
1036 | { | ||
1037 | int dest_idx = dfa->edests[node_idx].elems[0]; | ||
1038 | if (!re_node_set_contains (&init_nodes, dest_idx)) | ||
1039 | { | ||
1040 | err = re_node_set_merge (&init_nodes, | ||
1041 | dfa->eclosures + dest_idx); | ||
1042 | if (err != REG_NOERROR) | ||
1043 | return err; | ||
1044 | i = 0; | ||
1045 | } | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | /* It must be the first time to invoke acquire_state. */ | ||
1050 | dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0); | ||
1051 | /* We don't check ERR here, since the initial state must not be NULL. */ | ||
1052 | if (BE (dfa->init_state == NULL, 0)) | ||
1053 | return err; | ||
1054 | if (dfa->init_state->has_constraint) | ||
1055 | { | ||
1056 | dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes, | ||
1057 | CONTEXT_WORD); | ||
1058 | dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes, | ||
1059 | CONTEXT_NEWLINE); | ||
1060 | dfa->init_state_begbuf = re_acquire_state_context (&err, dfa, | ||
1061 | &init_nodes, | ||
1062 | CONTEXT_NEWLINE | ||
1063 | | CONTEXT_BEGBUF); | ||
1064 | if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL | ||
1065 | || dfa->init_state_begbuf == NULL, 0)) | ||
1066 | return err; | ||
1067 | } | ||
1068 | else | ||
1069 | dfa->init_state_word = dfa->init_state_nl | ||
1070 | = dfa->init_state_begbuf = dfa->init_state; | ||
1071 | |||
1072 | re_node_set_free (&init_nodes); | ||
1073 | return REG_NOERROR; | ||
1074 | } | ||
1075 | |||
1076 | #ifdef RE_ENABLE_I18N | ||
1077 | /* If it is possible to do searching in single byte encoding instead of UTF-8 | ||
1078 | to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change | ||
1079 | DFA nodes where needed. */ | ||
1080 | |||
1081 | static void | ||
1082 | optimize_utf8 (re_dfa_t *dfa) | ||
1083 | { | ||
1084 | int node, i, mb_chars = 0, has_period = 0; | ||
1085 | |||
1086 | for (node = 0; node < dfa->nodes_len; ++node) | ||
1087 | switch (dfa->nodes[node].type) | ||
1088 | { | ||
1089 | case CHARACTER: | ||
1090 | if (dfa->nodes[node].opr.c >= 0x80) | ||
1091 | mb_chars = 1; | ||
1092 | break; | ||
1093 | case ANCHOR: | ||
1094 | switch (dfa->nodes[node].opr.ctx_type) | ||
1095 | { | ||
1096 | case LINE_FIRST: | ||
1097 | case LINE_LAST: | ||
1098 | case BUF_FIRST: | ||
1099 | case BUF_LAST: | ||
1100 | break; | ||
1101 | default: | ||
1102 | /* Word anchors etc. cannot be handled. It's okay to test | ||
1103 | opr.ctx_type since constraints (for all DFA nodes) are | ||
1104 | created by ORing one or more opr.ctx_type values. */ | ||
1105 | return; | ||
1106 | } | ||
1107 | break; | ||
1108 | case OP_PERIOD: | ||
1109 | has_period = 1; | ||
1110 | break; | ||
1111 | case OP_BACK_REF: | ||
1112 | case OP_ALT: | ||
1113 | case END_OF_RE: | ||
1114 | case OP_DUP_ASTERISK: | ||
1115 | case OP_OPEN_SUBEXP: | ||
1116 | case OP_CLOSE_SUBEXP: | ||
1117 | break; | ||
1118 | case COMPLEX_BRACKET: | ||
1119 | return; | ||
1120 | case SIMPLE_BRACKET: | ||
1121 | /* Just double check. The non-ASCII range starts at 0x80. */ | ||
1122 | assert (0x80 % BITSET_WORD_BITS == 0); | ||
1123 | for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i) | ||
1124 | if (dfa->nodes[node].opr.sbcset[i]) | ||
1125 | return; | ||
1126 | break; | ||
1127 | default: | ||
1128 | abort (); | ||
1129 | } | ||
1130 | |||
1131 | if (mb_chars || has_period) | ||
1132 | for (node = 0; node < dfa->nodes_len; ++node) | ||
1133 | { | ||
1134 | if (dfa->nodes[node].type == CHARACTER | ||
1135 | && dfa->nodes[node].opr.c >= 0x80) | ||
1136 | dfa->nodes[node].mb_partial = 0; | ||
1137 | else if (dfa->nodes[node].type == OP_PERIOD) | ||
1138 | dfa->nodes[node].type = OP_UTF8_PERIOD; | ||
1139 | } | ||
1140 | |||
1141 | /* The search can be in single byte locale. */ | ||
1142 | dfa->mb_cur_max = 1; | ||
1143 | dfa->is_utf8 = 0; | ||
1144 | dfa->has_mb_node = dfa->nbackref > 0 || has_period; | ||
1145 | } | ||
1146 | #endif | ||
1147 | |||
1148 | /* Analyze the structure tree, and calculate "first", "next", "edest", | ||
1149 | "eclosure", and "inveclosure". */ | ||
1150 | |||
1151 | static reg_errcode_t | ||
1152 | analyze (regex_t *preg) | ||
1153 | { | ||
1154 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
1155 | reg_errcode_t ret; | ||
1156 | |||
1157 | /* Allocate arrays. */ | ||
1158 | dfa->nexts = re_malloc (int, dfa->nodes_alloc); | ||
1159 | dfa->org_indices = re_malloc (int, dfa->nodes_alloc); | ||
1160 | dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc); | ||
1161 | dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc); | ||
1162 | if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL | ||
1163 | || dfa->eclosures == NULL, 0)) | ||
1164 | return REG_ESPACE; | ||
1165 | |||
1166 | dfa->subexp_map = re_malloc (int, preg->re_nsub); | ||
1167 | if (dfa->subexp_map != NULL) | ||
1168 | { | ||
1169 | int i; | ||
1170 | for (i = 0; i < preg->re_nsub; i++) | ||
1171 | dfa->subexp_map[i] = i; | ||
1172 | preorder (dfa->str_tree, optimize_subexps, dfa); | ||
1173 | for (i = 0; i < preg->re_nsub; i++) | ||
1174 | if (dfa->subexp_map[i] != i) | ||
1175 | break; | ||
1176 | if (i == preg->re_nsub) | ||
1177 | { | ||
1178 | free (dfa->subexp_map); | ||
1179 | dfa->subexp_map = NULL; | ||
1180 | } | ||
1181 | } | ||
1182 | |||
1183 | ret = postorder (dfa->str_tree, lower_subexps, preg); | ||
1184 | if (BE (ret != REG_NOERROR, 0)) | ||
1185 | return ret; | ||
1186 | ret = postorder (dfa->str_tree, calc_first, dfa); | ||
1187 | if (BE (ret != REG_NOERROR, 0)) | ||
1188 | return ret; | ||
1189 | preorder (dfa->str_tree, calc_next, dfa); | ||
1190 | ret = preorder (dfa->str_tree, link_nfa_nodes, dfa); | ||
1191 | if (BE (ret != REG_NOERROR, 0)) | ||
1192 | return ret; | ||
1193 | ret = calc_eclosure (dfa); | ||
1194 | if (BE (ret != REG_NOERROR, 0)) | ||
1195 | return ret; | ||
1196 | |||
1197 | /* We only need this during the prune_impossible_nodes pass in regexec.c; | ||
1198 | skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */ | ||
1199 | if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match) | ||
1200 | || dfa->nbackref) | ||
1201 | { | ||
1202 | dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len); | ||
1203 | if (BE (dfa->inveclosures == NULL, 0)) | ||
1204 | return REG_ESPACE; | ||
1205 | ret = calc_inveclosure (dfa); | ||
1206 | } | ||
1207 | |||
1208 | return ret; | ||
1209 | } | ||
1210 | |||
1211 | /* Our parse trees are very unbalanced, so we cannot use a stack to | ||
1212 | implement parse tree visits. Instead, we use parent pointers and | ||
1213 | some hairy code in these two functions. */ | ||
1214 | static reg_errcode_t | ||
1215 | postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), | ||
1216 | void *extra) | ||
1217 | { | ||
1218 | bin_tree_t *node, *prev; | ||
1219 | |||
1220 | for (node = root; ; ) | ||
1221 | { | ||
1222 | /* Descend down the tree, preferably to the left (or to the right | ||
1223 | if that's the only child). */ | ||
1224 | while (node->left || node->right) | ||
1225 | if (node->left) | ||
1226 | node = node->left; | ||
1227 | else | ||
1228 | node = node->right; | ||
1229 | |||
1230 | do | ||
1231 | { | ||
1232 | reg_errcode_t err = fn (extra, node); | ||
1233 | if (BE (err != REG_NOERROR, 0)) | ||
1234 | return err; | ||
1235 | if (node->parent == NULL) | ||
1236 | return REG_NOERROR; | ||
1237 | prev = node; | ||
1238 | node = node->parent; | ||
1239 | } | ||
1240 | /* Go up while we have a node that is reached from the right. */ | ||
1241 | while (node->right == prev || node->right == NULL); | ||
1242 | node = node->right; | ||
1243 | } | ||
1244 | } | ||
1245 | |||
1246 | static reg_errcode_t | ||
1247 | preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)), | ||
1248 | void *extra) | ||
1249 | { | ||
1250 | bin_tree_t *node; | ||
1251 | |||
1252 | for (node = root; ; ) | ||
1253 | { | ||
1254 | reg_errcode_t err = fn (extra, node); | ||
1255 | if (BE (err != REG_NOERROR, 0)) | ||
1256 | return err; | ||
1257 | |||
1258 | /* Go to the left node, or up and to the right. */ | ||
1259 | if (node->left) | ||
1260 | node = node->left; | ||
1261 | else | ||
1262 | { | ||
1263 | bin_tree_t *prev = NULL; | ||
1264 | while (node->right == prev || node->right == NULL) | ||
1265 | { | ||
1266 | prev = node; | ||
1267 | node = node->parent; | ||
1268 | if (!node) | ||
1269 | return REG_NOERROR; | ||
1270 | } | ||
1271 | node = node->right; | ||
1272 | } | ||
1273 | } | ||
1274 | } | ||
1275 | |||
1276 | /* Optimization pass: if a SUBEXP is entirely contained, strip it and tell | ||
1277 | re_search_internal to map the inner one's opr.idx to this one's. Adjust | ||
1278 | backreferences as well. Requires a preorder visit. */ | ||
1279 | static reg_errcode_t | ||
1280 | optimize_subexps (void *extra, bin_tree_t *node) | ||
1281 | { | ||
1282 | re_dfa_t *dfa = (re_dfa_t *) extra; | ||
1283 | |||
1284 | if (node->token.type == OP_BACK_REF && dfa->subexp_map) | ||
1285 | { | ||
1286 | int idx = node->token.opr.idx; | ||
1287 | node->token.opr.idx = dfa->subexp_map[idx]; | ||
1288 | dfa->used_bkref_map |= 1 << node->token.opr.idx; | ||
1289 | } | ||
1290 | |||
1291 | else if (node->token.type == SUBEXP | ||
1292 | && node->left && node->left->token.type == SUBEXP) | ||
1293 | { | ||
1294 | int other_idx = node->left->token.opr.idx; | ||
1295 | |||
1296 | node->left = node->left->left; | ||
1297 | if (node->left) | ||
1298 | node->left->parent = node; | ||
1299 | |||
1300 | dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx]; | ||
1301 | if (other_idx < BITSET_WORD_BITS) | ||
1302 | dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx); | ||
1303 | } | ||
1304 | |||
1305 | return REG_NOERROR; | ||
1306 | } | ||
1307 | |||
1308 | /* Lowering pass: Turn each SUBEXP node into the appropriate concatenation | ||
1309 | of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */ | ||
1310 | static reg_errcode_t | ||
1311 | lower_subexps (void *extra, bin_tree_t *node) | ||
1312 | { | ||
1313 | regex_t *preg = (regex_t *) extra; | ||
1314 | reg_errcode_t err = REG_NOERROR; | ||
1315 | |||
1316 | if (node->left && node->left->token.type == SUBEXP) | ||
1317 | { | ||
1318 | node->left = lower_subexp (&err, preg, node->left); | ||
1319 | if (node->left) | ||
1320 | node->left->parent = node; | ||
1321 | } | ||
1322 | if (node->right && node->right->token.type == SUBEXP) | ||
1323 | { | ||
1324 | node->right = lower_subexp (&err, preg, node->right); | ||
1325 | if (node->right) | ||
1326 | node->right->parent = node; | ||
1327 | } | ||
1328 | |||
1329 | return err; | ||
1330 | } | ||
1331 | |||
1332 | static bin_tree_t * | ||
1333 | lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node) | ||
1334 | { | ||
1335 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
1336 | bin_tree_t *body = node->left; | ||
1337 | bin_tree_t *op, *cls, *tree1, *tree; | ||
1338 | |||
1339 | if (preg->no_sub | ||
1340 | /* We do not optimize empty subexpressions, because otherwise we may | ||
1341 | have bad CONCAT nodes with NULL children. This is obviously not | ||
1342 | very common, so we do not lose much. An example that triggers | ||
1343 | this case is the sed "script" /\(\)/x. */ | ||
1344 | && node->left != NULL | ||
1345 | && (node->token.opr.idx >= BITSET_WORD_BITS | ||
1346 | || !(dfa->used_bkref_map | ||
1347 | & ((bitset_word_t) 1 << node->token.opr.idx)))) | ||
1348 | return node->left; | ||
1349 | |||
1350 | /* Convert the SUBEXP node to the concatenation of an | ||
1351 | OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */ | ||
1352 | op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP); | ||
1353 | cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP); | ||
1354 | tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls; | ||
1355 | tree = create_tree (dfa, op, tree1, CONCAT); | ||
1356 | if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0)) | ||
1357 | { | ||
1358 | *err = REG_ESPACE; | ||
1359 | return NULL; | ||
1360 | } | ||
1361 | |||
1362 | op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx; | ||
1363 | op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp; | ||
1364 | return tree; | ||
1365 | } | ||
1366 | |||
1367 | /* Pass 1 in building the NFA: compute FIRST and create unlinked automaton | ||
1368 | nodes. Requires a postorder visit. */ | ||
1369 | static reg_errcode_t | ||
1370 | calc_first (void *extra, bin_tree_t *node) | ||
1371 | { | ||
1372 | re_dfa_t *dfa = (re_dfa_t *) extra; | ||
1373 | if (node->token.type == CONCAT) | ||
1374 | { | ||
1375 | node->first = node->left->first; | ||
1376 | node->node_idx = node->left->node_idx; | ||
1377 | } | ||
1378 | else | ||
1379 | { | ||
1380 | node->first = node; | ||
1381 | node->node_idx = re_dfa_add_node (dfa, node->token); | ||
1382 | if (BE (node->node_idx == -1, 0)) | ||
1383 | return REG_ESPACE; | ||
1384 | if (node->token.type == ANCHOR) | ||
1385 | dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type; | ||
1386 | } | ||
1387 | return REG_NOERROR; | ||
1388 | } | ||
1389 | |||
1390 | /* Pass 2: compute NEXT on the tree. Preorder visit. */ | ||
1391 | static reg_errcode_t | ||
1392 | calc_next (UNUSED_PARAM void *extra, bin_tree_t *node) | ||
1393 | { | ||
1394 | switch (node->token.type) | ||
1395 | { | ||
1396 | case OP_DUP_ASTERISK: | ||
1397 | node->left->next = node; | ||
1398 | break; | ||
1399 | case CONCAT: | ||
1400 | node->left->next = node->right->first; | ||
1401 | node->right->next = node->next; | ||
1402 | break; | ||
1403 | default: | ||
1404 | if (node->left) | ||
1405 | node->left->next = node->next; | ||
1406 | if (node->right) | ||
1407 | node->right->next = node->next; | ||
1408 | break; | ||
1409 | } | ||
1410 | return REG_NOERROR; | ||
1411 | } | ||
1412 | |||
1413 | /* Pass 3: link all DFA nodes to their NEXT node (any order will do). */ | ||
1414 | static reg_errcode_t | ||
1415 | link_nfa_nodes (void *extra, bin_tree_t *node) | ||
1416 | { | ||
1417 | re_dfa_t *dfa = (re_dfa_t *) extra; | ||
1418 | int idx = node->node_idx; | ||
1419 | reg_errcode_t err = REG_NOERROR; | ||
1420 | |||
1421 | switch (node->token.type) | ||
1422 | { | ||
1423 | case CONCAT: | ||
1424 | break; | ||
1425 | |||
1426 | case END_OF_RE: | ||
1427 | assert (node->next == NULL); | ||
1428 | break; | ||
1429 | |||
1430 | case OP_DUP_ASTERISK: | ||
1431 | case OP_ALT: | ||
1432 | { | ||
1433 | int left, right; | ||
1434 | dfa->has_plural_match = 1; | ||
1435 | if (node->left != NULL) | ||
1436 | left = node->left->first->node_idx; | ||
1437 | else | ||
1438 | left = node->next->node_idx; | ||
1439 | if (node->right != NULL) | ||
1440 | right = node->right->first->node_idx; | ||
1441 | else | ||
1442 | right = node->next->node_idx; | ||
1443 | assert (left > -1); | ||
1444 | assert (right > -1); | ||
1445 | err = re_node_set_init_2 (dfa->edests + idx, left, right); | ||
1446 | } | ||
1447 | break; | ||
1448 | |||
1449 | case ANCHOR: | ||
1450 | case OP_OPEN_SUBEXP: | ||
1451 | case OP_CLOSE_SUBEXP: | ||
1452 | err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx); | ||
1453 | break; | ||
1454 | |||
1455 | case OP_BACK_REF: | ||
1456 | dfa->nexts[idx] = node->next->node_idx; | ||
1457 | if (node->token.type == OP_BACK_REF) | ||
1458 | err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]); | ||
1459 | break; | ||
1460 | |||
1461 | default: | ||
1462 | assert (!IS_EPSILON_NODE (node->token.type)); | ||
1463 | dfa->nexts[idx] = node->next->node_idx; | ||
1464 | break; | ||
1465 | } | ||
1466 | |||
1467 | return err; | ||
1468 | } | ||
1469 | |||
1470 | /* Duplicate the epsilon closure of the node ROOT_NODE. | ||
1471 | Note that duplicated nodes have constraint INIT_CONSTRAINT in addition | ||
1472 | to their own constraint. */ | ||
1473 | |||
1474 | static reg_errcode_t | ||
1475 | internal_function | ||
1476 | duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node, | ||
1477 | int root_node, unsigned int init_constraint) | ||
1478 | { | ||
1479 | int org_node, clone_node, ret; | ||
1480 | unsigned int constraint = init_constraint; | ||
1481 | for (org_node = top_org_node, clone_node = top_clone_node;;) | ||
1482 | { | ||
1483 | int org_dest, clone_dest; | ||
1484 | if (dfa->nodes[org_node].type == OP_BACK_REF) | ||
1485 | { | ||
1486 | /* If the back reference epsilon-transit, its destination must | ||
1487 | also have the constraint. Then duplicate the epsilon closure | ||
1488 | of the destination of the back reference, and store it in | ||
1489 | edests of the back reference. */ | ||
1490 | org_dest = dfa->nexts[org_node]; | ||
1491 | re_node_set_empty (dfa->edests + clone_node); | ||
1492 | clone_dest = duplicate_node (dfa, org_dest, constraint); | ||
1493 | if (BE (clone_dest == -1, 0)) | ||
1494 | return REG_ESPACE; | ||
1495 | dfa->nexts[clone_node] = dfa->nexts[org_node]; | ||
1496 | ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); | ||
1497 | if (BE (ret < 0, 0)) | ||
1498 | return REG_ESPACE; | ||
1499 | } | ||
1500 | else if (dfa->edests[org_node].nelem == 0) | ||
1501 | { | ||
1502 | /* In case of the node can't epsilon-transit, don't duplicate the | ||
1503 | destination and store the original destination as the | ||
1504 | destination of the node. */ | ||
1505 | dfa->nexts[clone_node] = dfa->nexts[org_node]; | ||
1506 | break; | ||
1507 | } | ||
1508 | else if (dfa->edests[org_node].nelem == 1) | ||
1509 | { | ||
1510 | /* In case of the node can epsilon-transit, and it has only one | ||
1511 | destination. */ | ||
1512 | org_dest = dfa->edests[org_node].elems[0]; | ||
1513 | re_node_set_empty (dfa->edests + clone_node); | ||
1514 | /* If the node is root_node itself, it means the epsilon clsoure | ||
1515 | has a loop. Then tie it to the destination of the root_node. */ | ||
1516 | if (org_node == root_node && clone_node != org_node) | ||
1517 | { | ||
1518 | ret = re_node_set_insert (dfa->edests + clone_node, org_dest); | ||
1519 | if (BE (ret < 0, 0)) | ||
1520 | return REG_ESPACE; | ||
1521 | break; | ||
1522 | } | ||
1523 | /* In case of the node has another constraint, add it. */ | ||
1524 | constraint |= dfa->nodes[org_node].constraint; | ||
1525 | clone_dest = duplicate_node (dfa, org_dest, constraint); | ||
1526 | if (BE (clone_dest == -1, 0)) | ||
1527 | return REG_ESPACE; | ||
1528 | ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); | ||
1529 | if (BE (ret < 0, 0)) | ||
1530 | return REG_ESPACE; | ||
1531 | } | ||
1532 | else /* dfa->edests[org_node].nelem == 2 */ | ||
1533 | { | ||
1534 | /* In case of the node can epsilon-transit, and it has two | ||
1535 | destinations. In the bin_tree_t and DFA, that's '|' and '*'. */ | ||
1536 | org_dest = dfa->edests[org_node].elems[0]; | ||
1537 | re_node_set_empty (dfa->edests + clone_node); | ||
1538 | /* Search for a duplicated node which satisfies the constraint. */ | ||
1539 | clone_dest = search_duplicated_node (dfa, org_dest, constraint); | ||
1540 | if (clone_dest == -1) | ||
1541 | { | ||
1542 | /* There is no such duplicated node, create a new one. */ | ||
1543 | reg_errcode_t err; | ||
1544 | clone_dest = duplicate_node (dfa, org_dest, constraint); | ||
1545 | if (BE (clone_dest == -1, 0)) | ||
1546 | return REG_ESPACE; | ||
1547 | ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); | ||
1548 | if (BE (ret < 0, 0)) | ||
1549 | return REG_ESPACE; | ||
1550 | err = duplicate_node_closure (dfa, org_dest, clone_dest, | ||
1551 | root_node, constraint); | ||
1552 | if (BE (err != REG_NOERROR, 0)) | ||
1553 | return err; | ||
1554 | } | ||
1555 | else | ||
1556 | { | ||
1557 | /* There is a duplicated node which satisfies the constraint, | ||
1558 | use it to avoid infinite loop. */ | ||
1559 | ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); | ||
1560 | if (BE (ret < 0, 0)) | ||
1561 | return REG_ESPACE; | ||
1562 | } | ||
1563 | |||
1564 | org_dest = dfa->edests[org_node].elems[1]; | ||
1565 | clone_dest = duplicate_node (dfa, org_dest, constraint); | ||
1566 | if (BE (clone_dest == -1, 0)) | ||
1567 | return REG_ESPACE; | ||
1568 | ret = re_node_set_insert (dfa->edests + clone_node, clone_dest); | ||
1569 | if (BE (ret < 0, 0)) | ||
1570 | return REG_ESPACE; | ||
1571 | } | ||
1572 | org_node = org_dest; | ||
1573 | clone_node = clone_dest; | ||
1574 | } | ||
1575 | return REG_NOERROR; | ||
1576 | } | ||
1577 | |||
1578 | /* Search for a node which is duplicated from the node ORG_NODE, and | ||
1579 | satisfies the constraint CONSTRAINT. */ | ||
1580 | |||
1581 | static int | ||
1582 | search_duplicated_node (const re_dfa_t *dfa, int org_node, | ||
1583 | unsigned int constraint) | ||
1584 | { | ||
1585 | int idx; | ||
1586 | for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx) | ||
1587 | { | ||
1588 | if (org_node == dfa->org_indices[idx] | ||
1589 | && constraint == dfa->nodes[idx].constraint) | ||
1590 | return idx; /* Found. */ | ||
1591 | } | ||
1592 | return -1; /* Not found. */ | ||
1593 | } | ||
1594 | |||
1595 | /* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT. | ||
1596 | Return the index of the new node, or -1 if insufficient storage is | ||
1597 | available. */ | ||
1598 | |||
1599 | static int | ||
1600 | duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint) | ||
1601 | { | ||
1602 | int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]); | ||
1603 | if (BE (dup_idx != -1, 1)) | ||
1604 | { | ||
1605 | dfa->nodes[dup_idx].constraint = constraint; | ||
1606 | dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint; | ||
1607 | dfa->nodes[dup_idx].duplicated = 1; | ||
1608 | |||
1609 | /* Store the index of the original node. */ | ||
1610 | dfa->org_indices[dup_idx] = org_idx; | ||
1611 | } | ||
1612 | return dup_idx; | ||
1613 | } | ||
1614 | |||
1615 | static reg_errcode_t | ||
1616 | calc_inveclosure (re_dfa_t *dfa) | ||
1617 | { | ||
1618 | int src, idx, ret; | ||
1619 | for (idx = 0; idx < dfa->nodes_len; ++idx) | ||
1620 | re_node_set_init_empty (dfa->inveclosures + idx); | ||
1621 | |||
1622 | for (src = 0; src < dfa->nodes_len; ++src) | ||
1623 | { | ||
1624 | int *elems = dfa->eclosures[src].elems; | ||
1625 | for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx) | ||
1626 | { | ||
1627 | ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src); | ||
1628 | if (BE (ret == -1, 0)) | ||
1629 | return REG_ESPACE; | ||
1630 | } | ||
1631 | } | ||
1632 | |||
1633 | return REG_NOERROR; | ||
1634 | } | ||
1635 | |||
1636 | /* Calculate "eclosure" for all the node in DFA. */ | ||
1637 | |||
1638 | static reg_errcode_t | ||
1639 | calc_eclosure (re_dfa_t *dfa) | ||
1640 | { | ||
1641 | int node_idx, incomplete; | ||
1642 | #ifdef DEBUG | ||
1643 | assert (dfa->nodes_len > 0); | ||
1644 | #endif | ||
1645 | incomplete = 0; | ||
1646 | /* For each nodes, calculate epsilon closure. */ | ||
1647 | for (node_idx = 0; ; ++node_idx) | ||
1648 | { | ||
1649 | reg_errcode_t err; | ||
1650 | re_node_set eclosure_elem; | ||
1651 | if (node_idx == dfa->nodes_len) | ||
1652 | { | ||
1653 | if (!incomplete) | ||
1654 | break; | ||
1655 | incomplete = 0; | ||
1656 | node_idx = 0; | ||
1657 | } | ||
1658 | |||
1659 | #ifdef DEBUG | ||
1660 | assert (dfa->eclosures[node_idx].nelem != -1); | ||
1661 | #endif | ||
1662 | |||
1663 | /* If we have already calculated, skip it. */ | ||
1664 | if (dfa->eclosures[node_idx].nelem != 0) | ||
1665 | continue; | ||
1666 | /* Calculate epsilon closure of `node_idx'. */ | ||
1667 | err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1); | ||
1668 | if (BE (err != REG_NOERROR, 0)) | ||
1669 | return err; | ||
1670 | |||
1671 | if (dfa->eclosures[node_idx].nelem == 0) | ||
1672 | { | ||
1673 | incomplete = 1; | ||
1674 | re_node_set_free (&eclosure_elem); | ||
1675 | } | ||
1676 | } | ||
1677 | return REG_NOERROR; | ||
1678 | } | ||
1679 | |||
1680 | /* Calculate epsilon closure of NODE. */ | ||
1681 | |||
1682 | static reg_errcode_t | ||
1683 | calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root) | ||
1684 | { | ||
1685 | reg_errcode_t err; | ||
1686 | int i; | ||
1687 | re_node_set eclosure; | ||
1688 | int ret; | ||
1689 | int incomplete = 0; | ||
1690 | err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1); | ||
1691 | if (BE (err != REG_NOERROR, 0)) | ||
1692 | return err; | ||
1693 | |||
1694 | /* This indicates that we are calculating this node now. | ||
1695 | We reference this value to avoid infinite loop. */ | ||
1696 | dfa->eclosures[node].nelem = -1; | ||
1697 | |||
1698 | /* If the current node has constraints, duplicate all nodes | ||
1699 | since they must inherit the constraints. */ | ||
1700 | if (dfa->nodes[node].constraint | ||
1701 | && dfa->edests[node].nelem | ||
1702 | && !dfa->nodes[dfa->edests[node].elems[0]].duplicated) | ||
1703 | { | ||
1704 | err = duplicate_node_closure (dfa, node, node, node, | ||
1705 | dfa->nodes[node].constraint); | ||
1706 | if (BE (err != REG_NOERROR, 0)) | ||
1707 | return err; | ||
1708 | } | ||
1709 | |||
1710 | /* Expand each epsilon destination nodes. */ | ||
1711 | if (IS_EPSILON_NODE(dfa->nodes[node].type)) | ||
1712 | for (i = 0; i < dfa->edests[node].nelem; ++i) | ||
1713 | { | ||
1714 | re_node_set eclosure_elem; | ||
1715 | int edest = dfa->edests[node].elems[i]; | ||
1716 | /* If calculating the epsilon closure of `edest' is in progress, | ||
1717 | return intermediate result. */ | ||
1718 | if (dfa->eclosures[edest].nelem == -1) | ||
1719 | { | ||
1720 | incomplete = 1; | ||
1721 | continue; | ||
1722 | } | ||
1723 | /* If we haven't calculated the epsilon closure of `edest' yet, | ||
1724 | calculate now. Otherwise use calculated epsilon closure. */ | ||
1725 | if (dfa->eclosures[edest].nelem == 0) | ||
1726 | { | ||
1727 | err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0); | ||
1728 | if (BE (err != REG_NOERROR, 0)) | ||
1729 | return err; | ||
1730 | } | ||
1731 | else | ||
1732 | eclosure_elem = dfa->eclosures[edest]; | ||
1733 | /* Merge the epsilon closure of `edest'. */ | ||
1734 | err = re_node_set_merge (&eclosure, &eclosure_elem); | ||
1735 | if (BE (err != REG_NOERROR, 0)) | ||
1736 | return err; | ||
1737 | /* If the epsilon closure of `edest' is incomplete, | ||
1738 | the epsilon closure of this node is also incomplete. */ | ||
1739 | if (dfa->eclosures[edest].nelem == 0) | ||
1740 | { | ||
1741 | incomplete = 1; | ||
1742 | re_node_set_free (&eclosure_elem); | ||
1743 | } | ||
1744 | } | ||
1745 | |||
1746 | /* An epsilon closure includes itself. */ | ||
1747 | ret = re_node_set_insert (&eclosure, node); | ||
1748 | if (BE (ret < 0, 0)) | ||
1749 | return REG_ESPACE; | ||
1750 | if (incomplete && !root) | ||
1751 | dfa->eclosures[node].nelem = 0; | ||
1752 | else | ||
1753 | dfa->eclosures[node] = eclosure; | ||
1754 | *new_set = eclosure; | ||
1755 | return REG_NOERROR; | ||
1756 | } | ||
1757 | |||
1758 | /* Functions for token which are used in the parser. */ | ||
1759 | |||
1760 | /* Fetch a token from INPUT. | ||
1761 | We must not use this function inside bracket expressions. */ | ||
1762 | |||
1763 | static void | ||
1764 | internal_function | ||
1765 | fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax) | ||
1766 | { | ||
1767 | re_string_skip_bytes (input, peek_token (result, input, syntax)); | ||
1768 | } | ||
1769 | |||
1770 | /* Peek a token from INPUT, and return the length of the token. | ||
1771 | We must not use this function inside bracket expressions. */ | ||
1772 | |||
1773 | static int | ||
1774 | internal_function | ||
1775 | peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax) | ||
1776 | { | ||
1777 | unsigned char c; | ||
1778 | |||
1779 | if (re_string_eoi (input)) | ||
1780 | { | ||
1781 | token->type = END_OF_RE; | ||
1782 | return 0; | ||
1783 | } | ||
1784 | |||
1785 | c = re_string_peek_byte (input, 0); | ||
1786 | token->opr.c = c; | ||
1787 | |||
1788 | token->word_char = 0; | ||
1789 | #ifdef RE_ENABLE_I18N | ||
1790 | token->mb_partial = 0; | ||
1791 | if (input->mb_cur_max > 1 && | ||
1792 | !re_string_first_byte (input, re_string_cur_idx (input))) | ||
1793 | { | ||
1794 | token->type = CHARACTER; | ||
1795 | token->mb_partial = 1; | ||
1796 | return 1; | ||
1797 | } | ||
1798 | #endif | ||
1799 | if (c == '\\') | ||
1800 | { | ||
1801 | unsigned char c2; | ||
1802 | if (re_string_cur_idx (input) + 1 >= re_string_length (input)) | ||
1803 | { | ||
1804 | token->type = BACK_SLASH; | ||
1805 | return 1; | ||
1806 | } | ||
1807 | |||
1808 | c2 = re_string_peek_byte_case (input, 1); | ||
1809 | token->opr.c = c2; | ||
1810 | token->type = CHARACTER; | ||
1811 | #ifdef RE_ENABLE_I18N | ||
1812 | if (input->mb_cur_max > 1) | ||
1813 | { | ||
1814 | wint_t wc = re_string_wchar_at (input, | ||
1815 | re_string_cur_idx (input) + 1); | ||
1816 | token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; | ||
1817 | } | ||
1818 | else | ||
1819 | #endif | ||
1820 | token->word_char = IS_WORD_CHAR (c2) != 0; | ||
1821 | |||
1822 | switch (c2) | ||
1823 | { | ||
1824 | case '|': | ||
1825 | if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR)) | ||
1826 | token->type = OP_ALT; | ||
1827 | break; | ||
1828 | case '1': case '2': case '3': case '4': case '5': | ||
1829 | case '6': case '7': case '8': case '9': | ||
1830 | if (!(syntax & RE_NO_BK_REFS)) | ||
1831 | { | ||
1832 | token->type = OP_BACK_REF; | ||
1833 | token->opr.idx = c2 - '1'; | ||
1834 | } | ||
1835 | break; | ||
1836 | case '<': | ||
1837 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1838 | { | ||
1839 | token->type = ANCHOR; | ||
1840 | token->opr.ctx_type = WORD_FIRST; | ||
1841 | } | ||
1842 | break; | ||
1843 | case '>': | ||
1844 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1845 | { | ||
1846 | token->type = ANCHOR; | ||
1847 | token->opr.ctx_type = WORD_LAST; | ||
1848 | } | ||
1849 | break; | ||
1850 | case 'b': | ||
1851 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1852 | { | ||
1853 | token->type = ANCHOR; | ||
1854 | token->opr.ctx_type = WORD_DELIM; | ||
1855 | } | ||
1856 | break; | ||
1857 | case 'B': | ||
1858 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1859 | { | ||
1860 | token->type = ANCHOR; | ||
1861 | token->opr.ctx_type = NOT_WORD_DELIM; | ||
1862 | } | ||
1863 | break; | ||
1864 | case 'w': | ||
1865 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1866 | token->type = OP_WORD; | ||
1867 | break; | ||
1868 | case 'W': | ||
1869 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1870 | token->type = OP_NOTWORD; | ||
1871 | break; | ||
1872 | case 's': | ||
1873 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1874 | token->type = OP_SPACE; | ||
1875 | break; | ||
1876 | case 'S': | ||
1877 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1878 | token->type = OP_NOTSPACE; | ||
1879 | break; | ||
1880 | case '`': | ||
1881 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1882 | { | ||
1883 | token->type = ANCHOR; | ||
1884 | token->opr.ctx_type = BUF_FIRST; | ||
1885 | } | ||
1886 | break; | ||
1887 | case '\'': | ||
1888 | if (!(syntax & RE_NO_GNU_OPS)) | ||
1889 | { | ||
1890 | token->type = ANCHOR; | ||
1891 | token->opr.ctx_type = BUF_LAST; | ||
1892 | } | ||
1893 | break; | ||
1894 | case '(': | ||
1895 | if (!(syntax & RE_NO_BK_PARENS)) | ||
1896 | token->type = OP_OPEN_SUBEXP; | ||
1897 | break; | ||
1898 | case ')': | ||
1899 | if (!(syntax & RE_NO_BK_PARENS)) | ||
1900 | token->type = OP_CLOSE_SUBEXP; | ||
1901 | break; | ||
1902 | case '+': | ||
1903 | if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) | ||
1904 | token->type = OP_DUP_PLUS; | ||
1905 | break; | ||
1906 | case '?': | ||
1907 | if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM)) | ||
1908 | token->type = OP_DUP_QUESTION; | ||
1909 | break; | ||
1910 | case '{': | ||
1911 | if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) | ||
1912 | token->type = OP_OPEN_DUP_NUM; | ||
1913 | break; | ||
1914 | case '}': | ||
1915 | if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES))) | ||
1916 | token->type = OP_CLOSE_DUP_NUM; | ||
1917 | break; | ||
1918 | default: | ||
1919 | break; | ||
1920 | } | ||
1921 | return 2; | ||
1922 | } | ||
1923 | |||
1924 | token->type = CHARACTER; | ||
1925 | #ifdef RE_ENABLE_I18N | ||
1926 | if (input->mb_cur_max > 1) | ||
1927 | { | ||
1928 | wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input)); | ||
1929 | token->word_char = IS_WIDE_WORD_CHAR (wc) != 0; | ||
1930 | } | ||
1931 | else | ||
1932 | #endif | ||
1933 | token->word_char = IS_WORD_CHAR (token->opr.c); | ||
1934 | |||
1935 | switch (c) | ||
1936 | { | ||
1937 | case '\n': | ||
1938 | if (syntax & RE_NEWLINE_ALT) | ||
1939 | token->type = OP_ALT; | ||
1940 | break; | ||
1941 | case '|': | ||
1942 | if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR)) | ||
1943 | token->type = OP_ALT; | ||
1944 | break; | ||
1945 | case '*': | ||
1946 | token->type = OP_DUP_ASTERISK; | ||
1947 | break; | ||
1948 | case '+': | ||
1949 | if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) | ||
1950 | token->type = OP_DUP_PLUS; | ||
1951 | break; | ||
1952 | case '?': | ||
1953 | if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM)) | ||
1954 | token->type = OP_DUP_QUESTION; | ||
1955 | break; | ||
1956 | case '{': | ||
1957 | if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) | ||
1958 | token->type = OP_OPEN_DUP_NUM; | ||
1959 | break; | ||
1960 | case '}': | ||
1961 | if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES)) | ||
1962 | token->type = OP_CLOSE_DUP_NUM; | ||
1963 | break; | ||
1964 | case '(': | ||
1965 | if (syntax & RE_NO_BK_PARENS) | ||
1966 | token->type = OP_OPEN_SUBEXP; | ||
1967 | break; | ||
1968 | case ')': | ||
1969 | if (syntax & RE_NO_BK_PARENS) | ||
1970 | token->type = OP_CLOSE_SUBEXP; | ||
1971 | break; | ||
1972 | case '[': | ||
1973 | token->type = OP_OPEN_BRACKET; | ||
1974 | break; | ||
1975 | case '.': | ||
1976 | token->type = OP_PERIOD; | ||
1977 | break; | ||
1978 | case '^': | ||
1979 | if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) && | ||
1980 | re_string_cur_idx (input) != 0) | ||
1981 | { | ||
1982 | char prev = re_string_peek_byte (input, -1); | ||
1983 | if (!(syntax & RE_NEWLINE_ALT) || prev != '\n') | ||
1984 | break; | ||
1985 | } | ||
1986 | token->type = ANCHOR; | ||
1987 | token->opr.ctx_type = LINE_FIRST; | ||
1988 | break; | ||
1989 | case '$': | ||
1990 | if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) && | ||
1991 | re_string_cur_idx (input) + 1 != re_string_length (input)) | ||
1992 | { | ||
1993 | re_token_t next; | ||
1994 | re_string_skip_bytes (input, 1); | ||
1995 | peek_token (&next, input, syntax); | ||
1996 | re_string_skip_bytes (input, -1); | ||
1997 | if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP) | ||
1998 | break; | ||
1999 | } | ||
2000 | token->type = ANCHOR; | ||
2001 | token->opr.ctx_type = LINE_LAST; | ||
2002 | break; | ||
2003 | default: | ||
2004 | break; | ||
2005 | } | ||
2006 | return 1; | ||
2007 | } | ||
2008 | |||
2009 | /* Peek a token from INPUT, and return the length of the token. | ||
2010 | We must not use this function out of bracket expressions. */ | ||
2011 | |||
2012 | static int | ||
2013 | internal_function | ||
2014 | peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax) | ||
2015 | { | ||
2016 | unsigned char c; | ||
2017 | if (re_string_eoi (input)) | ||
2018 | { | ||
2019 | token->type = END_OF_RE; | ||
2020 | return 0; | ||
2021 | } | ||
2022 | c = re_string_peek_byte (input, 0); | ||
2023 | token->opr.c = c; | ||
2024 | |||
2025 | #ifdef RE_ENABLE_I18N | ||
2026 | if (input->mb_cur_max > 1 && | ||
2027 | !re_string_first_byte (input, re_string_cur_idx (input))) | ||
2028 | { | ||
2029 | token->type = CHARACTER; | ||
2030 | return 1; | ||
2031 | } | ||
2032 | #endif /* RE_ENABLE_I18N */ | ||
2033 | |||
2034 | if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS) | ||
2035 | && re_string_cur_idx (input) + 1 < re_string_length (input)) | ||
2036 | { | ||
2037 | /* In this case, '\' escape a character. */ | ||
2038 | unsigned char c2; | ||
2039 | re_string_skip_bytes (input, 1); | ||
2040 | c2 = re_string_peek_byte (input, 0); | ||
2041 | token->opr.c = c2; | ||
2042 | token->type = CHARACTER; | ||
2043 | return 1; | ||
2044 | } | ||
2045 | if (c == '[') /* '[' is a special char in a bracket exps. */ | ||
2046 | { | ||
2047 | unsigned char c2; | ||
2048 | int token_len; | ||
2049 | if (re_string_cur_idx (input) + 1 < re_string_length (input)) | ||
2050 | c2 = re_string_peek_byte (input, 1); | ||
2051 | else | ||
2052 | c2 = 0; | ||
2053 | token->opr.c = c2; | ||
2054 | token_len = 2; | ||
2055 | switch (c2) | ||
2056 | { | ||
2057 | case '.': | ||
2058 | token->type = OP_OPEN_COLL_ELEM; | ||
2059 | break; | ||
2060 | case '=': | ||
2061 | token->type = OP_OPEN_EQUIV_CLASS; | ||
2062 | break; | ||
2063 | case ':': | ||
2064 | if (syntax & RE_CHAR_CLASSES) | ||
2065 | { | ||
2066 | token->type = OP_OPEN_CHAR_CLASS; | ||
2067 | break; | ||
2068 | } | ||
2069 | /* else fall through. */ | ||
2070 | default: | ||
2071 | token->type = CHARACTER; | ||
2072 | token->opr.c = c; | ||
2073 | token_len = 1; | ||
2074 | break; | ||
2075 | } | ||
2076 | return token_len; | ||
2077 | } | ||
2078 | switch (c) | ||
2079 | { | ||
2080 | case '-': | ||
2081 | token->type = OP_CHARSET_RANGE; | ||
2082 | break; | ||
2083 | case ']': | ||
2084 | token->type = OP_CLOSE_BRACKET; | ||
2085 | break; | ||
2086 | case '^': | ||
2087 | token->type = OP_NON_MATCH_LIST; | ||
2088 | break; | ||
2089 | default: | ||
2090 | token->type = CHARACTER; | ||
2091 | } | ||
2092 | return 1; | ||
2093 | } | ||
2094 | |||
2095 | /* Functions for parser. */ | ||
2096 | |||
2097 | /* Entry point of the parser. | ||
2098 | Parse the regular expression REGEXP and return the structure tree. | ||
2099 | If an error has occurred, ERR is set by error code, and return NULL. | ||
2100 | This function build the following tree, from regular expression <reg_exp>: | ||
2101 | CAT | ||
2102 | / \ | ||
2103 | / \ | ||
2104 | <reg_exp> EOR | ||
2105 | |||
2106 | CAT means concatenation. | ||
2107 | EOR means end of regular expression. */ | ||
2108 | |||
2109 | static bin_tree_t * | ||
2110 | parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax, | ||
2111 | reg_errcode_t *err) | ||
2112 | { | ||
2113 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
2114 | bin_tree_t *tree, *eor, *root; | ||
2115 | re_token_t current_token; | ||
2116 | dfa->syntax = syntax; | ||
2117 | fetch_token (¤t_token, regexp, syntax | RE_CARET_ANCHORS_HERE); | ||
2118 | tree = parse_reg_exp (regexp, preg, ¤t_token, syntax, 0, err); | ||
2119 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2120 | return NULL; | ||
2121 | eor = create_tree (dfa, NULL, NULL, END_OF_RE); | ||
2122 | if (tree != NULL) | ||
2123 | root = create_tree (dfa, tree, eor, CONCAT); | ||
2124 | else | ||
2125 | root = eor; | ||
2126 | if (BE (eor == NULL || root == NULL, 0)) | ||
2127 | { | ||
2128 | *err = REG_ESPACE; | ||
2129 | return NULL; | ||
2130 | } | ||
2131 | return root; | ||
2132 | } | ||
2133 | |||
2134 | /* This function build the following tree, from regular expression | ||
2135 | <branch1>|<branch2>: | ||
2136 | ALT | ||
2137 | / \ | ||
2138 | / \ | ||
2139 | <branch1> <branch2> | ||
2140 | |||
2141 | ALT means alternative, which represents the operator `|'. */ | ||
2142 | |||
2143 | static bin_tree_t * | ||
2144 | parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, | ||
2145 | reg_syntax_t syntax, int nest, reg_errcode_t *err) | ||
2146 | { | ||
2147 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
2148 | bin_tree_t *tree, *branch = NULL; | ||
2149 | tree = parse_branch (regexp, preg, token, syntax, nest, err); | ||
2150 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2151 | return NULL; | ||
2152 | |||
2153 | while (token->type == OP_ALT) | ||
2154 | { | ||
2155 | fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); | ||
2156 | if (token->type != OP_ALT && token->type != END_OF_RE | ||
2157 | && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) | ||
2158 | { | ||
2159 | branch = parse_branch (regexp, preg, token, syntax, nest, err); | ||
2160 | if (BE (*err != REG_NOERROR && branch == NULL, 0)) | ||
2161 | return NULL; | ||
2162 | } | ||
2163 | else | ||
2164 | branch = NULL; | ||
2165 | tree = create_tree (dfa, tree, branch, OP_ALT); | ||
2166 | if (BE (tree == NULL, 0)) | ||
2167 | { | ||
2168 | *err = REG_ESPACE; | ||
2169 | return NULL; | ||
2170 | } | ||
2171 | } | ||
2172 | return tree; | ||
2173 | } | ||
2174 | |||
2175 | /* This function build the following tree, from regular expression | ||
2176 | <exp1><exp2>: | ||
2177 | CAT | ||
2178 | / \ | ||
2179 | / \ | ||
2180 | <exp1> <exp2> | ||
2181 | |||
2182 | CAT means concatenation. */ | ||
2183 | |||
2184 | static bin_tree_t * | ||
2185 | parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token, | ||
2186 | reg_syntax_t syntax, int nest, reg_errcode_t *err) | ||
2187 | { | ||
2188 | bin_tree_t *tree, *exp; | ||
2189 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
2190 | tree = parse_expression (regexp, preg, token, syntax, nest, err); | ||
2191 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2192 | return NULL; | ||
2193 | |||
2194 | while (token->type != OP_ALT && token->type != END_OF_RE | ||
2195 | && (nest == 0 || token->type != OP_CLOSE_SUBEXP)) | ||
2196 | { | ||
2197 | exp = parse_expression (regexp, preg, token, syntax, nest, err); | ||
2198 | if (BE (*err != REG_NOERROR && exp == NULL, 0)) | ||
2199 | { | ||
2200 | return NULL; | ||
2201 | } | ||
2202 | if (tree != NULL && exp != NULL) | ||
2203 | { | ||
2204 | tree = create_tree (dfa, tree, exp, CONCAT); | ||
2205 | if (tree == NULL) | ||
2206 | { | ||
2207 | *err = REG_ESPACE; | ||
2208 | return NULL; | ||
2209 | } | ||
2210 | } | ||
2211 | else if (tree == NULL) | ||
2212 | tree = exp; | ||
2213 | /* Otherwise exp == NULL, we don't need to create new tree. */ | ||
2214 | } | ||
2215 | return tree; | ||
2216 | } | ||
2217 | |||
2218 | /* This function build the following tree, from regular expression a*: | ||
2219 | * | ||
2220 | | | ||
2221 | a | ||
2222 | */ | ||
2223 | |||
2224 | static bin_tree_t * | ||
2225 | parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token, | ||
2226 | reg_syntax_t syntax, int nest, reg_errcode_t *err) | ||
2227 | { | ||
2228 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
2229 | bin_tree_t *tree; | ||
2230 | switch (token->type) | ||
2231 | { | ||
2232 | case CHARACTER: | ||
2233 | tree = create_token_tree (dfa, NULL, NULL, token); | ||
2234 | if (BE (tree == NULL, 0)) | ||
2235 | { | ||
2236 | *err = REG_ESPACE; | ||
2237 | return NULL; | ||
2238 | } | ||
2239 | #ifdef RE_ENABLE_I18N | ||
2240 | if (dfa->mb_cur_max > 1) | ||
2241 | { | ||
2242 | while (!re_string_eoi (regexp) | ||
2243 | && !re_string_first_byte (regexp, re_string_cur_idx (regexp))) | ||
2244 | { | ||
2245 | bin_tree_t *mbc_remain; | ||
2246 | fetch_token (token, regexp, syntax); | ||
2247 | mbc_remain = create_token_tree (dfa, NULL, NULL, token); | ||
2248 | tree = create_tree (dfa, tree, mbc_remain, CONCAT); | ||
2249 | if (BE (mbc_remain == NULL || tree == NULL, 0)) | ||
2250 | { | ||
2251 | *err = REG_ESPACE; | ||
2252 | return NULL; | ||
2253 | } | ||
2254 | } | ||
2255 | } | ||
2256 | #endif | ||
2257 | break; | ||
2258 | case OP_OPEN_SUBEXP: | ||
2259 | tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err); | ||
2260 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2261 | return NULL; | ||
2262 | break; | ||
2263 | case OP_OPEN_BRACKET: | ||
2264 | tree = parse_bracket_exp (regexp, dfa, token, syntax, err); | ||
2265 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2266 | return NULL; | ||
2267 | break; | ||
2268 | case OP_BACK_REF: | ||
2269 | if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1)) | ||
2270 | { | ||
2271 | *err = REG_ESUBREG; | ||
2272 | return NULL; | ||
2273 | } | ||
2274 | dfa->used_bkref_map |= 1 << token->opr.idx; | ||
2275 | tree = create_token_tree (dfa, NULL, NULL, token); | ||
2276 | if (BE (tree == NULL, 0)) | ||
2277 | { | ||
2278 | *err = REG_ESPACE; | ||
2279 | return NULL; | ||
2280 | } | ||
2281 | ++dfa->nbackref; | ||
2282 | dfa->has_mb_node = 1; | ||
2283 | break; | ||
2284 | case OP_OPEN_DUP_NUM: | ||
2285 | if (syntax & RE_CONTEXT_INVALID_DUP) | ||
2286 | { | ||
2287 | *err = REG_BADRPT; | ||
2288 | return NULL; | ||
2289 | } | ||
2290 | /* FALLTHROUGH */ | ||
2291 | case OP_DUP_ASTERISK: | ||
2292 | case OP_DUP_PLUS: | ||
2293 | case OP_DUP_QUESTION: | ||
2294 | if (syntax & RE_CONTEXT_INVALID_OPS) | ||
2295 | { | ||
2296 | *err = REG_BADRPT; | ||
2297 | return NULL; | ||
2298 | } | ||
2299 | else if (syntax & RE_CONTEXT_INDEP_OPS) | ||
2300 | { | ||
2301 | fetch_token (token, regexp, syntax); | ||
2302 | return parse_expression (regexp, preg, token, syntax, nest, err); | ||
2303 | } | ||
2304 | /* else fall through */ | ||
2305 | case OP_CLOSE_SUBEXP: | ||
2306 | if ((token->type == OP_CLOSE_SUBEXP) && | ||
2307 | !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD)) | ||
2308 | { | ||
2309 | *err = REG_ERPAREN; | ||
2310 | return NULL; | ||
2311 | } | ||
2312 | /* else fall through */ | ||
2313 | case OP_CLOSE_DUP_NUM: | ||
2314 | /* We treat it as a normal character. */ | ||
2315 | |||
2316 | /* Then we can these characters as normal characters. */ | ||
2317 | token->type = CHARACTER; | ||
2318 | /* mb_partial and word_char bits should be initialized already | ||
2319 | by peek_token. */ | ||
2320 | tree = create_token_tree (dfa, NULL, NULL, token); | ||
2321 | if (BE (tree == NULL, 0)) | ||
2322 | { | ||
2323 | *err = REG_ESPACE; | ||
2324 | return NULL; | ||
2325 | } | ||
2326 | break; | ||
2327 | case ANCHOR: | ||
2328 | if ((token->opr.ctx_type | ||
2329 | & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST)) | ||
2330 | && dfa->word_ops_used == 0) | ||
2331 | init_word_char (dfa); | ||
2332 | if (token->opr.ctx_type == WORD_DELIM | ||
2333 | || token->opr.ctx_type == NOT_WORD_DELIM) | ||
2334 | { | ||
2335 | bin_tree_t *tree_first, *tree_last; | ||
2336 | if (token->opr.ctx_type == WORD_DELIM) | ||
2337 | { | ||
2338 | token->opr.ctx_type = WORD_FIRST; | ||
2339 | tree_first = create_token_tree (dfa, NULL, NULL, token); | ||
2340 | token->opr.ctx_type = WORD_LAST; | ||
2341 | } | ||
2342 | else | ||
2343 | { | ||
2344 | token->opr.ctx_type = INSIDE_WORD; | ||
2345 | tree_first = create_token_tree (dfa, NULL, NULL, token); | ||
2346 | token->opr.ctx_type = INSIDE_NOTWORD; | ||
2347 | } | ||
2348 | tree_last = create_token_tree (dfa, NULL, NULL, token); | ||
2349 | tree = create_tree (dfa, tree_first, tree_last, OP_ALT); | ||
2350 | if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0)) | ||
2351 | { | ||
2352 | *err = REG_ESPACE; | ||
2353 | return NULL; | ||
2354 | } | ||
2355 | } | ||
2356 | else | ||
2357 | { | ||
2358 | tree = create_token_tree (dfa, NULL, NULL, token); | ||
2359 | if (BE (tree == NULL, 0)) | ||
2360 | { | ||
2361 | *err = REG_ESPACE; | ||
2362 | return NULL; | ||
2363 | } | ||
2364 | } | ||
2365 | /* We must return here, since ANCHORs can't be followed | ||
2366 | by repetition operators. | ||
2367 | eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>", | ||
2368 | it must not be "<ANCHOR(^)><REPEAT(*)>". */ | ||
2369 | fetch_token (token, regexp, syntax); | ||
2370 | return tree; | ||
2371 | case OP_PERIOD: | ||
2372 | tree = create_token_tree (dfa, NULL, NULL, token); | ||
2373 | if (BE (tree == NULL, 0)) | ||
2374 | { | ||
2375 | *err = REG_ESPACE; | ||
2376 | return NULL; | ||
2377 | } | ||
2378 | if (dfa->mb_cur_max > 1) | ||
2379 | dfa->has_mb_node = 1; | ||
2380 | break; | ||
2381 | case OP_WORD: | ||
2382 | case OP_NOTWORD: | ||
2383 | tree = build_charclass_op (dfa, regexp->trans, | ||
2384 | "alnum", | ||
2385 | "_", | ||
2386 | token->type == OP_NOTWORD, err); | ||
2387 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2388 | return NULL; | ||
2389 | break; | ||
2390 | case OP_SPACE: | ||
2391 | case OP_NOTSPACE: | ||
2392 | tree = build_charclass_op (dfa, regexp->trans, | ||
2393 | "space", | ||
2394 | "", | ||
2395 | token->type == OP_NOTSPACE, err); | ||
2396 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2397 | return NULL; | ||
2398 | break; | ||
2399 | case OP_ALT: | ||
2400 | case END_OF_RE: | ||
2401 | return NULL; | ||
2402 | case BACK_SLASH: | ||
2403 | *err = REG_EESCAPE; | ||
2404 | return NULL; | ||
2405 | default: | ||
2406 | /* Must not happen? */ | ||
2407 | #ifdef DEBUG | ||
2408 | assert (0); | ||
2409 | #endif | ||
2410 | return NULL; | ||
2411 | } | ||
2412 | fetch_token (token, regexp, syntax); | ||
2413 | |||
2414 | while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS | ||
2415 | || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM) | ||
2416 | { | ||
2417 | tree = parse_dup_op (tree, regexp, dfa, token, syntax, err); | ||
2418 | if (BE (*err != REG_NOERROR && tree == NULL, 0)) | ||
2419 | return NULL; | ||
2420 | /* In BRE consecutive duplications are not allowed. */ | ||
2421 | if ((syntax & RE_CONTEXT_INVALID_DUP) | ||
2422 | && (token->type == OP_DUP_ASTERISK | ||
2423 | || token->type == OP_OPEN_DUP_NUM)) | ||
2424 | { | ||
2425 | *err = REG_BADRPT; | ||
2426 | return NULL; | ||
2427 | } | ||
2428 | } | ||
2429 | |||
2430 | return tree; | ||
2431 | } | ||
2432 | |||
2433 | /* This function build the following tree, from regular expression | ||
2434 | (<reg_exp>): | ||
2435 | SUBEXP | ||
2436 | | | ||
2437 | <reg_exp> | ||
2438 | */ | ||
2439 | |||
2440 | static bin_tree_t * | ||
2441 | parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token, | ||
2442 | reg_syntax_t syntax, int nest, reg_errcode_t *err) | ||
2443 | { | ||
2444 | re_dfa_t *dfa = (re_dfa_t *) preg->buffer; | ||
2445 | bin_tree_t *tree; | ||
2446 | size_t cur_nsub; | ||
2447 | cur_nsub = preg->re_nsub++; | ||
2448 | |||
2449 | fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE); | ||
2450 | |||
2451 | /* The subexpression may be a null string. */ | ||
2452 | if (token->type == OP_CLOSE_SUBEXP) | ||
2453 | tree = NULL; | ||
2454 | else | ||
2455 | { | ||
2456 | tree = parse_reg_exp (regexp, preg, token, syntax, nest, err); | ||
2457 | if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0)) | ||
2458 | *err = REG_EPAREN; | ||
2459 | if (BE (*err != REG_NOERROR, 0)) | ||
2460 | return NULL; | ||
2461 | } | ||
2462 | |||
2463 | if (cur_nsub <= '9' - '1') | ||
2464 | dfa->completed_bkref_map |= 1 << cur_nsub; | ||
2465 | |||
2466 | tree = create_tree (dfa, tree, NULL, SUBEXP); | ||
2467 | if (BE (tree == NULL, 0)) | ||
2468 | { | ||
2469 | *err = REG_ESPACE; | ||
2470 | return NULL; | ||
2471 | } | ||
2472 | tree->token.opr.idx = cur_nsub; | ||
2473 | return tree; | ||
2474 | } | ||
2475 | |||
2476 | /* This function parse repetition operators like "*", "+", "{1,3}" etc. */ | ||
2477 | |||
2478 | static bin_tree_t * | ||
2479 | parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa, | ||
2480 | re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err) | ||
2481 | { | ||
2482 | bin_tree_t *tree = NULL, *old_tree = NULL; | ||
2483 | int i, start, end, start_idx = re_string_cur_idx (regexp); | ||
2484 | #ifndef RE_TOKEN_INIT_BUG | ||
2485 | re_token_t start_token = *token; | ||
2486 | #else | ||
2487 | re_token_t start_token; | ||
2488 | |||
2489 | memcpy ((void *) &start_token, (void *) token, sizeof start_token); | ||
2490 | #endif | ||
2491 | |||
2492 | if (token->type == OP_OPEN_DUP_NUM) | ||
2493 | { | ||
2494 | end = 0; | ||
2495 | start = fetch_number (regexp, token, syntax); | ||
2496 | if (start == -1) | ||
2497 | { | ||
2498 | if (token->type == CHARACTER && token->opr.c == ',') | ||
2499 | start = 0; /* We treat "{,m}" as "{0,m}". */ | ||
2500 | else | ||
2501 | { | ||
2502 | *err = REG_BADBR; /* <re>{} is invalid. */ | ||
2503 | return NULL; | ||
2504 | } | ||
2505 | } | ||
2506 | if (BE (start != -2, 1)) | ||
2507 | { | ||
2508 | /* We treat "{n}" as "{n,n}". */ | ||
2509 | end = ((token->type == OP_CLOSE_DUP_NUM) ? start | ||
2510 | : ((token->type == CHARACTER && token->opr.c == ',') | ||
2511 | ? fetch_number (regexp, token, syntax) : -2)); | ||
2512 | } | ||
2513 | if (BE (start == -2 || end == -2, 0)) | ||
2514 | { | ||
2515 | /* Invalid sequence. */ | ||
2516 | if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0)) | ||
2517 | { | ||
2518 | if (token->type == END_OF_RE) | ||
2519 | *err = REG_EBRACE; | ||
2520 | else | ||
2521 | *err = REG_BADBR; | ||
2522 | |||
2523 | return NULL; | ||
2524 | } | ||
2525 | |||
2526 | /* If the syntax bit is set, rollback. */ | ||
2527 | re_string_set_index (regexp, start_idx); | ||
2528 | *token = start_token; | ||
2529 | token->type = CHARACTER; | ||
2530 | /* mb_partial and word_char bits should be already initialized by | ||
2531 | peek_token. */ | ||
2532 | return elem; | ||
2533 | } | ||
2534 | |||
2535 | if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0)) | ||
2536 | { | ||
2537 | /* First number greater than second. */ | ||
2538 | *err = REG_BADBR; | ||
2539 | return NULL; | ||
2540 | } | ||
2541 | } | ||
2542 | else | ||
2543 | { | ||
2544 | start = (token->type == OP_DUP_PLUS) ? 1 : 0; | ||
2545 | end = (token->type == OP_DUP_QUESTION) ? 1 : -1; | ||
2546 | } | ||
2547 | |||
2548 | fetch_token (token, regexp, syntax); | ||
2549 | |||
2550 | if (BE (elem == NULL, 0)) | ||
2551 | return NULL; | ||
2552 | if (BE (start == 0 && end == 0, 0)) | ||
2553 | { | ||
2554 | postorder (elem, free_tree, NULL); | ||
2555 | return NULL; | ||
2556 | } | ||
2557 | |||
2558 | /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */ | ||
2559 | if (BE (start > 0, 0)) | ||
2560 | { | ||
2561 | tree = elem; | ||
2562 | for (i = 2; i <= start; ++i) | ||
2563 | { | ||
2564 | elem = duplicate_tree (elem, dfa); | ||
2565 | tree = create_tree (dfa, tree, elem, CONCAT); | ||
2566 | if (BE (elem == NULL || tree == NULL, 0)) | ||
2567 | goto parse_dup_op_espace; | ||
2568 | } | ||
2569 | |||
2570 | if (start == end) | ||
2571 | return tree; | ||
2572 | |||
2573 | /* Duplicate ELEM before it is marked optional. */ | ||
2574 | elem = duplicate_tree (elem, dfa); | ||
2575 | old_tree = tree; | ||
2576 | } | ||
2577 | else | ||
2578 | old_tree = NULL; | ||
2579 | |||
2580 | if (elem->token.type == SUBEXP) | ||
2581 | postorder (elem, mark_opt_subexp, (void *) (intptr_t) elem->token.opr.idx); | ||
2582 | |||
2583 | tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT)); | ||
2584 | if (BE (tree == NULL, 0)) | ||
2585 | goto parse_dup_op_espace; | ||
2586 | |||
2587 | /* This loop is actually executed only when end != -1, | ||
2588 | to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?... We have | ||
2589 | already created the start+1-th copy. */ | ||
2590 | for (i = start + 2; i <= end; ++i) | ||
2591 | { | ||
2592 | elem = duplicate_tree (elem, dfa); | ||
2593 | tree = create_tree (dfa, tree, elem, CONCAT); | ||
2594 | if (BE (elem == NULL || tree == NULL, 0)) | ||
2595 | goto parse_dup_op_espace; | ||
2596 | |||
2597 | tree = create_tree (dfa, tree, NULL, OP_ALT); | ||
2598 | if (BE (tree == NULL, 0)) | ||
2599 | goto parse_dup_op_espace; | ||
2600 | } | ||
2601 | |||
2602 | if (old_tree) | ||
2603 | tree = create_tree (dfa, old_tree, tree, CONCAT); | ||
2604 | |||
2605 | return tree; | ||
2606 | |||
2607 | parse_dup_op_espace: | ||
2608 | *err = REG_ESPACE; | ||
2609 | return NULL; | ||
2610 | } | ||
2611 | |||
2612 | /* Size of the names for collating symbol/equivalence_class/character_class. | ||
2613 | I'm not sure, but maybe enough. */ | ||
2614 | #define BRACKET_NAME_BUF_SIZE 32 | ||
2615 | |||
2616 | #ifndef _LIBC | ||
2617 | /* Local function for parse_bracket_exp only used in case of NOT _LIBC. | ||
2618 | Build the range expression which starts from START_ELEM, and ends | ||
2619 | at END_ELEM. The result are written to MBCSET and SBCSET. | ||
2620 | RANGE_ALLOC is the allocated size of mbcset->range_starts, and | ||
2621 | mbcset->range_ends, is a pointer argument since we may | ||
2622 | update it. */ | ||
2623 | |||
2624 | static reg_errcode_t | ||
2625 | internal_function | ||
2626 | # ifdef RE_ENABLE_I18N | ||
2627 | build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc, | ||
2628 | bracket_elem_t *start_elem, bracket_elem_t *end_elem) | ||
2629 | # else /* not RE_ENABLE_I18N */ | ||
2630 | build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem, | ||
2631 | bracket_elem_t *end_elem) | ||
2632 | # endif /* not RE_ENABLE_I18N */ | ||
2633 | { | ||
2634 | unsigned int start_ch, end_ch; | ||
2635 | /* Equivalence Classes and Character Classes can't be a range start/end. */ | ||
2636 | if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS | ||
2637 | || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, | ||
2638 | 0)) | ||
2639 | return REG_ERANGE; | ||
2640 | |||
2641 | /* We can handle no multi character collating elements without libc | ||
2642 | support. */ | ||
2643 | if (BE ((start_elem->type == COLL_SYM | ||
2644 | && strlen ((char *) start_elem->opr.name) > 1) | ||
2645 | || (end_elem->type == COLL_SYM | ||
2646 | && strlen ((char *) end_elem->opr.name) > 1), 0)) | ||
2647 | return REG_ECOLLATE; | ||
2648 | |||
2649 | # ifdef RE_ENABLE_I18N | ||
2650 | { | ||
2651 | wchar_t wc; | ||
2652 | wint_t start_wc; | ||
2653 | wint_t end_wc; | ||
2654 | wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; | ||
2655 | |||
2656 | start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch | ||
2657 | : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] | ||
2658 | : 0)); | ||
2659 | end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch | ||
2660 | : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] | ||
2661 | : 0)); | ||
2662 | #ifdef GAWK | ||
2663 | /* | ||
2664 | * Fedora Core 2, maybe others, have broken `btowc' that returns -1 | ||
2665 | * for any value > 127. Sigh. Note that `start_ch' and `end_ch' are | ||
2666 | * unsigned, so we don't have sign extension problems. | ||
2667 | */ | ||
2668 | start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) | ||
2669 | ? start_ch : start_elem->opr.wch); | ||
2670 | end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) | ||
2671 | ? end_ch : end_elem->opr.wch); | ||
2672 | #else | ||
2673 | start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM) | ||
2674 | ? __btowc (start_ch) : start_elem->opr.wch); | ||
2675 | end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM) | ||
2676 | ? __btowc (end_ch) : end_elem->opr.wch); | ||
2677 | #endif | ||
2678 | if (start_wc == WEOF || end_wc == WEOF) | ||
2679 | return REG_ECOLLATE; | ||
2680 | cmp_buf[0] = start_wc; | ||
2681 | cmp_buf[4] = end_wc; | ||
2682 | if (wcscoll (cmp_buf, cmp_buf + 4) > 0) | ||
2683 | return REG_ERANGE; | ||
2684 | |||
2685 | /* Got valid collation sequence values, add them as a new entry. | ||
2686 | However, for !_LIBC we have no collation elements: if the | ||
2687 | character set is single byte, the single byte character set | ||
2688 | that we build below suffices. parse_bracket_exp passes | ||
2689 | no MBCSET if dfa->mb_cur_max == 1. */ | ||
2690 | if (mbcset) | ||
2691 | { | ||
2692 | /* Check the space of the arrays. */ | ||
2693 | if (BE (*range_alloc == mbcset->nranges, 0)) | ||
2694 | { | ||
2695 | /* There is not enough space, need realloc. */ | ||
2696 | wchar_t *new_array_start, *new_array_end; | ||
2697 | int new_nranges; | ||
2698 | |||
2699 | /* +1 in case of mbcset->nranges is 0. */ | ||
2700 | new_nranges = 2 * mbcset->nranges + 1; | ||
2701 | /* Use realloc since mbcset->range_starts and mbcset->range_ends | ||
2702 | are NULL if *range_alloc == 0. */ | ||
2703 | new_array_start = re_realloc (mbcset->range_starts, wchar_t, | ||
2704 | new_nranges); | ||
2705 | new_array_end = re_realloc (mbcset->range_ends, wchar_t, | ||
2706 | new_nranges); | ||
2707 | |||
2708 | if (BE (new_array_start == NULL || new_array_end == NULL, 0)) | ||
2709 | return REG_ESPACE; | ||
2710 | |||
2711 | mbcset->range_starts = new_array_start; | ||
2712 | mbcset->range_ends = new_array_end; | ||
2713 | *range_alloc = new_nranges; | ||
2714 | } | ||
2715 | |||
2716 | mbcset->range_starts[mbcset->nranges] = start_wc; | ||
2717 | mbcset->range_ends[mbcset->nranges++] = end_wc; | ||
2718 | } | ||
2719 | |||
2720 | /* Build the table for single byte characters. */ | ||
2721 | for (wc = 0; wc < SBC_MAX; ++wc) | ||
2722 | { | ||
2723 | cmp_buf[2] = wc; | ||
2724 | if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 | ||
2725 | && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) | ||
2726 | bitset_set (sbcset, wc); | ||
2727 | } | ||
2728 | } | ||
2729 | # else /* not RE_ENABLE_I18N */ | ||
2730 | { | ||
2731 | unsigned int ch; | ||
2732 | start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch | ||
2733 | : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0] | ||
2734 | : 0)); | ||
2735 | end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch | ||
2736 | : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0] | ||
2737 | : 0)); | ||
2738 | if (start_ch > end_ch) | ||
2739 | return REG_ERANGE; | ||
2740 | /* Build the table for single byte characters. */ | ||
2741 | for (ch = 0; ch < SBC_MAX; ++ch) | ||
2742 | if (start_ch <= ch && ch <= end_ch) | ||
2743 | bitset_set (sbcset, ch); | ||
2744 | } | ||
2745 | # endif /* not RE_ENABLE_I18N */ | ||
2746 | return REG_NOERROR; | ||
2747 | } | ||
2748 | #endif /* not _LIBC */ | ||
2749 | |||
2750 | #ifndef _LIBC | ||
2751 | /* Helper function for parse_bracket_exp only used in case of NOT _LIBC.. | ||
2752 | Build the collating element which is represented by NAME. | ||
2753 | The result are written to MBCSET and SBCSET. | ||
2754 | COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a | ||
2755 | pointer argument since we may update it. */ | ||
2756 | |||
2757 | static reg_errcode_t | ||
2758 | internal_function | ||
2759 | # ifdef RE_ENABLE_I18N | ||
2760 | build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset, | ||
2761 | int *coll_sym_alloc, const unsigned char *name) | ||
2762 | # else /* not RE_ENABLE_I18N */ | ||
2763 | build_collating_symbol (bitset_t sbcset, const unsigned char *name) | ||
2764 | # endif /* not RE_ENABLE_I18N */ | ||
2765 | { | ||
2766 | size_t name_len = strlen ((const char *) name); | ||
2767 | if (BE (name_len != 1, 0)) | ||
2768 | return REG_ECOLLATE; | ||
2769 | else | ||
2770 | { | ||
2771 | bitset_set (sbcset, name[0]); | ||
2772 | return REG_NOERROR; | ||
2773 | } | ||
2774 | } | ||
2775 | #endif /* not _LIBC */ | ||
2776 | |||
2777 | /* This function parse bracket expression like "[abc]", "[a-c]", | ||
2778 | "[[.a-a.]]" etc. */ | ||
2779 | |||
2780 | static bin_tree_t * | ||
2781 | parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token, | ||
2782 | reg_syntax_t syntax, reg_errcode_t *err) | ||
2783 | { | ||
2784 | #ifdef _LIBC | ||
2785 | const unsigned char *collseqmb; | ||
2786 | const char *collseqwc; | ||
2787 | uint32_t nrules; | ||
2788 | int32_t table_size; | ||
2789 | const int32_t *symb_table; | ||
2790 | const unsigned char *extra; | ||
2791 | |||
2792 | /* Local function for parse_bracket_exp used in _LIBC environment. | ||
2793 | Seek the collating symbol entry correspondings to NAME. | ||
2794 | Return the index of the symbol in the SYMB_TABLE. */ | ||
2795 | |||
2796 | auto inline int32_t | ||
2797 | __attribute ((always_inline)) | ||
2798 | seek_collating_symbol_entry (name, name_len) | ||
2799 | const unsigned char *name; | ||
2800 | size_t name_len; | ||
2801 | { | ||
2802 | int32_t hash = elem_hash ((const char *) name, name_len); | ||
2803 | int32_t elem = hash % table_size; | ||
2804 | if (symb_table[2 * elem] != 0) | ||
2805 | { | ||
2806 | int32_t second = hash % (table_size - 2) + 1; | ||
2807 | |||
2808 | do | ||
2809 | { | ||
2810 | /* First compare the hashing value. */ | ||
2811 | if (symb_table[2 * elem] == hash | ||
2812 | /* Compare the length of the name. */ | ||
2813 | && name_len == extra[symb_table[2 * elem + 1]] | ||
2814 | /* Compare the name. */ | ||
2815 | && memcmp (name, &extra[symb_table[2 * elem + 1] + 1], | ||
2816 | name_len) == 0) | ||
2817 | { | ||
2818 | /* Yep, this is the entry. */ | ||
2819 | break; | ||
2820 | } | ||
2821 | |||
2822 | /* Next entry. */ | ||
2823 | elem += second; | ||
2824 | } | ||
2825 | while (symb_table[2 * elem] != 0); | ||
2826 | } | ||
2827 | return elem; | ||
2828 | } | ||
2829 | |||
2830 | /* Local function for parse_bracket_exp used in _LIBC environment. | ||
2831 | Look up the collation sequence value of BR_ELEM. | ||
2832 | Return the value if succeeded, UINT_MAX otherwise. */ | ||
2833 | |||
2834 | auto inline unsigned int | ||
2835 | __attribute ((always_inline)) | ||
2836 | lookup_collation_sequence_value (br_elem) | ||
2837 | bracket_elem_t *br_elem; | ||
2838 | { | ||
2839 | if (br_elem->type == SB_CHAR) | ||
2840 | { | ||
2841 | /* | ||
2842 | if (MB_CUR_MAX == 1) | ||
2843 | */ | ||
2844 | if (nrules == 0) | ||
2845 | return collseqmb[br_elem->opr.ch]; | ||
2846 | else | ||
2847 | { | ||
2848 | wint_t wc = __btowc (br_elem->opr.ch); | ||
2849 | return __collseq_table_lookup (collseqwc, wc); | ||
2850 | } | ||
2851 | } | ||
2852 | else if (br_elem->type == MB_CHAR) | ||
2853 | { | ||
2854 | if (nrules != 0) | ||
2855 | return __collseq_table_lookup (collseqwc, br_elem->opr.wch); | ||
2856 | } | ||
2857 | else if (br_elem->type == COLL_SYM) | ||
2858 | { | ||
2859 | size_t sym_name_len = strlen ((char *) br_elem->opr.name); | ||
2860 | if (nrules != 0) | ||
2861 | { | ||
2862 | int32_t elem, idx; | ||
2863 | elem = seek_collating_symbol_entry (br_elem->opr.name, | ||
2864 | sym_name_len); | ||
2865 | if (symb_table[2 * elem] != 0) | ||
2866 | { | ||
2867 | /* We found the entry. */ | ||
2868 | idx = symb_table[2 * elem + 1]; | ||
2869 | /* Skip the name of collating element name. */ | ||
2870 | idx += 1 + extra[idx]; | ||
2871 | /* Skip the byte sequence of the collating element. */ | ||
2872 | idx += 1 + extra[idx]; | ||
2873 | /* Adjust for the alignment. */ | ||
2874 | idx = (idx + 3) & ~3; | ||
2875 | /* Skip the multibyte collation sequence value. */ | ||
2876 | idx += sizeof (unsigned int); | ||
2877 | /* Skip the wide char sequence of the collating element. */ | ||
2878 | idx += sizeof (unsigned int) * | ||
2879 | (1 + *(unsigned int *) (extra + idx)); | ||
2880 | /* Return the collation sequence value. */ | ||
2881 | return *(unsigned int *) (extra + idx); | ||
2882 | } | ||
2883 | else if (symb_table[2 * elem] == 0 && sym_name_len == 1) | ||
2884 | { | ||
2885 | /* No valid character. Match it as a single byte | ||
2886 | character. */ | ||
2887 | return collseqmb[br_elem->opr.name[0]]; | ||
2888 | } | ||
2889 | } | ||
2890 | else if (sym_name_len == 1) | ||
2891 | return collseqmb[br_elem->opr.name[0]]; | ||
2892 | } | ||
2893 | return UINT_MAX; | ||
2894 | } | ||
2895 | |||
2896 | /* Local function for parse_bracket_exp used in _LIBC environment. | ||
2897 | Build the range expression which starts from START_ELEM, and ends | ||
2898 | at END_ELEM. The result are written to MBCSET and SBCSET. | ||
2899 | RANGE_ALLOC is the allocated size of mbcset->range_starts, and | ||
2900 | mbcset->range_ends, is a pointer argument since we may | ||
2901 | update it. */ | ||
2902 | |||
2903 | auto inline reg_errcode_t | ||
2904 | __attribute ((always_inline)) | ||
2905 | build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem) | ||
2906 | re_charset_t *mbcset; | ||
2907 | int *range_alloc; | ||
2908 | bitset_t sbcset; | ||
2909 | bracket_elem_t *start_elem, *end_elem; | ||
2910 | { | ||
2911 | unsigned int ch; | ||
2912 | uint32_t start_collseq; | ||
2913 | uint32_t end_collseq; | ||
2914 | |||
2915 | /* Equivalence Classes and Character Classes can't be a range | ||
2916 | start/end. */ | ||
2917 | if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS | ||
2918 | || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS, | ||
2919 | 0)) | ||
2920 | return REG_ERANGE; | ||
2921 | |||
2922 | start_collseq = lookup_collation_sequence_value (start_elem); | ||
2923 | end_collseq = lookup_collation_sequence_value (end_elem); | ||
2924 | /* Check start/end collation sequence values. */ | ||
2925 | if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0)) | ||
2926 | return REG_ECOLLATE; | ||
2927 | if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0)) | ||
2928 | return REG_ERANGE; | ||
2929 | |||
2930 | /* Got valid collation sequence values, add them as a new entry. | ||
2931 | However, if we have no collation elements, and the character set | ||
2932 | is single byte, the single byte character set that we | ||
2933 | build below suffices. */ | ||
2934 | if (nrules > 0 || dfa->mb_cur_max > 1) | ||
2935 | { | ||
2936 | /* Check the space of the arrays. */ | ||
2937 | if (BE (*range_alloc == mbcset->nranges, 0)) | ||
2938 | { | ||
2939 | /* There is not enough space, need realloc. */ | ||
2940 | uint32_t *new_array_start; | ||
2941 | uint32_t *new_array_end; | ||
2942 | int new_nranges; | ||
2943 | |||
2944 | /* +1 in case of mbcset->nranges is 0. */ | ||
2945 | new_nranges = 2 * mbcset->nranges + 1; | ||
2946 | new_array_start = re_realloc (mbcset->range_starts, uint32_t, | ||
2947 | new_nranges); | ||
2948 | new_array_end = re_realloc (mbcset->range_ends, uint32_t, | ||
2949 | new_nranges); | ||
2950 | |||
2951 | if (BE (new_array_start == NULL || new_array_end == NULL, 0)) | ||
2952 | return REG_ESPACE; | ||
2953 | |||
2954 | mbcset->range_starts = new_array_start; | ||
2955 | mbcset->range_ends = new_array_end; | ||
2956 | *range_alloc = new_nranges; | ||
2957 | } | ||
2958 | |||
2959 | mbcset->range_starts[mbcset->nranges] = start_collseq; | ||
2960 | mbcset->range_ends[mbcset->nranges++] = end_collseq; | ||
2961 | } | ||
2962 | |||
2963 | /* Build the table for single byte characters. */ | ||
2964 | for (ch = 0; ch < SBC_MAX; ch++) | ||
2965 | { | ||
2966 | uint32_t ch_collseq; | ||
2967 | /* | ||
2968 | if (MB_CUR_MAX == 1) | ||
2969 | */ | ||
2970 | if (nrules == 0) | ||
2971 | ch_collseq = collseqmb[ch]; | ||
2972 | else | ||
2973 | ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch)); | ||
2974 | if (start_collseq <= ch_collseq && ch_collseq <= end_collseq) | ||
2975 | bitset_set (sbcset, ch); | ||
2976 | } | ||
2977 | return REG_NOERROR; | ||
2978 | } | ||
2979 | |||
2980 | /* Local function for parse_bracket_exp used in _LIBC environment. | ||
2981 | Build the collating element which is represented by NAME. | ||
2982 | The result are written to MBCSET and SBCSET. | ||
2983 | COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a | ||
2984 | pointer argument since we may update it. */ | ||
2985 | |||
2986 | auto inline reg_errcode_t | ||
2987 | __attribute ((always_inline)) | ||
2988 | build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name) | ||
2989 | re_charset_t *mbcset; | ||
2990 | int *coll_sym_alloc; | ||
2991 | bitset_t sbcset; | ||
2992 | const unsigned char *name; | ||
2993 | { | ||
2994 | int32_t elem, idx; | ||
2995 | size_t name_len = strlen ((const char *) name); | ||
2996 | if (nrules != 0) | ||
2997 | { | ||
2998 | elem = seek_collating_symbol_entry (name, name_len); | ||
2999 | if (symb_table[2 * elem] != 0) | ||
3000 | { | ||
3001 | /* We found the entry. */ | ||
3002 | idx = symb_table[2 * elem + 1]; | ||
3003 | /* Skip the name of collating element name. */ | ||
3004 | idx += 1 + extra[idx]; | ||
3005 | } | ||
3006 | else if (symb_table[2 * elem] == 0 && name_len == 1) | ||
3007 | { | ||
3008 | /* No valid character, treat it as a normal | ||
3009 | character. */ | ||
3010 | bitset_set (sbcset, name[0]); | ||
3011 | return REG_NOERROR; | ||
3012 | } | ||
3013 | else | ||
3014 | return REG_ECOLLATE; | ||
3015 | |||
3016 | /* Got valid collation sequence, add it as a new entry. */ | ||
3017 | /* Check the space of the arrays. */ | ||
3018 | if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0)) | ||
3019 | { | ||
3020 | /* Not enough, realloc it. */ | ||
3021 | /* +1 in case of mbcset->ncoll_syms is 0. */ | ||
3022 | int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1; | ||
3023 | /* Use realloc since mbcset->coll_syms is NULL | ||
3024 | if *alloc == 0. */ | ||
3025 | int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t, | ||
3026 | new_coll_sym_alloc); | ||
3027 | if (BE (new_coll_syms == NULL, 0)) | ||
3028 | return REG_ESPACE; | ||
3029 | mbcset->coll_syms = new_coll_syms; | ||
3030 | *coll_sym_alloc = new_coll_sym_alloc; | ||
3031 | } | ||
3032 | mbcset->coll_syms[mbcset->ncoll_syms++] = idx; | ||
3033 | return REG_NOERROR; | ||
3034 | } | ||
3035 | else | ||
3036 | { | ||
3037 | if (BE (name_len != 1, 0)) | ||
3038 | return REG_ECOLLATE; | ||
3039 | else | ||
3040 | { | ||
3041 | bitset_set (sbcset, name[0]); | ||
3042 | return REG_NOERROR; | ||
3043 | } | ||
3044 | } | ||
3045 | } | ||
3046 | #endif | ||
3047 | |||
3048 | re_token_t br_token; | ||
3049 | re_bitset_ptr_t sbcset; | ||
3050 | #ifdef RE_ENABLE_I18N | ||
3051 | re_charset_t *mbcset; | ||
3052 | int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0; | ||
3053 | int equiv_class_alloc = 0, char_class_alloc = 0; | ||
3054 | #endif /* not RE_ENABLE_I18N */ | ||
3055 | int non_match = 0; | ||
3056 | bin_tree_t *work_tree; | ||
3057 | int token_len; | ||
3058 | int first_round = 1; | ||
3059 | #ifdef _LIBC | ||
3060 | collseqmb = (const unsigned char *) | ||
3061 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); | ||
3062 | nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); | ||
3063 | if (nrules) | ||
3064 | { | ||
3065 | /* | ||
3066 | if (MB_CUR_MAX > 1) | ||
3067 | */ | ||
3068 | collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); | ||
3069 | table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB); | ||
3070 | symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE, | ||
3071 | _NL_COLLATE_SYMB_TABLEMB); | ||
3072 | extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, | ||
3073 | _NL_COLLATE_SYMB_EXTRAMB); | ||
3074 | } | ||
3075 | #endif | ||
3076 | sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); | ||
3077 | #ifdef RE_ENABLE_I18N | ||
3078 | mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); | ||
3079 | #endif /* RE_ENABLE_I18N */ | ||
3080 | #ifdef RE_ENABLE_I18N | ||
3081 | if (BE (sbcset == NULL || mbcset == NULL, 0)) | ||
3082 | #else | ||
3083 | if (BE (sbcset == NULL, 0)) | ||
3084 | #endif /* RE_ENABLE_I18N */ | ||
3085 | { | ||
3086 | *err = REG_ESPACE; | ||
3087 | return NULL; | ||
3088 | } | ||
3089 | |||
3090 | token_len = peek_token_bracket (token, regexp, syntax); | ||
3091 | if (BE (token->type == END_OF_RE, 0)) | ||
3092 | { | ||
3093 | *err = REG_BADPAT; | ||
3094 | goto parse_bracket_exp_free_return; | ||
3095 | } | ||
3096 | if (token->type == OP_NON_MATCH_LIST) | ||
3097 | { | ||
3098 | #ifdef RE_ENABLE_I18N | ||
3099 | mbcset->non_match = 1; | ||
3100 | #endif /* not RE_ENABLE_I18N */ | ||
3101 | non_match = 1; | ||
3102 | if (syntax & RE_HAT_LISTS_NOT_NEWLINE) | ||
3103 | bitset_set (sbcset, '\n'); | ||
3104 | re_string_skip_bytes (regexp, token_len); /* Skip a token. */ | ||
3105 | token_len = peek_token_bracket (token, regexp, syntax); | ||
3106 | if (BE (token->type == END_OF_RE, 0)) | ||
3107 | { | ||
3108 | *err = REG_BADPAT; | ||
3109 | goto parse_bracket_exp_free_return; | ||
3110 | } | ||
3111 | } | ||
3112 | |||
3113 | /* We treat the first ']' as a normal character. */ | ||
3114 | if (token->type == OP_CLOSE_BRACKET) | ||
3115 | token->type = CHARACTER; | ||
3116 | |||
3117 | while (1) | ||
3118 | { | ||
3119 | bracket_elem_t start_elem, end_elem; | ||
3120 | unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE]; | ||
3121 | unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE]; | ||
3122 | reg_errcode_t ret; | ||
3123 | int token_len2 = 0, is_range_exp = 0; | ||
3124 | re_token_t token2; | ||
3125 | |||
3126 | start_elem.opr.name = start_name_buf; | ||
3127 | ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa, | ||
3128 | syntax, first_round); | ||
3129 | if (BE (ret != REG_NOERROR, 0)) | ||
3130 | { | ||
3131 | *err = ret; | ||
3132 | goto parse_bracket_exp_free_return; | ||
3133 | } | ||
3134 | first_round = 0; | ||
3135 | |||
3136 | /* Get information about the next token. We need it in any case. */ | ||
3137 | token_len = peek_token_bracket (token, regexp, syntax); | ||
3138 | |||
3139 | /* Do not check for ranges if we know they are not allowed. */ | ||
3140 | if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS) | ||
3141 | { | ||
3142 | if (BE (token->type == END_OF_RE, 0)) | ||
3143 | { | ||
3144 | *err = REG_EBRACK; | ||
3145 | goto parse_bracket_exp_free_return; | ||
3146 | } | ||
3147 | if (token->type == OP_CHARSET_RANGE) | ||
3148 | { | ||
3149 | re_string_skip_bytes (regexp, token_len); /* Skip '-'. */ | ||
3150 | token_len2 = peek_token_bracket (&token2, regexp, syntax); | ||
3151 | if (BE (token2.type == END_OF_RE, 0)) | ||
3152 | { | ||
3153 | *err = REG_EBRACK; | ||
3154 | goto parse_bracket_exp_free_return; | ||
3155 | } | ||
3156 | if (token2.type == OP_CLOSE_BRACKET) | ||
3157 | { | ||
3158 | /* We treat the last '-' as a normal character. */ | ||
3159 | re_string_skip_bytes (regexp, -token_len); | ||
3160 | token->type = CHARACTER; | ||
3161 | } | ||
3162 | else | ||
3163 | is_range_exp = 1; | ||
3164 | } | ||
3165 | } | ||
3166 | |||
3167 | if (is_range_exp == 1) | ||
3168 | { | ||
3169 | end_elem.opr.name = end_name_buf; | ||
3170 | ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2, | ||
3171 | dfa, syntax, 1); | ||
3172 | if (BE (ret != REG_NOERROR, 0)) | ||
3173 | { | ||
3174 | *err = ret; | ||
3175 | goto parse_bracket_exp_free_return; | ||
3176 | } | ||
3177 | |||
3178 | token_len = peek_token_bracket (token, regexp, syntax); | ||
3179 | |||
3180 | #ifdef _LIBC | ||
3181 | *err = build_range_exp (sbcset, mbcset, &range_alloc, | ||
3182 | &start_elem, &end_elem); | ||
3183 | #else | ||
3184 | # ifdef RE_ENABLE_I18N | ||
3185 | *err = build_range_exp (sbcset, | ||
3186 | dfa->mb_cur_max > 1 ? mbcset : NULL, | ||
3187 | &range_alloc, &start_elem, &end_elem); | ||
3188 | # else | ||
3189 | *err = build_range_exp (sbcset, &start_elem, &end_elem); | ||
3190 | # endif | ||
3191 | #endif /* RE_ENABLE_I18N */ | ||
3192 | if (BE (*err != REG_NOERROR, 0)) | ||
3193 | goto parse_bracket_exp_free_return; | ||
3194 | } | ||
3195 | else | ||
3196 | { | ||
3197 | switch (start_elem.type) | ||
3198 | { | ||
3199 | case SB_CHAR: | ||
3200 | bitset_set (sbcset, start_elem.opr.ch); | ||
3201 | break; | ||
3202 | #ifdef RE_ENABLE_I18N | ||
3203 | case MB_CHAR: | ||
3204 | /* Check whether the array has enough space. */ | ||
3205 | if (BE (mbchar_alloc == mbcset->nmbchars, 0)) | ||
3206 | { | ||
3207 | wchar_t *new_mbchars; | ||
3208 | /* Not enough, realloc it. */ | ||
3209 | /* +1 in case of mbcset->nmbchars is 0. */ | ||
3210 | mbchar_alloc = 2 * mbcset->nmbchars + 1; | ||
3211 | /* Use realloc since array is NULL if *alloc == 0. */ | ||
3212 | new_mbchars = re_realloc (mbcset->mbchars, wchar_t, | ||
3213 | mbchar_alloc); | ||
3214 | if (BE (new_mbchars == NULL, 0)) | ||
3215 | goto parse_bracket_exp_espace; | ||
3216 | mbcset->mbchars = new_mbchars; | ||
3217 | } | ||
3218 | mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch; | ||
3219 | break; | ||
3220 | #endif /* RE_ENABLE_I18N */ | ||
3221 | case EQUIV_CLASS: | ||
3222 | *err = build_equiv_class (sbcset, | ||
3223 | #ifdef RE_ENABLE_I18N | ||
3224 | mbcset, &equiv_class_alloc, | ||
3225 | #endif /* RE_ENABLE_I18N */ | ||
3226 | start_elem.opr.name); | ||
3227 | if (BE (*err != REG_NOERROR, 0)) | ||
3228 | goto parse_bracket_exp_free_return; | ||
3229 | break; | ||
3230 | case COLL_SYM: | ||
3231 | *err = build_collating_symbol (sbcset, | ||
3232 | #ifdef RE_ENABLE_I18N | ||
3233 | mbcset, &coll_sym_alloc, | ||
3234 | #endif /* RE_ENABLE_I18N */ | ||
3235 | start_elem.opr.name); | ||
3236 | if (BE (*err != REG_NOERROR, 0)) | ||
3237 | goto parse_bracket_exp_free_return; | ||
3238 | break; | ||
3239 | case CHAR_CLASS: | ||
3240 | *err = build_charclass (regexp->trans, sbcset, | ||
3241 | #ifdef RE_ENABLE_I18N | ||
3242 | mbcset, &char_class_alloc, | ||
3243 | #endif /* RE_ENABLE_I18N */ | ||
3244 | (const char *) start_elem.opr.name, syntax); | ||
3245 | if (BE (*err != REG_NOERROR, 0)) | ||
3246 | goto parse_bracket_exp_free_return; | ||
3247 | break; | ||
3248 | default: | ||
3249 | assert (0); | ||
3250 | break; | ||
3251 | } | ||
3252 | } | ||
3253 | if (BE (token->type == END_OF_RE, 0)) | ||
3254 | { | ||
3255 | *err = REG_EBRACK; | ||
3256 | goto parse_bracket_exp_free_return; | ||
3257 | } | ||
3258 | if (token->type == OP_CLOSE_BRACKET) | ||
3259 | break; | ||
3260 | } | ||
3261 | |||
3262 | re_string_skip_bytes (regexp, token_len); /* Skip a token. */ | ||
3263 | |||
3264 | /* If it is non-matching list. */ | ||
3265 | if (non_match) | ||
3266 | bitset_not (sbcset); | ||
3267 | |||
3268 | #ifdef RE_ENABLE_I18N | ||
3269 | /* Ensure only single byte characters are set. */ | ||
3270 | if (dfa->mb_cur_max > 1) | ||
3271 | bitset_mask (sbcset, dfa->sb_char); | ||
3272 | |||
3273 | if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes | ||
3274 | || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes | ||
3275 | || mbcset->non_match))) | ||
3276 | { | ||
3277 | bin_tree_t *mbc_tree; | ||
3278 | int sbc_idx; | ||
3279 | /* Build a tree for complex bracket. */ | ||
3280 | dfa->has_mb_node = 1; | ||
3281 | br_token.type = COMPLEX_BRACKET; | ||
3282 | br_token.opr.mbcset = mbcset; | ||
3283 | mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); | ||
3284 | if (BE (mbc_tree == NULL, 0)) | ||
3285 | goto parse_bracket_exp_espace; | ||
3286 | for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx) | ||
3287 | if (sbcset[sbc_idx]) | ||
3288 | break; | ||
3289 | /* If there are no bits set in sbcset, there is no point | ||
3290 | of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */ | ||
3291 | if (sbc_idx < BITSET_WORDS) | ||
3292 | { | ||
3293 | /* Build a tree for simple bracket. */ | ||
3294 | br_token.type = SIMPLE_BRACKET; | ||
3295 | br_token.opr.sbcset = sbcset; | ||
3296 | work_tree = create_token_tree (dfa, NULL, NULL, &br_token); | ||
3297 | if (BE (work_tree == NULL, 0)) | ||
3298 | goto parse_bracket_exp_espace; | ||
3299 | |||
3300 | /* Then join them by ALT node. */ | ||
3301 | work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT); | ||
3302 | if (BE (work_tree == NULL, 0)) | ||
3303 | goto parse_bracket_exp_espace; | ||
3304 | } | ||
3305 | else | ||
3306 | { | ||
3307 | re_free (sbcset); | ||
3308 | work_tree = mbc_tree; | ||
3309 | } | ||
3310 | } | ||
3311 | else | ||
3312 | #endif /* not RE_ENABLE_I18N */ | ||
3313 | { | ||
3314 | #ifdef RE_ENABLE_I18N | ||
3315 | free_charset (mbcset); | ||
3316 | #endif | ||
3317 | /* Build a tree for simple bracket. */ | ||
3318 | br_token.type = SIMPLE_BRACKET; | ||
3319 | br_token.opr.sbcset = sbcset; | ||
3320 | work_tree = create_token_tree (dfa, NULL, NULL, &br_token); | ||
3321 | if (BE (work_tree == NULL, 0)) | ||
3322 | goto parse_bracket_exp_espace; | ||
3323 | } | ||
3324 | return work_tree; | ||
3325 | |||
3326 | parse_bracket_exp_espace: | ||
3327 | *err = REG_ESPACE; | ||
3328 | parse_bracket_exp_free_return: | ||
3329 | re_free (sbcset); | ||
3330 | #ifdef RE_ENABLE_I18N | ||
3331 | free_charset (mbcset); | ||
3332 | #endif /* RE_ENABLE_I18N */ | ||
3333 | return NULL; | ||
3334 | } | ||
3335 | |||
3336 | /* Parse an element in the bracket expression. */ | ||
3337 | |||
3338 | static reg_errcode_t | ||
3339 | parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp, | ||
3340 | re_token_t *token, int token_len, | ||
3341 | UNUSED_PARAM re_dfa_t *dfa, reg_syntax_t syntax, | ||
3342 | int accept_hyphen) | ||
3343 | { | ||
3344 | #ifdef RE_ENABLE_I18N | ||
3345 | int cur_char_size; | ||
3346 | cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp)); | ||
3347 | if (cur_char_size > 1) | ||
3348 | { | ||
3349 | elem->type = MB_CHAR; | ||
3350 | elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp)); | ||
3351 | re_string_skip_bytes (regexp, cur_char_size); | ||
3352 | return REG_NOERROR; | ||
3353 | } | ||
3354 | #endif /* RE_ENABLE_I18N */ | ||
3355 | re_string_skip_bytes (regexp, token_len); /* Skip a token. */ | ||
3356 | if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS | ||
3357 | || token->type == OP_OPEN_EQUIV_CLASS) | ||
3358 | return parse_bracket_symbol (elem, regexp, token); | ||
3359 | if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen) | ||
3360 | { | ||
3361 | /* A '-' must only appear as anything but a range indicator before | ||
3362 | the closing bracket. Everything else is an error. */ | ||
3363 | re_token_t token2; | ||
3364 | (void) peek_token_bracket (&token2, regexp, syntax); | ||
3365 | if (token2.type != OP_CLOSE_BRACKET) | ||
3366 | /* The actual error value is not standardized since this whole | ||
3367 | case is undefined. But ERANGE makes good sense. */ | ||
3368 | return REG_ERANGE; | ||
3369 | } | ||
3370 | elem->type = SB_CHAR; | ||
3371 | elem->opr.ch = token->opr.c; | ||
3372 | return REG_NOERROR; | ||
3373 | } | ||
3374 | |||
3375 | /* Parse a bracket symbol in the bracket expression. Bracket symbols are | ||
3376 | such as [:<character_class>:], [.<collating_element>.], and | ||
3377 | [=<equivalent_class>=]. */ | ||
3378 | |||
3379 | static reg_errcode_t | ||
3380 | parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp, | ||
3381 | re_token_t *token) | ||
3382 | { | ||
3383 | unsigned char ch, delim = token->opr.c; | ||
3384 | int i = 0; | ||
3385 | if (re_string_eoi(regexp)) | ||
3386 | return REG_EBRACK; | ||
3387 | for (;; ++i) | ||
3388 | { | ||
3389 | if (i >= BRACKET_NAME_BUF_SIZE) | ||
3390 | return REG_EBRACK; | ||
3391 | if (token->type == OP_OPEN_CHAR_CLASS) | ||
3392 | ch = re_string_fetch_byte_case (regexp); | ||
3393 | else | ||
3394 | ch = re_string_fetch_byte (regexp); | ||
3395 | if (re_string_eoi(regexp)) | ||
3396 | return REG_EBRACK; | ||
3397 | if (ch == delim && re_string_peek_byte (regexp, 0) == ']') | ||
3398 | break; | ||
3399 | elem->opr.name[i] = ch; | ||
3400 | } | ||
3401 | re_string_skip_bytes (regexp, 1); | ||
3402 | elem->opr.name[i] = '\0'; | ||
3403 | switch (token->type) | ||
3404 | { | ||
3405 | case OP_OPEN_COLL_ELEM: | ||
3406 | elem->type = COLL_SYM; | ||
3407 | break; | ||
3408 | case OP_OPEN_EQUIV_CLASS: | ||
3409 | elem->type = EQUIV_CLASS; | ||
3410 | break; | ||
3411 | case OP_OPEN_CHAR_CLASS: | ||
3412 | elem->type = CHAR_CLASS; | ||
3413 | break; | ||
3414 | default: | ||
3415 | break; | ||
3416 | } | ||
3417 | return REG_NOERROR; | ||
3418 | } | ||
3419 | |||
3420 | /* Helper function for parse_bracket_exp. | ||
3421 | Build the equivalence class which is represented by NAME. | ||
3422 | The result are written to MBCSET and SBCSET. | ||
3423 | EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes, | ||
3424 | is a pointer argument since we may update it. */ | ||
3425 | |||
3426 | static reg_errcode_t | ||
3427 | #ifdef RE_ENABLE_I18N | ||
3428 | build_equiv_class (bitset_t sbcset, re_charset_t *mbcset, | ||
3429 | int *equiv_class_alloc, const unsigned char *name) | ||
3430 | #else /* not RE_ENABLE_I18N */ | ||
3431 | build_equiv_class (bitset_t sbcset, const unsigned char *name) | ||
3432 | #endif /* not RE_ENABLE_I18N */ | ||
3433 | { | ||
3434 | #ifdef _LIBC | ||
3435 | uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); | ||
3436 | if (nrules != 0) | ||
3437 | { | ||
3438 | const int32_t *table, *indirect; | ||
3439 | const unsigned char *weights, *extra, *cp; | ||
3440 | unsigned char char_buf[2]; | ||
3441 | int32_t idx1, idx2; | ||
3442 | unsigned int ch; | ||
3443 | size_t len; | ||
3444 | /* This #include defines a local function! */ | ||
3445 | # include <locale/weight.h> | ||
3446 | /* Calculate the index for equivalence class. */ | ||
3447 | cp = name; | ||
3448 | table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); | ||
3449 | weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE, | ||
3450 | _NL_COLLATE_WEIGHTMB); | ||
3451 | extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE, | ||
3452 | _NL_COLLATE_EXTRAMB); | ||
3453 | indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, | ||
3454 | _NL_COLLATE_INDIRECTMB); | ||
3455 | idx1 = findidx (&cp); | ||
3456 | if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0)) | ||
3457 | /* This isn't a valid character. */ | ||
3458 | return REG_ECOLLATE; | ||
3459 | |||
3460 | /* Build single byte matcing table for this equivalence class. */ | ||
3461 | char_buf[1] = (unsigned char) '\0'; | ||
3462 | len = weights[idx1 & 0xffffff]; | ||
3463 | for (ch = 0; ch < SBC_MAX; ++ch) | ||
3464 | { | ||
3465 | char_buf[0] = ch; | ||
3466 | cp = char_buf; | ||
3467 | idx2 = findidx (&cp); | ||
3468 | /* | ||
3469 | idx2 = table[ch]; | ||
3470 | */ | ||
3471 | if (idx2 == 0) | ||
3472 | /* This isn't a valid character. */ | ||
3473 | continue; | ||
3474 | /* Compare only if the length matches and the collation rule | ||
3475 | index is the same. */ | ||
3476 | if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24)) | ||
3477 | { | ||
3478 | int cnt = 0; | ||
3479 | |||
3480 | while (cnt <= len && | ||
3481 | weights[(idx1 & 0xffffff) + 1 + cnt] | ||
3482 | == weights[(idx2 & 0xffffff) + 1 + cnt]) | ||
3483 | ++cnt; | ||
3484 | |||
3485 | if (cnt > len) | ||
3486 | bitset_set (sbcset, ch); | ||
3487 | } | ||
3488 | } | ||
3489 | /* Check whether the array has enough space. */ | ||
3490 | if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0)) | ||
3491 | { | ||
3492 | /* Not enough, realloc it. */ | ||
3493 | /* +1 in case of mbcset->nequiv_classes is 0. */ | ||
3494 | int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1; | ||
3495 | /* Use realloc since the array is NULL if *alloc == 0. */ | ||
3496 | int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes, | ||
3497 | int32_t, | ||
3498 | new_equiv_class_alloc); | ||
3499 | if (BE (new_equiv_classes == NULL, 0)) | ||
3500 | return REG_ESPACE; | ||
3501 | mbcset->equiv_classes = new_equiv_classes; | ||
3502 | *equiv_class_alloc = new_equiv_class_alloc; | ||
3503 | } | ||
3504 | mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1; | ||
3505 | } | ||
3506 | else | ||
3507 | #endif /* _LIBC */ | ||
3508 | { | ||
3509 | if (BE (strlen ((const char *) name) != 1, 0)) | ||
3510 | return REG_ECOLLATE; | ||
3511 | bitset_set (sbcset, *name); | ||
3512 | } | ||
3513 | return REG_NOERROR; | ||
3514 | } | ||
3515 | |||
3516 | /* Helper function for parse_bracket_exp. | ||
3517 | Build the character class which is represented by NAME. | ||
3518 | The result are written to MBCSET and SBCSET. | ||
3519 | CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes, | ||
3520 | is a pointer argument since we may update it. */ | ||
3521 | |||
3522 | static reg_errcode_t | ||
3523 | #ifdef RE_ENABLE_I18N | ||
3524 | build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, | ||
3525 | re_charset_t *mbcset, int *char_class_alloc, | ||
3526 | const char *class_name, reg_syntax_t syntax) | ||
3527 | #else /* not RE_ENABLE_I18N */ | ||
3528 | build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset, | ||
3529 | const char *class_name, reg_syntax_t syntax) | ||
3530 | #endif /* not RE_ENABLE_I18N */ | ||
3531 | { | ||
3532 | int i; | ||
3533 | |||
3534 | /* In case of REG_ICASE "upper" and "lower" match the both of | ||
3535 | upper and lower cases. */ | ||
3536 | if ((syntax & RE_ICASE) | ||
3537 | && (strcmp (class_name, "upper") == 0 || strcmp (class_name, "lower") == 0)) | ||
3538 | class_name = "alpha"; | ||
3539 | |||
3540 | #ifdef RE_ENABLE_I18N | ||
3541 | /* Check the space of the arrays. */ | ||
3542 | if (BE (*char_class_alloc == mbcset->nchar_classes, 0)) | ||
3543 | { | ||
3544 | /* Not enough, realloc it. */ | ||
3545 | /* +1 in case of mbcset->nchar_classes is 0. */ | ||
3546 | int new_char_class_alloc = 2 * mbcset->nchar_classes + 1; | ||
3547 | /* Use realloc since array is NULL if *alloc == 0. */ | ||
3548 | wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t, | ||
3549 | new_char_class_alloc); | ||
3550 | if (BE (new_char_classes == NULL, 0)) | ||
3551 | return REG_ESPACE; | ||
3552 | mbcset->char_classes = new_char_classes; | ||
3553 | *char_class_alloc = new_char_class_alloc; | ||
3554 | } | ||
3555 | mbcset->char_classes[mbcset->nchar_classes++] = __wctype (class_name); | ||
3556 | #endif /* RE_ENABLE_I18N */ | ||
3557 | |||
3558 | #define BUILD_CHARCLASS_LOOP(ctype_func) \ | ||
3559 | do { \ | ||
3560 | if (BE (trans != NULL, 0)) \ | ||
3561 | { \ | ||
3562 | for (i = 0; i < SBC_MAX; ++i) \ | ||
3563 | if (ctype_func (i)) \ | ||
3564 | bitset_set (sbcset, trans[i]); \ | ||
3565 | } \ | ||
3566 | else \ | ||
3567 | { \ | ||
3568 | for (i = 0; i < SBC_MAX; ++i) \ | ||
3569 | if (ctype_func (i)) \ | ||
3570 | bitset_set (sbcset, i); \ | ||
3571 | } \ | ||
3572 | } while (0) | ||
3573 | |||
3574 | if (strcmp (class_name, "alnum") == 0) | ||
3575 | BUILD_CHARCLASS_LOOP (isalnum); | ||
3576 | else if (strcmp (class_name, "cntrl") == 0) | ||
3577 | BUILD_CHARCLASS_LOOP (iscntrl); | ||
3578 | else if (strcmp (class_name, "lower") == 0) | ||
3579 | BUILD_CHARCLASS_LOOP (islower); | ||
3580 | else if (strcmp (class_name, "space") == 0) | ||
3581 | BUILD_CHARCLASS_LOOP (isspace); | ||
3582 | else if (strcmp (class_name, "alpha") == 0) | ||
3583 | BUILD_CHARCLASS_LOOP (isalpha); | ||
3584 | else if (strcmp (class_name, "digit") == 0) | ||
3585 | BUILD_CHARCLASS_LOOP (isdigit); | ||
3586 | else if (strcmp (class_name, "print") == 0) | ||
3587 | BUILD_CHARCLASS_LOOP (isprint); | ||
3588 | else if (strcmp (class_name, "upper") == 0) | ||
3589 | BUILD_CHARCLASS_LOOP (isupper); | ||
3590 | else if (strcmp (class_name, "blank") == 0) | ||
3591 | #ifndef GAWK | ||
3592 | BUILD_CHARCLASS_LOOP (isblank); | ||
3593 | #else | ||
3594 | /* see comments above */ | ||
3595 | BUILD_CHARCLASS_LOOP (is_blank); | ||
3596 | #endif | ||
3597 | else if (strcmp (class_name, "graph") == 0) | ||
3598 | BUILD_CHARCLASS_LOOP (isgraph); | ||
3599 | else if (strcmp (class_name, "punct") == 0) | ||
3600 | BUILD_CHARCLASS_LOOP (ispunct); | ||
3601 | else if (strcmp (class_name, "xdigit") == 0) | ||
3602 | BUILD_CHARCLASS_LOOP (isxdigit); | ||
3603 | else | ||
3604 | return REG_ECTYPE; | ||
3605 | |||
3606 | return REG_NOERROR; | ||
3607 | } | ||
3608 | |||
3609 | static bin_tree_t * | ||
3610 | build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans, | ||
3611 | const char *class_name, | ||
3612 | const char *extra, int non_match, | ||
3613 | reg_errcode_t *err) | ||
3614 | { | ||
3615 | re_bitset_ptr_t sbcset; | ||
3616 | #ifdef RE_ENABLE_I18N | ||
3617 | re_charset_t *mbcset; | ||
3618 | int alloc = 0; | ||
3619 | #endif /* not RE_ENABLE_I18N */ | ||
3620 | reg_errcode_t ret; | ||
3621 | re_token_t br_token; | ||
3622 | bin_tree_t *tree; | ||
3623 | |||
3624 | sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1); | ||
3625 | #ifdef RE_ENABLE_I18N | ||
3626 | mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1); | ||
3627 | #endif /* RE_ENABLE_I18N */ | ||
3628 | |||
3629 | #ifdef RE_ENABLE_I18N | ||
3630 | if (BE (sbcset == NULL || mbcset == NULL, 0)) | ||
3631 | #else /* not RE_ENABLE_I18N */ | ||
3632 | if (BE (sbcset == NULL, 0)) | ||
3633 | #endif /* not RE_ENABLE_I18N */ | ||
3634 | { | ||
3635 | *err = REG_ESPACE; | ||
3636 | return NULL; | ||
3637 | } | ||
3638 | |||
3639 | if (non_match) | ||
3640 | { | ||
3641 | #ifdef RE_ENABLE_I18N | ||
3642 | mbcset->non_match = 1; | ||
3643 | #endif /* not RE_ENABLE_I18N */ | ||
3644 | } | ||
3645 | |||
3646 | /* We don't care the syntax in this case. */ | ||
3647 | ret = build_charclass (trans, sbcset, | ||
3648 | #ifdef RE_ENABLE_I18N | ||
3649 | mbcset, &alloc, | ||
3650 | #endif /* RE_ENABLE_I18N */ | ||
3651 | class_name, 0); | ||
3652 | |||
3653 | if (BE (ret != REG_NOERROR, 0)) | ||
3654 | { | ||
3655 | re_free (sbcset); | ||
3656 | #ifdef RE_ENABLE_I18N | ||
3657 | free_charset (mbcset); | ||
3658 | #endif /* RE_ENABLE_I18N */ | ||
3659 | *err = ret; | ||
3660 | return NULL; | ||
3661 | } | ||
3662 | /* \w match '_' also. */ | ||
3663 | for (; *extra; extra++) | ||
3664 | bitset_set (sbcset, *extra); | ||
3665 | |||
3666 | /* If it is non-matching list. */ | ||
3667 | if (non_match) | ||
3668 | bitset_not (sbcset); | ||
3669 | |||
3670 | #ifdef RE_ENABLE_I18N | ||
3671 | /* Ensure only single byte characters are set. */ | ||
3672 | if (dfa->mb_cur_max > 1) | ||
3673 | bitset_mask (sbcset, dfa->sb_char); | ||
3674 | #endif | ||
3675 | |||
3676 | /* Build a tree for simple bracket. */ | ||
3677 | br_token.type = SIMPLE_BRACKET; | ||
3678 | br_token.opr.sbcset = sbcset; | ||
3679 | tree = create_token_tree (dfa, NULL, NULL, &br_token); | ||
3680 | if (BE (tree == NULL, 0)) | ||
3681 | goto build_word_op_espace; | ||
3682 | |||
3683 | #ifdef RE_ENABLE_I18N | ||
3684 | if (dfa->mb_cur_max > 1) | ||
3685 | { | ||
3686 | bin_tree_t *mbc_tree; | ||
3687 | /* Build a tree for complex bracket. */ | ||
3688 | br_token.type = COMPLEX_BRACKET; | ||
3689 | br_token.opr.mbcset = mbcset; | ||
3690 | dfa->has_mb_node = 1; | ||
3691 | mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token); | ||
3692 | if (BE (mbc_tree == NULL, 0)) | ||
3693 | goto build_word_op_espace; | ||
3694 | /* Then join them by ALT node. */ | ||
3695 | tree = create_tree (dfa, tree, mbc_tree, OP_ALT); | ||
3696 | if (BE (mbc_tree != NULL, 1)) | ||
3697 | return tree; | ||
3698 | } | ||
3699 | else | ||
3700 | { | ||
3701 | free_charset (mbcset); | ||
3702 | return tree; | ||
3703 | } | ||
3704 | #else /* not RE_ENABLE_I18N */ | ||
3705 | return tree; | ||
3706 | #endif /* not RE_ENABLE_I18N */ | ||
3707 | |||
3708 | build_word_op_espace: | ||
3709 | re_free (sbcset); | ||
3710 | #ifdef RE_ENABLE_I18N | ||
3711 | free_charset (mbcset); | ||
3712 | #endif /* RE_ENABLE_I18N */ | ||
3713 | *err = REG_ESPACE; | ||
3714 | return NULL; | ||
3715 | } | ||
3716 | |||
3717 | /* This is intended for the expressions like "a{1,3}". | ||
3718 | Fetch a number from `input', and return the number. | ||
3719 | Return -1, if the number field is empty like "{,1}". | ||
3720 | Return -2, if an error has occurred. */ | ||
3721 | |||
3722 | static int | ||
3723 | fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax) | ||
3724 | { | ||
3725 | int num = -1; | ||
3726 | unsigned char c; | ||
3727 | while (1) | ||
3728 | { | ||
3729 | fetch_token (token, input, syntax); | ||
3730 | c = token->opr.c; | ||
3731 | if (BE (token->type == END_OF_RE, 0)) | ||
3732 | return -2; | ||
3733 | if (token->type == OP_CLOSE_DUP_NUM || c == ',') | ||
3734 | break; | ||
3735 | num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2) | ||
3736 | ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0')); | ||
3737 | num = (num > RE_DUP_MAX) ? -2 : num; | ||
3738 | } | ||
3739 | return num; | ||
3740 | } | ||
3741 | |||
3742 | #ifdef RE_ENABLE_I18N | ||
3743 | static void | ||
3744 | free_charset (re_charset_t *cset) | ||
3745 | { | ||
3746 | re_free (cset->mbchars); | ||
3747 | # ifdef _LIBC | ||
3748 | re_free (cset->coll_syms); | ||
3749 | re_free (cset->equiv_classes); | ||
3750 | re_free (cset->range_starts); | ||
3751 | re_free (cset->range_ends); | ||
3752 | # endif | ||
3753 | re_free (cset->char_classes); | ||
3754 | re_free (cset); | ||
3755 | } | ||
3756 | #endif /* RE_ENABLE_I18N */ | ||
3757 | |||
3758 | /* Functions for binary tree operation. */ | ||
3759 | |||
3760 | /* Create a tree node. */ | ||
3761 | |||
3762 | static bin_tree_t * | ||
3763 | create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, | ||
3764 | re_token_type_t type) | ||
3765 | { | ||
3766 | re_token_t t; | ||
3767 | t.type = type; | ||
3768 | return create_token_tree (dfa, left, right, &t); | ||
3769 | } | ||
3770 | |||
3771 | static bin_tree_t * | ||
3772 | create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right, | ||
3773 | const re_token_t *token) | ||
3774 | { | ||
3775 | bin_tree_t *tree; | ||
3776 | if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0)) | ||
3777 | { | ||
3778 | bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1); | ||
3779 | |||
3780 | if (storage == NULL) | ||
3781 | return NULL; | ||
3782 | storage->next = dfa->str_tree_storage; | ||
3783 | dfa->str_tree_storage = storage; | ||
3784 | dfa->str_tree_storage_idx = 0; | ||
3785 | } | ||
3786 | tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++]; | ||
3787 | |||
3788 | tree->parent = NULL; | ||
3789 | tree->left = left; | ||
3790 | tree->right = right; | ||
3791 | tree->token = *token; | ||
3792 | tree->token.duplicated = 0; | ||
3793 | tree->token.opt_subexp = 0; | ||
3794 | tree->first = NULL; | ||
3795 | tree->next = NULL; | ||
3796 | tree->node_idx = -1; | ||
3797 | |||
3798 | if (left != NULL) | ||
3799 | left->parent = tree; | ||
3800 | if (right != NULL) | ||
3801 | right->parent = tree; | ||
3802 | return tree; | ||
3803 | } | ||
3804 | |||
3805 | /* Mark the tree SRC as an optional subexpression. | ||
3806 | To be called from preorder or postorder. */ | ||
3807 | |||
3808 | static reg_errcode_t | ||
3809 | mark_opt_subexp (void *extra, bin_tree_t *node) | ||
3810 | { | ||
3811 | int idx = (int) (intptr_t) extra; | ||
3812 | if (node->token.type == SUBEXP && node->token.opr.idx == idx) | ||
3813 | node->token.opt_subexp = 1; | ||
3814 | |||
3815 | return REG_NOERROR; | ||
3816 | } | ||
3817 | |||
3818 | /* Free the allocated memory inside NODE. */ | ||
3819 | |||
3820 | static void | ||
3821 | free_token (re_token_t *node) | ||
3822 | { | ||
3823 | #ifdef RE_ENABLE_I18N | ||
3824 | if (node->type == COMPLEX_BRACKET && node->duplicated == 0) | ||
3825 | free_charset (node->opr.mbcset); | ||
3826 | else | ||
3827 | #endif /* RE_ENABLE_I18N */ | ||
3828 | if (node->type == SIMPLE_BRACKET && node->duplicated == 0) | ||
3829 | re_free (node->opr.sbcset); | ||
3830 | } | ||
3831 | |||
3832 | /* Worker function for tree walking. Free the allocated memory inside NODE | ||
3833 | and its children. */ | ||
3834 | |||
3835 | static reg_errcode_t | ||
3836 | free_tree (UNUSED_PARAM void *extra, bin_tree_t *node) | ||
3837 | { | ||
3838 | free_token (&node->token); | ||
3839 | return REG_NOERROR; | ||
3840 | } | ||
3841 | |||
3842 | |||
3843 | /* Duplicate the node SRC, and return new node. This is a preorder | ||
3844 | visit similar to the one implemented by the generic visitor, but | ||
3845 | we need more infrastructure to maintain two parallel trees --- so, | ||
3846 | it's easier to duplicate. */ | ||
3847 | |||
3848 | static bin_tree_t * | ||
3849 | duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa) | ||
3850 | { | ||
3851 | const bin_tree_t *node; | ||
3852 | bin_tree_t *dup_root; | ||
3853 | bin_tree_t **p_new = &dup_root, *dup_node = root->parent; | ||
3854 | |||
3855 | for (node = root; ; ) | ||
3856 | { | ||
3857 | /* Create a new tree and link it back to the current parent. */ | ||
3858 | *p_new = create_token_tree (dfa, NULL, NULL, &node->token); | ||
3859 | if (*p_new == NULL) | ||
3860 | return NULL; | ||
3861 | (*p_new)->parent = dup_node; | ||
3862 | (*p_new)->token.duplicated = 1; | ||
3863 | dup_node = *p_new; | ||
3864 | |||
3865 | /* Go to the left node, or up and to the right. */ | ||
3866 | if (node->left) | ||
3867 | { | ||
3868 | node = node->left; | ||
3869 | p_new = &dup_node->left; | ||
3870 | } | ||
3871 | else | ||
3872 | { | ||
3873 | const bin_tree_t *prev = NULL; | ||
3874 | while (node->right == prev || node->right == NULL) | ||
3875 | { | ||
3876 | prev = node; | ||
3877 | node = node->parent; | ||
3878 | dup_node = dup_node->parent; | ||
3879 | if (!node) | ||
3880 | return dup_root; | ||
3881 | } | ||
3882 | node = node->right; | ||
3883 | p_new = &dup_node->right; | ||
3884 | } | ||
3885 | } | ||
3886 | } | ||
diff --git a/win32/regex.c b/win32/regex.c new file mode 100644 index 000000000..e40a2ea01 --- /dev/null +++ b/win32/regex.c | |||
@@ -0,0 +1,90 @@ | |||
1 | /* Extended regular expression matching and search library. | ||
2 | Copyright (C) 2002, 2003, 2005 Free Software Foundation, Inc. | ||
3 | This file is part of the GNU C Library. | ||
4 | Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. | ||
5 | |||
6 | The GNU C Library is free software; you can redistribute it and/or | ||
7 | modify it under the terms of the GNU Lesser General Public | ||
8 | License as published by the Free Software Foundation; either | ||
9 | version 2.1 of the License, or (at your option) any later version. | ||
10 | |||
11 | The GNU C Library is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | Lesser General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Lesser General Public | ||
17 | License along with the GNU C Library; if not, write to the Free | ||
18 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | 02110-1301 USA. */ | ||
20 | |||
21 | #define HAVE_LIBINTL_H 0 | ||
22 | #define ENABLE_NLS 0 | ||
23 | #define HAVE_ALLOCA 0 | ||
24 | #define NO_MBSUPPORT 1 | ||
25 | #define GAWK 1 | ||
26 | |||
27 | /* Make sure no one compiles this code with a C++ compiler. */ | ||
28 | #ifdef __cplusplus | ||
29 | # error "This is C code, use a C compiler" | ||
30 | #endif | ||
31 | |||
32 | #ifdef _LIBC | ||
33 | /* We have to keep the namespace clean. */ | ||
34 | # define regfree(preg) __regfree (preg) | ||
35 | # define regexec(pr, st, nm, pm, ef) __regexec (pr, st, nm, pm, ef) | ||
36 | # define regcomp(preg, pattern, cflags) __regcomp (preg, pattern, cflags) | ||
37 | # define regerror(errcode, preg, errbuf, errbuf_size) \ | ||
38 | __regerror(errcode, preg, errbuf, errbuf_size) | ||
39 | # define re_set_registers(bu, re, nu, st, en) \ | ||
40 | __re_set_registers (bu, re, nu, st, en) | ||
41 | # define re_match_2(bufp, string1, size1, string2, size2, pos, regs, stop) \ | ||
42 | __re_match_2 (bufp, string1, size1, string2, size2, pos, regs, stop) | ||
43 | # define re_match(bufp, string, size, pos, regs) \ | ||
44 | __re_match (bufp, string, size, pos, regs) | ||
45 | # define re_search(bufp, string, size, startpos, range, regs) \ | ||
46 | __re_search (bufp, string, size, startpos, range, regs) | ||
47 | # define re_compile_pattern(pattern, length, bufp) \ | ||
48 | __re_compile_pattern (pattern, length, bufp) | ||
49 | # define re_set_syntax(syntax) __re_set_syntax (syntax) | ||
50 | # define re_search_2(bufp, st1, s1, st2, s2, startpos, range, regs, stop) \ | ||
51 | __re_search_2 (bufp, st1, s1, st2, s2, startpos, range, regs, stop) | ||
52 | # define re_compile_fastmap(bufp) __re_compile_fastmap (bufp) | ||
53 | |||
54 | # include "../locale/localeinfo.h" | ||
55 | #endif | ||
56 | |||
57 | #if defined (_MSC_VER) | ||
58 | #include <stdio.h> /* for size_t */ | ||
59 | #endif | ||
60 | |||
61 | /* On some systems, limits.h sets RE_DUP_MAX to a lower value than | ||
62 | GNU regex allows. Include it before <regex.h>, which correctly | ||
63 | #undefs RE_DUP_MAX and sets it to the right value. */ | ||
64 | #include <limits.h> | ||
65 | #include <stdint.h> | ||
66 | |||
67 | #ifdef GAWK | ||
68 | #undef alloca | ||
69 | #define alloca alloca_is_bad_you_should_never_use_it | ||
70 | #endif | ||
71 | #include <regex.h> | ||
72 | #include "regex_internal.h" | ||
73 | |||
74 | #include "regex_internal.c" | ||
75 | #ifdef GAWK | ||
76 | #define bool int | ||
77 | #define true (1) | ||
78 | #define false (0) | ||
79 | #endif | ||
80 | #include "regcomp.c" | ||
81 | #include "regexec.c" | ||
82 | |||
83 | /* Binary backward compatibility. */ | ||
84 | #ifdef _LIBC | ||
85 | # include <shlib-compat.h> | ||
86 | # if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3) | ||
87 | link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.") | ||
88 | int re_max_failures = 2000; | ||
89 | # endif | ||
90 | #endif | ||
diff --git a/win32/regex.h b/win32/regex.h new file mode 100644 index 000000000..61c968387 --- /dev/null +++ b/win32/regex.h | |||
@@ -0,0 +1,582 @@ | |||
1 | #include <stdio.h> | ||
2 | #include <stddef.h> | ||
3 | |||
4 | /* Definitions for data structures and routines for the regular | ||
5 | expression library. | ||
6 | Copyright (C) 1985,1989-93,1995-98,2000,2001,2002,2003,2005,2006,2008 | ||
7 | Free Software Foundation, Inc. | ||
8 | This file is part of the GNU C Library. | ||
9 | |||
10 | The GNU C Library is free software; you can redistribute it and/or | ||
11 | modify it under the terms of the GNU Lesser General Public | ||
12 | License as published by the Free Software Foundation; either | ||
13 | version 2.1 of the License, or (at your option) any later version. | ||
14 | |||
15 | The GNU C Library is distributed in the hope that it will be useful, | ||
16 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
18 | Lesser General Public License for more details. | ||
19 | |||
20 | You should have received a copy of the GNU Lesser General Public | ||
21 | License along with the GNU C Library; if not, write to the Free | ||
22 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
23 | 02110-1301 USA. */ | ||
24 | |||
25 | #ifndef _REGEX_H | ||
26 | #define _REGEX_H 1 | ||
27 | |||
28 | #ifdef HAVE_STDDEF_H | ||
29 | #include <stddef.h> | ||
30 | #endif | ||
31 | |||
32 | #ifdef HAVE_SYS_TYPES_H | ||
33 | #include <sys/types.h> | ||
34 | #endif | ||
35 | |||
36 | #ifndef _LIBC | ||
37 | #define __USE_GNU 1 | ||
38 | #endif | ||
39 | |||
40 | /* Allow the use in C++ code. */ | ||
41 | #ifdef __cplusplus | ||
42 | extern "C" { | ||
43 | #endif | ||
44 | |||
45 | /* The following two types have to be signed and unsigned integer type | ||
46 | wide enough to hold a value of a pointer. For most ANSI compilers | ||
47 | ptrdiff_t and size_t should be likely OK. Still size of these two | ||
48 | types is 2 for Microsoft C. Ugh... */ | ||
49 | typedef long int s_reg_t; | ||
50 | typedef unsigned long int active_reg_t; | ||
51 | |||
52 | /* The following bits are used to determine the regexp syntax we | ||
53 | recognize. The set/not-set meanings are chosen so that Emacs syntax | ||
54 | remains the value 0. The bits are given in alphabetical order, and | ||
55 | the definitions shifted by one from the previous bit; thus, when we | ||
56 | add or remove a bit, only one other definition need change. */ | ||
57 | typedef unsigned long int reg_syntax_t; | ||
58 | |||
59 | #ifdef __USE_GNU | ||
60 | /* If this bit is not set, then \ inside a bracket expression is literal. | ||
61 | If set, then such a \ quotes the following character. */ | ||
62 | # define RE_BACKSLASH_ESCAPE_IN_LISTS ((unsigned long int) 1) | ||
63 | |||
64 | /* If this bit is not set, then + and ? are operators, and \+ and \? are | ||
65 | literals. | ||
66 | If set, then \+ and \? are operators and + and ? are literals. */ | ||
67 | # define RE_BK_PLUS_QM (RE_BACKSLASH_ESCAPE_IN_LISTS << 1) | ||
68 | |||
69 | /* If this bit is set, then character classes are supported. They are: | ||
70 | [:alpha:], [:upper:], [:lower:], [:digit:], [:alnum:], [:xdigit:], | ||
71 | [:space:], [:print:], [:punct:], [:graph:], and [:cntrl:]. | ||
72 | If not set, then character classes are not supported. */ | ||
73 | # define RE_CHAR_CLASSES (RE_BK_PLUS_QM << 1) | ||
74 | |||
75 | /* If this bit is set, then ^ and $ are always anchors (outside bracket | ||
76 | expressions, of course). | ||
77 | If this bit is not set, then it depends: | ||
78 | ^ is an anchor if it is at the beginning of a regular | ||
79 | expression or after an open-group or an alternation operator; | ||
80 | $ is an anchor if it is at the end of a regular expression, or | ||
81 | before a close-group or an alternation operator. | ||
82 | |||
83 | This bit could be (re)combined with RE_CONTEXT_INDEP_OPS, because | ||
84 | POSIX draft 11.2 says that * etc. in leading positions is undefined. | ||
85 | We already implemented a previous draft which made those constructs | ||
86 | invalid, though, so we haven't changed the code back. */ | ||
87 | # define RE_CONTEXT_INDEP_ANCHORS (RE_CHAR_CLASSES << 1) | ||
88 | |||
89 | /* If this bit is set, then special characters are always special | ||
90 | regardless of where they are in the pattern. | ||
91 | If this bit is not set, then special characters are special only in | ||
92 | some contexts; otherwise they are ordinary. Specifically, | ||
93 | * + ? and intervals are only special when not after the beginning, | ||
94 | open-group, or alternation operator. */ | ||
95 | # define RE_CONTEXT_INDEP_OPS (RE_CONTEXT_INDEP_ANCHORS << 1) | ||
96 | |||
97 | /* If this bit is set, then *, +, ?, and { cannot be first in an re or | ||
98 | immediately after an alternation or begin-group operator. */ | ||
99 | # define RE_CONTEXT_INVALID_OPS (RE_CONTEXT_INDEP_OPS << 1) | ||
100 | |||
101 | /* If this bit is set, then . matches newline. | ||
102 | If not set, then it doesn't. */ | ||
103 | # define RE_DOT_NEWLINE (RE_CONTEXT_INVALID_OPS << 1) | ||
104 | |||
105 | /* If this bit is set, then . doesn't match NUL. | ||
106 | If not set, then it does. */ | ||
107 | # define RE_DOT_NOT_NULL (RE_DOT_NEWLINE << 1) | ||
108 | |||
109 | /* If this bit is set, nonmatching lists [^...] do not match newline. | ||
110 | If not set, they do. */ | ||
111 | # define RE_HAT_LISTS_NOT_NEWLINE (RE_DOT_NOT_NULL << 1) | ||
112 | |||
113 | /* If this bit is set, either \{...\} or {...} defines an | ||
114 | interval, depending on RE_NO_BK_BRACES. | ||
115 | If not set, \{, \}, {, and } are literals. */ | ||
116 | # define RE_INTERVALS (RE_HAT_LISTS_NOT_NEWLINE << 1) | ||
117 | |||
118 | /* If this bit is set, +, ? and | aren't recognized as operators. | ||
119 | If not set, they are. */ | ||
120 | # define RE_LIMITED_OPS (RE_INTERVALS << 1) | ||
121 | |||
122 | /* If this bit is set, newline is an alternation operator. | ||
123 | If not set, newline is literal. */ | ||
124 | # define RE_NEWLINE_ALT (RE_LIMITED_OPS << 1) | ||
125 | |||
126 | /* If this bit is set, then `{...}' defines an interval, and \{ and \} | ||
127 | are literals. | ||
128 | If not set, then `\{...\}' defines an interval. */ | ||
129 | # define RE_NO_BK_BRACES (RE_NEWLINE_ALT << 1) | ||
130 | |||
131 | /* If this bit is set, (...) defines a group, and \( and \) are literals. | ||
132 | If not set, \(...\) defines a group, and ( and ) are literals. */ | ||
133 | # define RE_NO_BK_PARENS (RE_NO_BK_BRACES << 1) | ||
134 | |||
135 | /* If this bit is set, then \<digit> matches <digit>. | ||
136 | If not set, then \<digit> is a back-reference. */ | ||
137 | # define RE_NO_BK_REFS (RE_NO_BK_PARENS << 1) | ||
138 | |||
139 | /* If this bit is set, then | is an alternation operator, and \| is literal. | ||
140 | If not set, then \| is an alternation operator, and | is literal. */ | ||
141 | # define RE_NO_BK_VBAR (RE_NO_BK_REFS << 1) | ||
142 | |||
143 | /* If this bit is set, then an ending range point collating higher | ||
144 | than the starting range point, as in [z-a], is invalid. | ||
145 | If not set, then when ending range point collates higher than the | ||
146 | starting range point, the range is ignored. */ | ||
147 | # define RE_NO_EMPTY_RANGES (RE_NO_BK_VBAR << 1) | ||
148 | |||
149 | /* If this bit is set, then an unmatched ) is ordinary. | ||
150 | If not set, then an unmatched ) is invalid. */ | ||
151 | # define RE_UNMATCHED_RIGHT_PAREN_ORD (RE_NO_EMPTY_RANGES << 1) | ||
152 | |||
153 | /* If this bit is set, succeed as soon as we match the whole pattern, | ||
154 | without further backtracking. */ | ||
155 | # define RE_NO_POSIX_BACKTRACKING (RE_UNMATCHED_RIGHT_PAREN_ORD << 1) | ||
156 | |||
157 | /* If this bit is set, do not process the GNU regex operators. | ||
158 | If not set, then the GNU regex operators are recognized. */ | ||
159 | # define RE_NO_GNU_OPS (RE_NO_POSIX_BACKTRACKING << 1) | ||
160 | |||
161 | /* If this bit is set, a syntactically invalid interval is treated as | ||
162 | a string of ordinary characters. For example, the ERE 'a{1' is | ||
163 | treated as 'a\{1'. */ | ||
164 | # define RE_INVALID_INTERVAL_ORD (RE_NO_GNU_OPS << 1) | ||
165 | |||
166 | /* If this bit is set, then ignore case when matching. | ||
167 | If not set, then case is significant. */ | ||
168 | # define RE_ICASE (RE_INVALID_INTERVAL_ORD << 1) | ||
169 | |||
170 | /* This bit is used internally like RE_CONTEXT_INDEP_ANCHORS but only | ||
171 | for ^, because it is difficult to scan the regex backwards to find | ||
172 | whether ^ should be special. */ | ||
173 | # define RE_CARET_ANCHORS_HERE (RE_ICASE << 1) | ||
174 | |||
175 | /* If this bit is set, then \{ cannot be first in an bre or | ||
176 | immediately after an alternation or begin-group operator. */ | ||
177 | # define RE_CONTEXT_INVALID_DUP (RE_CARET_ANCHORS_HERE << 1) | ||
178 | |||
179 | /* If this bit is set, then no_sub will be set to 1 during | ||
180 | re_compile_pattern. */ | ||
181 | #define RE_NO_SUB (RE_CONTEXT_INVALID_DUP << 1) | ||
182 | #endif | ||
183 | |||
184 | /* This global variable defines the particular regexp syntax to use (for | ||
185 | some interfaces). When a regexp is compiled, the syntax used is | ||
186 | stored in the pattern buffer, so changing this does not affect | ||
187 | already-compiled regexps. */ | ||
188 | extern reg_syntax_t re_syntax_options; | ||
189 | |||
190 | #ifdef __USE_GNU | ||
191 | /* Define combinations of the above bits for the standard possibilities. | ||
192 | (The [[[ comments delimit what gets put into the Texinfo file, so | ||
193 | don't delete them!) */ | ||
194 | /* [[[begin syntaxes]]] */ | ||
195 | #define RE_SYNTAX_EMACS 0 | ||
196 | |||
197 | #define RE_SYNTAX_AWK \ | ||
198 | (RE_BACKSLASH_ESCAPE_IN_LISTS | RE_DOT_NOT_NULL \ | ||
199 | | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | ||
200 | | RE_NO_BK_VBAR | RE_NO_EMPTY_RANGES \ | ||
201 | | RE_DOT_NEWLINE | RE_CONTEXT_INDEP_ANCHORS \ | ||
202 | | RE_UNMATCHED_RIGHT_PAREN_ORD | RE_NO_GNU_OPS) | ||
203 | |||
204 | #define RE_SYNTAX_GNU_AWK \ | ||
205 | ((RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ | ||
206 | | RE_INVALID_INTERVAL_ORD) \ | ||
207 | & ~(RE_DOT_NOT_NULL | RE_CONTEXT_INDEP_OPS \ | ||
208 | | RE_CONTEXT_INVALID_OPS )) | ||
209 | |||
210 | #define RE_SYNTAX_POSIX_AWK \ | ||
211 | (RE_SYNTAX_POSIX_EXTENDED | RE_BACKSLASH_ESCAPE_IN_LISTS \ | ||
212 | | RE_INTERVALS | RE_NO_GNU_OPS \ | ||
213 | | RE_INVALID_INTERVAL_ORD) | ||
214 | |||
215 | #define RE_SYNTAX_GREP \ | ||
216 | (RE_BK_PLUS_QM | RE_CHAR_CLASSES \ | ||
217 | | RE_HAT_LISTS_NOT_NEWLINE | RE_INTERVALS \ | ||
218 | | RE_NEWLINE_ALT) | ||
219 | |||
220 | #define RE_SYNTAX_EGREP \ | ||
221 | (RE_CHAR_CLASSES | RE_CONTEXT_INDEP_ANCHORS \ | ||
222 | | RE_CONTEXT_INDEP_OPS | RE_HAT_LISTS_NOT_NEWLINE \ | ||
223 | | RE_NEWLINE_ALT | RE_NO_BK_PARENS \ | ||
224 | | RE_NO_BK_VBAR) | ||
225 | |||
226 | #define RE_SYNTAX_POSIX_EGREP \ | ||
227 | (RE_SYNTAX_EGREP | RE_INTERVALS | RE_NO_BK_BRACES \ | ||
228 | | RE_INVALID_INTERVAL_ORD) | ||
229 | |||
230 | /* P1003.2/D11.2, section 4.20.7.1, lines 5078ff. */ | ||
231 | #define RE_SYNTAX_ED RE_SYNTAX_POSIX_BASIC | ||
232 | |||
233 | #define RE_SYNTAX_SED RE_SYNTAX_POSIX_BASIC | ||
234 | |||
235 | /* Syntax bits common to both basic and extended POSIX regex syntax. */ | ||
236 | #define _RE_SYNTAX_POSIX_COMMON \ | ||
237 | (RE_CHAR_CLASSES | RE_DOT_NEWLINE | RE_DOT_NOT_NULL \ | ||
238 | | RE_INTERVALS | RE_NO_EMPTY_RANGES) | ||
239 | |||
240 | #define RE_SYNTAX_POSIX_BASIC \ | ||
241 | (_RE_SYNTAX_POSIX_COMMON | RE_BK_PLUS_QM | RE_CONTEXT_INVALID_DUP) | ||
242 | |||
243 | /* Differs from ..._POSIX_BASIC only in that RE_BK_PLUS_QM becomes | ||
244 | RE_LIMITED_OPS, i.e., \? \+ \| are not recognized. Actually, this | ||
245 | isn't minimal, since other operators, such as \`, aren't disabled. */ | ||
246 | #define RE_SYNTAX_POSIX_MINIMAL_BASIC \ | ||
247 | (_RE_SYNTAX_POSIX_COMMON | RE_LIMITED_OPS) | ||
248 | |||
249 | #define RE_SYNTAX_POSIX_EXTENDED \ | ||
250 | (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | ||
251 | | RE_CONTEXT_INDEP_OPS | RE_NO_BK_BRACES \ | ||
252 | | RE_NO_BK_PARENS | RE_NO_BK_VBAR \ | ||
253 | | RE_CONTEXT_INVALID_OPS | RE_UNMATCHED_RIGHT_PAREN_ORD) | ||
254 | |||
255 | /* Differs from ..._POSIX_EXTENDED in that RE_CONTEXT_INDEP_OPS is | ||
256 | removed and RE_NO_BK_REFS is added. */ | ||
257 | #define RE_SYNTAX_POSIX_MINIMAL_EXTENDED \ | ||
258 | (_RE_SYNTAX_POSIX_COMMON | RE_CONTEXT_INDEP_ANCHORS \ | ||
259 | | RE_CONTEXT_INVALID_OPS | RE_NO_BK_BRACES \ | ||
260 | | RE_NO_BK_PARENS | RE_NO_BK_REFS \ | ||
261 | | RE_NO_BK_VBAR | RE_UNMATCHED_RIGHT_PAREN_ORD) | ||
262 | /* [[[end syntaxes]]] */ | ||
263 | |||
264 | /* Maximum number of duplicates an interval can allow. Some systems | ||
265 | (erroneously) define this in other header files, but we want our | ||
266 | value, so remove any previous define. */ | ||
267 | # ifdef RE_DUP_MAX | ||
268 | # undef RE_DUP_MAX | ||
269 | # endif | ||
270 | /* If sizeof(int) == 2, then ((1 << 15) - 1) overflows. */ | ||
271 | # define RE_DUP_MAX (0x7fff) | ||
272 | #endif | ||
273 | |||
274 | |||
275 | /* POSIX `cflags' bits (i.e., information for `regcomp'). */ | ||
276 | |||
277 | /* If this bit is set, then use extended regular expression syntax. | ||
278 | If not set, then use basic regular expression syntax. */ | ||
279 | #define REG_EXTENDED 1 | ||
280 | |||
281 | /* If this bit is set, then ignore case when matching. | ||
282 | If not set, then case is significant. */ | ||
283 | #define REG_ICASE (REG_EXTENDED << 1) | ||
284 | |||
285 | /* If this bit is set, then anchors do not match at newline | ||
286 | characters in the string. | ||
287 | If not set, then anchors do match at newlines. */ | ||
288 | #define REG_NEWLINE (REG_ICASE << 1) | ||
289 | |||
290 | /* If this bit is set, then report only success or fail in regexec. | ||
291 | If not set, then returns differ between not matching and errors. */ | ||
292 | #define REG_NOSUB (REG_NEWLINE << 1) | ||
293 | |||
294 | |||
295 | /* POSIX `eflags' bits (i.e., information for regexec). */ | ||
296 | |||
297 | /* If this bit is set, then the beginning-of-line operator doesn't match | ||
298 | the beginning of the string (presumably because it's not the | ||
299 | beginning of a line). | ||
300 | If not set, then the beginning-of-line operator does match the | ||
301 | beginning of the string. */ | ||
302 | #define REG_NOTBOL 1 | ||
303 | |||
304 | /* Like REG_NOTBOL, except for the end-of-line. */ | ||
305 | #define REG_NOTEOL (1 << 1) | ||
306 | |||
307 | /* Use PMATCH[0] to delimit the start and end of the search in the | ||
308 | buffer. */ | ||
309 | #define REG_STARTEND (1 << 2) | ||
310 | |||
311 | |||
312 | /* If any error codes are removed, changed, or added, update the | ||
313 | `re_error_msg' table in regex.c. */ | ||
314 | typedef enum | ||
315 | { | ||
316 | #if defined _XOPEN_SOURCE || defined __USE_XOPEN2K | ||
317 | REG_ENOSYS = -1, /* This will never happen for this implementation. */ | ||
318 | #endif | ||
319 | |||
320 | REG_NOERROR = 0, /* Success. */ | ||
321 | REG_NOMATCH, /* Didn't find a match (for regexec). */ | ||
322 | |||
323 | /* POSIX regcomp return error codes. (In the order listed in the | ||
324 | standard.) */ | ||
325 | REG_BADPAT, /* Invalid pattern. */ | ||
326 | REG_ECOLLATE, /* Inalid collating element. */ | ||
327 | REG_ECTYPE, /* Invalid character class name. */ | ||
328 | REG_EESCAPE, /* Trailing backslash. */ | ||
329 | REG_ESUBREG, /* Invalid back reference. */ | ||
330 | REG_EBRACK, /* Unmatched left bracket. */ | ||
331 | REG_EPAREN, /* Parenthesis imbalance. */ | ||
332 | REG_EBRACE, /* Unmatched \{. */ | ||
333 | REG_BADBR, /* Invalid contents of \{\}. */ | ||
334 | REG_ERANGE, /* Invalid range end. */ | ||
335 | REG_ESPACE, /* Ran out of memory. */ | ||
336 | REG_BADRPT, /* No preceding re for repetition op. */ | ||
337 | |||
338 | /* Error codes we've added. */ | ||
339 | REG_EEND, /* Premature end. */ | ||
340 | REG_ESIZE, /* Compiled pattern bigger than 2^16 bytes. */ | ||
341 | REG_ERPAREN /* Unmatched ) or \); not returned from regcomp. */ | ||
342 | } reg_errcode_t; | ||
343 | |||
344 | /* This data structure represents a compiled pattern. Before calling | ||
345 | the pattern compiler, the fields `buffer', `allocated', `fastmap', | ||
346 | `translate', and `no_sub' can be set. After the pattern has been | ||
347 | compiled, the `re_nsub' field is available. All other fields are | ||
348 | private to the regex routines. */ | ||
349 | |||
350 | #ifndef RE_TRANSLATE_TYPE | ||
351 | # define __RE_TRANSLATE_TYPE unsigned char * | ||
352 | # ifdef __USE_GNU | ||
353 | # define RE_TRANSLATE_TYPE __RE_TRANSLATE_TYPE | ||
354 | # endif | ||
355 | #endif | ||
356 | |||
357 | #ifdef __USE_GNU | ||
358 | # define __REPB_PREFIX(name) name | ||
359 | #else | ||
360 | # define __REPB_PREFIX(name) __##name | ||
361 | #endif | ||
362 | |||
363 | struct re_pattern_buffer | ||
364 | { | ||
365 | /* Space that holds the compiled pattern. It is declared as | ||
366 | `unsigned char *' because its elements are sometimes used as | ||
367 | array indexes. */ | ||
368 | unsigned char *__REPB_PREFIX(buffer); | ||
369 | |||
370 | /* Number of bytes to which `buffer' points. */ | ||
371 | unsigned long int __REPB_PREFIX(allocated); | ||
372 | |||
373 | /* Number of bytes actually used in `buffer'. */ | ||
374 | unsigned long int __REPB_PREFIX(used); | ||
375 | |||
376 | /* Syntax setting with which the pattern was compiled. */ | ||
377 | reg_syntax_t __REPB_PREFIX(syntax); | ||
378 | |||
379 | /* Pointer to a fastmap, if any, otherwise zero. re_search uses the | ||
380 | fastmap, if there is one, to skip over impossible starting points | ||
381 | for matches. */ | ||
382 | char *__REPB_PREFIX(fastmap); | ||
383 | |||
384 | /* Either a translate table to apply to all characters before | ||
385 | comparing them, or zero for no translation. The translation is | ||
386 | applied to a pattern when it is compiled and to a string when it | ||
387 | is matched. */ | ||
388 | __RE_TRANSLATE_TYPE __REPB_PREFIX(translate); | ||
389 | |||
390 | /* Number of subexpressions found by the compiler. */ | ||
391 | size_t re_nsub; | ||
392 | |||
393 | /* Zero if this pattern cannot match the empty string, one else. | ||
394 | Well, in truth it's used only in `re_search_2', to see whether or | ||
395 | not we should use the fastmap, so we don't set this absolutely | ||
396 | perfectly; see `re_compile_fastmap' (the `duplicate' case). */ | ||
397 | unsigned __REPB_PREFIX(can_be_null) : 1; | ||
398 | |||
399 | /* If REGS_UNALLOCATED, allocate space in the `regs' structure | ||
400 | for `max (RE_NREGS, re_nsub + 1)' groups. | ||
401 | If REGS_REALLOCATE, reallocate space if necessary. | ||
402 | If REGS_FIXED, use what's there. */ | ||
403 | #ifdef __USE_GNU | ||
404 | # define REGS_UNALLOCATED 0 | ||
405 | # define REGS_REALLOCATE 1 | ||
406 | # define REGS_FIXED 2 | ||
407 | #endif | ||
408 | unsigned __REPB_PREFIX(regs_allocated) : 2; | ||
409 | |||
410 | /* Set to zero when `regex_compile' compiles a pattern; set to one | ||
411 | by `re_compile_fastmap' if it updates the fastmap. */ | ||
412 | unsigned __REPB_PREFIX(fastmap_accurate) : 1; | ||
413 | |||
414 | /* If set, `re_match_2' does not return information about | ||
415 | subexpressions. */ | ||
416 | unsigned __REPB_PREFIX(no_sub) : 1; | ||
417 | |||
418 | /* If set, a beginning-of-line anchor doesn't match at the beginning | ||
419 | of the string. */ | ||
420 | unsigned __REPB_PREFIX(not_bol) : 1; | ||
421 | |||
422 | /* Similarly for an end-of-line anchor. */ | ||
423 | unsigned __REPB_PREFIX(not_eol) : 1; | ||
424 | |||
425 | /* If true, an anchor at a newline matches. */ | ||
426 | unsigned __REPB_PREFIX(newline_anchor) : 1; | ||
427 | }; | ||
428 | |||
429 | typedef struct re_pattern_buffer regex_t; | ||
430 | |||
431 | /* Type for byte offsets within the string. POSIX mandates this. */ | ||
432 | typedef int regoff_t; | ||
433 | |||
434 | |||
435 | #ifdef __USE_GNU | ||
436 | /* This is the structure we store register match data in. See | ||
437 | regex.texinfo for a full description of what registers match. */ | ||
438 | struct re_registers | ||
439 | { | ||
440 | unsigned num_regs; | ||
441 | regoff_t *start; | ||
442 | regoff_t *end; | ||
443 | }; | ||
444 | |||
445 | |||
446 | /* If `regs_allocated' is REGS_UNALLOCATED in the pattern buffer, | ||
447 | `re_match_2' returns information about at least this many registers | ||
448 | the first time a `regs' structure is passed. */ | ||
449 | # ifndef RE_NREGS | ||
450 | # define RE_NREGS 30 | ||
451 | # endif | ||
452 | #endif | ||
453 | |||
454 | |||
455 | /* POSIX specification for registers. Aside from the different names than | ||
456 | `re_registers', POSIX uses an array of structures, instead of a | ||
457 | structure of arrays. */ | ||
458 | typedef struct | ||
459 | { | ||
460 | regoff_t rm_so; /* Byte offset from string's start to substring's start. */ | ||
461 | regoff_t rm_eo; /* Byte offset from string's start to substring's end. */ | ||
462 | } regmatch_t; | ||
463 | |||
464 | /* Declarations for routines. */ | ||
465 | |||
466 | #ifdef __USE_GNU | ||
467 | /* Sets the current default syntax to SYNTAX, and return the old syntax. | ||
468 | You can also simply assign to the `re_syntax_options' variable. */ | ||
469 | extern reg_syntax_t re_set_syntax (reg_syntax_t __syntax); | ||
470 | |||
471 | /* Compile the regular expression PATTERN, with length LENGTH | ||
472 | and syntax given by the global `re_syntax_options', into the buffer | ||
473 | BUFFER. Return NULL if successful, and an error string if not. */ | ||
474 | extern const char *re_compile_pattern (const char *__pattern, size_t __length, | ||
475 | struct re_pattern_buffer *__buffer); | ||
476 | |||
477 | |||
478 | /* Compile a fastmap for the compiled pattern in BUFFER; used to | ||
479 | accelerate searches. Return 0 if successful and -2 if was an | ||
480 | internal error. */ | ||
481 | extern int re_compile_fastmap (struct re_pattern_buffer *__buffer); | ||
482 | |||
483 | |||
484 | /* Search in the string STRING (with length LENGTH) for the pattern | ||
485 | compiled into BUFFER. Start searching at position START, for RANGE | ||
486 | characters. Return the starting position of the match, -1 for no | ||
487 | match, or -2 for an internal error. Also return register | ||
488 | information in REGS (if REGS and BUFFER->no_sub are nonzero). */ | ||
489 | extern int re_search (struct re_pattern_buffer *__buffer, const char *__cstring, | ||
490 | int __length, int __start, int __range, | ||
491 | struct re_registers *__regs); | ||
492 | |||
493 | |||
494 | /* Like `re_search', but search in the concatenation of STRING1 and | ||
495 | STRING2. Also, stop searching at index START + STOP. */ | ||
496 | extern int re_search_2 (struct re_pattern_buffer *__buffer, | ||
497 | const char *__string1, int __length1, | ||
498 | const char *__string2, int __length2, int __start, | ||
499 | int __range, struct re_registers *__regs, int __stop); | ||
500 | |||
501 | |||
502 | /* Like `re_search', but return how many characters in STRING the regexp | ||
503 | in BUFFER matched, starting at position START. */ | ||
504 | extern int re_match (struct re_pattern_buffer *__buffer, const char *__cstring, | ||
505 | int __length, int __start, struct re_registers *__regs); | ||
506 | |||
507 | |||
508 | /* Relates to `re_match' as `re_search_2' relates to `re_search'. */ | ||
509 | extern int re_match_2 (struct re_pattern_buffer *__buffer, | ||
510 | const char *__string1, int __length1, | ||
511 | const char *__string2, int __length2, int __start, | ||
512 | struct re_registers *__regs, int __stop); | ||
513 | |||
514 | |||
515 | /* Set REGS to hold NUM_REGS registers, storing them in STARTS and | ||
516 | ENDS. Subsequent matches using BUFFER and REGS will use this memory | ||
517 | for recording register information. STARTS and ENDS must be | ||
518 | allocated with malloc, and must each be at least `NUM_REGS * sizeof | ||
519 | (regoff_t)' bytes long. | ||
520 | |||
521 | If NUM_REGS == 0, then subsequent matches should allocate their own | ||
522 | register data. | ||
523 | |||
524 | Unless this function is called, the first search or match using | ||
525 | PATTERN_BUFFER will allocate its own register data, without | ||
526 | freeing the old data. */ | ||
527 | extern void re_set_registers (struct re_pattern_buffer *__buffer, | ||
528 | struct re_registers *__regs, | ||
529 | unsigned int __num_regs, | ||
530 | regoff_t *__starts, regoff_t *__ends); | ||
531 | #endif /* Use GNU */ | ||
532 | |||
533 | #if defined _REGEX_RE_COMP || (defined _LIBC && defined __USE_BSD) | ||
534 | # ifndef _CRAY | ||
535 | /* 4.2 bsd compatibility. */ | ||
536 | extern char *re_comp (const char *); | ||
537 | extern int re_exec (const char *); | ||
538 | # endif | ||
539 | #endif | ||
540 | |||
541 | /* GCC 2.95 and later have "__restrict"; C99 compilers have | ||
542 | "restrict", and "configure" may have defined "restrict". */ | ||
543 | #ifndef __restrict | ||
544 | # if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) | ||
545 | # if defined restrict || 199901L <= __STDC_VERSION__ | ||
546 | # define __restrict restrict | ||
547 | # else | ||
548 | # define __restrict | ||
549 | # endif | ||
550 | # endif | ||
551 | #endif | ||
552 | /* gcc 3.1 and up support the [restrict] syntax. */ | ||
553 | #ifndef __restrict_arr | ||
554 | # if (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1)) \ | ||
555 | && !defined __GNUG__ | ||
556 | # define __restrict_arr __restrict | ||
557 | # else | ||
558 | # define __restrict_arr | ||
559 | # endif | ||
560 | #endif | ||
561 | |||
562 | /* POSIX compatibility. */ | ||
563 | extern int regcomp (regex_t *__restrict __preg, | ||
564 | const char *__restrict __pattern, | ||
565 | int __cflags); | ||
566 | |||
567 | extern int regexec (const regex_t *__restrict __preg, | ||
568 | const char *__restrict __cstring, size_t __nmatch, | ||
569 | regmatch_t __pmatch[__restrict_arr], | ||
570 | int __eflags); | ||
571 | |||
572 | extern size_t regerror (int __errcode, const regex_t *__restrict __preg, | ||
573 | char *__restrict __errbuf, size_t __errbuf_size); | ||
574 | |||
575 | extern void regfree (regex_t *__preg); | ||
576 | |||
577 | |||
578 | #ifdef __cplusplus | ||
579 | } | ||
580 | #endif /* C++ */ | ||
581 | |||
582 | #endif /* regex.h */ | ||
diff --git a/win32/regex_internal.c b/win32/regex_internal.c new file mode 100644 index 000000000..c33561743 --- /dev/null +++ b/win32/regex_internal.c | |||
@@ -0,0 +1,1744 @@ | |||
1 | /* Extended regular expression matching and search library. | ||
2 | Copyright (C) 2002-2006, 2010 Free Software Foundation, Inc. | ||
3 | This file is part of the GNU C Library. | ||
4 | Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. | ||
5 | |||
6 | The GNU C Library is free software; you can redistribute it and/or | ||
7 | modify it under the terms of the GNU Lesser General Public | ||
8 | License as published by the Free Software Foundation; either | ||
9 | version 2.1 of the License, or (at your option) any later version. | ||
10 | |||
11 | The GNU C Library is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | Lesser General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Lesser General Public | ||
17 | License along with the GNU C Library; if not, write to the Free | ||
18 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | 02110-1301 USA. */ | ||
20 | |||
21 | static void re_string_construct_common (const char *str, int len, | ||
22 | re_string_t *pstr, | ||
23 | RE_TRANSLATE_TYPE trans, int icase, | ||
24 | const re_dfa_t *dfa) internal_function; | ||
25 | static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa, | ||
26 | const re_node_set *nodes, | ||
27 | unsigned int hash) internal_function; | ||
28 | static re_dfastate_t *create_cd_newstate (const re_dfa_t *dfa, | ||
29 | const re_node_set *nodes, | ||
30 | unsigned int context, | ||
31 | unsigned int hash) internal_function; | ||
32 | |||
33 | #ifdef GAWK | ||
34 | #undef MAX /* safety */ | ||
35 | static int | ||
36 | MAX(size_t a, size_t b) | ||
37 | { | ||
38 | return (a > b ? a : b); | ||
39 | } | ||
40 | #endif | ||
41 | |||
42 | /* Functions for string operation. */ | ||
43 | |||
44 | /* This function allocate the buffers. It is necessary to call | ||
45 | re_string_reconstruct before using the object. */ | ||
46 | |||
47 | static reg_errcode_t | ||
48 | internal_function | ||
49 | re_string_allocate (re_string_t *pstr, const char *str, int len, int init_len, | ||
50 | RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) | ||
51 | { | ||
52 | reg_errcode_t ret; | ||
53 | int init_buf_len; | ||
54 | |||
55 | /* Ensure at least one character fits into the buffers. */ | ||
56 | if (init_len < dfa->mb_cur_max) | ||
57 | init_len = dfa->mb_cur_max; | ||
58 | init_buf_len = (len + 1 < init_len) ? len + 1: init_len; | ||
59 | re_string_construct_common (str, len, pstr, trans, icase, dfa); | ||
60 | |||
61 | ret = re_string_realloc_buffers (pstr, init_buf_len); | ||
62 | if (BE (ret != REG_NOERROR, 0)) | ||
63 | return ret; | ||
64 | |||
65 | pstr->word_char = dfa->word_char; | ||
66 | pstr->word_ops_used = dfa->word_ops_used; | ||
67 | pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; | ||
68 | pstr->valid_len = (pstr->mbs_allocated || dfa->mb_cur_max > 1) ? 0 : len; | ||
69 | pstr->valid_raw_len = pstr->valid_len; | ||
70 | return REG_NOERROR; | ||
71 | } | ||
72 | |||
73 | /* This function allocate the buffers, and initialize them. */ | ||
74 | |||
75 | static reg_errcode_t | ||
76 | internal_function | ||
77 | re_string_construct (re_string_t *pstr, const char *str, int len, | ||
78 | RE_TRANSLATE_TYPE trans, int icase, const re_dfa_t *dfa) | ||
79 | { | ||
80 | reg_errcode_t ret; | ||
81 | memset (pstr, '\0', sizeof (re_string_t)); | ||
82 | re_string_construct_common (str, len, pstr, trans, icase, dfa); | ||
83 | |||
84 | if (len > 0) | ||
85 | { | ||
86 | ret = re_string_realloc_buffers (pstr, len + 1); | ||
87 | if (BE (ret != REG_NOERROR, 0)) | ||
88 | return ret; | ||
89 | } | ||
90 | pstr->mbs = pstr->mbs_allocated ? pstr->mbs : (unsigned char *) str; | ||
91 | |||
92 | if (icase) | ||
93 | { | ||
94 | #ifdef RE_ENABLE_I18N | ||
95 | if (dfa->mb_cur_max > 1) | ||
96 | { | ||
97 | while (1) | ||
98 | { | ||
99 | ret = build_wcs_upper_buffer (pstr); | ||
100 | if (BE (ret != REG_NOERROR, 0)) | ||
101 | return ret; | ||
102 | if (pstr->valid_raw_len >= len) | ||
103 | break; | ||
104 | if (pstr->bufs_len > pstr->valid_len + dfa->mb_cur_max) | ||
105 | break; | ||
106 | ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); | ||
107 | if (BE (ret != REG_NOERROR, 0)) | ||
108 | return ret; | ||
109 | } | ||
110 | } | ||
111 | else | ||
112 | #endif /* RE_ENABLE_I18N */ | ||
113 | build_upper_buffer (pstr); | ||
114 | } | ||
115 | else | ||
116 | { | ||
117 | #ifdef RE_ENABLE_I18N | ||
118 | if (dfa->mb_cur_max > 1) | ||
119 | build_wcs_buffer (pstr); | ||
120 | else | ||
121 | #endif /* RE_ENABLE_I18N */ | ||
122 | { | ||
123 | if (trans != NULL) | ||
124 | re_string_translate_buffer (pstr); | ||
125 | else | ||
126 | { | ||
127 | pstr->valid_len = pstr->bufs_len; | ||
128 | pstr->valid_raw_len = pstr->bufs_len; | ||
129 | } | ||
130 | } | ||
131 | } | ||
132 | |||
133 | return REG_NOERROR; | ||
134 | } | ||
135 | |||
136 | /* Helper functions for re_string_allocate, and re_string_construct. */ | ||
137 | |||
138 | static reg_errcode_t | ||
139 | internal_function | ||
140 | re_string_realloc_buffers (re_string_t *pstr, int new_buf_len) | ||
141 | { | ||
142 | #ifdef RE_ENABLE_I18N | ||
143 | if (pstr->mb_cur_max > 1) | ||
144 | { | ||
145 | wint_t *new_wcs; | ||
146 | |||
147 | /* Avoid overflow in realloc. */ | ||
148 | const size_t max_object_size = MAX (sizeof (wint_t), sizeof (int)); | ||
149 | if (BE (SIZE_MAX / max_object_size < new_buf_len, 0)) | ||
150 | return REG_ESPACE; | ||
151 | |||
152 | new_wcs = re_realloc (pstr->wcs, wint_t, new_buf_len); | ||
153 | if (BE (new_wcs == NULL, 0)) | ||
154 | return REG_ESPACE; | ||
155 | pstr->wcs = new_wcs; | ||
156 | if (pstr->offsets != NULL) | ||
157 | { | ||
158 | int *new_offsets = re_realloc (pstr->offsets, int, new_buf_len); | ||
159 | if (BE (new_offsets == NULL, 0)) | ||
160 | return REG_ESPACE; | ||
161 | pstr->offsets = new_offsets; | ||
162 | } | ||
163 | } | ||
164 | #endif /* RE_ENABLE_I18N */ | ||
165 | if (pstr->mbs_allocated) | ||
166 | { | ||
167 | unsigned char *new_mbs = re_realloc (pstr->mbs, unsigned char, | ||
168 | new_buf_len); | ||
169 | if (BE (new_mbs == NULL, 0)) | ||
170 | return REG_ESPACE; | ||
171 | pstr->mbs = new_mbs; | ||
172 | } | ||
173 | pstr->bufs_len = new_buf_len; | ||
174 | return REG_NOERROR; | ||
175 | } | ||
176 | |||
177 | |||
178 | static void | ||
179 | internal_function | ||
180 | re_string_construct_common (const char *str, int len, re_string_t *pstr, | ||
181 | RE_TRANSLATE_TYPE trans, int icase, | ||
182 | const re_dfa_t *dfa) | ||
183 | { | ||
184 | pstr->raw_mbs = (const unsigned char *) str; | ||
185 | pstr->len = len; | ||
186 | pstr->raw_len = len; | ||
187 | pstr->trans = trans; | ||
188 | pstr->icase = icase ? 1 : 0; | ||
189 | pstr->mbs_allocated = (trans != NULL || icase); | ||
190 | pstr->mb_cur_max = dfa->mb_cur_max; | ||
191 | pstr->is_utf8 = dfa->is_utf8; | ||
192 | pstr->map_notascii = dfa->map_notascii; | ||
193 | pstr->stop = pstr->len; | ||
194 | pstr->raw_stop = pstr->stop; | ||
195 | } | ||
196 | |||
197 | #ifdef RE_ENABLE_I18N | ||
198 | |||
199 | /* Build wide character buffer PSTR->WCS. | ||
200 | If the byte sequence of the string are: | ||
201 | <mb1>(0), <mb1>(1), <mb2>(0), <mb2>(1), <sb3> | ||
202 | Then wide character buffer will be: | ||
203 | <wc1> , WEOF , <wc2> , WEOF , <wc3> | ||
204 | We use WEOF for padding, they indicate that the position isn't | ||
205 | a first byte of a multibyte character. | ||
206 | |||
207 | Note that this function assumes PSTR->VALID_LEN elements are already | ||
208 | built and starts from PSTR->VALID_LEN. */ | ||
209 | |||
210 | static void | ||
211 | internal_function | ||
212 | build_wcs_buffer (re_string_t *pstr) | ||
213 | { | ||
214 | #ifdef _LIBC | ||
215 | unsigned char buf[MB_LEN_MAX]; | ||
216 | assert (MB_LEN_MAX >= pstr->mb_cur_max); | ||
217 | #else | ||
218 | unsigned char buf[64]; | ||
219 | #endif | ||
220 | mbstate_t prev_st; | ||
221 | int byte_idx, end_idx, remain_len; | ||
222 | size_t mbclen; | ||
223 | |||
224 | /* Build the buffers from pstr->valid_len to either pstr->len or | ||
225 | pstr->bufs_len. */ | ||
226 | end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; | ||
227 | for (byte_idx = pstr->valid_len; byte_idx < end_idx;) | ||
228 | { | ||
229 | wchar_t wc; | ||
230 | const char *p; | ||
231 | |||
232 | remain_len = end_idx - byte_idx; | ||
233 | prev_st = pstr->cur_state; | ||
234 | /* Apply the translation if we need. */ | ||
235 | if (BE (pstr->trans != NULL, 0)) | ||
236 | { | ||
237 | int i, ch; | ||
238 | |||
239 | for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) | ||
240 | { | ||
241 | ch = pstr->raw_mbs [pstr->raw_mbs_idx + byte_idx + i]; | ||
242 | buf[i] = pstr->mbs[byte_idx + i] = pstr->trans[ch]; | ||
243 | } | ||
244 | p = (const char *) buf; | ||
245 | } | ||
246 | else | ||
247 | p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx; | ||
248 | mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); | ||
249 | if (BE (mbclen == (size_t) -2, 0)) | ||
250 | { | ||
251 | /* The buffer doesn't have enough space, finish to build. */ | ||
252 | pstr->cur_state = prev_st; | ||
253 | break; | ||
254 | } | ||
255 | else if (BE (mbclen == (size_t) -1 || mbclen == 0, 0)) | ||
256 | { | ||
257 | /* We treat these cases as a singlebyte character. */ | ||
258 | mbclen = 1; | ||
259 | wc = (wchar_t) pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; | ||
260 | if (BE (pstr->trans != NULL, 0)) | ||
261 | wc = pstr->trans[wc]; | ||
262 | pstr->cur_state = prev_st; | ||
263 | } | ||
264 | |||
265 | /* Write wide character and padding. */ | ||
266 | pstr->wcs[byte_idx++] = wc; | ||
267 | /* Write paddings. */ | ||
268 | for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) | ||
269 | pstr->wcs[byte_idx++] = WEOF; | ||
270 | } | ||
271 | pstr->valid_len = byte_idx; | ||
272 | pstr->valid_raw_len = byte_idx; | ||
273 | } | ||
274 | |||
275 | /* Build wide character buffer PSTR->WCS like build_wcs_buffer, | ||
276 | but for REG_ICASE. */ | ||
277 | |||
278 | static reg_errcode_t | ||
279 | internal_function | ||
280 | build_wcs_upper_buffer (re_string_t *pstr) | ||
281 | { | ||
282 | mbstate_t prev_st; | ||
283 | int src_idx, byte_idx, end_idx, remain_len; | ||
284 | size_t mbclen; | ||
285 | #ifdef _LIBC | ||
286 | char buf[MB_LEN_MAX]; | ||
287 | assert (MB_LEN_MAX >= pstr->mb_cur_max); | ||
288 | #else | ||
289 | char buf[64]; | ||
290 | #endif | ||
291 | |||
292 | byte_idx = pstr->valid_len; | ||
293 | end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; | ||
294 | |||
295 | /* The following optimization assumes that ASCII characters can be | ||
296 | mapped to wide characters with a simple cast. */ | ||
297 | if (! pstr->map_notascii && pstr->trans == NULL && !pstr->offsets_needed) | ||
298 | { | ||
299 | while (byte_idx < end_idx) | ||
300 | { | ||
301 | wchar_t wc; | ||
302 | |||
303 | if (isascii (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]) | ||
304 | && mbsinit (&pstr->cur_state)) | ||
305 | { | ||
306 | /* In case of a singlebyte character. */ | ||
307 | pstr->mbs[byte_idx] | ||
308 | = toupper (pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]); | ||
309 | /* The next step uses the assumption that wchar_t is encoded | ||
310 | ASCII-safe: all ASCII values can be converted like this. */ | ||
311 | pstr->wcs[byte_idx] = (wchar_t) pstr->mbs[byte_idx]; | ||
312 | ++byte_idx; | ||
313 | continue; | ||
314 | } | ||
315 | |||
316 | remain_len = end_idx - byte_idx; | ||
317 | prev_st = pstr->cur_state; | ||
318 | mbclen = __mbrtowc (&wc, | ||
319 | ((const char *) pstr->raw_mbs + pstr->raw_mbs_idx | ||
320 | + byte_idx), remain_len, &pstr->cur_state); | ||
321 | if (BE (mbclen + 2 > 2, 1)) | ||
322 | { | ||
323 | wchar_t wcu = wc; | ||
324 | if (iswlower (wc)) | ||
325 | { | ||
326 | size_t mbcdlen; | ||
327 | |||
328 | wcu = towupper (wc); | ||
329 | mbcdlen = wcrtomb (buf, wcu, &prev_st); | ||
330 | if (BE (mbclen == mbcdlen, 1)) | ||
331 | memcpy (pstr->mbs + byte_idx, buf, mbclen); | ||
332 | else | ||
333 | { | ||
334 | src_idx = byte_idx; | ||
335 | goto offsets_needed; | ||
336 | } | ||
337 | } | ||
338 | else | ||
339 | memcpy (pstr->mbs + byte_idx, | ||
340 | pstr->raw_mbs + pstr->raw_mbs_idx + byte_idx, mbclen); | ||
341 | pstr->wcs[byte_idx++] = wcu; | ||
342 | /* Write paddings. */ | ||
343 | for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) | ||
344 | pstr->wcs[byte_idx++] = WEOF; | ||
345 | } | ||
346 | else if (mbclen == (size_t) -1 || mbclen == 0) | ||
347 | { | ||
348 | /* It is an invalid character or '\0'. Just use the byte. */ | ||
349 | int ch = pstr->raw_mbs[pstr->raw_mbs_idx + byte_idx]; | ||
350 | pstr->mbs[byte_idx] = ch; | ||
351 | /* And also cast it to wide char. */ | ||
352 | pstr->wcs[byte_idx++] = (wchar_t) ch; | ||
353 | if (BE (mbclen == (size_t) -1, 0)) | ||
354 | pstr->cur_state = prev_st; | ||
355 | } | ||
356 | else | ||
357 | { | ||
358 | /* The buffer doesn't have enough space, finish to build. */ | ||
359 | pstr->cur_state = prev_st; | ||
360 | break; | ||
361 | } | ||
362 | } | ||
363 | pstr->valid_len = byte_idx; | ||
364 | pstr->valid_raw_len = byte_idx; | ||
365 | return REG_NOERROR; | ||
366 | } | ||
367 | else | ||
368 | for (src_idx = pstr->valid_raw_len; byte_idx < end_idx;) | ||
369 | { | ||
370 | wchar_t wc; | ||
371 | const char *p; | ||
372 | offsets_needed: | ||
373 | remain_len = end_idx - byte_idx; | ||
374 | prev_st = pstr->cur_state; | ||
375 | if (BE (pstr->trans != NULL, 0)) | ||
376 | { | ||
377 | int i, ch; | ||
378 | |||
379 | for (i = 0; i < pstr->mb_cur_max && i < remain_len; ++i) | ||
380 | { | ||
381 | ch = pstr->raw_mbs [pstr->raw_mbs_idx + src_idx + i]; | ||
382 | buf[i] = pstr->trans[ch]; | ||
383 | } | ||
384 | p = (const char *) buf; | ||
385 | } | ||
386 | else | ||
387 | p = (const char *) pstr->raw_mbs + pstr->raw_mbs_idx + src_idx; | ||
388 | mbclen = __mbrtowc (&wc, p, remain_len, &pstr->cur_state); | ||
389 | if (BE (mbclen + 2 > 2, 1)) | ||
390 | { | ||
391 | wchar_t wcu = wc; | ||
392 | if (iswlower (wc)) | ||
393 | { | ||
394 | size_t mbcdlen; | ||
395 | |||
396 | wcu = towupper (wc); | ||
397 | mbcdlen = wcrtomb ((char *) buf, wcu, &prev_st); | ||
398 | if (BE (mbclen == mbcdlen, 1)) | ||
399 | memcpy (pstr->mbs + byte_idx, buf, mbclen); | ||
400 | else if (mbcdlen != (size_t) -1) | ||
401 | { | ||
402 | size_t i; | ||
403 | |||
404 | if (byte_idx + mbcdlen > pstr->bufs_len) | ||
405 | { | ||
406 | pstr->cur_state = prev_st; | ||
407 | break; | ||
408 | } | ||
409 | |||
410 | if (pstr->offsets == NULL) | ||
411 | { | ||
412 | pstr->offsets = re_malloc (int, pstr->bufs_len); | ||
413 | |||
414 | if (pstr->offsets == NULL) | ||
415 | return REG_ESPACE; | ||
416 | } | ||
417 | if (!pstr->offsets_needed) | ||
418 | { | ||
419 | for (i = 0; i < (size_t) byte_idx; ++i) | ||
420 | pstr->offsets[i] = i; | ||
421 | pstr->offsets_needed = 1; | ||
422 | } | ||
423 | |||
424 | memcpy (pstr->mbs + byte_idx, buf, mbcdlen); | ||
425 | pstr->wcs[byte_idx] = wcu; | ||
426 | pstr->offsets[byte_idx] = src_idx; | ||
427 | for (i = 1; i < mbcdlen; ++i) | ||
428 | { | ||
429 | pstr->offsets[byte_idx + i] | ||
430 | = src_idx + (i < mbclen ? i : mbclen - 1); | ||
431 | pstr->wcs[byte_idx + i] = WEOF; | ||
432 | } | ||
433 | pstr->len += mbcdlen - mbclen; | ||
434 | if (pstr->raw_stop > src_idx) | ||
435 | pstr->stop += mbcdlen - mbclen; | ||
436 | end_idx = (pstr->bufs_len > pstr->len) | ||
437 | ? pstr->len : pstr->bufs_len; | ||
438 | byte_idx += mbcdlen; | ||
439 | src_idx += mbclen; | ||
440 | continue; | ||
441 | } | ||
442 | else | ||
443 | memcpy (pstr->mbs + byte_idx, p, mbclen); | ||
444 | } | ||
445 | else | ||
446 | memcpy (pstr->mbs + byte_idx, p, mbclen); | ||
447 | |||
448 | if (BE (pstr->offsets_needed != 0, 0)) | ||
449 | { | ||
450 | size_t i; | ||
451 | for (i = 0; i < mbclen; ++i) | ||
452 | pstr->offsets[byte_idx + i] = src_idx + i; | ||
453 | } | ||
454 | src_idx += mbclen; | ||
455 | |||
456 | pstr->wcs[byte_idx++] = wcu; | ||
457 | /* Write paddings. */ | ||
458 | for (remain_len = byte_idx + mbclen - 1; byte_idx < remain_len ;) | ||
459 | pstr->wcs[byte_idx++] = WEOF; | ||
460 | } | ||
461 | else if (mbclen == (size_t) -1 || mbclen == 0) | ||
462 | { | ||
463 | /* It is an invalid character or '\0'. Just use the byte. */ | ||
464 | int ch = pstr->raw_mbs[pstr->raw_mbs_idx + src_idx]; | ||
465 | |||
466 | if (BE (pstr->trans != NULL, 0)) | ||
467 | ch = pstr->trans [ch]; | ||
468 | pstr->mbs[byte_idx] = ch; | ||
469 | |||
470 | if (BE (pstr->offsets_needed != 0, 0)) | ||
471 | pstr->offsets[byte_idx] = src_idx; | ||
472 | ++src_idx; | ||
473 | |||
474 | /* And also cast it to wide char. */ | ||
475 | pstr->wcs[byte_idx++] = (wchar_t) ch; | ||
476 | if (BE (mbclen == (size_t) -1, 0)) | ||
477 | pstr->cur_state = prev_st; | ||
478 | } | ||
479 | else | ||
480 | { | ||
481 | /* The buffer doesn't have enough space, finish to build. */ | ||
482 | pstr->cur_state = prev_st; | ||
483 | break; | ||
484 | } | ||
485 | } | ||
486 | pstr->valid_len = byte_idx; | ||
487 | pstr->valid_raw_len = src_idx; | ||
488 | return REG_NOERROR; | ||
489 | } | ||
490 | |||
491 | /* Skip characters until the index becomes greater than NEW_RAW_IDX. | ||
492 | Return the index. */ | ||
493 | |||
494 | static int | ||
495 | internal_function | ||
496 | re_string_skip_chars (re_string_t *pstr, int new_raw_idx, wint_t *last_wc) | ||
497 | { | ||
498 | mbstate_t prev_st; | ||
499 | int rawbuf_idx; | ||
500 | size_t mbclen; | ||
501 | wint_t wc = WEOF; | ||
502 | |||
503 | /* Skip the characters which are not necessary to check. */ | ||
504 | for (rawbuf_idx = pstr->raw_mbs_idx + pstr->valid_raw_len; | ||
505 | rawbuf_idx < new_raw_idx;) | ||
506 | { | ||
507 | wchar_t wc2; | ||
508 | int remain_len = pstr->len - rawbuf_idx; | ||
509 | prev_st = pstr->cur_state; | ||
510 | mbclen = __mbrtowc (&wc2, (const char *) pstr->raw_mbs + rawbuf_idx, | ||
511 | remain_len, &pstr->cur_state); | ||
512 | if (BE (mbclen == (size_t) -2 || mbclen == (size_t) -1 || mbclen == 0, 0)) | ||
513 | { | ||
514 | /* We treat these cases as a single byte character. */ | ||
515 | if (mbclen == 0 || remain_len == 0) | ||
516 | wc = L'\0'; | ||
517 | else | ||
518 | wc = *(unsigned char *) (pstr->raw_mbs + rawbuf_idx); | ||
519 | mbclen = 1; | ||
520 | pstr->cur_state = prev_st; | ||
521 | } | ||
522 | else | ||
523 | wc = (wint_t) wc2; | ||
524 | /* Then proceed the next character. */ | ||
525 | rawbuf_idx += mbclen; | ||
526 | } | ||
527 | *last_wc = (wint_t) wc; | ||
528 | return rawbuf_idx; | ||
529 | } | ||
530 | #endif /* RE_ENABLE_I18N */ | ||
531 | |||
532 | /* Build the buffer PSTR->MBS, and apply the translation if we need. | ||
533 | This function is used in case of REG_ICASE. */ | ||
534 | |||
535 | static void | ||
536 | internal_function | ||
537 | build_upper_buffer (re_string_t *pstr) | ||
538 | { | ||
539 | int char_idx, end_idx; | ||
540 | end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; | ||
541 | |||
542 | for (char_idx = pstr->valid_len; char_idx < end_idx; ++char_idx) | ||
543 | { | ||
544 | int ch = pstr->raw_mbs[pstr->raw_mbs_idx + char_idx]; | ||
545 | if (BE (pstr->trans != NULL, 0)) | ||
546 | ch = pstr->trans[ch]; | ||
547 | if (islower (ch)) | ||
548 | pstr->mbs[char_idx] = toupper (ch); | ||
549 | else | ||
550 | pstr->mbs[char_idx] = ch; | ||
551 | } | ||
552 | pstr->valid_len = char_idx; | ||
553 | pstr->valid_raw_len = char_idx; | ||
554 | } | ||
555 | |||
556 | /* Apply TRANS to the buffer in PSTR. */ | ||
557 | |||
558 | static void | ||
559 | internal_function | ||
560 | re_string_translate_buffer (re_string_t *pstr) | ||
561 | { | ||
562 | int buf_idx, end_idx; | ||
563 | end_idx = (pstr->bufs_len > pstr->len) ? pstr->len : pstr->bufs_len; | ||
564 | |||
565 | for (buf_idx = pstr->valid_len; buf_idx < end_idx; ++buf_idx) | ||
566 | { | ||
567 | int ch = pstr->raw_mbs[pstr->raw_mbs_idx + buf_idx]; | ||
568 | pstr->mbs[buf_idx] = pstr->trans[ch]; | ||
569 | } | ||
570 | |||
571 | pstr->valid_len = buf_idx; | ||
572 | pstr->valid_raw_len = buf_idx; | ||
573 | } | ||
574 | |||
575 | /* This function re-construct the buffers. | ||
576 | Concretely, convert to wide character in case of pstr->mb_cur_max > 1, | ||
577 | convert to upper case in case of REG_ICASE, apply translation. */ | ||
578 | |||
579 | static reg_errcode_t | ||
580 | internal_function | ||
581 | re_string_reconstruct (re_string_t *pstr, int idx, int eflags) | ||
582 | { | ||
583 | int offset = idx - pstr->raw_mbs_idx; | ||
584 | if (BE (offset < 0, 0)) | ||
585 | { | ||
586 | /* Reset buffer. */ | ||
587 | #ifdef RE_ENABLE_I18N | ||
588 | if (pstr->mb_cur_max > 1) | ||
589 | memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); | ||
590 | #endif /* RE_ENABLE_I18N */ | ||
591 | pstr->len = pstr->raw_len; | ||
592 | pstr->stop = pstr->raw_stop; | ||
593 | pstr->valid_len = 0; | ||
594 | pstr->raw_mbs_idx = 0; | ||
595 | pstr->valid_raw_len = 0; | ||
596 | pstr->offsets_needed = 0; | ||
597 | pstr->tip_context = ((eflags & REG_NOTBOL) ? CONTEXT_BEGBUF | ||
598 | : CONTEXT_NEWLINE | CONTEXT_BEGBUF); | ||
599 | if (!pstr->mbs_allocated) | ||
600 | pstr->mbs = (unsigned char *) pstr->raw_mbs; | ||
601 | offset = idx; | ||
602 | } | ||
603 | |||
604 | if (BE (offset != 0, 1)) | ||
605 | { | ||
606 | /* Should the already checked characters be kept? */ | ||
607 | if (BE (offset < pstr->valid_raw_len, 1)) | ||
608 | { | ||
609 | /* Yes, move them to the front of the buffer. */ | ||
610 | #ifdef RE_ENABLE_I18N | ||
611 | if (BE (pstr->offsets_needed, 0)) | ||
612 | { | ||
613 | int low = 0, high = pstr->valid_len, mid; | ||
614 | do | ||
615 | { | ||
616 | mid = (high + low) / 2; | ||
617 | if (pstr->offsets[mid] > offset) | ||
618 | high = mid; | ||
619 | else if (pstr->offsets[mid] < offset) | ||
620 | low = mid + 1; | ||
621 | else | ||
622 | break; | ||
623 | } | ||
624 | while (low < high); | ||
625 | if (pstr->offsets[mid] < offset) | ||
626 | ++mid; | ||
627 | pstr->tip_context = re_string_context_at (pstr, mid - 1, | ||
628 | eflags); | ||
629 | /* This can be quite complicated, so handle specially | ||
630 | only the common and easy case where the character with | ||
631 | different length representation of lower and upper | ||
632 | case is present at or after offset. */ | ||
633 | if (pstr->valid_len > offset | ||
634 | && mid == offset && pstr->offsets[mid] == offset) | ||
635 | { | ||
636 | memmove (pstr->wcs, pstr->wcs + offset, | ||
637 | (pstr->valid_len - offset) * sizeof (wint_t)); | ||
638 | memmove (pstr->mbs, pstr->mbs + offset, pstr->valid_len - offset); | ||
639 | pstr->valid_len -= offset; | ||
640 | pstr->valid_raw_len -= offset; | ||
641 | for (low = 0; low < pstr->valid_len; low++) | ||
642 | pstr->offsets[low] = pstr->offsets[low + offset] - offset; | ||
643 | } | ||
644 | else | ||
645 | { | ||
646 | /* Otherwise, just find out how long the partial multibyte | ||
647 | character at offset is and fill it with WEOF/255. */ | ||
648 | pstr->len = pstr->raw_len - idx + offset; | ||
649 | pstr->stop = pstr->raw_stop - idx + offset; | ||
650 | pstr->offsets_needed = 0; | ||
651 | while (mid > 0 && pstr->offsets[mid - 1] == offset) | ||
652 | --mid; | ||
653 | while (mid < pstr->valid_len) | ||
654 | if (pstr->wcs[mid] != WEOF) | ||
655 | break; | ||
656 | else | ||
657 | ++mid; | ||
658 | if (mid == pstr->valid_len) | ||
659 | pstr->valid_len = 0; | ||
660 | else | ||
661 | { | ||
662 | pstr->valid_len = pstr->offsets[mid] - offset; | ||
663 | if (pstr->valid_len) | ||
664 | { | ||
665 | for (low = 0; low < pstr->valid_len; ++low) | ||
666 | pstr->wcs[low] = WEOF; | ||
667 | memset (pstr->mbs, 255, pstr->valid_len); | ||
668 | } | ||
669 | } | ||
670 | pstr->valid_raw_len = pstr->valid_len; | ||
671 | } | ||
672 | } | ||
673 | else | ||
674 | #endif | ||
675 | { | ||
676 | pstr->tip_context = re_string_context_at (pstr, offset - 1, | ||
677 | eflags); | ||
678 | #ifdef RE_ENABLE_I18N | ||
679 | if (pstr->mb_cur_max > 1) | ||
680 | memmove (pstr->wcs, pstr->wcs + offset, | ||
681 | (pstr->valid_len - offset) * sizeof (wint_t)); | ||
682 | #endif /* RE_ENABLE_I18N */ | ||
683 | if (BE (pstr->mbs_allocated, 0)) | ||
684 | memmove (pstr->mbs, pstr->mbs + offset, | ||
685 | pstr->valid_len - offset); | ||
686 | pstr->valid_len -= offset; | ||
687 | pstr->valid_raw_len -= offset; | ||
688 | #ifdef DEBUG | ||
689 | assert (pstr->valid_len > 0); | ||
690 | #endif | ||
691 | } | ||
692 | } | ||
693 | else | ||
694 | { | ||
695 | #ifdef RE_ENABLE_I18N | ||
696 | /* No, skip all characters until IDX. */ | ||
697 | int prev_valid_len = pstr->valid_len; | ||
698 | |||
699 | if (BE (pstr->offsets_needed, 0)) | ||
700 | { | ||
701 | pstr->len = pstr->raw_len - idx + offset; | ||
702 | pstr->stop = pstr->raw_stop - idx + offset; | ||
703 | pstr->offsets_needed = 0; | ||
704 | } | ||
705 | #endif | ||
706 | pstr->valid_len = 0; | ||
707 | #ifdef RE_ENABLE_I18N | ||
708 | if (pstr->mb_cur_max > 1) | ||
709 | { | ||
710 | int wcs_idx; | ||
711 | wint_t wc = WEOF; | ||
712 | |||
713 | if (pstr->is_utf8) | ||
714 | { | ||
715 | const unsigned char *raw, *p, *end; | ||
716 | |||
717 | /* Special case UTF-8. Multi-byte chars start with any | ||
718 | byte other than 0x80 - 0xbf. */ | ||
719 | raw = pstr->raw_mbs + pstr->raw_mbs_idx; | ||
720 | end = raw + (offset - pstr->mb_cur_max); | ||
721 | if (end < pstr->raw_mbs) | ||
722 | end = pstr->raw_mbs; | ||
723 | p = raw + offset - 1; | ||
724 | #ifdef _LIBC | ||
725 | /* We know the wchar_t encoding is UCS4, so for the simple | ||
726 | case, ASCII characters, skip the conversion step. */ | ||
727 | if (isascii (*p) && BE (pstr->trans == NULL, 1)) | ||
728 | { | ||
729 | memset (&pstr->cur_state, '\0', sizeof (mbstate_t)); | ||
730 | /* pstr->valid_len = 0; */ | ||
731 | wc = (wchar_t) *p; | ||
732 | } | ||
733 | else | ||
734 | #endif | ||
735 | for (; p >= end; --p) | ||
736 | if ((*p & 0xc0) != 0x80) | ||
737 | { | ||
738 | mbstate_t cur_state; | ||
739 | wchar_t wc2; | ||
740 | int mlen = raw + pstr->len - p; | ||
741 | unsigned char buf[6]; | ||
742 | size_t mbclen; | ||
743 | |||
744 | if (BE (pstr->trans != NULL, 0)) | ||
745 | { | ||
746 | int i = mlen < 6 ? mlen : 6; | ||
747 | while (--i >= 0) | ||
748 | buf[i] = pstr->trans[p[i]]; | ||
749 | } | ||
750 | /* XXX Don't use mbrtowc, we know which conversion | ||
751 | to use (UTF-8 -> UCS4). */ | ||
752 | memset (&cur_state, 0, sizeof (cur_state)); | ||
753 | mbclen = __mbrtowc (&wc2, (const char *) p, mlen, | ||
754 | &cur_state); | ||
755 | if (raw + offset - p <= mbclen | ||
756 | && mbclen < (size_t) -2) | ||
757 | { | ||
758 | memset (&pstr->cur_state, '\0', | ||
759 | sizeof (mbstate_t)); | ||
760 | pstr->valid_len = mbclen - (raw + offset - p); | ||
761 | wc = wc2; | ||
762 | } | ||
763 | break; | ||
764 | } | ||
765 | } | ||
766 | |||
767 | if (wc == WEOF) | ||
768 | pstr->valid_len = re_string_skip_chars (pstr, idx, &wc) - idx; | ||
769 | if (wc == WEOF) | ||
770 | pstr->tip_context | ||
771 | = re_string_context_at (pstr, prev_valid_len - 1, eflags); | ||
772 | else | ||
773 | pstr->tip_context = ((BE (pstr->word_ops_used != 0, 0) | ||
774 | && IS_WIDE_WORD_CHAR (wc)) | ||
775 | ? CONTEXT_WORD | ||
776 | : ((IS_WIDE_NEWLINE (wc) | ||
777 | && pstr->newline_anchor) | ||
778 | ? CONTEXT_NEWLINE : 0)); | ||
779 | if (BE (pstr->valid_len, 0)) | ||
780 | { | ||
781 | for (wcs_idx = 0; wcs_idx < pstr->valid_len; ++wcs_idx) | ||
782 | pstr->wcs[wcs_idx] = WEOF; | ||
783 | if (pstr->mbs_allocated) | ||
784 | memset (pstr->mbs, 255, pstr->valid_len); | ||
785 | } | ||
786 | pstr->valid_raw_len = pstr->valid_len; | ||
787 | } | ||
788 | else | ||
789 | #endif /* RE_ENABLE_I18N */ | ||
790 | { | ||
791 | int c = pstr->raw_mbs[pstr->raw_mbs_idx + offset - 1]; | ||
792 | pstr->valid_raw_len = 0; | ||
793 | if (pstr->trans) | ||
794 | c = pstr->trans[c]; | ||
795 | pstr->tip_context = (bitset_contain (pstr->word_char, c) | ||
796 | ? CONTEXT_WORD | ||
797 | : ((IS_NEWLINE (c) && pstr->newline_anchor) | ||
798 | ? CONTEXT_NEWLINE : 0)); | ||
799 | } | ||
800 | } | ||
801 | if (!BE (pstr->mbs_allocated, 0)) | ||
802 | pstr->mbs += offset; | ||
803 | } | ||
804 | pstr->raw_mbs_idx = idx; | ||
805 | pstr->len -= offset; | ||
806 | pstr->stop -= offset; | ||
807 | |||
808 | /* Then build the buffers. */ | ||
809 | #ifdef RE_ENABLE_I18N | ||
810 | if (pstr->mb_cur_max > 1) | ||
811 | { | ||
812 | if (pstr->icase) | ||
813 | { | ||
814 | reg_errcode_t ret = build_wcs_upper_buffer (pstr); | ||
815 | if (BE (ret != REG_NOERROR, 0)) | ||
816 | return ret; | ||
817 | } | ||
818 | else | ||
819 | build_wcs_buffer (pstr); | ||
820 | } | ||
821 | else | ||
822 | #endif /* RE_ENABLE_I18N */ | ||
823 | if (BE (pstr->mbs_allocated, 0)) | ||
824 | { | ||
825 | if (pstr->icase) | ||
826 | build_upper_buffer (pstr); | ||
827 | else if (pstr->trans != NULL) | ||
828 | re_string_translate_buffer (pstr); | ||
829 | } | ||
830 | else | ||
831 | pstr->valid_len = pstr->len; | ||
832 | |||
833 | pstr->cur_idx = 0; | ||
834 | return REG_NOERROR; | ||
835 | } | ||
836 | |||
837 | static unsigned char | ||
838 | internal_function __attribute ((pure)) | ||
839 | re_string_peek_byte_case (const re_string_t *pstr, int idx) | ||
840 | { | ||
841 | int ch, off; | ||
842 | |||
843 | /* Handle the common (easiest) cases first. */ | ||
844 | if (BE (!pstr->mbs_allocated, 1)) | ||
845 | return re_string_peek_byte (pstr, idx); | ||
846 | |||
847 | #ifdef RE_ENABLE_I18N | ||
848 | if (pstr->mb_cur_max > 1 | ||
849 | && ! re_string_is_single_byte_char (pstr, pstr->cur_idx + idx)) | ||
850 | return re_string_peek_byte (pstr, idx); | ||
851 | #endif | ||
852 | |||
853 | off = pstr->cur_idx + idx; | ||
854 | #ifdef RE_ENABLE_I18N | ||
855 | if (pstr->offsets_needed) | ||
856 | off = pstr->offsets[off]; | ||
857 | #endif | ||
858 | |||
859 | ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; | ||
860 | |||
861 | #ifdef RE_ENABLE_I18N | ||
862 | /* Ensure that e.g. for tr_TR.UTF-8 BACKSLASH DOTLESS SMALL LETTER I | ||
863 | this function returns CAPITAL LETTER I instead of first byte of | ||
864 | DOTLESS SMALL LETTER I. The latter would confuse the parser, | ||
865 | since peek_byte_case doesn't advance cur_idx in any way. */ | ||
866 | if (pstr->offsets_needed && !isascii (ch)) | ||
867 | return re_string_peek_byte (pstr, idx); | ||
868 | #endif | ||
869 | |||
870 | return ch; | ||
871 | } | ||
872 | |||
873 | static unsigned char | ||
874 | internal_function __attribute ((pure)) | ||
875 | re_string_fetch_byte_case (re_string_t *pstr) | ||
876 | { | ||
877 | if (BE (!pstr->mbs_allocated, 1)) | ||
878 | return re_string_fetch_byte (pstr); | ||
879 | |||
880 | #ifdef RE_ENABLE_I18N | ||
881 | if (pstr->offsets_needed) | ||
882 | { | ||
883 | int off, ch; | ||
884 | |||
885 | /* For tr_TR.UTF-8 [[:islower:]] there is | ||
886 | [[: CAPITAL LETTER I WITH DOT lower:]] in mbs. Skip | ||
887 | in that case the whole multi-byte character and return | ||
888 | the original letter. On the other side, with | ||
889 | [[: DOTLESS SMALL LETTER I return [[:I, as doing | ||
890 | anything else would complicate things too much. */ | ||
891 | |||
892 | if (!re_string_first_byte (pstr, pstr->cur_idx)) | ||
893 | return re_string_fetch_byte (pstr); | ||
894 | |||
895 | off = pstr->offsets[pstr->cur_idx]; | ||
896 | ch = pstr->raw_mbs[pstr->raw_mbs_idx + off]; | ||
897 | |||
898 | if (! isascii (ch)) | ||
899 | return re_string_fetch_byte (pstr); | ||
900 | |||
901 | re_string_skip_bytes (pstr, | ||
902 | re_string_char_size_at (pstr, pstr->cur_idx)); | ||
903 | return ch; | ||
904 | } | ||
905 | #endif | ||
906 | |||
907 | return pstr->raw_mbs[pstr->raw_mbs_idx + pstr->cur_idx++]; | ||
908 | } | ||
909 | |||
910 | static void | ||
911 | internal_function | ||
912 | re_string_destruct (re_string_t *pstr) | ||
913 | { | ||
914 | #ifdef RE_ENABLE_I18N | ||
915 | re_free (pstr->wcs); | ||
916 | re_free (pstr->offsets); | ||
917 | #endif /* RE_ENABLE_I18N */ | ||
918 | if (pstr->mbs_allocated) | ||
919 | re_free (pstr->mbs); | ||
920 | } | ||
921 | |||
922 | /* Return the context at IDX in INPUT. */ | ||
923 | |||
924 | static unsigned int | ||
925 | internal_function | ||
926 | re_string_context_at (const re_string_t *input, int idx, int eflags) | ||
927 | { | ||
928 | int c; | ||
929 | if (BE (idx < 0, 0)) | ||
930 | /* In this case, we use the value stored in input->tip_context, | ||
931 | since we can't know the character in input->mbs[-1] here. */ | ||
932 | return input->tip_context; | ||
933 | if (BE (idx == input->len, 0)) | ||
934 | return ((eflags & REG_NOTEOL) ? CONTEXT_ENDBUF | ||
935 | : CONTEXT_NEWLINE | CONTEXT_ENDBUF); | ||
936 | #ifdef RE_ENABLE_I18N | ||
937 | if (input->mb_cur_max > 1) | ||
938 | { | ||
939 | wint_t wc; | ||
940 | int wc_idx = idx; | ||
941 | while(input->wcs[wc_idx] == WEOF) | ||
942 | { | ||
943 | #ifdef DEBUG | ||
944 | /* It must not happen. */ | ||
945 | assert (wc_idx >= 0); | ||
946 | #endif | ||
947 | --wc_idx; | ||
948 | if (wc_idx < 0) | ||
949 | return input->tip_context; | ||
950 | } | ||
951 | wc = input->wcs[wc_idx]; | ||
952 | if (BE (input->word_ops_used != 0, 0) && IS_WIDE_WORD_CHAR (wc)) | ||
953 | return CONTEXT_WORD; | ||
954 | return (IS_WIDE_NEWLINE (wc) && input->newline_anchor | ||
955 | ? CONTEXT_NEWLINE : 0); | ||
956 | } | ||
957 | else | ||
958 | #endif | ||
959 | { | ||
960 | c = re_string_byte_at (input, idx); | ||
961 | if (bitset_contain (input->word_char, c)) | ||
962 | return CONTEXT_WORD; | ||
963 | return IS_NEWLINE (c) && input->newline_anchor ? CONTEXT_NEWLINE : 0; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | /* Functions for set operation. */ | ||
968 | |||
969 | static reg_errcode_t | ||
970 | internal_function | ||
971 | re_node_set_alloc (re_node_set *set, int size) | ||
972 | { | ||
973 | /* | ||
974 | * ADR: valgrind says size can be 0, which then doesn't | ||
975 | * free the block of size 0. Harumph. This seems | ||
976 | * to work ok, though. | ||
977 | */ | ||
978 | if (size == 0) | ||
979 | { | ||
980 | memset(set, 0, sizeof(*set)); | ||
981 | return REG_NOERROR; | ||
982 | } | ||
983 | set->alloc = size; | ||
984 | set->nelem = 0; | ||
985 | set->elems = re_malloc (int, size); | ||
986 | if (BE (set->elems == NULL, 0)) | ||
987 | return REG_ESPACE; | ||
988 | return REG_NOERROR; | ||
989 | } | ||
990 | |||
991 | static reg_errcode_t | ||
992 | internal_function | ||
993 | re_node_set_init_1 (re_node_set *set, int elem) | ||
994 | { | ||
995 | set->alloc = 1; | ||
996 | set->nelem = 1; | ||
997 | set->elems = re_malloc (int, 1); | ||
998 | if (BE (set->elems == NULL, 0)) | ||
999 | { | ||
1000 | set->alloc = set->nelem = 0; | ||
1001 | return REG_ESPACE; | ||
1002 | } | ||
1003 | set->elems[0] = elem; | ||
1004 | return REG_NOERROR; | ||
1005 | } | ||
1006 | |||
1007 | static reg_errcode_t | ||
1008 | internal_function | ||
1009 | re_node_set_init_2 (re_node_set *set, int elem1, int elem2) | ||
1010 | { | ||
1011 | set->alloc = 2; | ||
1012 | set->elems = re_malloc (int, 2); | ||
1013 | if (BE (set->elems == NULL, 0)) | ||
1014 | return REG_ESPACE; | ||
1015 | if (elem1 == elem2) | ||
1016 | { | ||
1017 | set->nelem = 1; | ||
1018 | set->elems[0] = elem1; | ||
1019 | } | ||
1020 | else | ||
1021 | { | ||
1022 | set->nelem = 2; | ||
1023 | if (elem1 < elem2) | ||
1024 | { | ||
1025 | set->elems[0] = elem1; | ||
1026 | set->elems[1] = elem2; | ||
1027 | } | ||
1028 | else | ||
1029 | { | ||
1030 | set->elems[0] = elem2; | ||
1031 | set->elems[1] = elem1; | ||
1032 | } | ||
1033 | } | ||
1034 | return REG_NOERROR; | ||
1035 | } | ||
1036 | |||
1037 | static reg_errcode_t | ||
1038 | internal_function | ||
1039 | re_node_set_init_copy (re_node_set *dest, const re_node_set *src) | ||
1040 | { | ||
1041 | dest->nelem = src->nelem; | ||
1042 | if (src->nelem > 0) | ||
1043 | { | ||
1044 | dest->alloc = dest->nelem; | ||
1045 | dest->elems = re_malloc (int, dest->alloc); | ||
1046 | if (BE (dest->elems == NULL, 0)) | ||
1047 | { | ||
1048 | dest->alloc = dest->nelem = 0; | ||
1049 | return REG_ESPACE; | ||
1050 | } | ||
1051 | memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); | ||
1052 | } | ||
1053 | else | ||
1054 | re_node_set_init_empty (dest); | ||
1055 | return REG_NOERROR; | ||
1056 | } | ||
1057 | |||
1058 | /* Calculate the intersection of the sets SRC1 and SRC2. And merge it to | ||
1059 | DEST. Return value indicate the error code or REG_NOERROR if succeeded. | ||
1060 | Note: We assume dest->elems is NULL, when dest->alloc is 0. */ | ||
1061 | |||
1062 | static reg_errcode_t | ||
1063 | internal_function | ||
1064 | re_node_set_add_intersect (re_node_set *dest, const re_node_set *src1, | ||
1065 | const re_node_set *src2) | ||
1066 | { | ||
1067 | int i1, i2, is, id, delta, sbase; | ||
1068 | if (src1->nelem == 0 || src2->nelem == 0) | ||
1069 | return REG_NOERROR; | ||
1070 | |||
1071 | /* We need dest->nelem + 2 * elems_in_intersection; this is a | ||
1072 | conservative estimate. */ | ||
1073 | if (src1->nelem + src2->nelem + dest->nelem > dest->alloc) | ||
1074 | { | ||
1075 | int new_alloc = src1->nelem + src2->nelem + dest->alloc; | ||
1076 | int *new_elems = re_realloc (dest->elems, int, new_alloc); | ||
1077 | if (BE (new_elems == NULL, 0)) | ||
1078 | return REG_ESPACE; | ||
1079 | dest->elems = new_elems; | ||
1080 | dest->alloc = new_alloc; | ||
1081 | } | ||
1082 | |||
1083 | /* Find the items in the intersection of SRC1 and SRC2, and copy | ||
1084 | into the top of DEST those that are not already in DEST itself. */ | ||
1085 | sbase = dest->nelem + src1->nelem + src2->nelem; | ||
1086 | i1 = src1->nelem - 1; | ||
1087 | i2 = src2->nelem - 1; | ||
1088 | id = dest->nelem - 1; | ||
1089 | for (;;) | ||
1090 | { | ||
1091 | if (src1->elems[i1] == src2->elems[i2]) | ||
1092 | { | ||
1093 | /* Try to find the item in DEST. Maybe we could binary search? */ | ||
1094 | while (id >= 0 && dest->elems[id] > src1->elems[i1]) | ||
1095 | --id; | ||
1096 | |||
1097 | if (id < 0 || dest->elems[id] != src1->elems[i1]) | ||
1098 | dest->elems[--sbase] = src1->elems[i1]; | ||
1099 | |||
1100 | if (--i1 < 0 || --i2 < 0) | ||
1101 | break; | ||
1102 | } | ||
1103 | |||
1104 | /* Lower the highest of the two items. */ | ||
1105 | else if (src1->elems[i1] < src2->elems[i2]) | ||
1106 | { | ||
1107 | if (--i2 < 0) | ||
1108 | break; | ||
1109 | } | ||
1110 | else | ||
1111 | { | ||
1112 | if (--i1 < 0) | ||
1113 | break; | ||
1114 | } | ||
1115 | } | ||
1116 | |||
1117 | id = dest->nelem - 1; | ||
1118 | is = dest->nelem + src1->nelem + src2->nelem - 1; | ||
1119 | delta = is - sbase + 1; | ||
1120 | |||
1121 | /* Now copy. When DELTA becomes zero, the remaining | ||
1122 | DEST elements are already in place; this is more or | ||
1123 | less the same loop that is in re_node_set_merge. */ | ||
1124 | dest->nelem += delta; | ||
1125 | if (delta > 0 && id >= 0) | ||
1126 | for (;;) | ||
1127 | { | ||
1128 | if (dest->elems[is] > dest->elems[id]) | ||
1129 | { | ||
1130 | /* Copy from the top. */ | ||
1131 | dest->elems[id + delta--] = dest->elems[is--]; | ||
1132 | if (delta == 0) | ||
1133 | break; | ||
1134 | } | ||
1135 | else | ||
1136 | { | ||
1137 | /* Slide from the bottom. */ | ||
1138 | dest->elems[id + delta] = dest->elems[id]; | ||
1139 | if (--id < 0) | ||
1140 | break; | ||
1141 | } | ||
1142 | } | ||
1143 | |||
1144 | /* Copy remaining SRC elements. */ | ||
1145 | memcpy (dest->elems, dest->elems + sbase, delta * sizeof (int)); | ||
1146 | |||
1147 | return REG_NOERROR; | ||
1148 | } | ||
1149 | |||
1150 | /* Calculate the union set of the sets SRC1 and SRC2. And store it to | ||
1151 | DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ | ||
1152 | |||
1153 | static reg_errcode_t | ||
1154 | internal_function | ||
1155 | re_node_set_init_union (re_node_set *dest, const re_node_set *src1, | ||
1156 | const re_node_set *src2) | ||
1157 | { | ||
1158 | int i1, i2, id; | ||
1159 | if (src1 != NULL && src1->nelem > 0 && src2 != NULL && src2->nelem > 0) | ||
1160 | { | ||
1161 | dest->alloc = src1->nelem + src2->nelem; | ||
1162 | dest->elems = re_malloc (int, dest->alloc); | ||
1163 | if (BE (dest->elems == NULL, 0)) | ||
1164 | return REG_ESPACE; | ||
1165 | } | ||
1166 | else | ||
1167 | { | ||
1168 | if (src1 != NULL && src1->nelem > 0) | ||
1169 | return re_node_set_init_copy (dest, src1); | ||
1170 | else if (src2 != NULL && src2->nelem > 0) | ||
1171 | return re_node_set_init_copy (dest, src2); | ||
1172 | else | ||
1173 | re_node_set_init_empty (dest); | ||
1174 | return REG_NOERROR; | ||
1175 | } | ||
1176 | for (i1 = i2 = id = 0 ; i1 < src1->nelem && i2 < src2->nelem ;) | ||
1177 | { | ||
1178 | if (src1->elems[i1] > src2->elems[i2]) | ||
1179 | { | ||
1180 | dest->elems[id++] = src2->elems[i2++]; | ||
1181 | continue; | ||
1182 | } | ||
1183 | if (src1->elems[i1] == src2->elems[i2]) | ||
1184 | ++i2; | ||
1185 | dest->elems[id++] = src1->elems[i1++]; | ||
1186 | } | ||
1187 | if (i1 < src1->nelem) | ||
1188 | { | ||
1189 | memcpy (dest->elems + id, src1->elems + i1, | ||
1190 | (src1->nelem - i1) * sizeof (int)); | ||
1191 | id += src1->nelem - i1; | ||
1192 | } | ||
1193 | else if (i2 < src2->nelem) | ||
1194 | { | ||
1195 | memcpy (dest->elems + id, src2->elems + i2, | ||
1196 | (src2->nelem - i2) * sizeof (int)); | ||
1197 | id += src2->nelem - i2; | ||
1198 | } | ||
1199 | dest->nelem = id; | ||
1200 | return REG_NOERROR; | ||
1201 | } | ||
1202 | |||
1203 | /* Calculate the union set of the sets DEST and SRC. And store it to | ||
1204 | DEST. Return value indicate the error code or REG_NOERROR if succeeded. */ | ||
1205 | |||
1206 | static reg_errcode_t | ||
1207 | internal_function | ||
1208 | re_node_set_merge (re_node_set *dest, const re_node_set *src) | ||
1209 | { | ||
1210 | int is, id, sbase, delta; | ||
1211 | if (src == NULL || src->nelem == 0) | ||
1212 | return REG_NOERROR; | ||
1213 | if (dest->alloc < 2 * src->nelem + dest->nelem) | ||
1214 | { | ||
1215 | int new_alloc = 2 * (src->nelem + dest->alloc); | ||
1216 | int *new_buffer = re_realloc (dest->elems, int, new_alloc); | ||
1217 | if (BE (new_buffer == NULL, 0)) | ||
1218 | return REG_ESPACE; | ||
1219 | dest->elems = new_buffer; | ||
1220 | dest->alloc = new_alloc; | ||
1221 | } | ||
1222 | |||
1223 | if (BE (dest->nelem == 0, 0)) | ||
1224 | { | ||
1225 | dest->nelem = src->nelem; | ||
1226 | memcpy (dest->elems, src->elems, src->nelem * sizeof (int)); | ||
1227 | return REG_NOERROR; | ||
1228 | } | ||
1229 | |||
1230 | /* Copy into the top of DEST the items of SRC that are not | ||
1231 | found in DEST. Maybe we could binary search in DEST? */ | ||
1232 | for (sbase = dest->nelem + 2 * src->nelem, | ||
1233 | is = src->nelem - 1, id = dest->nelem - 1; is >= 0 && id >= 0; ) | ||
1234 | { | ||
1235 | if (dest->elems[id] == src->elems[is]) | ||
1236 | is--, id--; | ||
1237 | else if (dest->elems[id] < src->elems[is]) | ||
1238 | dest->elems[--sbase] = src->elems[is--]; | ||
1239 | else /* if (dest->elems[id] > src->elems[is]) */ | ||
1240 | --id; | ||
1241 | } | ||
1242 | |||
1243 | if (is >= 0) | ||
1244 | { | ||
1245 | /* If DEST is exhausted, the remaining items of SRC must be unique. */ | ||
1246 | sbase -= is + 1; | ||
1247 | memcpy (dest->elems + sbase, src->elems, (is + 1) * sizeof (int)); | ||
1248 | } | ||
1249 | |||
1250 | id = dest->nelem - 1; | ||
1251 | is = dest->nelem + 2 * src->nelem - 1; | ||
1252 | delta = is - sbase + 1; | ||
1253 | if (delta == 0) | ||
1254 | return REG_NOERROR; | ||
1255 | |||
1256 | /* Now copy. When DELTA becomes zero, the remaining | ||
1257 | DEST elements are already in place. */ | ||
1258 | dest->nelem += delta; | ||
1259 | for (;;) | ||
1260 | { | ||
1261 | if (dest->elems[is] > dest->elems[id]) | ||
1262 | { | ||
1263 | /* Copy from the top. */ | ||
1264 | dest->elems[id + delta--] = dest->elems[is--]; | ||
1265 | if (delta == 0) | ||
1266 | break; | ||
1267 | } | ||
1268 | else | ||
1269 | { | ||
1270 | /* Slide from the bottom. */ | ||
1271 | dest->elems[id + delta] = dest->elems[id]; | ||
1272 | if (--id < 0) | ||
1273 | { | ||
1274 | /* Copy remaining SRC elements. */ | ||
1275 | memcpy (dest->elems, dest->elems + sbase, | ||
1276 | delta * sizeof (int)); | ||
1277 | break; | ||
1278 | } | ||
1279 | } | ||
1280 | } | ||
1281 | |||
1282 | return REG_NOERROR; | ||
1283 | } | ||
1284 | |||
1285 | /* Insert the new element ELEM to the re_node_set* SET. | ||
1286 | SET should not already have ELEM. | ||
1287 | return -1 if an error has occurred, return 1 otherwise. */ | ||
1288 | |||
1289 | static int | ||
1290 | internal_function | ||
1291 | re_node_set_insert (re_node_set *set, int elem) | ||
1292 | { | ||
1293 | int idx; | ||
1294 | /* In case the set is empty. */ | ||
1295 | if (set->alloc == 0) | ||
1296 | { | ||
1297 | if (BE (re_node_set_init_1 (set, elem) == REG_NOERROR, 1)) | ||
1298 | return 1; | ||
1299 | else | ||
1300 | return -1; | ||
1301 | } | ||
1302 | |||
1303 | if (BE (set->nelem, 0) == 0) | ||
1304 | { | ||
1305 | /* We already guaranteed above that set->alloc != 0. */ | ||
1306 | set->elems[0] = elem; | ||
1307 | ++set->nelem; | ||
1308 | return 1; | ||
1309 | } | ||
1310 | |||
1311 | /* Realloc if we need. */ | ||
1312 | if (set->alloc == set->nelem) | ||
1313 | { | ||
1314 | int *new_elems; | ||
1315 | set->alloc = set->alloc * 2; | ||
1316 | new_elems = re_realloc (set->elems, int, set->alloc); | ||
1317 | if (BE (new_elems == NULL, 0)) | ||
1318 | return -1; | ||
1319 | set->elems = new_elems; | ||
1320 | } | ||
1321 | |||
1322 | /* Move the elements which follows the new element. Test the | ||
1323 | first element separately to skip a check in the inner loop. */ | ||
1324 | if (elem < set->elems[0]) | ||
1325 | { | ||
1326 | idx = 0; | ||
1327 | for (idx = set->nelem; idx > 0; idx--) | ||
1328 | set->elems[idx] = set->elems[idx - 1]; | ||
1329 | } | ||
1330 | else | ||
1331 | { | ||
1332 | for (idx = set->nelem; set->elems[idx - 1] > elem; idx--) | ||
1333 | set->elems[idx] = set->elems[idx - 1]; | ||
1334 | } | ||
1335 | |||
1336 | /* Insert the new element. */ | ||
1337 | set->elems[idx] = elem; | ||
1338 | ++set->nelem; | ||
1339 | return 1; | ||
1340 | } | ||
1341 | |||
1342 | /* Insert the new element ELEM to the re_node_set* SET. | ||
1343 | SET should not already have any element greater than or equal to ELEM. | ||
1344 | Return -1 if an error has occurred, return 1 otherwise. */ | ||
1345 | |||
1346 | static int | ||
1347 | internal_function | ||
1348 | re_node_set_insert_last (re_node_set *set, int elem) | ||
1349 | { | ||
1350 | /* Realloc if we need. */ | ||
1351 | if (set->alloc == set->nelem) | ||
1352 | { | ||
1353 | int *new_elems; | ||
1354 | set->alloc = (set->alloc + 1) * 2; | ||
1355 | new_elems = re_realloc (set->elems, int, set->alloc); | ||
1356 | if (BE (new_elems == NULL, 0)) | ||
1357 | return -1; | ||
1358 | set->elems = new_elems; | ||
1359 | } | ||
1360 | |||
1361 | /* Insert the new element. */ | ||
1362 | set->elems[set->nelem++] = elem; | ||
1363 | return 1; | ||
1364 | } | ||
1365 | |||
1366 | /* Compare two node sets SET1 and SET2. | ||
1367 | return 1 if SET1 and SET2 are equivalent, return 0 otherwise. */ | ||
1368 | |||
1369 | static int | ||
1370 | internal_function __attribute ((pure)) | ||
1371 | re_node_set_compare (const re_node_set *set1, const re_node_set *set2) | ||
1372 | { | ||
1373 | int i; | ||
1374 | if (set1 == NULL || set2 == NULL || set1->nelem != set2->nelem) | ||
1375 | return 0; | ||
1376 | for (i = set1->nelem ; --i >= 0 ; ) | ||
1377 | if (set1->elems[i] != set2->elems[i]) | ||
1378 | return 0; | ||
1379 | return 1; | ||
1380 | } | ||
1381 | |||
1382 | /* Return (idx + 1) if SET contains the element ELEM, return 0 otherwise. */ | ||
1383 | |||
1384 | static int | ||
1385 | internal_function __attribute ((pure)) | ||
1386 | re_node_set_contains (const re_node_set *set, int elem) | ||
1387 | { | ||
1388 | unsigned int idx, right, mid; | ||
1389 | if (set->nelem <= 0) | ||
1390 | return 0; | ||
1391 | |||
1392 | /* Binary search the element. */ | ||
1393 | idx = 0; | ||
1394 | right = set->nelem - 1; | ||
1395 | while (idx < right) | ||
1396 | { | ||
1397 | mid = (idx + right) / 2; | ||
1398 | if (set->elems[mid] < elem) | ||
1399 | idx = mid + 1; | ||
1400 | else | ||
1401 | right = mid; | ||
1402 | } | ||
1403 | return set->elems[idx] == elem ? idx + 1 : 0; | ||
1404 | } | ||
1405 | |||
1406 | static void | ||
1407 | internal_function | ||
1408 | re_node_set_remove_at (re_node_set *set, int idx) | ||
1409 | { | ||
1410 | if (idx < 0 || idx >= set->nelem) | ||
1411 | return; | ||
1412 | --set->nelem; | ||
1413 | for (; idx < set->nelem; idx++) | ||
1414 | set->elems[idx] = set->elems[idx + 1]; | ||
1415 | } | ||
1416 | |||
1417 | |||
1418 | /* Add the token TOKEN to dfa->nodes, and return the index of the token. | ||
1419 | Or return -1, if an error has occurred. */ | ||
1420 | |||
1421 | static int | ||
1422 | internal_function | ||
1423 | re_dfa_add_node (re_dfa_t *dfa, re_token_t token) | ||
1424 | { | ||
1425 | if (BE (dfa->nodes_len >= dfa->nodes_alloc, 0)) | ||
1426 | { | ||
1427 | size_t new_nodes_alloc = dfa->nodes_alloc * 2; | ||
1428 | int *new_nexts, *new_indices; | ||
1429 | re_node_set *new_edests, *new_eclosures; | ||
1430 | re_token_t *new_nodes; | ||
1431 | |||
1432 | /* Avoid overflows in realloc. */ | ||
1433 | const size_t max_object_size = MAX (sizeof (re_token_t), | ||
1434 | MAX (sizeof (re_node_set), | ||
1435 | sizeof (int))); | ||
1436 | if (BE (SIZE_MAX / max_object_size < new_nodes_alloc, 0)) | ||
1437 | return -1; | ||
1438 | |||
1439 | new_nodes = re_realloc (dfa->nodes, re_token_t, new_nodes_alloc); | ||
1440 | if (BE (new_nodes == NULL, 0)) | ||
1441 | return -1; | ||
1442 | dfa->nodes = new_nodes; | ||
1443 | new_nexts = re_realloc (dfa->nexts, int, new_nodes_alloc); | ||
1444 | new_indices = re_realloc (dfa->org_indices, int, new_nodes_alloc); | ||
1445 | new_edests = re_realloc (dfa->edests, re_node_set, new_nodes_alloc); | ||
1446 | new_eclosures = re_realloc (dfa->eclosures, re_node_set, new_nodes_alloc); | ||
1447 | if (BE (new_nexts == NULL || new_indices == NULL | ||
1448 | || new_edests == NULL || new_eclosures == NULL, 0)) | ||
1449 | return -1; | ||
1450 | dfa->nexts = new_nexts; | ||
1451 | dfa->org_indices = new_indices; | ||
1452 | dfa->edests = new_edests; | ||
1453 | dfa->eclosures = new_eclosures; | ||
1454 | dfa->nodes_alloc = new_nodes_alloc; | ||
1455 | } | ||
1456 | dfa->nodes[dfa->nodes_len] = token; | ||
1457 | dfa->nodes[dfa->nodes_len].constraint = 0; | ||
1458 | #ifdef RE_ENABLE_I18N | ||
1459 | dfa->nodes[dfa->nodes_len].accept_mb = | ||
1460 | (token.type == OP_PERIOD && dfa->mb_cur_max > 1) || token.type == COMPLEX_BRACKET; | ||
1461 | #endif | ||
1462 | dfa->nexts[dfa->nodes_len] = -1; | ||
1463 | re_node_set_init_empty (dfa->edests + dfa->nodes_len); | ||
1464 | re_node_set_init_empty (dfa->eclosures + dfa->nodes_len); | ||
1465 | return dfa->nodes_len++; | ||
1466 | } | ||
1467 | |||
1468 | static inline unsigned int | ||
1469 | internal_function | ||
1470 | calc_state_hash (const re_node_set *nodes, unsigned int context) | ||
1471 | { | ||
1472 | unsigned int hash = nodes->nelem + context; | ||
1473 | int i; | ||
1474 | for (i = 0 ; i < nodes->nelem ; i++) | ||
1475 | hash += nodes->elems[i]; | ||
1476 | return hash; | ||
1477 | } | ||
1478 | |||
1479 | /* Search for the state whose node_set is equivalent to NODES. | ||
1480 | Return the pointer to the state, if we found it in the DFA. | ||
1481 | Otherwise create the new one and return it. In case of an error | ||
1482 | return NULL and set the error code in ERR. | ||
1483 | Note: - We assume NULL as the invalid state, then it is possible that | ||
1484 | return value is NULL and ERR is REG_NOERROR. | ||
1485 | - We never return non-NULL value in case of any errors, it is for | ||
1486 | optimization. */ | ||
1487 | |||
1488 | static re_dfastate_t * | ||
1489 | internal_function | ||
1490 | re_acquire_state (reg_errcode_t *err, const re_dfa_t *dfa, | ||
1491 | const re_node_set *nodes) | ||
1492 | { | ||
1493 | unsigned int hash; | ||
1494 | re_dfastate_t *new_state; | ||
1495 | struct re_state_table_entry *spot; | ||
1496 | int i; | ||
1497 | if (BE (nodes->nelem == 0, 0)) | ||
1498 | { | ||
1499 | *err = REG_NOERROR; | ||
1500 | return NULL; | ||
1501 | } | ||
1502 | hash = calc_state_hash (nodes, 0); | ||
1503 | spot = dfa->state_table + (hash & dfa->state_hash_mask); | ||
1504 | |||
1505 | for (i = 0 ; i < spot->num ; i++) | ||
1506 | { | ||
1507 | re_dfastate_t *state = spot->array[i]; | ||
1508 | if (hash != state->hash) | ||
1509 | continue; | ||
1510 | if (re_node_set_compare (&state->nodes, nodes)) | ||
1511 | return state; | ||
1512 | } | ||
1513 | |||
1514 | /* There are no appropriate state in the dfa, create the new one. */ | ||
1515 | new_state = create_ci_newstate (dfa, nodes, hash); | ||
1516 | if (BE (new_state == NULL, 0)) | ||
1517 | *err = REG_ESPACE; | ||
1518 | |||
1519 | return new_state; | ||
1520 | } | ||
1521 | |||
1522 | /* Search for the state whose node_set is equivalent to NODES and | ||
1523 | whose context is equivalent to CONTEXT. | ||
1524 | Return the pointer to the state, if we found it in the DFA. | ||
1525 | Otherwise create the new one and return it. In case of an error | ||
1526 | return NULL and set the error code in ERR. | ||
1527 | Note: - We assume NULL as the invalid state, then it is possible that | ||
1528 | return value is NULL and ERR is REG_NOERROR. | ||
1529 | - We never return non-NULL value in case of any errors, it is for | ||
1530 | optimization. */ | ||
1531 | |||
1532 | static re_dfastate_t * | ||
1533 | internal_function | ||
1534 | re_acquire_state_context (reg_errcode_t *err, const re_dfa_t *dfa, | ||
1535 | const re_node_set *nodes, unsigned int context) | ||
1536 | { | ||
1537 | unsigned int hash; | ||
1538 | re_dfastate_t *new_state; | ||
1539 | struct re_state_table_entry *spot; | ||
1540 | int i; | ||
1541 | if (nodes->nelem == 0) | ||
1542 | { | ||
1543 | *err = REG_NOERROR; | ||
1544 | return NULL; | ||
1545 | } | ||
1546 | hash = calc_state_hash (nodes, context); | ||
1547 | spot = dfa->state_table + (hash & dfa->state_hash_mask); | ||
1548 | |||
1549 | for (i = 0 ; i < spot->num ; i++) | ||
1550 | { | ||
1551 | re_dfastate_t *state = spot->array[i]; | ||
1552 | if (state->hash == hash | ||
1553 | && state->context == context | ||
1554 | && re_node_set_compare (state->entrance_nodes, nodes)) | ||
1555 | return state; | ||
1556 | } | ||
1557 | /* There are no appropriate state in `dfa', create the new one. */ | ||
1558 | new_state = create_cd_newstate (dfa, nodes, context, hash); | ||
1559 | if (BE (new_state == NULL, 0)) | ||
1560 | *err = REG_ESPACE; | ||
1561 | |||
1562 | return new_state; | ||
1563 | } | ||
1564 | |||
1565 | /* Finish initialization of the new state NEWSTATE, and using its hash value | ||
1566 | HASH put in the appropriate bucket of DFA's state table. Return value | ||
1567 | indicates the error code if failed. */ | ||
1568 | |||
1569 | static reg_errcode_t | ||
1570 | register_state (const re_dfa_t *dfa, re_dfastate_t *newstate, | ||
1571 | unsigned int hash) | ||
1572 | { | ||
1573 | struct re_state_table_entry *spot; | ||
1574 | reg_errcode_t err; | ||
1575 | int i; | ||
1576 | |||
1577 | newstate->hash = hash; | ||
1578 | err = re_node_set_alloc (&newstate->non_eps_nodes, newstate->nodes.nelem); | ||
1579 | if (BE (err != REG_NOERROR, 0)) | ||
1580 | return REG_ESPACE; | ||
1581 | for (i = 0; i < newstate->nodes.nelem; i++) | ||
1582 | { | ||
1583 | int elem = newstate->nodes.elems[i]; | ||
1584 | if (!IS_EPSILON_NODE (dfa->nodes[elem].type)) | ||
1585 | if (re_node_set_insert_last (&newstate->non_eps_nodes, elem) < 0) | ||
1586 | return REG_ESPACE; | ||
1587 | } | ||
1588 | |||
1589 | spot = dfa->state_table + (hash & dfa->state_hash_mask); | ||
1590 | if (BE (spot->alloc <= spot->num, 0)) | ||
1591 | { | ||
1592 | int new_alloc = 2 * spot->num + 2; | ||
1593 | re_dfastate_t **new_array = re_realloc (spot->array, re_dfastate_t *, | ||
1594 | new_alloc); | ||
1595 | if (BE (new_array == NULL, 0)) | ||
1596 | return REG_ESPACE; | ||
1597 | spot->array = new_array; | ||
1598 | spot->alloc = new_alloc; | ||
1599 | } | ||
1600 | spot->array[spot->num++] = newstate; | ||
1601 | return REG_NOERROR; | ||
1602 | } | ||
1603 | |||
1604 | static void | ||
1605 | free_state (re_dfastate_t *state) | ||
1606 | { | ||
1607 | re_node_set_free (&state->non_eps_nodes); | ||
1608 | re_node_set_free (&state->inveclosure); | ||
1609 | if (state->entrance_nodes != &state->nodes) | ||
1610 | { | ||
1611 | re_node_set_free (state->entrance_nodes); | ||
1612 | re_free (state->entrance_nodes); | ||
1613 | } | ||
1614 | re_node_set_free (&state->nodes); | ||
1615 | re_free (state->word_trtable); | ||
1616 | re_free (state->trtable); | ||
1617 | re_free (state); | ||
1618 | } | ||
1619 | |||
1620 | /* Create the new state which is independ of contexts. | ||
1621 | Return the new state if succeeded, otherwise return NULL. */ | ||
1622 | |||
1623 | static re_dfastate_t * | ||
1624 | internal_function | ||
1625 | create_ci_newstate (const re_dfa_t *dfa, const re_node_set *nodes, | ||
1626 | unsigned int hash) | ||
1627 | { | ||
1628 | int i; | ||
1629 | reg_errcode_t err; | ||
1630 | re_dfastate_t *newstate; | ||
1631 | |||
1632 | newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); | ||
1633 | if (BE (newstate == NULL, 0)) | ||
1634 | return NULL; | ||
1635 | err = re_node_set_init_copy (&newstate->nodes, nodes); | ||
1636 | if (BE (err != REG_NOERROR, 0)) | ||
1637 | { | ||
1638 | re_free (newstate); | ||
1639 | return NULL; | ||
1640 | } | ||
1641 | |||
1642 | newstate->entrance_nodes = &newstate->nodes; | ||
1643 | for (i = 0 ; i < nodes->nelem ; i++) | ||
1644 | { | ||
1645 | re_token_t *node = dfa->nodes + nodes->elems[i]; | ||
1646 | re_token_type_t type = node->type; | ||
1647 | if (type == CHARACTER && !node->constraint) | ||
1648 | continue; | ||
1649 | #ifdef RE_ENABLE_I18N | ||
1650 | newstate->accept_mb |= node->accept_mb; | ||
1651 | #endif /* RE_ENABLE_I18N */ | ||
1652 | |||
1653 | /* If the state has the halt node, the state is a halt state. */ | ||
1654 | if (type == END_OF_RE) | ||
1655 | newstate->halt = 1; | ||
1656 | else if (type == OP_BACK_REF) | ||
1657 | newstate->has_backref = 1; | ||
1658 | else if (type == ANCHOR || node->constraint) | ||
1659 | newstate->has_constraint = 1; | ||
1660 | } | ||
1661 | err = register_state (dfa, newstate, hash); | ||
1662 | if (BE (err != REG_NOERROR, 0)) | ||
1663 | { | ||
1664 | free_state (newstate); | ||
1665 | newstate = NULL; | ||
1666 | } | ||
1667 | return newstate; | ||
1668 | } | ||
1669 | |||
1670 | /* Create the new state which is depend on the context CONTEXT. | ||
1671 | Return the new state if succeeded, otherwise return NULL. */ | ||
1672 | |||
1673 | static re_dfastate_t * | ||
1674 | internal_function | ||
1675 | create_cd_newstate (const re_dfa_t *dfa, const re_node_set *nodes, | ||
1676 | unsigned int context, unsigned int hash) | ||
1677 | { | ||
1678 | int i, nctx_nodes = 0; | ||
1679 | reg_errcode_t err; | ||
1680 | re_dfastate_t *newstate; | ||
1681 | |||
1682 | newstate = (re_dfastate_t *) calloc (sizeof (re_dfastate_t), 1); | ||
1683 | if (BE (newstate == NULL, 0)) | ||
1684 | return NULL; | ||
1685 | err = re_node_set_init_copy (&newstate->nodes, nodes); | ||
1686 | if (BE (err != REG_NOERROR, 0)) | ||
1687 | { | ||
1688 | re_free (newstate); | ||
1689 | return NULL; | ||
1690 | } | ||
1691 | |||
1692 | newstate->context = context; | ||
1693 | newstate->entrance_nodes = &newstate->nodes; | ||
1694 | |||
1695 | for (i = 0 ; i < nodes->nelem ; i++) | ||
1696 | { | ||
1697 | re_token_t *node = dfa->nodes + nodes->elems[i]; | ||
1698 | re_token_type_t type = node->type; | ||
1699 | unsigned int constraint = node->constraint; | ||
1700 | |||
1701 | if (type == CHARACTER && !constraint) | ||
1702 | continue; | ||
1703 | #ifdef RE_ENABLE_I18N | ||
1704 | newstate->accept_mb |= node->accept_mb; | ||
1705 | #endif /* RE_ENABLE_I18N */ | ||
1706 | |||
1707 | /* If the state has the halt node, the state is a halt state. */ | ||
1708 | if (type == END_OF_RE) | ||
1709 | newstate->halt = 1; | ||
1710 | else if (type == OP_BACK_REF) | ||
1711 | newstate->has_backref = 1; | ||
1712 | |||
1713 | if (constraint) | ||
1714 | { | ||
1715 | if (newstate->entrance_nodes == &newstate->nodes) | ||
1716 | { | ||
1717 | newstate->entrance_nodes = re_malloc (re_node_set, 1); | ||
1718 | if (BE (newstate->entrance_nodes == NULL, 0)) | ||
1719 | { | ||
1720 | free_state (newstate); | ||
1721 | return NULL; | ||
1722 | } | ||
1723 | if (re_node_set_init_copy (newstate->entrance_nodes, nodes) | ||
1724 | != REG_NOERROR) | ||
1725 | return NULL; | ||
1726 | nctx_nodes = 0; | ||
1727 | newstate->has_constraint = 1; | ||
1728 | } | ||
1729 | |||
1730 | if (NOT_SATISFY_PREV_CONSTRAINT (constraint,context)) | ||
1731 | { | ||
1732 | re_node_set_remove_at (&newstate->nodes, i - nctx_nodes); | ||
1733 | ++nctx_nodes; | ||
1734 | } | ||
1735 | } | ||
1736 | } | ||
1737 | err = register_state (dfa, newstate, hash); | ||
1738 | if (BE (err != REG_NOERROR, 0)) | ||
1739 | { | ||
1740 | free_state (newstate); | ||
1741 | newstate = NULL; | ||
1742 | } | ||
1743 | return newstate; | ||
1744 | } | ||
diff --git a/win32/regex_internal.h b/win32/regex_internal.h new file mode 100644 index 000000000..1495059ab --- /dev/null +++ b/win32/regex_internal.h | |||
@@ -0,0 +1,810 @@ | |||
1 | /* Extended regular expression matching and search library. | ||
2 | Copyright (C) 2002-2005, 2007, 2008, 2010 Free Software Foundation, Inc. | ||
3 | This file is part of the GNU C Library. | ||
4 | Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. | ||
5 | |||
6 | The GNU C Library is free software; you can redistribute it and/or | ||
7 | modify it under the terms of the GNU Lesser General Public | ||
8 | License as published by the Free Software Foundation; either | ||
9 | version 2.1 of the License, or (at your option) any later version. | ||
10 | |||
11 | The GNU C Library is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | Lesser General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Lesser General Public | ||
17 | License along with the GNU C Library; if not, write to the Free | ||
18 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
19 | 02111-1307 USA. */ | ||
20 | |||
21 | #ifndef _REGEX_INTERNAL_H | ||
22 | #define _REGEX_INTERNAL_H 1 | ||
23 | |||
24 | #include <assert.h> | ||
25 | #include <ctype.h> | ||
26 | #include <stdio.h> | ||
27 | #include <stdlib.h> | ||
28 | #include <string.h> | ||
29 | |||
30 | #if defined HAVE_LANGINFO_H || defined HAVE_LANGINFO_CODESET || defined _LIBC | ||
31 | # include <langinfo.h> | ||
32 | #endif | ||
33 | #if defined HAVE_LOCALE_H || defined _LIBC | ||
34 | # include <locale.h> | ||
35 | #endif | ||
36 | #if defined HAVE_WCHAR_H || defined _LIBC | ||
37 | # include <wchar.h> | ||
38 | #endif /* HAVE_WCHAR_H || _LIBC */ | ||
39 | #if defined HAVE_WCTYPE_H || defined _LIBC | ||
40 | # include <wctype.h> | ||
41 | #endif /* HAVE_WCTYPE_H || _LIBC */ | ||
42 | #if defined HAVE_STDBOOL_H || defined _LIBC | ||
43 | # include <stdbool.h> | ||
44 | #endif /* HAVE_STDBOOL_H || _LIBC */ | ||
45 | #if !defined(ZOS_USS) | ||
46 | #if defined HAVE_STDINT_H || defined _LIBC | ||
47 | # include <stdint.h> | ||
48 | #endif /* HAVE_STDINT_H || _LIBC */ | ||
49 | #endif /* !ZOS_USS */ | ||
50 | #if defined _LIBC | ||
51 | # include <bits/libc-lock.h> | ||
52 | #else | ||
53 | # define __libc_lock_define(CLASS,NAME) | ||
54 | # define __libc_lock_init(NAME) do { } while (0) | ||
55 | # define __libc_lock_lock(NAME) do { } while (0) | ||
56 | # define __libc_lock_unlock(NAME) do { } while (0) | ||
57 | #endif | ||
58 | |||
59 | #ifndef GAWK | ||
60 | /* In case that the system doesn't have isblank(). */ | ||
61 | #if !defined _LIBC && !defined HAVE_ISBLANK && !defined isblank | ||
62 | # define isblank(ch) ((ch) == ' ' || (ch) == '\t') | ||
63 | #endif | ||
64 | #else /* GAWK */ | ||
65 | /* | ||
66 | * This is a freaking mess. On glibc systems you have to define | ||
67 | * a magic constant to get isblank() out of <ctype.h>, since it's | ||
68 | * a C99 function. To heck with all that and borrow a page from | ||
69 | * dfa.c's book. | ||
70 | */ | ||
71 | |||
72 | static int | ||
73 | is_blank (int c) | ||
74 | { | ||
75 | return (c == ' ' || c == '\t'); | ||
76 | } | ||
77 | #endif /* GAWK */ | ||
78 | |||
79 | #ifdef _LIBC | ||
80 | # ifndef _RE_DEFINE_LOCALE_FUNCTIONS | ||
81 | # define _RE_DEFINE_LOCALE_FUNCTIONS 1 | ||
82 | # include <locale/localeinfo.h> | ||
83 | # include <locale/elem-hash.h> | ||
84 | # include <locale/coll-lookup.h> | ||
85 | # endif | ||
86 | #endif | ||
87 | |||
88 | /* This is for other GNU distributions with internationalized messages. */ | ||
89 | #if (HAVE_LIBINTL_H && ENABLE_NLS) || defined _LIBC | ||
90 | # include <libintl.h> | ||
91 | # ifdef _LIBC | ||
92 | # undef gettext | ||
93 | # define gettext(msgid) \ | ||
94 | INTUSE(__dcgettext) (_libc_intl_domainname, msgid, LC_MESSAGES) | ||
95 | # endif | ||
96 | #else | ||
97 | # define gettext(msgid) (msgid) | ||
98 | #endif | ||
99 | |||
100 | #ifndef gettext_noop | ||
101 | /* This define is so xgettext can find the internationalizable | ||
102 | strings. */ | ||
103 | # define gettext_noop(String) String | ||
104 | #endif | ||
105 | |||
106 | /* For loser systems without the definition. */ | ||
107 | #ifndef SIZE_MAX | ||
108 | # define SIZE_MAX ((size_t) -1) | ||
109 | #endif | ||
110 | |||
111 | #ifndef NO_MBSUPPORT | ||
112 | #include "mbsupport.h" /* gawk */ | ||
113 | #endif | ||
114 | #ifndef MB_CUR_MAX | ||
115 | #define MB_CUR_MAX 1 | ||
116 | #endif | ||
117 | |||
118 | #if (defined MBS_SUPPORT) || defined _LIBC | ||
119 | # define RE_ENABLE_I18N | ||
120 | #endif | ||
121 | |||
122 | #if __GNUC__ >= 3 | ||
123 | # define BE(expr, val) __builtin_expect (expr, val) | ||
124 | #else | ||
125 | # define BE(expr, val) (expr) | ||
126 | # ifdef inline | ||
127 | # undef inline | ||
128 | # endif | ||
129 | # define inline | ||
130 | #endif | ||
131 | |||
132 | /* Number of single byte character. */ | ||
133 | #define SBC_MAX 256 | ||
134 | |||
135 | #define COLL_ELEM_LEN_MAX 8 | ||
136 | |||
137 | /* The character which represents newline. */ | ||
138 | #define NEWLINE_CHAR '\n' | ||
139 | #define WIDE_NEWLINE_CHAR L'\n' | ||
140 | |||
141 | /* Rename to standard API for using out of glibc. */ | ||
142 | #ifndef _LIBC | ||
143 | # ifdef __wctype | ||
144 | # undef __wctype | ||
145 | # endif | ||
146 | # define __wctype wctype | ||
147 | # ifdef __iswctype | ||
148 | # undef __iswctype | ||
149 | # endif | ||
150 | # define __iswctype iswctype | ||
151 | # define __btowc btowc | ||
152 | # define __mbrtowc mbrtowc | ||
153 | #undef __mempcpy /* GAWK */ | ||
154 | # define __mempcpy mempcpy | ||
155 | # define __wcrtomb wcrtomb | ||
156 | # define __regfree regfree | ||
157 | # define attribute_hidden | ||
158 | #endif /* not _LIBC */ | ||
159 | |||
160 | #ifdef __GNUC__ | ||
161 | # define __attribute(arg) __attribute__ (arg) | ||
162 | #else | ||
163 | # define __attribute(arg) | ||
164 | #endif | ||
165 | |||
166 | extern const char __re_error_msgid[] attribute_hidden; | ||
167 | extern const size_t __re_error_msgid_idx[] attribute_hidden; | ||
168 | |||
169 | /* An integer used to represent a set of bits. It must be unsigned, | ||
170 | and must be at least as wide as unsigned int. */ | ||
171 | typedef unsigned long int bitset_word_t; | ||
172 | /* All bits set in a bitset_word_t. */ | ||
173 | #define BITSET_WORD_MAX ULONG_MAX | ||
174 | /* Number of bits in a bitset_word_t. */ | ||
175 | #define BITSET_WORD_BITS (sizeof (bitset_word_t) * CHAR_BIT) | ||
176 | /* Number of bitset_word_t in a bit_set. */ | ||
177 | #define BITSET_WORDS (SBC_MAX / BITSET_WORD_BITS) | ||
178 | typedef bitset_word_t bitset_t[BITSET_WORDS]; | ||
179 | typedef bitset_word_t *re_bitset_ptr_t; | ||
180 | typedef const bitset_word_t *re_const_bitset_ptr_t; | ||
181 | |||
182 | #define bitset_set(set,i) \ | ||
183 | (set[i / BITSET_WORD_BITS] |= (bitset_word_t) 1 << i % BITSET_WORD_BITS) | ||
184 | #define bitset_clear(set,i) \ | ||
185 | (set[i / BITSET_WORD_BITS] &= ~((bitset_word_t) 1 << i % BITSET_WORD_BITS)) | ||
186 | #define bitset_contain(set,i) \ | ||
187 | (set[i / BITSET_WORD_BITS] & ((bitset_word_t) 1 << i % BITSET_WORD_BITS)) | ||
188 | #define bitset_empty(set) memset (set, '\0', sizeof (bitset_t)) | ||
189 | #define bitset_set_all(set) memset (set, '\xff', sizeof (bitset_t)) | ||
190 | #define bitset_copy(dest,src) memcpy (dest, src, sizeof (bitset_t)) | ||
191 | |||
192 | #define PREV_WORD_CONSTRAINT 0x0001 | ||
193 | #define PREV_NOTWORD_CONSTRAINT 0x0002 | ||
194 | #define NEXT_WORD_CONSTRAINT 0x0004 | ||
195 | #define NEXT_NOTWORD_CONSTRAINT 0x0008 | ||
196 | #define PREV_NEWLINE_CONSTRAINT 0x0010 | ||
197 | #define NEXT_NEWLINE_CONSTRAINT 0x0020 | ||
198 | #define PREV_BEGBUF_CONSTRAINT 0x0040 | ||
199 | #define NEXT_ENDBUF_CONSTRAINT 0x0080 | ||
200 | #define WORD_DELIM_CONSTRAINT 0x0100 | ||
201 | #define NOT_WORD_DELIM_CONSTRAINT 0x0200 | ||
202 | |||
203 | typedef enum | ||
204 | { | ||
205 | INSIDE_WORD = PREV_WORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, | ||
206 | WORD_FIRST = PREV_NOTWORD_CONSTRAINT | NEXT_WORD_CONSTRAINT, | ||
207 | WORD_LAST = PREV_WORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, | ||
208 | INSIDE_NOTWORD = PREV_NOTWORD_CONSTRAINT | NEXT_NOTWORD_CONSTRAINT, | ||
209 | LINE_FIRST = PREV_NEWLINE_CONSTRAINT, | ||
210 | LINE_LAST = NEXT_NEWLINE_CONSTRAINT, | ||
211 | BUF_FIRST = PREV_BEGBUF_CONSTRAINT, | ||
212 | BUF_LAST = NEXT_ENDBUF_CONSTRAINT, | ||
213 | WORD_DELIM = WORD_DELIM_CONSTRAINT, | ||
214 | NOT_WORD_DELIM = NOT_WORD_DELIM_CONSTRAINT | ||
215 | } re_context_type; | ||
216 | |||
217 | typedef struct | ||
218 | { | ||
219 | int alloc; | ||
220 | int nelem; | ||
221 | int *elems; | ||
222 | } re_node_set; | ||
223 | |||
224 | typedef enum | ||
225 | { | ||
226 | NON_TYPE = 0, | ||
227 | |||
228 | /* Node type, These are used by token, node, tree. */ | ||
229 | CHARACTER = 1, | ||
230 | END_OF_RE = 2, | ||
231 | SIMPLE_BRACKET = 3, | ||
232 | OP_BACK_REF = 4, | ||
233 | OP_PERIOD = 5, | ||
234 | #ifdef RE_ENABLE_I18N | ||
235 | COMPLEX_BRACKET = 6, | ||
236 | OP_UTF8_PERIOD = 7, | ||
237 | #endif /* RE_ENABLE_I18N */ | ||
238 | |||
239 | /* We define EPSILON_BIT as a macro so that OP_OPEN_SUBEXP is used | ||
240 | when the debugger shows values of this enum type. */ | ||
241 | #define EPSILON_BIT 8 | ||
242 | OP_OPEN_SUBEXP = EPSILON_BIT | 0, | ||
243 | OP_CLOSE_SUBEXP = EPSILON_BIT | 1, | ||
244 | OP_ALT = EPSILON_BIT | 2, | ||
245 | OP_DUP_ASTERISK = EPSILON_BIT | 3, | ||
246 | ANCHOR = EPSILON_BIT | 4, | ||
247 | |||
248 | /* Tree type, these are used only by tree. */ | ||
249 | CONCAT = 16, | ||
250 | SUBEXP = 17, | ||
251 | |||
252 | /* Token type, these are used only by token. */ | ||
253 | OP_DUP_PLUS = 18, | ||
254 | OP_DUP_QUESTION, | ||
255 | OP_OPEN_BRACKET, | ||
256 | OP_CLOSE_BRACKET, | ||
257 | OP_CHARSET_RANGE, | ||
258 | OP_OPEN_DUP_NUM, | ||
259 | OP_CLOSE_DUP_NUM, | ||
260 | OP_NON_MATCH_LIST, | ||
261 | OP_OPEN_COLL_ELEM, | ||
262 | OP_CLOSE_COLL_ELEM, | ||
263 | OP_OPEN_EQUIV_CLASS, | ||
264 | OP_CLOSE_EQUIV_CLASS, | ||
265 | OP_OPEN_CHAR_CLASS, | ||
266 | OP_CLOSE_CHAR_CLASS, | ||
267 | OP_WORD, | ||
268 | OP_NOTWORD, | ||
269 | OP_SPACE, | ||
270 | OP_NOTSPACE, | ||
271 | BACK_SLASH | ||
272 | |||
273 | } re_token_type_t; | ||
274 | |||
275 | #ifdef RE_ENABLE_I18N | ||
276 | typedef struct | ||
277 | { | ||
278 | /* Multibyte characters. */ | ||
279 | wchar_t *mbchars; | ||
280 | |||
281 | /* Collating symbols. */ | ||
282 | # ifdef _LIBC | ||
283 | int32_t *coll_syms; | ||
284 | # endif | ||
285 | |||
286 | /* Equivalence classes. */ | ||
287 | # ifdef _LIBC | ||
288 | int32_t *equiv_classes; | ||
289 | # endif | ||
290 | |||
291 | /* Range expressions. */ | ||
292 | # ifdef _LIBC | ||
293 | uint32_t *range_starts; | ||
294 | uint32_t *range_ends; | ||
295 | # else /* not _LIBC */ | ||
296 | wchar_t *range_starts; | ||
297 | wchar_t *range_ends; | ||
298 | # endif /* not _LIBC */ | ||
299 | |||
300 | /* Character classes. */ | ||
301 | wctype_t *char_classes; | ||
302 | |||
303 | /* If this character set is the non-matching list. */ | ||
304 | unsigned int non_match : 1; | ||
305 | |||
306 | /* # of multibyte characters. */ | ||
307 | int nmbchars; | ||
308 | |||
309 | /* # of collating symbols. */ | ||
310 | int ncoll_syms; | ||
311 | |||
312 | /* # of equivalence classes. */ | ||
313 | int nequiv_classes; | ||
314 | |||
315 | /* # of range expressions. */ | ||
316 | int nranges; | ||
317 | |||
318 | /* # of character classes. */ | ||
319 | int nchar_classes; | ||
320 | } re_charset_t; | ||
321 | #endif /* RE_ENABLE_I18N */ | ||
322 | |||
323 | typedef struct | ||
324 | { | ||
325 | union | ||
326 | { | ||
327 | unsigned char c; /* for CHARACTER */ | ||
328 | re_bitset_ptr_t sbcset; /* for SIMPLE_BRACKET */ | ||
329 | #ifdef RE_ENABLE_I18N | ||
330 | re_charset_t *mbcset; /* for COMPLEX_BRACKET */ | ||
331 | #endif /* RE_ENABLE_I18N */ | ||
332 | int idx; /* for BACK_REF */ | ||
333 | re_context_type ctx_type; /* for ANCHOR */ | ||
334 | } opr; | ||
335 | #if __GNUC__ >= 2 | ||
336 | re_token_type_t type : 8; | ||
337 | #else | ||
338 | re_token_type_t type; | ||
339 | #endif | ||
340 | unsigned int constraint : 10; /* context constraint */ | ||
341 | unsigned int duplicated : 1; | ||
342 | unsigned int opt_subexp : 1; | ||
343 | #ifdef RE_ENABLE_I18N | ||
344 | unsigned int accept_mb : 1; | ||
345 | /* These 2 bits can be moved into the union if needed (e.g. if running out | ||
346 | of bits; move opr.c to opr.c.c and move the flags to opr.c.flags). */ | ||
347 | unsigned int mb_partial : 1; | ||
348 | #endif | ||
349 | unsigned int word_char : 1; | ||
350 | } re_token_t; | ||
351 | |||
352 | #define IS_EPSILON_NODE(type) ((type) & EPSILON_BIT) | ||
353 | |||
354 | struct re_string_t | ||
355 | { | ||
356 | /* Indicate the raw buffer which is the original string passed as an | ||
357 | argument of regexec(), re_search(), etc.. */ | ||
358 | const unsigned char *raw_mbs; | ||
359 | /* Store the multibyte string. In case of "case insensitive mode" like | ||
360 | REG_ICASE, upper cases of the string are stored, otherwise MBS points | ||
361 | the same address that RAW_MBS points. */ | ||
362 | unsigned char *mbs; | ||
363 | #ifdef RE_ENABLE_I18N | ||
364 | /* Store the wide character string which is corresponding to MBS. */ | ||
365 | wint_t *wcs; | ||
366 | int *offsets; | ||
367 | mbstate_t cur_state; | ||
368 | #endif | ||
369 | /* Index in RAW_MBS. Each character mbs[i] corresponds to | ||
370 | raw_mbs[raw_mbs_idx + i]. */ | ||
371 | int raw_mbs_idx; | ||
372 | /* The length of the valid characters in the buffers. */ | ||
373 | int valid_len; | ||
374 | /* The corresponding number of bytes in raw_mbs array. */ | ||
375 | int valid_raw_len; | ||
376 | /* The length of the buffers MBS and WCS. */ | ||
377 | int bufs_len; | ||
378 | /* The index in MBS, which is updated by re_string_fetch_byte. */ | ||
379 | int cur_idx; | ||
380 | /* length of RAW_MBS array. */ | ||
381 | int raw_len; | ||
382 | /* This is RAW_LEN - RAW_MBS_IDX + VALID_LEN - VALID_RAW_LEN. */ | ||
383 | int len; | ||
384 | /* End of the buffer may be shorter than its length in the cases such | ||
385 | as re_match_2, re_search_2. Then, we use STOP for end of the buffer | ||
386 | instead of LEN. */ | ||
387 | int raw_stop; | ||
388 | /* This is RAW_STOP - RAW_MBS_IDX adjusted through OFFSETS. */ | ||
389 | int stop; | ||
390 | |||
391 | /* The context of mbs[0]. We store the context independently, since | ||
392 | the context of mbs[0] may be different from raw_mbs[0], which is | ||
393 | the beginning of the input string. */ | ||
394 | unsigned int tip_context; | ||
395 | /* The translation passed as a part of an argument of re_compile_pattern. */ | ||
396 | RE_TRANSLATE_TYPE trans; | ||
397 | /* Copy of re_dfa_t's word_char. */ | ||
398 | re_const_bitset_ptr_t word_char; | ||
399 | /* 1 if REG_ICASE. */ | ||
400 | unsigned char icase; | ||
401 | unsigned char is_utf8; | ||
402 | unsigned char map_notascii; | ||
403 | unsigned char mbs_allocated; | ||
404 | unsigned char offsets_needed; | ||
405 | unsigned char newline_anchor; | ||
406 | unsigned char word_ops_used; | ||
407 | int mb_cur_max; | ||
408 | }; | ||
409 | typedef struct re_string_t re_string_t; | ||
410 | |||
411 | |||
412 | struct re_dfa_t; | ||
413 | typedef struct re_dfa_t re_dfa_t; | ||
414 | |||
415 | #ifndef _LIBC | ||
416 | # ifdef __i386__ | ||
417 | # define internal_function __attribute ((regparm (3), stdcall)) | ||
418 | # else | ||
419 | # define internal_function | ||
420 | # endif | ||
421 | #endif | ||
422 | |||
423 | #ifndef NOT_IN_libc | ||
424 | static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr, | ||
425 | int new_buf_len) | ||
426 | internal_function; | ||
427 | # ifdef RE_ENABLE_I18N | ||
428 | static void build_wcs_buffer (re_string_t *pstr) internal_function; | ||
429 | static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr) | ||
430 | internal_function; | ||
431 | # endif /* RE_ENABLE_I18N */ | ||
432 | static void build_upper_buffer (re_string_t *pstr) internal_function; | ||
433 | static void re_string_translate_buffer (re_string_t *pstr) internal_function; | ||
434 | static unsigned int re_string_context_at (const re_string_t *input, int idx, | ||
435 | int eflags) | ||
436 | internal_function __attribute ((pure)); | ||
437 | #endif | ||
438 | #define re_string_peek_byte(pstr, offset) \ | ||
439 | ((pstr)->mbs[(pstr)->cur_idx + offset]) | ||
440 | #define re_string_fetch_byte(pstr) \ | ||
441 | ((pstr)->mbs[(pstr)->cur_idx++]) | ||
442 | #define re_string_first_byte(pstr, idx) \ | ||
443 | ((idx) == (pstr)->valid_len || (pstr)->wcs[idx] != WEOF) | ||
444 | #define re_string_is_single_byte_char(pstr, idx) \ | ||
445 | ((pstr)->wcs[idx] != WEOF && ((pstr)->valid_len == (idx) + 1 \ | ||
446 | || (pstr)->wcs[(idx) + 1] != WEOF)) | ||
447 | #define re_string_eoi(pstr) ((pstr)->stop <= (pstr)->cur_idx) | ||
448 | #define re_string_cur_idx(pstr) ((pstr)->cur_idx) | ||
449 | #define re_string_get_buffer(pstr) ((pstr)->mbs) | ||
450 | #define re_string_length(pstr) ((pstr)->len) | ||
451 | #define re_string_byte_at(pstr,idx) ((pstr)->mbs[idx]) | ||
452 | #define re_string_skip_bytes(pstr,idx) ((pstr)->cur_idx += (idx)) | ||
453 | #define re_string_set_index(pstr,idx) ((pstr)->cur_idx = (idx)) | ||
454 | |||
455 | #ifndef _LIBC | ||
456 | # if HAVE_ALLOCA | ||
457 | # ifdef (_MSC_VER) | ||
458 | # include <malloc.h> | ||
459 | # define __libc_use_alloca(n) 0 | ||
460 | # else | ||
461 | # include <alloca.h> | ||
462 | /* The OS usually guarantees only one guard page at the bottom of the stack, | ||
463 | and a page size can be as small as 4096 bytes. So we cannot safely | ||
464 | allocate anything larger than 4096 bytes. Also care for the possibility | ||
465 | of a few compiler-allocated temporary stack slots. */ | ||
466 | # define __libc_use_alloca(n) ((n) < 4032) | ||
467 | # endif | ||
468 | # else | ||
469 | /* alloca is implemented with malloc, so just use malloc. */ | ||
470 | # define __libc_use_alloca(n) 0 | ||
471 | # endif | ||
472 | #endif | ||
473 | |||
474 | #define re_malloc(t,n) ((t *) malloc ((n) * sizeof (t))) | ||
475 | /* SunOS 4.1.x realloc doesn't accept null pointers: pre-Standard C. Sigh. */ | ||
476 | #define re_realloc(p,t,n) ((p != NULL) ? (t *) realloc (p,(n)*sizeof(t)) : (t *) calloc(n,sizeof(t))) | ||
477 | #define re_free(p) free (p) | ||
478 | |||
479 | struct bin_tree_t | ||
480 | { | ||
481 | struct bin_tree_t *parent; | ||
482 | struct bin_tree_t *left; | ||
483 | struct bin_tree_t *right; | ||
484 | struct bin_tree_t *first; | ||
485 | struct bin_tree_t *next; | ||
486 | |||
487 | re_token_t token; | ||
488 | |||
489 | /* `node_idx' is the index in dfa->nodes, if `type' == 0. | ||
490 | Otherwise `type' indicate the type of this node. */ | ||
491 | int node_idx; | ||
492 | }; | ||
493 | typedef struct bin_tree_t bin_tree_t; | ||
494 | |||
495 | #define BIN_TREE_STORAGE_SIZE \ | ||
496 | ((1024 - sizeof (void *)) / sizeof (bin_tree_t)) | ||
497 | |||
498 | struct bin_tree_storage_t | ||
499 | { | ||
500 | struct bin_tree_storage_t *next; | ||
501 | bin_tree_t data[BIN_TREE_STORAGE_SIZE]; | ||
502 | }; | ||
503 | typedef struct bin_tree_storage_t bin_tree_storage_t; | ||
504 | |||
505 | #define CONTEXT_WORD 1 | ||
506 | #define CONTEXT_NEWLINE (CONTEXT_WORD << 1) | ||
507 | #define CONTEXT_BEGBUF (CONTEXT_NEWLINE << 1) | ||
508 | #define CONTEXT_ENDBUF (CONTEXT_BEGBUF << 1) | ||
509 | |||
510 | #define IS_WORD_CONTEXT(c) ((c) & CONTEXT_WORD) | ||
511 | #define IS_NEWLINE_CONTEXT(c) ((c) & CONTEXT_NEWLINE) | ||
512 | #define IS_BEGBUF_CONTEXT(c) ((c) & CONTEXT_BEGBUF) | ||
513 | #define IS_ENDBUF_CONTEXT(c) ((c) & CONTEXT_ENDBUF) | ||
514 | #define IS_ORDINARY_CONTEXT(c) ((c) == 0) | ||
515 | |||
516 | #define IS_WORD_CHAR(ch) (isalnum (ch) || (ch) == '_') | ||
517 | #define IS_NEWLINE(ch) ((ch) == NEWLINE_CHAR) | ||
518 | #define IS_WIDE_WORD_CHAR(ch) (iswalnum (ch) || (ch) == L'_') | ||
519 | #define IS_WIDE_NEWLINE(ch) ((ch) == WIDE_NEWLINE_CHAR) | ||
520 | |||
521 | #define NOT_SATISFY_PREV_CONSTRAINT(constraint,context) \ | ||
522 | ((((constraint) & PREV_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ | ||
523 | || ((constraint & PREV_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ | ||
524 | || ((constraint & PREV_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context))\ | ||
525 | || ((constraint & PREV_BEGBUF_CONSTRAINT) && !IS_BEGBUF_CONTEXT (context))) | ||
526 | |||
527 | #define NOT_SATISFY_NEXT_CONSTRAINT(constraint,context) \ | ||
528 | ((((constraint) & NEXT_WORD_CONSTRAINT) && !IS_WORD_CONTEXT (context)) \ | ||
529 | || (((constraint) & NEXT_NOTWORD_CONSTRAINT) && IS_WORD_CONTEXT (context)) \ | ||
530 | || (((constraint) & NEXT_NEWLINE_CONSTRAINT) && !IS_NEWLINE_CONTEXT (context)) \ | ||
531 | || (((constraint) & NEXT_ENDBUF_CONSTRAINT) && !IS_ENDBUF_CONTEXT (context))) | ||
532 | |||
533 | struct re_dfastate_t | ||
534 | { | ||
535 | unsigned int hash; | ||
536 | re_node_set nodes; | ||
537 | re_node_set non_eps_nodes; | ||
538 | re_node_set inveclosure; | ||
539 | re_node_set *entrance_nodes; | ||
540 | struct re_dfastate_t **trtable, **word_trtable; | ||
541 | unsigned int context : 4; | ||
542 | unsigned int halt : 1; | ||
543 | /* If this state can accept `multi byte'. | ||
544 | Note that we refer to multibyte characters, and multi character | ||
545 | collating elements as `multi byte'. */ | ||
546 | unsigned int accept_mb : 1; | ||
547 | /* If this state has backreference node(s). */ | ||
548 | unsigned int has_backref : 1; | ||
549 | unsigned int has_constraint : 1; | ||
550 | }; | ||
551 | typedef struct re_dfastate_t re_dfastate_t; | ||
552 | |||
553 | struct re_state_table_entry | ||
554 | { | ||
555 | int num; | ||
556 | int alloc; | ||
557 | re_dfastate_t **array; | ||
558 | }; | ||
559 | |||
560 | /* Array type used in re_sub_match_last_t and re_sub_match_top_t. */ | ||
561 | |||
562 | typedef struct | ||
563 | { | ||
564 | int next_idx; | ||
565 | int alloc; | ||
566 | re_dfastate_t **array; | ||
567 | } state_array_t; | ||
568 | |||
569 | /* Store information about the node NODE whose type is OP_CLOSE_SUBEXP. */ | ||
570 | |||
571 | typedef struct | ||
572 | { | ||
573 | int node; | ||
574 | int str_idx; /* The position NODE match at. */ | ||
575 | state_array_t path; | ||
576 | } re_sub_match_last_t; | ||
577 | |||
578 | /* Store information about the node NODE whose type is OP_OPEN_SUBEXP. | ||
579 | And information about the node, whose type is OP_CLOSE_SUBEXP, | ||
580 | corresponding to NODE is stored in LASTS. */ | ||
581 | |||
582 | typedef struct | ||
583 | { | ||
584 | int str_idx; | ||
585 | int node; | ||
586 | state_array_t *path; | ||
587 | int alasts; /* Allocation size of LASTS. */ | ||
588 | int nlasts; /* The number of LASTS. */ | ||
589 | re_sub_match_last_t **lasts; | ||
590 | } re_sub_match_top_t; | ||
591 | |||
592 | struct re_backref_cache_entry | ||
593 | { | ||
594 | int node; | ||
595 | int str_idx; | ||
596 | int subexp_from; | ||
597 | int subexp_to; | ||
598 | char more; | ||
599 | char unused; | ||
600 | unsigned short int eps_reachable_subexps_map; | ||
601 | }; | ||
602 | |||
603 | typedef struct | ||
604 | { | ||
605 | /* The string object corresponding to the input string. */ | ||
606 | re_string_t input; | ||
607 | #if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) | ||
608 | const re_dfa_t *const dfa; | ||
609 | #else | ||
610 | const re_dfa_t *dfa; | ||
611 | #endif | ||
612 | /* EFLAGS of the argument of regexec. */ | ||
613 | int eflags; | ||
614 | /* Where the matching ends. */ | ||
615 | int match_last; | ||
616 | int last_node; | ||
617 | /* The state log used by the matcher. */ | ||
618 | re_dfastate_t **state_log; | ||
619 | int state_log_top; | ||
620 | /* Back reference cache. */ | ||
621 | int nbkref_ents; | ||
622 | int abkref_ents; | ||
623 | struct re_backref_cache_entry *bkref_ents; | ||
624 | int max_mb_elem_len; | ||
625 | int nsub_tops; | ||
626 | int asub_tops; | ||
627 | re_sub_match_top_t **sub_tops; | ||
628 | } re_match_context_t; | ||
629 | |||
630 | typedef struct | ||
631 | { | ||
632 | re_dfastate_t **sifted_states; | ||
633 | re_dfastate_t **limited_states; | ||
634 | int last_node; | ||
635 | int last_str_idx; | ||
636 | re_node_set limits; | ||
637 | } re_sift_context_t; | ||
638 | |||
639 | struct re_fail_stack_ent_t | ||
640 | { | ||
641 | int idx; | ||
642 | int node; | ||
643 | regmatch_t *regs; | ||
644 | re_node_set eps_via_nodes; | ||
645 | }; | ||
646 | |||
647 | struct re_fail_stack_t | ||
648 | { | ||
649 | int num; | ||
650 | int alloc; | ||
651 | struct re_fail_stack_ent_t *stack; | ||
652 | }; | ||
653 | |||
654 | struct re_dfa_t | ||
655 | { | ||
656 | re_token_t *nodes; | ||
657 | size_t nodes_alloc; | ||
658 | size_t nodes_len; | ||
659 | int *nexts; | ||
660 | int *org_indices; | ||
661 | re_node_set *edests; | ||
662 | re_node_set *eclosures; | ||
663 | re_node_set *inveclosures; | ||
664 | struct re_state_table_entry *state_table; | ||
665 | re_dfastate_t *init_state; | ||
666 | re_dfastate_t *init_state_word; | ||
667 | re_dfastate_t *init_state_nl; | ||
668 | re_dfastate_t *init_state_begbuf; | ||
669 | bin_tree_t *str_tree; | ||
670 | bin_tree_storage_t *str_tree_storage; | ||
671 | re_bitset_ptr_t sb_char; | ||
672 | int str_tree_storage_idx; | ||
673 | |||
674 | /* number of subexpressions `re_nsub' is in regex_t. */ | ||
675 | unsigned int state_hash_mask; | ||
676 | int init_node; | ||
677 | int nbackref; /* The number of backreference in this dfa. */ | ||
678 | |||
679 | /* Bitmap expressing which backreference is used. */ | ||
680 | bitset_word_t used_bkref_map; | ||
681 | bitset_word_t completed_bkref_map; | ||
682 | |||
683 | unsigned int has_plural_match : 1; | ||
684 | /* If this dfa has "multibyte node", which is a backreference or | ||
685 | a node which can accept multibyte character or multi character | ||
686 | collating element. */ | ||
687 | unsigned int has_mb_node : 1; | ||
688 | unsigned int is_utf8 : 1; | ||
689 | unsigned int map_notascii : 1; | ||
690 | unsigned int word_ops_used : 1; | ||
691 | int mb_cur_max; | ||
692 | bitset_t word_char; | ||
693 | reg_syntax_t syntax; | ||
694 | int *subexp_map; | ||
695 | #ifdef DEBUG | ||
696 | char* re_str; | ||
697 | #endif | ||
698 | #if defined _LIBC | ||
699 | __libc_lock_define (, lock) | ||
700 | #endif | ||
701 | }; | ||
702 | |||
703 | #define re_node_set_init_empty(set) memset (set, '\0', sizeof (re_node_set)) | ||
704 | #define re_node_set_remove(set,id) \ | ||
705 | (re_node_set_remove_at (set, re_node_set_contains (set, id) - 1)) | ||
706 | #define re_node_set_empty(p) ((p)->nelem = 0) | ||
707 | #define re_node_set_free(set) re_free ((set)->elems) | ||
708 | |||
709 | |||
710 | typedef enum | ||
711 | { | ||
712 | SB_CHAR, | ||
713 | MB_CHAR, | ||
714 | EQUIV_CLASS, | ||
715 | COLL_SYM, | ||
716 | CHAR_CLASS | ||
717 | } bracket_elem_type; | ||
718 | |||
719 | typedef struct | ||
720 | { | ||
721 | bracket_elem_type type; | ||
722 | union | ||
723 | { | ||
724 | unsigned char ch; | ||
725 | unsigned char *name; | ||
726 | wchar_t wch; | ||
727 | } opr; | ||
728 | } bracket_elem_t; | ||
729 | |||
730 | |||
731 | /* Inline functions for bitset operation. */ | ||
732 | static inline void | ||
733 | bitset_not (bitset_t set) | ||
734 | { | ||
735 | int bitset_i; | ||
736 | for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) | ||
737 | set[bitset_i] = ~set[bitset_i]; | ||
738 | } | ||
739 | |||
740 | static inline void | ||
741 | bitset_merge (bitset_t dest, const bitset_t src) | ||
742 | { | ||
743 | int bitset_i; | ||
744 | for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) | ||
745 | dest[bitset_i] |= src[bitset_i]; | ||
746 | } | ||
747 | |||
748 | static inline void | ||
749 | bitset_mask (bitset_t dest, const bitset_t src) | ||
750 | { | ||
751 | int bitset_i; | ||
752 | for (bitset_i = 0; bitset_i < BITSET_WORDS; ++bitset_i) | ||
753 | dest[bitset_i] &= src[bitset_i]; | ||
754 | } | ||
755 | |||
756 | #ifdef RE_ENABLE_I18N | ||
757 | /* Inline functions for re_string. */ | ||
758 | static inline int | ||
759 | internal_function __attribute ((pure)) | ||
760 | re_string_char_size_at (const re_string_t *pstr, int idx) | ||
761 | { | ||
762 | int byte_idx; | ||
763 | if (pstr->mb_cur_max == 1) | ||
764 | return 1; | ||
765 | for (byte_idx = 1; idx + byte_idx < pstr->valid_len; ++byte_idx) | ||
766 | if (pstr->wcs[idx + byte_idx] != WEOF) | ||
767 | break; | ||
768 | return byte_idx; | ||
769 | } | ||
770 | |||
771 | static inline wint_t | ||
772 | internal_function __attribute ((pure)) | ||
773 | re_string_wchar_at (const re_string_t *pstr, int idx) | ||
774 | { | ||
775 | if (pstr->mb_cur_max == 1) | ||
776 | return (wint_t) pstr->mbs[idx]; | ||
777 | return (wint_t) pstr->wcs[idx]; | ||
778 | } | ||
779 | |||
780 | # ifndef NOT_IN_libc | ||
781 | static int | ||
782 | internal_function __attribute ((pure)) | ||
783 | re_string_elem_size_at (const re_string_t *pstr, int idx) | ||
784 | { | ||
785 | # ifdef _LIBC | ||
786 | const unsigned char *p, *extra; | ||
787 | const int32_t *table, *indirect; | ||
788 | int32_t tmp; | ||
789 | # include <locale/weight.h> | ||
790 | uint_fast32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); | ||
791 | |||
792 | if (nrules != 0) | ||
793 | { | ||
794 | table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); | ||
795 | extra = (const unsigned char *) | ||
796 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); | ||
797 | indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE, | ||
798 | _NL_COLLATE_INDIRECTMB); | ||
799 | p = pstr->mbs + idx; | ||
800 | tmp = findidx (&p); | ||
801 | return p - pstr->mbs - idx; | ||
802 | } | ||
803 | else | ||
804 | # endif /* _LIBC */ | ||
805 | return 1; | ||
806 | } | ||
807 | # endif | ||
808 | #endif /* RE_ENABLE_I18N */ | ||
809 | |||
810 | #endif /* _REGEX_INTERNAL_H */ | ||
diff --git a/win32/regexec.c b/win32/regexec.c new file mode 100644 index 000000000..eb5e1d443 --- /dev/null +++ b/win32/regexec.c | |||
@@ -0,0 +1,4369 @@ | |||
1 | /* Extended regular expression matching and search library. | ||
2 | Copyright (C) 2002-2005, 2007, 2009, 2010 Free Software Foundation, Inc. | ||
3 | This file is part of the GNU C Library. | ||
4 | Contributed by Isamu Hasegawa <isamu@yamato.ibm.com>. | ||
5 | |||
6 | The GNU C Library is free software; you can redistribute it and/or | ||
7 | modify it under the terms of the GNU Lesser General Public | ||
8 | License as published by the Free Software Foundation; either | ||
9 | version 2.1 of the License, or (at your option) any later version. | ||
10 | |||
11 | The GNU C Library is distributed in the hope that it will be useful, | ||
12 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
14 | Lesser General Public License for more details. | ||
15 | |||
16 | You should have received a copy of the GNU Lesser General Public | ||
17 | License along with the GNU C Library; if not, write to the Free | ||
18 | Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA | ||
19 | 02110-1301 USA. */ | ||
20 | |||
21 | static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags, | ||
22 | int n) internal_function; | ||
23 | static void match_ctx_clean (re_match_context_t *mctx) internal_function; | ||
24 | static void match_ctx_free (re_match_context_t *cache) internal_function; | ||
25 | static reg_errcode_t match_ctx_add_entry (re_match_context_t *cache, int node, | ||
26 | int str_idx, int from, int to) | ||
27 | internal_function; | ||
28 | static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx) | ||
29 | internal_function; | ||
30 | static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node, | ||
31 | int str_idx) internal_function; | ||
32 | static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop, | ||
33 | int node, int str_idx) | ||
34 | internal_function; | ||
35 | static void sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, | ||
36 | re_dfastate_t **limited_sts, int last_node, | ||
37 | int last_str_idx) | ||
38 | internal_function; | ||
39 | static reg_errcode_t re_search_internal (const regex_t *preg, | ||
40 | const char *string, int length, | ||
41 | int start, int range, int stop, | ||
42 | size_t nmatch, regmatch_t pmatch[], | ||
43 | int eflags); | ||
44 | static int re_search_2_stub (struct re_pattern_buffer *bufp, | ||
45 | const char *string1, int length1, | ||
46 | const char *string2, int length2, | ||
47 | int start, int range, struct re_registers *regs, | ||
48 | int stop, int ret_len); | ||
49 | static int re_search_stub (struct re_pattern_buffer *bufp, | ||
50 | const char *string, int length, int start, | ||
51 | int range, int stop, struct re_registers *regs, | ||
52 | int ret_len); | ||
53 | static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch, | ||
54 | int nregs, int regs_allocated); | ||
55 | static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx); | ||
56 | static int check_matching (re_match_context_t *mctx, int fl_longest_match, | ||
57 | int *p_match_first) internal_function; | ||
58 | static int check_halt_state_context (const re_match_context_t *mctx, | ||
59 | const re_dfastate_t *state, int idx) | ||
60 | internal_function; | ||
61 | static void update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, | ||
62 | regmatch_t *prev_idx_match, int cur_node, | ||
63 | int cur_idx, int nmatch) internal_function; | ||
64 | static reg_errcode_t push_fail_stack (struct re_fail_stack_t *fs, | ||
65 | int str_idx, int dest_node, int nregs, | ||
66 | regmatch_t *regs, | ||
67 | re_node_set *eps_via_nodes) | ||
68 | internal_function; | ||
69 | static reg_errcode_t set_regs (const regex_t *preg, | ||
70 | const re_match_context_t *mctx, | ||
71 | size_t nmatch, regmatch_t *pmatch, | ||
72 | int fl_backtrack) internal_function; | ||
73 | static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs) | ||
74 | internal_function; | ||
75 | |||
76 | #ifdef RE_ENABLE_I18N | ||
77 | static int sift_states_iter_mb (const re_match_context_t *mctx, | ||
78 | re_sift_context_t *sctx, | ||
79 | int node_idx, int str_idx, int max_str_idx) | ||
80 | internal_function; | ||
81 | #endif /* RE_ENABLE_I18N */ | ||
82 | static reg_errcode_t sift_states_backward (const re_match_context_t *mctx, | ||
83 | re_sift_context_t *sctx) | ||
84 | internal_function; | ||
85 | static reg_errcode_t build_sifted_states (const re_match_context_t *mctx, | ||
86 | re_sift_context_t *sctx, int str_idx, | ||
87 | re_node_set *cur_dest) | ||
88 | internal_function; | ||
89 | static reg_errcode_t update_cur_sifted_state (const re_match_context_t *mctx, | ||
90 | re_sift_context_t *sctx, | ||
91 | int str_idx, | ||
92 | re_node_set *dest_nodes) | ||
93 | internal_function; | ||
94 | static reg_errcode_t add_epsilon_src_nodes (const re_dfa_t *dfa, | ||
95 | re_node_set *dest_nodes, | ||
96 | const re_node_set *candidates) | ||
97 | internal_function; | ||
98 | static int check_dst_limits (const re_match_context_t *mctx, | ||
99 | re_node_set *limits, | ||
100 | int dst_node, int dst_idx, int src_node, | ||
101 | int src_idx) internal_function; | ||
102 | static int check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, | ||
103 | int boundaries, int subexp_idx, | ||
104 | int from_node, int bkref_idx) | ||
105 | internal_function; | ||
106 | static int check_dst_limits_calc_pos (const re_match_context_t *mctx, | ||
107 | int limit, int subexp_idx, | ||
108 | int node, int str_idx, | ||
109 | int bkref_idx) internal_function; | ||
110 | static reg_errcode_t check_subexp_limits (const re_dfa_t *dfa, | ||
111 | re_node_set *dest_nodes, | ||
112 | const re_node_set *candidates, | ||
113 | re_node_set *limits, | ||
114 | struct re_backref_cache_entry *bkref_ents, | ||
115 | int str_idx) internal_function; | ||
116 | static reg_errcode_t sift_states_bkref (const re_match_context_t *mctx, | ||
117 | re_sift_context_t *sctx, | ||
118 | int str_idx, const re_node_set *candidates) | ||
119 | internal_function; | ||
120 | static reg_errcode_t merge_state_array (const re_dfa_t *dfa, | ||
121 | re_dfastate_t **dst, | ||
122 | re_dfastate_t **src, int num) | ||
123 | internal_function; | ||
124 | static re_dfastate_t *find_recover_state (reg_errcode_t *err, | ||
125 | re_match_context_t *mctx) internal_function; | ||
126 | static re_dfastate_t *transit_state (reg_errcode_t *err, | ||
127 | re_match_context_t *mctx, | ||
128 | re_dfastate_t *state) internal_function; | ||
129 | static re_dfastate_t *merge_state_with_log (reg_errcode_t *err, | ||
130 | re_match_context_t *mctx, | ||
131 | re_dfastate_t *next_state) | ||
132 | internal_function; | ||
133 | static reg_errcode_t check_subexp_matching_top (re_match_context_t *mctx, | ||
134 | re_node_set *cur_nodes, | ||
135 | int str_idx) internal_function; | ||
136 | #if 0 | ||
137 | static re_dfastate_t *transit_state_sb (reg_errcode_t *err, | ||
138 | re_match_context_t *mctx, | ||
139 | re_dfastate_t *pstate) | ||
140 | internal_function; | ||
141 | #endif | ||
142 | #ifdef RE_ENABLE_I18N | ||
143 | static reg_errcode_t transit_state_mb (re_match_context_t *mctx, | ||
144 | re_dfastate_t *pstate) | ||
145 | internal_function; | ||
146 | #endif /* RE_ENABLE_I18N */ | ||
147 | static reg_errcode_t transit_state_bkref (re_match_context_t *mctx, | ||
148 | const re_node_set *nodes) | ||
149 | internal_function; | ||
150 | static reg_errcode_t get_subexp (re_match_context_t *mctx, | ||
151 | int bkref_node, int bkref_str_idx) | ||
152 | internal_function; | ||
153 | static reg_errcode_t get_subexp_sub (re_match_context_t *mctx, | ||
154 | const re_sub_match_top_t *sub_top, | ||
155 | re_sub_match_last_t *sub_last, | ||
156 | int bkref_node, int bkref_str) | ||
157 | internal_function; | ||
158 | static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, | ||
159 | int subexp_idx, int type) internal_function; | ||
160 | static reg_errcode_t check_arrival (re_match_context_t *mctx, | ||
161 | state_array_t *path, int top_node, | ||
162 | int top_str, int last_node, int last_str, | ||
163 | int type) internal_function; | ||
164 | static reg_errcode_t check_arrival_add_next_nodes (re_match_context_t *mctx, | ||
165 | int str_idx, | ||
166 | re_node_set *cur_nodes, | ||
167 | re_node_set *next_nodes) | ||
168 | internal_function; | ||
169 | static reg_errcode_t check_arrival_expand_ecl (const re_dfa_t *dfa, | ||
170 | re_node_set *cur_nodes, | ||
171 | int ex_subexp, int type) | ||
172 | internal_function; | ||
173 | static reg_errcode_t check_arrival_expand_ecl_sub (const re_dfa_t *dfa, | ||
174 | re_node_set *dst_nodes, | ||
175 | int target, int ex_subexp, | ||
176 | int type) internal_function; | ||
177 | static reg_errcode_t expand_bkref_cache (re_match_context_t *mctx, | ||
178 | re_node_set *cur_nodes, int cur_str, | ||
179 | int subexp_num, int type) | ||
180 | internal_function; | ||
181 | static int build_trtable (const re_dfa_t *dfa, | ||
182 | re_dfastate_t *state) internal_function; | ||
183 | #ifdef RE_ENABLE_I18N | ||
184 | static int check_node_accept_bytes (const re_dfa_t *dfa, int node_idx, | ||
185 | const re_string_t *input, int idx) | ||
186 | internal_function; | ||
187 | # ifdef _LIBC | ||
188 | static unsigned int find_collation_sequence_value (const unsigned char *mbs, | ||
189 | size_t name_len) | ||
190 | internal_function; | ||
191 | # endif /* _LIBC */ | ||
192 | #endif /* RE_ENABLE_I18N */ | ||
193 | static int group_nodes_into_DFAstates (const re_dfa_t *dfa, | ||
194 | const re_dfastate_t *state, | ||
195 | re_node_set *states_node, | ||
196 | bitset_t *states_ch) internal_function; | ||
197 | static int check_node_accept (const re_match_context_t *mctx, | ||
198 | const re_token_t *node, int idx) | ||
199 | internal_function; | ||
200 | static reg_errcode_t extend_buffers (re_match_context_t *mctx) | ||
201 | internal_function; | ||
202 | |||
203 | /* Entry point for POSIX code. */ | ||
204 | |||
205 | /* regexec searches for a given pattern, specified by PREG, in the | ||
206 | string STRING. | ||
207 | |||
208 | If NMATCH is zero or REG_NOSUB was set in the cflags argument to | ||
209 | `regcomp', we ignore PMATCH. Otherwise, we assume PMATCH has at | ||
210 | least NMATCH elements, and we set them to the offsets of the | ||
211 | corresponding matched substrings. | ||
212 | |||
213 | EFLAGS specifies `execution flags' which affect matching: if | ||
214 | REG_NOTBOL is set, then ^ does not match at the beginning of the | ||
215 | string; if REG_NOTEOL is set, then $ does not match at the end. | ||
216 | |||
217 | We return 0 if we find a match and REG_NOMATCH if not. */ | ||
218 | |||
219 | int | ||
220 | regexec ( | ||
221 | const regex_t *__restrict preg, | ||
222 | const char *__restrict string, | ||
223 | size_t nmatch, | ||
224 | regmatch_t pmatch[], | ||
225 | int eflags) | ||
226 | { | ||
227 | reg_errcode_t err; | ||
228 | int start, length; | ||
229 | |||
230 | if (eflags & ~(REG_NOTBOL | REG_NOTEOL | REG_STARTEND)) | ||
231 | return REG_BADPAT; | ||
232 | |||
233 | if (eflags & REG_STARTEND) | ||
234 | { | ||
235 | start = pmatch[0].rm_so; | ||
236 | length = pmatch[0].rm_eo; | ||
237 | } | ||
238 | else | ||
239 | { | ||
240 | start = 0; | ||
241 | length = strlen (string); | ||
242 | } | ||
243 | |||
244 | __libc_lock_lock (dfa->lock); | ||
245 | if (preg->no_sub) | ||
246 | err = re_search_internal (preg, string, length, start, length - start, | ||
247 | length, 0, NULL, eflags); | ||
248 | else | ||
249 | err = re_search_internal (preg, string, length, start, length - start, | ||
250 | length, nmatch, pmatch, eflags); | ||
251 | __libc_lock_unlock (dfa->lock); | ||
252 | return err != REG_NOERROR; | ||
253 | } | ||
254 | |||
255 | #ifdef _LIBC | ||
256 | # include <shlib-compat.h> | ||
257 | versioned_symbol (libc, __regexec, regexec, GLIBC_2_3_4); | ||
258 | |||
259 | # if SHLIB_COMPAT (libc, GLIBC_2_0, GLIBC_2_3_4) | ||
260 | __typeof__ (__regexec) __compat_regexec; | ||
261 | |||
262 | int | ||
263 | attribute_compat_text_section | ||
264 | __compat_regexec (const regex_t *__restrict preg, | ||
265 | const char *__restrict string, size_t nmatch, | ||
266 | regmatch_t pmatch[], int eflags) | ||
267 | { | ||
268 | return regexec (preg, string, nmatch, pmatch, | ||
269 | eflags & (REG_NOTBOL | REG_NOTEOL)); | ||
270 | } | ||
271 | compat_symbol (libc, __compat_regexec, regexec, GLIBC_2_0); | ||
272 | # endif | ||
273 | #endif | ||
274 | |||
275 | /* Entry points for GNU code. */ | ||
276 | |||
277 | /* re_match, re_search, re_match_2, re_search_2 | ||
278 | |||
279 | The former two functions operate on STRING with length LENGTH, | ||
280 | while the later two operate on concatenation of STRING1 and STRING2 | ||
281 | with lengths LENGTH1 and LENGTH2, respectively. | ||
282 | |||
283 | re_match() matches the compiled pattern in BUFP against the string, | ||
284 | starting at index START. | ||
285 | |||
286 | re_search() first tries matching at index START, then it tries to match | ||
287 | starting from index START + 1, and so on. The last start position tried | ||
288 | is START + RANGE. (Thus RANGE = 0 forces re_search to operate the same | ||
289 | way as re_match().) | ||
290 | |||
291 | The parameter STOP of re_{match,search}_2 specifies that no match exceeding | ||
292 | the first STOP characters of the concatenation of the strings should be | ||
293 | concerned. | ||
294 | |||
295 | If REGS is not NULL, and BUFP->no_sub is not set, the offsets of the match | ||
296 | and all groups is stroed in REGS. (For the "_2" variants, the offsets are | ||
297 | computed relative to the concatenation, not relative to the individual | ||
298 | strings.) | ||
299 | |||
300 | On success, re_match* functions return the length of the match, re_search* | ||
301 | return the position of the start of the match. Return value -1 means no | ||
302 | match was found and -2 indicates an internal error. */ | ||
303 | |||
304 | int | ||
305 | re_match (struct re_pattern_buffer *bufp, | ||
306 | const char *string, | ||
307 | int length, | ||
308 | int start, | ||
309 | struct re_registers *regs) | ||
310 | { | ||
311 | return re_search_stub (bufp, string, length, start, 0, length, regs, 1); | ||
312 | } | ||
313 | #ifdef _LIBC | ||
314 | weak_alias (__re_match, re_match) | ||
315 | #endif | ||
316 | |||
317 | int | ||
318 | re_search (struct re_pattern_buffer *bufp, | ||
319 | const char *string, | ||
320 | int length, int start, int range, | ||
321 | struct re_registers *regs) | ||
322 | { | ||
323 | return re_search_stub (bufp, string, length, start, range, length, regs, 0); | ||
324 | } | ||
325 | #ifdef _LIBC | ||
326 | weak_alias (__re_search, re_search) | ||
327 | #endif | ||
328 | |||
329 | int | ||
330 | re_match_2 (struct re_pattern_buffer *bufp, | ||
331 | const char *string1, int length1, | ||
332 | const char *string2, int length2, int start, | ||
333 | struct re_registers *regs, int stop) | ||
334 | { | ||
335 | return re_search_2_stub (bufp, string1, length1, string2, length2, | ||
336 | start, 0, regs, stop, 1); | ||
337 | } | ||
338 | #ifdef _LIBC | ||
339 | weak_alias (__re_match_2, re_match_2) | ||
340 | #endif | ||
341 | |||
342 | int | ||
343 | re_search_2 (struct re_pattern_buffer *bufp, | ||
344 | const char *string1, int length1, | ||
345 | const char *string2, int length2, int start, | ||
346 | int range, struct re_registers *regs, int stop) | ||
347 | { | ||
348 | return re_search_2_stub (bufp, string1, length1, string2, length2, | ||
349 | start, range, regs, stop, 0); | ||
350 | } | ||
351 | #ifdef _LIBC | ||
352 | weak_alias (__re_search_2, re_search_2) | ||
353 | #endif | ||
354 | |||
355 | static int | ||
356 | re_search_2_stub (struct re_pattern_buffer *bufp, | ||
357 | const char *string1, int length1, | ||
358 | const char *string2, int length2, int start, | ||
359 | int range, struct re_registers *regs, | ||
360 | int stop, int ret_len) | ||
361 | { | ||
362 | const char *str; | ||
363 | int rval; | ||
364 | int len = length1 + length2; | ||
365 | int free_str = 0; | ||
366 | |||
367 | if (BE (length1 < 0 || length2 < 0 || stop < 0, 0)) | ||
368 | return -2; | ||
369 | |||
370 | /* Concatenate the strings. */ | ||
371 | if (length2 > 0) | ||
372 | if (length1 > 0) | ||
373 | { | ||
374 | char *s = re_malloc (char, len); | ||
375 | |||
376 | if (BE (s == NULL, 0)) | ||
377 | return -2; | ||
378 | memcpy (s, string1, length1); | ||
379 | memcpy (s + length1, string2, length2); | ||
380 | str = s; | ||
381 | free_str = 1; | ||
382 | } | ||
383 | else | ||
384 | str = string2; | ||
385 | else | ||
386 | str = string1; | ||
387 | |||
388 | rval = re_search_stub (bufp, str, len, start, range, stop, regs, ret_len); | ||
389 | if (free_str) | ||
390 | re_free ((char *) str); | ||
391 | return rval; | ||
392 | } | ||
393 | |||
394 | /* The parameters have the same meaning as those of re_search. | ||
395 | Additional parameters: | ||
396 | If RET_LEN is nonzero the length of the match is returned (re_match style); | ||
397 | otherwise the position of the match is returned. */ | ||
398 | |||
399 | static int | ||
400 | re_search_stub (struct re_pattern_buffer *bufp, | ||
401 | const char *string, int length, int start, | ||
402 | int range, int stop, | ||
403 | struct re_registers *regs, int ret_len) | ||
404 | { | ||
405 | reg_errcode_t result; | ||
406 | regmatch_t *pmatch; | ||
407 | int nregs, rval; | ||
408 | int eflags = 0; | ||
409 | |||
410 | /* Check for out-of-range. */ | ||
411 | if (BE (start < 0 || start > length, 0)) | ||
412 | return -1; | ||
413 | if (BE (start + range > length, 0)) | ||
414 | range = length - start; | ||
415 | else if (BE (start + range < 0, 0)) | ||
416 | range = -start; | ||
417 | |||
418 | __libc_lock_lock (dfa->lock); | ||
419 | |||
420 | eflags |= (bufp->not_bol) ? REG_NOTBOL : 0; | ||
421 | eflags |= (bufp->not_eol) ? REG_NOTEOL : 0; | ||
422 | |||
423 | /* Compile fastmap if we haven't yet. */ | ||
424 | if (range > 0 && bufp->fastmap != NULL && !bufp->fastmap_accurate) | ||
425 | re_compile_fastmap (bufp); | ||
426 | |||
427 | if (BE (bufp->no_sub, 0)) | ||
428 | regs = NULL; | ||
429 | |||
430 | /* We need at least 1 register. */ | ||
431 | if (regs == NULL) | ||
432 | nregs = 1; | ||
433 | else if (BE (bufp->regs_allocated == REGS_FIXED && | ||
434 | regs->num_regs < bufp->re_nsub + 1, 0)) | ||
435 | { | ||
436 | nregs = regs->num_regs; | ||
437 | if (BE (nregs < 1, 0)) | ||
438 | { | ||
439 | /* Nothing can be copied to regs. */ | ||
440 | regs = NULL; | ||
441 | nregs = 1; | ||
442 | } | ||
443 | } | ||
444 | else | ||
445 | nregs = bufp->re_nsub + 1; | ||
446 | pmatch = re_malloc (regmatch_t, nregs); | ||
447 | if (BE (pmatch == NULL, 0)) | ||
448 | { | ||
449 | rval = -2; | ||
450 | goto out; | ||
451 | } | ||
452 | |||
453 | result = re_search_internal (bufp, string, length, start, range, stop, | ||
454 | nregs, pmatch, eflags); | ||
455 | |||
456 | rval = 0; | ||
457 | |||
458 | /* I hope we needn't fill their regs with -1's when no match was found. */ | ||
459 | if (result != REG_NOERROR) | ||
460 | rval = -1; | ||
461 | else if (regs != NULL) | ||
462 | { | ||
463 | /* If caller wants register contents data back, copy them. */ | ||
464 | bufp->regs_allocated = re_copy_regs (regs, pmatch, nregs, | ||
465 | bufp->regs_allocated); | ||
466 | if (BE (bufp->regs_allocated == REGS_UNALLOCATED, 0)) | ||
467 | rval = -2; | ||
468 | } | ||
469 | |||
470 | if (BE (rval == 0, 1)) | ||
471 | { | ||
472 | if (ret_len) | ||
473 | { | ||
474 | assert (pmatch[0].rm_so == start); | ||
475 | rval = pmatch[0].rm_eo - start; | ||
476 | } | ||
477 | else | ||
478 | rval = pmatch[0].rm_so; | ||
479 | } | ||
480 | re_free (pmatch); | ||
481 | out: | ||
482 | __libc_lock_unlock (dfa->lock); | ||
483 | return rval; | ||
484 | } | ||
485 | |||
486 | static unsigned | ||
487 | re_copy_regs (struct re_registers *regs, | ||
488 | regmatch_t *pmatch, | ||
489 | int nregs, int regs_allocated) | ||
490 | { | ||
491 | int rval = REGS_REALLOCATE; | ||
492 | int i; | ||
493 | int need_regs = nregs + 1; | ||
494 | /* We need one extra element beyond `num_regs' for the `-1' marker GNU code | ||
495 | uses. */ | ||
496 | |||
497 | /* Have the register data arrays been allocated? */ | ||
498 | if (regs_allocated == REGS_UNALLOCATED) | ||
499 | { /* No. So allocate them with malloc. */ | ||
500 | regs->start = re_malloc (regoff_t, need_regs); | ||
501 | if (BE (regs->start == NULL, 0)) | ||
502 | return REGS_UNALLOCATED; | ||
503 | regs->end = re_malloc (regoff_t, need_regs); | ||
504 | if (BE (regs->end == NULL, 0)) | ||
505 | { | ||
506 | re_free (regs->start); | ||
507 | return REGS_UNALLOCATED; | ||
508 | } | ||
509 | regs->num_regs = need_regs; | ||
510 | } | ||
511 | else if (regs_allocated == REGS_REALLOCATE) | ||
512 | { /* Yes. If we need more elements than were already | ||
513 | allocated, reallocate them. If we need fewer, just | ||
514 | leave it alone. */ | ||
515 | if (BE (need_regs > regs->num_regs, 0)) | ||
516 | { | ||
517 | regoff_t *new_start = re_realloc (regs->start, regoff_t, need_regs); | ||
518 | regoff_t *new_end; | ||
519 | if (BE (new_start == NULL, 0)) | ||
520 | return REGS_UNALLOCATED; | ||
521 | new_end = re_realloc (regs->end, regoff_t, need_regs); | ||
522 | if (BE (new_end == NULL, 0)) | ||
523 | { | ||
524 | re_free (new_start); | ||
525 | return REGS_UNALLOCATED; | ||
526 | } | ||
527 | regs->start = new_start; | ||
528 | regs->end = new_end; | ||
529 | regs->num_regs = need_regs; | ||
530 | } | ||
531 | } | ||
532 | else | ||
533 | { | ||
534 | assert (regs_allocated == REGS_FIXED); | ||
535 | /* This function may not be called with REGS_FIXED and nregs too big. */ | ||
536 | assert (regs->num_regs >= nregs); | ||
537 | rval = REGS_FIXED; | ||
538 | } | ||
539 | |||
540 | /* Copy the regs. */ | ||
541 | for (i = 0; i < nregs; ++i) | ||
542 | { | ||
543 | regs->start[i] = pmatch[i].rm_so; | ||
544 | regs->end[i] = pmatch[i].rm_eo; | ||
545 | } | ||
546 | for ( ; i < regs->num_regs; ++i) | ||
547 | regs->start[i] = regs->end[i] = -1; | ||
548 | |||
549 | return rval; | ||
550 | } | ||
551 | |||
552 | /* Set REGS to hold NUM_REGS registers, storing them in STARTS and | ||
553 | ENDS. Subsequent matches using PATTERN_BUFFER and REGS will use | ||
554 | this memory for recording register information. STARTS and ENDS | ||
555 | must be allocated using the malloc library routine, and must each | ||
556 | be at least NUM_REGS * sizeof (regoff_t) bytes long. | ||
557 | |||
558 | If NUM_REGS == 0, then subsequent matches should allocate their own | ||
559 | register data. | ||
560 | |||
561 | Unless this function is called, the first search or match using | ||
562 | PATTERN_BUFFER will allocate its own register data, without | ||
563 | freeing the old data. */ | ||
564 | |||
565 | void | ||
566 | re_set_registers (struct re_pattern_buffer *bufp, | ||
567 | struct re_registers *regs, | ||
568 | unsigned num_regs, | ||
569 | regoff_t *starts, | ||
570 | regoff_t *ends) | ||
571 | { | ||
572 | if (num_regs) | ||
573 | { | ||
574 | bufp->regs_allocated = REGS_REALLOCATE; | ||
575 | regs->num_regs = num_regs; | ||
576 | regs->start = starts; | ||
577 | regs->end = ends; | ||
578 | } | ||
579 | else | ||
580 | { | ||
581 | bufp->regs_allocated = REGS_UNALLOCATED; | ||
582 | regs->num_regs = 0; | ||
583 | regs->start = regs->end = (regoff_t *) 0; | ||
584 | } | ||
585 | } | ||
586 | #ifdef _LIBC | ||
587 | weak_alias (__re_set_registers, re_set_registers) | ||
588 | #endif | ||
589 | |||
590 | /* Entry points compatible with 4.2 BSD regex library. We don't define | ||
591 | them unless specifically requested. */ | ||
592 | |||
593 | #if defined _REGEX_RE_COMP || defined _LIBC | ||
594 | int | ||
595 | # ifdef _LIBC | ||
596 | weak_function | ||
597 | # endif | ||
598 | re_exec (s) | ||
599 | const char *s; | ||
600 | { | ||
601 | return 0 == regexec (&re_comp_buf, s, 0, NULL, 0); | ||
602 | } | ||
603 | #endif /* _REGEX_RE_COMP */ | ||
604 | |||
605 | /* Internal entry point. */ | ||
606 | |||
607 | /* Searches for a compiled pattern PREG in the string STRING, whose | ||
608 | length is LENGTH. NMATCH, PMATCH, and EFLAGS have the same | ||
609 | mingings with regexec. START, and RANGE have the same meanings | ||
610 | with re_search. | ||
611 | Return REG_NOERROR if we find a match, and REG_NOMATCH if not, | ||
612 | otherwise return the error code. | ||
613 | Note: We assume front end functions already check ranges. | ||
614 | (START + RANGE >= 0 && START + RANGE <= LENGTH) */ | ||
615 | |||
616 | static reg_errcode_t | ||
617 | re_search_internal (const regex_t *preg, | ||
618 | const char *string, | ||
619 | int length, int start, int range, int stop, | ||
620 | size_t nmatch, regmatch_t pmatch[], | ||
621 | int eflags) | ||
622 | { | ||
623 | reg_errcode_t err; | ||
624 | const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; | ||
625 | int left_lim, right_lim, incr; | ||
626 | int fl_longest_match, match_first, match_kind, match_last = -1; | ||
627 | int extra_nmatch; | ||
628 | int sb, ch; | ||
629 | #if defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L) | ||
630 | re_match_context_t mctx = { .dfa = dfa }; | ||
631 | #else | ||
632 | re_match_context_t mctx; | ||
633 | #endif | ||
634 | char *fastmap = (preg->fastmap != NULL && preg->fastmap_accurate | ||
635 | && range && !preg->can_be_null) ? preg->fastmap : NULL; | ||
636 | RE_TRANSLATE_TYPE t = preg->translate; | ||
637 | |||
638 | #if !(defined _LIBC || (defined __STDC_VERSION__ && __STDC_VERSION__ >= 199901L)) | ||
639 | memset (&mctx, '\0', sizeof (re_match_context_t)); | ||
640 | mctx.dfa = dfa; | ||
641 | #endif | ||
642 | |||
643 | extra_nmatch = (nmatch > preg->re_nsub) ? nmatch - (preg->re_nsub + 1) : 0; | ||
644 | nmatch -= extra_nmatch; | ||
645 | |||
646 | /* Check if the DFA haven't been compiled. */ | ||
647 | if (BE (preg->used == 0 || dfa->init_state == NULL | ||
648 | || dfa->init_state_word == NULL || dfa->init_state_nl == NULL | ||
649 | || dfa->init_state_begbuf == NULL, 0)) | ||
650 | return REG_NOMATCH; | ||
651 | |||
652 | #ifdef DEBUG | ||
653 | /* We assume front-end functions already check them. */ | ||
654 | assert (start + range >= 0 && start + range <= length); | ||
655 | #endif | ||
656 | |||
657 | /* If initial states with non-begbuf contexts have no elements, | ||
658 | the regex must be anchored. If preg->newline_anchor is set, | ||
659 | we'll never use init_state_nl, so do not check it. */ | ||
660 | if (dfa->init_state->nodes.nelem == 0 | ||
661 | && dfa->init_state_word->nodes.nelem == 0 | ||
662 | && (dfa->init_state_nl->nodes.nelem == 0 | ||
663 | || !preg->newline_anchor)) | ||
664 | { | ||
665 | if (start != 0 && start + range != 0) | ||
666 | return REG_NOMATCH; | ||
667 | start = range = 0; | ||
668 | } | ||
669 | |||
670 | /* We must check the longest matching, if nmatch > 0. */ | ||
671 | fl_longest_match = (nmatch != 0 || dfa->nbackref); | ||
672 | |||
673 | err = re_string_allocate (&mctx.input, string, length, dfa->nodes_len + 1, | ||
674 | preg->translate, preg->syntax & RE_ICASE, dfa); | ||
675 | if (BE (err != REG_NOERROR, 0)) | ||
676 | goto free_return; | ||
677 | mctx.input.stop = stop; | ||
678 | mctx.input.raw_stop = stop; | ||
679 | mctx.input.newline_anchor = preg->newline_anchor; | ||
680 | |||
681 | err = match_ctx_init (&mctx, eflags, dfa->nbackref * 2); | ||
682 | if (BE (err != REG_NOERROR, 0)) | ||
683 | goto free_return; | ||
684 | |||
685 | /* We will log all the DFA states through which the dfa pass, | ||
686 | if nmatch > 1, or this dfa has "multibyte node", which is a | ||
687 | back-reference or a node which can accept multibyte character or | ||
688 | multi character collating element. */ | ||
689 | if (nmatch > 1 || dfa->has_mb_node) | ||
690 | { | ||
691 | /* Avoid overflow. */ | ||
692 | if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= mctx.input.bufs_len, 0)) | ||
693 | { | ||
694 | err = REG_ESPACE; | ||
695 | goto free_return; | ||
696 | } | ||
697 | |||
698 | mctx.state_log = re_malloc (re_dfastate_t *, mctx.input.bufs_len + 1); | ||
699 | if (BE (mctx.state_log == NULL, 0)) | ||
700 | { | ||
701 | err = REG_ESPACE; | ||
702 | goto free_return; | ||
703 | } | ||
704 | } | ||
705 | else | ||
706 | mctx.state_log = NULL; | ||
707 | |||
708 | match_first = start; | ||
709 | mctx.input.tip_context = (eflags & REG_NOTBOL) ? CONTEXT_BEGBUF | ||
710 | : CONTEXT_NEWLINE | CONTEXT_BEGBUF; | ||
711 | |||
712 | /* Check incrementally whether of not the input string match. */ | ||
713 | incr = (range < 0) ? -1 : 1; | ||
714 | left_lim = (range < 0) ? start + range : start; | ||
715 | right_lim = (range < 0) ? start : start + range; | ||
716 | sb = dfa->mb_cur_max == 1; | ||
717 | match_kind = | ||
718 | (fastmap | ||
719 | ? ((sb || !(preg->syntax & RE_ICASE || t) ? 4 : 0) | ||
720 | | (range >= 0 ? 2 : 0) | ||
721 | | (t != NULL ? 1 : 0)) | ||
722 | : 8); | ||
723 | |||
724 | for (;; match_first += incr) | ||
725 | { | ||
726 | err = REG_NOMATCH; | ||
727 | if (match_first < left_lim || right_lim < match_first) | ||
728 | goto free_return; | ||
729 | |||
730 | /* Advance as rapidly as possible through the string, until we | ||
731 | find a plausible place to start matching. This may be done | ||
732 | with varying efficiency, so there are various possibilities: | ||
733 | only the most common of them are specialized, in order to | ||
734 | save on code size. We use a switch statement for speed. */ | ||
735 | switch (match_kind) | ||
736 | { | ||
737 | case 8: | ||
738 | /* No fastmap. */ | ||
739 | break; | ||
740 | |||
741 | case 7: | ||
742 | /* Fastmap with single-byte translation, match forward. */ | ||
743 | while (BE (match_first < right_lim, 1) | ||
744 | && !fastmap[t[(unsigned char) string[match_first]]]) | ||
745 | ++match_first; | ||
746 | goto forward_match_found_start_or_reached_end; | ||
747 | |||
748 | case 6: | ||
749 | /* Fastmap without translation, match forward. */ | ||
750 | while (BE (match_first < right_lim, 1) | ||
751 | && !fastmap[(unsigned char) string[match_first]]) | ||
752 | ++match_first; | ||
753 | |||
754 | forward_match_found_start_or_reached_end: | ||
755 | if (BE (match_first == right_lim, 0)) | ||
756 | { | ||
757 | ch = match_first >= length | ||
758 | ? 0 : (unsigned char) string[match_first]; | ||
759 | if (!fastmap[t ? t[ch] : ch]) | ||
760 | goto free_return; | ||
761 | } | ||
762 | break; | ||
763 | |||
764 | case 4: | ||
765 | case 5: | ||
766 | /* Fastmap without multi-byte translation, match backwards. */ | ||
767 | while (match_first >= left_lim) | ||
768 | { | ||
769 | ch = match_first >= length | ||
770 | ? 0 : (unsigned char) string[match_first]; | ||
771 | if (fastmap[t ? t[ch] : ch]) | ||
772 | break; | ||
773 | --match_first; | ||
774 | } | ||
775 | if (match_first < left_lim) | ||
776 | goto free_return; | ||
777 | break; | ||
778 | |||
779 | default: | ||
780 | /* In this case, we can't determine easily the current byte, | ||
781 | since it might be a component byte of a multibyte | ||
782 | character. Then we use the constructed buffer instead. */ | ||
783 | for (;;) | ||
784 | { | ||
785 | /* If MATCH_FIRST is out of the valid range, reconstruct the | ||
786 | buffers. */ | ||
787 | unsigned int offset = match_first - mctx.input.raw_mbs_idx; | ||
788 | if (BE (offset >= (unsigned int) mctx.input.valid_raw_len, 0)) | ||
789 | { | ||
790 | err = re_string_reconstruct (&mctx.input, match_first, | ||
791 | eflags); | ||
792 | if (BE (err != REG_NOERROR, 0)) | ||
793 | goto free_return; | ||
794 | |||
795 | offset = match_first - mctx.input.raw_mbs_idx; | ||
796 | } | ||
797 | /* If MATCH_FIRST is out of the buffer, leave it as '\0'. | ||
798 | Note that MATCH_FIRST must not be smaller than 0. */ | ||
799 | ch = (match_first >= length | ||
800 | ? 0 : re_string_byte_at (&mctx.input, offset)); | ||
801 | if (fastmap[ch]) | ||
802 | break; | ||
803 | match_first += incr; | ||
804 | if (match_first < left_lim || match_first > right_lim) | ||
805 | { | ||
806 | err = REG_NOMATCH; | ||
807 | goto free_return; | ||
808 | } | ||
809 | } | ||
810 | break; | ||
811 | } | ||
812 | |||
813 | /* Reconstruct the buffers so that the matcher can assume that | ||
814 | the matching starts from the beginning of the buffer. */ | ||
815 | err = re_string_reconstruct (&mctx.input, match_first, eflags); | ||
816 | if (BE (err != REG_NOERROR, 0)) | ||
817 | goto free_return; | ||
818 | |||
819 | #ifdef RE_ENABLE_I18N | ||
820 | /* Don't consider this char as a possible match start if it part, | ||
821 | yet isn't the head, of a multibyte character. */ | ||
822 | if (!sb && !re_string_first_byte (&mctx.input, 0)) | ||
823 | continue; | ||
824 | #endif | ||
825 | |||
826 | /* It seems to be appropriate one, then use the matcher. */ | ||
827 | /* We assume that the matching starts from 0. */ | ||
828 | mctx.state_log_top = mctx.nbkref_ents = mctx.max_mb_elem_len = 0; | ||
829 | match_last = check_matching (&mctx, fl_longest_match, | ||
830 | range >= 0 ? &match_first : NULL); | ||
831 | if (match_last != -1) | ||
832 | { | ||
833 | if (BE (match_last == -2, 0)) | ||
834 | { | ||
835 | err = REG_ESPACE; | ||
836 | goto free_return; | ||
837 | } | ||
838 | else | ||
839 | { | ||
840 | mctx.match_last = match_last; | ||
841 | if ((!preg->no_sub && nmatch > 1) || dfa->nbackref) | ||
842 | { | ||
843 | re_dfastate_t *pstate = mctx.state_log[match_last]; | ||
844 | mctx.last_node = check_halt_state_context (&mctx, pstate, | ||
845 | match_last); | ||
846 | } | ||
847 | if ((!preg->no_sub && nmatch > 1 && dfa->has_plural_match) | ||
848 | || dfa->nbackref) | ||
849 | { | ||
850 | err = prune_impossible_nodes (&mctx); | ||
851 | if (err == REG_NOERROR) | ||
852 | break; | ||
853 | if (BE (err != REG_NOMATCH, 0)) | ||
854 | goto free_return; | ||
855 | match_last = -1; | ||
856 | } | ||
857 | else | ||
858 | break; /* We found a match. */ | ||
859 | } | ||
860 | } | ||
861 | |||
862 | match_ctx_clean (&mctx); | ||
863 | } | ||
864 | |||
865 | #ifdef DEBUG | ||
866 | assert (match_last != -1); | ||
867 | assert (err == REG_NOERROR); | ||
868 | #endif | ||
869 | |||
870 | /* Set pmatch[] if we need. */ | ||
871 | if (nmatch > 0) | ||
872 | { | ||
873 | int reg_idx; | ||
874 | |||
875 | /* Initialize registers. */ | ||
876 | for (reg_idx = 1; reg_idx < nmatch; ++reg_idx) | ||
877 | pmatch[reg_idx].rm_so = pmatch[reg_idx].rm_eo = -1; | ||
878 | |||
879 | /* Set the points where matching start/end. */ | ||
880 | pmatch[0].rm_so = 0; | ||
881 | pmatch[0].rm_eo = mctx.match_last; | ||
882 | |||
883 | if (!preg->no_sub && nmatch > 1) | ||
884 | { | ||
885 | err = set_regs (preg, &mctx, nmatch, pmatch, | ||
886 | dfa->has_plural_match && dfa->nbackref > 0); | ||
887 | if (BE (err != REG_NOERROR, 0)) | ||
888 | goto free_return; | ||
889 | } | ||
890 | |||
891 | /* At last, add the offset to the each registers, since we slided | ||
892 | the buffers so that we could assume that the matching starts | ||
893 | from 0. */ | ||
894 | for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) | ||
895 | if (pmatch[reg_idx].rm_so != -1) | ||
896 | { | ||
897 | #ifdef RE_ENABLE_I18N | ||
898 | if (BE (mctx.input.offsets_needed != 0, 0)) | ||
899 | { | ||
900 | pmatch[reg_idx].rm_so = | ||
901 | (pmatch[reg_idx].rm_so == mctx.input.valid_len | ||
902 | ? mctx.input.valid_raw_len | ||
903 | : mctx.input.offsets[pmatch[reg_idx].rm_so]); | ||
904 | pmatch[reg_idx].rm_eo = | ||
905 | (pmatch[reg_idx].rm_eo == mctx.input.valid_len | ||
906 | ? mctx.input.valid_raw_len | ||
907 | : mctx.input.offsets[pmatch[reg_idx].rm_eo]); | ||
908 | } | ||
909 | #else | ||
910 | assert (mctx.input.offsets_needed == 0); | ||
911 | #endif | ||
912 | pmatch[reg_idx].rm_so += match_first; | ||
913 | pmatch[reg_idx].rm_eo += match_first; | ||
914 | } | ||
915 | for (reg_idx = 0; reg_idx < extra_nmatch; ++reg_idx) | ||
916 | { | ||
917 | pmatch[nmatch + reg_idx].rm_so = -1; | ||
918 | pmatch[nmatch + reg_idx].rm_eo = -1; | ||
919 | } | ||
920 | |||
921 | if (dfa->subexp_map) | ||
922 | for (reg_idx = 0; reg_idx + 1 < nmatch; reg_idx++) | ||
923 | if (dfa->subexp_map[reg_idx] != reg_idx) | ||
924 | { | ||
925 | pmatch[reg_idx + 1].rm_so | ||
926 | = pmatch[dfa->subexp_map[reg_idx] + 1].rm_so; | ||
927 | pmatch[reg_idx + 1].rm_eo | ||
928 | = pmatch[dfa->subexp_map[reg_idx] + 1].rm_eo; | ||
929 | } | ||
930 | } | ||
931 | |||
932 | free_return: | ||
933 | re_free (mctx.state_log); | ||
934 | if (dfa->nbackref) | ||
935 | match_ctx_free (&mctx); | ||
936 | re_string_destruct (&mctx.input); | ||
937 | return err; | ||
938 | } | ||
939 | |||
940 | static reg_errcode_t | ||
941 | prune_impossible_nodes (re_match_context_t *mctx) | ||
942 | { | ||
943 | const re_dfa_t *const dfa = mctx->dfa; | ||
944 | int halt_node, match_last; | ||
945 | reg_errcode_t ret; | ||
946 | re_dfastate_t **sifted_states; | ||
947 | re_dfastate_t **lim_states = NULL; | ||
948 | re_sift_context_t sctx; | ||
949 | #ifdef DEBUG | ||
950 | assert (mctx->state_log != NULL); | ||
951 | #endif | ||
952 | match_last = mctx->match_last; | ||
953 | halt_node = mctx->last_node; | ||
954 | |||
955 | /* Avoid overflow. */ | ||
956 | if (BE (SIZE_MAX / sizeof (re_dfastate_t *) <= match_last, 0)) | ||
957 | return REG_ESPACE; | ||
958 | |||
959 | sifted_states = re_malloc (re_dfastate_t *, match_last + 1); | ||
960 | if (BE (sifted_states == NULL, 0)) | ||
961 | { | ||
962 | ret = REG_ESPACE; | ||
963 | goto free_return; | ||
964 | } | ||
965 | if (dfa->nbackref) | ||
966 | { | ||
967 | lim_states = re_malloc (re_dfastate_t *, match_last + 1); | ||
968 | if (BE (lim_states == NULL, 0)) | ||
969 | { | ||
970 | ret = REG_ESPACE; | ||
971 | goto free_return; | ||
972 | } | ||
973 | while (1) | ||
974 | { | ||
975 | memset (lim_states, '\0', | ||
976 | sizeof (re_dfastate_t *) * (match_last + 1)); | ||
977 | sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, | ||
978 | match_last); | ||
979 | ret = sift_states_backward (mctx, &sctx); | ||
980 | re_node_set_free (&sctx.limits); | ||
981 | if (BE (ret != REG_NOERROR, 0)) | ||
982 | goto free_return; | ||
983 | if (sifted_states[0] != NULL || lim_states[0] != NULL) | ||
984 | break; | ||
985 | do | ||
986 | { | ||
987 | --match_last; | ||
988 | if (match_last < 0) | ||
989 | { | ||
990 | ret = REG_NOMATCH; | ||
991 | goto free_return; | ||
992 | } | ||
993 | } while (mctx->state_log[match_last] == NULL | ||
994 | || !mctx->state_log[match_last]->halt); | ||
995 | halt_node = check_halt_state_context (mctx, | ||
996 | mctx->state_log[match_last], | ||
997 | match_last); | ||
998 | } | ||
999 | ret = merge_state_array (dfa, sifted_states, lim_states, | ||
1000 | match_last + 1); | ||
1001 | re_free (lim_states); | ||
1002 | lim_states = NULL; | ||
1003 | if (BE (ret != REG_NOERROR, 0)) | ||
1004 | goto free_return; | ||
1005 | } | ||
1006 | else | ||
1007 | { | ||
1008 | sift_ctx_init (&sctx, sifted_states, lim_states, halt_node, match_last); | ||
1009 | ret = sift_states_backward (mctx, &sctx); | ||
1010 | re_node_set_free (&sctx.limits); | ||
1011 | if (BE (ret != REG_NOERROR, 0)) | ||
1012 | goto free_return; | ||
1013 | if (sifted_states[0] == NULL) | ||
1014 | { | ||
1015 | ret = REG_NOMATCH; | ||
1016 | goto free_return; | ||
1017 | } | ||
1018 | } | ||
1019 | re_free (mctx->state_log); | ||
1020 | mctx->state_log = sifted_states; | ||
1021 | sifted_states = NULL; | ||
1022 | mctx->last_node = halt_node; | ||
1023 | mctx->match_last = match_last; | ||
1024 | ret = REG_NOERROR; | ||
1025 | free_return: | ||
1026 | re_free (sifted_states); | ||
1027 | re_free (lim_states); | ||
1028 | return ret; | ||
1029 | } | ||
1030 | |||
1031 | /* Acquire an initial state and return it. | ||
1032 | We must select appropriate initial state depending on the context, | ||
1033 | since initial states may have constraints like "\<", "^", etc.. */ | ||
1034 | |||
1035 | static inline re_dfastate_t * | ||
1036 | __attribute ((always_inline)) internal_function | ||
1037 | acquire_init_state_context (reg_errcode_t *err, const re_match_context_t *mctx, | ||
1038 | int idx) | ||
1039 | { | ||
1040 | const re_dfa_t *const dfa = mctx->dfa; | ||
1041 | if (dfa->init_state->has_constraint) | ||
1042 | { | ||
1043 | unsigned int context; | ||
1044 | context = re_string_context_at (&mctx->input, idx - 1, mctx->eflags); | ||
1045 | if (IS_WORD_CONTEXT (context)) | ||
1046 | return dfa->init_state_word; | ||
1047 | else if (IS_ORDINARY_CONTEXT (context)) | ||
1048 | return dfa->init_state; | ||
1049 | else if (IS_BEGBUF_CONTEXT (context) && IS_NEWLINE_CONTEXT (context)) | ||
1050 | return dfa->init_state_begbuf; | ||
1051 | else if (IS_NEWLINE_CONTEXT (context)) | ||
1052 | return dfa->init_state_nl; | ||
1053 | else if (IS_BEGBUF_CONTEXT (context)) | ||
1054 | { | ||
1055 | /* It is relatively rare case, then calculate on demand. */ | ||
1056 | return re_acquire_state_context (err, dfa, | ||
1057 | dfa->init_state->entrance_nodes, | ||
1058 | context); | ||
1059 | } | ||
1060 | else | ||
1061 | /* Must not happen? */ | ||
1062 | return dfa->init_state; | ||
1063 | } | ||
1064 | else | ||
1065 | return dfa->init_state; | ||
1066 | } | ||
1067 | |||
1068 | /* Check whether the regular expression match input string INPUT or not, | ||
1069 | and return the index where the matching end, return -1 if not match, | ||
1070 | or return -2 in case of an error. | ||
1071 | FL_LONGEST_MATCH means we want the POSIX longest matching. | ||
1072 | If P_MATCH_FIRST is not NULL, and the match fails, it is set to the | ||
1073 | next place where we may want to try matching. | ||
1074 | Note that the matcher assume that the matching starts from the current | ||
1075 | index of the buffer. */ | ||
1076 | |||
1077 | static int | ||
1078 | internal_function | ||
1079 | check_matching (re_match_context_t *mctx, int fl_longest_match, | ||
1080 | int *p_match_first) | ||
1081 | { | ||
1082 | const re_dfa_t *const dfa = mctx->dfa; | ||
1083 | reg_errcode_t err; | ||
1084 | int match = 0; | ||
1085 | int match_last = -1; | ||
1086 | int cur_str_idx = re_string_cur_idx (&mctx->input); | ||
1087 | re_dfastate_t *cur_state; | ||
1088 | int at_init_state = p_match_first != NULL; | ||
1089 | int next_start_idx = cur_str_idx; | ||
1090 | |||
1091 | err = REG_NOERROR; | ||
1092 | cur_state = acquire_init_state_context (&err, mctx, cur_str_idx); | ||
1093 | /* An initial state must not be NULL (invalid). */ | ||
1094 | if (BE (cur_state == NULL, 0)) | ||
1095 | { | ||
1096 | assert (err == REG_ESPACE); | ||
1097 | return -2; | ||
1098 | } | ||
1099 | |||
1100 | if (mctx->state_log != NULL) | ||
1101 | { | ||
1102 | mctx->state_log[cur_str_idx] = cur_state; | ||
1103 | |||
1104 | /* Check OP_OPEN_SUBEXP in the initial state in case that we use them | ||
1105 | later. E.g. Processing back references. */ | ||
1106 | if (BE (dfa->nbackref, 0)) | ||
1107 | { | ||
1108 | at_init_state = 0; | ||
1109 | err = check_subexp_matching_top (mctx, &cur_state->nodes, 0); | ||
1110 | if (BE (err != REG_NOERROR, 0)) | ||
1111 | return err; | ||
1112 | |||
1113 | if (cur_state->has_backref) | ||
1114 | { | ||
1115 | err = transit_state_bkref (mctx, &cur_state->nodes); | ||
1116 | if (BE (err != REG_NOERROR, 0)) | ||
1117 | return err; | ||
1118 | } | ||
1119 | } | ||
1120 | } | ||
1121 | |||
1122 | /* If the RE accepts NULL string. */ | ||
1123 | if (BE (cur_state->halt, 0)) | ||
1124 | { | ||
1125 | if (!cur_state->has_constraint | ||
1126 | || check_halt_state_context (mctx, cur_state, cur_str_idx)) | ||
1127 | { | ||
1128 | if (!fl_longest_match) | ||
1129 | return cur_str_idx; | ||
1130 | else | ||
1131 | { | ||
1132 | match_last = cur_str_idx; | ||
1133 | match = 1; | ||
1134 | } | ||
1135 | } | ||
1136 | } | ||
1137 | |||
1138 | while (!re_string_eoi (&mctx->input)) | ||
1139 | { | ||
1140 | re_dfastate_t *old_state = cur_state; | ||
1141 | int next_char_idx = re_string_cur_idx (&mctx->input) + 1; | ||
1142 | |||
1143 | if (BE (next_char_idx >= mctx->input.bufs_len, 0) | ||
1144 | || (BE (next_char_idx >= mctx->input.valid_len, 0) | ||
1145 | && mctx->input.valid_len < mctx->input.len)) | ||
1146 | { | ||
1147 | err = extend_buffers (mctx); | ||
1148 | if (BE (err != REG_NOERROR, 0)) | ||
1149 | { | ||
1150 | assert (err == REG_ESPACE); | ||
1151 | return -2; | ||
1152 | } | ||
1153 | } | ||
1154 | |||
1155 | cur_state = transit_state (&err, mctx, cur_state); | ||
1156 | if (mctx->state_log != NULL) | ||
1157 | cur_state = merge_state_with_log (&err, mctx, cur_state); | ||
1158 | |||
1159 | if (cur_state == NULL) | ||
1160 | { | ||
1161 | /* Reached the invalid state or an error. Try to recover a valid | ||
1162 | state using the state log, if available and if we have not | ||
1163 | already found a valid (even if not the longest) match. */ | ||
1164 | if (BE (err != REG_NOERROR, 0)) | ||
1165 | return -2; | ||
1166 | |||
1167 | if (mctx->state_log == NULL | ||
1168 | || (match && !fl_longest_match) | ||
1169 | || (cur_state = find_recover_state (&err, mctx)) == NULL) | ||
1170 | break; | ||
1171 | } | ||
1172 | |||
1173 | if (BE (at_init_state, 0)) | ||
1174 | { | ||
1175 | if (old_state == cur_state) | ||
1176 | next_start_idx = next_char_idx; | ||
1177 | else | ||
1178 | at_init_state = 0; | ||
1179 | } | ||
1180 | |||
1181 | if (cur_state->halt) | ||
1182 | { | ||
1183 | /* Reached a halt state. | ||
1184 | Check the halt state can satisfy the current context. */ | ||
1185 | if (!cur_state->has_constraint | ||
1186 | || check_halt_state_context (mctx, cur_state, | ||
1187 | re_string_cur_idx (&mctx->input))) | ||
1188 | { | ||
1189 | /* We found an appropriate halt state. */ | ||
1190 | match_last = re_string_cur_idx (&mctx->input); | ||
1191 | match = 1; | ||
1192 | |||
1193 | /* We found a match, do not modify match_first below. */ | ||
1194 | p_match_first = NULL; | ||
1195 | if (!fl_longest_match) | ||
1196 | break; | ||
1197 | } | ||
1198 | } | ||
1199 | } | ||
1200 | |||
1201 | if (p_match_first) | ||
1202 | *p_match_first += next_start_idx; | ||
1203 | |||
1204 | return match_last; | ||
1205 | } | ||
1206 | |||
1207 | /* Check NODE match the current context. */ | ||
1208 | |||
1209 | static int | ||
1210 | internal_function | ||
1211 | check_halt_node_context (const re_dfa_t *dfa, int node, unsigned int context) | ||
1212 | { | ||
1213 | re_token_type_t type = dfa->nodes[node].type; | ||
1214 | unsigned int constraint = dfa->nodes[node].constraint; | ||
1215 | if (type != END_OF_RE) | ||
1216 | return 0; | ||
1217 | if (!constraint) | ||
1218 | return 1; | ||
1219 | if (NOT_SATISFY_NEXT_CONSTRAINT (constraint, context)) | ||
1220 | return 0; | ||
1221 | return 1; | ||
1222 | } | ||
1223 | |||
1224 | /* Check the halt state STATE match the current context. | ||
1225 | Return 0 if not match, if the node, STATE has, is a halt node and | ||
1226 | match the context, return the node. */ | ||
1227 | |||
1228 | static int | ||
1229 | internal_function | ||
1230 | check_halt_state_context (const re_match_context_t *mctx, | ||
1231 | const re_dfastate_t *state, int idx) | ||
1232 | { | ||
1233 | int i; | ||
1234 | unsigned int context; | ||
1235 | #ifdef DEBUG | ||
1236 | assert (state->halt); | ||
1237 | #endif | ||
1238 | context = re_string_context_at (&mctx->input, idx, mctx->eflags); | ||
1239 | for (i = 0; i < state->nodes.nelem; ++i) | ||
1240 | if (check_halt_node_context (mctx->dfa, state->nodes.elems[i], context)) | ||
1241 | return state->nodes.elems[i]; | ||
1242 | return 0; | ||
1243 | } | ||
1244 | |||
1245 | /* Compute the next node to which "NFA" transit from NODE("NFA" is a NFA | ||
1246 | corresponding to the DFA). | ||
1247 | Return the destination node, and update EPS_VIA_NODES, return -1 in case | ||
1248 | of errors. */ | ||
1249 | |||
1250 | static int | ||
1251 | internal_function | ||
1252 | proceed_next_node (const re_match_context_t *mctx, int nregs, regmatch_t *regs, | ||
1253 | int *pidx, int node, re_node_set *eps_via_nodes, | ||
1254 | struct re_fail_stack_t *fs) | ||
1255 | { | ||
1256 | const re_dfa_t *const dfa = mctx->dfa; | ||
1257 | int i, err; | ||
1258 | if (IS_EPSILON_NODE (dfa->nodes[node].type)) | ||
1259 | { | ||
1260 | re_node_set *cur_nodes = &mctx->state_log[*pidx]->nodes; | ||
1261 | re_node_set *edests = &dfa->edests[node]; | ||
1262 | int dest_node; | ||
1263 | err = re_node_set_insert (eps_via_nodes, node); | ||
1264 | if (BE (err < 0, 0)) | ||
1265 | return -2; | ||
1266 | /* Pick up a valid destination, or return -1 if none is found. */ | ||
1267 | for (dest_node = -1, i = 0; i < edests->nelem; ++i) | ||
1268 | { | ||
1269 | int candidate = edests->elems[i]; | ||
1270 | if (!re_node_set_contains (cur_nodes, candidate)) | ||
1271 | continue; | ||
1272 | if (dest_node == -1) | ||
1273 | dest_node = candidate; | ||
1274 | |||
1275 | else | ||
1276 | { | ||
1277 | /* In order to avoid infinite loop like "(a*)*", return the second | ||
1278 | epsilon-transition if the first was already considered. */ | ||
1279 | if (re_node_set_contains (eps_via_nodes, dest_node)) | ||
1280 | return candidate; | ||
1281 | |||
1282 | /* Otherwise, push the second epsilon-transition on the fail stack. */ | ||
1283 | else if (fs != NULL | ||
1284 | && push_fail_stack (fs, *pidx, candidate, nregs, regs, | ||
1285 | eps_via_nodes)) | ||
1286 | return -2; | ||
1287 | |||
1288 | /* We know we are going to exit. */ | ||
1289 | break; | ||
1290 | } | ||
1291 | } | ||
1292 | return dest_node; | ||
1293 | } | ||
1294 | else | ||
1295 | { | ||
1296 | int naccepted = 0; | ||
1297 | re_token_type_t type = dfa->nodes[node].type; | ||
1298 | |||
1299 | #ifdef RE_ENABLE_I18N | ||
1300 | if (dfa->nodes[node].accept_mb) | ||
1301 | naccepted = check_node_accept_bytes (dfa, node, &mctx->input, *pidx); | ||
1302 | else | ||
1303 | #endif /* RE_ENABLE_I18N */ | ||
1304 | if (type == OP_BACK_REF) | ||
1305 | { | ||
1306 | int subexp_idx = dfa->nodes[node].opr.idx + 1; | ||
1307 | naccepted = regs[subexp_idx].rm_eo - regs[subexp_idx].rm_so; | ||
1308 | if (fs != NULL) | ||
1309 | { | ||
1310 | if (regs[subexp_idx].rm_so == -1 || regs[subexp_idx].rm_eo == -1) | ||
1311 | return -1; | ||
1312 | else if (naccepted) | ||
1313 | { | ||
1314 | char *buf = (char *) re_string_get_buffer (&mctx->input); | ||
1315 | if (memcmp (buf + regs[subexp_idx].rm_so, buf + *pidx, | ||
1316 | naccepted) != 0) | ||
1317 | return -1; | ||
1318 | } | ||
1319 | } | ||
1320 | |||
1321 | if (naccepted == 0) | ||
1322 | { | ||
1323 | int dest_node; | ||
1324 | err = re_node_set_insert (eps_via_nodes, node); | ||
1325 | if (BE (err < 0, 0)) | ||
1326 | return -2; | ||
1327 | dest_node = dfa->edests[node].elems[0]; | ||
1328 | if (re_node_set_contains (&mctx->state_log[*pidx]->nodes, | ||
1329 | dest_node)) | ||
1330 | return dest_node; | ||
1331 | } | ||
1332 | } | ||
1333 | |||
1334 | if (naccepted != 0 | ||
1335 | || check_node_accept (mctx, dfa->nodes + node, *pidx)) | ||
1336 | { | ||
1337 | int dest_node = dfa->nexts[node]; | ||
1338 | *pidx = (naccepted == 0) ? *pidx + 1 : *pidx + naccepted; | ||
1339 | if (fs && (*pidx > mctx->match_last || mctx->state_log[*pidx] == NULL | ||
1340 | || !re_node_set_contains (&mctx->state_log[*pidx]->nodes, | ||
1341 | dest_node))) | ||
1342 | return -1; | ||
1343 | re_node_set_empty (eps_via_nodes); | ||
1344 | return dest_node; | ||
1345 | } | ||
1346 | } | ||
1347 | return -1; | ||
1348 | } | ||
1349 | |||
1350 | static reg_errcode_t | ||
1351 | internal_function | ||
1352 | push_fail_stack (struct re_fail_stack_t *fs, int str_idx, int dest_node, | ||
1353 | int nregs, regmatch_t *regs, re_node_set *eps_via_nodes) | ||
1354 | { | ||
1355 | reg_errcode_t err; | ||
1356 | int num = fs->num++; | ||
1357 | if (fs->num == fs->alloc) | ||
1358 | { | ||
1359 | struct re_fail_stack_ent_t *new_array; | ||
1360 | new_array = realloc (fs->stack, (sizeof (struct re_fail_stack_ent_t) | ||
1361 | * fs->alloc * 2)); | ||
1362 | if (new_array == NULL) | ||
1363 | return REG_ESPACE; | ||
1364 | fs->alloc *= 2; | ||
1365 | fs->stack = new_array; | ||
1366 | } | ||
1367 | fs->stack[num].idx = str_idx; | ||
1368 | fs->stack[num].node = dest_node; | ||
1369 | fs->stack[num].regs = re_malloc (regmatch_t, nregs); | ||
1370 | if (fs->stack[num].regs == NULL) | ||
1371 | return REG_ESPACE; | ||
1372 | memcpy (fs->stack[num].regs, regs, sizeof (regmatch_t) * nregs); | ||
1373 | err = re_node_set_init_copy (&fs->stack[num].eps_via_nodes, eps_via_nodes); | ||
1374 | return err; | ||
1375 | } | ||
1376 | |||
1377 | static int | ||
1378 | internal_function | ||
1379 | pop_fail_stack (struct re_fail_stack_t *fs, int *pidx, int nregs, | ||
1380 | regmatch_t *regs, re_node_set *eps_via_nodes) | ||
1381 | { | ||
1382 | int num = --fs->num; | ||
1383 | assert (num >= 0); | ||
1384 | *pidx = fs->stack[num].idx; | ||
1385 | memcpy (regs, fs->stack[num].regs, sizeof (regmatch_t) * nregs); | ||
1386 | re_node_set_free (eps_via_nodes); | ||
1387 | re_free (fs->stack[num].regs); | ||
1388 | *eps_via_nodes = fs->stack[num].eps_via_nodes; | ||
1389 | return fs->stack[num].node; | ||
1390 | } | ||
1391 | |||
1392 | /* Set the positions where the subexpressions are starts/ends to registers | ||
1393 | PMATCH. | ||
1394 | Note: We assume that pmatch[0] is already set, and | ||
1395 | pmatch[i].rm_so == pmatch[i].rm_eo == -1 for 0 < i < nmatch. */ | ||
1396 | |||
1397 | static reg_errcode_t | ||
1398 | internal_function | ||
1399 | set_regs (const regex_t *preg, const re_match_context_t *mctx, size_t nmatch, | ||
1400 | regmatch_t *pmatch, int fl_backtrack) | ||
1401 | { | ||
1402 | const re_dfa_t *dfa = (const re_dfa_t *) preg->buffer; | ||
1403 | int idx, cur_node; | ||
1404 | re_node_set eps_via_nodes; | ||
1405 | struct re_fail_stack_t *fs; | ||
1406 | struct re_fail_stack_t fs_body = { 0, 2, NULL }; | ||
1407 | regmatch_t *prev_idx_match; | ||
1408 | int prev_idx_match_malloced = 0; | ||
1409 | |||
1410 | #ifdef DEBUG | ||
1411 | assert (nmatch > 1); | ||
1412 | assert (mctx->state_log != NULL); | ||
1413 | #endif | ||
1414 | if (fl_backtrack) | ||
1415 | { | ||
1416 | fs = &fs_body; | ||
1417 | fs->stack = re_malloc (struct re_fail_stack_ent_t, fs->alloc); | ||
1418 | if (fs->stack == NULL) | ||
1419 | return REG_ESPACE; | ||
1420 | } | ||
1421 | else | ||
1422 | fs = NULL; | ||
1423 | |||
1424 | cur_node = dfa->init_node; | ||
1425 | re_node_set_init_empty (&eps_via_nodes); | ||
1426 | |||
1427 | #ifdef HAVE_ALLOCA | ||
1428 | if (__libc_use_alloca (nmatch * sizeof (regmatch_t))) | ||
1429 | prev_idx_match = (regmatch_t *) alloca (nmatch * sizeof (regmatch_t)); | ||
1430 | else | ||
1431 | #endif | ||
1432 | { | ||
1433 | prev_idx_match = re_malloc (regmatch_t, nmatch); | ||
1434 | if (prev_idx_match == NULL) | ||
1435 | { | ||
1436 | free_fail_stack_return (fs); | ||
1437 | return REG_ESPACE; | ||
1438 | } | ||
1439 | prev_idx_match_malloced = 1; | ||
1440 | } | ||
1441 | memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); | ||
1442 | |||
1443 | for (idx = pmatch[0].rm_so; idx <= pmatch[0].rm_eo ;) | ||
1444 | { | ||
1445 | update_regs (dfa, pmatch, prev_idx_match, cur_node, idx, nmatch); | ||
1446 | |||
1447 | if (idx == pmatch[0].rm_eo && cur_node == mctx->last_node) | ||
1448 | { | ||
1449 | int reg_idx; | ||
1450 | if (fs) | ||
1451 | { | ||
1452 | for (reg_idx = 0; reg_idx < nmatch; ++reg_idx) | ||
1453 | if (pmatch[reg_idx].rm_so > -1 && pmatch[reg_idx].rm_eo == -1) | ||
1454 | break; | ||
1455 | if (reg_idx == nmatch) | ||
1456 | { | ||
1457 | re_node_set_free (&eps_via_nodes); | ||
1458 | if (prev_idx_match_malloced) | ||
1459 | re_free (prev_idx_match); | ||
1460 | return free_fail_stack_return (fs); | ||
1461 | } | ||
1462 | cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, | ||
1463 | &eps_via_nodes); | ||
1464 | } | ||
1465 | else | ||
1466 | { | ||
1467 | re_node_set_free (&eps_via_nodes); | ||
1468 | if (prev_idx_match_malloced) | ||
1469 | re_free (prev_idx_match); | ||
1470 | return REG_NOERROR; | ||
1471 | } | ||
1472 | } | ||
1473 | |||
1474 | /* Proceed to next node. */ | ||
1475 | cur_node = proceed_next_node (mctx, nmatch, pmatch, &idx, cur_node, | ||
1476 | &eps_via_nodes, fs); | ||
1477 | |||
1478 | if (BE (cur_node < 0, 0)) | ||
1479 | { | ||
1480 | if (BE (cur_node == -2, 0)) | ||
1481 | { | ||
1482 | re_node_set_free (&eps_via_nodes); | ||
1483 | if (prev_idx_match_malloced) | ||
1484 | re_free (prev_idx_match); | ||
1485 | free_fail_stack_return (fs); | ||
1486 | return REG_ESPACE; | ||
1487 | } | ||
1488 | if (fs) | ||
1489 | cur_node = pop_fail_stack (fs, &idx, nmatch, pmatch, | ||
1490 | &eps_via_nodes); | ||
1491 | else | ||
1492 | { | ||
1493 | re_node_set_free (&eps_via_nodes); | ||
1494 | if (prev_idx_match_malloced) | ||
1495 | re_free (prev_idx_match); | ||
1496 | return REG_NOMATCH; | ||
1497 | } | ||
1498 | } | ||
1499 | } | ||
1500 | re_node_set_free (&eps_via_nodes); | ||
1501 | if (prev_idx_match_malloced) | ||
1502 | re_free (prev_idx_match); | ||
1503 | return free_fail_stack_return (fs); | ||
1504 | } | ||
1505 | |||
1506 | static reg_errcode_t | ||
1507 | internal_function | ||
1508 | free_fail_stack_return (struct re_fail_stack_t *fs) | ||
1509 | { | ||
1510 | if (fs) | ||
1511 | { | ||
1512 | int fs_idx; | ||
1513 | for (fs_idx = 0; fs_idx < fs->num; ++fs_idx) | ||
1514 | { | ||
1515 | re_node_set_free (&fs->stack[fs_idx].eps_via_nodes); | ||
1516 | re_free (fs->stack[fs_idx].regs); | ||
1517 | } | ||
1518 | re_free (fs->stack); | ||
1519 | } | ||
1520 | return REG_NOERROR; | ||
1521 | } | ||
1522 | |||
1523 | static void | ||
1524 | internal_function | ||
1525 | update_regs (const re_dfa_t *dfa, regmatch_t *pmatch, | ||
1526 | regmatch_t *prev_idx_match, int cur_node, int cur_idx, int nmatch) | ||
1527 | { | ||
1528 | int type = dfa->nodes[cur_node].type; | ||
1529 | if (type == OP_OPEN_SUBEXP) | ||
1530 | { | ||
1531 | int reg_num = dfa->nodes[cur_node].opr.idx + 1; | ||
1532 | |||
1533 | /* We are at the first node of this sub expression. */ | ||
1534 | if (reg_num < nmatch) | ||
1535 | { | ||
1536 | pmatch[reg_num].rm_so = cur_idx; | ||
1537 | pmatch[reg_num].rm_eo = -1; | ||
1538 | } | ||
1539 | } | ||
1540 | else if (type == OP_CLOSE_SUBEXP) | ||
1541 | { | ||
1542 | int reg_num = dfa->nodes[cur_node].opr.idx + 1; | ||
1543 | if (reg_num < nmatch) | ||
1544 | { | ||
1545 | /* We are at the last node of this sub expression. */ | ||
1546 | if (pmatch[reg_num].rm_so < cur_idx) | ||
1547 | { | ||
1548 | pmatch[reg_num].rm_eo = cur_idx; | ||
1549 | /* This is a non-empty match or we are not inside an optional | ||
1550 | subexpression. Accept this right away. */ | ||
1551 | memcpy (prev_idx_match, pmatch, sizeof (regmatch_t) * nmatch); | ||
1552 | } | ||
1553 | else | ||
1554 | { | ||
1555 | if (dfa->nodes[cur_node].opt_subexp | ||
1556 | && prev_idx_match[reg_num].rm_so != -1) | ||
1557 | /* We transited through an empty match for an optional | ||
1558 | subexpression, like (a?)*, and this is not the subexp's | ||
1559 | first match. Copy back the old content of the registers | ||
1560 | so that matches of an inner subexpression are undone as | ||
1561 | well, like in ((a?))*. */ | ||
1562 | memcpy (pmatch, prev_idx_match, sizeof (regmatch_t) * nmatch); | ||
1563 | else | ||
1564 | /* We completed a subexpression, but it may be part of | ||
1565 | an optional one, so do not update PREV_IDX_MATCH. */ | ||
1566 | pmatch[reg_num].rm_eo = cur_idx; | ||
1567 | } | ||
1568 | } | ||
1569 | } | ||
1570 | } | ||
1571 | |||
1572 | /* This function checks the STATE_LOG from the SCTX->last_str_idx to 0 | ||
1573 | and sift the nodes in each states according to the following rules. | ||
1574 | Updated state_log will be wrote to STATE_LOG. | ||
1575 | |||
1576 | Rules: We throw away the Node `a' in the STATE_LOG[STR_IDX] if... | ||
1577 | 1. When STR_IDX == MATCH_LAST(the last index in the state_log): | ||
1578 | If `a' isn't the LAST_NODE and `a' can't epsilon transit to | ||
1579 | the LAST_NODE, we throw away the node `a'. | ||
1580 | 2. When 0 <= STR_IDX < MATCH_LAST and `a' accepts | ||
1581 | string `s' and transit to `b': | ||
1582 | i. If 'b' isn't in the STATE_LOG[STR_IDX+strlen('s')], we throw | ||
1583 | away the node `a'. | ||
1584 | ii. If 'b' is in the STATE_LOG[STR_IDX+strlen('s')] but 'b' is | ||
1585 | thrown away, we throw away the node `a'. | ||
1586 | 3. When 0 <= STR_IDX < MATCH_LAST and 'a' epsilon transit to 'b': | ||
1587 | i. If 'b' isn't in the STATE_LOG[STR_IDX], we throw away the | ||
1588 | node `a'. | ||
1589 | ii. If 'b' is in the STATE_LOG[STR_IDX] but 'b' is thrown away, | ||
1590 | we throw away the node `a'. */ | ||
1591 | |||
1592 | #define STATE_NODE_CONTAINS(state,node) \ | ||
1593 | ((state) != NULL && re_node_set_contains (&(state)->nodes, node)) | ||
1594 | |||
1595 | static reg_errcode_t | ||
1596 | internal_function | ||
1597 | sift_states_backward (const re_match_context_t *mctx, re_sift_context_t *sctx) | ||
1598 | { | ||
1599 | reg_errcode_t err; | ||
1600 | int null_cnt = 0; | ||
1601 | int str_idx = sctx->last_str_idx; | ||
1602 | re_node_set cur_dest; | ||
1603 | |||
1604 | #ifdef DEBUG | ||
1605 | assert (mctx->state_log != NULL && mctx->state_log[str_idx] != NULL); | ||
1606 | #endif | ||
1607 | |||
1608 | /* Build sifted state_log[str_idx]. It has the nodes which can epsilon | ||
1609 | transit to the last_node and the last_node itself. */ | ||
1610 | err = re_node_set_init_1 (&cur_dest, sctx->last_node); | ||
1611 | if (BE (err != REG_NOERROR, 0)) | ||
1612 | return err; | ||
1613 | err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); | ||
1614 | if (BE (err != REG_NOERROR, 0)) | ||
1615 | goto free_return; | ||
1616 | |||
1617 | /* Then check each states in the state_log. */ | ||
1618 | while (str_idx > 0) | ||
1619 | { | ||
1620 | /* Update counters. */ | ||
1621 | null_cnt = (sctx->sifted_states[str_idx] == NULL) ? null_cnt + 1 : 0; | ||
1622 | if (null_cnt > mctx->max_mb_elem_len) | ||
1623 | { | ||
1624 | memset (sctx->sifted_states, '\0', | ||
1625 | sizeof (re_dfastate_t *) * str_idx); | ||
1626 | re_node_set_free (&cur_dest); | ||
1627 | return REG_NOERROR; | ||
1628 | } | ||
1629 | re_node_set_empty (&cur_dest); | ||
1630 | --str_idx; | ||
1631 | |||
1632 | if (mctx->state_log[str_idx]) | ||
1633 | { | ||
1634 | err = build_sifted_states (mctx, sctx, str_idx, &cur_dest); | ||
1635 | if (BE (err != REG_NOERROR, 0)) | ||
1636 | goto free_return; | ||
1637 | } | ||
1638 | |||
1639 | /* Add all the nodes which satisfy the following conditions: | ||
1640 | - It can epsilon transit to a node in CUR_DEST. | ||
1641 | - It is in CUR_SRC. | ||
1642 | And update state_log. */ | ||
1643 | err = update_cur_sifted_state (mctx, sctx, str_idx, &cur_dest); | ||
1644 | if (BE (err != REG_NOERROR, 0)) | ||
1645 | goto free_return; | ||
1646 | } | ||
1647 | err = REG_NOERROR; | ||
1648 | free_return: | ||
1649 | re_node_set_free (&cur_dest); | ||
1650 | return err; | ||
1651 | } | ||
1652 | |||
1653 | static reg_errcode_t | ||
1654 | internal_function | ||
1655 | build_sifted_states (const re_match_context_t *mctx, re_sift_context_t *sctx, | ||
1656 | int str_idx, re_node_set *cur_dest) | ||
1657 | { | ||
1658 | const re_dfa_t *const dfa = mctx->dfa; | ||
1659 | const re_node_set *cur_src = &mctx->state_log[str_idx]->non_eps_nodes; | ||
1660 | int i; | ||
1661 | |||
1662 | /* Then build the next sifted state. | ||
1663 | We build the next sifted state on `cur_dest', and update | ||
1664 | `sifted_states[str_idx]' with `cur_dest'. | ||
1665 | Note: | ||
1666 | `cur_dest' is the sifted state from `state_log[str_idx + 1]'. | ||
1667 | `cur_src' points the node_set of the old `state_log[str_idx]' | ||
1668 | (with the epsilon nodes pre-filtered out). */ | ||
1669 | for (i = 0; i < cur_src->nelem; i++) | ||
1670 | { | ||
1671 | int prev_node = cur_src->elems[i]; | ||
1672 | int naccepted = 0; | ||
1673 | int ret; | ||
1674 | |||
1675 | #ifdef DEBUG | ||
1676 | re_token_type_t type = dfa->nodes[prev_node].type; | ||
1677 | assert (!IS_EPSILON_NODE (type)); | ||
1678 | #endif | ||
1679 | #ifdef RE_ENABLE_I18N | ||
1680 | /* If the node may accept `multi byte'. */ | ||
1681 | if (dfa->nodes[prev_node].accept_mb) | ||
1682 | naccepted = sift_states_iter_mb (mctx, sctx, prev_node, | ||
1683 | str_idx, sctx->last_str_idx); | ||
1684 | #endif /* RE_ENABLE_I18N */ | ||
1685 | |||
1686 | /* We don't check backreferences here. | ||
1687 | See update_cur_sifted_state(). */ | ||
1688 | if (!naccepted | ||
1689 | && check_node_accept (mctx, dfa->nodes + prev_node, str_idx) | ||
1690 | && STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + 1], | ||
1691 | dfa->nexts[prev_node])) | ||
1692 | naccepted = 1; | ||
1693 | |||
1694 | if (naccepted == 0) | ||
1695 | continue; | ||
1696 | |||
1697 | if (sctx->limits.nelem) | ||
1698 | { | ||
1699 | int to_idx = str_idx + naccepted; | ||
1700 | if (check_dst_limits (mctx, &sctx->limits, | ||
1701 | dfa->nexts[prev_node], to_idx, | ||
1702 | prev_node, str_idx)) | ||
1703 | continue; | ||
1704 | } | ||
1705 | ret = re_node_set_insert (cur_dest, prev_node); | ||
1706 | if (BE (ret == -1, 0)) | ||
1707 | return REG_ESPACE; | ||
1708 | } | ||
1709 | |||
1710 | return REG_NOERROR; | ||
1711 | } | ||
1712 | |||
1713 | /* Helper functions. */ | ||
1714 | |||
1715 | static reg_errcode_t | ||
1716 | internal_function | ||
1717 | clean_state_log_if_needed (re_match_context_t *mctx, int next_state_log_idx) | ||
1718 | { | ||
1719 | int top = mctx->state_log_top; | ||
1720 | |||
1721 | if (next_state_log_idx >= mctx->input.bufs_len | ||
1722 | || (next_state_log_idx >= mctx->input.valid_len | ||
1723 | && mctx->input.valid_len < mctx->input.len)) | ||
1724 | { | ||
1725 | reg_errcode_t err; | ||
1726 | err = extend_buffers (mctx); | ||
1727 | if (BE (err != REG_NOERROR, 0)) | ||
1728 | return err; | ||
1729 | } | ||
1730 | |||
1731 | if (top < next_state_log_idx) | ||
1732 | { | ||
1733 | memset (mctx->state_log + top + 1, '\0', | ||
1734 | sizeof (re_dfastate_t *) * (next_state_log_idx - top)); | ||
1735 | mctx->state_log_top = next_state_log_idx; | ||
1736 | } | ||
1737 | return REG_NOERROR; | ||
1738 | } | ||
1739 | |||
1740 | static reg_errcode_t | ||
1741 | internal_function | ||
1742 | merge_state_array (const re_dfa_t *dfa, re_dfastate_t **dst, | ||
1743 | re_dfastate_t **src, int num) | ||
1744 | { | ||
1745 | int st_idx; | ||
1746 | reg_errcode_t err; | ||
1747 | for (st_idx = 0; st_idx < num; ++st_idx) | ||
1748 | { | ||
1749 | if (dst[st_idx] == NULL) | ||
1750 | dst[st_idx] = src[st_idx]; | ||
1751 | else if (src[st_idx] != NULL) | ||
1752 | { | ||
1753 | re_node_set merged_set; | ||
1754 | err = re_node_set_init_union (&merged_set, &dst[st_idx]->nodes, | ||
1755 | &src[st_idx]->nodes); | ||
1756 | if (BE (err != REG_NOERROR, 0)) | ||
1757 | return err; | ||
1758 | dst[st_idx] = re_acquire_state (&err, dfa, &merged_set); | ||
1759 | re_node_set_free (&merged_set); | ||
1760 | if (BE (err != REG_NOERROR, 0)) | ||
1761 | return err; | ||
1762 | } | ||
1763 | } | ||
1764 | return REG_NOERROR; | ||
1765 | } | ||
1766 | |||
1767 | static reg_errcode_t | ||
1768 | internal_function | ||
1769 | update_cur_sifted_state (const re_match_context_t *mctx, | ||
1770 | re_sift_context_t *sctx, int str_idx, | ||
1771 | re_node_set *dest_nodes) | ||
1772 | { | ||
1773 | const re_dfa_t *const dfa = mctx->dfa; | ||
1774 | reg_errcode_t err = REG_NOERROR; | ||
1775 | const re_node_set *candidates; | ||
1776 | candidates = ((mctx->state_log[str_idx] == NULL) ? NULL | ||
1777 | : &mctx->state_log[str_idx]->nodes); | ||
1778 | |||
1779 | if (dest_nodes->nelem == 0) | ||
1780 | sctx->sifted_states[str_idx] = NULL; | ||
1781 | else | ||
1782 | { | ||
1783 | if (candidates) | ||
1784 | { | ||
1785 | /* At first, add the nodes which can epsilon transit to a node in | ||
1786 | DEST_NODE. */ | ||
1787 | err = add_epsilon_src_nodes (dfa, dest_nodes, candidates); | ||
1788 | if (BE (err != REG_NOERROR, 0)) | ||
1789 | return err; | ||
1790 | |||
1791 | /* Then, check the limitations in the current sift_context. */ | ||
1792 | if (sctx->limits.nelem) | ||
1793 | { | ||
1794 | err = check_subexp_limits (dfa, dest_nodes, candidates, &sctx->limits, | ||
1795 | mctx->bkref_ents, str_idx); | ||
1796 | if (BE (err != REG_NOERROR, 0)) | ||
1797 | return err; | ||
1798 | } | ||
1799 | } | ||
1800 | |||
1801 | sctx->sifted_states[str_idx] = re_acquire_state (&err, dfa, dest_nodes); | ||
1802 | if (BE (err != REG_NOERROR, 0)) | ||
1803 | return err; | ||
1804 | } | ||
1805 | |||
1806 | if (candidates && mctx->state_log[str_idx]->has_backref) | ||
1807 | { | ||
1808 | err = sift_states_bkref (mctx, sctx, str_idx, candidates); | ||
1809 | if (BE (err != REG_NOERROR, 0)) | ||
1810 | return err; | ||
1811 | } | ||
1812 | return REG_NOERROR; | ||
1813 | } | ||
1814 | |||
1815 | static reg_errcode_t | ||
1816 | internal_function | ||
1817 | add_epsilon_src_nodes (const re_dfa_t *dfa, re_node_set *dest_nodes, | ||
1818 | const re_node_set *candidates) | ||
1819 | { | ||
1820 | reg_errcode_t err = REG_NOERROR; | ||
1821 | int i; | ||
1822 | |||
1823 | re_dfastate_t *state = re_acquire_state (&err, dfa, dest_nodes); | ||
1824 | if (BE (err != REG_NOERROR, 0)) | ||
1825 | return err; | ||
1826 | |||
1827 | if (!state->inveclosure.alloc) | ||
1828 | { | ||
1829 | err = re_node_set_alloc (&state->inveclosure, dest_nodes->nelem); | ||
1830 | if (BE (err != REG_NOERROR, 0)) | ||
1831 | return REG_ESPACE; | ||
1832 | for (i = 0; i < dest_nodes->nelem; i++) | ||
1833 | { | ||
1834 | err = re_node_set_merge (&state->inveclosure, | ||
1835 | dfa->inveclosures + dest_nodes->elems[i]); | ||
1836 | if (BE (err != REG_NOERROR, 0)) | ||
1837 | return REG_ESPACE; | ||
1838 | } | ||
1839 | } | ||
1840 | return re_node_set_add_intersect (dest_nodes, candidates, | ||
1841 | &state->inveclosure); | ||
1842 | } | ||
1843 | |||
1844 | static reg_errcode_t | ||
1845 | internal_function | ||
1846 | sub_epsilon_src_nodes (const re_dfa_t *dfa, int node, re_node_set *dest_nodes, | ||
1847 | const re_node_set *candidates) | ||
1848 | { | ||
1849 | int ecl_idx; | ||
1850 | reg_errcode_t err; | ||
1851 | re_node_set *inv_eclosure = dfa->inveclosures + node; | ||
1852 | re_node_set except_nodes; | ||
1853 | re_node_set_init_empty (&except_nodes); | ||
1854 | for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) | ||
1855 | { | ||
1856 | int cur_node = inv_eclosure->elems[ecl_idx]; | ||
1857 | if (cur_node == node) | ||
1858 | continue; | ||
1859 | if (IS_EPSILON_NODE (dfa->nodes[cur_node].type)) | ||
1860 | { | ||
1861 | int edst1 = dfa->edests[cur_node].elems[0]; | ||
1862 | int edst2 = ((dfa->edests[cur_node].nelem > 1) | ||
1863 | ? dfa->edests[cur_node].elems[1] : -1); | ||
1864 | if ((!re_node_set_contains (inv_eclosure, edst1) | ||
1865 | && re_node_set_contains (dest_nodes, edst1)) | ||
1866 | || (edst2 > 0 | ||
1867 | && !re_node_set_contains (inv_eclosure, edst2) | ||
1868 | && re_node_set_contains (dest_nodes, edst2))) | ||
1869 | { | ||
1870 | err = re_node_set_add_intersect (&except_nodes, candidates, | ||
1871 | dfa->inveclosures + cur_node); | ||
1872 | if (BE (err != REG_NOERROR, 0)) | ||
1873 | { | ||
1874 | re_node_set_free (&except_nodes); | ||
1875 | return err; | ||
1876 | } | ||
1877 | } | ||
1878 | } | ||
1879 | } | ||
1880 | for (ecl_idx = 0; ecl_idx < inv_eclosure->nelem; ++ecl_idx) | ||
1881 | { | ||
1882 | int cur_node = inv_eclosure->elems[ecl_idx]; | ||
1883 | if (!re_node_set_contains (&except_nodes, cur_node)) | ||
1884 | { | ||
1885 | int idx = re_node_set_contains (dest_nodes, cur_node) - 1; | ||
1886 | re_node_set_remove_at (dest_nodes, idx); | ||
1887 | } | ||
1888 | } | ||
1889 | re_node_set_free (&except_nodes); | ||
1890 | return REG_NOERROR; | ||
1891 | } | ||
1892 | |||
1893 | static int | ||
1894 | internal_function | ||
1895 | check_dst_limits (const re_match_context_t *mctx, re_node_set *limits, | ||
1896 | int dst_node, int dst_idx, int src_node, int src_idx) | ||
1897 | { | ||
1898 | const re_dfa_t *const dfa = mctx->dfa; | ||
1899 | int lim_idx, src_pos, dst_pos; | ||
1900 | |||
1901 | int dst_bkref_idx = search_cur_bkref_entry (mctx, dst_idx); | ||
1902 | int src_bkref_idx = search_cur_bkref_entry (mctx, src_idx); | ||
1903 | for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) | ||
1904 | { | ||
1905 | int subexp_idx; | ||
1906 | struct re_backref_cache_entry *ent; | ||
1907 | ent = mctx->bkref_ents + limits->elems[lim_idx]; | ||
1908 | subexp_idx = dfa->nodes[ent->node].opr.idx; | ||
1909 | |||
1910 | dst_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], | ||
1911 | subexp_idx, dst_node, dst_idx, | ||
1912 | dst_bkref_idx); | ||
1913 | src_pos = check_dst_limits_calc_pos (mctx, limits->elems[lim_idx], | ||
1914 | subexp_idx, src_node, src_idx, | ||
1915 | src_bkref_idx); | ||
1916 | |||
1917 | /* In case of: | ||
1918 | <src> <dst> ( <subexp> ) | ||
1919 | ( <subexp> ) <src> <dst> | ||
1920 | ( <subexp1> <src> <subexp2> <dst> <subexp3> ) */ | ||
1921 | if (src_pos == dst_pos) | ||
1922 | continue; /* This is unrelated limitation. */ | ||
1923 | else | ||
1924 | return 1; | ||
1925 | } | ||
1926 | return 0; | ||
1927 | } | ||
1928 | |||
1929 | static int | ||
1930 | internal_function | ||
1931 | check_dst_limits_calc_pos_1 (const re_match_context_t *mctx, int boundaries, | ||
1932 | int subexp_idx, int from_node, int bkref_idx) | ||
1933 | { | ||
1934 | const re_dfa_t *const dfa = mctx->dfa; | ||
1935 | const re_node_set *eclosures = dfa->eclosures + from_node; | ||
1936 | int node_idx; | ||
1937 | |||
1938 | /* Else, we are on the boundary: examine the nodes on the epsilon | ||
1939 | closure. */ | ||
1940 | for (node_idx = 0; node_idx < eclosures->nelem; ++node_idx) | ||
1941 | { | ||
1942 | int node = eclosures->elems[node_idx]; | ||
1943 | switch (dfa->nodes[node].type) | ||
1944 | { | ||
1945 | case OP_BACK_REF: | ||
1946 | if (bkref_idx != -1) | ||
1947 | { | ||
1948 | struct re_backref_cache_entry *ent = mctx->bkref_ents + bkref_idx; | ||
1949 | do | ||
1950 | { | ||
1951 | int dst, cpos; | ||
1952 | |||
1953 | if (ent->node != node) | ||
1954 | continue; | ||
1955 | |||
1956 | if (subexp_idx < BITSET_WORD_BITS | ||
1957 | && !(ent->eps_reachable_subexps_map | ||
1958 | & ((bitset_word_t) 1 << subexp_idx))) | ||
1959 | continue; | ||
1960 | |||
1961 | /* Recurse trying to reach the OP_OPEN_SUBEXP and | ||
1962 | OP_CLOSE_SUBEXP cases below. But, if the | ||
1963 | destination node is the same node as the source | ||
1964 | node, don't recurse because it would cause an | ||
1965 | infinite loop: a regex that exhibits this behavior | ||
1966 | is ()\1*\1* */ | ||
1967 | dst = dfa->edests[node].elems[0]; | ||
1968 | if (dst == from_node) | ||
1969 | { | ||
1970 | if (boundaries & 1) | ||
1971 | return -1; | ||
1972 | else /* if (boundaries & 2) */ | ||
1973 | return 0; | ||
1974 | } | ||
1975 | |||
1976 | cpos = | ||
1977 | check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, | ||
1978 | dst, bkref_idx); | ||
1979 | if (cpos == -1 /* && (boundaries & 1) */) | ||
1980 | return -1; | ||
1981 | if (cpos == 0 && (boundaries & 2)) | ||
1982 | return 0; | ||
1983 | |||
1984 | if (subexp_idx < BITSET_WORD_BITS) | ||
1985 | ent->eps_reachable_subexps_map | ||
1986 | &= ~((bitset_word_t) 1 << subexp_idx); | ||
1987 | } | ||
1988 | while (ent++->more); | ||
1989 | } | ||
1990 | break; | ||
1991 | |||
1992 | case OP_OPEN_SUBEXP: | ||
1993 | if ((boundaries & 1) && subexp_idx == dfa->nodes[node].opr.idx) | ||
1994 | return -1; | ||
1995 | break; | ||
1996 | |||
1997 | case OP_CLOSE_SUBEXP: | ||
1998 | if ((boundaries & 2) && subexp_idx == dfa->nodes[node].opr.idx) | ||
1999 | return 0; | ||
2000 | break; | ||
2001 | |||
2002 | default: | ||
2003 | break; | ||
2004 | } | ||
2005 | } | ||
2006 | |||
2007 | return (boundaries & 2) ? 1 : 0; | ||
2008 | } | ||
2009 | |||
2010 | static int | ||
2011 | internal_function | ||
2012 | check_dst_limits_calc_pos (const re_match_context_t *mctx, int limit, | ||
2013 | int subexp_idx, int from_node, int str_idx, | ||
2014 | int bkref_idx) | ||
2015 | { | ||
2016 | struct re_backref_cache_entry *lim = mctx->bkref_ents + limit; | ||
2017 | int boundaries; | ||
2018 | |||
2019 | /* If we are outside the range of the subexpression, return -1 or 1. */ | ||
2020 | if (str_idx < lim->subexp_from) | ||
2021 | return -1; | ||
2022 | |||
2023 | if (lim->subexp_to < str_idx) | ||
2024 | return 1; | ||
2025 | |||
2026 | /* If we are within the subexpression, return 0. */ | ||
2027 | boundaries = (str_idx == lim->subexp_from); | ||
2028 | boundaries |= (str_idx == lim->subexp_to) << 1; | ||
2029 | if (boundaries == 0) | ||
2030 | return 0; | ||
2031 | |||
2032 | /* Else, examine epsilon closure. */ | ||
2033 | return check_dst_limits_calc_pos_1 (mctx, boundaries, subexp_idx, | ||
2034 | from_node, bkref_idx); | ||
2035 | } | ||
2036 | |||
2037 | /* Check the limitations of sub expressions LIMITS, and remove the nodes | ||
2038 | which are against limitations from DEST_NODES. */ | ||
2039 | |||
2040 | static reg_errcode_t | ||
2041 | internal_function | ||
2042 | check_subexp_limits (const re_dfa_t *dfa, re_node_set *dest_nodes, | ||
2043 | const re_node_set *candidates, re_node_set *limits, | ||
2044 | struct re_backref_cache_entry *bkref_ents, int str_idx) | ||
2045 | { | ||
2046 | reg_errcode_t err; | ||
2047 | int node_idx, lim_idx; | ||
2048 | |||
2049 | for (lim_idx = 0; lim_idx < limits->nelem; ++lim_idx) | ||
2050 | { | ||
2051 | int subexp_idx; | ||
2052 | struct re_backref_cache_entry *ent; | ||
2053 | ent = bkref_ents + limits->elems[lim_idx]; | ||
2054 | |||
2055 | if (str_idx <= ent->subexp_from || ent->str_idx < str_idx) | ||
2056 | continue; /* This is unrelated limitation. */ | ||
2057 | |||
2058 | subexp_idx = dfa->nodes[ent->node].opr.idx; | ||
2059 | if (ent->subexp_to == str_idx) | ||
2060 | { | ||
2061 | int ops_node = -1; | ||
2062 | int cls_node = -1; | ||
2063 | for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) | ||
2064 | { | ||
2065 | int node = dest_nodes->elems[node_idx]; | ||
2066 | re_token_type_t type = dfa->nodes[node].type; | ||
2067 | if (type == OP_OPEN_SUBEXP | ||
2068 | && subexp_idx == dfa->nodes[node].opr.idx) | ||
2069 | ops_node = node; | ||
2070 | else if (type == OP_CLOSE_SUBEXP | ||
2071 | && subexp_idx == dfa->nodes[node].opr.idx) | ||
2072 | cls_node = node; | ||
2073 | } | ||
2074 | |||
2075 | /* Check the limitation of the open subexpression. */ | ||
2076 | /* Note that (ent->subexp_to = str_idx != ent->subexp_from). */ | ||
2077 | if (ops_node >= 0) | ||
2078 | { | ||
2079 | err = sub_epsilon_src_nodes (dfa, ops_node, dest_nodes, | ||
2080 | candidates); | ||
2081 | if (BE (err != REG_NOERROR, 0)) | ||
2082 | return err; | ||
2083 | } | ||
2084 | |||
2085 | /* Check the limitation of the close subexpression. */ | ||
2086 | if (cls_node >= 0) | ||
2087 | for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) | ||
2088 | { | ||
2089 | int node = dest_nodes->elems[node_idx]; | ||
2090 | if (!re_node_set_contains (dfa->inveclosures + node, | ||
2091 | cls_node) | ||
2092 | && !re_node_set_contains (dfa->eclosures + node, | ||
2093 | cls_node)) | ||
2094 | { | ||
2095 | /* It is against this limitation. | ||
2096 | Remove it form the current sifted state. */ | ||
2097 | err = sub_epsilon_src_nodes (dfa, node, dest_nodes, | ||
2098 | candidates); | ||
2099 | if (BE (err != REG_NOERROR, 0)) | ||
2100 | return err; | ||
2101 | --node_idx; | ||
2102 | } | ||
2103 | } | ||
2104 | } | ||
2105 | else /* (ent->subexp_to != str_idx) */ | ||
2106 | { | ||
2107 | for (node_idx = 0; node_idx < dest_nodes->nelem; ++node_idx) | ||
2108 | { | ||
2109 | int node = dest_nodes->elems[node_idx]; | ||
2110 | re_token_type_t type = dfa->nodes[node].type; | ||
2111 | if (type == OP_CLOSE_SUBEXP || type == OP_OPEN_SUBEXP) | ||
2112 | { | ||
2113 | if (subexp_idx != dfa->nodes[node].opr.idx) | ||
2114 | continue; | ||
2115 | /* It is against this limitation. | ||
2116 | Remove it form the current sifted state. */ | ||
2117 | err = sub_epsilon_src_nodes (dfa, node, dest_nodes, | ||
2118 | candidates); | ||
2119 | if (BE (err != REG_NOERROR, 0)) | ||
2120 | return err; | ||
2121 | } | ||
2122 | } | ||
2123 | } | ||
2124 | } | ||
2125 | return REG_NOERROR; | ||
2126 | } | ||
2127 | |||
2128 | static reg_errcode_t | ||
2129 | internal_function | ||
2130 | sift_states_bkref (const re_match_context_t *mctx, re_sift_context_t *sctx, | ||
2131 | int str_idx, const re_node_set *candidates) | ||
2132 | { | ||
2133 | const re_dfa_t *const dfa = mctx->dfa; | ||
2134 | reg_errcode_t err; | ||
2135 | int node_idx, node; | ||
2136 | re_sift_context_t local_sctx; | ||
2137 | int first_idx = search_cur_bkref_entry (mctx, str_idx); | ||
2138 | |||
2139 | if (first_idx == -1) | ||
2140 | return REG_NOERROR; | ||
2141 | |||
2142 | local_sctx.sifted_states = NULL; /* Mark that it hasn't been initialized. */ | ||
2143 | |||
2144 | for (node_idx = 0; node_idx < candidates->nelem; ++node_idx) | ||
2145 | { | ||
2146 | int enabled_idx; | ||
2147 | re_token_type_t type; | ||
2148 | struct re_backref_cache_entry *entry; | ||
2149 | node = candidates->elems[node_idx]; | ||
2150 | type = dfa->nodes[node].type; | ||
2151 | /* Avoid infinite loop for the REs like "()\1+". */ | ||
2152 | if (node == sctx->last_node && str_idx == sctx->last_str_idx) | ||
2153 | continue; | ||
2154 | if (type != OP_BACK_REF) | ||
2155 | continue; | ||
2156 | |||
2157 | entry = mctx->bkref_ents + first_idx; | ||
2158 | enabled_idx = first_idx; | ||
2159 | do | ||
2160 | { | ||
2161 | int subexp_len; | ||
2162 | int to_idx; | ||
2163 | int dst_node; | ||
2164 | int ret; | ||
2165 | re_dfastate_t *cur_state; | ||
2166 | |||
2167 | if (entry->node != node) | ||
2168 | continue; | ||
2169 | subexp_len = entry->subexp_to - entry->subexp_from; | ||
2170 | to_idx = str_idx + subexp_len; | ||
2171 | dst_node = (subexp_len ? dfa->nexts[node] | ||
2172 | : dfa->edests[node].elems[0]); | ||
2173 | |||
2174 | if (to_idx > sctx->last_str_idx | ||
2175 | || sctx->sifted_states[to_idx] == NULL | ||
2176 | || !STATE_NODE_CONTAINS (sctx->sifted_states[to_idx], dst_node) | ||
2177 | || check_dst_limits (mctx, &sctx->limits, node, | ||
2178 | str_idx, dst_node, to_idx)) | ||
2179 | continue; | ||
2180 | |||
2181 | if (local_sctx.sifted_states == NULL) | ||
2182 | { | ||
2183 | local_sctx = *sctx; | ||
2184 | err = re_node_set_init_copy (&local_sctx.limits, &sctx->limits); | ||
2185 | if (BE (err != REG_NOERROR, 0)) | ||
2186 | goto free_return; | ||
2187 | } | ||
2188 | local_sctx.last_node = node; | ||
2189 | local_sctx.last_str_idx = str_idx; | ||
2190 | ret = re_node_set_insert (&local_sctx.limits, enabled_idx); | ||
2191 | if (BE (ret < 0, 0)) | ||
2192 | { | ||
2193 | err = REG_ESPACE; | ||
2194 | goto free_return; | ||
2195 | } | ||
2196 | cur_state = local_sctx.sifted_states[str_idx]; | ||
2197 | err = sift_states_backward (mctx, &local_sctx); | ||
2198 | if (BE (err != REG_NOERROR, 0)) | ||
2199 | goto free_return; | ||
2200 | if (sctx->limited_states != NULL) | ||
2201 | { | ||
2202 | err = merge_state_array (dfa, sctx->limited_states, | ||
2203 | local_sctx.sifted_states, | ||
2204 | str_idx + 1); | ||
2205 | if (BE (err != REG_NOERROR, 0)) | ||
2206 | goto free_return; | ||
2207 | } | ||
2208 | local_sctx.sifted_states[str_idx] = cur_state; | ||
2209 | re_node_set_remove (&local_sctx.limits, enabled_idx); | ||
2210 | |||
2211 | /* mctx->bkref_ents may have changed, reload the pointer. */ | ||
2212 | entry = mctx->bkref_ents + enabled_idx; | ||
2213 | } | ||
2214 | while (enabled_idx++, entry++->more); | ||
2215 | } | ||
2216 | err = REG_NOERROR; | ||
2217 | free_return: | ||
2218 | if (local_sctx.sifted_states != NULL) | ||
2219 | { | ||
2220 | re_node_set_free (&local_sctx.limits); | ||
2221 | } | ||
2222 | |||
2223 | return err; | ||
2224 | } | ||
2225 | |||
2226 | |||
2227 | #ifdef RE_ENABLE_I18N | ||
2228 | static int | ||
2229 | internal_function | ||
2230 | sift_states_iter_mb (const re_match_context_t *mctx, re_sift_context_t *sctx, | ||
2231 | int node_idx, int str_idx, int max_str_idx) | ||
2232 | { | ||
2233 | const re_dfa_t *const dfa = mctx->dfa; | ||
2234 | int naccepted; | ||
2235 | /* Check the node can accept `multi byte'. */ | ||
2236 | naccepted = check_node_accept_bytes (dfa, node_idx, &mctx->input, str_idx); | ||
2237 | if (naccepted > 0 && str_idx + naccepted <= max_str_idx && | ||
2238 | !STATE_NODE_CONTAINS (sctx->sifted_states[str_idx + naccepted], | ||
2239 | dfa->nexts[node_idx])) | ||
2240 | /* The node can't accept the `multi byte', or the | ||
2241 | destination was already thrown away, then the node | ||
2242 | couldn't accept the current input `multi byte'. */ | ||
2243 | naccepted = 0; | ||
2244 | /* Otherwise, it is sure that the node could accept | ||
2245 | `naccepted' bytes input. */ | ||
2246 | return naccepted; | ||
2247 | } | ||
2248 | #endif /* RE_ENABLE_I18N */ | ||
2249 | |||
2250 | |||
2251 | /* Functions for state transition. */ | ||
2252 | |||
2253 | /* Return the next state to which the current state STATE will transit by | ||
2254 | accepting the current input byte, and update STATE_LOG if necessary. | ||
2255 | If STATE can accept a multibyte char/collating element/back reference | ||
2256 | update the destination of STATE_LOG. */ | ||
2257 | |||
2258 | static re_dfastate_t * | ||
2259 | internal_function | ||
2260 | transit_state (reg_errcode_t *err, re_match_context_t *mctx, | ||
2261 | re_dfastate_t *state) | ||
2262 | { | ||
2263 | re_dfastate_t **trtable; | ||
2264 | unsigned char ch; | ||
2265 | |||
2266 | #ifdef RE_ENABLE_I18N | ||
2267 | /* If the current state can accept multibyte. */ | ||
2268 | if (BE (state->accept_mb, 0)) | ||
2269 | { | ||
2270 | *err = transit_state_mb (mctx, state); | ||
2271 | if (BE (*err != REG_NOERROR, 0)) | ||
2272 | return NULL; | ||
2273 | } | ||
2274 | #endif /* RE_ENABLE_I18N */ | ||
2275 | |||
2276 | /* Then decide the next state with the single byte. */ | ||
2277 | #if 0 | ||
2278 | if (0) | ||
2279 | /* don't use transition table */ | ||
2280 | return transit_state_sb (err, mctx, state); | ||
2281 | #endif | ||
2282 | |||
2283 | /* Use transition table */ | ||
2284 | ch = re_string_fetch_byte (&mctx->input); | ||
2285 | for (;;) | ||
2286 | { | ||
2287 | trtable = state->trtable; | ||
2288 | if (BE (trtable != NULL, 1)) | ||
2289 | return trtable[ch]; | ||
2290 | |||
2291 | trtable = state->word_trtable; | ||
2292 | if (BE (trtable != NULL, 1)) | ||
2293 | { | ||
2294 | unsigned int context; | ||
2295 | context | ||
2296 | = re_string_context_at (&mctx->input, | ||
2297 | re_string_cur_idx (&mctx->input) - 1, | ||
2298 | mctx->eflags); | ||
2299 | if (IS_WORD_CONTEXT (context)) | ||
2300 | return trtable[ch + SBC_MAX]; | ||
2301 | else | ||
2302 | return trtable[ch]; | ||
2303 | } | ||
2304 | |||
2305 | if (!build_trtable (mctx->dfa, state)) | ||
2306 | { | ||
2307 | *err = REG_ESPACE; | ||
2308 | return NULL; | ||
2309 | } | ||
2310 | |||
2311 | /* Retry, we now have a transition table. */ | ||
2312 | } | ||
2313 | } | ||
2314 | |||
2315 | /* Update the state_log if we need */ | ||
2316 | static re_dfastate_t * | ||
2317 | internal_function | ||
2318 | merge_state_with_log (reg_errcode_t *err, re_match_context_t *mctx, | ||
2319 | re_dfastate_t *next_state) | ||
2320 | { | ||
2321 | const re_dfa_t *const dfa = mctx->dfa; | ||
2322 | int cur_idx = re_string_cur_idx (&mctx->input); | ||
2323 | |||
2324 | if (cur_idx > mctx->state_log_top) | ||
2325 | { | ||
2326 | mctx->state_log[cur_idx] = next_state; | ||
2327 | mctx->state_log_top = cur_idx; | ||
2328 | } | ||
2329 | else if (mctx->state_log[cur_idx] == NULL) | ||
2330 | { | ||
2331 | mctx->state_log[cur_idx] = next_state; | ||
2332 | } | ||
2333 | else | ||
2334 | { | ||
2335 | re_dfastate_t *pstate; | ||
2336 | unsigned int context; | ||
2337 | re_node_set next_nodes, *log_nodes, *table_nodes = NULL; | ||
2338 | /* If (state_log[cur_idx] != 0), it implies that cur_idx is | ||
2339 | the destination of a multibyte char/collating element/ | ||
2340 | back reference. Then the next state is the union set of | ||
2341 | these destinations and the results of the transition table. */ | ||
2342 | pstate = mctx->state_log[cur_idx]; | ||
2343 | log_nodes = pstate->entrance_nodes; | ||
2344 | if (next_state != NULL) | ||
2345 | { | ||
2346 | table_nodes = next_state->entrance_nodes; | ||
2347 | *err = re_node_set_init_union (&next_nodes, table_nodes, | ||
2348 | log_nodes); | ||
2349 | if (BE (*err != REG_NOERROR, 0)) | ||
2350 | return NULL; | ||
2351 | } | ||
2352 | else | ||
2353 | next_nodes = *log_nodes; | ||
2354 | /* Note: We already add the nodes of the initial state, | ||
2355 | then we don't need to add them here. */ | ||
2356 | |||
2357 | context = re_string_context_at (&mctx->input, | ||
2358 | re_string_cur_idx (&mctx->input) - 1, | ||
2359 | mctx->eflags); | ||
2360 | next_state = mctx->state_log[cur_idx] | ||
2361 | = re_acquire_state_context (err, dfa, &next_nodes, context); | ||
2362 | /* We don't need to check errors here, since the return value of | ||
2363 | this function is next_state and ERR is already set. */ | ||
2364 | |||
2365 | if (table_nodes != NULL) | ||
2366 | re_node_set_free (&next_nodes); | ||
2367 | } | ||
2368 | |||
2369 | if (BE (dfa->nbackref, 0) && next_state != NULL) | ||
2370 | { | ||
2371 | /* Check OP_OPEN_SUBEXP in the current state in case that we use them | ||
2372 | later. We must check them here, since the back references in the | ||
2373 | next state might use them. */ | ||
2374 | *err = check_subexp_matching_top (mctx, &next_state->nodes, | ||
2375 | cur_idx); | ||
2376 | if (BE (*err != REG_NOERROR, 0)) | ||
2377 | return NULL; | ||
2378 | |||
2379 | /* If the next state has back references. */ | ||
2380 | if (next_state->has_backref) | ||
2381 | { | ||
2382 | *err = transit_state_bkref (mctx, &next_state->nodes); | ||
2383 | if (BE (*err != REG_NOERROR, 0)) | ||
2384 | return NULL; | ||
2385 | next_state = mctx->state_log[cur_idx]; | ||
2386 | } | ||
2387 | } | ||
2388 | |||
2389 | return next_state; | ||
2390 | } | ||
2391 | |||
2392 | /* Skip bytes in the input that correspond to part of a | ||
2393 | multi-byte match, then look in the log for a state | ||
2394 | from which to restart matching. */ | ||
2395 | static re_dfastate_t * | ||
2396 | internal_function | ||
2397 | find_recover_state (reg_errcode_t *err, re_match_context_t *mctx) | ||
2398 | { | ||
2399 | re_dfastate_t *cur_state; | ||
2400 | do | ||
2401 | { | ||
2402 | int max = mctx->state_log_top; | ||
2403 | int cur_str_idx = re_string_cur_idx (&mctx->input); | ||
2404 | |||
2405 | do | ||
2406 | { | ||
2407 | if (++cur_str_idx > max) | ||
2408 | return NULL; | ||
2409 | re_string_skip_bytes (&mctx->input, 1); | ||
2410 | } | ||
2411 | while (mctx->state_log[cur_str_idx] == NULL); | ||
2412 | |||
2413 | cur_state = merge_state_with_log (err, mctx, NULL); | ||
2414 | } | ||
2415 | while (*err == REG_NOERROR && cur_state == NULL); | ||
2416 | return cur_state; | ||
2417 | } | ||
2418 | |||
2419 | /* Helper functions for transit_state. */ | ||
2420 | |||
2421 | /* From the node set CUR_NODES, pick up the nodes whose types are | ||
2422 | OP_OPEN_SUBEXP and which have corresponding back references in the regular | ||
2423 | expression. And register them to use them later for evaluating the | ||
2424 | correspoding back references. */ | ||
2425 | |||
2426 | static reg_errcode_t | ||
2427 | internal_function | ||
2428 | check_subexp_matching_top (re_match_context_t *mctx, re_node_set *cur_nodes, | ||
2429 | int str_idx) | ||
2430 | { | ||
2431 | const re_dfa_t *const dfa = mctx->dfa; | ||
2432 | int node_idx; | ||
2433 | reg_errcode_t err; | ||
2434 | |||
2435 | /* TODO: This isn't efficient. | ||
2436 | Because there might be more than one nodes whose types are | ||
2437 | OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all | ||
2438 | nodes. | ||
2439 | E.g. RE: (a){2} */ | ||
2440 | for (node_idx = 0; node_idx < cur_nodes->nelem; ++node_idx) | ||
2441 | { | ||
2442 | int node = cur_nodes->elems[node_idx]; | ||
2443 | if (dfa->nodes[node].type == OP_OPEN_SUBEXP | ||
2444 | && dfa->nodes[node].opr.idx < BITSET_WORD_BITS | ||
2445 | && (dfa->used_bkref_map | ||
2446 | & ((bitset_word_t) 1 << dfa->nodes[node].opr.idx))) | ||
2447 | { | ||
2448 | err = match_ctx_add_subtop (mctx, node, str_idx); | ||
2449 | if (BE (err != REG_NOERROR, 0)) | ||
2450 | return err; | ||
2451 | } | ||
2452 | } | ||
2453 | return REG_NOERROR; | ||
2454 | } | ||
2455 | |||
2456 | #if 0 | ||
2457 | /* Return the next state to which the current state STATE will transit by | ||
2458 | accepting the current input byte. */ | ||
2459 | |||
2460 | static re_dfastate_t * | ||
2461 | transit_state_sb (reg_errcode_t *err, re_match_context_t *mctx, | ||
2462 | re_dfastate_t *state) | ||
2463 | { | ||
2464 | const re_dfa_t *const dfa = mctx->dfa; | ||
2465 | re_node_set next_nodes; | ||
2466 | re_dfastate_t *next_state; | ||
2467 | int node_cnt, cur_str_idx = re_string_cur_idx (&mctx->input); | ||
2468 | unsigned int context; | ||
2469 | |||
2470 | *err = re_node_set_alloc (&next_nodes, state->nodes.nelem + 1); | ||
2471 | if (BE (*err != REG_NOERROR, 0)) | ||
2472 | return NULL; | ||
2473 | for (node_cnt = 0; node_cnt < state->nodes.nelem; ++node_cnt) | ||
2474 | { | ||
2475 | int cur_node = state->nodes.elems[node_cnt]; | ||
2476 | if (check_node_accept (mctx, dfa->nodes + cur_node, cur_str_idx)) | ||
2477 | { | ||
2478 | *err = re_node_set_merge (&next_nodes, | ||
2479 | dfa->eclosures + dfa->nexts[cur_node]); | ||
2480 | if (BE (*err != REG_NOERROR, 0)) | ||
2481 | { | ||
2482 | re_node_set_free (&next_nodes); | ||
2483 | return NULL; | ||
2484 | } | ||
2485 | } | ||
2486 | } | ||
2487 | context = re_string_context_at (&mctx->input, cur_str_idx, mctx->eflags); | ||
2488 | next_state = re_acquire_state_context (err, dfa, &next_nodes, context); | ||
2489 | /* We don't need to check errors here, since the return value of | ||
2490 | this function is next_state and ERR is already set. */ | ||
2491 | |||
2492 | re_node_set_free (&next_nodes); | ||
2493 | re_string_skip_bytes (&mctx->input, 1); | ||
2494 | return next_state; | ||
2495 | } | ||
2496 | #endif | ||
2497 | |||
2498 | #ifdef RE_ENABLE_I18N | ||
2499 | static reg_errcode_t | ||
2500 | internal_function | ||
2501 | transit_state_mb (re_match_context_t *mctx, re_dfastate_t *pstate) | ||
2502 | { | ||
2503 | const re_dfa_t *const dfa = mctx->dfa; | ||
2504 | reg_errcode_t err; | ||
2505 | int i; | ||
2506 | |||
2507 | for (i = 0; i < pstate->nodes.nelem; ++i) | ||
2508 | { | ||
2509 | re_node_set dest_nodes, *new_nodes; | ||
2510 | int cur_node_idx = pstate->nodes.elems[i]; | ||
2511 | int naccepted, dest_idx; | ||
2512 | unsigned int context; | ||
2513 | re_dfastate_t *dest_state; | ||
2514 | |||
2515 | if (!dfa->nodes[cur_node_idx].accept_mb) | ||
2516 | continue; | ||
2517 | |||
2518 | if (dfa->nodes[cur_node_idx].constraint) | ||
2519 | { | ||
2520 | context = re_string_context_at (&mctx->input, | ||
2521 | re_string_cur_idx (&mctx->input), | ||
2522 | mctx->eflags); | ||
2523 | if (NOT_SATISFY_NEXT_CONSTRAINT (dfa->nodes[cur_node_idx].constraint, | ||
2524 | context)) | ||
2525 | continue; | ||
2526 | } | ||
2527 | |||
2528 | /* How many bytes the node can accept? */ | ||
2529 | naccepted = check_node_accept_bytes (dfa, cur_node_idx, &mctx->input, | ||
2530 | re_string_cur_idx (&mctx->input)); | ||
2531 | if (naccepted == 0) | ||
2532 | continue; | ||
2533 | |||
2534 | /* The node can accepts `naccepted' bytes. */ | ||
2535 | dest_idx = re_string_cur_idx (&mctx->input) + naccepted; | ||
2536 | mctx->max_mb_elem_len = ((mctx->max_mb_elem_len < naccepted) ? naccepted | ||
2537 | : mctx->max_mb_elem_len); | ||
2538 | err = clean_state_log_if_needed (mctx, dest_idx); | ||
2539 | if (BE (err != REG_NOERROR, 0)) | ||
2540 | return err; | ||
2541 | #ifdef DEBUG | ||
2542 | assert (dfa->nexts[cur_node_idx] != -1); | ||
2543 | #endif | ||
2544 | new_nodes = dfa->eclosures + dfa->nexts[cur_node_idx]; | ||
2545 | |||
2546 | dest_state = mctx->state_log[dest_idx]; | ||
2547 | if (dest_state == NULL) | ||
2548 | dest_nodes = *new_nodes; | ||
2549 | else | ||
2550 | { | ||
2551 | err = re_node_set_init_union (&dest_nodes, | ||
2552 | dest_state->entrance_nodes, new_nodes); | ||
2553 | if (BE (err != REG_NOERROR, 0)) | ||
2554 | return err; | ||
2555 | } | ||
2556 | context = re_string_context_at (&mctx->input, dest_idx - 1, | ||
2557 | mctx->eflags); | ||
2558 | mctx->state_log[dest_idx] | ||
2559 | = re_acquire_state_context (&err, dfa, &dest_nodes, context); | ||
2560 | if (dest_state != NULL) | ||
2561 | re_node_set_free (&dest_nodes); | ||
2562 | if (BE (mctx->state_log[dest_idx] == NULL && err != REG_NOERROR, 0)) | ||
2563 | return err; | ||
2564 | } | ||
2565 | return REG_NOERROR; | ||
2566 | } | ||
2567 | #endif /* RE_ENABLE_I18N */ | ||
2568 | |||
2569 | static reg_errcode_t | ||
2570 | internal_function | ||
2571 | transit_state_bkref (re_match_context_t *mctx, const re_node_set *nodes) | ||
2572 | { | ||
2573 | const re_dfa_t *const dfa = mctx->dfa; | ||
2574 | reg_errcode_t err; | ||
2575 | int i; | ||
2576 | int cur_str_idx = re_string_cur_idx (&mctx->input); | ||
2577 | |||
2578 | for (i = 0; i < nodes->nelem; ++i) | ||
2579 | { | ||
2580 | int dest_str_idx, prev_nelem, bkc_idx; | ||
2581 | int node_idx = nodes->elems[i]; | ||
2582 | unsigned int context; | ||
2583 | const re_token_t *node = dfa->nodes + node_idx; | ||
2584 | re_node_set *new_dest_nodes; | ||
2585 | |||
2586 | /* Check whether `node' is a backreference or not. */ | ||
2587 | if (node->type != OP_BACK_REF) | ||
2588 | continue; | ||
2589 | |||
2590 | if (node->constraint) | ||
2591 | { | ||
2592 | context = re_string_context_at (&mctx->input, cur_str_idx, | ||
2593 | mctx->eflags); | ||
2594 | if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) | ||
2595 | continue; | ||
2596 | } | ||
2597 | |||
2598 | /* `node' is a backreference. | ||
2599 | Check the substring which the substring matched. */ | ||
2600 | bkc_idx = mctx->nbkref_ents; | ||
2601 | err = get_subexp (mctx, node_idx, cur_str_idx); | ||
2602 | if (BE (err != REG_NOERROR, 0)) | ||
2603 | goto free_return; | ||
2604 | |||
2605 | /* And add the epsilon closures (which is `new_dest_nodes') of | ||
2606 | the backreference to appropriate state_log. */ | ||
2607 | #ifdef DEBUG | ||
2608 | assert (dfa->nexts[node_idx] != -1); | ||
2609 | #endif | ||
2610 | for (; bkc_idx < mctx->nbkref_ents; ++bkc_idx) | ||
2611 | { | ||
2612 | int subexp_len; | ||
2613 | re_dfastate_t *dest_state; | ||
2614 | struct re_backref_cache_entry *bkref_ent; | ||
2615 | bkref_ent = mctx->bkref_ents + bkc_idx; | ||
2616 | if (bkref_ent->node != node_idx || bkref_ent->str_idx != cur_str_idx) | ||
2617 | continue; | ||
2618 | subexp_len = bkref_ent->subexp_to - bkref_ent->subexp_from; | ||
2619 | new_dest_nodes = (subexp_len == 0 | ||
2620 | ? dfa->eclosures + dfa->edests[node_idx].elems[0] | ||
2621 | : dfa->eclosures + dfa->nexts[node_idx]); | ||
2622 | dest_str_idx = (cur_str_idx + bkref_ent->subexp_to | ||
2623 | - bkref_ent->subexp_from); | ||
2624 | context = re_string_context_at (&mctx->input, dest_str_idx - 1, | ||
2625 | mctx->eflags); | ||
2626 | dest_state = mctx->state_log[dest_str_idx]; | ||
2627 | prev_nelem = ((mctx->state_log[cur_str_idx] == NULL) ? 0 | ||
2628 | : mctx->state_log[cur_str_idx]->nodes.nelem); | ||
2629 | /* Add `new_dest_node' to state_log. */ | ||
2630 | if (dest_state == NULL) | ||
2631 | { | ||
2632 | mctx->state_log[dest_str_idx] | ||
2633 | = re_acquire_state_context (&err, dfa, new_dest_nodes, | ||
2634 | context); | ||
2635 | if (BE (mctx->state_log[dest_str_idx] == NULL | ||
2636 | && err != REG_NOERROR, 0)) | ||
2637 | goto free_return; | ||
2638 | } | ||
2639 | else | ||
2640 | { | ||
2641 | re_node_set dest_nodes; | ||
2642 | err = re_node_set_init_union (&dest_nodes, | ||
2643 | dest_state->entrance_nodes, | ||
2644 | new_dest_nodes); | ||
2645 | if (BE (err != REG_NOERROR, 0)) | ||
2646 | { | ||
2647 | re_node_set_free (&dest_nodes); | ||
2648 | goto free_return; | ||
2649 | } | ||
2650 | mctx->state_log[dest_str_idx] | ||
2651 | = re_acquire_state_context (&err, dfa, &dest_nodes, context); | ||
2652 | re_node_set_free (&dest_nodes); | ||
2653 | if (BE (mctx->state_log[dest_str_idx] == NULL | ||
2654 | && err != REG_NOERROR, 0)) | ||
2655 | goto free_return; | ||
2656 | } | ||
2657 | /* We need to check recursively if the backreference can epsilon | ||
2658 | transit. */ | ||
2659 | if (subexp_len == 0 | ||
2660 | && mctx->state_log[cur_str_idx]->nodes.nelem > prev_nelem) | ||
2661 | { | ||
2662 | err = check_subexp_matching_top (mctx, new_dest_nodes, | ||
2663 | cur_str_idx); | ||
2664 | if (BE (err != REG_NOERROR, 0)) | ||
2665 | goto free_return; | ||
2666 | err = transit_state_bkref (mctx, new_dest_nodes); | ||
2667 | if (BE (err != REG_NOERROR, 0)) | ||
2668 | goto free_return; | ||
2669 | } | ||
2670 | } | ||
2671 | } | ||
2672 | err = REG_NOERROR; | ||
2673 | free_return: | ||
2674 | return err; | ||
2675 | } | ||
2676 | |||
2677 | /* Enumerate all the candidates which the backreference BKREF_NODE can match | ||
2678 | at BKREF_STR_IDX, and register them by match_ctx_add_entry(). | ||
2679 | Note that we might collect inappropriate candidates here. | ||
2680 | However, the cost of checking them strictly here is too high, then we | ||
2681 | delay these checking for prune_impossible_nodes(). */ | ||
2682 | |||
2683 | static reg_errcode_t | ||
2684 | internal_function | ||
2685 | get_subexp (re_match_context_t *mctx, int bkref_node, int bkref_str_idx) | ||
2686 | { | ||
2687 | const re_dfa_t *const dfa = mctx->dfa; | ||
2688 | int subexp_num, sub_top_idx; | ||
2689 | const char *buf = (const char *) re_string_get_buffer (&mctx->input); | ||
2690 | /* Return if we have already checked BKREF_NODE at BKREF_STR_IDX. */ | ||
2691 | int cache_idx = search_cur_bkref_entry (mctx, bkref_str_idx); | ||
2692 | if (cache_idx != -1) | ||
2693 | { | ||
2694 | const struct re_backref_cache_entry *entry | ||
2695 | = mctx->bkref_ents + cache_idx; | ||
2696 | do | ||
2697 | if (entry->node == bkref_node) | ||
2698 | return REG_NOERROR; /* We already checked it. */ | ||
2699 | while (entry++->more); | ||
2700 | } | ||
2701 | |||
2702 | subexp_num = dfa->nodes[bkref_node].opr.idx; | ||
2703 | |||
2704 | /* For each sub expression */ | ||
2705 | for (sub_top_idx = 0; sub_top_idx < mctx->nsub_tops; ++sub_top_idx) | ||
2706 | { | ||
2707 | reg_errcode_t err; | ||
2708 | re_sub_match_top_t *sub_top = mctx->sub_tops[sub_top_idx]; | ||
2709 | re_sub_match_last_t *sub_last; | ||
2710 | int sub_last_idx, sl_str, bkref_str_off; | ||
2711 | |||
2712 | if (dfa->nodes[sub_top->node].opr.idx != subexp_num) | ||
2713 | continue; /* It isn't related. */ | ||
2714 | |||
2715 | sl_str = sub_top->str_idx; | ||
2716 | bkref_str_off = bkref_str_idx; | ||
2717 | /* At first, check the last node of sub expressions we already | ||
2718 | evaluated. */ | ||
2719 | for (sub_last_idx = 0; sub_last_idx < sub_top->nlasts; ++sub_last_idx) | ||
2720 | { | ||
2721 | int sl_str_diff; | ||
2722 | sub_last = sub_top->lasts[sub_last_idx]; | ||
2723 | sl_str_diff = sub_last->str_idx - sl_str; | ||
2724 | /* The matched string by the sub expression match with the substring | ||
2725 | at the back reference? */ | ||
2726 | if (sl_str_diff > 0) | ||
2727 | { | ||
2728 | if (BE (bkref_str_off + sl_str_diff > mctx->input.valid_len, 0)) | ||
2729 | { | ||
2730 | /* Not enough chars for a successful match. */ | ||
2731 | if (bkref_str_off + sl_str_diff > mctx->input.len) | ||
2732 | break; | ||
2733 | |||
2734 | err = clean_state_log_if_needed (mctx, | ||
2735 | bkref_str_off | ||
2736 | + sl_str_diff); | ||
2737 | if (BE (err != REG_NOERROR, 0)) | ||
2738 | return err; | ||
2739 | buf = (const char *) re_string_get_buffer (&mctx->input); | ||
2740 | } | ||
2741 | if (memcmp (buf + bkref_str_off, buf + sl_str, sl_str_diff) != 0) | ||
2742 | /* We don't need to search this sub expression any more. */ | ||
2743 | break; | ||
2744 | } | ||
2745 | bkref_str_off += sl_str_diff; | ||
2746 | sl_str += sl_str_diff; | ||
2747 | err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, | ||
2748 | bkref_str_idx); | ||
2749 | |||
2750 | /* Reload buf, since the preceding call might have reallocated | ||
2751 | the buffer. */ | ||
2752 | buf = (const char *) re_string_get_buffer (&mctx->input); | ||
2753 | |||
2754 | if (err == REG_NOMATCH) | ||
2755 | continue; | ||
2756 | if (BE (err != REG_NOERROR, 0)) | ||
2757 | return err; | ||
2758 | } | ||
2759 | |||
2760 | if (sub_last_idx < sub_top->nlasts) | ||
2761 | continue; | ||
2762 | if (sub_last_idx > 0) | ||
2763 | ++sl_str; | ||
2764 | /* Then, search for the other last nodes of the sub expression. */ | ||
2765 | for (; sl_str <= bkref_str_idx; ++sl_str) | ||
2766 | { | ||
2767 | int cls_node, sl_str_off; | ||
2768 | const re_node_set *nodes; | ||
2769 | sl_str_off = sl_str - sub_top->str_idx; | ||
2770 | /* The matched string by the sub expression match with the substring | ||
2771 | at the back reference? */ | ||
2772 | if (sl_str_off > 0) | ||
2773 | { | ||
2774 | if (BE (bkref_str_off >= mctx->input.valid_len, 0)) | ||
2775 | { | ||
2776 | /* If we are at the end of the input, we cannot match. */ | ||
2777 | if (bkref_str_off >= mctx->input.len) | ||
2778 | break; | ||
2779 | |||
2780 | err = extend_buffers (mctx); | ||
2781 | if (BE (err != REG_NOERROR, 0)) | ||
2782 | return err; | ||
2783 | |||
2784 | buf = (const char *) re_string_get_buffer (&mctx->input); | ||
2785 | } | ||
2786 | if (buf [bkref_str_off++] != buf[sl_str - 1]) | ||
2787 | break; /* We don't need to search this sub expression | ||
2788 | any more. */ | ||
2789 | } | ||
2790 | if (mctx->state_log[sl_str] == NULL) | ||
2791 | continue; | ||
2792 | /* Does this state have a ')' of the sub expression? */ | ||
2793 | nodes = &mctx->state_log[sl_str]->nodes; | ||
2794 | cls_node = find_subexp_node (dfa, nodes, subexp_num, | ||
2795 | OP_CLOSE_SUBEXP); | ||
2796 | if (cls_node == -1) | ||
2797 | continue; /* No. */ | ||
2798 | if (sub_top->path == NULL) | ||
2799 | { | ||
2800 | sub_top->path = calloc (sizeof (state_array_t), | ||
2801 | sl_str - sub_top->str_idx + 1); | ||
2802 | if (sub_top->path == NULL) | ||
2803 | return REG_ESPACE; | ||
2804 | } | ||
2805 | /* Can the OP_OPEN_SUBEXP node arrive the OP_CLOSE_SUBEXP node | ||
2806 | in the current context? */ | ||
2807 | err = check_arrival (mctx, sub_top->path, sub_top->node, | ||
2808 | sub_top->str_idx, cls_node, sl_str, | ||
2809 | OP_CLOSE_SUBEXP); | ||
2810 | if (err == REG_NOMATCH) | ||
2811 | continue; | ||
2812 | if (BE (err != REG_NOERROR, 0)) | ||
2813 | return err; | ||
2814 | sub_last = match_ctx_add_sublast (sub_top, cls_node, sl_str); | ||
2815 | if (BE (sub_last == NULL, 0)) | ||
2816 | return REG_ESPACE; | ||
2817 | err = get_subexp_sub (mctx, sub_top, sub_last, bkref_node, | ||
2818 | bkref_str_idx); | ||
2819 | if (err == REG_NOMATCH) | ||
2820 | continue; | ||
2821 | } | ||
2822 | } | ||
2823 | return REG_NOERROR; | ||
2824 | } | ||
2825 | |||
2826 | /* Helper functions for get_subexp(). */ | ||
2827 | |||
2828 | /* Check SUB_LAST can arrive to the back reference BKREF_NODE at BKREF_STR. | ||
2829 | If it can arrive, register the sub expression expressed with SUB_TOP | ||
2830 | and SUB_LAST. */ | ||
2831 | |||
2832 | static reg_errcode_t | ||
2833 | internal_function | ||
2834 | get_subexp_sub (re_match_context_t *mctx, const re_sub_match_top_t *sub_top, | ||
2835 | re_sub_match_last_t *sub_last, int bkref_node, int bkref_str) | ||
2836 | { | ||
2837 | reg_errcode_t err; | ||
2838 | int to_idx; | ||
2839 | /* Can the subexpression arrive the back reference? */ | ||
2840 | err = check_arrival (mctx, &sub_last->path, sub_last->node, | ||
2841 | sub_last->str_idx, bkref_node, bkref_str, | ||
2842 | OP_OPEN_SUBEXP); | ||
2843 | if (err != REG_NOERROR) | ||
2844 | return err; | ||
2845 | err = match_ctx_add_entry (mctx, bkref_node, bkref_str, sub_top->str_idx, | ||
2846 | sub_last->str_idx); | ||
2847 | if (BE (err != REG_NOERROR, 0)) | ||
2848 | return err; | ||
2849 | to_idx = bkref_str + sub_last->str_idx - sub_top->str_idx; | ||
2850 | return clean_state_log_if_needed (mctx, to_idx); | ||
2851 | } | ||
2852 | |||
2853 | /* Find the first node which is '(' or ')' and whose index is SUBEXP_IDX. | ||
2854 | Search '(' if FL_OPEN, or search ')' otherwise. | ||
2855 | TODO: This function isn't efficient... | ||
2856 | Because there might be more than one nodes whose types are | ||
2857 | OP_OPEN_SUBEXP and whose index is SUBEXP_IDX, we must check all | ||
2858 | nodes. | ||
2859 | E.g. RE: (a){2} */ | ||
2860 | |||
2861 | static int | ||
2862 | internal_function | ||
2863 | find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes, | ||
2864 | int subexp_idx, int type) | ||
2865 | { | ||
2866 | int cls_idx; | ||
2867 | for (cls_idx = 0; cls_idx < nodes->nelem; ++cls_idx) | ||
2868 | { | ||
2869 | int cls_node = nodes->elems[cls_idx]; | ||
2870 | const re_token_t *node = dfa->nodes + cls_node; | ||
2871 | if (node->type == type | ||
2872 | && node->opr.idx == subexp_idx) | ||
2873 | return cls_node; | ||
2874 | } | ||
2875 | return -1; | ||
2876 | } | ||
2877 | |||
2878 | /* Check whether the node TOP_NODE at TOP_STR can arrive to the node | ||
2879 | LAST_NODE at LAST_STR. We record the path onto PATH since it will be | ||
2880 | heavily reused. | ||
2881 | Return REG_NOERROR if it can arrive, or REG_NOMATCH otherwise. */ | ||
2882 | |||
2883 | static reg_errcode_t | ||
2884 | internal_function | ||
2885 | check_arrival (re_match_context_t *mctx, state_array_t *path, int top_node, | ||
2886 | int top_str, int last_node, int last_str, int type) | ||
2887 | { | ||
2888 | const re_dfa_t *const dfa = mctx->dfa; | ||
2889 | reg_errcode_t err = REG_NOERROR; | ||
2890 | int subexp_num, backup_cur_idx, str_idx, null_cnt; | ||
2891 | re_dfastate_t *cur_state = NULL; | ||
2892 | re_node_set *cur_nodes, next_nodes; | ||
2893 | re_dfastate_t **backup_state_log; | ||
2894 | unsigned int context; | ||
2895 | |||
2896 | subexp_num = dfa->nodes[top_node].opr.idx; | ||
2897 | /* Extend the buffer if we need. */ | ||
2898 | if (BE (path->alloc < last_str + mctx->max_mb_elem_len + 1, 0)) | ||
2899 | { | ||
2900 | re_dfastate_t **new_array; | ||
2901 | int old_alloc = path->alloc; | ||
2902 | path->alloc += last_str + mctx->max_mb_elem_len + 1; | ||
2903 | new_array = re_realloc (path->array, re_dfastate_t *, path->alloc); | ||
2904 | if (BE (new_array == NULL, 0)) | ||
2905 | { | ||
2906 | path->alloc = old_alloc; | ||
2907 | return REG_ESPACE; | ||
2908 | } | ||
2909 | path->array = new_array; | ||
2910 | memset (new_array + old_alloc, '\0', | ||
2911 | sizeof (re_dfastate_t *) * (path->alloc - old_alloc)); | ||
2912 | } | ||
2913 | |||
2914 | str_idx = path->next_idx ? path->next_idx : top_str; | ||
2915 | |||
2916 | /* Temporary modify MCTX. */ | ||
2917 | backup_state_log = mctx->state_log; | ||
2918 | backup_cur_idx = mctx->input.cur_idx; | ||
2919 | mctx->state_log = path->array; | ||
2920 | mctx->input.cur_idx = str_idx; | ||
2921 | |||
2922 | /* Setup initial node set. */ | ||
2923 | context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); | ||
2924 | if (str_idx == top_str) | ||
2925 | { | ||
2926 | err = re_node_set_init_1 (&next_nodes, top_node); | ||
2927 | if (BE (err != REG_NOERROR, 0)) | ||
2928 | return err; | ||
2929 | err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); | ||
2930 | if (BE (err != REG_NOERROR, 0)) | ||
2931 | { | ||
2932 | re_node_set_free (&next_nodes); | ||
2933 | return err; | ||
2934 | } | ||
2935 | } | ||
2936 | else | ||
2937 | { | ||
2938 | cur_state = mctx->state_log[str_idx]; | ||
2939 | if (cur_state && cur_state->has_backref) | ||
2940 | { | ||
2941 | err = re_node_set_init_copy (&next_nodes, &cur_state->nodes); | ||
2942 | if (BE (err != REG_NOERROR, 0)) | ||
2943 | return err; | ||
2944 | } | ||
2945 | else | ||
2946 | re_node_set_init_empty (&next_nodes); | ||
2947 | } | ||
2948 | if (str_idx == top_str || (cur_state && cur_state->has_backref)) | ||
2949 | { | ||
2950 | if (next_nodes.nelem) | ||
2951 | { | ||
2952 | err = expand_bkref_cache (mctx, &next_nodes, str_idx, | ||
2953 | subexp_num, type); | ||
2954 | if (BE (err != REG_NOERROR, 0)) | ||
2955 | { | ||
2956 | re_node_set_free (&next_nodes); | ||
2957 | return err; | ||
2958 | } | ||
2959 | } | ||
2960 | cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); | ||
2961 | if (BE (cur_state == NULL && err != REG_NOERROR, 0)) | ||
2962 | { | ||
2963 | re_node_set_free (&next_nodes); | ||
2964 | return err; | ||
2965 | } | ||
2966 | mctx->state_log[str_idx] = cur_state; | ||
2967 | } | ||
2968 | |||
2969 | for (null_cnt = 0; str_idx < last_str && null_cnt <= mctx->max_mb_elem_len;) | ||
2970 | { | ||
2971 | re_node_set_empty (&next_nodes); | ||
2972 | if (mctx->state_log[str_idx + 1]) | ||
2973 | { | ||
2974 | err = re_node_set_merge (&next_nodes, | ||
2975 | &mctx->state_log[str_idx + 1]->nodes); | ||
2976 | if (BE (err != REG_NOERROR, 0)) | ||
2977 | { | ||
2978 | re_node_set_free (&next_nodes); | ||
2979 | return err; | ||
2980 | } | ||
2981 | } | ||
2982 | if (cur_state) | ||
2983 | { | ||
2984 | err = check_arrival_add_next_nodes (mctx, str_idx, | ||
2985 | &cur_state->non_eps_nodes, | ||
2986 | &next_nodes); | ||
2987 | if (BE (err != REG_NOERROR, 0)) | ||
2988 | { | ||
2989 | re_node_set_free (&next_nodes); | ||
2990 | return err; | ||
2991 | } | ||
2992 | } | ||
2993 | ++str_idx; | ||
2994 | if (next_nodes.nelem) | ||
2995 | { | ||
2996 | err = check_arrival_expand_ecl (dfa, &next_nodes, subexp_num, type); | ||
2997 | if (BE (err != REG_NOERROR, 0)) | ||
2998 | { | ||
2999 | re_node_set_free (&next_nodes); | ||
3000 | return err; | ||
3001 | } | ||
3002 | err = expand_bkref_cache (mctx, &next_nodes, str_idx, | ||
3003 | subexp_num, type); | ||
3004 | if (BE (err != REG_NOERROR, 0)) | ||
3005 | { | ||
3006 | re_node_set_free (&next_nodes); | ||
3007 | return err; | ||
3008 | } | ||
3009 | } | ||
3010 | context = re_string_context_at (&mctx->input, str_idx - 1, mctx->eflags); | ||
3011 | cur_state = re_acquire_state_context (&err, dfa, &next_nodes, context); | ||
3012 | if (BE (cur_state == NULL && err != REG_NOERROR, 0)) | ||
3013 | { | ||
3014 | re_node_set_free (&next_nodes); | ||
3015 | return err; | ||
3016 | } | ||
3017 | mctx->state_log[str_idx] = cur_state; | ||
3018 | null_cnt = cur_state == NULL ? null_cnt + 1 : 0; | ||
3019 | } | ||
3020 | re_node_set_free (&next_nodes); | ||
3021 | cur_nodes = (mctx->state_log[last_str] == NULL ? NULL | ||
3022 | : &mctx->state_log[last_str]->nodes); | ||
3023 | path->next_idx = str_idx; | ||
3024 | |||
3025 | /* Fix MCTX. */ | ||
3026 | mctx->state_log = backup_state_log; | ||
3027 | mctx->input.cur_idx = backup_cur_idx; | ||
3028 | |||
3029 | /* Then check the current node set has the node LAST_NODE. */ | ||
3030 | if (cur_nodes != NULL && re_node_set_contains (cur_nodes, last_node)) | ||
3031 | return REG_NOERROR; | ||
3032 | |||
3033 | return REG_NOMATCH; | ||
3034 | } | ||
3035 | |||
3036 | /* Helper functions for check_arrival. */ | ||
3037 | |||
3038 | /* Calculate the destination nodes of CUR_NODES at STR_IDX, and append them | ||
3039 | to NEXT_NODES. | ||
3040 | TODO: This function is similar to the functions transit_state*(), | ||
3041 | however this function has many additional works. | ||
3042 | Can't we unify them? */ | ||
3043 | |||
3044 | static reg_errcode_t | ||
3045 | internal_function | ||
3046 | check_arrival_add_next_nodes (re_match_context_t *mctx, int str_idx, | ||
3047 | re_node_set *cur_nodes, re_node_set *next_nodes) | ||
3048 | { | ||
3049 | const re_dfa_t *const dfa = mctx->dfa; | ||
3050 | int result; | ||
3051 | int cur_idx; | ||
3052 | #ifdef RE_ENABLE_I18N | ||
3053 | reg_errcode_t err = REG_NOERROR; | ||
3054 | #endif | ||
3055 | re_node_set union_set; | ||
3056 | re_node_set_init_empty (&union_set); | ||
3057 | for (cur_idx = 0; cur_idx < cur_nodes->nelem; ++cur_idx) | ||
3058 | { | ||
3059 | int naccepted = 0; | ||
3060 | int cur_node = cur_nodes->elems[cur_idx]; | ||
3061 | #ifdef DEBUG | ||
3062 | re_token_type_t type = dfa->nodes[cur_node].type; | ||
3063 | assert (!IS_EPSILON_NODE (type)); | ||
3064 | #endif | ||
3065 | #ifdef RE_ENABLE_I18N | ||
3066 | /* If the node may accept `multi byte'. */ | ||
3067 | if (dfa->nodes[cur_node].accept_mb) | ||
3068 | { | ||
3069 | naccepted = check_node_accept_bytes (dfa, cur_node, &mctx->input, | ||
3070 | str_idx); | ||
3071 | if (naccepted > 1) | ||
3072 | { | ||
3073 | re_dfastate_t *dest_state; | ||
3074 | int next_node = dfa->nexts[cur_node]; | ||
3075 | int next_idx = str_idx + naccepted; | ||
3076 | dest_state = mctx->state_log[next_idx]; | ||
3077 | re_node_set_empty (&union_set); | ||
3078 | if (dest_state) | ||
3079 | { | ||
3080 | err = re_node_set_merge (&union_set, &dest_state->nodes); | ||
3081 | if (BE (err != REG_NOERROR, 0)) | ||
3082 | { | ||
3083 | re_node_set_free (&union_set); | ||
3084 | return err; | ||
3085 | } | ||
3086 | } | ||
3087 | result = re_node_set_insert (&union_set, next_node); | ||
3088 | if (BE (result < 0, 0)) | ||
3089 | { | ||
3090 | re_node_set_free (&union_set); | ||
3091 | return REG_ESPACE; | ||
3092 | } | ||
3093 | mctx->state_log[next_idx] = re_acquire_state (&err, dfa, | ||
3094 | &union_set); | ||
3095 | if (BE (mctx->state_log[next_idx] == NULL | ||
3096 | && err != REG_NOERROR, 0)) | ||
3097 | { | ||
3098 | re_node_set_free (&union_set); | ||
3099 | return err; | ||
3100 | } | ||
3101 | } | ||
3102 | } | ||
3103 | #endif /* RE_ENABLE_I18N */ | ||
3104 | if (naccepted | ||
3105 | || check_node_accept (mctx, dfa->nodes + cur_node, str_idx)) | ||
3106 | { | ||
3107 | result = re_node_set_insert (next_nodes, dfa->nexts[cur_node]); | ||
3108 | if (BE (result < 0, 0)) | ||
3109 | { | ||
3110 | re_node_set_free (&union_set); | ||
3111 | return REG_ESPACE; | ||
3112 | } | ||
3113 | } | ||
3114 | } | ||
3115 | re_node_set_free (&union_set); | ||
3116 | return REG_NOERROR; | ||
3117 | } | ||
3118 | |||
3119 | /* For all the nodes in CUR_NODES, add the epsilon closures of them to | ||
3120 | CUR_NODES, however exclude the nodes which are: | ||
3121 | - inside the sub expression whose number is EX_SUBEXP, if FL_OPEN. | ||
3122 | - out of the sub expression whose number is EX_SUBEXP, if !FL_OPEN. | ||
3123 | */ | ||
3124 | |||
3125 | static reg_errcode_t | ||
3126 | internal_function | ||
3127 | check_arrival_expand_ecl (const re_dfa_t *dfa, re_node_set *cur_nodes, | ||
3128 | int ex_subexp, int type) | ||
3129 | { | ||
3130 | reg_errcode_t err; | ||
3131 | int idx, outside_node; | ||
3132 | re_node_set new_nodes; | ||
3133 | #ifdef DEBUG | ||
3134 | assert (cur_nodes->nelem); | ||
3135 | #endif | ||
3136 | err = re_node_set_alloc (&new_nodes, cur_nodes->nelem); | ||
3137 | if (BE (err != REG_NOERROR, 0)) | ||
3138 | return err; | ||
3139 | /* Create a new node set NEW_NODES with the nodes which are epsilon | ||
3140 | closures of the node in CUR_NODES. */ | ||
3141 | |||
3142 | for (idx = 0; idx < cur_nodes->nelem; ++idx) | ||
3143 | { | ||
3144 | int cur_node = cur_nodes->elems[idx]; | ||
3145 | const re_node_set *eclosure = dfa->eclosures + cur_node; | ||
3146 | outside_node = find_subexp_node (dfa, eclosure, ex_subexp, type); | ||
3147 | if (outside_node == -1) | ||
3148 | { | ||
3149 | /* There are no problematic nodes, just merge them. */ | ||
3150 | err = re_node_set_merge (&new_nodes, eclosure); | ||
3151 | if (BE (err != REG_NOERROR, 0)) | ||
3152 | { | ||
3153 | re_node_set_free (&new_nodes); | ||
3154 | return err; | ||
3155 | } | ||
3156 | } | ||
3157 | else | ||
3158 | { | ||
3159 | /* There are problematic nodes, re-calculate incrementally. */ | ||
3160 | err = check_arrival_expand_ecl_sub (dfa, &new_nodes, cur_node, | ||
3161 | ex_subexp, type); | ||
3162 | if (BE (err != REG_NOERROR, 0)) | ||
3163 | { | ||
3164 | re_node_set_free (&new_nodes); | ||
3165 | return err; | ||
3166 | } | ||
3167 | } | ||
3168 | } | ||
3169 | re_node_set_free (cur_nodes); | ||
3170 | *cur_nodes = new_nodes; | ||
3171 | return REG_NOERROR; | ||
3172 | } | ||
3173 | |||
3174 | /* Helper function for check_arrival_expand_ecl. | ||
3175 | Check incrementally the epsilon closure of TARGET, and if it isn't | ||
3176 | problematic append it to DST_NODES. */ | ||
3177 | |||
3178 | static reg_errcode_t | ||
3179 | internal_function | ||
3180 | check_arrival_expand_ecl_sub (const re_dfa_t *dfa, re_node_set *dst_nodes, | ||
3181 | int target, int ex_subexp, int type) | ||
3182 | { | ||
3183 | int cur_node; | ||
3184 | for (cur_node = target; !re_node_set_contains (dst_nodes, cur_node);) | ||
3185 | { | ||
3186 | int err; | ||
3187 | |||
3188 | if (dfa->nodes[cur_node].type == type | ||
3189 | && dfa->nodes[cur_node].opr.idx == ex_subexp) | ||
3190 | { | ||
3191 | if (type == OP_CLOSE_SUBEXP) | ||
3192 | { | ||
3193 | err = re_node_set_insert (dst_nodes, cur_node); | ||
3194 | if (BE (err == -1, 0)) | ||
3195 | return REG_ESPACE; | ||
3196 | } | ||
3197 | break; | ||
3198 | } | ||
3199 | err = re_node_set_insert (dst_nodes, cur_node); | ||
3200 | if (BE (err == -1, 0)) | ||
3201 | return REG_ESPACE; | ||
3202 | if (dfa->edests[cur_node].nelem == 0) | ||
3203 | break; | ||
3204 | if (dfa->edests[cur_node].nelem == 2) | ||
3205 | { | ||
3206 | err = check_arrival_expand_ecl_sub (dfa, dst_nodes, | ||
3207 | dfa->edests[cur_node].elems[1], | ||
3208 | ex_subexp, type); | ||
3209 | if (BE (err != REG_NOERROR, 0)) | ||
3210 | return err; | ||
3211 | } | ||
3212 | cur_node = dfa->edests[cur_node].elems[0]; | ||
3213 | } | ||
3214 | return REG_NOERROR; | ||
3215 | } | ||
3216 | |||
3217 | |||
3218 | /* For all the back references in the current state, calculate the | ||
3219 | destination of the back references by the appropriate entry | ||
3220 | in MCTX->BKREF_ENTS. */ | ||
3221 | |||
3222 | static reg_errcode_t | ||
3223 | internal_function | ||
3224 | expand_bkref_cache (re_match_context_t *mctx, re_node_set *cur_nodes, | ||
3225 | int cur_str, int subexp_num, int type) | ||
3226 | { | ||
3227 | const re_dfa_t *const dfa = mctx->dfa; | ||
3228 | reg_errcode_t err; | ||
3229 | int cache_idx_start = search_cur_bkref_entry (mctx, cur_str); | ||
3230 | struct re_backref_cache_entry *ent; | ||
3231 | |||
3232 | if (cache_idx_start == -1) | ||
3233 | return REG_NOERROR; | ||
3234 | |||
3235 | restart: | ||
3236 | ent = mctx->bkref_ents + cache_idx_start; | ||
3237 | do | ||
3238 | { | ||
3239 | int to_idx, next_node; | ||
3240 | |||
3241 | /* Is this entry ENT is appropriate? */ | ||
3242 | if (!re_node_set_contains (cur_nodes, ent->node)) | ||
3243 | continue; /* No. */ | ||
3244 | |||
3245 | to_idx = cur_str + ent->subexp_to - ent->subexp_from; | ||
3246 | /* Calculate the destination of the back reference, and append it | ||
3247 | to MCTX->STATE_LOG. */ | ||
3248 | if (to_idx == cur_str) | ||
3249 | { | ||
3250 | /* The backreference did epsilon transit, we must re-check all the | ||
3251 | node in the current state. */ | ||
3252 | re_node_set new_dests; | ||
3253 | reg_errcode_t err2, err3; | ||
3254 | next_node = dfa->edests[ent->node].elems[0]; | ||
3255 | if (re_node_set_contains (cur_nodes, next_node)) | ||
3256 | continue; | ||
3257 | err = re_node_set_init_1 (&new_dests, next_node); | ||
3258 | err2 = check_arrival_expand_ecl (dfa, &new_dests, subexp_num, type); | ||
3259 | err3 = re_node_set_merge (cur_nodes, &new_dests); | ||
3260 | re_node_set_free (&new_dests); | ||
3261 | if (BE (err != REG_NOERROR || err2 != REG_NOERROR | ||
3262 | || err3 != REG_NOERROR, 0)) | ||
3263 | { | ||
3264 | err = (err != REG_NOERROR ? err | ||
3265 | : (err2 != REG_NOERROR ? err2 : err3)); | ||
3266 | return err; | ||
3267 | } | ||
3268 | /* TODO: It is still inefficient... */ | ||
3269 | goto restart; | ||
3270 | } | ||
3271 | else | ||
3272 | { | ||
3273 | re_node_set union_set; | ||
3274 | next_node = dfa->nexts[ent->node]; | ||
3275 | if (mctx->state_log[to_idx]) | ||
3276 | { | ||
3277 | int ret; | ||
3278 | if (re_node_set_contains (&mctx->state_log[to_idx]->nodes, | ||
3279 | next_node)) | ||
3280 | continue; | ||
3281 | err = re_node_set_init_copy (&union_set, | ||
3282 | &mctx->state_log[to_idx]->nodes); | ||
3283 | ret = re_node_set_insert (&union_set, next_node); | ||
3284 | if (BE (err != REG_NOERROR || ret < 0, 0)) | ||
3285 | { | ||
3286 | re_node_set_free (&union_set); | ||
3287 | err = err != REG_NOERROR ? err : REG_ESPACE; | ||
3288 | return err; | ||
3289 | } | ||
3290 | } | ||
3291 | else | ||
3292 | { | ||
3293 | err = re_node_set_init_1 (&union_set, next_node); | ||
3294 | if (BE (err != REG_NOERROR, 0)) | ||
3295 | return err; | ||
3296 | } | ||
3297 | mctx->state_log[to_idx] = re_acquire_state (&err, dfa, &union_set); | ||
3298 | re_node_set_free (&union_set); | ||
3299 | if (BE (mctx->state_log[to_idx] == NULL | ||
3300 | && err != REG_NOERROR, 0)) | ||
3301 | return err; | ||
3302 | } | ||
3303 | } | ||
3304 | while (ent++->more); | ||
3305 | return REG_NOERROR; | ||
3306 | } | ||
3307 | |||
3308 | /* Build transition table for the state. | ||
3309 | Return 1 if succeeded, otherwise return NULL. */ | ||
3310 | |||
3311 | static int | ||
3312 | internal_function | ||
3313 | build_trtable (const re_dfa_t *dfa, re_dfastate_t *state) | ||
3314 | { | ||
3315 | reg_errcode_t err; | ||
3316 | int i, j, ch, need_word_trtable = 0; | ||
3317 | bitset_word_t elem, mask; | ||
3318 | bool dests_node_malloced = false; | ||
3319 | bool dest_states_malloced = false; | ||
3320 | int ndests; /* Number of the destination states from `state'. */ | ||
3321 | re_dfastate_t **trtable; | ||
3322 | re_dfastate_t **dest_states = NULL, **dest_states_word, **dest_states_nl; | ||
3323 | re_node_set follows, *dests_node; | ||
3324 | bitset_t *dests_ch; | ||
3325 | bitset_t acceptable; | ||
3326 | |||
3327 | struct dests_alloc | ||
3328 | { | ||
3329 | re_node_set dests_node[SBC_MAX]; | ||
3330 | bitset_t dests_ch[SBC_MAX]; | ||
3331 | } *dests_alloc; | ||
3332 | |||
3333 | /* We build DFA states which corresponds to the destination nodes | ||
3334 | from `state'. `dests_node[i]' represents the nodes which i-th | ||
3335 | destination state contains, and `dests_ch[i]' represents the | ||
3336 | characters which i-th destination state accepts. */ | ||
3337 | #ifdef HAVE_ALLOCA | ||
3338 | if (__libc_use_alloca (sizeof (struct dests_alloc))) | ||
3339 | dests_alloc = (struct dests_alloc *) alloca (sizeof (struct dests_alloc)); | ||
3340 | else | ||
3341 | #endif | ||
3342 | { | ||
3343 | dests_alloc = re_malloc (struct dests_alloc, 1); | ||
3344 | if (BE (dests_alloc == NULL, 0)) | ||
3345 | return 0; | ||
3346 | dests_node_malloced = true; | ||
3347 | } | ||
3348 | dests_node = dests_alloc->dests_node; | ||
3349 | dests_ch = dests_alloc->dests_ch; | ||
3350 | |||
3351 | /* Initialize transiton table. */ | ||
3352 | state->word_trtable = state->trtable = NULL; | ||
3353 | |||
3354 | /* At first, group all nodes belonging to `state' into several | ||
3355 | destinations. */ | ||
3356 | ndests = group_nodes_into_DFAstates (dfa, state, dests_node, dests_ch); | ||
3357 | if (BE (ndests <= 0, 0)) | ||
3358 | { | ||
3359 | if (dests_node_malloced) | ||
3360 | free (dests_alloc); | ||
3361 | /* Return 0 in case of an error, 1 otherwise. */ | ||
3362 | if (ndests == 0) | ||
3363 | { | ||
3364 | state->trtable = (re_dfastate_t **) | ||
3365 | calloc (sizeof (re_dfastate_t *), SBC_MAX); | ||
3366 | return 1; | ||
3367 | } | ||
3368 | return 0; | ||
3369 | } | ||
3370 | |||
3371 | err = re_node_set_alloc (&follows, ndests + 1); | ||
3372 | if (BE (err != REG_NOERROR, 0)) | ||
3373 | goto out_free; | ||
3374 | |||
3375 | /* Avoid arithmetic overflow in size calculation. */ | ||
3376 | if (BE ((((SIZE_MAX - (sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX) | ||
3377 | / (3 * sizeof (re_dfastate_t *))) | ||
3378 | < ndests), | ||
3379 | 0)) | ||
3380 | goto out_free; | ||
3381 | |||
3382 | #ifdef HAVE_ALLOCA | ||
3383 | if (__libc_use_alloca ((sizeof (re_node_set) + sizeof (bitset_t)) * SBC_MAX | ||
3384 | + ndests * 3 * sizeof (re_dfastate_t *))) | ||
3385 | dest_states = (re_dfastate_t **) | ||
3386 | alloca (ndests * 3 * sizeof (re_dfastate_t *)); | ||
3387 | else | ||
3388 | #endif | ||
3389 | { | ||
3390 | dest_states = (re_dfastate_t **) | ||
3391 | malloc (ndests * 3 * sizeof (re_dfastate_t *)); | ||
3392 | if (BE (dest_states == NULL, 0)) | ||
3393 | { | ||
3394 | out_free: | ||
3395 | if (dest_states_malloced) | ||
3396 | free (dest_states); | ||
3397 | re_node_set_free (&follows); | ||
3398 | for (i = 0; i < ndests; ++i) | ||
3399 | re_node_set_free (dests_node + i); | ||
3400 | if (dests_node_malloced) | ||
3401 | free (dests_alloc); | ||
3402 | return 0; | ||
3403 | } | ||
3404 | dest_states_malloced = true; | ||
3405 | } | ||
3406 | dest_states_word = dest_states + ndests; | ||
3407 | dest_states_nl = dest_states_word + ndests; | ||
3408 | bitset_empty (acceptable); | ||
3409 | |||
3410 | /* Then build the states for all destinations. */ | ||
3411 | for (i = 0; i < ndests; ++i) | ||
3412 | { | ||
3413 | int next_node; | ||
3414 | re_node_set_empty (&follows); | ||
3415 | /* Merge the follows of this destination states. */ | ||
3416 | for (j = 0; j < dests_node[i].nelem; ++j) | ||
3417 | { | ||
3418 | next_node = dfa->nexts[dests_node[i].elems[j]]; | ||
3419 | if (next_node != -1) | ||
3420 | { | ||
3421 | err = re_node_set_merge (&follows, dfa->eclosures + next_node); | ||
3422 | if (BE (err != REG_NOERROR, 0)) | ||
3423 | goto out_free; | ||
3424 | } | ||
3425 | } | ||
3426 | dest_states[i] = re_acquire_state_context (&err, dfa, &follows, 0); | ||
3427 | if (BE (dest_states[i] == NULL && err != REG_NOERROR, 0)) | ||
3428 | goto out_free; | ||
3429 | /* If the new state has context constraint, | ||
3430 | build appropriate states for these contexts. */ | ||
3431 | if (dest_states[i]->has_constraint) | ||
3432 | { | ||
3433 | dest_states_word[i] = re_acquire_state_context (&err, dfa, &follows, | ||
3434 | CONTEXT_WORD); | ||
3435 | if (BE (dest_states_word[i] == NULL && err != REG_NOERROR, 0)) | ||
3436 | goto out_free; | ||
3437 | |||
3438 | if (dest_states[i] != dest_states_word[i] && dfa->mb_cur_max > 1) | ||
3439 | need_word_trtable = 1; | ||
3440 | |||
3441 | dest_states_nl[i] = re_acquire_state_context (&err, dfa, &follows, | ||
3442 | CONTEXT_NEWLINE); | ||
3443 | if (BE (dest_states_nl[i] == NULL && err != REG_NOERROR, 0)) | ||
3444 | goto out_free; | ||
3445 | } | ||
3446 | else | ||
3447 | { | ||
3448 | dest_states_word[i] = dest_states[i]; | ||
3449 | dest_states_nl[i] = dest_states[i]; | ||
3450 | } | ||
3451 | bitset_merge (acceptable, dests_ch[i]); | ||
3452 | } | ||
3453 | |||
3454 | if (!BE (need_word_trtable, 0)) | ||
3455 | { | ||
3456 | /* We don't care about whether the following character is a word | ||
3457 | character, or we are in a single-byte character set so we can | ||
3458 | discern by looking at the character code: allocate a | ||
3459 | 256-entry transition table. */ | ||
3460 | trtable = state->trtable = | ||
3461 | (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), SBC_MAX); | ||
3462 | if (BE (trtable == NULL, 0)) | ||
3463 | goto out_free; | ||
3464 | |||
3465 | /* For all characters ch...: */ | ||
3466 | for (i = 0; i < BITSET_WORDS; ++i) | ||
3467 | for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; | ||
3468 | elem; | ||
3469 | mask <<= 1, elem >>= 1, ++ch) | ||
3470 | if (BE (elem & 1, 0)) | ||
3471 | { | ||
3472 | /* There must be exactly one destination which accepts | ||
3473 | character ch. See group_nodes_into_DFAstates. */ | ||
3474 | for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) | ||
3475 | ; | ||
3476 | |||
3477 | /* j-th destination accepts the word character ch. */ | ||
3478 | if (dfa->word_char[i] & mask) | ||
3479 | trtable[ch] = dest_states_word[j]; | ||
3480 | else | ||
3481 | trtable[ch] = dest_states[j]; | ||
3482 | } | ||
3483 | } | ||
3484 | else | ||
3485 | { | ||
3486 | /* We care about whether the following character is a word | ||
3487 | character, and we are in a multi-byte character set: discern | ||
3488 | by looking at the character code: build two 256-entry | ||
3489 | transition tables, one starting at trtable[0] and one | ||
3490 | starting at trtable[SBC_MAX]. */ | ||
3491 | trtable = state->word_trtable = | ||
3492 | (re_dfastate_t **) calloc (sizeof (re_dfastate_t *), 2 * SBC_MAX); | ||
3493 | if (BE (trtable == NULL, 0)) | ||
3494 | goto out_free; | ||
3495 | |||
3496 | /* For all characters ch...: */ | ||
3497 | for (i = 0; i < BITSET_WORDS; ++i) | ||
3498 | for (ch = i * BITSET_WORD_BITS, elem = acceptable[i], mask = 1; | ||
3499 | elem; | ||
3500 | mask <<= 1, elem >>= 1, ++ch) | ||
3501 | if (BE (elem & 1, 0)) | ||
3502 | { | ||
3503 | /* There must be exactly one destination which accepts | ||
3504 | character ch. See group_nodes_into_DFAstates. */ | ||
3505 | for (j = 0; (dests_ch[j][i] & mask) == 0; ++j) | ||
3506 | ; | ||
3507 | |||
3508 | /* j-th destination accepts the word character ch. */ | ||
3509 | trtable[ch] = dest_states[j]; | ||
3510 | trtable[ch + SBC_MAX] = dest_states_word[j]; | ||
3511 | } | ||
3512 | } | ||
3513 | |||
3514 | /* new line */ | ||
3515 | if (bitset_contain (acceptable, NEWLINE_CHAR)) | ||
3516 | { | ||
3517 | /* The current state accepts newline character. */ | ||
3518 | for (j = 0; j < ndests; ++j) | ||
3519 | if (bitset_contain (dests_ch[j], NEWLINE_CHAR)) | ||
3520 | { | ||
3521 | /* k-th destination accepts newline character. */ | ||
3522 | trtable[NEWLINE_CHAR] = dest_states_nl[j]; | ||
3523 | if (need_word_trtable) | ||
3524 | trtable[NEWLINE_CHAR + SBC_MAX] = dest_states_nl[j]; | ||
3525 | /* There must be only one destination which accepts | ||
3526 | newline. See group_nodes_into_DFAstates. */ | ||
3527 | break; | ||
3528 | } | ||
3529 | } | ||
3530 | |||
3531 | if (dest_states_malloced) | ||
3532 | free (dest_states); | ||
3533 | |||
3534 | re_node_set_free (&follows); | ||
3535 | for (i = 0; i < ndests; ++i) | ||
3536 | re_node_set_free (dests_node + i); | ||
3537 | |||
3538 | if (dests_node_malloced) | ||
3539 | free (dests_alloc); | ||
3540 | |||
3541 | return 1; | ||
3542 | } | ||
3543 | |||
3544 | /* Group all nodes belonging to STATE into several destinations. | ||
3545 | Then for all destinations, set the nodes belonging to the destination | ||
3546 | to DESTS_NODE[i] and set the characters accepted by the destination | ||
3547 | to DEST_CH[i]. This function return the number of destinations. */ | ||
3548 | |||
3549 | static int | ||
3550 | internal_function | ||
3551 | group_nodes_into_DFAstates (const re_dfa_t *dfa, const re_dfastate_t *state, | ||
3552 | re_node_set *dests_node, bitset_t *dests_ch) | ||
3553 | { | ||
3554 | reg_errcode_t err; | ||
3555 | int result; | ||
3556 | int i, j, k; | ||
3557 | int ndests; /* Number of the destinations from `state'. */ | ||
3558 | bitset_t accepts; /* Characters a node can accept. */ | ||
3559 | const re_node_set *cur_nodes = &state->nodes; | ||
3560 | bitset_empty (accepts); | ||
3561 | ndests = 0; | ||
3562 | |||
3563 | /* For all the nodes belonging to `state', */ | ||
3564 | for (i = 0; i < cur_nodes->nelem; ++i) | ||
3565 | { | ||
3566 | re_token_t *node = &dfa->nodes[cur_nodes->elems[i]]; | ||
3567 | re_token_type_t type = node->type; | ||
3568 | unsigned int constraint = node->constraint; | ||
3569 | |||
3570 | /* Enumerate all single byte character this node can accept. */ | ||
3571 | if (type == CHARACTER) | ||
3572 | bitset_set (accepts, node->opr.c); | ||
3573 | else if (type == SIMPLE_BRACKET) | ||
3574 | { | ||
3575 | bitset_merge (accepts, node->opr.sbcset); | ||
3576 | } | ||
3577 | else if (type == OP_PERIOD) | ||
3578 | { | ||
3579 | #ifdef RE_ENABLE_I18N | ||
3580 | if (dfa->mb_cur_max > 1) | ||
3581 | bitset_merge (accepts, dfa->sb_char); | ||
3582 | else | ||
3583 | #endif | ||
3584 | bitset_set_all (accepts); | ||
3585 | if (!(dfa->syntax & RE_DOT_NEWLINE)) | ||
3586 | bitset_clear (accepts, '\n'); | ||
3587 | if (dfa->syntax & RE_DOT_NOT_NULL) | ||
3588 | bitset_clear (accepts, '\0'); | ||
3589 | } | ||
3590 | #ifdef RE_ENABLE_I18N | ||
3591 | else if (type == OP_UTF8_PERIOD) | ||
3592 | { | ||
3593 | memset (accepts, '\xff', sizeof (bitset_t) / 2); | ||
3594 | if (!(dfa->syntax & RE_DOT_NEWLINE)) | ||
3595 | bitset_clear (accepts, '\n'); | ||
3596 | if (dfa->syntax & RE_DOT_NOT_NULL) | ||
3597 | bitset_clear (accepts, '\0'); | ||
3598 | } | ||
3599 | #endif | ||
3600 | else | ||
3601 | continue; | ||
3602 | |||
3603 | /* Check the `accepts' and sift the characters which are not | ||
3604 | match it the context. */ | ||
3605 | if (constraint) | ||
3606 | { | ||
3607 | if (constraint & NEXT_NEWLINE_CONSTRAINT) | ||
3608 | { | ||
3609 | bool accepts_newline = bitset_contain (accepts, NEWLINE_CHAR); | ||
3610 | bitset_empty (accepts); | ||
3611 | if (accepts_newline) | ||
3612 | bitset_set (accepts, NEWLINE_CHAR); | ||
3613 | else | ||
3614 | continue; | ||
3615 | } | ||
3616 | if (constraint & NEXT_ENDBUF_CONSTRAINT) | ||
3617 | { | ||
3618 | bitset_empty (accepts); | ||
3619 | continue; | ||
3620 | } | ||
3621 | |||
3622 | if (constraint & NEXT_WORD_CONSTRAINT) | ||
3623 | { | ||
3624 | bitset_word_t any_set = 0; | ||
3625 | if (type == CHARACTER && !node->word_char) | ||
3626 | { | ||
3627 | bitset_empty (accepts); | ||
3628 | continue; | ||
3629 | } | ||
3630 | #ifdef RE_ENABLE_I18N | ||
3631 | if (dfa->mb_cur_max > 1) | ||
3632 | for (j = 0; j < BITSET_WORDS; ++j) | ||
3633 | any_set |= (accepts[j] &= (dfa->word_char[j] | ~dfa->sb_char[j])); | ||
3634 | else | ||
3635 | #endif | ||
3636 | for (j = 0; j < BITSET_WORDS; ++j) | ||
3637 | any_set |= (accepts[j] &= dfa->word_char[j]); | ||
3638 | if (!any_set) | ||
3639 | continue; | ||
3640 | } | ||
3641 | if (constraint & NEXT_NOTWORD_CONSTRAINT) | ||
3642 | { | ||
3643 | bitset_word_t any_set = 0; | ||
3644 | if (type == CHARACTER && node->word_char) | ||
3645 | { | ||
3646 | bitset_empty (accepts); | ||
3647 | continue; | ||
3648 | } | ||
3649 | #ifdef RE_ENABLE_I18N | ||
3650 | if (dfa->mb_cur_max > 1) | ||
3651 | for (j = 0; j < BITSET_WORDS; ++j) | ||
3652 | any_set |= (accepts[j] &= ~(dfa->word_char[j] & dfa->sb_char[j])); | ||
3653 | else | ||
3654 | #endif | ||
3655 | for (j = 0; j < BITSET_WORDS; ++j) | ||
3656 | any_set |= (accepts[j] &= ~dfa->word_char[j]); | ||
3657 | if (!any_set) | ||
3658 | continue; | ||
3659 | } | ||
3660 | } | ||
3661 | |||
3662 | /* Then divide `accepts' into DFA states, or create a new | ||
3663 | state. Above, we make sure that accepts is not empty. */ | ||
3664 | for (j = 0; j < ndests; ++j) | ||
3665 | { | ||
3666 | bitset_t intersec; /* Intersection sets, see below. */ | ||
3667 | bitset_t remains; | ||
3668 | /* Flags, see below. */ | ||
3669 | bitset_word_t has_intersec, not_subset, not_consumed; | ||
3670 | |||
3671 | /* Optimization, skip if this state doesn't accept the character. */ | ||
3672 | if (type == CHARACTER && !bitset_contain (dests_ch[j], node->opr.c)) | ||
3673 | continue; | ||
3674 | |||
3675 | /* Enumerate the intersection set of this state and `accepts'. */ | ||
3676 | has_intersec = 0; | ||
3677 | for (k = 0; k < BITSET_WORDS; ++k) | ||
3678 | has_intersec |= intersec[k] = accepts[k] & dests_ch[j][k]; | ||
3679 | /* And skip if the intersection set is empty. */ | ||
3680 | if (!has_intersec) | ||
3681 | continue; | ||
3682 | |||
3683 | /* Then check if this state is a subset of `accepts'. */ | ||
3684 | not_subset = not_consumed = 0; | ||
3685 | for (k = 0; k < BITSET_WORDS; ++k) | ||
3686 | { | ||
3687 | not_subset |= remains[k] = ~accepts[k] & dests_ch[j][k]; | ||
3688 | not_consumed |= accepts[k] = accepts[k] & ~dests_ch[j][k]; | ||
3689 | } | ||
3690 | |||
3691 | /* If this state isn't a subset of `accepts', create a | ||
3692 | new group state, which has the `remains'. */ | ||
3693 | if (not_subset) | ||
3694 | { | ||
3695 | bitset_copy (dests_ch[ndests], remains); | ||
3696 | bitset_copy (dests_ch[j], intersec); | ||
3697 | err = re_node_set_init_copy (dests_node + ndests, &dests_node[j]); | ||
3698 | if (BE (err != REG_NOERROR, 0)) | ||
3699 | goto error_return; | ||
3700 | ++ndests; | ||
3701 | } | ||
3702 | |||
3703 | /* Put the position in the current group. */ | ||
3704 | result = re_node_set_insert (&dests_node[j], cur_nodes->elems[i]); | ||
3705 | if (BE (result < 0, 0)) | ||
3706 | goto error_return; | ||
3707 | |||
3708 | /* If all characters are consumed, go to next node. */ | ||
3709 | if (!not_consumed) | ||
3710 | break; | ||
3711 | } | ||
3712 | /* Some characters remain, create a new group. */ | ||
3713 | if (j == ndests) | ||
3714 | { | ||
3715 | bitset_copy (dests_ch[ndests], accepts); | ||
3716 | err = re_node_set_init_1 (dests_node + ndests, cur_nodes->elems[i]); | ||
3717 | if (BE (err != REG_NOERROR, 0)) | ||
3718 | goto error_return; | ||
3719 | ++ndests; | ||
3720 | bitset_empty (accepts); | ||
3721 | } | ||
3722 | } | ||
3723 | return ndests; | ||
3724 | error_return: | ||
3725 | for (j = 0; j < ndests; ++j) | ||
3726 | re_node_set_free (dests_node + j); | ||
3727 | return -1; | ||
3728 | } | ||
3729 | |||
3730 | #ifdef RE_ENABLE_I18N | ||
3731 | /* Check how many bytes the node `dfa->nodes[node_idx]' accepts. | ||
3732 | Return the number of the bytes the node accepts. | ||
3733 | STR_IDX is the current index of the input string. | ||
3734 | |||
3735 | This function handles the nodes which can accept one character, or | ||
3736 | one collating element like '.', '[a-z]', opposite to the other nodes | ||
3737 | can only accept one byte. */ | ||
3738 | |||
3739 | static int | ||
3740 | internal_function | ||
3741 | check_node_accept_bytes (const re_dfa_t *dfa, int node_idx, | ||
3742 | const re_string_t *input, int str_idx) | ||
3743 | { | ||
3744 | const re_token_t *node = dfa->nodes + node_idx; | ||
3745 | int char_len, elem_len; | ||
3746 | int i; | ||
3747 | wint_t wc; | ||
3748 | |||
3749 | if (BE (node->type == OP_UTF8_PERIOD, 0)) | ||
3750 | { | ||
3751 | unsigned char c = re_string_byte_at (input, str_idx), d; | ||
3752 | if (BE (c < 0xc2, 1)) | ||
3753 | return 0; | ||
3754 | |||
3755 | if (str_idx + 2 > input->len) | ||
3756 | return 0; | ||
3757 | |||
3758 | d = re_string_byte_at (input, str_idx + 1); | ||
3759 | if (c < 0xe0) | ||
3760 | return (d < 0x80 || d > 0xbf) ? 0 : 2; | ||
3761 | else if (c < 0xf0) | ||
3762 | { | ||
3763 | char_len = 3; | ||
3764 | if (c == 0xe0 && d < 0xa0) | ||
3765 | return 0; | ||
3766 | } | ||
3767 | else if (c < 0xf8) | ||
3768 | { | ||
3769 | char_len = 4; | ||
3770 | if (c == 0xf0 && d < 0x90) | ||
3771 | return 0; | ||
3772 | } | ||
3773 | else if (c < 0xfc) | ||
3774 | { | ||
3775 | char_len = 5; | ||
3776 | if (c == 0xf8 && d < 0x88) | ||
3777 | return 0; | ||
3778 | } | ||
3779 | else if (c < 0xfe) | ||
3780 | { | ||
3781 | char_len = 6; | ||
3782 | if (c == 0xfc && d < 0x84) | ||
3783 | return 0; | ||
3784 | } | ||
3785 | else | ||
3786 | return 0; | ||
3787 | |||
3788 | if (str_idx + char_len > input->len) | ||
3789 | return 0; | ||
3790 | |||
3791 | for (i = 1; i < char_len; ++i) | ||
3792 | { | ||
3793 | d = re_string_byte_at (input, str_idx + i); | ||
3794 | if (d < 0x80 || d > 0xbf) | ||
3795 | return 0; | ||
3796 | } | ||
3797 | return char_len; | ||
3798 | } | ||
3799 | |||
3800 | char_len = re_string_char_size_at (input, str_idx); | ||
3801 | if (node->type == OP_PERIOD) | ||
3802 | { | ||
3803 | if (char_len <= 1) | ||
3804 | return 0; | ||
3805 | /* FIXME: I don't think this if is needed, as both '\n' | ||
3806 | and '\0' are char_len == 1. */ | ||
3807 | /* '.' accepts any one character except the following two cases. */ | ||
3808 | if ((!(dfa->syntax & RE_DOT_NEWLINE) && | ||
3809 | re_string_byte_at (input, str_idx) == '\n') || | ||
3810 | ((dfa->syntax & RE_DOT_NOT_NULL) && | ||
3811 | re_string_byte_at (input, str_idx) == '\0')) | ||
3812 | return 0; | ||
3813 | return char_len; | ||
3814 | } | ||
3815 | |||
3816 | elem_len = re_string_elem_size_at (input, str_idx); | ||
3817 | wc = __btowc(*(input->mbs+str_idx)); | ||
3818 | if (((elem_len <= 1 && char_len <= 1) || char_len == 0) && (wc != WEOF && wc < SBC_MAX)) | ||
3819 | return 0; | ||
3820 | |||
3821 | if (node->type == COMPLEX_BRACKET) | ||
3822 | { | ||
3823 | const re_charset_t *cset = node->opr.mbcset; | ||
3824 | # ifdef _LIBC | ||
3825 | const unsigned char *pin | ||
3826 | = ((const unsigned char *) re_string_get_buffer (input) + str_idx); | ||
3827 | int j; | ||
3828 | uint32_t nrules; | ||
3829 | # endif /* _LIBC */ | ||
3830 | int match_len = 0; | ||
3831 | wchar_t wc = ((cset->nranges || cset->nchar_classes || cset->nmbchars) | ||
3832 | ? re_string_wchar_at (input, str_idx) : 0); | ||
3833 | |||
3834 | /* match with multibyte character? */ | ||
3835 | for (i = 0; i < cset->nmbchars; ++i) | ||
3836 | if (wc == cset->mbchars[i]) | ||
3837 | { | ||
3838 | match_len = char_len; | ||
3839 | goto check_node_accept_bytes_match; | ||
3840 | } | ||
3841 | /* match with character_class? */ | ||
3842 | for (i = 0; i < cset->nchar_classes; ++i) | ||
3843 | { | ||
3844 | wctype_t wt = cset->char_classes[i]; | ||
3845 | if (__iswctype (wc, wt)) | ||
3846 | { | ||
3847 | match_len = char_len; | ||
3848 | goto check_node_accept_bytes_match; | ||
3849 | } | ||
3850 | } | ||
3851 | |||
3852 | # ifdef _LIBC | ||
3853 | nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); | ||
3854 | if (nrules != 0) | ||
3855 | { | ||
3856 | unsigned int in_collseq = 0; | ||
3857 | const int32_t *table, *indirect; | ||
3858 | const unsigned char *weights, *extra; | ||
3859 | const char *collseqwc; | ||
3860 | /* This #include defines a local function! */ | ||
3861 | # include <locale/weight.h> | ||
3862 | |||
3863 | /* match with collating_symbol? */ | ||
3864 | if (cset->ncoll_syms) | ||
3865 | extra = (const unsigned char *) | ||
3866 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); | ||
3867 | for (i = 0; i < cset->ncoll_syms; ++i) | ||
3868 | { | ||
3869 | const unsigned char *coll_sym = extra + cset->coll_syms[i]; | ||
3870 | /* Compare the length of input collating element and | ||
3871 | the length of current collating element. */ | ||
3872 | if (*coll_sym != elem_len) | ||
3873 | continue; | ||
3874 | /* Compare each bytes. */ | ||
3875 | for (j = 0; j < *coll_sym; j++) | ||
3876 | if (pin[j] != coll_sym[1 + j]) | ||
3877 | break; | ||
3878 | if (j == *coll_sym) | ||
3879 | { | ||
3880 | /* Match if every bytes is equal. */ | ||
3881 | match_len = j; | ||
3882 | goto check_node_accept_bytes_match; | ||
3883 | } | ||
3884 | } | ||
3885 | |||
3886 | if (cset->nranges) | ||
3887 | { | ||
3888 | if (elem_len <= char_len) | ||
3889 | { | ||
3890 | collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC); | ||
3891 | in_collseq = __collseq_table_lookup (collseqwc, wc); | ||
3892 | } | ||
3893 | else | ||
3894 | in_collseq = find_collation_sequence_value (pin, elem_len); | ||
3895 | } | ||
3896 | /* match with range expression? */ | ||
3897 | for (i = 0; i < cset->nranges; ++i) | ||
3898 | if (cset->range_starts[i] <= in_collseq | ||
3899 | && in_collseq <= cset->range_ends[i]) | ||
3900 | { | ||
3901 | match_len = elem_len; | ||
3902 | goto check_node_accept_bytes_match; | ||
3903 | } | ||
3904 | |||
3905 | /* match with equivalence_class? */ | ||
3906 | if (cset->nequiv_classes) | ||
3907 | { | ||
3908 | const unsigned char *cp = pin; | ||
3909 | table = (const int32_t *) | ||
3910 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB); | ||
3911 | weights = (const unsigned char *) | ||
3912 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_WEIGHTMB); | ||
3913 | extra = (const unsigned char *) | ||
3914 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_EXTRAMB); | ||
3915 | indirect = (const int32_t *) | ||
3916 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_INDIRECTMB); | ||
3917 | int32_t idx = findidx (&cp); | ||
3918 | if (idx > 0) | ||
3919 | for (i = 0; i < cset->nequiv_classes; ++i) | ||
3920 | { | ||
3921 | int32_t equiv_class_idx = cset->equiv_classes[i]; | ||
3922 | size_t weight_len = weights[idx & 0xffffff]; | ||
3923 | if (weight_len == weights[equiv_class_idx & 0xffffff] | ||
3924 | && (idx >> 24) == (equiv_class_idx >> 24)) | ||
3925 | { | ||
3926 | int cnt = 0; | ||
3927 | |||
3928 | idx &= 0xffffff; | ||
3929 | equiv_class_idx &= 0xffffff; | ||
3930 | |||
3931 | while (cnt <= weight_len | ||
3932 | && (weights[equiv_class_idx + 1 + cnt] | ||
3933 | == weights[idx + 1 + cnt])) | ||
3934 | ++cnt; | ||
3935 | if (cnt > weight_len) | ||
3936 | { | ||
3937 | match_len = elem_len; | ||
3938 | goto check_node_accept_bytes_match; | ||
3939 | } | ||
3940 | } | ||
3941 | } | ||
3942 | } | ||
3943 | } | ||
3944 | else | ||
3945 | # endif /* _LIBC */ | ||
3946 | { | ||
3947 | /* match with range expression? */ | ||
3948 | #if __GNUC__ >= 2 | ||
3949 | wchar_t cmp_buf[] = {L'\0', L'\0', wc, L'\0', L'\0', L'\0'}; | ||
3950 | #else | ||
3951 | wchar_t cmp_buf[] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'}; | ||
3952 | cmp_buf[2] = wc; | ||
3953 | #endif | ||
3954 | for (i = 0; i < cset->nranges; ++i) | ||
3955 | { | ||
3956 | cmp_buf[0] = cset->range_starts[i]; | ||
3957 | cmp_buf[4] = cset->range_ends[i]; | ||
3958 | if (wcscoll (cmp_buf, cmp_buf + 2) <= 0 | ||
3959 | && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0) | ||
3960 | { | ||
3961 | match_len = char_len; | ||
3962 | goto check_node_accept_bytes_match; | ||
3963 | } | ||
3964 | } | ||
3965 | } | ||
3966 | check_node_accept_bytes_match: | ||
3967 | if (!cset->non_match) | ||
3968 | return match_len; | ||
3969 | else | ||
3970 | { | ||
3971 | if (match_len > 0) | ||
3972 | return 0; | ||
3973 | else | ||
3974 | return (elem_len > char_len) ? elem_len : char_len; | ||
3975 | } | ||
3976 | } | ||
3977 | return 0; | ||
3978 | } | ||
3979 | |||
3980 | # ifdef _LIBC | ||
3981 | static unsigned int | ||
3982 | internal_function | ||
3983 | find_collation_sequence_value (const unsigned char *mbs, size_t mbs_len) | ||
3984 | { | ||
3985 | uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES); | ||
3986 | if (nrules == 0) | ||
3987 | { | ||
3988 | if (mbs_len == 1) | ||
3989 | { | ||
3990 | /* No valid character. Match it as a single byte character. */ | ||
3991 | const unsigned char *collseq = (const unsigned char *) | ||
3992 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB); | ||
3993 | return collseq[mbs[0]]; | ||
3994 | } | ||
3995 | return UINT_MAX; | ||
3996 | } | ||
3997 | else | ||
3998 | { | ||
3999 | int32_t idx; | ||
4000 | const unsigned char *extra = (const unsigned char *) | ||
4001 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB); | ||
4002 | int32_t extrasize = (const unsigned char *) | ||
4003 | _NL_CURRENT (LC_COLLATE, _NL_COLLATE_SYMB_EXTRAMB + 1) - extra; | ||
4004 | |||
4005 | for (idx = 0; idx < extrasize;) | ||
4006 | { | ||
4007 | int mbs_cnt, found = 0; | ||
4008 | int32_t elem_mbs_len; | ||
4009 | /* Skip the name of collating element name. */ | ||
4010 | idx = idx + extra[idx] + 1; | ||
4011 | elem_mbs_len = extra[idx++]; | ||
4012 | if (mbs_len == elem_mbs_len) | ||
4013 | { | ||
4014 | for (mbs_cnt = 0; mbs_cnt < elem_mbs_len; ++mbs_cnt) | ||
4015 | if (extra[idx + mbs_cnt] != mbs[mbs_cnt]) | ||
4016 | break; | ||
4017 | if (mbs_cnt == elem_mbs_len) | ||
4018 | /* Found the entry. */ | ||
4019 | found = 1; | ||
4020 | } | ||
4021 | /* Skip the byte sequence of the collating element. */ | ||
4022 | idx += elem_mbs_len; | ||
4023 | /* Adjust for the alignment. */ | ||
4024 | idx = (idx + 3) & ~3; | ||
4025 | /* Skip the collation sequence value. */ | ||
4026 | idx += sizeof (uint32_t); | ||
4027 | /* Skip the wide char sequence of the collating element. */ | ||
4028 | idx = idx + sizeof (uint32_t) * (extra[idx] + 1); | ||
4029 | /* If we found the entry, return the sequence value. */ | ||
4030 | if (found) | ||
4031 | return *(uint32_t *) (extra + idx); | ||
4032 | /* Skip the collation sequence value. */ | ||
4033 | idx += sizeof (uint32_t); | ||
4034 | } | ||
4035 | return UINT_MAX; | ||
4036 | } | ||
4037 | } | ||
4038 | # endif /* _LIBC */ | ||
4039 | #endif /* RE_ENABLE_I18N */ | ||
4040 | |||
4041 | /* Check whether the node accepts the byte which is IDX-th | ||
4042 | byte of the INPUT. */ | ||
4043 | |||
4044 | static int | ||
4045 | internal_function | ||
4046 | check_node_accept (const re_match_context_t *mctx, const re_token_t *node, | ||
4047 | int idx) | ||
4048 | { | ||
4049 | unsigned char ch; | ||
4050 | ch = re_string_byte_at (&mctx->input, idx); | ||
4051 | switch (node->type) | ||
4052 | { | ||
4053 | case CHARACTER: | ||
4054 | if (node->opr.c != ch) | ||
4055 | return 0; | ||
4056 | break; | ||
4057 | |||
4058 | case SIMPLE_BRACKET: | ||
4059 | if (!bitset_contain (node->opr.sbcset, ch)) | ||
4060 | return 0; | ||
4061 | break; | ||
4062 | |||
4063 | #ifdef RE_ENABLE_I18N | ||
4064 | case OP_UTF8_PERIOD: | ||
4065 | if (ch >= 0x80) | ||
4066 | return 0; | ||
4067 | /* FALLTHROUGH */ | ||
4068 | #endif | ||
4069 | case OP_PERIOD: | ||
4070 | if ((ch == '\n' && !(mctx->dfa->syntax & RE_DOT_NEWLINE)) | ||
4071 | || (ch == '\0' && (mctx->dfa->syntax & RE_DOT_NOT_NULL))) | ||
4072 | return 0; | ||
4073 | break; | ||
4074 | |||
4075 | default: | ||
4076 | return 0; | ||
4077 | } | ||
4078 | |||
4079 | if (node->constraint) | ||
4080 | { | ||
4081 | /* The node has constraints. Check whether the current context | ||
4082 | satisfies the constraints. */ | ||
4083 | unsigned int context = re_string_context_at (&mctx->input, idx, | ||
4084 | mctx->eflags); | ||
4085 | if (NOT_SATISFY_NEXT_CONSTRAINT (node->constraint, context)) | ||
4086 | return 0; | ||
4087 | } | ||
4088 | |||
4089 | return 1; | ||
4090 | } | ||
4091 | |||
4092 | /* Extend the buffers, if the buffers have run out. */ | ||
4093 | |||
4094 | static reg_errcode_t | ||
4095 | internal_function | ||
4096 | extend_buffers (re_match_context_t *mctx) | ||
4097 | { | ||
4098 | reg_errcode_t ret; | ||
4099 | re_string_t *pstr = &mctx->input; | ||
4100 | |||
4101 | /* Avoid overflow. */ | ||
4102 | if (BE (INT_MAX / 2 / sizeof (re_dfastate_t *) <= pstr->bufs_len, 0)) | ||
4103 | return REG_ESPACE; | ||
4104 | |||
4105 | /* Double the lengthes of the buffers. */ | ||
4106 | ret = re_string_realloc_buffers (pstr, pstr->bufs_len * 2); | ||
4107 | if (BE (ret != REG_NOERROR, 0)) | ||
4108 | return ret; | ||
4109 | |||
4110 | if (mctx->state_log != NULL) | ||
4111 | { | ||
4112 | /* And double the length of state_log. */ | ||
4113 | /* XXX We have no indication of the size of this buffer. If this | ||
4114 | allocation fail we have no indication that the state_log array | ||
4115 | does not have the right size. */ | ||
4116 | re_dfastate_t **new_array = re_realloc (mctx->state_log, re_dfastate_t *, | ||
4117 | pstr->bufs_len + 1); | ||
4118 | if (BE (new_array == NULL, 0)) | ||
4119 | return REG_ESPACE; | ||
4120 | mctx->state_log = new_array; | ||
4121 | } | ||
4122 | |||
4123 | /* Then reconstruct the buffers. */ | ||
4124 | if (pstr->icase) | ||
4125 | { | ||
4126 | #ifdef RE_ENABLE_I18N | ||
4127 | if (pstr->mb_cur_max > 1) | ||
4128 | { | ||
4129 | ret = build_wcs_upper_buffer (pstr); | ||
4130 | if (BE (ret != REG_NOERROR, 0)) | ||
4131 | return ret; | ||
4132 | } | ||
4133 | else | ||
4134 | #endif /* RE_ENABLE_I18N */ | ||
4135 | build_upper_buffer (pstr); | ||
4136 | } | ||
4137 | else | ||
4138 | { | ||
4139 | #ifdef RE_ENABLE_I18N | ||
4140 | if (pstr->mb_cur_max > 1) | ||
4141 | build_wcs_buffer (pstr); | ||
4142 | else | ||
4143 | #endif /* RE_ENABLE_I18N */ | ||
4144 | { | ||
4145 | if (pstr->trans != NULL) | ||
4146 | re_string_translate_buffer (pstr); | ||
4147 | } | ||
4148 | } | ||
4149 | return REG_NOERROR; | ||
4150 | } | ||
4151 | |||
4152 | |||
4153 | /* Functions for matching context. */ | ||
4154 | |||
4155 | /* Initialize MCTX. */ | ||
4156 | |||
4157 | static reg_errcode_t | ||
4158 | internal_function | ||
4159 | match_ctx_init (re_match_context_t *mctx, int eflags, int n) | ||
4160 | { | ||
4161 | mctx->eflags = eflags; | ||
4162 | mctx->match_last = -1; | ||
4163 | if (n > 0) | ||
4164 | { | ||
4165 | mctx->bkref_ents = re_malloc (struct re_backref_cache_entry, n); | ||
4166 | mctx->sub_tops = re_malloc (re_sub_match_top_t *, n); | ||
4167 | if (BE (mctx->bkref_ents == NULL || mctx->sub_tops == NULL, 0)) | ||
4168 | return REG_ESPACE; | ||
4169 | } | ||
4170 | /* Already zero-ed by the caller. | ||
4171 | else | ||
4172 | mctx->bkref_ents = NULL; | ||
4173 | mctx->nbkref_ents = 0; | ||
4174 | mctx->nsub_tops = 0; */ | ||
4175 | mctx->abkref_ents = n; | ||
4176 | mctx->max_mb_elem_len = 1; | ||
4177 | mctx->asub_tops = n; | ||
4178 | return REG_NOERROR; | ||
4179 | } | ||
4180 | |||
4181 | /* Clean the entries which depend on the current input in MCTX. | ||
4182 | This function must be invoked when the matcher changes the start index | ||
4183 | of the input, or changes the input string. */ | ||
4184 | |||
4185 | static void | ||
4186 | internal_function | ||
4187 | match_ctx_clean (re_match_context_t *mctx) | ||
4188 | { | ||
4189 | int st_idx; | ||
4190 | for (st_idx = 0; st_idx < mctx->nsub_tops; ++st_idx) | ||
4191 | { | ||
4192 | int sl_idx; | ||
4193 | re_sub_match_top_t *top = mctx->sub_tops[st_idx]; | ||
4194 | for (sl_idx = 0; sl_idx < top->nlasts; ++sl_idx) | ||
4195 | { | ||
4196 | re_sub_match_last_t *last = top->lasts[sl_idx]; | ||
4197 | re_free (last->path.array); | ||
4198 | re_free (last); | ||
4199 | } | ||
4200 | re_free (top->lasts); | ||
4201 | if (top->path) | ||
4202 | { | ||
4203 | re_free (top->path->array); | ||
4204 | re_free (top->path); | ||
4205 | } | ||
4206 | free (top); | ||
4207 | } | ||
4208 | |||
4209 | mctx->nsub_tops = 0; | ||
4210 | mctx->nbkref_ents = 0; | ||
4211 | } | ||
4212 | |||
4213 | /* Free all the memory associated with MCTX. */ | ||
4214 | |||
4215 | static void | ||
4216 | internal_function | ||
4217 | match_ctx_free (re_match_context_t *mctx) | ||
4218 | { | ||
4219 | /* First, free all the memory associated with MCTX->SUB_TOPS. */ | ||
4220 | match_ctx_clean (mctx); | ||
4221 | re_free (mctx->sub_tops); | ||
4222 | re_free (mctx->bkref_ents); | ||
4223 | } | ||
4224 | |||
4225 | /* Add a new backreference entry to MCTX. | ||
4226 | Note that we assume that caller never call this function with duplicate | ||
4227 | entry, and call with STR_IDX which isn't smaller than any existing entry. | ||
4228 | */ | ||
4229 | |||
4230 | static reg_errcode_t | ||
4231 | internal_function | ||
4232 | match_ctx_add_entry (re_match_context_t *mctx, int node, int str_idx, int from, | ||
4233 | int to) | ||
4234 | { | ||
4235 | if (mctx->nbkref_ents >= mctx->abkref_ents) | ||
4236 | { | ||
4237 | struct re_backref_cache_entry* new_entry; | ||
4238 | new_entry = re_realloc (mctx->bkref_ents, struct re_backref_cache_entry, | ||
4239 | mctx->abkref_ents * 2); | ||
4240 | if (BE (new_entry == NULL, 0)) | ||
4241 | { | ||
4242 | re_free (mctx->bkref_ents); | ||
4243 | return REG_ESPACE; | ||
4244 | } | ||
4245 | mctx->bkref_ents = new_entry; | ||
4246 | memset (mctx->bkref_ents + mctx->nbkref_ents, '\0', | ||
4247 | sizeof (struct re_backref_cache_entry) * mctx->abkref_ents); | ||
4248 | mctx->abkref_ents *= 2; | ||
4249 | } | ||
4250 | if (mctx->nbkref_ents > 0 | ||
4251 | && mctx->bkref_ents[mctx->nbkref_ents - 1].str_idx == str_idx) | ||
4252 | mctx->bkref_ents[mctx->nbkref_ents - 1].more = 1; | ||
4253 | |||
4254 | mctx->bkref_ents[mctx->nbkref_ents].node = node; | ||
4255 | mctx->bkref_ents[mctx->nbkref_ents].str_idx = str_idx; | ||
4256 | mctx->bkref_ents[mctx->nbkref_ents].subexp_from = from; | ||
4257 | mctx->bkref_ents[mctx->nbkref_ents].subexp_to = to; | ||
4258 | |||
4259 | /* This is a cache that saves negative results of check_dst_limits_calc_pos. | ||
4260 | If bit N is clear, means that this entry won't epsilon-transition to | ||
4261 | an OP_OPEN_SUBEXP or OP_CLOSE_SUBEXP for the N+1-th subexpression. If | ||
4262 | it is set, check_dst_limits_calc_pos_1 will recurse and try to find one | ||
4263 | such node. | ||
4264 | |||
4265 | A backreference does not epsilon-transition unless it is empty, so set | ||
4266 | to all zeros if FROM != TO. */ | ||
4267 | mctx->bkref_ents[mctx->nbkref_ents].eps_reachable_subexps_map | ||
4268 | = (from == to ? ~0 : 0); | ||
4269 | |||
4270 | mctx->bkref_ents[mctx->nbkref_ents++].more = 0; | ||
4271 | if (mctx->max_mb_elem_len < to - from) | ||
4272 | mctx->max_mb_elem_len = to - from; | ||
4273 | return REG_NOERROR; | ||
4274 | } | ||
4275 | |||
4276 | /* Search for the first entry which has the same str_idx, or -1 if none is | ||
4277 | found. Note that MCTX->BKREF_ENTS is already sorted by MCTX->STR_IDX. */ | ||
4278 | |||
4279 | static int | ||
4280 | internal_function | ||
4281 | search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx) | ||
4282 | { | ||
4283 | int left, right, mid, last; | ||
4284 | last = right = mctx->nbkref_ents; | ||
4285 | for (left = 0; left < right;) | ||
4286 | { | ||
4287 | mid = (left + right) / 2; | ||
4288 | if (mctx->bkref_ents[mid].str_idx < str_idx) | ||
4289 | left = mid + 1; | ||
4290 | else | ||
4291 | right = mid; | ||
4292 | } | ||
4293 | if (left < last && mctx->bkref_ents[left].str_idx == str_idx) | ||
4294 | return left; | ||
4295 | else | ||
4296 | return -1; | ||
4297 | } | ||
4298 | |||
4299 | /* Register the node NODE, whose type is OP_OPEN_SUBEXP, and which matches | ||
4300 | at STR_IDX. */ | ||
4301 | |||
4302 | static reg_errcode_t | ||
4303 | internal_function | ||
4304 | match_ctx_add_subtop (re_match_context_t *mctx, int node, int str_idx) | ||
4305 | { | ||
4306 | #ifdef DEBUG | ||
4307 | assert (mctx->sub_tops != NULL); | ||
4308 | assert (mctx->asub_tops > 0); | ||
4309 | #endif | ||
4310 | if (BE (mctx->nsub_tops == mctx->asub_tops, 0)) | ||
4311 | { | ||
4312 | int new_asub_tops = mctx->asub_tops * 2; | ||
4313 | re_sub_match_top_t **new_array = re_realloc (mctx->sub_tops, | ||
4314 | re_sub_match_top_t *, | ||
4315 | new_asub_tops); | ||
4316 | if (BE (new_array == NULL, 0)) | ||
4317 | return REG_ESPACE; | ||
4318 | mctx->sub_tops = new_array; | ||
4319 | mctx->asub_tops = new_asub_tops; | ||
4320 | } | ||
4321 | mctx->sub_tops[mctx->nsub_tops] = calloc (1, sizeof (re_sub_match_top_t)); | ||
4322 | if (BE (mctx->sub_tops[mctx->nsub_tops] == NULL, 0)) | ||
4323 | return REG_ESPACE; | ||
4324 | mctx->sub_tops[mctx->nsub_tops]->node = node; | ||
4325 | mctx->sub_tops[mctx->nsub_tops++]->str_idx = str_idx; | ||
4326 | return REG_NOERROR; | ||
4327 | } | ||
4328 | |||
4329 | /* Register the node NODE, whose type is OP_CLOSE_SUBEXP, and which matches | ||
4330 | at STR_IDX, whose corresponding OP_OPEN_SUBEXP is SUB_TOP. */ | ||
4331 | |||
4332 | static re_sub_match_last_t * | ||
4333 | internal_function | ||
4334 | match_ctx_add_sublast (re_sub_match_top_t *subtop, int node, int str_idx) | ||
4335 | { | ||
4336 | re_sub_match_last_t *new_entry; | ||
4337 | if (BE (subtop->nlasts == subtop->alasts, 0)) | ||
4338 | { | ||
4339 | int new_alasts = 2 * subtop->alasts + 1; | ||
4340 | re_sub_match_last_t **new_array = re_realloc (subtop->lasts, | ||
4341 | re_sub_match_last_t *, | ||
4342 | new_alasts); | ||
4343 | if (BE (new_array == NULL, 0)) | ||
4344 | return NULL; | ||
4345 | subtop->lasts = new_array; | ||
4346 | subtop->alasts = new_alasts; | ||
4347 | } | ||
4348 | new_entry = calloc (1, sizeof (re_sub_match_last_t)); | ||
4349 | if (BE (new_entry != NULL, 1)) | ||
4350 | { | ||
4351 | subtop->lasts[subtop->nlasts] = new_entry; | ||
4352 | new_entry->node = node; | ||
4353 | new_entry->str_idx = str_idx; | ||
4354 | ++subtop->nlasts; | ||
4355 | } | ||
4356 | return new_entry; | ||
4357 | } | ||
4358 | |||
4359 | static void | ||
4360 | internal_function | ||
4361 | sift_ctx_init (re_sift_context_t *sctx, re_dfastate_t **sifted_sts, | ||
4362 | re_dfastate_t **limited_sts, int last_node, int last_str_idx) | ||
4363 | { | ||
4364 | sctx->sifted_states = sifted_sts; | ||
4365 | sctx->limited_states = limited_sts; | ||
4366 | sctx->last_node = last_node; | ||
4367 | sctx->last_str_idx = last_str_idx; | ||
4368 | re_node_set_init_empty (&sctx->limits); | ||
4369 | } | ||
diff --git a/win32/sched.h b/win32/sched.h new file mode 100644 index 000000000..128bfe698 --- /dev/null +++ b/win32/sched.h | |||
@@ -0,0 +1 @@ | |||
static inline void sched_yield(void) {} | |||
diff --git a/win32/select.c b/win32/select.c new file mode 100644 index 000000000..416174b3e --- /dev/null +++ b/win32/select.c | |||
@@ -0,0 +1,573 @@ | |||
1 | /* Emulation for select(2) | ||
2 | Contributed by Paolo Bonzini. | ||
3 | |||
4 | Copyright 2008-2015 Free Software Foundation, Inc. | ||
5 | |||
6 | This file is part of gnulib. | ||
7 | |||
8 | This program is free software; you can redistribute it and/or modify | ||
9 | it under the terms of the GNU General Public License as published by | ||
10 | the Free Software Foundation; either version 2, or (at your option) | ||
11 | any later version. | ||
12 | |||
13 | This program is distributed in the hope that it will be useful, | ||
14 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
16 | GNU General Public License for more details. | ||
17 | |||
18 | You should have received a copy of the GNU General Public License along | ||
19 | with this program; if not, see <http://www.gnu.org/licenses/>. */ | ||
20 | |||
21 | #include <malloc.h> | ||
22 | #include <assert.h> | ||
23 | |||
24 | #if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__ | ||
25 | /* Native Windows. */ | ||
26 | |||
27 | #include <sys/types.h> | ||
28 | #include <errno.h> | ||
29 | #include <limits.h> | ||
30 | |||
31 | #include <winsock2.h> | ||
32 | #include <windows.h> | ||
33 | #include <io.h> | ||
34 | #include <stdio.h> | ||
35 | #include <conio.h> | ||
36 | #include <time.h> | ||
37 | |||
38 | /* Get the overridden 'struct timeval'. */ | ||
39 | #include <sys/time.h> | ||
40 | |||
41 | #undef select | ||
42 | |||
43 | struct bitset { | ||
44 | unsigned char in[FD_SETSIZE / CHAR_BIT]; | ||
45 | unsigned char out[FD_SETSIZE / CHAR_BIT]; | ||
46 | }; | ||
47 | |||
48 | /* Declare data structures for ntdll functions. */ | ||
49 | typedef struct _FILE_PIPE_LOCAL_INFORMATION { | ||
50 | ULONG NamedPipeType; | ||
51 | ULONG NamedPipeConfiguration; | ||
52 | ULONG MaximumInstances; | ||
53 | ULONG CurrentInstances; | ||
54 | ULONG InboundQuota; | ||
55 | ULONG ReadDataAvailable; | ||
56 | ULONG OutboundQuota; | ||
57 | ULONG WriteQuotaAvailable; | ||
58 | ULONG NamedPipeState; | ||
59 | ULONG NamedPipeEnd; | ||
60 | } FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION; | ||
61 | |||
62 | typedef struct _IO_STATUS_BLOCK | ||
63 | { | ||
64 | union { | ||
65 | DWORD Status; | ||
66 | PVOID Pointer; | ||
67 | } u; | ||
68 | ULONG_PTR Information; | ||
69 | } IO_STATUS_BLOCK, *PIO_STATUS_BLOCK; | ||
70 | |||
71 | typedef enum _FILE_INFORMATION_CLASS { | ||
72 | FilePipeLocalInformation = 24 | ||
73 | } FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS; | ||
74 | |||
75 | typedef DWORD (WINAPI *PNtQueryInformationFile) | ||
76 | (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS); | ||
77 | |||
78 | #ifndef PIPE_BUF | ||
79 | #define PIPE_BUF 512 | ||
80 | #endif | ||
81 | |||
82 | static BOOL IsConsoleHandle (HANDLE h) | ||
83 | { | ||
84 | DWORD mode; | ||
85 | return GetConsoleMode (h, &mode) != 0; | ||
86 | } | ||
87 | |||
88 | static BOOL | ||
89 | IsSocketHandle (HANDLE h) | ||
90 | { | ||
91 | WSANETWORKEVENTS ev; | ||
92 | |||
93 | if (IsConsoleHandle (h)) | ||
94 | return FALSE; | ||
95 | |||
96 | /* Under Wine, it seems that getsockopt returns 0 for pipes too. | ||
97 | WSAEnumNetworkEvents instead distinguishes the two correctly. */ | ||
98 | ev.lNetworkEvents = 0xDEADBEEF; | ||
99 | WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev); | ||
100 | return ev.lNetworkEvents != 0xDEADBEEF; | ||
101 | } | ||
102 | |||
103 | /* Compute output fd_sets for libc descriptor FD (whose Windows handle is | ||
104 | H). */ | ||
105 | |||
106 | static int | ||
107 | windows_poll_handle (HANDLE h, int fd, | ||
108 | struct bitset *rbits, | ||
109 | struct bitset *wbits, | ||
110 | struct bitset *xbits) | ||
111 | { | ||
112 | BOOL read, write, except; | ||
113 | int i, ret; | ||
114 | INPUT_RECORD *irbuffer; | ||
115 | DWORD avail, nbuffer; | ||
116 | BOOL bRet; | ||
117 | IO_STATUS_BLOCK iosb; | ||
118 | FILE_PIPE_LOCAL_INFORMATION fpli; | ||
119 | static PNtQueryInformationFile NtQueryInformationFile; | ||
120 | static BOOL once_only; | ||
121 | |||
122 | read = write = except = FALSE; | ||
123 | switch (GetFileType (h)) | ||
124 | { | ||
125 | case FILE_TYPE_DISK: | ||
126 | read = TRUE; | ||
127 | write = TRUE; | ||
128 | break; | ||
129 | |||
130 | case FILE_TYPE_PIPE: | ||
131 | if (!once_only) | ||
132 | { | ||
133 | NtQueryInformationFile = (PNtQueryInformationFile) | ||
134 | GetProcAddress (GetModuleHandle ("ntdll.dll"), | ||
135 | "NtQueryInformationFile"); | ||
136 | once_only = TRUE; | ||
137 | } | ||
138 | |||
139 | if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0) | ||
140 | { | ||
141 | if (avail) | ||
142 | read = TRUE; | ||
143 | } | ||
144 | else if (GetLastError () == ERROR_BROKEN_PIPE) | ||
145 | read = TRUE; | ||
146 | |||
147 | else | ||
148 | { | ||
149 | /* It was the write-end of the pipe. Check if it is writable. | ||
150 | If NtQueryInformationFile fails, optimistically assume the pipe is | ||
151 | writable. This could happen on Windows 9x, where | ||
152 | NtQueryInformationFile is not available, or if we inherit a pipe | ||
153 | that doesn't permit FILE_READ_ATTRIBUTES access on the write end | ||
154 | (I think this should not happen since Windows XP SP2; WINE seems | ||
155 | fine too). Otherwise, ensure that enough space is available for | ||
156 | atomic writes. */ | ||
157 | memset (&iosb, 0, sizeof (iosb)); | ||
158 | memset (&fpli, 0, sizeof (fpli)); | ||
159 | |||
160 | if (!NtQueryInformationFile | ||
161 | || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli), | ||
162 | FilePipeLocalInformation) | ||
163 | || fpli.WriteQuotaAvailable >= PIPE_BUF | ||
164 | || (fpli.OutboundQuota < PIPE_BUF && | ||
165 | fpli.WriteQuotaAvailable == fpli.OutboundQuota)) | ||
166 | write = TRUE; | ||
167 | } | ||
168 | break; | ||
169 | |||
170 | case FILE_TYPE_CHAR: | ||
171 | write = TRUE; | ||
172 | if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | ||
173 | break; | ||
174 | |||
175 | ret = WaitForSingleObject (h, 0); | ||
176 | if (ret == WAIT_OBJECT_0) | ||
177 | { | ||
178 | if (!IsConsoleHandle (h)) | ||
179 | { | ||
180 | read = TRUE; | ||
181 | break; | ||
182 | } | ||
183 | |||
184 | nbuffer = avail = 0; | ||
185 | bRet = GetNumberOfConsoleInputEvents (h, &nbuffer); | ||
186 | |||
187 | /* Screen buffers handles are filtered earlier. */ | ||
188 | assert (bRet); | ||
189 | if (nbuffer == 0) | ||
190 | { | ||
191 | except = TRUE; | ||
192 | break; | ||
193 | } | ||
194 | |||
195 | irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD)); | ||
196 | bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail); | ||
197 | if (!bRet || avail == 0) | ||
198 | { | ||
199 | except = TRUE; | ||
200 | break; | ||
201 | } | ||
202 | |||
203 | for (i = 0; i < avail; i++) | ||
204 | if (irbuffer[i].EventType == KEY_EVENT && | ||
205 | irbuffer[i].Event.KeyEvent.bKeyDown) | ||
206 | read = TRUE; | ||
207 | } | ||
208 | break; | ||
209 | |||
210 | default: | ||
211 | ret = WaitForSingleObject (h, 0); | ||
212 | write = TRUE; | ||
213 | if (ret == WAIT_OBJECT_0) | ||
214 | read = TRUE; | ||
215 | |||
216 | break; | ||
217 | } | ||
218 | |||
219 | ret = 0; | ||
220 | if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | ||
221 | { | ||
222 | rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | ||
223 | ret++; | ||
224 | } | ||
225 | |||
226 | if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | ||
227 | { | ||
228 | wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | ||
229 | ret++; | ||
230 | } | ||
231 | |||
232 | if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1))))) | ||
233 | { | ||
234 | xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1))); | ||
235 | ret++; | ||
236 | } | ||
237 | |||
238 | return ret; | ||
239 | } | ||
240 | |||
241 | int | ||
242 | mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, | ||
243 | struct timeval *timeout) | ||
244 | #undef timeval | ||
245 | { | ||
246 | static struct timeval tv0; | ||
247 | static HANDLE hEvent; | ||
248 | HANDLE h, handle_array[FD_SETSIZE + 2]; | ||
249 | fd_set handle_rfds, handle_wfds, handle_xfds; | ||
250 | struct bitset rbits, wbits, xbits; | ||
251 | unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT]; | ||
252 | DWORD ret, wait_timeout, nhandles, nsock, nbuffer; | ||
253 | MSG msg; | ||
254 | int i, fd, rc; | ||
255 | clock_t tend; | ||
256 | |||
257 | if (nfds > FD_SETSIZE) | ||
258 | nfds = FD_SETSIZE; | ||
259 | |||
260 | if (!timeout) | ||
261 | wait_timeout = INFINITE; | ||
262 | else | ||
263 | { | ||
264 | wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000; | ||
265 | |||
266 | /* select is also used as a portable usleep. */ | ||
267 | if (!rfds && !wfds && !xfds) | ||
268 | { | ||
269 | Sleep (wait_timeout); | ||
270 | return 0; | ||
271 | } | ||
272 | } | ||
273 | |||
274 | if (!hEvent) | ||
275 | hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); | ||
276 | |||
277 | handle_array[0] = hEvent; | ||
278 | nhandles = 1; | ||
279 | nsock = 0; | ||
280 | |||
281 | /* Copy descriptors to bitsets. At the same time, eliminate | ||
282 | bits in the "wrong" direction for console input buffers | ||
283 | and screen buffers, because screen buffers are waitable | ||
284 | and they will block until a character is available. */ | ||
285 | memset (&rbits, 0, sizeof (rbits)); | ||
286 | memset (&wbits, 0, sizeof (wbits)); | ||
287 | memset (&xbits, 0, sizeof (xbits)); | ||
288 | memset (anyfds_in, 0, sizeof (anyfds_in)); | ||
289 | if (rfds) | ||
290 | for (i = 0; i < rfds->fd_count; i++) | ||
291 | { | ||
292 | fd = rfds->fd_array[i]; | ||
293 | h = (HANDLE) _get_osfhandle (fd); | ||
294 | if (IsConsoleHandle (h) | ||
295 | && !GetNumberOfConsoleInputEvents (h, &nbuffer)) | ||
296 | continue; | ||
297 | |||
298 | rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | ||
299 | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | ||
300 | } | ||
301 | else | ||
302 | rfds = (fd_set *) alloca (sizeof (fd_set)); | ||
303 | |||
304 | if (wfds) | ||
305 | for (i = 0; i < wfds->fd_count; i++) | ||
306 | { | ||
307 | fd = wfds->fd_array[i]; | ||
308 | h = (HANDLE) _get_osfhandle (fd); | ||
309 | if (IsConsoleHandle (h) | ||
310 | && GetNumberOfConsoleInputEvents (h, &nbuffer)) | ||
311 | continue; | ||
312 | |||
313 | wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | ||
314 | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | ||
315 | } | ||
316 | else | ||
317 | wfds = (fd_set *) alloca (sizeof (fd_set)); | ||
318 | |||
319 | if (xfds) | ||
320 | for (i = 0; i < xfds->fd_count; i++) | ||
321 | { | ||
322 | fd = xfds->fd_array[i]; | ||
323 | xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | ||
324 | anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1)); | ||
325 | } | ||
326 | else | ||
327 | xfds = (fd_set *) alloca (sizeof (fd_set)); | ||
328 | |||
329 | /* Zero all the fd_sets, including the application's. */ | ||
330 | FD_ZERO (rfds); | ||
331 | FD_ZERO (wfds); | ||
332 | FD_ZERO (xfds); | ||
333 | FD_ZERO (&handle_rfds); | ||
334 | FD_ZERO (&handle_wfds); | ||
335 | FD_ZERO (&handle_xfds); | ||
336 | |||
337 | /* Classify handles. Create fd sets for sockets, poll the others. */ | ||
338 | for (i = 0; i < nfds; i++) | ||
339 | { | ||
340 | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | ||
341 | continue; | ||
342 | |||
343 | h = (HANDLE) _get_osfhandle (i); | ||
344 | if (!h) | ||
345 | { | ||
346 | errno = EBADF; | ||
347 | return -1; | ||
348 | } | ||
349 | |||
350 | if (IsSocketHandle (h)) | ||
351 | { | ||
352 | int requested = FD_CLOSE; | ||
353 | |||
354 | /* See above; socket handles are mapped onto select, but we | ||
355 | need to map descriptors to handles. */ | ||
356 | if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | ||
357 | { | ||
358 | requested |= FD_READ | FD_ACCEPT; | ||
359 | FD_SET ((SOCKET) h, rfds); | ||
360 | FD_SET ((SOCKET) h, &handle_rfds); | ||
361 | } | ||
362 | if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | ||
363 | { | ||
364 | requested |= FD_WRITE | FD_CONNECT; | ||
365 | FD_SET ((SOCKET) h, wfds); | ||
366 | FD_SET ((SOCKET) h, &handle_wfds); | ||
367 | } | ||
368 | if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | ||
369 | { | ||
370 | requested |= FD_OOB; | ||
371 | FD_SET ((SOCKET) h, xfds); | ||
372 | FD_SET ((SOCKET) h, &handle_xfds); | ||
373 | } | ||
374 | |||
375 | WSAEventSelect ((SOCKET) h, hEvent, requested); | ||
376 | nsock++; | ||
377 | } | ||
378 | else | ||
379 | { | ||
380 | handle_array[nhandles++] = h; | ||
381 | |||
382 | /* Poll now. If we get an event, do not wait below. */ | ||
383 | if (wait_timeout != 0 | ||
384 | && windows_poll_handle (h, i, &rbits, &wbits, &xbits)) | ||
385 | wait_timeout = 0; | ||
386 | } | ||
387 | } | ||
388 | |||
389 | /* Place a sentinel at the end of the array. */ | ||
390 | handle_array[nhandles] = NULL; | ||
391 | |||
392 | /* When will the waiting period expire? */ | ||
393 | if (wait_timeout != INFINITE) | ||
394 | tend = clock () + wait_timeout; | ||
395 | |||
396 | restart: | ||
397 | if (wait_timeout == 0 || nsock == 0) | ||
398 | rc = 0; | ||
399 | else | ||
400 | { | ||
401 | /* See if we need to wait in the loop below. If any select is ready, | ||
402 | do MsgWaitForMultipleObjects anyway to dispatch messages, but | ||
403 | no need to call select again. */ | ||
404 | rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); | ||
405 | if (rc == 0) | ||
406 | { | ||
407 | /* Restore the fd_sets for the other select we do below. */ | ||
408 | memcpy (&handle_rfds, rfds, sizeof (fd_set)); | ||
409 | memcpy (&handle_wfds, wfds, sizeof (fd_set)); | ||
410 | memcpy (&handle_xfds, xfds, sizeof (fd_set)); | ||
411 | } | ||
412 | else | ||
413 | wait_timeout = 0; | ||
414 | } | ||
415 | |||
416 | /* How much is left to wait? */ | ||
417 | if (wait_timeout != INFINITE) | ||
418 | { | ||
419 | clock_t tnow = clock (); | ||
420 | if (tend >= tnow) | ||
421 | wait_timeout = tend - tnow; | ||
422 | else | ||
423 | wait_timeout = 0; | ||
424 | } | ||
425 | |||
426 | for (;;) | ||
427 | { | ||
428 | ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE, | ||
429 | wait_timeout, QS_ALLINPUT); | ||
430 | |||
431 | if (ret == WAIT_OBJECT_0 + nhandles) | ||
432 | { | ||
433 | /* new input of some other kind */ | ||
434 | BOOL bRet; | ||
435 | while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0) | ||
436 | { | ||
437 | TranslateMessage (&msg); | ||
438 | DispatchMessage (&msg); | ||
439 | } | ||
440 | } | ||
441 | else | ||
442 | break; | ||
443 | } | ||
444 | |||
445 | /* If we haven't done it yet, check the status of the sockets. */ | ||
446 | if (rc == 0 && nsock > 0) | ||
447 | rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0); | ||
448 | |||
449 | if (nhandles > 1) | ||
450 | { | ||
451 | /* Count results that are not counted in the return value of select. */ | ||
452 | nhandles = 1; | ||
453 | for (i = 0; i < nfds; i++) | ||
454 | { | ||
455 | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | ||
456 | continue; | ||
457 | |||
458 | h = (HANDLE) _get_osfhandle (i); | ||
459 | if (h == handle_array[nhandles]) | ||
460 | { | ||
461 | /* Not a socket. */ | ||
462 | nhandles++; | ||
463 | windows_poll_handle (h, i, &rbits, &wbits, &xbits); | ||
464 | if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) | ||
465 | || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))) | ||
466 | || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | ||
467 | rc++; | ||
468 | } | ||
469 | } | ||
470 | |||
471 | if (rc == 0 | ||
472 | && (wait_timeout == INFINITE | ||
473 | /* If NHANDLES > 1, but no bits are set, it means we've | ||
474 | been told incorrectly that some handle was signaled. | ||
475 | This happens with anonymous pipes, which always cause | ||
476 | MsgWaitForMultipleObjects to exit immediately, but no | ||
477 | data is found ready to be read by windows_poll_handle. | ||
478 | To avoid a total failure (whereby we return zero and | ||
479 | don't wait at all), let's poll in a more busy loop. */ | ||
480 | || (wait_timeout != 0 && nhandles > 1))) | ||
481 | { | ||
482 | /* Sleep 1 millisecond to avoid busy wait and retry with the | ||
483 | original fd_sets. */ | ||
484 | memcpy (&handle_rfds, rfds, sizeof (fd_set)); | ||
485 | memcpy (&handle_wfds, wfds, sizeof (fd_set)); | ||
486 | memcpy (&handle_xfds, xfds, sizeof (fd_set)); | ||
487 | SleepEx (1, TRUE); | ||
488 | goto restart; | ||
489 | } | ||
490 | if (timeout && wait_timeout == 0 && rc == 0) | ||
491 | timeout->tv_sec = timeout->tv_usec = 0; | ||
492 | } | ||
493 | |||
494 | /* Now fill in the results. */ | ||
495 | FD_ZERO (rfds); | ||
496 | FD_ZERO (wfds); | ||
497 | FD_ZERO (xfds); | ||
498 | nhandles = 1; | ||
499 | for (i = 0; i < nfds; i++) | ||
500 | { | ||
501 | if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0) | ||
502 | continue; | ||
503 | |||
504 | h = (HANDLE) _get_osfhandle (i); | ||
505 | if (h != handle_array[nhandles]) | ||
506 | { | ||
507 | /* Perform handle->descriptor mapping. */ | ||
508 | WSAEventSelect ((SOCKET) h, NULL, 0); | ||
509 | if (FD_ISSET (h, &handle_rfds)) | ||
510 | FD_SET (i, rfds); | ||
511 | if (FD_ISSET (h, &handle_wfds)) | ||
512 | FD_SET (i, wfds); | ||
513 | if (FD_ISSET (h, &handle_xfds)) | ||
514 | FD_SET (i, xfds); | ||
515 | } | ||
516 | else | ||
517 | { | ||
518 | /* Not a socket. */ | ||
519 | nhandles++; | ||
520 | if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | ||
521 | FD_SET (i, rfds); | ||
522 | if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | ||
523 | FD_SET (i, wfds); | ||
524 | if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) | ||
525 | FD_SET (i, xfds); | ||
526 | } | ||
527 | } | ||
528 | |||
529 | return rc; | ||
530 | } | ||
531 | |||
532 | #else /* ! Native Windows. */ | ||
533 | |||
534 | #include <sys/select.h> | ||
535 | #include <stddef.h> /* NULL */ | ||
536 | #include <errno.h> | ||
537 | #include <unistd.h> | ||
538 | |||
539 | #undef select | ||
540 | |||
541 | int | ||
542 | rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds, | ||
543 | struct timeval *timeout) | ||
544 | { | ||
545 | int i; | ||
546 | |||
547 | /* FreeBSD 8.2 has a bug: it does not always detect invalid fds. */ | ||
548 | if (nfds < 0 || nfds > FD_SETSIZE) | ||
549 | { | ||
550 | errno = EINVAL; | ||
551 | return -1; | ||
552 | } | ||
553 | for (i = 0; i < nfds; i++) | ||
554 | { | ||
555 | if (((rfds && FD_ISSET (i, rfds)) | ||
556 | || (wfds && FD_ISSET (i, wfds)) | ||
557 | || (xfds && FD_ISSET (i, xfds))) | ||
558 | && dup2 (i, i) != i) | ||
559 | return -1; | ||
560 | } | ||
561 | |||
562 | /* Interix 3.5 has a bug: it does not support nfds == 0. */ | ||
563 | if (nfds == 0) | ||
564 | { | ||
565 | nfds = 1; | ||
566 | rfds = NULL; | ||
567 | wfds = NULL; | ||
568 | xfds = NULL; | ||
569 | } | ||
570 | return select (nfds, rfds, wfds, xfds, timeout); | ||
571 | } | ||
572 | |||
573 | #endif | ||
diff --git a/win32/statfs.c b/win32/statfs.c new file mode 100644 index 000000000..a35c9adea --- /dev/null +++ b/win32/statfs.c | |||
@@ -0,0 +1,80 @@ | |||
1 | #include <sys/vfs.h> | ||
2 | #include "libbb.h" | ||
3 | |||
4 | /* | ||
5 | * Code from libguestfs (with addition of GetVolumeInformation call) | ||
6 | */ | ||
7 | int statfs(const char *file, struct statfs *buf) | ||
8 | { | ||
9 | ULONGLONG free_bytes_available; /* for user - similar to bavail */ | ||
10 | ULONGLONG total_number_of_bytes; | ||
11 | ULONGLONG total_number_of_free_bytes; /* for everyone - bfree */ | ||
12 | DWORD serial, namelen, flags; | ||
13 | char fsname[100]; | ||
14 | struct mntent *mnt; | ||
15 | |||
16 | if ( (mnt=find_mount_point(file, 0)) == NULL ) { | ||
17 | return -1; | ||
18 | } | ||
19 | |||
20 | file = mnt->mnt_dir; | ||
21 | if ( !GetDiskFreeSpaceEx(file, (PULARGE_INTEGER) &free_bytes_available, | ||
22 | (PULARGE_INTEGER) &total_number_of_bytes, | ||
23 | (PULARGE_INTEGER) &total_number_of_free_bytes) ) { | ||
24 | errno = err_win_to_posix(GetLastError()); | ||
25 | return -1; | ||
26 | } | ||
27 | |||
28 | if ( !GetVolumeInformation(file, NULL, 0, &serial, &namelen, &flags, | ||
29 | fsname, 100) ) { | ||
30 | errno = err_win_to_posix(GetLastError()); | ||
31 | return -1; | ||
32 | } | ||
33 | |||
34 | /* XXX I couldn't determine how to get block size. MSDN has a | ||
35 | * unhelpful hard-coded list here: | ||
36 | * http://support.microsoft.com/kb/140365 | ||
37 | * but this depends on the filesystem type, the size of the disk and | ||
38 | * the version of Windows. So this code assumes the disk is NTFS | ||
39 | * and the version of Windows is >= Win2K. | ||
40 | */ | ||
41 | if (total_number_of_bytes < UINT64_C(16) * 1024 * 1024 * 1024 * 1024) | ||
42 | buf->f_bsize = 4096; | ||
43 | else if (total_number_of_bytes < UINT64_C(32) * 1024 * 1024 * 1024 * 1024) | ||
44 | buf->f_bsize = 8192; | ||
45 | else if (total_number_of_bytes < UINT64_C(64) * 1024 * 1024 * 1024 * 1024) | ||
46 | buf->f_bsize = 16384; | ||
47 | else if (total_number_of_bytes < UINT64_C(128) * 1024 * 1024 * 1024 * 1024) | ||
48 | buf->f_bsize = 32768; | ||
49 | else | ||
50 | buf->f_bsize = 65536; | ||
51 | |||
52 | /* | ||
53 | * Valid filesystem names don't seem to be documented. The following | ||
54 | * are present in Wine. | ||
55 | */ | ||
56 | if ( strcmp(fsname, "NTFS") == 0 ) { | ||
57 | buf->f_type = 0x5346544e; | ||
58 | } | ||
59 | else if ( strcmp(fsname, "FAT") == 0 || strcmp(fsname, "FAT32") == 0 ) { | ||
60 | buf->f_type = 0x4006; | ||
61 | } | ||
62 | else if ( strcmp(fsname, "CDFS") == 0 ) { | ||
63 | buf->f_type = 0x9660; | ||
64 | } | ||
65 | else { | ||
66 | buf->f_type = 0; | ||
67 | } | ||
68 | |||
69 | buf->f_frsize = buf->f_bsize; | ||
70 | buf->f_blocks = total_number_of_bytes / buf->f_bsize; | ||
71 | buf->f_bfree = total_number_of_free_bytes / buf->f_bsize; | ||
72 | buf->f_bavail = free_bytes_available / buf->f_bsize; | ||
73 | buf->f_files = UINT32_MAX; | ||
74 | buf->f_ffree = UINT32_MAX; | ||
75 | buf->f_fsid = serial; | ||
76 | buf->f_flag = UINT64_MAX; | ||
77 | buf->f_namelen = namelen; | ||
78 | |||
79 | return 0; | ||
80 | } | ||
diff --git a/win32/strptime.c b/win32/strptime.c new file mode 100644 index 000000000..89fb8b736 --- /dev/null +++ b/win32/strptime.c | |||
@@ -0,0 +1,646 @@ | |||
1 | /* Copyright (C) 2002, 2004-2005, 2007, 2009-2014 Free Software Foundation, | ||
2 | Inc. | ||
3 | This file is part of the GNU C Library. | ||
4 | |||
5 | This program is free software; you can redistribute it and/or modify | ||
6 | it under the terms of the GNU General Public License as published by | ||
7 | the Free Software Foundation; either version 2, or (at your option) | ||
8 | any later version. | ||
9 | |||
10 | This program is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | GNU General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU General Public License along | ||
16 | with this program; if not, see <http://www.gnu.org/licenses/>. */ | ||
17 | |||
18 | /* | ||
19 | * File from gnulib (http://www.gnu.org/software/gnulib/), processed with | ||
20 | * coan source -U_LIBC -U_NL_CURRENT -UHAVE_TM_GMTOFF strptime.c | ||
21 | * and lightly edited. | ||
22 | */ | ||
23 | |||
24 | #include <time.h> | ||
25 | |||
26 | #include <assert.h> | ||
27 | #include <ctype.h> | ||
28 | #include <limits.h> | ||
29 | #include <string.h> | ||
30 | #include <stdbool.h> | ||
31 | |||
32 | |||
33 | enum ptime_locale_status { not, loc, raw }; | ||
34 | |||
35 | |||
36 | |||
37 | #define match_char(ch1, ch2) if (ch1 != ch2) return NULL | ||
38 | /* Oh come on. Get a reasonable compiler. */ | ||
39 | # define match_string(cs1, s2) \ | ||
40 | (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1)) | ||
41 | /* We intentionally do not use isdigit() for testing because this will | ||
42 | lead to problems with the wide character version. */ | ||
43 | #define get_number(from, to, n) \ | ||
44 | do { \ | ||
45 | int __n = n; \ | ||
46 | val = 0; \ | ||
47 | while (*rp == ' ') \ | ||
48 | ++rp; \ | ||
49 | if (*rp < '0' || *rp > '9') \ | ||
50 | return NULL; \ | ||
51 | do { \ | ||
52 | val *= 10; \ | ||
53 | val += *rp++ - '0'; \ | ||
54 | } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \ | ||
55 | if (val < from || val > to) \ | ||
56 | return NULL; \ | ||
57 | } while (0) | ||
58 | # define get_alt_number(from, to, n) \ | ||
59 | /* We don't have the alternate representation. */ \ | ||
60 | get_number(from, to, n) | ||
61 | #define recursive(new_fmt) \ | ||
62 | (*(new_fmt) != '\0' \ | ||
63 | && (rp = __strptime_internal (rp, (new_fmt), tm, \ | ||
64 | decided, era_cnt LOCALE_ARG)) != NULL) | ||
65 | |||
66 | |||
67 | static char const weekday_name[][10] = | ||
68 | { | ||
69 | "Sunday", "Monday", "Tuesday", "Wednesday", | ||
70 | "Thursday", "Friday", "Saturday" | ||
71 | }; | ||
72 | static char const ab_weekday_name[][4] = | ||
73 | { | ||
74 | "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" | ||
75 | }; | ||
76 | static char const month_name[][10] = | ||
77 | { | ||
78 | "January", "February", "March", "April", "May", "June", | ||
79 | "July", "August", "September", "October", "November", "December" | ||
80 | }; | ||
81 | static char const ab_month_name[][4] = | ||
82 | { | ||
83 | "Jan", "Feb", "Mar", "Apr", "May", "Jun", | ||
84 | "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" | ||
85 | }; | ||
86 | # define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y" | ||
87 | # define HERE_D_FMT "%m/%d/%y" | ||
88 | # define HERE_AM_STR "AM" | ||
89 | # define HERE_PM_STR "PM" | ||
90 | # define HERE_T_FMT_AMPM "%I:%M:%S %p" | ||
91 | # define HERE_T_FMT "%H:%M:%S" | ||
92 | |||
93 | static const unsigned short int __mon_yday[2][13] = | ||
94 | { | ||
95 | /* Normal years. */ | ||
96 | { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 }, | ||
97 | /* Leap years. */ | ||
98 | { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 } | ||
99 | }; | ||
100 | |||
101 | # define LOCALE_PARAM | ||
102 | # define LOCALE_ARG | ||
103 | # define LOCALE_PARAM_DECL | ||
104 | # define LOCALE_PARAM_PROTO | ||
105 | # define HELPER_LOCALE_ARG | ||
106 | # define ISSPACE(Ch) isspace (Ch) | ||
107 | |||
108 | |||
109 | |||
110 | |||
111 | #ifndef __isleap | ||
112 | /* Nonzero if YEAR is a leap year (every 4 years, | ||
113 | except every 100th isn't, and every 400th is). */ | ||
114 | # define __isleap(year) \ | ||
115 | ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0)) | ||
116 | #endif | ||
117 | |||
118 | /* Compute the day of the week. */ | ||
119 | static void | ||
120 | day_of_the_week (struct tm *tm) | ||
121 | { | ||
122 | /* We know that January 1st 1970 was a Thursday (= 4). Compute the | ||
123 | difference between this data in the one on TM and so determine | ||
124 | the weekday. */ | ||
125 | int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2); | ||
126 | int wday = (-473 | ||
127 | + (365 * (tm->tm_year - 70)) | ||
128 | + (corr_year / 4) | ||
129 | - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0) | ||
130 | + (((corr_year / 4) / 25) / 4) | ||
131 | + __mon_yday[0][tm->tm_mon] | ||
132 | + tm->tm_mday - 1); | ||
133 | tm->tm_wday = ((wday % 7) + 7) % 7; | ||
134 | } | ||
135 | |||
136 | /* Compute the day of the year. */ | ||
137 | static void | ||
138 | day_of_the_year (struct tm *tm) | ||
139 | { | ||
140 | tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon] | ||
141 | + (tm->tm_mday - 1)); | ||
142 | } | ||
143 | |||
144 | |||
145 | static char * | ||
146 | __strptime_internal (rp, fmt, tm, decided, era_cnt LOCALE_PARAM) | ||
147 | const char *rp; | ||
148 | const char *fmt; | ||
149 | struct tm *tm; | ||
150 | enum ptime_locale_status *decided; | ||
151 | int era_cnt; | ||
152 | LOCALE_PARAM_DECL | ||
153 | { | ||
154 | |||
155 | int cnt; | ||
156 | size_t val; | ||
157 | int have_I, is_pm; | ||
158 | int century, want_century; | ||
159 | int want_era; | ||
160 | int have_wday, want_xday; | ||
161 | int have_yday; | ||
162 | int have_mon, have_mday; | ||
163 | int have_uweek, have_wweek; | ||
164 | int week_no; | ||
165 | |||
166 | have_I = is_pm = 0; | ||
167 | century = -1; | ||
168 | want_century = 0; | ||
169 | want_era = 0; | ||
170 | week_no = 0; | ||
171 | |||
172 | have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0; | ||
173 | have_wweek = 0; | ||
174 | |||
175 | while (*fmt != '\0') | ||
176 | { | ||
177 | /* A white space in the format string matches 0 more or white | ||
178 | space in the input string. */ | ||
179 | if (ISSPACE (*fmt)) | ||
180 | { | ||
181 | while (ISSPACE (*rp)) | ||
182 | ++rp; | ||
183 | ++fmt; | ||
184 | continue; | ||
185 | } | ||
186 | |||
187 | /* Any character but '%' must be matched by the same character | ||
188 | in the iput string. */ | ||
189 | if (*fmt != '%') | ||
190 | { | ||
191 | match_char (*fmt++, *rp++); | ||
192 | continue; | ||
193 | } | ||
194 | |||
195 | ++fmt; | ||
196 | /* We need this for handling the 'E' modifier. */ | ||
197 | start_over: | ||
198 | |||
199 | switch (*fmt++) | ||
200 | { | ||
201 | case '%': | ||
202 | /* Match the '%' character itself. */ | ||
203 | match_char ('%', *rp++); | ||
204 | break; | ||
205 | case 'a': | ||
206 | case 'A': | ||
207 | /* Match day of week. */ | ||
208 | for (cnt = 0; cnt < 7; ++cnt) | ||
209 | { | ||
210 | if (*decided != loc | ||
211 | && (match_string (weekday_name[cnt], rp) | ||
212 | || match_string (ab_weekday_name[cnt], rp))) | ||
213 | { | ||
214 | *decided = raw; | ||
215 | break; | ||
216 | } | ||
217 | } | ||
218 | if (cnt == 7) | ||
219 | /* Does not match a weekday name. */ | ||
220 | return NULL; | ||
221 | tm->tm_wday = cnt; | ||
222 | have_wday = 1; | ||
223 | break; | ||
224 | case 'b': | ||
225 | case 'B': | ||
226 | case 'h': | ||
227 | /* Match month name. */ | ||
228 | for (cnt = 0; cnt < 12; ++cnt) | ||
229 | { | ||
230 | if (match_string (month_name[cnt], rp) | ||
231 | || match_string (ab_month_name[cnt], rp)) | ||
232 | { | ||
233 | *decided = raw; | ||
234 | break; | ||
235 | } | ||
236 | } | ||
237 | if (cnt == 12) | ||
238 | /* Does not match a month name. */ | ||
239 | return NULL; | ||
240 | tm->tm_mon = cnt; | ||
241 | want_xday = 1; | ||
242 | break; | ||
243 | case 'c': | ||
244 | /* Match locale's date and time format. */ | ||
245 | if (!recursive (HERE_D_T_FMT)) | ||
246 | return NULL; | ||
247 | want_xday = 1; | ||
248 | break; | ||
249 | case 'C': | ||
250 | /* Match century number. */ | ||
251 | get_number (0, 99, 2); | ||
252 | century = val; | ||
253 | want_xday = 1; | ||
254 | break; | ||
255 | case 'd': | ||
256 | case 'e': | ||
257 | /* Match day of month. */ | ||
258 | get_number (1, 31, 2); | ||
259 | tm->tm_mday = val; | ||
260 | have_mday = 1; | ||
261 | want_xday = 1; | ||
262 | break; | ||
263 | case 'F': | ||
264 | if (!recursive ("%Y-%m-%d")) | ||
265 | return NULL; | ||
266 | want_xday = 1; | ||
267 | break; | ||
268 | case 'x': | ||
269 | /* Fall through. */ | ||
270 | case 'D': | ||
271 | /* Match standard day format. */ | ||
272 | if (!recursive (HERE_D_FMT)) | ||
273 | return NULL; | ||
274 | want_xday = 1; | ||
275 | break; | ||
276 | case 'k': | ||
277 | case 'H': | ||
278 | /* Match hour in 24-hour clock. */ | ||
279 | get_number (0, 23, 2); | ||
280 | tm->tm_hour = val; | ||
281 | have_I = 0; | ||
282 | break; | ||
283 | case 'l': | ||
284 | /* Match hour in 12-hour clock. GNU extension. */ | ||
285 | case 'I': | ||
286 | /* Match hour in 12-hour clock. */ | ||
287 | get_number (1, 12, 2); | ||
288 | tm->tm_hour = val % 12; | ||
289 | have_I = 1; | ||
290 | break; | ||
291 | case 'j': | ||
292 | /* Match day number of year. */ | ||
293 | get_number (1, 366, 3); | ||
294 | tm->tm_yday = val - 1; | ||
295 | have_yday = 1; | ||
296 | break; | ||
297 | case 'm': | ||
298 | /* Match number of month. */ | ||
299 | get_number (1, 12, 2); | ||
300 | tm->tm_mon = val - 1; | ||
301 | have_mon = 1; | ||
302 | want_xday = 1; | ||
303 | break; | ||
304 | case 'M': | ||
305 | /* Match minute. */ | ||
306 | get_number (0, 59, 2); | ||
307 | tm->tm_min = val; | ||
308 | break; | ||
309 | case 'n': | ||
310 | case 't': | ||
311 | /* Match any white space. */ | ||
312 | while (ISSPACE (*rp)) | ||
313 | ++rp; | ||
314 | break; | ||
315 | case 'p': | ||
316 | /* Match locale's equivalent of AM/PM. */ | ||
317 | if (!match_string (HERE_AM_STR, rp)) | ||
318 | { | ||
319 | if (match_string (HERE_PM_STR, rp)) | ||
320 | is_pm = 1; | ||
321 | else | ||
322 | return NULL; | ||
323 | } | ||
324 | break; | ||
325 | case 'r': | ||
326 | if (!recursive (HERE_T_FMT_AMPM)) | ||
327 | return NULL; | ||
328 | break; | ||
329 | case 'R': | ||
330 | if (!recursive ("%H:%M")) | ||
331 | return NULL; | ||
332 | break; | ||
333 | case 's': | ||
334 | { | ||
335 | /* The number of seconds may be very high so we cannot use | ||
336 | the 'get_number' macro. Instead read the number | ||
337 | character for character and construct the result while | ||
338 | doing this. */ | ||
339 | time_t secs = 0; | ||
340 | if (*rp < '0' || *rp > '9') | ||
341 | /* We need at least one digit. */ | ||
342 | return NULL; | ||
343 | |||
344 | do | ||
345 | { | ||
346 | secs *= 10; | ||
347 | secs += *rp++ - '0'; | ||
348 | } | ||
349 | while (*rp >= '0' && *rp <= '9'); | ||
350 | |||
351 | if (localtime_r (&secs, tm) == NULL) | ||
352 | /* Error in function. */ | ||
353 | return NULL; | ||
354 | } | ||
355 | break; | ||
356 | case 'S': | ||
357 | get_number (0, 61, 2); | ||
358 | tm->tm_sec = val; | ||
359 | break; | ||
360 | case 'X': | ||
361 | /* Fall through. */ | ||
362 | case 'T': | ||
363 | if (!recursive (HERE_T_FMT)) | ||
364 | return NULL; | ||
365 | break; | ||
366 | case 'u': | ||
367 | get_number (1, 7, 1); | ||
368 | tm->tm_wday = val % 7; | ||
369 | have_wday = 1; | ||
370 | break; | ||
371 | case 'g': | ||
372 | get_number (0, 99, 2); | ||
373 | /* XXX This cannot determine any field in TM. */ | ||
374 | break; | ||
375 | case 'G': | ||
376 | if (*rp < '0' || *rp > '9') | ||
377 | return NULL; | ||
378 | /* XXX Ignore the number since we would need some more | ||
379 | information to compute a real date. */ | ||
380 | do | ||
381 | ++rp; | ||
382 | while (*rp >= '0' && *rp <= '9'); | ||
383 | break; | ||
384 | case 'U': | ||
385 | get_number (0, 53, 2); | ||
386 | week_no = val; | ||
387 | have_uweek = 1; | ||
388 | break; | ||
389 | case 'W': | ||
390 | get_number (0, 53, 2); | ||
391 | week_no = val; | ||
392 | have_wweek = 1; | ||
393 | break; | ||
394 | case 'V': | ||
395 | get_number (0, 53, 2); | ||
396 | /* XXX This cannot determine any field in TM without some | ||
397 | information. */ | ||
398 | break; | ||
399 | case 'w': | ||
400 | /* Match number of weekday. */ | ||
401 | get_number (0, 6, 1); | ||
402 | tm->tm_wday = val; | ||
403 | have_wday = 1; | ||
404 | break; | ||
405 | case 'y': | ||
406 | /* Match year within century. */ | ||
407 | get_number (0, 99, 2); | ||
408 | /* The "Year 2000: The Millennium Rollover" paper suggests that | ||
409 | values in the range 69-99 refer to the twentieth century. */ | ||
410 | tm->tm_year = val >= 69 ? val : val + 100; | ||
411 | /* Indicate that we want to use the century, if specified. */ | ||
412 | want_century = 1; | ||
413 | want_xday = 1; | ||
414 | break; | ||
415 | case 'Y': | ||
416 | /* Match year including century number. */ | ||
417 | get_number (0, 9999, 4); | ||
418 | tm->tm_year = val - 1900; | ||
419 | want_century = 0; | ||
420 | want_xday = 1; | ||
421 | break; | ||
422 | case 'Z': | ||
423 | /* XXX How to handle this? */ | ||
424 | break; | ||
425 | case 'z': | ||
426 | /* We recognize two formats: if two digits are given, these | ||
427 | specify hours. If fours digits are used, minutes are | ||
428 | also specified. */ | ||
429 | { | ||
430 | bool neg; | ||
431 | int n; | ||
432 | |||
433 | val = 0; | ||
434 | while (*rp == ' ') | ||
435 | ++rp; | ||
436 | if (*rp != '+' && *rp != '-') | ||
437 | return NULL; | ||
438 | neg = *rp++ == '-'; | ||
439 | n = 0; | ||
440 | while (n < 4 && *rp >= '0' && *rp <= '9') | ||
441 | { | ||
442 | val = val * 10 + *rp++ - '0'; | ||
443 | ++n; | ||
444 | } | ||
445 | if (n == 2) | ||
446 | val *= 100; | ||
447 | else if (n != 4) | ||
448 | /* Only two or four digits recognized. */ | ||
449 | return NULL; | ||
450 | else | ||
451 | { | ||
452 | /* We have to convert the minutes into decimal. */ | ||
453 | if (val % 100 >= 60) | ||
454 | return NULL; | ||
455 | val = (val / 100) * 100 + ((val % 100) * 50) / 30; | ||
456 | } | ||
457 | if (val > 1200) | ||
458 | return NULL; | ||
459 | } | ||
460 | break; | ||
461 | case 'E': | ||
462 | /* We have no information about the era format. Just use | ||
463 | the normal format. */ | ||
464 | if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y' | ||
465 | && *fmt != 'x' && *fmt != 'X') | ||
466 | /* This is an illegal format. */ | ||
467 | return NULL; | ||
468 | |||
469 | goto start_over; | ||
470 | case 'O': | ||
471 | switch (*fmt++) | ||
472 | { | ||
473 | case 'd': | ||
474 | case 'e': | ||
475 | /* Match day of month using alternate numeric symbols. */ | ||
476 | get_alt_number (1, 31, 2); | ||
477 | tm->tm_mday = val; | ||
478 | have_mday = 1; | ||
479 | want_xday = 1; | ||
480 | break; | ||
481 | case 'H': | ||
482 | /* Match hour in 24-hour clock using alternate numeric | ||
483 | symbols. */ | ||
484 | get_alt_number (0, 23, 2); | ||
485 | tm->tm_hour = val; | ||
486 | have_I = 0; | ||
487 | break; | ||
488 | case 'I': | ||
489 | /* Match hour in 12-hour clock using alternate numeric | ||
490 | symbols. */ | ||
491 | get_alt_number (1, 12, 2); | ||
492 | tm->tm_hour = val % 12; | ||
493 | have_I = 1; | ||
494 | break; | ||
495 | case 'm': | ||
496 | /* Match month using alternate numeric symbols. */ | ||
497 | get_alt_number (1, 12, 2); | ||
498 | tm->tm_mon = val - 1; | ||
499 | have_mon = 1; | ||
500 | want_xday = 1; | ||
501 | break; | ||
502 | case 'M': | ||
503 | /* Match minutes using alternate numeric symbols. */ | ||
504 | get_alt_number (0, 59, 2); | ||
505 | tm->tm_min = val; | ||
506 | break; | ||
507 | case 'S': | ||
508 | /* Match seconds using alternate numeric symbols. */ | ||
509 | get_alt_number (0, 61, 2); | ||
510 | tm->tm_sec = val; | ||
511 | break; | ||
512 | case 'U': | ||
513 | get_alt_number (0, 53, 2); | ||
514 | week_no = val; | ||
515 | have_uweek = 1; | ||
516 | break; | ||
517 | case 'W': | ||
518 | get_alt_number (0, 53, 2); | ||
519 | week_no = val; | ||
520 | have_wweek = 1; | ||
521 | break; | ||
522 | case 'V': | ||
523 | get_alt_number (0, 53, 2); | ||
524 | /* XXX This cannot determine any field in TM without | ||
525 | further information. */ | ||
526 | break; | ||
527 | case 'w': | ||
528 | /* Match number of weekday using alternate numeric symbols. */ | ||
529 | get_alt_number (0, 6, 1); | ||
530 | tm->tm_wday = val; | ||
531 | have_wday = 1; | ||
532 | break; | ||
533 | case 'y': | ||
534 | /* Match year within century using alternate numeric symbols. */ | ||
535 | get_alt_number (0, 99, 2); | ||
536 | tm->tm_year = val >= 69 ? val : val + 100; | ||
537 | want_xday = 1; | ||
538 | break; | ||
539 | default: | ||
540 | return NULL; | ||
541 | } | ||
542 | break; | ||
543 | default: | ||
544 | return NULL; | ||
545 | } | ||
546 | } | ||
547 | |||
548 | if (have_I && is_pm) | ||
549 | tm->tm_hour += 12; | ||
550 | |||
551 | if (century != -1) | ||
552 | { | ||
553 | if (want_century) | ||
554 | tm->tm_year = tm->tm_year % 100 + (century - 19) * 100; | ||
555 | else | ||
556 | /* Only the century, but not the year. Strange, but so be it. */ | ||
557 | tm->tm_year = (century - 19) * 100; | ||
558 | } | ||
559 | |||
560 | if (era_cnt != -1) | ||
561 | { | ||
562 | } | ||
563 | else | ||
564 | if (want_era) | ||
565 | { | ||
566 | /* No era found but we have seen an E modifier. Rectify some | ||
567 | values. */ | ||
568 | if (want_century && century == -1 && tm->tm_year < 69) | ||
569 | tm->tm_year += 100; | ||
570 | } | ||
571 | |||
572 | if (want_xday && !have_wday) | ||
573 | { | ||
574 | if ( !(have_mon && have_mday) && have_yday) | ||
575 | { | ||
576 | /* We don't have tm_mon and/or tm_mday, compute them. */ | ||
577 | int t_mon = 0; | ||
578 | while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday) | ||
579 | t_mon++; | ||
580 | if (!have_mon) | ||
581 | tm->tm_mon = t_mon - 1; | ||
582 | if (!have_mday) | ||
583 | tm->tm_mday = | ||
584 | (tm->tm_yday | ||
585 | - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); | ||
586 | } | ||
587 | day_of_the_week (tm); | ||
588 | } | ||
589 | |||
590 | if (want_xday && !have_yday) | ||
591 | day_of_the_year (tm); | ||
592 | |||
593 | if ((have_uweek || have_wweek) && have_wday) | ||
594 | { | ||
595 | int save_wday = tm->tm_wday; | ||
596 | int save_mday = tm->tm_mday; | ||
597 | int save_mon = tm->tm_mon; | ||
598 | int w_offset = have_uweek ? 0 : 1; | ||
599 | |||
600 | tm->tm_mday = 1; | ||
601 | tm->tm_mon = 0; | ||
602 | day_of_the_week (tm); | ||
603 | if (have_mday) | ||
604 | tm->tm_mday = save_mday; | ||
605 | if (have_mon) | ||
606 | tm->tm_mon = save_mon; | ||
607 | |||
608 | if (!have_yday) | ||
609 | tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7 | ||
610 | + (week_no - 1) *7 | ||
611 | + save_wday - w_offset); | ||
612 | |||
613 | if (!have_mday || !have_mon) | ||
614 | { | ||
615 | int t_mon = 0; | ||
616 | while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] | ||
617 | <= tm->tm_yday) | ||
618 | t_mon++; | ||
619 | if (!have_mon) | ||
620 | tm->tm_mon = t_mon - 1; | ||
621 | if (!have_mday) | ||
622 | tm->tm_mday = | ||
623 | (tm->tm_yday | ||
624 | - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1); | ||
625 | } | ||
626 | |||
627 | tm->tm_wday = save_wday; | ||
628 | } | ||
629 | |||
630 | return (char *) rp; | ||
631 | } | ||
632 | |||
633 | |||
634 | char * | ||
635 | strptime (buf, format, tm LOCALE_PARAM) | ||
636 | const char *buf; | ||
637 | const char *format; | ||
638 | struct tm *tm; | ||
639 | LOCALE_PARAM_DECL | ||
640 | { | ||
641 | enum ptime_locale_status decided; | ||
642 | |||
643 | decided = raw; | ||
644 | return __strptime_internal (buf, format, tm, &decided, -1 LOCALE_ARG); | ||
645 | } | ||
646 | |||
diff --git a/win32/sys/ioctl.h b/win32/sys/ioctl.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/ioctl.h | |||
diff --git a/win32/sys/mman.h b/win32/sys/mman.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/mman.h | |||
diff --git a/win32/sys/prctl.h b/win32/sys/prctl.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/prctl.h | |||
diff --git a/win32/sys/resource.h b/win32/sys/resource.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/resource.h | |||
diff --git a/win32/sys/socket.h b/win32/sys/socket.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/socket.h | |||
diff --git a/win32/sys/statfs.h b/win32/sys/statfs.h new file mode 100644 index 000000000..498f41e50 --- /dev/null +++ b/win32/sys/statfs.h | |||
@@ -0,0 +1,22 @@ | |||
1 | #ifndef _SYS_STATFS_H | ||
2 | #define _SYS_STATFS_H 1 | ||
3 | |||
4 | #include <stdint.h> | ||
5 | |||
6 | struct statfs { | ||
7 | int f_type; | ||
8 | uint64_t f_bsize; | ||
9 | uint64_t f_frsize; | ||
10 | uint64_t f_blocks; | ||
11 | uint64_t f_bfree; | ||
12 | uint64_t f_bavail; | ||
13 | uint64_t f_files; | ||
14 | uint64_t f_ffree; | ||
15 | uint64_t f_fsid; | ||
16 | uint64_t f_flag; | ||
17 | uint64_t f_namelen; | ||
18 | }; | ||
19 | |||
20 | extern int statfs(const char *file, struct statfs *buf); | ||
21 | |||
22 | #endif | ||
diff --git a/win32/sys/syscall.h b/win32/sys/syscall.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/syscall.h | |||
diff --git a/win32/sys/sysmacros.h b/win32/sys/sysmacros.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/sysmacros.h | |||
diff --git a/win32/sys/times.h b/win32/sys/times.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/times.h | |||
diff --git a/win32/sys/un.h b/win32/sys/un.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/un.h | |||
diff --git a/win32/sys/utsname.h b/win32/sys/utsname.h new file mode 100644 index 000000000..6f12efd58 --- /dev/null +++ b/win32/sys/utsname.h | |||
@@ -0,0 +1,66 @@ | |||
1 | /* Copyright (C) 1991,92,94,96,97,99,2002 Free Software Foundation, Inc. | ||
2 | This file is part of the GNU C Library. | ||
3 | |||
4 | The GNU C Library is free software; you can redistribute it and/or | ||
5 | modify it under the terms of the GNU Lesser General Public | ||
6 | License as published by the Free Software Foundation; either | ||
7 | version 2.1 of the License, or (at your option) any later version. | ||
8 | |||
9 | The GNU C Library is distributed in the hope that it will be useful, | ||
10 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
12 | Lesser General Public License for more details. | ||
13 | |||
14 | You should have received a copy of the GNU Lesser General Public | ||
15 | License along with the GNU C Library; if not, write to the Free | ||
16 | Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA | ||
17 | 02111-1307 USA. */ | ||
18 | |||
19 | /* | ||
20 | * POSIX Standard: 4.4 System Identification <sys/utsname.h> | ||
21 | */ | ||
22 | |||
23 | #ifndef _SYS_UTSNAME_H | ||
24 | #define _SYS_UTSNAME_H 1 | ||
25 | |||
26 | #define _UTSNAME_LENGTH 65 | ||
27 | |||
28 | #ifndef _UTSNAME_SYSNAME_LENGTH | ||
29 | # define _UTSNAME_SYSNAME_LENGTH _UTSNAME_LENGTH | ||
30 | #endif | ||
31 | #ifndef _UTSNAME_NODENAME_LENGTH | ||
32 | # define _UTSNAME_NODENAME_LENGTH _UTSNAME_LENGTH | ||
33 | #endif | ||
34 | #ifndef _UTSNAME_RELEASE_LENGTH | ||
35 | # define _UTSNAME_RELEASE_LENGTH _UTSNAME_LENGTH | ||
36 | #endif | ||
37 | #ifndef _UTSNAME_VERSION_LENGTH | ||
38 | # define _UTSNAME_VERSION_LENGTH _UTSNAME_LENGTH | ||
39 | #endif | ||
40 | #ifndef _UTSNAME_MACHINE_LENGTH | ||
41 | # define _UTSNAME_MACHINE_LENGTH _UTSNAME_LENGTH | ||
42 | #endif | ||
43 | |||
44 | /* Structure describing the system and machine. */ | ||
45 | struct utsname | ||
46 | { | ||
47 | /* Name of the implementation of the operating system. */ | ||
48 | char sysname[_UTSNAME_SYSNAME_LENGTH]; | ||
49 | |||
50 | /* Name of this node on the network. */ | ||
51 | char nodename[_UTSNAME_NODENAME_LENGTH]; | ||
52 | |||
53 | /* Current release level of this implementation. */ | ||
54 | char release[_UTSNAME_RELEASE_LENGTH]; | ||
55 | /* Current version level of this release. */ | ||
56 | char version[_UTSNAME_VERSION_LENGTH]; | ||
57 | |||
58 | /* Name of the hardware type the system is running on. */ | ||
59 | char machine[_UTSNAME_MACHINE_LENGTH]; | ||
60 | }; | ||
61 | |||
62 | /* Put information about the system in NAME. */ | ||
63 | extern int uname (struct utsname *__name); | ||
64 | |||
65 | |||
66 | #endif /* sys/utsname.h */ | ||
diff --git a/win32/sys/vfs.h b/win32/sys/vfs.h new file mode 100644 index 000000000..a899db276 --- /dev/null +++ b/win32/sys/vfs.h | |||
@@ -0,0 +1 @@ | |||
#include <sys/statfs.h> | |||
diff --git a/win32/sys/wait.h b/win32/sys/wait.h new file mode 100644 index 000000000..e69de29bb --- /dev/null +++ b/win32/sys/wait.h | |||
diff --git a/win32/system.c b/win32/system.c new file mode 100644 index 000000000..02aaaa0a1 --- /dev/null +++ b/win32/system.c | |||
@@ -0,0 +1,22 @@ | |||
1 | #include "libbb.h" | ||
2 | |||
3 | int mingw_system(const char *cmd) | ||
4 | { | ||
5 | const char *argv[4] = { "sh", "-c", cmd, NULL }; | ||
6 | intptr_t proc; | ||
7 | HANDLE h; | ||
8 | DWORD ret = 0; | ||
9 | |||
10 | if (cmd == NULL) | ||
11 | return 1; | ||
12 | |||
13 | if ((proc=mingw_spawn_proc((char **)argv)) == -1) | ||
14 | return -1; | ||
15 | |||
16 | h = (HANDLE)proc; | ||
17 | WaitForSingleObject(h, INFINITE); | ||
18 | GetExitCodeProcess(h, &ret); | ||
19 | CloseHandle(h); | ||
20 | |||
21 | return ret << 8; | ||
22 | } | ||
diff --git a/win32/termios.c b/win32/termios.c new file mode 100644 index 000000000..658af4a26 --- /dev/null +++ b/win32/termios.c | |||
@@ -0,0 +1,83 @@ | |||
1 | #include "libbb.h" | ||
2 | |||
3 | int tcsetattr(int fd UNUSED_PARAM, int mode UNUSED_PARAM, const struct termios *t UNUSED_PARAM) | ||
4 | { | ||
5 | return -1; | ||
6 | } | ||
7 | |||
8 | int tcgetattr(int fd UNUSED_PARAM, struct termios *t UNUSED_PARAM) | ||
9 | { | ||
10 | return -1; | ||
11 | } | ||
12 | |||
13 | int64_t FAST_FUNC read_key(int fd, char *buf UNUSED_PARAM, int timeout) | ||
14 | { | ||
15 | HANDLE cin = GetStdHandle(STD_INPUT_HANDLE); | ||
16 | INPUT_RECORD record; | ||
17 | DWORD nevent_out, mode; | ||
18 | int ret = -1; | ||
19 | char *s; | ||
20 | |||
21 | if (fd != 0) | ||
22 | bb_error_msg_and_die("read_key only works on stdin"); | ||
23 | if (cin == INVALID_HANDLE_VALUE) | ||
24 | return -1; | ||
25 | GetConsoleMode(cin, &mode); | ||
26 | SetConsoleMode(cin, 0); | ||
27 | |||
28 | if (timeout > 0) { | ||
29 | if (WaitForSingleObject(cin, timeout) != WAIT_OBJECT_0) | ||
30 | goto done; | ||
31 | } | ||
32 | while (1) { | ||
33 | if (!ReadConsoleInput(cin, &record, 1, &nevent_out)) | ||
34 | goto done; | ||
35 | if (record.EventType != KEY_EVENT || !record.Event.KeyEvent.bKeyDown) | ||
36 | continue; | ||
37 | if (!record.Event.KeyEvent.uChar.AsciiChar) { | ||
38 | DWORD state = record.Event.KeyEvent.dwControlKeyState; | ||
39 | |||
40 | if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) && | ||
41 | (record.Event.KeyEvent.wVirtualKeyCode >= 'A' && | ||
42 | record.Event.KeyEvent.wVirtualKeyCode <= 'Z')) { | ||
43 | ret = record.Event.KeyEvent.wVirtualKeyCode & ~0x40; | ||
44 | break; | ||
45 | } | ||
46 | |||
47 | switch (record.Event.KeyEvent.wVirtualKeyCode) { | ||
48 | case VK_DELETE: ret = KEYCODE_DELETE; goto done; | ||
49 | case VK_INSERT: ret = KEYCODE_INSERT; goto done; | ||
50 | case VK_UP: ret = KEYCODE_UP; goto done; | ||
51 | case VK_DOWN: ret = KEYCODE_DOWN; goto done; | ||
52 | case VK_RIGHT: | ||
53 | if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) { | ||
54 | ret = KEYCODE_CTRL_RIGHT; | ||
55 | goto done; | ||
56 | } | ||
57 | ret = KEYCODE_RIGHT; | ||
58 | goto done; | ||
59 | case VK_LEFT: | ||
60 | if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) { | ||
61 | ret = KEYCODE_CTRL_LEFT; | ||
62 | goto done; | ||
63 | } | ||
64 | ret = KEYCODE_LEFT; | ||
65 | goto done; | ||
66 | case VK_HOME: ret = KEYCODE_HOME; goto done; | ||
67 | case VK_END: ret = KEYCODE_END; goto done; | ||
68 | case VK_PRIOR: ret = KEYCODE_PAGEUP; goto done; | ||
69 | case VK_NEXT: ret = KEYCODE_PAGEDOWN; goto done; | ||
70 | } | ||
71 | continue; | ||
72 | } | ||
73 | if ( (record.Event.KeyEvent.uChar.AsciiChar & 0x80) == 0x80 ) { | ||
74 | s = &record.Event.KeyEvent.uChar.AsciiChar; | ||
75 | OemToCharBuff(s, s, 1); | ||
76 | } | ||
77 | ret = record.Event.KeyEvent.uChar.AsciiChar; | ||
78 | break; | ||
79 | } | ||
80 | done: | ||
81 | SetConsoleMode(cin, mode); | ||
82 | return ret; | ||
83 | } | ||
diff --git a/win32/termios.h b/win32/termios.h new file mode 100644 index 000000000..011a37eb9 --- /dev/null +++ b/win32/termios.h | |||
@@ -0,0 +1,129 @@ | |||
1 | /* iflag bits */ | ||
2 | #define IGNBRK 0x00001 | ||
3 | #define BRKINT 0x00002 | ||
4 | #define IGNPAR 0x00004 | ||
5 | #define IMAXBEL 0x00008 | ||
6 | #define INPCK 0x00010 | ||
7 | #define ISTRIP 0x00020 | ||
8 | #define INLCR 0x00040 | ||
9 | #define IGNCR 0x00080 | ||
10 | #define ICRNL 0x00100 | ||
11 | #define IXON 0x00400 | ||
12 | #define IXOFF 0x01000 | ||
13 | #define IUCLC 0x04000 | ||
14 | #define IXANY 0x08000 | ||
15 | #define PARMRK 0x10000 | ||
16 | |||
17 | /* oflag bits */ | ||
18 | |||
19 | #define OPOST 0x00001 | ||
20 | #define OLCUC 0x00002 | ||
21 | #define OCRNL 0x00004 | ||
22 | #define ONLCR 0x00008 | ||
23 | #define ONOCR 0x00010 | ||
24 | #define ONLRET 0x00020 | ||
25 | #define OFILL 0x00040 | ||
26 | #define CRDLY 0x00180 | ||
27 | #define CR0 0x00000 | ||
28 | #define CR1 0x00080 | ||
29 | #define CR2 0x00100 | ||
30 | #define CR3 0x00180 | ||
31 | #define NLDLY 0x00200 | ||
32 | #define NL0 0x00000 | ||
33 | #define NL1 0x00200 | ||
34 | #define BSDLY 0x00400 | ||
35 | #define BS0 0x00000 | ||
36 | #define BS1 0x00400 | ||
37 | #define TABDLY 0x01800 | ||
38 | #define TAB0 0x00000 | ||
39 | #define TAB1 0x00800 | ||
40 | #define TAB2 0x01000 | ||
41 | #define TAB3 0x01800 | ||
42 | #define XTABS 0x01800 | ||
43 | #define VTDLY 0x02000 | ||
44 | #define VT0 0x00000 | ||
45 | #define VT1 0x02000 | ||
46 | #define FFDLY 0x04000 | ||
47 | #define FF0 0x00000 | ||
48 | #define FF1 0x04000 | ||
49 | #define OFDEL 0x08000 | ||
50 | |||
51 | /* lflag bits */ | ||
52 | #define ISIG 0x0001 | ||
53 | #define ICANON 0x0002 | ||
54 | #define ECHO 0x0004 | ||
55 | #define ECHOE 0x0008 | ||
56 | #define ECHOK 0x0010 | ||
57 | #define ECHONL 0x0020 | ||
58 | #define NOFLSH 0x0040 | ||
59 | #define TOSTOP 0x0080 | ||
60 | #define IEXTEN 0x0100 | ||
61 | #define FLUSHO 0x0200 | ||
62 | #define ECHOKE 0x0400 | ||
63 | #define ECHOCTL 0x0800 | ||
64 | |||
65 | #define VDISCARD 1 | ||
66 | #define VEOL 2 | ||
67 | #define VEOL2 3 | ||
68 | #define VEOF 4 | ||
69 | #define VERASE 5 | ||
70 | #define VINTR 6 | ||
71 | #define VKILL 7 | ||
72 | #define VLNEXT 8 | ||
73 | #define VMIN 9 | ||
74 | #define VQUIT 10 | ||
75 | #define VREPRINT 11 | ||
76 | #define VSTART 12 | ||
77 | #define VSTOP 13 | ||
78 | #define VSUSP 14 | ||
79 | #define VSWTC 15 | ||
80 | #define VTIME 16 | ||
81 | #define VWERASE 17 | ||
82 | |||
83 | #define TCIFLUSH 0 | ||
84 | #define TCSAFLUSH 1 | ||
85 | #define TCSANOW 2 | ||
86 | #define TCSADRAIN 3 | ||
87 | #define TCSADFLUSH 4 | ||
88 | |||
89 | #define B0 0000000 /* hang up */ | ||
90 | #define B50 0000001 | ||
91 | #define B75 0000002 | ||
92 | #define B110 0000003 | ||
93 | #define B134 0000004 | ||
94 | #define B150 0000005 | ||
95 | #define B200 0000006 | ||
96 | #define B300 0000007 | ||
97 | #define B600 0000010 | ||
98 | #define B1200 0000011 | ||
99 | #define B1800 0000012 | ||
100 | #define B2400 0000013 | ||
101 | #define B4800 0000014 | ||
102 | #define B9600 0000015 | ||
103 | |||
104 | typedef unsigned char cc_t; | ||
105 | typedef unsigned int tcflag_t; | ||
106 | typedef unsigned int speed_t; | ||
107 | typedef unsigned short otcflag_t; | ||
108 | typedef unsigned char ospeed_t; | ||
109 | |||
110 | #define NCCS 18 | ||
111 | struct termios { | ||
112 | tcflag_t c_iflag; | ||
113 | tcflag_t c_oflag; | ||
114 | tcflag_t c_cflag; | ||
115 | tcflag_t c_lflag; | ||
116 | char c_line; | ||
117 | cc_t c_cc[NCCS]; | ||
118 | speed_t c_ispeed; | ||
119 | speed_t c_ospeed; | ||
120 | }; | ||
121 | |||
122 | struct winsize { | ||
123 | unsigned short ws_row, ws_col; | ||
124 | unsigned short ws_xpixel, ws_ypixel; | ||
125 | }; | ||
126 | |||
127 | int tcflush(int fd, int queue_selector); | ||
128 | int tcgetattr(int fd, struct termios *t); | ||
129 | int tcsetattr(int fd, int mode, const struct termios *t); | ||
diff --git a/win32/uname.c b/win32/uname.c new file mode 100644 index 000000000..3b3e21f8d --- /dev/null +++ b/win32/uname.c | |||
@@ -0,0 +1,48 @@ | |||
1 | #include "libbb.h" | ||
2 | /* After libbb.h, since it needs sys/types.h on some systems */ | ||
3 | #include <sys/utsname.h> | ||
4 | |||
5 | int uname(struct utsname *name) | ||
6 | { | ||
7 | const char *unk = "unknown"; | ||
8 | OSVERSIONINFO os_info; | ||
9 | SYSTEM_INFO sys_info; | ||
10 | |||
11 | strcpy(name->sysname, "Windows_NT"); | ||
12 | |||
13 | if ( gethostname(name->nodename, sizeof(name->nodename)) != 0 ) { | ||
14 | strcpy(name->nodename, unk); | ||
15 | } | ||
16 | |||
17 | memset(&os_info, 0, sizeof(OSVERSIONINFO)); | ||
18 | os_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); | ||
19 | |||
20 | strcpy(name->release, unk); | ||
21 | strcpy(name->version, unk); | ||
22 | if (GetVersionEx(&os_info)) { | ||
23 | sprintf(name->release, "%u.%u", (unsigned int)os_info.dwMajorVersion, | ||
24 | (unsigned int)os_info.dwMinorVersion); | ||
25 | sprintf(name->version, "%u", (unsigned int)os_info.dwBuildNumber); | ||
26 | } | ||
27 | |||
28 | strcpy(name->machine, unk); | ||
29 | GetSystemInfo(&sys_info); | ||
30 | switch (sys_info.wProcessorArchitecture) { | ||
31 | case PROCESSOR_ARCHITECTURE_AMD64: | ||
32 | strcpy(name->machine, "x86_64"); | ||
33 | break; | ||
34 | case PROCESSOR_ARCHITECTURE_IA64: | ||
35 | strcpy(name->machine, "ia64"); | ||
36 | break; | ||
37 | case PROCESSOR_ARCHITECTURE_INTEL: | ||
38 | if (sys_info.wProcessorLevel < 6) { | ||
39 | strcpy(name->machine, "i386"); | ||
40 | } | ||
41 | else { | ||
42 | strcpy(name->machine, "i686"); | ||
43 | } | ||
44 | break; | ||
45 | } | ||
46 | |||
47 | return 0; | ||
48 | } | ||
diff --git a/win32/winansi.c b/win32/winansi.c new file mode 100644 index 000000000..7c7e1a626 --- /dev/null +++ b/win32/winansi.c | |||
@@ -0,0 +1,735 @@ | |||
1 | /* | ||
2 | * Copyright 2008 Peter Harris <git@peter.is-a-geek.org> | ||
3 | */ | ||
4 | |||
5 | #include "libbb.h" | ||
6 | #include <windows.h> | ||
7 | #undef PACKED | ||
8 | |||
9 | /* | ||
10 | Functions to be wrapped: | ||
11 | */ | ||
12 | #undef vfprintf | ||
13 | #undef vprintf | ||
14 | #undef printf | ||
15 | #undef fprintf | ||
16 | #undef fputs | ||
17 | #undef putchar | ||
18 | #undef fwrite | ||
19 | #undef puts | ||
20 | #undef write | ||
21 | #undef read | ||
22 | #undef getc | ||
23 | |||
24 | /* | ||
25 | ANSI codes used by git: m, K | ||
26 | |||
27 | This file is git-specific. Therefore, this file does not attempt | ||
28 | to implement any codes that are not used by git. | ||
29 | */ | ||
30 | |||
31 | static HANDLE console; | ||
32 | static HANDLE console_in; | ||
33 | static WORD plain_attr; | ||
34 | static WORD attr; | ||
35 | static int negative; | ||
36 | |||
37 | static void init(void) | ||
38 | { | ||
39 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
40 | |||
41 | static int initialized = 0; | ||
42 | if (initialized) | ||
43 | return; | ||
44 | |||
45 | console_in = GetStdHandle(STD_INPUT_HANDLE); | ||
46 | if (console_in == INVALID_HANDLE_VALUE) | ||
47 | console_in = NULL; | ||
48 | |||
49 | console = GetStdHandle(STD_OUTPUT_HANDLE); | ||
50 | if (console == INVALID_HANDLE_VALUE) | ||
51 | console = NULL; | ||
52 | |||
53 | if (!console) | ||
54 | return; | ||
55 | |||
56 | GetConsoleScreenBufferInfo(console, &sbi); | ||
57 | attr = plain_attr = sbi.wAttributes; | ||
58 | negative = 0; | ||
59 | |||
60 | initialized = 1; | ||
61 | } | ||
62 | |||
63 | static int skip_ansi_emulation(void) | ||
64 | { | ||
65 | static char *var = NULL; | ||
66 | static int got_var = FALSE; | ||
67 | |||
68 | if (!got_var) { | ||
69 | var = getenv("BB_SKIP_ANSI_EMULATION"); | ||
70 | got_var = TRUE; | ||
71 | } | ||
72 | |||
73 | return var != NULL; | ||
74 | } | ||
75 | |||
76 | |||
77 | #define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE) | ||
78 | #define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE) | ||
79 | |||
80 | static void set_console_attr(void) | ||
81 | { | ||
82 | WORD attributes = attr; | ||
83 | if (negative) { | ||
84 | attributes &= ~FOREGROUND_ALL; | ||
85 | attributes &= ~BACKGROUND_ALL; | ||
86 | |||
87 | /* This could probably use a bitmask | ||
88 | instead of a series of ifs */ | ||
89 | if (attr & FOREGROUND_RED) | ||
90 | attributes |= BACKGROUND_RED; | ||
91 | if (attr & FOREGROUND_GREEN) | ||
92 | attributes |= BACKGROUND_GREEN; | ||
93 | if (attr & FOREGROUND_BLUE) | ||
94 | attributes |= BACKGROUND_BLUE; | ||
95 | |||
96 | if (attr & BACKGROUND_RED) | ||
97 | attributes |= FOREGROUND_RED; | ||
98 | if (attr & BACKGROUND_GREEN) | ||
99 | attributes |= FOREGROUND_GREEN; | ||
100 | if (attr & BACKGROUND_BLUE) | ||
101 | attributes |= FOREGROUND_BLUE; | ||
102 | } | ||
103 | SetConsoleTextAttribute(console, attributes); | ||
104 | } | ||
105 | |||
106 | static void erase_in_line(void) | ||
107 | { | ||
108 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
109 | DWORD dummy; /* Needed for Windows 7 (or Vista) regression */ | ||
110 | |||
111 | if (!console) | ||
112 | return; | ||
113 | |||
114 | GetConsoleScreenBufferInfo(console, &sbi); | ||
115 | FillConsoleOutputCharacterA(console, ' ', | ||
116 | sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, | ||
117 | &dummy); | ||
118 | FillConsoleOutputAttribute(console, plain_attr, | ||
119 | sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition, | ||
120 | &dummy); | ||
121 | } | ||
122 | |||
123 | static void erase_till_end_of_screen(void) | ||
124 | { | ||
125 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
126 | DWORD dummy, len; | ||
127 | |||
128 | if (!console) | ||
129 | return; | ||
130 | |||
131 | GetConsoleScreenBufferInfo(console, &sbi); | ||
132 | len = sbi.dwSize.X - sbi.dwCursorPosition.X + | ||
133 | sbi.dwSize.X * (sbi.srWindow.Bottom - sbi.dwCursorPosition.Y); | ||
134 | |||
135 | FillConsoleOutputCharacterA(console, ' ', len, sbi.dwCursorPosition, | ||
136 | &dummy); | ||
137 | FillConsoleOutputAttribute(console, plain_attr, len, sbi.dwCursorPosition, | ||
138 | &dummy); | ||
139 | } | ||
140 | |||
141 | void reset_screen(void) | ||
142 | { | ||
143 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
144 | COORD pos; | ||
145 | DWORD dummy, len; | ||
146 | |||
147 | if (!console) | ||
148 | return; | ||
149 | |||
150 | /* move to start of screen buffer and clear it all */ | ||
151 | GetConsoleScreenBufferInfo(console, &sbi); | ||
152 | pos.X = 0; | ||
153 | pos.Y = 0; | ||
154 | SetConsoleCursorPosition(console, pos); | ||
155 | len = sbi.dwSize.X * sbi.dwSize.Y; | ||
156 | FillConsoleOutputCharacterA(console, ' ', len, pos, &dummy); | ||
157 | FillConsoleOutputAttribute(console, plain_attr, len, pos, &dummy); | ||
158 | } | ||
159 | |||
160 | void move_cursor_row(int n) | ||
161 | { | ||
162 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
163 | |||
164 | if (!console) | ||
165 | return; | ||
166 | |||
167 | GetConsoleScreenBufferInfo(console, &sbi); | ||
168 | sbi.dwCursorPosition.Y += n; | ||
169 | SetConsoleCursorPosition(console, sbi.dwCursorPosition); | ||
170 | } | ||
171 | |||
172 | static void move_cursor_column(int n) | ||
173 | { | ||
174 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
175 | |||
176 | if (!console) | ||
177 | return; | ||
178 | |||
179 | GetConsoleScreenBufferInfo(console, &sbi); | ||
180 | sbi.dwCursorPosition.X += n; | ||
181 | SetConsoleCursorPosition(console, sbi.dwCursorPosition); | ||
182 | } | ||
183 | |||
184 | static void move_cursor(int x, int y) | ||
185 | { | ||
186 | COORD pos; | ||
187 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
188 | |||
189 | if (!console) | ||
190 | return; | ||
191 | |||
192 | GetConsoleScreenBufferInfo(console, &sbi); | ||
193 | pos.X = sbi.srWindow.Left + x; | ||
194 | pos.Y = sbi.srWindow.Top + y; | ||
195 | SetConsoleCursorPosition(console, pos); | ||
196 | } | ||
197 | |||
198 | static const char *set_attr(const char *str) | ||
199 | { | ||
200 | const char *func; | ||
201 | size_t len = strspn(str, "0123456789;"); | ||
202 | func = str + len; | ||
203 | |||
204 | switch (*func) { | ||
205 | case 'm': | ||
206 | do { | ||
207 | long val = strtol(str, (char **)&str, 10); | ||
208 | switch (val) { | ||
209 | case 0: /* reset */ | ||
210 | attr = plain_attr; | ||
211 | negative = 0; | ||
212 | break; | ||
213 | case 1: /* bold */ | ||
214 | attr |= FOREGROUND_INTENSITY; | ||
215 | break; | ||
216 | case 2: /* faint */ | ||
217 | case 22: /* normal */ | ||
218 | attr &= ~FOREGROUND_INTENSITY; | ||
219 | break; | ||
220 | case 3: /* italic */ | ||
221 | /* Unsupported */ | ||
222 | break; | ||
223 | case 4: /* underline */ | ||
224 | case 21: /* double underline */ | ||
225 | /* Wikipedia says this flag does nothing */ | ||
226 | /* Furthermore, mingw doesn't define this flag | ||
227 | attr |= COMMON_LVB_UNDERSCORE; */ | ||
228 | break; | ||
229 | case 24: /* no underline */ | ||
230 | /* attr &= ~COMMON_LVB_UNDERSCORE; */ | ||
231 | break; | ||
232 | case 5: /* slow blink */ | ||
233 | case 6: /* fast blink */ | ||
234 | /* We don't have blink, but we do have | ||
235 | background intensity */ | ||
236 | attr |= BACKGROUND_INTENSITY; | ||
237 | break; | ||
238 | case 25: /* no blink */ | ||
239 | attr &= ~BACKGROUND_INTENSITY; | ||
240 | break; | ||
241 | case 7: /* negative */ | ||
242 | negative = 1; | ||
243 | break; | ||
244 | case 27: /* positive */ | ||
245 | negative = 0; | ||
246 | break; | ||
247 | case 8: /* conceal */ | ||
248 | case 28: /* reveal */ | ||
249 | /* Unsupported */ | ||
250 | break; | ||
251 | case 30: /* Black */ | ||
252 | attr &= ~FOREGROUND_ALL; | ||
253 | break; | ||
254 | case 31: /* Red */ | ||
255 | attr &= ~FOREGROUND_ALL; | ||
256 | attr |= FOREGROUND_RED; | ||
257 | break; | ||
258 | case 32: /* Green */ | ||
259 | attr &= ~FOREGROUND_ALL; | ||
260 | attr |= FOREGROUND_GREEN; | ||
261 | break; | ||
262 | case 33: /* Yellow */ | ||
263 | attr &= ~FOREGROUND_ALL; | ||
264 | attr |= FOREGROUND_RED | FOREGROUND_GREEN; | ||
265 | break; | ||
266 | case 34: /* Blue */ | ||
267 | attr &= ~FOREGROUND_ALL; | ||
268 | attr |= FOREGROUND_BLUE; | ||
269 | break; | ||
270 | case 35: /* Magenta */ | ||
271 | attr &= ~FOREGROUND_ALL; | ||
272 | attr |= FOREGROUND_RED | FOREGROUND_BLUE; | ||
273 | break; | ||
274 | case 36: /* Cyan */ | ||
275 | attr &= ~FOREGROUND_ALL; | ||
276 | attr |= FOREGROUND_GREEN | FOREGROUND_BLUE; | ||
277 | break; | ||
278 | case 37: /* White */ | ||
279 | attr |= FOREGROUND_RED | | ||
280 | FOREGROUND_GREEN | | ||
281 | FOREGROUND_BLUE; | ||
282 | break; | ||
283 | case 38: /* Unknown */ | ||
284 | break; | ||
285 | case 39: /* reset */ | ||
286 | attr &= ~FOREGROUND_ALL; | ||
287 | attr |= (plain_attr & FOREGROUND_ALL); | ||
288 | break; | ||
289 | case 40: /* Black */ | ||
290 | attr &= ~BACKGROUND_ALL; | ||
291 | break; | ||
292 | case 41: /* Red */ | ||
293 | attr &= ~BACKGROUND_ALL; | ||
294 | attr |= BACKGROUND_RED; | ||
295 | break; | ||
296 | case 42: /* Green */ | ||
297 | attr &= ~BACKGROUND_ALL; | ||
298 | attr |= BACKGROUND_GREEN; | ||
299 | break; | ||
300 | case 43: /* Yellow */ | ||
301 | attr &= ~BACKGROUND_ALL; | ||
302 | attr |= BACKGROUND_RED | BACKGROUND_GREEN; | ||
303 | break; | ||
304 | case 44: /* Blue */ | ||
305 | attr &= ~BACKGROUND_ALL; | ||
306 | attr |= BACKGROUND_BLUE; | ||
307 | break; | ||
308 | case 45: /* Magenta */ | ||
309 | attr &= ~BACKGROUND_ALL; | ||
310 | attr |= BACKGROUND_RED | BACKGROUND_BLUE; | ||
311 | break; | ||
312 | case 46: /* Cyan */ | ||
313 | attr &= ~BACKGROUND_ALL; | ||
314 | attr |= BACKGROUND_GREEN | BACKGROUND_BLUE; | ||
315 | break; | ||
316 | case 47: /* White */ | ||
317 | attr |= BACKGROUND_RED | | ||
318 | BACKGROUND_GREEN | | ||
319 | BACKGROUND_BLUE; | ||
320 | break; | ||
321 | case 48: /* Unknown */ | ||
322 | break; | ||
323 | case 49: /* reset */ | ||
324 | attr &= ~BACKGROUND_ALL; | ||
325 | attr |= (plain_attr & BACKGROUND_ALL); | ||
326 | break; | ||
327 | default: | ||
328 | /* Unsupported code */ | ||
329 | break; | ||
330 | } | ||
331 | str++; | ||
332 | } while (*(str-1) == ';'); | ||
333 | |||
334 | set_console_attr(); | ||
335 | break; | ||
336 | case 'A': /* up */ | ||
337 | move_cursor_row(-strtol(str, (char **)&str, 10)); | ||
338 | break; | ||
339 | case 'B': /* down */ | ||
340 | move_cursor_row(strtol(str, (char **)&str, 10)); | ||
341 | break; | ||
342 | case 'C': /* forward */ | ||
343 | move_cursor_column(strtol(str, (char **)&str, 10)); | ||
344 | break; | ||
345 | case 'D': /* back */ | ||
346 | move_cursor_column(-strtol(str, (char **)&str, 10)); | ||
347 | break; | ||
348 | case 'H': | ||
349 | if (!len) | ||
350 | move_cursor(0, 0); | ||
351 | else { | ||
352 | int row, col = 1; | ||
353 | |||
354 | row = strtol(str, (char **)&str, 10); | ||
355 | if (*str == ';') { | ||
356 | col = strtol(str+1, (char **)&str, 10); | ||
357 | } | ||
358 | move_cursor(col > 0 ? col-1 : 0, row > 0 ? row-1 : 0); | ||
359 | } | ||
360 | break; | ||
361 | case 'J': | ||
362 | erase_till_end_of_screen(); | ||
363 | break; | ||
364 | case 'K': | ||
365 | erase_in_line(); | ||
366 | break; | ||
367 | case '?': | ||
368 | /* skip this to avoid ugliness when vi is shut down */ | ||
369 | ++str; | ||
370 | while (isdigit(*str)) | ||
371 | ++str; | ||
372 | func = str; | ||
373 | break; | ||
374 | default: | ||
375 | /* Unsupported code */ | ||
376 | break; | ||
377 | } | ||
378 | |||
379 | return func + 1; | ||
380 | } | ||
381 | |||
382 | static int ansi_emulate(const char *s, FILE *stream) | ||
383 | { | ||
384 | int rv = 0; | ||
385 | const char *t; | ||
386 | char *pos, *str; | ||
387 | size_t out_len, cur_len; | ||
388 | static size_t max_len = 0; | ||
389 | static char *mem = NULL; | ||
390 | |||
391 | /* if no special treatment is required output the string as-is */ | ||
392 | for ( t=s; *t; ++t ) { | ||
393 | if ( *t == '\033' || *t > 0x7f ) { | ||
394 | break; | ||
395 | } | ||
396 | } | ||
397 | |||
398 | if ( *t == '\0' ) { | ||
399 | return fputs(s, stream) == EOF ? EOF : strlen(s); | ||
400 | } | ||
401 | |||
402 | /* make a writable copy of the string and retain it for reuse */ | ||
403 | cur_len = strlen(s); | ||
404 | if ( cur_len == 0 || cur_len > max_len ) { | ||
405 | free(mem); | ||
406 | mem = strdup(s); | ||
407 | max_len = cur_len; | ||
408 | } | ||
409 | else { | ||
410 | strcpy(mem, s); | ||
411 | } | ||
412 | pos = str = mem; | ||
413 | |||
414 | while (*pos) { | ||
415 | pos = strstr(str, "\033["); | ||
416 | if (pos && !skip_ansi_emulation()) { | ||
417 | size_t len = pos - str; | ||
418 | |||
419 | if (len) { | ||
420 | CharToOemBuff(str, str, len); | ||
421 | out_len = fwrite(str, 1, len, stream); | ||
422 | rv += out_len; | ||
423 | if (out_len < len) | ||
424 | return rv; | ||
425 | } | ||
426 | |||
427 | str = pos + 2; | ||
428 | rv += 2; | ||
429 | |||
430 | fflush(stream); | ||
431 | |||
432 | pos = (char *)set_attr(str); | ||
433 | rv += pos - str; | ||
434 | str = pos; | ||
435 | } else { | ||
436 | rv += strlen(str); | ||
437 | CharToOem(str, str); | ||
438 | fputs(str, stream); | ||
439 | return rv; | ||
440 | } | ||
441 | } | ||
442 | return rv; | ||
443 | } | ||
444 | |||
445 | int winansi_putchar(int c) | ||
446 | { | ||
447 | char t = c; | ||
448 | char *s = &t; | ||
449 | |||
450 | if (!isatty(STDOUT_FILENO)) | ||
451 | return putchar(c); | ||
452 | |||
453 | init(); | ||
454 | |||
455 | if (!console) | ||
456 | return putchar(c); | ||
457 | |||
458 | CharToOemBuff(s, s, 1); | ||
459 | return putchar(t) == EOF ? EOF : c; | ||
460 | } | ||
461 | |||
462 | int winansi_puts(const char *s) | ||
463 | { | ||
464 | int rv; | ||
465 | |||
466 | if (!isatty(STDOUT_FILENO)) | ||
467 | return puts(s); | ||
468 | |||
469 | init(); | ||
470 | |||
471 | if (!console) | ||
472 | return puts(s); | ||
473 | |||
474 | rv = ansi_emulate(s, stdout); | ||
475 | putchar('\n'); | ||
476 | |||
477 | return rv; | ||
478 | } | ||
479 | |||
480 | size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) | ||
481 | { | ||
482 | size_t lsize, lmemb; | ||
483 | char *str; | ||
484 | int rv; | ||
485 | |||
486 | lsize = MIN(size, nmemb); | ||
487 | lmemb = MAX(size, nmemb); | ||
488 | if (lsize != 1) | ||
489 | return fwrite(ptr, size, nmemb, stream); | ||
490 | |||
491 | if (!isatty(fileno(stream))) | ||
492 | return fwrite(ptr, size, nmemb, stream); | ||
493 | |||
494 | init(); | ||
495 | |||
496 | if (!console) | ||
497 | return fwrite(ptr, size, nmemb, stream); | ||
498 | |||
499 | str = xmalloc(lmemb+1); | ||
500 | memcpy(str, ptr, lmemb); | ||
501 | str[lmemb] = '\0'; | ||
502 | |||
503 | rv = ansi_emulate(str, stream); | ||
504 | free(str); | ||
505 | |||
506 | return rv; | ||
507 | } | ||
508 | |||
509 | int winansi_fputs(const char *str, FILE *stream) | ||
510 | { | ||
511 | int rv; | ||
512 | |||
513 | if (!isatty(fileno(stream))) | ||
514 | return fputs(str, stream); | ||
515 | |||
516 | init(); | ||
517 | |||
518 | if (!console) | ||
519 | return fputs(str, stream); | ||
520 | |||
521 | rv = ansi_emulate(str, stream); | ||
522 | |||
523 | if (rv >= 0) | ||
524 | return 0; | ||
525 | else | ||
526 | return EOF; | ||
527 | } | ||
528 | |||
529 | int winansi_vfprintf(FILE *stream, const char *format, va_list list) | ||
530 | { | ||
531 | int len, rv; | ||
532 | char small_buf[256]; | ||
533 | char *buf = small_buf; | ||
534 | va_list cp; | ||
535 | |||
536 | if (!isatty(fileno(stream))) | ||
537 | goto abort; | ||
538 | |||
539 | init(); | ||
540 | |||
541 | if (!console) | ||
542 | goto abort; | ||
543 | |||
544 | va_copy(cp, list); | ||
545 | len = vsnprintf(small_buf, sizeof(small_buf), format, cp); | ||
546 | va_end(cp); | ||
547 | |||
548 | if (len > sizeof(small_buf) - 1) { | ||
549 | buf = malloc(len + 1); | ||
550 | if (!buf) | ||
551 | goto abort; | ||
552 | |||
553 | va_copy(cp, list); | ||
554 | len = vsnprintf(buf, len + 1, format, cp); | ||
555 | va_end(cp); | ||
556 | } | ||
557 | |||
558 | if (len == -1) | ||
559 | goto abort; | ||
560 | |||
561 | rv = ansi_emulate(buf, stream); | ||
562 | |||
563 | if (buf != small_buf) | ||
564 | free(buf); | ||
565 | return rv; | ||
566 | |||
567 | abort: | ||
568 | rv = vfprintf(stream, format, list); | ||
569 | return rv; | ||
570 | } | ||
571 | |||
572 | int winansi_fprintf(FILE *stream, const char *format, ...) | ||
573 | { | ||
574 | va_list list; | ||
575 | int rv; | ||
576 | |||
577 | va_start(list, format); | ||
578 | rv = winansi_vfprintf(stream, format, list); | ||
579 | va_end(list); | ||
580 | |||
581 | return rv; | ||
582 | } | ||
583 | |||
584 | int winansi_printf(const char *format, ...) | ||
585 | { | ||
586 | va_list list; | ||
587 | int rv; | ||
588 | |||
589 | va_start(list, format); | ||
590 | rv = winansi_vfprintf(stdout, format, list); | ||
591 | va_end(list); | ||
592 | |||
593 | return rv; | ||
594 | } | ||
595 | |||
596 | int winansi_get_terminal_width_height(struct winsize *win) | ||
597 | { | ||
598 | BOOL ret; | ||
599 | CONSOLE_SCREEN_BUFFER_INFO sbi; | ||
600 | |||
601 | init(); | ||
602 | |||
603 | win->ws_row = 0; | ||
604 | win->ws_col = 0; | ||
605 | if ((ret=GetConsoleScreenBufferInfo(console, &sbi)) != 0) { | ||
606 | win->ws_row = sbi.srWindow.Bottom - sbi.srWindow.Top + 1; | ||
607 | win->ws_col = sbi.srWindow.Right - sbi.srWindow.Left + 1; | ||
608 | } | ||
609 | |||
610 | return ret ? 0 : -1; | ||
611 | } | ||
612 | |||
613 | static int ansi_emulate_write(int fd, const void *buf, size_t count) | ||
614 | { | ||
615 | int rv = 0, i; | ||
616 | int special = FALSE, has_null = FALSE; | ||
617 | const char *s = (const char *)buf; | ||
618 | char *pos, *str; | ||
619 | size_t len, out_len; | ||
620 | static size_t max_len = 0; | ||
621 | static char *mem = NULL; | ||
622 | |||
623 | for ( i=0; i<count; ++i ) { | ||
624 | if ( s[i] == '\033' || s[i] > 0x7f ) { | ||
625 | special = TRUE; | ||
626 | } | ||
627 | else if ( !s[i] ) { | ||
628 | has_null = TRUE; | ||
629 | } | ||
630 | } | ||
631 | |||
632 | /* | ||
633 | * If no special treatment is required or the data contains NUL | ||
634 | * characters output the string as-is. | ||
635 | */ | ||
636 | if ( !special || has_null ) { | ||
637 | return write(fd, buf, count); | ||
638 | } | ||
639 | |||
640 | /* make a writable copy of the data and retain it for reuse */ | ||
641 | if ( count > max_len ) { | ||
642 | free(mem); | ||
643 | mem = malloc(count+1); | ||
644 | max_len = count; | ||
645 | } | ||
646 | memcpy(mem, buf, count); | ||
647 | mem[count] = '\0'; | ||
648 | pos = str = mem; | ||
649 | |||
650 | /* we've checked the data doesn't contain any NULs */ | ||
651 | while (*pos) { | ||
652 | pos = strstr(str, "\033["); | ||
653 | if (pos && !skip_ansi_emulation()) { | ||
654 | len = pos - str; | ||
655 | |||
656 | if (len) { | ||
657 | CharToOemBuff(str, str, len); | ||
658 | out_len = write(fd, str, len); | ||
659 | rv += out_len; | ||
660 | if (out_len < len) | ||
661 | return rv; | ||
662 | } | ||
663 | |||
664 | str = pos + 2; | ||
665 | rv += 2; | ||
666 | |||
667 | pos = (char *)set_attr(str); | ||
668 | rv += pos - str; | ||
669 | str = pos; | ||
670 | } else { | ||
671 | len = strlen(str); | ||
672 | rv += len; | ||
673 | CharToOem(str, str); | ||
674 | write(fd, str, len); | ||
675 | return rv; | ||
676 | } | ||
677 | } | ||
678 | return rv; | ||
679 | } | ||
680 | |||
681 | int winansi_write(int fd, const void *buf, size_t count) | ||
682 | { | ||
683 | if (!isatty(fd)) | ||
684 | return write(fd, buf, count); | ||
685 | |||
686 | init(); | ||
687 | |||
688 | if (!console) | ||
689 | return write(fd, buf, count); | ||
690 | |||
691 | return ansi_emulate_write(fd, buf, count); | ||
692 | } | ||
693 | |||
694 | int winansi_read(int fd, void *buf, size_t count) | ||
695 | { | ||
696 | int rv; | ||
697 | |||
698 | rv = read(fd, buf, count); | ||
699 | if (!isatty(fd)) | ||
700 | return rv; | ||
701 | |||
702 | init(); | ||
703 | |||
704 | if (!console_in) | ||
705 | return rv; | ||
706 | |||
707 | if ( rv > 0 ) { | ||
708 | OemToCharBuff(buf, buf, rv); | ||
709 | } | ||
710 | |||
711 | return rv; | ||
712 | } | ||
713 | |||
714 | int winansi_getc(FILE *stream) | ||
715 | { | ||
716 | int rv; | ||
717 | |||
718 | rv = getc(stream); | ||
719 | if (!isatty(fileno(stream))) | ||
720 | return rv; | ||
721 | |||
722 | init(); | ||
723 | |||
724 | if (!console_in) | ||
725 | return rv; | ||
726 | |||
727 | if ( rv != EOF ) { | ||
728 | unsigned char c = (unsigned char)rv; | ||
729 | char *s = (char *)&c; | ||
730 | OemToCharBuff(s, s, 1); | ||
731 | rv = (int)c; | ||
732 | } | ||
733 | |||
734 | return rv; | ||
735 | } | ||