aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Config.in95
-rw-r--r--Makefile17
-rw-r--r--Makefile.flags8
-rw-r--r--README1
-rw-r--r--README.md24
-rw-r--r--applets/applet_tables.c2
-rw-r--r--archival/ar.c18
-rw-r--r--archival/bbunzip.c2
-rw-r--r--archival/libarchive/decompress_gunzip.c6
-rw-r--r--archival/libarchive/open_transformer.c21
-rw-r--r--archival/libarchive/unsafe_symlink_target.c12
-rw-r--r--archival/rpm.c9
-rw-r--r--archival/tar.c41
-rw-r--r--archival/unzip.c5
-rw-r--r--configs/mingw32_defconfig1174
-rw-r--r--configs/mingw64_defconfig1174
-rw-r--r--coreutils/dd.c15
-rw-r--r--coreutils/du.c2
-rw-r--r--coreutils/expr.c2
-rw-r--r--coreutils/factor.c4
-rw-r--r--coreutils/ls.c4
-rw-r--r--coreutils/od_bloaty.c7
-rw-r--r--coreutils/shred.c10
-rw-r--r--coreutils/stat.c9
-rw-r--r--coreutils/sum.c4
-rw-r--r--coreutils/timeout.c53
-rw-r--r--coreutils/yes.c4
-rw-r--r--debianutils/pipe_progress.c6
-rw-r--r--debianutils/which.c28
-rw-r--r--editors/awk.c6
-rw-r--r--editors/diff.c23
-rw-r--r--editors/sed.c21
-rw-r--r--editors/vi.c52
-rw-r--r--findutils/xargs.c95
-rw-r--r--include/bb_archive.h18
-rw-r--r--include/libbb.h69
-rw-r--r--include/mingw.h494
-rw-r--r--include/platform.h41
-rw-r--r--libbb/Kbuild.src55
-rw-r--r--libbb/appletlib.c184
-rw-r--r--libbb/bb_qsort.c12
-rw-r--r--libbb/compare_string_array.c41
-rw-r--r--libbb/copy_file.c3
-rw-r--r--libbb/executable.c14
-rw-r--r--libbb/find_mount_point.c45
-rw-r--r--libbb/find_pid_by_name.c6
-rw-r--r--libbb/get_last_path_component.c28
-rw-r--r--libbb/get_line_from_file.c4
-rw-r--r--libbb/human_readable.c6
-rw-r--r--libbb/lineedit.c96
-rw-r--r--libbb/make_directory.c29
-rw-r--r--libbb/messages.c2
-rw-r--r--libbb/mode_string.c2
-rw-r--r--libbb/printable_string.c2
-rw-r--r--libbb/procps.c3
-rw-r--r--libbb/read_printf.c5
-rw-r--r--libbb/u_signal_names.c4
-rw-r--r--libbb/vfork_daemon_rexec.c6
-rw-r--r--libbb/xatonum_template.c5
-rw-r--r--libbb/xconnect.c14
-rw-r--r--libbb/xfuncs.c4
-rw-r--r--libbb/xfuncs_printf.c2
-rw-r--r--libbb/xreadlink.c2
-rw-r--r--miscutils/bbconfig.c1
-rw-r--r--miscutils/dc.c2
-rw-r--r--miscutils/iconv.c1846
-rw-r--r--miscutils/less.c86
-rw-r--r--miscutils/man.c15
-rw-r--r--networking/ftpgetput.c6
-rw-r--r--networking/nc.c41
-rw-r--r--networking/ssl_client.c20
-rw-r--r--networking/tls.c6
-rw-r--r--networking/wget.c32
-rw-r--r--procps/iostat.c2
-rw-r--r--procps/kill.c2
-rw-r--r--procps/mpstat.c2
-rw-r--r--procps/ps.c13
-rw-r--r--procps/smemcap.c1
-rw-r--r--scripts/basic/docproc.c15
-rw-r--r--scripts/basic/fixdep.c68
-rw-r--r--scripts/basic/split-include.c13
-rw-r--r--scripts/kconfig/conf.c15
-rw-r--r--scripts/kconfig/confdata.c9
-rw-r--r--scripts/kconfig/lkc.h6
-rw-r--r--scripts/kconfig/mconf.c23
-rw-r--r--scripts/kconfig/symbol.c15
-rw-r--r--scripts/kconfig/zconf.hash.c_shipped60
-rw-r--r--scripts/kconfig/zconf.tab.c_shipped1
-rw-r--r--shell/Kbuild.src1
-rw-r--r--shell/ash.c1556
-rw-r--r--shell/math.h2
-rw-r--r--shell/random.c11
-rw-r--r--shell/random.h3
-rw-r--r--shell/shell_common.c50
-rw-r--r--util-linux/more.c19
-rw-r--r--util-linux/rev.c4
-rw-r--r--win32/Kbuild27
-rw-r--r--win32/arpa/inet.h0
-rw-r--r--win32/env.c94
-rw-r--r--win32/fnmatch.c484
-rw-r--r--win32/fnmatch.h84
-rw-r--r--win32/fsync.c75
-rw-r--r--win32/grp.h0
-rw-r--r--win32/inet_pton.c203
-rw-r--r--win32/ioctl.c46
-rw-r--r--win32/isaac.c232
-rw-r--r--win32/lazyload.h39
-rw-r--r--win32/mingw.c1357
-rw-r--r--win32/mntent.c75
-rw-r--r--win32/mntent.h19
-rw-r--r--win32/net.c101
-rw-r--r--win32/net/if.h0
-rw-r--r--win32/netdb.h0
-rw-r--r--win32/netinet/in.h0
-rw-r--r--win32/paths.h0
-rw-r--r--win32/poll.c604
-rw-r--r--win32/poll.h53
-rw-r--r--win32/popen.c308
-rw-r--r--win32/process.c767
-rw-r--r--win32/pwd.h0
-rw-r--r--win32/regcomp.c3886
-rw-r--r--win32/regex.c90
-rw-r--r--win32/regex.h582
-rw-r--r--win32/regex_internal.c1744
-rw-r--r--win32/regex_internal.h810
-rw-r--r--win32/regexec.c4369
-rw-r--r--win32/resources/COPYING_CCBYSA37
-rw-r--r--win32/resources/Kbuild.src31
-rw-r--r--win32/resources/README9
-rw-r--r--win32/resources/aterm.icobin0 -> 15086 bytes
-rw-r--r--win32/resources/busybox-w32.manifest.src25
-rw-r--r--win32/resources/dummy.c0
-rw-r--r--win32/resources/resources.rc38
-rw-r--r--win32/resources/sterm.icobin0 -> 15086 bytes
-rw-r--r--win32/sched.h1
-rw-r--r--win32/select.c574
-rw-r--r--win32/sh_random.c76
-rw-r--r--win32/statfs.c80
-rw-r--r--win32/strptime.c635
-rw-r--r--win32/sys/ioctl.h0
-rw-r--r--win32/sys/mman.h0
-rw-r--r--win32/sys/prctl.h0
-rw-r--r--win32/sys/resource.h0
-rw-r--r--win32/sys/select.h0
-rw-r--r--win32/sys/socket.h0
-rw-r--r--win32/sys/statfs.h22
-rw-r--r--win32/sys/statvfs.h3
-rw-r--r--win32/sys/syscall.h0
-rw-r--r--win32/sys/sysmacros.h0
-rw-r--r--win32/sys/times.h0
-rw-r--r--win32/sys/un.h0
-rw-r--r--win32/sys/utsname.h66
-rw-r--r--win32/sys/wait.h0
-rw-r--r--win32/system.c22
-rw-r--r--win32/termios.c73
-rw-r--r--win32/termios.h125
-rw-r--r--win32/uname.c48
-rw-r--r--win32/winansi.c776
159 files changed, 26585 insertions, 173 deletions
diff --git a/.gitignore b/.gitignore
index becd9bf6d..cc485189e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,8 +18,10 @@ 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*
24win32/resources/busybox-w32.manifest
23 25
24# 26#
25# Backups / patches 27# Backups / patches
diff --git a/Config.in b/Config.in
index 1a44c5b6d..5bd98aa23 100644
--- a/Config.in
+++ b/Config.in
@@ -9,6 +9,20 @@ config HAVE_DOT_CONFIG
9 bool 9 bool
10 default y 10 default y
11 11
12choice
13 prompt "Target platform"
14 default PLATFORM_POSIX
15 help
16 Target platform you are building busybox for
17
18config PLATFORM_POSIX
19 bool "POSIX"
20
21config PLATFORM_MINGW32
22 bool "MS Windows (MinGW port)"
23
24endchoice
25
12menu "Settings" 26menu "Settings"
13 27
14config DESKTOP 28config DESKTOP
@@ -359,6 +373,87 @@ config PLATFORM_LINUX
359 #This is automatically selected if any applet or feature requires 373 #This is automatically selected if any applet or feature requires
360 #Linux-specific interfaces. You do not need to select it manually. 374 #Linux-specific interfaces. You do not need to select it manually.
361 375
376config GLOBBING
377 bool "Allow busybox.exe to expand wildcards"
378 default n
379 depends on PLATFORM_MINGW32
380 help
381 In Microsoft Windows expansion of wildcards on the command line
382 ('globbing') is handled by the C runtime while the BusyBox shell
383 does its own wildcard expansion. For best results when using the
384 shell globbing by the C runtime should be turned off. If you want
385 the BusyBox binary to handle wildcard expansion using the C runtime
386 set this to 'Y'.
387
388choice
389 prompt "Random number generator"
390 default FEATURE_PRNG_SHELL
391 depends on PLATFORM_MINGW32
392 help
393 BusyBox on Microsoft Windows uses a pseudo-random number
394 generator to emulate the Linux /dev/urandom device. There
395 are two options:
396 - The shell's built-in PRNG.
397 - Bob Jenkins' ISAAC. This is intended to be a secure PRNG. It's
398 slightly faster than the shell's PRNG but is larger both in terms
399 of code and runtime memory.
400
401config FEATURE_PRNG_SHELL
402 bool "Use shell PRNG"
403
404config FEATURE_PRNG_ISAAC
405 bool "Use ISAAC PRNG"
406
407endchoice
408
409config FEATURE_RESOURCES
410 bool "Include resources in binary"
411 default y
412 depends on PLATFORM_MINGW32
413 help
414 Microsoft Windows applications can contain non-executable resources
415 of various sorts.
416
417config FEATURE_VERSIONINFO
418 bool "Include version information in binary (1.0 kb)"
419 default y
420 depends on FEATURE_RESOURCES
421 help
422 Include version information in the application.
423
424config FEATURE_MANIFEST
425 bool "Include manifest in binary (1.5 kb)"
426 default y
427 depends on FEATURE_RESOURCES
428 help
429 Include a manifest which declares privileges required by the
430 application.
431
432config FEATURE_ICON
433 bool "Include application icon in binary"
434 default y
435 depends on FEATURE_RESOURCES
436 help
437 Microsoft Windows applications can contain icons which are used in
438 various places in the user interface. Each icon adds 15 Kbytes to
439 the size of the binary.
440
441choice
442 prompt "Application icon"
443 default FEATURE_ICON_ALL
444 depends on FEATURE_ICON
445
446config FEATURE_ICON_ATERM
447 bool "Adwaita terminal"
448
449config FEATURE_ICON_STERM
450 bool "Adwaita terminal (symbolic)"
451
452config FEATURE_ICON_ALL
453 bool "All available icons"
454
455endchoice
456
362comment 'Build Options' 457comment 'Build Options'
363 458
364config STATIC 459config STATIC
diff --git a/Makefile b/Makefile
index 6fedcffba..cb1a932b5 100644
--- a/Makefile
+++ b/Makefile
@@ -297,6 +297,7 @@ NM = $(CROSS_COMPILE)nm
297STRIP = $(CROSS_COMPILE)strip 297STRIP = $(CROSS_COMPILE)strip
298OBJCOPY = $(CROSS_COMPILE)objcopy 298OBJCOPY = $(CROSS_COMPILE)objcopy
299OBJDUMP = $(CROSS_COMPILE)objdump 299OBJDUMP = $(CROSS_COMPILE)objdump
300WINDRES = $(CROSS_COMPILE)windres
300PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config 301PKG_CONFIG ?= $(CROSS_COMPILE)pkg-config
301AWK = awk 302AWK = awk
302GENKSYMS = scripts/genksyms/genksyms 303GENKSYMS = scripts/genksyms/genksyms
@@ -312,6 +313,7 @@ AFLAGS_MODULE = $(MODFLAGS)
312LDFLAGS_MODULE = -r 313LDFLAGS_MODULE = -r
313CFLAGS_KERNEL = 314CFLAGS_KERNEL =
314AFLAGS_KERNEL = 315AFLAGS_KERNEL =
316EXEEXT =
315 317
316 318
317# Use LINUXINCLUDE when you must reference the include/ directory. 319# Use LINUXINCLUDE when you must reference the include/ directory.
@@ -330,7 +332,7 @@ KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
330 332
331export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \ 333export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \
332 ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ 334 ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
333 CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \ 335 CPP AR NM STRIP OBJCOPY OBJDUMP WINDRES MAKE AWK GENKSYMS PERL UTS_MACHINE \
334 HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS 336 HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
335 337
336export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS 338export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
@@ -462,6 +464,7 @@ scripts_basic: include/autoconf.h
462# Objects we will link into busybox / subdirs we need to visit 464# Objects we will link into busybox / subdirs we need to visit
463core-y := \ 465core-y := \
464 applets/ \ 466 applets/ \
467 win32/resources/ \
465 468
466libs-y := \ 469libs-y := \
467 archival/ \ 470 archival/ \
@@ -492,6 +495,7 @@ libs-y := \
492 sysklogd/ \ 495 sysklogd/ \
493 util-linux/ \ 496 util-linux/ \
494 util-linux/volume_id/ \ 497 util-linux/volume_id/ \
498 win32/ \
495 499
496endif # KBUILD_EXTMOD 500endif # KBUILD_EXTMOD
497 501
@@ -532,7 +536,7 @@ endif
532# command line. 536# command line.
533# This allow a user to issue only 'make' to build a kernel including modules 537# This allow a user to issue only 'make' to build a kernel including modules
534# Defaults busybox but it is usually overridden in the arch makefile 538# Defaults busybox but it is usually overridden in the arch makefile
535all: busybox doc 539all: busybox$(EXEEXT) doc
536 540
537# arch Makefile may override CC so keep this after arch Makefile is included 541# arch Makefile may override CC so keep this after arch Makefile is included
538#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include) 542#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
@@ -714,16 +718,16 @@ debug_kallsyms: .tmp_map$(last_kallsyms)
714endif # ifdef CONFIG_KALLSYMS 718endif # ifdef CONFIG_KALLSYMS
715 719
716# busybox image - including updated kernel symbols 720# busybox image - including updated kernel symbols
717busybox_unstripped: $(busybox-all) FORCE 721busybox_unstripped$(EXEEXT): $(busybox-all) FORCE
718 $(call if_changed_rule,busybox__) 722 $(call if_changed_rule,busybox__)
719 $(Q)rm -f .old_version 723 $(Q)rm -f .old_version
720 724
721busybox: busybox_unstripped 725busybox$(EXEEXT): busybox_unstripped$(EXEEXT)
722ifeq ($(SKIP_STRIP),y) 726ifeq ($(SKIP_STRIP),y)
723 $(Q)cp $< $@ 727 $(Q)cp $< $@
724else 728else
725 $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \ 729 $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \
726 busybox_unstripped -o $@ 730 busybox_unstripped$(EXEEXT) -o $@
727# strip is confused by PIE executable and does not set exec bits 731# strip is confused by PIE executable and does not set exec bits
728 $(Q)chmod a+x $@ 732 $(Q)chmod a+x $@
729endif 733endif
@@ -965,7 +969,7 @@ endif # CONFIG_MODULES
965 969
966# Directories & files removed with 'make clean' 970# Directories & files removed with 'make clean'
967CLEAN_DIRS += $(MODVERDIR) _install 0_lib 971CLEAN_DIRS += $(MODVERDIR) _install 0_lib
968CLEAN_FILES += busybox busybox_unstripped* busybox.links \ 972CLEAN_FILES += busybox$(EXEEXT) busybox_unstripped* busybox.links \
969 System.map .kernelrelease \ 973 System.map .kernelrelease \
970 .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map 974 .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map
971 975
@@ -983,6 +987,7 @@ MRPROPER_FILES += .config .config.old include/asm .version .old_version \
983 include/applets.h \ 987 include/applets.h \
984 include/usage.h \ 988 include/usage.h \
985 applets/usage \ 989 applets/usage \
990 win32/resources/busybox-w32.manifest \
986 .kernelrelease Module.symvers tags TAGS cscope* \ 991 .kernelrelease Module.symvers tags TAGS cscope* \
987 busybox_old 992 busybox_old
988 993
diff --git a/Makefile.flags b/Makefile.flags
index 6f6142cc5..6d4b2c3aa 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
20CFLAGS += $(call cc-option,-Wall,) 20CFLAGS += $(call cc-option,-Wall,)
21CFLAGS += $(call cc-option,-Wshadow,) 21CFLAGS += $(call cc-option,-Wshadow,)
@@ -123,6 +123,12 @@ CFLAGS += --sysroot=$(CONFIG_SYSROOT)
123export SYSROOT=$(CONFIG_SYSROOT) 123export SYSROOT=$(CONFIG_SYSROOT)
124endif 124endif
125 125
126ifeq ($(CONFIG_PLATFORM_MINGW32),y)
127CFLAGS += -Iwin32 -DHAVE_STRING_H=1 -DHAVE_CONFIG_H=0 -fno-builtin-stpcpy -fno-ident
128EXEEXT = .exe
129LDLIBS += userenv psapi ws2_32
130endif
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 -
diff --git a/README b/README
index ada5935b9..9936e6cc3 100644
--- a/README
+++ b/README
@@ -1,3 +1,4 @@
1Please see README.md for Windows-specific info.
1Please see the LICENSE file for details on copying and usage. 2Please see the LICENSE file for details on copying and usage.
2Please refer to the INSTALL file for instructions on how to build. 3Please 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..14737b8c0
--- /dev/null
+++ b/README.md
@@ -0,0 +1,24 @@
1### Status
2
3Things 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 on [GitHub] (https://github.com/rmyorston/busybox-w32) or [GitLab] (https://gitlab.com/rmyorston/busybox-w32). If you don't have an account on one of those you can email me: [rmy@pobox.com] (mailto:rmy@pobox.com).
4
5### Building
6
7You 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
9To start, run `make mingw32_defconfig` or `make mingw64_defconfig`. You can then customize your build with `make menuconfig`.
10
11In particular you may need to adjust the compiler by going to Busybox Settings -> Build Options -> Cross Compiler Prefix
12
13Then 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).
24 - It's possible to obtain pseudo-random numbers using `if=/dev/urandom` as the input file to `dd`. The same emulation of `/dev/urandom` is used internally by the `shred` utility and to support https in `wget`. Since the pseudo-random number generator isn't being seeded with sufficient entropy the randomness shouldn't be relied on for any serious use.
diff --git a/applets/applet_tables.c b/applets/applet_tables.c
index ce2037440..02352113f 100644
--- a/applets/applet_tables.c
+++ b/applets/applet_tables.c
@@ -190,7 +190,7 @@ int main(int argc, char **argv)
190 printf("};\n\n"); 190 printf("};\n\n");
191#endif 191#endif
192 192
193#if ENABLE_FEATURE_INSTALLER 193#if ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32
194 printf("const uint8_t applet_install_loc[] ALIGN1 = {\n"); 194 printf("const uint8_t applet_install_loc[] ALIGN1 = {\n");
195 i = 0; 195 i = 0;
196 while (i < NUM_APPLETS) { 196 while (i < NUM_APPLETS) {
diff --git a/archival/ar.c b/archival/ar.c
index f4edeb087..66930f0b8 100644
--- a/archival/ar.c
+++ b/archival/ar.c
@@ -163,6 +163,9 @@ 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#if ENABLE_PLATFORM_MINGW32
167 char *temp_fn = NULL;
168#endif
166 169
167 xfstat(handle->src_fd, &st, handle->ar__name); 170 xfstat(handle->src_fd, &st, handle->ar__name);
168 171
@@ -171,8 +174,14 @@ static int write_ar_archive(archive_handle_t *handle)
171 */ 174 */
172 if (st.st_size != 0) { 175 if (st.st_size != 0) {
173 out_handle = init_handle(); 176 out_handle = init_handle();
177#if !ENABLE_PLATFORM_MINGW32
174 xunlink(handle->ar__name); 178 xunlink(handle->ar__name);
175 out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC); 179 out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC);
180#else
181 /* can't unlink open file, create temporary output file */
182 temp_fn = xasprintf("%sXXXXXX", handle->ar__name);
183 out_handle->src_fd = xmkstemp(temp_fn);
184#endif
176 out_handle->accept = handle->accept; 185 out_handle->accept = handle->accept;
177 } else { 186 } else {
178 out_handle = handle; 187 out_handle = handle;
@@ -194,12 +203,19 @@ static int write_ar_archive(archive_handle_t *handle)
194 continue; 203 continue;
195 204
196 /* optional, since we exit right after we return */ 205 /* optional, since we exit right after we return */
197 if (ENABLE_FEATURE_CLEAN_UP) { 206 if (ENABLE_FEATURE_CLEAN_UP || ENABLE_PLATFORM_MINGW32) {
198 close(handle->src_fd); 207 close(handle->src_fd);
199 if (out_handle->src_fd != handle->src_fd) 208 if (out_handle->src_fd != handle->src_fd)
200 close(out_handle->src_fd); 209 close(out_handle->src_fd);
201 } 210 }
202 211
212#if ENABLE_PLATFORM_MINGW32
213 if ( temp_fn != NULL ) {
214 xrename(temp_fn, handle->ar__name);
215 free(temp_fn);
216 }
217#endif
218
203 return EXIT_SUCCESS; 219 return EXIT_SUCCESS;
204} 220}
205#endif /* FEATURE_AR_CREATE */ 221#endif /* FEATURE_AR_CREATE */
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index 93f30d324..bf99656e2 100644
--- a/archival/bbunzip.c
+++ b/archival/bbunzip.c
@@ -178,6 +178,8 @@ int FAST_FUNC bbunpack(char **argv,
178 if (option_mask32 & BBUNPK_OPT_KEEP) /* ... unless -k */ 178 if (option_mask32 & BBUNPK_OPT_KEEP) /* ... unless -k */
179 del = NULL; 179 del = NULL;
180 } 180 }
181 if (ENABLE_PLATFORM_MINGW32)
182 xclose(STDIN_FILENO);
181 if (del) 183 if (del)
182 xunlink(del); 184 xunlink(del);
183 free_name: 185 free_name:
diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c
index 7f9046b82..32fcb6b51 100644
--- a/archival/libarchive/decompress_gunzip.c
+++ b/archival/libarchive/decompress_gunzip.c
@@ -1123,6 +1123,9 @@ static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY)
1123 return res; 1123 return res;
1124} 1124}
1125 1125
1126#if ENABLE_PLATFORM_MINGW32 && __GNUC__
1127#pragma pack(2)
1128#endif
1126static int check_header_gzip(STATE_PARAM transformer_state_t *xstate) 1129static int check_header_gzip(STATE_PARAM transformer_state_t *xstate)
1127{ 1130{
1128 union { 1131 union {
@@ -1194,6 +1197,9 @@ static int check_header_gzip(STATE_PARAM transformer_state_t *xstate)
1194 } 1197 }
1195 return 1; 1198 return 1;
1196} 1199}
1200#if ENABLE_PLATFORM_MINGW32 && __GNUC__
1201#pragma pack()
1202#endif
1197 1203
1198IF_DESKTOP(long long) int FAST_FUNC 1204IF_DESKTOP(long long) int FAST_FUNC
1199unpack_gz_stream(transformer_state_t *xstate) 1205unpack_gz_stream(transformer_state_t *xstate)
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index 4a4bf3916..69f3ecc66 100644
--- a/archival/libarchive/open_transformer.c
+++ b/archival/libarchive/open_transformer.c
@@ -64,6 +64,7 @@ ssize_t FAST_FUNC xtransformer_write(transformer_state_t *xstate, const void *bu
64 return nwrote; 64 return nwrote;
65} 65}
66 66
67#if !ENABLE_PLATFORM_MINGW32
67void check_errors_in_children(int signo) 68void check_errors_in_children(int signo)
68{ 69{
69 int status; 70 int status;
@@ -150,6 +151,26 @@ void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
150 close(fd_pipe.wr); /* don't want to write to the child */ 151 close(fd_pipe.wr); /* don't want to write to the child */
151 xmove_fd(fd_pipe.rd, fd); 152 xmove_fd(fd_pipe.rd, fd);
152} 153}
154#else /* ENABLE_PLATFORM_MINGW */
155void FAST_FUNC fork_transformer(int fd, const char *transform_prog)
156{
157 char *cmd;
158 int fd1;
159
160 if (find_applet_by_name(transform_prog) >= 0) {
161 cmd = xasprintf("%s --busybox %s -cf -", bb_busybox_exec_path,
162 transform_prog);
163 }
164 else {
165 cmd = xasprintf("%s -cf -", transform_prog);
166 }
167 if ( (fd1=mingw_popen_fd(cmd, "r", fd, NULL)) == -1 ) {
168 bb_perror_msg_and_die("can't execute '%s'", transform_prog);
169 }
170 free(cmd);
171 xmove_fd(fd1, fd);
172}
173#endif
153 174
154 175
155#if SEAMLESS_COMPRESSION 176#if SEAMLESS_COMPRESSION
diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c
index f8dc8033d..f53881f2f 100644
--- a/archival/libarchive/unsafe_symlink_target.c
+++ b/archival/libarchive/unsafe_symlink_target.c
@@ -5,11 +5,13 @@
5#include "libbb.h" 5#include "libbb.h"
6#include "bb_archive.h" 6#include "bb_archive.h"
7 7
8void FAST_FUNC create_or_remember_link(llist_t **link_placeholders, 8void FAST_FUNC create_or_remember_link(llist_t **link_placeholders
9 IF_PLATFORM_MINGW32(UNUSED_PARAM),
9 const char *target, 10 const char *target,
10 const char *linkname, 11 const char *linkname,
11 int hard_link) 12 int hard_link IF_PLATFORM_MINGW32(UNUSED_PARAM))
12{ 13{
14#if !ENABLE_PLATFORM_MINGW32
13 if (hard_link || target[0] == '/' || strstr(target, "..")) { 15 if (hard_link || target[0] == '/' || strstr(target, "..")) {
14 llist_add_to_end(link_placeholders, 16 llist_add_to_end(link_placeholders,
15 xasprintf("%c%s%c%s", hard_link, linkname, '\0', target) 17 xasprintf("%c%s%c%s", hard_link, linkname, '\0', target)
@@ -22,8 +24,13 @@ void FAST_FUNC create_or_remember_link(llist_t **link_placeholders,
22 "sym", linkname, target 24 "sym", linkname, target
23 ); 25 );
24 } 26 }
27#else
28 /* symlink isn't implemented for WIN32, just issue a warning */
29 bb_perror_msg("can't create %slink '%s' to '%s'", "sym", linkname, target);
30#endif
25} 31}
26 32
33#if !ENABLE_PLATFORM_MINGW32
27void FAST_FUNC create_links_from_list(llist_t *list) 34void FAST_FUNC create_links_from_list(llist_t *list)
28{ 35{
29 while (list) { 36 while (list) {
@@ -40,3 +47,4 @@ void FAST_FUNC create_links_from_list(llist_t *list)
40 list = list->link; 47 list = list->link;
41 } 48 }
42} 49}
50#endif
diff --git a/archival/rpm.c b/archival/rpm.c
index 95b2531e8..3dd4d4777 100644
--- a/archival/rpm.c
+++ b/archival/rpm.c
@@ -140,6 +140,7 @@ static int rpm_gettags(const char *filename)
140 } 140 }
141 G.mytags = tags; 141 G.mytags = tags;
142 142
143#if !ENABLE_PLATFORM_MINGW32
143 /* Map the store */ 144 /* Map the store */
144 storepos = (storepos + G.pagesize) & -(int)G.pagesize; 145 storepos = (storepos + G.pagesize) & -(int)G.pagesize;
145 /* remember size for munmap */ 146 /* remember size for munmap */
@@ -148,6 +149,14 @@ static int rpm_gettags(const char *filename)
148 G.map = mmap(0, storepos, PROT_READ, MAP_PRIVATE, fd, 0); 149 G.map = mmap(0, storepos, PROT_READ, MAP_PRIVATE, fd, 0);
149 if (G.map == MAP_FAILED) 150 if (G.map == MAP_FAILED)
150 bb_perror_msg_and_die("mmap '%s'", filename); 151 bb_perror_msg_and_die("mmap '%s'", filename);
152#else
153# undef munmap
154# define munmap(p, l) free(p)
155 /* Allocate memory for the store */
156 G.map = xmalloc(storepos);
157 xlseek(fd, 0, SEEK_SET);
158 full_read(fd, G.map, storepos);
159#endif
151 160
152 return fd; 161 return fd;
153} 162}
diff --git a/archival/tar.c b/archival/tar.c
index 3ef89fb0a..b9ae93004 100644
--- a/archival/tar.c
+++ b/archival/tar.c
@@ -161,11 +161,13 @@ typedef struct TarBallInfo {
161# endif 161# endif
162 HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */ 162 HardLinkInfo *hlInfoHead; /* Hard Link Tracking Information */
163 HardLinkInfo *hlInfo; /* Hard Link Info for the current file */ 163 HardLinkInfo *hlInfo; /* Hard Link Info for the current file */
164#if !ENABLE_PLATFORM_MINGW32
164//TODO: save only st_dev + st_ino 165//TODO: save only st_dev + st_ino
165 struct stat tarFileStatBuf; /* Stat info for the tarball, letting 166 struct stat tarFileStatBuf; /* Stat info for the tarball, letting
166 * us know the inode and device that the 167 * us know the inode and device that the
167 * tarball lives, so we can avoid trying 168 * tarball lives, so we can avoid trying
168 * to include the tarball into itself */ 169 * to include the tarball into itself */
170#endif
169} TarBallInfo; 171} TarBallInfo;
170 172
171/* A nice enum with all the possible tar file content types */ 173/* A nice enum with all the possible tar file content types */
@@ -527,6 +529,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
527 } 529 }
528 } 530 }
529 531
532#if !ENABLE_PLATFORM_MINGW32
530 /* It is a bad idea to store the archive we are in the process of creating, 533 /* It is a bad idea to store the archive we are in the process of creating,
531 * so check the device and inode to be sure that this particular file isn't 534 * so check the device and inode to be sure that this particular file isn't
532 * the new tarball */ 535 * the new tarball */
@@ -536,6 +539,7 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
536 bb_error_msg("%s: file is the archive; skipping", fileName); 539 bb_error_msg("%s: file is the archive; skipping", fileName);
537 return TRUE; 540 return TRUE;
538 } 541 }
542#endif
539 543
540 if (exclude_file(tbInfo->excludeList, header_name)) 544 if (exclude_file(tbInfo->excludeList, header_name))
541 return SKIP; 545 return SKIP;
@@ -592,7 +596,8 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
592 return TRUE; 596 return TRUE;
593} 597}
594 598
595# if SEAMLESS_COMPRESSION 599#if SEAMLESS_COMPRESSION
600#if !ENABLE_PLATFORM_MINGW32
596/* Don't inline: vfork scares gcc and pessimizes code */ 601/* Don't inline: vfork scares gcc and pessimizes code */
597static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) 602static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
598{ 603{
@@ -661,7 +666,28 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
661 bb_perror_msg_and_die("can't execute '%s'", gzip); 666 bb_perror_msg_and_die("can't execute '%s'", gzip);
662 } 667 }
663} 668}
664# endif /* SEAMLESS_COMPRESSION */ 669#else
670static pid_t vfork_compressor(int tar_fd, const char *gzip)
671{
672 char *cmd;
673 int fd1;
674 pid_t pid;
675
676 if (find_applet_by_name(gzip) >= 0) {
677 cmd = xasprintf("%s --busybox %s -cf -", bb_busybox_exec_path, gzip);
678 }
679 else {
680 cmd = xasprintf("%s -cf -", gzip);
681 }
682 if ( (fd1=mingw_popen_fd(cmd, "w", tar_fd, &pid)) == -1 ) {
683 bb_perror_msg_and_die("can't execute '%s'", gzip);
684 }
685 free(cmd);
686 xmove_fd(fd1, tar_fd);
687 return pid;
688}
689#endif /* ENABLE_PLATFORM_MINGW32 */
690#endif /* SEAMLESS_COMPRESSION */
665 691
666 692
667# if !SEAMLESS_COMPRESSION 693# if !SEAMLESS_COMPRESSION
@@ -677,17 +703,20 @@ static NOINLINE int writeTarFile(
677 const char *gzip) 703 const char *gzip)
678{ 704{
679 int errorFlag = FALSE; 705 int errorFlag = FALSE;
706 IF_PLATFORM_MINGW32(pid_t pid = 0;)
680 707
681 /*tbInfo->hlInfoHead = NULL; - already is */ 708 /*tbInfo->hlInfoHead = NULL; - already is */
682 709
710#if !ENABLE_PLATFORM_MINGW32
683 /* Store the stat info for the tarball's file, so 711 /* Store the stat info for the tarball's file, so
684 * can avoid including the tarball into itself.... */ 712 * can avoid including the tarball into itself.... */
685 xfstat(tbInfo->tarFd, &tbInfo->tarFileStatBuf, "can't stat tar file"); 713 xfstat(tbInfo->tarFd, &tbInfo->tarFileStatBuf, "can't stat tar file");
714#endif
686 715
687# if SEAMLESS_COMPRESSION 716# if SEAMLESS_COMPRESSION
688 if (gzip) 717 if (gzip)
689 vfork_compressor(tbInfo->tarFd, gzip); 718 IF_PLATFORM_MINGW32(pid = )vfork_compressor(tbInfo->tarFd, gzip);
690# endif 719#endif
691 720
692 /* Read the directory/files and iterate over them one at a time */ 721 /* Read the directory/files and iterate over them one at a time */
693 while (filelist) { 722 while (filelist) {
@@ -720,7 +749,11 @@ static NOINLINE int writeTarFile(
720# if SEAMLESS_COMPRESSION 749# if SEAMLESS_COMPRESSION
721 if (gzip) { 750 if (gzip) {
722 int status; 751 int status;
752#if !ENABLE_PLATFORM_MINGW32
723 if (safe_waitpid(-1, &status, 0) == -1) 753 if (safe_waitpid(-1, &status, 0) == -1)
754#else
755 if (safe_waitpid(pid, &status, 0) == -1)
756#endif
724 bb_perror_msg("waitpid"); 757 bb_perror_msg("waitpid");
725 else if (!WIFEXITED(status) || WEXITSTATUS(status)) 758 else if (!WIFEXITED(status) || WEXITSTATUS(status))
726 /* gzip was killed or has exited with nonzero! */ 759 /* gzip was killed or has exited with nonzero! */
diff --git a/archival/unzip.c b/archival/unzip.c
index 466794031..6328f4bab 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__)
@@ -596,7 +599,7 @@ int unzip_main(int argc, char **argv)
596 } 599 }
597 } 600 }
598 601
599#ifndef __GLIBC__ 602#if !defined(__GLIBC__) && !ENABLE_PLATFORM_MINGW32
600 /* 603 /*
601 * This code is needed for non-GNU getopt 604 * This code is needed for non-GNU getopt
602 * which doesn't understand "-" in option string. 605 * which doesn't understand "-" in option string.
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig
new file mode 100644
index 000000000..0b8cf83ce
--- /dev/null
+++ b/configs/mingw32_defconfig
@@ -0,0 +1,1174 @@
1#
2# Automatically generated make config: don't edit
3# Busybox version: 1.30.0.git
4# Wed Nov 28 10:24:41 2018
5#
6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set
8CONFIG_PLATFORM_MINGW32=y
9
10#
11# Settings
12#
13CONFIG_DESKTOP=y
14# CONFIG_EXTRA_COMPAT is not set
15# CONFIG_FEDORA_COMPAT is not set
16# CONFIG_INCLUDE_SUSv2 is not set
17CONFIG_LONG_OPTS=y
18CONFIG_SHOW_USAGE=y
19CONFIG_FEATURE_VERBOSE_USAGE=y
20CONFIG_FEATURE_COMPRESS_USAGE=y
21CONFIG_LFS=y
22# CONFIG_PAM is not set
23# CONFIG_FEATURE_DEVPTS is not set
24# CONFIG_FEATURE_UTMP is not set
25# CONFIG_FEATURE_WTMP is not set
26# CONFIG_FEATURE_PIDFILE is not set
27CONFIG_PID_FILE_PATH=""
28CONFIG_BUSYBOX=y
29CONFIG_FEATURE_SHOW_SCRIPT=y
30CONFIG_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
35CONFIG_FEATURE_PREFER_APPLETS=y
36CONFIG_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_PLATFORM_LINUX is not set
41# CONFIG_GLOBBING is not set
42CONFIG_FEATURE_PRNG_SHELL=y
43# CONFIG_FEATURE_PRNG_ISAAC is not set
44CONFIG_FEATURE_RESOURCES=y
45CONFIG_FEATURE_VERSIONINFO=y
46CONFIG_FEATURE_MANIFEST=y
47CONFIG_FEATURE_ICON=y
48# CONFIG_FEATURE_ICON_ATERM is not set
49# CONFIG_FEATURE_ICON_STERM is not set
50CONFIG_FEATURE_ICON_ALL=y
51
52#
53# Build Options
54#
55# CONFIG_STATIC is not set
56# CONFIG_PIE is not set
57# CONFIG_NOMMU is not set
58# CONFIG_BUILD_LIBBUSYBOX is not set
59# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set
60# CONFIG_FEATURE_INDIVIDUAL is not set
61# CONFIG_FEATURE_SHARED_BUSYBOX is not set
62CONFIG_CROSS_COMPILER_PREFIX="i686-w64-mingw32-"
63CONFIG_SYSROOT=""
64CONFIG_EXTRA_CFLAGS=""
65CONFIG_EXTRA_LDFLAGS=""
66CONFIG_EXTRA_LDLIBS=""
67CONFIG_USE_PORTABLE_CODE=y
68CONFIG_STACK_OPTIMIZATION_386=y
69
70#
71# Installation Options ("make install" behavior)
72#
73CONFIG_INSTALL_APPLET_SYMLINKS=y
74# CONFIG_INSTALL_APPLET_HARDLINKS is not set
75# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
76# CONFIG_INSTALL_APPLET_DONT is not set
77# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
78# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
79# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
80CONFIG_PREFIX=""
81
82#
83# Debugging Options
84#
85CONFIG_DEBUG=y
86# CONFIG_DEBUG_PESSIMIZE is not set
87# CONFIG_DEBUG_SANITIZE is not set
88# CONFIG_UNIT_TEST is not set
89# CONFIG_WERROR is not set
90CONFIG_NO_DEBUG_LIB=y
91# CONFIG_DMALLOC is not set
92# CONFIG_EFENCE is not set
93
94#
95# Library Tuning
96#
97# CONFIG_FEATURE_USE_BSS_TAIL is not set
98CONFIG_FLOAT_DURATION=y
99CONFIG_FEATURE_RTMINMAX=y
100CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS=y
101CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
102# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
103# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
104CONFIG_PASSWORD_MINLEN=6
105CONFIG_MD5_SMALL=1
106CONFIG_SHA3_SMALL=1
107# CONFIG_FEATURE_FAST_TOP is not set
108# CONFIG_FEATURE_ETC_NETWORKS is not set
109# CONFIG_FEATURE_ETC_SERVICES is not set
110CONFIG_FEATURE_EDITING=y
111CONFIG_FEATURE_EDITING_MAX_LEN=1024
112CONFIG_FEATURE_EDITING_VI=y
113CONFIG_FEATURE_EDITING_HISTORY=255
114CONFIG_FEATURE_EDITING_SAVEHISTORY=y
115# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
116CONFIG_FEATURE_REVERSE_SEARCH=y
117CONFIG_FEATURE_TAB_COMPLETION=y
118# CONFIG_FEATURE_USERNAME_COMPLETION is not set
119CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
120# CONFIG_FEATURE_EDITING_WINCH is not set
121# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
122# CONFIG_LOCALE_SUPPORT is not set
123# CONFIG_UNICODE_SUPPORT is not set
124# CONFIG_UNICODE_USING_LOCALE is not set
125# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
126CONFIG_SUBST_WCHAR=0
127CONFIG_LAST_SUPPORTED_WCHAR=0
128# CONFIG_UNICODE_COMBINING_WCHARS is not set
129# CONFIG_UNICODE_WIDE_WCHARS is not set
130# CONFIG_UNICODE_BIDI_SUPPORT is not set
131# CONFIG_UNICODE_NEUTRAL_TABLE is not set
132# CONFIG_UNICODE_PRESERVE_BROKEN is not set
133CONFIG_FEATURE_NON_POSIX_CP=y
134# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
135# CONFIG_FEATURE_USE_SENDFILE is not set
136CONFIG_FEATURE_COPYBUF_KB=4
137CONFIG_FEATURE_SKIP_ROOTFS=y
138# CONFIG_MONOTONIC_SYSCALL is not set
139CONFIG_IOCTL_HEX2STR_ERROR=y
140# CONFIG_FEATURE_HWIB is not set
141
142#
143# Applets
144#
145
146#
147# Archival Utilities
148#
149CONFIG_FEATURE_SEAMLESS_XZ=y
150CONFIG_FEATURE_SEAMLESS_LZMA=y
151CONFIG_FEATURE_SEAMLESS_BZ2=y
152CONFIG_FEATURE_SEAMLESS_GZ=y
153CONFIG_FEATURE_SEAMLESS_Z=y
154CONFIG_AR=y
155CONFIG_FEATURE_AR_LONG_FILENAMES=y
156CONFIG_FEATURE_AR_CREATE=y
157CONFIG_UNCOMPRESS=y
158CONFIG_GUNZIP=y
159CONFIG_ZCAT=y
160CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
161CONFIG_BUNZIP2=y
162CONFIG_BZCAT=y
163CONFIG_UNLZMA=y
164CONFIG_LZCAT=y
165CONFIG_LZMA=y
166CONFIG_UNXZ=y
167CONFIG_XZCAT=y
168CONFIG_XZ=y
169CONFIG_BZIP2=y
170CONFIG_BZIP2_SMALL=8
171CONFIG_FEATURE_BZIP2_DECOMPRESS=y
172CONFIG_CPIO=y
173CONFIG_FEATURE_CPIO_O=y
174# CONFIG_FEATURE_CPIO_P is not set
175# CONFIG_DPKG is not set
176CONFIG_DPKG_DEB=y
177CONFIG_GZIP=y
178CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
179CONFIG_GZIP_FAST=2
180CONFIG_FEATURE_GZIP_LEVELS=y
181CONFIG_FEATURE_GZIP_DECOMPRESS=y
182CONFIG_LZOP=y
183CONFIG_UNLZOP=y
184CONFIG_LZOPCAT=y
185# CONFIG_LZOP_COMPR_HIGH is not set
186CONFIG_RPM=y
187CONFIG_RPM2CPIO=y
188CONFIG_TAR=y
189CONFIG_FEATURE_TAR_LONG_OPTIONS=y
190CONFIG_FEATURE_TAR_CREATE=y
191CONFIG_FEATURE_TAR_AUTODETECT=y
192CONFIG_FEATURE_TAR_FROM=y
193CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
194# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
195CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
196# CONFIG_FEATURE_TAR_TO_COMMAND is not set
197# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
198CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
199# CONFIG_FEATURE_TAR_SELINUX is not set
200CONFIG_UNZIP=y
201CONFIG_FEATURE_UNZIP_CDF=y
202CONFIG_FEATURE_UNZIP_BZIP2=y
203CONFIG_FEATURE_UNZIP_LZMA=y
204CONFIG_FEATURE_UNZIP_XZ=y
205CONFIG_FEATURE_LZMA_FAST=y
206
207#
208# Coreutils
209#
210CONFIG_BASENAME=y
211CONFIG_CAT=y
212CONFIG_FEATURE_CATN=y
213CONFIG_FEATURE_CATV=y
214# CONFIG_CHGRP is not set
215CONFIG_CHMOD=y
216# CONFIG_CHOWN is not set
217# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
218# CONFIG_CHROOT is not set
219CONFIG_CKSUM=y
220CONFIG_COMM=y
221CONFIG_CP=y
222CONFIG_FEATURE_CP_LONG_OPTIONS=y
223# CONFIG_FEATURE_CP_REFLINK is not set
224CONFIG_CUT=y
225CONFIG_DATE=y
226CONFIG_FEATURE_DATE_ISOFMT=y
227# CONFIG_FEATURE_DATE_NANO is not set
228CONFIG_FEATURE_DATE_COMPAT=y
229CONFIG_DD=y
230# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set
231# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
232CONFIG_FEATURE_DD_IBS_OBS=y
233CONFIG_FEATURE_DD_STATUS=y
234CONFIG_DF=y
235# CONFIG_FEATURE_DF_FANCY is not set
236CONFIG_DIRNAME=y
237CONFIG_DOS2UNIX=y
238CONFIG_UNIX2DOS=y
239CONFIG_DU=y
240CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
241CONFIG_ECHO=y
242CONFIG_FEATURE_FANCY_ECHO=y
243CONFIG_ENV=y
244CONFIG_EXPAND=y
245CONFIG_UNEXPAND=y
246CONFIG_EXPR=y
247CONFIG_EXPR_MATH_SUPPORT_64=y
248CONFIG_FACTOR=y
249CONFIG_FALSE=y
250CONFIG_FOLD=y
251CONFIG_FSYNC=y
252CONFIG_HEAD=y
253CONFIG_FEATURE_FANCY_HEAD=y
254# CONFIG_HOSTID is not set
255CONFIG_ID=y
256CONFIG_GROUPS=y
257# CONFIG_INSTALL is not set
258# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
259CONFIG_LINK=y
260CONFIG_LN=y
261CONFIG_LOGNAME=y
262CONFIG_LS=y
263CONFIG_FEATURE_LS_FILETYPES=y
264CONFIG_FEATURE_LS_FOLLOWLINKS=y
265CONFIG_FEATURE_LS_RECURSIVE=y
266CONFIG_FEATURE_LS_WIDTH=y
267CONFIG_FEATURE_LS_SORTFILES=y
268CONFIG_FEATURE_LS_TIMESTAMPS=y
269CONFIG_FEATURE_LS_USERNAME=y
270CONFIG_FEATURE_LS_COLOR=y
271CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
272CONFIG_MD5SUM=y
273CONFIG_SHA1SUM=y
274CONFIG_SHA256SUM=y
275CONFIG_SHA512SUM=y
276CONFIG_SHA3SUM=y
277
278#
279# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
280#
281CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
282CONFIG_MKDIR=y
283# CONFIG_MKFIFO is not set
284# CONFIG_MKNOD is not set
285CONFIG_MKTEMP=y
286CONFIG_MV=y
287# CONFIG_NICE is not set
288CONFIG_NL=y
289# CONFIG_NOHUP is not set
290# CONFIG_NPROC is not set
291CONFIG_OD=y
292CONFIG_PASTE=y
293CONFIG_PRINTENV=y
294CONFIG_PRINTF=y
295CONFIG_PWD=y
296# CONFIG_READLINK is not set
297# CONFIG_FEATURE_READLINK_FOLLOW is not set
298CONFIG_REALPATH=y
299CONFIG_RM=y
300CONFIG_RMDIR=y
301CONFIG_SEQ=y
302CONFIG_SHRED=y
303CONFIG_SHUF=y
304CONFIG_SLEEP=y
305CONFIG_FEATURE_FANCY_SLEEP=y
306CONFIG_SORT=y
307CONFIG_FEATURE_SORT_BIG=y
308# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set
309CONFIG_SPLIT=y
310CONFIG_FEATURE_SPLIT_FANCY=y
311CONFIG_STAT=y
312CONFIG_FEATURE_STAT_FORMAT=y
313CONFIG_FEATURE_STAT_FILESYSTEM=y
314# CONFIG_STTY is not set
315CONFIG_SUM=y
316# CONFIG_SYNC is not set
317# CONFIG_FEATURE_SYNC_FANCY is not set
318CONFIG_TAC=y
319CONFIG_TAIL=y
320CONFIG_FEATURE_FANCY_TAIL=y
321CONFIG_TEE=y
322CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
323CONFIG_TEST=y
324CONFIG_TEST1=y
325CONFIG_TEST2=y
326CONFIG_FEATURE_TEST_64=y
327CONFIG_TIMEOUT=y
328CONFIG_TOUCH=y
329# CONFIG_FEATURE_TOUCH_NODEREF is not set
330CONFIG_FEATURE_TOUCH_SUSV3=y
331CONFIG_TR=y
332CONFIG_FEATURE_TR_CLASSES=y
333CONFIG_FEATURE_TR_EQUIV=y
334CONFIG_TRUE=y
335CONFIG_TRUNCATE=y
336# CONFIG_TTY is not set
337CONFIG_UNAME=y
338CONFIG_UNAME_OSNAME="MS/Windows"
339CONFIG_BB_ARCH=y
340CONFIG_UNIQ=y
341CONFIG_UNLINK=y
342CONFIG_USLEEP=y
343CONFIG_UUDECODE=y
344CONFIG_BASE64=y
345CONFIG_UUENCODE=y
346CONFIG_WC=y
347CONFIG_FEATURE_WC_LARGE=y
348CONFIG_WHOAMI=y
349# CONFIG_WHO is not set
350# CONFIG_W is not set
351# CONFIG_USERS is not set
352CONFIG_YES=y
353
354#
355# Common options
356#
357CONFIG_FEATURE_VERBOSE=y
358
359#
360# Common options for cp and mv
361#
362# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
363
364#
365# Common options for df, du, ls
366#
367CONFIG_FEATURE_HUMAN_READABLE=y
368
369#
370# Console Utilities
371#
372# CONFIG_CHVT is not set
373CONFIG_CLEAR=y
374# CONFIG_DEALLOCVT is not set
375# CONFIG_DUMPKMAP is not set
376# CONFIG_FGCONSOLE is not set
377# CONFIG_KBD_MODE is not set
378# CONFIG_LOADFONT is not set
379# CONFIG_SETFONT is not set
380# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
381CONFIG_DEFAULT_SETFONT_DIR=""
382# CONFIG_FEATURE_LOADFONT_PSF2 is not set
383# CONFIG_FEATURE_LOADFONT_RAW is not set
384# CONFIG_LOADKMAP is not set
385# CONFIG_OPENVT is not set
386# CONFIG_RESET is not set
387# CONFIG_RESIZE is not set
388# CONFIG_FEATURE_RESIZE_PRINT is not set
389# CONFIG_SETCONSOLE is not set
390# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
391# CONFIG_SETKEYCODES is not set
392# CONFIG_SETLOGCONS is not set
393# CONFIG_SHOWKEY is not set
394
395#
396# Debian Utilities
397#
398CONFIG_PIPE_PROGRESS=y
399# CONFIG_RUN_PARTS is not set
400# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
401# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
402# CONFIG_START_STOP_DAEMON is not set
403# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
404# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
405CONFIG_WHICH=y
406
407#
408# klibc-utils
409#
410# CONFIG_MINIPS is not set
411# CONFIG_NUKE is not set
412# CONFIG_RESUME is not set
413# CONFIG_RUN_INIT is not set
414
415#
416# Editors
417#
418CONFIG_AWK=y
419CONFIG_FEATURE_AWK_LIBM=y
420CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
421CONFIG_CMP=y
422CONFIG_DIFF=y
423CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
424CONFIG_FEATURE_DIFF_DIR=y
425CONFIG_ED=y
426CONFIG_PATCH=y
427CONFIG_SED=y
428CONFIG_VI=y
429CONFIG_FEATURE_VI_MAX_LEN=4096
430CONFIG_FEATURE_VI_8BIT=y
431CONFIG_FEATURE_VI_COLON=y
432CONFIG_FEATURE_VI_YANKMARK=y
433CONFIG_FEATURE_VI_SEARCH=y
434# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
435# CONFIG_FEATURE_VI_USE_SIGNALS is not set
436CONFIG_FEATURE_VI_DOT_CMD=y
437CONFIG_FEATURE_VI_READONLY=y
438CONFIG_FEATURE_VI_SETOPTS=y
439CONFIG_FEATURE_VI_SET=y
440CONFIG_FEATURE_VI_WIN_RESIZE=y
441# CONFIG_FEATURE_VI_ASK_TERMINAL is not set
442CONFIG_FEATURE_VI_UNDO=y
443CONFIG_FEATURE_VI_UNDO_QUEUE=y
444CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
445CONFIG_FEATURE_ALLOW_EXEC=y
446
447#
448# Finding Utilities
449#
450CONFIG_FIND=y
451CONFIG_FEATURE_FIND_PRINT0=y
452CONFIG_FEATURE_FIND_MTIME=y
453CONFIG_FEATURE_FIND_MMIN=y
454CONFIG_FEATURE_FIND_PERM=y
455CONFIG_FEATURE_FIND_TYPE=y
456CONFIG_FEATURE_FIND_EXECUTABLE=y
457# CONFIG_FEATURE_FIND_XDEV is not set
458CONFIG_FEATURE_FIND_MAXDEPTH=y
459CONFIG_FEATURE_FIND_NEWER=y
460# CONFIG_FEATURE_FIND_INUM is not set
461CONFIG_FEATURE_FIND_EXEC=y
462CONFIG_FEATURE_FIND_EXEC_PLUS=y
463# CONFIG_FEATURE_FIND_USER is not set
464# CONFIG_FEATURE_FIND_GROUP is not set
465CONFIG_FEATURE_FIND_NOT=y
466CONFIG_FEATURE_FIND_DEPTH=y
467CONFIG_FEATURE_FIND_PAREN=y
468CONFIG_FEATURE_FIND_SIZE=y
469CONFIG_FEATURE_FIND_PRUNE=y
470CONFIG_FEATURE_FIND_QUIT=y
471CONFIG_FEATURE_FIND_DELETE=y
472CONFIG_FEATURE_FIND_PATH=y
473CONFIG_FEATURE_FIND_REGEX=y
474# CONFIG_FEATURE_FIND_CONTEXT is not set
475# CONFIG_FEATURE_FIND_LINKS is not set
476CONFIG_GREP=y
477CONFIG_EGREP=y
478CONFIG_FGREP=y
479CONFIG_FEATURE_GREP_CONTEXT=y
480CONFIG_XARGS=y
481CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
482CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
483CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
484CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
485CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
486CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y
487CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y
488
489#
490# Init Utilities
491#
492# CONFIG_BOOTCHARTD is not set
493# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
494# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
495# CONFIG_HALT is not set
496# CONFIG_POWEROFF is not set
497# CONFIG_REBOOT is not set
498# CONFIG_FEATURE_WAIT_FOR_INIT is not set
499# CONFIG_FEATURE_CALL_TELINIT is not set
500CONFIG_TELINIT_PATH=""
501# CONFIG_INIT is not set
502# CONFIG_LINUXRC is not set
503# CONFIG_FEATURE_USE_INITTAB is not set
504# CONFIG_FEATURE_KILL_REMOVED is not set
505CONFIG_FEATURE_KILL_DELAY=0
506# CONFIG_FEATURE_INIT_SCTTY is not set
507# CONFIG_FEATURE_INIT_SYSLOG is not set
508# CONFIG_FEATURE_INIT_QUIET is not set
509# CONFIG_FEATURE_INIT_COREDUMPS is not set
510CONFIG_INIT_TERMINAL_TYPE=""
511# CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set
512
513#
514# Login/Password Management Utilities
515#
516# CONFIG_FEATURE_SHADOWPASSWDS is not set
517# CONFIG_USE_BB_PWD_GRP is not set
518# CONFIG_USE_BB_SHADOW is not set
519# CONFIG_USE_BB_CRYPT is not set
520# CONFIG_USE_BB_CRYPT_SHA is not set
521# CONFIG_ADDGROUP is not set
522# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
523# CONFIG_ADD_SHELL is not set
524# CONFIG_REMOVE_SHELL is not set
525# CONFIG_ADDUSER is not set
526# CONFIG_FEATURE_CHECK_NAMES is not set
527CONFIG_LAST_ID=0
528CONFIG_FIRST_SYSTEM_ID=0
529CONFIG_LAST_SYSTEM_ID=0
530# CONFIG_CHPASSWD is not set
531CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
532# CONFIG_CRYPTPW is not set
533# CONFIG_MKPASSWD is not set
534# CONFIG_DELUSER is not set
535# CONFIG_DELGROUP is not set
536# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
537# CONFIG_GETTY is not set
538# CONFIG_LOGIN is not set
539# CONFIG_LOGIN_SESSION_AS_CHILD is not set
540# CONFIG_LOGIN_SCRIPTS is not set
541# CONFIG_FEATURE_NOLOGIN is not set
542# CONFIG_FEATURE_SECURETTY is not set
543# CONFIG_PASSWD is not set
544# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
545# CONFIG_SU is not set
546# CONFIG_FEATURE_SU_SYSLOG is not set
547# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
548# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set
549# CONFIG_SULOGIN is not set
550# CONFIG_VLOCK is not set
551
552#
553# Linux Ext2 FS Progs
554#
555# CONFIG_CHATTR is not set
556# CONFIG_FSCK is not set
557# CONFIG_LSATTR is not set
558# CONFIG_TUNE2FS is not set
559
560#
561# Linux Module Utilities
562#
563# CONFIG_MODPROBE_SMALL is not set
564# CONFIG_DEPMOD is not set
565# CONFIG_INSMOD is not set
566# CONFIG_LSMOD is not set
567# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
568# CONFIG_MODINFO is not set
569# CONFIG_MODPROBE is not set
570# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
571# CONFIG_RMMOD is not set
572
573#
574# Options common to multiple modutils
575#
576# CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set
577# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
578# CONFIG_FEATURE_2_4_MODULES is not set
579# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
580# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
581# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
582# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
583# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
584# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
585# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
586# CONFIG_FEATURE_MODUTILS_ALIAS is not set
587# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
588CONFIG_DEFAULT_MODULES_DIR=""
589CONFIG_DEFAULT_DEPMOD_FILE=""
590
591#
592# Linux System Utilities
593#
594# CONFIG_ACPID is not set
595# CONFIG_FEATURE_ACPID_COMPAT is not set
596# CONFIG_BLKDISCARD is not set
597# CONFIG_BLKID is not set
598# CONFIG_FEATURE_BLKID_TYPE is not set
599# CONFIG_BLOCKDEV is not set
600CONFIG_CAL=y
601# CONFIG_CHRT is not set
602# CONFIG_DMESG is not set
603# CONFIG_FEATURE_DMESG_PRETTY is not set
604# CONFIG_EJECT is not set
605# CONFIG_FEATURE_EJECT_SCSI is not set
606# CONFIG_FALLOCATE is not set
607# CONFIG_FATATTR is not set
608# CONFIG_FBSET is not set
609# CONFIG_FEATURE_FBSET_FANCY is not set
610# CONFIG_FEATURE_FBSET_READMODE is not set
611# CONFIG_FDFORMAT is not set
612# CONFIG_FDISK is not set
613# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
614# CONFIG_FEATURE_FDISK_WRITABLE is not set
615# CONFIG_FEATURE_AIX_LABEL is not set
616# CONFIG_FEATURE_SGI_LABEL is not set
617# CONFIG_FEATURE_SUN_LABEL is not set
618# CONFIG_FEATURE_OSF_LABEL is not set
619# CONFIG_FEATURE_GPT_LABEL is not set
620# CONFIG_FEATURE_FDISK_ADVANCED is not set
621# CONFIG_FINDFS is not set
622# CONFIG_FLOCK is not set
623# CONFIG_FDFLUSH is not set
624# CONFIG_FREERAMDISK is not set
625# CONFIG_FSCK_MINIX is not set
626# CONFIG_FSFREEZE is not set
627# CONFIG_FSTRIM is not set
628CONFIG_GETOPT=y
629CONFIG_FEATURE_GETOPT_LONG=y
630CONFIG_HEXDUMP=y
631CONFIG_FEATURE_HEXDUMP_REVERSE=y
632CONFIG_HD=y
633CONFIG_XXD=y
634# CONFIG_HWCLOCK is not set
635# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
636# CONFIG_IONICE is not set
637# CONFIG_IPCRM is not set
638# CONFIG_IPCS is not set
639# CONFIG_LAST is not set
640# CONFIG_FEATURE_LAST_FANCY is not set
641# CONFIG_LOSETUP is not set
642# CONFIG_LSPCI is not set
643# CONFIG_LSUSB is not set
644# CONFIG_MDEV is not set
645# CONFIG_FEATURE_MDEV_CONF is not set
646# CONFIG_FEATURE_MDEV_RENAME is not set
647# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
648# CONFIG_FEATURE_MDEV_EXEC is not set
649# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
650# CONFIG_MESG is not set
651# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
652# CONFIG_MKE2FS is not set
653# CONFIG_MKFS_EXT2 is not set
654# CONFIG_MKFS_MINIX is not set
655# CONFIG_FEATURE_MINIX2 is not set
656# CONFIG_MKFS_REISER is not set
657# CONFIG_MKDOSFS is not set
658# CONFIG_MKFS_VFAT is not set
659# CONFIG_MKSWAP is not set
660# CONFIG_FEATURE_MKSWAP_UUID is not set
661# CONFIG_MORE is not set
662# CONFIG_MOUNT is not set
663# CONFIG_FEATURE_MOUNT_FAKE is not set
664# CONFIG_FEATURE_MOUNT_VERBOSE is not set
665# CONFIG_FEATURE_MOUNT_HELPERS is not set
666# CONFIG_FEATURE_MOUNT_LABEL is not set
667# CONFIG_FEATURE_MOUNT_NFS is not set
668# CONFIG_FEATURE_MOUNT_CIFS is not set
669# CONFIG_FEATURE_MOUNT_FLAGS is not set
670# CONFIG_FEATURE_MOUNT_FSTAB is not set
671# CONFIG_FEATURE_MOUNT_OTHERTAB is not set
672# CONFIG_MOUNTPOINT is not set
673# CONFIG_NOLOGIN is not set
674# CONFIG_NOLOGIN_DEPENDENCIES is not set
675# CONFIG_NSENTER is not set
676# CONFIG_PIVOT_ROOT is not set
677# CONFIG_RDATE is not set
678# CONFIG_RDEV is not set
679# CONFIG_READPROFILE is not set
680# CONFIG_RENICE is not set
681CONFIG_REV=y
682# CONFIG_RTCWAKE is not set
683# CONFIG_SCRIPT is not set
684# CONFIG_SCRIPTREPLAY is not set
685# CONFIG_SETARCH is not set
686# CONFIG_LINUX32 is not set
687# CONFIG_LINUX64 is not set
688# CONFIG_SETPRIV is not set
689# CONFIG_FEATURE_SETPRIV_DUMP is not set
690# CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set
691# CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set
692# CONFIG_SETSID is not set
693# CONFIG_SWAPON is not set
694# CONFIG_FEATURE_SWAPON_DISCARD is not set
695# CONFIG_FEATURE_SWAPON_PRI is not set
696# CONFIG_SWAPOFF is not set
697# CONFIG_FEATURE_SWAPONOFF_LABEL is not set
698# CONFIG_SWITCH_ROOT is not set
699# CONFIG_TASKSET is not set
700# CONFIG_FEATURE_TASKSET_FANCY is not set
701# CONFIG_UEVENT is not set
702# CONFIG_UMOUNT is not set
703# CONFIG_FEATURE_UMOUNT_ALL is not set
704# CONFIG_UNSHARE is not set
705# CONFIG_WALL is not set
706# CONFIG_FEATURE_MOUNT_LOOP is not set
707# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
708# CONFIG_FEATURE_MTAB_SUPPORT is not set
709# CONFIG_VOLUMEID is not set
710# CONFIG_FEATURE_VOLUMEID_BCACHE is not set
711# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
712# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
713# CONFIG_FEATURE_VOLUMEID_EXFAT is not set
714# CONFIG_FEATURE_VOLUMEID_EXT is not set
715# CONFIG_FEATURE_VOLUMEID_F2FS is not set
716# CONFIG_FEATURE_VOLUMEID_FAT is not set
717# CONFIG_FEATURE_VOLUMEID_HFS is not set
718# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
719# CONFIG_FEATURE_VOLUMEID_JFS is not set
720# CONFIG_FEATURE_VOLUMEID_LFS is not set
721# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
722# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
723# CONFIG_FEATURE_VOLUMEID_LUKS is not set
724# CONFIG_FEATURE_VOLUMEID_MINIX is not set
725# CONFIG_FEATURE_VOLUMEID_NILFS is not set
726# CONFIG_FEATURE_VOLUMEID_NTFS is not set
727# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
728# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
729# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
730# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set
731# CONFIG_FEATURE_VOLUMEID_SYSV is not set
732# CONFIG_FEATURE_VOLUMEID_UBIFS is not set
733# CONFIG_FEATURE_VOLUMEID_UDF is not set
734# CONFIG_FEATURE_VOLUMEID_XFS is not set
735
736#
737# Miscellaneous Utilities
738#
739# CONFIG_ADJTIMEX is not set
740# CONFIG_BBCONFIG is not set
741# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
742# CONFIG_BEEP is not set
743CONFIG_FEATURE_BEEP_FREQ=0
744CONFIG_FEATURE_BEEP_LENGTH_MS=0
745# CONFIG_CHAT is not set
746# CONFIG_FEATURE_CHAT_NOFAIL is not set
747# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
748# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
749# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
750# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
751# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
752# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
753# CONFIG_CONSPY is not set
754# CONFIG_CROND is not set
755# CONFIG_FEATURE_CROND_D is not set
756# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
757# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set
758CONFIG_FEATURE_CROND_DIR=""
759# CONFIG_CRONTAB is not set
760CONFIG_DC=y
761CONFIG_FEATURE_DC_LIBM=y
762# CONFIG_DEVFSD is not set
763# CONFIG_DEVFSD_MODLOAD is not set
764# CONFIG_DEVFSD_FG_NP is not set
765# CONFIG_DEVFSD_VERBOSE is not set
766# CONFIG_FEATURE_DEVFS is not set
767# CONFIG_DEVMEM is not set
768# CONFIG_FBSPLASH is not set
769# CONFIG_FLASHCP is not set
770# CONFIG_FLASH_ERASEALL is not set
771# CONFIG_FLASH_LOCK is not set
772# CONFIG_FLASH_UNLOCK is not set
773# CONFIG_HDPARM is not set
774# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
775# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
776# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
777# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
778# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
779# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
780# CONFIG_HEXEDIT is not set
781# CONFIG_I2CGET is not set
782# CONFIG_I2CSET is not set
783# CONFIG_I2CDUMP is not set
784# CONFIG_I2CDETECT is not set
785CONFIG_ICONV=y
786# CONFIG_INOTIFYD is not set
787CONFIG_LESS=y
788CONFIG_FEATURE_LESS_MAXLINES=9999999
789CONFIG_FEATURE_LESS_BRACKETS=y
790CONFIG_FEATURE_LESS_FLAGS=y
791CONFIG_FEATURE_LESS_TRUNCATE=y
792CONFIG_FEATURE_LESS_MARKS=y
793CONFIG_FEATURE_LESS_REGEXP=y
794# CONFIG_FEATURE_LESS_WINCH is not set
795# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set
796CONFIG_FEATURE_LESS_DASHCMD=y
797CONFIG_FEATURE_LESS_LINENUMS=y
798CONFIG_FEATURE_LESS_RAW=y
799CONFIG_FEATURE_LESS_ENV=y
800# CONFIG_LSSCSI is not set
801# CONFIG_MAKEDEVS is not set
802# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
803# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
804CONFIG_MAN=y
805# CONFIG_MICROCOM is not set
806# CONFIG_MT is not set
807# CONFIG_NANDWRITE is not set
808# CONFIG_NANDDUMP is not set
809# CONFIG_PARTPROBE is not set
810# CONFIG_RAIDAUTORUN is not set
811# CONFIG_READAHEAD is not set
812# CONFIG_RFKILL is not set
813# CONFIG_RUNLEVEL is not set
814# CONFIG_RX is not set
815# CONFIG_SETFATTR is not set
816# CONFIG_SETSERIAL is not set
817CONFIG_STRINGS=y
818# CONFIG_TIME is not set
819CONFIG_TTYSIZE=y
820# CONFIG_UBIRENAME is not set
821# CONFIG_UBIATTACH is not set
822# CONFIG_UBIDETACH is not set
823# CONFIG_UBIMKVOL is not set
824# CONFIG_UBIRMVOL is not set
825# CONFIG_UBIRSVOL is not set
826# CONFIG_UBIUPDATEVOL is not set
827# CONFIG_VOLNAME is not set
828# CONFIG_WATCHDOG is not set
829
830#
831# Networking Utilities
832#
833CONFIG_FEATURE_IPV6=y
834# CONFIG_FEATURE_UNIX_LOCAL is not set
835CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
836# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
837# CONFIG_ARP is not set
838# CONFIG_ARPING is not set
839# CONFIG_BRCTL is not set
840# CONFIG_FEATURE_BRCTL_FANCY is not set
841# CONFIG_FEATURE_BRCTL_SHOW is not set
842# CONFIG_DNSD is not set
843# CONFIG_ETHER_WAKE is not set
844# CONFIG_FTPD is not set
845# CONFIG_FEATURE_FTPD_WRITE is not set
846# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
847# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set
848CONFIG_FTPGET=y
849CONFIG_FTPPUT=y
850CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
851# CONFIG_HOSTNAME is not set
852# CONFIG_DNSDOMAINNAME is not set
853# CONFIG_HTTPD is not set
854# CONFIG_FEATURE_HTTPD_RANGES is not set
855# CONFIG_FEATURE_HTTPD_SETUID is not set
856# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
857# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
858# CONFIG_FEATURE_HTTPD_CGI is not set
859# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
860# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
861# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
862# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
863# CONFIG_FEATURE_HTTPD_PROXY is not set
864# CONFIG_FEATURE_HTTPD_GZIP is not set
865# CONFIG_IFCONFIG is not set
866# CONFIG_FEATURE_IFCONFIG_STATUS is not set
867# CONFIG_FEATURE_IFCONFIG_SLIP is not set
868# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
869# CONFIG_FEATURE_IFCONFIG_HW is not set
870# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
871# CONFIG_IFENSLAVE is not set
872# CONFIG_IFPLUGD is not set
873# CONFIG_IFUP is not set
874# CONFIG_IFDOWN is not set
875CONFIG_IFUPDOWN_IFSTATE_PATH=""
876# CONFIG_FEATURE_IFUPDOWN_IP is not set
877# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
878# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
879# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
880# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
881# CONFIG_INETD is not set
882# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
883# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
884# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
885# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
886# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
887# CONFIG_FEATURE_INETD_RPC is not set
888# CONFIG_IP is not set
889# CONFIG_IPADDR is not set
890# CONFIG_IPLINK is not set
891# CONFIG_IPROUTE is not set
892# CONFIG_IPTUNNEL is not set
893# CONFIG_IPRULE is not set
894# CONFIG_IPNEIGH is not set
895# CONFIG_FEATURE_IP_ADDRESS is not set
896# CONFIG_FEATURE_IP_LINK is not set
897# CONFIG_FEATURE_IP_ROUTE is not set
898CONFIG_FEATURE_IP_ROUTE_DIR=""
899# CONFIG_FEATURE_IP_TUNNEL is not set
900# CONFIG_FEATURE_IP_RULE is not set
901# CONFIG_FEATURE_IP_NEIGH is not set
902# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
903CONFIG_IPCALC=y
904CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
905CONFIG_FEATURE_IPCALC_FANCY=y
906# CONFIG_FAKEIDENTD is not set
907# CONFIG_NAMEIF is not set
908# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
909# CONFIG_NBDCLIENT is not set
910CONFIG_NC=y
911# CONFIG_NETCAT is not set
912CONFIG_NC_SERVER=y
913# CONFIG_NC_EXTRA is not set
914# CONFIG_NC_110_COMPAT is not set
915# CONFIG_NETSTAT is not set
916# CONFIG_FEATURE_NETSTAT_WIDE is not set
917# CONFIG_FEATURE_NETSTAT_PRG is not set
918# CONFIG_NSLOOKUP is not set
919# CONFIG_FEATURE_NSLOOKUP_BIG is not set
920# CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS is not set
921# CONFIG_NTPD is not set
922# CONFIG_FEATURE_NTPD_SERVER is not set
923# CONFIG_FEATURE_NTPD_CONF is not set
924# CONFIG_FEATURE_NTP_AUTH is not set
925# CONFIG_PING is not set
926# CONFIG_PING6 is not set
927# CONFIG_FEATURE_FANCY_PING is not set
928# CONFIG_PSCAN is not set
929# CONFIG_ROUTE is not set
930# CONFIG_SLATTACH is not set
931CONFIG_SSL_CLIENT=y
932# CONFIG_TC is not set
933# CONFIG_FEATURE_TC_INGRESS is not set
934# CONFIG_TCPSVD is not set
935# CONFIG_UDPSVD is not set
936# CONFIG_TELNET is not set
937# CONFIG_FEATURE_TELNET_TTYPE is not set
938# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
939# CONFIG_FEATURE_TELNET_WIDTH is not set
940# CONFIG_TELNETD is not set
941# CONFIG_FEATURE_TELNETD_STANDALONE is not set
942# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
943# CONFIG_TFTP is not set
944# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
945# CONFIG_TFTPD is not set
946# CONFIG_FEATURE_TFTP_GET is not set
947# CONFIG_FEATURE_TFTP_PUT is not set
948# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
949# CONFIG_TFTP_DEBUG is not set
950CONFIG_TLS=y
951# CONFIG_TRACEROUTE is not set
952# CONFIG_TRACEROUTE6 is not set
953# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
954# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
955# CONFIG_TUNCTL is not set
956# CONFIG_FEATURE_TUNCTL_UG is not set
957# CONFIG_VCONFIG is not set
958CONFIG_WGET=y
959CONFIG_FEATURE_WGET_LONG_OPTIONS=y
960# CONFIG_FEATURE_WGET_STATUSBAR is not set
961CONFIG_FEATURE_WGET_AUTHENTICATION=y
962# CONFIG_FEATURE_WGET_TIMEOUT is not set
963CONFIG_FEATURE_WGET_HTTPS=y
964# CONFIG_FEATURE_WGET_OPENSSL is not set
965CONFIG_WHOIS=y
966# CONFIG_ZCIP is not set
967# CONFIG_UDHCPD is not set
968# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
969# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
970CONFIG_DHCPD_LEASES_FILE=""
971# CONFIG_DUMPLEASES is not set
972# CONFIG_DHCPRELAY is not set
973# CONFIG_UDHCPC is not set
974# CONFIG_FEATURE_UDHCPC_ARPING is not set
975# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set
976CONFIG_UDHCPC_DEFAULT_SCRIPT=""
977# CONFIG_UDHCPC6 is not set
978# CONFIG_FEATURE_UDHCPC6_RFC3646 is not set
979# CONFIG_FEATURE_UDHCPC6_RFC4704 is not set
980# CONFIG_FEATURE_UDHCPC6_RFC4833 is not set
981# CONFIG_FEATURE_UDHCPC6_RFC5970 is not set
982# CONFIG_FEATURE_UDHCP_PORT is not set
983CONFIG_UDHCP_DEBUG=0
984CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
985# CONFIG_FEATURE_UDHCP_RFC3397 is not set
986# CONFIG_FEATURE_UDHCP_8021Q is not set
987CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
988
989#
990# Print Utilities
991#
992# CONFIG_LPD is not set
993# CONFIG_LPR is not set
994# CONFIG_LPQ is not set
995
996#
997# Mail Utilities
998#
999# CONFIG_MAKEMIME is not set
1000# CONFIG_POPMAILDIR is not set
1001# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
1002# CONFIG_REFORMIME is not set
1003# CONFIG_FEATURE_REFORMIME_COMPAT is not set
1004# CONFIG_SENDMAIL is not set
1005CONFIG_FEATURE_MIME_CHARSET=""
1006
1007#
1008# Process Utilities
1009#
1010# CONFIG_FREE is not set
1011# CONFIG_FUSER is not set
1012# CONFIG_IOSTAT is not set
1013CONFIG_KILL=y
1014CONFIG_KILLALL=y
1015# CONFIG_KILLALL5 is not set
1016# CONFIG_LSOF is not set
1017# CONFIG_MPSTAT is not set
1018# CONFIG_NMETER is not set
1019CONFIG_PGREP=y
1020CONFIG_PKILL=y
1021CONFIG_PIDOF=y
1022CONFIG_FEATURE_PIDOF_SINGLE=y
1023CONFIG_FEATURE_PIDOF_OMIT=y
1024# CONFIG_PMAP is not set
1025# CONFIG_POWERTOP is not set
1026# CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set
1027CONFIG_PS=y
1028# CONFIG_FEATURE_PS_WIDE is not set
1029# CONFIG_FEATURE_PS_LONG is not set
1030CONFIG_FEATURE_PS_TIME=y
1031# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
1032# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
1033# CONFIG_PSTREE is not set
1034# CONFIG_PWDX is not set
1035# CONFIG_SMEMCAP is not set
1036# CONFIG_BB_SYSCTL is not set
1037# CONFIG_TOP is not set
1038# CONFIG_FEATURE_TOP_INTERACTIVE is not set
1039# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
1040# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
1041# CONFIG_FEATURE_TOP_SMP_CPU is not set
1042# CONFIG_FEATURE_TOP_DECIMALS is not set
1043# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
1044# CONFIG_FEATURE_TOPMEM is not set
1045# CONFIG_UPTIME is not set
1046# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
1047CONFIG_WATCH=y
1048# CONFIG_FEATURE_SHOW_THREADS is not set
1049
1050#
1051# Runit Utilities
1052#
1053# CONFIG_CHPST is not set
1054# CONFIG_SETUIDGID is not set
1055# CONFIG_ENVUIDGID is not set
1056# CONFIG_ENVDIR is not set
1057# CONFIG_SOFTLIMIT is not set
1058# CONFIG_RUNSV is not set
1059# CONFIG_RUNSVDIR is not set
1060# CONFIG_FEATURE_RUNSVDIR_LOG is not set
1061# CONFIG_SV is not set
1062CONFIG_SV_DEFAULT_SERVICE_DIR=""
1063# CONFIG_SVC is not set
1064# CONFIG_SVOK is not set
1065# CONFIG_SVLOGD is not set
1066# CONFIG_CHCON is not set
1067# CONFIG_GETENFORCE is not set
1068# CONFIG_GETSEBOOL is not set
1069# CONFIG_LOAD_POLICY is not set
1070# CONFIG_MATCHPATHCON is not set
1071# CONFIG_RUNCON is not set
1072# CONFIG_SELINUXENABLED is not set
1073# CONFIG_SESTATUS is not set
1074# CONFIG_SETENFORCE is not set
1075# CONFIG_SETFILES is not set
1076# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
1077# CONFIG_RESTORECON is not set
1078# CONFIG_SETSEBOOL is not set
1079
1080#
1081# Shells
1082#
1083CONFIG_SH_IS_ASH=y
1084# CONFIG_SH_IS_HUSH is not set
1085# CONFIG_SH_IS_NONE is not set
1086CONFIG_BASH_IS_ASH=y
1087# CONFIG_BASH_IS_HUSH is not set
1088# CONFIG_BASH_IS_NONE is not set
1089CONFIG_ASH=y
1090CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
1091CONFIG_ASH_INTERNAL_GLOB=y
1092CONFIG_ASH_BASH_COMPAT=y
1093# CONFIG_ASH_BASH_SOURCE_CURDIR is not set
1094CONFIG_ASH_BASH_NOT_FOUND_HOOK=y
1095# CONFIG_ASH_JOB_CONTROL is not set
1096CONFIG_ASH_ALIAS=y
1097CONFIG_ASH_RANDOM_SUPPORT=y
1098CONFIG_ASH_EXPAND_PRMT=y
1099# CONFIG_ASH_IDLE_TIMEOUT is not set
1100# CONFIG_ASH_MAIL is not set
1101CONFIG_ASH_ECHO=y
1102CONFIG_ASH_PRINTF=y
1103CONFIG_ASH_TEST=y
1104CONFIG_ASH_HELP=y
1105CONFIG_ASH_GETOPTS=y
1106CONFIG_ASH_CMDCMD=y
1107CONFIG_ASH_NOCONSOLE=y
1108# CONFIG_CTTYHACK is not set
1109# CONFIG_HUSH is not set
1110# CONFIG_HUSH_BASH_COMPAT is not set
1111# CONFIG_HUSH_BRACE_EXPANSION is not set
1112# CONFIG_HUSH_LINENO_VAR is not set
1113# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set
1114# CONFIG_HUSH_INTERACTIVE is not set
1115# CONFIG_HUSH_SAVEHISTORY is not set
1116# CONFIG_HUSH_JOB is not set
1117# CONFIG_HUSH_TICK is not set
1118# CONFIG_HUSH_IF is not set
1119# CONFIG_HUSH_LOOPS is not set
1120# CONFIG_HUSH_CASE is not set
1121# CONFIG_HUSH_FUNCTIONS is not set
1122# CONFIG_HUSH_LOCAL is not set
1123# CONFIG_HUSH_RANDOM_SUPPORT is not set
1124# CONFIG_HUSH_MODE_X is not set
1125# CONFIG_HUSH_ECHO is not set
1126# CONFIG_HUSH_PRINTF is not set
1127# CONFIG_HUSH_TEST is not set
1128# CONFIG_HUSH_HELP is not set
1129# CONFIG_HUSH_EXPORT is not set
1130# CONFIG_HUSH_EXPORT_N is not set
1131# CONFIG_HUSH_READONLY is not set
1132# CONFIG_HUSH_KILL is not set
1133# CONFIG_HUSH_WAIT is not set
1134# CONFIG_HUSH_COMMAND is not set
1135# CONFIG_HUSH_TRAP is not set
1136# CONFIG_HUSH_TYPE is not set
1137# CONFIG_HUSH_TIMES is not set
1138# CONFIG_HUSH_READ is not set
1139# CONFIG_HUSH_SET is not set
1140# CONFIG_HUSH_UNSET is not set
1141# CONFIG_HUSH_ULIMIT is not set
1142# CONFIG_HUSH_UMASK is not set
1143# CONFIG_HUSH_GETOPTS is not set
1144# CONFIG_HUSH_MEMLEAK is not set
1145
1146#
1147# Options common to all shells
1148#
1149CONFIG_FEATURE_SH_MATH=y
1150CONFIG_FEATURE_SH_MATH_64=y
1151CONFIG_FEATURE_SH_EXTRA_QUIET=y
1152CONFIG_FEATURE_SH_STANDALONE=y
1153CONFIG_FEATURE_SH_NOFORK=y
1154# CONFIG_FEATURE_SH_READ_FRAC is not set
1155CONFIG_FEATURE_SH_HISTFILESIZE=y
1156CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y
1157
1158#
1159# System Logging Utilities
1160#
1161# CONFIG_KLOGD is not set
1162# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
1163# CONFIG_LOGGER is not set
1164# CONFIG_LOGREAD is not set
1165# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
1166# CONFIG_SYSLOGD is not set
1167# CONFIG_FEATURE_ROTATE_LOGFILE is not set
1168# CONFIG_FEATURE_REMOTE_LOG is not set
1169# CONFIG_FEATURE_SYSLOGD_DUP is not set
1170# CONFIG_FEATURE_SYSLOGD_CFG is not set
1171CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
1172# CONFIG_FEATURE_IPC_SYSLOG is not set
1173CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
1174# CONFIG_FEATURE_KMSG_SYSLOG is not set
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig
new file mode 100644
index 000000000..46ebfbbfc
--- /dev/null
+++ b/configs/mingw64_defconfig
@@ -0,0 +1,1174 @@
1#
2# Automatically generated make config: don't edit
3# Busybox version: 1.30.0.git
4# Wed Nov 28 10:26:29 2018
5#
6CONFIG_HAVE_DOT_CONFIG=y
7# CONFIG_PLATFORM_POSIX is not set
8CONFIG_PLATFORM_MINGW32=y
9
10#
11# Settings
12#
13CONFIG_DESKTOP=y
14# CONFIG_EXTRA_COMPAT is not set
15# CONFIG_FEDORA_COMPAT is not set
16# CONFIG_INCLUDE_SUSv2 is not set
17CONFIG_LONG_OPTS=y
18CONFIG_SHOW_USAGE=y
19CONFIG_FEATURE_VERBOSE_USAGE=y
20CONFIG_FEATURE_COMPRESS_USAGE=y
21CONFIG_LFS=y
22# CONFIG_PAM is not set
23# CONFIG_FEATURE_DEVPTS is not set
24# CONFIG_FEATURE_UTMP is not set
25# CONFIG_FEATURE_WTMP is not set
26# CONFIG_FEATURE_PIDFILE is not set
27CONFIG_PID_FILE_PATH=""
28CONFIG_BUSYBOX=y
29CONFIG_FEATURE_SHOW_SCRIPT=y
30CONFIG_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
35CONFIG_FEATURE_PREFER_APPLETS=y
36CONFIG_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_PLATFORM_LINUX is not set
41# CONFIG_GLOBBING is not set
42CONFIG_FEATURE_PRNG_SHELL=y
43# CONFIG_FEATURE_PRNG_ISAAC is not set
44CONFIG_FEATURE_RESOURCES=y
45CONFIG_FEATURE_VERSIONINFO=y
46CONFIG_FEATURE_MANIFEST=y
47CONFIG_FEATURE_ICON=y
48# CONFIG_FEATURE_ICON_ATERM is not set
49# CONFIG_FEATURE_ICON_STERM is not set
50CONFIG_FEATURE_ICON_ALL=y
51
52#
53# Build Options
54#
55# CONFIG_STATIC is not set
56# CONFIG_PIE is not set
57# CONFIG_NOMMU is not set
58# CONFIG_BUILD_LIBBUSYBOX is not set
59# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set
60# CONFIG_FEATURE_INDIVIDUAL is not set
61# CONFIG_FEATURE_SHARED_BUSYBOX is not set
62CONFIG_CROSS_COMPILER_PREFIX="x86_64-w64-mingw32-"
63CONFIG_SYSROOT=""
64CONFIG_EXTRA_CFLAGS="-funwind-tables -fasynchronous-unwind-tables"
65CONFIG_EXTRA_LDFLAGS=""
66CONFIG_EXTRA_LDLIBS=""
67CONFIG_USE_PORTABLE_CODE=y
68CONFIG_STACK_OPTIMIZATION_386=y
69
70#
71# Installation Options ("make install" behavior)
72#
73CONFIG_INSTALL_APPLET_SYMLINKS=y
74# CONFIG_INSTALL_APPLET_HARDLINKS is not set
75# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
76# CONFIG_INSTALL_APPLET_DONT is not set
77# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
78# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
79# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
80CONFIG_PREFIX=""
81
82#
83# Debugging Options
84#
85CONFIG_DEBUG=y
86# CONFIG_DEBUG_PESSIMIZE is not set
87# CONFIG_DEBUG_SANITIZE is not set
88# CONFIG_UNIT_TEST is not set
89# CONFIG_WERROR is not set
90CONFIG_NO_DEBUG_LIB=y
91# CONFIG_DMALLOC is not set
92# CONFIG_EFENCE is not set
93
94#
95# Library Tuning
96#
97# CONFIG_FEATURE_USE_BSS_TAIL is not set
98CONFIG_FLOAT_DURATION=y
99CONFIG_FEATURE_RTMINMAX=y
100CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS=y
101CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
102# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
103# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
104CONFIG_PASSWORD_MINLEN=6
105CONFIG_MD5_SMALL=1
106CONFIG_SHA3_SMALL=1
107# CONFIG_FEATURE_FAST_TOP is not set
108# CONFIG_FEATURE_ETC_NETWORKS is not set
109# CONFIG_FEATURE_ETC_SERVICES is not set
110CONFIG_FEATURE_EDITING=y
111CONFIG_FEATURE_EDITING_MAX_LEN=1024
112CONFIG_FEATURE_EDITING_VI=y
113CONFIG_FEATURE_EDITING_HISTORY=255
114CONFIG_FEATURE_EDITING_SAVEHISTORY=y
115# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
116CONFIG_FEATURE_REVERSE_SEARCH=y
117CONFIG_FEATURE_TAB_COMPLETION=y
118# CONFIG_FEATURE_USERNAME_COMPLETION is not set
119CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
120# CONFIG_FEATURE_EDITING_WINCH is not set
121# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
122# CONFIG_LOCALE_SUPPORT is not set
123# CONFIG_UNICODE_SUPPORT is not set
124# CONFIG_UNICODE_USING_LOCALE is not set
125# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
126CONFIG_SUBST_WCHAR=0
127CONFIG_LAST_SUPPORTED_WCHAR=0
128# CONFIG_UNICODE_COMBINING_WCHARS is not set
129# CONFIG_UNICODE_WIDE_WCHARS is not set
130# CONFIG_UNICODE_BIDI_SUPPORT is not set
131# CONFIG_UNICODE_NEUTRAL_TABLE is not set
132# CONFIG_UNICODE_PRESERVE_BROKEN is not set
133CONFIG_FEATURE_NON_POSIX_CP=y
134# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
135# CONFIG_FEATURE_USE_SENDFILE is not set
136CONFIG_FEATURE_COPYBUF_KB=4
137CONFIG_FEATURE_SKIP_ROOTFS=y
138# CONFIG_MONOTONIC_SYSCALL is not set
139CONFIG_IOCTL_HEX2STR_ERROR=y
140# CONFIG_FEATURE_HWIB is not set
141
142#
143# Applets
144#
145
146#
147# Archival Utilities
148#
149CONFIG_FEATURE_SEAMLESS_XZ=y
150CONFIG_FEATURE_SEAMLESS_LZMA=y
151CONFIG_FEATURE_SEAMLESS_BZ2=y
152CONFIG_FEATURE_SEAMLESS_GZ=y
153CONFIG_FEATURE_SEAMLESS_Z=y
154CONFIG_AR=y
155CONFIG_FEATURE_AR_LONG_FILENAMES=y
156CONFIG_FEATURE_AR_CREATE=y
157CONFIG_UNCOMPRESS=y
158CONFIG_GUNZIP=y
159CONFIG_ZCAT=y
160CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
161CONFIG_BUNZIP2=y
162CONFIG_BZCAT=y
163CONFIG_UNLZMA=y
164CONFIG_LZCAT=y
165CONFIG_LZMA=y
166CONFIG_UNXZ=y
167CONFIG_XZCAT=y
168CONFIG_XZ=y
169CONFIG_BZIP2=y
170CONFIG_BZIP2_SMALL=8
171CONFIG_FEATURE_BZIP2_DECOMPRESS=y
172CONFIG_CPIO=y
173CONFIG_FEATURE_CPIO_O=y
174# CONFIG_FEATURE_CPIO_P is not set
175# CONFIG_DPKG is not set
176CONFIG_DPKG_DEB=y
177CONFIG_GZIP=y
178CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
179CONFIG_GZIP_FAST=2
180CONFIG_FEATURE_GZIP_LEVELS=y
181CONFIG_FEATURE_GZIP_DECOMPRESS=y
182CONFIG_LZOP=y
183CONFIG_UNLZOP=y
184CONFIG_LZOPCAT=y
185# CONFIG_LZOP_COMPR_HIGH is not set
186CONFIG_RPM=y
187CONFIG_RPM2CPIO=y
188CONFIG_TAR=y
189CONFIG_FEATURE_TAR_LONG_OPTIONS=y
190CONFIG_FEATURE_TAR_CREATE=y
191CONFIG_FEATURE_TAR_AUTODETECT=y
192CONFIG_FEATURE_TAR_FROM=y
193CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
194# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
195CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
196# CONFIG_FEATURE_TAR_TO_COMMAND is not set
197# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
198CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
199# CONFIG_FEATURE_TAR_SELINUX is not set
200CONFIG_UNZIP=y
201CONFIG_FEATURE_UNZIP_CDF=y
202CONFIG_FEATURE_UNZIP_BZIP2=y
203CONFIG_FEATURE_UNZIP_LZMA=y
204CONFIG_FEATURE_UNZIP_XZ=y
205CONFIG_FEATURE_LZMA_FAST=y
206
207#
208# Coreutils
209#
210CONFIG_BASENAME=y
211CONFIG_CAT=y
212CONFIG_FEATURE_CATN=y
213CONFIG_FEATURE_CATV=y
214# CONFIG_CHGRP is not set
215CONFIG_CHMOD=y
216# CONFIG_CHOWN is not set
217# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
218# CONFIG_CHROOT is not set
219CONFIG_CKSUM=y
220CONFIG_COMM=y
221CONFIG_CP=y
222CONFIG_FEATURE_CP_LONG_OPTIONS=y
223# CONFIG_FEATURE_CP_REFLINK is not set
224CONFIG_CUT=y
225CONFIG_DATE=y
226CONFIG_FEATURE_DATE_ISOFMT=y
227# CONFIG_FEATURE_DATE_NANO is not set
228CONFIG_FEATURE_DATE_COMPAT=y
229CONFIG_DD=y
230# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set
231# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
232CONFIG_FEATURE_DD_IBS_OBS=y
233CONFIG_FEATURE_DD_STATUS=y
234CONFIG_DF=y
235# CONFIG_FEATURE_DF_FANCY is not set
236CONFIG_DIRNAME=y
237CONFIG_DOS2UNIX=y
238CONFIG_UNIX2DOS=y
239CONFIG_DU=y
240CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
241CONFIG_ECHO=y
242CONFIG_FEATURE_FANCY_ECHO=y
243CONFIG_ENV=y
244CONFIG_EXPAND=y
245CONFIG_UNEXPAND=y
246CONFIG_EXPR=y
247CONFIG_EXPR_MATH_SUPPORT_64=y
248CONFIG_FACTOR=y
249CONFIG_FALSE=y
250CONFIG_FOLD=y
251CONFIG_FSYNC=y
252CONFIG_HEAD=y
253CONFIG_FEATURE_FANCY_HEAD=y
254# CONFIG_HOSTID is not set
255CONFIG_ID=y
256CONFIG_GROUPS=y
257# CONFIG_INSTALL is not set
258# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
259CONFIG_LINK=y
260CONFIG_LN=y
261CONFIG_LOGNAME=y
262CONFIG_LS=y
263CONFIG_FEATURE_LS_FILETYPES=y
264CONFIG_FEATURE_LS_FOLLOWLINKS=y
265CONFIG_FEATURE_LS_RECURSIVE=y
266CONFIG_FEATURE_LS_WIDTH=y
267CONFIG_FEATURE_LS_SORTFILES=y
268CONFIG_FEATURE_LS_TIMESTAMPS=y
269CONFIG_FEATURE_LS_USERNAME=y
270CONFIG_FEATURE_LS_COLOR=y
271CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
272CONFIG_MD5SUM=y
273CONFIG_SHA1SUM=y
274CONFIG_SHA256SUM=y
275CONFIG_SHA512SUM=y
276CONFIG_SHA3SUM=y
277
278#
279# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
280#
281CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
282CONFIG_MKDIR=y
283# CONFIG_MKFIFO is not set
284# CONFIG_MKNOD is not set
285CONFIG_MKTEMP=y
286CONFIG_MV=y
287# CONFIG_NICE is not set
288CONFIG_NL=y
289# CONFIG_NOHUP is not set
290# CONFIG_NPROC is not set
291CONFIG_OD=y
292CONFIG_PASTE=y
293CONFIG_PRINTENV=y
294CONFIG_PRINTF=y
295CONFIG_PWD=y
296# CONFIG_READLINK is not set
297# CONFIG_FEATURE_READLINK_FOLLOW is not set
298CONFIG_REALPATH=y
299CONFIG_RM=y
300CONFIG_RMDIR=y
301CONFIG_SEQ=y
302CONFIG_SHRED=y
303CONFIG_SHUF=y
304CONFIG_SLEEP=y
305CONFIG_FEATURE_FANCY_SLEEP=y
306CONFIG_SORT=y
307CONFIG_FEATURE_SORT_BIG=y
308# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set
309CONFIG_SPLIT=y
310CONFIG_FEATURE_SPLIT_FANCY=y
311CONFIG_STAT=y
312CONFIG_FEATURE_STAT_FORMAT=y
313CONFIG_FEATURE_STAT_FILESYSTEM=y
314# CONFIG_STTY is not set
315CONFIG_SUM=y
316# CONFIG_SYNC is not set
317# CONFIG_FEATURE_SYNC_FANCY is not set
318CONFIG_TAC=y
319CONFIG_TAIL=y
320CONFIG_FEATURE_FANCY_TAIL=y
321CONFIG_TEE=y
322CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
323CONFIG_TEST=y
324CONFIG_TEST1=y
325CONFIG_TEST2=y
326CONFIG_FEATURE_TEST_64=y
327CONFIG_TIMEOUT=y
328CONFIG_TOUCH=y
329# CONFIG_FEATURE_TOUCH_NODEREF is not set
330CONFIG_FEATURE_TOUCH_SUSV3=y
331CONFIG_TR=y
332CONFIG_FEATURE_TR_CLASSES=y
333CONFIG_FEATURE_TR_EQUIV=y
334CONFIG_TRUE=y
335CONFIG_TRUNCATE=y
336# CONFIG_TTY is not set
337CONFIG_UNAME=y
338CONFIG_UNAME_OSNAME="MS/Windows"
339CONFIG_BB_ARCH=y
340CONFIG_UNIQ=y
341CONFIG_UNLINK=y
342CONFIG_USLEEP=y
343CONFIG_UUDECODE=y
344CONFIG_BASE64=y
345CONFIG_UUENCODE=y
346CONFIG_WC=y
347CONFIG_FEATURE_WC_LARGE=y
348CONFIG_WHOAMI=y
349# CONFIG_WHO is not set
350# CONFIG_W is not set
351# CONFIG_USERS is not set
352CONFIG_YES=y
353
354#
355# Common options
356#
357CONFIG_FEATURE_VERBOSE=y
358
359#
360# Common options for cp and mv
361#
362# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
363
364#
365# Common options for df, du, ls
366#
367CONFIG_FEATURE_HUMAN_READABLE=y
368
369#
370# Console Utilities
371#
372# CONFIG_CHVT is not set
373CONFIG_CLEAR=y
374# CONFIG_DEALLOCVT is not set
375# CONFIG_DUMPKMAP is not set
376# CONFIG_FGCONSOLE is not set
377# CONFIG_KBD_MODE is not set
378# CONFIG_LOADFONT is not set
379# CONFIG_SETFONT is not set
380# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
381CONFIG_DEFAULT_SETFONT_DIR=""
382# CONFIG_FEATURE_LOADFONT_PSF2 is not set
383# CONFIG_FEATURE_LOADFONT_RAW is not set
384# CONFIG_LOADKMAP is not set
385# CONFIG_OPENVT is not set
386# CONFIG_RESET is not set
387# CONFIG_RESIZE is not set
388# CONFIG_FEATURE_RESIZE_PRINT is not set
389# CONFIG_SETCONSOLE is not set
390# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
391# CONFIG_SETKEYCODES is not set
392# CONFIG_SETLOGCONS is not set
393# CONFIG_SHOWKEY is not set
394
395#
396# Debian Utilities
397#
398CONFIG_PIPE_PROGRESS=y
399# CONFIG_RUN_PARTS is not set
400# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
401# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
402# CONFIG_START_STOP_DAEMON is not set
403# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
404# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
405CONFIG_WHICH=y
406
407#
408# klibc-utils
409#
410# CONFIG_MINIPS is not set
411# CONFIG_NUKE is not set
412# CONFIG_RESUME is not set
413# CONFIG_RUN_INIT is not set
414
415#
416# Editors
417#
418CONFIG_AWK=y
419CONFIG_FEATURE_AWK_LIBM=y
420CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
421CONFIG_CMP=y
422CONFIG_DIFF=y
423CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
424CONFIG_FEATURE_DIFF_DIR=y
425CONFIG_ED=y
426CONFIG_PATCH=y
427CONFIG_SED=y
428CONFIG_VI=y
429CONFIG_FEATURE_VI_MAX_LEN=4096
430CONFIG_FEATURE_VI_8BIT=y
431CONFIG_FEATURE_VI_COLON=y
432CONFIG_FEATURE_VI_YANKMARK=y
433CONFIG_FEATURE_VI_SEARCH=y
434# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
435# CONFIG_FEATURE_VI_USE_SIGNALS is not set
436CONFIG_FEATURE_VI_DOT_CMD=y
437CONFIG_FEATURE_VI_READONLY=y
438CONFIG_FEATURE_VI_SETOPTS=y
439CONFIG_FEATURE_VI_SET=y
440CONFIG_FEATURE_VI_WIN_RESIZE=y
441# CONFIG_FEATURE_VI_ASK_TERMINAL is not set
442CONFIG_FEATURE_VI_UNDO=y
443CONFIG_FEATURE_VI_UNDO_QUEUE=y
444CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
445CONFIG_FEATURE_ALLOW_EXEC=y
446
447#
448# Finding Utilities
449#
450CONFIG_FIND=y
451CONFIG_FEATURE_FIND_PRINT0=y
452CONFIG_FEATURE_FIND_MTIME=y
453CONFIG_FEATURE_FIND_MMIN=y
454CONFIG_FEATURE_FIND_PERM=y
455CONFIG_FEATURE_FIND_TYPE=y
456CONFIG_FEATURE_FIND_EXECUTABLE=y
457# CONFIG_FEATURE_FIND_XDEV is not set
458CONFIG_FEATURE_FIND_MAXDEPTH=y
459CONFIG_FEATURE_FIND_NEWER=y
460# CONFIG_FEATURE_FIND_INUM is not set
461CONFIG_FEATURE_FIND_EXEC=y
462CONFIG_FEATURE_FIND_EXEC_PLUS=y
463# CONFIG_FEATURE_FIND_USER is not set
464# CONFIG_FEATURE_FIND_GROUP is not set
465CONFIG_FEATURE_FIND_NOT=y
466CONFIG_FEATURE_FIND_DEPTH=y
467CONFIG_FEATURE_FIND_PAREN=y
468CONFIG_FEATURE_FIND_SIZE=y
469CONFIG_FEATURE_FIND_PRUNE=y
470CONFIG_FEATURE_FIND_QUIT=y
471CONFIG_FEATURE_FIND_DELETE=y
472CONFIG_FEATURE_FIND_PATH=y
473CONFIG_FEATURE_FIND_REGEX=y
474# CONFIG_FEATURE_FIND_CONTEXT is not set
475# CONFIG_FEATURE_FIND_LINKS is not set
476CONFIG_GREP=y
477CONFIG_EGREP=y
478CONFIG_FGREP=y
479CONFIG_FEATURE_GREP_CONTEXT=y
480CONFIG_XARGS=y
481CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
482CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
483CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
484CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
485CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
486CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y
487CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y
488
489#
490# Init Utilities
491#
492# CONFIG_BOOTCHARTD is not set
493# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
494# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
495# CONFIG_HALT is not set
496# CONFIG_POWEROFF is not set
497# CONFIG_REBOOT is not set
498# CONFIG_FEATURE_WAIT_FOR_INIT is not set
499# CONFIG_FEATURE_CALL_TELINIT is not set
500CONFIG_TELINIT_PATH=""
501# CONFIG_INIT is not set
502# CONFIG_LINUXRC is not set
503# CONFIG_FEATURE_USE_INITTAB is not set
504# CONFIG_FEATURE_KILL_REMOVED is not set
505CONFIG_FEATURE_KILL_DELAY=0
506# CONFIG_FEATURE_INIT_SCTTY is not set
507# CONFIG_FEATURE_INIT_SYSLOG is not set
508# CONFIG_FEATURE_INIT_QUIET is not set
509# CONFIG_FEATURE_INIT_COREDUMPS is not set
510CONFIG_INIT_TERMINAL_TYPE=""
511# CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set
512
513#
514# Login/Password Management Utilities
515#
516# CONFIG_FEATURE_SHADOWPASSWDS is not set
517# CONFIG_USE_BB_PWD_GRP is not set
518# CONFIG_USE_BB_SHADOW is not set
519# CONFIG_USE_BB_CRYPT is not set
520# CONFIG_USE_BB_CRYPT_SHA is not set
521# CONFIG_ADDGROUP is not set
522# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
523# CONFIG_ADD_SHELL is not set
524# CONFIG_REMOVE_SHELL is not set
525# CONFIG_ADDUSER is not set
526# CONFIG_FEATURE_CHECK_NAMES is not set
527CONFIG_LAST_ID=0
528CONFIG_FIRST_SYSTEM_ID=0
529CONFIG_LAST_SYSTEM_ID=0
530# CONFIG_CHPASSWD is not set
531CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
532# CONFIG_CRYPTPW is not set
533# CONFIG_MKPASSWD is not set
534# CONFIG_DELUSER is not set
535# CONFIG_DELGROUP is not set
536# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
537# CONFIG_GETTY is not set
538# CONFIG_LOGIN is not set
539# CONFIG_LOGIN_SESSION_AS_CHILD is not set
540# CONFIG_LOGIN_SCRIPTS is not set
541# CONFIG_FEATURE_NOLOGIN is not set
542# CONFIG_FEATURE_SECURETTY is not set
543# CONFIG_PASSWD is not set
544# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
545# CONFIG_SU is not set
546# CONFIG_FEATURE_SU_SYSLOG is not set
547# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
548# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set
549# CONFIG_SULOGIN is not set
550# CONFIG_VLOCK is not set
551
552#
553# Linux Ext2 FS Progs
554#
555# CONFIG_CHATTR is not set
556# CONFIG_FSCK is not set
557# CONFIG_LSATTR is not set
558# CONFIG_TUNE2FS is not set
559
560#
561# Linux Module Utilities
562#
563# CONFIG_MODPROBE_SMALL is not set
564# CONFIG_DEPMOD is not set
565# CONFIG_INSMOD is not set
566# CONFIG_LSMOD is not set
567# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
568# CONFIG_MODINFO is not set
569# CONFIG_MODPROBE is not set
570# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
571# CONFIG_RMMOD is not set
572
573#
574# Options common to multiple modutils
575#
576# CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set
577# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
578# CONFIG_FEATURE_2_4_MODULES is not set
579# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
580# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
581# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
582# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
583# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
584# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
585# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
586# CONFIG_FEATURE_MODUTILS_ALIAS is not set
587# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
588CONFIG_DEFAULT_MODULES_DIR=""
589CONFIG_DEFAULT_DEPMOD_FILE=""
590
591#
592# Linux System Utilities
593#
594# CONFIG_ACPID is not set
595# CONFIG_FEATURE_ACPID_COMPAT is not set
596# CONFIG_BLKDISCARD is not set
597# CONFIG_BLKID is not set
598# CONFIG_FEATURE_BLKID_TYPE is not set
599# CONFIG_BLOCKDEV is not set
600CONFIG_CAL=y
601# CONFIG_CHRT is not set
602# CONFIG_DMESG is not set
603# CONFIG_FEATURE_DMESG_PRETTY is not set
604# CONFIG_EJECT is not set
605# CONFIG_FEATURE_EJECT_SCSI is not set
606# CONFIG_FALLOCATE is not set
607# CONFIG_FATATTR is not set
608# CONFIG_FBSET is not set
609# CONFIG_FEATURE_FBSET_FANCY is not set
610# CONFIG_FEATURE_FBSET_READMODE is not set
611# CONFIG_FDFORMAT is not set
612# CONFIG_FDISK is not set
613# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
614# CONFIG_FEATURE_FDISK_WRITABLE is not set
615# CONFIG_FEATURE_AIX_LABEL is not set
616# CONFIG_FEATURE_SGI_LABEL is not set
617# CONFIG_FEATURE_SUN_LABEL is not set
618# CONFIG_FEATURE_OSF_LABEL is not set
619# CONFIG_FEATURE_GPT_LABEL is not set
620# CONFIG_FEATURE_FDISK_ADVANCED is not set
621# CONFIG_FINDFS is not set
622# CONFIG_FLOCK is not set
623# CONFIG_FDFLUSH is not set
624# CONFIG_FREERAMDISK is not set
625# CONFIG_FSCK_MINIX is not set
626# CONFIG_FSFREEZE is not set
627# CONFIG_FSTRIM is not set
628CONFIG_GETOPT=y
629CONFIG_FEATURE_GETOPT_LONG=y
630CONFIG_HEXDUMP=y
631CONFIG_FEATURE_HEXDUMP_REVERSE=y
632CONFIG_HD=y
633CONFIG_XXD=y
634# CONFIG_HWCLOCK is not set
635# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
636# CONFIG_IONICE is not set
637# CONFIG_IPCRM is not set
638# CONFIG_IPCS is not set
639# CONFIG_LAST is not set
640# CONFIG_FEATURE_LAST_FANCY is not set
641# CONFIG_LOSETUP is not set
642# CONFIG_LSPCI is not set
643# CONFIG_LSUSB is not set
644# CONFIG_MDEV is not set
645# CONFIG_FEATURE_MDEV_CONF is not set
646# CONFIG_FEATURE_MDEV_RENAME is not set
647# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
648# CONFIG_FEATURE_MDEV_EXEC is not set
649# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
650# CONFIG_MESG is not set
651# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
652# CONFIG_MKE2FS is not set
653# CONFIG_MKFS_EXT2 is not set
654# CONFIG_MKFS_MINIX is not set
655# CONFIG_FEATURE_MINIX2 is not set
656# CONFIG_MKFS_REISER is not set
657# CONFIG_MKDOSFS is not set
658# CONFIG_MKFS_VFAT is not set
659# CONFIG_MKSWAP is not set
660# CONFIG_FEATURE_MKSWAP_UUID is not set
661# CONFIG_MORE is not set
662# CONFIG_MOUNT is not set
663# CONFIG_FEATURE_MOUNT_FAKE is not set
664# CONFIG_FEATURE_MOUNT_VERBOSE is not set
665# CONFIG_FEATURE_MOUNT_HELPERS is not set
666# CONFIG_FEATURE_MOUNT_LABEL is not set
667# CONFIG_FEATURE_MOUNT_NFS is not set
668# CONFIG_FEATURE_MOUNT_CIFS is not set
669# CONFIG_FEATURE_MOUNT_FLAGS is not set
670# CONFIG_FEATURE_MOUNT_FSTAB is not set
671# CONFIG_FEATURE_MOUNT_OTHERTAB is not set
672# CONFIG_MOUNTPOINT is not set
673# CONFIG_NOLOGIN is not set
674# CONFIG_NOLOGIN_DEPENDENCIES is not set
675# CONFIG_NSENTER is not set
676# CONFIG_PIVOT_ROOT is not set
677# CONFIG_RDATE is not set
678# CONFIG_RDEV is not set
679# CONFIG_READPROFILE is not set
680# CONFIG_RENICE is not set
681CONFIG_REV=y
682# CONFIG_RTCWAKE is not set
683# CONFIG_SCRIPT is not set
684# CONFIG_SCRIPTREPLAY is not set
685# CONFIG_SETARCH is not set
686# CONFIG_LINUX32 is not set
687# CONFIG_LINUX64 is not set
688# CONFIG_SETPRIV is not set
689# CONFIG_FEATURE_SETPRIV_DUMP is not set
690# CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set
691# CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set
692# CONFIG_SETSID is not set
693# CONFIG_SWAPON is not set
694# CONFIG_FEATURE_SWAPON_DISCARD is not set
695# CONFIG_FEATURE_SWAPON_PRI is not set
696# CONFIG_SWAPOFF is not set
697# CONFIG_FEATURE_SWAPONOFF_LABEL is not set
698# CONFIG_SWITCH_ROOT is not set
699# CONFIG_TASKSET is not set
700# CONFIG_FEATURE_TASKSET_FANCY is not set
701# CONFIG_UEVENT is not set
702# CONFIG_UMOUNT is not set
703# CONFIG_FEATURE_UMOUNT_ALL is not set
704# CONFIG_UNSHARE is not set
705# CONFIG_WALL is not set
706# CONFIG_FEATURE_MOUNT_LOOP is not set
707# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
708# CONFIG_FEATURE_MTAB_SUPPORT is not set
709# CONFIG_VOLUMEID is not set
710# CONFIG_FEATURE_VOLUMEID_BCACHE is not set
711# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
712# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
713# CONFIG_FEATURE_VOLUMEID_EXFAT is not set
714# CONFIG_FEATURE_VOLUMEID_EXT is not set
715# CONFIG_FEATURE_VOLUMEID_F2FS is not set
716# CONFIG_FEATURE_VOLUMEID_FAT is not set
717# CONFIG_FEATURE_VOLUMEID_HFS is not set
718# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
719# CONFIG_FEATURE_VOLUMEID_JFS is not set
720# CONFIG_FEATURE_VOLUMEID_LFS is not set
721# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
722# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
723# CONFIG_FEATURE_VOLUMEID_LUKS is not set
724# CONFIG_FEATURE_VOLUMEID_MINIX is not set
725# CONFIG_FEATURE_VOLUMEID_NILFS is not set
726# CONFIG_FEATURE_VOLUMEID_NTFS is not set
727# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
728# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
729# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
730# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set
731# CONFIG_FEATURE_VOLUMEID_SYSV is not set
732# CONFIG_FEATURE_VOLUMEID_UBIFS is not set
733# CONFIG_FEATURE_VOLUMEID_UDF is not set
734# CONFIG_FEATURE_VOLUMEID_XFS is not set
735
736#
737# Miscellaneous Utilities
738#
739# CONFIG_ADJTIMEX is not set
740# CONFIG_BBCONFIG is not set
741# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
742# CONFIG_BEEP is not set
743CONFIG_FEATURE_BEEP_FREQ=0
744CONFIG_FEATURE_BEEP_LENGTH_MS=0
745# CONFIG_CHAT is not set
746# CONFIG_FEATURE_CHAT_NOFAIL is not set
747# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
748# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
749# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
750# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
751# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
752# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
753# CONFIG_CONSPY is not set
754# CONFIG_CROND is not set
755# CONFIG_FEATURE_CROND_D is not set
756# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
757# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set
758CONFIG_FEATURE_CROND_DIR=""
759# CONFIG_CRONTAB is not set
760CONFIG_DC=y
761CONFIG_FEATURE_DC_LIBM=y
762# CONFIG_DEVFSD is not set
763# CONFIG_DEVFSD_MODLOAD is not set
764# CONFIG_DEVFSD_FG_NP is not set
765# CONFIG_DEVFSD_VERBOSE is not set
766# CONFIG_FEATURE_DEVFS is not set
767# CONFIG_DEVMEM is not set
768# CONFIG_FBSPLASH is not set
769# CONFIG_FLASHCP is not set
770# CONFIG_FLASH_ERASEALL is not set
771# CONFIG_FLASH_LOCK is not set
772# CONFIG_FLASH_UNLOCK is not set
773# CONFIG_HDPARM is not set
774# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
775# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
776# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
777# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
778# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
779# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
780# CONFIG_HEXEDIT is not set
781# CONFIG_I2CGET is not set
782# CONFIG_I2CSET is not set
783# CONFIG_I2CDUMP is not set
784# CONFIG_I2CDETECT is not set
785CONFIG_ICONV=y
786# CONFIG_INOTIFYD is not set
787CONFIG_LESS=y
788CONFIG_FEATURE_LESS_MAXLINES=9999999
789CONFIG_FEATURE_LESS_BRACKETS=y
790CONFIG_FEATURE_LESS_FLAGS=y
791CONFIG_FEATURE_LESS_TRUNCATE=y
792CONFIG_FEATURE_LESS_MARKS=y
793CONFIG_FEATURE_LESS_REGEXP=y
794# CONFIG_FEATURE_LESS_WINCH is not set
795# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set
796CONFIG_FEATURE_LESS_DASHCMD=y
797CONFIG_FEATURE_LESS_LINENUMS=y
798CONFIG_FEATURE_LESS_RAW=y
799CONFIG_FEATURE_LESS_ENV=y
800# CONFIG_LSSCSI is not set
801# CONFIG_MAKEDEVS is not set
802# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
803# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
804CONFIG_MAN=y
805# CONFIG_MICROCOM is not set
806# CONFIG_MT is not set
807# CONFIG_NANDWRITE is not set
808# CONFIG_NANDDUMP is not set
809# CONFIG_PARTPROBE is not set
810# CONFIG_RAIDAUTORUN is not set
811# CONFIG_READAHEAD is not set
812# CONFIG_RFKILL is not set
813# CONFIG_RUNLEVEL is not set
814# CONFIG_RX is not set
815# CONFIG_SETFATTR is not set
816# CONFIG_SETSERIAL is not set
817CONFIG_STRINGS=y
818# CONFIG_TIME is not set
819CONFIG_TTYSIZE=y
820# CONFIG_UBIRENAME is not set
821# CONFIG_UBIATTACH is not set
822# CONFIG_UBIDETACH is not set
823# CONFIG_UBIMKVOL is not set
824# CONFIG_UBIRMVOL is not set
825# CONFIG_UBIRSVOL is not set
826# CONFIG_UBIUPDATEVOL is not set
827# CONFIG_VOLNAME is not set
828# CONFIG_WATCHDOG is not set
829
830#
831# Networking Utilities
832#
833CONFIG_FEATURE_IPV6=y
834# CONFIG_FEATURE_UNIX_LOCAL is not set
835CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
836# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
837# CONFIG_ARP is not set
838# CONFIG_ARPING is not set
839# CONFIG_BRCTL is not set
840# CONFIG_FEATURE_BRCTL_FANCY is not set
841# CONFIG_FEATURE_BRCTL_SHOW is not set
842# CONFIG_DNSD is not set
843# CONFIG_ETHER_WAKE is not set
844# CONFIG_FTPD is not set
845# CONFIG_FEATURE_FTPD_WRITE is not set
846# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
847# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set
848CONFIG_FTPGET=y
849CONFIG_FTPPUT=y
850CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
851# CONFIG_HOSTNAME is not set
852# CONFIG_DNSDOMAINNAME is not set
853# CONFIG_HTTPD is not set
854# CONFIG_FEATURE_HTTPD_RANGES is not set
855# CONFIG_FEATURE_HTTPD_SETUID is not set
856# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
857# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
858# CONFIG_FEATURE_HTTPD_CGI is not set
859# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
860# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
861# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
862# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
863# CONFIG_FEATURE_HTTPD_PROXY is not set
864# CONFIG_FEATURE_HTTPD_GZIP is not set
865# CONFIG_IFCONFIG is not set
866# CONFIG_FEATURE_IFCONFIG_STATUS is not set
867# CONFIG_FEATURE_IFCONFIG_SLIP is not set
868# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
869# CONFIG_FEATURE_IFCONFIG_HW is not set
870# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
871# CONFIG_IFENSLAVE is not set
872# CONFIG_IFPLUGD is not set
873# CONFIG_IFUP is not set
874# CONFIG_IFDOWN is not set
875CONFIG_IFUPDOWN_IFSTATE_PATH=""
876# CONFIG_FEATURE_IFUPDOWN_IP is not set
877# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
878# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
879# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
880# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
881# CONFIG_INETD is not set
882# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
883# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
884# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
885# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
886# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
887# CONFIG_FEATURE_INETD_RPC is not set
888# CONFIG_IP is not set
889# CONFIG_IPADDR is not set
890# CONFIG_IPLINK is not set
891# CONFIG_IPROUTE is not set
892# CONFIG_IPTUNNEL is not set
893# CONFIG_IPRULE is not set
894# CONFIG_IPNEIGH is not set
895# CONFIG_FEATURE_IP_ADDRESS is not set
896# CONFIG_FEATURE_IP_LINK is not set
897# CONFIG_FEATURE_IP_ROUTE is not set
898CONFIG_FEATURE_IP_ROUTE_DIR=""
899# CONFIG_FEATURE_IP_TUNNEL is not set
900# CONFIG_FEATURE_IP_RULE is not set
901# CONFIG_FEATURE_IP_NEIGH is not set
902# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
903CONFIG_IPCALC=y
904CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
905CONFIG_FEATURE_IPCALC_FANCY=y
906# CONFIG_FAKEIDENTD is not set
907# CONFIG_NAMEIF is not set
908# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
909# CONFIG_NBDCLIENT is not set
910CONFIG_NC=y
911# CONFIG_NETCAT is not set
912CONFIG_NC_SERVER=y
913# CONFIG_NC_EXTRA is not set
914# CONFIG_NC_110_COMPAT is not set
915# CONFIG_NETSTAT is not set
916# CONFIG_FEATURE_NETSTAT_WIDE is not set
917# CONFIG_FEATURE_NETSTAT_PRG is not set
918# CONFIG_NSLOOKUP is not set
919# CONFIG_FEATURE_NSLOOKUP_BIG is not set
920# CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS is not set
921# CONFIG_NTPD is not set
922# CONFIG_FEATURE_NTPD_SERVER is not set
923# CONFIG_FEATURE_NTPD_CONF is not set
924# CONFIG_FEATURE_NTP_AUTH is not set
925# CONFIG_PING is not set
926# CONFIG_PING6 is not set
927# CONFIG_FEATURE_FANCY_PING is not set
928# CONFIG_PSCAN is not set
929# CONFIG_ROUTE is not set
930# CONFIG_SLATTACH is not set
931CONFIG_SSL_CLIENT=y
932# CONFIG_TC is not set
933# CONFIG_FEATURE_TC_INGRESS is not set
934# CONFIG_TCPSVD is not set
935# CONFIG_UDPSVD is not set
936# CONFIG_TELNET is not set
937# CONFIG_FEATURE_TELNET_TTYPE is not set
938# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
939# CONFIG_FEATURE_TELNET_WIDTH is not set
940# CONFIG_TELNETD is not set
941# CONFIG_FEATURE_TELNETD_STANDALONE is not set
942# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
943# CONFIG_TFTP is not set
944# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
945# CONFIG_TFTPD is not set
946# CONFIG_FEATURE_TFTP_GET is not set
947# CONFIG_FEATURE_TFTP_PUT is not set
948# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
949# CONFIG_TFTP_DEBUG is not set
950CONFIG_TLS=y
951# CONFIG_TRACEROUTE is not set
952# CONFIG_TRACEROUTE6 is not set
953# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
954# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
955# CONFIG_TUNCTL is not set
956# CONFIG_FEATURE_TUNCTL_UG is not set
957# CONFIG_VCONFIG is not set
958CONFIG_WGET=y
959CONFIG_FEATURE_WGET_LONG_OPTIONS=y
960# CONFIG_FEATURE_WGET_STATUSBAR is not set
961CONFIG_FEATURE_WGET_AUTHENTICATION=y
962# CONFIG_FEATURE_WGET_TIMEOUT is not set
963CONFIG_FEATURE_WGET_HTTPS=y
964# CONFIG_FEATURE_WGET_OPENSSL is not set
965CONFIG_WHOIS=y
966# CONFIG_ZCIP is not set
967# CONFIG_UDHCPD is not set
968# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
969# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
970CONFIG_DHCPD_LEASES_FILE=""
971# CONFIG_DUMPLEASES is not set
972# CONFIG_DHCPRELAY is not set
973# CONFIG_UDHCPC is not set
974# CONFIG_FEATURE_UDHCPC_ARPING is not set
975# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set
976CONFIG_UDHCPC_DEFAULT_SCRIPT=""
977# CONFIG_UDHCPC6 is not set
978# CONFIG_FEATURE_UDHCPC6_RFC3646 is not set
979# CONFIG_FEATURE_UDHCPC6_RFC4704 is not set
980# CONFIG_FEATURE_UDHCPC6_RFC4833 is not set
981# CONFIG_FEATURE_UDHCPC6_RFC5970 is not set
982# CONFIG_FEATURE_UDHCP_PORT is not set
983CONFIG_UDHCP_DEBUG=0
984CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
985# CONFIG_FEATURE_UDHCP_RFC3397 is not set
986# CONFIG_FEATURE_UDHCP_8021Q is not set
987CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
988
989#
990# Print Utilities
991#
992# CONFIG_LPD is not set
993# CONFIG_LPR is not set
994# CONFIG_LPQ is not set
995
996#
997# Mail Utilities
998#
999# CONFIG_MAKEMIME is not set
1000# CONFIG_POPMAILDIR is not set
1001# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
1002# CONFIG_REFORMIME is not set
1003# CONFIG_FEATURE_REFORMIME_COMPAT is not set
1004# CONFIG_SENDMAIL is not set
1005CONFIG_FEATURE_MIME_CHARSET=""
1006
1007#
1008# Process Utilities
1009#
1010# CONFIG_FREE is not set
1011# CONFIG_FUSER is not set
1012# CONFIG_IOSTAT is not set
1013CONFIG_KILL=y
1014CONFIG_KILLALL=y
1015# CONFIG_KILLALL5 is not set
1016# CONFIG_LSOF is not set
1017# CONFIG_MPSTAT is not set
1018# CONFIG_NMETER is not set
1019CONFIG_PGREP=y
1020CONFIG_PKILL=y
1021CONFIG_PIDOF=y
1022CONFIG_FEATURE_PIDOF_SINGLE=y
1023CONFIG_FEATURE_PIDOF_OMIT=y
1024# CONFIG_PMAP is not set
1025# CONFIG_POWERTOP is not set
1026# CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set
1027CONFIG_PS=y
1028# CONFIG_FEATURE_PS_WIDE is not set
1029# CONFIG_FEATURE_PS_LONG is not set
1030CONFIG_FEATURE_PS_TIME=y
1031# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
1032# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
1033# CONFIG_PSTREE is not set
1034# CONFIG_PWDX is not set
1035# CONFIG_SMEMCAP is not set
1036# CONFIG_BB_SYSCTL is not set
1037# CONFIG_TOP is not set
1038# CONFIG_FEATURE_TOP_INTERACTIVE is not set
1039# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
1040# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
1041# CONFIG_FEATURE_TOP_SMP_CPU is not set
1042# CONFIG_FEATURE_TOP_DECIMALS is not set
1043# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
1044# CONFIG_FEATURE_TOPMEM is not set
1045# CONFIG_UPTIME is not set
1046# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
1047CONFIG_WATCH=y
1048# CONFIG_FEATURE_SHOW_THREADS is not set
1049
1050#
1051# Runit Utilities
1052#
1053# CONFIG_CHPST is not set
1054# CONFIG_SETUIDGID is not set
1055# CONFIG_ENVUIDGID is not set
1056# CONFIG_ENVDIR is not set
1057# CONFIG_SOFTLIMIT is not set
1058# CONFIG_RUNSV is not set
1059# CONFIG_RUNSVDIR is not set
1060# CONFIG_FEATURE_RUNSVDIR_LOG is not set
1061# CONFIG_SV is not set
1062CONFIG_SV_DEFAULT_SERVICE_DIR=""
1063# CONFIG_SVC is not set
1064# CONFIG_SVOK is not set
1065# CONFIG_SVLOGD is not set
1066# CONFIG_CHCON is not set
1067# CONFIG_GETENFORCE is not set
1068# CONFIG_GETSEBOOL is not set
1069# CONFIG_LOAD_POLICY is not set
1070# CONFIG_MATCHPATHCON is not set
1071# CONFIG_RUNCON is not set
1072# CONFIG_SELINUXENABLED is not set
1073# CONFIG_SESTATUS is not set
1074# CONFIG_SETENFORCE is not set
1075# CONFIG_SETFILES is not set
1076# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
1077# CONFIG_RESTORECON is not set
1078# CONFIG_SETSEBOOL is not set
1079
1080#
1081# Shells
1082#
1083CONFIG_SH_IS_ASH=y
1084# CONFIG_SH_IS_HUSH is not set
1085# CONFIG_SH_IS_NONE is not set
1086CONFIG_BASH_IS_ASH=y
1087# CONFIG_BASH_IS_HUSH is not set
1088# CONFIG_BASH_IS_NONE is not set
1089CONFIG_ASH=y
1090CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
1091CONFIG_ASH_INTERNAL_GLOB=y
1092CONFIG_ASH_BASH_COMPAT=y
1093# CONFIG_ASH_BASH_SOURCE_CURDIR is not set
1094CONFIG_ASH_BASH_NOT_FOUND_HOOK=y
1095# CONFIG_ASH_JOB_CONTROL is not set
1096CONFIG_ASH_ALIAS=y
1097CONFIG_ASH_RANDOM_SUPPORT=y
1098CONFIG_ASH_EXPAND_PRMT=y
1099# CONFIG_ASH_IDLE_TIMEOUT is not set
1100# CONFIG_ASH_MAIL is not set
1101CONFIG_ASH_ECHO=y
1102CONFIG_ASH_PRINTF=y
1103CONFIG_ASH_TEST=y
1104CONFIG_ASH_HELP=y
1105CONFIG_ASH_GETOPTS=y
1106CONFIG_ASH_CMDCMD=y
1107# CONFIG_ASH_NOCONSOLE is not set
1108# CONFIG_CTTYHACK is not set
1109# CONFIG_HUSH is not set
1110# CONFIG_HUSH_BASH_COMPAT is not set
1111# CONFIG_HUSH_BRACE_EXPANSION is not set
1112# CONFIG_HUSH_LINENO_VAR is not set
1113# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set
1114# CONFIG_HUSH_INTERACTIVE is not set
1115# CONFIG_HUSH_SAVEHISTORY is not set
1116# CONFIG_HUSH_JOB is not set
1117# CONFIG_HUSH_TICK is not set
1118# CONFIG_HUSH_IF is not set
1119# CONFIG_HUSH_LOOPS is not set
1120# CONFIG_HUSH_CASE is not set
1121# CONFIG_HUSH_FUNCTIONS is not set
1122# CONFIG_HUSH_LOCAL is not set
1123# CONFIG_HUSH_RANDOM_SUPPORT is not set
1124# CONFIG_HUSH_MODE_X is not set
1125# CONFIG_HUSH_ECHO is not set
1126# CONFIG_HUSH_PRINTF is not set
1127# CONFIG_HUSH_TEST is not set
1128# CONFIG_HUSH_HELP is not set
1129# CONFIG_HUSH_EXPORT is not set
1130# CONFIG_HUSH_EXPORT_N is not set
1131# CONFIG_HUSH_READONLY is not set
1132# CONFIG_HUSH_KILL is not set
1133# CONFIG_HUSH_WAIT is not set
1134# CONFIG_HUSH_COMMAND is not set
1135# CONFIG_HUSH_TRAP is not set
1136# CONFIG_HUSH_TYPE is not set
1137# CONFIG_HUSH_TIMES is not set
1138# CONFIG_HUSH_READ is not set
1139# CONFIG_HUSH_SET is not set
1140# CONFIG_HUSH_UNSET is not set
1141# CONFIG_HUSH_ULIMIT is not set
1142# CONFIG_HUSH_UMASK is not set
1143# CONFIG_HUSH_GETOPTS is not set
1144# CONFIG_HUSH_MEMLEAK is not set
1145
1146#
1147# Options common to all shells
1148#
1149CONFIG_FEATURE_SH_MATH=y
1150CONFIG_FEATURE_SH_MATH_64=y
1151CONFIG_FEATURE_SH_EXTRA_QUIET=y
1152CONFIG_FEATURE_SH_STANDALONE=y
1153CONFIG_FEATURE_SH_NOFORK=y
1154# CONFIG_FEATURE_SH_READ_FRAC is not set
1155CONFIG_FEATURE_SH_HISTFILESIZE=y
1156CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y
1157
1158#
1159# System Logging Utilities
1160#
1161# CONFIG_KLOGD is not set
1162# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
1163# CONFIG_LOGGER is not set
1164# CONFIG_LOGREAD is not set
1165# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
1166# CONFIG_SYSLOGD is not set
1167# CONFIG_FEATURE_ROTATE_LOGFILE is not set
1168# CONFIG_FEATURE_REMOTE_LOG is not set
1169# CONFIG_FEATURE_SYSLOGD_DUP is not set
1170# CONFIG_FEATURE_SYSLOGD_CFG is not set
1171CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
1172# CONFIG_FEATURE_IPC_SYSLOG is not set
1173CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
1174# CONFIG_FEATURE_KMSG_SYSLOG is not set
diff --git a/coreutils/dd.c b/coreutils/dd.c
index 4b31e9a7b..e0af8f4a3 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -90,6 +90,9 @@
90//usage: "\n status=none Suppress all output" 90//usage: "\n status=none Suppress all output"
91//usage: ) 91//usage: )
92//usage: "\n" 92//usage: "\n"
93//usage: IF_PLATFORM_MINGW32(
94//usage: "\nif=/dev/zero and if=/dev/urandom are supported"
95//usage: )
93//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G" 96//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G"
94//usage: 97//usage:
95//usage:#define dd_example_usage 98//usage:#define dd_example_usage
@@ -365,11 +368,11 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
365#if ENABLE_FEATURE_DD_IBS_OBS 368#if ENABLE_FEATURE_DD_IBS_OBS
366 if (what == OP_ibs) { 369 if (what == OP_ibs) {
367 /* Must fit into positive ssize_t */ 370 /* Must fit into positive ssize_t */
368 ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 371 ibs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
369 /*continue;*/ 372 /*continue;*/
370 } 373 }
371 if (what == OP_obs) { 374 if (what == OP_obs) {
372 obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 375 obs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
373 /*continue;*/ 376 /*continue;*/
374 } 377 }
375 if (what == OP_conv) { 378 if (what == OP_conv) {
@@ -386,7 +389,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
386 } 389 }
387#endif 390#endif
388 if (what == OP_bs) { 391 if (what == OP_bs) {
389 ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 392 ibs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
390 obs = ibs; 393 obs = ibs;
391 /*continue;*/ 394 /*continue;*/
392 } 395 }
@@ -442,7 +445,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
442#endif 445#endif
443 446
444 if (infile) { 447 if (infile) {
448#if !ENABLE_PLATFORM_MINGW32
445 xmove_fd(xopen(infile, O_RDONLY), ifd); 449 xmove_fd(xopen(infile, O_RDONLY), ifd);
450#else
451 xmove_fd(mingw_xopen(infile, O_RDONLY), ifd);
452 update_dev_fd(get_dev_type(infile), ifd);
453#endif
446 } else { 454 } else {
447 infile = bb_msg_standard_input; 455 infile = bb_msg_standard_input;
448 } 456 }
@@ -496,6 +504,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
496 504
497 while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { 505 while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
498 ssize_t n; 506 ssize_t n;
507
499#if ENABLE_FEATURE_DD_IBS_OBS 508#if ENABLE_FEATURE_DD_IBS_OBS
500 if (G.flags & FLAG_FULLBLOCK) 509 if (G.flags & FLAG_FULLBLOCK)
501 n = full_read(ifd, ibuf, ibs); 510 n = full_read(ifd, ibuf, ibs);
diff --git a/coreutils/du.c b/coreutils/du.c
index 6d737fbfb..0615a6e49 100644
--- a/coreutils/du.c
+++ b/coreutils/du.c
@@ -131,7 +131,7 @@ static void print(unsigned long long size, const char *filename)
131 size++; 131 size++;
132 size >>= 1; 132 size >>= 1;
133 } 133 }
134 printf("%llu\t%s\n", size, filename); 134 printf("%"LL_FMT"u\t%s\n", size, filename);
135#endif 135#endif
136} 136}
137 137
diff --git a/coreutils/expr.c b/coreutils/expr.c
index 1bdfba004..900248103 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
85typedef int64_t arith_t; 85typedef 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 1f24784fd..47fe179dc 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 b2adb0c06..48927c964 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -492,12 +492,14 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
492 /* Do readlink early, so that if it fails, error message 492 /* Do readlink early, so that if it fails, error message
493 * does not appear *inside* the "ls -l" line */ 493 * does not appear *inside* the "ls -l" line */
494 lpath = NULL; 494 lpath = NULL;
495#if !ENABLE_PLATFORM_MINGW32
495 if (opt & OPT_l) 496 if (opt & OPT_l)
496 if (S_ISLNK(dn->dn_mode)) 497 if (S_ISLNK(dn->dn_mode))
497 lpath = xmalloc_readlink_or_warn(dn->fullname); 498 lpath = xmalloc_readlink_or_warn(dn->fullname);
499#endif
498 500
499 if (opt & OPT_i) /* show inode# */ 501 if (opt & OPT_i) /* show inode# */
500 column += printf("%7llu ", (long long) dn->dn_ino); 502 column += printf("%7"LL_FMT"u ", (long long) dn->dn_ino);
501//TODO: -h should affect -s too: 503//TODO: -h should affect -s too:
502 if (opt & OPT_s) /* show allocated blocks */ 504 if (opt & OPT_s) /* show allocated blocks */
503 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); 505 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 4cae0c529..75e14ef7d 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
104enum size_spec { 111enum size_spec {
105 NO_SIZE, 112 NO_SIZE,
106 CHAR, 113 CHAR,
diff --git a/coreutils/shred.c b/coreutils/shred.c
index 8f3d9c5c9..86d4b66b4 100644
--- a/coreutils/shred.c
+++ b/coreutils/shred.c
@@ -38,6 +38,10 @@
38 38
39#include "libbb.h" 39#include "libbb.h"
40 40
41#if ENABLE_PLATFORM_MINGW32
42#define xopen mingw_xopen
43#endif
44
41int shred_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 45int shred_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
42int shred_main(int argc UNUSED_PARAM, char **argv) 46int shred_main(int argc UNUSED_PARAM, char **argv)
43{ 47{
@@ -96,8 +100,14 @@ int shred_main(int argc UNUSED_PARAM, char **argv)
96 } 100 }
97 if (opt & OPT_u) { 101 if (opt & OPT_u) {
98 ftruncate(fd, 0); 102 ftruncate(fd, 0);
103#if ENABLE_PLATFORM_MINGW32
104 xclose(fd);
105#endif
99 xunlink(fname); 106 xunlink(fname);
100 } 107 }
108#if ENABLE_PLATFORM_MINGW32
109 else
110#endif
101 xclose(fd); 111 xclose(fd);
102 } 112 }
103 } 113 }
diff --git a/coreutils/stat.c b/coreutils/stat.c
index 122029bda..32bc5e2d3 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.
@@ -318,6 +317,7 @@ static void FAST_FUNC print_stat(char *pformat, const char m,
318 printfs(pformat, filename); 317 printfs(pformat, filename);
319 } else if (m == 'N') { 318 } else if (m == 'N') {
320 strcatc(pformat, 's'); 319 strcatc(pformat, 's');
320#if !ENABLE_PLATFORM_MINGW32
321 if (S_ISLNK(statbuf->st_mode)) { 321 if (S_ISLNK(statbuf->st_mode)) {
322 char *linkname = xmalloc_readlink_or_warn(filename); 322 char *linkname = xmalloc_readlink_or_warn(filename);
323 if (linkname == NULL) 323 if (linkname == NULL)
@@ -327,6 +327,9 @@ static void FAST_FUNC print_stat(char *pformat, const char m,
327 } else { 327 } else {
328 printf(pformat, filename); 328 printf(pformat, filename);
329 } 329 }
330#else
331 printf(pformat, filename);
332#endif
330 } else if (m == 'd') { 333 } else if (m == 'd') {
331 strcat(pformat, "llu"); 334 strcat(pformat, "llu");
332 printf(pformat, (unsigned long long) statbuf->st_dev); 335 printf(pformat, (unsigned long long) statbuf->st_dev);
@@ -709,6 +712,7 @@ static bool do_stat(const char *filename, const char *format)
709 gw_ent = getgrgid(statbuf.st_gid); 712 gw_ent = getgrgid(statbuf.st_gid);
710 pw_ent = getpwuid(statbuf.st_uid); 713 pw_ent = getpwuid(statbuf.st_uid);
711 714
715#if !ENABLE_PLATFORM_MINGW32
712 if (S_ISLNK(statbuf.st_mode)) 716 if (S_ISLNK(statbuf.st_mode))
713 linkname = xmalloc_readlink_or_warn(filename); 717 linkname = xmalloc_readlink_or_warn(filename);
714 if (linkname) { 718 if (linkname) {
@@ -717,6 +721,9 @@ static bool do_stat(const char *filename, const char *format)
717 } else { 721 } else {
718 printf(" File: '%s'\n", filename); 722 printf(" File: '%s'\n", filename);
719 } 723 }
724#else
725 printf(" File: '%s'\n", filename);
726#endif
720 727
721 printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n" 728 printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n"
722 "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu", 729 "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu",
diff --git a/coreutils/sum.c b/coreutils/sum.c
index 16ec44540..dba8e8e76 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/timeout.c b/coreutils/timeout.c
index 8b7bc2eaa..258cd276f 100644
--- a/coreutils/timeout.c
+++ b/coreutils/timeout.c
@@ -46,11 +46,27 @@
46 46
47#include "libbb.h" 47#include "libbb.h"
48 48
49#if ENABLE_PLATFORM_MINGW32
50HANDLE child = INVALID_HANDLE_VALUE;
51
52static void kill_child(void)
53{
54 if (child != INVALID_HANDLE_VALUE) {
55 kill_SIGTERM_by_handle(child, 128+SIGTERM);
56 }
57}
58#endif
59
49int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 60int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
50int timeout_main(int argc UNUSED_PARAM, char **argv) 61int timeout_main(int argc UNUSED_PARAM, char **argv)
51{ 62{
52 int signo; 63 int signo;
64#if !ENABLE_PLATFORM_MINGW32
53 int status; 65 int status;
66#else
67 intptr_t ret;
68 DWORD status = EXIT_SUCCESS;
69#endif
54 int parent = 0; 70 int parent = 0;
55 int timeout; 71 int timeout;
56 pid_t pid; 72 pid_t pid;
@@ -59,6 +75,10 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
59#endif 75#endif
60 const char *opt_s = "TERM"; 76 const char *opt_s = "TERM";
61 77
78#if ENABLE_PLATFORM_MINGW32
79 xfunc_error_retval = 125;
80#endif
81
62 /* -p option is not documented, it is needed to support NOMMU. */ 82 /* -p option is not documented, it is needed to support NOMMU. */
63 83
64 /* -t SECONDS; -p PARENT_PID */ 84 /* -t SECONDS; -p PARENT_PID */
@@ -67,7 +87,11 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
67 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ 87 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */
68 88
69 signo = get_signum(opt_s); 89 signo = get_signum(opt_s);
90#if !ENABLE_PLATFORM_MINGW32
70 if (signo < 0) 91 if (signo < 0)
92#else
93 if (signo != SIGTERM && signo != SIGKILL && signo != 0)
94#endif
71 bb_error_msg_and_die("unknown signal '%s'", opt_s); 95 bb_error_msg_and_die("unknown signal '%s'", opt_s);
72 96
73 if (!argv[optind]) 97 if (!argv[optind])
@@ -76,6 +100,7 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
76 if (!argv[optind]) /* no PROG? */ 100 if (!argv[optind]) /* no PROG? */
77 bb_show_usage(); 101 bb_show_usage();
78 102
103#if !ENABLE_PLATFORM_MINGW32
79 /* We want to create a grandchild which will watch 104 /* We want to create a grandchild which will watch
80 * and kill the grandparent. Other methods: 105 * and kill the grandparent. Other methods:
81 * making parent watch child disrupts parent<->child link 106 * making parent watch child disrupts parent<->child link
@@ -129,4 +154,32 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
129 argv[1] = sv2; 154 argv[1] = sv2;
130#endif 155#endif
131 BB_EXECVP_or_die(argv); 156 BB_EXECVP_or_die(argv);
157#else /* ENABLE_PLATFORM_MINGW32 */
158 if ((ret=mingw_spawn_proc((const char **)argv)) == -1) {
159 xfunc_error_retval = errno == EACCES ? 126 : 127;
160 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
161 }
162
163 child = (HANDLE)ret;
164 atexit(kill_child);
165 while (1) {
166 sleep(1);
167 if (signo && --timeout <= 0) {
168 status = signo == SIGKILL ? 137 : 124;
169 break;
170 }
171 if (WaitForSingleObject(child, 0) == WAIT_OBJECT_0) {
172 /* process is gone */
173 GetExitCodeProcess(child, &status);
174 goto finish;
175 }
176 }
177
178 pid = (pid_t)GetProcessId(child);
179 kill(pid, signo);
180 finish:
181 CloseHandle(child);
182 child = INVALID_HANDLE_VALUE;
183 return status;
184#endif
132} 185}
diff --git a/coreutils/yes.c b/coreutils/yes.c
index 9a435a761..a51b1ad8e 100644
--- a/coreutils/yes.c
+++ b/coreutils/yes.c
@@ -41,6 +41,10 @@ int yes_main(int argc UNUSED_PARAM, char **argv)
41 ++argv; 41 ++argv;
42 42
43 do { 43 do {
44#if ENABLE_PLATFORM_MINGW32
45 if (ferror(stdout) != 0)
46 break;
47#endif
44 pp = argv; 48 pp = argv;
45 while (1) { 49 while (1) {
46 fputs(*pp, stdout); 50 fputs(*pp, stdout);
diff --git a/debianutils/pipe_progress.c b/debianutils/pipe_progress.c
index ab7e2528f..fd08a145e 100644
--- a/debianutils/pipe_progress.c
+++ b/debianutils/pipe_progress.c
@@ -16,8 +16,10 @@
16 16
17//kbuild:lib-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o 17//kbuild:lib-$(CONFIG_PIPE_PROGRESS) += pipe_progress.o
18 18
19//usage:#define pipe_progress_trivial_usage NOUSAGE_STR 19//usage:#define pipe_progress_trivial_usage
20//usage:#define pipe_progress_full_usage "" 20//usage: ""
21//usage:#define pipe_progress_full_usage "\n\n"
22//usage: "Display a dot to indicate pipe activity"
21 23
22#include "libbb.h" 24#include "libbb.h"
23 25
diff --git a/debianutils/which.c b/debianutils/which.c
index 98876521f..14d452cd5 100644
--- a/debianutils/which.c
+++ b/debianutils/which.c
@@ -45,16 +45,40 @@ int which_main(int argc UNUSED_PARAM, char **argv)
45 45
46 do { 46 do {
47 int missing = 1; 47 int missing = 1;
48 char *p;
49
50#if ENABLE_FEATURE_SH_STANDALONE
51 if (strcmp(*argv, "busybox") == 0 &&
52 is_suffixed_with(bb_busybox_exec_path, "busybox.exe")) {
53 missing = 0;
54 puts(bb_busybox_exec_path);
55 if (!option_mask32) /* -a not set */
56 break;
57 }
58 else if (find_applet_by_name(*argv) >= 0 ||
59 is_prefixed_with(*argv, "busybox")) {
60 missing = 0;
61 puts(*argv);
62 if (!option_mask32) /* -a not set */
63 break;
64 }
65#endif
48 66
49 /* If file contains a slash don't use PATH */ 67 /* If file contains a slash don't use PATH */
50 if (strchr(*argv, '/')) { 68 if (strchr(*argv, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(*argv, '\\'))) {
69#if ENABLE_PLATFORM_MINGW32
70 if ((p=auto_win32_extension(*argv)) != NULL) {
71 missing = 0;
72 puts(p);
73 }
74 else
75#endif
51 if (file_is_executable(*argv)) { 76 if (file_is_executable(*argv)) {
52 missing = 0; 77 missing = 0;
53 puts(*argv); 78 puts(*argv);
54 } 79 }
55 } else { 80 } else {
56 char *path; 81 char *path;
57 char *p;
58 82
59 path = env_path; 83 path = env_path;
60 /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */ 84 /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */
diff --git a/editors/awk.c b/editors/awk.c
index b6d8cf203..17bbdc62a 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -731,7 +731,11 @@ static char *skip_spaces(char *p)
731 if (*p == '\\' && p[1] == '\n') { 731 if (*p == '\\' && p[1] == '\n') {
732 p++; 732 p++;
733 t_lineno++; 733 t_lineno++;
734#if !ENABLE_PLATFORM_MINGW32
734 } else if (*p != ' ' && *p != '\t') { 735 } else if (*p != ' ' && *p != '\t') {
736#else
737 } else if (*p != ' ' && *p != '\t' && *p != '\r') {
738#endif
735 break; 739 break;
736 } 740 }
737 p++; 741 p++;
@@ -2079,7 +2083,7 @@ static int fmt_num(char *b, int size, const char *format, double n, int int_as_i
2079 const char *s = format; 2083 const char *s = format;
2080 2084
2081 if (int_as_int && n == (long long)n) { 2085 if (int_as_int && n == (long long)n) {
2082 r = snprintf(b, size, "%lld", (long long)n); 2086 r = snprintf(b, size, "%"LL_FMT"d", (long long)n);
2083 } else { 2087 } else {
2084 do { c = *s; } while (c && *++s); 2088 do { c = *s; } while (c && *++s);
2085 if (strchr("diouxX", c)) { 2089 if (strchr("diouxX", c)) {
diff --git a/editors/diff.c b/editors/diff.c
index 1462a9b18..929beb054 100644
--- a/editors/diff.c
+++ b/editors/diff.c
@@ -709,6 +709,10 @@ static int diffreg(char *file[2])
709 FILE *fp[2]; 709 FILE *fp[2];
710 bool binary = false, differ = false; 710 bool binary = false, differ = false;
711 int status = STATUS_SAME, i; 711 int status = STATUS_SAME, i;
712#if ENABLE_PLATFORM_MINGW32
713 char *tmpfile[2] = { NULL, NULL };
714 char *tmpdir;
715#endif
712 716
713 fp[0] = stdin; 717 fp[0] = stdin;
714 fp[1] = stdin; 718 fp[1] = stdin;
@@ -730,10 +734,19 @@ static int diffreg(char *file[2])
730 * When we meet non-seekable file, we must make a temp copy. 734 * When we meet non-seekable file, we must make a temp copy.
731 */ 735 */
732 if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) { 736 if (lseek(fd, 0, SEEK_SET) == -1 && errno == ESPIPE) {
737#if !ENABLE_PLATFORM_MINGW32
733 char name[] = "/tmp/difXXXXXX"; 738 char name[] = "/tmp/difXXXXXX";
734 int fd_tmp = xmkstemp(name); 739 int fd_tmp = xmkstemp(name);
735 740
736 unlink(name); 741 unlink(name);
742#else
743 int fd_tmp;
744
745 if (!(tmpdir=getenv("TMPDIR")))
746 goto out;
747 tmpfile[i] = xasprintf("%s/difXXXXXX", tmpdir);
748 fd_tmp = xmkstemp(tmpfile[i]);
749#endif
737 if (bb_copyfd_eof(fd, fd_tmp) < 0) 750 if (bb_copyfd_eof(fd, fd_tmp) < 0)
738 xfunc_die(); 751 xfunc_die();
739 if (fd != STDIN_FILENO) 752 if (fd != STDIN_FILENO)
@@ -776,6 +789,14 @@ static int diffreg(char *file[2])
776out: 789out:
777 fclose_if_not_stdin(fp[0]); 790 fclose_if_not_stdin(fp[0]);
778 fclose_if_not_stdin(fp[1]); 791 fclose_if_not_stdin(fp[1]);
792#if ENABLE_PLATFORM_MINGW32
793 for (i = 0; i < 2; i++) {
794 if (tmpfile[i]) {
795 unlink(tmpfile[i]);
796 free(tmpfile[i]);
797 }
798 }
799#endif
779 800
780 return status; 801 return status;
781} 802}
@@ -1018,7 +1039,7 @@ int diff_main(int argc UNUSED_PARAM, char **argv)
1018 * single NFS file system, if a local device number (st_dev) exceeds 1039 * single NFS file system, if a local device number (st_dev) exceeds
1019 * 255, or if a local inode number (st_ino) exceeds 16777215. 1040 * 255, or if a local inode number (st_ino) exceeds 16777215.
1020 */ 1041 */
1021 if (ENABLE_DESKTOP 1042 if (ENABLE_DESKTOP && !ENABLE_PLATFORM_MINGW32
1022 && stb[0].st_ino == stb[1].st_ino 1043 && stb[0].st_ino == stb[1].st_ino
1023 && stb[0].st_dev == stb[1].st_dev 1044 && stb[0].st_dev == stb[1].st_dev
1024 && stb[0].st_size == stb[1].st_size 1045 && stb[0].st_size == stb[1].st_size
diff --git a/editors/sed.c b/editors/sed.c
index 1054c1302..fd56e0e7f 100644
--- a/editors/sed.c
+++ b/editors/sed.c
@@ -74,6 +74,9 @@
74//usage: "\n Optionally back files up, appending SFX" 74//usage: "\n Optionally back files up, appending SFX"
75//usage: "\n -n Suppress automatic printing of pattern space" 75//usage: "\n -n Suppress automatic printing of pattern space"
76//usage: "\n -r,-E Use extended regex syntax" 76//usage: "\n -r,-E Use extended regex syntax"
77//usage: IF_PLATFORM_MINGW32(
78//usage: "\n -b Keep CR/LF (Windows-only)"
79//usage: )
77//usage: "\n" 80//usage: "\n"
78//usage: "\nIf no -e or -f, the first non-option argument is the sed command string." 81//usage: "\nIf no -e or -f, the first non-option argument is the sed command string."
79//usage: "\nRemaining arguments are input files (stdin if none)." 82//usage: "\nRemaining arguments are input files (stdin if none)."
@@ -132,6 +135,9 @@ static const char semicolon_whitespace[] ALIGN1 = "; \n\r\t\v";
132struct globals { 135struct globals {
133 /* options */ 136 /* options */
134 int be_quiet, regex_type; 137 int be_quiet, regex_type;
138#if ENABLE_PLATFORM_MINGW32
139 int keep_cr;
140#endif
135 141
136 FILE *nonstdout; 142 FILE *nonstdout;
137 char *outname, *hold_space; 143 char *outname, *hold_space;
@@ -1024,6 +1030,11 @@ static char *get_next_line(char *gets_char, char *last_puts_char)
1024 char c = temp[len-1]; 1030 char c = temp[len-1];
1025 if (c == '\n' || c == '\0') { 1031 if (c == '\n' || c == '\0') {
1026 temp[len-1] = '\0'; 1032 temp[len-1] = '\0';
1033#if ENABLE_PLATFORM_MINGW32
1034 if (!G.keep_cr && c == '\n' && len > 1 && temp[len-2] == '\r') {
1035 temp[len-2] = '\0';
1036 }
1037#endif
1027 gc = c; 1038 gc = c;
1028 if (c == '\0') { 1039 if (c == '\0') {
1029 int ch = fgetc(fp); 1040 int ch = fgetc(fp);
@@ -1498,7 +1509,12 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1498 "quiet\0" No_argument "n" 1509 "quiet\0" No_argument "n"
1499 "silent\0" No_argument "n" 1510 "silent\0" No_argument "n"
1500 "expression\0" Required_argument "e" 1511 "expression\0" Required_argument "e"
1512# if !ENABLE_PLATFORM_MINGW32
1501 "file\0" Required_argument "f"; 1513 "file\0" Required_argument "f";
1514# else
1515 "file\0" Required_argument "f"
1516 "binary\0" No_argument "b";
1517# endif
1502#endif 1518#endif
1503 1519
1504 INIT_G(); 1520 INIT_G();
@@ -1522,6 +1538,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1522 */ 1538 */
1523 opt = getopt32long(argv, "^" 1539 opt = getopt32long(argv, "^"
1524 "i::rEne:*f:*" 1540 "i::rEne:*f:*"
1541 IF_PLATFORM_MINGW32("b")
1525 "\0" "nn"/*count -n*/, 1542 "\0" "nn"/*count -n*/,
1526 sed_longopts, 1543 sed_longopts,
1527 &opt_i, &opt_e, &opt_f, 1544 &opt_i, &opt_e, &opt_f,
@@ -1535,6 +1552,10 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1535 G.regex_type |= REG_EXTENDED; // -r or -E 1552 G.regex_type |= REG_EXTENDED; // -r or -E
1536 //if (opt & 8) 1553 //if (opt & 8)
1537 // G.be_quiet++; // -n (implemented with a counter instead) 1554 // G.be_quiet++; // -n (implemented with a counter instead)
1555#if ENABLE_PLATFORM_MINGW32
1556 if (opt & 0x40)
1557 G.keep_cr = 1;
1558#endif
1538 while (opt_e) { // -e 1559 while (opt_e) { // -e
1539 add_cmd_block(llist_pop(&opt_e)); 1560 add_cmd_block(llist_pop(&opt_e));
1540 } 1561 }
diff --git a/editors/vi.c b/editors/vi.c
index 32144abaa..433a37908 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -353,7 +353,9 @@ struct globals {
353#if ENABLE_FEATURE_VI_USE_SIGNALS 353#if ENABLE_FEATURE_VI_USE_SIGNALS
354 sigjmp_buf restart; // catch_sig() 354 sigjmp_buf restart; // catch_sig()
355#endif 355#endif
356#if !ENABLE_PLATFORM_MINGW32
356 struct termios term_orig; // remember what the cooked mode was 357 struct termios term_orig; // remember what the cooked mode was
358#endif
357#if ENABLE_FEATURE_VI_COLON 359#if ENABLE_FEATURE_VI_COLON
358 char *initial_cmds[3]; // currently 2 entries, NULL terminated 360 char *initial_cmds[3]; // currently 2 entries, NULL terminated
359#endif 361#endif
@@ -2756,15 +2758,19 @@ static char *swap_context(char *p) // goto new context for '' command make this
2756//----- Set terminal attributes -------------------------------- 2758//----- Set terminal attributes --------------------------------
2757static void rawmode(void) 2759static void rawmode(void)
2758{ 2760{
2761#if !ENABLE_PLATFORM_MINGW32
2759 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals 2762 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
2760 set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL); 2763 set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL);
2761 erase_char = term_orig.c_cc[VERASE]; 2764 erase_char = term_orig.c_cc[VERASE];
2765#endif
2762} 2766}
2763 2767
2764static void cookmode(void) 2768static void cookmode(void)
2765{ 2769{
2766 fflush_all(); 2770 fflush_all();
2771#if !ENABLE_PLATFORM_MINGW32
2767 tcsetattr_stdin_TCSANOW(&term_orig); 2772 tcsetattr_stdin_TCSANOW(&term_orig);
2773#endif
2768} 2774}
2769 2775
2770#if ENABLE_FEATURE_VI_USE_SIGNALS 2776#if ENABLE_FEATURE_VI_USE_SIGNALS
@@ -2817,6 +2823,14 @@ static void catch_sig(int sig)
2817 2823
2818static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready 2824static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
2819{ 2825{
2826#if ENABLE_PLATFORM_MINGW32
2827 HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
2828 DWORD ret;
2829
2830 fflush(stdout);
2831 ret = WaitForSingleObject(h, hund*10);
2832 return ret != WAIT_TIMEOUT;
2833#else
2820 struct pollfd pfd[1]; 2834 struct pollfd pfd[1];
2821 2835
2822 if (hund != 0) 2836 if (hund != 0)
@@ -2825,6 +2839,7 @@ static int mysleep(int hund) // sleep for 'hund' 1/100 seconds or stdin ready
2825 pfd[0].fd = STDIN_FILENO; 2839 pfd[0].fd = STDIN_FILENO;
2826 pfd[0].events = POLLIN; 2840 pfd[0].events = POLLIN;
2827 return safe_poll(pfd, 1, hund*10) > 0; 2841 return safe_poll(pfd, 1, hund*10) > 0;
2842#endif
2828} 2843}
2829 2844
2830//----- IO Routines -------------------------------------------- 2845//----- IO Routines --------------------------------------------
@@ -2953,6 +2968,9 @@ static int file_insert(const char *fn, char *p, int initial)
2953 status_line_bold("'%s' is not a regular file", fn); 2968 status_line_bold("'%s' is not a regular file", fn);
2954 goto fi; 2969 goto fi;
2955 } 2970 }
2971#if ENABLE_PLATFORM_MINGW32
2972 _setmode(fd, _O_TEXT);
2973#endif
2956 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX); 2974 size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
2957 p += text_hole_make(p, size); 2975 p += text_hole_make(p, size);
2958 cnt = full_read(fd, p, size); 2976 cnt = full_read(fd, p, size);
@@ -2960,8 +2978,19 @@ static int file_insert(const char *fn, char *p, int initial)
2960 status_line_bold_errno(fn); 2978 status_line_bold_errno(fn);
2961 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert 2979 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert
2962 } else if (cnt < size) { 2980 } else if (cnt < size) {
2981#if ENABLE_PLATFORM_MINGW32
2982 int i, cnt_cr;
2983
2984 // on WIN32 a partial read might just mean CRs have been removed
2985 for (i = 0, cnt_cr = cnt; i < cnt; ++i)
2986 if (p[i] == '\n')
2987 ++cnt_cr;
2988#endif
2963 // There was a partial read, shrink unused space 2989 // There was a partial read, shrink unused space
2964 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); 2990 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
2991#if ENABLE_PLATFORM_MINGW32
2992 if (cnt_cr < size)
2993#endif
2965 status_line_bold("can't read '%s'", fn); 2994 status_line_bold("can't read '%s'", fn);
2966 } 2995 }
2967 fi: 2996 fi:
@@ -2984,6 +3013,9 @@ static int file_insert(const char *fn, char *p, int initial)
2984static int file_write(char *fn, char *first, char *last) 3013static int file_write(char *fn, char *first, char *last)
2985{ 3014{
2986 int fd, cnt, charcnt; 3015 int fd, cnt, charcnt;
3016#if ENABLE_PLATFORM_MINGW32
3017 int i, newline;
3018#endif
2987 3019
2988 if (fn == 0) { 3020 if (fn == 0) {
2989 status_line_bold("No current filename"); 3021 status_line_bold("No current filename");
@@ -2997,8 +3029,23 @@ static int file_write(char *fn, char *first, char *last)
2997 if (fd < 0) 3029 if (fd < 0)
2998 return -1; 3030 return -1;
2999 cnt = last - first + 1; 3031 cnt = last - first + 1;
3032#if ENABLE_PLATFORM_MINGW32
3033 /* write file in text mode; this makes it bigger so adjust
3034 * the truncation to match
3035 */
3036 _setmode(fd, _O_TEXT);
3037 newline = 0;
3038 for ( i=0; i<cnt; ++i ) {
3039 if ( first[i] == '\n' ) {
3040 ++newline;
3041 }
3042 }
3043 charcnt = full_write(fd, first, cnt);
3044 ftruncate(fd, charcnt+newline);
3045#else
3000 charcnt = full_write(fd, first, cnt); 3046 charcnt = full_write(fd, first, cnt);
3001 ftruncate(fd, charcnt); 3047 ftruncate(fd, charcnt);
3048#endif
3002 if (charcnt == cnt) { 3049 if (charcnt == cnt) {
3003 // good write 3050 // good write
3004 //modified_count = FALSE; 3051 //modified_count = FALSE;
@@ -3049,7 +3096,12 @@ static void go_bottom_and_clear_to_eol(void)
3049//----- Erase from cursor to end of screen ----------------------- 3096//----- Erase from cursor to end of screen -----------------------
3050static void clear_to_eos(void) 3097static void clear_to_eos(void)
3051{ 3098{
3099#if !ENABLE_PLATFORM_MINGW32
3052 write1(ESC_CLEAR2EOS); 3100 write1(ESC_CLEAR2EOS);
3101#else
3102 /* in practice clear_to_eos() always clears the entire screen */
3103 reset_screen();
3104#endif
3053} 3105}
3054 3106
3055//----- Start standout mode ------------------------------------ 3107//----- Start standout mode ------------------------------------
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 35a06f723..d014669bd 100644
--- a/findutils/xargs.c
+++ b/findutils/xargs.c
@@ -74,6 +74,9 @@
74 74
75//kbuild:lib-$(CONFIG_XARGS) += xargs.o 75//kbuild:lib-$(CONFIG_XARGS) += xargs.o
76 76
77#if ENABLE_PLATFORM_MINGW32
78#include <conio.h>
79#endif
77#include "libbb.h" 80#include "libbb.h"
78#include "common_bufsiz.h" 81#include "common_bufsiz.h"
79 82
@@ -112,6 +115,9 @@ struct globals {
112#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL 115#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
113 int running_procs; 116 int running_procs;
114 int max_procs; 117 int max_procs;
118#if ENABLE_PLATFORM_MINGW32
119 HANDLE *procs;
120#endif
115#endif 121#endif
116 smalluint xargs_exitcode; 122 smalluint xargs_exitcode;
117} FIX_ALIASING; 123} FIX_ALIASING;
@@ -122,12 +128,61 @@ struct globals {
122 G.idx = 0; \ 128 G.idx = 0; \
123 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \ 129 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \
124 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \ 130 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \
131 IF_FEATURE_XARGS_SUPPORT_PARALLEL(IF_PLATFORM_MINGW32(G.procs = NULL;)) \
125 G.xargs_exitcode = 0; \ 132 G.xargs_exitcode = 0; \
126 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \ 133 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.repl_str = "{}";) \
127 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \ 134 IF_FEATURE_XARGS_SUPPORT_REPL_STR(G.eol_ch = '\n';) \
128} while (0) 135} while (0)
129 136
130 137
138#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL && ENABLE_PLATFORM_MINGW32
139static int wait_for_slot(int *idx)
140{
141 int i;
142
143 /* if less than max_procs running, set status to 0, return next free slot */
144 if (G.running_procs < G.max_procs) {
145 *idx = G.running_procs++;
146 return 0;
147 }
148
149check_exit_codes:
150 for (i = G.running_procs - 1; i >= 0; i--) {
151 DWORD status = 0;
152 if (!GetExitCodeProcess(G.procs[i], &status) ||
153 status != STILL_ACTIVE) {
154 CloseHandle(G.procs[i]);
155 if (i + 1 < G.running_procs)
156 G.procs[i] = G.procs[G.running_procs - 1];
157 *idx = G.running_procs - 1;
158 if (!G.max_procs)
159 G.running_procs--;
160 return status;
161 }
162 }
163
164 if (G.running_procs < MAXIMUM_WAIT_OBJECTS)
165 WaitForMultipleObjects((DWORD)G.running_procs, G.procs, FALSE,
166 INFINITE);
167 else {
168 /* Fall back to polling */
169 for (;;) {
170 DWORD nr = i + MAXIMUM_WAIT_OBJECTS > G.running_procs ?
171 MAXIMUM_WAIT_OBJECTS : (DWORD)(G.running_procs - i);
172 DWORD ret = WaitForMultipleObjects(nr, G.procs + i, FALSE, 100);
173
174 if (ret != WAIT_TIMEOUT)
175 break;
176 i += MAXIMUM_WAIT_OBJECTS;
177 if (i > G.running_procs)
178 i = 0;
179 }
180 }
181
182 goto check_exit_codes;
183}
184#endif /* SUPPORT_PARALLEL && PLATFORM_MINGW32 */
185
131/* 186/*
132 * Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123). 187 * Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123).
133 * Else sets G.xargs_exitcode to error code and returns nonzero. 188 * Else sets G.xargs_exitcode to error code and returns nonzero.
@@ -144,6 +199,23 @@ static int xargs_exec(void)
144 if (G.max_procs == 1) { 199 if (G.max_procs == 1) {
145 status = spawn_and_wait(G.args); 200 status = spawn_and_wait(G.args);
146 } else { 201 } else {
202#if ENABLE_PLATFORM_MINGW32
203 int idx;
204 status = !G.running_procs && !G.max_procs ? 0 : wait_for_slot(&idx);
205 if (G.max_procs) {
206 HANDLE p = (HANDLE)mingw_spawn_proc((const char **)G.args);
207 if (p < 0)
208 status = -1;
209 else
210 G.procs[idx] = p;
211 } else {
212 while (G.running_procs) {
213 int status2 = wait_for_slot(&idx);
214 if (status2 && !status)
215 status = status2;
216 }
217 }
218#else
147 pid_t pid; 219 pid_t pid;
148 int wstat; 220 int wstat;
149 again: 221 again:
@@ -184,6 +256,7 @@ static int xargs_exec(void)
184 /* final waitpid() loop: must be ECHILD "no more children" */ 256 /* final waitpid() loop: must be ECHILD "no more children" */
185 status = 0; 257 status = 0;
186 } 258 }
259#endif
187 } 260 }
188#endif 261#endif
189 /* Manpage: 262 /* Manpage:
@@ -496,6 +569,7 @@ static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg
496 */ 569 */
497static int xargs_ask_confirmation(void) 570static int xargs_ask_confirmation(void)
498{ 571{
572#if !ENABLE_PLATFORM_MINGW32
499 FILE *tty_stream; 573 FILE *tty_stream;
500 int r; 574 int r;
501 575
@@ -505,6 +579,18 @@ static int xargs_ask_confirmation(void)
505 r = bb_ask_y_confirmation_FILE(tty_stream); 579 r = bb_ask_y_confirmation_FILE(tty_stream);
506 580
507 fclose(tty_stream); 581 fclose(tty_stream);
582#else
583 int r, c, savec;
584
585 fputs(" ?...", stderr);
586 fflush_all();
587 c = savec = getche();
588 while (c != EOF && c != '\r')
589 c = getche();
590 fputs("\n", stderr);
591 fflush_all();
592 r = (savec == 'y' || savec == 'Y');
593#endif
508 594
509 return r; 595 return r;
510} 596}
@@ -608,7 +694,12 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
608 694
609#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL 695#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
610 if (G.max_procs <= 0) /* -P0 means "run lots of them" */ 696 if (G.max_procs <= 0) /* -P0 means "run lots of them" */
697#if !ENABLE_PLATFORM_MINGW32
611 G.max_procs = 100; /* let's not go crazy high */ 698 G.max_procs = 100; /* let's not go crazy high */
699#else
700 G.max_procs = MAXIMUM_WAIT_OBJECTS;
701 G.procs = xmalloc(sizeof(G.procs[0]) * G.max_procs);
702#endif
612#endif 703#endif
613 704
614#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE 705#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
@@ -730,7 +821,11 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
730 fmt = " %s"; 821 fmt = " %s";
731 } 822 }
732 if (!(opt & OPT_INTERACTIVE)) 823 if (!(opt & OPT_INTERACTIVE))
824#if !ENABLE_PLATFORM_MINGW32
733 bb_putchar_stderr('\n'); 825 bb_putchar_stderr('\n');
826#else
827 fprintf(stderr, "\n");
828#endif
734 } 829 }
735 830
736 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { 831 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
diff --git a/include/bb_archive.h b/include/bb_archive.h
index 561dd0c9d..7d70ac086 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
5PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN 15PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
6 16
7enum { 17enum {
@@ -207,7 +217,11 @@ void create_or_remember_link(llist_t **link_placeholders,
207 const char *target, 217 const char *target,
208 const char *linkname, 218 const char *linkname,
209 int hard_link) FAST_FUNC; 219 int hard_link) FAST_FUNC;
220#if !ENABLE_PLATFORM_MINGW32
210void create_links_from_list(llist_t *list) FAST_FUNC; 221void create_links_from_list(llist_t *list) FAST_FUNC;
222#else
223#define create_links_from_list(l) (void)0
224#endif
211 225
212void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; 226void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC;
213const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC; 227const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC;
@@ -271,7 +285,11 @@ enum {
271 BBUNPK_SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION, 285 BBUNPK_SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION,
272}; 286};
273 287
288#if !ENABLE_PLATFORM_MINGW32
274void check_errors_in_children(int signo); 289void check_errors_in_children(int signo);
290#else
291#define check_errors_in_children(s) ((void)0)
292#endif
275#if BB_MMU 293#if BB_MMU
276void fork_transformer(int fd, 294void fork_transformer(int fd,
277 int signature_skipped, 295 int signature_skipped,
diff --git a/include/libbb.h b/include/libbb.h
index d2563999a..1b5994357 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -139,6 +139,12 @@
139# include <arpa/inet.h> 139# include <arpa/inet.h>
140#elif defined __APPLE__ 140#elif defined __APPLE__
141# include <netinet/in.h> 141# include <netinet/in.h>
142#elif ENABLE_PLATFORM_MINGW32
143# ifndef WINVER
144# define WINVER 0x0501
145# endif
146# include <winsock2.h>
147# include <ws2tcpip.h>
142#else 148#else
143# include <arpa/inet.h> 149# include <arpa/inet.h>
144//This breaks on bionic: 150//This breaks on bionic:
@@ -178,7 +184,9 @@
178 184
179/* Some libc's forget to declare these, do it ourself */ 185/* Some libc's forget to declare these, do it ourself */
180 186
187#if !ENABLE_PLATFORM_MINGW32
181extern char **environ; 188extern char **environ;
189#endif
182/* klogctl is in libc's klog.h, but we cheat and not #include that */ 190/* klogctl is in libc's klog.h, but we cheat and not #include that */
183int klogctl(int type, char *b, int len); 191int klogctl(int type, char *b, int len);
184#ifndef PATH_MAX 192#ifndef PATH_MAX
@@ -188,6 +196,9 @@ int klogctl(int type, char *b, int len);
188# define BUFSIZ 4096 196# define BUFSIZ 4096
189#endif 197#endif
190 198
199#if ENABLE_PLATFORM_MINGW32
200# include "mingw.h"
201#endif
191 202
192/* Busybox does not use threads, we can speed up stdio. */ 203/* Busybox does not use threads, we can speed up stdio. */
193#ifdef HAVE_UNLOCKED_STDIO 204#ifdef HAVE_UNLOCKED_STDIO
@@ -250,6 +261,13 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
250 : ((T)1 << (sizeof(T)*8-1)) \ 261 : ((T)1 << (sizeof(T)*8-1)) \
251 ) 262 )
252 263
264#if ENABLE_PLATFORM_MINGW32 && \
265 (!defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO)
266#define LL_FMT "I64"
267#else
268#define LL_FMT "ll"
269#endif
270
253/* Large file support */ 271/* Large file support */
254/* Note that CONFIG_LFS=y forces bbox to be built with all common ops 272/* Note that CONFIG_LFS=y forces bbox to be built with all common ops
255 * (stat, lseek etc) mapped to "largefile" variants by libc. 273 * (stat, lseek etc) mapped to "largefile" variants by libc.
@@ -275,7 +293,7 @@ typedef unsigned long long uoff_t;
275# define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX) 293# define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX)
276# define BB_STRTOOFF bb_strtoull 294# define BB_STRTOOFF bb_strtoull
277# define STRTOOFF strtoull 295# define STRTOOFF strtoull
278# define OFF_FMT "ll" 296# define OFF_FMT LL_FMT
279# endif 297# endif
280#else 298#else
281/* CONFIG_LFS is off */ 299/* CONFIG_LFS is off */
@@ -513,20 +531,37 @@ enum {
513 * Dance around with long long to guard against that... 531 * Dance around with long long to guard against that...
514 */ 532 */
515 BB_FATAL_SIGS = (int)(0 533 BB_FATAL_SIGS = (int)(0
534#ifdef SIGHUP
516 + (1LL << SIGHUP) 535 + (1LL << SIGHUP)
536#endif
517 + (1LL << SIGINT) 537 + (1LL << SIGINT)
518 + (1LL << SIGTERM) 538 + (1LL << SIGTERM)
519 + (1LL << SIGPIPE) // Write to pipe with no readers 539 + (1LL << SIGPIPE) // Write to pipe with no readers
540#ifdef SIGQUIT
520 + (1LL << SIGQUIT) // Quit from keyboard 541 + (1LL << SIGQUIT) // Quit from keyboard
542#endif
521 + (1LL << SIGABRT) // Abort signal from abort(3) 543 + (1LL << SIGABRT) // Abort signal from abort(3)
544#ifdef SIGALRM
522 + (1LL << SIGALRM) // Timer signal from alarm(2) 545 + (1LL << SIGALRM) // Timer signal from alarm(2)
546#endif
547#ifdef SIGVTALRM
523 + (1LL << SIGVTALRM) // Virtual alarm clock 548 + (1LL << SIGVTALRM) // Virtual alarm clock
549#endif
550#ifdef SIGXCPU
524 + (1LL << SIGXCPU) // CPU time limit exceeded 551 + (1LL << SIGXCPU) // CPU time limit exceeded
552#endif
553#ifdef SIGXFSZ
525 + (1LL << SIGXFSZ) // File size limit exceeded 554 + (1LL << SIGXFSZ) // File size limit exceeded
555#endif
556#ifdef SIGUSR1
526 + (1LL << SIGUSR1) // Yes kids, these are also fatal! 557 + (1LL << SIGUSR1) // Yes kids, these are also fatal!
558#endif
559#ifdef SIGUSR1
527 + (1LL << SIGUSR2) 560 + (1LL << SIGUSR2)
561#endif
528 + 0), 562 + 0),
529}; 563};
564#if !ENABLE_PLATFORM_MINGW32
530void bb_signals(int sigs, void (*f)(int)) FAST_FUNC; 565void bb_signals(int sigs, void (*f)(int)) FAST_FUNC;
531/* Unlike signal() and bb_signals, sets handler with sigaction() 566/* Unlike signal() and bb_signals, sets handler with sigaction()
532 * and in a way that while signal handler is run, no other signals 567 * and in a way that while signal handler is run, no other signals
@@ -546,6 +581,10 @@ int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC;
546int sigprocmask_allsigs(int how) FAST_FUNC; 581int sigprocmask_allsigs(int how) FAST_FUNC;
547/* Return old set in the same set: */ 582/* Return old set in the same set: */
548int sigprocmask2(int how, sigset_t *set) FAST_FUNC; 583int sigprocmask2(int how, sigset_t *set) FAST_FUNC;
584#else
585#define bb_signals(s, f)
586#define kill_myself_with_sig(s)
587#endif
549/* Standard handler which just records signo */ 588/* Standard handler which just records signo */
550extern smallint bb_got_signal; 589extern smallint bb_got_signal;
551void record_signo(int signo); /* not FAST_FUNC! */ 590void record_signo(int signo); /* not FAST_FUNC! */
@@ -625,7 +664,7 @@ char *strftime_YYYYMMDDHHMMSS(char *buf, unsigned len, time_t *tp) FAST_FUNC;
625int xsocket(int domain, int type, int protocol) FAST_FUNC; 664int xsocket(int domain, int type, int protocol) FAST_FUNC;
626void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC; 665void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC;
627void xlisten(int s, int backlog) FAST_FUNC; 666void xlisten(int s, int backlog) FAST_FUNC;
628void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) FAST_FUNC; 667void xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen) FAST_FUNC;
629ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, 668ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
630 socklen_t tolen) FAST_FUNC; 669 socklen_t tolen) FAST_FUNC;
631 670
@@ -1129,6 +1168,7 @@ void exec_prog_or_SHELL(char **argv) NORETURN FAST_FUNC;
1129 1168
1130/* xvfork() can't be a _function_, return after vfork in child mangles stack 1169/* xvfork() can't be a _function_, return after vfork in child mangles stack
1131 * in the parent. It must be a macro. */ 1170 * in the parent. It must be a macro. */
1171#if !ENABLE_PLATFORM_MINGW32
1132#define xvfork() \ 1172#define xvfork() \
1133({ \ 1173({ \
1134 pid_t bb__xvfork_pid = vfork(); \ 1174 pid_t bb__xvfork_pid = vfork(); \
@@ -1136,6 +1176,9 @@ void exec_prog_or_SHELL(char **argv) NORETURN FAST_FUNC;
1136 bb_perror_msg_and_die("vfork"); \ 1176 bb_perror_msg_and_die("vfork"); \
1137 bb__xvfork_pid; \ 1177 bb__xvfork_pid; \
1138}) 1178})
1179#else
1180#define xvfork() vfork()
1181#endif
1139#if BB_MMU 1182#if BB_MMU
1140pid_t xfork(void) FAST_FUNC; 1183pid_t xfork(void) FAST_FUNC;
1141#endif 1184#endif
@@ -1669,9 +1712,15 @@ int bb_xioctl(int fd, unsigned request, void *argp) FAST_FUNC;
1669#define xioctl(fd,request,argp) bb_xioctl(fd,request,argp) 1712#define xioctl(fd,request,argp) bb_xioctl(fd,request,argp)
1670#endif 1713#endif
1671 1714
1715#if !ENABLE_PLATFORM_MINGW32
1672char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC; 1716char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC;
1673void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC; 1717void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC;
1674void reset_ino_dev_hashtable(void) FAST_FUNC; 1718void reset_ino_dev_hashtable(void) FAST_FUNC;
1719#else
1720#define add_to_ino_dev_hashtable(s, n) (void)0
1721#define is_in_ino_dev_hashtable(s) NULL
1722#define reset_ino_dev_hashtable()
1723#endif
1675#ifdef __GLIBC__ 1724#ifdef __GLIBC__
1676/* At least glibc has horrendously large inline for this, so wrap it */ 1725/* At least glibc has horrendously large inline for this, so wrap it */
1677unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC; 1726unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC;
@@ -1846,7 +1895,11 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
1846 void (*cb)(struct smaprec *, void *), void *data); 1895 void (*cb)(struct smaprec *, void *), void *data);
1847 1896
1848typedef struct procps_status_t { 1897typedef struct procps_status_t {
1898#if !ENABLE_PLATFORM_MINGW32
1849 DIR *dir; 1899 DIR *dir;
1900#else
1901 HANDLE snapshot;
1902#endif
1850 IF_FEATURE_SHOW_THREADS(DIR *task_dir;) 1903 IF_FEATURE_SHOW_THREADS(DIR *task_dir;)
1851 uint8_t shift_pages_to_bytes; 1904 uint8_t shift_pages_to_bytes;
1852 uint8_t shift_pages_to_kb; 1905 uint8_t shift_pages_to_kb;
@@ -2070,12 +2123,24 @@ extern const char bb_path_wtmp_file[] ALIGN1;
2070#define bb_path_motd_file "/etc/motd" 2123#define bb_path_motd_file "/etc/motd"
2071 2124
2072#define bb_dev_null "/dev/null" 2125#define bb_dev_null "/dev/null"
2126#if ENABLE_PLATFORM_MINGW32
2127#define bb_busybox_exec_path get_busybox_exec_path()
2128extern char bb_comm[];
2129extern char bb_command_line[];
2130#else
2073extern const char bb_busybox_exec_path[] ALIGN1; 2131extern const char bb_busybox_exec_path[] ALIGN1;
2132#endif
2074/* allow default system PATH to be extended via CFLAGS */ 2133/* allow default system PATH to be extended via CFLAGS */
2075#ifndef BB_ADDITIONAL_PATH 2134#ifndef BB_ADDITIONAL_PATH
2076#define BB_ADDITIONAL_PATH "" 2135#define BB_ADDITIONAL_PATH ""
2077#endif 2136#endif
2137#if !ENABLE_PLATFORM_MINGW32
2078#define BB_PATH_ROOT_PATH "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH 2138#define BB_PATH_ROOT_PATH "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH
2139#define PATH_SEP ':'
2140#else
2141#define BB_PATH_ROOT_PATH "PATH=/sbin;/usr/sbin;/bin;/usr/bin" BB_ADDITIONAL_PATH
2142#define PATH_SEP ';'
2143#endif
2079extern const char bb_PATH_root_path[] ALIGN1; /* BB_PATH_ROOT_PATH */ 2144extern const char bb_PATH_root_path[] ALIGN1; /* BB_PATH_ROOT_PATH */
2080#define bb_default_root_path (bb_PATH_root_path + sizeof("PATH")) 2145#define bb_default_root_path (bb_PATH_root_path + sizeof("PATH"))
2081/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, 2146/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
diff --git a/include/mingw.h b/include/mingw.h
new file mode 100644
index 000000000..f9c098917
--- /dev/null
+++ b/include/mingw.h
@@ -0,0 +1,494 @@
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 */
8typedef int gid_t;
9typedef int uid_t;
10
11#define DEFAULT_UID 1000
12#define DEFAULT_GID 1000
13
14/*
15 * arpa/inet.h
16 */
17static inline unsigned int git_ntohl(unsigned int x) { return (unsigned int)ntohl(x); }
18#define ntohl git_ntohl
19int inet_aton(const char *cp, struct in_addr *inp);
20int inet_pton(int af, const char *src, void *dst);
21
22/*
23 * fcntl.h
24 */
25#define F_DUPFD 0
26#define F_GETFD 1
27#define F_SETFD 2
28#define F_GETFL 3
29#define F_SETFL 3
30#define FD_CLOEXEC 0x1
31#define O_NONBLOCK 0
32#define O_NOFOLLOW 0
33#define O_NOCTTY 0
34#define O_SPECIAL 0x800000
35
36/*
37 * grp.h
38 */
39
40struct group {
41 char *gr_name;
42 char *gr_passwd;
43 gid_t gr_gid;
44 char **gr_mem;
45};
46IMPL(getgrnam,struct group *,NULL,const char *name UNUSED_PARAM);
47struct group *getgrgid(gid_t gid);
48NOIMPL(initgroups,const char *group UNUSED_PARAM,gid_t gid UNUSED_PARAM);
49static inline void endgrent(void) {}
50int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups);
51
52/*
53 * limits.h
54 */
55#define NAME_MAX 255
56#define MAXSYMLINKS 20
57
58/*
59 * netdb.h
60 */
61
62typedef int sa_family_t;
63
64/*
65 * linux/un.h
66 */
67struct sockaddr_un {
68 sa_family_t sun_family;
69 char sun_path[1]; /* to make compiler happy, don't bother */
70};
71
72/*
73 * pwd.h
74 */
75struct passwd {
76 char *pw_name;
77 char *pw_passwd;
78 char *pw_gecos;
79 char *pw_dir;
80 char *pw_shell;
81 uid_t pw_uid;
82 gid_t pw_gid;
83};
84
85struct passwd *getpwnam(const char *name);
86struct passwd *getpwuid(uid_t uid);
87static inline void setpwent(void) {}
88static inline void endpwent(void) {}
89IMPL(getpwent_r,int,ENOENT,struct passwd *pwbuf UNUSED_PARAM,char *buf UNUSED_PARAM,size_t buflen UNUSED_PARAM,struct passwd **pwbufp UNUSED_PARAM);
90IMPL(getpwent,struct passwd *,NULL,void)
91
92/*
93 * signal.h
94 */
95#define SIGKILL 9
96#define SIGPIPE 13
97
98#define SIG_UNBLOCK 1
99
100NOIMPL(FAST_FUNC sigprocmask_allsigs, int how UNUSED_PARAM);
101typedef void (*sighandler_t)(int);
102sighandler_t winansi_signal(int signum, sighandler_t handler);
103#define signal(s, h) winansi_signal(s, h)
104
105/*
106 * stdio.h
107 */
108#undef fseeko
109#define fseeko(f,o,w) fseek(f,o,w)
110
111int fdprintf(int fd, const char *format, ...);
112FILE* mingw_fopen(const char *filename, const char *mode);
113int mingw_rename(const char*, const char*);
114#define fopen mingw_fopen
115#define rename mingw_rename
116
117FILE *mingw_popen(const char *cmd, const char *mode);
118int mingw_popen_fd(const char *cmd, const char *mode, int fd0, pid_t *pid);
119int mingw_pclose(FILE *fd);
120#undef popen
121#undef pclose
122#define popen mingw_popen
123#define pclose mingw_pclose
124
125#define setlinebuf(fd) setvbuf(fd, (char *) NULL, _IOLBF, 0)
126
127/*
128 * ANSI emulation wrappers
129 */
130
131void move_cursor_row(int n);
132void reset_screen(void);
133int winansi_putchar(int c);
134int winansi_puts(const char *s);
135size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
136int winansi_fputs(const char *str, FILE *stream);
137int winansi_vsnprintf(char *buf, size_t size, const char *format, va_list list);
138int winansi_vfprintf(FILE *stream, const char *format, va_list list);
139int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
140int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
141int winansi_write(int fd, const void *buf, size_t count);
142int winansi_read(int fd, void *buf, size_t count);
143int winansi_getc(FILE *stream);
144#define putchar winansi_putchar
145#define puts winansi_puts
146#define fwrite winansi_fwrite
147#define fputs winansi_fputs
148#if !defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO
149#define vsnprintf(buf, size, ...) winansi_vsnprintf(buf, size, __VA_ARGS__)
150#endif
151#define vfprintf(stream, ...) winansi_vfprintf(stream, __VA_ARGS__)
152#define vprintf(...) winansi_vfprintf(stdout, __VA_ARGS__)
153#define printf(...) winansi_printf(__VA_ARGS__)
154#define fprintf(...) winansi_fprintf(__VA_ARGS__)
155#define write winansi_write
156#define read winansi_read
157#define getc winansi_getc
158
159/*
160 * stdlib.h
161 */
162#define WTERMSIG(x) ((x) & 0x7f)
163#define WIFEXITED(x) (WTERMSIG(x) == 0)
164#define WEXITSTATUS(x) (((x) & 0xff00) >> 8)
165#define WIFSIGNALED(x) (((signed char) (((x) & 0x7f) + 1) >> 1) > 0)
166#define WCOREDUMP(x) 0
167
168int mingw_system(const char *cmd);
169#define system mingw_system
170
171int clearenv(void);
172char *mingw_getenv(const char *name);
173int mingw_putenv(const char *env);
174char *mingw_mktemp(char *template);
175int mkstemp(char *template);
176char *realpath(const char *path, char *resolved_path);
177int setenv(const char *name, const char *value, int replace);
178int unsetenv(const char *env);
179
180#define getenv mingw_getenv
181#define putenv mingw_putenv
182#define mktemp mingw_mktemp
183
184/*
185 * strings.h
186 */
187int ffs(int i);
188
189/*
190 * sys/ioctl.h
191 */
192
193#define TIOCGWINSZ 0x5413
194
195int ioctl(int fd, int code, ...);
196
197/*
198 * sys/socket.h
199 */
200#define hstrerror strerror
201
202#define SHUT_WR SD_SEND
203
204int mingw_socket(int domain, int type, int protocol);
205int mingw_connect(int sockfd, const struct sockaddr *sa, size_t sz);
206int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
207int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
208int mingw_shutdown(int sockfd, int how);
209int mingw_listen(int sockfd, int backlog);
210int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz);
211int mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
212 struct timeval *timeout);
213
214NOIMPL(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);
215
216#define socket mingw_socket
217#define connect mingw_connect
218#define sendto mingw_sendto
219#define listen mingw_listen
220#define bind mingw_bind
221#define setsockopt mingw_setsockopt
222#define shutdown mingw_shutdown
223#define accept mingw_accept
224#define select mingw_select
225
226/*
227 * sys/stat.h
228 */
229#define S_ISUID 04000
230#define S_ISGID 02000
231#define S_ISVTX 01000
232#ifndef S_IRWXU
233#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
234#endif
235#define S_IRWXG (S_IRWXU >> 3)
236#define S_IRWXO (S_IRWXG >> 3)
237
238#define S_IFSOCK 0140000
239#define S_IFLNK 0120000 /* Symbolic link */
240#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
241#define S_ISSOCK(x) 0
242
243#define S_IRGRP (S_IRUSR >> 3)
244#define S_IWGRP (S_IWUSR >> 3)
245#define S_IXGRP (S_IXUSR >> 3)
246#define S_IROTH (S_IRGRP >> 3)
247#define S_IWOTH (S_IWGRP >> 3)
248#define S_IXOTH (S_IXGRP >> 3)
249
250mode_t mingw_umask(mode_t mode);
251#define umask mingw_umask
252
253#define DEFAULT_UMASK 0002
254
255IMPL(fchmod,int,0,int fildes UNUSED_PARAM, mode_t mode UNUSED_PARAM);
256NOIMPL(fchown,int fd UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM);
257int mingw_mkdir(const char *path, int mode);
258int mingw_chmod(const char *path, int mode);
259
260#define mkdir mingw_mkdir
261#define chmod mingw_chmod
262
263#if ENABLE_LFS && !defined(__MINGW64_VERSION_MAJOR)
264# define off_t off64_t
265#endif
266
267typedef int nlink_t;
268typedef int blksize_t;
269typedef off_t blkcnt_t;
270
271struct mingw_stat {
272 dev_t st_dev;
273 ino_t st_ino;
274 mode_t st_mode;
275 nlink_t st_nlink;
276 uid_t st_uid;
277 gid_t st_gid;
278 dev_t st_rdev;
279 off_t st_size;
280 time_t st_atime;
281 time_t st_mtime;
282 time_t st_ctime;
283 blksize_t st_blksize;
284 blkcnt_t st_blocks;
285};
286
287int mingw_lstat(const char *file_name, struct mingw_stat *buf);
288int mingw_stat(const char *file_name, struct mingw_stat *buf);
289int mingw_fstat(int fd, struct mingw_stat *buf);
290#undef lstat
291#undef stat
292#undef fstat
293#define lstat mingw_lstat
294#define stat mingw_stat
295#define fstat mingw_fstat
296
297/*
298 * sys/sysmacros.h
299 */
300#define makedev(a,b) 0*(a)*(b) /* avoid unused warning */
301#define minor(x) 0
302#define major(x) 0
303
304/*
305 * sys/time.h
306 */
307#ifndef _TIMESPEC_DEFINED
308#define _TIMESPEC_DEFINED
309struct timespec {
310 time_t tv_sec;
311 long int tv_nsec;
312};
313#endif
314
315int nanosleep(const struct timespec *req, struct timespec *rem);
316
317/*
318 * sys/wait.h
319 */
320#define WNOHANG 1
321#define WUNTRACED 2
322int waitpid(pid_t pid, int *status, int options);
323
324/*
325 * time.h
326 */
327struct tm *gmtime_r(const time_t *timep, struct tm *result);
328struct tm *localtime_r(const time_t *timep, struct tm *result);
329char *strptime(const char *s, const char *format, struct tm *tm);
330size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm);
331int stime(time_t *t);
332
333#define strftime mingw_strftime
334
335/*
336 * times.h
337 */
338#define clock_t long
339
340struct tms {
341 clock_t tms_utime; /* user CPU time */
342 clock_t tms_stime; /* system CPU time */
343 clock_t tms_cutime; /* user CPU time of children */
344 clock_t tms_cstime; /* system CPU time of children */
345};
346
347clock_t times(struct tms *buf);
348
349/*
350 * unistd.h
351 */
352#define PIPE_BUF 8192
353
354#define _SC_CLK_TCK 2
355
356#define TICKS_PER_SECOND 100
357#define MS_PER_TICK 10
358#define HNSEC_PER_TICK 100000
359
360IMPL(alarm,unsigned int,0,unsigned int seconds UNUSED_PARAM);
361IMPL(chown,int,0,const char *path UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM);
362NOIMPL(chroot,const char *root UNUSED_PARAM);
363NOIMPL(fchdir,int fd UNUSED_PARAM);
364int mingw_dup2 (int fd, int fdto);
365char *mingw_getcwd(char *pointer, int len);
366off_t mingw_lseek(int fd, off_t offset, int whence);
367
368
369IMPL(getgid,int,DEFAULT_GID,void);
370int getgroups(int n, gid_t *groups);
371IMPL(getppid,int,1,void);
372IMPL(getegid,int,DEFAULT_GID,void);
373IMPL(geteuid,int,DEFAULT_UID,void);
374NOIMPL(getsid,pid_t pid UNUSED_PARAM);
375IMPL(getuid,int,DEFAULT_UID,void);
376int getlogin_r(char *buf, size_t len);
377int fcntl(int fd, int cmd, ...);
378int fsync(int fd);
379int kill(pid_t pid, int sig);
380int link(const char *oldpath, const char *newpath);
381NOIMPL(mknod,const char *name UNUSED_PARAM, mode_t mode UNUSED_PARAM, dev_t device UNUSED_PARAM);
382/* order of devices must match that in get_dev_type */
383enum {DEV_NULL, DEV_ZERO, DEV_URANDOM, NOT_DEVICE};
384int get_dev_type(const char *filename);
385void update_dev_fd(int dev, int fd);
386int mingw_open (const char *filename, int oflags, ...);
387int mingw_xopen(const char *filename, int oflags);
388ssize_t mingw_read(int fd, void *buf, size_t count);
389int mingw_close(int fd);
390int pipe(int filedes[2]);
391NOIMPL(readlink,const char *path UNUSED_PARAM, char *buf UNUSED_PARAM, size_t bufsiz UNUSED_PARAM);
392NOIMPL(setgid,gid_t gid UNUSED_PARAM);
393NOIMPL(setegid,gid_t gid UNUSED_PARAM);
394NOIMPL(setsid,void);
395NOIMPL(setuid,uid_t gid UNUSED_PARAM);
396NOIMPL(seteuid,uid_t gid UNUSED_PARAM);
397unsigned int sleep(unsigned int seconds);
398NOIMPL(symlink,const char *oldpath UNUSED_PARAM, const char *newpath UNUSED_PARAM);
399static inline void sync(void) {}
400long sysconf(int name);
401IMPL(getpagesize,int,4096,void);
402NOIMPL(ttyname_r,int fd UNUSED_PARAM, char *buf UNUSED_PARAM, int sz UNUSED_PARAM);
403int mingw_unlink(const char *pathname);
404pid_t vfork(void);
405int mingw_access(const char *name, int mode);
406int mingw_rmdir(const char *name);
407int mingw_isatty(int fd);
408
409#define dup2 mingw_dup2
410#define getcwd mingw_getcwd
411#define lchown chown
412#define open mingw_open
413#define close mingw_close
414#define unlink mingw_unlink
415#define rmdir mingw_rmdir
416#undef lseek
417#define lseek mingw_lseek
418
419#undef access
420#define access mingw_access
421#define isatty mingw_isatty
422
423/*
424 * utime.h
425 */
426int utimes(const char *file_name, const struct timeval times[2]);
427
428/*
429 * dirent.h
430 */
431DIR *mingw_opendir(const char *path);
432#define opendir mingw_opendir
433
434/*
435 * Functions with different prototypes in BusyBox and WIN32
436 */
437#define itoa bb_itoa
438#define strrev bb_strrev
439
440/*
441 * MinGW specific
442 */
443#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
444
445pid_t FAST_FUNC mingw_spawn(char **argv);
446intptr_t FAST_FUNC mingw_spawn_proc(const char **argv);
447int mingw_execv(const char *cmd, char *const *argv);
448int mingw_execvp(const char *cmd, char *const *argv);
449int mingw_execve(const char *cmd, char *const *argv, char *const *envp);
450#define spawn mingw_spawn
451#define execvp mingw_execvp
452#define execve mingw_execve
453#define execv mingw_execv
454
455#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
456#define is_absolute_path(path) ((path)[0] == '/' || (path)[0] == '\\' || has_dos_drive_prefix(path))
457
458int kill_SIGTERM_by_handle(HANDLE process, int exit_code);
459
460#define find_mount_point(n, s) find_mount_point(n)
461
462char *is_prefixed_with_case(const char *string, const char *key) FAST_FUNC;
463char *is_suffixed_with_case(const char *string, const char *key) FAST_FUNC;
464void qsort_string_vector_case(char **sv, unsigned count) FAST_FUNC;
465
466/*
467 * helpers
468 */
469
470const char *get_busybox_exec_path(void);
471void init_winsock(void);
472
473int has_bat_suffix(const char *p);
474int has_exe_suffix(const char *p);
475int has_exe_suffix_or_dot(const char *name);
476char *alloc_win32_extension(const char *p);
477int add_win32_extension(char *p);
478
479static inline char *auto_win32_extension(const char *p)
480{
481 extern char *auto_string(char *str) FAST_FUNC;
482 char *s = alloc_win32_extension(p);
483 return s ? auto_string(s) : NULL;
484}
485
486void FAST_FUNC convert_slashes(char *p);
487
488int err_win_to_posix(DWORD winerr);
489
490ULONGLONG CompatGetTickCount64(void);
491#define GetTickCount64 CompatGetTickCount64
492
493ssize_t get_random_bytes(void *buf, ssize_t count);
494int enumerate_links(const char *file, char *name);
diff --git a/include/platform.h b/include/platform.h
index 50365a31c..afd8cf292 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,13 @@
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#elif ENABLE_PLATFORM_MINGW32
173# define __BIG_ENDIAN 0
174# define __LITTLE_ENDIAN 1
175# define __BYTE_ORDER __LITTLE_ENDIAN
176# define bswap_16(x) ((((x) & 0xFF00) >> 8) | (((x) & 0xFF) << 8))
177# define bswap_32(x) ((bswap_16(((x) & 0xFFFF0000L) >> 16)) | (bswap_16((x) & 0xFFFFL) << 16))
178# define bswap_64(x) ((bswap_32(((x) & 0xFFFFFFFF00000000LL) >> 32)) | (bswap_32((x) & 0xFFFFFFFFLL) << 32))
163#else 179#else
164# include <byteswap.h> 180# include <byteswap.h>
165# include <endian.h> 181# include <endian.h>
@@ -429,6 +445,27 @@ typedef unsigned smalluint;
429# endif 445# endif
430#endif 446#endif
431 447
448#if ENABLE_PLATFORM_MINGW32
449# undef HAVE_FDATASYNC
450# undef HAVE_DPRINTF
451# undef HAVE_GETLINE
452# undef HAVE_MEMRCHR
453# undef HAVE_MKDTEMP
454# undef HAVE_SETBIT
455# undef HAVE_STPCPY
456# undef HAVE_STRCASESTR
457# undef HAVE_STRCHRNUL
458# undef HAVE_STRSEP
459# undef HAVE_STRSIGNAL
460# undef HAVE_STRVERSCMP
461#if !defined(__MINGW64_VERSION_MAJOR)
462# undef HAVE_VASPRINTF
463#endif
464# undef HAVE_UNLOCKED_STDIO
465# undef HAVE_UNLOCKED_LINE_OPS
466# undef HAVE_PRINTF_PERCENTM
467#endif
468
432#if defined(__WATCOMC__) 469#if defined(__WATCOMC__)
433# undef HAVE_DPRINTF 470# undef HAVE_DPRINTF
434# undef HAVE_GETLINE 471# undef HAVE_GETLINE
@@ -543,6 +580,7 @@ extern int dprintf(int fd, const char *format, ...);
543#endif 580#endif
544 581
545#ifndef HAVE_MEMRCHR 582#ifndef HAVE_MEMRCHR
583#include <stddef.h>
546extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; 584extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC;
547#endif 585#endif
548 586
@@ -602,6 +640,7 @@ extern int usleep(unsigned) FAST_FUNC;
602#endif 640#endif
603 641
604#ifndef HAVE_VASPRINTF 642#ifndef HAVE_VASPRINTF
643# include <stdarg.h>
605extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; 644extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC;
606#endif 645#endif
607 646
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src
index 8c9ba8cca..b298040ac 100644
--- a/libbb/Kbuild.src
+++ b/libbb/Kbuild.src
@@ -12,14 +12,11 @@ INSERT
12 12
13lib-y += appletlib.o 13lib-y += appletlib.o
14lib-y += ask_confirmation.o 14lib-y += ask_confirmation.o
15lib-y += bb_askpass.o
16lib-y += bb_bswap_64.o 15lib-y += bb_bswap_64.o
17lib-y += bb_do_delay.o
18lib-y += bb_pwd.o 16lib-y += bb_pwd.o
19lib-y += bb_qsort.o 17lib-y += bb_qsort.o
20#lib-y += bb_strtod.o 18#lib-y += bb_strtod.o
21lib-y += bb_strtonum.o 19lib-y += bb_strtonum.o
22lib-y += change_identity.o
23lib-y += chomp.o 20lib-y += chomp.o
24lib-y += compare_string_array.o 21lib-y += compare_string_array.o
25lib-y += concat_path_file.o 22lib-y += concat_path_file.o
@@ -28,32 +25,23 @@ lib-y += copy_file.o
28lib-y += copyfd.o 25lib-y += copyfd.o
29lib-y += crc32.o 26lib-y += crc32.o
30lib-y += default_error_retval.o 27lib-y += default_error_retval.o
31lib-y += device_open.o
32lib-y += dump.o 28lib-y += dump.o
33lib-y += executable.o 29lib-y += executable.o
34lib-y += fclose_nonstdin.o 30lib-y += fclose_nonstdin.o
35lib-y += fflush_stdout_and_exit.o 31lib-y += fflush_stdout_and_exit.o
36lib-y += fgets_str.o 32lib-y += fgets_str.o
37lib-y += find_pid_by_name.o 33lib-y += find_pid_by_name.o
38lib-y += find_root_device.o
39lib-y += full_write.o 34lib-y += full_write.o
40lib-y += get_console.o
41lib-y += get_last_path_component.o 35lib-y += get_last_path_component.o
42lib-y += get_line_from_file.o 36lib-y += get_line_from_file.o
43lib-y += getpty.o 37lib-y += getopt32.o
44lib-y += get_volsize.o
45lib-y += herror_msg.o 38lib-y += herror_msg.o
46lib-y += human_readable.o 39lib-y += human_readable.o
47lib-y += inet_common.o
48lib-y += inode_hash.o
49lib-y += isdirectory.o 40lib-y += isdirectory.o
50lib-y += kernel_version.o
51lib-y += last_char_is.o 41lib-y += last_char_is.o
52lib-y += lineedit.o lineedit_ptr_hack.o 42lib-y += lineedit.o lineedit_ptr_hack.o
53lib-y += llist.o 43lib-y += llist.o
54lib-y += login.o
55lib-y += make_directory.o 44lib-y += make_directory.o
56lib-y += makedev.o
57lib-y += hash_md5_sha.o 45lib-y += hash_md5_sha.o
58# Alternative (disabled) MD5 implementation 46# Alternative (disabled) MD5 implementation
59#lib-y += hash_md5prime.o 47#lib-y += hash_md5prime.o
@@ -61,20 +49,15 @@ lib-y += messages.o
61lib-y += mode_string.o 49lib-y += mode_string.o
62lib-y += parse_mode.o 50lib-y += parse_mode.o
63lib-y += perror_msg.o 51lib-y += perror_msg.o
64lib-y += perror_nomsg.o
65lib-y += perror_nomsg_and_die.o 52lib-y += perror_nomsg_and_die.o
66lib-y += pidfile.o
67lib-y += platform.o 53lib-y += platform.o
68lib-y += printable.o 54lib-y += printable.o
69lib-y += printable_string.o 55lib-y += printable_string.o
70lib-y += print_flags.o
71lib-y += process_escape_sequence.o 56lib-y += process_escape_sequence.o
72lib-y += procps.o 57lib-y += procps.o
73lib-y += progress.o
74lib-y += ptr_to_globals.o 58lib-y += ptr_to_globals.o
75lib-y += read.o 59lib-y += read.o
76lib-y += read_printf.o 60lib-y += read_printf.o
77lib-y += read_key.o
78lib-y += recursive_action.o 61lib-y += recursive_action.o
79lib-y += remove_file.o 62lib-y += remove_file.o
80lib-y += run_shell.o 63lib-y += run_shell.o
@@ -83,12 +66,8 @@ lib-y += safe_poll.o
83lib-y += safe_strncpy.o 66lib-y += safe_strncpy.o
84lib-y += safe_write.o 67lib-y += safe_write.o
85lib-y += securetty.o 68lib-y += securetty.o
86lib-y += setup_environment.o
87lib-y += signals.o
88lib-y += simplify_path.o
89lib-y += single_argv.o 69lib-y += single_argv.o
90lib-y += skip_whitespace.o 70lib-y += skip_whitespace.o
91lib-y += speed_table.o
92lib-y += str_tolower.o 71lib-y += str_tolower.o
93lib-y += strrstr.o 72lib-y += strrstr.o
94lib-y += sysconf.o 73lib-y += sysconf.o
@@ -98,20 +77,44 @@ lib-y += u_signal_names.o
98lib-y += uuencode.o 77lib-y += uuencode.o
99lib-y += verror_msg.o 78lib-y += verror_msg.o
100lib-y += vfork_daemon_rexec.o 79lib-y += vfork_daemon_rexec.o
101lib-y += warn_ignoring_args.o
102lib-y += wfopen.o 80lib-y += wfopen.o
103lib-y += wfopen_input.o 81lib-y += wfopen_input.o
104lib-y += write.o
105lib-y += xatonum.o 82lib-y += xatonum.o
106lib-y += xconnect.o 83lib-y += xconnect.o
107lib-y += xfuncs.o 84lib-y += xfuncs.o
108lib-y += xfuncs_printf.o 85lib-y += xfuncs_printf.o
109lib-y += xfunc_die.o 86lib-y += xfunc_die.o
110lib-y += xgetcwd.o 87lib-y += xgetcwd.o
111lib-y += xgethostbyname.o
112lib-y += xreadlink.o 88lib-y += xreadlink.o
113lib-y += xrealloc_vector.o 89lib-y += xrealloc_vector.o
114 90
91lib-$(CONFIG_PLATFORM_POSIX) += bb_askpass.o
92lib-$(CONFIG_PLATFORM_POSIX) += bb_do_delay.o
93lib-$(CONFIG_PLATFORM_POSIX) += change_identity.o
94lib-$(CONFIG_PLATFORM_POSIX) += device_open.o
95lib-$(CONFIG_PLATFORM_POSIX) += find_root_device.o
96lib-$(CONFIG_PLATFORM_POSIX) += get_console.o
97lib-$(CONFIG_PLATFORM_POSIX) += getpty.o
98lib-$(CONFIG_PLATFORM_POSIX) += get_volsize.o
99lib-$(CONFIG_PLATFORM_POSIX) += inet_common.o
100lib-$(CONFIG_PLATFORM_POSIX) += inode_hash.o
101lib-$(CONFIG_PLATFORM_POSIX) += kernel_version.o
102lib-$(CONFIG_PLATFORM_POSIX) += login.o
103lib-$(CONFIG_PLATFORM_POSIX) += makedev.o
104lib-$(CONFIG_PLATFORM_POSIX) += perror_nomsg.o
105lib-$(CONFIG_PLATFORM_POSIX) += pidfile.o
106lib-$(CONFIG_PLATFORM_POSIX) += print_flags.o
107lib-$(CONFIG_PLATFORM_POSIX) += progress.o
108lib-$(CONFIG_PLATFORM_POSIX) += read_key.o
109lib-$(CONFIG_PLATFORM_POSIX) += setup_environment.o
110lib-$(CONFIG_PLATFORM_POSIX) += signals.o
111lib-$(CONFIG_PLATFORM_POSIX) += simplify_path.o
112lib-$(CONFIG_PLATFORM_POSIX) += speed_table.o
113lib-$(CONFIG_PLATFORM_POSIX) += udp_io.o
114lib-$(CONFIG_PLATFORM_POSIX) += warn_ignoring_args.o
115lib-$(CONFIG_PLATFORM_POSIX) += write.o
116lib-$(CONFIG_PLATFORM_POSIX) += xgethostbyname.o
117
115lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o 118lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o
116 119
117lib-$(CONFIG_FEATURE_UTMP) += utmp.o 120lib-$(CONFIG_FEATURE_UTMP) += utmp.o
@@ -123,7 +126,7 @@ lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
123lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o 126lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o
124lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o 127lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o
125 128
126lib-$(CONFIG_NC) += udp_io.o 129lib-$(CONFIG_NC_110_COMPAT) += udp_io.o
127lib-$(CONFIG_NETCAT) += udp_io.o 130lib-$(CONFIG_NETCAT) += udp_io.o
128lib-$(CONFIG_DNSD) += udp_io.o 131lib-$(CONFIG_DNSD) += udp_io.o
129lib-$(CONFIG_NTPD) += udp_io.o 132lib-$(CONFIG_NTPD) += udp_io.o
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index c15014a34..8efb1754a 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -48,6 +48,15 @@
48# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ 48# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
49#endif 49#endif
50 50
51#if (ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32) || \
52 (ENABLE_PLATFORM_MINGW32 && (ENABLE_FEATURE_PREFER_APPLETS \
53 || ENABLE_FEATURE_SH_STANDALONE \
54 || ENABLE_FEATURE_SH_NOFORK))
55# define IF_FULL_LIST_OPTION(...) __VA_ARGS__
56#else
57# define IF_FULL_LIST_OPTION(...)
58#endif
59
51#include "usage_compressed.h" 60#include "usage_compressed.h"
52 61
53#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS 62#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS
@@ -57,6 +66,7 @@
57# define NUM_SCRIPTS 0 66# define NUM_SCRIPTS 0
58#endif 67#endif
59#if NUM_SCRIPTS > 0 68#if NUM_SCRIPTS > 0
69# define BB_ARCHIVE_PUBLIC
60# include "bb_archive.h" 70# include "bb_archive.h"
61static const char packed_scripts[] ALIGN1 = { PACKED_SCRIPTS }; 71static const char packed_scripts[] ALIGN1 = { PACKED_SCRIPTS };
62#endif 72#endif
@@ -106,6 +116,7 @@ static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
106#if ENABLE_FEATURE_COMPRESS_USAGE 116#if ENABLE_FEATURE_COMPRESS_USAGE
107 117
108static const char packed_usage[] ALIGN1 = { PACKED_USAGE }; 118static const char packed_usage[] ALIGN1 = { PACKED_USAGE };
119# define BB_ARCHIVE_PUBLIC
109# include "bb_archive.h" 120# include "bb_archive.h"
110# define unpack_usage_messages() \ 121# define unpack_usage_messages() \
111 unpack_bz2_data(packed_usage, sizeof(packed_usage), sizeof(UNPACKED_USAGE)) 122 unpack_bz2_data(packed_usage, sizeof(packed_usage), sizeof(UNPACKED_USAGE))
@@ -149,7 +160,7 @@ void FAST_FUNC bb_show_usage(void)
149 ap--; 160 ap--;
150 } 161 }
151 full_write2_str(bb_banner); 162 full_write2_str(bb_banner);
152 full_write2_str(" multi-call binary.\n"); /* common string */ 163 full_write2_str(" multi-call binary\n");
153 if (*p == '\b') 164 if (*p == '\b')
154 full_write2_str("\nNo help available\n"); 165 full_write2_str("\nNo help available\n");
155 else { 166 else {
@@ -312,6 +323,10 @@ void lbb_prepare(const char *applet
312 if (ENABLE_LOCALE_SUPPORT) 323 if (ENABLE_LOCALE_SUPPORT)
313 setlocale(LC_ALL, ""); 324 setlocale(LC_ALL, "");
314 325
326#if ENABLE_PLATFORM_MINGW32
327 init_winsock();
328#endif
329
315#if ENABLE_FEATURE_INDIVIDUAL 330#if ENABLE_FEATURE_INDIVIDUAL
316 /* Redundant for busybox (run_applet_and_exit covers that case) 331 /* Redundant for busybox (run_applet_and_exit covers that case)
317 * but needed for "individual applet" mode */ 332 * but needed for "individual applet" mode */
@@ -702,6 +717,7 @@ static void check_suid(int applet_no)
702 717
703 718
704# if ENABLE_FEATURE_INSTALLER 719# if ENABLE_FEATURE_INSTALLER
720# if !ENABLE_PLATFORM_MINGW32
705static const char usr_bin [] ALIGN1 = "/usr/bin/"; 721static const char usr_bin [] ALIGN1 = "/usr/bin/";
706static const char usr_sbin[] ALIGN1 = "/usr/sbin/"; 722static const char usr_sbin[] ALIGN1 = "/usr/sbin/";
707static const char *const install_dir[] = { 723static const char *const install_dir[] = {
@@ -746,6 +762,30 @@ static void install_links(const char *busybox, int use_symbolic_links,
746 continue; 762 continue;
747 } 763 }
748} 764}
765# else /* ENABLE_PLATFORM_MINGW32 */
766static void install_links(const char *busybox,
767 int use_symbolic_links UNUSED_PARAM, char *custom_install_dir)
768{
769 char *fpc;
770 const char *appname = applet_names;
771 int rc;
772
773 if (!is_directory(custom_install_dir, FALSE))
774 bb_error_msg_and_die("'%s' is not a directory", custom_install_dir);
775
776 while (*appname) {
777 fpc = xasprintf("%s/%s.exe", custom_install_dir, appname);
778 rc = link(busybox, fpc);
779 if (rc != 0 && (errno != EEXIST ||
780 strcmp("busybox.exe", bb_basename(fpc)) != 0)) {
781 bb_simple_perror_msg(fpc);
782 }
783 free(fpc);
784 while (*appname++ != '\0')
785 continue;
786 }
787}
788# endif
749# elif ENABLE_BUSYBOX 789# elif ENABLE_BUSYBOX
750static void install_links(const char *busybox UNUSED_PARAM, 790static void install_links(const char *busybox UNUSED_PARAM,
751 int use_symbolic_links UNUSED_PARAM, 791 int use_symbolic_links UNUSED_PARAM,
@@ -829,20 +869,31 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
829 869
830 dup2(1, 2); 870 dup2(1, 2);
831 full_write2_str(bb_banner); /* reuse const string */ 871 full_write2_str(bb_banner); /* reuse const string */
832 full_write2_str(" multi-call binary.\n"); /* reuse */ 872 full_write2_str(" multi-call binary\n"); /* reuse */
873#if defined(MINGW_VER)
874 if (sizeof(MINGW_VER) > 5) {
875 full_write2_str(MINGW_VER "\n\n");
876 }
877#endif
833 full_write2_str( 878 full_write2_str(
834 "BusyBox is copyrighted by many authors between 1998-2015.\n" 879 "BusyBox is copyrighted by many authors between 1998-2018.\n"
835 "Licensed under GPLv2. See source distribution for detailed\n" 880 "Licensed under GPLv2. See source distribution for detailed\n"
836 "copyright notices.\n" 881 "copyright notices.\n"
837 "\n" 882 "\n"
838 "Usage: busybox [function [arguments]...]\n" 883 "Usage: busybox [function [arguments]...]\n"
839 " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n" 884 " or: busybox --list"IF_FULL_LIST_OPTION("[-full]")"\n"
840# if ENABLE_FEATURE_SHOW_SCRIPT && NUM_SCRIPTS > 0 885# if ENABLE_FEATURE_SHOW_SCRIPT && NUM_SCRIPTS > 0
841 " or: busybox --show SCRIPT\n" 886 " or: busybox --show SCRIPT\n"
842# endif 887# endif
843 IF_FEATURE_INSTALLER( 888 IF_FEATURE_INSTALLER(
889 IF_NOT_PLATFORM_MINGW32(
844 " or: busybox --install [-s] [DIR]\n" 890 " or: busybox --install [-s] [DIR]\n"
845 ) 891 )
892 IF_PLATFORM_MINGW32(
893 " or: busybox --install [DIR]\n"
894 " or: busybox --uninstall [-n] file\n"
895 )
896 )
846 " or: function [arguments]...\n" 897 " or: function [arguments]...\n"
847 "\n" 898 "\n"
848 IF_NOT_FEATURE_SH_STANDALONE( 899 IF_NOT_FEATURE_SH_STANDALONE(
@@ -859,6 +910,11 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
859 "\tTo run external program, use full path (/sbin/ip instead of ip).\n" 910 "\tTo run external program, use full path (/sbin/ip instead of ip).\n"
860 ) 911 )
861 "\n" 912 "\n"
913#if ENABLE_GLOBBING
914 "\tSupport for native Windows wildcards is enabled. In some\n"
915 "\tcases this may result in wildcards being processed twice.\n"
916 "\n"
917#endif
862 "Currently defined functions:\n" 918 "Currently defined functions:\n"
863 ); 919 );
864 col = 0; 920 col = 0;
@@ -903,9 +959,27 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
903 const char *a = applet_names; 959 const char *a = applet_names;
904 dup2(1, 2); 960 dup2(1, 2);
905 while (*a) { 961 while (*a) {
906# if ENABLE_FEATURE_INSTALLER 962# if ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32
907 if (argv[1][6]) /* --list-full? */ 963 if (argv[1][6]) /* --list-full? */
908 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); 964 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
965# elif ENABLE_PLATFORM_MINGW32 && (ENABLE_FEATURE_PREFER_APPLETS \
966 || ENABLE_FEATURE_SH_STANDALONE \
967 || ENABLE_FEATURE_SH_NOFORK)
968 if (argv[1][6]) { /* --list-full? */
969 const char *str;
970
971 if (APPLET_IS_NOFORK(i))
972 str = "NOFORK ";
973 else if (APPLET_IS_NOEXEC(i))
974 str = "noexec ";
975# if NUM_SCRIPTS > 0
976 else if (applet_main[i] == scripted_main)
977 str = "script ";
978# endif
979 else
980 str = " ";
981 full_write2_str(str);
982 }
909# endif 983# endif
910 full_write2_str(a); 984 full_write2_str(a);
911 full_write2_str("\n"); 985 full_write2_str("\n");
@@ -917,6 +991,7 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
917 } 991 }
918 992
919 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { 993 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
994#if !ENABLE_PLATFORM_MINGW32
920 int use_symbolic_links; 995 int use_symbolic_links;
921 const char *busybox; 996 const char *busybox;
922 997
@@ -937,9 +1012,40 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
937 */ 1012 */
938 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv); 1013 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv);
939 install_links(busybox, use_symbolic_links, argv[2]); 1014 install_links(busybox, use_symbolic_links, argv[2]);
1015#else
1016 /* busybox --install [DIR]
1017 * where DIR is the directory to install to. If DIR is not
1018 * provided put the links in the same directory as busybox.
1019 */
1020 install_links(bb_busybox_exec_path, FALSE, argv[2] ? argv[2] :
1021 dirname(xstrdup(bb_busybox_exec_path)));
1022#endif
940 return 0; 1023 return 0;
941 } 1024 }
942 1025
1026#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_INSTALLER
1027 if (strcmp(argv[1], "--uninstall") == 0) {
1028 char name[PATH_MAX];
1029 int dry_run = (argv[2] && strcmp(argv[2], "-n") == 0 && ++argv);
1030 const char *file = argv[2];
1031
1032 if (!argv[2])
1033 bb_error_msg_and_die(bb_msg_requires_arg, "--uninstall");
1034
1035 while (enumerate_links(file, name)) {
1036 if (dry_run) {
1037 full_write1_str(name);
1038 full_write1_str("\n");
1039 }
1040 else if (unlink(name) != 0) {
1041 bb_simple_perror_msg(name);
1042 }
1043 file = NULL;
1044 }
1045 return 0;
1046 }
1047#endif
1048
943 if (strcmp(argv[1], "--help") == 0) { 1049 if (strcmp(argv[1], "--help") == 0) {
944 /* "busybox --help [<applet>]" */ 1050 /* "busybox --help [<applet>]" */
945 if (!argv[2]) 1051 if (!argv[2])
@@ -959,15 +1065,42 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
959# endif 1065# endif
960 1066
961# if NUM_APPLETS > 0 1067# if NUM_APPLETS > 0
1068
1069# if ENABLE_PLATFORM_MINGW32
1070static int interp = 0;
1071char bb_comm[COMM_LEN];
1072char bb_command_line[128];
1073# endif
1074
962void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv) 1075void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv)
963{ 1076{
964 int argc = string_array_len(argv); 1077 int argc = string_array_len(argv);
1078# if ENABLE_PLATFORM_MINGW32
1079 int i;
1080 const char *vmask;
1081 unsigned int mask;
1082# endif
965 1083
966 /* 1084 /*
967 * We do not use argv[0]: do not want to repeat massaging of 1085 * We do not use argv[0]: do not want to repeat massaging of
968 * "-/sbin/halt" -> "halt", for example. 1086 * "-/sbin/halt" -> "halt", for example.
969 */ 1087 */
970 applet_name = name; 1088 applet_name = name;
1089# if ENABLE_PLATFORM_MINGW32
1090 safe_strncpy(bb_comm,
1091 interp ? bb_basename(argv[interp]) : applet_name,
1092 sizeof(bb_comm));
1093
1094 safe_strncpy(bb_command_line, applet_name, sizeof(bb_command_line));
1095 for (i=1; i < argc && argv[i] &&
1096 strlen(bb_command_line) + strlen(argv[i]) + 2 < 128; ++i) {
1097 strcat(strcat(bb_command_line, " "), argv[i]);
1098 }
1099
1100 vmask = getenv("BB_UMASK");
1101 if (vmask && sscanf(vmask, "%o", &mask) == 1)
1102 umask((mode_t)(mask&0777));
1103# endif
971 1104
972 /* Special case. POSIX says "test --help" 1105 /* Special case. POSIX says "test --help"
973 * should be no different from e.g. "test --foo". 1106 * should be no different from e.g. "test --foo".
@@ -993,6 +1126,7 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **ar
993 } 1126 }
994 if (ENABLE_FEATURE_SUID) 1127 if (ENABLE_FEATURE_SUID)
995 check_suid(applet_no); 1128 check_suid(applet_no);
1129
996 xfunc_error_retval = applet_main[applet_no](argc, argv); 1130 xfunc_error_retval = applet_main[applet_no](argc, argv);
997 /* Note: applet_main() may also not return (die on a xfunc or such) */ 1131 /* Note: applet_main() may also not return (die on a xfunc or such) */
998 xfunc_die(); 1132 xfunc_die();
@@ -1075,6 +1209,31 @@ int main(int argc UNUSED_PARAM, char **argv)
1075 argv[0][0] &= 0x7f; 1209 argv[0][0] &= 0x7f;
1076 } 1210 }
1077#endif 1211#endif
1212#if ENABLE_PLATFORM_MINGW32
1213 /* detect if we're running an interpreted script */
1214 if (argv[0][1] == ':' && argv[0][2] == '/') {
1215 switch (argv[0][0]) {
1216 case '1':
1217 interp = 1;
1218 break;
1219 case'2':
1220 interp = 2;
1221 break;
1222 }
1223 }
1224#endif
1225
1226#if defined(__MINGW64_VERSION_MAJOR)
1227 if ( stdin ) {
1228 _setmode(fileno(stdin), _O_BINARY);
1229 }
1230 if ( stdout ) {
1231 _setmode(fileno(stdout), _O_BINARY);
1232 }
1233 if ( stderr ) {
1234 _setmode(fileno(stderr), _O_BINARY);
1235 }
1236#endif
1078 1237
1079#if defined(SINGLE_APPLET_MAIN) 1238#if defined(SINGLE_APPLET_MAIN)
1080 1239
@@ -1107,6 +1266,21 @@ int main(int argc UNUSED_PARAM, char **argv)
1107 applet_name = argv[0]; 1266 applet_name = argv[0];
1108 if (applet_name[0] == '-') 1267 if (applet_name[0] == '-')
1109 applet_name++; 1268 applet_name++;
1269# if ENABLE_PLATFORM_MINGW32
1270 if ( argv[1] && argv[2] && strcmp(argv[1], "--busybox") == 0 ) {
1271 argv += 2;
1272 applet_name = argv[0];
1273 }
1274 else {
1275 char *s;
1276
1277 str_tolower(argv[0]);
1278 convert_slashes(argv[0]);
1279 if (has_exe_suffix_or_dot(argv[0]) && (s=strrchr(argv[0], '.'))) {
1280 *s = '\0';
1281 }
1282 }
1283# endif
1110 applet_name = bb_basename(applet_name); 1284 applet_name = bb_basename(applet_name);
1111 1285
1112 /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */ 1286 /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */
diff --git a/libbb/bb_qsort.c b/libbb/bb_qsort.c
index 505045533..7afddf468 100644
--- a/libbb/bb_qsort.c
+++ b/libbb/bb_qsort.c
@@ -17,3 +17,15 @@ void FAST_FUNC qsort_string_vector(char **sv, unsigned count)
17{ 17{
18 qsort(sv, count, sizeof(char*), bb_pstrcmp); 18 qsort(sv, count, sizeof(char*), bb_pstrcmp);
19} 19}
20
21#if ENABLE_PLATFORM_MINGW32
22static int bb_pstrcasecmp(const void *a, const void *b)
23{
24 return strcasecmp(*(char**)a, *(char**)b);
25}
26
27void FAST_FUNC qsort_string_vector_case(char **sv, unsigned count)
28{
29 qsort(sv, count, sizeof(char*), bb_pstrcasecmp);
30}
31#endif
diff --git a/libbb/compare_string_array.c b/libbb/compare_string_array.c
index 01a9df0e2..856739c41 100644
--- a/libbb/compare_string_array.c
+++ b/libbb/compare_string_array.c
@@ -27,11 +27,25 @@ char* FAST_FUNC is_prefixed_with(const char *string, const char *key)
27#endif 27#endif
28} 28}
29 29
30#if ENABLE_PLATFORM_MINGW32
31char* FAST_FUNC is_prefixed_with_case(const char *string, const char *key)
32{
33 while (*key != '\0') {
34 if (tolower(*key) != tolower(*string))
35 return NULL;
36 key++;
37 string++;
38 }
39 return (char*)string;
40}
41#endif
42
30/* 43/*
31 * Return NULL if string is not suffixed with key. Return pointer to the 44 * Return NULL if string is not suffixed with key. Return pointer to the
32 * beginning of prefix key in string. If key is an empty string return pointer 45 * beginning of prefix key in string. If key is an empty string return pointer
33 * to the end of string. 46 * to the end of string.
34 */ 47 */
48#if !ENABLE_PLATFORM_MINGW32
35char* FAST_FUNC is_suffixed_with(const char *string, const char *key) 49char* FAST_FUNC is_suffixed_with(const char *string, const char *key)
36{ 50{
37 size_t key_len = strlen(key); 51 size_t key_len = strlen(key);
@@ -46,6 +60,33 @@ char* FAST_FUNC is_suffixed_with(const char *string, const char *key)
46 60
47 return NULL; 61 return NULL;
48} 62}
63#else
64static char* FAST_FUNC is_suffixed(const char *string, const char *key,
65 int (*fn)(const char *, const char*))
66{
67 size_t key_len = strlen(key);
68 ssize_t len_diff = strlen(string) - key_len;
69
70 if (len_diff >= 0) {
71 string += len_diff;
72 if (fn(string, key) == 0) {
73 return (char*)string;
74 }
75 }
76
77 return NULL;
78}
79
80char* FAST_FUNC is_suffixed_with(const char *string, const char *key)
81{
82 return is_suffixed(string, key, strcmp);
83}
84
85char* FAST_FUNC is_suffixed_with_case(const char *string, const char *key)
86{
87 return is_suffixed(string, key, strcasecmp);
88}
89#endif
49 90
50/* returns the array index of the string */ 91/* returns the array index of the string */
51/* (index of first match is returned, or -1) */ 92/* (index of first match is returned, or -1) */
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 2d6557cd4..299cd7bea 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 29d2a2c85..87a40eeda 100644
--- a/libbb/executable.c
+++ b/libbb/executable.c
@@ -39,19 +39,29 @@ char* FAST_FUNC find_executable(const char *filename, char **PATHp)
39 * following the rest of the list. 39 * following the rest of the list.
40 */ 40 */
41 char *p, *n; 41 char *p, *n;
42#if ENABLE_PLATFORM_MINGW32
43 char *w;
44#endif
42 45
43 p = *PATHp; 46 p = *PATHp;
44 while (p) { 47 while (p) {
45 int ex; 48 int ex;
46 49
47 n = strchr(p, ':'); 50 n = strchr(p, PATH_SEP);
48 if (n) *n = '\0'; 51 if (n) *n = '\0';
49 p = concat_path_file( 52 p = concat_path_file(
50 p[0] ? p : ".", /* handle "::" case */ 53 p[0] ? p : ".", /* handle "::" case */
51 filename 54 filename
52 ); 55 );
56 if (n) *n++ = PATH_SEP;
57#if ENABLE_PLATFORM_MINGW32
58 if ((w=alloc_win32_extension(p))) {
59 free(p);
60 p = w;
61 /* following test will succeed */
62 }
63#endif
53 ex = file_is_executable(p); 64 ex = file_is_executable(p);
54 if (n) *n++ = ':';
55 if (ex) { 65 if (ex) {
56 *PATHp = n; 66 *PATHp = n;
57 return p; 67 return p;
diff --git a/libbb/find_mount_point.c b/libbb/find_mount_point.c
index 94bbf1d4a..341c30102 100644
--- a/libbb/find_mount_point.c
+++ b/libbb/find_mount_point.c
@@ -19,14 +19,28 @@
19struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too) 19struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
20{ 20{
21 struct stat s; 21 struct stat s;
22 FILE *mtab_fp;
23 struct mntent *mountEntry; 22 struct mntent *mountEntry;
23#if !ENABLE_PLATFORM_MINGW32
24 FILE *mtab_fp;
24 dev_t devno_of_name; 25 dev_t devno_of_name;
25 bool block_dev; 26 bool block_dev;
27#else
28 static char mnt_fsname[4];
29 static char mnt_dir[4];
30 static char mnt_type[1];
31 static char mnt_opts[1];
32 static struct mntent my_mount_entry = {
33 mnt_fsname, mnt_dir, mnt_type, mnt_opts, 0, 0
34 };
35 char *current;
36 const char *path;
37 DWORD len;
38#endif
26 39
27 if (stat(name, &s) != 0) 40 if (stat(name, &s) != 0)
28 return NULL; 41 return NULL;
29 42
43#if !ENABLE_PLATFORM_MINGW32
30 devno_of_name = s.st_dev; 44 devno_of_name = s.st_dev;
31 block_dev = 0; 45 block_dev = 0;
32 /* Why S_ISCHR? - UBI volumes use char devices, not block */ 46 /* Why S_ISCHR? - UBI volumes use char devices, not block */
@@ -63,6 +77,35 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
63 break; 77 break;
64 } 78 }
65 endmntent(mtab_fp); 79 endmntent(mtab_fp);
80#else
81 mountEntry = NULL;
82 path = NULL;
83 current = NULL;
84
85 if ( isalpha(name[0]) && name[1] == ':' ) {
86 path = name;
87 }
88 else {
89 if ( (len=GetCurrentDirectory(0, NULL)) > 0 &&
90 (current=malloc(len+1)) != NULL &&
91 GetCurrentDirectory(len, current) ) {
92 path = current;
93 }
94 }
95
96 if ( path && isalpha(path[0]) && path[1] == ':' ) {
97 mnt_fsname[0] = path[0];
98 mnt_fsname[1] = path[1];
99 mnt_fsname[2] = '\0';
100 mnt_dir[0] = path[0];
101 mnt_dir[1] = path[1];
102 mnt_dir[2] = '\\';
103 mnt_dir[3] = '\0';
104
105 mountEntry = &my_mount_entry;
106 }
107 free(current);
108#endif
66 109
67 return mountEntry; 110 return mountEntry;
68} 111}
diff --git a/libbb/find_pid_by_name.c b/libbb/find_pid_by_name.c
index abbf293e8..52debb171 100644
--- a/libbb/find_pid_by_name.c
+++ b/libbb/find_pid_by_name.c
@@ -39,8 +39,10 @@ and therefore comm field contains "exe".
39 39
40static int comm_match(procps_status_t *p, const char *procName) 40static int comm_match(procps_status_t *p, const char *procName)
41{ 41{
42#if !ENABLE_PLATFORM_MINGW32
42 int argv1idx; 43 int argv1idx;
43 const char *argv1; 44 const char *argv1;
45#endif
44 46
45 if (strncmp(p->comm, procName, 15) != 0) 47 if (strncmp(p->comm, procName, 15) != 0)
46 return 0; /* comm does not match */ 48 return 0; /* comm does not match */
@@ -55,6 +57,7 @@ static int comm_match(procps_status_t *p, const char *procName)
55 * This can be crazily_long_script_name.sh! 57 * This can be crazily_long_script_name.sh!
56 * The telltale sign is basename(argv[1]) == procName */ 58 * The telltale sign is basename(argv[1]) == procName */
57 59
60#if !ENABLE_PLATFORM_MINGW32
58 if (!p->argv0) 61 if (!p->argv0)
59 return 0; 62 return 0;
60 63
@@ -65,6 +68,7 @@ static int comm_match(procps_status_t *p, const char *procName)
65 68
66 if (strcmp(bb_basename(argv1), procName) != 0) 69 if (strcmp(bb_basename(argv1), procName) != 0)
67 return 0; 70 return 0;
71#endif
68 72
69 return 1; 73 return 1;
70} 74}
@@ -87,10 +91,12 @@ pid_t* FAST_FUNC find_pid_by_name(const char *procName)
87 pidList = xzalloc(sizeof(*pidList)); 91 pidList = xzalloc(sizeof(*pidList));
88 while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN|PSSCAN_EXE))) { 92 while ((p = procps_scan(p, PSSCAN_PID|PSSCAN_COMM|PSSCAN_ARGVN|PSSCAN_EXE))) {
89 if (comm_match(p, procName) 93 if (comm_match(p, procName)
94#if !ENABLE_PLATFORM_MINGW32
90 /* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/ 95 /* or we require argv0 to match (essential for matching reexeced /proc/self/exe)*/
91 || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0) 96 || (p->argv0 && strcmp(bb_basename(p->argv0), procName) == 0)
92 /* or we require /proc/PID/exe link to match */ 97 /* or we require /proc/PID/exe link to match */
93 || (p->exe && strcmp(bb_basename(p->exe), procName) == 0) 98 || (p->exe && strcmp(bb_basename(p->exe), procName) == 0)
99#endif
94 ) { 100 ) {
95 pidList = xrealloc_vector(pidList, 2, i); 101 pidList = xrealloc_vector(pidList, 2, i);
96 pidList[i++] = p->pid; 102 pidList[i++] = p->pid;
diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c
index 04fdf2a3e..3a9b9237e 100644
--- a/libbb/get_last_path_component.c
+++ b/libbb/get_last_path_component.c
@@ -10,9 +10,16 @@
10 10
11const char* FAST_FUNC bb_basename(const char *name) 11const char* FAST_FUNC bb_basename(const char *name)
12{ 12{
13#if ENABLE_PLATFORM_MINGW32
14 const char *cp;
15 for (cp = name; *cp; cp++)
16 if (*cp == '/' || *cp == '\\')
17 name = cp + 1;
18#else
13 const char *cp = strrchr(name, '/'); 19 const char *cp = strrchr(name, '/');
14 if (cp) 20 if (cp)
15 return cp + 1; 21 return cp + 1;
22#endif
16 return name; 23 return name;
17} 24}
18 25
@@ -26,8 +33,18 @@ char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path)
26{ 33{
27 char *slash = strrchr(path, '/'); 34 char *slash = strrchr(path, '/');
28 35
36#if ENABLE_PLATFORM_MINGW32
37 const char *start = has_dos_drive_prefix(path) ? path+2 : path;
38
39 if (!slash)
40 slash = strrchr(path, '\\');
41
42 if (!slash || (slash == start && !slash[1]))
43 return (char*)path;
44#else
29 if (!slash || (slash == path && !slash[1])) 45 if (!slash || (slash == path && !slash[1]))
30 return (char*)path; 46 return (char*)path;
47#endif
31 48
32 return slash + 1; 49 return slash + 1;
33} 50}
@@ -42,9 +59,20 @@ char* FAST_FUNC bb_get_last_path_component_strip(char *path)
42{ 59{
43 char *slash = last_char_is(path, '/'); 60 char *slash = last_char_is(path, '/');
44 61
62#if ENABLE_PLATFORM_MINGW32
63 const char *start = has_dos_drive_prefix(path) ? path+2 : path;
64
65 if (!slash)
66 slash = last_char_is(path, '\\');
67
68 if (slash)
69 while ((*slash == '/' || *slash == '\\') && slash != start)
70 *slash-- = '\0';
71#else
45 if (slash) 72 if (slash)
46 while (*slash == '/' && slash != path) 73 while (*slash == '/' && slash != path)
47 *slash-- = '\0'; 74 *slash-- = '\0';
75#endif
48 76
49 return bb_get_last_path_component_nostrip(path); 77 return bb_get_last_path_component_nostrip(path);
50} 78}
diff --git a/libbb/get_line_from_file.c b/libbb/get_line_from_file.c
index 903ff1fb6..929bab78a 100644
--- a/libbb/get_line_from_file.c
+++ b/libbb/get_line_from_file.c
@@ -59,6 +59,10 @@ char* FAST_FUNC xmalloc_fgetline(FILE *file)
59 59
60 if (i && c[--i] == '\n') 60 if (i && c[--i] == '\n')
61 c[i] = '\0'; 61 c[i] = '\0';
62#if ENABLE_PLATFORM_MINGW32
63 if (i && c[--i] == '\r')
64 c[i] = '\0';
65#endif
62 66
63 return c; 67 return c;
64} 68}
diff --git a/libbb/human_readable.c b/libbb/human_readable.c
index 09221a186..3199ede6e 100644
--- a/libbb/human_readable.c
+++ b/libbb/human_readable.c
@@ -38,7 +38,7 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val,
38 if (val == 0) 38 if (val == 0)
39 return "0"; 39 return "0";
40 40
41 fmt = "%llu"; 41 fmt = "%"LL_FMT"u";
42 if (block_size > 1) 42 if (block_size > 1)
43 val *= block_size; 43 val *= block_size;
44 frac = 0; 44 frac = 0;
@@ -52,7 +52,7 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val,
52 while ((val >= 1024) 52 while ((val >= 1024)
53 /* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */ 53 /* && (u < unit_chars + sizeof(unit_chars) - 1) - always true */
54 ) { 54 ) {
55 fmt = "%llu.%u%c"; 55 fmt = "%"LL_FMT"u.%u%c";
56 u++; 56 u++;
57 frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024; 57 frac = (((unsigned)val % 1024) * 10 + 1024/2) / 1024;
58 val /= 1024; 58 val /= 1024;
@@ -67,7 +67,7 @@ const char* FAST_FUNC make_human_readable_str(unsigned long long val,
67 if (frac >= 5) { 67 if (frac >= 5) {
68 ++val; 68 ++val;
69 } 69 }
70 fmt = "%llu%*c"; 70 fmt = "%"LL_FMT"u%*c";
71 frac = 1; 71 frac = 1;
72 } 72 }
73#endif 73#endif
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index 0a888fa70..0b4e326d8 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -356,7 +356,7 @@ int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
356/* Put 'command_ps[cursor]', cursor++. 356/* Put 'command_ps[cursor]', cursor++.
357 * Advance cursor on screen. If we reached right margin, scroll text up 357 * Advance cursor on screen. If we reached right margin, scroll text up
358 * and remove terminal margin effect by printing 'next_char' */ 358 * and remove terminal margin effect by printing 'next_char' */
359#define HACK_FOR_WRONG_WIDTH 1 359#define HACK_FOR_WRONG_WIDTH 1 && !ENABLE_PLATFORM_MINGW32
360static void put_cur_glyph_and_inc_cursor(void) 360static void put_cur_glyph_and_inc_cursor(void)
361{ 361{
362 CHAR_T c = command_ps[cursor]; 362 CHAR_T c = command_ps[cursor];
@@ -419,6 +419,42 @@ static void put_cur_glyph_and_inc_cursor(void)
419 } 419 }
420} 420}
421 421
422#if ENABLE_PLATFORM_MINGW32
423static void inc_cursor(void)
424{
425 CHAR_T c = command_ps[cursor];
426 unsigned width = 0;
427 int ofs_to_right;
428
429 /* advance cursor */
430 cursor++;
431 if (unicode_status == UNICODE_ON) {
432 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
433 c = adjust_width_and_validate_wc(&cmdedit_x, c);
434 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
435 } else {
436 cmdedit_x++;
437 }
438
439 ofs_to_right = cmdedit_x - cmdedit_termw;
440 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
441 /* cursor remains on this line */
442 printf(ESC"[1C");
443 }
444
445 if (ofs_to_right >= 0) {
446 /* we go to the next line */
447 printf(ESC"[1B");
448 bb_putchar('\r');
449 cmdedit_y++;
450 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
451 width = 0;
452 }
453 cmdedit_x = width;
454 }
455}
456#endif
457
422/* Move to end of line (by printing all chars till the end) */ 458/* Move to end of line (by printing all chars till the end) */
423static void put_till_end_and_adv_cursor(void) 459static void put_till_end_and_adv_cursor(void)
424{ 460{
@@ -478,6 +514,7 @@ static void input_backward(unsigned num)
478 514
479 if (cmdedit_x >= num) { 515 if (cmdedit_x >= num) {
480 cmdedit_x -= num; 516 cmdedit_x -= num;
517#if !ENABLE_PLATFORM_MINGW32
481 if (num <= 4) { 518 if (num <= 4) {
482 /* This is longer by 5 bytes on x86. 519 /* This is longer by 5 bytes on x86.
483 * Also gets miscompiled for ARM users 520 * Also gets miscompiled for ARM users
@@ -490,6 +527,7 @@ static void input_backward(unsigned num)
490 } while (--num); 527 } while (--num);
491 return; 528 return;
492 } 529 }
530#endif
493 printf(ESC"[%uD", num); 531 printf(ESC"[%uD", num);
494 return; 532 return;
495 } 533 }
@@ -628,7 +666,11 @@ static void input_backspace(void)
628static void input_forward(void) 666static void input_forward(void)
629{ 667{
630 if (cursor < command_len) 668 if (cursor < command_len)
669#if !ENABLE_PLATFORM_MINGW32
631 put_cur_glyph_and_inc_cursor(); 670 put_cur_glyph_and_inc_cursor();
671#else
672 inc_cursor();
673#endif
632} 674}
633 675
634#if ENABLE_FEATURE_TAB_COMPLETION 676#if ENABLE_FEATURE_TAB_COMPLETION
@@ -639,6 +681,14 @@ static void input_forward(void)
639//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>, 681//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
640//not "foo bar <cursor>... 682//not "foo bar <cursor>...
641 683
684# if ENABLE_PLATFORM_MINGW32
685/* use case-insensitive comparisons for filenames */
686# define is_prefixed_with(s, k) is_prefixed_with_case(s, k)
687# define qsort_string_vector(s, c) qsort_string_vector_case(s, c)
688# define strcmp(s, t) strcasecmp(s, t)
689# define strncmp(s, t, n) strncasecmp(s, t, n)
690# endif
691
642static void free_tab_completion_data(void) 692static void free_tab_completion_data(void)
643{ 693{
644 if (matches) { 694 if (matches) {
@@ -655,8 +705,12 @@ static void add_match(char *matched)
655 while (*p) { 705 while (*p) {
656 /* ESC attack fix: drop any string with control chars */ 706 /* ESC attack fix: drop any string with control chars */
657 if (*p < ' ' 707 if (*p < ' '
708# if !ENABLE_PLATFORM_MINGW32
658 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f) 709 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f)
659 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f) 710 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f)
711# else
712 || *p == 0x7f
713# endif
660 ) { 714 ) {
661 free(matched); 715 free(matched);
662 return; 716 return;
@@ -748,7 +802,7 @@ static int path_parse(char ***p)
748 tmp = (char*)pth; 802 tmp = (char*)pth;
749 npth = 1; /* path component count */ 803 npth = 1; /* path component count */
750 while (1) { 804 while (1) {
751 tmp = strchr(tmp, ':'); 805 tmp = strchr(tmp, PATH_SEP);
752 if (!tmp) 806 if (!tmp)
753 break; 807 break;
754 tmp++; 808 tmp++;
@@ -761,7 +815,7 @@ static int path_parse(char ***p)
761 res[0] = tmp = xstrdup(pth); 815 res[0] = tmp = xstrdup(pth);
762 npth = 1; 816 npth = 1;
763 while (1) { 817 while (1) {
764 tmp = strchr(tmp, ':'); 818 tmp = strchr(tmp, PATH_SEP);
765 if (!tmp) 819 if (!tmp)
766 break; 820 break;
767 *tmp++ = '\0'; /* ':' -> '\0' */ 821 *tmp++ = '\0'; /* ':' -> '\0' */
@@ -847,6 +901,12 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
847 if (stat(found, &st) && lstat(found, &st)) 901 if (stat(found, &st) && lstat(found, &st))
848 goto cont; /* hmm, remove in progress? */ 902 goto cont; /* hmm, remove in progress? */
849 903
904# if ENABLE_PLATFORM_MINGW32
905 if (type == FIND_EXE_ONLY && !S_ISDIR(st.st_mode) &&
906 !file_is_executable(found))
907 goto cont;
908# endif
909
850 /* Save only name */ 910 /* Save only name */
851 len = strlen(name_found); 911 len = strlen(name_found);
852 found = xrealloc(found, len + 2); /* +2: for slash and NUL */ 912 found = xrealloc(found, len + 2); /* +2: for slash and NUL */
@@ -1217,7 +1277,11 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1217 for (cp = chosen_match; *cp; cp++) { 1277 for (cp = chosen_match; *cp; cp++) {
1218 unsigned n; 1278 unsigned n;
1219 for (n = 1; n < num_matches; n++) { 1279 for (n = 1; n < num_matches; n++) {
1280# if !ENABLE_PLATFORM_MINGW32
1220 if (matches[n][cp - chosen_match] != *cp) { 1281 if (matches[n][cp - chosen_match] != *cp) {
1282# else
1283 if (tolower(matches[n][cp - chosen_match]) != tolower(*cp)) {
1284# endif
1221 goto stop; 1285 goto stop;
1222 } 1286 }
1223 } 1287 }
@@ -1290,6 +1354,13 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1290 free(match_buf); 1354 free(match_buf);
1291} 1355}
1292 1356
1357# if ENABLE_PLATFORM_MINGW32
1358# undef is_prefixed_with
1359# undef qsort_string_vector
1360# undef strcmp
1361# undef strncmp
1362# endif
1363
1293#endif /* FEATURE_TAB_COMPLETION */ 1364#endif /* FEATURE_TAB_COMPLETION */
1294 1365
1295 1366
@@ -2332,7 +2403,7 @@ static void sigaction2(int sig, struct sigaction *act)
2332 */ 2403 */
2333int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize) 2404int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize)
2334{ 2405{
2335 int len, n; 2406 int len IF_NOT_PLATFORM_MINGW32(, n);
2336 int timeout; 2407 int timeout;
2337#if ENABLE_FEATURE_TAB_COMPLETION 2408#if ENABLE_FEATURE_TAB_COMPLETION
2338 smallint lastWasTab = 0; 2409 smallint lastWasTab = 0;
@@ -2342,15 +2413,23 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2342 smallint vi_cmdmode = 0; 2413 smallint vi_cmdmode = 0;
2343#endif 2414#endif
2344 struct termios initial_settings; 2415 struct termios initial_settings;
2416#if !ENABLE_PLATFORM_MINGW32
2345 struct termios new_settings; 2417 struct termios new_settings;
2418#endif
2346 char read_key_buffer[KEYCODE_BUFFER_SIZE]; 2419 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2347 2420
2348 INIT_S(); 2421 INIT_S();
2349 2422
2423#if !ENABLE_PLATFORM_MINGW32
2350 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0 2424 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0
2351 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */ 2425 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */
2352 ); 2426 );
2353 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) { 2427 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) {
2428#else
2429 initial_settings.c_cc[VINTR] = CTRL('C');
2430 initial_settings.c_cc[VEOF] = CTRL('D');
2431 if (!isatty(0) || !isatty(1)) {
2432#endif
2354 /* Happens when e.g. stty -echo was run before. 2433 /* Happens when e.g. stty -echo was run before.
2355 * But if ICANON is not set, we don't come here. 2434 * But if ICANON is not set, we don't come here.
2356 * (example: interactive python ^Z-backgrounded, 2435 * (example: interactive python ^Z-backgrounded,
@@ -2403,7 +2482,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2403#endif 2482#endif
2404#define command command_must_not_be_used 2483#define command command_must_not_be_used
2405 2484
2485#if !ENABLE_PLATFORM_MINGW32
2406 tcsetattr_stdin_TCSANOW(&new_settings); 2486 tcsetattr_stdin_TCSANOW(&new_settings);
2487#endif
2407 2488
2408#if ENABLE_USERNAME_OR_HOMEDIR 2489#if ENABLE_USERNAME_OR_HOMEDIR
2409 { 2490 {
@@ -2456,6 +2537,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2456 } 2537 }
2457#endif 2538#endif
2458 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); 2539 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2540#if ENABLE_PLATFORM_MINGW32
2541 /* scroll to cursor position on any keypress */
2542 if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
2543 move_cursor_row(0);
2544#endif
2459 2545
2460#if ENABLE_FEATURE_REVERSE_SEARCH 2546#if ENABLE_FEATURE_REVERSE_SEARCH
2461 again: 2547 again:
@@ -2889,8 +2975,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2889 free_tab_completion_data(); 2975 free_tab_completion_data();
2890#endif 2976#endif
2891 2977
2978#if !ENABLE_PLATFORM_MINGW32
2892 /* restore initial_settings */ 2979 /* restore initial_settings */
2893 tcsetattr_stdin_TCSANOW(&initial_settings); 2980 tcsetattr_stdin_TCSANOW(&initial_settings);
2981#endif
2894#if ENABLE_FEATURE_EDITING_WINCH 2982#if ENABLE_FEATURE_EDITING_WINCH
2895 /* restore SIGWINCH handler */ 2983 /* restore SIGWINCH handler */
2896 sigaction_set(SIGWINCH, &S.SIGWINCH_handler); 2984 sigaction_set(SIGWINCH, &S.SIGWINCH_handler);
diff --git a/libbb/make_directory.c b/libbb/make_directory.c
index 9b03bb8d0..ef0a8acd8 100644
--- a/libbb/make_directory.c
+++ b/libbb/make_directory.c
@@ -49,11 +49,40 @@ int FAST_FUNC bb_make_directory(char *path, long mode, int flags)
49 } 49 }
50 50
51 org_mask = cur_mask = (mode_t)-1L; 51 org_mask = cur_mask = (mode_t)-1L;
52#if ENABLE_PLATFORM_MINGW32
53 /* normalise path separators, path is already assumed writable */
54 convert_slashes(path);
55#endif
52 s = path; 56 s = path;
53 while (1) { 57 while (1) {
54 c = '\0'; 58 c = '\0';
55 59
56 if (flags & FILEUTILS_RECUR) { /* Get the parent */ 60 if (flags & FILEUTILS_RECUR) { /* Get the parent */
61#if ENABLE_PLATFORM_MINGW32
62 if (s == path && *s && s[1] == ':') {
63 /* skip drive letter */
64 s += 2;
65 }
66 else if (s == path && s[0] == '/' && s[1] == '/' ) {
67 /* skip UNC server and share */
68 int count = 0;
69 s += 2;
70 while (*s) {
71 if (*s == '/') {
72 do {
73 ++s;
74 } while (*s == '/');
75 if (++count == 2) {
76 --s;
77 break;
78 }
79 }
80 else {
81 ++s;
82 }
83 }
84 }
85#endif
57 /* Bypass leading non-'/'s and then subsequent '/'s */ 86 /* Bypass leading non-'/'s and then subsequent '/'s */
58 while (*s) { 87 while (*s) {
59 if (*s == '/') { 88 if (*s == '/') {
diff --git a/libbb/messages.c b/libbb/messages.c
index 6914d5701..f5bbd3e32 100644
--- a/libbb/messages.c
+++ b/libbb/messages.c
@@ -27,7 +27,9 @@ const char bb_msg_standard_output[] ALIGN1 = "standard output";
27 27
28const char bb_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF"; 28const char bb_hexdigits_upcase[] ALIGN1 = "0123456789ABCDEF";
29 29
30#if !ENABLE_PLATFORM_MINGW32
30const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH; 31const char bb_busybox_exec_path[] ALIGN1 = CONFIG_BUSYBOX_EXEC_PATH;
32#endif
31const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; 33const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
32/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, 34/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin,
33 * but I want to save a few bytes here. Check libbb.h before changing! */ 35 * but I want to save a few bytes here. Check libbb.h before changing! */
diff --git a/libbb/mode_string.c b/libbb/mode_string.c
index 5ffd5683e..6c5c08acd 100644
--- a/libbb/mode_string.c
+++ b/libbb/mode_string.c
@@ -17,7 +17,7 @@
17#endif 17#endif
18 18
19#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \ 19#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
20 || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \ 20 || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 && S_IFBLK != 0030000 ) \
21 || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \ 21 || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
22 || ( S_IFIFO != 0010000 ) 22 || ( S_IFIFO != 0010000 )
23#warning mode type bitflag value assumption(s) violated! falling back to larger version 23#warning mode type bitflag value assumption(s) violated! falling back to larger version
diff --git a/libbb/printable_string.c b/libbb/printable_string.c
index a814fd03c..2e8895a4f 100644
--- a/libbb/printable_string.c
+++ b/libbb/printable_string.c
@@ -42,7 +42,7 @@ const char* FAST_FUNC printable_string2(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 af3ad86ff..e6892d7ff 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -74,6 +74,7 @@ const char* FAST_FUNC get_cached_groupname(gid_t gid)
74 return get_cached(&groupname, gid, gid2group_utoa); 74 return get_cached(&groupname, gid, gid2group_utoa);
75} 75}
76 76
77#if !ENABLE_PLATFORM_MINGW32
77 78
78#define PROCPS_BUFSIZE 1024 79#define PROCPS_BUFSIZE 1024
79 80
@@ -621,6 +622,8 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
621 } 622 }
622} 623}
623 624
625#endif /* ENABLE_PLATFORM_MINGW32 */
626
624/* from kernel: 627/* from kernel:
625 // pid comm S ppid pgid sid tty_nr tty_pgrp flg 628 // pid comm S ppid pgid sid tty_nr tty_pgrp flg
626 sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ 629 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/u_signal_names.c b/libbb/u_signal_names.c
index f7d598c7a..036ad0038 100644
--- a/libbb/u_signal_names.c
+++ b/libbb/u_signal_names.c
@@ -27,6 +27,10 @@
27 27
28#include "libbb.h" 28#include "libbb.h"
29 29
30#if ENABLE_PLATFORM_MINGW32
31# undef SIGPIPE
32#endif
33
30/* Believe it or not, but some arches have more than 32 SIGs! 34/* Believe it or not, but some arches have more than 32 SIGs!
31 * HPPA: SIGSTKFLT == 36. */ 35 * HPPA: SIGSTKFLT == 36. */
32 36
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index c0bea0ed2..569a6fc34 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -150,6 +150,7 @@ void FAST_FUNC run_noexec_applet_and_exit(int a, const char *name, char **argv)
150 * Higher-level code, hiding optional NOFORK/NOEXEC trickery. 150 * Higher-level code, hiding optional NOFORK/NOEXEC trickery.
151 */ 151 */
152 152
153#if !ENABLE_PLATFORM_MINGW32
153/* This does a fork/exec in one call, using vfork(). Returns PID of new child, 154/* This does a fork/exec in one call, using vfork(). Returns PID of new child,
154 * -1 for failure. Runs argv[0], searching path if that has no / in it. */ 155 * -1 for failure. Runs argv[0], searching path if that has no / in it. */
155pid_t FAST_FUNC spawn(char **argv) 156pid_t FAST_FUNC spawn(char **argv)
@@ -191,6 +192,7 @@ pid_t FAST_FUNC spawn(char **argv)
191 } 192 }
192 return pid; 193 return pid;
193} 194}
195#endif
194 196
195/* Die with an error message if we can't spawn a child process. */ 197/* Die with an error message if we can't spawn a child process. */
196pid_t FAST_FUNC xspawn(char **argv) 198pid_t FAST_FUNC xspawn(char **argv)
@@ -211,6 +213,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
211 if (APPLET_IS_NOFORK(a)) 213 if (APPLET_IS_NOFORK(a))
212 return run_nofork_applet(a, argv); 214 return run_nofork_applet(a, argv);
213# if BB_MMU /* NOEXEC needs fork(), thus this is done only on MMU machines: */ 215# if BB_MMU /* NOEXEC needs fork(), thus this is done only on MMU machines: */
216# if !ENABLE_PLATFORM_MINGW32 /* and then only if not on Microsoft Windows */
214 if (APPLET_IS_NOEXEC(a)) { 217 if (APPLET_IS_NOEXEC(a)) {
215 fflush_all(); 218 fflush_all();
216 rc = fork(); 219 rc = fork();
@@ -220,6 +223,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
220 /* child */ 223 /* child */
221 run_noexec_applet_and_exit(a, argv[0], argv); 224 run_noexec_applet_and_exit(a, argv[0], argv);
222 } 225 }
226# endif
223# endif 227# endif
224 } 228 }
225#endif 229#endif
@@ -227,6 +231,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
227 return wait4pid(rc); 231 return wait4pid(rc);
228} 232}
229 233
234#if !ENABLE_PLATFORM_MINGW32
230#if !BB_MMU 235#if !BB_MMU
231void FAST_FUNC re_exec(char **argv) 236void FAST_FUNC re_exec(char **argv)
232{ 237{
@@ -313,3 +318,4 @@ void FAST_FUNC bb_sanitize_stdio(void)
313{ 318{
314 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL); 319 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
315} 320}
321#endif /* !MINGW32 */
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 39e56b223..a6127508b 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -68,6 +68,7 @@ int FAST_FUNC setsockopt_bindtodevice(int fd UNUSED_PARAM,
68} 68}
69#endif 69#endif
70 70
71#if !ENABLE_PLATFORM_MINGW32
71static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen)) 72static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen))
72{ 73{
73 len_and_sockaddr lsa; 74 len_and_sockaddr lsa;
@@ -96,16 +97,17 @@ len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd)
96{ 97{
97 return get_lsa(fd, getpeername); 98 return get_lsa(fd, getpeername);
98} 99}
100#endif
99 101
100void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) 102void FAST_FUNC xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen)
101{ 103{
102 if (connect(s, s_addr, addrlen) < 0) { 104 if (connect(s, saddr, addrlen) < 0) {
103 if (ENABLE_FEATURE_CLEAN_UP) 105 if (ENABLE_FEATURE_CLEAN_UP)
104 close(s); 106 close(s);
105 if (s_addr->sa_family == AF_INET) 107 if (saddr->sa_family == AF_INET)
106 bb_perror_msg_and_die("%s (%s)", 108 bb_perror_msg_and_die("%s (%s)",
107 "can't connect to remote host", 109 "can't connect to remote host",
108 inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); 110 inet_ntoa(((struct sockaddr_in *)saddr)->sin_addr));
109 bb_perror_msg_and_die("can't connect to remote host"); 111 bb_perror_msg_and_die("can't connect to remote host");
110 } 112 }
111} 113}
@@ -352,6 +354,10 @@ int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type)
352#if ENABLE_FEATURE_IPV6 354#if ENABLE_FEATURE_IPV6
353 fd = socket(AF_INET6, sock_type, 0); 355 fd = socket(AF_INET6, sock_type, 0);
354 if (fd >= 0) { 356 if (fd >= 0) {
357#if ENABLE_PLATFORM_MINGW32
358 DWORD buffer = 0;
359 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &buffer, sizeof(DWORD));
360#endif
355 family = AF_INET6; 361 family = AF_INET6;
356 goto done; 362 goto done;
357 } 363 }
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index b4d512bd6..57bda6204 100644
--- a/libbb/xfuncs.c
+++ b/libbb/xfuncs.c
@@ -268,6 +268,7 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh
268 int err; 268 int err;
269 int close_me = -1; 269 int close_me = -1;
270 270
271#if !ENABLE_PLATFORM_MINGW32
271 if (fd == -1) { 272 if (fd == -1) {
272 if (isatty(STDOUT_FILENO)) 273 if (isatty(STDOUT_FILENO))
273 fd = STDOUT_FILENO; 274 fd = STDOUT_FILENO;
@@ -280,6 +281,7 @@ int FAST_FUNC get_terminal_width_height(int fd, unsigned *width, unsigned *heigh
280 else 281 else
281 close_me = fd = open("/dev/tty", O_RDONLY); 282 close_me = fd = open("/dev/tty", O_RDONLY);
282 } 283 }
284#endif
283 285
284 win.ws_row = 0; 286 win.ws_row = 0;
285 win.ws_col = 0; 287 win.ws_col = 0;
@@ -303,6 +305,7 @@ int FAST_FUNC get_terminal_width(int fd)
303 return width; 305 return width;
304} 306}
305 307
308#if !ENABLE_PLATFORM_MINGW32
306int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp) 309int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp)
307{ 310{
308 return tcsetattr(STDIN_FILENO, TCSANOW, tp); 311 return tcsetattr(STDIN_FILENO, TCSANOW, tp);
@@ -383,6 +386,7 @@ int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags)
383 get_termios_and_make_raw(fd, &newterm, oldterm, flags); 386 get_termios_and_make_raw(fd, &newterm, oldterm, flags);
384 return tcsetattr(fd, TCSANOW, &newterm); 387 return tcsetattr(fd, TCSANOW, &newterm);
385} 388}
389#endif
386 390
387pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options) 391pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options)
388{ 392{
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index 6cc60f6c0..a0db2b86e 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -513,6 +513,7 @@ void FAST_FUNC xfstat(int fd, struct stat *stat_buf, const char *errmsg)
513 bb_simple_perror_msg_and_die(errmsg); 513 bb_simple_perror_msg_and_die(errmsg);
514} 514}
515 515
516#if !ENABLE_PLATFORM_MINGW32
516// selinux_or_die() - die if SELinux is disabled. 517// selinux_or_die() - die if SELinux is disabled.
517void FAST_FUNC selinux_or_die(void) 518void FAST_FUNC selinux_or_die(void)
518{ 519{
@@ -695,3 +696,4 @@ void FAST_FUNC xvfork_parent_waits_and_exits(void)
695 } 696 }
696 /* Child continues */ 697 /* Child continues */
697} 698}
699#endif /* !ENABLE_PLATFORM_MINGW32 */
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index ead30e499..9ae70de99 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -64,7 +64,7 @@ char* FAST_FUNC xmalloc_follow_symlinks(const char *path)
64 linkpath = xmalloc_readlink(buf); 64 linkpath = xmalloc_readlink(buf);
65 if (!linkpath) { 65 if (!linkpath) {
66 /* not a symlink, or doesn't exist */ 66 /* not a symlink, or doesn't exist */
67 if (errno == EINVAL || errno == ENOENT) 67 if (errno == EINVAL || errno == ENOENT || (ENABLE_PLATFORM_MINGW32 && errno == ENOSYS))
68 return buf; 68 return buf;
69 goto free_buf_ret_null; 69 goto free_buf_ret_null;
70 } 70 }
diff --git a/miscutils/bbconfig.c b/miscutils/bbconfig.c
index fe02516a8..077e03c5d 100644
--- a/miscutils/bbconfig.c
+++ b/miscutils/bbconfig.c
@@ -35,6 +35,7 @@
35#include "libbb.h" 35#include "libbb.h"
36#include "bbconfigopts.h" 36#include "bbconfigopts.h"
37#if ENABLE_FEATURE_COMPRESS_BBCONFIG 37#if ENABLE_FEATURE_COMPRESS_BBCONFIG
38#define BB_ARCHIVE_PUBLIC
38# include "bb_archive.h" 39# include "bb_archive.h"
39# include "bbconfigopts_bz2.h" 40# include "bbconfigopts_bz2.h"
40#endif 41#endif
diff --git a/miscutils/dc.c b/miscutils/dc.c
index 17fdda8fd..0d09f5e2b 100644
--- a/miscutils/dc.c
+++ b/miscutils/dc.c
@@ -17,7 +17,7 @@ typedef unsigned long data_t;
17#define DATA_FMT "l" 17#define DATA_FMT "l"
18#else 18#else
19typedef unsigned long long data_t; 19typedef unsigned long long data_t;
20#define DATA_FMT "ll" 20#define DATA_FMT LL_FMT
21#endif 21#endif
22 22
23struct globals { 23struct globals {
diff --git a/miscutils/iconv.c b/miscutils/iconv.c
new file mode 100644
index 000000000..828c38213
--- /dev/null
+++ b/miscutils/iconv.c
@@ -0,0 +1,1846 @@
1/*
2 * iconv implementation using Win32 API to convert.
3 *
4 * This file is placed in the public domain.
5 */
6
7/*
8 * This code was obtained from:
9 *
10 * https://github.com/win-iconv/win-iconv
11 *
12 * Modified for busybox-w32 by Ronald M Yorston. These modifications
13 * are also dedicated to the public domain.
14 */
15
16//config:config ICONV
17//config: bool "iconv"
18//config: default y
19//config: help
20//config: 'iconv' converts text between character encodings.
21
22//applet:IF_ICONV(APPLET(iconv, BB_DIR_USR_BIN, BB_SUID_DROP))
23
24//kbuild:lib-$(CONFIG_ICONV) += iconv.o
25
26//usage:#define iconv_trivial_usage
27//usage: "[-lc] [-o outfile] -f from-enc -t to-enc [FILE]..."
28//usage:#define iconv_full_usage "\n\n"
29//usage: "Convert text between character encodings\n"
30//usage: "\n -l List all known character encodings"
31//usage: "\n -c Silently discard characters that cannot be converted"
32//usage: "\n -o Use outfile for output"
33//usage: "\n -f Use from-enc for input characters"
34//usage: "\n -t Use to-enc for output characters"
35
36#include "libbb.h"
37
38/* WORKAROUND: */
39#define GetProcAddressA GetProcAddress
40
41#define MB_CHAR_MAX 16
42
43#define UNICODE_MODE_BOM_DONE 1
44#define UNICODE_MODE_SWAPPED 2
45
46#define FLAG_USE_BOM 1
47#define FLAG_TRANSLIT 2 /* //TRANSLIT */
48#define FLAG_IGNORE 4 /* //IGNORE */
49
50typedef unsigned char uchar;
51typedef unsigned short ushort;
52typedef unsigned int uint;
53
54typedef void* iconv_t;
55
56iconv_t iconv_open(const char *tocode, const char *fromcode);
57int iconv_close(iconv_t cd);
58size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
59
60typedef struct compat_t compat_t;
61typedef struct csconv_t csconv_t;
62typedef struct rec_iconv_t rec_iconv_t;
63
64typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode);
65typedef int (*f_iconv_close)(iconv_t cd);
66typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
67typedef int* (*f_errno)(void);
68typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
69typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
70typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize);
71typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize);
72
73#define COMPAT_IN 1
74#define COMPAT_OUT 2
75
76/* unicode mapping for compatibility with other conversion table. */
77struct compat_t {
78 uint in;
79 uint out;
80 uint flag;
81};
82
83struct csconv_t {
84 int codepage;
85 int flags;
86 f_mbtowc mbtowc;
87 f_wctomb wctomb;
88 f_mblen mblen;
89 f_flush flush;
90 DWORD mode;
91 compat_t *compat;
92};
93
94struct rec_iconv_t {
95 iconv_t cd;
96 f_iconv_close iconv_close;
97 f_iconv iconv;
98 f_errno _errno;
99 csconv_t from;
100 csconv_t to;
101};
102
103static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);
104static int win_iconv_close(iconv_t cd);
105static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
106
107static int load_mlang(void);
108static int make_csconv(const char *name, csconv_t *cv);
109static int name_to_codepage(const char *name);
110static uint utf16_to_ucs4(const ushort *wbuf);
111static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize);
112static int mbtowc_flags(int codepage);
113static int must_use_null_useddefaultchar(int codepage);
114static int seterror(int err);
115
116static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
117static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
118static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
119static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize);
120static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize);
121
122static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
123static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
124static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
125static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
126static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
127static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
128static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
129static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
130static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
131static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
132static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize);
133
134static struct {
135 int codepage;
136 const char *name;
137} codepage_alias[] = {
138 {65001, "CP65001"},
139 {65001, "UTF8"},
140 {65001, "UTF-8"},
141
142 {1200, "CP1200"},
143 {1200, "UTF16LE"},
144 {1200, "UTF-16LE"},
145 {1200, "UCS2LE"},
146 {1200, "UCS-2LE"},
147 {1200, "UCS-2-INTERNAL"},
148
149 {1201, "CP1201"},
150 {1201, "UTF16BE"},
151 {1201, "UTF-16BE"},
152 {1201, "UCS2BE"},
153 {1201, "UCS-2BE"},
154 {1201, "unicodeFFFE"},
155
156 {12000, "CP12000"},
157 {12000, "UTF32LE"},
158 {12000, "UTF-32LE"},
159 {12000, "UCS4LE"},
160 {12000, "UCS-4LE"},
161
162 {12001, "CP12001"},
163 {12001, "UTF32BE"},
164 {12001, "UTF-32BE"},
165 {12001, "UCS4BE"},
166 {12001, "UCS-4BE"},
167
168#ifndef GLIB_COMPILATION
169 /*
170 * Default is big endian.
171 * See rfc2781 4.3 Interpreting text labelled as UTF-16.
172 */
173 {1201, "UTF16"},
174 {1201, "UTF-16"},
175 {1201, "UCS2"},
176 {1201, "UCS-2"},
177 {12001, "UTF32"},
178 {12001, "UTF-32"},
179 {12001, "UCS-4"},
180 {12001, "UCS4"},
181#else
182 /* Default is little endian, because the platform is */
183 {1200, "UTF16"},
184 {1200, "UTF-16"},
185 {1200, "UCS2"},
186 {1200, "UCS-2"},
187 {12000, "UTF32"},
188 {12000, "UTF-32"},
189 {12000, "UCS4"},
190 {12000, "UCS-4"},
191#endif
192
193 /* copy from libiconv `iconv -l` */
194 /* !IsValidCodePage(367) */
195 {20127, "ANSI_X3.4-1968"},
196 {20127, "ANSI_X3.4-1986"},
197 {20127, "ASCII"},
198 {20127, "CP367"},
199 {20127, "IBM367"},
200 {20127, "ISO-IR-6"},
201 {20127, "ISO646-US"},
202 {20127, "ISO_646.IRV:1991"},
203 {20127, "US"},
204 {20127, "US-ASCII"},
205 {20127, "CSASCII"},
206
207 /* !IsValidCodePage(819) */
208 {1252, "CP819"},
209 {1252, "IBM819"},
210 {28591, "ISO-8859-1"},
211 {28591, "ISO-IR-100"},
212 {28591, "ISO8859-1"},
213 {28591, "ISO_8859-1"},
214 {28591, "ISO_8859-1:1987"},
215 {28591, "L1"},
216 {28591, "LATIN1"},
217 {28591, "CSISOLATIN1"},
218
219 {1250, "CP1250"},
220 {1250, "MS-EE"},
221 {1250, "WINDOWS-1250"},
222
223 {1251, "CP1251"},
224 {1251, "MS-CYRL"},
225 {1251, "WINDOWS-1251"},
226
227 {1252, "CP1252"},
228 {1252, "MS-ANSI"},
229 {1252, "WINDOWS-1252"},
230
231 {1253, "CP1253"},
232 {1253, "MS-GREEK"},
233 {1253, "WINDOWS-1253"},
234
235 {1254, "CP1254"},
236 {1254, "MS-TURK"},
237 {1254, "WINDOWS-1254"},
238
239 {1255, "CP1255"},
240 {1255, "MS-HEBR"},
241 {1255, "WINDOWS-1255"},
242
243 {1256, "CP1256"},
244 {1256, "MS-ARAB"},
245 {1256, "WINDOWS-1256"},
246
247 {1257, "CP1257"},
248 {1257, "WINBALTRIM"},
249 {1257, "WINDOWS-1257"},
250
251 {1258, "CP1258"},
252 {1258, "WINDOWS-1258"},
253
254 {850, "850"},
255 {850, "CP850"},
256 {850, "IBM850"},
257 {850, "CSPC850MULTILINGUAL"},
258
259 /* !IsValidCodePage(862) */
260 {862, "862"},
261 {862, "CP862"},
262 {862, "IBM862"},
263 {862, "CSPC862LATINHEBREW"},
264
265 {866, "866"},
266 {866, "CP866"},
267 {866, "IBM866"},
268 {866, "CSIBM866"},
269
270 /* !IsValidCodePage(154) */
271 {154, "CP154"},
272 {154, "CYRILLIC-ASIAN"},
273 {154, "PT154"},
274 {154, "PTCP154"},
275 {154, "CSPTCP154"},
276
277 /* !IsValidCodePage(1133) */
278 {1133, "CP1133"},
279 {1133, "IBM-CP1133"},
280
281 {874, "CP874"},
282 {874, "WINDOWS-874"},
283
284 /* !IsValidCodePage(51932) */
285 {51932, "CP51932"},
286 {51932, "MS51932"},
287 {51932, "WINDOWS-51932"},
288 {51932, "EUC-JP"},
289
290 {932, "CP932"},
291 {932, "MS932"},
292 {932, "SHIFFT_JIS"},
293 {932, "SHIFFT_JIS-MS"},
294 {932, "SJIS"},
295 {932, "SJIS-MS"},
296 {932, "SJIS-OPEN"},
297 {932, "SJIS-WIN"},
298 {932, "WINDOWS-31J"},
299 {932, "WINDOWS-932"},
300 {932, "CSWINDOWS31J"},
301
302 {50221, "CP50221"},
303 {50221, "ISO-2022-JP"},
304 {50221, "ISO-2022-JP-MS"},
305 {50221, "ISO2022-JP"},
306 {50221, "ISO2022-JP-MS"},
307 {50221, "MS50221"},
308 {50221, "WINDOWS-50221"},
309
310 {936, "CP936"},
311 {936, "GBK"},
312 {936, "MS936"},
313 {936, "WINDOWS-936"},
314
315 {950, "CP950"},
316 {950, "BIG5"},
317 {950, "BIG5HKSCS"},
318 {950, "BIG5-HKSCS"},
319
320 {949, "CP949"},
321 {949, "UHC"},
322 {949, "EUC-KR"},
323
324 {1361, "CP1361"},
325 {1361, "JOHAB"},
326
327 {437, "437"},
328 {437, "CP437"},
329 {437, "IBM437"},
330 {437, "CSPC8CODEPAGE437"},
331
332 {737, "CP737"},
333
334 {775, "CP775"},
335 {775, "IBM775"},
336 {775, "CSPC775BALTIC"},
337
338 {852, "852"},
339 {852, "CP852"},
340 {852, "IBM852"},
341 {852, "CSPCP852"},
342
343 /* !IsValidCodePage(853) */
344 {853, "CP853"},
345
346 {855, "855"},
347 {855, "CP855"},
348 {855, "IBM855"},
349 {855, "CSIBM855"},
350
351 {857, "857"},
352 {857, "CP857"},
353 {857, "IBM857"},
354 {857, "CSIBM857"},
355
356 /* !IsValidCodePage(858) */
357 {858, "CP858"},
358
359 {860, "860"},
360 {860, "CP860"},
361 {860, "IBM860"},
362 {860, "CSIBM860"},
363
364 {861, "861"},
365 {861, "CP-IS"},
366 {861, "CP861"},
367 {861, "IBM861"},
368 {861, "CSIBM861"},
369
370 {863, "863"},
371 {863, "CP863"},
372 {863, "IBM863"},
373 {863, "CSIBM863"},
374
375 {864, "CP864"},
376 {864, "IBM864"},
377 {864, "CSIBM864"},
378
379 {865, "865"},
380 {865, "CP865"},
381 {865, "IBM865"},
382 {865, "CSIBM865"},
383
384 {869, "869"},
385 {869, "CP-GR"},
386 {869, "CP869"},
387 {869, "IBM869"},
388 {869, "CSIBM869"},
389
390 /* !IsValidCodePage(1152) */
391 {1125, "CP1125"},
392
393 /*
394 * Code Page Identifiers
395 * http://msdn2.microsoft.com/en-us/library/ms776446.aspx
396 */
397 {37, "IBM037"}, /* IBM EBCDIC US-Canada */
398 {437, "IBM437"}, /* OEM United States */
399 {500, "IBM500"}, /* IBM EBCDIC International */
400 {708, "ASMO-708"}, /* Arabic (ASMO 708) */
401 /* 709 Arabic (ASMO-449+, BCON V4) */
402 /* 710 Arabic - Transparent Arabic */
403 {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */
404 {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */
405 {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */
406 {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */
407 {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */
408 {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */
409 {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */
410 {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */
411 {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */
412 {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */
413 {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */
414 {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */
415 {864, "IBM864"}, /* OEM Arabic; Arabic (864) */
416 {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */
417 {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */
418 {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */
419 {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */
420 {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */
421 {875, "cp875"}, /* IBM EBCDIC Greek Modern */
422 {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */
423 {932, "shift-jis"}, /* alternative name for it */
424 {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */
425 {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */
426 {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */
427 {950, "big5hkscs"}, /* ANSI/OEM Traditional Chinese (Hong Kong SAR); Chinese Traditional (Big5-HKSCS) */
428 {950, "big5-hkscs"}, /* alternative name for it */
429 {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */
430 {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */
431 {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */
432 {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */
433 {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */
434 {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */
435 {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */
436 {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */
437 {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */
438 {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */
439 {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */
440 {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */
441 {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */
442 {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */
443 {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */
444 {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */
445 {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */
446 {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */
447 {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */
448 {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */
449 {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */
450 {1361, "Johab"}, /* Korean (Johab) */
451 {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */
452 {10001, "x-mac-japanese"}, /* Japanese (Mac) */
453 {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */
454 {10003, "x-mac-korean"}, /* Korean (Mac) */
455 {10004, "x-mac-arabic"}, /* Arabic (Mac) */
456 {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */
457 {10006, "x-mac-greek"}, /* Greek (Mac) */
458 {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */
459 {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */
460 {10010, "x-mac-romanian"}, /* Romanian (Mac) */
461 {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */
462 {10021, "x-mac-thai"}, /* Thai (Mac) */
463 {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */
464 {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */
465 {10081, "x-mac-turkish"}, /* Turkish (Mac) */
466 {10082, "x-mac-croatian"}, /* Croatian (Mac) */
467 {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */
468 {20001, "x-cp20001"}, /* TCA Taiwan */
469 {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */
470 {20003, "x-cp20003"}, /* IBM5550 Taiwan */
471 {20004, "x-cp20004"}, /* TeleText Taiwan */
472 {20005, "x-cp20005"}, /* Wang Taiwan */
473 {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */
474 {20106, "x-IA5-German"}, /* IA5 German (7-bit) */
475 {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */
476 {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */
477 {20127, "us-ascii"}, /* US-ASCII (7-bit) */
478 {20261, "x-cp20261"}, /* T.61 */
479 {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */
480 {20273, "IBM273"}, /* IBM EBCDIC Germany */
481 {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */
482 {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */
483 {20280, "IBM280"}, /* IBM EBCDIC Italy */
484 {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */
485 {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */
486 {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */
487 {20297, "IBM297"}, /* IBM EBCDIC France */
488 {20420, "IBM420"}, /* IBM EBCDIC Arabic */
489 {20423, "IBM423"}, /* IBM EBCDIC Greek */
490 {20424, "IBM424"}, /* IBM EBCDIC Hebrew */
491 {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */
492 {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */
493 {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */
494 {20871, "IBM871"}, /* IBM EBCDIC Icelandic */
495 {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */
496 {20905, "IBM905"}, /* IBM EBCDIC Turkish */
497 {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */
498 {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */
499 {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */
500 {20949, "x-cp20949"}, /* Korean Wansung */
501 {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */
502 /* 21027 (deprecated) */
503 {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */
504 {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */
505 {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */
506 {28591, "iso_8859-1"},
507 {28591, "iso_8859_1"},
508 {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */
509 {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */
510 {28592, "iso_8859-2"},
511 {28592, "iso_8859_2"},
512 {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */
513 {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */
514 {28593, "iso_8859-3"},
515 {28593, "iso_8859_3"},
516 {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */
517 {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */
518 {28594, "iso_8859-4"},
519 {28594, "iso_8859_4"},
520 {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */
521 {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */
522 {28595, "iso_8859-5"},
523 {28595, "iso_8859_5"},
524 {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */
525 {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */
526 {28596, "iso_8859-6"},
527 {28596, "iso_8859_6"},
528 {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */
529 {28597, "iso8859-7"}, /* ISO 8859-7 Greek */
530 {28597, "iso_8859-7"},
531 {28597, "iso_8859_7"},
532 {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */
533 {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */
534 {28598, "iso_8859-8"},
535 {28598, "iso_8859_8"},
536 {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */
537 {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */
538 {28599, "iso_8859-9"},
539 {28599, "iso_8859_9"},
540 {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */
541 {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */
542 {28603, "iso_8859-13"},
543 {28603, "iso_8859_13"},
544 {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */
545 {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */
546 {28605, "iso_8859-15"},
547 {28605, "iso_8859_15"},
548 {29001, "x-Europa"}, /* Europa 3 */
549 {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */
550 {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */
551 {38598, "iso_8859-8-i"},
552 {38598, "iso_8859_8-i"},
553 {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */
554 {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */
555 {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */
556 {50225, "iso-2022-kr"}, /* ISO 2022 Korean */
557 {50225, "iso2022-kr"}, /* ISO 2022 Korean */
558 {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */
559 /* 50229 ISO 2022 Traditional Chinese */
560 /* 50930 EBCDIC Japanese (Katakana) Extended */
561 /* 50931 EBCDIC US-Canada and Japanese */
562 /* 50933 EBCDIC Korean Extended and Korean */
563 /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */
564 /* 50936 EBCDIC Simplified Chinese */
565 /* 50937 EBCDIC US-Canada and Traditional Chinese */
566 /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */
567 {51932, "euc-jp"}, /* EUC Japanese */
568 {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */
569 {51949, "euc-kr"}, /* EUC Korean */
570 /* 51950 EUC Traditional Chinese */
571 {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */
572 {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */
573 {57002, "x-iscii-de"}, /* ISCII Devanagari */
574 {57003, "x-iscii-be"}, /* ISCII Bengali */
575 {57004, "x-iscii-ta"}, /* ISCII Tamil */
576 {57005, "x-iscii-te"}, /* ISCII Telugu */
577 {57006, "x-iscii-as"}, /* ISCII Assamese */
578 {57007, "x-iscii-or"}, /* ISCII Oriya */
579 {57008, "x-iscii-ka"}, /* ISCII Kannada */
580 {57009, "x-iscii-ma"}, /* ISCII Malayalam */
581 {57010, "x-iscii-gu"}, /* ISCII Gujarati */
582 {57011, "x-iscii-pa"}, /* ISCII Punjabi */
583
584 {0, NULL}
585};
586
587/*
588 * SJIS SHIFTJIS table CP932 table
589 * ---- --------------------------- --------------------------------
590 * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS
591 * 7E U+203E OVERLINE U+007E TILDE
592 * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR
593 * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS
594 * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE
595 * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO
596 * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS
597 * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN
598 * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN
599 * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN
600 *
601 * EUC-JP and ISO-2022-JP should be compatible with CP932.
602 *
603 * Kernel and MLang have different Unicode mapping table. Make sure
604 * which API is used.
605 */
606static compat_t cp932_compat[] = {
607 {0x00A5, 0x005C, COMPAT_OUT},
608 {0x203E, 0x007E, COMPAT_OUT},
609 {0x2014, 0x2015, COMPAT_OUT},
610 {0x301C, 0xFF5E, COMPAT_OUT},
611 {0x2016, 0x2225, COMPAT_OUT},
612 {0x2212, 0xFF0D, COMPAT_OUT},
613 {0x00A2, 0xFFE0, COMPAT_OUT},
614 {0x00A3, 0xFFE1, COMPAT_OUT},
615 {0x00AC, 0xFFE2, COMPAT_OUT},
616 {0, 0, 0}
617};
618
619static compat_t cp20932_compat[] = {
620 {0x00A5, 0x005C, COMPAT_OUT},
621 {0x203E, 0x007E, COMPAT_OUT},
622 {0x2014, 0x2015, COMPAT_OUT},
623 {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN},
624 {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN},
625 {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN},
626 {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN},
627 {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN},
628 {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN},
629 {0, 0, 0}
630};
631
632static compat_t *cp51932_compat = cp932_compat;
633
634/* cp20932_compat for kernel. cp932_compat for mlang. */
635static compat_t *cp5022x_compat = cp932_compat;
636
637typedef HRESULT (WINAPI *CONVERTINETSTRING)(
638 LPDWORD lpdwMode,
639 DWORD dwSrcEncoding,
640 DWORD dwDstEncoding,
641 LPCSTR lpSrcStr,
642 LPINT lpnSrcSize,
643 LPBYTE lpDstStr,
644 LPINT lpnDstSize
645);
646typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)(
647 LPDWORD lpdwMode,
648 DWORD dwSrcEncoding,
649 LPCSTR lpSrcStr,
650 LPINT lpnMultiCharCount,
651 LPWSTR lpDstStr,
652 LPINT lpnWideCharCount
653);
654typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)(
655 LPDWORD lpdwMode,
656 DWORD dwEncoding,
657 LPCWSTR lpSrcStr,
658 LPINT lpnWideCharCount,
659 LPSTR lpDstStr,
660 LPINT lpnMultiCharCount
661);
662typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)(
663 DWORD dwSrcEncoding,
664 DWORD dwDstEncoding
665);
666typedef HRESULT (WINAPI *LCIDTORFC1766A)(
667 LCID Locale,
668 LPSTR pszRfc1766,
669 int nChar
670);
671typedef HRESULT (WINAPI *LCIDTORFC1766W)(
672 LCID Locale,
673 LPWSTR pszRfc1766,
674 int nChar
675);
676typedef HRESULT (WINAPI *RFC1766TOLCIDA)(
677 LCID *pLocale,
678 LPSTR pszRfc1766
679);
680typedef HRESULT (WINAPI *RFC1766TOLCIDW)(
681 LCID *pLocale,
682 LPWSTR pszRfc1766
683);
684static CONVERTINETSTRING ConvertINetString;
685static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode;
686static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte;
687static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable;
688static LCIDTORFC1766A LcidToRfc1766A;
689static RFC1766TOLCIDA Rfc1766ToLcidA;
690
691static int
692load_mlang(void)
693{
694 HMODULE h;
695 if (ConvertINetString != NULL)
696 return TRUE;
697 h = LoadLibrary(TEXT("mlang.dll"));
698 if (!h)
699 return FALSE;
700 ConvertINetString = (CONVERTINETSTRING)GetProcAddressA(h, "ConvertINetString");
701 ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddressA(h, "ConvertINetMultiByteToUnicode");
702 ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddressA(h, "ConvertINetUnicodeToMultiByte");
703 IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddressA(h, "IsConvertINetStringAvailable");
704 LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddressA(h, "LcidToRfc1766A");
705 Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddressA(h, "Rfc1766ToLcidA");
706 return TRUE;
707}
708
709iconv_t
710iconv_open(const char *tocode, const char *fromcode)
711{
712 rec_iconv_t *cd;
713
714 cd = (rec_iconv_t *)xzalloc(sizeof(rec_iconv_t));
715
716 /* reset the errno to prevent reporting wrong error code.
717 * 0 for unsorted error. */
718 errno = 0;
719 if (win_iconv_open(cd, tocode, fromcode))
720 return (iconv_t)cd;
721
722 free(cd);
723
724 return (iconv_t)(-1);
725}
726
727int
728iconv_close(iconv_t _cd)
729{
730 rec_iconv_t *cd = (rec_iconv_t *)_cd;
731 int r = cd->iconv_close(cd->cd);
732 int e = *(cd->_errno());
733 free(cd);
734 errno = e;
735 return r;
736}
737
738size_t
739iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
740{
741 rec_iconv_t *cd = (rec_iconv_t *)_cd;
742 size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft);
743 errno = *(cd->_errno());
744 return r;
745}
746
747static int
748win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)
749{
750 if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to))
751 return FALSE;
752 cd->iconv_close = win_iconv_close;
753 cd->iconv = win_iconv;
754 cd->_errno = _errno;
755 cd->cd = (iconv_t)cd;
756 return TRUE;
757}
758
759static int
760win_iconv_close(iconv_t cd UNUSED_PARAM)
761{
762 return 0;
763}
764
765static size_t
766win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
767{
768 rec_iconv_t *cd = (rec_iconv_t *)_cd;
769 ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */
770 int insize;
771 int outsize;
772 int wsize;
773 DWORD frommode;
774 DWORD tomode;
775 uint wc;
776 compat_t *cp;
777 int i;
778
779 if (inbuf == NULL || *inbuf == NULL)
780 {
781 if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL)
782 {
783 tomode = cd->to.mode;
784 outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft);
785 if (outsize == -1)
786 {
787 if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG)
788 {
789 outsize = 0;
790 }
791 else
792 {
793 cd->to.mode = tomode;
794 return (size_t)(-1);
795 }
796 }
797 *outbuf += outsize;
798 *outbytesleft -= outsize;
799 }
800 cd->from.mode = 0;
801 cd->to.mode = 0;
802 return 0;
803 }
804
805 while (*inbytesleft != 0)
806 {
807 frommode = cd->from.mode;
808 tomode = cd->to.mode;
809 wsize = MB_CHAR_MAX;
810
811 insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize);
812 if (insize == -1)
813 {
814 if (cd->to.flags & FLAG_IGNORE)
815 {
816 cd->from.mode = frommode;
817 insize = 1;
818 wsize = 0;
819 }
820 else
821 {
822 cd->from.mode = frommode;
823 return (size_t)(-1);
824 }
825 }
826
827 if (wsize == 0)
828 {
829 *inbuf += insize;
830 *inbytesleft -= insize;
831 continue;
832 }
833
834 if (cd->from.compat != NULL)
835 {
836 wc = utf16_to_ucs4(wbuf);
837 cp = cd->from.compat;
838 for (i = 0; cp[i].in != 0; ++i)
839 {
840 if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc)
841 {
842 ucs4_to_utf16(cp[i].in, wbuf, &wsize);
843 break;
844 }
845 }
846 }
847
848 if (cd->to.compat != NULL)
849 {
850 wc = utf16_to_ucs4(wbuf);
851 cp = cd->to.compat;
852 for (i = 0; cp[i].in != 0; ++i)
853 {
854 if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc)
855 {
856 ucs4_to_utf16(cp[i].out, wbuf, &wsize);
857 break;
858 }
859 }
860 }
861
862 outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft);
863 if (outsize == -1)
864 {
865 if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG)
866 {
867 cd->to.mode = tomode;
868 outsize = 0;
869 }
870 else
871 {
872 cd->from.mode = frommode;
873 cd->to.mode = tomode;
874 return (size_t)(-1);
875 }
876 }
877
878 *inbuf += insize;
879 *outbuf += outsize;
880 *inbytesleft -= insize;
881 *outbytesleft -= outsize;
882 }
883
884 return 0;
885}
886
887static int
888make_csconv(const char *_name, csconv_t *cv)
889{
890 CPINFO cpinfo;
891 int use_compat = TRUE;
892 int flag = 0;
893 char *name;
894 char *p;
895
896 name = xstrndup(_name, strlen(_name));
897 if (name == NULL)
898 return FALSE;
899
900 /* check for option "enc_name//opt1//opt2" */
901 while ((p = strrstr(name, "//")) != NULL)
902 {
903 if (_stricmp(p + 2, "nocompat") == 0)
904 use_compat = FALSE;
905 else if (_stricmp(p + 2, "translit") == 0)
906 flag |= FLAG_TRANSLIT;
907 else if (_stricmp(p + 2, "ignore") == 0)
908 flag |= FLAG_IGNORE;
909 *p = 0;
910 }
911
912 cv->mode = 0;
913 cv->flags = flag;
914 cv->mblen = NULL;
915 cv->flush = NULL;
916 cv->compat = NULL;
917 cv->codepage = name_to_codepage(name);
918 if (cv->codepage == 1200 || cv->codepage == 1201)
919 {
920 cv->mbtowc = utf16_mbtowc;
921 cv->wctomb = utf16_wctomb;
922 if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0 ||
923 _stricmp(name, "UCS-2") == 0 || _stricmp(name, "UCS2") == 0 ||
924 _stricmp(name,"UCS-2-INTERNAL") == 0)
925 cv->flags |= FLAG_USE_BOM;
926 }
927 else if (cv->codepage == 12000 || cv->codepage == 12001)
928 {
929 cv->mbtowc = utf32_mbtowc;
930 cv->wctomb = utf32_wctomb;
931 if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0 ||
932 _stricmp(name, "UCS-4") == 0 || _stricmp(name, "UCS4") == 0)
933 cv->flags |= FLAG_USE_BOM;
934 }
935 else if (cv->codepage == 65001)
936 {
937 cv->mbtowc = kernel_mbtowc;
938 cv->wctomb = kernel_wctomb;
939 cv->mblen = utf8_mblen;
940 }
941 else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang())
942 {
943 cv->mbtowc = iso2022jp_mbtowc;
944 cv->wctomb = iso2022jp_wctomb;
945 cv->flush = iso2022jp_flush;
946 }
947 else if (cv->codepage == 51932 && load_mlang())
948 {
949 cv->mbtowc = mlang_mbtowc;
950 cv->wctomb = mlang_wctomb;
951 cv->mblen = eucjp_mblen;
952 }
953 else if (IsValidCodePage(cv->codepage)
954 && GetCPInfo(cv->codepage, &cpinfo) != 0)
955 {
956 cv->mbtowc = kernel_mbtowc;
957 cv->wctomb = kernel_wctomb;
958 if (cpinfo.MaxCharSize == 1)
959 cv->mblen = sbcs_mblen;
960 else if (cpinfo.MaxCharSize == 2)
961 cv->mblen = dbcs_mblen;
962 else
963 cv->mblen = mbcs_mblen;
964 }
965 else
966 {
967 /* not supported */
968 free(name);
969 errno = EINVAL;
970 return FALSE;
971 }
972
973 if (use_compat)
974 {
975 switch (cv->codepage)
976 {
977 case 932: cv->compat = cp932_compat; break;
978 case 20932: cv->compat = cp20932_compat; break;
979 case 51932: cv->compat = cp51932_compat; break;
980 case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break;
981 }
982 }
983
984 free(name);
985
986 return TRUE;
987}
988
989static int
990name_to_codepage(const char *name)
991{
992 int i;
993
994 if (*name == '\0' ||
995 strcmp(name, "char") == 0)
996 return GetACP();
997 else if (strcmp(name, "wchar_t") == 0)
998 return 1200;
999 else if (_strnicmp(name, "cp", 2) == 0)
1000 return atoi(name + 2); /* CP123 */
1001 else if ('0' <= name[0] && name[0] <= '9')
1002 return atoi(name); /* 123 */
1003 else if (_strnicmp(name, "xx", 2) == 0)
1004 return atoi(name + 2); /* XX123 for debug */
1005
1006 for (i = 0; codepage_alias[i].name != NULL; ++i)
1007 if (_stricmp(name, codepage_alias[i].name) == 0)
1008 return codepage_alias[i].codepage;
1009 return -1;
1010}
1011
1012/*
1013 * http://www.faqs.org/rfcs/rfc2781.html
1014 */
1015static uint
1016utf16_to_ucs4(const ushort *wbuf)
1017{
1018 uint wc = wbuf[0];
1019 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1020 wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000;
1021 return wc;
1022}
1023
1024static void
1025ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize)
1026{
1027 if (wc < 0x10000)
1028 {
1029 wbuf[0] = wc;
1030 *wbufsize = 1;
1031 }
1032 else
1033 {
1034 wc -= 0x10000;
1035 wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF);
1036 wbuf[1] = 0xDC00 | (wc & 0x3FF);
1037 *wbufsize = 2;
1038 }
1039}
1040
1041/*
1042 * Check if codepage is one of those for which the dwFlags parameter
1043 * to MultiByteToWideChar() must be zero. Return zero or
1044 * MB_ERR_INVALID_CHARS. The docs in Platform SDK for Windows
1045 * Server 2003 R2 claims that also codepage 65001 is one of these, but
1046 * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave
1047 * out 65001 (UTF-8), and that indeed seems to be the case on XP, it
1048 * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting
1049 * from UTF-8.
1050 */
1051static int
1052mbtowc_flags(int codepage)
1053{
1054 return (codepage == 50220 || codepage == 50221 ||
1055 codepage == 50222 || codepage == 50225 ||
1056 codepage == 50227 || codepage == 50229 ||
1057 codepage == 52936 || codepage == 54936 ||
1058 (codepage >= 57002 && codepage <= 57011) ||
1059 codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS;
1060}
1061
1062/*
1063 * Check if codepage is one those for which the lpUsedDefaultChar
1064 * parameter to WideCharToMultiByte() must be NULL. The docs in
1065 * Platform SDK for Windows Server 2003 R2 claims that this is the
1066 * list below, while the MSDN docs for MSVS2008 claim that it is only
1067 * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform
1068 * SDK seems to be correct, at least for XP.
1069 */
1070static int
1071must_use_null_useddefaultchar(int codepage)
1072{
1073 return (codepage == 65000 || codepage == 65001 ||
1074 codepage == 50220 || codepage == 50221 ||
1075 codepage == 50222 || codepage == 50225 ||
1076 codepage == 50227 || codepage == 50229 ||
1077 codepage == 52936 || codepage == 54936 ||
1078 (codepage >= 57002 && codepage <= 57011) ||
1079 codepage == 42);
1080}
1081
1082static int
1083seterror(int err)
1084{
1085 errno = err;
1086 return -1;
1087}
1088
1089static int
1090sbcs_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf UNUSED_PARAM,
1091 int bufsize UNUSED_PARAM)
1092{
1093 return 1;
1094}
1095
1096static int
1097dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
1098{
1099 int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1;
1100 if (bufsize < len)
1101 return seterror(EINVAL);
1102 return len;
1103}
1104
1105static int
1106mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
1107{
1108 int len = 0;
1109
1110 if (cv->codepage == 54936) {
1111 if (buf[0] <= 0x7F)
1112 len = 1;
1113 else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
1114 bufsize >= 2 &&
1115 ((buf[1] >= 0x40 && buf[1] <= 0x7E) ||
1116 (buf[1] >= 0x80 && buf[1] <= 0xFE)))
1117 len = 2;
1118 else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
1119 bufsize >= 4 &&
1120 buf[1] >= 0x30 && buf[1] <= 0x39)
1121 len = 4;
1122 else
1123 return seterror(EINVAL);
1124 return len;
1125 }
1126 else
1127 return seterror(EINVAL);
1128}
1129
1130static int
1131utf8_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize)
1132{
1133 int len = 0;
1134
1135 if (buf[0] < 0x80) len = 1;
1136 else if ((buf[0] & 0xE0) == 0xC0) len = 2;
1137 else if ((buf[0] & 0xF0) == 0xE0) len = 3;
1138 else if ((buf[0] & 0xF8) == 0xF0) len = 4;
1139 else if ((buf[0] & 0xFC) == 0xF8) len = 5;
1140 else if ((buf[0] & 0xFE) == 0xFC) len = 6;
1141
1142 if (len == 0)
1143 return seterror(EILSEQ);
1144 else if (bufsize < len)
1145 return seterror(EINVAL);
1146 return len;
1147}
1148
1149static int
1150eucjp_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize)
1151{
1152 if (buf[0] < 0x80) /* ASCII */
1153 return 1;
1154 else if (buf[0] == 0x8E) /* JIS X 0201 */
1155 {
1156 if (bufsize < 2)
1157 return seterror(EINVAL);
1158 else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF))
1159 return seterror(EILSEQ);
1160 return 2;
1161 }
1162 else if (buf[0] == 0x8F) /* JIS X 0212 */
1163 {
1164 if (bufsize < 3)
1165 return seterror(EINVAL);
1166 else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE)
1167 || !(0xA1 <= buf[2] && buf[2] <= 0xFE))
1168 return seterror(EILSEQ);
1169 return 3;
1170 }
1171 else /* JIS X 0208 */
1172 {
1173 if (bufsize < 2)
1174 return seterror(EINVAL);
1175 else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE)
1176 || !(0xA1 <= buf[1] && buf[1] <= 0xFE))
1177 return seterror(EILSEQ);
1178 return 2;
1179 }
1180}
1181
1182static int
1183kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1184{
1185 int len;
1186
1187 len = cv->mblen(cv, buf, bufsize);
1188 if (len == -1)
1189 return -1;
1190 /* If converting from ASCII, reject 8bit
1191 * chars. MultiByteToWideChar() doesn't. Note that for ASCII we
1192 * know that the mblen function is sbcs_mblen() so len is 1.
1193 */
1194 if (cv->codepage == 20127 && buf[0] >= 0x80)
1195 return seterror(EILSEQ);
1196 *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage),
1197 (const char *)buf, len, (wchar_t *)wbuf, *wbufsize);
1198 if (*wbufsize == 0)
1199 return seterror(EILSEQ);
1200 return len;
1201}
1202
1203static int
1204kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1205{
1206 BOOL usedDefaultChar = 0;
1207 BOOL *p = NULL;
1208 int flags = 0;
1209 int len;
1210
1211 if (bufsize == 0)
1212 return seterror(E2BIG);
1213 if (!must_use_null_useddefaultchar(cv->codepage))
1214 {
1215 p = &usedDefaultChar;
1216#ifdef WC_NO_BEST_FIT_CHARS
1217 if (!(cv->flags & FLAG_TRANSLIT))
1218 flags |= WC_NO_BEST_FIT_CHARS;
1219#endif
1220 }
1221 len = WideCharToMultiByte(cv->codepage, flags,
1222 (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p);
1223 if (len == 0)
1224 {
1225 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1226 return seterror(E2BIG);
1227 return seterror(EILSEQ);
1228 }
1229 else if (usedDefaultChar && !(cv->flags & FLAG_TRANSLIT))
1230 return seterror(EILSEQ);
1231 else if (cv->mblen(cv, buf, len) != len) /* validate result */
1232 return seterror(EILSEQ);
1233 return len;
1234}
1235
1236/*
1237 * It seems that the mode (cv->mode) is fixnum.
1238 * For example, when converting iso-2022-jp(cp50221) to unicode:
1239 * in ascii sequence: mode=0xC42C0000
1240 * in jisx0208 sequence: mode=0xC42C0001
1241 * "C42C" is same for each convert session.
1242 * It should be: ((codepage-1)<<16)|state
1243 */
1244static int
1245mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1246{
1247 int len;
1248 int insize;
1249 HRESULT hr;
1250
1251 len = cv->mblen(cv, buf, bufsize);
1252 if (len == -1)
1253 return -1;
1254 insize = len;
1255 hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage,
1256 (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize);
1257 if (hr != S_OK || insize != len)
1258 return seterror(EILSEQ);
1259 return len;
1260}
1261
1262static int
1263mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1264{
1265 char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */
1266 int tmpsize = MB_CHAR_MAX;
1267 int insize = wbufsize;
1268 HRESULT hr;
1269
1270 hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage,
1271 (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize);
1272 if (hr != S_OK || insize != wbufsize)
1273 return seterror(EILSEQ);
1274 else if (bufsize < tmpsize)
1275 return seterror(E2BIG);
1276 else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize)
1277 return seterror(EILSEQ);
1278 memcpy(buf, tmpbuf, tmpsize);
1279 return tmpsize;
1280}
1281
1282static int
1283utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1284{
1285 int codepage = cv->codepage;
1286
1287 /* swap endian: 1200 <-> 1201 */
1288 if (cv->mode & UNICODE_MODE_SWAPPED)
1289 codepage ^= 1;
1290
1291 if (bufsize < 2)
1292 return seterror(EINVAL);
1293 if (codepage == 1200) /* little endian */
1294 wbuf[0] = (buf[1] << 8) | buf[0];
1295 else if (codepage == 1201) /* big endian */
1296 wbuf[0] = (buf[0] << 8) | buf[1];
1297
1298 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1299 {
1300 cv->mode |= UNICODE_MODE_BOM_DONE;
1301 if (wbuf[0] == 0xFFFE)
1302 {
1303 cv->mode |= UNICODE_MODE_SWAPPED;
1304 *wbufsize = 0;
1305 return 2;
1306 }
1307 else if (wbuf[0] == 0xFEFF)
1308 {
1309 *wbufsize = 0;
1310 return 2;
1311 }
1312 }
1313
1314 if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF)
1315 return seterror(EILSEQ);
1316 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1317 {
1318 if (bufsize < 4)
1319 return seterror(EINVAL);
1320 if (codepage == 1200) /* little endian */
1321 wbuf[1] = (buf[3] << 8) | buf[2];
1322 else if (codepage == 1201) /* big endian */
1323 wbuf[1] = (buf[2] << 8) | buf[3];
1324 if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF))
1325 return seterror(EILSEQ);
1326 *wbufsize = 2;
1327 return 4;
1328 }
1329 *wbufsize = 1;
1330 return 2;
1331}
1332
1333static int
1334utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1335{
1336 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1337 {
1338 int r;
1339
1340 cv->mode |= UNICODE_MODE_BOM_DONE;
1341 if (bufsize < 2)
1342 return seterror(E2BIG);
1343 if (cv->codepage == 1200) /* little endian */
1344 memcpy(buf, "\xFF\xFE", 2);
1345 else if (cv->codepage == 1201) /* big endian */
1346 memcpy(buf, "\xFE\xFF", 2);
1347
1348 r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2);
1349 if (r == -1)
1350 return -1;
1351 return r + 2;
1352 }
1353
1354 if (bufsize < 2)
1355 return seterror(E2BIG);
1356 if (cv->codepage == 1200) /* little endian */
1357 {
1358 buf[0] = (wbuf[0] & 0x00FF);
1359 buf[1] = (wbuf[0] & 0xFF00) >> 8;
1360 }
1361 else if (cv->codepage == 1201) /* big endian */
1362 {
1363 buf[0] = (wbuf[0] & 0xFF00) >> 8;
1364 buf[1] = (wbuf[0] & 0x00FF);
1365 }
1366 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1367 {
1368 if (bufsize < 4)
1369 return seterror(E2BIG);
1370 if (cv->codepage == 1200) /* little endian */
1371 {
1372 buf[2] = (wbuf[1] & 0x00FF);
1373 buf[3] = (wbuf[1] & 0xFF00) >> 8;
1374 }
1375 else if (cv->codepage == 1201) /* big endian */
1376 {
1377 buf[2] = (wbuf[1] & 0xFF00) >> 8;
1378 buf[3] = (wbuf[1] & 0x00FF);
1379 }
1380 return 4;
1381 }
1382 return 2;
1383}
1384
1385static int
1386utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1387{
1388 int codepage = cv->codepage;
1389 uint wc = 0xD800;
1390
1391 /* swap endian: 12000 <-> 12001 */
1392 if (cv->mode & UNICODE_MODE_SWAPPED)
1393 codepage ^= 1;
1394
1395 if (bufsize < 4)
1396 return seterror(EINVAL);
1397 if (codepage == 12000) /* little endian */
1398 wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
1399 else if (codepage == 12001) /* big endian */
1400 wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
1401
1402 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1403 {
1404 cv->mode |= UNICODE_MODE_BOM_DONE;
1405 if (wc == 0xFFFE0000)
1406 {
1407 cv->mode |= UNICODE_MODE_SWAPPED;
1408 *wbufsize = 0;
1409 return 4;
1410 }
1411 else if (wc == 0x0000FEFF)
1412 {
1413 *wbufsize = 0;
1414 return 4;
1415 }
1416 }
1417
1418 if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc)
1419 return seterror(EILSEQ);
1420 ucs4_to_utf16(wc, wbuf, wbufsize);
1421 return 4;
1422}
1423
1424static int
1425utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1426{
1427 uint wc;
1428
1429 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1430 {
1431 int r;
1432
1433 cv->mode |= UNICODE_MODE_BOM_DONE;
1434 if (bufsize < 4)
1435 return seterror(E2BIG);
1436 if (cv->codepage == 12000) /* little endian */
1437 memcpy(buf, "\xFF\xFE\x00\x00", 4);
1438 else if (cv->codepage == 12001) /* big endian */
1439 memcpy(buf, "\x00\x00\xFE\xFF", 4);
1440
1441 r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4);
1442 if (r == -1)
1443 return -1;
1444 return r + 4;
1445 }
1446
1447 if (bufsize < 4)
1448 return seterror(E2BIG);
1449 wc = utf16_to_ucs4(wbuf);
1450 if (cv->codepage == 12000) /* little endian */
1451 {
1452 buf[0] = wc & 0x000000FF;
1453 buf[1] = (wc & 0x0000FF00) >> 8;
1454 buf[2] = (wc & 0x00FF0000) >> 16;
1455 buf[3] = (wc & 0xFF000000) >> 24;
1456 }
1457 else if (cv->codepage == 12001) /* big endian */
1458 {
1459 buf[0] = (wc & 0xFF000000) >> 24;
1460 buf[1] = (wc & 0x00FF0000) >> 16;
1461 buf[2] = (wc & 0x0000FF00) >> 8;
1462 buf[3] = wc & 0x000000FF;
1463 }
1464 return 4;
1465}
1466
1467/*
1468 * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
1469 * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow
1470 * 1 byte Kana)
1471 * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte
1472 * Kana - SO/SI)
1473 *
1474 * MultiByteToWideChar() and WideCharToMultiByte() behave differently
1475 * depending on Windows version. On XP, WideCharToMultiByte() doesn't
1476 * terminate result sequence with ascii escape. But Vista does.
1477 * Use MLang instead.
1478 */
1479
1480#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift))
1481#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF)
1482#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF)
1483
1484#define ISO2022_SI 0
1485#define ISO2022_SO 1
1486
1487/* shift in */
1488static const char iso2022_SI_seq[] = "\x0F";
1489/* shift out */
1490static const char iso2022_SO_seq[] = "\x0E";
1491
1492typedef struct iso2022_esc_t iso2022_esc_t;
1493struct iso2022_esc_t {
1494 const char *esc;
1495 int esc_len;
1496 int len;
1497 int cs;
1498};
1499
1500#define ISO2022JP_CS_ASCII 0
1501#define ISO2022JP_CS_JISX0201_ROMAN 1
1502#define ISO2022JP_CS_JISX0201_KANA 2
1503#define ISO2022JP_CS_JISX0208_1978 3
1504#define ISO2022JP_CS_JISX0208_1983 4
1505#define ISO2022JP_CS_JISX0212 5
1506
1507static iso2022_esc_t iso2022jp_esc[] = {
1508 {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII},
1509 {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN},
1510 {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA},
1511 {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */
1512 {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983},
1513 {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212},
1514 {NULL, 0, 0, 0}
1515};
1516
1517static int
1518iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1519{
1520 iso2022_esc_t *iesc = iso2022jp_esc;
1521 char tmp[MB_CHAR_MAX];
1522 int insize;
1523 HRESULT hr;
1524 DWORD dummy = 0;
1525 int len;
1526 int esc_len;
1527 int cs;
1528 int shift;
1529 int i;
1530
1531 if (buf[0] == 0x1B)
1532 {
1533 for (i = 0; iesc[i].esc != NULL; ++i)
1534 {
1535 esc_len = iesc[i].esc_len;
1536 if (bufsize < esc_len)
1537 {
1538 if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0)
1539 return seterror(EINVAL);
1540 }
1541 else
1542 {
1543 if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0)
1544 {
1545 cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI);
1546 *wbufsize = 0;
1547 return esc_len;
1548 }
1549 }
1550 }
1551 /* not supported escape sequence */
1552 return seterror(EILSEQ);
1553 }
1554 else if (buf[0] == iso2022_SO_seq[0])
1555 {
1556 cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO);
1557 *wbufsize = 0;
1558 return 1;
1559 }
1560 else if (buf[0] == iso2022_SI_seq[0])
1561 {
1562 cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI);
1563 *wbufsize = 0;
1564 return 1;
1565 }
1566
1567 cs = ISO2022_MODE_CS(cv->mode);
1568 shift = ISO2022_MODE_SHIFT(cv->mode);
1569
1570 /* reset the mode for informal sequence */
1571 if (buf[0] < 0x20)
1572 {
1573 cs = ISO2022JP_CS_ASCII;
1574 shift = ISO2022_SI;
1575 }
1576
1577 len = iesc[cs].len;
1578 if (bufsize < len)
1579 return seterror(EINVAL);
1580 for (i = 0; i < len; ++i)
1581 if (!(buf[i] < 0x80))
1582 return seterror(EILSEQ);
1583 esc_len = iesc[cs].esc_len;
1584 memcpy(tmp, iesc[cs].esc, esc_len);
1585 if (shift == ISO2022_SO)
1586 {
1587 memcpy(tmp + esc_len, iso2022_SO_seq, 1);
1588 esc_len += 1;
1589 }
1590 memcpy(tmp + esc_len, buf, len);
1591
1592 if ((cv->codepage == 50220 || cv->codepage == 50221
1593 || cv->codepage == 50222) && shift == ISO2022_SO)
1594 {
1595 /* XXX: shift-out cannot be used for mbtowc (both kernel and
1596 * mlang) */
1597 esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len;
1598 memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len);
1599 memcpy(tmp + esc_len, buf, len);
1600 }
1601
1602 insize = len + esc_len;
1603 hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage,
1604 (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize);
1605 if (hr != S_OK || insize != len + esc_len)
1606 return seterror(EILSEQ);
1607
1608 /* Check for conversion error. Assuming defaultChar is 0x3F. */
1609 /* ascii should be converted from ascii */
1610 if (wbuf[0] == buf[0]
1611 && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
1612 return seterror(EILSEQ);
1613
1614 /* reset the mode for informal sequence */
1615 if (cv->mode != ISO2022_MODE(cs, shift))
1616 cv->mode = ISO2022_MODE(cs, shift);
1617
1618 return len;
1619}
1620
1621static int
1622iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1623{
1624 iso2022_esc_t *iesc = iso2022jp_esc;
1625 char tmp[MB_CHAR_MAX];
1626 int tmpsize = MB_CHAR_MAX;
1627 int insize = wbufsize;
1628 HRESULT hr;
1629 DWORD dummy = 0;
1630 int len;
1631 int esc_len;
1632 int cs;
1633 int shift;
1634 int i;
1635
1636 /*
1637 * MultiByte = [escape sequence] + character + [escape sequence]
1638 *
1639 * Whether trailing escape sequence is added depends on which API is
1640 * used (kernel or MLang, and its version).
1641 */
1642 hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage,
1643 (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize);
1644 if (hr != S_OK || insize != wbufsize)
1645 return seterror(EILSEQ);
1646 else if (bufsize < tmpsize)
1647 return seterror(E2BIG);
1648
1649 if (tmpsize == 1)
1650 {
1651 cs = ISO2022JP_CS_ASCII;
1652 esc_len = 0;
1653 }
1654 else
1655 {
1656 for (i = 1; iesc[i].esc != NULL; ++i)
1657 {
1658 esc_len = iesc[i].esc_len;
1659 if (strncmp(tmp, iesc[i].esc, esc_len) == 0)
1660 {
1661 cs = iesc[i].cs;
1662 break;
1663 }
1664 }
1665 if (iesc[i].esc == NULL)
1666 /* not supported escape sequence */
1667 return seterror(EILSEQ);
1668 }
1669
1670 shift = ISO2022_SI;
1671 if (tmp[esc_len] == iso2022_SO_seq[0])
1672 {
1673 shift = ISO2022_SO;
1674 esc_len += 1;
1675 }
1676
1677 len = iesc[cs].len;
1678
1679 /* Check for converting error. Assuming defaultChar is 0x3F. */
1680 /* ascii should be converted from ascii */
1681 if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80))
1682 return seterror(EILSEQ);
1683 else if (tmpsize < esc_len + len)
1684 return seterror(EILSEQ);
1685
1686 if (cv->mode == ISO2022_MODE(cs, shift))
1687 {
1688 /* remove escape sequence */
1689 if (esc_len != 0)
1690 memmove(tmp, tmp + esc_len, len);
1691 esc_len = 0;
1692 }
1693 else
1694 {
1695 if (cs == ISO2022JP_CS_ASCII)
1696 {
1697 esc_len = iesc[ISO2022JP_CS_ASCII].esc_len;
1698 memmove(tmp + esc_len, tmp, len);
1699 memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len);
1700 }
1701 if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO)
1702 {
1703 /* shift-in before changing to other mode */
1704 memmove(tmp + 1, tmp, len + esc_len);
1705 memcpy(tmp, iso2022_SI_seq, 1);
1706 esc_len += 1;
1707 }
1708 }
1709
1710 if (bufsize < len + esc_len)
1711 return seterror(E2BIG);
1712 memcpy(buf, tmp, len + esc_len);
1713 cv->mode = ISO2022_MODE(cs, shift);
1714 return len + esc_len;
1715}
1716
1717static int
1718iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize)
1719{
1720 iso2022_esc_t *iesc = iso2022jp_esc;
1721 int esc_len;
1722
1723 if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
1724 {
1725 esc_len = 0;
1726 if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
1727 esc_len += 1;
1728 if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
1729 esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
1730 if (bufsize < esc_len)
1731 return seterror(E2BIG);
1732
1733 esc_len = 0;
1734 if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
1735 {
1736 memcpy(buf, iso2022_SI_seq, 1);
1737 esc_len += 1;
1738 }
1739 if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
1740 {
1741 memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc,
1742 iesc[ISO2022JP_CS_ASCII].esc_len);
1743 esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
1744 }
1745 return esc_len;
1746 }
1747 return 0;
1748}
1749
1750static void process_file(iconv_t cd, FILE *in, FILE *out)
1751{
1752 char inbuf[BUFSIZ];
1753 char outbuf[BUFSIZ];
1754 const char *pin;
1755 char *pout;
1756 size_t inbytesleft;
1757 size_t outbytesleft;
1758 size_t rest = 0;
1759 size_t r;
1760
1761 while ((inbytesleft=fread(inbuf+rest, 1, sizeof(inbuf)-rest, in)) != 0
1762 || rest != 0) {
1763 inbytesleft += rest;
1764 pin = inbuf;
1765 pout = outbuf;
1766 outbytesleft = sizeof(outbuf);
1767 r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);
1768 fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out);
1769 if (r == (size_t)(-1) && errno != E2BIG &&
1770 (errno != EINVAL || feof(in)))
1771 bb_perror_msg_and_die("conversion error");
1772 memmove(inbuf, pin, inbytesleft);
1773 rest = inbytesleft;
1774 }
1775 pout = outbuf;
1776 outbytesleft = sizeof(outbuf);
1777 r = iconv(cd, NULL, NULL, &pout, &outbytesleft);
1778 fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out);
1779 if (r == (size_t)(-1))
1780 bb_perror_msg_and_die("conversion error");
1781}
1782
1783int iconv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1784int iconv_main(int argc, char **argv)
1785{
1786 char *fromcode = NULL;
1787 char *tocode = NULL;
1788 int i;
1789 iconv_t cd;
1790 FILE *in = stdin;
1791 FILE *out = stdout;
1792 int ignore = 0;
1793
1794 while ((i = getopt(argc, argv, "f:t:lco:")) != -1) {
1795 switch (i) {
1796 case 'l':
1797 for (i = 0; codepage_alias[i].name != NULL; ++i)
1798 printf("%s\n", codepage_alias[i].name);
1799 return 0;
1800
1801 case 'f':
1802 fromcode = optarg;
1803 break;
1804
1805 case 't':
1806 tocode = optarg;
1807 break;
1808
1809 case 'c':
1810 ignore = 1;
1811 break;
1812
1813 case 'o':
1814 out = xfopen(optarg, "wb");
1815 break;
1816
1817 default:
1818 bb_show_usage();
1819 }
1820 }
1821
1822 if (fromcode == NULL || tocode == NULL)
1823 bb_show_usage();
1824
1825 if (ignore)
1826 tocode = xasprintf("%s//IGNORE", tocode);
1827
1828 cd = iconv_open(tocode, fromcode);
1829 if (cd == (iconv_t)(-1))
1830 bb_perror_msg_and_die("iconv_open error");
1831
1832 if (optind == argc ||
1833 (optind == argc-1 && strcmp(argv[optind], "-") == 0)) {
1834 process_file(cd, in, out);
1835 }
1836 else {
1837 for (i=optind; i<argc; ++i) {
1838 in = xfopen(argv[i], "rb");
1839 process_file(cd, in, out);
1840 fclose(in);
1841 }
1842 }
1843
1844 iconv_close(cd);
1845 return 0;
1846}
diff --git a/miscutils/less.c b/miscutils/less.c
index 223c2558d..ad23b7d0d 100644
--- a/miscutils/less.c
+++ b/miscutils/less.c
@@ -145,6 +145,10 @@
145 145
146#include <sched.h> /* sched_yield() */ 146#include <sched.h> /* sched_yield() */
147 147
148#if ENABLE_PLATFORM_MINGW32
149#include <conio.h>
150#endif
151
148#include "libbb.h" 152#include "libbb.h"
149#include "common_bufsiz.h" 153#include "common_bufsiz.h"
150#if ENABLE_FEATURE_LESS_REGEXP 154#if ENABLE_FEATURE_LESS_REGEXP
@@ -236,7 +240,9 @@ struct globals {
236 smallint winsize_err; 240 smallint winsize_err;
237#endif 241#endif
238 smallint terminated; 242 smallint terminated;
243#if !ENABLE_PLATFORM_MINGW32
239 struct termios term_orig, term_less; 244 struct termios term_orig, term_less;
245#endif
240 char kbd_input[KEYCODE_BUFFER_SIZE]; 246 char kbd_input[KEYCODE_BUFFER_SIZE];
241}; 247};
242#define G (*ptr_to_globals) 248#define G (*ptr_to_globals)
@@ -298,7 +304,9 @@ struct globals {
298static void set_tty_cooked(void) 304static void set_tty_cooked(void)
299{ 305{
300 fflush_all(); 306 fflush_all();
307#if !ENABLE_PLATFORM_MINGW32
301 tcsetattr(kbd_fd, TCSANOW, &term_orig); 308 tcsetattr(kbd_fd, TCSANOW, &term_orig);
309#endif
302} 310}
303 311
304/* Move the cursor to a position (x,y), where (0,0) is the 312/* Move the cursor to a position (x,y), where (0,0) is the
@@ -575,6 +583,11 @@ static void read_lines(void)
575 last_line_pos = 0; 583 last_line_pos = 0;
576 break; 584 break;
577 } 585 }
586#if ENABLE_PLATFORM_MINGW32
587 if (c == '\r') {
588 continue;
589 }
590#endif
578 /* NUL is substituted by '\n'! */ 591 /* NUL is substituted by '\n'! */
579 if (c == '\0') c = '\n'; 592 if (c == '\0') c = '\n';
580 *p++ = c; 593 *p++ = c;
@@ -671,7 +684,12 @@ static void update_num_lines(void)
671 /* only do this for regular files */ 684 /* only do this for regular files */
672 if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) { 685 if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) {
673 count = 0; 686 count = 0;
687#if !ENABLE_PLATFORM_MINGW32
674 fd = open("/proc/self/fd/0", O_RDONLY); 688 fd = open("/proc/self/fd/0", O_RDONLY);
689#else
690 /* don't even try to access /proc on WIN32 */
691 fd = -1;
692#endif
675 if (fd < 0 && num_lines == REOPEN_AND_COUNT) { 693 if (fd < 0 && num_lines == REOPEN_AND_COUNT) {
676 /* "filename" is valid only if REOPEN_AND_COUNT */ 694 /* "filename" is valid only if REOPEN_AND_COUNT */
677 fd = open(filename, O_RDONLY); 695 fd = open(filename, O_RDONLY);
@@ -854,7 +872,12 @@ static void print_found(const char *line)
854 match_status = 1; 872 match_status = 1;
855 } 873 }
856 874
875#if !ENABLE_PLATFORM_MINGW32
857 printf("%s%s\n", growline ? growline : "", str); 876 printf("%s%s\n", growline ? growline : "", str);
877#else
878 /* skip newline, we use explicit positioning on WIN32 */
879 printf("%s%s", growline ? growline : "", str);
880#endif
858 free(growline); 881 free(growline);
859} 882}
860#else 883#else
@@ -890,7 +913,12 @@ static void print_ascii(const char *str)
890 *p = '\0'; 913 *p = '\0';
891 print_hilite(buf); 914 print_hilite(buf);
892 } 915 }
916#if !ENABLE_PLATFORM_MINGW32
893 puts(str); 917 puts(str);
918#else
919 /* skip newline, we use explicit positioning on WIN32 */
920 printf("%s", str);
921#endif
894} 922}
895 923
896/* Print the buffer */ 924/* Print the buffer */
@@ -900,6 +928,10 @@ static void buffer_print(void)
900 928
901 move_cursor(0, 0); 929 move_cursor(0, 0);
902 for (i = 0; i <= max_displayed_line; i++) { 930 for (i = 0; i <= max_displayed_line; i++) {
931#if ENABLE_PLATFORM_MINGW32
932 /* make sure we're on the right line */
933 move_cursor(i+1, 0);
934#endif
903 printf(CLEAR_2_EOL); 935 printf(CLEAR_2_EOL);
904 if (option_mask32 & FLAG_N) 936 if (option_mask32 & FLAG_N)
905 print_lineno(buffer[i]); 937 print_lineno(buffer[i]);
@@ -1087,9 +1119,13 @@ static void reinitialize(void)
1087 if (G.winsize_err) 1119 if (G.winsize_err)
1088 printf(ESC"[999;999H" ESC"[6n"); 1120 printf(ESC"[999;999H" ESC"[6n");
1089#endif 1121#endif
1122#if ENABLE_PLATFORM_MINGW32
1123 reset_screen();
1124#endif
1090 buffer_fill_and_print(); 1125 buffer_fill_and_print();
1091} 1126}
1092 1127
1128#if !ENABLE_PLATFORM_MINGW32
1093static int64_t getch_nowait(void) 1129static int64_t getch_nowait(void)
1094{ 1130{
1095 int rd; 1131 int rd;
@@ -1151,6 +1187,46 @@ static int64_t getch_nowait(void)
1151 set_tty_cooked(); 1187 set_tty_cooked();
1152 return key64; 1188 return key64;
1153} 1189}
1190#else
1191static int64_t getch_nowait(void)
1192{
1193 int64_t c;
1194
1195retry:
1196 c = _getch();
1197 if (c == 0 || c == 0xe0) {
1198 switch (_getch()) {
1199 case 0x48:
1200 c = KEYCODE_UP;
1201 break;
1202 case 0x50:
1203 c = KEYCODE_DOWN;
1204 break;
1205 case 0x49:
1206 c = KEYCODE_PAGEUP;
1207 break;
1208 case 0x51:
1209 c = KEYCODE_PAGEDOWN;
1210 break;
1211 case 0x47:
1212 c = KEYCODE_HOME;
1213 break;
1214 case 0x4f:
1215 c = KEYCODE_END;
1216 break;
1217 default:
1218 goto retry;
1219 }
1220 }
1221
1222 /* Position cursor if line input is done */
1223 if (less_gets_pos >= 0)
1224 move_cursor(max_displayed_line + 2, less_gets_pos + 1);
1225 fflush_all();
1226
1227 return c;
1228}
1229#endif
1154 1230
1155/* Grab a character from input without requiring the return key. 1231/* Grab a character from input without requiring the return key.
1156 * May return KEYCODE_xxx values. 1232 * May return KEYCODE_xxx values.
@@ -1791,10 +1867,12 @@ static void keypress_process(int keypress)
1791 number_process(keypress); 1867 number_process(keypress);
1792} 1868}
1793 1869
1870#if !ENABLE_PLATFORM_MINGW32
1794static void sig_catcher(int sig) 1871static void sig_catcher(int sig)
1795{ 1872{
1796 less_exit(- sig); 1873 less_exit(- sig);
1797} 1874}
1875#endif
1798 1876
1799#if ENABLE_FEATURE_LESS_WINCH 1877#if ENABLE_FEATURE_LESS_WINCH
1800static void sigwinch_handler(int sig UNUSED_PARAM) 1878static void sigwinch_handler(int sig UNUSED_PARAM)
@@ -1806,7 +1884,9 @@ static void sigwinch_handler(int sig UNUSED_PARAM)
1806int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1884int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1807int less_main(int argc, char **argv) 1885int less_main(int argc, char **argv)
1808{ 1886{
1887#if !ENABLE_PLATFORM_MINGW32
1809 char *tty_name; 1888 char *tty_name;
1889#endif
1810 int tty_fd; 1890 int tty_fd;
1811 1891
1812 INIT_G(); 1892 INIT_G();
@@ -1865,6 +1945,7 @@ int less_main(int argc, char **argv)
1865 if (option_mask32 & FLAG_TILDE) 1945 if (option_mask32 & FLAG_TILDE)
1866 empty_line_marker = ""; 1946 empty_line_marker = "";
1867 1947
1948#if !ENABLE_PLATFORM_MINGW32
1868 /* Some versions of less can survive w/o controlling tty, 1949 /* Some versions of less can survive w/o controlling tty,
1869 * try to do the same. This also allows to specify an alternative 1950 * try to do the same. This also allows to specify an alternative
1870 * tty via "less 1<>TTY". 1951 * tty via "less 1<>TTY".
@@ -1890,8 +1971,13 @@ int less_main(int argc, char **argv)
1890 } 1971 }
1891 G.kbd_fd_orig_flags = ndelay_on(tty_fd); 1972 G.kbd_fd_orig_flags = ndelay_on(tty_fd);
1892 kbd_fd = tty_fd; /* save in a global */ 1973 kbd_fd = tty_fd; /* save in a global */
1974#else
1975 kbd_fd = tty_fd = 0;
1976#endif
1893 1977
1978#if !ENABLE_PLATFORM_MINGW32
1894 get_termios_and_make_raw(tty_fd, &term_less, &term_orig, TERMIOS_RAW_CRNL_INPUT); 1979 get_termios_and_make_raw(tty_fd, &term_less, &term_orig, TERMIOS_RAW_CRNL_INPUT);
1980#endif
1895 1981
1896 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(tty_fd, &width, &max_displayed_line); 1982 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(tty_fd, &width, &max_displayed_line);
1897 /* 20: two tabstops + 4 */ 1983 /* 20: two tabstops + 4 */
diff --git a/miscutils/man.c b/miscutils/man.c
index 01155c8f0..fd5d90c1a 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -199,8 +199,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
199 if (path) while (*path) { 199 if (path) while (*path) {
200 char *next_path; 200 char *next_path;
201 char **path_element; 201 char **path_element;
202 202 next_path = strchr(path, PATH_SEP);
203 next_path = strchr(path, ':');
204 if (next_path) { 203 if (next_path) {
205 if (next_path == path) /* "::"? */ 204 if (next_path == path) /* "::"? */
206 goto next; 205 goto next;
@@ -222,7 +221,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
222 if (!next_path) 221 if (!next_path)
223 break; 222 break;
224 /* "path" may be a result of getenv(), be nice and don't mangle it */ 223 /* "path" may be a result of getenv(), be nice and don't mangle it */
225 *next_path = ':'; 224 *next_path = PATH_SEP;
226 next: 225 next:
227 path = next_path + 1; 226 path = next_path + 1;
228 } 227 }
@@ -302,6 +301,16 @@ int man_main(int argc UNUSED_PARAM, char **argv)
302 } 301 }
303 config_close(parser); 302 config_close(parser);
304 303
304#if ENABLE_PLATFORM_MINGW32
305 {
306 char *exepath = xstrdup(bb_busybox_exec_path);
307 char *relpath = concat_path_file(dirname(exepath), "man");
308 man_path_list = add_MANPATH(man_path_list, &count_mp, relpath);
309 free(relpath);
310 free(exepath);
311 }
312#endif
313
305 { 314 {
306 /* environment overrides setting from man.config */ 315 /* environment overrides setting from man.config */
307 char *env_pager = getenv("MANPAGER"); 316 char *env_pager = getenv("MANPAGER");
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index cb6910fb0..bff90538f 100644
--- a/networking/ftpgetput.c
+++ b/networking/ftpgetput.c
@@ -106,6 +106,9 @@ static int ftpcmd(const char *s1, const char *s2)
106 fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3), 106 fprintf(control_stream, (s2 ? "%s %s\r\n" : "%s %s\r\n"+3),
107 s1, s2); 107 s1, s2);
108 fflush(control_stream); 108 fflush(control_stream);
109#if ENABLE_PLATFORM_MINGW32
110 fseek(control_stream, 0L, SEEK_CUR);
111#endif
109 } 112 }
110 113
111 do { 114 do {
@@ -114,6 +117,9 @@ static int ftpcmd(const char *s1, const char *s2)
114 ftp_die(NULL); 117 ftp_die(NULL);
115 } 118 }
116 } while (!isdigit(buf[0]) || buf[3] != ' '); 119 } while (!isdigit(buf[0]) || buf[3] != ' ');
120#if ENABLE_PLATFORM_MINGW32
121 fseek(control_stream, 0L, SEEK_CUR);
122#endif
117 123
118 buf[3] = '\0'; 124 buf[3] = '\0';
119 n = xatou(buf); 125 n = xatou(buf);
diff --git a/networking/nc.c b/networking/nc.c
index b208f46c6..3e122b787 100644
--- a/networking/nc.c
+++ b/networking/nc.c
@@ -110,10 +110,12 @@
110 * when compared to "standard" nc 110 * when compared to "standard" nc
111 */ 111 */
112 112
113#if ENABLE_NC_EXTRA
113static void timeout(int signum UNUSED_PARAM) 114static void timeout(int signum UNUSED_PARAM)
114{ 115{
115 bb_error_msg_and_die("timed out"); 116 bb_error_msg_and_die("timed out");
116} 117}
118#endif
117 119
118int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 120int nc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
119int nc_main(int argc, char **argv) 121int nc_main(int argc, char **argv)
@@ -127,7 +129,7 @@ int nc_main(int argc, char **argv)
127 IF_NOT_NC_EXTRA (const) unsigned delay = 0; 129 IF_NOT_NC_EXTRA (const) unsigned delay = 0;
128 IF_NOT_NC_EXTRA (const int execparam = 0;) 130 IF_NOT_NC_EXTRA (const int execparam = 0;)
129 IF_NC_EXTRA (char **execparam = NULL;) 131 IF_NC_EXTRA (char **execparam = NULL;)
130 struct pollfd pfds[2]; 132 fd_set readfds, testfds;
131 int opt; /* must be signed (getopt returns -1) */ 133 int opt; /* must be signed (getopt returns -1) */
132 134
133 if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { 135 if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
@@ -187,10 +189,12 @@ int nc_main(int argc, char **argv)
187 argv++; 189 argv++;
188 } 190 }
189 191
192#if ENABLE_NC_EXTRA
190 if (wsecs) { 193 if (wsecs) {
191 signal(SIGALRM, timeout); 194 signal(SIGALRM, timeout);
192 alarm(wsecs); 195 alarm(wsecs);
193 } 196 }
197#endif
194 198
195 if (!cfd) { 199 if (!cfd) {
196 if (do_listen) { 200 if (do_listen) {
@@ -208,7 +212,7 @@ int nc_main(int argc, char **argv)
208 } 212 }
209#endif 213#endif
210 close_on_exec_on(sfd); 214 close_on_exec_on(sfd);
211 accept_again: 215 IF_NC_EXTRA(accept_again:)
212 cfd = accept(sfd, NULL, 0); 216 cfd = accept(sfd, NULL, 0);
213 if (cfd < 0) 217 if (cfd < 0)
214 bb_perror_msg_and_die("accept"); 218 bb_perror_msg_and_die("accept");
@@ -226,6 +230,7 @@ int nc_main(int argc, char **argv)
226 /*signal(SIGALRM, SIG_DFL);*/ 230 /*signal(SIGALRM, SIG_DFL);*/
227 } 231 }
228 232
233#if ENABLE_NC_EXTRA
229 /* -e given? */ 234 /* -e given? */
230 if (execparam) { 235 if (execparam) {
231 pid_t pid; 236 pid_t pid;
@@ -244,29 +249,31 @@ int nc_main(int argc, char **argv)
244 IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);) 249 IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
245 IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);) 250 IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);)
246 } 251 }
252#endif
247 253
248 /* loop copying stdin to cfd, and cfd to stdout */ 254 /* Select loop copying stdin to cfd, and cfd to stdout */
249 255
250 pfds[0].fd = STDIN_FILENO; 256 FD_ZERO(&readfds);
251 pfds[0].events = POLLIN; 257 FD_SET(cfd, &readfds);
252 pfds[1].fd = cfd; 258 FD_SET(STDIN_FILENO, &readfds);
253 pfds[1].events = POLLIN;
254 259
255#define iobuf bb_common_bufsiz1 260#define iobuf bb_common_bufsiz1
256 setup_common_bufsiz(); 261 setup_common_bufsiz();
257 for (;;) { 262 for (;;) {
258 int fdidx; 263 int fd;
259 int ofd; 264 int ofd;
260 int nread; 265 int nread;
261 266
262 if (safe_poll(pfds, 2, -1) < 0) 267 testfds = readfds;
263 bb_perror_msg_and_die("poll"); 268
269 if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0)
270 bb_perror_msg_and_die("select");
264 271
265 fdidx = 0; 272 fd = STDIN_FILENO;
266 while (1) { 273 while (1) {
267 if (pfds[fdidx].revents) { 274 if (FD_ISSET(fd, &testfds)) {
268 nread = safe_read(pfds[fdidx].fd, iobuf, COMMON_BUFSIZE); 275 nread = safe_read(fd, iobuf, COMMON_BUFSIZE);
269 if (fdidx != 0) { 276 if (fd == cfd) {
270 if (nread < 1) 277 if (nread < 1)
271 exit(EXIT_SUCCESS); 278 exit(EXIT_SUCCESS);
272 ofd = STDOUT_FILENO; 279 ofd = STDOUT_FILENO;
@@ -275,7 +282,7 @@ int nc_main(int argc, char **argv)
275 /* Close outgoing half-connection so they get EOF, 282 /* Close outgoing half-connection so they get EOF,
276 * but leave incoming alone so we can see response */ 283 * but leave incoming alone so we can see response */
277 shutdown(cfd, SHUT_WR); 284 shutdown(cfd, SHUT_WR);
278 pfds[0].fd = -1; 285 FD_CLR(STDIN_FILENO, &readfds);
279 } 286 }
280 ofd = cfd; 287 ofd = cfd;
281 } 288 }
@@ -283,9 +290,9 @@ int nc_main(int argc, char **argv)
283 if (delay > 0) 290 if (delay > 0)
284 sleep(delay); 291 sleep(delay);
285 } 292 }
286 if (fdidx == 1) 293 if (fd == cfd)
287 break; 294 break;
288 fdidx++; 295 fd = cfd;
289 } 296 }
290 } 297 }
291} 298}
diff --git a/networking/ssl_client.c b/networking/ssl_client.c
index 397aad297..cd0ee5722 100644
--- a/networking/ssl_client.c
+++ b/networking/ssl_client.c
@@ -15,7 +15,12 @@
15//kbuild:lib-$(CONFIG_SSL_CLIENT) += ssl_client.o 15//kbuild:lib-$(CONFIG_SSL_CLIENT) += ssl_client.o
16 16
17//usage:#define ssl_client_trivial_usage 17//usage:#define ssl_client_trivial_usage
18//usage: IF_NOT_PLATFORM_MINGW32(
18//usage: "[-e] -s FD [-r FD] [-n SNI]" 19//usage: "[-e] -s FD [-r FD] [-n SNI]"
20//usage: )
21//usage: IF_PLATFORM_MINGW32(
22//usage: "[-e] -h handle [-n SNI]"
23//usage: )
19//usage:#define ssl_client_full_usage "" 24//usage:#define ssl_client_full_usage ""
20 25
21#include "libbb.h" 26#include "libbb.h"
@@ -26,15 +31,23 @@ int ssl_client_main(int argc UNUSED_PARAM, char **argv)
26 tls_state_t *tls; 31 tls_state_t *tls;
27 const char *sni = NULL; 32 const char *sni = NULL;
28 int opt; 33 int opt;
34#if ENABLE_PLATFORM_MINGW32
35 char *hstr = NULL;
36 HANDLE h;
37#endif
29 38
30 // INIT_G(); 39 // INIT_G();
31 40
32 tls = new_tls_state(); 41 tls = new_tls_state();
42#if !ENABLE_PLATFORM_MINGW32
33 opt = getopt32(argv, "es:+r:+n:", &tls->ofd, &tls->ifd, &sni); 43 opt = getopt32(argv, "es:+r:+n:", &tls->ofd, &tls->ifd, &sni);
34 if (!(opt & (1<<2))) { 44 if (!(opt & (1<<2))) {
35 /* -r N defaults to -s N */ 45 /* -r N defaults to -s N */
36 tls->ifd = tls->ofd; 46 tls->ifd = tls->ofd;
37 } 47 }
48#else
49 opt = getopt32(argv, "eh:n:", &hstr, &sni);
50#endif
38 51
39 if (!(opt & (3<<1))) { 52 if (!(opt & (3<<1))) {
40 if (!argv[1]) 53 if (!argv[1])
@@ -47,6 +60,13 @@ int ssl_client_main(int argc UNUSED_PARAM, char **argv)
47 sni = argv[1]; 60 sni = argv[1];
48 tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[1], 443); 61 tls->ifd = tls->ofd = create_and_connect_stream_or_die(argv[1], 443);
49 } 62 }
63#if ENABLE_PLATFORM_MINGW32
64 else {
65 if (!hstr || sscanf(hstr, "%p", &h) != 1)
66 bb_error_msg_and_die("invalid handle");
67 tls->ifd = tls->ofd = _open_osfhandle((intptr_t)h, _O_RDWR|_O_BINARY);
68 }
69#endif
50 70
51 tls_handshake(tls, sni); 71 tls_handshake(tls, sni);
52 72
diff --git a/networking/tls.c b/networking/tls.c
index db0034e66..d2385efe8 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -351,8 +351,14 @@ static void dump_tls_record(const void *vp, int len)
351 351
352void FAST_FUNC tls_get_random(void *buf, unsigned len) 352void FAST_FUNC tls_get_random(void *buf, unsigned len)
353{ 353{
354#if !ENABLE_PLATFORM_MINGW32
354 if (len != open_read_close("/dev/urandom", buf, len)) 355 if (len != open_read_close("/dev/urandom", buf, len))
355 xfunc_die(); 356 xfunc_die();
357#else
358 int fd = mingw_open("/dev/urandom", O_RDONLY|O_SPECIAL);
359 if (fd < 0 || len != read_close(fd, buf, len))
360 xfunc_die();
361#endif
356} 362}
357 363
358static void xorbuf3(void *dst, const void *src1, const void *src2, unsigned count) 364static void xorbuf3(void *dst, const void *src1, const void *src2, unsigned count)
diff --git a/networking/wget.c b/networking/wget.c
index 3a02de6ca..3cae1192c 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -489,6 +489,9 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp)
489 fprintf(stderr, "--> %s%s\n\n", s1, s2); 489 fprintf(stderr, "--> %s%s\n\n", s1, s2);
490 fflush(fp); 490 fflush(fp);
491 log_io("> %s%s", s1, s2); 491 log_io("> %s%s", s1, s2);
492#if ENABLE_PLATFORM_MINGW32
493 fseek(fp, 0L, SEEK_CUR);
494#endif
492 } 495 }
493 496
494 /* Read until "Nxx something" is received */ 497 /* Read until "Nxx something" is received */
@@ -496,6 +499,9 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp)
496 do { 499 do {
497 fgets_trim_sanitize(fp, "%s\n"); 500 fgets_trim_sanitize(fp, "%s\n");
498 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' '); 501 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
502#if ENABLE_PLATFORM_MINGW32
503 fseek(fp, 0L, SEEK_CUR);
504#endif
499 505
500 G.wget_buf[3] = '\0'; 506 G.wget_buf[3] = '\0';
501 result = xatoi_positive(G.wget_buf); 507 result = xatoi_positive(G.wget_buf);
@@ -719,6 +725,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port)
719#endif 725#endif
720 726
721#if ENABLE_FEATURE_WGET_HTTPS 727#if ENABLE_FEATURE_WGET_HTTPS
728# if !ENABLE_PLATFORM_MINGW32
722static void spawn_ssl_client(const char *host, int network_fd, int flags) 729static void spawn_ssl_client(const char *host, int network_fd, int flags)
723{ 730{
724 int sp[2]; 731 int sp[2];
@@ -773,6 +780,31 @@ static void spawn_ssl_client(const char *host, int network_fd, int flags)
773 close(sp[1]); 780 close(sp[1]);
774 xmove_fd(sp[0], network_fd); 781 xmove_fd(sp[0], network_fd);
775} 782}
783# else
784static void spawn_ssl_client(const char *host, int network_fd, int flags)
785{
786 int fd1;
787 char *servername, *p, *cmd;
788
789 servername = xstrdup(host);
790 p = strrchr(servername, ':');
791 if (p) *p = '\0';
792
793 fflush_all();
794
795 cmd = xasprintf("%s ssl_client -h %p -n %s%s", bb_busybox_exec_path,
796 (void *)_get_osfhandle(network_fd), servername,
797 flags & TLSLOOP_EXIT_ON_LOCAL_EOF ? " -e" : "");
798
799 if ( (fd1=mingw_popen_fd(cmd, "b", -1, NULL)) == -1 ) {
800 bb_perror_msg_and_die("can't execute ssl_client");
801 }
802
803 free(cmd);
804 free(servername);
805 xmove_fd(fd1, network_fd);
806}
807# endif
776#endif 808#endif
777 809
778static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) 810static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa)
diff --git a/procps/iostat.c b/procps/iostat.c
index 1c6fb87ba..7bf567c34 100644
--- a/procps/iostat.c
+++ b/procps/iostat.c
@@ -28,7 +28,7 @@
28#if 1 28#if 1
29typedef unsigned long long cputime_t; 29typedef unsigned long long cputime_t;
30typedef long long icputime_t; 30typedef long long icputime_t;
31# define FMT_DATA "ll" 31# define FMT_DATA LL_FMT
32# define CPUTIME_MAX (~0ULL) 32# define CPUTIME_MAX (~0ULL)
33#else 33#else
34typedef unsigned long cputime_t; 34typedef unsigned long cputime_t;
diff --git a/procps/kill.c b/procps/kill.c
index a30a79dd8..073e74332 100644
--- a/procps/kill.c
+++ b/procps/kill.c
@@ -207,6 +207,7 @@ int kill_main(int argc UNUSED_PARAM, char **argv)
207 do_it_now: 207 do_it_now:
208 pid = getpid(); 208 pid = getpid();
209 209
210#if ENABLE_KILLALL5
210 if (is_killall5) { 211 if (is_killall5) {
211 pid_t sid; 212 pid_t sid;
212 procps_status_t* p = NULL; 213 procps_status_t* p = NULL;
@@ -264,6 +265,7 @@ int kill_main(int argc UNUSED_PARAM, char **argv)
264 kill(-1, SIGCONT); 265 kill(-1, SIGCONT);
265 return errors; 266 return errors;
266 } 267 }
268#endif
267 269
268#if ENABLE_KILL || ENABLE_KILLALL 270#if ENABLE_KILL || ENABLE_KILLALL
269 /* Pid or name is required for kill/killall */ 271 /* Pid or name is required for kill/killall */
diff --git a/procps/mpstat.c b/procps/mpstat.c
index 4ea1b5d97..e7dc4312a 100644
--- a/procps/mpstat.c
+++ b/procps/mpstat.c
@@ -49,7 +49,7 @@
49#if 1 49#if 1
50typedef unsigned long long data_t; 50typedef unsigned long long data_t;
51typedef long long idata_t; 51typedef 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
55typedef unsigned long data_t; 55typedef unsigned long data_t;
diff --git a/procps/ps.c b/procps/ps.c
index 54e6c40fc..c08d03146 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -35,7 +35,6 @@
35//config: bool "Enable -o time and -o etime specifiers" 35//config: bool "Enable -o time and -o etime specifiers"
36//config: default y 36//config: default y
37//config: depends on (PS || MINIPS) && DESKTOP 37//config: depends on (PS || MINIPS) && DESKTOP
38//config: select PLATFORM_LINUX
39//config: 38//config:
40//config:config FEATURE_PS_UNUSUAL_SYSTEMS 39//config:config FEATURE_PS_UNUSUAL_SYSTEMS
41//config: bool "Support Linux prior to 2.4.0 and non-ELF systems" 40//config: bool "Support Linux prior to 2.4.0 and non-ELF systems"
@@ -126,6 +125,8 @@ static unsigned long get_uptime(void)
126 if (sysinfo(&info) < 0) 125 if (sysinfo(&info) < 0)
127 return 0; 126 return 0;
128 return info.uptime; 127 return info.uptime;
128#elif ENABLE_PLATFORM_MINGW32
129 return GetTickCount64()/1000;
129#elif 1 130#elif 1
130 unsigned long uptime; 131 unsigned long uptime;
131 char buf[sizeof(uptime)*3 + 2]; 132 char buf[sizeof(uptime)*3 + 2];
@@ -238,10 +239,12 @@ static void func_comm(char *buf, int size, const procps_status_t *ps)
238 safe_strncpy(buf, ps->comm, size+1); 239 safe_strncpy(buf, ps->comm, size+1);
239} 240}
240 241
242#if !ENABLE_PLATFORM_MINGW32
241static void func_state(char *buf, int size, const procps_status_t *ps) 243static void func_state(char *buf, int size, const procps_status_t *ps)
242{ 244{
243 safe_strncpy(buf, ps->state, size+1); 245 safe_strncpy(buf, ps->state, size+1);
244} 246}
247#endif
245 248
246static void func_args(char *buf, int size, const procps_status_t *ps) 249static void func_args(char *buf, int size, const procps_status_t *ps)
247{ 250{
@@ -258,6 +261,7 @@ static void func_ppid(char *buf, int size, const procps_status_t *ps)
258 sprintf(buf, "%*u", size, ps->ppid); 261 sprintf(buf, "%*u", size, ps->ppid);
259} 262}
260 263
264#if !ENABLE_PLATFORM_MINGW32
261static void func_pgid(char *buf, int size, const procps_status_t *ps) 265static void func_pgid(char *buf, int size, const procps_status_t *ps)
262{ 266{
263 sprintf(buf, "%*u", size, ps->pgid); 267 sprintf(buf, "%*u", size, ps->pgid);
@@ -294,6 +298,7 @@ static void func_tty(char *buf, int size, const procps_status_t *ps)
294 if (ps->tty_major) /* tty field of "0" means "no tty" */ 298 if (ps->tty_major) /* tty field of "0" means "no tty" */
295 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); 299 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
296} 300}
301#endif
297 302
298#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 303#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
299static void func_rgroup(char *buf, int size, const procps_status_t *ps) 304static void func_rgroup(char *buf, int size, const procps_status_t *ps)
@@ -384,7 +389,9 @@ static const ps_out_t out_spec[] = {
384 { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, 389 { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM },
385 { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, 390 { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID },
386 { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, 391 { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID },
392#if !ENABLE_PLATFORM_MINGW32
387 { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, 393 { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID },
394#endif
388#if ENABLE_FEATURE_PS_TIME 395#if ENABLE_FEATURE_PS_TIME
389 { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, 396 { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME },
390#endif 397#endif
@@ -397,12 +404,14 @@ static const ps_out_t out_spec[] = {
397#if ENABLE_FEATURE_PS_TIME 404#if ENABLE_FEATURE_PS_TIME
398 { 5 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, 405 { 5 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME },
399#endif 406#endif
407#if !ENABLE_PLATFORM_MINGW32
400 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, 408 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY },
401 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, 409 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ },
402/* Not mandated, but useful: */ 410/* Not mandated, but useful: */
403 { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID }, 411 { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID },
404 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE }, 412 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE },
405 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, 413 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS },
414#endif
406#if ENABLE_SELINUX 415#if ENABLE_SELINUX
407 { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, 416 { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT },
408#endif 417#endif
@@ -541,6 +550,8 @@ static void format_process(const procps_status_t *ps)
541#if ENABLE_SELINUX 550#if ENABLE_SELINUX
542# define SELINUX_O_PREFIX "label," 551# define SELINUX_O_PREFIX "label,"
543# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args") 552# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
553#elif ENABLE_PLATFORM_MINGW32
554# define DEFAULT_O_STR ("pid,ppid,user" IF_FEATURE_PS_TIME(",time,etime") ",args")
544#else 555#else
545# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args") 556# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
546#endif 557#endif
diff --git a/procps/smemcap.c b/procps/smemcap.c
index 2f8ab192e..01acbf74e 100644
--- a/procps/smemcap.c
+++ b/procps/smemcap.c
@@ -19,6 +19,7 @@
19//kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o 19//kbuild:lib-$(CONFIG_SMEMCAP) += smemcap.o
20 20
21#include "libbb.h" 21#include "libbb.h"
22#define BB_ARCHIVE_PUBLIC
22#include "bb_archive.h" 23#include "bb_archive.h"
23 24
24struct fileblock { 25struct 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 */
79void exec_kernel_doc(char **svec) 81void 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
139void *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}
174void munmap(void *p, size_t size UNUSED)
175{
176 free(p);
177}
178#endif
179
125char *target; 180char *target;
126char *depfile; 181char *depfile;
127char *cmdline; 182char *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 791d142a8..9a9260f2c 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
44int main(int argc, const char * argv []) 42int 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
@@ -149,7 +151,12 @@ int main(int argc, const char * argv [])
149 { 151 {
150 ptarget[islash] = '\0'; 152 ptarget[islash] = '\0';
151 if (stat(ptarget, &stat_buf) != 0 153 if (stat(ptarget, &stat_buf) != 0
152 && mkdir(ptarget, 0755) != 0) 154#ifdef __MINGW32__
155 && mkdir(ptarget) != 0
156#else
157 && mkdir(ptarget, 0755) != 0
158#endif
159 )
153 ERROR_EXIT( ptarget ); 160 ERROR_EXIT( ptarget );
154 ptarget[islash] = '/'; 161 ptarget[islash] = '/';
155 } 162 }
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 866a7c544..80bd55a68 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -193,9 +193,14 @@ static void conf_askvalue(struct symbol *sym, const char *def)
193 break; 193 break;
194 } 194 }
195 case set_random: 195 case set_random:
196#ifdef __MINGW32__
197 fprintf(stderr, "set_random not supported\n");
198 exit(1);
199#else
196 do { 200 do {
197 val = (tristate)(random() % 3); 201 val = (tristate)(random() % 3);
198 } while (!sym_tristate_within_range(sym, val)); 202 } while (!sym_tristate_within_range(sym, val));
203#endif
199 switch (val) { 204 switch (val) {
200 case no: line[0] = 'n'; break; 205 case no: line[0] = 'n'; break;
201 case mod: line[0] = 'm'; break; 206 case mod: line[0] = 'm'; break;
@@ -407,7 +412,12 @@ static int conf_choice(struct menu *menu)
407 continue; 412 continue;
408 break; 413 break;
409 case set_random: 414 case set_random:
415#ifdef __MINGW32__
416 fprintf(stderr, "set_random not supported\n");
417 exit(1);
418#else
410 def = (random() % cnt) + 1; 419 def = (random() % cnt) + 1;
420#endif
411 case set_default: 421 case set_default:
412 case set_yes: 422 case set_yes:
413 case set_mod: 423 case set_mod:
@@ -563,8 +573,13 @@ int main(int ac, char **av)
563 input_mode = set_yes; 573 input_mode = set_yes;
564 break; 574 break;
565 case 'r': 575 case 'r':
576#ifdef __MINGW32__
577 fprintf(stderr, "set_random not supported\n");
578 exit(1);
579#else
566 input_mode = set_random; 580 input_mode = set_random;
567 srandom(time(NULL)); 581 srandom(time(NULL));
582#endif
568 break; 583 break;
569 case 'h': 584 case 'h':
570 case '?': 585 case '?':
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index b05b96e45..b92c2324e 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -573,15 +573,24 @@ int conf_write(const char *name)
573 fclose(out); 573 fclose(out);
574 if (out_h) { 574 if (out_h) {
575 fclose(out_h); 575 fclose(out_h);
576#ifdef __MINGW32__
577 unlink("include/autoconf.h");
578#endif
576 rename(".tmpconfig.h", "include/autoconf.h"); 579 rename(".tmpconfig.h", "include/autoconf.h");
577 } 580 }
578 if (!name || basename != conf_def_filename) { 581 if (!name || basename != conf_def_filename) {
579 if (!name) 582 if (!name)
580 name = conf_def_filename; 583 name = conf_def_filename;
581 sprintf(tmpname, "%s.old", name); 584 sprintf(tmpname, "%s.old", name);
585#ifdef __MINGW32__
586 unlink(tmpname);
587#endif
582 rename(name, tmpname); 588 rename(name, tmpname);
583 } 589 }
584 sprintf(tmpname, "%s%s", dirname, basename); 590 sprintf(tmpname, "%s%s", dirname, basename);
591#ifdef __MINGW32__
592 unlink(tmpname);
593#endif
585 if (rename(newname, tmpname)) 594 if (rename(newname, tmpname))
586 return 1; 595 return 1;
587 596
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)) 14static inline const char *gettext(const char *txt) { return txt; }
15# define textdomain(Domainname) ((const char *) (Domainname)) 15static inline void textdomain(const char *domainname) {}
16# define bindtextdomain(Domainname, Dirname) ((const char *) (Dirname)) 16static 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 c3a837a14..cda0473ac 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
@@ -270,11 +274,15 @@ static char input_buf[4096];
270static const char filename[] = ".config"; 274static const char filename[] = ".config";
271static char *args[1024], **argptr = args; 275static char *args[1024], **argptr = args;
272static int indent; 276static int indent;
277#ifndef __MINGW32__
273static struct termios ios_org; 278static struct termios ios_org;
279#endif
274static int rows = 0, cols = 0; 280static int rows = 0, cols = 0;
275static struct menu *current_menu; 281static struct menu *current_menu;
276static int child_count; 282static int child_count;
283#ifndef __MINGW32__
277static int do_resize; 284static int do_resize;
285#endif
278static int single_menu_mode; 286static int single_menu_mode;
279 287
280static void conf(struct menu *menu); 288static void conf(struct menu *menu);
@@ -294,6 +302,9 @@ static int cprint(const char *fmt, ...);
294 302
295static void init_wsize(void) 303static void init_wsize(void)
296{ 304{
305#ifdef __MINGW32__
306 fprintf(stderr, "Skipping attempt to change window size\n");
307#else
297 struct winsize ws; 308 struct winsize ws;
298 char *env; 309 char *env;
299 310
@@ -325,6 +336,7 @@ static void init_wsize(void)
325 336
326 rows -= 4; 337 rows -= 4;
327 cols -= 5; 338 cols -= 5;
339#endif
328} 340}
329 341
330static void cprint_init(void) 342static void cprint_init(void)
@@ -461,6 +473,10 @@ static void winch_handler(int sig)
461 473
462static int exec_conf(void) 474static int exec_conf(void)
463{ 475{
476#ifdef __MINGW32__
477 fprintf(stderr, "exec_conf not implemented\n");
478 exit(1);
479#else
464 int pipefd[2], stat, size; 480 int pipefd[2], stat, size;
465 sigset_t sset, osset; 481 sigset_t sset, osset;
466 482
@@ -535,6 +551,7 @@ static int exec_conf(void)
535 sigprocmask(SIG_SETMASK, &osset, NULL); 551 sigprocmask(SIG_SETMASK, &osset, NULL);
536 552
537 return WEXITSTATUS(stat); 553 return WEXITSTATUS(stat);
554#endif
538} 555}
539 556
540static void search_conf(void) 557static void search_conf(void)
@@ -788,7 +805,7 @@ static void conf(struct menu *menu)
788 switch (type) { 805 switch (type) {
789 case 'm': 806 case 'm':
790 if (single_menu_mode) 807 if (single_menu_mode)
791 submenu->data = (void *) (long) !submenu->data; 808 submenu->data = (void *) (intptr_t) !submenu->data;
792 else 809 else
793 conf(submenu); 810 conf(submenu);
794 break; 811 break;
@@ -1051,7 +1068,9 @@ static void conf_save(void)
1051 1068
1052static void conf_cleanup(void) 1069static void conf_cleanup(void)
1053{ 1070{
1071#ifndef __MINGW32__
1054 tcsetattr(1, TCSAFLUSH, &ios_org); 1072 tcsetattr(1, TCSAFLUSH, &ios_org);
1073#endif
1055 unlink(".help.tmp"); 1074 unlink(".help.tmp");
1056 unlink("lxdialog.scrltmp"); 1075 unlink("lxdialog.scrltmp");
1057} 1076}
@@ -1080,7 +1099,9 @@ int main(int ac, char **av)
1080 single_menu_mode = 1; 1099 single_menu_mode = 1;
1081 } 1100 }
1082 1101
1102#ifndef __MINGW32__
1083 tcgetattr(1, &ios_org); 1103 tcgetattr(1, &ios_org);
1104#endif
1084 atexit(conf_cleanup); 1105 atexit(conf_cleanup);
1085 init_wsize(); 1106 init_wsize();
1086 conf(&rootmenu); 1107 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)
44void sym_init(void) 46void 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
77enum symbol_type sym_get_type(struct symbol *sym) 87enum symbol_type sym_get_type(struct symbol *sym)
@@ -720,6 +730,10 @@ struct symbol *sym_find(const char *name)
720 730
721struct symbol **sym_re_search(const char *pattern) 731struct 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/Kbuild.src b/shell/Kbuild.src
index 6bba4989f..a287fce4e 100644
--- a/shell/Kbuild.src
+++ b/shell/Kbuild.src
@@ -9,3 +9,4 @@ lib-y:=
9INSERT 9INSERT
10 10
11lib-$(CONFIG_FEATURE_SH_MATH) += math.o 11lib-$(CONFIG_FEATURE_SH_MATH) += math.o
12lib-$(CONFIG_FEATURE_PRNG_SHELL) += random.o
diff --git a/shell/ash.c b/shell/ash.c
index a284b084d..50d5ce9d4 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 (78 kb)" 34//config: bool "ash (78 kb)"
20//config: default y 35//config: default y
@@ -148,6 +163,18 @@
148//config: you to run the specified command or builtin, 163//config: you to run the specified command or builtin,
149//config: even when there is a function with the same name. 164//config: even when there is a function with the same name.
150//config: 165//config:
166//config:
167//config:config ASH_NOCONSOLE
168//config: bool "'noconsole' option"
169//config: default y
170//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
171//config: help
172//config: Enable support for the 'noconsole' option, which attempts to
173//config: hide the console normally associated with a command line
174//config: application. This may be useful when running a shell script
175//config: from a GUI application. Disable this if your platform doesn't
176//config: support the required APIs.
177//config:
151//config:endif # ash options 178//config:endif # ash options
152 179
153//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 180//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
@@ -282,6 +309,10 @@ typedef long arith_t;
282# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 309# define PIPE_BUF 4096 /* amount of buffering in a pipe */
283#endif 310#endif
284 311
312#if !ENABLE_PLATFORM_MINGW32
313# define is_absolute_path(path) ((path)[0] == '/')
314#endif
315
285#if !BB_MMU 316#if !BB_MMU
286# error "Do not even bother, ash will not run on NOMMU machine" 317# error "Do not even bother, ash will not run on NOMMU machine"
287#endif 318#endif
@@ -299,6 +330,65 @@ typedef long arith_t;
299# define BB_GLOBAL_CONST const 330# define BB_GLOBAL_CONST const
300#endif 331#endif
301 332
333#define FORKSHELL_DEBUG 0
334#if ENABLE_PLATFORM_MINGW32
335union node;
336struct strlist;
337struct job;
338
339struct forkshell {
340 /* filled by forkshell_copy() */
341 struct globals_var *gvp;
342 struct globals_misc *gmp;
343 struct tblentry **cmdtable;
344 /* struct alias **atab; */
345 /* struct parsefile *g_parsefile; */
346 HANDLE hMapFile;
347 char *old_base;
348# if FORKSHELL_DEBUG
349 int nodeptrcount;
350 int funcblocksize;
351 int funcstringsize;
352# endif
353 int size;
354
355 /* type of forkshell */
356 int fpid;
357
358 /* generic data, used by forkshell_child */
359 int mode;
360 int nprocs;
361
362 /* optional data, used by forkshell_child */
363 int flags;
364 int fd[3];
365 union node *n;
366 char **argv;
367 char *path;
368 struct strlist *varlist;
369
370 /* start of data block */
371 char **nodeptr[1];
372};
373
374enum {
375 FS_OPENHERE,
376 FS_EVALBACKCMD,
377 FS_EVALSUBSHELL,
378 FS_EVALPIPE,
379 FS_SHELLEXEC
380};
381
382static struct forkshell* forkshell_prepare(struct forkshell *fs);
383static void forkshell_init(const char *idstr);
384static void forkshell_child(struct forkshell *fs);
385static void sticky_free(void *p);
386# define free(p) sticky_free(p)
387static void spawn_forkshell(struct job *jp, struct forkshell *fs, int mode);
388# if FORKSHELL_DEBUG
389static void forkshell_print(FILE *fp0, struct forkshell *fs, char **notes);
390# endif
391#endif
302 392
303/* ============ Hash table sizes. Configurable. */ 393/* ============ Hash table sizes. Configurable. */
304 394
@@ -331,6 +421,12 @@ static const char *const optletters_optnames[] = {
331 ,"\0" "nolog" 421 ,"\0" "nolog"
332 ,"\0" "debug" 422 ,"\0" "debug"
333#endif 423#endif
424#if ENABLE_PLATFORM_MINGW32
425 ,"X" "winxp"
426#endif
427#if ENABLE_ASH_NOCONSOLE
428 ,"\0" "noconsole"
429#endif
334}; 430};
335 431
336#define optletters(n) optletters_optnames[n][0] 432#define optletters(n) optletters_optnames[n][0]
@@ -363,6 +459,7 @@ struct globals_misc {
363 int rootpid; /* pid of main shell */ 459 int rootpid; /* pid of main shell */
364 /* shell level: 0 for the main shell, 1 for its children, and so on */ 460 /* shell level: 0 for the main shell, 1 for its children, and so on */
365 int shlvl; 461 int shlvl;
462 int loopnest; /* current loop nesting level */
366#define rootshell (!shlvl) 463#define rootshell (!shlvl)
367 int errlinno; 464 int errlinno;
368 465
@@ -372,6 +469,7 @@ struct globals_misc {
372 char *physdir; // = nullstr; /* physical working directory */ 469 char *physdir; // = nullstr; /* physical working directory */
373 470
374 char *arg0; /* value of $0 */ 471 char *arg0; /* value of $0 */
472 char *commandname;
375 473
376 struct jmploc *exception_handler; 474 struct jmploc *exception_handler;
377 475
@@ -411,6 +509,12 @@ struct globals_misc {
411# define nolog optlist[14 + BASH_PIPEFAIL] 509# define nolog optlist[14 + BASH_PIPEFAIL]
412# define debug optlist[15 + BASH_PIPEFAIL] 510# define debug optlist[15 + BASH_PIPEFAIL]
413#endif 511#endif
512#if ENABLE_PLATFORM_MINGW32
513# define winxp optlist[14 + ENABLE_ASH_BASH_COMPAT + 2*DEBUG]
514#endif
515#if ENABLE_ASH_NOCONSOLE
516# define noconsole optlist[15 + ENABLE_ASH_BASH_COMPAT + 2*DEBUG]
517#endif
414 518
415 /* trap handler commands */ 519 /* trap handler commands */
416 /* 520 /*
@@ -444,10 +548,12 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
444#define rootpid (G_misc.rootpid ) 548#define rootpid (G_misc.rootpid )
445#define shlvl (G_misc.shlvl ) 549#define shlvl (G_misc.shlvl )
446#define errlinno (G_misc.errlinno ) 550#define errlinno (G_misc.errlinno )
551#define loopnest (G_misc.loopnest )
447#define minusc (G_misc.minusc ) 552#define minusc (G_misc.minusc )
448#define curdir (G_misc.curdir ) 553#define curdir (G_misc.curdir )
449#define physdir (G_misc.physdir ) 554#define physdir (G_misc.physdir )
450#define arg0 (G_misc.arg0 ) 555#define arg0 (G_misc.arg0 )
556#define commandname (G_misc.commandname)
451#define exception_handler (G_misc.exception_handler) 557#define exception_handler (G_misc.exception_handler)
452#define exception_type (G_misc.exception_type ) 558#define exception_type (G_misc.exception_type )
453#define suppress_int (G_misc.suppress_int ) 559#define suppress_int (G_misc.suppress_int )
@@ -1320,7 +1426,6 @@ struct parsefile {
1320 1426
1321static struct parsefile basepf; /* top level input file */ 1427static struct parsefile basepf; /* top level input file */
1322static struct parsefile *g_parsefile = &basepf; /* current input file */ 1428static struct parsefile *g_parsefile = &basepf; /* current input file */
1323static char *commandname; /* currently executing command */
1324 1429
1325 1430
1326/* ============ Message printing */ 1431/* ============ Message printing */
@@ -2317,6 +2422,42 @@ bltinlookup(const char *name)
2317 return lookupvar(name); 2422 return lookupvar(name);
2318} 2423}
2319 2424
2425#if ENABLE_PLATFORM_MINGW32
2426static char *
2427fix_pathvar(const char *path)
2428{
2429 char *newpath = xstrdup(path);
2430 char *p;
2431 int modified = FALSE;
2432
2433 p = newpath + 5;
2434 while (*p) {
2435 if (*p != ':' && *p != ';') {
2436 /* skip drive */
2437 if (isalpha(*p) && p[1] == ':')
2438 p += 2;
2439 /* skip through path component */
2440 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2441 continue;
2442 }
2443 /* *p is ':', ';' or '\0' here */
2444 if (*p == ':') {
2445 *p++ = ';';
2446 modified = TRUE;
2447 }
2448 else if (*p == ';') {
2449 ++p;
2450 }
2451 }
2452
2453 if (!modified) {
2454 free(newpath);
2455 newpath = NULL;
2456 }
2457 return newpath;
2458}
2459#endif
2460
2320/* 2461/*
2321 * Same as setvar except that the variable and value are passed in 2462 * Same as setvar except that the variable and value are passed in
2322 * the first argument as name=value. Since the first argument will 2463 * the first argument as name=value. Since the first argument will
@@ -2329,6 +2470,19 @@ setvareq(char *s, int flags)
2329{ 2470{
2330 struct var *vp, **vpp; 2471 struct var *vp, **vpp;
2331 2472
2473#if ENABLE_PLATFORM_MINGW32
2474 if (strncmp(s, "PATH=", 5) == 0) {
2475 char *newpath = fix_pathvar(s);
2476 if (newpath) {
2477 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2478 free(s);
2479 flags |= VNOSAVE;
2480 flags &= ~(VTEXTFIXED|VSTACK);
2481 s = newpath;
2482 }
2483 }
2484#endif
2485
2332 vpp = hashvar(s); 2486 vpp = hashvar(s);
2333 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2487 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2334 vpp = findvar(vpp, s); 2488 vpp = findvar(vpp, s);
@@ -2541,10 +2695,12 @@ path_advance(const char **path, const char *name)
2541 if (*path == NULL) 2695 if (*path == NULL)
2542 return NULL; 2696 return NULL;
2543 start = *path; 2697 start = *path;
2544 for (p = start; *p && *p != ':' && *p != '%'; p++) 2698 for (p = start; *p && *p != PATH_SEP && *p != '%'; p++)
2545 continue; 2699 continue;
2546 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 2700 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2547 while (stackblocksize() < len) 2701
2702 /* reserve space for suffix on WIN32 */
2703 while (stackblocksize() < (ENABLE_PLATFORM_MINGW32 ? len+4 : len))
2548 growstackblock(); 2704 growstackblock();
2549 q = stackblock(); 2705 q = stackblock();
2550 if (p != start) { 2706 if (p != start) {
@@ -2555,10 +2711,10 @@ path_advance(const char **path, const char *name)
2555 pathopt = NULL; 2711 pathopt = NULL;
2556 if (*p == '%') { 2712 if (*p == '%') {
2557 pathopt = ++p; 2713 pathopt = ++p;
2558 while (*p && *p != ':') 2714 while (*p && *p != PATH_SEP)
2559 p++; 2715 p++;
2560 } 2716 }
2561 if (*p == ':') 2717 if (*p == PATH_SEP)
2562 *path = p + 1; 2718 *path = p + 1;
2563 else 2719 else
2564 *path = NULL; 2720 *path = NULL;
@@ -2667,6 +2823,106 @@ cdopt(void)
2667static const char * 2823static const char *
2668updatepwd(const char *dir) 2824updatepwd(const char *dir)
2669{ 2825{
2826#if ENABLE_PLATFORM_MINGW32
2827#define is_path_sep(x) ((x) == '/' || (x) == '\\')
2828#define is_unc_path(x) (is_path_sep(x[0]) && is_path_sep(x[1]))
2829 /*
2830 * Due to Windows drive notion, getting pwd is a completely
2831 * different thing. Handle it in a separate routine
2832 */
2833
2834 char *new;
2835 char *p;
2836 char *cdcomppath;
2837 const char *lim;
2838 /*
2839 * There are five cases that make some kind of sense
2840 * absdrive + abspath: c:/path
2841 * absdrive + !abspath: c:path
2842 * !absdrive + abspath: /path
2843 * !absdrive + uncpath: //host/share
2844 * !absdrive + !abspath: path
2845 *
2846 * Damn DOS!
2847 * c:path behaviour is "undefined"
2848 * To properly handle this case, I have to keep track of cwd
2849 * of every drive, which is too painful to do.
2850 * So when c:path is given, I assume it's c:${curdir}path
2851 * with ${curdir} comes from the current drive
2852 */
2853 int absdrive = *dir && dir[1] == ':';
2854 int abspath = absdrive ? is_path_sep(dir[2]) : is_path_sep(*dir);
2855
2856 cdcomppath = sstrdup(dir);
2857 STARTSTACKSTR(new);
2858 if (!absdrive && curdir == nullstr)
2859 return 0;
2860 if (!abspath) {
2861 if (curdir == nullstr)
2862 return 0;
2863 new = stack_putstr(curdir, new);
2864 }
2865 new = makestrspace(strlen(dir) + 2, new);
2866
2867 if ( is_unc_path(dir) || (!absdrive && !abspath && is_unc_path(curdir)) ) {
2868 lim = (char *)stackblock() + 1;
2869 }
2870 else {
2871 char *drive = stackblock();
2872 if (absdrive) {
2873 *drive = *dir;
2874 cdcomppath += 2;
2875 dir += 2;
2876 } else {
2877 *drive = *curdir;
2878 }
2879 drive[1] = ':'; /* in case of absolute drive+path */
2880
2881 if (abspath)
2882 new = drive + 2;
2883 lim = drive + 3;
2884 }
2885
2886 if (!abspath) {
2887 if (!is_path_sep(new[-1]))
2888 USTPUTC('/', new);
2889 if (new > lim && is_path_sep(*lim))
2890 lim++;
2891 } else {
2892 USTPUTC('/', new);
2893 cdcomppath ++;
2894 if (is_path_sep(dir[1]) && !is_path_sep(dir[2])) {
2895 USTPUTC('/', new);
2896 cdcomppath++;
2897 lim++;
2898 }
2899 }
2900 p = strtok(cdcomppath, "/\\");
2901 while (p) {
2902 switch (*p) {
2903 case '.':
2904 if (p[1] == '.' && p[2] == '\0') {
2905 while (new > lim) {
2906 STUNPUTC(new);
2907 if (is_path_sep(new[-1]))
2908 break;
2909 }
2910 break;
2911 }
2912 if (p[1] == '\0')
2913 break;
2914 /* fall through */
2915 default:
2916 new = stack_putstr(p, new);
2917 USTPUTC('/', new);
2918 }
2919 p = strtok(0, "/\\");
2920 }
2921 if (new > lim)
2922 STUNPUTC(new);
2923 *new = 0;
2924 return stackblock();
2925#else
2670 char *new; 2926 char *new;
2671 char *p; 2927 char *p;
2672 char *cdcomppath; 2928 char *cdcomppath;
@@ -2720,6 +2976,7 @@ updatepwd(const char *dir)
2720 STUNPUTC(new); 2976 STUNPUTC(new);
2721 *new = 0; 2977 *new = 0;
2722 return stackblock(); 2978 return stackblock();
2979#endif
2723} 2980}
2724 2981
2725/* 2982/*
@@ -2814,7 +3071,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2814 } 3071 }
2815 if (!dest) 3072 if (!dest)
2816 dest = nullstr; 3073 dest = nullstr;
2817 if (*dest == '/') 3074 if (is_absolute_path(dest))
2818 goto step6; 3075 goto step6;
2819 if (*dest == '.') { 3076 if (*dest == '.') {
2820 c = dest[1]; 3077 c = dest[1];
@@ -3516,6 +3773,9 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3516 */ 3773 */
3517struct procstat { 3774struct procstat {
3518 pid_t ps_pid; /* process id */ 3775 pid_t ps_pid; /* process id */
3776#if ENABLE_PLATFORM_MINGW32
3777 HANDLE ps_proc;
3778#endif
3519 int ps_status; /* last process status from wait() */ 3779 int ps_status; /* last process status from wait() */
3520 char *ps_cmd; /* text of command being run */ 3780 char *ps_cmd; /* text of command being run */
3521}; 3781};
@@ -3544,7 +3804,9 @@ struct job {
3544}; 3804};
3545 3805
3546static struct job *makejob(/*union node *,*/ int); 3806static struct job *makejob(/*union node *,*/ int);
3807#if !ENABLE_PLATFORM_MINGW32
3547static int forkshell(struct job *, union node *, int); 3808static int forkshell(struct job *, union node *, int);
3809#endif
3548static int waitforjob(struct job *); 3810static int waitforjob(struct job *);
3549 3811
3550#if !JOBS 3812#if !JOBS
@@ -3555,6 +3817,7 @@ static smallint doing_jobctl; //references:8
3555static void setjobctl(int); 3817static void setjobctl(int);
3556#endif 3818#endif
3557 3819
3820#if !ENABLE_PLATFORM_MINGW32
3558/* 3821/*
3559 * Ignore a signal. 3822 * Ignore a signal.
3560 */ 3823 */
@@ -3701,6 +3964,10 @@ setsignal(int signo)
3701 3964
3702 sigaction_set(signo, &act); 3965 sigaction_set(signo, &act);
3703} 3966}
3967#else
3968#define setsignal(s)
3969#define ignoresig(s)
3970#endif
3704 3971
3705/* mode flags for set_curjob */ 3972/* mode flags for set_curjob */
3706#define CUR_DELETE 2 3973#define CUR_DELETE 2
@@ -4199,6 +4466,122 @@ sprint_status48(char *s, int status, int sigonly)
4199 return col; 4466 return col;
4200} 4467}
4201 4468
4469#if ENABLE_PLATFORM_MINGW32
4470
4471HANDLE hSIGINT; /* Ctrl-C is pressed */
4472
4473static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4474{
4475 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4476 pending_int = 1;
4477 SetEvent(hSIGINT);
4478 return TRUE;
4479 }
4480 return FALSE;
4481}
4482
4483/*
4484 * Windows does not know about parent-child relationship
4485 * They don't support waitpid(-1)
4486 */
4487static pid_t
4488waitpid_child(int *status, int wait_flags)
4489{
4490 struct job *jb;
4491 struct procstat *ps;
4492 int pid_nr = 0;
4493 pid_t *pidlist;
4494 HANDLE *proclist;
4495 pid_t pid = -1;
4496 DWORD win_status, idx;
4497 int i, delay;
4498
4499 for (jb = curjob; jb; jb = jb->prev_job) {
4500 if (jb->state != JOBDONE)
4501 pid_nr += jb->nprocs;
4502 }
4503 if ( pid_nr++ == 0 )
4504 return -1;
4505
4506 pidlist = ckmalloc(sizeof(*pidlist)*pid_nr);
4507 proclist = ckmalloc(sizeof(*proclist)*pid_nr);
4508
4509 pidlist[0] = -1;
4510 proclist[0] = hSIGINT;
4511 pid_nr = 1;
4512 for (jb = curjob; jb; jb = jb->prev_job) {
4513 if (jb->state == JOBDONE)
4514 continue;
4515 ps = jb->ps;
4516 for (i = 0; i < jb->nprocs; ++i) {
4517 if (ps[i].ps_proc) {
4518 pidlist[pid_nr] = ps[i].ps_pid;
4519 proclist[pid_nr++] = ps[i].ps_proc;
4520 }
4521 }
4522 }
4523
4524 if (pid_nr == 1)
4525 goto done;
4526
4527 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4528 wait_flags&WNOHANG ? 1 : INFINITE);
4529 if (idx < pid_nr) {
4530 if (idx == 0) { /* hSIGINT */
4531 ResetEvent(hSIGINT);
4532
4533 ps = curjob->ps;
4534 for (i = 0; i < curjob->nprocs; ++i) {
4535 if (ps[i].ps_proc) {
4536 kill_SIGTERM_by_handle(ps[i].ps_proc, 128+SIGINT);
4537 pid = ps[i].ps_pid; /* remember last valid pid */
4538 }
4539 }
4540
4541 Sleep(200);
4542 delay = FALSE;
4543 for (i = 0; i < curjob->nprocs; ++i) {
4544 DWORD code;
4545 if (ps[i].ps_proc &&
4546 GetExitCodeProcess(ps[i].ps_proc, &code) &&
4547 code == STILL_ACTIVE) {
4548 TerminateProcess(ps[i].ps_proc, 128+SIGINT);
4549 delay = TRUE;
4550 }
4551 }
4552
4553 if (delay)
4554 Sleep(200);
4555 for (i = 0; i < curjob->nprocs; ++i) {
4556 /* mark all pids dead except the one we'll return */
4557 if (ps[i].ps_pid != pid) {
4558 ps[i].ps_status = 128 + SIGINT;
4559 ps[i].ps_pid = -1;
4560 CloseHandle(ps[i].ps_proc);
4561 ps[i].ps_proc = NULL;
4562 }
4563 }
4564
4565 *status = 128 + SIGINT; /* terminated by a signal */
4566 if (iflag)
4567 write(STDOUT_FILENO, "^C", 2);
4568 }
4569 else { /* valid pid index */
4570 GetExitCodeProcess(proclist[idx], &win_status);
4571 *status = (int)win_status << 8;
4572 pid = pidlist[idx];
4573 }
4574 }
4575 done:
4576 free(pidlist);
4577 free(proclist);
4578 return pid;
4579}
4580#define waitpid(p, s, f) waitpid_child(s, f)
4581#define wait_block_or_sig(s) waitpid_child(s, 0)
4582
4583#else
4584
4202static int 4585static int
4203wait_block_or_sig(int *status) 4586wait_block_or_sig(int *status)
4204{ 4587{
@@ -4231,6 +4614,7 @@ wait_block_or_sig(int *status)
4231 4614
4232 return pid; 4615 return pid;
4233} 4616}
4617#endif
4234 4618
4235#define DOWAIT_NONBLOCK 0 4619#define DOWAIT_NONBLOCK 0
4236#define DOWAIT_BLOCK 1 4620#define DOWAIT_BLOCK 1
@@ -4299,6 +4683,11 @@ dowait(int block, struct job *job)
4299 jobno(jp), pid, ps->ps_status, status)); 4683 jobno(jp), pid, ps->ps_status, status));
4300 ps->ps_status = status; 4684 ps->ps_status = status;
4301 thisjob = jp; 4685 thisjob = jp;
4686#if ENABLE_PLATFORM_MINGW32
4687 ps->ps_pid = -1;
4688 CloseHandle(ps->ps_proc);
4689 ps->ps_proc = NULL;
4690#endif
4302 } 4691 }
4303 if (ps->ps_status == -1) 4692 if (ps->ps_status == -1)
4304 jobstate = JOBRUNNING; 4693 jobstate = JOBRUNNING;
@@ -4981,6 +5370,7 @@ commandtext(union node *n)
4981 * 5370 *
4982 * Called with interrupts off. 5371 * Called with interrupts off.
4983 */ 5372 */
5373#if !ENABLE_PLATFORM_MINGW32
4984/* 5374/*
4985 * Clear traps on a fork. 5375 * Clear traps on a fork.
4986 */ 5376 */
@@ -5130,16 +5520,24 @@ forkchild(struct job *jp, union node *n, int mode)
5130 freejob(jp); 5520 freejob(jp);
5131 jobless = 0; 5521 jobless = 0;
5132} 5522}
5523#endif
5133 5524
5134/* Called after fork(), in parent */ 5525/* Called after fork(), in parent */
5135#if !JOBS 5526#if !JOBS
5136#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 5527#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5137#endif 5528#endif
5138static void 5529static void
5530#if !ENABLE_PLATFORM_MINGW32
5139forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5531forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5532#else
5533forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
5534#endif
5140{ 5535{
5536#if ENABLE_PLATFORM_MINGW32
5537 pid_t pid = GetProcessId(proc);
5538#endif
5141 TRACE(("In parent shell: child = %d\n", pid)); 5539 TRACE(("In parent shell: child = %d\n", pid));
5142 if (!jp) { 5540 if (!jp && !ENABLE_PLATFORM_MINGW32) { /* FIXME not quite understand this */
5143 /* jp is NULL when called by openhere() for heredoc support */ 5541 /* jp is NULL when called by openhere() for heredoc support */
5144 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) 5542 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
5145 continue; 5543 continue;
@@ -5165,6 +5563,9 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5165 if (jp) { 5563 if (jp) {
5166 struct procstat *ps = &jp->ps[jp->nprocs++]; 5564 struct procstat *ps = &jp->ps[jp->nprocs++];
5167 ps->ps_pid = pid; 5565 ps->ps_pid = pid;
5566#if ENABLE_PLATFORM_MINGW32
5567 ps->ps_proc = proc;
5568#endif
5168 ps->ps_status = -1; 5569 ps->ps_status = -1;
5169 ps->ps_cmd = nullstr; 5570 ps->ps_cmd = nullstr;
5170#if JOBS 5571#if JOBS
@@ -5174,6 +5575,7 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5174 } 5575 }
5175} 5576}
5176 5577
5578#if !ENABLE_PLATFORM_MINGW32
5177/* jp and n are NULL when called by openhere() for heredoc support */ 5579/* jp and n are NULL when called by openhere() for heredoc support */
5178static int 5580static int
5179forkshell(struct job *jp, union node *n, int mode) 5581forkshell(struct job *jp, union node *n, int mode)
@@ -5196,6 +5598,7 @@ forkshell(struct job *jp, union node *n, int mode)
5196 } 5598 }
5197 return pid; 5599 return pid;
5198} 5600}
5601#endif
5199 5602
5200/* 5603/*
5201 * Wait for job to finish. 5604 * Wait for job to finish.
@@ -5327,6 +5730,7 @@ openhere(union node *redir)
5327{ 5730{
5328 int pip[2]; 5731 int pip[2];
5329 size_t len = 0; 5732 size_t len = 0;
5733 IF_PLATFORM_MINGW32(struct forkshell fs);
5330 5734
5331 if (pipe(pip) < 0) 5735 if (pipe(pip) < 0)
5332 ash_msg_and_raise_perror("can't create pipe"); 5736 ash_msg_and_raise_perror("can't create pipe");
@@ -5337,6 +5741,14 @@ openhere(union node *redir)
5337 goto out; 5741 goto out;
5338 } 5742 }
5339 } 5743 }
5744#if ENABLE_PLATFORM_MINGW32
5745 memset(&fs, 0, sizeof(fs));
5746 fs.fpid = FS_OPENHERE;
5747 fs.n = redir;
5748 fs.fd[0] = pip[0];
5749 fs.fd[1] = pip[1];
5750 spawn_forkshell(NULL, &fs, FORK_NOJOB);
5751#else
5340 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 5752 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5341 /* child */ 5753 /* child */
5342 close(pip[0]); 5754 close(pip[0]);
@@ -5351,6 +5763,7 @@ openhere(union node *redir)
5351 expandhere(redir->nhere.doc, pip[1]); 5763 expandhere(redir->nhere.doc, pip[1]);
5352 _exit(EXIT_SUCCESS); 5764 _exit(EXIT_SUCCESS);
5353 } 5765 }
5766#endif
5354 out: 5767 out:
5355 close(pip[1]); 5768 close(pip[1]);
5356 return pip[0]; 5769 return pip[0];
@@ -5428,6 +5841,9 @@ openredirect(union node *redir)
5428 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 5841 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5429 if (f < 0) 5842 if (f < 0)
5430 goto ecreate; 5843 goto ecreate;
5844#if ENABLE_PLATFORM_MINGW32
5845 lseek(f, 0, SEEK_END);
5846#endif
5431 break; 5847 break;
5432 } 5848 }
5433 5849
@@ -6407,6 +6823,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6407{ 6823{
6408 int pip[2]; 6824 int pip[2];
6409 struct job *jp; 6825 struct job *jp;
6826 IF_PLATFORM_MINGW32(struct forkshell fs);
6410 6827
6411 result->fd = -1; 6828 result->fd = -1;
6412 result->buf = NULL; 6829 result->buf = NULL;
@@ -6419,6 +6836,14 @@ evalbackcmd(union node *n, struct backcmd *result)
6419 if (pipe(pip) < 0) 6836 if (pipe(pip) < 0)
6420 ash_msg_and_raise_perror("can't create pipe"); 6837 ash_msg_and_raise_perror("can't create pipe");
6421 jp = makejob(/*n,*/ 1); 6838 jp = makejob(/*n,*/ 1);
6839#if ENABLE_PLATFORM_MINGW32
6840 memset(&fs, 0, sizeof(fs));
6841 fs.fpid = FS_EVALBACKCMD;
6842 fs.n = n;
6843 fs.fd[0] = pip[0];
6844 fs.fd[1] = pip[1];
6845 spawn_forkshell(jp, &fs, FORK_NOJOB);
6846#else
6422 if (forkshell(jp, n, FORK_NOJOB) == 0) { 6847 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6423 /* child */ 6848 /* child */
6424 FORCE_INT_ON; 6849 FORCE_INT_ON;
@@ -6441,6 +6866,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6441 evaltreenr(n, EV_EXIT); 6866 evaltreenr(n, EV_EXIT);
6442 /* NOTREACHED */ 6867 /* NOTREACHED */
6443 } 6868 }
6869#endif
6444 /* parent */ 6870 /* parent */
6445 close(pip[1]); 6871 close(pip[1]);
6446 result->fd = pip[0]; 6872 result->fd = pip[0];
@@ -6497,7 +6923,8 @@ expbackq(union node *cmd, int flag)
6497 6923
6498 /* Eat all trailing newlines */ 6924 /* Eat all trailing newlines */
6499 dest = expdest; 6925 dest = expdest;
6500 for (; dest > (char *)stackblock() && dest[-1] == '\n';) 6926 for (; dest > (char *)stackblock() && (dest[-1] == '\n' ||
6927 (ENABLE_PLATFORM_MINGW32 && dest[-1] == '\r'));)
6501 STUNPUTC(dest); 6928 STUNPUTC(dest);
6502 expdest = dest; 6929 expdest = dest;
6503 6930
@@ -8020,6 +8447,11 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8020 } 8447 }
8021#endif 8448#endif
8022 8449
8450#if ENABLE_PLATFORM_MINGW32
8451 cmd = auto_win32_extension(cmd) ?: cmd;
8452 execve(cmd, argv, envp);
8453 /* skip POSIX-mandated retry on ENOEXEC */
8454#else
8023 repeat: 8455 repeat:
8024#ifdef SYSV 8456#ifdef SYSV
8025 do { 8457 do {
@@ -8055,6 +8487,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8055 argv[0] = (char*) "ash"; 8487 argv[0] = (char*) "ash";
8056 goto repeat; 8488 goto repeat;
8057 } 8489 }
8490#endif /* ENABLE_PLATFORM_MINGW32 */
8058} 8491}
8059 8492
8060/* 8493/*
@@ -8072,7 +8505,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8072 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 8505 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8073 8506
8074 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 8507 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
8075 if (strchr(prog, '/') != NULL 8508 if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\')))
8076#if ENABLE_FEATURE_SH_STANDALONE 8509#if ENABLE_FEATURE_SH_STANDALONE
8077 || (applet_no = find_applet_by_name(prog)) >= 0 8510 || (applet_no = find_applet_by_name(prog)) >= 0
8078#endif 8511#endif
@@ -8131,6 +8564,9 @@ printentry(struct tblentry *cmdp)
8131 name = path_advance(&path, cmdp->cmdname); 8564 name = path_advance(&path, cmdp->cmdname);
8132 stunalloc(name); 8565 stunalloc(name);
8133 } while (--idx >= 0); 8566 } while (--idx >= 0);
8567#if ENABLE_PLATFORM_MINGW32
8568 add_win32_extension(name);
8569#endif
8134 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 8570 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8135} 8571}
8136 8572
@@ -8327,8 +8763,8 @@ changepath(const char *new)
8327 for (;;) { 8763 for (;;) {
8328 if (*old != *new) { 8764 if (*old != *new) {
8329 firstchange = idx; 8765 firstchange = idx;
8330 if ((*old == '\0' && *new == ':') 8766 if ((*old == '\0' && *new == PATH_SEP)
8331 || (*old == ':' && *new == '\0') 8767 || (*old == PATH_SEP && *new == '\0')
8332 ) { 8768 ) {
8333 firstchange++; 8769 firstchange++;
8334 } 8770 }
@@ -8338,7 +8774,7 @@ changepath(const char *new)
8338 break; 8774 break;
8339 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) 8775 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
8340 idx_bltin = idx; 8776 idx_bltin = idx;
8341 if (*new == ':') 8777 if (*new == PATH_SEP)
8342 idx++; 8778 idx++;
8343 new++; 8779 new++;
8344 old++; 8780 old++;
@@ -8527,6 +8963,9 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8527 stunalloc(p); 8963 stunalloc(p);
8528 } while (--j >= 0); 8964 } while (--j >= 0);
8529 } 8965 }
8966#if ENABLE_PLATFORM_MINGW32
8967 add_win32_extension(p);
8968#endif
8530 if (describe_command_verbose) { 8969 if (describe_command_verbose) {
8531 out1fmt(" is %s", p); 8970 out1fmt(" is %s", p);
8532 } else { 8971 } else {
@@ -8661,6 +9100,14 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8661/*static int funcstringsize; // size of strings in node */ 9100/*static int funcstringsize; // size of strings in node */
8662static void *funcblock; /* block to allocate function from */ 9101static void *funcblock; /* block to allocate function from */
8663static char *funcstring_end; /* end of block to allocate strings from */ 9102static char *funcstring_end; /* end of block to allocate strings from */
9103#if ENABLE_PLATFORM_MINGW32
9104static int nodeptrcount;
9105static char ***nodeptr;
9106# if FORKSHELL_DEBUG
9107static int annot_count;
9108static char **annot;
9109# endif
9110#endif
8664 9111
8665static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9112static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8666 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9113 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -8701,6 +9148,7 @@ sizenodelist(int funcblocksize, struct nodelist *lp)
8701{ 9148{
8702 while (lp) { 9149 while (lp) {
8703 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); 9150 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
9151 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8704 funcblocksize = calcsize(funcblocksize, lp->n); 9152 funcblocksize = calcsize(funcblocksize, lp->n);
8705 lp = lp->next; 9153 lp = lp->next;
8706 } 9154 }
@@ -8718,15 +9166,18 @@ calcsize(int funcblocksize, union node *n)
8718 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect); 9166 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8719 funcblocksize = calcsize(funcblocksize, n->ncmd.args); 9167 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8720 funcblocksize = calcsize(funcblocksize, n->ncmd.assign); 9168 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
9169 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8721 break; 9170 break;
8722 case NPIPE: 9171 case NPIPE:
8723 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist); 9172 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
9173 IF_PLATFORM_MINGW32(nodeptrcount++);
8724 break; 9174 break;
8725 case NREDIR: 9175 case NREDIR:
8726 case NBACKGND: 9176 case NBACKGND:
8727 case NSUBSHELL: 9177 case NSUBSHELL:
8728 funcblocksize = calcsize(funcblocksize, n->nredir.redirect); 9178 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8729 funcblocksize = calcsize(funcblocksize, n->nredir.n); 9179 funcblocksize = calcsize(funcblocksize, n->nredir.n);
9180 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8730 break; 9181 break;
8731 case NAND: 9182 case NAND:
8732 case NOR: 9183 case NOR:
@@ -8735,34 +9186,41 @@ calcsize(int funcblocksize, union node *n)
8735 case NUNTIL: 9186 case NUNTIL:
8736 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2); 9187 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8737 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1); 9188 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
9189 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8738 break; 9190 break;
8739 case NIF: 9191 case NIF:
8740 funcblocksize = calcsize(funcblocksize, n->nif.elsepart); 9192 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
8741 funcblocksize = calcsize(funcblocksize, n->nif.ifpart); 9193 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
8742 funcblocksize = calcsize(funcblocksize, n->nif.test); 9194 funcblocksize = calcsize(funcblocksize, n->nif.test);
9195 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8743 break; 9196 break;
8744 case NFOR: 9197 case NFOR:
8745 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */ 9198 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
8746 funcblocksize = calcsize(funcblocksize, n->nfor.body); 9199 funcblocksize = calcsize(funcblocksize, n->nfor.body);
8747 funcblocksize = calcsize(funcblocksize, n->nfor.args); 9200 funcblocksize = calcsize(funcblocksize, n->nfor.args);
9201 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8748 break; 9202 break;
8749 case NCASE: 9203 case NCASE:
8750 funcblocksize = calcsize(funcblocksize, n->ncase.cases); 9204 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
8751 funcblocksize = calcsize(funcblocksize, n->ncase.expr); 9205 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
9206 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8752 break; 9207 break;
8753 case NCLIST: 9208 case NCLIST:
8754 funcblocksize = calcsize(funcblocksize, n->nclist.body); 9209 funcblocksize = calcsize(funcblocksize, n->nclist.body);
8755 funcblocksize = calcsize(funcblocksize, n->nclist.pattern); 9210 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
8756 funcblocksize = calcsize(funcblocksize, n->nclist.next); 9211 funcblocksize = calcsize(funcblocksize, n->nclist.next);
9212 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8757 break; 9213 break;
8758 case NDEFUN: 9214 case NDEFUN:
8759 funcblocksize = calcsize(funcblocksize, n->ndefun.body); 9215 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
8760 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1); 9216 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
9217 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8761 break; 9218 break;
8762 case NARG: 9219 case NARG:
8763 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote); 9220 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
8764 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */ 9221 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
8765 funcblocksize = calcsize(funcblocksize, n->narg.next); 9222 funcblocksize = calcsize(funcblocksize, n->narg.next);
9223 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8766 break; 9224 break;
8767 case NTO: 9225 case NTO:
8768#if BASH_REDIR_OUTPUT 9226#if BASH_REDIR_OUTPUT
@@ -8774,33 +9232,65 @@ calcsize(int funcblocksize, union node *n)
8774 case NAPPEND: 9232 case NAPPEND:
8775 funcblocksize = calcsize(funcblocksize, n->nfile.fname); 9233 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
8776 funcblocksize = calcsize(funcblocksize, n->nfile.next); 9234 funcblocksize = calcsize(funcblocksize, n->nfile.next);
9235 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8777 break; 9236 break;
8778 case NTOFD: 9237 case NTOFD:
8779 case NFROMFD: 9238 case NFROMFD:
8780 funcblocksize = calcsize(funcblocksize, n->ndup.vname); 9239 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
8781 funcblocksize = calcsize(funcblocksize, n->ndup.next); 9240 funcblocksize = calcsize(funcblocksize, n->ndup.next);
9241 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8782 break; 9242 break;
8783 case NHERE: 9243 case NHERE:
8784 case NXHERE: 9244 case NXHERE:
8785 funcblocksize = calcsize(funcblocksize, n->nhere.doc); 9245 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
8786 funcblocksize = calcsize(funcblocksize, n->nhere.next); 9246 funcblocksize = calcsize(funcblocksize, n->nhere.next);
9247 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8787 break; 9248 break;
8788 case NNOT: 9249 case NNOT:
8789 funcblocksize = calcsize(funcblocksize, n->nnot.com); 9250 funcblocksize = calcsize(funcblocksize, n->nnot.com);
9251 IF_PLATFORM_MINGW32(nodeptrcount++);
8790 break; 9252 break;
8791 }; 9253 };
8792 return funcblocksize; 9254 return funcblocksize;
8793} 9255}
8794 9256
8795static char * 9257static char *
8796nodeckstrdup(char *s) 9258nodeckstrdup(const char *s)
8797{ 9259{
9260#if ENABLE_PLATFORM_MINGW32
9261 if(!s)
9262 return NULL;
9263#endif
8798 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 9264 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
8799 return strcpy(funcstring_end, s); 9265 return strcpy(funcstring_end, s);
8800} 9266}
8801 9267
8802static union node *copynode(union node *); 9268static union node *copynode(union node *);
8803 9269
9270#if ENABLE_PLATFORM_MINGW32
9271# define SAVE_PTR(dst) {if (nodeptr) *nodeptr++ = (char **)&(dst);}
9272# define SAVE_PTR2(dst1,dst2) {if (nodeptr) { *nodeptr++ = (char **)&(dst1);*nodeptr++ = (char **)&(dst2);}}
9273# define SAVE_PTR3(dst1,dst2,dst3) {if (nodeptr) { *nodeptr++ = (char **)&(dst1);*nodeptr++ = (char **)&(dst2);*nodeptr++ = (char **)&(dst3);}}
9274#else
9275# define SAVE_PTR(dst)
9276# define SAVE_PTR2(dst,dst2)
9277# define SAVE_PTR3(dst,dst2,dst3)
9278#endif
9279
9280#if ENABLE_PLATFORM_MINGW32 && FORKSHELL_DEBUG
9281# define ANNOT_NO_DUP(note) {if (annot) annot[annot_count++] = note;}
9282# define ANNOT(note) {ANNOT_NO_DUP(xstrdup(note));}
9283# define ANNOT2(n1,n2) {ANNOT(n1); ANNOT(n2)}
9284# define ANNOT3(n1,n2,n3) {ANNOT2(n1,n2); ANNOT(n3);}
9285# define ANNOT4(n1,n2,n3,n4) {ANNOT3(n1,n2,n3); ANNOT(n4);}
9286#else
9287# define ANNOT_NO_DUP(note)
9288# define ANNOT(note)
9289# define ANNOT2(n1,n2)
9290# define ANNOT3(n1,n2,n3)
9291# define ANNOT4(n1,n2,n3,n4)
9292#endif
9293
8804static struct nodelist * 9294static struct nodelist *
8805copynodelist(struct nodelist *lp) 9295copynodelist(struct nodelist *lp)
8806{ 9296{
@@ -8812,6 +9302,8 @@ copynodelist(struct nodelist *lp)
8812 *lpp = funcblock; 9302 *lpp = funcblock;
8813 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 9303 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8814 (*lpp)->n = copynode(lp->n); 9304 (*lpp)->n = copynode(lp->n);
9305 SAVE_PTR2((*lpp)->n, (*lpp)->next);
9306 ANNOT2("(*lpp)->n","(*lpp)->next");
8815 lp = lp->next; 9307 lp = lp->next;
8816 lpp = &(*lpp)->next; 9308 lpp = &(*lpp)->next;
8817 } 9309 }
@@ -8835,10 +9327,14 @@ copynode(union node *n)
8835 new->ncmd.args = copynode(n->ncmd.args); 9327 new->ncmd.args = copynode(n->ncmd.args);
8836 new->ncmd.assign = copynode(n->ncmd.assign); 9328 new->ncmd.assign = copynode(n->ncmd.assign);
8837 new->ncmd.linno = n->ncmd.linno; 9329 new->ncmd.linno = n->ncmd.linno;
9330 SAVE_PTR3(new->ncmd.redirect,new->ncmd.args, new->ncmd.assign);
9331 ANNOT3("ncmd.redirect","ncmd.args","ncmd.assign");
8838 break; 9332 break;
8839 case NPIPE: 9333 case NPIPE:
8840 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 9334 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8841 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 9335 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
9336 SAVE_PTR(new->npipe.cmdlist);
9337 ANNOT("npipe.cmdlist");
8842 break; 9338 break;
8843 case NREDIR: 9339 case NREDIR:
8844 case NBACKGND: 9340 case NBACKGND:
@@ -8846,6 +9342,8 @@ copynode(union node *n)
8846 new->nredir.redirect = copynode(n->nredir.redirect); 9342 new->nredir.redirect = copynode(n->nredir.redirect);
8847 new->nredir.n = copynode(n->nredir.n); 9343 new->nredir.n = copynode(n->nredir.n);
8848 new->nredir.linno = n->nredir.linno; 9344 new->nredir.linno = n->nredir.linno;
9345 SAVE_PTR2(new->nredir.redirect,new->nredir.n);
9346 ANNOT2("nredir.redirect","nredir.n");
8849 break; 9347 break;
8850 case NAND: 9348 case NAND:
8851 case NOR: 9349 case NOR:
@@ -8854,37 +9352,54 @@ copynode(union node *n)
8854 case NUNTIL: 9352 case NUNTIL:
8855 new->nbinary.ch2 = copynode(n->nbinary.ch2); 9353 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8856 new->nbinary.ch1 = copynode(n->nbinary.ch1); 9354 new->nbinary.ch1 = copynode(n->nbinary.ch1);
9355 SAVE_PTR2(new->nbinary.ch1,new->nbinary.ch2);
9356 ANNOT2("nbinary.ch1","nbinary.ch2");
8857 break; 9357 break;
8858 case NIF: 9358 case NIF:
8859 new->nif.elsepart = copynode(n->nif.elsepart); 9359 new->nif.elsepart = copynode(n->nif.elsepart);
8860 new->nif.ifpart = copynode(n->nif.ifpart); 9360 new->nif.ifpart = copynode(n->nif.ifpart);
8861 new->nif.test = copynode(n->nif.test); 9361 new->nif.test = copynode(n->nif.test);
9362 SAVE_PTR3(new->nif.elsepart,new->nif.ifpart,new->nif.test);
9363 ANNOT3("nif.elsepart","nif.ifpart","nif.test");
8862 break; 9364 break;
8863 case NFOR: 9365 case NFOR:
8864 new->nfor.var = nodeckstrdup(n->nfor.var); 9366 new->nfor.var = nodeckstrdup(n->nfor.var);
8865 new->nfor.body = copynode(n->nfor.body); 9367 new->nfor.body = copynode(n->nfor.body);
8866 new->nfor.args = copynode(n->nfor.args); 9368 new->nfor.args = copynode(n->nfor.args);
8867 new->nfor.linno = n->nfor.linno; 9369 new->nfor.linno = n->nfor.linno;
9370 SAVE_PTR3(new->nfor.var,new->nfor.body,new->nfor.args);
9371 ANNOT_NO_DUP(xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"));
9372 ANNOT2("nfor.body","nfor.args");
8868 break; 9373 break;
8869 case NCASE: 9374 case NCASE:
8870 new->ncase.cases = copynode(n->ncase.cases); 9375 new->ncase.cases = copynode(n->ncase.cases);
8871 new->ncase.expr = copynode(n->ncase.expr); 9376 new->ncase.expr = copynode(n->ncase.expr);
8872 new->ncase.linno = n->ncase.linno; 9377 new->ncase.linno = n->ncase.linno;
9378 SAVE_PTR2(new->ncase.cases,new->ncase.expr);
9379 ANNOT2("ncase.cases","ncase.expr");
8873 break; 9380 break;
8874 case NCLIST: 9381 case NCLIST:
8875 new->nclist.body = copynode(n->nclist.body); 9382 new->nclist.body = copynode(n->nclist.body);
8876 new->nclist.pattern = copynode(n->nclist.pattern); 9383 new->nclist.pattern = copynode(n->nclist.pattern);
8877 new->nclist.next = copynode(n->nclist.next); 9384 new->nclist.next = copynode(n->nclist.next);
9385 SAVE_PTR3(new->nclist.body,new->nclist.pattern,new->nclist.next);
9386 ANNOT3("nclist.body","nclist.pattern","nclist.next");
8878 break; 9387 break;
8879 case NDEFUN: 9388 case NDEFUN:
8880 new->ndefun.body = copynode(n->ndefun.body); 9389 new->ndefun.body = copynode(n->ndefun.body);
8881 new->ndefun.text = nodeckstrdup(n->ndefun.text); 9390 new->ndefun.text = nodeckstrdup(n->ndefun.text);
8882 new->ndefun.linno = n->ndefun.linno; 9391 new->ndefun.linno = n->ndefun.linno;
9392 SAVE_PTR2(new->ndefun.body,new->ndefun.text);
9393 ANNOT("ndefun.body");
9394 ANNOT_NO_DUP(xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"));
8883 break; 9395 break;
8884 case NARG: 9396 case NARG:
8885 new->narg.backquote = copynodelist(n->narg.backquote); 9397 new->narg.backquote = copynodelist(n->narg.backquote);
8886 new->narg.text = nodeckstrdup(n->narg.text); 9398 new->narg.text = nodeckstrdup(n->narg.text);
8887 new->narg.next = copynode(n->narg.next); 9399 new->narg.next = copynode(n->narg.next);
9400 SAVE_PTR3(new->narg.backquote,new->narg.text,new->narg.next);
9401 ANNOT2("narg.backquote","narg.next");
9402 ANNOT_NO_DUP(xasprintf("narg.text '%s'", n->narg.text ?: "NULL"));
8888 break; 9403 break;
8889 case NTO: 9404 case NTO:
8890#if BASH_REDIR_OUTPUT 9405#if BASH_REDIR_OUTPUT
@@ -8897,6 +9412,8 @@ copynode(union node *n)
8897 new->nfile.fname = copynode(n->nfile.fname); 9412 new->nfile.fname = copynode(n->nfile.fname);
8898 new->nfile.fd = n->nfile.fd; 9413 new->nfile.fd = n->nfile.fd;
8899 new->nfile.next = copynode(n->nfile.next); 9414 new->nfile.next = copynode(n->nfile.next);
9415 SAVE_PTR2(new->nfile.fname,new->nfile.next);
9416 ANNOT2("nfile.fname","nfile.next");
8900 break; 9417 break;
8901 case NTOFD: 9418 case NTOFD:
8902 case NFROMFD: 9419 case NFROMFD:
@@ -8904,15 +9421,21 @@ copynode(union node *n)
8904 new->ndup.dupfd = n->ndup.dupfd; 9421 new->ndup.dupfd = n->ndup.dupfd;
8905 new->ndup.fd = n->ndup.fd; 9422 new->ndup.fd = n->ndup.fd;
8906 new->ndup.next = copynode(n->ndup.next); 9423 new->ndup.next = copynode(n->ndup.next);
9424 SAVE_PTR2(new->ndup.vname,new->ndup.next);
9425 ANNOT2("ndup.vname","ndup.next");
8907 break; 9426 break;
8908 case NHERE: 9427 case NHERE:
8909 case NXHERE: 9428 case NXHERE:
8910 new->nhere.doc = copynode(n->nhere.doc); 9429 new->nhere.doc = copynode(n->nhere.doc);
8911 new->nhere.fd = n->nhere.fd; 9430 new->nhere.fd = n->nhere.fd;
8912 new->nhere.next = copynode(n->nhere.next); 9431 new->nhere.next = copynode(n->nhere.next);
9432 SAVE_PTR2(new->nhere.doc,new->nhere.next);
9433 ANNOT2("nhere.doc","nhere.next");
8913 break; 9434 break;
8914 case NNOT: 9435 case NNOT:
8915 new->nnot.com = copynode(n->nnot.com); 9436 new->nnot.com = copynode(n->nnot.com);
9437 SAVE_PTR(new->nnot.com);
9438 ANNOT("nnot.com");
8916 break; 9439 break;
8917 }; 9440 };
8918 new->type = n->type; 9441 new->type = n->type;
@@ -8933,6 +9456,7 @@ copyfunc(union node *n)
8933 f = ckzalloc(blocksize /* + funcstringsize */); 9456 f = ckzalloc(blocksize /* + funcstringsize */);
8934 funcblock = (char *) f + offsetof(struct funcnode, n); 9457 funcblock = (char *) f + offsetof(struct funcnode, n);
8935 funcstring_end = (char *) f + blocksize; 9458 funcstring_end = (char *) f + blocksize;
9459 IF_PLATFORM_MINGW32(nodeptr = NULL);
8936 copynode(n); 9460 copynode(n);
8937 /* f->count = 0; - ckzalloc did it */ 9461 /* f->count = 0; - ckzalloc did it */
8938 return f; 9462 return f;
@@ -8959,7 +9483,6 @@ defun(union node *func)
8959#define SKIPFUNC (1 << 2) 9483#define SKIPFUNC (1 << 2)
8960static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 9484static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
8961static int skipcount; /* number of levels to skip */ 9485static int skipcount; /* number of levels to skip */
8962static int loopnest; /* current loop nesting level */
8963static int funcline; /* starting line number of current function, or 0 if not in a function */ 9486static int funcline; /* starting line number of current function, or 0 if not in a function */
8964 9487
8965/* Forward decl way out to parsing code - dotrap needs it */ 9488/* Forward decl way out to parsing code - dotrap needs it */
@@ -9281,6 +9804,7 @@ evalcase(union node *n, int flags)
9281static int 9804static int
9282evalsubshell(union node *n, int flags) 9805evalsubshell(union node *n, int flags)
9283{ 9806{
9807 IF_PLATFORM_MINGW32(struct forkshell fs;)
9284 struct job *jp; 9808 struct job *jp;
9285 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 9809 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9286 int status; 9810 int status;
@@ -9296,12 +9820,21 @@ evalsubshell(union node *n, int flags)
9296 if (backgnd == FORK_FG) 9820 if (backgnd == FORK_FG)
9297 get_tty_state(); 9821 get_tty_state();
9298 jp = makejob(/*n,*/ 1); 9822 jp = makejob(/*n,*/ 1);
9823#if ENABLE_PLATFORM_MINGW32
9824 memset(&fs, 0, sizeof(fs));
9825 fs.fpid = FS_EVALSUBSHELL;
9826 fs.n = n;
9827 fs.flags = flags;
9828 spawn_forkshell(jp, &fs, backgnd);
9829 if ( 0 ) {
9830#else
9299 if (forkshell(jp, n, backgnd) == 0) { 9831 if (forkshell(jp, n, backgnd) == 0) {
9300 /* child */ 9832 /* child */
9301 INT_ON; 9833 INT_ON;
9302 flags |= EV_EXIT; 9834 flags |= EV_EXIT;
9303 if (backgnd) 9835 if (backgnd)
9304 flags &= ~EV_TESTED; 9836 flags &= ~EV_TESTED;
9837#endif
9305 nofork: 9838 nofork:
9306 redirect(n->nredir.redirect, 0); 9839 redirect(n->nredir.redirect, 0);
9307 evaltreenr(n->nredir.n, flags); 9840 evaltreenr(n->nredir.n, flags);
@@ -9388,6 +9921,7 @@ expredir(union node *n)
9388static int 9921static int
9389evalpipe(union node *n, int flags) 9922evalpipe(union node *n, int flags)
9390{ 9923{
9924 IF_PLATFORM_MINGW32(struct forkshell fs;)
9391 struct job *jp; 9925 struct job *jp;
9392 struct nodelist *lp; 9926 struct nodelist *lp;
9393 int pipelen; 9927 int pipelen;
@@ -9414,6 +9948,16 @@ evalpipe(union node *n, int flags)
9414 ash_msg_and_raise_perror("can't create pipe"); 9948 ash_msg_and_raise_perror("can't create pipe");
9415 } 9949 }
9416 } 9950 }
9951#if ENABLE_PLATFORM_MINGW32
9952 memset(&fs, 0, sizeof(fs));
9953 fs.fpid = FS_EVALPIPE;
9954 fs.flags = flags;
9955 fs.n = lp->n;
9956 fs.fd[0] = pip[0];
9957 fs.fd[1] = pip[1];
9958 fs.fd[2] = prevfd;
9959 spawn_forkshell(jp, &fs, n->npipe.pipe_backgnd);
9960#else
9417 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 9961 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9418 /* child */ 9962 /* child */
9419 INT_ON; 9963 INT_ON;
@@ -9431,6 +9975,7 @@ evalpipe(union node *n, int flags)
9431 evaltreenr(lp->n, flags); 9975 evaltreenr(lp->n, flags);
9432 /* never returns */ 9976 /* never returns */
9433 } 9977 }
9978#endif
9434 /* parent */ 9979 /* parent */
9435 if (prevfd >= 0) 9980 if (prevfd >= 0)
9436 close(prevfd); 9981 close(prevfd);
@@ -9961,6 +10506,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
9961 * as POSIX mandates */ 10506 * as POSIX mandates */
9962 return back_exitstatus; 10507 return back_exitstatus;
9963} 10508}
10509
9964static int 10510static int
9965evalcommand(union node *cmd, int flags) 10511evalcommand(union node *cmd, int flags)
9966{ 10512{
@@ -9985,6 +10531,9 @@ evalcommand(union node *cmd, int flags)
9985 int status; 10531 int status;
9986 char **nargv; 10532 char **nargv;
9987 smallint cmd_is_exec; 10533 smallint cmd_is_exec;
10534#if ENABLE_PLATFORM_MINGW32
10535 int cmdpath;
10536#endif
9988 10537
9989 errlinno = lineno = cmd->ncmd.linno; 10538 errlinno = lineno = cmd->ncmd.linno;
9990 if (funcline) 10539 if (funcline)
@@ -10053,6 +10602,7 @@ evalcommand(union node *cmd, int flags)
10053 } 10602 }
10054 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); 10603 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
10055 10604
10605#if !ENABLE_PLATFORM_MINGW32
10056 path = vpath.var_text; 10606 path = vpath.var_text;
10057 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { 10607 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10058 struct strlist **spp; 10608 struct strlist **spp;
@@ -10071,6 +10621,15 @@ evalcommand(union node *cmd, int flags)
10071 if (varcmp(p, path) == 0) 10621 if (varcmp(p, path) == 0)
10072 path = p; 10622 path = p;
10073 } 10623 }
10624#else
10625 /* Set path after any local PATH= has been processed. */
10626 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10627 struct strlist **spp = varlist.lastp;
10628 expandarg(argp, &varlist, EXP_VARTILDE);
10629 mklocal((*spp)->text);
10630 }
10631 path = vpath.var_text;
10632#endif
10074 10633
10075 /* Print the command if xflag is set. */ 10634 /* Print the command if xflag is set. */
10076 if (xflag) { 10635 if (xflag) {
@@ -10209,6 +10768,33 @@ evalcommand(union node *cmd, int flags)
10209 * in a script or a subshell does not need forking, 10768 * in a script or a subshell does not need forking,
10210 * we can just exec it. 10769 * we can just exec it.
10211 */ 10770 */
10771#if ENABLE_PLATFORM_MINGW32
10772 if (!(flags & EV_EXIT) || may_have_traps) {
10773 /* No, forking off a child is necessary */
10774 struct forkshell fs;
10775
10776 INT_OFF;
10777 memset(&fs, 0, sizeof(fs));
10778 fs.fpid = FS_SHELLEXEC;
10779 fs.argv = argv;
10780 fs.path = (char*)path;
10781 fs.fd[0] = cmdentry.u.index;
10782 fs.varlist = varlist.list;
10783 jp = makejob(/*cmd,*/ 1);
10784 spawn_forkshell(jp, &fs, FORK_FG);
10785 status = waitforjob(jp);
10786 INT_ON;
10787 TRACE(("forked child exited with %d\n", status));
10788 break;
10789 }
10790 /* If we're running 'command -p' we need to use the value stored
10791 * in path by parse_command_args(). If PATH is a local variable
10792 * listsetvar() will free the value currently in path so we need
10793 * to fetch the updated version. */
10794 cmdpath = (path != pathval());
10795 listsetvar(varlist.list, VEXPORT|VSTACK);
10796 shellexec(argv[0], argv, cmdpath ? path : pathval(), cmdentry.u.index);
10797#else
10212 if (!(flags & EV_EXIT) || may_have_traps) { 10798 if (!(flags & EV_EXIT) || may_have_traps) {
10213 /* No, forking off a child is necessary */ 10799 /* No, forking off a child is necessary */
10214 INT_OFF; 10800 INT_OFF;
@@ -10227,6 +10813,7 @@ evalcommand(union node *cmd, int flags)
10227 } 10813 }
10228 listsetvar(varlist.list, VEXPORT|VSTACK); 10814 listsetvar(varlist.list, VEXPORT|VSTACK);
10229 shellexec(argv[0], argv, path, cmdentry.u.index); 10815 shellexec(argv[0], argv, path, cmdentry.u.index);
10816#endif
10230 /* NOTREACHED */ 10817 /* NOTREACHED */
10231 } /* default */ 10818 } /* default */
10232 case CMDBUILTIN: 10819 case CMDBUILTIN:
@@ -10609,7 +11196,7 @@ preadbuffer(void)
10609 more--; 11196 more--;
10610 11197
10611 c = *q; 11198 c = *q;
10612 if (c == '\0') { 11199 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) {
10613 memmove(q, q + 1, more); 11200 memmove(q, q + 1, more);
10614 } else { 11201 } else {
10615 q++; 11202 q++;
@@ -10805,6 +11392,7 @@ popallfiles(void)
10805 unwindfiles(&basepf); 11392 unwindfiles(&basepf);
10806} 11393}
10807 11394
11395#if !ENABLE_PLATFORM_MINGW32
10808/* 11396/*
10809 * Close the file(s) that the shell is reading commands from. Called 11397 * Close the file(s) that the shell is reading commands from. Called
10810 * after a fork is done. 11398 * after a fork is done.
@@ -10818,6 +11406,7 @@ closescript(void)
10818 g_parsefile->pf_fd = 0; 11406 g_parsefile->pf_fd = 0;
10819 } 11407 }
10820} 11408}
11409#endif
10821 11410
10822/* 11411/*
10823 * Like setinputfile, but takes an open file descriptor. Call this with 11412 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -13168,7 +13757,7 @@ find_dot_file(char *name)
13168 struct stat statb; 13757 struct stat statb;
13169 13758
13170 /* don't try this for absolute or relative paths */ 13759 /* don't try this for absolute or relative paths */
13171 if (strchr(name, '/')) 13760 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\')))
13172 return name; 13761 return name;
13173 13762
13174 while ((fullname = path_advance(&path, name)) != NULL) { 13763 while ((fullname = path_advance(&path, name)) != NULL) {
@@ -13290,14 +13879,18 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13290 struct builtincmd *bcmd; 13879 struct builtincmd *bcmd;
13291 13880
13292 /* If name contains a slash, don't use PATH or hash table */ 13881 /* If name contains a slash, don't use PATH or hash table */
13293 if (strchr(name, '/') != NULL) { 13882 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\'))) {
13294 entry->u.index = -1; 13883 entry->u.index = -1;
13295 if (act & DO_ABS) { 13884 if (act & DO_ABS) {
13885#if ENABLE_PLATFORM_MINGW32
13886 if (auto_win32_extension(name) == NULL && stat(name, &statb) < 0) {
13887#else
13296 while (stat(name, &statb) < 0) { 13888 while (stat(name, &statb) < 0) {
13297#ifdef SYSV 13889#ifdef SYSV
13298 if (errno == EINTR) 13890 if (errno == EINTR)
13299 continue; 13891 continue;
13300#endif 13892#endif
13893#endif
13301 entry->cmdtype = CMDUNKNOWN; 13894 entry->cmdtype = CMDUNKNOWN;
13302 return; 13895 return;
13303 } 13896 }
@@ -13397,12 +13990,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13397 } 13990 }
13398 } 13991 }
13399 /* if rehash, don't redo absolute path names */ 13992 /* if rehash, don't redo absolute path names */
13400 if (fullname[0] == '/' && idx <= prev) { 13993 if (is_absolute_path(fullname) && idx <= prev) {
13401 if (idx < prev) 13994 if (idx < prev)
13402 continue; 13995 continue;
13403 TRACE(("searchexec \"%s\": no change\n", name)); 13996 TRACE(("searchexec \"%s\": no change\n", name));
13404 goto success; 13997 goto success;
13405 } 13998 }
13999#if ENABLE_PLATFORM_MINGW32
14000 add_win32_extension(fullname);
14001#endif
13406 while (stat(fullname, &statb) < 0) { 14002 while (stat(fullname, &statb) < 0) {
13407#ifdef SYSV 14003#ifdef SYSV
13408 if (errno == EINTR) 14004 if (errno == EINTR)
@@ -13893,6 +14489,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13893 if (!isdigit(modestr[0])) 14489 if (!isdigit(modestr[0]))
13894 mask ^= 0777; 14490 mask ^= 0777;
13895 umask(mask); 14491 umask(mask);
14492#if ENABLE_PLATFORM_MINGW32
14493 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
14494#endif
13896 } 14495 }
13897 return 0; 14496 return 0;
13898} 14497}
@@ -13945,12 +14544,17 @@ exitshell(void)
13945 14544
13946/* Don't inline: conserve stack of caller from having our locals too */ 14545/* Don't inline: conserve stack of caller from having our locals too */
13947static NOINLINE void 14546static NOINLINE void
14547#if ENABLE_PLATFORM_MINGW32
14548init(int xp)
14549#else
13948init(void) 14550init(void)
14551#endif
13949{ 14552{
13950 /* we will never free this */ 14553 /* we will never free this */
13951 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 14554 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
13952 basepf.linno = 1; 14555 basepf.linno = 1;
13953 14556
14557#if !ENABLE_PLATFORM_MINGW32
13954 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 14558 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
13955 setsignal(SIGCHLD); 14559 setsignal(SIGCHLD);
13956 14560
@@ -13958,12 +14562,89 @@ init(void)
13958 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" 14562 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
13959 */ 14563 */
13960 signal(SIGHUP, SIG_DFL); 14564 signal(SIGHUP, SIG_DFL);
14565#endif
13961 14566
13962 { 14567 {
13963 char **envp; 14568 char **envp;
13964 const char *p; 14569 const char *p;
13965 14570
13966 initvar(); 14571 initvar();
14572
14573#if ENABLE_PLATFORM_MINGW32
14574 /*
14575 * case insensitive env names from Windows world
14576 *
14577 * Some standard env names such as PATH is named Path and so on
14578 * ash itself is case sensitive, so "Path" will confuse it, as
14579 * MSVC getenv() is case insensitive.
14580 *
14581 * We may end up having both Path and PATH. Then Path will be chosen
14582 * because it appears first.
14583 */
14584 for (envp = environ; envp && *envp; envp++) {
14585 if (strncasecmp(*envp, "PATH=", 5) == 0 &&
14586 strncmp(*envp, "PATH=", 5) != 0) {
14587 break;
14588 }
14589 }
14590
14591 if (envp && *envp) {
14592 /*
14593 * If we get here it's because the environment contains a path
14594 * variable called something other than PATH. This suggests we
14595 * haven't been invoked from an earlier instance of BusyBox.
14596 */
14597 char *start, *end;
14598 struct passwd *pw;
14599
14600 for (envp = environ; envp && *envp; envp++) {
14601 if (!(end=strchr(*envp, '=')))
14602 continue;
14603
14604 /* make all variable names uppercase */
14605 for (start = *envp;start < end;start++)
14606 *start = toupper(*start);
14607
14608 /* Convert backslashes to forward slashes in value but
14609 * not if we're on Windows XP or for variables known to
14610 * cause problems */
14611 if ( !xp && strncmp(*envp, "SYSTEMROOT=", 11) != 0 &&
14612 strncmp(*envp, "COMSPEC=", 8) != 0 ) {
14613 convert_slashes(end+1);
14614 }
14615
14616 /* check for invalid characters in name */
14617 for (start = *envp;start < end;start++) {
14618 if (!isdigit(*start) && !isalpha(*start) && *start != '_') {
14619 break;
14620 }
14621 }
14622
14623 if (start != end) {
14624 /*
14625 * Make a copy of the variable, replacing invalid
14626 * characters in the name with underscores.
14627 */
14628 char *var = xstrdup(*envp);
14629
14630 for (start = var;*start != '=';start++) {
14631 if (!isdigit(*start) && !isalpha(*start)) {
14632 *start = '_';
14633 }
14634 }
14635 setvareq(var, VEXPORT|VNOSAVE);
14636 }
14637 }
14638
14639 /* Initialise some variables normally set at login, but
14640 * only if someone hasn't already set them. */
14641 pw = xgetpwuid(getuid());
14642 if (!getenv("USER")) xsetenv("USER", pw->pw_name);
14643 if (!getenv("LOGNAME")) xsetenv("LOGNAME", pw->pw_name);
14644 if (!getenv("HOME")) xsetenv("HOME", pw->pw_dir);
14645 if (!getenv("SHELL")) xsetenv("SHELL", DEFAULT_SHELL);
14646 }
14647#endif
13967 for (envp = environ; envp && *envp; envp++) { 14648 for (envp = environ; envp && *envp; envp++) {
13968/* Used to have 14649/* Used to have
13969 * p = endofname(*envp); 14650 * p = endofname(*envp);
@@ -13977,7 +14658,11 @@ init(void)
13977 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 14658 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
13978 */ 14659 */
13979 if (strchr(*envp, '=')) { 14660 if (strchr(*envp, '=')) {
14661#if !ENABLE_PLATFORM_MINGW32
13980 setvareq(*envp, VEXPORT|VTEXTFIXED); 14662 setvareq(*envp, VEXPORT|VTEXTFIXED);
14663#else
14664 setvareq(*envp, VEXPORT);
14665#endif
13981 } 14666 }
13982 } 14667 }
13983 14668
@@ -14066,6 +14751,9 @@ procargs(char **argv)
14066 goto setarg0; 14751 goto setarg0;
14067 } else if (!sflag) { 14752 } else if (!sflag) {
14068 setinputfile(*xargv, 0); 14753 setinputfile(*xargv, 0);
14754#if ENABLE_PLATFORM_MINGW32
14755 convert_slashes(*xargv);
14756#endif
14069 setarg0: 14757 setarg0:
14070 arg0 = *xargv++; 14758 arg0 = *xargv++;
14071 commandname = arg0; 14759 commandname = arg0;
@@ -14196,9 +14884,21 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14196 exception_handler = &jmploc; 14884 exception_handler = &jmploc;
14197 rootpid = getpid(); 14885 rootpid = getpid();
14198 14886
14199 init(); 14887 init(IF_PLATFORM_MINGW32(argc >= 2 && strcmp(argv[1], "-X") == 0));
14200 setstackmark(&smark); 14888 setstackmark(&smark);
14201 14889
14890#if ENABLE_PLATFORM_MINGW32
14891 hSIGINT = CreateEvent(NULL, TRUE, FALSE, NULL);
14892 SetConsoleCtrlHandler(ctrl_handler, TRUE);
14893
14894 if (argc == 3 && !strcmp(argv[1], "--fs")) {
14895 forkshell_init(argv[2]);
14896
14897 /* NOTREACHED */
14898 bb_error_msg_and_die("forkshell failed");
14899 }
14900#endif
14901
14202#if NUM_SCRIPTS > 0 14902#if NUM_SCRIPTS > 0
14203 if (argc < 0) 14903 if (argc < 0)
14204 /* Non-NULL minusc tells procargs that an embedded script is being run */ 14904 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14210,9 +14910,24 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14210 trace_puts_args(argv); 14910 trace_puts_args(argv);
14211#endif 14911#endif
14212 14912
14913#if ENABLE_ASH_NOCONSOLE
14914 if ( noconsole ) {
14915 DWORD dummy;
14916
14917 if ( GetConsoleProcessList(&dummy, 1) == 1 ) {
14918 ShowWindow(GetConsoleWindow(), SW_HIDE);
14919 }
14920 }
14921#endif
14922
14213 if (login_sh) { 14923 if (login_sh) {
14214 const char *hp; 14924 const char *hp;
14215 14925
14926#if ENABLE_PLATFORM_MINGW32
14927 chdir(xgetpwuid(getuid())->pw_dir);
14928 setpwd(NULL, 0);
14929#endif
14930
14216 state = 1; 14931 state = 1;
14217 read_profile("/etc/profile"); 14932 read_profile("/etc/profile");
14218 state1: 14933 state1:
@@ -14287,6 +15002,809 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14287 /* NOTREACHED */ 15002 /* NOTREACHED */
14288} 15003}
14289 15004
15005#if ENABLE_PLATFORM_MINGW32
15006static void
15007forkshell_openhere(struct forkshell *fs)
15008{
15009 union node *redir = fs->n;
15010 int pip[2];
15011
15012 pip[0] = fs->fd[0];
15013 pip[1] = fs->fd[1];
15014
15015 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15016
15017 close(pip[0]);
15018 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
15019 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
15020 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
15021 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
15022 signal(SIGPIPE, SIG_DFL);
15023 if (redir->type == NHERE) {
15024 size_t len = strlen(redir->nhere.doc->narg.text);
15025 full_write(pip[1], redir->nhere.doc->narg.text, len);
15026 } else /* NXHERE */
15027 expandhere(redir->nhere.doc, pip[1]);
15028 _exit(EXIT_SUCCESS);
15029}
15030
15031static void
15032forkshell_evalbackcmd(struct forkshell *fs)
15033{
15034 union node *n = fs->n;
15035 int pip[2] = {fs->fd[0], fs->fd[1]};
15036
15037 FORCE_INT_ON;
15038 close(pip[0]);
15039 if (pip[1] != 1) {
15040 /*close(1);*/
15041 dup2_or_raise(pip[1], 1);
15042 close(pip[1]);
15043 }
15044 eflag = 0;
15045 ifsfree();
15046 evaltreenr(n, EV_EXIT);
15047 /* NOTREACHED */
15048}
15049
15050static void
15051forkshell_evalsubshell(struct forkshell *fs)
15052{
15053 union node *n = fs->n;
15054 int flags = fs->flags;
15055
15056 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15057 INT_ON;
15058 flags |= EV_EXIT;
15059 expredir(n->nredir.redirect);
15060 redirect(n->nredir.redirect, 0);
15061 evaltreenr(n->nredir.n, flags);
15062 /* never returns */
15063}
15064
15065static void
15066forkshell_evalpipe(struct forkshell *fs)
15067{
15068 union node *n = fs->n;
15069 int flags = fs->flags;
15070 int prevfd = fs->fd[2];
15071 int pip[2] = {fs->fd[0], fs->fd[1]};
15072
15073 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15074 INT_ON;
15075 if (pip[1] >= 0) {
15076 close(pip[0]);
15077 }
15078 if (prevfd > 0) {
15079 dup2(prevfd, 0);
15080 close(prevfd);
15081 }
15082 if (pip[1] > 1) {
15083 dup2(pip[1], 1);
15084 close(pip[1]);
15085 }
15086 evaltreenr(n, flags);
15087}
15088
15089static void
15090forkshell_shellexec(struct forkshell *fs)
15091{
15092 int idx = fs->fd[0];
15093 struct strlist *varlist = fs->varlist;
15094 char **argv = fs->argv;
15095 char *path = fs->path;
15096
15097 FORCE_INT_ON;
15098 listsetvar(varlist, VEXPORT|VSTACK);
15099 shellexec(argv[0], argv, path, idx);
15100}
15101
15102static void
15103forkshell_child(struct forkshell *fs)
15104{
15105 switch ( fs->fpid ) {
15106 case FS_OPENHERE:
15107 forkshell_openhere(fs);
15108 break;
15109 case FS_EVALBACKCMD:
15110 forkshell_evalbackcmd(fs);
15111 break;
15112 case FS_EVALSUBSHELL:
15113 forkshell_evalsubshell(fs);
15114 break;
15115 case FS_EVALPIPE:
15116 forkshell_evalpipe(fs);
15117 break;
15118 case FS_SHELLEXEC:
15119 forkshell_shellexec(fs);
15120 break;
15121 }
15122}
15123
15124/*
15125 * Reinitialise the builtin environment variables in varinit. Their
15126 * current settings have been copied from the parent in vartab. Look
15127 * these up using the names from varinit_data, copy the details from
15128 * vartab to varinit and replace the old copy in vartab with the new
15129 * one in varinit.
15130 *
15131 * Also reinitialise the function pointers and line number variable.
15132 */
15133static void
15134reinitvar(void)
15135{
15136 int i;
15137 const char *name;
15138 struct var **vpp, **old;
15139
15140 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
15141 name = varinit_data[i].var_text ? varinit_data[i].var_text : "LINENO=";
15142 vpp = hashvar(name);
15143 if ( (old=findvar(vpp, name)) != NULL ) {
15144 varinit[i] = **old;
15145 *old = varinit+i;
15146 }
15147 varinit[i].var_func = varinit_data[i].var_func;
15148 }
15149 vlineno.var_text = linenovar;
15150}
15151
15152static void
15153spawn_forkshell(struct job *jp, struct forkshell *fs, int mode)
15154{
15155 struct forkshell *new;
15156 char buf[32];
15157 const char *argv[] = { "sh", "--fs", NULL, NULL };
15158 intptr_t ret;
15159
15160 new = forkshell_prepare(fs);
15161 new->mode = mode;
15162 new->nprocs = jp == NULL ? 0 : jp->nprocs;
15163 sprintf(buf, "%p", new->hMapFile);
15164 argv[2] = buf;
15165 ret = mingw_spawn_proc(argv);
15166 CloseHandle(new->hMapFile);
15167 UnmapViewOfFile(new);
15168 if (ret == -1) {
15169 if (jp)
15170 freejob(jp);
15171 ash_msg_and_raise_error("unable to spawn shell");
15172 }
15173 forkparent(jp, fs->node, mode, (HANDLE)ret);
15174}
15175
15176/*
15177 * forkshell_prepare() and friends
15178 *
15179 * The sequence is as follows:
15180 * - funcblocksize, nodeptrcount are initialized
15181 * - forkshell_size(fs) is called to calculate the exact memory needed
15182 * - a new struct is allocated
15183 * - funcblock, funcstring, nodeptr are initialized from the new block
15184 * - forkshell_copy(fs) is called to copy recursively everything over
15185 * it will record all pointers along the way, to nodeptr
15186 *
15187 * When this memory is mapped elsewhere, pointer fixup will be needed
15188 */
15189
15190/* redefine without test that nodeptr is non-NULL */
15191#undef SAVE_PTR
15192#undef SAVE_PTR2
15193#undef SAVE_PTR3
15194#define SAVE_PTR(dst) {*nodeptr++ = (char **)&(dst);}
15195#define SAVE_PTR2(dst1,dst2) {SAVE_PTR(dst1); SAVE_PTR(dst2);}
15196#define SAVE_PTR3(dst1,dst2,dst3) {SAVE_PTR2(dst1,dst2); SAVE_PTR(dst3);}
15197#define SAVE_PTR4(dst1,dst2,dst3,dst4) {SAVE_PTR2(dst1,dst2); SAVE_PTR2(dst3,dst4);}
15198
15199static int align_len(const char *s)
15200{
15201 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
15202}
15203
15204#define SLIST_SIZE_BEGIN(name,type) \
15205static int \
15206name(int funcblocksize, type *p) \
15207{ \
15208 while (p) { \
15209 funcblocksize += sizeof(type);
15210 /* do something here with p */
15211#define SLIST_SIZE_END() \
15212 nodeptrcount++; \
15213 p = p->next; \
15214 } \
15215 return funcblocksize; \
15216}
15217
15218#define SLIST_COPY_BEGIN(name,type) \
15219static type * \
15220name(type *vp) \
15221{ \
15222 type *start; \
15223 type **vpp; \
15224 vpp = &start; \
15225 while (vp) { \
15226 *vpp = funcblock; \
15227 funcblock = (char *) funcblock + sizeof(type);
15228 /* do something here with vpp and vp */
15229#define SLIST_COPY_END() \
15230 SAVE_PTR((*vpp)->next); \
15231 ANNOT("(*vpp)->next"); \
15232 vp = vp->next; \
15233 vpp = &(*vpp)->next; \
15234 } \
15235 *vpp = NULL; \
15236 return start; \
15237}
15238
15239/*
15240 * struct var
15241 */
15242SLIST_SIZE_BEGIN(var_size,struct var)
15243funcblocksize += align_len(p->var_text);
15244nodeptrcount++; /* p->text */
15245SLIST_SIZE_END()
15246
15247SLIST_COPY_BEGIN(var_copy,struct var)
15248(*vpp)->var_text = nodeckstrdup(vp->var_text);
15249(*vpp)->flags = vp->flags;
15250(*vpp)->var_func = NULL;
15251SAVE_PTR((*vpp)->var_text);
15252ANNOT_NO_DUP(xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"));
15253SLIST_COPY_END()
15254
15255/*
15256 * struct strlist
15257 */
15258SLIST_SIZE_BEGIN(strlist_size,struct strlist)
15259funcblocksize += align_len(p->text);
15260nodeptrcount++; /* p->text */
15261SLIST_SIZE_END()
15262
15263SLIST_COPY_BEGIN(strlist_copy,struct strlist)
15264(*vpp)->text = nodeckstrdup(vp->text);
15265SAVE_PTR((*vpp)->text);
15266ANNOT_NO_DUP(xasprintf("(*vpp)->text '%s'", vp->text ?: "NULL"));
15267SLIST_COPY_END()
15268
15269/*
15270 * struct tblentry
15271 */
15272static int
15273tblentry_size(int funcblocksize, struct tblentry *tep)
15274{
15275 while (tep) {
15276 funcblocksize += sizeof(struct tblentry) + strlen(tep->cmdname);
15277 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
15278 if (tep->cmdtype == CMDFUNCTION) {
15279 funcblocksize += offsetof(struct funcnode, n);
15280 funcblocksize = calcsize(funcblocksize, &tep->param.func->n);
15281 nodeptrcount++; /* tep->param.func */
15282 }
15283 nodeptrcount++; /* tep->next */
15284 tep = tep->next;
15285 }
15286 return funcblocksize;
15287}
15288
15289static struct tblentry *
15290tblentry_copy(struct tblentry *tep)
15291{
15292 struct tblentry *start;
15293 struct tblentry **newp;
15294 int size;
15295
15296 newp = &start;
15297 while (tep) {
15298 *newp = funcblock;
15299 size = sizeof(struct tblentry) + strlen(tep->cmdname);
15300
15301 funcblock = (char *) funcblock + size;
15302 memcpy(*newp, tep, size);
15303 switch (tep->cmdtype) {
15304 case CMDBUILTIN:
15305 /* Save index of builtin, not pointer; fixed by forkshell_init() */
15306 (*newp)->param.index = tep->param.cmd - builtintab;
15307 break;
15308 case CMDFUNCTION:
15309 (*newp)->param.func = funcblock;
15310 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
15311 copynode(&tep->param.func->n);
15312 SAVE_PTR((*newp)->param.func);
15313 ANNOT("param.func");
15314 break;
15315 default:
15316 break;
15317 }
15318 SAVE_PTR((*newp)->next);
15319 ANNOT_NO_DUP(xasprintf("cmdname '%s'", tep->cmdname));
15320 tep = tep->next;
15321 newp = &(*newp)->next;
15322 }
15323 *newp = NULL;
15324 return start;
15325}
15326
15327static int
15328cmdtable_size(int funcblocksize, struct tblentry **cmdtablep)
15329{
15330 int i;
15331 nodeptrcount += CMDTABLESIZE;
15332 funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
15333 for (i = 0; i < CMDTABLESIZE; i++)
15334 funcblocksize = tblentry_size(funcblocksize, cmdtablep[i]);
15335 return funcblocksize;
15336}
15337
15338static struct tblentry **
15339cmdtable_copy(struct tblentry **cmdtablep)
15340{
15341 struct tblentry **new = funcblock;
15342 int i;
15343
15344 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
15345 for (i = 0; i < CMDTABLESIZE; i++) {
15346 new[i] = tblentry_copy(cmdtablep[i]);
15347 SAVE_PTR(new[i]);
15348 ANNOT_NO_DUP(xasprintf("cmdtablep[%d]", i));
15349 }
15350 return new;
15351}
15352
15353/*
15354 * char **
15355 */
15356static int
15357argv_size(int funcblocksize, char **p)
15358{
15359 while (p && *p) {
15360 funcblocksize += sizeof(char *);
15361 funcblocksize += align_len(*p);
15362 nodeptrcount++;
15363 p++;
15364 }
15365 funcblocksize += sizeof(char *);
15366 return funcblocksize;
15367}
15368
15369static char **
15370argv_copy(char **p)
15371{
15372 char **new, **start = funcblock;
15373#if FORKSHELL_DEBUG
15374 int i = 0;
15375#endif
15376
15377 while (p && *p) {
15378 new = funcblock;
15379 funcblock = (char *) funcblock + sizeof(char *);
15380 *new = nodeckstrdup(*p);
15381 SAVE_PTR(*new);
15382 ANNOT_NO_DUP(xasprintf("argv[%d] '%s'", i++, *p));
15383 p++;
15384 new++;
15385 }
15386 new = funcblock;
15387 funcblock = (char *) funcblock + sizeof(char *);
15388 *new = NULL;
15389 return start;
15390}
15391
15392/*
15393 * struct redirtab
15394 */
15395static int
15396redirtab_size(int funcblocksize, struct redirtab *rdtp)
15397{
15398 while (rdtp) {
15399 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
15400 rdtp = rdtp->next;
15401 nodeptrcount++; /* rdtp->next */
15402 }
15403 return funcblocksize;
15404}
15405
15406static struct redirtab *
15407redirtab_copy(struct redirtab *rdtp)
15408{
15409 struct redirtab *start;
15410 struct redirtab **vpp;
15411
15412 vpp = &start;
15413 while (rdtp) {
15414 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
15415 *vpp = funcblock;
15416 funcblock = (char *) funcblock + size;
15417 memcpy(*vpp, rdtp, size);
15418 SAVE_PTR((*vpp)->next);
15419 ANNOT("(*vpp)->next");
15420 rdtp = rdtp->next;
15421 vpp = &(*vpp)->next;
15422 }
15423 *vpp = NULL;
15424 return start;
15425}
15426
15427#undef shellparam
15428#undef redirlist
15429#undef vartab
15430static int
15431globals_var_size(int funcblocksize, struct globals_var *gvp)
15432{
15433 int i;
15434
15435 funcblocksize += sizeof(struct globals_var);
15436 funcblocksize = argv_size(funcblocksize, gvp->shellparam.p);
15437 funcblocksize = redirtab_size(funcblocksize, gvp->redirlist);
15438 for (i = 0; i < VTABSIZE; i++)
15439 funcblocksize = var_size(funcblocksize, gvp->vartab[i]);
15440 /* gvp->redirlist, gvp->shellparam.p, vartab */
15441 nodeptrcount += 2 + VTABSIZE;
15442 return funcblocksize;
15443}
15444
15445static struct globals_var *
15446globals_var_copy(struct globals_var *gvp)
15447{
15448 int i;
15449 struct globals_var *new;
15450
15451 new = funcblock;
15452 funcblock = (char *) funcblock + sizeof(struct globals_var);
15453 memcpy(new, gvp, sizeof(struct globals_var));
15454
15455 /* shparam */
15456 new->shellparam.malloced = 0;
15457 new->shellparam.p = argv_copy(gvp->shellparam.p);
15458 SAVE_PTR(new->shellparam.p);
15459 ANNOT("shellparam.p");
15460
15461 new->redirlist = redirtab_copy(gvp->redirlist);
15462 SAVE_PTR(new->redirlist);
15463 ANNOT("redirlist");
15464
15465 for (i = 0; i < VTABSIZE; i++) {
15466 new->vartab[i] = var_copy(gvp->vartab[i]);
15467 SAVE_PTR(new->vartab[i]);
15468 ANNOT_NO_DUP(xasprintf("vartab[%d]", i));
15469 }
15470
15471 return new;
15472}
15473
15474#undef minusc
15475#undef curdir
15476#undef physdir
15477#undef arg0
15478#undef commandname
15479#undef nullstr
15480static int
15481globals_misc_size(int funcblocksize, struct globals_misc *p)
15482{
15483 funcblocksize += sizeof(struct globals_misc);
15484 funcblocksize += align_len(p->minusc);
15485 if (p->curdir != p->nullstr)
15486 funcblocksize += align_len(p->curdir);
15487 if (p->physdir != p->nullstr)
15488 funcblocksize += align_len(p->physdir);
15489 funcblocksize += align_len(p->arg0);
15490 funcblocksize += align_len(p->commandname);
15491 nodeptrcount += 5; /* minusc, curdir, physdir, arg0, commandname */
15492 return funcblocksize;
15493}
15494
15495static struct globals_misc *
15496globals_misc_copy(struct globals_misc *p)
15497{
15498 struct globals_misc *new = funcblock;
15499
15500 funcblock = (char *) funcblock + sizeof(struct globals_misc);
15501 memcpy(new, p, sizeof(struct globals_misc));
15502
15503 new->minusc = nodeckstrdup(p->minusc);
15504 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
15505 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
15506 new->arg0 = nodeckstrdup(p->arg0);
15507 new->commandname = nodeckstrdup(p->commandname);
15508 SAVE_PTR4(new->minusc, new->curdir, new->physdir, new->arg0);
15509 SAVE_PTR(new->commandname);
15510 ANNOT_NO_DUP(xasprintf("minusc '%s'", p->minusc ?: "NULL"));
15511 ANNOT_NO_DUP(xasprintf("curdir '%s'", new->curdir ?: "NULL"));
15512 ANNOT_NO_DUP(xasprintf("physdir '%s'", new->physdir ?: "NULL"));
15513 ANNOT_NO_DUP(xasprintf("arg0 '%s'", p->arg0 ?: "NULL");)
15514 ANNOT_NO_DUP(xasprintf("commandname '%s'", p->commandname ?: "NULL"));
15515 return new;
15516}
15517
15518static int
15519forkshell_size(int funcblocksize, struct forkshell *fs)
15520{
15521 funcblocksize = globals_var_size(funcblocksize, fs->gvp);
15522 funcblocksize = globals_misc_size(funcblocksize, fs->gmp);
15523 funcblocksize = cmdtable_size(funcblocksize, fs->cmdtable);
15524 /* optlist_transfer(sending, fd); */
15525 /* misc_transfer(sending, fd); */
15526
15527 funcblocksize = calcsize(funcblocksize, fs->n);
15528 funcblocksize = argv_size(funcblocksize, fs->argv);
15529 funcblocksize += align_len(fs->path);
15530 funcblocksize = strlist_size(funcblocksize, fs->varlist);
15531
15532 nodeptrcount += 7; /* gvp, gmp, cmdtable, n, argv, string, strlist */
15533 return funcblocksize;
15534}
15535
15536static void
15537forkshell_copy(struct forkshell *fs, struct forkshell *new)
15538{
15539 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
15540 new->gvp = globals_var_copy(fs->gvp);
15541 new->gmp = globals_misc_copy(fs->gmp);
15542 new->cmdtable = cmdtable_copy(fs->cmdtable);
15543 SAVE_PTR3(new->gvp, new->gmp, new->cmdtable);
15544 ANNOT3("gvp", "gmp", "cmdtable");
15545
15546 new->n = copynode(fs->n);
15547 new->argv = argv_copy(fs->argv);
15548 new->path = nodeckstrdup(fs->path);
15549 new->varlist = strlist_copy(fs->varlist);
15550 SAVE_PTR4(new->n, new->argv, new->path, new->varlist);
15551 ANNOT2("n", "argv");
15552 ANNOT_NO_DUP(xasprintf("path '%s'", fs->path ?: "NULL"));
15553 ANNOT("varlist");
15554}
15555
15556#if FORKSHELL_DEBUG
15557/* fp and notes can each be NULL */
15558static void
15559forkshell_print(FILE *fp0, struct forkshell *fs, char **notes)
15560{
15561 FILE *fp;
15562 void *lfuncblock;
15563 char *lfuncstring;
15564 char ***lnodeptr;
15565 char *s;
15566 int count;
15567
15568 if (fp0 != NULL) {
15569 fp = fp0;
15570 }
15571 else {
15572 char name[32];
15573
15574 sprintf(name, "fs_%d.out", getpid());
15575 if ((fp=fopen(name, "w")) == NULL)
15576 return;
15577 }
15578
15579 fprintf(fp, "size %d = %d + %d*%d + %d + %d\n", fs->size,
15580 (int)sizeof(struct forkshell), fs->nodeptrcount,
15581 (int)sizeof(char *), fs->funcblocksize, fs->funcstringsize);
15582
15583 lnodeptr = fs->nodeptr;
15584 lfuncblock = (char *)lnodeptr + (fs->nodeptrcount+1)*sizeof(char *);
15585 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
15586
15587 fprintf(fp, "funcblocksize %d = %d + %d + %d\n\n", fs->funcblocksize,
15588 (int)((char *)fs->gmp-(char *)fs->gvp),
15589 (int)((char *)fs->cmdtable-(char *)fs->gmp),
15590 (int)(lfuncstring-(char *)fs->cmdtable));
15591
15592 fprintf(fp, "--- nodeptr ---\n");
15593 count = 0;
15594 if (notes == NULL) {
15595 while (*lnodeptr) {
15596 fprintf(fp, "%p ", *lnodeptr++);
15597 if ((count&3) == 3)
15598 fprintf(fp, "\n");
15599 ++count;
15600 }
15601 if (((count-1)&3) != 3)
15602 fprintf(fp, "\n");
15603 }
15604 else {
15605 while (*lnodeptr) {
15606 fprintf(fp, "%p %p %s\n", *lnodeptr, **lnodeptr, notes[count++]);
15607 lnodeptr++;
15608 }
15609 }
15610 if (count != fs->nodeptrcount)
15611 fprintf(fp, "--- %d pointers (expected %d) ---\n\n", count,
15612 fs->nodeptrcount);
15613 else
15614 fprintf(fp, "--- %d pointers ---\n\n", count);
15615
15616 fprintf(fp, "--- funcstring ---\n");
15617 count = 0;
15618 s = lfuncstring;
15619 while (s-lfuncstring < fs->funcstringsize) {
15620 if (!*s) {
15621 ++s;
15622 continue;
15623 }
15624 fprintf(fp, "%p '%s'\n", s, s);
15625 s += strlen(s)+1;
15626 ++count;
15627 }
15628 fprintf(fp, "--- %d strings ---\n", count);
15629
15630 if (fp0 == NULL)
15631 fclose(fp);
15632}
15633#endif
15634
15635static struct forkshell *
15636forkshell_prepare(struct forkshell *fs)
15637{
15638 int funcblocksize;
15639 struct forkshell *new;
15640 int size;
15641 HANDLE h;
15642 SECURITY_ATTRIBUTES sa;
15643#if FORKSHELL_DEBUG
15644 void *fb0;
15645 char name[32];
15646 FILE *fp;
15647#endif
15648
15649 /* Calculate size of "new" */
15650 fs->gvp = ash_ptr_to_globals_var;
15651 fs->gmp = ash_ptr_to_globals_misc;
15652 fs->cmdtable = cmdtable;
15653
15654 /*
15655 * Careful: much scope for off-by-one errors. nodeptrcount is the
15656 * number of actual pointers. There's also a terminating NULL pointer.
15657 * The array in the forkshell structure gives us one element for free.
15658 */
15659 nodeptrcount = 0;
15660 funcblocksize = forkshell_size(0, fs);
15661 size = sizeof(struct forkshell) + nodeptrcount*sizeof(char *) +
15662 funcblocksize;
15663
15664 /* Allocate, initialize pointers */
15665 memset(&sa, 0, sizeof(sa));
15666 sa.nLength = sizeof(sa);
15667 sa.lpSecurityDescriptor = NULL;
15668 sa.bInheritHandle = TRUE;
15669 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, size, NULL);
15670 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
15671 nodeptr = new->nodeptr;
15672 funcblock = (char *)nodeptr + (nodeptrcount+1)*sizeof(char *);
15673 funcstring_end = (char *)new + size;
15674#if FORKSHELL_DEBUG
15675 fb0 = funcblock;
15676 annot = xmalloc(sizeof(char *)*nodeptrcount);
15677 annot_count = 0;
15678#endif
15679
15680 /* Now pack them all */
15681 forkshell_copy(fs, new);
15682
15683 /* Finish it up */
15684 *nodeptr = NULL;
15685 new->size = size;
15686 new->old_base = (char *)new;
15687 new->hMapFile = h;
15688#if FORKSHELL_DEBUG
15689 sprintf(name, "fs_%d.out", getpid());
15690 if ((fp=fopen(name, "w")) != NULL) {
15691 int i;
15692
15693 /* perform some sanity checks on pointers */
15694 fprintf(fp, "%p start of forkshell struct\n", new);
15695 fprintf(fp, "%p start of nodeptr\n", new->nodeptr);
15696 fprintf(fp, "%p start of funcblock", fb0);
15697 if ((char *)(nodeptr+1) != (char *)fb0)
15698 fprintf(fp, " != end nodeptr block + 1 %p\n", nodeptr+1);
15699 else
15700 fprintf(fp, "\n");
15701
15702 fprintf(fp, "%p start of funcstring", funcstring_end);
15703 if ((char *)funcblock != funcstring_end)
15704 fprintf(fp, " != end funcblock + 1 %p\n\n", funcblock);
15705 else
15706 fprintf(fp, "\n\n");
15707
15708 if (nodeptrcount != annot_count)
15709 fprintf(fp, "nodeptrcount (%d) != annot_count (%d)\n\n",
15710 nodeptrcount, annot_count);
15711
15712 new->nodeptrcount = nodeptrcount;
15713 new->funcblocksize = (char *)funcblock - (char *)fb0;
15714 new->funcstringsize = (char *)new + size - funcstring_end;
15715 forkshell_print(fp, new, nodeptrcount == annot_count ? annot : NULL);
15716
15717 for (i = 0; i < annot_count; ++i)
15718 free(annot[i]);
15719 free(annot);
15720 annot = NULL;
15721 annot_count = 0;
15722 fclose(fp);
15723 }
15724#endif
15725 return new;
15726}
15727
15728#undef exception_handler
15729#undef trap
15730#undef trap_ptr
15731static void *sticky_mem_start, *sticky_mem_end;
15732static void
15733forkshell_init(const char *idstr)
15734{
15735 struct forkshell *fs;
15736 void *map_handle;
15737 HANDLE h;
15738 struct globals_var **gvpp;
15739 struct globals_misc **gmpp;
15740 int i;
15741 char **ptr;
15742
15743 if (sscanf(idstr, "%p", &map_handle) != 1)
15744 bb_error_msg_and_die("invalid forkshell ID");
15745
15746 h = (HANDLE)map_handle;
15747 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
15748 if (!fs)
15749 bb_error_msg_and_die("Invalid forkshell memory");
15750
15751 /* this memory can't be freed */
15752 sticky_mem_start = fs;
15753 sticky_mem_end = (char *) fs + fs->size;
15754
15755 /* pointer fixup */
15756 for ( i=0; fs->nodeptr[i]; ++i ) {
15757 ptr = (char **)((char *)fs + ((char *)fs->nodeptr[i] - fs->old_base));
15758 if (*ptr)
15759 *ptr = (char *)fs + (*ptr - fs->old_base);
15760 }
15761
15762 /* Now fix up stuff that can't be transferred */
15763 for (i = 0; i < CMDTABLESIZE; i++) {
15764 struct tblentry *e = fs->cmdtable[i];
15765 while (e) {
15766 if (e->cmdtype == CMDBUILTIN)
15767 e->param.cmd = builtintab + e->param.index;
15768 e = e->next;
15769 }
15770 }
15771 fs->gmp->exception_handler = ash_ptr_to_globals_misc->exception_handler;
15772 for (i = 0; i < NSIG; i++)
15773 fs->gmp->trap[i] = ash_ptr_to_globals_misc->trap[i];
15774 fs->gmp->trap_ptr = ash_ptr_to_globals_misc->trap_ptr;
15775
15776 /* Switch global variables */
15777 gvpp = (struct globals_var **)&ash_ptr_to_globals_var;
15778 *gvpp = fs->gvp;
15779 gmpp = (struct globals_misc **)&ash_ptr_to_globals_misc;
15780 *gmpp = fs->gmp;
15781 cmdtable = fs->cmdtable;
15782
15783 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
15784
15785 reinitvar();
15786
15787 shlvl++;
15788 if (fs->mode == FORK_BG) {
15789 SetConsoleCtrlHandler(NULL, TRUE);
15790 if (fs->nprocs == 0) {
15791 close(0);
15792 if (open(bb_dev_null, O_RDONLY) != 0)
15793 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
15794 }
15795 }
15796 forkshell_child(fs);
15797}
15798
15799#undef free
15800static void
15801sticky_free(void *base)
15802{
15803 if (base >= sticky_mem_start && base < sticky_mem_end)
15804 return;
15805 free(base);
15806}
15807#endif
14290 15808
14291/*- 15809/*-
14292 * Copyright (c) 1989, 1991, 1993, 1994 15810 * 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
67typedef long long arith_t; 67typedef 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
71typedef long arith_t; 71typedef long arith_t;
diff --git a/shell/random.c b/shell/random.c
index 56c7c5a3c..ffe0cc937 100644
--- a/shell/random.c
+++ b/shell/random.c
@@ -19,6 +19,17 @@
19# include "random.h" 19# include "random.h"
20# define RAND_BASH_MASK 0x7fff 20# define RAND_BASH_MASK 0x7fff
21 21
22# if ENABLE_FEATURE_PRNG_SHELL
23uint32_t FAST_FUNC
24next_random(random_t *rnd)
25{
26 return full_random(rnd) & RAND_BASH_MASK;
27}
28# undef RAND_BASH_MASK
29# define RAND_BASH_MASK 0xffffffff
30# define next_random full_random
31# endif
32
22#else 33#else
23# include <stdint.h> 34# include <stdint.h>
24# include <unistd.h> 35# include <unistd.h>
diff --git a/shell/random.h b/shell/random.h
index c4eb44c13..75fe0f69f 100644
--- a/shell/random.h
+++ b/shell/random.h
@@ -35,6 +35,9 @@ typedef struct random_t {
35 ((rnd)->galois_LFSR = 0) 35 ((rnd)->galois_LFSR = 0)
36 36
37uint32_t next_random(random_t *rnd) FAST_FUNC; 37uint32_t next_random(random_t *rnd) FAST_FUNC;
38#if ENABLE_FEATURE_PRNG_SHELL
39uint32_t full_random(random_t *rnd) FAST_FUNC;
40#endif
38 41
39POP_SAVED_FUNCTION_VISIBILITY 42POP_SAVED_FUNCTION_VISIBILITY
40 43
diff --git a/shell/shell_common.c b/shell/shell_common.c
index f2bf5ab65..23e5f1c25 100644
--- a/shell/shell_common.c
+++ b/shell/shell_common.c
@@ -19,7 +19,11 @@
19#include "libbb.h" 19#include "libbb.h"
20#include "shell_common.h" 20#include "shell_common.h"
21 21
22#if !ENABLE_PLATFORM_MINGW32
22const char defifsvar[] ALIGN1 = "IFS= \t\n"; 23const char defifsvar[] ALIGN1 = "IFS= \t\n";
24#else
25const char defifsvar[] ALIGN1 = "IFS= \t\n\r";
26#endif
23const char defoptindvar[] ALIGN1 = "OPTIND=1"; 27const char defoptindvar[] ALIGN1 = "OPTIND=1";
24 28
25 29
@@ -56,7 +60,9 @@ shell_builtin_read(struct builtin_read_params *params)
56 char **pp; 60 char **pp;
57 char *buffer; 61 char *buffer;
58 char delim; 62 char delim;
63#if !ENABLE_PLATFORM_MINGW32
59 struct termios tty, old_tty; 64 struct termios tty, old_tty;
65#endif
60 const char *retval; 66 const char *retval;
61 int bufpos; /* need to be able to hold -1 */ 67 int bufpos; /* need to be able to hold -1 */
62 int startword; 68 int startword;
@@ -130,6 +136,7 @@ shell_builtin_read(struct builtin_read_params *params)
130 } 136 }
131 137
132 if (params->opt_t && end_ms == 0) { 138 if (params->opt_t && end_ms == 0) {
139#if !ENABLE_PLATFORM_MINGW32
133 /* "If timeout is 0, read returns immediately, without trying 140 /* "If timeout is 0, read returns immediately, without trying
134 * to read any data. The exit status is 0 if input is available 141 * to read any data. The exit status is 0 if input is available
135 * on the specified file descriptor, non-zero otherwise." 142 * on the specified file descriptor, non-zero otherwise."
@@ -140,6 +147,9 @@ shell_builtin_read(struct builtin_read_params *params)
140 r = poll(pfd, 1, /*timeout:*/ 0); 147 r = poll(pfd, 1, /*timeout:*/ 0);
141 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 148 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
142 return (const char *)(uintptr_t)(r <= 0); 149 return (const char *)(uintptr_t)(r <= 0);
150#else
151 return (const char *)(uintptr_t)(1);
152#endif
143 } 153 }
144 154
145 if (params->opt_p && isatty(fd)) { 155 if (params->opt_p && isatty(fd)) {
@@ -152,6 +162,7 @@ shell_builtin_read(struct builtin_read_params *params)
152 ifs = defifs; 162 ifs = defifs;
153 163
154 read_flags = params->read_flags; 164 read_flags = params->read_flags;
165#if !ENABLE_PLATFORM_MINGW32
155 if (nchars || (read_flags & BUILTIN_READ_SILENT)) { 166 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
156 tcgetattr(fd, &tty); 167 tcgetattr(fd, &tty);
157 old_tty = tty; 168 old_tty = tty;
@@ -174,6 +185,7 @@ shell_builtin_read(struct builtin_read_params *params)
174 * Ignoring, it's harmless. */ 185 * Ignoring, it's harmless. */
175 tcsetattr(fd, TCSANOW, &tty); 186 tcsetattr(fd, TCSANOW, &tty);
176 } 187 }
188#endif
177 189
178 retval = (const char *)(uintptr_t)0; 190 retval = (const char *)(uintptr_t)0;
179 startword = 1; 191 startword = 1;
@@ -203,6 +215,7 @@ shell_builtin_read(struct builtin_read_params *params)
203 } 215 }
204 } 216 }
205 217
218#if !ENABLE_PLATFORM_MINGW32
206 /* We must poll even if timeout is -1: 219 /* We must poll even if timeout is -1:
207 * we want to be interrupted if signal arrives, 220 * we want to be interrupted if signal arrives,
208 * regardless of SA_RESTART-ness of that signal! 221 * regardless of SA_RESTART-ness of that signal!
@@ -220,9 +233,35 @@ shell_builtin_read(struct builtin_read_params *params)
220 retval = (const char *)(uintptr_t)1; 233 retval = (const char *)(uintptr_t)1;
221 break; 234 break;
222 } 235 }
236#else
237 errno = 0;
238 if (isatty(fd) && (params->opt_n || params->opt_d || params->opt_t ||
239 (read_flags & BUILTIN_READ_SILENT))) {
240 int64_t key;
241
242 key = read_key(fd, NULL, timeout);
243 if (key == 0x03 || key == -1) {
244 /* ^C or timeout */
245 retval = (const char *)(uintptr_t)1;
246 goto ret;
247 }
248 buffer[bufpos] = key == '\r' ? '\n' : key;
249 if (!(read_flags & BUILTIN_READ_SILENT)) {
250 /* echo input if not in silent mode */
251 putchar(buffer[bufpos]);
252 }
253 }
254 else {
255 if (read(fd, &buffer[bufpos], 1) != 1) {
256 err = errno;
257 retval = (const char *)(uintptr_t)1;
258 break;
259 }
260 }
261#endif
223 262
224 c = buffer[bufpos]; 263 c = buffer[bufpos];
225 if (c == '\0') 264 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r'))
226 continue; 265 continue;
227 if (!(read_flags & BUILTIN_READ_RAW)) { 266 if (!(read_flags & BUILTIN_READ_RAW)) {
228 if (backslash) { 267 if (backslash) {
@@ -322,8 +361,10 @@ shell_builtin_read(struct builtin_read_params *params)
322 361
323 ret: 362 ret:
324 free(buffer); 363 free(buffer);
364#if !ENABLE_PLATFORM_MINGW32
325 if (read_flags & BUILTIN_READ_SILENT) 365 if (read_flags & BUILTIN_READ_SILENT)
326 tcsetattr(fd, TCSANOW, &old_tty); 366 tcsetattr(fd, TCSANOW, &old_tty);
367#endif
327 368
328 errno = err; 369 errno = err;
329 return retval; 370 return retval;
@@ -332,6 +373,7 @@ shell_builtin_read(struct builtin_read_params *params)
332 373
333/* ulimit builtin */ 374/* ulimit builtin */
334 375
376#if !ENABLE_PLATFORM_MINGW32
335struct limits { 377struct limits {
336 uint8_t cmd; /* RLIMIT_xxx fit into it */ 378 uint8_t cmd; /* RLIMIT_xxx fit into it */
337 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 379 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
@@ -548,3 +590,9 @@ shell_builtin_ulimit(char **argv)
548 590
549 return 0; 591 return 0;
550} 592}
593#else
594int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM)
595{
596 return 1;
597}
598#endif
diff --git a/util-linux/more.c b/util-linux/more.c
index eea69da06..a9ea76ab4 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 ede4fbc9c..31800fa51 100644
--- a/util-linux/rev.c
+++ b/util-linux/rev.c
@@ -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--) {
diff --git a/win32/Kbuild b/win32/Kbuild
new file mode 100644
index 000000000..f8c8fb5af
--- /dev/null
+++ b/win32/Kbuild
@@ -0,0 +1,27 @@
1# Makefile for busybox
2#
3# Licensed under the GPL v2, see the file LICENSE in this tarball.
4
5lib-y:=
6
7lib-$(CONFIG_PLATFORM_MINGW32) += env.o
8lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o
9lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o
10lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o
11lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o
12lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o
13lib-$(CONFIG_PLATFORM_MINGW32) += mingw.o
14lib-$(CONFIG_PLATFORM_MINGW32) += process.o
15lib-$(CONFIG_PLATFORM_MINGW32) += mntent.o
16lib-$(CONFIG_PLATFORM_MINGW32) += net.o
17lib-$(CONFIG_PLATFORM_MINGW32) += poll.o
18lib-$(CONFIG_PLATFORM_MINGW32) += popen.o
19lib-$(CONFIG_PLATFORM_MINGW32) += regex.o
20lib-$(CONFIG_PLATFORM_MINGW32) += select.o
21lib-$(CONFIG_FEATURE_PRNG_SHELL) += sh_random.o
22lib-$(CONFIG_PLATFORM_MINGW32) += statfs.o
23lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o
24lib-$(CONFIG_PLATFORM_MINGW32) += system.o
25lib-$(CONFIG_PLATFORM_MINGW32) += termios.o
26lib-$(CONFIG_PLATFORM_MINGW32) += uname.o
27lib-$(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..2837c0720
--- /dev/null
+++ b/win32/env.c
@@ -0,0 +1,94 @@
1#include "libbb.h"
2
3#undef getenv
4#undef putenv
5
6char *mingw_getenv(const char *name)
7{
8 char *result = getenv(name);
9 if (!result && !strcmp(name, "TMPDIR")) {
10 /* on Windows it is TMP and TEMP */
11 result = getenv("TMP");
12 if (!result)
13 result = getenv("TEMP");
14 }
15 return result;
16}
17
18int setenv(const char *name, const char *value, int replace)
19{
20 int out;
21 char *envstr;
22
23 if (!name || !*name || strchr(name, '=') || !value) return -1;
24 if (!replace) {
25 if (getenv(name)) return 0;
26 }
27
28 envstr = xasprintf("%s=%s", name, value);
29 out = mingw_putenv(envstr);
30 free(envstr);
31
32 return out;
33}
34
35/*
36 * Removing an environment variable with WIN32 putenv requires an argument
37 * like "NAME="; glibc omits the '='. The implementations of unsetenv and
38 * clearenv allow for this.
39 *
40 * It isn't possible to create an environment variable with an empty value
41 * using WIN32 putenv.
42 */
43int unsetenv(const char *name)
44{
45 char *envstr;
46 int ret;
47
48 if (!name || !*name || strchr(name, '=') ) {
49 return -1;
50 }
51
52 envstr = xmalloc(strlen(name)+2);
53 strcat(strcpy(envstr, name), "=");
54 ret = putenv(envstr);
55 free(envstr);
56
57 return ret;
58}
59
60int clearenv(void)
61{
62 char *envp, *name, *s;
63
64 while ( environ && (envp=*environ) ) {
65 if ( (s=strchr(envp, '=')) != NULL ) {
66 name = xstrndup(envp, s-envp+1);
67 if ( putenv(name) == -1 ) {
68 free(name);
69 return -1;
70 }
71 free(name);
72 }
73 else {
74 return -1;
75 }
76 }
77 return 0;
78}
79
80int mingw_putenv(const char *env)
81{
82 char *s;
83
84 if ( (s=strchr(env, '=')) == NULL ) {
85 return unsetenv(env);
86 }
87
88 if ( s[1] != '\0' ) {
89 return putenv(env);
90 }
91
92 /* can't set empty value */
93 return 0;
94}
diff --git a/win32/fnmatch.c b/win32/fnmatch.c
new file mode 100644
index 000000000..de456e526
--- /dev/null
+++ b/win32/fnmatch.c
@@ -0,0 +1,484 @@
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#include <platform.h>
20
21#if HAVE_CONFIG_H
22# include <config.h>
23#endif
24
25/* Enable GNU extensions in fnmatch.h. */
26#ifndef _GNU_SOURCE
27# define _GNU_SOURCE 1
28#endif
29
30#include <errno.h>
31#include <fnmatch.h>
32#include <ctype.h>
33
34#if HAVE_STRING_H || defined _LIBC
35# include <string.h>
36#else
37# include <strings.h>
38#endif
39
40#if defined STDC_HEADERS || defined _LIBC
41# include <stdlib.h>
42#endif
43
44/* For platform which support the ISO C amendement 1 functionality we
45 support user defined character classes. */
46#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
47/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */
48# include <wchar.h>
49# include <wctype.h>
50#endif
51
52/* Comment out all this code if we are using the GNU C Library, and are not
53 actually compiling the library itself. This code is part of the GNU C
54 Library, but also included in many other GNU distributions. Compiling
55 and linking in this code is a waste when using the GNU C library
56 (especially if it is a shared library). Rather than having every GNU
57 program understand `configure --with-gnu-libc' and omit the object files,
58 it is simpler to just do this in the source for each such file. */
59
60#if defined _LIBC || !defined __GNU_LIBRARY__
61
62
63# if defined STDC_HEADERS || !defined isascii
64# define ISASCII(c) 1
65# else
66# define ISASCII(c) isascii(c)
67# endif
68
69# ifdef isblank
70# define ISBLANK(c) (ISASCII (c) && isblank (c))
71# else
72# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
73# endif
74# ifdef isgraph
75# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
76# else
77# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
78# endif
79
80# define ISPRINT(c) (ISASCII (c) && isprint (c))
81# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
82# define ISALNUM(c) (ISASCII (c) && isalnum (c))
83# define ISALPHA(c) (ISASCII (c) && isalpha (c))
84# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
85# define ISLOWER(c) (ISASCII (c) && islower (c))
86# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
87# define ISSPACE(c) (ISASCII (c) && isspace (c))
88# define ISUPPER(c) (ISASCII (c) && isupper (c))
89# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
90
91# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
92
93# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
94/* The GNU C library provides support for user-defined character classes
95 and the functions from ISO C amendement 1. */
96# ifdef CHARCLASS_NAME_MAX
97# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
98# else
99/* This shouldn't happen but some implementation might still have this
100 problem. Use a reasonable default value. */
101# define CHAR_CLASS_MAX_LENGTH 256
102# endif
103
104# ifdef _LIBC
105# define IS_CHAR_CLASS(string) __wctype (string)
106# else
107# define IS_CHAR_CLASS(string) wctype (string)
108# endif
109# else
110# define CHAR_CLASS_MAX_LENGTH 6 /* Namely, `xdigit'. */
111
112# define IS_CHAR_CLASS(string) \
113 (STREQ (string, "alpha") || STREQ (string, "upper") \
114 || STREQ (string, "lower") || STREQ (string, "digit") \
115 || STREQ (string, "alnum") || STREQ (string, "xdigit") \
116 || STREQ (string, "space") || STREQ (string, "print") \
117 || STREQ (string, "punct") || STREQ (string, "graph") \
118 || STREQ (string, "cntrl") || STREQ (string, "blank"))
119# endif
120
121/* Avoid depending on library functions or files
122 whose names are inconsistent. */
123
124# if !defined _LIBC && !defined getenv
125extern char *getenv (const char *);
126# endif
127
128# ifndef errno
129extern int errno;
130# endif
131
132/* This function doesn't exist on most systems. */
133
134# if !defined HAVE___STRCHRNUL && !defined _LIBC && 0
135static char *
136__strchrnul (const char *s, int c)
137{
138 char *result = strchr (s, c);
139 if (result == NULL)
140 result = strchr (s, '\0');
141 return result;
142}
143# else
144# define __strchrnul strchrnul
145# endif
146
147# ifndef internal_function
148/* Inside GNU libc we mark some function in a special way. In other
149 environments simply ignore the marking. */
150# define internal_function
151# endif
152
153/* Match STRING against the filename pattern PATTERN, returning zero if
154 it matches, nonzero if not. */
155static int internal_fnmatch __P ((const char *pattern, const char *string,
156 int no_leading_period, int flags))
157 internal_function;
158static int
159internal_function
160internal_fnmatch (const char *pattern, const char *string,
161 int no_leading_period, int flags)
162{
163 register const char *p = pattern, *n = string;
164 register unsigned char c;
165
166/* Note that this evaluates C many times. */
167# ifdef _LIBC
168# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
169# else
170# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
171# endif
172
173 while ((c = *p++) != '\0')
174 {
175 c = FOLD (c);
176
177 switch (c)
178 {
179 case '?':
180 if (*n == '\0')
181 return FNM_NOMATCH;
182 else if (*n == '/' && (flags & FNM_FILE_NAME))
183 return FNM_NOMATCH;
184 else if (*n == '.' && no_leading_period
185 && (n == string
186 || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
187 return FNM_NOMATCH;
188 break;
189
190 case '\\':
191 if (!(flags & FNM_NOESCAPE))
192 {
193 c = *p++;
194 if (c == '\0')
195 /* Trailing \ loses. */
196 return FNM_NOMATCH;
197 c = FOLD (c);
198 }
199 if (FOLD ((unsigned char) *n) != c)
200 return FNM_NOMATCH;
201 break;
202
203 case '*':
204 if (*n == '.' && no_leading_period
205 && (n == string
206 || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
207 return FNM_NOMATCH;
208
209 for (c = *p++; c == '?' || c == '*'; c = *p++)
210 {
211 if (*n == '/' && (flags & FNM_FILE_NAME))
212 /* A slash does not match a wildcard under FNM_FILE_NAME. */
213 return FNM_NOMATCH;
214 else if (c == '?')
215 {
216 /* A ? needs to match one character. */
217 if (*n == '\0')
218 /* There isn't another character; no match. */
219 return FNM_NOMATCH;
220 else
221 /* One character of the string is consumed in matching
222 this ? wildcard, so *??? won't match if there are
223 less than three characters. */
224 ++n;
225 }
226 }
227
228 if (c == '\0')
229 /* The wildcard(s) is/are the last element of the pattern.
230 If the name is a file name and contains another slash
231 this does mean it cannot match. */
232 return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL
233 ? FNM_NOMATCH : 0);
234 else
235 {
236 const char *endp;
237
238 endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');
239
240 if (c == '[')
241 {
242 int flags2 = ((flags & FNM_FILE_NAME)
243 ? flags : (flags & ~FNM_PERIOD));
244
245 for (--p; n < endp; ++n)
246 if (internal_fnmatch (p, n,
247 (no_leading_period
248 && (n == string
249 || (n[-1] == '/'
250 && (flags
251 & FNM_FILE_NAME)))),
252 flags2)
253 == 0)
254 return 0;
255 }
256 else if (c == '/' && (flags & FNM_FILE_NAME))
257 {
258 while (*n != '\0' && *n != '/')
259 ++n;
260 if (*n == '/'
261 && (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
262 flags) == 0))
263 return 0;
264 }
265 else
266 {
267 int flags2 = ((flags & FNM_FILE_NAME)
268 ? flags : (flags & ~FNM_PERIOD));
269
270 if (c == '\\' && !(flags & FNM_NOESCAPE))
271 c = *p;
272 c = FOLD (c);
273 for (--p; n < endp; ++n)
274 if (FOLD ((unsigned char) *n) == c
275 && (internal_fnmatch (p, n,
276 (no_leading_period
277 && (n == string
278 || (n[-1] == '/'
279 && (flags
280 & FNM_FILE_NAME)))),
281 flags2) == 0))
282 return 0;
283 }
284 }
285
286 /* If we come here no match is possible with the wildcard. */
287 return FNM_NOMATCH;
288
289 case '[':
290 {
291 /* Nonzero if the sense of the character class is inverted. */
292 static int posixly_correct;
293 register int not;
294 char cold;
295
296 if (posixly_correct == 0)
297 posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
298
299 if (*n == '\0')
300 return FNM_NOMATCH;
301
302 if (*n == '.' && no_leading_period && (n == string
303 || (n[-1] == '/'
304 && (flags
305 & FNM_FILE_NAME))))
306 return FNM_NOMATCH;
307
308 if (*n == '/' && (flags & FNM_FILE_NAME))
309 /* `/' cannot be matched. */
310 return FNM_NOMATCH;
311
312 not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
313 if (not)
314 ++p;
315
316 c = *p++;
317 for (;;)
318 {
319 unsigned char fn = FOLD ((unsigned char) *n);
320
321 if (!(flags & FNM_NOESCAPE) && c == '\\')
322 {
323 if (*p == '\0')
324 return FNM_NOMATCH;
325 c = FOLD ((unsigned char) *p);
326 ++p;
327
328 if (c == fn)
329 goto matched;
330 }
331 else if (c == '[' && *p == ':')
332 {
333 /* Leave room for the null. */
334 char str[CHAR_CLASS_MAX_LENGTH + 1];
335 size_t c1 = 0;
336# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
337 wctype_t wt;
338# endif
339 const char *startp = p;
340
341 for (;;)
342 {
343 if (c1 == CHAR_CLASS_MAX_LENGTH)
344 /* The name is too long and therefore the pattern
345 is ill-formed. */
346 return FNM_NOMATCH;
347
348 c = *++p;
349 if (c == ':' && p[1] == ']')
350 {
351 p += 2;
352 break;
353 }
354 if (c < 'a' || c >= 'z')
355 {
356 /* This cannot possibly be a character class name.
357 Match it as a normal range. */
358 p = startp;
359 c = '[';
360 goto normal_bracket;
361 }
362 str[c1++] = c;
363 }
364 str[c1] = '\0';
365
366# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
367 wt = IS_CHAR_CLASS (str);
368 if (wt == 0)
369 /* Invalid character class name. */
370 return FNM_NOMATCH;
371
372 if (__iswctype (__btowc ((unsigned char) *n), wt))
373 goto matched;
374# else
375 if ((STREQ (str, "alnum") && ISALNUM ((unsigned char) *n))
376 || (STREQ (str, "alpha") && ISALPHA ((unsigned char) *n))
377 || (STREQ (str, "blank") && ISBLANK ((unsigned char) *n))
378 || (STREQ (str, "cntrl") && ISCNTRL ((unsigned char) *n))
379 || (STREQ (str, "digit") && ISDIGIT ((unsigned char) *n))
380 || (STREQ (str, "graph") && ISGRAPH ((unsigned char) *n))
381 || (STREQ (str, "lower") && ISLOWER ((unsigned char) *n))
382 || (STREQ (str, "print") && ISPRINT ((unsigned char) *n))
383 || (STREQ (str, "punct") && ISPUNCT ((unsigned char) *n))
384 || (STREQ (str, "space") && ISSPACE ((unsigned char) *n))
385 || (STREQ (str, "upper") && ISUPPER ((unsigned char) *n))
386 || (STREQ (str, "xdigit") && ISXDIGIT ((unsigned char) *n)))
387 goto matched;
388# endif
389 }
390 else if (c == '\0')
391 /* [ (unterminated) loses. */
392 return FNM_NOMATCH;
393 else
394 {
395 normal_bracket:
396 if (FOLD (c) == fn)
397 goto matched;
398
399 cold = c;
400 c = *p++;
401
402 if (c == '-' && *p != ']')
403 {
404 /* It is a range. */
405 unsigned char cend = *p++;
406 if (!(flags & FNM_NOESCAPE) && cend == '\\')
407 cend = *p++;
408 if (cend == '\0')
409 return FNM_NOMATCH;
410
411 if (cold <= fn && fn <= FOLD (cend))
412 goto matched;
413
414 c = *p++;
415 }
416 }
417
418 if (c == ']')
419 break;
420 }
421
422 if (!not)
423 return FNM_NOMATCH;
424 break;
425
426 matched:
427 /* Skip the rest of the [...] that already matched. */
428 while (c != ']')
429 {
430 if (c == '\0')
431 /* [... (unterminated) loses. */
432 return FNM_NOMATCH;
433
434 c = *p++;
435 if (!(flags & FNM_NOESCAPE) && c == '\\')
436 {
437 if (*p == '\0')
438 return FNM_NOMATCH;
439 /* XXX 1003.2d11 is unclear if this is right. */
440 ++p;
441 }
442 else if (c == '[' && *p == ':')
443 {
444 do
445 if (*++p == '\0')
446 return FNM_NOMATCH;
447 while (*p != ':' || p[1] == ']');
448 p += 2;
449 c = *p;
450 }
451 }
452 if (not)
453 return FNM_NOMATCH;
454 }
455 break;
456
457 default:
458 if (c != FOLD ((unsigned char) *n))
459 return FNM_NOMATCH;
460 }
461
462 ++n;
463 }
464
465 if (*n == '\0')
466 return 0;
467
468 if ((flags & FNM_LEADING_DIR) && *n == '/')
469 /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
470 return 0;
471
472 return FNM_NOMATCH;
473
474# undef FOLD
475}
476
477
478int
479fnmatch (const char *pattern, const char *string, int flags)
480{
481 return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
482}
483
484#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
23extern "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. */
77extern 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/fsync.c b/win32/fsync.c
new file mode 100644
index 000000000..6ab44d434
--- /dev/null
+++ b/win32/fsync.c
@@ -0,0 +1,75 @@
1/* Emulate fsync on platforms that lack it, primarily Windows and
2 cross-compilers like MinGW.
3
4 This is derived from sqlite3 sources.
5 https://www.sqlite.org/src/finfo?name=src/os_win.c
6 https://www.sqlite.org/copyright.html
7
8 Written by Richard W.M. Jones <rjones.at.redhat.com>
9
10 Copyright (C) 2008-2018 Free Software Foundation, Inc.
11
12 This library is free software; you can redistribute it and/or
13 modify it under the terms of the GNU Lesser General Public
14 License as published by the Free Software Foundation; either
15 version 2.1 of the License, or (at your option) any later version.
16
17 This library is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 Lesser General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <https://www.gnu.org/licenses/>. */
24
25#include "libbb.h"
26#include <unistd.h>
27
28/* FlushFileBuffers */
29# define WIN32_LEAN_AND_MEAN
30# include <windows.h>
31
32# include <errno.h>
33
34/* Get _get_osfhandle. */
35# include <io.h>
36
37int
38fsync (int fd)
39{
40 HANDLE h = (HANDLE) _get_osfhandle (fd);
41 DWORD err;
42
43 if (h == INVALID_HANDLE_VALUE)
44 {
45 errno = EBADF;
46 return -1;
47 }
48
49 if (!FlushFileBuffers (h))
50 {
51 /* Translate some Windows errors into rough approximations of Unix
52 * errors. MSDN is useless as usual - in this case it doesn't
53 * document the full range of errors.
54 */
55 err = GetLastError ();
56 switch (err)
57 {
58 case ERROR_ACCESS_DENIED:
59 /* For a read-only handle, fsync should succeed, even though we have
60 no way to sync the access-time changes. */
61 return 0;
62
63 /* eg. Trying to fsync a tty. */
64 case ERROR_INVALID_HANDLE:
65 errno = EINVAL;
66 break;
67
68 default:
69 errno = EIO;
70 }
71 return -1;
72 }
73
74 return 0;
75}
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..310c2ce58
--- /dev/null
+++ b/win32/inet_pton.c
@@ -0,0 +1,203 @@
1/*
2 * Copyright (C) 1996-2005, 2007, 2013, 2014, 2016, 2017 Internet Systems Consortium, Inc. ("ISC")
3 *
4 * This Source Code Form is subject to the terms of the Mozilla Public
5 * License, v. 2.0. If a copy of the MPL was not distributed with this
6 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
7 */
8
9#include "libbb.h"
10
11#include <errno.h>
12#include <string.h>
13
14/*% INT16 Size */
15#define NS_INT16SZ 2
16/*% IPv4 Address Size */
17#define NS_INADDRSZ 4
18/*% IPv6 Address Size */
19#define NS_IN6ADDRSZ 16
20
21/*
22 * WARNING: Don't even consider trying to compile this on a system where
23 * sizeof(int) < 4. sizeof(int) > 4 is fine; all the world's not a VAX.
24 */
25
26static int inet_pton4(const char *src, unsigned char *dst);
27#if ENABLE_FEATURE_IPV6
28static int inet_pton6(const char *src, unsigned char *dst);
29#endif
30
31/*%
32 * convert from presentation format (which usually means ASCII printable)
33 * to network format (which is usually some kind of binary format).
34 * \return
35 * 1 if the address was valid for the specified address family
36 * 0 if the address wasn't valid (`dst' is untouched in this case)
37 * -1 if some other error occurred (`dst' is untouched in this case, too)
38 * \author
39 * Paul Vixie, 1996.
40 */
41int
42inet_pton(int af, const char *src, void *dst) {
43 switch (af) {
44 case AF_INET:
45 return (inet_pton4(src, dst));
46#if ENABLE_FEATURE_IPV6
47 case AF_INET6:
48 return (inet_pton6(src, dst));
49#endif
50 default:
51 errno = EAFNOSUPPORT;
52 return (-1);
53 }
54 /* NOTREACHED */
55}
56
57/*!\fn static int inet_pton4(const char *src, unsigned char *dst)
58 * \brief
59 * like inet_aton() but without all the hexadecimal and shorthand.
60 * \return
61 * 1 if `src' is a valid dotted quad, else 0.
62 * \note
63 * does not touch `dst' unless it's returning 1.
64 * \author
65 * Paul Vixie, 1996.
66 */
67static int
68inet_pton4(const char *src, unsigned char *dst) {
69 static const char digits[] = "0123456789";
70 int saw_digit, octets, ch;
71 unsigned char tmp[NS_INADDRSZ], *tp;
72
73 saw_digit = 0;
74 octets = 0;
75 *(tp = tmp) = 0;
76 while ((ch = *src++) != '\0') {
77 const char *pch;
78
79 if ((pch = strchr(digits, ch)) != NULL) {
80 unsigned int byte = *tp * 10;
81
82 byte += (int)(pch - digits);
83 if (saw_digit && *tp == 0)
84 return (0);
85 if (byte > 255)
86 return (0);
87 *tp = byte;
88 if (!saw_digit) {
89 if (++octets > 4)
90 return (0);
91 saw_digit = 1;
92 }
93 } else if (ch == '.' && saw_digit) {
94 if (octets == 4)
95 return (0);
96 *++tp = 0;
97 saw_digit = 0;
98 } else
99 return (0);
100 }
101 if (octets < 4)
102 return (0);
103 memmove(dst, tmp, NS_INADDRSZ);
104 return (1);
105}
106
107/*%
108 * convert presentation level address to network order binary form.
109 * \return
110 * 1 if `src' is a valid [RFC1884 2.2] address, else 0.
111 * \note
112 * (1) does not touch `dst' unless it's returning 1.
113 * \note
114 * (2) :: in a full address is silently ignored.
115 * \author
116 * inspired by Mark Andrews.
117 * \author
118 * Paul Vixie, 1996.
119 */
120#if ENABLE_FEATURE_IPV6
121static int
122inet_pton6(const char *src, unsigned char *dst) {
123 static const char xdigits_l[] = "0123456789abcdef",
124 xdigits_u[] = "0123456789ABCDEF";
125 unsigned char tmp[NS_IN6ADDRSZ], *tp, *endp, *colonp;
126 const char *xdigits, *curtok;
127 int ch, seen_xdigits;
128 unsigned int val;
129
130 memset((tp = tmp), '\0', NS_IN6ADDRSZ);
131 endp = tp + NS_IN6ADDRSZ;
132 colonp = NULL;
133 /* Leading :: requires some special handling. */
134 if (*src == ':')
135 if (*++src != ':')
136 return (0);
137 curtok = src;
138 seen_xdigits = 0;
139 val = 0;
140 while ((ch = *src++) != '\0') {
141 const char *pch;
142
143 if ((pch = strchr((xdigits = xdigits_l), ch)) == NULL)
144 pch = strchr((xdigits = xdigits_u), ch);
145 if (pch != NULL) {
146 val <<= 4;
147 val |= (pch - xdigits);
148 if (++seen_xdigits > 4)
149 return (0);
150 continue;
151 }
152 if (ch == ':') {
153 curtok = src;
154 if (!seen_xdigits) {
155 if (colonp)
156 return (0);
157 colonp = tp;
158 continue;
159 }
160 if (tp + NS_INT16SZ > endp)
161 return (0);
162 *tp++ = (unsigned char) (val >> 8) & 0xff;
163 *tp++ = (unsigned char) val & 0xff;
164 seen_xdigits = 0;
165 val = 0;
166 continue;
167 }
168 if (ch == '.' && ((tp + NS_INADDRSZ) <= endp) &&
169 inet_pton4(curtok, tp) > 0) {
170 tp += NS_INADDRSZ;
171 seen_xdigits = 0;
172 break; /* '\0' was seen by inet_pton4(). */
173 }
174 return (0);
175 }
176 if (seen_xdigits) {
177 if (tp + NS_INT16SZ > endp)
178 return (0);
179 *tp++ = (unsigned char) (val >> 8) & 0xff;
180 *tp++ = (unsigned char) val & 0xff;
181 }
182 if (colonp != NULL) {
183 /*
184 * Since some memmove()'s erroneously fail to handle
185 * overlapping regions, we'll do the shift by hand.
186 */
187 const int n = (int)(tp - colonp);
188 int i;
189
190 if (tp == endp)
191 return (0);
192 for (i = 1; i <= n; i++) {
193 endp[- i] = colonp[n - i];
194 colonp[n - i] = 0;
195 }
196 tp = endp;
197 }
198 if (tp != endp)
199 return (0);
200 memmove(dst, tmp, NS_IN6ADDRSZ);
201 return (1);
202}
203#endif
diff --git a/win32/ioctl.c b/win32/ioctl.c
new file mode 100644
index 000000000..93f9f504d
--- /dev/null
+++ b/win32/ioctl.c
@@ -0,0 +1,46 @@
1#include "libbb.h"
2
3static int mingw_get_terminal_width_height(struct winsize *win)
4{
5 int fd;
6 HANDLE handle;
7 CONSOLE_SCREEN_BUFFER_INFO sbi;
8
9 win->ws_row = 0;
10 win->ws_col = 0;
11
12 for (fd=STDOUT_FILENO; fd<=STDERR_FILENO; ++fd) {
13 handle = (HANDLE)_get_osfhandle(fd);
14 if (handle != INVALID_HANDLE_VALUE &&
15 GetConsoleScreenBufferInfo(handle, &sbi) != 0) {
16 win->ws_row = sbi.srWindow.Bottom - sbi.srWindow.Top + 1;
17 win->ws_col = sbi.srWindow.Right - sbi.srWindow.Left + 1;
18 return 0;
19 }
20 }
21
22 return -1;
23}
24
25int ioctl(int fd UNUSED_PARAM, int code, ...)
26{
27 va_list ap;
28 void *arg;
29 int ret = -1;
30
31 va_start(ap, code);
32
33 switch (code) {
34 case TIOCGWINSZ:
35 arg = va_arg(ap, void *);
36 ret = mingw_get_terminal_width_height((struct winsize *)arg);
37 break;
38 default:
39 ret = -1;
40 errno = EINVAL;
41 break;
42 }
43
44 va_end(ap);
45 return ret;
46}
diff --git a/win32/isaac.c b/win32/isaac.c
new file mode 100644
index 000000000..2b02eaff2
--- /dev/null
+++ b/win32/isaac.c
@@ -0,0 +1,232 @@
1/*
2------------------------------------------------------------------------------
3readable.c: My random number generator, ISAAC.
4(c) Bob Jenkins, March 1996, Public Domain
5You may use this code in any way you wish, and it is free. No warrantee.
6* May 2008 -- made it not depend on standard.h
7------------------------------------------------------------------------------
8
9 The original version of this file was downloaded from Bob Jenkins website:
10
11 http://burtleburtle.net/bob/rand/isaacafa.html
12
13 The isaac and randinit functions have been slightly modified to silence
14 warnings in modern compilers; the get_entropy and get_random_bytes have
15 been added.
16
17 These changes were made by R M Yorston and are also dedicated to the
18 public domain.
19*/
20#include "libbb.h"
21
22typedef struct {
23 /* external results */
24 uint32_t randrsl[256];
25
26 /* internal state */
27 uint32_t mm[256];
28 uint32_t aa, bb, cc;
29} isaac_t;
30
31
32static void isaac(isaac_t *t)
33{
34 register uint32_t i,x,y;
35
36 t->cc = t->cc + 1; /* cc just gets incremented once per 256 results */
37 t->bb = t->bb + t->cc; /* then combined with bb */
38
39 for (i=0; i<256; ++i)
40 {
41 x = t->mm[i];
42 switch (i%4)
43 {
44 case 0: t->aa = t->aa^(t->aa<<13); break;
45 case 1: t->aa = t->aa^(t->aa>>6); break;
46 case 2: t->aa = t->aa^(t->aa<<2); break;
47 case 3: t->aa = t->aa^(t->aa>>16); break;
48 }
49 t->aa = t->mm[(i+128)%256] + t->aa;
50 t->mm[i] = y = t->mm[(x>>2)%256] + t->aa + t->bb;
51 t->randrsl[i] = t->bb = t->mm[(y>>10)%256] + x;
52
53 /* Note that bits 2..9 are chosen from x but 10..17 are chosen
54 from y. The only important thing here is that 2..9 and 10..17
55 don't overlap. 2..9 and 10..17 were then chosen for speed in
56 the optimized version (rand.c) */
57 /* See http://burtleburtle.net/bob/rand/isaac.html
58 for further explanations and analysis. */
59 }
60}
61
62
63/* if (flag!=0), then use the contents of randrsl[] to initialize mm[]. */
64#define mix(a,b,c,d,e,f,g,h) \
65{ \
66 a^=b<<11; d+=a; b+=c; \
67 b^=c>>2; e+=b; c+=d; \
68 c^=d<<8; f+=c; d+=e; \
69 d^=e>>16; g+=d; e+=f; \
70 e^=f<<10; h+=e; f+=g; \
71 f^=g>>4; a+=f; g+=h; \
72 g^=h<<8; b+=g; h+=a; \
73 h^=a>>9; c+=h; a+=b; \
74}
75
76static void randinit(isaac_t *t, int flag)
77{
78 int i;
79 uint32_t a,b,c,d,e,f,g,h;
80 t->aa = t->bb = t->cc = 0;
81 a=b=c=d=e=f=g=h=0x9e3779b9; /* the golden ratio */
82
83 for (i=0; i<4; ++i) /* scramble it */
84 {
85 mix(a,b,c,d,e,f,g,h);
86 }
87
88 for (i=0; i<256; i+=8) /* fill in mm[] with messy stuff */
89 {
90 if (flag) /* use all the information in the seed */
91 {
92 a+=t->randrsl[i ]; b+=t->randrsl[i+1]; c+=t->randrsl[i+2];
93 d+=t->randrsl[i+3]; e+=t->randrsl[i+4]; f+=t->randrsl[i+5];
94 g+=t->randrsl[i+6]; h+=t->randrsl[i+7];
95 }
96 mix(a,b,c,d,e,f,g,h);
97 t->mm[i ]=a; t->mm[i+1]=b; t->mm[i+2]=c; t->mm[i+3]=d;
98 t->mm[i+4]=e; t->mm[i+5]=f; t->mm[i+6]=g; t->mm[i+7]=h;
99 }
100
101 if (flag)
102 { /* do a second pass to make all of the seed affect all of mm */
103 for (i=0; i<256; i+=8)
104 {
105 a+=t->mm[i ]; b+=t->mm[i+1]; c+=t->mm[i+2]; d+=t->mm[i+3];
106 e+=t->mm[i+4]; f+=t->mm[i+5]; g+=t->mm[i+6]; h+=t->mm[i+7];
107 mix(a,b,c,d,e,f,g,h);
108 t->mm[i ]=a; t->mm[i+1]=b; t->mm[i+2]=c; t->mm[i+3]=d;
109 t->mm[i+4]=e; t->mm[i+5]=f; t->mm[i+6]=g; t->mm[i+7]=h;
110 }
111 }
112
113 isaac(t); /* fill in the first set of results */
114}
115
116/* call 'fn' to put data in 'dt' then copy it to generator state */
117#define GET_DATA(fn, dt) \
118 fn(&dt); \
119 u = (uint32_t *)&dt; \
120 for (j=0; j<sizeof(dt)/sizeof(uint32_t); ++j) { \
121 t->randrsl[i++] = *u++; \
122 }
123
124/*
125 * Stuff a few bytes of random-ish data into the generator state.
126 * This is unlikely to be very robust: don't rely on it for
127 * anything that needs to be secure.
128 */
129static void get_entropy(isaac_t *t)
130{
131 int i, j, len;
132 SYSTEMTIME tm;
133 MEMORYSTATUS ms;
134 SYSTEM_INFO si;
135 LARGE_INTEGER pc;
136 uint32_t *u;
137 char *env, *s;
138 md5_ctx_t ctx;
139 unsigned char buf[16];
140
141 i = 0;
142 t->randrsl[i++] = (uint32_t)GetCurrentProcessId();
143 t->randrsl[i++] = (uint32_t)GetCurrentThreadId();
144 t->randrsl[i++] = (uint32_t)GetTickCount();
145
146 GET_DATA(GetLocalTime, tm)
147 GET_DATA(GlobalMemoryStatus, ms)
148 GET_DATA(GetSystemInfo, si)
149 GET_DATA(QueryPerformanceCounter, pc)
150
151 env = GetEnvironmentStringsA();
152
153 /* get length of environment: it ends with two nuls */
154 for (s=env,len=0; *s && *(s+1); ++s,++len)
155 ;
156
157 md5_begin(&ctx);
158 md5_hash(&ctx, env, len);
159 md5_end(&ctx, buf);
160
161 FreeEnvironmentStringsA(env);
162
163 u = (uint32_t *)buf;
164 for (j=0; j<sizeof(buf)/sizeof(uint32_t); ++j) {
165 t->randrsl[i++] = *u++;
166 }
167
168#if 0
169 {
170 unsigned char *p = (unsigned char *)t->randrsl;
171
172 for (j=0; j<i*sizeof(uint32_t); ++j) {
173 fprintf(stderr, "%02x", *p++);
174 if ((j&31) == 31) {
175 fprintf(stderr, "\n");
176 }
177 else if ((j&3) == 3) {
178 fprintf(stderr, " ");
179 }
180 }
181 fprintf(stderr, "\n");
182 }
183#endif
184}
185
186#define RAND_BYTES sizeof(t->randrsl)
187#define RAND_WORDS (sizeof(t->randrsl)/sizeof(t->randrsl[0]))
188
189/*
190 * Place 'count' random bytes in the buffer 'buf'. You're responsible
191 * for ensuring the buffer is big enough.
192 */
193ssize_t get_random_bytes(void *buf, ssize_t count)
194{
195 static isaac_t *t = NULL;
196 static int rand_index = 0;
197 ssize_t save_count = count;
198 unsigned char *ptr;
199
200 if (buf == NULL || count < 0) {
201 errno = EINVAL;
202 return -1;
203 }
204
205 if (!t) {
206 t = xzalloc(sizeof(isaac_t));
207
208 get_entropy(t);
209 randinit(t, 1);
210 isaac(t);
211 rand_index = 0;
212 }
213
214 ptr = (unsigned char *)t->randrsl;
215 while (count > 0) {
216 int bytes_left = RAND_BYTES - rand_index;
217 ssize_t delta = MIN(bytes_left, count);
218
219 memcpy(buf, ptr+rand_index, delta);
220 buf += delta;
221 count -= delta;
222 rand_index += delta;
223
224 if (rand_index >= RAND_BYTES) {
225 /* generate more */
226 isaac(t);
227 rand_index = 0;
228 }
229 }
230
231 return save_count;
232}
diff --git a/win32/lazyload.h b/win32/lazyload.h
new file mode 100644
index 000000000..a9fcff209
--- /dev/null
+++ b/win32/lazyload.h
@@ -0,0 +1,39 @@
1#ifndef LAZYLOAD_H
2#define LAZYLOAD_H
3
4/* simplify loading of DLL functions */
5
6struct proc_addr {
7 FARPROC pfunction;
8 unsigned initialized;
9};
10
11/* Declares a function to be loaded dynamically from a DLL. */
12#define DECLARE_PROC_ADDR(rettype, function, ...) \
13 static struct proc_addr proc_addr_##function = { NULL, 0 }; \
14 rettype (WINAPI *function)(__VA_ARGS__)
15
16/*
17 * Loads a function from a DLL (once-only).
18 * Returns non-NULL function pointer on success.
19 * Returns NULL and sets errno == ENOSYS on failure.
20 */
21#define INIT_PROC_ADDR(dll, function) \
22 (function = get_proc_addr(#dll, #function, &proc_addr_##function))
23
24static inline void *get_proc_addr(const char *dll, const char *function, struct proc_addr *proc)
25{
26 /* only do this once */
27 if (!proc->initialized) {
28 HANDLE hnd = LoadLibraryExA(dll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
29 if (hnd)
30 proc->pfunction = GetProcAddress(hnd, function);
31 proc->initialized = 1;
32 }
33 /* set ENOSYS if DLL or function was not found */
34 if (!proc->pfunction)
35 errno = ENOSYS;
36 return proc->pfunction;
37}
38
39#endif
diff --git a/win32/mingw.c b/win32/mingw.c
new file mode 100644
index 000000000..2a5f96c31
--- /dev/null
+++ b/win32/mingw.c
@@ -0,0 +1,1357 @@
1#include "libbb.h"
2#include <userenv.h>
3#include "lazyload.h"
4
5#if defined(__MINGW64_VERSION_MAJOR)
6#if ENABLE_GLOBBING
7int _dowildcard = -1;
8#else
9int _dowildcard = 0;
10#endif
11
12#undef _fmode
13int _fmode = _O_BINARY;
14#endif
15
16#if !defined(__MINGW64_VERSION_MAJOR)
17#if ENABLE_GLOBBING
18int _CRT_glob = 1;
19#else
20int _CRT_glob = 0;
21#endif
22
23unsigned int _CRT_fmode = _O_BINARY;
24#endif
25
26smallint bb_got_signal;
27
28int err_win_to_posix(DWORD winerr)
29{
30 int error = ENOSYS;
31 switch(winerr) {
32 case ERROR_ACCESS_DENIED: error = EACCES; break;
33 case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
34 case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
35 case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
36 case ERROR_ALREADY_EXISTS: error = EEXIST; break;
37 case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
38 case ERROR_BAD_COMMAND: error = EIO; break;
39 case ERROR_BAD_DEVICE: error = ENODEV; break;
40 case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
41 case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
42 case ERROR_BAD_FORMAT: error = ENOEXEC; break;
43 case ERROR_BAD_LENGTH: error = EINVAL; break;
44 case ERROR_BAD_PATHNAME: error = ENOENT; break;
45 case ERROR_BAD_PIPE: error = EPIPE; break;
46 case ERROR_BAD_UNIT: error = ENODEV; break;
47 case ERROR_BAD_USERNAME: error = EINVAL; break;
48 case ERROR_BROKEN_PIPE: error = EPIPE; break;
49 case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
50 case ERROR_BUSY: error = EBUSY; break;
51 case ERROR_BUSY_DRIVE: error = EBUSY; break;
52 case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
53 case ERROR_CANNOT_MAKE: error = EACCES; break;
54 case ERROR_CANTOPEN: error = EIO; break;
55 case ERROR_CANTREAD: error = EIO; break;
56 case ERROR_CANTWRITE: error = EIO; break;
57 case ERROR_CRC: error = EIO; break;
58 case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
59 case ERROR_DEVICE_IN_USE: error = EBUSY; break;
60 case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
61 case ERROR_DIRECTORY: error = EINVAL; break;
62 case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
63 case ERROR_DISK_CHANGE: error = EIO; break;
64 case ERROR_DISK_FULL: error = ENOSPC; break;
65 case ERROR_DRIVE_LOCKED: error = EBUSY; break;
66 case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
67 case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
68 case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
69 case ERROR_FILE_EXISTS: error = EEXIST; break;
70 case ERROR_FILE_INVALID: error = ENODEV; break;
71 case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
72 case ERROR_GEN_FAILURE: error = EIO; break;
73 case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
74 case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
75 case ERROR_INVALID_ACCESS: error = EACCES; break;
76 case ERROR_INVALID_ADDRESS: error = EFAULT; break;
77 case ERROR_INVALID_BLOCK: error = EFAULT; break;
78 case ERROR_INVALID_DATA: error = EINVAL; break;
79 case ERROR_INVALID_DRIVE: error = ENODEV; break;
80 case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
81 case ERROR_INVALID_FLAGS: error = EINVAL; break;
82 case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
83 case ERROR_INVALID_HANDLE: error = EBADF; break;
84 case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
85 case ERROR_INVALID_NAME: error = EINVAL; break;
86 case ERROR_INVALID_OWNER: error = EINVAL; break;
87 case ERROR_INVALID_PARAMETER: error = EINVAL; break;
88 case ERROR_INVALID_PASSWORD: error = EPERM; break;
89 case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
90 case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
91 case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
92 case ERROR_INVALID_WORKSTATION: error = EACCES; break;
93 case ERROR_IO_DEVICE: error = EIO; break;
94 case ERROR_IO_INCOMPLETE: error = EINTR; break;
95 case ERROR_LOCKED: error = EBUSY; break;
96 case ERROR_LOCK_VIOLATION: error = EACCES; break;
97 case ERROR_LOGON_FAILURE: error = EACCES; break;
98 case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
99 case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
100 case ERROR_MORE_DATA: error = EPIPE; break;
101 case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
102 case ERROR_NOACCESS: error = EFAULT; break;
103 case ERROR_NONE_MAPPED: error = EINVAL; break;
104 case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
105 case ERROR_NOT_READY: error = EAGAIN; break;
106 case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
107 case ERROR_NO_DATA: error = EPIPE; break;
108 case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
109 case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
110 case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
111 case ERROR_OPEN_FAILED: error = EIO; break;
112 case ERROR_OPEN_FILES: error = EBUSY; break;
113 case ERROR_OPERATION_ABORTED: error = EINTR; break;
114 case ERROR_OUTOFMEMORY: error = ENOMEM; break;
115 case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
116 case ERROR_PATH_BUSY: error = EBUSY; break;
117 case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
118 case ERROR_PIPE_BUSY: error = EBUSY; break;
119 case ERROR_PIPE_CONNECTED: error = EPIPE; break;
120 case ERROR_PIPE_LISTENING: error = EPIPE; break;
121 case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
122 case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
123 case ERROR_READ_FAULT: error = EIO; break;
124 case ERROR_SEEK: error = EIO; break;
125 case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
126 case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
127 case ERROR_SHARING_VIOLATION: error = EACCES; break;
128 case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
129 case ERROR_SWAPERROR: error = ENOENT; break;
130 case ERROR_TOO_MANY_LINKS: error = EMLINK; break;
131 case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
132 case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
133 case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
134 case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
135 case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
136 case ERROR_WRITE_FAULT: error = EIO; break;
137 case ERROR_WRITE_PROTECT: error = EROFS; break;
138 }
139 return error;
140}
141
142static int zero_fd = -1;
143static int rand_fd = -1;
144
145/*
146 * Determine if 'filename' corresponds to one of the supported
147 * device files. Constants for these are defined as an enum
148 * in mingw.h.
149 */
150int get_dev_type(const char *filename)
151{
152 int i;
153 const char *devname[NOT_DEVICE] = { "null", "zero", "urandom" };
154
155 if (filename && !strncmp(filename, "/dev/", 5)) {
156 for (i=0; i<NOT_DEVICE; ++i ) {
157 if (!strcmp(filename+5, devname[i])) {
158 return i;
159 }
160 }
161 }
162
163 return NOT_DEVICE;
164}
165
166void update_dev_fd(int dev, int fd)
167{
168 if (dev == DEV_ZERO)
169 zero_fd = fd;
170 else if (dev == DEV_URANDOM)
171 rand_fd = fd;
172}
173
174#undef open
175int mingw_open (const char *filename, int oflags, ...)
176{
177 va_list args;
178 unsigned mode;
179 int fd;
180 int special = (oflags & O_SPECIAL);
181 int dev = get_dev_type(filename);
182
183 /* /dev/null is always allowed, others only if O_SPECIAL is set */
184 if (dev != NOT_DEVICE && (dev == DEV_NULL || special)) {
185 filename = "nul";
186 oflags = O_RDWR;
187 }
188
189 va_start(args, oflags);
190 mode = va_arg(args, int);
191 va_end(args);
192
193 fd = open(filename, oflags&~O_SPECIAL, mode);
194 if (fd >= 0) {
195 update_dev_fd(dev, fd);
196 }
197 else if ((oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
198 DWORD attrs = GetFileAttributes(filename);
199 if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
200 errno = EISDIR;
201 }
202 return fd;
203}
204
205int mingw_xopen(const char *pathname, int flags)
206{
207 int ret;
208
209 /* allow use of special devices */
210 ret = mingw_open(pathname, flags|O_SPECIAL);
211 if (ret < 0) {
212 bb_perror_msg_and_die("can't open '%s'", pathname);
213 }
214 return ret;
215}
216
217#undef fopen
218FILE *mingw_fopen (const char *filename, const char *otype)
219{
220 if (filename && !strcmp(filename, "/dev/null"))
221 filename = "nul";
222 return fopen(filename, otype);
223}
224
225#undef read
226ssize_t mingw_read(int fd, void *buf, size_t count)
227{
228 if (fd == zero_fd) {
229 memset(buf, 0, count);
230 return count;
231 }
232 else if (fd == rand_fd) {
233 return get_random_bytes(buf, count);
234 }
235 return read(fd, buf, count);
236}
237
238#undef close
239int mingw_close(int fd)
240{
241 if (fd == zero_fd) {
242 zero_fd = -1;
243 }
244 if (fd == rand_fd) {
245 rand_fd = -1;
246 }
247 return close(fd);
248}
249
250#undef dup2
251int mingw_dup2 (int fd, int fdto)
252{
253 int ret = dup2(fd, fdto);
254 return ret != -1 ? fdto : -1;
255}
256
257/*
258 * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
259 * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
260 */
261static inline long long filetime_to_hnsec(const FILETIME *ft)
262{
263 long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
264 /* Windows to Unix Epoch conversion */
265 return winTime - 116444736000000000LL;
266}
267
268static inline time_t filetime_to_time_t(const FILETIME *ft)
269{
270 return (time_t)(filetime_to_hnsec(ft) / 10000000);
271}
272
273static inline mode_t file_attr_to_st_mode(DWORD attr)
274{
275 mode_t fMode = S_IRUSR|S_IRGRP|S_IROTH;
276 if (attr & FILE_ATTRIBUTE_DIRECTORY)
277 fMode |= S_IFDIR|S_IWUSR|S_IWGRP|S_IXUSR|S_IXGRP|S_IXOTH;
278 else
279 fMode |= S_IFREG;
280 if (!(attr & FILE_ATTRIBUTE_READONLY))
281 fMode |= S_IWUSR|S_IWGRP;
282 return fMode;
283}
284
285static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
286{
287 if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata))
288 return 0;
289
290 if (GetLastError() == ERROR_SHARING_VIOLATION) {
291 HANDLE hnd;
292 WIN32_FIND_DATA fd;
293
294 if ((hnd=FindFirstFile(fname, &fd)) != INVALID_HANDLE_VALUE) {
295 fdata->dwFileAttributes = fd.dwFileAttributes;
296 fdata->ftCreationTime = fd.ftCreationTime;
297 fdata->ftLastAccessTime = fd.ftLastAccessTime;
298 fdata->ftLastWriteTime = fd.ftLastWriteTime;
299 fdata->nFileSizeHigh = fd.nFileSizeHigh;
300 fdata->nFileSizeLow = fd.nFileSizeLow;
301 FindClose(hnd);
302 return 0;
303 }
304 }
305
306 switch (GetLastError()) {
307 case ERROR_ACCESS_DENIED:
308 case ERROR_SHARING_VIOLATION:
309 case ERROR_LOCK_VIOLATION:
310 case ERROR_SHARING_BUFFER_EXCEEDED:
311 return EACCES;
312 case ERROR_BUFFER_OVERFLOW:
313 return ENAMETOOLONG;
314 case ERROR_NOT_ENOUGH_MEMORY:
315 return ENOMEM;
316 default:
317 return ENOENT;
318 }
319}
320
321#undef umask
322mode_t mingw_umask(mode_t new_mode)
323{
324 static mode_t old_mode = DEFAULT_UMASK;
325 mode_t tmp_mode;
326
327 tmp_mode = old_mode;
328 old_mode = new_mode;
329
330 umask((new_mode & S_IWUSR) ? _S_IWRITE : 0);
331
332 return tmp_mode;
333}
334
335/*
336 * Examine a file's contents to determine if it can be executed. This
337 * should be a last resort: in most cases it's much more efficient to
338 * check the file extension.
339 *
340 * We look for two types of file: shell scripts and binary executables.
341 */
342static int has_exec_format(const char *name)
343{
344 int n, sig;
345 unsigned int offset;
346 unsigned char buf[1024];
347
348 /* special case: skip DLLs, there are thousands of them! */
349 if (is_suffixed_with_case(name, ".dll"))
350 return 0;
351
352 n = open_read_close(name, buf, sizeof(buf));
353 if (n < 4) /* at least '#!/x' and not error */
354 return 0;
355
356 /* shell script */
357 if (buf[0] == '#' && buf[1] == '!') {
358 return 1;
359 }
360
361 /*
362 * Poke about in file to see if it's a PE binary. I've just copied
363 * the magic from the file command.
364 */
365 if (buf[0] == 'M' && buf[1] == 'Z') {
366 offset = (buf[0x19] << 8) + buf[0x18];
367 if (offset > 0x3f) {
368 offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) +
369 (buf[0x3d] << 8) + buf[0x3c];
370 if (offset < sizeof(buf)-100) {
371 if (memcmp(buf+offset, "PE\0\0", 4) == 0) {
372 sig = (buf[offset+25] << 8) + buf[offset+24];
373 if (sig == 0x10b || sig == 0x20b) {
374 sig = (buf[offset+23] << 8) + buf[offset+22];
375 if ((sig & 0x2000) != 0) {
376 /* DLL */
377 return 0;
378 }
379 sig = buf[offset+92];
380 return (sig == 1 || sig == 2 || sig == 3 || sig == 7);
381 }
382 }
383 }
384 }
385 }
386
387 return 0;
388}
389
390/* We keep the do_lstat code in a separate function to avoid recursion.
391 * When a path ends with a slash, the stat will fail with ENOENT. In
392 * this case, we strip the trailing slashes and stat again.
393 *
394 * If follow is true then act like stat() and report on the link
395 * target. Otherwise report on the link itself.
396 */
397static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf)
398{
399 int err;
400 WIN32_FILE_ATTRIBUTE_DATA fdata;
401
402 if (!(err = get_file_attr(file_name, &fdata))) {
403 buf->st_ino = 0;
404 buf->st_uid = DEFAULT_UID;
405 buf->st_gid = DEFAULT_GID;
406 buf->st_nlink = 1;
407 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
408 if (S_ISREG(buf->st_mode) &&
409 (has_exe_suffix(file_name) || has_exec_format(file_name)))
410 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH;
411 buf->st_size = fdata.nFileSizeLow |
412 (((off64_t)fdata.nFileSizeHigh)<<32);
413 buf->st_dev = buf->st_rdev = 0; /* not used by Git */
414 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
415 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
416 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
417 if (fdata.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
418 WIN32_FIND_DATAA findbuf;
419 HANDLE handle = FindFirstFileA(file_name, &findbuf);
420 if (handle != INVALID_HANDLE_VALUE) {
421 if ((findbuf.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
422 (findbuf.dwReserved0 == IO_REPARSE_TAG_SYMLINK)) {
423 if (follow) {
424 char buffer[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
425 buf->st_size = readlink(file_name, buffer, MAXIMUM_REPARSE_DATA_BUFFER_SIZE);
426 } else {
427 buf->st_mode = S_IFLNK;
428 }
429 buf->st_mode |= S_IRUSR|S_IRGRP|S_IROTH;
430 if (!(findbuf.dwFileAttributes & FILE_ATTRIBUTE_READONLY))
431 buf->st_mode |= S_IWUSR|S_IWGRP;
432 }
433 FindClose(handle);
434 }
435 }
436
437 /*
438 * Assume a block is 4096 bytes and calculate number of 512 byte
439 * sectors.
440 */
441 buf->st_blksize = 4096;
442 buf->st_blocks = ((buf->st_size+4095)>>12)<<3;
443 return 0;
444 }
445 errno = err;
446 return -1;
447}
448
449/* We provide our own lstat/fstat functions, since the provided
450 * lstat/fstat functions are so slow. These stat functions are
451 * tailored for Git's usage (read: fast), and are not meant to be
452 * complete. Note that Git stat()s are redirected to mingw_lstat()
453 * too, since Windows doesn't really handle symlinks that well.
454 */
455static int do_stat_internal(int follow, const char *file_name, struct mingw_stat *buf)
456{
457 int namelen;
458 char alt_name[PATH_MAX];
459
460 if (!do_lstat(follow, file_name, buf))
461 return 0;
462
463 /* if file_name ended in a '/', Windows returned ENOENT;
464 * try again without trailing slashes
465 */
466 if (errno != ENOENT)
467 return -1;
468
469 namelen = strlen(file_name);
470 if (namelen && file_name[namelen-1] != '/')
471 return -1;
472 while (namelen && file_name[namelen-1] == '/')
473 --namelen;
474 if (!namelen || namelen >= PATH_MAX)
475 return -1;
476
477 memcpy(alt_name, file_name, namelen);
478 alt_name[namelen] = 0;
479 return do_lstat(follow, alt_name, buf);
480}
481
482int mingw_lstat(const char *file_name, struct mingw_stat *buf)
483{
484 return do_stat_internal(0, file_name, buf);
485}
486int mingw_stat(const char *file_name, struct mingw_stat *buf)
487{
488 return do_stat_internal(1, file_name, buf);
489}
490
491int mingw_fstat(int fd, struct mingw_stat *buf)
492{
493 HANDLE fh = (HANDLE)_get_osfhandle(fd);
494 BY_HANDLE_FILE_INFORMATION fdata;
495
496 if (fh == INVALID_HANDLE_VALUE) {
497 errno = EBADF;
498 return -1;
499 }
500 /* direct non-file handles to MS's fstat() */
501 if (GetFileType(fh) != FILE_TYPE_DISK) {
502 struct _stati64 buf64;
503
504 if ( _fstati64(fd, &buf64) != 0 ) {
505 return -1;
506 }
507 buf->st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
508 buf->st_size = buf64.st_size;
509 buf->st_atime = buf64.st_atime;
510 buf->st_mtime = buf64.st_mtime;
511 buf->st_ctime = buf64.st_ctime;
512 buf->st_blocks = ((buf64.st_size+4095)>>12)<<3;
513 goto success;
514 }
515
516 if (GetFileInformationByHandle(fh, &fdata)) {
517 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
518 buf->st_size = fdata.nFileSizeLow |
519 (((off64_t)fdata.nFileSizeHigh)<<32);
520 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
521 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
522 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
523 buf->st_blocks = ((buf->st_size+4095)>>12)<<3;
524 success:
525 buf->st_dev = buf->st_rdev = 0;
526 buf->st_ino = 0;
527 buf->st_uid = DEFAULT_UID;
528 buf->st_gid = DEFAULT_GID;
529 /* could use fdata.nNumberOfLinks but it's inconsistent with stat */
530 buf->st_nlink = 1;
531 buf->st_blksize = 4096;
532 return 0;
533 }
534
535 errno = EBADF;
536 return -1;
537}
538
539static inline void timeval_to_filetime(const struct timeval tv, FILETIME *ft)
540{
541 long long winTime = ((tv.tv_sec * 1000000LL) + tv.tv_usec) * 10LL + 116444736000000000LL;
542 ft->dwLowDateTime = winTime;
543 ft->dwHighDateTime = winTime >> 32;
544}
545
546int utimes(const char *file_name, const struct timeval tims[2])
547{
548 FILETIME mft, aft;
549 HANDLE fh;
550 DWORD flags, attrs;
551 int rc;
552
553 flags = FILE_ATTRIBUTE_NORMAL;
554
555 /* must have write permission */
556 attrs = GetFileAttributes(file_name);
557 if ( attrs != INVALID_FILE_ATTRIBUTES ) {
558 if ( attrs & FILE_ATTRIBUTE_READONLY ) {
559 /* ignore errors here; open() will report them */
560 SetFileAttributes(file_name, attrs & ~FILE_ATTRIBUTE_READONLY);
561 }
562
563 if ( attrs & FILE_ATTRIBUTE_DIRECTORY ) {
564 flags = FILE_FLAG_BACKUP_SEMANTICS;
565 }
566 }
567
568 fh = CreateFile(file_name, GENERIC_READ|GENERIC_WRITE,
569 FILE_SHARE_READ|FILE_SHARE_WRITE,
570 NULL, OPEN_EXISTING, flags, NULL);
571 if ( fh == INVALID_HANDLE_VALUE ) {
572 errno = err_win_to_posix(GetLastError());
573 rc = -1;
574 goto revert_attrs;
575 }
576
577 if (tims) {
578 timeval_to_filetime(tims[0], &aft);
579 timeval_to_filetime(tims[1], &mft);
580 }
581 else {
582 GetSystemTimeAsFileTime(&mft);
583 aft = mft;
584 }
585 if (!SetFileTime(fh, NULL, &aft, &mft)) {
586 errno = EINVAL;
587 rc = -1;
588 } else
589 rc = 0;
590 CloseHandle(fh);
591
592revert_attrs:
593 if (attrs != INVALID_FILE_ATTRIBUTES &&
594 (attrs & FILE_ATTRIBUTE_READONLY)) {
595 /* ignore errors again */
596 SetFileAttributes(file_name, attrs);
597 }
598 return rc;
599}
600
601unsigned int sleep (unsigned int seconds)
602{
603 Sleep(seconds*1000);
604 return 0;
605}
606
607int nanosleep(const struct timespec *req, struct timespec *rem)
608{
609 if (req->tv_nsec < 0 || 1000000000 <= req->tv_nsec) {
610 errno = EINVAL;
611 return -1;
612 }
613
614 Sleep(req->tv_sec*1000 + req->tv_nsec/1000000);
615
616 /* Sleep is not interruptible. So there is no remaining delay. */
617 if (rem != NULL) {
618 rem->tv_sec = 0;
619 rem->tv_nsec = 0;
620 }
621
622 return 0;
623}
624
625/*
626 * Windows' mktemp returns NULL on error whereas POSIX always returns the
627 * template and signals an error by making it an empty string.
628 */
629#undef mktemp
630char *mingw_mktemp(char *template)
631{
632 if ( mktemp(template) == NULL ) {
633 template[0] = '\0';
634 }
635
636 return template;
637}
638
639int mkstemp(char *template)
640{
641 char *filename = mktemp(template);
642 if (filename == NULL)
643 return -1;
644 return open(filename, O_RDWR | O_CREAT, 0600);
645}
646
647int gettimeofday(struct timeval *tv, void *tz UNUSED_PARAM)
648{
649 FILETIME ft;
650 long long hnsec;
651
652 GetSystemTimeAsFileTime(&ft);
653 hnsec = filetime_to_hnsec(&ft);
654 tv->tv_sec = hnsec / 10000000;
655 tv->tv_usec = (hnsec % 10000000) / 10;
656 return 0;
657}
658
659int pipe(int filedes[2])
660{
661 if (_pipe(filedes, PIPE_BUF, 0) < 0)
662 return -1;
663 return 0;
664}
665
666struct tm *gmtime_r(const time_t *timep, struct tm *result)
667{
668 /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
669 memcpy(result, gmtime(timep), sizeof(struct tm));
670 return result;
671}
672
673struct tm *localtime_r(const time_t *timep, struct tm *result)
674{
675 /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
676 memcpy(result, localtime(timep), sizeof(struct tm));
677 return result;
678}
679
680#undef getcwd
681char *mingw_getcwd(char *pointer, int len)
682{
683 char *ret = getcwd(pointer, len);
684 if (!ret)
685 return ret;
686 convert_slashes(ret);
687 return ret;
688}
689
690#undef rename
691int mingw_rename(const char *pold, const char *pnew)
692{
693 DWORD attrs;
694
695 /*
696 * Try native rename() first to get errno right.
697 * It is based on MoveFile(), which cannot overwrite existing files.
698 */
699 if (!rename(pold, pnew))
700 return 0;
701 if (errno != EEXIST)
702 return -1;
703 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
704 return 0;
705 /* TODO: translate more errors */
706 if (GetLastError() == ERROR_ACCESS_DENIED &&
707 (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
708 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
709 errno = EISDIR;
710 return -1;
711 }
712 if ((attrs & FILE_ATTRIBUTE_READONLY) &&
713 SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
714 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
715 return 0;
716 /* revert file attributes on failure */
717 SetFileAttributes(pnew, attrs);
718 }
719 }
720 errno = EACCES;
721 return -1;
722}
723
724static char *gethomedir(void)
725{
726 static char *buf = NULL;
727 DWORD len = PATH_MAX;
728 HANDLE h;
729
730 if (!buf)
731 buf = xzalloc(PATH_MAX);
732
733 if (buf[0])
734 return buf;
735
736 if ( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h) )
737 return buf;
738
739 GetUserProfileDirectory(h, buf, &len);
740 CloseHandle(h);
741 convert_slashes(buf);
742
743 return buf;
744}
745
746#define NAME_LEN 100
747static char *get_user_name(void)
748{
749 static char *user_name = NULL;
750 char *s;
751 DWORD len = NAME_LEN;
752
753 if ( user_name == NULL ) {
754 user_name = xzalloc(NAME_LEN);
755 }
756
757 if ( user_name[0] != '\0' ) {
758 return user_name;
759 }
760
761 if ( !GetUserName(user_name, &len) ) {
762 return NULL;
763 }
764
765 for ( s=user_name; *s; ++s ) {
766 if ( *s == ' ' ) {
767 *s = '_';
768 }
769 }
770
771 return user_name;
772}
773
774struct passwd *getpwnam(const char *name)
775{
776 const char *myname;
777
778 if ( (myname=get_user_name()) != NULL &&
779 strcmp(myname, name) == 0 ) {
780 return getpwuid(DEFAULT_UID);
781 }
782 else if (strcmp(name, "root") == 0) {
783 return getpwuid(0);
784 }
785
786 return NULL;
787}
788
789struct passwd *getpwuid(uid_t uid)
790{
791 static struct passwd p;
792
793 if (uid == 0) {
794 p.pw_name = (char *)"root";
795 p.pw_dir = (char *)"/";
796 }
797 else if (uid == DEFAULT_UID && (p.pw_name=get_user_name()) != NULL) {
798 p.pw_dir = gethomedir();
799 }
800 else {
801 return NULL;
802 }
803
804 p.pw_passwd = (char *)"";
805 p.pw_gecos = p.pw_name;
806 p.pw_shell = NULL;
807 p.pw_uid = uid;
808 p.pw_gid = uid;
809
810 return &p;
811}
812
813struct group *getgrgid(gid_t gid)
814{
815 static char *members[2] = { NULL, NULL };
816 static struct group g;
817
818 if (gid == 0) {
819 g.gr_name = (char *)"root";
820 }
821 else if (gid != DEFAULT_GID || (g.gr_name=get_user_name()) == NULL) {
822 return NULL;
823 }
824 g.gr_passwd = (char *)"";
825 g.gr_gid = gid;
826 members[0] = g.gr_name;
827 g.gr_mem = members;
828
829 return &g;
830}
831
832int getgrouplist(const char *user UNUSED_PARAM, gid_t group,
833 gid_t *groups, int *ngroups)
834{
835 if ( *ngroups == 0 ) {
836 *ngroups = 1;
837 return -1;
838 }
839
840 *ngroups = 1;
841 groups[0] = group;
842 return 1;
843}
844
845int getgroups(int n, gid_t *groups)
846{
847 if ( n == 0 ) {
848 return 1;
849 }
850
851 groups[0] = DEFAULT_GID;
852 return 1;
853}
854
855int getlogin_r(char *buf, size_t len)
856{
857 char *name;
858
859 if ( (name=get_user_name()) == NULL ) {
860 return -1;
861 }
862
863 if ( strlen(name) >= len ) {
864 errno = ERANGE;
865 return -1;
866 }
867
868 strcpy(buf, name);
869 return 0;
870}
871
872long sysconf(int name)
873{
874 if ( name == _SC_CLK_TCK ) {
875 return TICKS_PER_SECOND;
876 }
877 errno = EINVAL;
878 return -1;
879}
880
881clock_t times(struct tms *buf)
882{
883 memset(buf, 0, sizeof(*buf));
884 return 0;
885}
886
887int link(const char *oldpath, const char *newpath)
888{
889 typedef BOOL (WINAPI *T)(const char*, const char*, LPSECURITY_ATTRIBUTES);
890 static T create_hard_link = NULL;
891 if (!create_hard_link) {
892 create_hard_link = (T) GetProcAddress(
893 GetModuleHandle("kernel32.dll"), "CreateHardLinkA");
894 if (!create_hard_link)
895 create_hard_link = (T)-1;
896 }
897 if (create_hard_link == (T)-1) {
898 errno = ENOSYS;
899 return -1;
900 }
901 if (!create_hard_link(newpath, oldpath, NULL)) {
902 errno = err_win_to_posix(GetLastError());
903 return -1;
904 }
905 return 0;
906}
907
908static char *resolve_symlinks(char *path)
909{
910 HANDLE h = INVALID_HANDLE_VALUE;
911 DECLARE_PROC_ADDR(DWORD, GetFinalPathNameByHandleA, HANDLE,
912 LPSTR, DWORD, DWORD);
913
914 if (!INIT_PROC_ADDR(kernel32.dll, GetFinalPathNameByHandleA))
915 return NULL;
916
917 /* need a file handle to resolve symlinks */
918 h = CreateFileA(path, GENERIC_READ, FILE_SHARE_READ, NULL,
919 OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_BACKUP_SEMANTICS,
920 NULL);
921 if (h != INVALID_HANDLE_VALUE) {
922 /* normalize the path and return it on success */
923 DWORD status = GetFinalPathNameByHandleA(h, path, MAX_PATH,
924 FILE_NAME_NORMALIZED|VOLUME_NAME_DOS);
925 CloseHandle(h);
926 if (status != 0 && status < MAX_PATH) {
927 /* skip '\\?\' prefix */
928 return (!strncmp(path, "\\\\?\\", 4)) ? (path + 4) : path;
929 }
930 }
931
932 errno = err_win_to_posix(GetLastError());
933 return NULL;
934}
935
936/*
937 * Emulate realpath in two stages:
938 *
939 * - _fullpath handles './', '../' and extra '/' characters. The
940 * resulting path may not refer to an actual file.
941 *
942 * - resolve_symlinks checks that the file exists (by opening it) and
943 * resolves symlinks by calling GetFinalPathNameByHandleA.
944 */
945char *realpath(const char *path, char *resolved_path)
946{
947 char buffer[MAX_PATH];
948 char *real_path;
949
950 /* enforce glibc pre-2.3 behaviour */
951 if (path == NULL || resolved_path == NULL) {
952 errno = EINVAL;
953 return NULL;
954 }
955
956 if (_fullpath(buffer, path, MAX_PATH) &&
957 (real_path=resolve_symlinks(buffer))) {
958 strcpy(resolved_path, real_path);
959 convert_slashes(resolved_path);
960 return resolved_path;
961 }
962 return NULL;
963}
964
965const char *get_busybox_exec_path(void)
966{
967 static char *path = NULL;
968
969 if (!path) {
970 path = xzalloc(PATH_MAX);
971 }
972
973 if (!*path) {
974 GetModuleFileName(NULL, path, PATH_MAX);
975 convert_slashes(path);
976 }
977 return path;
978}
979
980#undef mkdir
981int mingw_mkdir(const char *path, int mode UNUSED_PARAM)
982{
983 int ret;
984 struct stat st;
985 int lerrno = 0;
986
987 if ( (ret=mkdir(path)) < 0 ) {
988 lerrno = errno;
989 if ( lerrno == EACCES && stat(path, &st) == 0 ) {
990 ret = 0;
991 lerrno = 0;
992 }
993 }
994
995 errno = lerrno;
996 return ret;
997}
998
999#undef chmod
1000int mingw_chmod(const char *path, int mode)
1001{
1002 WIN32_FILE_ATTRIBUTE_DATA fdata;
1003
1004 if ( get_file_attr(path, &fdata) == 0 &&
1005 fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
1006 mode |= 0222;
1007 }
1008
1009 return chmod(path, mode);
1010}
1011
1012int fcntl(int fd, int cmd, ...)
1013{
1014 va_list arg;
1015 int result = -1;
1016 char *fds;
1017 int target, i, newfd;
1018
1019 va_start(arg, cmd);
1020
1021 switch (cmd) {
1022 case F_GETFD:
1023 case F_SETFD:
1024 case F_GETFL:
1025 /*
1026 * Our fake F_GETFL won't matter if the return value is used as
1027 * fcntl(fd, F_SETFL, ret|something);
1028 * because F_SETFL isn't supported either.
1029 */
1030 result = 0;
1031 break;
1032 case F_DUPFD:
1033 target = va_arg(arg, int);
1034 fds = xzalloc(target);
1035 while ((newfd = dup(fd)) < target && newfd >= 0) {
1036 fds[newfd] = 1;
1037 }
1038 for (i = 0; i < target; ++i) {
1039 if (fds[i]) {
1040 close(i);
1041 }
1042 }
1043 free(fds);
1044 result = newfd;
1045 break;
1046 default:
1047 errno = ENOSYS;
1048 break;
1049 }
1050
1051 va_end(arg);
1052 return result;
1053}
1054
1055#undef unlink
1056int mingw_unlink(const char *pathname)
1057{
1058 /* read-only files cannot be removed */
1059 chmod(pathname, 0666);
1060 return unlink(pathname);
1061}
1062
1063#undef strftime
1064size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm)
1065{
1066 size_t ret;
1067 char buffer[64];
1068 const char *replace;
1069 char *t;
1070 char *fmt;
1071 struct tm tm2;
1072
1073 /*
1074 * Emulate some formats that Windows' strftime lacks.
1075 * - '%e' day of the month with space padding
1076 * - '%s' number of seconds since the Unix epoch
1077 * - '%T' same as %H:%M:%S
1078 * - '%z' timezone offset
1079 * Also, permit the '-' modifier to omit padding. Windows uses '#'.
1080 */
1081 fmt = xstrdup(format);
1082 for ( t=fmt; *t; ++t ) {
1083 if ( *t == '%' ) {
1084 replace = NULL;
1085 if ( t[1] == 'e' ) {
1086 if ( tm->tm_mday >= 0 && tm->tm_mday <= 99 ) {
1087 sprintf(buffer, "%2d", tm->tm_mday);
1088 }
1089 else {
1090 strcpy(buffer, " ");
1091 }
1092 replace = buffer;
1093 }
1094 else if ( t[1] == 's' ) {
1095 tm2 = *tm;
1096 sprintf(buffer, "%d", (int)mktime(&tm2));
1097 replace = buffer;
1098 }
1099 else if ( t[1] == 'T' ) {
1100 replace = "%H:%M:%S";
1101 }
1102 else if ( t[1] == 'z' ) {
1103 _tzset();
1104 if ( tm->tm_isdst >= 0 ) {
1105 int offset = (int)_timezone - (tm->tm_isdst > 0 ? 3600 : 0);
1106 int hr, min;
1107
1108 if ( offset > 0 ) {
1109 buffer[0] = '-';
1110 }
1111 else {
1112 buffer[0] = '+';
1113 offset = -offset;
1114 }
1115
1116 hr = offset / 3600;
1117 min = (offset % 3600) / 60;
1118 sprintf(buffer+1, "%04d", hr*100 + min);
1119 }
1120 else {
1121 buffer[0] = '\0';
1122 }
1123 replace = buffer;
1124 }
1125 else if ( t[1] == '-' && t[2] != '\0' &&
1126 strchr("dHIjmMSUwWyY", t[2]) ) {
1127 /* Microsoft uses '#' rather than '-' to remove padding */
1128 t[1] = '#';
1129 }
1130 else if ( t[1] != '\0' ) {
1131 ++t;
1132 }
1133
1134 if (replace) {
1135 int m;
1136 char *newfmt;
1137
1138 *t = '\0';
1139 m = t - fmt;
1140 newfmt = xasprintf("%s%s%s", fmt, replace, t+2);
1141 free(fmt);
1142 t = newfmt + m + strlen(replace);
1143 fmt = newfmt;
1144 }
1145 }
1146 }
1147
1148 ret = strftime(buf, max, fmt, tm);
1149 free(fmt);
1150
1151 return ret;
1152}
1153
1154int stime(time_t *t UNUSED_PARAM)
1155{
1156 errno = EPERM;
1157 return -1;
1158}
1159
1160#undef access
1161int mingw_access(const char *name, int mode)
1162{
1163 int ret;
1164 struct stat s;
1165
1166 /* Windows can only handle test for existence, read or write */
1167 if (mode == F_OK || (mode & ~X_OK)) {
1168 ret = _access(name, mode & ~X_OK);
1169 if (ret < 0 || !(mode & X_OK)) {
1170 return ret;
1171 }
1172 }
1173
1174 if (!mingw_stat(name, &s)) {
1175 if ((s.st_mode&S_IXUSR)) {
1176 return 0;
1177 }
1178 errno = EACCES;
1179 }
1180
1181 return -1;
1182}
1183
1184#undef rmdir
1185int mingw_rmdir(const char *path)
1186{
1187 /* read-only directories cannot be removed */
1188 chmod(path, 0666);
1189 return rmdir(path);
1190}
1191
1192#define NUMEXT 5
1193static const char win_suffix[NUMEXT][4] = { "sh", "com", "exe", "bat", "cmd" };
1194
1195static int has_win_suffix(const char *name, int start)
1196{
1197 const char *dot = strrchr(bb_basename(name), '.');
1198 int i;
1199
1200 if (dot != NULL && strlen(dot) < 5) {
1201 for (i=start; i<NUMEXT; ++i) {
1202 if (!strcasecmp(dot+1, win_suffix[i])) {
1203 return 1;
1204 }
1205 }
1206 }
1207 return 0;
1208}
1209
1210int has_bat_suffix(const char *name)
1211{
1212 return has_win_suffix(name, 3);
1213}
1214
1215int has_exe_suffix(const char *name)
1216{
1217 return has_win_suffix(name, 0);
1218}
1219
1220int has_exe_suffix_or_dot(const char *name)
1221{
1222 return last_char_is(name, '.') || has_win_suffix(name, 0);
1223}
1224
1225/* Check if path can be made into an executable by adding a suffix.
1226 * The suffix is added to the end of the argument which must be
1227 * long enough to allow this.
1228 *
1229 * If the return value is TRUE the argument contains the new path,
1230 * if FALSE the argument is unchanged.
1231 */
1232int add_win32_extension(char *p)
1233{
1234 if (!has_exe_suffix_or_dot(p)) {
1235 int i, len = strlen(p);
1236
1237 p[len] = '.';
1238 for (i=0; i<NUMEXT; ++i) {
1239 strcpy(p+len+1, win_suffix[i]);
1240 if (file_is_executable(p))
1241 return TRUE;
1242 }
1243 p[len] = '\0';
1244 }
1245 return FALSE;
1246}
1247
1248/* Check if path can be made into an executable by adding a suffix.
1249 * Return an allocated string containing the path if it can;
1250 * return NULL if not.
1251 *
1252 * If path already has a suffix don't even bother trying.
1253 */
1254char *alloc_win32_extension(const char *p)
1255{
1256 if (!has_exe_suffix_or_dot(p)) {
1257 int len = strlen(p);
1258 char *path = strcpy(xmalloc(len+5), p);
1259
1260 if (add_win32_extension(path))
1261 return path;
1262 free(path);
1263 }
1264 return NULL;
1265}
1266
1267void FAST_FUNC convert_slashes(char *p)
1268{
1269 for (; *p; ++p) {
1270 if ( *p == '\\' ) {
1271 *p = '/';
1272 }
1273 }
1274}
1275
1276#undef opendir
1277DIR *mingw_opendir(const char *path)
1278{
1279 char name[4];
1280
1281 if (isalpha(path[0]) && path[1] == ':' && path[2] == '\0') {
1282 strcpy(name, path);
1283 name[2] = '/';
1284 name[3] = '\0';
1285 path = name;
1286 }
1287
1288 return opendir(path);
1289}
1290
1291off_t mingw_lseek(int fd, off_t offset, int whence)
1292{
1293 HANDLE h = (HANDLE)_get_osfhandle(fd);
1294 if (h == INVALID_HANDLE_VALUE) {
1295 errno = EBADF;
1296 return -1;
1297 }
1298 if (GetFileType(h) != FILE_TYPE_DISK) {
1299 errno = ESPIPE;
1300 return -1;
1301 }
1302 return _lseeki64(fd, offset, whence);
1303}
1304
1305#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
1306#undef GetTickCount64
1307
1308ULONGLONG CompatGetTickCount64(void)
1309{
1310 DECLARE_PROC_ADDR(ULONGLONG, GetTickCount64, void);
1311
1312 if (!INIT_PROC_ADDR(kernel32.dll, GetTickCount64)) {
1313 return (ULONGLONG)GetTickCount();
1314 }
1315
1316 return GetTickCount64();
1317}
1318#endif
1319
1320#if ENABLE_FEATURE_INSTALLER
1321/*
1322 * Enumerate the names of all hard links to a file. The first call
1323 * provides the file name as the first argument; subsequent calls must
1324 * set the first argument to NULL. Returns 0 on error or when there are
1325 * no more links.
1326 */
1327int enumerate_links(const char *file, char *name)
1328{
1329 static HANDLE h = INVALID_HANDLE_VALUE;
1330 char aname[PATH_MAX];
1331 wchar_t wname[PATH_MAX];
1332 DWORD length = PATH_MAX;
1333 DECLARE_PROC_ADDR(HANDLE, FindFirstFileNameW, LPCWSTR, DWORD, LPDWORD,
1334 PWSTR);
1335 DECLARE_PROC_ADDR(BOOL, FindNextFileNameW, HANDLE, LPDWORD, PWSTR);
1336
1337 if (!INIT_PROC_ADDR(kernel32.dll, FindFirstFileNameW) ||
1338 !INIT_PROC_ADDR(kernel32.dll, FindNextFileNameW))
1339 return 0;
1340
1341 if (file != NULL) {
1342 wchar_t wfile[PATH_MAX];
1343 MultiByteToWideChar(CP_ACP, 0, file, -1, wfile, PATH_MAX);
1344 h = FindFirstFileNameW(wfile, 0, &length, wname);
1345 if (h == INVALID_HANDLE_VALUE)
1346 return 0;
1347 }
1348 else if (!FindNextFileNameW(h, &length, wname)) {
1349 FindClose(h);
1350 h = INVALID_HANDLE_VALUE;
1351 return 0;
1352 }
1353 WideCharToMultiByte(CP_ACP, 0, wname, -1, aname, PATH_MAX, NULL, NULL);
1354 realpath(aname, name);
1355 return 1;
1356}
1357#endif
diff --git a/win32/mntent.c b/win32/mntent.c
new file mode 100644
index 000000000..f9a2d26d4
--- /dev/null
+++ b/win32/mntent.c
@@ -0,0 +1,75 @@
1/*
2 * A simple WIN32 implementation of mntent routines. It only handles
3 * fixed logical drives.
4 */
5#include "libbb.h"
6
7struct mntdata {
8 DWORD flags;
9 int index;
10 struct mntent me;
11 char mnt_fsname[4];
12 char mnt_dir[4];
13 char mnt_type[100];
14 char mnt_opts[4];
15};
16
17FILE *setmntent(const char *file UNUSED_PARAM, const char *mode UNUSED_PARAM)
18{
19 struct mntdata *data;
20
21 if ( (data=malloc(sizeof(struct mntdata))) == NULL ) {
22 return NULL;
23 }
24
25 data->flags = GetLogicalDrives();
26 data->index = -1;
27
28 return (FILE *)data;
29}
30
31struct mntent *getmntent(FILE *stream)
32{
33 struct mntdata *data = (struct mntdata *)stream;
34 struct mntent *entry;
35
36 data->me.mnt_fsname = data->mnt_fsname;
37 data->me.mnt_dir = data->mnt_dir;
38 data->me.mnt_type = data->mnt_type;
39 data->me.mnt_opts = data->mnt_opts;
40 data->me.mnt_freq = 0;
41 data->me.mnt_passno = 0;
42
43 entry = NULL;
44 while ( ++data->index < 26 ) {
45 if ( (data->flags & 1<<data->index) != 0 ) {
46 data->mnt_fsname[0] = 'A' + data->index;
47 data->mnt_fsname[1] = ':';
48 data->mnt_fsname[2] = '\0';
49 data->mnt_dir[0] = 'A' + data->index;
50 data->mnt_dir[1] = ':';
51 data->mnt_dir[2] = '\\';
52 data->mnt_dir[3] = '\0';
53 data->mnt_type[0] = '\0';
54 data->mnt_opts[0] = '\0';
55
56 if ( GetDriveType(data->mnt_dir) == DRIVE_FIXED ) {
57 if ( !GetVolumeInformation(data->mnt_dir, NULL, 0, NULL, NULL,
58 NULL, data->mnt_type, 100) ) {
59 data->mnt_type[0] = '\0';
60 }
61
62 entry = &data->me;
63 break;
64 }
65 }
66 }
67
68 return entry;
69}
70
71int endmntent(FILE *stream)
72{
73 free(stream);
74 return 0;
75}
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
6struct 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
15extern FILE *setmntent(const char *file, const char *mode);
16extern struct mntent *getmntent(FILE *stream);
17extern 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
3int 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
13void 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
22int 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
50int 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
57int 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
64int 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
71int mingw_shutdown(int sockfd, int how)
72{
73 SOCKET s = (SOCKET)_get_osfhandle(sockfd);
74 return shutdown(s, how);
75}
76
77#undef listen
78int mingw_listen(int sockfd, int backlog)
79{
80 SOCKET s = (SOCKET)_get_osfhandle(sockfd);
81 return listen(s, backlog);
82}
83
84#undef accept
85int 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..3294fdc96
--- /dev/null
+++ b/win32/poll.c
@@ -0,0 +1,604 @@
1/* Emulation for poll(2)
2 Contributed by Paolo Bonzini.
3
4 Copyright 2001-2003, 2006-2018 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 <https://www.gnu.org/licenses/>. */
20
21/* Tell gcc not to warn about the (nfd < 0) tests, below. */
22#if (__GNUC__ == 4 && 3 <= __GNUC_MINOR__) || 4 < __GNUC__
23# pragma GCC diagnostic ignored "-Wtype-limits"
24#endif
25
26#include <malloc.h>
27
28#include <sys/types.h>
29
30/* Specification. */
31#include <poll.h>
32
33#include <errno.h>
34#include <limits.h>
35#include <assert.h>
36
37#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
38# define WINDOWS_NATIVE
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 <unistd.h>
51#endif
52
53#include <sys/select.h>
54#include <sys/socket.h>
55
56#ifdef HAVE_SYS_IOCTL_H
57# include <sys/ioctl.h>
58#endif
59#ifdef HAVE_SYS_FILIO_H
60# include <sys/filio.h>
61#endif
62
63#include <time.h>
64
65#ifndef INFTIM
66# define INFTIM (-1)
67#endif
68
69/* BeOS does not have MSG_PEEK. */
70#ifndef MSG_PEEK
71# define MSG_PEEK 0
72#endif
73
74#ifdef WINDOWS_NATIVE
75
76/* Here we need the recv() function from Windows, that takes a SOCKET as
77 first argument, not any possible gnulib override. */
78# undef recv
79
80/* Here we need the select() function from Windows, because we pass bit masks
81 of SOCKETs, not bit masks of FDs. */
82# undef select
83
84static BOOL IsConsoleHandle (HANDLE h)
85{
86 DWORD mode;
87 return GetConsoleMode (h, &mode) != 0;
88}
89
90static BOOL
91IsSocketHandle (HANDLE h)
92{
93 WSANETWORKEVENTS ev;
94
95 if (IsConsoleHandle (h))
96 return FALSE;
97
98 /* Under Wine, it seems that getsockopt returns 0 for pipes too.
99 WSAEnumNetworkEvents instead distinguishes the two correctly. */
100 ev.lNetworkEvents = 0xDEADBEEF;
101 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
102 return ev.lNetworkEvents != 0xDEADBEEF;
103}
104
105/* Declare data structures for ntdll functions. */
106typedef struct _FILE_PIPE_LOCAL_INFORMATION {
107 ULONG NamedPipeType;
108 ULONG NamedPipeConfiguration;
109 ULONG MaximumInstances;
110 ULONG CurrentInstances;
111 ULONG InboundQuota;
112 ULONG ReadDataAvailable;
113 ULONG OutboundQuota;
114 ULONG WriteQuotaAvailable;
115 ULONG NamedPipeState;
116 ULONG NamedPipeEnd;
117} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
118
119typedef struct _IO_STATUS_BLOCK
120{
121 union {
122 DWORD Status;
123 PVOID Pointer;
124 } u;
125 ULONG_PTR Information;
126} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
127
128typedef enum _FILE_INFORMATION_CLASS {
129 FilePipeLocalInformation = 24
130} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
131
132typedef DWORD (WINAPI *PNtQueryInformationFile)
133 (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
134
135# ifndef PIPE_BUF
136# define PIPE_BUF 512
137# endif
138
139/* Compute revents values for file handle H. If some events cannot happen
140 for the handle, eliminate them from *P_SOUGHT. */
141
142static int
143windows_compute_revents (HANDLE h, int *p_sought)
144{
145 int i, ret, happened;
146 INPUT_RECORD *irbuffer;
147 DWORD avail, nbuffer;
148 BOOL bRet;
149 IO_STATUS_BLOCK iosb;
150 FILE_PIPE_LOCAL_INFORMATION fpli;
151 static PNtQueryInformationFile NtQueryInformationFile;
152 static BOOL once_only;
153
154 switch (GetFileType (h))
155 {
156 case FILE_TYPE_PIPE:
157 if (!once_only)
158 {
159 NtQueryInformationFile = (PNtQueryInformationFile)
160 GetProcAddress (GetModuleHandle ("ntdll.dll"),
161 "NtQueryInformationFile");
162 once_only = TRUE;
163 }
164
165 happened = 0;
166 if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
167 {
168 if (avail)
169 happened |= *p_sought & (POLLIN | POLLRDNORM);
170 }
171 else if (GetLastError () == ERROR_BROKEN_PIPE)
172 happened |= POLLHUP;
173
174 else
175 {
176 /* It was the write-end of the pipe. Check if it is writable.
177 If NtQueryInformationFile fails, optimistically assume the pipe is
178 writable. This could happen on Windows 9x, where
179 NtQueryInformationFile is not available, or if we inherit a pipe
180 that doesn't permit FILE_READ_ATTRIBUTES access on the write end
181 (I think this should not happen since Windows XP SP2; WINE seems
182 fine too). Otherwise, ensure that enough space is available for
183 atomic writes. */
184 memset (&iosb, 0, sizeof (iosb));
185 memset (&fpli, 0, sizeof (fpli));
186
187 if (!NtQueryInformationFile
188 || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
189 FilePipeLocalInformation)
190 || fpli.WriteQuotaAvailable >= PIPE_BUF
191 || (fpli.OutboundQuota < PIPE_BUF &&
192 fpli.WriteQuotaAvailable == fpli.OutboundQuota))
193 happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
194 }
195 return happened;
196
197 case FILE_TYPE_CHAR:
198 ret = WaitForSingleObject (h, 0);
199 if (!IsConsoleHandle (h))
200 return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0;
201
202 nbuffer = avail = 0;
203 bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
204 if (bRet)
205 {
206 /* Input buffer. */
207 *p_sought &= POLLIN | POLLRDNORM;
208 if (nbuffer == 0)
209 return POLLHUP;
210 if (!*p_sought)
211 return 0;
212
213 irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
214 bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
215 if (!bRet || avail == 0)
216 return POLLHUP;
217
218 for (i = 0; i < avail; i++)
219 if (irbuffer[i].EventType == KEY_EVENT)
220 return *p_sought;
221 return 0;
222 }
223 else
224 {
225 /* Screen buffer. */
226 *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND;
227 return *p_sought;
228 }
229
230 default:
231 ret = WaitForSingleObject (h, 0);
232 if (ret == WAIT_OBJECT_0)
233 return *p_sought & ~(POLLPRI | POLLRDBAND);
234
235 return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
236 }
237}
238
239/* Convert fd_sets returned by select into revents values. */
240
241static int
242windows_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
243{
244 int happened = 0;
245
246 if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT)
247 happened |= (POLLIN | POLLRDNORM) & sought;
248
249 else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
250 {
251 int r, error;
252
253 char data[64];
254 WSASetLastError (0);
255 r = recv (h, data, sizeof (data), MSG_PEEK);
256 error = WSAGetLastError ();
257 WSASetLastError (0);
258
259 if (r > 0 || error == WSAENOTCONN)
260 happened |= (POLLIN | POLLRDNORM) & sought;
261
262 /* Distinguish hung-up sockets from other errors. */
263 else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET
264 || error == WSAECONNABORTED || error == WSAENETRESET)
265 happened |= POLLHUP;
266
267 else
268 happened |= POLLERR;
269 }
270
271 if (lNetworkEvents & (FD_WRITE | FD_CONNECT))
272 happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
273
274 if (lNetworkEvents & FD_OOB)
275 happened |= (POLLPRI | POLLRDBAND) & sought;
276
277 return happened;
278}
279
280#else /* !MinGW */
281
282/* Convert select(2) returned fd_sets into poll(2) revents values. */
283static int
284compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds)
285{
286 int happened = 0;
287 if (FD_ISSET (fd, rfds))
288 {
289 int r;
290 int socket_errno;
291
292# if defined __MACH__ && defined __APPLE__
293 /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
294 for some kinds of descriptors. Detect if this descriptor is a
295 connected socket, a server socket, or something else using a
296 0-byte recv, and use ioctl(2) to detect POLLHUP. */
297 r = recv (fd, NULL, 0, MSG_PEEK);
298 socket_errno = (r < 0) ? errno : 0;
299 if (r == 0 || socket_errno == ENOTSOCK)
300 ioctl (fd, FIONREAD, &r);
301# else
302 char data[64];
303 r = recv (fd, data, sizeof (data), MSG_PEEK);
304 socket_errno = (r < 0) ? errno : 0;
305# endif
306 if (r == 0)
307 happened |= POLLHUP;
308
309 /* If the event happened on an unconnected server socket,
310 that's fine. */
311 else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN))
312 happened |= (POLLIN | POLLRDNORM) & sought;
313
314 /* Distinguish hung-up sockets from other errors. */
315 else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET
316 || socket_errno == ECONNABORTED || socket_errno == ENETRESET)
317 happened |= POLLHUP;
318
319 /* some systems can't use recv() on non-socket, including HP NonStop */
320 else if (socket_errno == ENOTSOCK)
321 happened |= (POLLIN | POLLRDNORM) & sought;
322
323 else
324 happened |= POLLERR;
325 }
326
327 if (FD_ISSET (fd, wfds))
328 happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
329
330 if (FD_ISSET (fd, efds))
331 happened |= (POLLPRI | POLLRDBAND) & sought;
332
333 return happened;
334}
335#endif /* !MinGW */
336
337int
338poll (struct pollfd *pfd, nfds_t nfd, int timeout)
339{
340#ifndef WINDOWS_NATIVE
341 fd_set rfds, wfds, efds;
342 struct timeval tv;
343 struct timeval *ptv;
344 int maxfd, rc;
345 nfds_t i;
346
347 if (nfd > INT_MAX)
348 {
349 errno = EINVAL;
350 return -1;
351 }
352 /* Don't check directly for NFD greater than OPEN_MAX. Any practical use
353 of a too-large NFD is caught by one of the other checks below, and
354 checking directly for getdtablesize is too much of a portability
355 and/or performance and/or correctness hassle. */
356
357 /* EFAULT is not necessary to implement, but let's do it in the
358 simplest case. */
359 if (!pfd && nfd)
360 {
361 errno = EFAULT;
362 return -1;
363 }
364
365 /* convert timeout number into a timeval structure */
366 if (timeout == 0)
367 {
368 ptv = &tv;
369 ptv->tv_sec = 0;
370 ptv->tv_usec = 0;
371 }
372 else if (timeout > 0)
373 {
374 ptv = &tv;
375 ptv->tv_sec = timeout / 1000;
376 ptv->tv_usec = (timeout % 1000) * 1000;
377 }
378 else if (timeout == INFTIM)
379 /* wait forever */
380 ptv = NULL;
381 else
382 {
383 errno = EINVAL;
384 return -1;
385 }
386
387 /* create fd sets and determine max fd */
388 maxfd = -1;
389 FD_ZERO (&rfds);
390 FD_ZERO (&wfds);
391 FD_ZERO (&efds);
392 for (i = 0; i < nfd; i++)
393 {
394 if (pfd[i].fd < 0)
395 continue;
396 if (maxfd < pfd[i].fd)
397 {
398 maxfd = pfd[i].fd;
399 if (FD_SETSIZE <= maxfd)
400 {
401 errno = EINVAL;
402 return -1;
403 }
404 }
405 if (pfd[i].events & (POLLIN | POLLRDNORM))
406 FD_SET (pfd[i].fd, &rfds);
407
408 /* see select(2): "the only exceptional condition detectable
409 is out-of-band data received on a socket", hence we push
410 POLLWRBAND events onto wfds instead of efds. */
411 if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
412 FD_SET (pfd[i].fd, &wfds);
413 if (pfd[i].events & (POLLPRI | POLLRDBAND))
414 FD_SET (pfd[i].fd, &efds);
415 }
416
417 /* examine fd sets */
418 rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
419 if (rc < 0)
420 return rc;
421
422 /* establish results */
423 rc = 0;
424 for (i = 0; i < nfd; i++)
425 {
426 pfd[i].revents = (pfd[i].fd < 0
427 ? 0
428 : compute_revents (pfd[i].fd, pfd[i].events,
429 &rfds, &wfds, &efds));
430 rc += pfd[i].revents != 0;
431 }
432
433 return rc;
434#else
435 static struct timeval tv0;
436 static HANDLE hEvent;
437 WSANETWORKEVENTS ev;
438 HANDLE h, handle_array[FD_SETSIZE + 2];
439 DWORD ret, wait_timeout, nhandles;
440 fd_set rfds, wfds, xfds;
441 BOOL poll_again;
442 MSG msg;
443 int rc = 0;
444 nfds_t i;
445
446 if (nfd > INT_MAX || timeout < -1)
447 {
448 errno = EINVAL;
449 return -1;
450 }
451
452 if (!hEvent)
453 hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
454
455restart:
456 handle_array[0] = hEvent;
457 nhandles = 1;
458 FD_ZERO (&rfds);
459 FD_ZERO (&wfds);
460 FD_ZERO (&xfds);
461
462 /* Classify socket handles and create fd sets. */
463 for (i = 0; i < nfd; i++)
464 {
465 int sought = pfd[i].events;
466 pfd[i].revents = 0;
467 if (pfd[i].fd < 0)
468 continue;
469 if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND
470 | POLLPRI | POLLRDBAND)))
471 continue;
472
473 h = (HANDLE) _get_osfhandle (pfd[i].fd);
474 assert (h != NULL);
475 if (IsSocketHandle (h))
476 {
477 int requested = FD_CLOSE;
478
479 /* see above; socket handles are mapped onto select. */
480 if (sought & (POLLIN | POLLRDNORM))
481 {
482 requested |= FD_READ | FD_ACCEPT;
483 FD_SET ((SOCKET) h, &rfds);
484 }
485 if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND))
486 {
487 requested |= FD_WRITE | FD_CONNECT;
488 FD_SET ((SOCKET) h, &wfds);
489 }
490 if (sought & (POLLPRI | POLLRDBAND))
491 {
492 requested |= FD_OOB;
493 FD_SET ((SOCKET) h, &xfds);
494 }
495
496 if (requested)
497 WSAEventSelect ((SOCKET) h, hEvent, requested);
498 }
499 else
500 {
501 /* Poll now. If we get an event, do not poll again. Also,
502 screen buffer handles are waitable, and they'll block until
503 a character is available. windows_compute_revents eliminates
504 bits for the "wrong" direction. */
505 pfd[i].revents = windows_compute_revents (h, &sought);
506 if (sought)
507 handle_array[nhandles++] = h;
508 if (pfd[i].revents)
509 timeout = 0;
510 }
511 }
512
513 if (select (0, &rfds, &wfds, &xfds, &tv0) > 0)
514 {
515 /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but
516 no need to call select again. */
517 poll_again = FALSE;
518 wait_timeout = 0;
519 }
520 else
521 {
522 poll_again = TRUE;
523 if (timeout == INFTIM)
524 wait_timeout = INFINITE;
525 else
526 wait_timeout = timeout;
527 }
528
529 for (;;)
530 {
531 ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
532 wait_timeout, QS_ALLINPUT);
533
534 if (ret == WAIT_OBJECT_0 + nhandles)
535 {
536 /* new input of some other kind */
537 BOOL bRet;
538 while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
539 {
540 TranslateMessage (&msg);
541 DispatchMessage (&msg);
542 }
543 }
544 else
545 break;
546 }
547
548 if (poll_again)
549 select (0, &rfds, &wfds, &xfds, &tv0);
550
551 /* Place a sentinel at the end of the array. */
552 handle_array[nhandles] = NULL;
553 nhandles = 1;
554 for (i = 0; i < nfd; i++)
555 {
556 int happened;
557
558 if (pfd[i].fd < 0)
559 continue;
560 if (!(pfd[i].events & (POLLIN | POLLRDNORM |
561 POLLOUT | POLLWRNORM | POLLWRBAND)))
562 continue;
563
564 h = (HANDLE) _get_osfhandle (pfd[i].fd);
565 if (h != handle_array[nhandles])
566 {
567 /* It's a socket. */
568 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
569 WSAEventSelect ((SOCKET) h, 0, 0);
570
571 /* If we're lucky, WSAEnumNetworkEvents already provided a way
572 to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */
573 if (FD_ISSET ((SOCKET) h, &rfds)
574 && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT)))
575 ev.lNetworkEvents |= FD_READ | FD_ACCEPT;
576 if (FD_ISSET ((SOCKET) h, &wfds))
577 ev.lNetworkEvents |= FD_WRITE | FD_CONNECT;
578 if (FD_ISSET ((SOCKET) h, &xfds))
579 ev.lNetworkEvents |= FD_OOB;
580
581 happened = windows_compute_revents_socket ((SOCKET) h, pfd[i].events,
582 ev.lNetworkEvents);
583 }
584 else
585 {
586 /* Not a socket. */
587 int sought = pfd[i].events;
588 happened = windows_compute_revents (h, &sought);
589 nhandles++;
590 }
591
592 if ((pfd[i].revents |= happened) != 0)
593 rc++;
594 }
595
596 if (!rc && timeout == INFTIM)
597 {
598 SleepEx (1, TRUE);
599 goto restart;
600 }
601
602 return rc;
603#endif
604}
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
37struct 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
44typedef unsigned long nfds_t;
45
46extern 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..0da5cde23
--- /dev/null
+++ b/win32/popen.c
@@ -0,0 +1,308 @@
1#include <fcntl.h>
2#include "libbb.h"
3#include "NUM_APPLETS.h"
4
5typedef struct {
6 PROCESS_INFORMATION piProcInfo;
7 HANDLE pipe[2];
8 int fd;
9} pipe_data;
10
11static pipe_data *pipes = NULL;
12static int num_pipes = 0;
13
14static int mingw_popen_internal(pipe_data *p, const char *cmd,
15 const char *mode, int fd0, pid_t *pid);
16
17static int mingw_pipe(pipe_data *p, int bidi)
18{
19 SECURITY_ATTRIBUTES sa;
20
21 sa.nLength = sizeof(sa); /* Length in bytes */
22 sa.bInheritHandle = 1; /* the child must inherit these handles */
23 sa.lpSecurityDescriptor = NULL;
24
25 if (!bidi) {
26 /* pipe[0] is the read handle, pipe[i] the write handle */
27 if ( !CreatePipe (&p->pipe[0], &p->pipe[1], &sa, 1 << 13) ) {
28 return -1;
29 }
30 }
31 else {
32 char *name;
33 const int ip = 1; /* index of parent end of pipe */
34 const int ic = 0; /* index of child end of pipe */
35 static int count = 0;
36
37 name = xasprintf("\\\\.\\pipe\\bb_pipe.%d.%d", getpid(), ++count);
38
39 p->pipe[ip] = CreateNamedPipe(name,
40 PIPE_ACCESS_DUPLEX|FILE_FLAG_OVERLAPPED,
41 PIPE_TYPE_BYTE|PIPE_WAIT,
42 1, 4096, 4096, 0, &sa);
43
44 p->pipe[ic] = CreateFile(name, GENERIC_READ|GENERIC_WRITE, 0, &sa,
45 OPEN_EXISTING,
46 FILE_ATTRIBUTE_NORMAL|FILE_FLAG_OVERLAPPED,
47 NULL);
48 free(name);
49 }
50
51 return (p->pipe[0] == INVALID_HANDLE_VALUE ||
52 p->pipe[1] == INVALID_HANDLE_VALUE) ? -1 : 0;
53}
54
55static void clear_pipe_data(pipe_data *p)
56{
57 memset(p, 0, sizeof(pipe_data));
58 p->pipe[0] = INVALID_HANDLE_VALUE;
59 p->pipe[1] = INVALID_HANDLE_VALUE;
60 p->fd = -1;
61}
62
63static void close_pipe_data(pipe_data *p)
64{
65 if (p->pipe[0] != INVALID_HANDLE_VALUE)
66 CloseHandle(p->pipe[0]);
67 if (p->pipe[1] != INVALID_HANDLE_VALUE)
68 CloseHandle(p->pipe[1]);
69 clear_pipe_data(p);
70}
71
72/*
73 * Search for a pipe_data structure with file descriptor fd. If fd is
74 * -1 and no empty slots are available the array is extended. Return
75 * NULL if the file descriptor can't be found or the array can't be
76 * extended.
77 */
78static pipe_data *find_pipe(int fd)
79{
80 int i;
81 pipe_data *p = NULL;
82
83 /* find a matching pipe structure */
84 for ( i=0; i<num_pipes; ++i ) {
85 if (pipes[i].fd == fd) {
86 p = pipes+i;
87 break;
88 }
89 }
90
91 /* if looking for valid file descriptor return now */
92 if (fd != -1)
93 return p;
94
95 if ( p == NULL ) {
96 /* need to extend array */
97 if ( (p=realloc(pipes, sizeof(pipe_data)*(num_pipes+10))) == NULL ) {
98 return NULL;
99 }
100
101 pipes = p;
102 p = pipes + num_pipes;
103 for ( i=0; i<10; ++i ) {
104 clear_pipe_data(p+i);
105 }
106 num_pipes += 10;
107 }
108 clear_pipe_data(p);
109
110 return p;
111}
112
113FILE *mingw_popen(const char *cmd, const char *mode)
114{
115 pipe_data *p;
116 FILE *fptr = NULL;
117 int fd;
118 int len, count;
119 char *cmd_buff = NULL;
120 const char *s;
121 char *t;
122
123 if ( cmd == NULL || *cmd == '\0' || mode == NULL ||
124 (*mode != 'r' && *mode != 'w') ) {
125 return NULL;
126 }
127
128 /* find an unused pipe structure */
129 if ((p=find_pipe(-1)) == NULL) {
130 return NULL;
131 }
132
133 /* count double quotes */
134 count = 0;
135 for ( s=cmd; *s; ++s ) {
136 if ( *s == '"' ) {
137 ++count;
138 }
139 }
140
141 len = strlen(bb_busybox_exec_path) + strlen(cmd) + 32 + count;
142 if ( (cmd_buff=malloc(len)) == NULL ) {
143 return NULL;
144 }
145 cmd_buff[0] = '\0';
146
147#if (ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE) && \
148 NUM_APPLETS > 1
149 if (find_applet_by_name("sh") >= 0) {
150 sprintf(cmd_buff, "%s --busybox ", bb_busybox_exec_path);
151 }
152#endif
153 strcat(cmd_buff, "sh -c \"");
154
155 /* escape double quotes */
156 for ( s=cmd,t=cmd_buff+strlen(cmd_buff); *s; ++s ) {
157 if ( *s == '"' ) {
158 *t++ = '\\';
159 }
160 *t++ = *s;
161 }
162 *t++ = '"';
163 *t = '\0';
164
165 /* Create the pipe */
166 if ((fd=mingw_popen_internal(p, cmd_buff, mode, -1, NULL)) != -1) {
167 fptr = _fdopen(fd, *mode == 'r' ? "rb" : "wb");
168 }
169
170 free(cmd_buff);
171
172 return fptr;
173}
174
175/*
176 * Open a pipe to a command.
177 *
178 * - mode may be "r", "w" or "b" for read-only, write-only or
179 * bidirectional (from the perspective of the parent).
180 * - if fd0 is a valid file descriptor it's used as input to the
181 * command ("r") or as the destination of the output from the
182 * command ("w"). Otherwise (and if not "b") use stdin or stdout.
183 * - the pid of the command is returned in the variable pid, which
184 * can be NULL if the pid is not required.
185 */
186static int mingw_popen_internal(pipe_data *p, const char *cmd,
187 const char *mode, int fd0, pid_t *pid)
188{
189 pipe_data pd;
190 STARTUPINFO siStartInfo;
191 int success;
192 int fd = -1;
193 int ip, ic, flags;
194
195 if ( cmd == NULL || *cmd == '\0' || mode == NULL ) {
196 return -1;
197 }
198
199 switch (*mode) {
200 case 'r':
201 ip = 0;
202 flags = _O_RDONLY|_O_BINARY;
203 break;
204 case 'w':
205 ip = 1;
206 flags = _O_WRONLY|_O_BINARY;
207 break;
208 case 'b':
209 ip = 1;
210 flags = _O_RDWR|_O_BINARY;
211 break;
212 default:
213 return -1;
214 }
215 ic = !ip;
216
217 if (!p) {
218 /* no struct provided, use a local one */
219 p = &pd;
220 }
221
222 /* Create the pipe */
223 if ( mingw_pipe(p, *mode == 'b') == -1 ) {
224 goto finito;
225 }
226
227 /* Make the parent end of the pipe non-inheritable */
228 SetHandleInformation(p->pipe[ip], HANDLE_FLAG_INHERIT, 0);
229
230 /* Now create the child process */
231 ZeroMemory(&siStartInfo, sizeof(STARTUPINFO));
232 siStartInfo.cb = sizeof(STARTUPINFO);
233 /* default settings for a bidirectional pipe */
234 siStartInfo.hStdInput = p->pipe[ic];
235 siStartInfo.hStdOutput = p->pipe[ic];
236 /* override for read-only or write-only */
237 if ( *mode == 'r' ) {
238 siStartInfo.hStdInput = fd0 >= 0 ? (HANDLE)_get_osfhandle(fd0) :
239 GetStdHandle(STD_INPUT_HANDLE);
240 }
241 else if ( *mode == 'w' ) {
242 siStartInfo.hStdOutput = fd0 >= 0 ? (HANDLE)_get_osfhandle(fd0) :
243 GetStdHandle(STD_OUTPUT_HANDLE);
244 }
245 siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
246 siStartInfo.wShowWindow = SW_HIDE;
247 siStartInfo.dwFlags = STARTF_USESTDHANDLES|STARTF_USESHOWWINDOW;
248
249 success = CreateProcess(NULL,
250 (LPTSTR)cmd, /* command line */
251 NULL, /* process security attributes */
252 NULL, /* primary thread security attributes */
253 TRUE, /* handles are inherited */
254 0, /* creation flags */
255 NULL, /* use parent's environment */
256 NULL, /* use parent's current directory */
257 &siStartInfo, /* STARTUPINFO pointer */
258 &p->piProcInfo); /* receives PROCESS_INFORMATION */
259
260 if ( !success ) {
261 goto finito;
262 }
263
264 /* close child end of pipe */
265 CloseHandle(p->pipe[ic]);
266 p->pipe[ic] = INVALID_HANDLE_VALUE;
267
268 fd = _open_osfhandle((intptr_t)p->pipe[ip], flags);
269
270finito:
271 if ( fd == -1 ) {
272 close_pipe_data(p);
273 }
274 else {
275 p->fd = fd;
276 if ( pid ) {
277 *pid = (pid_t)p->piProcInfo.dwProcessId;
278 }
279 }
280
281 return fd;
282}
283
284int mingw_popen_fd(const char *cmd, const char *mode, int fd0, pid_t *pid)
285{
286 return mingw_popen_internal(NULL, cmd, mode, fd0, pid);
287}
288
289int mingw_pclose(FILE *fp)
290{
291 int fd;
292 pipe_data *p;
293 DWORD ret;
294
295 /* find struct containing fd */
296 if (fp == NULL || (fd=fileno(fp)) == -1 || (p=find_pipe(fd)) == NULL)
297 return -1;
298
299 fclose(fp);
300
301 ret = WaitForSingleObject(p->piProcInfo.hProcess, INFINITE);
302
303 CloseHandle(p->piProcInfo.hProcess);
304 CloseHandle(p->piProcInfo.hThread);
305 close_pipe_data(p);
306
307 return (ret == WAIT_OBJECT_0) ? 0 : -1;
308}
diff --git a/win32/process.c b/win32/process.c
new file mode 100644
index 000000000..7210934fe
--- /dev/null
+++ b/win32/process.c
@@ -0,0 +1,767 @@
1#include "libbb.h"
2#include <tlhelp32.h>
3#include <psapi.h>
4
5int waitpid(pid_t pid, int *status, int options)
6{
7 HANDLE proc;
8 intptr_t ret;
9
10 /* Windows does not understand parent-child */
11 if (pid > 0 && options == 0) {
12 if ( (proc=OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
13 FALSE, pid)) != NULL ) {
14 ret = _cwait(status, (intptr_t)proc, 0);
15 CloseHandle(proc);
16 if (ret == -1) {
17 return -1;
18 }
19 *status <<= 8;
20 return pid;
21 }
22 }
23 errno = pid < 0 ? ENOSYS : EINVAL;
24 return -1;
25}
26
27typedef struct {
28 char *path;
29 char *name;
30 char *opts;
31 char buf[100];
32} interp_t;
33
34static int
35parse_interpreter(const char *cmd, interp_t *interp)
36{
37 char *path, *t;
38 int n;
39
40 while (TRUE) {
41 n = open_read_close(cmd, interp->buf, sizeof(interp->buf)-1);
42 if (n < 4) /* at least '#!/x' and not error */
43 break;
44
45 /*
46 * See http://www.in-ulm.de/~mascheck/various/shebang/ for trivia
47 * relating to '#!'.
48 */
49 if (interp->buf[0] != '#' || interp->buf[1] != '!')
50 break;
51 interp->buf[n] = '\0';
52 if ((t=strchr(interp->buf, '\n')) == NULL)
53 break;
54 t[1] = '\0';
55
56 if ((path=strtok(interp->buf+2, " \t\r\n")) == NULL)
57 break;
58
59 t = (char *)bb_basename(path);
60 if (*t == '\0')
61 break;
62
63 interp->path = path;
64 interp->name = t;
65 interp->opts = strtok(NULL, "\r\n");
66 return 1;
67 }
68
69 if (is_suffixed_with_case(cmd, ".sh")) {
70 interp->path = (char *)DEFAULT_SHELL;
71 interp->name = (char *)DEFAULT_SHELL_SHORT_NAME;
72 interp->opts = NULL;
73 return 1;
74 }
75 return 0;
76}
77
78/*
79 * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
80 * (Parsing C++ Command-Line Arguments)
81 */
82static char *
83quote_arg(const char *arg)
84{
85 int len = 0, n = 0;
86 int force_quotes = 0;
87 char *q, *d;
88 const char *p = arg;
89
90 /* empty arguments must be quoted */
91 if (!*p) {
92 force_quotes = 1;
93 }
94
95 while (*p) {
96 if (isspace(*p)) {
97 /* arguments containing whitespace must be quoted */
98 force_quotes = 1;
99 }
100 else if (*p == '"') {
101 /* double quotes in arguments need to be escaped */
102 n++;
103 }
104 else if (*p == '\\') {
105 /* count contiguous backslashes */
106 int count = 0;
107 while (*p == '\\') {
108 count++;
109 p++;
110 len++;
111 }
112
113 /*
114 * Only escape backslashes before explicit double quotes or
115 * or where the backslashes are at the end of an argument
116 * that is scheduled to be quoted.
117 */
118 if (*p == '"' || (force_quotes && *p == '\0')) {
119 n += count*2 + 1;
120 }
121
122 if (*p == '\0') {
123 break;
124 }
125 continue;
126 }
127 len++;
128 p++;
129 }
130
131 if (!force_quotes && n == 0) {
132 return (char*)arg;
133 }
134
135 /* insert double quotes and backslashes where necessary */
136 d = q = xmalloc(len+n+3);
137 if (force_quotes) {
138 *d++ = '"';
139 }
140
141 while (*arg) {
142 if (*arg == '"') {
143 *d++ = '\\';
144 }
145 else if (*arg == '\\') {
146 int count = 0;
147 while (*arg == '\\') {
148 count++;
149 *d++ = *arg++;
150 }
151
152 if (*arg == '"' || (force_quotes && *arg == '\0')) {
153 while (count-- > 0) {
154 *d++ = '\\';
155 }
156 if (*arg == '"') {
157 *d++ = '\\';
158 }
159 }
160 }
161 if (*arg != '\0') {
162 *d++ = *arg++;
163 }
164 }
165 if (force_quotes) {
166 *d++ = '"';
167 }
168 *d = '\0';
169
170 return q;
171}
172
173static char *
174find_first_executable(const char *name)
175{
176 char *tmp, *path = getenv("PATH");
177 char *exe_path = NULL;
178
179 if (path) {
180 tmp = path = xstrdup(path);
181 exe_path = find_executable(name, &tmp);
182 free(path);
183 }
184
185 return exe_path;
186}
187
188static intptr_t
189spawnveq(int mode, const char *path, char *const *argv, char *const *env)
190{
191 char **new_argv;
192 char *s, *new_path = NULL;
193 int i, argc;
194 intptr_t ret;
195 struct stat st;
196
197 /*
198 * Require that the file exists, is a regular file and is executable.
199 * It may still contain garbage but we let spawnve deal with that.
200 */
201 if (stat(path, &st) == 0) {
202 if (!S_ISREG(st.st_mode) || !(st.st_mode&S_IXUSR)) {
203 errno = EACCES;
204 return -1;
205 }
206 }
207 else {
208 return -1;
209 }
210
211 argc = string_array_len((char **)argv);
212 new_argv = xmalloc(sizeof(*argv)*(argc+1));
213 for (i = 0;i < argc;i++)
214 new_argv[i] = quote_arg(argv[i]);
215 new_argv[argc] = NULL;
216
217 /* Special case: spawnve won't execute a batch file if the first
218 * argument is a relative path containing forward slashes. Absolute
219 * paths are fine but there's no harm in converting them too. */
220 if (has_bat_suffix(path)) {
221 for (s=new_argv[0]; *s; ++s) {
222 if (*s == '/')
223 *s = '\\';
224 }
225
226 /* Another special case: spawnve returns ENOEXEC when passed an
227 * empty batch file. Pretend it worked. */
228 if (st.st_size == 0) {
229 ret = 0;
230 goto done;
231 }
232 }
233
234 /*
235 * Another special case: if a file doesn't have an extension add
236 * a '.' at the end. This forces spawnve to use precisely the
237 * file specified without trying to add an extension.
238 */
239 if (!strchr(bb_basename(path), '.')) {
240 new_path = xasprintf("%s.", path);
241 }
242
243 ret = spawnve(mode, new_path ? new_path : path, new_argv, env);
244
245 done:
246 for (i = 0;i < argc;i++)
247 if (new_argv[i] != argv[i])
248 free(new_argv[i]);
249 free(new_argv);
250 free(new_path);
251
252 return ret;
253}
254
255#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
256static intptr_t
257mingw_spawn_applet(int mode,
258 char *const *argv,
259 char *const *envp)
260{
261 return spawnveq(mode, bb_busybox_exec_path, argv, envp);
262}
263#endif
264
265static intptr_t
266mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, char *const *envp)
267{
268 intptr_t ret;
269 int nopts;
270 interp_t interp;
271 char **new_argv;
272 int argc;
273 char *fullpath = NULL;
274
275 if (!parse_interpreter(prog, &interp))
276 return spawnveq(mode, prog, argv, envp);
277
278 nopts = interp.opts != NULL;
279 argc = string_array_len((char **)argv);
280 new_argv = xmalloc(sizeof(*argv)*(argc+nopts+2));
281 new_argv[1] = interp.opts;
282 new_argv[nopts+1] = (char *)prog; /* pass absolute path */
283 memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc);
284
285 if ((fullpath=alloc_win32_extension(interp.path)) != NULL ||
286 file_is_executable(interp.path)) {
287 new_argv[0] = fullpath ? fullpath : interp.path;
288 ret = spawnveq(mode, new_argv[0], new_argv, envp);
289 } else
290#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
291 if (find_applet_by_name(interp.name) >= 0) {
292 /* the fake path indicates the index of the script */
293 new_argv[0] = fullpath = xasprintf("%d:/%s", nopts+1, interp.name);
294 ret = mingw_spawn_applet(mode, new_argv, envp);
295 } else
296#endif
297 if ((fullpath=find_first_executable(interp.name)) != NULL) {
298 new_argv[0] = fullpath;
299 ret = spawnveq(mode, fullpath, new_argv, envp);
300 }
301 else {
302 errno = ENOENT;
303 ret = -1;
304 }
305
306 free(fullpath);
307 free(new_argv);
308 return ret;
309}
310
311static intptr_t
312mingw_spawn_1(int mode, const char *cmd, char *const *argv, char *const *envp)
313{
314 char *prog;
315
316#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
317 if (find_applet_by_name(cmd) >= 0)
318 return mingw_spawn_applet(mode, argv, envp);
319 else
320#endif
321 if (strchr(cmd, '/') || strchr(cmd, '\\')) {
322 return mingw_spawn_interpreter(mode, cmd, argv, envp);
323 }
324 else if ((prog=find_first_executable(cmd)) != NULL) {
325 intptr_t ret = mingw_spawn_interpreter(mode, prog, argv, envp);
326 free(prog);
327 return ret;
328 }
329
330 errno = ENOENT;
331 return -1;
332}
333
334pid_t FAST_FUNC
335mingw_spawn(char **argv)
336{
337 intptr_t ret;
338
339 ret = mingw_spawn_proc((const char **)argv);
340
341 return ret == -1 ? -1 : GetProcessId((HANDLE)ret);
342}
343
344intptr_t FAST_FUNC
345mingw_spawn_proc(const char **argv)
346{
347 return mingw_spawn_1(P_NOWAIT, argv[0], (char *const *)argv, environ);
348}
349
350int
351mingw_execvp(const char *cmd, char *const *argv)
352{
353 int ret = (int)mingw_spawn_1(P_WAIT, cmd, argv, environ);
354 if (ret != -1)
355 exit(ret);
356 return ret;
357}
358
359int
360mingw_execve(const char *cmd, char *const *argv, char *const *envp)
361{
362 int ret = (int)mingw_spawn_interpreter(P_WAIT, cmd, argv, envp);
363 if (ret != -1)
364 exit(ret);
365 return ret;
366}
367
368int
369mingw_execv(const char *cmd, char *const *argv)
370{
371 return mingw_execve(cmd, argv, environ);
372}
373
374static inline long long filetime_to_ticks(const FILETIME *ft)
375{
376 return (((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime)/
377 HNSEC_PER_TICK;
378}
379
380/*
381 * Attempt to get a string from another instance of busybox.exe.
382 * This will only work if the other process is using the same binary
383 * as the current process. If anything goes wrong just give up.
384 */
385static char *get_bb_string(DWORD pid, const char *exe, char *string)
386{
387 HANDLE proc;
388 HMODULE mlist[32];
389 DWORD needed;
390 void *address;
391 char *my_base;
392 char buffer[128];
393 char exepath[PATH_MAX];
394 char *name = NULL;
395 int i;
396
397 if (!(proc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
398 FALSE, pid))) {
399 return NULL;
400 }
401
402 if (exe == NULL) {
403 if (GetProcessImageFileName(proc, exepath, PATH_MAX) != 0) {
404 exe = bb_basename(exepath);
405 }
406 }
407
408 /*
409 * Search for the module that matches the name of the executable.
410 * The values returned in mlist are actually the base address of
411 * the module in the other process (as noted in the documentation
412 * for the MODULEINFO structure).
413 */
414 if (!EnumProcessModules(proc, mlist, sizeof(mlist), &needed)) {
415 goto finish;
416 }
417
418 for (i=0; exe != NULL && i<needed/sizeof(HMODULE); ++i) {
419 char modname[MAX_PATH];
420 if (GetModuleFileNameEx(proc, mlist[i], modname, sizeof(modname))) {
421 if (strcasecmp(bb_basename(modname), exe) == 0) {
422 break;
423 }
424 }
425 }
426
427 if (i == needed/sizeof(HMODULE)) {
428 goto finish;
429 }
430
431 /* attempt to read the BusyBox version string */
432 my_base = (char *)GetModuleHandle(NULL);
433 address = (char *)mlist[i] + ((char *)bb_banner - my_base);
434 if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) {
435 goto finish;
436 }
437
438 if (memcmp(buffer, bb_banner, strlen(bb_banner)) != 0) {
439 /* version mismatch (or not BusyBox at all) */
440 goto finish;
441 }
442
443 /* attempt to read the required string */
444 address = (char *)mlist[i] + ((char *)string - my_base);
445 if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) {
446 goto finish;
447 }
448
449 buffer[127] = '\0';
450 name = auto_string(xstrdup(buffer));
451
452 finish:
453 CloseHandle(proc);
454 return name;
455}
456
457/* POSIX version in libbb/procps.c */
458procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags
459#if !ENABLE_FEATURE_PS_TIME && !ENABLE_FEATURE_PS_LONG
460UNUSED_PARAM
461#endif
462)
463{
464 PROCESSENTRY32 pe;
465 HANDLE proc;
466 const char *comm, *name;
467 BOOL ret;
468
469 pe.dwSize = sizeof(pe);
470 if (!sp) {
471 sp = xzalloc(sizeof(struct procps_status_t));
472 sp->snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
473 if (sp->snapshot == INVALID_HANDLE_VALUE) {
474 free(sp);
475 return NULL;
476 }
477 ret = Process32First(sp->snapshot, &pe);
478 }
479 else {
480 ret = Process32Next(sp->snapshot, &pe);
481 }
482
483 if (!ret) {
484 CloseHandle(sp->snapshot);
485 free(sp);
486 return NULL;
487 }
488
489 memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
490#if !ENABLE_DESKTOP
491 strcpy(sp->state, " ");
492#endif
493
494#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
495 if (flags & (PSSCAN_STIME|PSSCAN_UTIME|PSSCAN_START_TIME)) {
496 FILETIME crTime, exTime, keTime, usTime;
497
498 if ((proc=OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
499 FALSE, pe.th32ProcessID))) {
500 if (GetProcessTimes(proc, &crTime, &exTime, &keTime, &usTime)) {
501 long long ticks_since_boot, boot_time, create_time;
502 FILETIME now;
503
504 ticks_since_boot = GetTickCount64()/MS_PER_TICK;
505 GetSystemTimeAsFileTime(&now);
506 boot_time = filetime_to_ticks(&now) - ticks_since_boot;
507 create_time = filetime_to_ticks(&crTime);
508
509 sp->start_time = (unsigned long)(create_time - boot_time);
510 sp->stime = (unsigned long)filetime_to_ticks(&keTime);
511 sp->utime = (unsigned long)filetime_to_ticks(&usTime);
512 }
513 CloseHandle(proc);
514 }
515 }
516#endif
517
518 if (flags & PSSCAN_UIDGID) {
519 /* if we can open the process it belongs to us */
520 if ((proc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID))) {
521 sp->uid = DEFAULT_UID;
522 sp->gid = DEFAULT_GID;
523 CloseHandle(proc);
524 }
525 }
526
527 sp->pid = pe.th32ProcessID;
528 sp->ppid = pe.th32ParentProcessID;
529
530 if (sp->pid == GetProcessId(GetCurrentProcess())) {
531 comm = applet_name;
532 }
533 else if ((name=get_bb_string(sp->pid, pe.szExeFile, bb_comm)) != NULL) {
534 comm = name;
535 }
536 else {
537 comm = pe.szExeFile;
538 }
539 safe_strncpy(sp->comm, comm, COMM_LEN);
540
541 return sp;
542}
543
544void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
545{
546 const char *str, *cmdline;
547
548 *buf = '\0';
549 if (pid == GetProcessId(GetCurrentProcess()))
550 cmdline = bb_command_line;
551 else if ((str=get_bb_string(pid, NULL, bb_command_line)) != NULL)
552 cmdline = str;
553 else
554 cmdline = comm;
555 safe_strncpy(buf, cmdline, col);
556}
557
558/**
559 * If the process ID is positive invoke the callback for that process
560 * only. If negative or zero invoke the callback for all descendants
561 * of the indicated process. Zero indicates the current process; negative
562 * indicates the process with process ID -pid.
563 */
564typedef int (*kill_callback)(pid_t pid, int exit_code);
565
566static int kill_pids(pid_t pid, int exit_code, kill_callback killer)
567{
568 DWORD pids[16384];
569 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0;
570
571 if(pid > 0)
572 pids[0] = (DWORD)pid;
573 else if (pid == 0)
574 pids[0] = (DWORD)getpid();
575 else
576 pids[0] = (DWORD)-pid;
577 len = 1;
578
579 /*
580 * Even if Process32First()/Process32Next() seem to traverse the
581 * processes in topological order (i.e. parent processes before
582 * child processes), there is nothing in the Win32 API documentation
583 * suggesting that this is guaranteed.
584 *
585 * Therefore, run through them at least twice and stop when no more
586 * process IDs were added to the list.
587 */
588 if (pid <= 0) {
589 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
590
591 if (snapshot == INVALID_HANDLE_VALUE) {
592 errno = err_win_to_posix(GetLastError());
593 return -1;
594 }
595
596 for (;;) {
597 PROCESSENTRY32 entry;
598 int orig_len = len;
599
600 memset(&entry, 0, sizeof(entry));
601 entry.dwSize = sizeof(entry);
602
603 if (!Process32First(snapshot, &entry))
604 break;
605
606 do {
607 for (i = len - 1; i >= 0; i--) {
608 if (pids[i] == entry.th32ProcessID)
609 break;
610 if (pids[i] == entry.th32ParentProcessID)
611 pids[len++] = entry.th32ProcessID;
612 }
613 } while (len < max_len && Process32Next(snapshot, &entry));
614
615 if (orig_len == len || len >= max_len)
616 break;
617 }
618
619 CloseHandle(snapshot);
620 }
621
622 for (i = len - 1; i >= 0; i--) {
623 SetLastError(0);
624 if (killer(pids[i], exit_code)) {
625 errno = err_win_to_posix(GetLastError());
626 ret = -1;
627 }
628 }
629
630 return ret;
631}
632
633/**
634 * Determine whether a process runs in the same architecture as the current
635 * one. That test is required before we assume that GetProcAddress() returns
636 * a valid address *for the target process*.
637 */
638static inline int process_architecture_matches_current(HANDLE process)
639{
640 static BOOL current_is_wow = -1;
641 BOOL is_wow;
642
643 if (current_is_wow == -1 &&
644 !IsWow64Process (GetCurrentProcess(), &current_is_wow))
645 current_is_wow = -2;
646 if (current_is_wow == -2)
647 return 0; /* could not determine current process' WoW-ness */
648 if (!IsWow64Process (process, &is_wow))
649 return 0; /* cannot determine */
650 return is_wow == current_is_wow;
651}
652
653/**
654 * This function tries to terminate a Win32 process, as gently as possible,
655 * by injecting a thread that calls ExitProcess().
656 *
657 * Note: as kernel32.dll is loaded before any process, the other process and
658 * this process will have ExitProcess() at the same address.
659 *
660 * The idea comes from the Dr Dobb's article "A Safer Alternative to
661 * TerminateProcess()" by Andrew Tucker (July 1, 1999),
662 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
663 *
664 */
665int kill_SIGTERM_by_handle(HANDLE process, int exit_code)
666{
667 DWORD code;
668 int ret = 0;
669
670 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) {
671 static int initialized;
672 static LPTHREAD_START_ROUTINE exit_process_address;
673 PVOID arg = (PVOID)(intptr_t)exit_code;
674 DWORD thread_id;
675 HANDLE thread;
676
677 if (!initialized) {
678 HINSTANCE kernel32 = GetModuleHandle("kernel32");
679 if (!kernel32) {
680 fprintf(stderr, "BUG: cannot find kernel32");
681 ret = -1;
682 goto finish;
683 }
684 exit_process_address = (LPTHREAD_START_ROUTINE)
685 GetProcAddress(kernel32, "ExitProcess");
686 initialized = 1;
687 }
688 if (!exit_process_address ||
689 !process_architecture_matches_current(process)) {
690 SetLastError(ERROR_ACCESS_DENIED);
691 ret = -1;
692 goto finish;
693 }
694
695 if ((thread = CreateRemoteThread(process, NULL, 0,
696 exit_process_address,
697 arg, 0, &thread_id))) {
698 CloseHandle(thread);
699 }
700 }
701
702 finish:
703 CloseHandle(process);
704 return ret;
705}
706
707static int kill_SIGTERM(pid_t pid, int exit_code)
708{
709 HANDLE process;
710
711 if (!(process = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD |
712 PROCESS_QUERY_INFORMATION |
713 PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
714 PROCESS_VM_READ, FALSE, pid))) {
715 return -1;
716 }
717
718 return kill_SIGTERM_by_handle(process, exit_code);
719}
720
721/*
722 * This way of terminating processes is not gentle: they get no chance to
723 * clean up after themselves (closing file handles, removing .lock files,
724 * terminating spawned processes (if any), etc).
725 */
726static int kill_SIGKILL(pid_t pid, int exit_code)
727{
728 HANDLE process;
729 int ret;
730
731 if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) {
732 return -1;
733 }
734
735 ret = !TerminateProcess(process, exit_code);
736 CloseHandle(process);
737
738 return ret;
739}
740
741static int kill_SIGTEST(pid_t pid, int exit_code UNUSED_PARAM)
742{
743 HANDLE process;
744
745 if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) {
746 return -1;
747 }
748
749 CloseHandle(process);
750 return 0;
751}
752
753int kill(pid_t pid, int sig)
754{
755 if (sig == SIGTERM) {
756 return kill_pids(pid, 128+sig, kill_SIGTERM);
757 }
758 else if (sig == SIGKILL) {
759 return kill_pids(pid, 128+sig, kill_SIGKILL);
760 }
761 else if (sig == 0) {
762 return kill_pids(pid, 128+sig, kill_SIGTEST);
763 }
764
765 errno = EINVAL;
766 return -1;
767}
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
23static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
24 size_t length, reg_syntax_t syntax);
25static void re_compile_fastmap_iter (regex_t *bufp,
26 const re_dfastate_t *init_state,
27 char *fastmap);
28static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
29#ifdef RE_ENABLE_I18N
30static void free_charset (re_charset_t *cset);
31#endif /* RE_ENABLE_I18N */
32static void free_workarea_compile (regex_t *preg);
33static reg_errcode_t create_initial_state (re_dfa_t *dfa);
34#ifdef RE_ENABLE_I18N
35static void optimize_utf8 (re_dfa_t *dfa);
36#endif
37static reg_errcode_t analyze (regex_t *preg);
38static reg_errcode_t preorder (bin_tree_t *root,
39 reg_errcode_t (fn (void *, bin_tree_t *)),
40 void *extra);
41static reg_errcode_t postorder (bin_tree_t *root,
42 reg_errcode_t (fn (void *, bin_tree_t *)),
43 void *extra);
44static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
45static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
46static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
47 bin_tree_t *node);
48static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
49static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
50static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
51static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint);
52static int search_duplicated_node (const re_dfa_t *dfa, int org_node,
53 unsigned int constraint);
54static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
55static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
56 int node, int root);
57static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
58static int fetch_number (re_string_t *input, re_token_t *token,
59 reg_syntax_t syntax);
60static int peek_token (re_token_t *token, re_string_t *input,
61 reg_syntax_t syntax) internal_function;
62static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
63 reg_syntax_t syntax, reg_errcode_t *err);
64static 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);
67static 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);
70static 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);
73static 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);
76static 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);
79static 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);
82static 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);
88static 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
92static reg_errcode_t build_equiv_class (bitset_t sbcset,
93 re_charset_t *mbcset,
94 int *equiv_class_alloc,
95 const unsigned char *name);
96static 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 */
103static reg_errcode_t build_equiv_class (bitset_t sbcset,
104 const unsigned char *name);
105static 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 */
110static 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);
115static bin_tree_t *create_tree (re_dfa_t *dfa,
116 bin_tree_t *left, bin_tree_t *right,
117 re_token_type_t type);
118static 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);
121static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
122static void free_token (re_token_t *node);
123static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
124static 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
131const 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
185const 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
213wchar_t
214btowc (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
234const char *
235re_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
256weak_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. */
264reg_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
274reg_syntax_t
275re_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
283weak_alias (__re_set_syntax, re_set_syntax)
284#endif
285
286int
287re_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
304weak_alias (__re_compile_fastmap, re_compile_fastmap)
305#endif
306
307static inline void
308__attribute ((always_inline))
309re_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
319static void
320re_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
485int
486regcomp (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
540weak_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
546size_t
547regerror(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
580weak_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
590static 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) */
595static bitset_t utf8_sb_map;
596#endif /* __GNUC__ >= 3 */
597#endif /* RE_ENABLE_I18N */
598
599
600static void
601free_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
650void
651regfree (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
666weak_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. */
675static struct re_pattern_buffer re_comp_buf;
676
677char *
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. */
682weak_function
683# endif
684re_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
730libc_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
742static reg_errcode_t
743re_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 (&regexp, 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 (&regexp);
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 (&regexp, 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 (&regexp);
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
841static reg_errcode_t
842init_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
963static void
964internal_function
965init_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
977static void
978free_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
996static reg_errcode_t
997create_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
1081static void
1082optimize_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
1151static reg_errcode_t
1152analyze (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. */
1214static reg_errcode_t
1215postorder (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
1246static reg_errcode_t
1247preorder (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. */
1279static reg_errcode_t
1280optimize_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. */
1310static reg_errcode_t
1311lower_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
1332static bin_tree_t *
1333lower_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. */
1369static reg_errcode_t
1370calc_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. */
1391static reg_errcode_t
1392calc_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). */
1414static reg_errcode_t
1415link_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
1474static reg_errcode_t
1475internal_function
1476duplicate_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
1581static int
1582search_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
1599static int
1600duplicate_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
1615static reg_errcode_t
1616calc_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
1638static reg_errcode_t
1639calc_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
1682static reg_errcode_t
1683calc_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
1763static void
1764internal_function
1765fetch_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
1773static int
1774internal_function
1775peek_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
2012static int
2013internal_function
2014peek_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
2109static bin_tree_t *
2110parse (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 (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
2118 tree = parse_reg_exp (regexp, preg, &current_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
2143static bin_tree_t *
2144parse_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
2184static bin_tree_t *
2185parse_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
2224static bin_tree_t *
2225parse_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
2440static bin_tree_t *
2441parse_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
2478static bin_tree_t *
2479parse_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
2624static reg_errcode_t
2625internal_function
2626# ifdef RE_ENABLE_I18N
2627build_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 */
2630build_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
2757static reg_errcode_t
2758internal_function
2759# ifdef RE_ENABLE_I18N
2760build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
2761 int *coll_sym_alloc, const unsigned char *name)
2762# else /* not RE_ENABLE_I18N */
2763build_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
2780static bin_tree_t *
2781parse_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
3338static reg_errcode_t
3339parse_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
3379static reg_errcode_t
3380parse_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
3426static reg_errcode_t
3427#ifdef RE_ENABLE_I18N
3428build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
3429 int *equiv_class_alloc, const unsigned char *name)
3430#else /* not RE_ENABLE_I18N */
3431build_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
3522static reg_errcode_t
3523#ifdef RE_ENABLE_I18N
3524build_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 */
3528build_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
3609static bin_tree_t *
3610build_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
3722static int
3723fetch_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
3743static void
3744free_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
3762static bin_tree_t *
3763create_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
3771static bin_tree_t *
3772create_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
3808static reg_errcode_t
3809mark_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
3820static void
3821free_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
3835static reg_errcode_t
3836free_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
3848static bin_tree_t *
3849duplicate_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)
87link_warning (re_max_failures, "the 're_max_failures' variable is obsolete and will go away.")
88int 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
42extern "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... */
49typedef long int s_reg_t;
50typedef 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. */
57typedef 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. */
188extern 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. */
314typedef 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
363struct 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
429typedef struct re_pattern_buffer regex_t;
430
431/* Type for byte offsets within the string. POSIX mandates this. */
432typedef 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. */
438struct 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. */
458typedef 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. */
469extern 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. */
474extern 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. */
481extern 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). */
489extern 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. */
496extern 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. */
504extern 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'. */
509extern 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. */
527extern 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. */
536extern char *re_comp (const char *);
537extern 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. */
563extern int regcomp (regex_t *__restrict __preg,
564 const char *__restrict __pattern,
565 int __cflags);
566
567extern 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
572extern size_t regerror (int __errcode, const regex_t *__restrict __preg,
573 char *__restrict __errbuf, size_t __errbuf_size);
574
575extern 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
21static 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;
25static re_dfastate_t *create_ci_newstate (const re_dfa_t *dfa,
26 const re_node_set *nodes,
27 unsigned int hash) internal_function;
28static 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 */
35static int
36MAX(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
47static reg_errcode_t
48internal_function
49re_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
75static reg_errcode_t
76internal_function
77re_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
138static reg_errcode_t
139internal_function
140re_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
178static void
179internal_function
180re_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
210static void
211internal_function
212build_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
278static reg_errcode_t
279internal_function
280build_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
494static int
495internal_function
496re_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
535static void
536internal_function
537build_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
558static void
559internal_function
560re_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
579static reg_errcode_t
580internal_function
581re_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
837static unsigned char
838internal_function __attribute ((pure))
839re_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
873static unsigned char
874internal_function __attribute ((pure))
875re_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
910static void
911internal_function
912re_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
924static unsigned int
925internal_function
926re_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
969static reg_errcode_t
970internal_function
971re_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
991static reg_errcode_t
992internal_function
993re_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
1007static reg_errcode_t
1008internal_function
1009re_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
1037static reg_errcode_t
1038internal_function
1039re_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
1062static reg_errcode_t
1063internal_function
1064re_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
1153static reg_errcode_t
1154internal_function
1155re_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
1206static reg_errcode_t
1207internal_function
1208re_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
1289static int
1290internal_function
1291re_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
1346static int
1347internal_function
1348re_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
1369static int
1370internal_function __attribute ((pure))
1371re_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
1384static int
1385internal_function __attribute ((pure))
1386re_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
1406static void
1407internal_function
1408re_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
1421static int
1422internal_function
1423re_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
1468static inline unsigned int
1469internal_function
1470calc_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
1488static re_dfastate_t *
1489internal_function
1490re_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
1532static re_dfastate_t *
1533internal_function
1534re_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
1569static reg_errcode_t
1570register_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
1604static void
1605free_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
1623static re_dfastate_t *
1624internal_function
1625create_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
1673static re_dfastate_t *
1674internal_function
1675create_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
72static int
73is_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
166extern const char __re_error_msgid[] attribute_hidden;
167extern 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. */
171typedef 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)
178typedef bitset_word_t bitset_t[BITSET_WORDS];
179typedef bitset_word_t *re_bitset_ptr_t;
180typedef 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
203typedef 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
217typedef struct
218{
219 int alloc;
220 int nelem;
221 int *elems;
222} re_node_set;
223
224typedef 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
276typedef 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
323typedef 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
354struct 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};
409typedef struct re_string_t re_string_t;
410
411
412struct re_dfa_t;
413typedef 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
424static reg_errcode_t re_string_realloc_buffers (re_string_t *pstr,
425 int new_buf_len)
426 internal_function;
427# ifdef RE_ENABLE_I18N
428static void build_wcs_buffer (re_string_t *pstr) internal_function;
429static reg_errcode_t build_wcs_upper_buffer (re_string_t *pstr)
430 internal_function;
431# endif /* RE_ENABLE_I18N */
432static void build_upper_buffer (re_string_t *pstr) internal_function;
433static void re_string_translate_buffer (re_string_t *pstr) internal_function;
434static 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
479struct 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};
493typedef struct bin_tree_t bin_tree_t;
494
495#define BIN_TREE_STORAGE_SIZE \
496 ((1024 - sizeof (void *)) / sizeof (bin_tree_t))
497
498struct bin_tree_storage_t
499{
500 struct bin_tree_storage_t *next;
501 bin_tree_t data[BIN_TREE_STORAGE_SIZE];
502};
503typedef 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
533struct 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};
551typedef struct re_dfastate_t re_dfastate_t;
552
553struct 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
562typedef 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
571typedef 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
582typedef 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
592struct 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
603typedef 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
630typedef 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
639struct 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
647struct re_fail_stack_t
648{
649 int num;
650 int alloc;
651 struct re_fail_stack_ent_t *stack;
652};
653
654struct 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
710typedef enum
711{
712 SB_CHAR,
713 MB_CHAR,
714 EQUIV_CLASS,
715 COLL_SYM,
716 CHAR_CLASS
717} bracket_elem_type;
718
719typedef 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. */
732static inline void
733bitset_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
740static inline void
741bitset_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
748static inline void
749bitset_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. */
758static inline int
759internal_function __attribute ((pure))
760re_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
771static inline wint_t
772internal_function __attribute ((pure))
773re_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
781static int
782internal_function __attribute ((pure))
783re_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
21static reg_errcode_t match_ctx_init (re_match_context_t *cache, int eflags,
22 int n) internal_function;
23static void match_ctx_clean (re_match_context_t *mctx) internal_function;
24static void match_ctx_free (re_match_context_t *cache) internal_function;
25static 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;
28static int search_cur_bkref_entry (const re_match_context_t *mctx, int str_idx)
29 internal_function;
30static reg_errcode_t match_ctx_add_subtop (re_match_context_t *mctx, int node,
31 int str_idx) internal_function;
32static re_sub_match_last_t * match_ctx_add_sublast (re_sub_match_top_t *subtop,
33 int node, int str_idx)
34 internal_function;
35static 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;
39static 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);
44static 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);
49static 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);
53static unsigned re_copy_regs (struct re_registers *regs, regmatch_t *pmatch,
54 int nregs, int regs_allocated);
55static reg_errcode_t prune_impossible_nodes (re_match_context_t *mctx);
56static int check_matching (re_match_context_t *mctx, int fl_longest_match,
57 int *p_match_first) internal_function;
58static int check_halt_state_context (const re_match_context_t *mctx,
59 const re_dfastate_t *state, int idx)
60 internal_function;
61static 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;
64static 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;
69static 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;
73static reg_errcode_t free_fail_stack_return (struct re_fail_stack_t *fs)
74 internal_function;
75
76#ifdef RE_ENABLE_I18N
77static 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 */
82static reg_errcode_t sift_states_backward (const re_match_context_t *mctx,
83 re_sift_context_t *sctx)
84 internal_function;
85static 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;
89static 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;
94static 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;
98static 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;
102static 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;
106static 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;
110static 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;
116static 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;
120static 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;
124static re_dfastate_t *find_recover_state (reg_errcode_t *err,
125 re_match_context_t *mctx) internal_function;
126static re_dfastate_t *transit_state (reg_errcode_t *err,
127 re_match_context_t *mctx,
128 re_dfastate_t *state) internal_function;
129static 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;
133static 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
137static 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
143static reg_errcode_t transit_state_mb (re_match_context_t *mctx,
144 re_dfastate_t *pstate)
145 internal_function;
146#endif /* RE_ENABLE_I18N */
147static reg_errcode_t transit_state_bkref (re_match_context_t *mctx,
148 const re_node_set *nodes)
149 internal_function;
150static reg_errcode_t get_subexp (re_match_context_t *mctx,
151 int bkref_node, int bkref_str_idx)
152 internal_function;
153static 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;
158static int find_subexp_node (const re_dfa_t *dfa, const re_node_set *nodes,
159 int subexp_idx, int type) internal_function;
160static 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;
164static 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;
169static 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;
173static 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;
177static 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;
181static int build_trtable (const re_dfa_t *dfa,
182 re_dfastate_t *state) internal_function;
183#ifdef RE_ENABLE_I18N
184static 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
188static 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 */
193static 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;
197static int check_node_accept (const re_match_context_t *mctx,
198 const re_token_t *node, int idx)
199 internal_function;
200static 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
219int
220regexec (
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>
257versioned_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
262int
263attribute_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}
271compat_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
304int
305re_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
314weak_alias (__re_match, re_match)
315#endif
316
317int
318re_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
326weak_alias (__re_search, re_search)
327#endif
328
329int
330re_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
339weak_alias (__re_match_2, re_match_2)
340#endif
341
342int
343re_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
352weak_alias (__re_search_2, re_search_2)
353#endif
354
355static int
356re_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
399static int
400re_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
486static unsigned
487re_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
565void
566re_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
587weak_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
594int
595# ifdef _LIBC
596weak_function
597# endif
598re_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
616static reg_errcode_t
617re_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
940static reg_errcode_t
941prune_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
1035static inline re_dfastate_t *
1036__attribute ((always_inline)) internal_function
1037acquire_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
1077static int
1078internal_function
1079check_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
1209static int
1210internal_function
1211check_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
1228static int
1229internal_function
1230check_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
1250static int
1251internal_function
1252proceed_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
1350static reg_errcode_t
1351internal_function
1352push_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
1377static int
1378internal_function
1379pop_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
1397static reg_errcode_t
1398internal_function
1399set_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
1506static reg_errcode_t
1507internal_function
1508free_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
1523static void
1524internal_function
1525update_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
1595static reg_errcode_t
1596internal_function
1597sift_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
1653static reg_errcode_t
1654internal_function
1655build_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
1715static reg_errcode_t
1716internal_function
1717clean_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
1740static reg_errcode_t
1741internal_function
1742merge_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
1767static reg_errcode_t
1768internal_function
1769update_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
1815static reg_errcode_t
1816internal_function
1817add_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
1844static reg_errcode_t
1845internal_function
1846sub_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
1893static int
1894internal_function
1895check_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
1929static int
1930internal_function
1931check_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
2010static int
2011internal_function
2012check_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
2040static reg_errcode_t
2041internal_function
2042check_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
2128static reg_errcode_t
2129internal_function
2130sift_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
2228static int
2229internal_function
2230sift_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
2258static re_dfastate_t *
2259internal_function
2260transit_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 */
2316static re_dfastate_t *
2317internal_function
2318merge_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. */
2395static re_dfastate_t *
2396internal_function
2397find_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
2426static reg_errcode_t
2427internal_function
2428check_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
2460static re_dfastate_t *
2461transit_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
2499static reg_errcode_t
2500internal_function
2501transit_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
2569static reg_errcode_t
2570internal_function
2571transit_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
2683static reg_errcode_t
2684internal_function
2685get_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
2832static reg_errcode_t
2833internal_function
2834get_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
2861static int
2862internal_function
2863find_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
2883static reg_errcode_t
2884internal_function
2885check_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
3044static reg_errcode_t
3045internal_function
3046check_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
3125static reg_errcode_t
3126internal_function
3127check_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
3178static reg_errcode_t
3179internal_function
3180check_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
3222static reg_errcode_t
3223internal_function
3224expand_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
3311static int
3312internal_function
3313build_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 {
3394out_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
3549static int
3550internal_function
3551group_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
3739static int
3740internal_function
3741check_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
3981static unsigned int
3982internal_function
3983find_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
4044static int
4045internal_function
4046check_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
4094static reg_errcode_t
4095internal_function
4096extend_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
4157static reg_errcode_t
4158internal_function
4159match_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
4185static void
4186internal_function
4187match_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
4215static void
4216internal_function
4217match_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
4230static reg_errcode_t
4231internal_function
4232match_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
4279static int
4280internal_function
4281search_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
4302static reg_errcode_t
4303internal_function
4304match_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
4332static re_sub_match_last_t *
4333internal_function
4334match_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
4359static void
4360internal_function
4361sift_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/resources/COPYING_CCBYSA3 b/win32/resources/COPYING_CCBYSA3
new file mode 100644
index 000000000..fc45d7818
--- /dev/null
+++ b/win32/resources/COPYING_CCBYSA3
@@ -0,0 +1,7 @@
1This work is licenced under the Creative Commons Attribution-Share Alike 3.0
2United States License. To view a copy of this licence, visit
3http://creativecommons.org/licenses/by-sa/3.0/ or send a letter to Creative
4Commons, 171 Second Street, Suite 300, San Francisco, California 94105, USA.
5
6When attributing the artwork, using "GNOME Project" is enough.
7Please link to http://www.gnome.org where available.
diff --git a/win32/resources/Kbuild.src b/win32/resources/Kbuild.src
new file mode 100644
index 000000000..7cc010613
--- /dev/null
+++ b/win32/resources/Kbuild.src
@@ -0,0 +1,31 @@
1# Makefile for busybox
2#
3# Copyright (C) 2018 by R M Yorston <rmy@pobox.com>
4#
5# Licensed under GPLv2, see file LICENSE in this source tree.
6
7obj-y :=
8
9obj-$(CONFIG_FEATURE_RESOURCES) += resources.o
10
11# return commit level if available or 0
12bb_level = $(or $(word 2,$(subst -, ,$1)),0)
13
14WRFLAGS := -D"KBUILD_STR(s)=\#s" -D"BB_VER=KBUILD_STR($(BB_VER))" \
15 -D"BB_VERSION=$(VERSION)" -D"BB_PATCHLEVEL=$(PATCHLEVEL)" \
16 -D"BB_SUBLEVEL=$(SUBLEVEL)" \
17 -D"BB_EXTRAVERSION=$(call bb_level,$(EXTRAVERSION))" \
18 --include-dir=$(objtree)/include --include-dir=$(objtree)/win32/resources
19
20quiet_cmd_windres = WINDRES $@
21 cmd_windres = $(WINDRES) $(WRFLAGS) $< $@
22
23%.o: %.rc FORCE
24 $(call if_changed,windres)
25
26win32/resources/resources.o: win32/resources/resources.rc .config
27win32/resources/resources.o: win32/resources/aterm.ico win32/resources/sterm.ico
28win32/resources/resources.o: win32/resources/busybox-w32.manifest
29
30win32/resources/busybox-w32.manifest: win32/resources/busybox-w32.manifest.src .config
31 @sed -b "s/VERSION/$(BB_VER)/" $< >$@
diff --git a/win32/resources/README b/win32/resources/README
new file mode 100644
index 000000000..33a245386
--- /dev/null
+++ b/win32/resources/README
@@ -0,0 +1,9 @@
1The icons are based on those for GNOME terminal in the Adwaita theme.
2
3They were generated by importing the 16x16, 32x32 and 48x48 PNG files
4into GIMP as separate layers then exporting as a single .ico file.
5
6The original files are dual-licensed under either the GNU LGPL v3 or
7Creative Commons Attribution-Share Alike 3.0 United States License.
8
9The .ico files are licensed under the latter.
diff --git a/win32/resources/aterm.ico b/win32/resources/aterm.ico
new file mode 100644
index 000000000..e680216a2
--- /dev/null
+++ b/win32/resources/aterm.ico
Binary files differ
diff --git a/win32/resources/busybox-w32.manifest.src b/win32/resources/busybox-w32.manifest.src
new file mode 100644
index 000000000..c75dbdb96
--- /dev/null
+++ b/win32/resources/busybox-w32.manifest.src
@@ -0,0 +1,25 @@
1<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
3 <assemblyIdentity type="win32" name="busybox-w32" version="VERSION" />
4 <trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
5 <security>
6 <requestedPrivileges>
7 <requestedExecutionLevel level="asInvoker" uiAccess="false" />
8 </requestedPrivileges>
9 </security>
10 </trustInfo>
11 <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
12 <application>
13 <!-- Windows Vista -->
14 <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
15 <!-- Windows 7 -->
16 <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
17 <!-- Windows 8 -->
18 <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
19 <!-- Windows 8.1 -->
20 <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
21 <!-- Windows 10 -->
22 <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
23 </application>
24 </compatibility>
25</assembly>
diff --git a/win32/resources/dummy.c b/win32/resources/dummy.c
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/win32/resources/dummy.c
diff --git a/win32/resources/resources.rc b/win32/resources/resources.rc
new file mode 100644
index 000000000..f77a3bcc9
--- /dev/null
+++ b/win32/resources/resources.rc
@@ -0,0 +1,38 @@
1#include <autoconf.h>
2
3#if ENABLE_FEATURE_ICON_ATERM || ENABLE_FEATURE_ICON_ALL
41 ICON "aterm.ico"
5#endif
6#if ENABLE_FEATURE_ICON_STERM || ENABLE_FEATURE_ICON_ALL
72 ICON "sterm.ico"
8#endif
9
10#if ENABLE_FEATURE_VERSIONINFO
111 VERSIONINFO
12FILEVERSION BB_VERSION,BB_PATCHLEVEL,BB_SUBLEVEL,BB_EXTRAVERSION
13PRODUCTVERSION BB_VERSION,BB_PATCHLEVEL,BB_SUBLEVEL,BB_EXTRAVERSION
14BEGIN
15 BLOCK "StringFileInfo"
16 BEGIN
17 BLOCK "080904E4"
18 BEGIN
19 VALUE "CompanyName", "frippery.org"
20 VALUE "FileDescription", "BusyBox multi-call binary"
21 VALUE "FileVersion", BB_VER
22 VALUE "InternalName", "busybox"
23 VALUE "LegalCopyright", "(C) 1998-2018 Many authors"
24 VALUE "OriginalFilename", "busybox.exe"
25 VALUE "ProductName", "busybox-w32"
26 VALUE "ProductVersion", BB_VER
27 END
28 END
29 BLOCK "VarFileInfo"
30 BEGIN
31 VALUE "Translation", 0x809, 1252
32 END
33END
34#endif
35
36#if ENABLE_FEATURE_MANIFEST
371 RT_MANIFEST "busybox-w32.manifest"
38#endif
diff --git a/win32/resources/sterm.ico b/win32/resources/sterm.ico
new file mode 100644
index 000000000..b9125b34d
--- /dev/null
+++ b/win32/resources/sterm.ico
Binary files differ
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..2c9d2d873
--- /dev/null
+++ b/win32/select.c
@@ -0,0 +1,574 @@
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 "libbb.h"
22#include <malloc.h>
23#include <assert.h>
24
25#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
26/* Native Windows. */
27
28#include <sys/types.h>
29#include <errno.h>
30#include <limits.h>
31
32#include <winsock2.h>
33#include <windows.h>
34#include <io.h>
35#include <stdio.h>
36#include <conio.h>
37#include <time.h>
38
39/* Get the overridden 'struct timeval'. */
40#include <sys/time.h>
41
42#undef select
43
44struct bitset {
45 unsigned char in[FD_SETSIZE / CHAR_BIT];
46 unsigned char out[FD_SETSIZE / CHAR_BIT];
47};
48
49/* Declare data structures for ntdll functions. */
50typedef struct _FILE_PIPE_LOCAL_INFORMATION {
51 ULONG NamedPipeType;
52 ULONG NamedPipeConfiguration;
53 ULONG MaximumInstances;
54 ULONG CurrentInstances;
55 ULONG InboundQuota;
56 ULONG ReadDataAvailable;
57 ULONG OutboundQuota;
58 ULONG WriteQuotaAvailable;
59 ULONG NamedPipeState;
60 ULONG NamedPipeEnd;
61} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
62
63typedef struct _IO_STATUS_BLOCK
64{
65 union {
66 DWORD Status;
67 PVOID Pointer;
68 } u;
69 ULONG_PTR Information;
70} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
71
72typedef enum _FILE_INFORMATION_CLASS {
73 FilePipeLocalInformation = 24
74} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
75
76typedef DWORD (WINAPI *PNtQueryInformationFile)
77 (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
78
79#ifndef PIPE_BUF
80#define PIPE_BUF 512
81#endif
82
83static BOOL IsConsoleHandle (HANDLE h)
84{
85 DWORD mode;
86 return GetConsoleMode (h, &mode) != 0;
87}
88
89static BOOL
90IsSocketHandle (HANDLE h)
91{
92 WSANETWORKEVENTS ev;
93
94 if (IsConsoleHandle (h))
95 return FALSE;
96
97 /* Under Wine, it seems that getsockopt returns 0 for pipes too.
98 WSAEnumNetworkEvents instead distinguishes the two correctly. */
99 ev.lNetworkEvents = 0xDEADBEEF;
100 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
101 return ev.lNetworkEvents != 0xDEADBEEF;
102}
103
104/* Compute output fd_sets for libc descriptor FD (whose Windows handle is
105 H). */
106
107static int
108windows_poll_handle (HANDLE h, int fd,
109 struct bitset *rbits,
110 struct bitset *wbits,
111 struct bitset *xbits)
112{
113 BOOL read, write, except;
114 int i, ret;
115 INPUT_RECORD *irbuffer;
116 DWORD avail, nbuffer;
117 BOOL bRet;
118 IO_STATUS_BLOCK iosb;
119 FILE_PIPE_LOCAL_INFORMATION fpli;
120 static PNtQueryInformationFile NtQueryInformationFile;
121 static BOOL once_only;
122
123 read = write = except = FALSE;
124 switch (GetFileType (h))
125 {
126 case FILE_TYPE_DISK:
127 read = TRUE;
128 write = TRUE;
129 break;
130
131 case FILE_TYPE_PIPE:
132 if (!once_only)
133 {
134 NtQueryInformationFile = (PNtQueryInformationFile)
135 GetProcAddress (GetModuleHandle ("ntdll.dll"),
136 "NtQueryInformationFile");
137 once_only = TRUE;
138 }
139
140 if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
141 {
142 if (avail)
143 read = TRUE;
144 }
145 else if (GetLastError () == ERROR_BROKEN_PIPE)
146 read = TRUE;
147
148 else
149 {
150 /* It was the write-end of the pipe. Check if it is writable.
151 If NtQueryInformationFile fails, optimistically assume the pipe is
152 writable. This could happen on Windows 9x, where
153 NtQueryInformationFile is not available, or if we inherit a pipe
154 that doesn't permit FILE_READ_ATTRIBUTES access on the write end
155 (I think this should not happen since Windows XP SP2; WINE seems
156 fine too). Otherwise, ensure that enough space is available for
157 atomic writes. */
158 memset (&iosb, 0, sizeof (iosb));
159 memset (&fpli, 0, sizeof (fpli));
160
161 if (!NtQueryInformationFile
162 || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
163 FilePipeLocalInformation)
164 || fpli.WriteQuotaAvailable >= PIPE_BUF
165 || (fpli.OutboundQuota < PIPE_BUF &&
166 fpli.WriteQuotaAvailable == fpli.OutboundQuota))
167 write = TRUE;
168 }
169 break;
170
171 case FILE_TYPE_CHAR:
172 write = TRUE;
173 if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
174 break;
175
176 ret = WaitForSingleObject (h, 0);
177 if (ret == WAIT_OBJECT_0)
178 {
179 if (!IsConsoleHandle (h))
180 {
181 read = TRUE;
182 break;
183 }
184
185 nbuffer = avail = 0;
186 bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
187
188 /* Screen buffers handles are filtered earlier. */
189 assert (bRet);
190 if (nbuffer == 0)
191 {
192 except = TRUE;
193 break;
194 }
195
196 irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
197 bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
198 if (!bRet || avail == 0)
199 {
200 except = TRUE;
201 break;
202 }
203
204 for (i = 0; i < avail; i++)
205 if (irbuffer[i].EventType == KEY_EVENT &&
206 irbuffer[i].Event.KeyEvent.bKeyDown)
207 read = TRUE;
208 }
209 break;
210
211 default:
212 ret = WaitForSingleObject (h, 0);
213 write = TRUE;
214 if (ret == WAIT_OBJECT_0)
215 read = TRUE;
216
217 break;
218 }
219
220 ret = 0;
221 if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
222 {
223 rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
224 ret++;
225 }
226
227 if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
228 {
229 wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
230 ret++;
231 }
232
233 if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
234 {
235 xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
236 ret++;
237 }
238
239 return ret;
240}
241
242int
243mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
244 struct timeval *timeout)
245#undef timeval
246{
247 static struct timeval tv0;
248 static HANDLE hEvent;
249 HANDLE h, handle_array[FD_SETSIZE + 2];
250 fd_set handle_rfds, handle_wfds, handle_xfds;
251 struct bitset rbits, wbits, xbits;
252 unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT];
253 DWORD ret, wait_timeout, nhandles, nsock, nbuffer;
254 MSG msg;
255 int i, fd, rc;
256 clock_t tend = 0;
257
258 if (nfds > FD_SETSIZE)
259 nfds = FD_SETSIZE;
260
261 if (!timeout)
262 wait_timeout = INFINITE;
263 else
264 {
265 wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
266
267 /* select is also used as a portable usleep. */
268 if (!rfds && !wfds && !xfds)
269 {
270 Sleep (wait_timeout);
271 return 0;
272 }
273 }
274
275 if (!hEvent)
276 hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
277
278 handle_array[0] = hEvent;
279 nhandles = 1;
280 nsock = 0;
281
282 /* Copy descriptors to bitsets. At the same time, eliminate
283 bits in the "wrong" direction for console input buffers
284 and screen buffers, because screen buffers are waitable
285 and they will block until a character is available. */
286 memset (&rbits, 0, sizeof (rbits));
287 memset (&wbits, 0, sizeof (wbits));
288 memset (&xbits, 0, sizeof (xbits));
289 memset (anyfds_in, 0, sizeof (anyfds_in));
290 if (rfds)
291 for (i = 0; i < rfds->fd_count; i++)
292 {
293 fd = rfds->fd_array[i];
294 h = (HANDLE) _get_osfhandle (fd);
295 if (IsConsoleHandle (h)
296 && !GetNumberOfConsoleInputEvents (h, &nbuffer))
297 continue;
298
299 rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
300 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
301 }
302 else
303 rfds = (fd_set *) alloca (sizeof (fd_set));
304
305 if (wfds)
306 for (i = 0; i < wfds->fd_count; i++)
307 {
308 fd = wfds->fd_array[i];
309 h = (HANDLE) _get_osfhandle (fd);
310 if (IsConsoleHandle (h)
311 && GetNumberOfConsoleInputEvents (h, &nbuffer))
312 continue;
313
314 wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
315 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
316 }
317 else
318 wfds = (fd_set *) alloca (sizeof (fd_set));
319
320 if (xfds)
321 for (i = 0; i < xfds->fd_count; i++)
322 {
323 fd = xfds->fd_array[i];
324 xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
325 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
326 }
327 else
328 xfds = (fd_set *) alloca (sizeof (fd_set));
329
330 /* Zero all the fd_sets, including the application's. */
331 FD_ZERO (rfds);
332 FD_ZERO (wfds);
333 FD_ZERO (xfds);
334 FD_ZERO (&handle_rfds);
335 FD_ZERO (&handle_wfds);
336 FD_ZERO (&handle_xfds);
337
338 /* Classify handles. Create fd sets for sockets, poll the others. */
339 for (i = 0; i < nfds; i++)
340 {
341 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
342 continue;
343
344 h = (HANDLE) _get_osfhandle (i);
345 if (!h)
346 {
347 errno = EBADF;
348 return -1;
349 }
350
351 if (IsSocketHandle (h))
352 {
353 int requested = FD_CLOSE;
354
355 /* See above; socket handles are mapped onto select, but we
356 need to map descriptors to handles. */
357 if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
358 {
359 requested |= FD_READ | FD_ACCEPT;
360 FD_SET ((SOCKET) h, rfds);
361 FD_SET ((SOCKET) h, &handle_rfds);
362 }
363 if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
364 {
365 requested |= FD_WRITE | FD_CONNECT;
366 FD_SET ((SOCKET) h, wfds);
367 FD_SET ((SOCKET) h, &handle_wfds);
368 }
369 if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
370 {
371 requested |= FD_OOB;
372 FD_SET ((SOCKET) h, xfds);
373 FD_SET ((SOCKET) h, &handle_xfds);
374 }
375
376 WSAEventSelect ((SOCKET) h, hEvent, requested);
377 nsock++;
378 }
379 else
380 {
381 handle_array[nhandles++] = h;
382
383 /* Poll now. If we get an event, do not wait below. */
384 if (wait_timeout != 0
385 && windows_poll_handle (h, i, &rbits, &wbits, &xbits))
386 wait_timeout = 0;
387 }
388 }
389
390 /* Place a sentinel at the end of the array. */
391 handle_array[nhandles] = NULL;
392
393 /* When will the waiting period expire? */
394 if (wait_timeout != INFINITE)
395 tend = clock () + wait_timeout;
396
397restart:
398 if (wait_timeout == 0 || nsock == 0)
399 rc = 0;
400 else
401 {
402 /* See if we need to wait in the loop below. If any select is ready,
403 do MsgWaitForMultipleObjects anyway to dispatch messages, but
404 no need to call select again. */
405 rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
406 if (rc == 0)
407 {
408 /* Restore the fd_sets for the other select we do below. */
409 memcpy (&handle_rfds, rfds, sizeof (fd_set));
410 memcpy (&handle_wfds, wfds, sizeof (fd_set));
411 memcpy (&handle_xfds, xfds, sizeof (fd_set));
412 }
413 else
414 wait_timeout = 0;
415 }
416
417 /* How much is left to wait? */
418 if (wait_timeout != INFINITE)
419 {
420 clock_t tnow = clock ();
421 if (tend >= tnow)
422 wait_timeout = tend - tnow;
423 else
424 wait_timeout = 0;
425 }
426
427 for (;;)
428 {
429 ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
430 wait_timeout, QS_ALLINPUT);
431
432 if (ret == WAIT_OBJECT_0 + nhandles)
433 {
434 /* new input of some other kind */
435 BOOL bRet;
436 while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
437 {
438 TranslateMessage (&msg);
439 DispatchMessage (&msg);
440 }
441 }
442 else
443 break;
444 }
445
446 /* If we haven't done it yet, check the status of the sockets. */
447 if (rc == 0 && nsock > 0)
448 rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
449
450 if (nhandles > 1)
451 {
452 /* Count results that are not counted in the return value of select. */
453 nhandles = 1;
454 for (i = 0; i < nfds; i++)
455 {
456 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
457 continue;
458
459 h = (HANDLE) _get_osfhandle (i);
460 if (h == handle_array[nhandles])
461 {
462 /* Not a socket. */
463 nhandles++;
464 windows_poll_handle (h, i, &rbits, &wbits, &xbits);
465 if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
466 || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
467 || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
468 rc++;
469 }
470 }
471
472 if (rc == 0
473 && (wait_timeout == INFINITE
474 /* If NHANDLES > 1, but no bits are set, it means we've
475 been told incorrectly that some handle was signaled.
476 This happens with anonymous pipes, which always cause
477 MsgWaitForMultipleObjects to exit immediately, but no
478 data is found ready to be read by windows_poll_handle.
479 To avoid a total failure (whereby we return zero and
480 don't wait at all), let's poll in a more busy loop. */
481 || (wait_timeout != 0 && nhandles > 1)))
482 {
483 /* Sleep 1 millisecond to avoid busy wait and retry with the
484 original fd_sets. */
485 memcpy (&handle_rfds, rfds, sizeof (fd_set));
486 memcpy (&handle_wfds, wfds, sizeof (fd_set));
487 memcpy (&handle_xfds, xfds, sizeof (fd_set));
488 SleepEx (1, TRUE);
489 goto restart;
490 }
491 if (timeout && wait_timeout == 0 && rc == 0)
492 timeout->tv_sec = timeout->tv_usec = 0;
493 }
494
495 /* Now fill in the results. */
496 FD_ZERO (rfds);
497 FD_ZERO (wfds);
498 FD_ZERO (xfds);
499 nhandles = 1;
500 for (i = 0; i < nfds; i++)
501 {
502 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
503 continue;
504
505 h = (HANDLE) _get_osfhandle (i);
506 if (h != handle_array[nhandles])
507 {
508 /* Perform handle->descriptor mapping. */
509 WSAEventSelect ((SOCKET) h, NULL, 0);
510 if (FD_ISSET (h, &handle_rfds))
511 FD_SET (i, rfds);
512 if (FD_ISSET (h, &handle_wfds))
513 FD_SET (i, wfds);
514 if (FD_ISSET (h, &handle_xfds))
515 FD_SET (i, xfds);
516 }
517 else
518 {
519 /* Not a socket. */
520 nhandles++;
521 if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
522 FD_SET (i, rfds);
523 if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
524 FD_SET (i, wfds);
525 if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
526 FD_SET (i, xfds);
527 }
528 }
529
530 return rc;
531}
532
533#else /* ! Native Windows. */
534
535#include <sys/select.h>
536#include <stddef.h> /* NULL */
537#include <errno.h>
538#include <unistd.h>
539
540#undef select
541
542int
543rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
544 struct timeval *timeout)
545{
546 int i;
547
548 /* FreeBSD 8.2 has a bug: it does not always detect invalid fds. */
549 if (nfds < 0 || nfds > FD_SETSIZE)
550 {
551 errno = EINVAL;
552 return -1;
553 }
554 for (i = 0; i < nfds; i++)
555 {
556 if (((rfds && FD_ISSET (i, rfds))
557 || (wfds && FD_ISSET (i, wfds))
558 || (xfds && FD_ISSET (i, xfds)))
559 && dup2 (i, i) != i)
560 return -1;
561 }
562
563 /* Interix 3.5 has a bug: it does not support nfds == 0. */
564 if (nfds == 0)
565 {
566 nfds = 1;
567 rfds = NULL;
568 wfds = NULL;
569 xfds = NULL;
570 }
571 return select (nfds, rfds, wfds, xfds, timeout);
572}
573
574#endif
diff --git a/win32/sh_random.c b/win32/sh_random.c
new file mode 100644
index 000000000..2948e4a55
--- /dev/null
+++ b/win32/sh_random.c
@@ -0,0 +1,76 @@
1#include "libbb.h"
2#include "../shell/random.h"
3
4/* call 'fn' to put data in 'dt' then copy it to generator state */
5#define GET_DATA(fn, dt) \
6 fn(&dt); \
7 u = (uint32_t *)&dt; \
8 for (j=0; j<sizeof(dt)/sizeof(uint32_t); ++j) { \
9 state[i++%2] ^= *u++; \
10 }
11
12/*
13 * Obtain a few bytes of random-ish data to initialise the generator.
14 * This is unlikely to be very robust: don't rely on it for
15 * anything that needs to be secure.
16 */
17static void get_entropy(uint32_t state[2])
18{
19 int i, j;
20 SYSTEMTIME tm;
21 MEMORYSTATUS ms;
22 SYSTEM_INFO si;
23 LARGE_INTEGER pc;
24 uint32_t *u;
25
26 i = 0;
27 state[i++%2] ^= (uint32_t)GetCurrentProcessId();
28 state[i++%2] ^= (uint32_t)GetCurrentThreadId();
29 state[i++%2] ^= (uint32_t)GetTickCount();
30
31 GET_DATA(GetLocalTime, tm)
32 GET_DATA(GlobalMemoryStatus, ms)
33 GET_DATA(GetSystemInfo, si)
34 GET_DATA(QueryPerformanceCounter, pc)
35
36#if 0
37 {
38 unsigned char *p = (unsigned char *)state;
39
40 for (j=0; j<8; ++j) {
41 fprintf(stderr, "%02x", *p++);
42 if ((j&3) == 3) {
43 fprintf(stderr, " ");
44 }
45 }
46 fprintf(stderr, "\n");
47 }
48#endif
49}
50
51ssize_t get_random_bytes(void *buf, ssize_t count)
52{
53 static random_t rnd;
54 ssize_t save_count = count;
55 uint32_t value;
56 unsigned char *ptr = (unsigned char *)&value;
57
58 if (buf == NULL || count < 0) {
59 errno = EINVAL;
60 return -1;
61 }
62
63 if (UNINITED_RANDOM_T(&rnd)) {
64 uint32_t state[2] = {0, 0};
65
66 get_entropy(state);
67 INIT_RANDOM_T(&rnd, state[0] ? state[0] : 1, state[1]);
68 }
69
70 for (;count > 0; buf+=4, count-=4) {
71 value = full_random(&rnd);
72 memcpy(buf, ptr, count >= 4 ? 4 : count);
73 }
74
75 return save_count;
76}
diff --git a/win32/statfs.c b/win32/statfs.c
new file mode 100644
index 000000000..4e5e4612d
--- /dev/null
+++ b/win32/statfs.c
@@ -0,0 +1,80 @@
1#include <sys/statfs.h>
2#include "libbb.h"
3
4/*
5 * Code from libguestfs (with addition of GetVolumeInformation call)
6 */
7int 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..5dab9b4f4
--- /dev/null
+++ b/win32/strptime.c
@@ -0,0 +1,635 @@
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 "libbb.h"
25#include <time.h>
26
27#include <assert.h>
28#include <ctype.h>
29#include <limits.h>
30#include <string.h>
31#include <stdbool.h>
32
33
34enum ptime_locale_status { not, loc, raw };
35
36
37
38#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
39/* Oh come on. Get a reasonable compiler. */
40# define match_string(cs1, s2) \
41 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
42/* We intentionally do not use isdigit() for testing because this will
43 lead to problems with the wide character version. */
44#define get_number(from, to, n) \
45 do { \
46 int __n = n; \
47 val = 0; \
48 while (*rp == ' ') \
49 ++rp; \
50 if (*rp < '0' || *rp > '9') \
51 return NULL; \
52 do { \
53 val *= 10; \
54 val += *rp++ - '0'; \
55 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
56 if (val < from || val > to) \
57 return NULL; \
58 } while (0)
59# define get_alt_number(from, to, n) \
60 /* We don't have the alternate representation. */ \
61 get_number(from, to, n)
62#define recursive(new_fmt) \
63 (*(new_fmt) != '\0' \
64 && (rp = __strptime_internal (rp, (new_fmt), tm, \
65 decided, era_cnt)) != NULL)
66
67
68static char const weekday_name[][10] =
69 {
70 "Sunday", "Monday", "Tuesday", "Wednesday",
71 "Thursday", "Friday", "Saturday"
72 };
73static char const ab_weekday_name[][4] =
74 {
75 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
76 };
77static char const month_name[][10] =
78 {
79 "January", "February", "March", "April", "May", "June",
80 "July", "August", "September", "October", "November", "December"
81 };
82static char const ab_month_name[][4] =
83 {
84 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
85 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
86 };
87# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
88# define HERE_D_FMT "%m/%d/%y"
89# define HERE_AM_STR "AM"
90# define HERE_PM_STR "PM"
91# define HERE_T_FMT_AMPM "%I:%M:%S %p"
92# define HERE_T_FMT "%H:%M:%S"
93
94static const unsigned short int __mon_yday[2][13] =
95 {
96 /* Normal years. */
97 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
98 /* Leap years. */
99 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
100 };
101
102# define ISSPACE(Ch) isspace (Ch)
103
104
105
106
107#ifndef __isleap
108/* Nonzero if YEAR is a leap year (every 4 years,
109 except every 100th isn't, and every 400th is). */
110# define __isleap(year) \
111 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
112#endif
113
114/* Compute the day of the week. */
115static void
116day_of_the_week (struct tm *tm)
117{
118 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
119 difference between this data in the one on TM and so determine
120 the weekday. */
121 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
122 int wday = (-473
123 + (365 * (tm->tm_year - 70))
124 + (corr_year / 4)
125 - ((corr_year / 4) / 25) + ((corr_year / 4) % 25 < 0)
126 + (((corr_year / 4) / 25) / 4)
127 + __mon_yday[0][tm->tm_mon]
128 + tm->tm_mday - 1);
129 tm->tm_wday = ((wday % 7) + 7) % 7;
130}
131
132/* Compute the day of the year. */
133static void
134day_of_the_year (struct tm *tm)
135{
136 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
137 + (tm->tm_mday - 1));
138}
139
140
141static char *
142__strptime_internal (const char *rp, const char *fmt, struct tm *tm,
143 enum ptime_locale_status *decided, int era_cnt)
144{
145
146 int cnt;
147 size_t val;
148 int have_I, is_pm;
149 int century, want_century;
150 int want_era;
151 int have_wday, want_xday;
152 int have_yday;
153 int have_mon, have_mday;
154 int have_uweek, have_wweek;
155 int week_no;
156
157 have_I = is_pm = 0;
158 century = -1;
159 want_century = 0;
160 want_era = 0;
161 week_no = 0;
162
163 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
164 have_wweek = 0;
165
166 while (*fmt != '\0')
167 {
168 /* A white space in the format string matches 0 more or white
169 space in the input string. */
170 if (ISSPACE (*fmt))
171 {
172 while (ISSPACE (*rp))
173 ++rp;
174 ++fmt;
175 continue;
176 }
177
178 /* Any character but '%' must be matched by the same character
179 in the iput string. */
180 if (*fmt != '%')
181 {
182 match_char (*fmt++, *rp++);
183 continue;
184 }
185
186 ++fmt;
187 /* We need this for handling the 'E' modifier. */
188 start_over:
189
190 switch (*fmt++)
191 {
192 case '%':
193 /* Match the '%' character itself. */
194 match_char ('%', *rp++);
195 break;
196 case 'a':
197 case 'A':
198 /* Match day of week. */
199 for (cnt = 0; cnt < 7; ++cnt)
200 {
201 if (*decided != loc
202 && (match_string (weekday_name[cnt], rp)
203 || match_string (ab_weekday_name[cnt], rp)))
204 {
205 *decided = raw;
206 break;
207 }
208 }
209 if (cnt == 7)
210 /* Does not match a weekday name. */
211 return NULL;
212 tm->tm_wday = cnt;
213 have_wday = 1;
214 break;
215 case 'b':
216 case 'B':
217 case 'h':
218 /* Match month name. */
219 for (cnt = 0; cnt < 12; ++cnt)
220 {
221 if (match_string (month_name[cnt], rp)
222 || match_string (ab_month_name[cnt], rp))
223 {
224 *decided = raw;
225 break;
226 }
227 }
228 if (cnt == 12)
229 /* Does not match a month name. */
230 return NULL;
231 tm->tm_mon = cnt;
232 want_xday = 1;
233 break;
234 case 'c':
235 /* Match locale's date and time format. */
236 if (!recursive (HERE_D_T_FMT))
237 return NULL;
238 want_xday = 1;
239 break;
240 case 'C':
241 /* Match century number. */
242 get_number (0, 99, 2);
243 century = val;
244 want_xday = 1;
245 break;
246 case 'd':
247 case 'e':
248 /* Match day of month. */
249 get_number (1, 31, 2);
250 tm->tm_mday = val;
251 have_mday = 1;
252 want_xday = 1;
253 break;
254 case 'F':
255 if (!recursive ("%Y-%m-%d"))
256 return NULL;
257 want_xday = 1;
258 break;
259 case 'x':
260 /* Fall through. */
261 case 'D':
262 /* Match standard day format. */
263 if (!recursive (HERE_D_FMT))
264 return NULL;
265 want_xday = 1;
266 break;
267 case 'k':
268 case 'H':
269 /* Match hour in 24-hour clock. */
270 get_number (0, 23, 2);
271 tm->tm_hour = val;
272 have_I = 0;
273 break;
274 case 'l':
275 /* Match hour in 12-hour clock. GNU extension. */
276 case 'I':
277 /* Match hour in 12-hour clock. */
278 get_number (1, 12, 2);
279 tm->tm_hour = val % 12;
280 have_I = 1;
281 break;
282 case 'j':
283 /* Match day number of year. */
284 get_number (1, 366, 3);
285 tm->tm_yday = val - 1;
286 have_yday = 1;
287 break;
288 case 'm':
289 /* Match number of month. */
290 get_number (1, 12, 2);
291 tm->tm_mon = val - 1;
292 have_mon = 1;
293 want_xday = 1;
294 break;
295 case 'M':
296 /* Match minute. */
297 get_number (0, 59, 2);
298 tm->tm_min = val;
299 break;
300 case 'n':
301 case 't':
302 /* Match any white space. */
303 while (ISSPACE (*rp))
304 ++rp;
305 break;
306 case 'p':
307 /* Match locale's equivalent of AM/PM. */
308 if (!match_string (HERE_AM_STR, rp))
309 {
310 if (match_string (HERE_PM_STR, rp))
311 is_pm = 1;
312 else
313 return NULL;
314 }
315 break;
316 case 'r':
317 if (!recursive (HERE_T_FMT_AMPM))
318 return NULL;
319 break;
320 case 'R':
321 if (!recursive ("%H:%M"))
322 return NULL;
323 break;
324 case 's':
325 {
326 /* The number of seconds may be very high so we cannot use
327 the 'get_number' macro. Instead read the number
328 character for character and construct the result while
329 doing this. */
330 time_t secs = 0;
331 if (*rp < '0' || *rp > '9')
332 /* We need at least one digit. */
333 return NULL;
334
335 do
336 {
337 secs *= 10;
338 secs += *rp++ - '0';
339 }
340 while (*rp >= '0' && *rp <= '9');
341
342 if (localtime_r (&secs, tm) == NULL)
343 /* Error in function. */
344 return NULL;
345 }
346 break;
347 case 'S':
348 get_number (0, 61, 2);
349 tm->tm_sec = val;
350 break;
351 case 'X':
352 /* Fall through. */
353 case 'T':
354 if (!recursive (HERE_T_FMT))
355 return NULL;
356 break;
357 case 'u':
358 get_number (1, 7, 1);
359 tm->tm_wday = val % 7;
360 have_wday = 1;
361 break;
362 case 'g':
363 get_number (0, 99, 2);
364 /* XXX This cannot determine any field in TM. */
365 break;
366 case 'G':
367 if (*rp < '0' || *rp > '9')
368 return NULL;
369 /* XXX Ignore the number since we would need some more
370 information to compute a real date. */
371 do
372 ++rp;
373 while (*rp >= '0' && *rp <= '9');
374 break;
375 case 'U':
376 get_number (0, 53, 2);
377 week_no = val;
378 have_uweek = 1;
379 break;
380 case 'W':
381 get_number (0, 53, 2);
382 week_no = val;
383 have_wweek = 1;
384 break;
385 case 'V':
386 get_number (0, 53, 2);
387 /* XXX This cannot determine any field in TM without some
388 information. */
389 break;
390 case 'w':
391 /* Match number of weekday. */
392 get_number (0, 6, 1);
393 tm->tm_wday = val;
394 have_wday = 1;
395 break;
396 case 'y':
397 /* Match year within century. */
398 get_number (0, 99, 2);
399 /* The "Year 2000: The Millennium Rollover" paper suggests that
400 values in the range 69-99 refer to the twentieth century. */
401 tm->tm_year = val >= 69 ? val : val + 100;
402 /* Indicate that we want to use the century, if specified. */
403 want_century = 1;
404 want_xday = 1;
405 break;
406 case 'Y':
407 /* Match year including century number. */
408 get_number (0, 9999, 4);
409 tm->tm_year = val - 1900;
410 want_century = 0;
411 want_xday = 1;
412 break;
413 case 'Z':
414 /* XXX How to handle this? */
415 break;
416 case 'z':
417 /* We recognize two formats: if two digits are given, these
418 specify hours. If fours digits are used, minutes are
419 also specified. */
420 {
421 bool neg;
422 int n;
423
424 val = 0;
425 while (*rp == ' ')
426 ++rp;
427 if (*rp != '+' && *rp != '-')
428 return NULL;
429 neg = *rp++ == '-';
430 n = 0;
431 while (n < 4 && *rp >= '0' && *rp <= '9')
432 {
433 val = val * 10 + *rp++ - '0';
434 ++n;
435 }
436 if (n == 2)
437 val *= 100;
438 else if (n != 4)
439 /* Only two or four digits recognized. */
440 return NULL;
441 else
442 {
443 /* We have to convert the minutes into decimal. */
444 if (val % 100 >= 60)
445 return NULL;
446 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
447 }
448 if (val > 1200)
449 return NULL;
450 if (neg)
451 val = -val;
452 }
453 break;
454 case 'E':
455 /* We have no information about the era format. Just use
456 the normal format. */
457 if (*fmt != 'c' && *fmt != 'C' && *fmt != 'y' && *fmt != 'Y'
458 && *fmt != 'x' && *fmt != 'X')
459 /* This is an illegal format. */
460 return NULL;
461
462 goto start_over;
463 case 'O':
464 switch (*fmt++)
465 {
466 case 'd':
467 case 'e':
468 /* Match day of month using alternate numeric symbols. */
469 get_alt_number (1, 31, 2);
470 tm->tm_mday = val;
471 have_mday = 1;
472 want_xday = 1;
473 break;
474 case 'H':
475 /* Match hour in 24-hour clock using alternate numeric
476 symbols. */
477 get_alt_number (0, 23, 2);
478 tm->tm_hour = val;
479 have_I = 0;
480 break;
481 case 'I':
482 /* Match hour in 12-hour clock using alternate numeric
483 symbols. */
484 get_alt_number (1, 12, 2);
485 tm->tm_hour = val % 12;
486 have_I = 1;
487 break;
488 case 'm':
489 /* Match month using alternate numeric symbols. */
490 get_alt_number (1, 12, 2);
491 tm->tm_mon = val - 1;
492 have_mon = 1;
493 want_xday = 1;
494 break;
495 case 'M':
496 /* Match minutes using alternate numeric symbols. */
497 get_alt_number (0, 59, 2);
498 tm->tm_min = val;
499 break;
500 case 'S':
501 /* Match seconds using alternate numeric symbols. */
502 get_alt_number (0, 61, 2);
503 tm->tm_sec = val;
504 break;
505 case 'U':
506 get_alt_number (0, 53, 2);
507 week_no = val;
508 have_uweek = 1;
509 break;
510 case 'W':
511 get_alt_number (0, 53, 2);
512 week_no = val;
513 have_wweek = 1;
514 break;
515 case 'V':
516 get_alt_number (0, 53, 2);
517 /* XXX This cannot determine any field in TM without
518 further information. */
519 break;
520 case 'w':
521 /* Match number of weekday using alternate numeric symbols. */
522 get_alt_number (0, 6, 1);
523 tm->tm_wday = val;
524 have_wday = 1;
525 break;
526 case 'y':
527 /* Match year within century using alternate numeric symbols. */
528 get_alt_number (0, 99, 2);
529 tm->tm_year = val >= 69 ? val : val + 100;
530 want_xday = 1;
531 break;
532 default:
533 return NULL;
534 }
535 break;
536 default:
537 return NULL;
538 }
539 }
540
541 if (have_I && is_pm)
542 tm->tm_hour += 12;
543
544 if (century != -1)
545 {
546 if (want_century)
547 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
548 else
549 /* Only the century, but not the year. Strange, but so be it. */
550 tm->tm_year = (century - 19) * 100;
551 }
552
553 if (era_cnt != -1)
554 {
555 }
556 else
557 if (want_era)
558 {
559 /* No era found but we have seen an E modifier. Rectify some
560 values. */
561 if (want_century && century == -1 && tm->tm_year < 69)
562 tm->tm_year += 100;
563 }
564
565 if (want_xday && !have_wday)
566 {
567 if ( !(have_mon && have_mday) && have_yday)
568 {
569 /* We don't have tm_mon and/or tm_mday, compute them. */
570 int t_mon = 0;
571 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
572 t_mon++;
573 if (!have_mon)
574 tm->tm_mon = t_mon - 1;
575 if (!have_mday)
576 tm->tm_mday =
577 (tm->tm_yday
578 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
579 }
580 day_of_the_week (tm);
581 }
582
583 if (want_xday && !have_yday)
584 day_of_the_year (tm);
585
586 if ((have_uweek || have_wweek) && have_wday)
587 {
588 int save_wday = tm->tm_wday;
589 int save_mday = tm->tm_mday;
590 int save_mon = tm->tm_mon;
591 int w_offset = have_uweek ? 0 : 1;
592
593 tm->tm_mday = 1;
594 tm->tm_mon = 0;
595 day_of_the_week (tm);
596 if (have_mday)
597 tm->tm_mday = save_mday;
598 if (have_mon)
599 tm->tm_mon = save_mon;
600
601 if (!have_yday)
602 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
603 + (week_no - 1) *7
604 + save_wday - w_offset);
605
606 if (!have_mday || !have_mon)
607 {
608 int t_mon = 0;
609 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
610 <= tm->tm_yday)
611 t_mon++;
612 if (!have_mon)
613 tm->tm_mon = t_mon - 1;
614 if (!have_mday)
615 tm->tm_mday =
616 (tm->tm_yday
617 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
618 }
619
620 tm->tm_wday = save_wday;
621 }
622
623 return (char *) rp;
624}
625
626
627char *
628strptime (const char *buf, const char *format, struct tm *tm)
629{
630 enum ptime_locale_status decided;
631
632 decided = raw;
633 return __strptime_internal (buf, format, tm, &decided, -1);
634}
635
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/select.h b/win32/sys/select.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/win32/sys/select.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
6struct 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
20extern int statfs(const char *file, struct statfs *buf);
21
22#endif
diff --git a/win32/sys/statvfs.h b/win32/sys/statvfs.h
new file mode 100644
index 000000000..ceb9ee353
--- /dev/null
+++ b/win32/sys/statvfs.h
@@ -0,0 +1,3 @@
1#include <sys/statfs.h>
2
3#define statvfs statfs
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. */
45struct 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. */
63extern int uname (struct utsname *__name);
64
65
66#endif /* sys/utsname.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..44a47f861
--- /dev/null
+++ b/win32/system.c
@@ -0,0 +1,22 @@
1#include "libbb.h"
2
3int 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(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..7115bc0da
--- /dev/null
+++ b/win32/termios.c
@@ -0,0 +1,73 @@
1#include "libbb.h"
2
3int64_t FAST_FUNC read_key(int fd, char *buf UNUSED_PARAM, int timeout)
4{
5 HANDLE cin = GetStdHandle(STD_INPUT_HANDLE);
6 INPUT_RECORD record;
7 DWORD nevent_out, mode;
8 int ret = -1;
9 char *s;
10
11 if (fd != 0)
12 bb_error_msg_and_die("read_key only works on stdin");
13 if (cin == INVALID_HANDLE_VALUE)
14 return -1;
15 GetConsoleMode(cin, &mode);
16 SetConsoleMode(cin, 0);
17
18 while (1) {
19 if (timeout > 0) {
20 if (WaitForSingleObject(cin, timeout) != WAIT_OBJECT_0)
21 goto done;
22 }
23 if (!ReadConsoleInput(cin, &record, 1, &nevent_out))
24 goto done;
25 if (record.EventType != KEY_EVENT || !record.Event.KeyEvent.bKeyDown)
26 continue;
27 if (!record.Event.KeyEvent.uChar.AsciiChar) {
28 DWORD state = record.Event.KeyEvent.dwControlKeyState;
29
30 if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED) &&
31 (record.Event.KeyEvent.wVirtualKeyCode >= 'A' &&
32 record.Event.KeyEvent.wVirtualKeyCode <= 'Z')) {
33 ret = record.Event.KeyEvent.wVirtualKeyCode & ~0x40;
34 break;
35 }
36
37 switch (record.Event.KeyEvent.wVirtualKeyCode) {
38 case VK_DELETE: ret = KEYCODE_DELETE; goto done;
39 case VK_INSERT: ret = KEYCODE_INSERT; goto done;
40 case VK_UP: ret = KEYCODE_UP; goto done;
41 case VK_DOWN: ret = KEYCODE_DOWN; goto done;
42 case VK_RIGHT:
43 if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) {
44 ret = KEYCODE_CTRL_RIGHT;
45 goto done;
46 }
47 ret = KEYCODE_RIGHT;
48 goto done;
49 case VK_LEFT:
50 if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) {
51 ret = KEYCODE_CTRL_LEFT;
52 goto done;
53 }
54 ret = KEYCODE_LEFT;
55 goto done;
56 case VK_HOME: ret = KEYCODE_HOME; goto done;
57 case VK_END: ret = KEYCODE_END; goto done;
58 case VK_PRIOR: ret = KEYCODE_PAGEUP; goto done;
59 case VK_NEXT: ret = KEYCODE_PAGEDOWN; goto done;
60 }
61 continue;
62 }
63 if ( (record.Event.KeyEvent.uChar.AsciiChar & 0x80) == 0x80 ) {
64 s = &record.Event.KeyEvent.uChar.AsciiChar;
65 OemToCharBuff(s, s, 1);
66 }
67 ret = record.Event.KeyEvent.uChar.AsciiChar;
68 break;
69 }
70 done:
71 SetConsoleMode(cin, mode);
72 return ret;
73}
diff --git a/win32/termios.h b/win32/termios.h
new file mode 100644
index 000000000..c98d414f3
--- /dev/null
+++ b/win32/termios.h
@@ -0,0 +1,125 @@
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
104typedef unsigned char cc_t;
105typedef unsigned int tcflag_t;
106typedef unsigned int speed_t;
107typedef unsigned short otcflag_t;
108typedef unsigned char ospeed_t;
109
110#define NCCS 18
111struct 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
122struct winsize {
123 unsigned short ws_row, ws_col;
124 unsigned short ws_xpixel, ws_ypixel;
125};
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
5int 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..9e7d10a56
--- /dev/null
+++ b/win32/winansi.c
@@ -0,0 +1,776 @@
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
31static HANDLE console;
32static HANDLE console_in;
33static WORD plain_attr;
34static WORD attr;
35static int negative;
36
37static 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
63static int is_console(int fd)
64{
65 init();
66 return isatty(fd) && console;
67}
68
69static int is_console_in(int fd)
70{
71 init();
72 return isatty(fd) && console_in;
73}
74
75static int skip_ansi_emulation(void)
76{
77 static char *var = NULL;
78 static int got_var = FALSE;
79
80 if (!got_var) {
81 var = getenv("BB_SKIP_ANSI_EMULATION");
82 got_var = TRUE;
83 }
84
85 return var != NULL;
86}
87
88
89#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
90#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
91
92static void set_console_attr(void)
93{
94 WORD attributes = attr;
95 if (negative) {
96 attributes &= ~FOREGROUND_ALL;
97 attributes &= ~BACKGROUND_ALL;
98
99 /* This could probably use a bitmask
100 instead of a series of ifs */
101 if (attr & FOREGROUND_RED)
102 attributes |= BACKGROUND_RED;
103 if (attr & FOREGROUND_GREEN)
104 attributes |= BACKGROUND_GREEN;
105 if (attr & FOREGROUND_BLUE)
106 attributes |= BACKGROUND_BLUE;
107
108 if (attr & BACKGROUND_RED)
109 attributes |= FOREGROUND_RED;
110 if (attr & BACKGROUND_GREEN)
111 attributes |= FOREGROUND_GREEN;
112 if (attr & BACKGROUND_BLUE)
113 attributes |= FOREGROUND_BLUE;
114 }
115 SetConsoleTextAttribute(console, attributes);
116}
117
118static void erase_in_line(void)
119{
120 CONSOLE_SCREEN_BUFFER_INFO sbi;
121 DWORD dummy; /* Needed for Windows 7 (or Vista) regression */
122
123 if (!console)
124 return;
125
126 GetConsoleScreenBufferInfo(console, &sbi);
127 FillConsoleOutputCharacterA(console, ' ',
128 sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
129 &dummy);
130 FillConsoleOutputAttribute(console, plain_attr,
131 sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition,
132 &dummy);
133}
134
135static void erase_till_end_of_screen(void)
136{
137 CONSOLE_SCREEN_BUFFER_INFO sbi;
138 DWORD dummy, len;
139
140 if (!console)
141 return;
142
143 GetConsoleScreenBufferInfo(console, &sbi);
144 len = sbi.dwSize.X - sbi.dwCursorPosition.X +
145 sbi.dwSize.X * (sbi.srWindow.Bottom - sbi.dwCursorPosition.Y);
146
147 FillConsoleOutputCharacterA(console, ' ', len, sbi.dwCursorPosition,
148 &dummy);
149 FillConsoleOutputAttribute(console, plain_attr, len, sbi.dwCursorPosition,
150 &dummy);
151}
152
153void reset_screen(void)
154{
155 CONSOLE_SCREEN_BUFFER_INFO sbi;
156 COORD pos;
157 DWORD dummy, len;
158
159 if (!console)
160 return;
161
162 /* move to start of screen buffer and clear it all */
163 GetConsoleScreenBufferInfo(console, &sbi);
164 pos.X = 0;
165 pos.Y = 0;
166 SetConsoleCursorPosition(console, pos);
167 len = sbi.dwSize.X * sbi.dwSize.Y;
168 FillConsoleOutputCharacterA(console, ' ', len, pos, &dummy);
169 FillConsoleOutputAttribute(console, plain_attr, len, pos, &dummy);
170}
171
172void move_cursor_row(int n)
173{
174 CONSOLE_SCREEN_BUFFER_INFO sbi;
175
176 if (!console)
177 return;
178
179 GetConsoleScreenBufferInfo(console, &sbi);
180 sbi.dwCursorPosition.Y += n;
181 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
182}
183
184static void move_cursor_column(int n)
185{
186 CONSOLE_SCREEN_BUFFER_INFO sbi;
187
188 if (!console)
189 return;
190
191 GetConsoleScreenBufferInfo(console, &sbi);
192 sbi.dwCursorPosition.X += n;
193 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
194}
195
196static void move_cursor(int x, int y)
197{
198 COORD pos;
199 CONSOLE_SCREEN_BUFFER_INFO sbi;
200
201 if (!console)
202 return;
203
204 GetConsoleScreenBufferInfo(console, &sbi);
205 pos.X = sbi.srWindow.Left + x;
206 pos.Y = sbi.srWindow.Top + y;
207 SetConsoleCursorPosition(console, pos);
208}
209
210static const char *set_attr(const char *str)
211{
212 const char *func;
213 size_t len = strspn(str, "0123456789;");
214 func = str + len;
215
216 switch (*func) {
217 case 'm':
218 do {
219 long val = strtol(str, (char **)&str, 10);
220 switch (val) {
221 case 0: /* reset */
222 attr = plain_attr;
223 negative = 0;
224 break;
225 case 1: /* bold */
226 attr |= FOREGROUND_INTENSITY;
227 break;
228 case 2: /* faint */
229 case 22: /* normal */
230 attr &= ~FOREGROUND_INTENSITY;
231 break;
232 case 3: /* italic */
233 /* Unsupported */
234 break;
235 case 4: /* underline */
236 case 21: /* double underline */
237 /* Wikipedia says this flag does nothing */
238 /* Furthermore, mingw doesn't define this flag
239 attr |= COMMON_LVB_UNDERSCORE; */
240 break;
241 case 24: /* no underline */
242 /* attr &= ~COMMON_LVB_UNDERSCORE; */
243 break;
244 case 5: /* slow blink */
245 case 6: /* fast blink */
246 /* We don't have blink, but we do have
247 background intensity */
248 attr |= BACKGROUND_INTENSITY;
249 break;
250 case 25: /* no blink */
251 attr &= ~BACKGROUND_INTENSITY;
252 break;
253 case 7: /* negative */
254 negative = 1;
255 break;
256 case 27: /* positive */
257 negative = 0;
258 break;
259 case 8: /* conceal */
260 case 28: /* reveal */
261 /* Unsupported */
262 break;
263 case 30: /* Black */
264 attr &= ~FOREGROUND_ALL;
265 break;
266 case 31: /* Red */
267 attr &= ~FOREGROUND_ALL;
268 attr |= FOREGROUND_RED;
269 break;
270 case 32: /* Green */
271 attr &= ~FOREGROUND_ALL;
272 attr |= FOREGROUND_GREEN;
273 break;
274 case 33: /* Yellow */
275 attr &= ~FOREGROUND_ALL;
276 attr |= FOREGROUND_RED | FOREGROUND_GREEN;
277 break;
278 case 34: /* Blue */
279 attr &= ~FOREGROUND_ALL;
280 attr |= FOREGROUND_BLUE;
281 break;
282 case 35: /* Magenta */
283 attr &= ~FOREGROUND_ALL;
284 attr |= FOREGROUND_RED | FOREGROUND_BLUE;
285 break;
286 case 36: /* Cyan */
287 attr &= ~FOREGROUND_ALL;
288 attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
289 break;
290 case 37: /* White */
291 attr |= FOREGROUND_RED |
292 FOREGROUND_GREEN |
293 FOREGROUND_BLUE;
294 break;
295 case 38: /* Unknown */
296 break;
297 case 39: /* reset */
298 attr &= ~FOREGROUND_ALL;
299 attr |= (plain_attr & FOREGROUND_ALL);
300 break;
301 case 40: /* Black */
302 attr &= ~BACKGROUND_ALL;
303 break;
304 case 41: /* Red */
305 attr &= ~BACKGROUND_ALL;
306 attr |= BACKGROUND_RED;
307 break;
308 case 42: /* Green */
309 attr &= ~BACKGROUND_ALL;
310 attr |= BACKGROUND_GREEN;
311 break;
312 case 43: /* Yellow */
313 attr &= ~BACKGROUND_ALL;
314 attr |= BACKGROUND_RED | BACKGROUND_GREEN;
315 break;
316 case 44: /* Blue */
317 attr &= ~BACKGROUND_ALL;
318 attr |= BACKGROUND_BLUE;
319 break;
320 case 45: /* Magenta */
321 attr &= ~BACKGROUND_ALL;
322 attr |= BACKGROUND_RED | BACKGROUND_BLUE;
323 break;
324 case 46: /* Cyan */
325 attr &= ~BACKGROUND_ALL;
326 attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
327 break;
328 case 47: /* White */
329 attr |= BACKGROUND_RED |
330 BACKGROUND_GREEN |
331 BACKGROUND_BLUE;
332 break;
333 case 48: /* Unknown */
334 break;
335 case 49: /* reset */
336 attr &= ~BACKGROUND_ALL;
337 attr |= (plain_attr & BACKGROUND_ALL);
338 break;
339 default:
340 /* Unsupported code */
341 break;
342 }
343 str++;
344 } while (*(str-1) == ';');
345
346 set_console_attr();
347 break;
348 case 'A': /* up */
349 move_cursor_row(-strtol(str, (char **)&str, 10));
350 break;
351 case 'B': /* down */
352 move_cursor_row(strtol(str, (char **)&str, 10));
353 break;
354 case 'C': /* forward */
355 move_cursor_column(strtol(str, (char **)&str, 10));
356 break;
357 case 'D': /* back */
358 move_cursor_column(-strtol(str, (char **)&str, 10));
359 break;
360 case 'H':
361 if (!len)
362 move_cursor(0, 0);
363 else {
364 int row, col = 1;
365
366 row = strtol(str, (char **)&str, 10);
367 if (*str == ';') {
368 col = strtol(str+1, (char **)&str, 10);
369 }
370 move_cursor(col > 0 ? col-1 : 0, row > 0 ? row-1 : 0);
371 }
372 break;
373 case 'J':
374 erase_till_end_of_screen();
375 break;
376 case 'K':
377 erase_in_line();
378 break;
379 case '?':
380 /* skip this to avoid ugliness when vi is shut down */
381 ++str;
382 while (isdigit(*str))
383 ++str;
384 func = str;
385 break;
386 default:
387 /* Unsupported code */
388 break;
389 }
390
391 return func + 1;
392}
393
394static int ansi_emulate(const char *s, FILE *stream)
395{
396 int rv = 0;
397 const unsigned char *t;
398 char *pos, *str;
399 size_t cur_len;
400 static size_t max_len = 0;
401 static char *mem = NULL;
402
403 /* if no special treatment is required output the string as-is */
404 for ( t=(unsigned char *)s; *t; ++t ) {
405 if ( *t == '\033' || *t > 0x7f ) {
406 break;
407 }
408 }
409
410 if ( *t == '\0' ) {
411 return fputs(s, stream) == EOF ? EOF : strlen(s);
412 }
413
414 /*
415 * Make a writable copy of the string and retain array for reuse.
416 * The test above guarantees that the string length won't be zero
417 * so the array will always be allocated.
418 */
419 cur_len = strlen(s);
420 if ( cur_len > max_len ) {
421 free(mem);
422 mem = strdup(s);
423 max_len = cur_len;
424 }
425 else {
426 strcpy(mem, s);
427 }
428 pos = str = mem;
429
430 while (*pos) {
431 pos = strstr(str, "\033[");
432 if (pos && !skip_ansi_emulation()) {
433 size_t len = pos - str;
434
435 if (len) {
436 *pos = '\0';
437 CharToOem(str, str);
438 if (fputs(str, stream) == EOF)
439 return EOF;
440 rv += len;
441 }
442
443 str = pos + 2;
444 rv += 2;
445
446 if (fflush(stream) == EOF)
447 return EOF;
448
449 pos = (char *)set_attr(str);
450 rv += pos - str;
451 str = pos;
452 } else {
453 rv += strlen(str);
454 CharToOem(str, str);
455 return fputs(str, stream) == EOF ? EOF : rv;
456 }
457 }
458 return rv;
459}
460
461int winansi_putchar(int c)
462{
463 char t = c;
464 char *s = &t;
465
466 if (!is_console(STDOUT_FILENO))
467 return putchar(c);
468
469 CharToOemBuff(s, s, 1);
470 return putchar(t) == EOF ? EOF : c;
471}
472
473int winansi_puts(const char *s)
474{
475 return (winansi_fputs(s, stdout) == EOF || putchar('\n') == EOF) ? EOF : 0;
476}
477
478static sighandler_t sigpipe_handler = SIG_DFL;
479
480#undef signal
481sighandler_t winansi_signal(int signum, sighandler_t handler)
482{
483 sighandler_t old;
484
485 if (signum == SIGPIPE) {
486 old = sigpipe_handler;
487 sigpipe_handler = handler;
488 return old;
489 }
490 return signal(signum, handler);
491}
492
493static void check_pipe_fd(int fd)
494{
495 int error = GetLastError();
496
497 if ((error == ERROR_NO_DATA &&
498 GetFileType((HANDLE)_get_osfhandle(fd)) == FILE_TYPE_PIPE) ||
499 error == ERROR_BROKEN_PIPE) {
500 if (sigpipe_handler == SIG_DFL)
501 exit(128+SIGPIPE);
502 else /* SIG_IGN */
503 errno = EPIPE;
504 }
505}
506
507static void check_pipe(FILE *stream)
508{
509 int fd = fileno(stream);
510
511 if (fd != -1 && ferror(stream)) {
512 check_pipe_fd(fd);
513 }
514}
515
516size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
517{
518 size_t lsize, lmemb, ret;
519 char *str;
520 int rv;
521
522 lsize = MIN(size, nmemb);
523 lmemb = MAX(size, nmemb);
524 if (lsize != 1 || !is_console(fileno(stream))) {
525 SetLastError(0);
526 if ((ret=fwrite(ptr, size, nmemb, stream)) < nmemb)
527 check_pipe(stream);
528 return ret;
529 }
530
531 str = xmalloc(lmemb+1);
532 memcpy(str, ptr, lmemb);
533 str[lmemb] = '\0';
534
535 rv = ansi_emulate(str, stream);
536 free(str);
537
538 return rv == EOF ? 0 : nmemb;
539}
540
541int winansi_fputs(const char *str, FILE *stream)
542{
543 int ret;
544
545 if (!is_console(fileno(stream))) {
546 SetLastError(0);
547 if ((ret=fputs(str, stream)) == EOF)
548 check_pipe(stream);
549 return ret;
550 }
551
552 return ansi_emulate(str, stream) == EOF ? EOF : 0;
553}
554
555#if !defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO
556/*
557 * Prior to Windows 10 vsnprintf was incompatible with the C99 standard.
558 * Implement a replacement using _vsnprintf.
559 */
560int winansi_vsnprintf(char *buf, size_t size, const char *format, va_list list)
561{
562 size_t len;
563 va_list list2;
564
565 va_copy(list2, list);
566 len = _vsnprintf(NULL, 0, format, list2);
567 if (len < 0)
568 return -1;
569
570 _vsnprintf(buf, size, format, list);
571 buf[size-1] = '\0';
572 return len;
573}
574#endif
575
576int winansi_vfprintf(FILE *stream, const char *format, va_list list)
577{
578 int len, rv;
579 char small_buf[256];
580 char *buf = small_buf;
581 va_list cp;
582
583 if (!is_console(fileno(stream)))
584 goto abort;
585
586 va_copy(cp, list);
587 len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
588 va_end(cp);
589
590 if (len > sizeof(small_buf) - 1) {
591 buf = malloc(len + 1);
592 if (!buf)
593 goto abort;
594
595 va_copy(cp, list);
596 len = vsnprintf(buf, len + 1, format, cp);
597 va_end(cp);
598 }
599
600 if (len == -1)
601 goto abort;
602
603 rv = ansi_emulate(buf, stream);
604
605 if (buf != small_buf)
606 free(buf);
607 return rv;
608
609abort:
610 SetLastError(0);
611 if ((rv=vfprintf(stream, format, list)) == EOF)
612 check_pipe(stream);
613 return rv;
614}
615
616int winansi_fprintf(FILE *stream, const char *format, ...)
617{
618 va_list list;
619 int rv;
620
621 va_start(list, format);
622 rv = winansi_vfprintf(stream, format, list);
623 va_end(list);
624
625 return rv;
626}
627
628int winansi_printf(const char *format, ...)
629{
630 va_list list;
631 int rv;
632
633 va_start(list, format);
634 rv = winansi_vfprintf(stdout, format, list);
635 va_end(list);
636
637 return rv;
638}
639
640static int ansi_emulate_write(int fd, const void *buf, size_t count)
641{
642 int rv = 0, i;
643 int special = FALSE, has_null = FALSE;
644 const unsigned char *s = (const unsigned char *)buf;
645 char *pos, *str;
646 size_t len, out_len;
647 static size_t max_len = 0;
648 static char *mem = NULL;
649
650 for ( i=0; i<count; ++i ) {
651 if ( s[i] == '\033' || s[i] > 0x7f ) {
652 special = TRUE;
653 }
654 else if ( !s[i] ) {
655 has_null = TRUE;
656 }
657 }
658
659 /*
660 * If no special treatment is required or the data contains NUL
661 * characters output the string as-is.
662 */
663 if ( !special || has_null ) {
664 return write(fd, buf, count);
665 }
666
667 /* make a writable copy of the data and retain array for reuse */
668 if ( count > max_len ) {
669 free(mem);
670 mem = malloc(count+1);
671 max_len = count;
672 }
673 memcpy(mem, buf, count);
674 mem[count] = '\0';
675 pos = str = mem;
676
677 /* we've checked the data doesn't contain any NULs */
678 while (*pos) {
679 pos = strstr(str, "\033[");
680 if (pos && !skip_ansi_emulation()) {
681 len = pos - str;
682
683 if (len) {
684 CharToOemBuff(str, str, len);
685 out_len = write(fd, str, len);
686 if (out_len == -1)
687 return -1;
688 rv += out_len;
689 }
690
691 str = pos + 2;
692 rv += 2;
693
694 pos = (char *)set_attr(str);
695 rv += pos - str;
696 str = pos;
697 } else {
698 len = strlen(str);
699 CharToOem(str, str);
700 out_len = write(fd, str, len);
701 return (out_len == -1) ? -1 : rv+out_len;
702 }
703 }
704 return rv;
705}
706
707int winansi_write(int fd, const void *buf, size_t count)
708{
709 if (!is_console(fd)) {
710 int ret;
711
712 SetLastError(0);
713 if ((ret=write(fd, buf, count)) == -1) {
714 check_pipe_fd(fd);
715 }
716 return ret;
717 }
718
719 return ansi_emulate_write(fd, buf, count);
720}
721
722int winansi_read(int fd, void *buf, size_t count)
723{
724 int rv;
725
726 rv = mingw_read(fd, buf, count);
727 if (!is_console_in(fd))
728 return rv;
729
730 if ( rv > 0 ) {
731 OemToCharBuff(buf, buf, rv);
732 }
733
734 return rv;
735}
736
737int winansi_getc(FILE *stream)
738{
739 int rv;
740
741 rv = getc(stream);
742 if (!is_console_in(fileno(stream)))
743 return rv;
744
745 if ( rv != EOF ) {
746 unsigned char c = (unsigned char)rv;
747 char *s = (char *)&c;
748 OemToCharBuff(s, s, 1);
749 rv = (int)c;
750 }
751
752 return rv;
753}
754
755/* Ensure that isatty(fd) returns 0 for the NUL device */
756int mingw_isatty(int fd)
757{
758 int result = _isatty(fd);
759
760 if (result) {
761 HANDLE handle = (HANDLE) _get_osfhandle(fd);
762 DWORD mode;
763
764 if (handle == INVALID_HANDLE_VALUE)
765 return 0;
766
767 /* check if its a device (i.e. console, printer, serial port) */
768 if (GetFileType(handle) != FILE_TYPE_CHAR)
769 return 0;
770
771 if (!GetConsoleMode(handle, &mode))
772 return 0;
773 }
774
775 return result;
776}