aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore2
-rw-r--r--Config.in120
-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/dpkg.c29
-rw-r--r--archival/libarchive/decompress_gunzip.c6
-rw-r--r--archival/libarchive/open_transformer.c21
-rw-r--r--archival/libarchive/unsafe_symlink_target.c7
-rw-r--r--archival/rpm.c12
-rw-r--r--archival/tar.c45
-rw-r--r--archival/unzip.c10
-rw-r--r--configs/mingw32_defconfig1185
-rw-r--r--configs/mingw64_defconfig1185
-rw-r--r--console-tools/reset.c15
-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.c12
-rw-r--r--coreutils/od_bloaty.c7
-rw-r--r--coreutils/shred.c10
-rw-r--r--coreutils/stat.c10
-rw-r--r--coreutils/sum.c4
-rw-r--r--coreutils/timeout.c53
-rw-r--r--coreutils/yes.c4
-rw-r--r--debianutils/pipe_progress.c7
-rw-r--r--debianutils/which.c38
-rw-r--r--editors/awk.c21
-rw-r--r--editors/diff.c26
-rw-r--r--editors/sed.c21
-rw-r--r--editors/vi.c66
-rw-r--r--findutils/find.c10
-rw-r--r--findutils/xargs.c95
-rw-r--r--include/bb_archive.h21
-rw-r--r--include/libbb.h69
-rw-r--r--include/mingw.h533
-rw-r--r--include/platform.h41
-rw-r--r--libbb/Kbuild.src56
-rw-r--r--libbb/appletlib.c195
-rw-r--r--libbb/bb_qsort.c12
-rw-r--r--libbb/compare_string_array.c41
-rw-r--r--libbb/copy_file.c6
-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/inode_hash.c5
-rw-r--r--libbb/lineedit.c126
-rw-r--r--libbb/make_directory.c8
-rw-r--r--libbb/messages.c2
-rw-r--r--libbb/mode_string.c2
-rw-r--r--libbb/parse_config.c4
-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--loginutils/su.c1
-rw-r--r--loginutils/suw32.c67
-rw-r--r--miscutils/bbconfig.c1
-rw-r--r--miscutils/dc.c2
-rw-r--r--miscutils/iconv.c1847
-rw-r--r--miscutils/less.c90
-rw-r--r--miscutils/man.c32
-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.c33
-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.c1701
-rw-r--r--shell/math.h2
-rw-r--r--shell/random.c11
-rw-r--r--shell/random.h3
-rw-r--r--shell/shell_common.c57
-rwxr-xr-xtestsuite/busybox.tests19
-rwxr-xr-xtestsuite/runtest18
-rwxr-xr-xtestsuite/sh.tests90
-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.c1742
-rw-r--r--win32/mntent.c86
-rw-r--r--win32/mntent.h21
-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.c756
-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.src27
-rw-r--r--win32/resources/README9
-rw-r--r--win32/resources/aterm.icobin0 -> 15086 bytes
-rw-r--r--win32/resources/dummy.c0
-rw-r--r--win32/resources/resources.rc34
-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.c83
-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/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/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.c119
-rw-r--r--win32/termios.h125
-rw-r--r--win32/uname.c47
-rw-r--r--win32/winansi.c908
166 files changed, 27718 insertions, 179 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 1a726f043..f6ad627b2 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
@@ -368,6 +382,112 @@ config PLATFORM_LINUX
368 #This is automatically selected if any applet or feature requires 382 #This is automatically selected if any applet or feature requires
369 #Linux-specific interfaces. You do not need to select it manually. 383 #Linux-specific interfaces. You do not need to select it manually.
370 384
385comment 'Settings for MINGW32'
386
387config GLOBBING
388 bool "Allow busybox.exe to expand wildcards"
389 default n
390 depends on PLATFORM_MINGW32
391 help
392 In Microsoft Windows expansion of wildcards on the command line
393 ('globbing') is handled by the C runtime while the BusyBox shell
394 does its own wildcard expansion. For best results when using the
395 shell globbing by the C runtime should be turned off. If you want
396 the BusyBox binary to handle wildcard expansion using the C runtime
397 set this to 'Y'.
398
399choice
400 prompt "Random number generator"
401 default FEATURE_PRNG_SHELL
402 depends on PLATFORM_MINGW32
403 help
404 BusyBox on Microsoft Windows uses a pseudo-random number
405 generator to emulate the Linux /dev/urandom device. There
406 are two options:
407 - The shell's built-in PRNG.
408 - Bob Jenkins' ISAAC. This is intended to be a secure PRNG. It's
409 slightly faster than the shell's PRNG but is larger both in terms
410 of code and runtime memory.
411
412config FEATURE_PRNG_SHELL
413 bool "Use shell PRNG"
414
415config FEATURE_PRNG_ISAAC
416 bool "Use ISAAC PRNG"
417
418endchoice
419
420config FEATURE_RESOURCES
421 bool "Include resources in binary"
422 default y
423 depends on PLATFORM_MINGW32
424 help
425 Microsoft Windows applications can contain non-executable resources
426 of various sorts.
427
428config FEATURE_VERSIONINFO
429 bool "Include version information in binary (1.0 kb)"
430 default y
431 depends on FEATURE_RESOURCES
432 help
433 Include version information in the application.
434
435config FEATURE_ICON
436 bool "Include application icon in binary"
437 default y
438 depends on FEATURE_RESOURCES
439 help
440 Microsoft Windows applications can contain icons which are used in
441 various places in the user interface. Each icon adds 15 Kbytes to
442 the size of the binary.
443
444choice
445 prompt "Application icon"
446 default FEATURE_ICON_ALL
447 depends on FEATURE_ICON
448
449config FEATURE_ICON_ATERM
450 bool "Adwaita terminal"
451
452config FEATURE_ICON_STERM
453 bool "Adwaita terminal (symbolic)"
454
455config FEATURE_ICON_ALL
456 bool "All available icons"
457
458endchoice
459
460config FEATURE_EURO
461 bool "Support the euro currency symbol (0.5 kb)"
462 default y
463 depends on PLATFORM_MINGW32
464 help
465 Support the entry and display of the euro currency symbol. This
466 requires the OEM code page to be 858. If the OEM code page of
467 the console is 850 when BusyBox starts it's changed to 858.
468
469config FEATURE_EXTRA_FILE_DATA
470 bool "Read additional file metadata (2.1 kb)"
471 default y
472 depends on PLATFORM_MINGW32
473 help
474 Read additional file metadata: device id, inode number and number
475 of hard links. This may slow down some file operations but it
476 permits extra features such as warning of attempts to copy a file
477 onto itself or to store a tar archive in itself. Also try to
478 determine the ownership of files so that, for example, 'ls' can
479 distinguish files belonging to the current user.
480
481config FEATURE_READLINK2
482 bool "Read the contents of symbolic links (1.1 kb)"
483 default y
484 depends on PLATFORM_MINGW32
485 help
486 Implement the readlink(2) system call to allow applets to read
487 the contents of symbolic links. With this feature ls and stat
488 can display the target of symbolic links and it makes sense to
489 enable the readlink applet.
490
371comment 'Build Options' 491comment 'Build Options'
372 492
373config STATIC 493config 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..b8b6fbad7
--- /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 and mingw32-windows-default-manifest (32-bit build) or mingw64-gcc and mingw64-windows-default-manifest (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/dpkg.c b/archival/dpkg.c
index ddb5daf09..08f15ad44 100644
--- a/archival/dpkg.c
+++ b/archival/dpkg.c
@@ -149,6 +149,11 @@ enum edge_type_e {
149 EDGE_RECOMMENDS = 13, 149 EDGE_RECOMMENDS = 13,
150 EDGE_ENHANCES = 15 150 EDGE_ENHANCES = 15
151}; 151};
152#if ENABLE_PLATFORM_MINGW32
153#undef VER_EQUAL
154#undef VER_LESS
155#undef VER_LESS_EQUAL
156#endif
152enum operator_e { 157enum operator_e {
153 VER_NULL = 0, 158 VER_NULL = 0,
154 VER_EQUAL = 1, 159 VER_EQUAL = 1,
@@ -1747,6 +1752,10 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
1747 int state_status; 1752 int state_status;
1748 int status_num; 1753 int status_num;
1749 int i; 1754 int i;
1755#if ENABLE_PLATFORM_MINGW32
1756 char **ptr, *path;
1757 int fd;
1758#endif
1750#if ENABLE_LONG_OPTS 1759#if ENABLE_LONG_OPTS
1751 static const char dpkg_longopts[] ALIGN1 = 1760 static const char dpkg_longopts[] ALIGN1 =
1752// FIXME: we use -C non-compatibly, should be: 1761// FIXME: we use -C non-compatibly, should be:
@@ -1791,6 +1800,26 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
1791 bb_show_usage(); 1800 bb_show_usage();
1792 } 1801 }
1793 1802
1803#if ENABLE_PLATFORM_MINGW32
1804 if (opt & OPT_install) {
1805 /* add system drive prefix to filenames, if necessary */
1806 for (ptr = argv; *ptr; ++ptr) {
1807 *ptr = xabsolute_path(*ptr);
1808 }
1809 }
1810
1811 chdir_system_drive();
1812
1813 /* initialise data store */
1814 path = xstrdup("/var/lib/dpkg/info");
1815 bb_make_directory(path, -1, FILEUTILS_RECUR);
1816 free(path);
1817
1818 fd = open("/var/lib/dpkg/status", O_RDWR|O_CREAT, 0666);
1819 if (fd >= 0)
1820 xclose(fd);
1821#endif
1822
1794/* puts("(Reading database ... xxxxx files and directories installed.)"); */ 1823/* puts("(Reading database ... xxxxx files and directories installed.)"); */
1795 index_status_file("/var/lib/dpkg/status"); 1824 index_status_file("/var/lib/dpkg/status");
1796 1825
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 a90f42a45..3a0fc4712 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..ff96991f5 100644
--- a/archival/libarchive/unsafe_symlink_target.c
+++ b/archival/libarchive/unsafe_symlink_target.c
@@ -10,6 +10,7 @@ void FAST_FUNC create_or_remember_link(llist_t **link_placeholders,
10 const char *linkname, 10 const char *linkname,
11 int hard_link) 11 int hard_link)
12{ 12{
13#if !ENABLE_PLATFORM_MINGW32
13 if (hard_link || target[0] == '/' || strstr(target, "..")) { 14 if (hard_link || target[0] == '/' || strstr(target, "..")) {
14 llist_add_to_end(link_placeholders, 15 llist_add_to_end(link_placeholders,
15 xasprintf("%c%s%c%s", hard_link, linkname, '\0', target) 16 xasprintf("%c%s%c%s", hard_link, linkname, '\0', target)
@@ -22,8 +23,13 @@ void FAST_FUNC create_or_remember_link(llist_t **link_placeholders,
22 "sym", linkname, target 23 "sym", linkname, target
23 ); 24 );
24 } 25 }
26#else
27 /* symlink isn't implemented for WIN32, just issue a warning */
28 bb_perror_msg("can't create %slink '%s' to '%s'", "sym", linkname, target);
29#endif
25} 30}
26 31
32#if !ENABLE_PLATFORM_MINGW32
27void FAST_FUNC create_links_from_list(llist_t *list) 33void FAST_FUNC create_links_from_list(llist_t *list)
28{ 34{
29 while (list) { 35 while (list) {
@@ -40,3 +46,4 @@ void FAST_FUNC create_links_from_list(llist_t *list)
40 list = list->link; 46 list = list->link;
41 } 47 }
42} 48}
49#endif
diff --git a/archival/rpm.c b/archival/rpm.c
index 95b2531e8..c9e8785e2 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}
@@ -294,6 +303,9 @@ static void extract_cpio(int fd, const char *source_rpm)
294 303
295 if (source_rpm != NULL) { 304 if (source_rpm != NULL) {
296 /* Binary rpm (it was built from some SRPM), install to root */ 305 /* Binary rpm (it was built from some SRPM), install to root */
306#if ENABLE_PLATFORM_MINGW32
307 if (chdir_system_drive())
308#endif
297 xchdir("/"); 309 xchdir("/");
298 } /* else: SRPM, install to current dir */ 310 } /* else: SRPM, install to current dir */
299 311
diff --git a/archival/tar.c b/archival/tar.c
index 3ef89fb0a..54961ff07 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_POSIX || ENABLE_FEATURE_EXTRA_FILE_DATA
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,15 +529,21 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
527 } 529 }
528 } 530 }
529 531
532#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_EXTRA_FILE_DATA
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 */
533 if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev 536 if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev
534 && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino 537 && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino
538# if ENABLE_FEATURE_EXTRA_FILE_DATA
539 /* ignore invalid inode numbers */
540 && statbuf->st_ino != 0
541# endif
535 ) { 542 ) {
536 bb_error_msg("%s: file is the archive; skipping", fileName); 543 bb_error_msg("%s: file is the archive; skipping", fileName);
537 return TRUE; 544 return TRUE;
538 } 545 }
546#endif
539 547
540 if (exclude_file(tbInfo->excludeList, header_name)) 548 if (exclude_file(tbInfo->excludeList, header_name))
541 return SKIP; 549 return SKIP;
@@ -592,7 +600,8 @@ static int FAST_FUNC writeFileToTarball(const char *fileName, struct stat *statb
592 return TRUE; 600 return TRUE;
593} 601}
594 602
595# if SEAMLESS_COMPRESSION 603#if SEAMLESS_COMPRESSION
604#if !ENABLE_PLATFORM_MINGW32
596/* Don't inline: vfork scares gcc and pessimizes code */ 605/* Don't inline: vfork scares gcc and pessimizes code */
597static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) 606static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
598{ 607{
@@ -661,7 +670,28 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
661 bb_perror_msg_and_die("can't execute '%s'", gzip); 670 bb_perror_msg_and_die("can't execute '%s'", gzip);
662 } 671 }
663} 672}
664# endif /* SEAMLESS_COMPRESSION */ 673#else
674static pid_t vfork_compressor(int tar_fd, const char *gzip)
675{
676 char *cmd;
677 int fd1;
678 pid_t pid;
679
680 if (find_applet_by_name(gzip) >= 0) {
681 cmd = xasprintf("%s --busybox %s -cf -", bb_busybox_exec_path, gzip);
682 }
683 else {
684 cmd = xasprintf("%s -cf -", gzip);
685 }
686 if ( (fd1=mingw_popen_fd(cmd, "w", tar_fd, &pid)) == -1 ) {
687 bb_perror_msg_and_die("can't execute '%s'", gzip);
688 }
689 free(cmd);
690 xmove_fd(fd1, tar_fd);
691 return pid;
692}
693#endif /* ENABLE_PLATFORM_MINGW32 */
694#endif /* SEAMLESS_COMPRESSION */
665 695
666 696
667# if !SEAMLESS_COMPRESSION 697# if !SEAMLESS_COMPRESSION
@@ -677,17 +707,20 @@ static NOINLINE int writeTarFile(
677 const char *gzip) 707 const char *gzip)
678{ 708{
679 int errorFlag = FALSE; 709 int errorFlag = FALSE;
710 IF_PLATFORM_MINGW32(pid_t pid = 0;)
680 711
681 /*tbInfo->hlInfoHead = NULL; - already is */ 712 /*tbInfo->hlInfoHead = NULL; - already is */
682 713
714#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_EXTRA_FILE_DATA
683 /* Store the stat info for the tarball's file, so 715 /* Store the stat info for the tarball's file, so
684 * can avoid including the tarball into itself.... */ 716 * can avoid including the tarball into itself.... */
685 xfstat(tbInfo->tarFd, &tbInfo->tarFileStatBuf, "can't stat tar file"); 717 xfstat(tbInfo->tarFd, &tbInfo->tarFileStatBuf, "can't stat tar file");
718#endif
686 719
687# if SEAMLESS_COMPRESSION 720# if SEAMLESS_COMPRESSION
688 if (gzip) 721 if (gzip)
689 vfork_compressor(tbInfo->tarFd, gzip); 722 IF_PLATFORM_MINGW32(pid = )vfork_compressor(tbInfo->tarFd, gzip);
690# endif 723#endif
691 724
692 /* Read the directory/files and iterate over them one at a time */ 725 /* Read the directory/files and iterate over them one at a time */
693 while (filelist) { 726 while (filelist) {
@@ -720,7 +753,11 @@ static NOINLINE int writeTarFile(
720# if SEAMLESS_COMPRESSION 753# if SEAMLESS_COMPRESSION
721 if (gzip) { 754 if (gzip) {
722 int status; 755 int status;
756#if !ENABLE_PLATFORM_MINGW32
723 if (safe_waitpid(-1, &status, 0) == -1) 757 if (safe_waitpid(-1, &status, 0) == -1)
758#else
759 if (safe_waitpid(pid, &status, 0) == -1)
760#endif
724 bb_perror_msg("waitpid"); 761 bb_perror_msg("waitpid");
725 else if (!WIFEXITED(status) || WEXITSTATUS(status)) 762 else if (!WIFEXITED(status) || WEXITSTATUS(status))
726 /* gzip was killed or has exited with nonzero! */ 763 /* gzip was killed or has exited with nonzero! */
diff --git a/archival/unzip.c b/archival/unzip.c
index 466794031..4e8ed0eae 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__)
@@ -345,6 +348,9 @@ static void unzip_create_leading_dirs(const char *fn)
345} 348}
346 349
347#if ENABLE_FEATURE_UNZIP_CDF 350#if ENABLE_FEATURE_UNZIP_CDF
351#if ENABLE_PLATFORM_MINGW32
352#define unzip_extract_symlink(s, z, d) unzip_extract_symlink(z, d)
353#endif
348static void unzip_extract_symlink(llist_t **symlink_placeholders, 354static void unzip_extract_symlink(llist_t **symlink_placeholders,
349 zip_header_t *zip, 355 zip_header_t *zip,
350 const char *dst_fn) 356 const char *dst_fn)
@@ -489,7 +495,7 @@ int unzip_main(int argc, char **argv)
489 llist_t *zaccept = NULL; 495 llist_t *zaccept = NULL;
490 llist_t *zreject = NULL; 496 llist_t *zreject = NULL;
491 char *base_dir = NULL; 497 char *base_dir = NULL;
492#if ENABLE_FEATURE_UNZIP_CDF 498#if ENABLE_FEATURE_UNZIP_CDF && !ENABLE_PLATFORM_MINGW32
493 llist_t *symlink_placeholders = NULL; 499 llist_t *symlink_placeholders = NULL;
494#endif 500#endif
495 int i; 501 int i;
@@ -596,7 +602,7 @@ int unzip_main(int argc, char **argv)
596 } 602 }
597 } 603 }
598 604
599#ifndef __GLIBC__ 605#if !defined(__GLIBC__) && !ENABLE_PLATFORM_MINGW32
600 /* 606 /*
601 * This code is needed for non-GNU getopt 607 * This code is needed for non-GNU getopt
602 * which doesn't understand "-" in option string. 608 * which doesn't understand "-" in option string.
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig
new file mode 100644
index 000000000..34213e05f
--- /dev/null
+++ b/configs/mingw32_defconfig
@@ -0,0 +1,1185 @@
1#
2# Automatically generated make config: don't edit
3# Busybox version: 1.31.0.git
4# Mon Apr 8 21:26:25 2019
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_ICON=y
47# CONFIG_FEATURE_ICON_ATERM is not set
48# CONFIG_FEATURE_ICON_STERM is not set
49CONFIG_FEATURE_ICON_ALL=y
50CONFIG_FEATURE_EURO=y
51CONFIG_FEATURE_EXTRA_FILE_DATA=y
52CONFIG_FEATURE_READLINK2=y
53
54#
55# Build Options
56#
57# CONFIG_STATIC is not set
58# CONFIG_PIE is not set
59# CONFIG_NOMMU is not set
60# CONFIG_BUILD_LIBBUSYBOX is not set
61# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set
62# CONFIG_FEATURE_INDIVIDUAL is not set
63# CONFIG_FEATURE_SHARED_BUSYBOX is not set
64CONFIG_CROSS_COMPILER_PREFIX="i686-w64-mingw32-"
65CONFIG_SYSROOT=""
66CONFIG_EXTRA_CFLAGS=""
67CONFIG_EXTRA_LDFLAGS=""
68CONFIG_EXTRA_LDLIBS=""
69CONFIG_USE_PORTABLE_CODE=y
70CONFIG_STACK_OPTIMIZATION_386=y
71
72#
73# Installation Options ("make install" behavior)
74#
75CONFIG_INSTALL_APPLET_SYMLINKS=y
76# CONFIG_INSTALL_APPLET_HARDLINKS is not set
77# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
78# CONFIG_INSTALL_APPLET_DONT is not set
79# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
80# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
81# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
82CONFIG_PREFIX=""
83
84#
85# Debugging Options
86#
87CONFIG_DEBUG=y
88# CONFIG_DEBUG_PESSIMIZE is not set
89# CONFIG_DEBUG_SANITIZE is not set
90# CONFIG_UNIT_TEST is not set
91# CONFIG_WERROR is not set
92CONFIG_NO_DEBUG_LIB=y
93# CONFIG_DMALLOC is not set
94# CONFIG_EFENCE is not set
95
96#
97# Library Tuning
98#
99# CONFIG_FEATURE_USE_BSS_TAIL is not set
100CONFIG_FLOAT_DURATION=y
101CONFIG_FEATURE_RTMINMAX=y
102CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS=y
103CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
104# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
105# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
106CONFIG_PASSWORD_MINLEN=6
107CONFIG_MD5_SMALL=1
108CONFIG_SHA3_SMALL=1
109# CONFIG_FEATURE_FAST_TOP is not set
110# CONFIG_FEATURE_ETC_NETWORKS is not set
111# CONFIG_FEATURE_ETC_SERVICES is not set
112CONFIG_FEATURE_EDITING=y
113CONFIG_FEATURE_EDITING_MAX_LEN=1024
114CONFIG_FEATURE_EDITING_VI=y
115CONFIG_FEATURE_EDITING_HISTORY=255
116CONFIG_FEATURE_EDITING_SAVEHISTORY=y
117# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
118CONFIG_FEATURE_REVERSE_SEARCH=y
119CONFIG_FEATURE_TAB_COMPLETION=y
120CONFIG_FEATURE_USERNAME_COMPLETION=y
121CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
122# CONFIG_FEATURE_EDITING_WINCH is not set
123# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
124# CONFIG_LOCALE_SUPPORT is not set
125# CONFIG_UNICODE_SUPPORT is not set
126# CONFIG_UNICODE_USING_LOCALE is not set
127# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
128CONFIG_SUBST_WCHAR=0
129CONFIG_LAST_SUPPORTED_WCHAR=0
130# CONFIG_UNICODE_COMBINING_WCHARS is not set
131# CONFIG_UNICODE_WIDE_WCHARS is not set
132# CONFIG_UNICODE_BIDI_SUPPORT is not set
133# CONFIG_UNICODE_NEUTRAL_TABLE is not set
134# CONFIG_UNICODE_PRESERVE_BROKEN is not set
135CONFIG_FEATURE_NON_POSIX_CP=y
136# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
137# CONFIG_FEATURE_USE_SENDFILE is not set
138CONFIG_FEATURE_COPYBUF_KB=4
139CONFIG_FEATURE_SKIP_ROOTFS=y
140# CONFIG_MONOTONIC_SYSCALL is not set
141CONFIG_IOCTL_HEX2STR_ERROR=y
142# CONFIG_FEATURE_HWIB is not set
143
144#
145# Applets
146#
147
148#
149# Archival Utilities
150#
151CONFIG_FEATURE_SEAMLESS_XZ=y
152CONFIG_FEATURE_SEAMLESS_LZMA=y
153CONFIG_FEATURE_SEAMLESS_BZ2=y
154CONFIG_FEATURE_SEAMLESS_GZ=y
155CONFIG_FEATURE_SEAMLESS_Z=y
156CONFIG_AR=y
157CONFIG_FEATURE_AR_LONG_FILENAMES=y
158CONFIG_FEATURE_AR_CREATE=y
159CONFIG_UNCOMPRESS=y
160CONFIG_GUNZIP=y
161CONFIG_ZCAT=y
162CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
163CONFIG_BUNZIP2=y
164CONFIG_BZCAT=y
165CONFIG_UNLZMA=y
166CONFIG_LZCAT=y
167CONFIG_LZMA=y
168CONFIG_UNXZ=y
169CONFIG_XZCAT=y
170CONFIG_XZ=y
171CONFIG_BZIP2=y
172CONFIG_BZIP2_SMALL=8
173CONFIG_FEATURE_BZIP2_DECOMPRESS=y
174CONFIG_CPIO=y
175CONFIG_FEATURE_CPIO_O=y
176# CONFIG_FEATURE_CPIO_P is not set
177CONFIG_DPKG=y
178CONFIG_DPKG_DEB=y
179CONFIG_GZIP=y
180CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
181CONFIG_GZIP_FAST=2
182CONFIG_FEATURE_GZIP_LEVELS=y
183CONFIG_FEATURE_GZIP_DECOMPRESS=y
184CONFIG_LZOP=y
185CONFIG_UNLZOP=y
186CONFIG_LZOPCAT=y
187# CONFIG_LZOP_COMPR_HIGH is not set
188CONFIG_RPM=y
189CONFIG_RPM2CPIO=y
190CONFIG_TAR=y
191CONFIG_FEATURE_TAR_LONG_OPTIONS=y
192CONFIG_FEATURE_TAR_CREATE=y
193CONFIG_FEATURE_TAR_AUTODETECT=y
194CONFIG_FEATURE_TAR_FROM=y
195CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
196# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
197CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
198# CONFIG_FEATURE_TAR_TO_COMMAND is not set
199# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
200CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
201# CONFIG_FEATURE_TAR_SELINUX is not set
202CONFIG_UNZIP=y
203CONFIG_FEATURE_UNZIP_CDF=y
204CONFIG_FEATURE_UNZIP_BZIP2=y
205CONFIG_FEATURE_UNZIP_LZMA=y
206CONFIG_FEATURE_UNZIP_XZ=y
207CONFIG_FEATURE_LZMA_FAST=y
208
209#
210# Coreutils
211#
212CONFIG_BASENAME=y
213CONFIG_CAT=y
214CONFIG_FEATURE_CATN=y
215CONFIG_FEATURE_CATV=y
216# CONFIG_CHGRP is not set
217CONFIG_CHMOD=y
218# CONFIG_CHOWN is not set
219# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
220# CONFIG_CHROOT is not set
221CONFIG_CKSUM=y
222CONFIG_COMM=y
223CONFIG_CP=y
224CONFIG_FEATURE_CP_LONG_OPTIONS=y
225# CONFIG_FEATURE_CP_REFLINK is not set
226CONFIG_CUT=y
227CONFIG_DATE=y
228CONFIG_FEATURE_DATE_ISOFMT=y
229# CONFIG_FEATURE_DATE_NANO is not set
230CONFIG_FEATURE_DATE_COMPAT=y
231CONFIG_DD=y
232# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set
233# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
234CONFIG_FEATURE_DD_IBS_OBS=y
235CONFIG_FEATURE_DD_STATUS=y
236CONFIG_DF=y
237# CONFIG_FEATURE_DF_FANCY is not set
238CONFIG_DIRNAME=y
239CONFIG_DOS2UNIX=y
240CONFIG_UNIX2DOS=y
241CONFIG_DU=y
242CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
243CONFIG_ECHO=y
244CONFIG_FEATURE_FANCY_ECHO=y
245CONFIG_ENV=y
246CONFIG_EXPAND=y
247CONFIG_UNEXPAND=y
248CONFIG_EXPR=y
249CONFIG_EXPR_MATH_SUPPORT_64=y
250CONFIG_FACTOR=y
251CONFIG_FALSE=y
252CONFIG_FOLD=y
253CONFIG_FSYNC=y
254CONFIG_HEAD=y
255CONFIG_FEATURE_FANCY_HEAD=y
256# CONFIG_HOSTID is not set
257CONFIG_ID=y
258CONFIG_GROUPS=y
259# CONFIG_INSTALL is not set
260# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
261CONFIG_LINK=y
262CONFIG_LN=y
263CONFIG_LOGNAME=y
264CONFIG_LS=y
265CONFIG_FEATURE_LS_FILETYPES=y
266CONFIG_FEATURE_LS_FOLLOWLINKS=y
267CONFIG_FEATURE_LS_RECURSIVE=y
268CONFIG_FEATURE_LS_WIDTH=y
269CONFIG_FEATURE_LS_SORTFILES=y
270CONFIG_FEATURE_LS_TIMESTAMPS=y
271CONFIG_FEATURE_LS_USERNAME=y
272CONFIG_FEATURE_LS_COLOR=y
273CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
274CONFIG_MD5SUM=y
275CONFIG_SHA1SUM=y
276CONFIG_SHA256SUM=y
277CONFIG_SHA512SUM=y
278CONFIG_SHA3SUM=y
279
280#
281# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
282#
283CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
284CONFIG_MKDIR=y
285# CONFIG_MKFIFO is not set
286# CONFIG_MKNOD is not set
287CONFIG_MKTEMP=y
288CONFIG_MV=y
289# CONFIG_NICE is not set
290CONFIG_NL=y
291# CONFIG_NOHUP is not set
292# CONFIG_NPROC is not set
293CONFIG_OD=y
294CONFIG_PASTE=y
295CONFIG_PRINTENV=y
296CONFIG_PRINTF=y
297CONFIG_PWD=y
298CONFIG_READLINK=y
299CONFIG_FEATURE_READLINK_FOLLOW=y
300CONFIG_REALPATH=y
301CONFIG_RM=y
302CONFIG_RMDIR=y
303CONFIG_SEQ=y
304CONFIG_SHRED=y
305CONFIG_SHUF=y
306CONFIG_SLEEP=y
307CONFIG_FEATURE_FANCY_SLEEP=y
308CONFIG_SORT=y
309CONFIG_FEATURE_SORT_BIG=y
310# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set
311CONFIG_SPLIT=y
312CONFIG_FEATURE_SPLIT_FANCY=y
313CONFIG_STAT=y
314CONFIG_FEATURE_STAT_FORMAT=y
315CONFIG_FEATURE_STAT_FILESYSTEM=y
316# CONFIG_STTY is not set
317CONFIG_SUM=y
318# CONFIG_SYNC is not set
319# CONFIG_FEATURE_SYNC_FANCY is not set
320CONFIG_TAC=y
321CONFIG_TAIL=y
322CONFIG_FEATURE_FANCY_TAIL=y
323CONFIG_TEE=y
324CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
325CONFIG_TEST=y
326CONFIG_TEST1=y
327CONFIG_TEST2=y
328CONFIG_FEATURE_TEST_64=y
329CONFIG_TIMEOUT=y
330CONFIG_TOUCH=y
331# CONFIG_FEATURE_TOUCH_NODEREF is not set
332CONFIG_FEATURE_TOUCH_SUSV3=y
333CONFIG_TR=y
334CONFIG_FEATURE_TR_CLASSES=y
335CONFIG_FEATURE_TR_EQUIV=y
336CONFIG_TRUE=y
337CONFIG_TRUNCATE=y
338# CONFIG_TTY is not set
339CONFIG_UNAME=y
340CONFIG_UNAME_OSNAME="MS/Windows"
341CONFIG_BB_ARCH=y
342CONFIG_UNIQ=y
343CONFIG_UNLINK=y
344CONFIG_USLEEP=y
345CONFIG_UUDECODE=y
346CONFIG_BASE64=y
347CONFIG_UUENCODE=y
348CONFIG_WC=y
349CONFIG_FEATURE_WC_LARGE=y
350CONFIG_WHOAMI=y
351# CONFIG_WHO is not set
352# CONFIG_W is not set
353# CONFIG_USERS is not set
354CONFIG_YES=y
355
356#
357# Common options
358#
359CONFIG_FEATURE_VERBOSE=y
360
361#
362# Common options for cp and mv
363#
364# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
365
366#
367# Common options for df, du, ls
368#
369CONFIG_FEATURE_HUMAN_READABLE=y
370
371#
372# Console Utilities
373#
374# CONFIG_CHVT is not set
375CONFIG_CLEAR=y
376# CONFIG_DEALLOCVT is not set
377# CONFIG_DUMPKMAP is not set
378# CONFIG_FGCONSOLE is not set
379# CONFIG_KBD_MODE is not set
380# CONFIG_LOADFONT is not set
381# CONFIG_SETFONT is not set
382# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
383CONFIG_DEFAULT_SETFONT_DIR=""
384# CONFIG_FEATURE_LOADFONT_PSF2 is not set
385# CONFIG_FEATURE_LOADFONT_RAW is not set
386# CONFIG_LOADKMAP is not set
387# CONFIG_OPENVT is not set
388CONFIG_RESET=y
389# CONFIG_RESIZE is not set
390# CONFIG_FEATURE_RESIZE_PRINT is not set
391# CONFIG_SETCONSOLE is not set
392# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
393# CONFIG_SETKEYCODES is not set
394# CONFIG_SETLOGCONS is not set
395# CONFIG_SHOWKEY is not set
396
397#
398# Debian Utilities
399#
400CONFIG_PIPE_PROGRESS=y
401# CONFIG_RUN_PARTS is not set
402# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
403# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
404# CONFIG_START_STOP_DAEMON is not set
405# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
406# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
407CONFIG_WHICH=y
408
409#
410# klibc-utils
411#
412# CONFIG_MINIPS is not set
413# CONFIG_NUKE is not set
414# CONFIG_RESUME is not set
415# CONFIG_RUN_INIT is not set
416
417#
418# Editors
419#
420CONFIG_AWK=y
421CONFIG_FEATURE_AWK_LIBM=y
422CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
423CONFIG_CMP=y
424CONFIG_DIFF=y
425CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
426CONFIG_FEATURE_DIFF_DIR=y
427CONFIG_ED=y
428CONFIG_PATCH=y
429CONFIG_SED=y
430CONFIG_VI=y
431CONFIG_FEATURE_VI_MAX_LEN=4096
432CONFIG_FEATURE_VI_8BIT=y
433CONFIG_FEATURE_VI_COLON=y
434CONFIG_FEATURE_VI_YANKMARK=y
435CONFIG_FEATURE_VI_SEARCH=y
436# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
437# CONFIG_FEATURE_VI_USE_SIGNALS is not set
438CONFIG_FEATURE_VI_DOT_CMD=y
439CONFIG_FEATURE_VI_READONLY=y
440CONFIG_FEATURE_VI_SETOPTS=y
441CONFIG_FEATURE_VI_SET=y
442CONFIG_FEATURE_VI_WIN_RESIZE=y
443# CONFIG_FEATURE_VI_ASK_TERMINAL is not set
444CONFIG_FEATURE_VI_UNDO=y
445CONFIG_FEATURE_VI_UNDO_QUEUE=y
446CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
447CONFIG_FEATURE_ALLOW_EXEC=y
448
449#
450# Finding Utilities
451#
452CONFIG_FIND=y
453CONFIG_FEATURE_FIND_PRINT0=y
454CONFIG_FEATURE_FIND_MTIME=y
455CONFIG_FEATURE_FIND_MMIN=y
456CONFIG_FEATURE_FIND_PERM=y
457CONFIG_FEATURE_FIND_TYPE=y
458CONFIG_FEATURE_FIND_EXECUTABLE=y
459CONFIG_FEATURE_FIND_XDEV=y
460CONFIG_FEATURE_FIND_MAXDEPTH=y
461CONFIG_FEATURE_FIND_NEWER=y
462CONFIG_FEATURE_FIND_INUM=y
463CONFIG_FEATURE_FIND_EXEC=y
464CONFIG_FEATURE_FIND_EXEC_PLUS=y
465# CONFIG_FEATURE_FIND_USER is not set
466# CONFIG_FEATURE_FIND_GROUP is not set
467CONFIG_FEATURE_FIND_NOT=y
468CONFIG_FEATURE_FIND_DEPTH=y
469CONFIG_FEATURE_FIND_PAREN=y
470CONFIG_FEATURE_FIND_SIZE=y
471CONFIG_FEATURE_FIND_PRUNE=y
472CONFIG_FEATURE_FIND_QUIT=y
473CONFIG_FEATURE_FIND_DELETE=y
474CONFIG_FEATURE_FIND_PATH=y
475CONFIG_FEATURE_FIND_REGEX=y
476# CONFIG_FEATURE_FIND_CONTEXT is not set
477CONFIG_FEATURE_FIND_LINKS=y
478CONFIG_GREP=y
479CONFIG_EGREP=y
480CONFIG_FGREP=y
481CONFIG_FEATURE_GREP_CONTEXT=y
482CONFIG_XARGS=y
483CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
484CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
485CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
486CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
487CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
488CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y
489CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y
490
491#
492# Init Utilities
493#
494# CONFIG_BOOTCHARTD is not set
495# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
496# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
497# CONFIG_HALT is not set
498# CONFIG_POWEROFF is not set
499# CONFIG_REBOOT is not set
500# CONFIG_FEATURE_WAIT_FOR_INIT is not set
501# CONFIG_FEATURE_CALL_TELINIT is not set
502CONFIG_TELINIT_PATH=""
503# CONFIG_INIT is not set
504# CONFIG_LINUXRC is not set
505# CONFIG_FEATURE_USE_INITTAB is not set
506# CONFIG_FEATURE_KILL_REMOVED is not set
507CONFIG_FEATURE_KILL_DELAY=0
508# CONFIG_FEATURE_INIT_SCTTY is not set
509# CONFIG_FEATURE_INIT_SYSLOG is not set
510# CONFIG_FEATURE_INIT_QUIET is not set
511# CONFIG_FEATURE_INIT_COREDUMPS is not set
512CONFIG_INIT_TERMINAL_TYPE=""
513# CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set
514
515#
516# Login/Password Management Utilities
517#
518# CONFIG_FEATURE_SHADOWPASSWDS is not set
519# CONFIG_USE_BB_PWD_GRP is not set
520# CONFIG_USE_BB_SHADOW is not set
521# CONFIG_USE_BB_CRYPT is not set
522# CONFIG_USE_BB_CRYPT_SHA is not set
523# CONFIG_ADDGROUP is not set
524# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
525# CONFIG_ADD_SHELL is not set
526# CONFIG_REMOVE_SHELL is not set
527# CONFIG_ADDUSER is not set
528# CONFIG_FEATURE_CHECK_NAMES is not set
529CONFIG_LAST_ID=0
530CONFIG_FIRST_SYSTEM_ID=0
531CONFIG_LAST_SYSTEM_ID=0
532# CONFIG_CHPASSWD is not set
533CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
534# CONFIG_CRYPTPW is not set
535# CONFIG_MKPASSWD is not set
536# CONFIG_DELUSER is not set
537# CONFIG_DELGROUP is not set
538# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
539# CONFIG_GETTY is not set
540# CONFIG_LOGIN is not set
541# CONFIG_LOGIN_SESSION_AS_CHILD is not set
542# CONFIG_LOGIN_SCRIPTS is not set
543# CONFIG_FEATURE_NOLOGIN is not set
544# CONFIG_FEATURE_SECURETTY is not set
545# CONFIG_PASSWD is not set
546# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
547# CONFIG_SU is not set
548# CONFIG_FEATURE_SU_SYSLOG is not set
549# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
550# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set
551# CONFIG_SULOGIN is not set
552CONFIG_SUW32=y
553# CONFIG_VLOCK is not set
554
555#
556# Linux Ext2 FS Progs
557#
558# CONFIG_CHATTR is not set
559# CONFIG_FSCK is not set
560# CONFIG_LSATTR is not set
561# CONFIG_TUNE2FS is not set
562
563#
564# Linux Module Utilities
565#
566# CONFIG_MODPROBE_SMALL is not set
567# CONFIG_DEPMOD is not set
568# CONFIG_INSMOD is not set
569# CONFIG_LSMOD is not set
570# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
571# CONFIG_MODINFO is not set
572# CONFIG_MODPROBE is not set
573# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
574# CONFIG_RMMOD is not set
575
576#
577# Options common to multiple modutils
578#
579# CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set
580# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
581# CONFIG_FEATURE_2_4_MODULES is not set
582# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
583# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
584# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
585# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
586# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
587# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
588# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
589# CONFIG_FEATURE_MODUTILS_ALIAS is not set
590# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
591CONFIG_DEFAULT_MODULES_DIR=""
592CONFIG_DEFAULT_DEPMOD_FILE=""
593
594#
595# Linux System Utilities
596#
597# CONFIG_ACPID is not set
598# CONFIG_FEATURE_ACPID_COMPAT is not set
599# CONFIG_BLKDISCARD is not set
600# CONFIG_BLKID is not set
601# CONFIG_FEATURE_BLKID_TYPE is not set
602# CONFIG_BLOCKDEV is not set
603CONFIG_CAL=y
604# CONFIG_CHRT is not set
605# CONFIG_DMESG is not set
606# CONFIG_FEATURE_DMESG_PRETTY is not set
607# CONFIG_EJECT is not set
608# CONFIG_FEATURE_EJECT_SCSI is not set
609# CONFIG_FALLOCATE is not set
610# CONFIG_FATATTR is not set
611# CONFIG_FBSET is not set
612# CONFIG_FEATURE_FBSET_FANCY is not set
613# CONFIG_FEATURE_FBSET_READMODE is not set
614# CONFIG_FDFORMAT is not set
615# CONFIG_FDISK is not set
616# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
617# CONFIG_FEATURE_FDISK_WRITABLE is not set
618# CONFIG_FEATURE_AIX_LABEL is not set
619# CONFIG_FEATURE_SGI_LABEL is not set
620# CONFIG_FEATURE_SUN_LABEL is not set
621# CONFIG_FEATURE_OSF_LABEL is not set
622# CONFIG_FEATURE_GPT_LABEL is not set
623# CONFIG_FEATURE_FDISK_ADVANCED is not set
624# CONFIG_FINDFS is not set
625# CONFIG_FLOCK is not set
626# CONFIG_FDFLUSH is not set
627# CONFIG_FREERAMDISK is not set
628# CONFIG_FSCK_MINIX is not set
629# CONFIG_FSFREEZE is not set
630# CONFIG_FSTRIM is not set
631CONFIG_GETOPT=y
632CONFIG_FEATURE_GETOPT_LONG=y
633CONFIG_HEXDUMP=y
634CONFIG_FEATURE_HEXDUMP_REVERSE=y
635CONFIG_HD=y
636CONFIG_XXD=y
637# CONFIG_HWCLOCK is not set
638# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
639# CONFIG_IONICE is not set
640# CONFIG_IPCRM is not set
641# CONFIG_IPCS is not set
642# CONFIG_LAST is not set
643# CONFIG_FEATURE_LAST_FANCY is not set
644# CONFIG_LOSETUP is not set
645# CONFIG_LSPCI is not set
646# CONFIG_LSUSB is not set
647# CONFIG_MDEV is not set
648# CONFIG_FEATURE_MDEV_CONF is not set
649# CONFIG_FEATURE_MDEV_RENAME is not set
650# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
651# CONFIG_FEATURE_MDEV_EXEC is not set
652# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
653# CONFIG_MESG is not set
654# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
655# CONFIG_MKE2FS is not set
656# CONFIG_MKFS_EXT2 is not set
657# CONFIG_MKFS_MINIX is not set
658# CONFIG_FEATURE_MINIX2 is not set
659# CONFIG_MKFS_REISER is not set
660# CONFIG_MKDOSFS is not set
661# CONFIG_MKFS_VFAT is not set
662# CONFIG_MKSWAP is not set
663# CONFIG_FEATURE_MKSWAP_UUID is not set
664# CONFIG_MORE is not set
665# CONFIG_MOUNT is not set
666# CONFIG_FEATURE_MOUNT_FAKE is not set
667# CONFIG_FEATURE_MOUNT_VERBOSE is not set
668# CONFIG_FEATURE_MOUNT_HELPERS is not set
669# CONFIG_FEATURE_MOUNT_LABEL is not set
670# CONFIG_FEATURE_MOUNT_NFS is not set
671# CONFIG_FEATURE_MOUNT_CIFS is not set
672# CONFIG_FEATURE_MOUNT_FLAGS is not set
673# CONFIG_FEATURE_MOUNT_FSTAB is not set
674# CONFIG_FEATURE_MOUNT_OTHERTAB is not set
675# CONFIG_MOUNTPOINT is not set
676# CONFIG_NOLOGIN is not set
677# CONFIG_NOLOGIN_DEPENDENCIES is not set
678# CONFIG_NSENTER is not set
679# CONFIG_PIVOT_ROOT is not set
680# CONFIG_RDATE is not set
681# CONFIG_RDEV is not set
682# CONFIG_READPROFILE is not set
683# CONFIG_RENICE is not set
684CONFIG_REV=y
685# CONFIG_RTCWAKE is not set
686# CONFIG_SCRIPT is not set
687# CONFIG_SCRIPTREPLAY is not set
688# CONFIG_SETARCH is not set
689# CONFIG_LINUX32 is not set
690# CONFIG_LINUX64 is not set
691# CONFIG_SETPRIV is not set
692# CONFIG_FEATURE_SETPRIV_DUMP is not set
693# CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set
694# CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set
695# CONFIG_SETSID is not set
696# CONFIG_SWAPON is not set
697# CONFIG_FEATURE_SWAPON_DISCARD is not set
698# CONFIG_FEATURE_SWAPON_PRI is not set
699# CONFIG_SWAPOFF is not set
700# CONFIG_FEATURE_SWAPONOFF_LABEL is not set
701# CONFIG_SWITCH_ROOT is not set
702# CONFIG_TASKSET is not set
703# CONFIG_FEATURE_TASKSET_FANCY is not set
704# CONFIG_UEVENT is not set
705# CONFIG_UMOUNT is not set
706# CONFIG_FEATURE_UMOUNT_ALL is not set
707# CONFIG_UNSHARE is not set
708# CONFIG_WALL is not set
709# CONFIG_FEATURE_MOUNT_LOOP is not set
710# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
711# CONFIG_FEATURE_MTAB_SUPPORT is not set
712# CONFIG_VOLUMEID is not set
713# CONFIG_FEATURE_VOLUMEID_BCACHE is not set
714# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
715# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
716# CONFIG_FEATURE_VOLUMEID_EXFAT is not set
717# CONFIG_FEATURE_VOLUMEID_EXT is not set
718# CONFIG_FEATURE_VOLUMEID_F2FS is not set
719# CONFIG_FEATURE_VOLUMEID_FAT is not set
720# CONFIG_FEATURE_VOLUMEID_HFS is not set
721# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
722# CONFIG_FEATURE_VOLUMEID_JFS is not set
723# CONFIG_FEATURE_VOLUMEID_LFS is not set
724# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
725# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
726# CONFIG_FEATURE_VOLUMEID_LUKS is not set
727# CONFIG_FEATURE_VOLUMEID_MINIX is not set
728# CONFIG_FEATURE_VOLUMEID_NILFS is not set
729# CONFIG_FEATURE_VOLUMEID_NTFS is not set
730# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
731# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
732# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
733# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set
734# CONFIG_FEATURE_VOLUMEID_SYSV is not set
735# CONFIG_FEATURE_VOLUMEID_UBIFS is not set
736# CONFIG_FEATURE_VOLUMEID_UDF is not set
737# CONFIG_FEATURE_VOLUMEID_XFS is not set
738
739#
740# Miscellaneous Utilities
741#
742# CONFIG_ADJTIMEX is not set
743# CONFIG_BBCONFIG is not set
744# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
745# CONFIG_BC is not set
746CONFIG_DC=y
747# CONFIG_FEATURE_DC_BIG is not set
748CONFIG_FEATURE_DC_LIBM=y
749# CONFIG_FEATURE_BC_INTERACTIVE is not set
750# CONFIG_FEATURE_BC_LONG_OPTIONS is not set
751# CONFIG_BEEP is not set
752CONFIG_FEATURE_BEEP_FREQ=0
753CONFIG_FEATURE_BEEP_LENGTH_MS=0
754# CONFIG_CHAT is not set
755# CONFIG_FEATURE_CHAT_NOFAIL is not set
756# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
757# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
758# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
759# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
760# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
761# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
762# CONFIG_CONSPY is not set
763# CONFIG_CROND is not set
764# CONFIG_FEATURE_CROND_D is not set
765# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
766# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set
767CONFIG_FEATURE_CROND_DIR=""
768# CONFIG_CRONTAB is not set
769# CONFIG_DEVFSD is not set
770# CONFIG_DEVFSD_MODLOAD is not set
771# CONFIG_DEVFSD_FG_NP is not set
772# CONFIG_DEVFSD_VERBOSE is not set
773# CONFIG_FEATURE_DEVFS is not set
774# CONFIG_DEVMEM is not set
775# CONFIG_FBSPLASH is not set
776# CONFIG_FLASHCP is not set
777# CONFIG_FLASH_ERASEALL is not set
778# CONFIG_FLASH_LOCK is not set
779# CONFIG_FLASH_UNLOCK is not set
780# CONFIG_HDPARM is not set
781# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
782# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
783# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
784# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
785# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
786# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
787# CONFIG_HEXEDIT is not set
788# CONFIG_I2CGET is not set
789# CONFIG_I2CSET is not set
790# CONFIG_I2CDUMP is not set
791# CONFIG_I2CDETECT is not set
792# CONFIG_I2CTRANSFER is not set
793CONFIG_ICONV=y
794# CONFIG_INOTIFYD is not set
795CONFIG_LESS=y
796CONFIG_FEATURE_LESS_MAXLINES=9999999
797CONFIG_FEATURE_LESS_BRACKETS=y
798CONFIG_FEATURE_LESS_FLAGS=y
799CONFIG_FEATURE_LESS_TRUNCATE=y
800CONFIG_FEATURE_LESS_MARKS=y
801CONFIG_FEATURE_LESS_REGEXP=y
802# CONFIG_FEATURE_LESS_WINCH is not set
803# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set
804CONFIG_FEATURE_LESS_DASHCMD=y
805CONFIG_FEATURE_LESS_LINENUMS=y
806CONFIG_FEATURE_LESS_RAW=y
807CONFIG_FEATURE_LESS_ENV=y
808# CONFIG_LSSCSI is not set
809# CONFIG_MAKEDEVS is not set
810# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
811# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
812CONFIG_MAN=y
813# CONFIG_MICROCOM is not set
814# CONFIG_MT is not set
815# CONFIG_NANDWRITE is not set
816# CONFIG_NANDDUMP is not set
817# CONFIG_PARTPROBE is not set
818# CONFIG_RAIDAUTORUN is not set
819# CONFIG_READAHEAD is not set
820# CONFIG_RFKILL is not set
821# CONFIG_RUNLEVEL is not set
822# CONFIG_RX is not set
823# CONFIG_SETFATTR is not set
824# CONFIG_SETSERIAL is not set
825CONFIG_STRINGS=y
826# CONFIG_TIME is not set
827CONFIG_TS=y
828CONFIG_TTYSIZE=y
829# CONFIG_UBIRENAME is not set
830# CONFIG_UBIATTACH is not set
831# CONFIG_UBIDETACH is not set
832# CONFIG_UBIMKVOL is not set
833# CONFIG_UBIRMVOL is not set
834# CONFIG_UBIRSVOL is not set
835# CONFIG_UBIUPDATEVOL is not set
836# CONFIG_VOLNAME is not set
837# CONFIG_WATCHDOG is not set
838
839#
840# Networking Utilities
841#
842CONFIG_FEATURE_IPV6=y
843# CONFIG_FEATURE_UNIX_LOCAL is not set
844CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
845# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
846# CONFIG_FEATURE_TLS_SHA1 is not set
847# CONFIG_ARP is not set
848# CONFIG_ARPING is not set
849# CONFIG_BRCTL is not set
850# CONFIG_FEATURE_BRCTL_FANCY is not set
851# CONFIG_FEATURE_BRCTL_SHOW is not set
852# CONFIG_DNSD is not set
853# CONFIG_ETHER_WAKE is not set
854# CONFIG_FTPD is not set
855# CONFIG_FEATURE_FTPD_WRITE is not set
856# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
857# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set
858CONFIG_FTPGET=y
859CONFIG_FTPPUT=y
860CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
861# CONFIG_HOSTNAME is not set
862# CONFIG_DNSDOMAINNAME is not set
863# CONFIG_HTTPD is not set
864# CONFIG_FEATURE_HTTPD_RANGES is not set
865# CONFIG_FEATURE_HTTPD_SETUID is not set
866# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
867# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
868# CONFIG_FEATURE_HTTPD_CGI is not set
869# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
870# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
871# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
872# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
873# CONFIG_FEATURE_HTTPD_PROXY is not set
874# CONFIG_FEATURE_HTTPD_GZIP is not set
875# CONFIG_IFCONFIG is not set
876# CONFIG_FEATURE_IFCONFIG_STATUS is not set
877# CONFIG_FEATURE_IFCONFIG_SLIP is not set
878# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
879# CONFIG_FEATURE_IFCONFIG_HW is not set
880# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
881# CONFIG_IFENSLAVE is not set
882# CONFIG_IFPLUGD is not set
883# CONFIG_IFUP is not set
884# CONFIG_IFDOWN is not set
885CONFIG_IFUPDOWN_IFSTATE_PATH=""
886# CONFIG_FEATURE_IFUPDOWN_IP is not set
887# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
888# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
889# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
890# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
891# CONFIG_INETD is not set
892# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
893# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
894# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
895# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
896# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
897# CONFIG_FEATURE_INETD_RPC is not set
898# CONFIG_IP is not set
899# CONFIG_IPADDR is not set
900# CONFIG_IPLINK is not set
901# CONFIG_IPROUTE is not set
902# CONFIG_IPTUNNEL is not set
903# CONFIG_IPRULE is not set
904# CONFIG_IPNEIGH is not set
905# CONFIG_FEATURE_IP_ADDRESS is not set
906# CONFIG_FEATURE_IP_LINK is not set
907# CONFIG_FEATURE_IP_ROUTE is not set
908CONFIG_FEATURE_IP_ROUTE_DIR=""
909# CONFIG_FEATURE_IP_TUNNEL is not set
910# CONFIG_FEATURE_IP_RULE is not set
911# CONFIG_FEATURE_IP_NEIGH is not set
912# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
913CONFIG_IPCALC=y
914CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
915CONFIG_FEATURE_IPCALC_FANCY=y
916# CONFIG_FAKEIDENTD is not set
917# CONFIG_NAMEIF is not set
918# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
919# CONFIG_NBDCLIENT is not set
920CONFIG_NC=y
921# CONFIG_NETCAT is not set
922CONFIG_NC_SERVER=y
923# CONFIG_NC_EXTRA is not set
924# CONFIG_NC_110_COMPAT is not set
925# CONFIG_NETSTAT is not set
926# CONFIG_FEATURE_NETSTAT_WIDE is not set
927# CONFIG_FEATURE_NETSTAT_PRG is not set
928# CONFIG_NSLOOKUP is not set
929# CONFIG_FEATURE_NSLOOKUP_BIG is not set
930# CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS is not set
931# CONFIG_NTPD is not set
932# CONFIG_FEATURE_NTPD_SERVER is not set
933# CONFIG_FEATURE_NTPD_CONF is not set
934# CONFIG_FEATURE_NTP_AUTH is not set
935# CONFIG_PING is not set
936# CONFIG_PING6 is not set
937# CONFIG_FEATURE_FANCY_PING is not set
938# CONFIG_PSCAN is not set
939# CONFIG_ROUTE is not set
940# CONFIG_SLATTACH is not set
941CONFIG_SSL_CLIENT=y
942# CONFIG_TC is not set
943# CONFIG_FEATURE_TC_INGRESS is not set
944# CONFIG_TCPSVD is not set
945# CONFIG_UDPSVD is not set
946# CONFIG_TELNET is not set
947# CONFIG_FEATURE_TELNET_TTYPE is not set
948# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
949# CONFIG_FEATURE_TELNET_WIDTH is not set
950# CONFIG_TELNETD is not set
951# CONFIG_FEATURE_TELNETD_STANDALONE is not set
952# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
953# CONFIG_TFTP is not set
954# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
955# CONFIG_TFTPD is not set
956# CONFIG_FEATURE_TFTP_GET is not set
957# CONFIG_FEATURE_TFTP_PUT is not set
958# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
959# CONFIG_TFTP_DEBUG is not set
960CONFIG_TLS=y
961# CONFIG_TRACEROUTE is not set
962# CONFIG_TRACEROUTE6 is not set
963# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
964# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
965# CONFIG_TUNCTL is not set
966# CONFIG_FEATURE_TUNCTL_UG is not set
967# CONFIG_VCONFIG is not set
968CONFIG_WGET=y
969CONFIG_FEATURE_WGET_LONG_OPTIONS=y
970# CONFIG_FEATURE_WGET_STATUSBAR is not set
971CONFIG_FEATURE_WGET_AUTHENTICATION=y
972# CONFIG_FEATURE_WGET_TIMEOUT is not set
973CONFIG_FEATURE_WGET_HTTPS=y
974# CONFIG_FEATURE_WGET_OPENSSL is not set
975CONFIG_WHOIS=y
976# CONFIG_ZCIP is not set
977# CONFIG_UDHCPD is not set
978# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
979# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
980CONFIG_DHCPD_LEASES_FILE=""
981# CONFIG_DUMPLEASES is not set
982# CONFIG_DHCPRELAY is not set
983# CONFIG_UDHCPC is not set
984# CONFIG_FEATURE_UDHCPC_ARPING is not set
985# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set
986CONFIG_UDHCPC_DEFAULT_SCRIPT=""
987# CONFIG_UDHCPC6 is not set
988# CONFIG_FEATURE_UDHCPC6_RFC3646 is not set
989# CONFIG_FEATURE_UDHCPC6_RFC4704 is not set
990# CONFIG_FEATURE_UDHCPC6_RFC4833 is not set
991# CONFIG_FEATURE_UDHCPC6_RFC5970 is not set
992# CONFIG_FEATURE_UDHCP_PORT is not set
993CONFIG_UDHCP_DEBUG=0
994CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
995# CONFIG_FEATURE_UDHCP_RFC3397 is not set
996# CONFIG_FEATURE_UDHCP_8021Q is not set
997CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
998
999#
1000# Print Utilities
1001#
1002# CONFIG_LPD is not set
1003# CONFIG_LPR is not set
1004# CONFIG_LPQ is not set
1005
1006#
1007# Mail Utilities
1008#
1009# CONFIG_MAKEMIME is not set
1010# CONFIG_POPMAILDIR is not set
1011# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
1012# CONFIG_REFORMIME is not set
1013# CONFIG_FEATURE_REFORMIME_COMPAT is not set
1014# CONFIG_SENDMAIL is not set
1015CONFIG_FEATURE_MIME_CHARSET=""
1016
1017#
1018# Process Utilities
1019#
1020# CONFIG_FREE is not set
1021# CONFIG_FUSER is not set
1022# CONFIG_IOSTAT is not set
1023CONFIG_KILL=y
1024CONFIG_KILLALL=y
1025# CONFIG_KILLALL5 is not set
1026# CONFIG_LSOF is not set
1027# CONFIG_MPSTAT is not set
1028# CONFIG_NMETER is not set
1029CONFIG_PGREP=y
1030CONFIG_PKILL=y
1031CONFIG_PIDOF=y
1032CONFIG_FEATURE_PIDOF_SINGLE=y
1033CONFIG_FEATURE_PIDOF_OMIT=y
1034# CONFIG_PMAP is not set
1035# CONFIG_POWERTOP is not set
1036# CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set
1037CONFIG_PS=y
1038# CONFIG_FEATURE_PS_WIDE is not set
1039# CONFIG_FEATURE_PS_LONG is not set
1040CONFIG_FEATURE_PS_TIME=y
1041# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
1042# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
1043# CONFIG_PSTREE is not set
1044# CONFIG_PWDX is not set
1045# CONFIG_SMEMCAP is not set
1046# CONFIG_BB_SYSCTL is not set
1047# CONFIG_TOP is not set
1048# CONFIG_FEATURE_TOP_INTERACTIVE is not set
1049# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
1050# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
1051# CONFIG_FEATURE_TOP_SMP_CPU is not set
1052# CONFIG_FEATURE_TOP_DECIMALS is not set
1053# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
1054# CONFIG_FEATURE_TOPMEM is not set
1055# CONFIG_UPTIME is not set
1056# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
1057CONFIG_WATCH=y
1058# CONFIG_FEATURE_SHOW_THREADS is not set
1059
1060#
1061# Runit Utilities
1062#
1063# CONFIG_CHPST is not set
1064# CONFIG_SETUIDGID is not set
1065# CONFIG_ENVUIDGID is not set
1066# CONFIG_ENVDIR is not set
1067# CONFIG_SOFTLIMIT is not set
1068# CONFIG_RUNSV is not set
1069# CONFIG_RUNSVDIR is not set
1070# CONFIG_FEATURE_RUNSVDIR_LOG is not set
1071# CONFIG_SV is not set
1072CONFIG_SV_DEFAULT_SERVICE_DIR=""
1073# CONFIG_SVC is not set
1074# CONFIG_SVOK is not set
1075# CONFIG_SVLOGD is not set
1076# CONFIG_CHCON is not set
1077# CONFIG_GETENFORCE is not set
1078# CONFIG_GETSEBOOL is not set
1079# CONFIG_LOAD_POLICY is not set
1080# CONFIG_MATCHPATHCON is not set
1081# CONFIG_RUNCON is not set
1082# CONFIG_SELINUXENABLED is not set
1083# CONFIG_SESTATUS is not set
1084# CONFIG_SETENFORCE is not set
1085# CONFIG_SETFILES is not set
1086# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
1087# CONFIG_RESTORECON is not set
1088# CONFIG_SETSEBOOL is not set
1089
1090#
1091# Shells
1092#
1093CONFIG_SH_IS_ASH=y
1094# CONFIG_SH_IS_HUSH is not set
1095# CONFIG_SH_IS_NONE is not set
1096CONFIG_BASH_IS_ASH=y
1097# CONFIG_BASH_IS_HUSH is not set
1098# CONFIG_BASH_IS_NONE is not set
1099CONFIG_ASH=y
1100CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
1101CONFIG_ASH_INTERNAL_GLOB=y
1102CONFIG_ASH_BASH_COMPAT=y
1103# CONFIG_ASH_BASH_SOURCE_CURDIR is not set
1104CONFIG_ASH_BASH_NOT_FOUND_HOOK=y
1105# CONFIG_ASH_JOB_CONTROL is not set
1106CONFIG_ASH_ALIAS=y
1107CONFIG_ASH_RANDOM_SUPPORT=y
1108CONFIG_ASH_EXPAND_PRMT=y
1109# CONFIG_ASH_IDLE_TIMEOUT is not set
1110# CONFIG_ASH_MAIL is not set
1111CONFIG_ASH_ECHO=y
1112CONFIG_ASH_PRINTF=y
1113CONFIG_ASH_TEST=y
1114CONFIG_ASH_HELP=y
1115CONFIG_ASH_GETOPTS=y
1116CONFIG_ASH_CMDCMD=y
1117CONFIG_ASH_NOCONSOLE=y
1118CONFIG_ASH_NOCASEGLOB=y
1119# CONFIG_CTTYHACK is not set
1120# CONFIG_HUSH is not set
1121# CONFIG_HUSH_BASH_COMPAT is not set
1122# CONFIG_HUSH_BRACE_EXPANSION is not set
1123# CONFIG_HUSH_LINENO_VAR is not set
1124# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set
1125# CONFIG_HUSH_INTERACTIVE is not set
1126# CONFIG_HUSH_SAVEHISTORY is not set
1127# CONFIG_HUSH_JOB is not set
1128# CONFIG_HUSH_TICK is not set
1129# CONFIG_HUSH_IF is not set
1130# CONFIG_HUSH_LOOPS is not set
1131# CONFIG_HUSH_CASE is not set
1132# CONFIG_HUSH_FUNCTIONS is not set
1133# CONFIG_HUSH_LOCAL is not set
1134# CONFIG_HUSH_RANDOM_SUPPORT is not set
1135# CONFIG_HUSH_MODE_X is not set
1136# CONFIG_HUSH_ECHO is not set
1137# CONFIG_HUSH_PRINTF is not set
1138# CONFIG_HUSH_TEST is not set
1139# CONFIG_HUSH_HELP is not set
1140# CONFIG_HUSH_EXPORT is not set
1141# CONFIG_HUSH_EXPORT_N is not set
1142# CONFIG_HUSH_READONLY is not set
1143# CONFIG_HUSH_KILL is not set
1144# CONFIG_HUSH_WAIT is not set
1145# CONFIG_HUSH_COMMAND is not set
1146# CONFIG_HUSH_TRAP is not set
1147# CONFIG_HUSH_TYPE is not set
1148# CONFIG_HUSH_TIMES is not set
1149# CONFIG_HUSH_READ is not set
1150# CONFIG_HUSH_SET is not set
1151# CONFIG_HUSH_UNSET is not set
1152# CONFIG_HUSH_ULIMIT is not set
1153# CONFIG_HUSH_UMASK is not set
1154# CONFIG_HUSH_GETOPTS is not set
1155# CONFIG_HUSH_MEMLEAK is not set
1156
1157#
1158# Options common to all shells
1159#
1160CONFIG_FEATURE_SH_MATH=y
1161CONFIG_FEATURE_SH_MATH_64=y
1162CONFIG_FEATURE_SH_EXTRA_QUIET=y
1163CONFIG_FEATURE_SH_STANDALONE=y
1164CONFIG_FEATURE_SH_NOFORK=y
1165# CONFIG_FEATURE_SH_READ_FRAC is not set
1166CONFIG_FEATURE_SH_HISTFILESIZE=y
1167CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y
1168
1169#
1170# System Logging Utilities
1171#
1172# CONFIG_KLOGD is not set
1173# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
1174# CONFIG_LOGGER is not set
1175# CONFIG_LOGREAD is not set
1176# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
1177# CONFIG_SYSLOGD is not set
1178# CONFIG_FEATURE_ROTATE_LOGFILE is not set
1179# CONFIG_FEATURE_REMOTE_LOG is not set
1180# CONFIG_FEATURE_SYSLOGD_DUP is not set
1181# CONFIG_FEATURE_SYSLOGD_CFG is not set
1182CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
1183# CONFIG_FEATURE_IPC_SYSLOG is not set
1184CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
1185# CONFIG_FEATURE_KMSG_SYSLOG is not set
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig
new file mode 100644
index 000000000..b8a3a03d5
--- /dev/null
+++ b/configs/mingw64_defconfig
@@ -0,0 +1,1185 @@
1#
2# Automatically generated make config: don't edit
3# Busybox version: 1.31.0.git
4# Mon Apr 8 21:26:25 2019
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_ICON=y
47# CONFIG_FEATURE_ICON_ATERM is not set
48# CONFIG_FEATURE_ICON_STERM is not set
49CONFIG_FEATURE_ICON_ALL=y
50CONFIG_FEATURE_EURO=y
51CONFIG_FEATURE_EXTRA_FILE_DATA=y
52CONFIG_FEATURE_READLINK2=y
53
54#
55# Build Options
56#
57# CONFIG_STATIC is not set
58# CONFIG_PIE is not set
59# CONFIG_NOMMU is not set
60# CONFIG_BUILD_LIBBUSYBOX is not set
61# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set
62# CONFIG_FEATURE_INDIVIDUAL is not set
63# CONFIG_FEATURE_SHARED_BUSYBOX is not set
64CONFIG_CROSS_COMPILER_PREFIX="x86_64-w64-mingw32-"
65CONFIG_SYSROOT=""
66CONFIG_EXTRA_CFLAGS="-funwind-tables -fasynchronous-unwind-tables"
67CONFIG_EXTRA_LDFLAGS=""
68CONFIG_EXTRA_LDLIBS=""
69CONFIG_USE_PORTABLE_CODE=y
70CONFIG_STACK_OPTIMIZATION_386=y
71
72#
73# Installation Options ("make install" behavior)
74#
75CONFIG_INSTALL_APPLET_SYMLINKS=y
76# CONFIG_INSTALL_APPLET_HARDLINKS is not set
77# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
78# CONFIG_INSTALL_APPLET_DONT is not set
79# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
80# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
81# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
82CONFIG_PREFIX=""
83
84#
85# Debugging Options
86#
87CONFIG_DEBUG=y
88# CONFIG_DEBUG_PESSIMIZE is not set
89# CONFIG_DEBUG_SANITIZE is not set
90# CONFIG_UNIT_TEST is not set
91# CONFIG_WERROR is not set
92CONFIG_NO_DEBUG_LIB=y
93# CONFIG_DMALLOC is not set
94# CONFIG_EFENCE is not set
95
96#
97# Library Tuning
98#
99# CONFIG_FEATURE_USE_BSS_TAIL is not set
100CONFIG_FLOAT_DURATION=y
101CONFIG_FEATURE_RTMINMAX=y
102CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS=y
103CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
104# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
105# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
106CONFIG_PASSWORD_MINLEN=6
107CONFIG_MD5_SMALL=1
108CONFIG_SHA3_SMALL=1
109# CONFIG_FEATURE_FAST_TOP is not set
110# CONFIG_FEATURE_ETC_NETWORKS is not set
111# CONFIG_FEATURE_ETC_SERVICES is not set
112CONFIG_FEATURE_EDITING=y
113CONFIG_FEATURE_EDITING_MAX_LEN=1024
114CONFIG_FEATURE_EDITING_VI=y
115CONFIG_FEATURE_EDITING_HISTORY=255
116CONFIG_FEATURE_EDITING_SAVEHISTORY=y
117# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
118CONFIG_FEATURE_REVERSE_SEARCH=y
119CONFIG_FEATURE_TAB_COMPLETION=y
120CONFIG_FEATURE_USERNAME_COMPLETION=y
121CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
122# CONFIG_FEATURE_EDITING_WINCH is not set
123# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
124# CONFIG_LOCALE_SUPPORT is not set
125# CONFIG_UNICODE_SUPPORT is not set
126# CONFIG_UNICODE_USING_LOCALE is not set
127# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
128CONFIG_SUBST_WCHAR=0
129CONFIG_LAST_SUPPORTED_WCHAR=0
130# CONFIG_UNICODE_COMBINING_WCHARS is not set
131# CONFIG_UNICODE_WIDE_WCHARS is not set
132# CONFIG_UNICODE_BIDI_SUPPORT is not set
133# CONFIG_UNICODE_NEUTRAL_TABLE is not set
134# CONFIG_UNICODE_PRESERVE_BROKEN is not set
135CONFIG_FEATURE_NON_POSIX_CP=y
136# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
137# CONFIG_FEATURE_USE_SENDFILE is not set
138CONFIG_FEATURE_COPYBUF_KB=4
139CONFIG_FEATURE_SKIP_ROOTFS=y
140# CONFIG_MONOTONIC_SYSCALL is not set
141CONFIG_IOCTL_HEX2STR_ERROR=y
142# CONFIG_FEATURE_HWIB is not set
143
144#
145# Applets
146#
147
148#
149# Archival Utilities
150#
151CONFIG_FEATURE_SEAMLESS_XZ=y
152CONFIG_FEATURE_SEAMLESS_LZMA=y
153CONFIG_FEATURE_SEAMLESS_BZ2=y
154CONFIG_FEATURE_SEAMLESS_GZ=y
155CONFIG_FEATURE_SEAMLESS_Z=y
156CONFIG_AR=y
157CONFIG_FEATURE_AR_LONG_FILENAMES=y
158CONFIG_FEATURE_AR_CREATE=y
159CONFIG_UNCOMPRESS=y
160CONFIG_GUNZIP=y
161CONFIG_ZCAT=y
162CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
163CONFIG_BUNZIP2=y
164CONFIG_BZCAT=y
165CONFIG_UNLZMA=y
166CONFIG_LZCAT=y
167CONFIG_LZMA=y
168CONFIG_UNXZ=y
169CONFIG_XZCAT=y
170CONFIG_XZ=y
171CONFIG_BZIP2=y
172CONFIG_BZIP2_SMALL=8
173CONFIG_FEATURE_BZIP2_DECOMPRESS=y
174CONFIG_CPIO=y
175CONFIG_FEATURE_CPIO_O=y
176# CONFIG_FEATURE_CPIO_P is not set
177CONFIG_DPKG=y
178CONFIG_DPKG_DEB=y
179CONFIG_GZIP=y
180CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
181CONFIG_GZIP_FAST=2
182CONFIG_FEATURE_GZIP_LEVELS=y
183CONFIG_FEATURE_GZIP_DECOMPRESS=y
184CONFIG_LZOP=y
185CONFIG_UNLZOP=y
186CONFIG_LZOPCAT=y
187# CONFIG_LZOP_COMPR_HIGH is not set
188CONFIG_RPM=y
189CONFIG_RPM2CPIO=y
190CONFIG_TAR=y
191CONFIG_FEATURE_TAR_LONG_OPTIONS=y
192CONFIG_FEATURE_TAR_CREATE=y
193CONFIG_FEATURE_TAR_AUTODETECT=y
194CONFIG_FEATURE_TAR_FROM=y
195CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
196# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
197CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
198# CONFIG_FEATURE_TAR_TO_COMMAND is not set
199# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
200CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
201# CONFIG_FEATURE_TAR_SELINUX is not set
202CONFIG_UNZIP=y
203CONFIG_FEATURE_UNZIP_CDF=y
204CONFIG_FEATURE_UNZIP_BZIP2=y
205CONFIG_FEATURE_UNZIP_LZMA=y
206CONFIG_FEATURE_UNZIP_XZ=y
207CONFIG_FEATURE_LZMA_FAST=y
208
209#
210# Coreutils
211#
212CONFIG_BASENAME=y
213CONFIG_CAT=y
214CONFIG_FEATURE_CATN=y
215CONFIG_FEATURE_CATV=y
216# CONFIG_CHGRP is not set
217CONFIG_CHMOD=y
218# CONFIG_CHOWN is not set
219# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
220# CONFIG_CHROOT is not set
221CONFIG_CKSUM=y
222CONFIG_COMM=y
223CONFIG_CP=y
224CONFIG_FEATURE_CP_LONG_OPTIONS=y
225# CONFIG_FEATURE_CP_REFLINK is not set
226CONFIG_CUT=y
227CONFIG_DATE=y
228CONFIG_FEATURE_DATE_ISOFMT=y
229# CONFIG_FEATURE_DATE_NANO is not set
230CONFIG_FEATURE_DATE_COMPAT=y
231CONFIG_DD=y
232# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set
233# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
234CONFIG_FEATURE_DD_IBS_OBS=y
235CONFIG_FEATURE_DD_STATUS=y
236CONFIG_DF=y
237# CONFIG_FEATURE_DF_FANCY is not set
238CONFIG_DIRNAME=y
239CONFIG_DOS2UNIX=y
240CONFIG_UNIX2DOS=y
241CONFIG_DU=y
242CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
243CONFIG_ECHO=y
244CONFIG_FEATURE_FANCY_ECHO=y
245CONFIG_ENV=y
246CONFIG_EXPAND=y
247CONFIG_UNEXPAND=y
248CONFIG_EXPR=y
249CONFIG_EXPR_MATH_SUPPORT_64=y
250CONFIG_FACTOR=y
251CONFIG_FALSE=y
252CONFIG_FOLD=y
253CONFIG_FSYNC=y
254CONFIG_HEAD=y
255CONFIG_FEATURE_FANCY_HEAD=y
256# CONFIG_HOSTID is not set
257CONFIG_ID=y
258CONFIG_GROUPS=y
259# CONFIG_INSTALL is not set
260# CONFIG_FEATURE_INSTALL_LONG_OPTIONS is not set
261CONFIG_LINK=y
262CONFIG_LN=y
263CONFIG_LOGNAME=y
264CONFIG_LS=y
265CONFIG_FEATURE_LS_FILETYPES=y
266CONFIG_FEATURE_LS_FOLLOWLINKS=y
267CONFIG_FEATURE_LS_RECURSIVE=y
268CONFIG_FEATURE_LS_WIDTH=y
269CONFIG_FEATURE_LS_SORTFILES=y
270CONFIG_FEATURE_LS_TIMESTAMPS=y
271CONFIG_FEATURE_LS_USERNAME=y
272CONFIG_FEATURE_LS_COLOR=y
273CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
274CONFIG_MD5SUM=y
275CONFIG_SHA1SUM=y
276CONFIG_SHA256SUM=y
277CONFIG_SHA512SUM=y
278CONFIG_SHA3SUM=y
279
280#
281# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
282#
283CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
284CONFIG_MKDIR=y
285# CONFIG_MKFIFO is not set
286# CONFIG_MKNOD is not set
287CONFIG_MKTEMP=y
288CONFIG_MV=y
289# CONFIG_NICE is not set
290CONFIG_NL=y
291# CONFIG_NOHUP is not set
292# CONFIG_NPROC is not set
293CONFIG_OD=y
294CONFIG_PASTE=y
295CONFIG_PRINTENV=y
296CONFIG_PRINTF=y
297CONFIG_PWD=y
298CONFIG_READLINK=y
299CONFIG_FEATURE_READLINK_FOLLOW=y
300CONFIG_REALPATH=y
301CONFIG_RM=y
302CONFIG_RMDIR=y
303CONFIG_SEQ=y
304CONFIG_SHRED=y
305CONFIG_SHUF=y
306CONFIG_SLEEP=y
307CONFIG_FEATURE_FANCY_SLEEP=y
308CONFIG_SORT=y
309CONFIG_FEATURE_SORT_BIG=y
310# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set
311CONFIG_SPLIT=y
312CONFIG_FEATURE_SPLIT_FANCY=y
313CONFIG_STAT=y
314CONFIG_FEATURE_STAT_FORMAT=y
315CONFIG_FEATURE_STAT_FILESYSTEM=y
316# CONFIG_STTY is not set
317CONFIG_SUM=y
318# CONFIG_SYNC is not set
319# CONFIG_FEATURE_SYNC_FANCY is not set
320CONFIG_TAC=y
321CONFIG_TAIL=y
322CONFIG_FEATURE_FANCY_TAIL=y
323CONFIG_TEE=y
324CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
325CONFIG_TEST=y
326CONFIG_TEST1=y
327CONFIG_TEST2=y
328CONFIG_FEATURE_TEST_64=y
329CONFIG_TIMEOUT=y
330CONFIG_TOUCH=y
331# CONFIG_FEATURE_TOUCH_NODEREF is not set
332CONFIG_FEATURE_TOUCH_SUSV3=y
333CONFIG_TR=y
334CONFIG_FEATURE_TR_CLASSES=y
335CONFIG_FEATURE_TR_EQUIV=y
336CONFIG_TRUE=y
337CONFIG_TRUNCATE=y
338# CONFIG_TTY is not set
339CONFIG_UNAME=y
340CONFIG_UNAME_OSNAME="MS/Windows"
341CONFIG_BB_ARCH=y
342CONFIG_UNIQ=y
343CONFIG_UNLINK=y
344CONFIG_USLEEP=y
345CONFIG_UUDECODE=y
346CONFIG_BASE64=y
347CONFIG_UUENCODE=y
348CONFIG_WC=y
349CONFIG_FEATURE_WC_LARGE=y
350CONFIG_WHOAMI=y
351# CONFIG_WHO is not set
352# CONFIG_W is not set
353# CONFIG_USERS is not set
354CONFIG_YES=y
355
356#
357# Common options
358#
359CONFIG_FEATURE_VERBOSE=y
360
361#
362# Common options for cp and mv
363#
364# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
365
366#
367# Common options for df, du, ls
368#
369CONFIG_FEATURE_HUMAN_READABLE=y
370
371#
372# Console Utilities
373#
374# CONFIG_CHVT is not set
375CONFIG_CLEAR=y
376# CONFIG_DEALLOCVT is not set
377# CONFIG_DUMPKMAP is not set
378# CONFIG_FGCONSOLE is not set
379# CONFIG_KBD_MODE is not set
380# CONFIG_LOADFONT is not set
381# CONFIG_SETFONT is not set
382# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
383CONFIG_DEFAULT_SETFONT_DIR=""
384# CONFIG_FEATURE_LOADFONT_PSF2 is not set
385# CONFIG_FEATURE_LOADFONT_RAW is not set
386# CONFIG_LOADKMAP is not set
387# CONFIG_OPENVT is not set
388CONFIG_RESET=y
389# CONFIG_RESIZE is not set
390# CONFIG_FEATURE_RESIZE_PRINT is not set
391# CONFIG_SETCONSOLE is not set
392# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
393# CONFIG_SETKEYCODES is not set
394# CONFIG_SETLOGCONS is not set
395# CONFIG_SHOWKEY is not set
396
397#
398# Debian Utilities
399#
400CONFIG_PIPE_PROGRESS=y
401# CONFIG_RUN_PARTS is not set
402# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
403# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
404# CONFIG_START_STOP_DAEMON is not set
405# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
406# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
407CONFIG_WHICH=y
408
409#
410# klibc-utils
411#
412# CONFIG_MINIPS is not set
413# CONFIG_NUKE is not set
414# CONFIG_RESUME is not set
415# CONFIG_RUN_INIT is not set
416
417#
418# Editors
419#
420CONFIG_AWK=y
421CONFIG_FEATURE_AWK_LIBM=y
422CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
423CONFIG_CMP=y
424CONFIG_DIFF=y
425CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
426CONFIG_FEATURE_DIFF_DIR=y
427CONFIG_ED=y
428CONFIG_PATCH=y
429CONFIG_SED=y
430CONFIG_VI=y
431CONFIG_FEATURE_VI_MAX_LEN=4096
432CONFIG_FEATURE_VI_8BIT=y
433CONFIG_FEATURE_VI_COLON=y
434CONFIG_FEATURE_VI_YANKMARK=y
435CONFIG_FEATURE_VI_SEARCH=y
436# CONFIG_FEATURE_VI_REGEX_SEARCH is not set
437# CONFIG_FEATURE_VI_USE_SIGNALS is not set
438CONFIG_FEATURE_VI_DOT_CMD=y
439CONFIG_FEATURE_VI_READONLY=y
440CONFIG_FEATURE_VI_SETOPTS=y
441CONFIG_FEATURE_VI_SET=y
442CONFIG_FEATURE_VI_WIN_RESIZE=y
443# CONFIG_FEATURE_VI_ASK_TERMINAL is not set
444CONFIG_FEATURE_VI_UNDO=y
445CONFIG_FEATURE_VI_UNDO_QUEUE=y
446CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
447CONFIG_FEATURE_ALLOW_EXEC=y
448
449#
450# Finding Utilities
451#
452CONFIG_FIND=y
453CONFIG_FEATURE_FIND_PRINT0=y
454CONFIG_FEATURE_FIND_MTIME=y
455CONFIG_FEATURE_FIND_MMIN=y
456CONFIG_FEATURE_FIND_PERM=y
457CONFIG_FEATURE_FIND_TYPE=y
458CONFIG_FEATURE_FIND_EXECUTABLE=y
459CONFIG_FEATURE_FIND_XDEV=y
460CONFIG_FEATURE_FIND_MAXDEPTH=y
461CONFIG_FEATURE_FIND_NEWER=y
462CONFIG_FEATURE_FIND_INUM=y
463CONFIG_FEATURE_FIND_EXEC=y
464CONFIG_FEATURE_FIND_EXEC_PLUS=y
465# CONFIG_FEATURE_FIND_USER is not set
466# CONFIG_FEATURE_FIND_GROUP is not set
467CONFIG_FEATURE_FIND_NOT=y
468CONFIG_FEATURE_FIND_DEPTH=y
469CONFIG_FEATURE_FIND_PAREN=y
470CONFIG_FEATURE_FIND_SIZE=y
471CONFIG_FEATURE_FIND_PRUNE=y
472CONFIG_FEATURE_FIND_QUIT=y
473CONFIG_FEATURE_FIND_DELETE=y
474CONFIG_FEATURE_FIND_PATH=y
475CONFIG_FEATURE_FIND_REGEX=y
476# CONFIG_FEATURE_FIND_CONTEXT is not set
477CONFIG_FEATURE_FIND_LINKS=y
478CONFIG_GREP=y
479CONFIG_EGREP=y
480CONFIG_FGREP=y
481CONFIG_FEATURE_GREP_CONTEXT=y
482CONFIG_XARGS=y
483CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
484CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
485CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
486CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
487CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
488CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y
489CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y
490
491#
492# Init Utilities
493#
494# CONFIG_BOOTCHARTD is not set
495# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
496# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
497# CONFIG_HALT is not set
498# CONFIG_POWEROFF is not set
499# CONFIG_REBOOT is not set
500# CONFIG_FEATURE_WAIT_FOR_INIT is not set
501# CONFIG_FEATURE_CALL_TELINIT is not set
502CONFIG_TELINIT_PATH=""
503# CONFIG_INIT is not set
504# CONFIG_LINUXRC is not set
505# CONFIG_FEATURE_USE_INITTAB is not set
506# CONFIG_FEATURE_KILL_REMOVED is not set
507CONFIG_FEATURE_KILL_DELAY=0
508# CONFIG_FEATURE_INIT_SCTTY is not set
509# CONFIG_FEATURE_INIT_SYSLOG is not set
510# CONFIG_FEATURE_INIT_QUIET is not set
511# CONFIG_FEATURE_INIT_COREDUMPS is not set
512CONFIG_INIT_TERMINAL_TYPE=""
513# CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set
514
515#
516# Login/Password Management Utilities
517#
518# CONFIG_FEATURE_SHADOWPASSWDS is not set
519# CONFIG_USE_BB_PWD_GRP is not set
520# CONFIG_USE_BB_SHADOW is not set
521# CONFIG_USE_BB_CRYPT is not set
522# CONFIG_USE_BB_CRYPT_SHA is not set
523# CONFIG_ADDGROUP is not set
524# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
525# CONFIG_ADD_SHELL is not set
526# CONFIG_REMOVE_SHELL is not set
527# CONFIG_ADDUSER is not set
528# CONFIG_FEATURE_CHECK_NAMES is not set
529CONFIG_LAST_ID=0
530CONFIG_FIRST_SYSTEM_ID=0
531CONFIG_LAST_SYSTEM_ID=0
532# CONFIG_CHPASSWD is not set
533CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
534# CONFIG_CRYPTPW is not set
535# CONFIG_MKPASSWD is not set
536# CONFIG_DELUSER is not set
537# CONFIG_DELGROUP is not set
538# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
539# CONFIG_GETTY is not set
540# CONFIG_LOGIN is not set
541# CONFIG_LOGIN_SESSION_AS_CHILD is not set
542# CONFIG_LOGIN_SCRIPTS is not set
543# CONFIG_FEATURE_NOLOGIN is not set
544# CONFIG_FEATURE_SECURETTY is not set
545# CONFIG_PASSWD is not set
546# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
547# CONFIG_SU is not set
548# CONFIG_FEATURE_SU_SYSLOG is not set
549# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
550# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set
551# CONFIG_SULOGIN is not set
552CONFIG_SUW32=y
553# CONFIG_VLOCK is not set
554
555#
556# Linux Ext2 FS Progs
557#
558# CONFIG_CHATTR is not set
559# CONFIG_FSCK is not set
560# CONFIG_LSATTR is not set
561# CONFIG_TUNE2FS is not set
562
563#
564# Linux Module Utilities
565#
566# CONFIG_MODPROBE_SMALL is not set
567# CONFIG_DEPMOD is not set
568# CONFIG_INSMOD is not set
569# CONFIG_LSMOD is not set
570# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
571# CONFIG_MODINFO is not set
572# CONFIG_MODPROBE is not set
573# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
574# CONFIG_RMMOD is not set
575
576#
577# Options common to multiple modutils
578#
579# CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set
580# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
581# CONFIG_FEATURE_2_4_MODULES is not set
582# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
583# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
584# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
585# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
586# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
587# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
588# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
589# CONFIG_FEATURE_MODUTILS_ALIAS is not set
590# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
591CONFIG_DEFAULT_MODULES_DIR=""
592CONFIG_DEFAULT_DEPMOD_FILE=""
593
594#
595# Linux System Utilities
596#
597# CONFIG_ACPID is not set
598# CONFIG_FEATURE_ACPID_COMPAT is not set
599# CONFIG_BLKDISCARD is not set
600# CONFIG_BLKID is not set
601# CONFIG_FEATURE_BLKID_TYPE is not set
602# CONFIG_BLOCKDEV is not set
603CONFIG_CAL=y
604# CONFIG_CHRT is not set
605# CONFIG_DMESG is not set
606# CONFIG_FEATURE_DMESG_PRETTY is not set
607# CONFIG_EJECT is not set
608# CONFIG_FEATURE_EJECT_SCSI is not set
609# CONFIG_FALLOCATE is not set
610# CONFIG_FATATTR is not set
611# CONFIG_FBSET is not set
612# CONFIG_FEATURE_FBSET_FANCY is not set
613# CONFIG_FEATURE_FBSET_READMODE is not set
614# CONFIG_FDFORMAT is not set
615# CONFIG_FDISK is not set
616# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
617# CONFIG_FEATURE_FDISK_WRITABLE is not set
618# CONFIG_FEATURE_AIX_LABEL is not set
619# CONFIG_FEATURE_SGI_LABEL is not set
620# CONFIG_FEATURE_SUN_LABEL is not set
621# CONFIG_FEATURE_OSF_LABEL is not set
622# CONFIG_FEATURE_GPT_LABEL is not set
623# CONFIG_FEATURE_FDISK_ADVANCED is not set
624# CONFIG_FINDFS is not set
625# CONFIG_FLOCK is not set
626# CONFIG_FDFLUSH is not set
627# CONFIG_FREERAMDISK is not set
628# CONFIG_FSCK_MINIX is not set
629# CONFIG_FSFREEZE is not set
630# CONFIG_FSTRIM is not set
631CONFIG_GETOPT=y
632CONFIG_FEATURE_GETOPT_LONG=y
633CONFIG_HEXDUMP=y
634CONFIG_FEATURE_HEXDUMP_REVERSE=y
635CONFIG_HD=y
636CONFIG_XXD=y
637# CONFIG_HWCLOCK is not set
638# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
639# CONFIG_IONICE is not set
640# CONFIG_IPCRM is not set
641# CONFIG_IPCS is not set
642# CONFIG_LAST is not set
643# CONFIG_FEATURE_LAST_FANCY is not set
644# CONFIG_LOSETUP is not set
645# CONFIG_LSPCI is not set
646# CONFIG_LSUSB is not set
647# CONFIG_MDEV is not set
648# CONFIG_FEATURE_MDEV_CONF is not set
649# CONFIG_FEATURE_MDEV_RENAME is not set
650# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
651# CONFIG_FEATURE_MDEV_EXEC is not set
652# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
653# CONFIG_MESG is not set
654# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
655# CONFIG_MKE2FS is not set
656# CONFIG_MKFS_EXT2 is not set
657# CONFIG_MKFS_MINIX is not set
658# CONFIG_FEATURE_MINIX2 is not set
659# CONFIG_MKFS_REISER is not set
660# CONFIG_MKDOSFS is not set
661# CONFIG_MKFS_VFAT is not set
662# CONFIG_MKSWAP is not set
663# CONFIG_FEATURE_MKSWAP_UUID is not set
664# CONFIG_MORE is not set
665# CONFIG_MOUNT is not set
666# CONFIG_FEATURE_MOUNT_FAKE is not set
667# CONFIG_FEATURE_MOUNT_VERBOSE is not set
668# CONFIG_FEATURE_MOUNT_HELPERS is not set
669# CONFIG_FEATURE_MOUNT_LABEL is not set
670# CONFIG_FEATURE_MOUNT_NFS is not set
671# CONFIG_FEATURE_MOUNT_CIFS is not set
672# CONFIG_FEATURE_MOUNT_FLAGS is not set
673# CONFIG_FEATURE_MOUNT_FSTAB is not set
674# CONFIG_FEATURE_MOUNT_OTHERTAB is not set
675# CONFIG_MOUNTPOINT is not set
676# CONFIG_NOLOGIN is not set
677# CONFIG_NOLOGIN_DEPENDENCIES is not set
678# CONFIG_NSENTER is not set
679# CONFIG_PIVOT_ROOT is not set
680# CONFIG_RDATE is not set
681# CONFIG_RDEV is not set
682# CONFIG_READPROFILE is not set
683# CONFIG_RENICE is not set
684CONFIG_REV=y
685# CONFIG_RTCWAKE is not set
686# CONFIG_SCRIPT is not set
687# CONFIG_SCRIPTREPLAY is not set
688# CONFIG_SETARCH is not set
689# CONFIG_LINUX32 is not set
690# CONFIG_LINUX64 is not set
691# CONFIG_SETPRIV is not set
692# CONFIG_FEATURE_SETPRIV_DUMP is not set
693# CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set
694# CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set
695# CONFIG_SETSID is not set
696# CONFIG_SWAPON is not set
697# CONFIG_FEATURE_SWAPON_DISCARD is not set
698# CONFIG_FEATURE_SWAPON_PRI is not set
699# CONFIG_SWAPOFF is not set
700# CONFIG_FEATURE_SWAPONOFF_LABEL is not set
701# CONFIG_SWITCH_ROOT is not set
702# CONFIG_TASKSET is not set
703# CONFIG_FEATURE_TASKSET_FANCY is not set
704# CONFIG_UEVENT is not set
705# CONFIG_UMOUNT is not set
706# CONFIG_FEATURE_UMOUNT_ALL is not set
707# CONFIG_UNSHARE is not set
708# CONFIG_WALL is not set
709# CONFIG_FEATURE_MOUNT_LOOP is not set
710# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
711# CONFIG_FEATURE_MTAB_SUPPORT is not set
712# CONFIG_VOLUMEID is not set
713# CONFIG_FEATURE_VOLUMEID_BCACHE is not set
714# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
715# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
716# CONFIG_FEATURE_VOLUMEID_EXFAT is not set
717# CONFIG_FEATURE_VOLUMEID_EXT is not set
718# CONFIG_FEATURE_VOLUMEID_F2FS is not set
719# CONFIG_FEATURE_VOLUMEID_FAT is not set
720# CONFIG_FEATURE_VOLUMEID_HFS is not set
721# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
722# CONFIG_FEATURE_VOLUMEID_JFS is not set
723# CONFIG_FEATURE_VOLUMEID_LFS is not set
724# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
725# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
726# CONFIG_FEATURE_VOLUMEID_LUKS is not set
727# CONFIG_FEATURE_VOLUMEID_MINIX is not set
728# CONFIG_FEATURE_VOLUMEID_NILFS is not set
729# CONFIG_FEATURE_VOLUMEID_NTFS is not set
730# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
731# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
732# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
733# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set
734# CONFIG_FEATURE_VOLUMEID_SYSV is not set
735# CONFIG_FEATURE_VOLUMEID_UBIFS is not set
736# CONFIG_FEATURE_VOLUMEID_UDF is not set
737# CONFIG_FEATURE_VOLUMEID_XFS is not set
738
739#
740# Miscellaneous Utilities
741#
742# CONFIG_ADJTIMEX is not set
743# CONFIG_BBCONFIG is not set
744# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
745# CONFIG_BC is not set
746CONFIG_DC=y
747# CONFIG_FEATURE_DC_BIG is not set
748CONFIG_FEATURE_DC_LIBM=y
749# CONFIG_FEATURE_BC_INTERACTIVE is not set
750# CONFIG_FEATURE_BC_LONG_OPTIONS is not set
751# CONFIG_BEEP is not set
752CONFIG_FEATURE_BEEP_FREQ=0
753CONFIG_FEATURE_BEEP_LENGTH_MS=0
754# CONFIG_CHAT is not set
755# CONFIG_FEATURE_CHAT_NOFAIL is not set
756# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
757# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
758# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
759# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
760# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
761# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
762# CONFIG_CONSPY is not set
763# CONFIG_CROND is not set
764# CONFIG_FEATURE_CROND_D is not set
765# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
766# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set
767CONFIG_FEATURE_CROND_DIR=""
768# CONFIG_CRONTAB is not set
769# CONFIG_DEVFSD is not set
770# CONFIG_DEVFSD_MODLOAD is not set
771# CONFIG_DEVFSD_FG_NP is not set
772# CONFIG_DEVFSD_VERBOSE is not set
773# CONFIG_FEATURE_DEVFS is not set
774# CONFIG_DEVMEM is not set
775# CONFIG_FBSPLASH is not set
776# CONFIG_FLASHCP is not set
777# CONFIG_FLASH_ERASEALL is not set
778# CONFIG_FLASH_LOCK is not set
779# CONFIG_FLASH_UNLOCK is not set
780# CONFIG_HDPARM is not set
781# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
782# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
783# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
784# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
785# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
786# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
787# CONFIG_HEXEDIT is not set
788# CONFIG_I2CGET is not set
789# CONFIG_I2CSET is not set
790# CONFIG_I2CDUMP is not set
791# CONFIG_I2CDETECT is not set
792# CONFIG_I2CTRANSFER is not set
793CONFIG_ICONV=y
794# CONFIG_INOTIFYD is not set
795CONFIG_LESS=y
796CONFIG_FEATURE_LESS_MAXLINES=9999999
797CONFIG_FEATURE_LESS_BRACKETS=y
798CONFIG_FEATURE_LESS_FLAGS=y
799CONFIG_FEATURE_LESS_TRUNCATE=y
800CONFIG_FEATURE_LESS_MARKS=y
801CONFIG_FEATURE_LESS_REGEXP=y
802# CONFIG_FEATURE_LESS_WINCH is not set
803# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set
804CONFIG_FEATURE_LESS_DASHCMD=y
805CONFIG_FEATURE_LESS_LINENUMS=y
806CONFIG_FEATURE_LESS_RAW=y
807CONFIG_FEATURE_LESS_ENV=y
808# CONFIG_LSSCSI is not set
809# CONFIG_MAKEDEVS is not set
810# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
811# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
812CONFIG_MAN=y
813# CONFIG_MICROCOM is not set
814# CONFIG_MT is not set
815# CONFIG_NANDWRITE is not set
816# CONFIG_NANDDUMP is not set
817# CONFIG_PARTPROBE is not set
818# CONFIG_RAIDAUTORUN is not set
819# CONFIG_READAHEAD is not set
820# CONFIG_RFKILL is not set
821# CONFIG_RUNLEVEL is not set
822# CONFIG_RX is not set
823# CONFIG_SETFATTR is not set
824# CONFIG_SETSERIAL is not set
825CONFIG_STRINGS=y
826# CONFIG_TIME is not set
827CONFIG_TS=y
828CONFIG_TTYSIZE=y
829# CONFIG_UBIRENAME is not set
830# CONFIG_UBIATTACH is not set
831# CONFIG_UBIDETACH is not set
832# CONFIG_UBIMKVOL is not set
833# CONFIG_UBIRMVOL is not set
834# CONFIG_UBIRSVOL is not set
835# CONFIG_UBIUPDATEVOL is not set
836# CONFIG_VOLNAME is not set
837# CONFIG_WATCHDOG is not set
838
839#
840# Networking Utilities
841#
842CONFIG_FEATURE_IPV6=y
843# CONFIG_FEATURE_UNIX_LOCAL is not set
844CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
845# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
846# CONFIG_FEATURE_TLS_SHA1 is not set
847# CONFIG_ARP is not set
848# CONFIG_ARPING is not set
849# CONFIG_BRCTL is not set
850# CONFIG_FEATURE_BRCTL_FANCY is not set
851# CONFIG_FEATURE_BRCTL_SHOW is not set
852# CONFIG_DNSD is not set
853# CONFIG_ETHER_WAKE is not set
854# CONFIG_FTPD is not set
855# CONFIG_FEATURE_FTPD_WRITE is not set
856# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
857# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set
858CONFIG_FTPGET=y
859CONFIG_FTPPUT=y
860CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
861# CONFIG_HOSTNAME is not set
862# CONFIG_DNSDOMAINNAME is not set
863# CONFIG_HTTPD is not set
864# CONFIG_FEATURE_HTTPD_RANGES is not set
865# CONFIG_FEATURE_HTTPD_SETUID is not set
866# CONFIG_FEATURE_HTTPD_BASIC_AUTH is not set
867# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
868# CONFIG_FEATURE_HTTPD_CGI is not set
869# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
870# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
871# CONFIG_FEATURE_HTTPD_ENCODE_URL_STR is not set
872# CONFIG_FEATURE_HTTPD_ERROR_PAGES is not set
873# CONFIG_FEATURE_HTTPD_PROXY is not set
874# CONFIG_FEATURE_HTTPD_GZIP is not set
875# CONFIG_IFCONFIG is not set
876# CONFIG_FEATURE_IFCONFIG_STATUS is not set
877# CONFIG_FEATURE_IFCONFIG_SLIP is not set
878# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
879# CONFIG_FEATURE_IFCONFIG_HW is not set
880# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
881# CONFIG_IFENSLAVE is not set
882# CONFIG_IFPLUGD is not set
883# CONFIG_IFUP is not set
884# CONFIG_IFDOWN is not set
885CONFIG_IFUPDOWN_IFSTATE_PATH=""
886# CONFIG_FEATURE_IFUPDOWN_IP is not set
887# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
888# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
889# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
890# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
891# CONFIG_INETD is not set
892# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
893# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
894# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
895# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
896# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
897# CONFIG_FEATURE_INETD_RPC is not set
898# CONFIG_IP is not set
899# CONFIG_IPADDR is not set
900# CONFIG_IPLINK is not set
901# CONFIG_IPROUTE is not set
902# CONFIG_IPTUNNEL is not set
903# CONFIG_IPRULE is not set
904# CONFIG_IPNEIGH is not set
905# CONFIG_FEATURE_IP_ADDRESS is not set
906# CONFIG_FEATURE_IP_LINK is not set
907# CONFIG_FEATURE_IP_ROUTE is not set
908CONFIG_FEATURE_IP_ROUTE_DIR=""
909# CONFIG_FEATURE_IP_TUNNEL is not set
910# CONFIG_FEATURE_IP_RULE is not set
911# CONFIG_FEATURE_IP_NEIGH is not set
912# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
913CONFIG_IPCALC=y
914CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
915CONFIG_FEATURE_IPCALC_FANCY=y
916# CONFIG_FAKEIDENTD is not set
917# CONFIG_NAMEIF is not set
918# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
919# CONFIG_NBDCLIENT is not set
920CONFIG_NC=y
921# CONFIG_NETCAT is not set
922CONFIG_NC_SERVER=y
923# CONFIG_NC_EXTRA is not set
924# CONFIG_NC_110_COMPAT is not set
925# CONFIG_NETSTAT is not set
926# CONFIG_FEATURE_NETSTAT_WIDE is not set
927# CONFIG_FEATURE_NETSTAT_PRG is not set
928# CONFIG_NSLOOKUP is not set
929# CONFIG_FEATURE_NSLOOKUP_BIG is not set
930# CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS is not set
931# CONFIG_NTPD is not set
932# CONFIG_FEATURE_NTPD_SERVER is not set
933# CONFIG_FEATURE_NTPD_CONF is not set
934# CONFIG_FEATURE_NTP_AUTH is not set
935# CONFIG_PING is not set
936# CONFIG_PING6 is not set
937# CONFIG_FEATURE_FANCY_PING is not set
938# CONFIG_PSCAN is not set
939# CONFIG_ROUTE is not set
940# CONFIG_SLATTACH is not set
941CONFIG_SSL_CLIENT=y
942# CONFIG_TC is not set
943# CONFIG_FEATURE_TC_INGRESS is not set
944# CONFIG_TCPSVD is not set
945# CONFIG_UDPSVD is not set
946# CONFIG_TELNET is not set
947# CONFIG_FEATURE_TELNET_TTYPE is not set
948# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
949# CONFIG_FEATURE_TELNET_WIDTH is not set
950# CONFIG_TELNETD is not set
951# CONFIG_FEATURE_TELNETD_STANDALONE is not set
952# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
953# CONFIG_TFTP is not set
954# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
955# CONFIG_TFTPD is not set
956# CONFIG_FEATURE_TFTP_GET is not set
957# CONFIG_FEATURE_TFTP_PUT is not set
958# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
959# CONFIG_TFTP_DEBUG is not set
960CONFIG_TLS=y
961# CONFIG_TRACEROUTE is not set
962# CONFIG_TRACEROUTE6 is not set
963# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
964# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
965# CONFIG_TUNCTL is not set
966# CONFIG_FEATURE_TUNCTL_UG is not set
967# CONFIG_VCONFIG is not set
968CONFIG_WGET=y
969CONFIG_FEATURE_WGET_LONG_OPTIONS=y
970# CONFIG_FEATURE_WGET_STATUSBAR is not set
971CONFIG_FEATURE_WGET_AUTHENTICATION=y
972# CONFIG_FEATURE_WGET_TIMEOUT is not set
973CONFIG_FEATURE_WGET_HTTPS=y
974# CONFIG_FEATURE_WGET_OPENSSL is not set
975CONFIG_WHOIS=y
976# CONFIG_ZCIP is not set
977# CONFIG_UDHCPD is not set
978# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
979# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
980CONFIG_DHCPD_LEASES_FILE=""
981# CONFIG_DUMPLEASES is not set
982# CONFIG_DHCPRELAY is not set
983# CONFIG_UDHCPC is not set
984# CONFIG_FEATURE_UDHCPC_ARPING is not set
985# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set
986CONFIG_UDHCPC_DEFAULT_SCRIPT=""
987# CONFIG_UDHCPC6 is not set
988# CONFIG_FEATURE_UDHCPC6_RFC3646 is not set
989# CONFIG_FEATURE_UDHCPC6_RFC4704 is not set
990# CONFIG_FEATURE_UDHCPC6_RFC4833 is not set
991# CONFIG_FEATURE_UDHCPC6_RFC5970 is not set
992# CONFIG_FEATURE_UDHCP_PORT is not set
993CONFIG_UDHCP_DEBUG=0
994CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
995# CONFIG_FEATURE_UDHCP_RFC3397 is not set
996# CONFIG_FEATURE_UDHCP_8021Q is not set
997CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
998
999#
1000# Print Utilities
1001#
1002# CONFIG_LPD is not set
1003# CONFIG_LPR is not set
1004# CONFIG_LPQ is not set
1005
1006#
1007# Mail Utilities
1008#
1009# CONFIG_MAKEMIME is not set
1010# CONFIG_POPMAILDIR is not set
1011# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
1012# CONFIG_REFORMIME is not set
1013# CONFIG_FEATURE_REFORMIME_COMPAT is not set
1014# CONFIG_SENDMAIL is not set
1015CONFIG_FEATURE_MIME_CHARSET=""
1016
1017#
1018# Process Utilities
1019#
1020# CONFIG_FREE is not set
1021# CONFIG_FUSER is not set
1022# CONFIG_IOSTAT is not set
1023CONFIG_KILL=y
1024CONFIG_KILLALL=y
1025# CONFIG_KILLALL5 is not set
1026# CONFIG_LSOF is not set
1027# CONFIG_MPSTAT is not set
1028# CONFIG_NMETER is not set
1029CONFIG_PGREP=y
1030CONFIG_PKILL=y
1031CONFIG_PIDOF=y
1032CONFIG_FEATURE_PIDOF_SINGLE=y
1033CONFIG_FEATURE_PIDOF_OMIT=y
1034# CONFIG_PMAP is not set
1035# CONFIG_POWERTOP is not set
1036# CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set
1037CONFIG_PS=y
1038# CONFIG_FEATURE_PS_WIDE is not set
1039# CONFIG_FEATURE_PS_LONG is not set
1040CONFIG_FEATURE_PS_TIME=y
1041# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
1042# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
1043# CONFIG_PSTREE is not set
1044# CONFIG_PWDX is not set
1045# CONFIG_SMEMCAP is not set
1046# CONFIG_BB_SYSCTL is not set
1047# CONFIG_TOP is not set
1048# CONFIG_FEATURE_TOP_INTERACTIVE is not set
1049# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
1050# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
1051# CONFIG_FEATURE_TOP_SMP_CPU is not set
1052# CONFIG_FEATURE_TOP_DECIMALS is not set
1053# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
1054# CONFIG_FEATURE_TOPMEM is not set
1055# CONFIG_UPTIME is not set
1056# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
1057CONFIG_WATCH=y
1058# CONFIG_FEATURE_SHOW_THREADS is not set
1059
1060#
1061# Runit Utilities
1062#
1063# CONFIG_CHPST is not set
1064# CONFIG_SETUIDGID is not set
1065# CONFIG_ENVUIDGID is not set
1066# CONFIG_ENVDIR is not set
1067# CONFIG_SOFTLIMIT is not set
1068# CONFIG_RUNSV is not set
1069# CONFIG_RUNSVDIR is not set
1070# CONFIG_FEATURE_RUNSVDIR_LOG is not set
1071# CONFIG_SV is not set
1072CONFIG_SV_DEFAULT_SERVICE_DIR=""
1073# CONFIG_SVC is not set
1074# CONFIG_SVOK is not set
1075# CONFIG_SVLOGD is not set
1076# CONFIG_CHCON is not set
1077# CONFIG_GETENFORCE is not set
1078# CONFIG_GETSEBOOL is not set
1079# CONFIG_LOAD_POLICY is not set
1080# CONFIG_MATCHPATHCON is not set
1081# CONFIG_RUNCON is not set
1082# CONFIG_SELINUXENABLED is not set
1083# CONFIG_SESTATUS is not set
1084# CONFIG_SETENFORCE is not set
1085# CONFIG_SETFILES is not set
1086# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
1087# CONFIG_RESTORECON is not set
1088# CONFIG_SETSEBOOL is not set
1089
1090#
1091# Shells
1092#
1093CONFIG_SH_IS_ASH=y
1094# CONFIG_SH_IS_HUSH is not set
1095# CONFIG_SH_IS_NONE is not set
1096CONFIG_BASH_IS_ASH=y
1097# CONFIG_BASH_IS_HUSH is not set
1098# CONFIG_BASH_IS_NONE is not set
1099CONFIG_ASH=y
1100CONFIG_ASH_OPTIMIZE_FOR_SIZE=y
1101CONFIG_ASH_INTERNAL_GLOB=y
1102CONFIG_ASH_BASH_COMPAT=y
1103# CONFIG_ASH_BASH_SOURCE_CURDIR is not set
1104CONFIG_ASH_BASH_NOT_FOUND_HOOK=y
1105# CONFIG_ASH_JOB_CONTROL is not set
1106CONFIG_ASH_ALIAS=y
1107CONFIG_ASH_RANDOM_SUPPORT=y
1108CONFIG_ASH_EXPAND_PRMT=y
1109# CONFIG_ASH_IDLE_TIMEOUT is not set
1110# CONFIG_ASH_MAIL is not set
1111CONFIG_ASH_ECHO=y
1112CONFIG_ASH_PRINTF=y
1113CONFIG_ASH_TEST=y
1114CONFIG_ASH_HELP=y
1115CONFIG_ASH_GETOPTS=y
1116CONFIG_ASH_CMDCMD=y
1117CONFIG_ASH_NOCONSOLE=y
1118CONFIG_ASH_NOCASEGLOB=y
1119# CONFIG_CTTYHACK is not set
1120# CONFIG_HUSH is not set
1121# CONFIG_HUSH_BASH_COMPAT is not set
1122# CONFIG_HUSH_BRACE_EXPANSION is not set
1123# CONFIG_HUSH_LINENO_VAR is not set
1124# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set
1125# CONFIG_HUSH_INTERACTIVE is not set
1126# CONFIG_HUSH_SAVEHISTORY is not set
1127# CONFIG_HUSH_JOB is not set
1128# CONFIG_HUSH_TICK is not set
1129# CONFIG_HUSH_IF is not set
1130# CONFIG_HUSH_LOOPS is not set
1131# CONFIG_HUSH_CASE is not set
1132# CONFIG_HUSH_FUNCTIONS is not set
1133# CONFIG_HUSH_LOCAL is not set
1134# CONFIG_HUSH_RANDOM_SUPPORT is not set
1135# CONFIG_HUSH_MODE_X is not set
1136# CONFIG_HUSH_ECHO is not set
1137# CONFIG_HUSH_PRINTF is not set
1138# CONFIG_HUSH_TEST is not set
1139# CONFIG_HUSH_HELP is not set
1140# CONFIG_HUSH_EXPORT is not set
1141# CONFIG_HUSH_EXPORT_N is not set
1142# CONFIG_HUSH_READONLY is not set
1143# CONFIG_HUSH_KILL is not set
1144# CONFIG_HUSH_WAIT is not set
1145# CONFIG_HUSH_COMMAND is not set
1146# CONFIG_HUSH_TRAP is not set
1147# CONFIG_HUSH_TYPE is not set
1148# CONFIG_HUSH_TIMES is not set
1149# CONFIG_HUSH_READ is not set
1150# CONFIG_HUSH_SET is not set
1151# CONFIG_HUSH_UNSET is not set
1152# CONFIG_HUSH_ULIMIT is not set
1153# CONFIG_HUSH_UMASK is not set
1154# CONFIG_HUSH_GETOPTS is not set
1155# CONFIG_HUSH_MEMLEAK is not set
1156
1157#
1158# Options common to all shells
1159#
1160CONFIG_FEATURE_SH_MATH=y
1161CONFIG_FEATURE_SH_MATH_64=y
1162CONFIG_FEATURE_SH_EXTRA_QUIET=y
1163CONFIG_FEATURE_SH_STANDALONE=y
1164CONFIG_FEATURE_SH_NOFORK=y
1165# CONFIG_FEATURE_SH_READ_FRAC is not set
1166CONFIG_FEATURE_SH_HISTFILESIZE=y
1167CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y
1168
1169#
1170# System Logging Utilities
1171#
1172# CONFIG_KLOGD is not set
1173# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
1174# CONFIG_LOGGER is not set
1175# CONFIG_LOGREAD is not set
1176# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
1177# CONFIG_SYSLOGD is not set
1178# CONFIG_FEATURE_ROTATE_LOGFILE is not set
1179# CONFIG_FEATURE_REMOTE_LOG is not set
1180# CONFIG_FEATURE_SYSLOGD_DUP is not set
1181# CONFIG_FEATURE_SYSLOGD_CFG is not set
1182CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
1183# CONFIG_FEATURE_IPC_SYSLOG is not set
1184CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
1185# CONFIG_FEATURE_KMSG_SYSLOG is not set
diff --git a/console-tools/reset.c b/console-tools/reset.c
index 113bb5c76..2f65aad15 100644
--- a/console-tools/reset.c
+++ b/console-tools/reset.c
@@ -14,7 +14,10 @@
14//config: This program is used to reset the terminal screen, if it 14//config: This program is used to reset the terminal screen, if it
15//config: gets messed up. 15//config: gets messed up.
16 16
17//applet:IF_RESET(APPLET_NOEXEC(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset)) 17// NOTE: For WIN32 this applet is NOFORK so we can change the screen
18// buffer for the current process.
19
20//applet:IF_RESET(APPLET_NOFORK(reset, reset, BB_DIR_USR_BIN, BB_SUID_DROP, reset))
18 21
19//kbuild:lib-$(CONFIG_RESET) += reset.o 22//kbuild:lib-$(CONFIG_RESET) += reset.o
20 23
@@ -36,6 +39,7 @@ int stty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
36int reset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 39int reset_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
37int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 40int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
38{ 41{
42#if !ENABLE_PLATFORM_MINGW32
39 static const char *const args[] = { 43 static const char *const args[] = {
40 "stty", "sane", NULL 44 "stty", "sane", NULL
41 }; 45 };
@@ -61,5 +65,14 @@ int reset_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
61 execvp("stty", (char**)args); 65 execvp("stty", (char**)args);
62#endif 66#endif
63 } 67 }
68#else
69 if (isatty(STDOUT_FILENO)) {
70 // "ESC [ m" -- Reset all display attributes
71 // "ESC [ ? 1049 l" -- Use normal screen buffer
72 // reset_screen -- Reset cursor and clear screen buffer
73 full_write1_str(ESC"[m" ESC"[?1049l");
74 reset_screen();
75 }
76#endif
64 return EXIT_SUCCESS; 77 return EXIT_SUCCESS;
65} 78}
diff --git a/coreutils/dd.c b/coreutils/dd.c
index b5f3cbec5..3054ec6ea 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -91,6 +91,9 @@
91//usage: "\n status=none Suppress all output" 91//usage: "\n status=none Suppress all output"
92//usage: ) 92//usage: )
93//usage: "\n" 93//usage: "\n"
94//usage: IF_PLATFORM_MINGW32(
95//usage: "\nif=/dev/zero and if=/dev/urandom are supported"
96//usage: )
94//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G" 97//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G"
95//usage: 98//usage:
96//usage:#define dd_example_usage 99//usage:#define dd_example_usage
@@ -367,11 +370,11 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
367#if ENABLE_FEATURE_DD_IBS_OBS 370#if ENABLE_FEATURE_DD_IBS_OBS
368 if (what == OP_ibs) { 371 if (what == OP_ibs) {
369 /* Must fit into positive ssize_t */ 372 /* Must fit into positive ssize_t */
370 ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 373 ibs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
371 /*continue;*/ 374 /*continue;*/
372 } 375 }
373 if (what == OP_obs) { 376 if (what == OP_obs) {
374 obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 377 obs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
375 /*continue;*/ 378 /*continue;*/
376 } 379 }
377 if (what == OP_conv) { 380 if (what == OP_conv) {
@@ -388,7 +391,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
388 } 391 }
389#endif 392#endif
390 if (what == OP_bs) { 393 if (what == OP_bs) {
391 ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 394 ibs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
392 obs = ibs; 395 obs = ibs;
393 /*continue;*/ 396 /*continue;*/
394 } 397 }
@@ -444,7 +447,12 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
444#endif 447#endif
445 448
446 if (infile) { 449 if (infile) {
450#if !ENABLE_PLATFORM_MINGW32
447 xmove_fd(xopen(infile, O_RDONLY), ifd); 451 xmove_fd(xopen(infile, O_RDONLY), ifd);
452#else
453 xmove_fd(mingw_xopen(infile, O_RDONLY), ifd);
454 update_dev_fd(get_dev_type(infile), ifd);
455#endif
448 } else { 456 } else {
449 infile = bb_msg_standard_input; 457 infile = bb_msg_standard_input;
450 } 458 }
@@ -500,6 +508,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
500 508
501 while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) { 509 while (!(G.flags & FLAG_COUNT) || (G.in_full + G.in_part != count)) {
502 ssize_t n; 510 ssize_t n;
511
503#if ENABLE_FEATURE_DD_IBS_OBS 512#if ENABLE_FEATURE_DD_IBS_OBS
504 if (G.flags & FLAG_FULLBLOCK) 513 if (G.flags & FLAG_FULLBLOCK)
505 n = full_read(ifd, ibuf, ibs); 514 n = full_read(ifd, ibuf, ibs);
diff --git a/coreutils/du.c b/coreutils/du.c
index d14d9e4ea..4fd09a8ee 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 e5375a61a..3eff5a949 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -492,12 +492,18 @@ 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_POSIX || ENABLE_FEATURE_READLINK2
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#if !ENABLE_FEATURE_EXTRA_FILE_DATA
503 column += printf("%7"LL_FMT"u ", (long long) dn->dn_ino);
504#else
505 column += printf("%19"LL_FMT"u ", (long long) dn->dn_ino);
506#endif
501//TODO: -h should affect -s too: 507//TODO: -h should affect -s too:
502 if (opt & OPT_s) /* show allocated blocks */ 508 if (opt & OPT_s) /* show allocated blocks */
503 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); 509 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
@@ -651,7 +657,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
651 } 657 }
652 column_width += 2 658 column_width += 2
653 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */ 659 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
660#if !ENABLE_FEATURE_EXTRA_FILE_DATA
654 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */ 661 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
662#else
663 + ((option_mask32 & OPT_i) ? 20 : 0) /* inode# width */
664#endif
655 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */ 665 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
656 ; 666 ;
657 ncols = (unsigned)G_terminal_width / column_width; 667 ncols = (unsigned)G_terminal_width / column_width;
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 b6ab5205b..d9287b34e 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.
@@ -195,6 +194,7 @@ FS_TYPE(0x012FF7B4, "xenix") \
195FS_TYPE(0x012FF7B5, "sysv4") \ 194FS_TYPE(0x012FF7B5, "sysv4") \
196FS_TYPE(0x012FF7B6, "sysv2") \ 195FS_TYPE(0x012FF7B6, "sysv2") \
197FS_TYPE(0x012FF7B7, "coh") \ 196FS_TYPE(0x012FF7B7, "coh") \
197IF_PLATFORM_MINGW32(FS_TYPE(0x15013346, "udf")) \
198FS_TYPE(0x00011954, "ufs") \ 198FS_TYPE(0x00011954, "ufs") \
199FS_TYPE(0x012FD16D, "xia") \ 199FS_TYPE(0x012FD16D, "xia") \
200FS_TYPE(0x5346544e, "ntfs") \ 200FS_TYPE(0x5346544e, "ntfs") \
@@ -322,6 +322,7 @@ static void FAST_FUNC print_stat(char *pformat, const char m,
322 printfs(pformat, filename); 322 printfs(pformat, filename);
323 } else if (m == 'N') { 323 } else if (m == 'N') {
324 strcatc(pformat, 's'); 324 strcatc(pformat, 's');
325#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_READLINK2
325 if (S_ISLNK(statbuf->st_mode)) { 326 if (S_ISLNK(statbuf->st_mode)) {
326 char *linkname = xmalloc_readlink_or_warn(filename); 327 char *linkname = xmalloc_readlink_or_warn(filename);
327 if (linkname == NULL) 328 if (linkname == NULL)
@@ -331,6 +332,9 @@ static void FAST_FUNC print_stat(char *pformat, const char m,
331 } else { 332 } else {
332 printf(pformat, filename); 333 printf(pformat, filename);
333 } 334 }
335#else
336 printf(pformat, filename);
337#endif
334 } else if (m == 'd') { 338 } else if (m == 'd') {
335 strcat(pformat, "llu"); 339 strcat(pformat, "llu");
336 printf(pformat, (unsigned long long) statbuf->st_dev); 340 printf(pformat, (unsigned long long) statbuf->st_dev);
@@ -713,6 +717,7 @@ static bool do_stat(const char *filename, const char *format)
713 gw_ent = getgrgid(statbuf.st_gid); 717 gw_ent = getgrgid(statbuf.st_gid);
714 pw_ent = getpwuid(statbuf.st_uid); 718 pw_ent = getpwuid(statbuf.st_uid);
715 719
720#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_READLINK2
716 if (S_ISLNK(statbuf.st_mode)) 721 if (S_ISLNK(statbuf.st_mode))
717 linkname = xmalloc_readlink_or_warn(filename); 722 linkname = xmalloc_readlink_or_warn(filename);
718 if (linkname) { 723 if (linkname) {
@@ -721,6 +726,9 @@ static bool do_stat(const char *filename, const char *format)
721 } else { 726 } else {
722 printf(" File: '%s'\n", filename); 727 printf(" File: '%s'\n", filename);
723 } 728 }
729#else
730 printf(" File: '%s'\n", filename);
731#endif
724 732
725 printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n" 733 printf(" Size: %-10llu\tBlocks: %-10llu IO Block: %-6lu %s\n"
726 "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu", 734 "Device: %llxh/%llud\tInode: %-10llu Links: %-5lu",
diff --git a/coreutils/sum.c b/coreutils/sum.c
index 16ec44540..a15d1932d 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"LL_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..28c4fd70e 100644
--- a/debianutils/pipe_progress.c
+++ b/debianutils/pipe_progress.c
@@ -16,8 +16,11 @@
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 IF_PLATFORM_POSIX(NOUSAGE_STR)
20//usage:#define pipe_progress_full_usage "" 20//usage: IF_PLATFORM_MINGW32("")
21//usage:#define pipe_progress_full_usage IF_PLATFORM_POSIX("")
22//usage: IF_PLATFORM_MINGW32("\n\n")
23//usage: IF_PLATFORM_MINGW32("Display a dot to indicate pipe activity")
21 24
22#include "libbb.h" 25#include "libbb.h"
23 26
diff --git a/debianutils/which.c b/debianutils/which.c
index 98876521f..61f5770c5 100644
--- a/debianutils/which.c
+++ b/debianutils/which.c
@@ -45,21 +45,55 @@ 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 bs_to_slash(p);
73 puts(p);
74 }
75 else
76#endif
51 if (file_is_executable(*argv)) { 77 if (file_is_executable(*argv)) {
52 missing = 0; 78 missing = 0;
79#if ENABLE_PLATFORM_MINGW32
80 p = auto_string(xstrdup(*argv));
81 bs_to_slash(p);
82 puts(p);
83#else
53 puts(*argv); 84 puts(*argv);
85#endif
54 } 86 }
55 } else { 87 } else {
56 char *path; 88 char *path;
57 char *p;
58 89
59 path = env_path; 90 path = env_path;
60 /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */ 91 /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */
61 while ((p = find_executable(*argv, &path)) != NULL) { 92 while ((p = find_executable(*argv, &path)) != NULL) {
62 missing = 0; 93 missing = 0;
94#if ENABLE_PLATFORM_MINGW32
95 bs_to_slash(p);
96#endif
63 puts(p); 97 puts(p);
64 free(p); 98 free(p);
65 if (!option_mask32) /* -a not set */ 99 if (!option_mask32) /* -a not set */
diff --git a/editors/awk.c b/editors/awk.c
index d25508e5d..c4553728d 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -734,7 +734,11 @@ static char *skip_spaces(char *p)
734 if (*p == '\\' && p[1] == '\n') { 734 if (*p == '\\' && p[1] == '\n') {
735 p++; 735 p++;
736 t_lineno++; 736 t_lineno++;
737#if !ENABLE_PLATFORM_MINGW32
737 } else if (*p != ' ' && *p != '\t') { 738 } else if (*p != ' ' && *p != '\t') {
739#else
740 } else if (*p != ' ' && *p != '\t' && *p != '\r') {
741#endif
738 break; 742 break;
739 } 743 }
740 p++; 744 p++;
@@ -1991,6 +1995,21 @@ static int ptest(node *pattern)
1991 return istrue(evaluate(pattern, &G.ptest__v)); 1995 return istrue(evaluate(pattern, &G.ptest__v));
1992} 1996}
1993 1997
1998#if ENABLE_PLATFORM_MINGW32
1999static ssize_t FAST_FUNC safe_read_strip_cr(int fd, void *buf, size_t count)
2000{
2001 ssize_t n;
2002
2003 do {
2004 n = safe_read(fd, buf, count);
2005 } while (n > 0 && (n=remove_cr((char *)buf, n)) == 0);
2006
2007 return n;
2008}
2009
2010#define safe_read safe_read_strip_cr
2011#endif
2012
1994/* read next record from stream rsm into a variable v */ 2013/* read next record from stream rsm into a variable v */
1995static int awk_getline(rstream *rsm, var *v) 2014static int awk_getline(rstream *rsm, var *v)
1996{ 2015{
@@ -2100,7 +2119,7 @@ static int fmt_num(char *b, int size, const char *format, double n, int int_as_i
2100 const char *s = format; 2119 const char *s = format;
2101 2120
2102 if (int_as_int && n == (long long)n) { 2121 if (int_as_int && n == (long long)n) {
2103 r = snprintf(b, size, "%lld", (long long)n); 2122 r = snprintf(b, size, "%"LL_FMT"d", (long long)n);
2104 } else { 2123 } else {
2105 do { c = *s; } while (c && *++s); 2124 do { c = *s; } while (c && *++s);
2106 if (strchr("diouxX", c)) { 2125 if (strchr("diouxX", c)) {
diff --git a/editors/diff.c b/editors/diff.c
index 1462a9b18..cf531460f 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}
@@ -1019,6 +1040,11 @@ int diff_main(int argc UNUSED_PARAM, char **argv)
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
1043 && (ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_EXTRA_FILE_DATA)
1044#if ENABLE_FEATURE_EXTRA_FILE_DATA
1045 /* ignore invalid inode numbers */
1046 && stb[0].st_ino != 0
1047#endif
1022 && stb[0].st_ino == stb[1].st_ino 1048 && stb[0].st_ino == stb[1].st_ino
1023 && stb[0].st_dev == stb[1].st_dev 1049 && stb[0].st_dev == stb[1].st_dev
1024 && stb[0].st_size == stb[1].st_size 1050 && stb[0].st_size == stb[1].st_size
diff --git a/editors/sed.c b/editors/sed.c
index 57d3dda16..a93e5494a 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);
@@ -1500,7 +1511,12 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1500 "quiet\0" No_argument "n" 1511 "quiet\0" No_argument "n"
1501 "silent\0" No_argument "n" 1512 "silent\0" No_argument "n"
1502 "expression\0" Required_argument "e" 1513 "expression\0" Required_argument "e"
1514# if !ENABLE_PLATFORM_MINGW32
1503 "file\0" Required_argument "f"; 1515 "file\0" Required_argument "f";
1516# else
1517 "file\0" Required_argument "f"
1518 "binary\0" No_argument "b";
1519# endif
1504#endif 1520#endif
1505 1521
1506 INIT_G(); 1522 INIT_G();
@@ -1524,6 +1540,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1524 */ 1540 */
1525 opt = getopt32long(argv, "^" 1541 opt = getopt32long(argv, "^"
1526 "i::rEne:*f:*" 1542 "i::rEne:*f:*"
1543 IF_PLATFORM_MINGW32("b")
1527 "\0" "nn"/*count -n*/, 1544 "\0" "nn"/*count -n*/,
1528 sed_longopts, 1545 sed_longopts,
1529 &opt_i, &opt_e, &opt_f, 1546 &opt_i, &opt_e, &opt_f,
@@ -1537,6 +1554,10 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1537 G.regex_type |= REG_EXTENDED; // -r or -E 1554 G.regex_type |= REG_EXTENDED; // -r or -E
1538 //if (opt & 8) 1555 //if (opt & 8)
1539 // G.be_quiet++; // -n (implemented with a counter instead) 1556 // G.be_quiet++; // -n (implemented with a counter instead)
1557#if ENABLE_PLATFORM_MINGW32
1558 if (opt & 0x40)
1559 G.keep_cr = 1;
1560#endif
1540 while (opt_e) { // -e 1561 while (opt_e) { // -e
1541 add_cmd_block(llist_pop(&opt_e)); 1562 add_cmd_block(llist_pop(&opt_e));
1542 } 1563 }
diff --git a/editors/vi.c b/editors/vi.c
index 8af1ef76b..70c8f3daf 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -349,7 +349,9 @@ struct globals {
349#if ENABLE_FEATURE_VI_USE_SIGNALS 349#if ENABLE_FEATURE_VI_USE_SIGNALS
350 sigjmp_buf restart; // int_handler() jumps to location remembered here 350 sigjmp_buf restart; // int_handler() jumps to location remembered here
351#endif 351#endif
352#if !ENABLE_PLATFORM_MINGW32
352 struct termios term_orig; // remember what the cooked mode was 353 struct termios term_orig; // remember what the cooked mode was
354#endif
353#if ENABLE_FEATURE_VI_COLON 355#if ENABLE_FEATURE_VI_COLON
354 char *initial_cmds[3]; // currently 2 entries, NULL terminated 356 char *initial_cmds[3]; // currently 2 entries, NULL terminated
355#endif 357#endif
@@ -545,6 +547,23 @@ static ALWAYS_INLINE int query_screen_dimensions(void)
545// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready) 547// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
546static int mysleep(int hund) 548static int mysleep(int hund)
547{ 549{
550#if ENABLE_PLATFORM_MINGW32
551 HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
552 DWORD ret;
553
554 if (hund == 0) {
555 // Allow two events in the queue. Otherwise pasted test isn't
556 // displayed because there's still a key release event waiting
557 // after the last character is processed.
558 DWORD nevent_out;
559
560 ret = GetNumberOfConsoleInputEvents(h, &nevent_out);
561 return ret != 0 ? (nevent_out > 2) : 0;
562 }
563 fflush_all();
564 ret = WaitForSingleObject(h, hund*10);
565 return ret != WAIT_TIMEOUT;
566#else
548 struct pollfd pfd[1]; 567 struct pollfd pfd[1];
549 568
550 if (hund != 0) 569 if (hund != 0)
@@ -553,9 +572,11 @@ static int mysleep(int hund)
553 pfd[0].fd = STDIN_FILENO; 572 pfd[0].fd = STDIN_FILENO;
554 pfd[0].events = POLLIN; 573 pfd[0].events = POLLIN;
555 return safe_poll(pfd, 1, hund*10) > 0; 574 return safe_poll(pfd, 1, hund*10) > 0;
575#endif
556} 576}
557 577
558//----- Set terminal attributes -------------------------------- 578//----- Set terminal attributes --------------------------------
579#if !ENABLE_PLATFORM_MINGW32
559static void rawmode(void) 580static void rawmode(void)
560{ 581{
561 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals 582 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
@@ -567,6 +588,10 @@ static void cookmode(void)
567 fflush_all(); 588 fflush_all();
568 tcsetattr_stdin_TCSANOW(&term_orig); 589 tcsetattr_stdin_TCSANOW(&term_orig);
569} 590}
591#else
592#define rawmode() ((void)0)
593#define cookmode fflush_all
594#endif
570 595
571//----- Terminal Drawing --------------------------------------- 596//----- Terminal Drawing ---------------------------------------
572// The terminal is made up of 'rows' line of 'columns' columns. 597// The terminal is made up of 'rows' line of 'columns' columns.
@@ -1105,7 +1130,11 @@ static char *get_input_line(const char *prompt)
1105 c = get_one_char(); 1130 c = get_one_char();
1106 if (c == '\n' || c == '\r' || c == 27) 1131 if (c == '\n' || c == '\r' || c == 27)
1107 break; // this is end of input 1132 break; // this is end of input
1133#if !ENABLE_PLATFORM_MINGW32
1108 if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { 1134 if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) {
1135#else
1136 if (c == 8 || c == 127) {
1137#endif
1109 // user wants to erase prev char 1138 // user wants to erase prev char
1110 buf[--i] = '\0'; 1139 buf[--i] = '\0';
1111 write1("\b \b"); // erase char on screen 1140 write1("\b \b"); // erase char on screen
@@ -1858,6 +1887,18 @@ static char *yank_delete(char *start, char *stop, int dist, int yf, int undo)
1858 return p; 1887 return p;
1859} 1888}
1860 1889
1890#if ENABLE_PLATFORM_MINGW32
1891static int count_cr(char *p, int len)
1892{
1893 int i, cnt;
1894
1895 for (i = cnt = 0; i < len; ++i)
1896 if (p[i] == '\n')
1897 ++cnt;
1898 return cnt;
1899}
1900#endif
1901
1861// might reallocate text[]! 1902// might reallocate text[]!
1862static int file_insert(const char *fn, char *p, int initial) 1903static int file_insert(const char *fn, char *p, int initial)
1863{ 1904{
@@ -1870,7 +1911,11 @@ static int file_insert(const char *fn, char *p, int initial)
1870 if (p > end) 1911 if (p > end)
1871 p = end; 1912 p = end;
1872 1913
1914#if !ENABLE_PLATFORM_MINGW32
1873 fd = open(fn, O_RDONLY); 1915 fd = open(fn, O_RDONLY);
1916#else
1917 fd = open(fn, O_RDONLY | _O_TEXT);
1918#endif
1874 if (fd < 0) { 1919 if (fd < 0) {
1875 if (!initial) 1920 if (!initial)
1876 status_line_bold_errno(fn); 1921 status_line_bold_errno(fn);
@@ -1893,8 +1938,15 @@ static int file_insert(const char *fn, char *p, int initial)
1893 status_line_bold_errno(fn); 1938 status_line_bold_errno(fn);
1894 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert 1939 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert
1895 } else if (cnt < size) { 1940 } else if (cnt < size) {
1941#if ENABLE_PLATFORM_MINGW32
1942 // On WIN32 a partial read might just mean CRs have been removed
1943 int cnt_cr = cnt + count_cr(p, cnt);
1944#endif
1896 // There was a partial read, shrink unused space 1945 // There was a partial read, shrink unused space
1897 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); 1946 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
1947#if ENABLE_PLATFORM_MINGW32
1948 if (cnt_cr < size)
1949#endif
1898 status_line_bold("can't read '%s'", fn); 1950 status_line_bold("can't read '%s'", fn);
1899 } 1951 }
1900 fi: 1952 fi:
@@ -2001,7 +2053,11 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2001 if ((p[-1] != '\n') && (dot > text)) { 2053 if ((p[-1] != '\n') && (dot > text)) {
2002 p--; 2054 p--;
2003 } 2055 }
2056#if !ENABLE_PLATFORM_MINGW32
2004 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS 2057 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS
2058#else
2059 } else if (c == 8 || c == 127) { // Is this a BS
2060#endif
2005 if (p > text) { 2061 if (p > text) {
2006 p--; 2062 p--;
2007 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char 2063 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char
@@ -2122,12 +2178,22 @@ static int file_write(char *fn, char *first, char *last)
2122 // By popular request we do not open file with O_TRUNC, 2178 // By popular request we do not open file with O_TRUNC,
2123 // but instead ftruncate() it _after_ successful write. 2179 // but instead ftruncate() it _after_ successful write.
2124 // Might reduce amount of data lost on power fail etc. 2180 // Might reduce amount of data lost on power fail etc.
2181#if !ENABLE_PLATFORM_MINGW32
2125 fd = open(fn, (O_WRONLY | O_CREAT), 0666); 2182 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
2183#else
2184 fd = open(fn, (O_WRONLY | O_CREAT | _O_TEXT), 0666);
2185#endif
2126 if (fd < 0) 2186 if (fd < 0)
2127 return -1; 2187 return -1;
2128 cnt = last - first + 1; 2188 cnt = last - first + 1;
2129 charcnt = full_write(fd, first, cnt); 2189 charcnt = full_write(fd, first, cnt);
2190#if !ENABLE_PLATFORM_MINGW32
2130 ftruncate(fd, charcnt); 2191 ftruncate(fd, charcnt);
2192#else
2193 // File was written in text mode; this makes it bigger so adjust
2194 // the truncation to match.
2195 ftruncate(fd, charcnt + count_cr(first, cnt));
2196#endif
2131 if (charcnt == cnt) { 2197 if (charcnt == cnt) {
2132 // good write 2198 // good write
2133 //modified_count = FALSE; 2199 //modified_count = FALSE;
diff --git a/findutils/find.c b/findutils/find.c
index 06ad1b39c..8706e2cf3 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -103,7 +103,7 @@
103//config:config FEATURE_FIND_XDEV 103//config:config FEATURE_FIND_XDEV
104//config: bool "Enable -xdev: 'stay in filesystem'" 104//config: bool "Enable -xdev: 'stay in filesystem'"
105//config: default y 105//config: default y
106//config: depends on FIND 106//config: depends on FIND && (PLATFORM_POSIX || FEATURE_EXTRA_FILE_DATA)
107//config: 107//config:
108//config:config FEATURE_FIND_MAXDEPTH 108//config:config FEATURE_FIND_MAXDEPTH
109//config: bool "Enable -mindepth N and -maxdepth N" 109//config: bool "Enable -mindepth N and -maxdepth N"
@@ -121,7 +121,7 @@
121//config:config FEATURE_FIND_INUM 121//config:config FEATURE_FIND_INUM
122//config: bool "Enable -inum: inode number matching" 122//config: bool "Enable -inum: inode number matching"
123//config: default y 123//config: default y
124//config: depends on FIND 124//config: depends on FIND && (PLATFORM_POSIX || FEATURE_EXTRA_FILE_DATA)
125//config: 125//config:
126//config:config FEATURE_FIND_EXEC 126//config:config FEATURE_FIND_EXEC
127//config: bool "Enable -exec: execute commands" 127//config: bool "Enable -exec: execute commands"
@@ -227,7 +227,7 @@
227//config:config FEATURE_FIND_LINKS 227//config:config FEATURE_FIND_LINKS
228//config: bool "Enable -links: link count matching" 228//config: bool "Enable -links: link count matching"
229//config: default y 229//config: default y
230//config: depends on FIND 230//config: depends on FIND && (PLATFORM_POSIX || FEATURE_EXTRA_FILE_DATA)
231//config: help 231//config: help
232//config: Support the 'find -links' option for matching number of links. 232//config: Support the 'find -links' option for matching number of links.
233 233
@@ -1361,7 +1361,11 @@ static action*** parse_params(char **argv)
1361 action_inum *ap; 1361 action_inum *ap;
1362 dbg("%d", __LINE__); 1362 dbg("%d", __LINE__);
1363 ap = ALLOC_ACTION(inum); 1363 ap = ALLOC_ACTION(inum);
1364# if !ENABLE_FEATURE_EXTRA_FILE_DATA
1364 ap->inode_num = xatoul(arg1); 1365 ap->inode_num = xatoul(arg1);
1366# else
1367 ap->inode_num = xatoull(arg1);
1368# endif
1365 } 1369 }
1366#endif 1370#endif
1367#if ENABLE_FEATURE_FIND_USER 1371#if ENABLE_FEATURE_FIND_USER
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 03eafd616..8331931bc 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 9b1db5b3e..58e4c21bb 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 {
@@ -203,11 +213,18 @@ void seek_by_jump(int fd, off_t amount) FAST_FUNC;
203void seek_by_read(int fd, off_t amount) FAST_FUNC; 213void seek_by_read(int fd, off_t amount) FAST_FUNC;
204 214
205const char *strip_unsafe_prefix(const char *str) FAST_FUNC; 215const char *strip_unsafe_prefix(const char *str) FAST_FUNC;
216#if ENABLE_PLATFORM_MINGW32
217#define create_or_remember_link(lp, t, ln, hl) create_or_remember_link(t, ln)
218#endif
206void create_or_remember_link(llist_t **link_placeholders, 219void create_or_remember_link(llist_t **link_placeholders,
207 const char *target, 220 const char *target,
208 const char *linkname, 221 const char *linkname,
209 int hard_link) FAST_FUNC; 222 int hard_link) FAST_FUNC;
223#if !ENABLE_PLATFORM_MINGW32
210void create_links_from_list(llist_t *list) FAST_FUNC; 224void create_links_from_list(llist_t *list) FAST_FUNC;
225#else
226#define create_links_from_list(l) (void)0
227#endif
211 228
212void data_align(archive_handle_t *archive_handle, unsigned boundary) FAST_FUNC; 229void 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; 230const llist_t *find_list_entry(const llist_t *list, const char *filename) FAST_FUNC;
@@ -277,7 +294,11 @@ enum {
277 BBUNPK_SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION, 294 BBUNPK_SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION,
278}; 295};
279 296
297#if !ENABLE_PLATFORM_MINGW32
280void check_errors_in_children(int signo); 298void check_errors_in_children(int signo);
299#else
300#define check_errors_in_children(s) ((void)0)
301#endif
281#if BB_MMU 302#if BB_MMU
282void fork_transformer(int fd, 303void fork_transformer(int fd,
283 int signature_skipped, 304 int signature_skipped,
diff --git a/include/libbb.h b/include/libbb.h
index 33766e989..9da94638b 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
@@ -1132,6 +1171,7 @@ void exec_prog_or_SHELL(char **argv) NORETURN FAST_FUNC;
1132 1171
1133/* xvfork() can't be a _function_, return after vfork in child mangles stack 1172/* xvfork() can't be a _function_, return after vfork in child mangles stack
1134 * in the parent. It must be a macro. */ 1173 * in the parent. It must be a macro. */
1174#if !ENABLE_PLATFORM_MINGW32
1135#define xvfork() \ 1175#define xvfork() \
1136({ \ 1176({ \
1137 pid_t bb__xvfork_pid = vfork(); \ 1177 pid_t bb__xvfork_pid = vfork(); \
@@ -1139,6 +1179,9 @@ void exec_prog_or_SHELL(char **argv) NORETURN FAST_FUNC;
1139 bb_perror_msg_and_die("vfork"); \ 1179 bb_perror_msg_and_die("vfork"); \
1140 bb__xvfork_pid; \ 1180 bb__xvfork_pid; \
1141}) 1181})
1182#else
1183#define xvfork() vfork()
1184#endif
1142#if BB_MMU 1185#if BB_MMU
1143pid_t xfork(void) FAST_FUNC; 1186pid_t xfork(void) FAST_FUNC;
1144#endif 1187#endif
@@ -1683,9 +1726,15 @@ int bb_xioctl(int fd, unsigned request, void *argp) FAST_FUNC;
1683#define xioctl(fd,request,argp) bb_xioctl(fd,request,argp) 1726#define xioctl(fd,request,argp) bb_xioctl(fd,request,argp)
1684#endif 1727#endif
1685 1728
1729#if !ENABLE_PLATFORM_MINGW32 || ENABLE_FEATURE_EXTRA_FILE_DATA
1686char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC; 1730char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC;
1687void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC; 1731void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC;
1688void reset_ino_dev_hashtable(void) FAST_FUNC; 1732void reset_ino_dev_hashtable(void) FAST_FUNC;
1733#else
1734#define add_to_ino_dev_hashtable(s, n) (void)0
1735#define is_in_ino_dev_hashtable(s) NULL
1736#define reset_ino_dev_hashtable()
1737#endif
1689#ifdef __GLIBC__ 1738#ifdef __GLIBC__
1690/* At least glibc has horrendously large inline for this, so wrap it */ 1739/* At least glibc has horrendously large inline for this, so wrap it */
1691unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC; 1740unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC;
@@ -1860,7 +1909,11 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
1860 void (*cb)(struct smaprec *, void *), void *data); 1909 void (*cb)(struct smaprec *, void *), void *data);
1861 1910
1862typedef struct procps_status_t { 1911typedef struct procps_status_t {
1912#if !ENABLE_PLATFORM_MINGW32
1863 DIR *dir; 1913 DIR *dir;
1914#else
1915 HANDLE snapshot;
1916#endif
1864 IF_FEATURE_SHOW_THREADS(DIR *task_dir;) 1917 IF_FEATURE_SHOW_THREADS(DIR *task_dir;)
1865 uint8_t shift_pages_to_bytes; 1918 uint8_t shift_pages_to_bytes;
1866 uint8_t shift_pages_to_kb; 1919 uint8_t shift_pages_to_kb;
@@ -2084,12 +2137,24 @@ extern const char bb_path_wtmp_file[] ALIGN1;
2084#define bb_path_motd_file "/etc/motd" 2137#define bb_path_motd_file "/etc/motd"
2085 2138
2086#define bb_dev_null "/dev/null" 2139#define bb_dev_null "/dev/null"
2140#if ENABLE_PLATFORM_MINGW32
2141#define bb_busybox_exec_path get_busybox_exec_path()
2142extern char bb_comm[];
2143extern char bb_command_line[];
2144#else
2087extern const char bb_busybox_exec_path[] ALIGN1; 2145extern const char bb_busybox_exec_path[] ALIGN1;
2146#endif
2088/* allow default system PATH to be extended via CFLAGS */ 2147/* allow default system PATH to be extended via CFLAGS */
2089#ifndef BB_ADDITIONAL_PATH 2148#ifndef BB_ADDITIONAL_PATH
2090#define BB_ADDITIONAL_PATH "" 2149#define BB_ADDITIONAL_PATH ""
2091#endif 2150#endif
2151#if !ENABLE_PLATFORM_MINGW32
2092#define BB_PATH_ROOT_PATH "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH 2152#define BB_PATH_ROOT_PATH "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH
2153#define PATH_SEP ':'
2154#else
2155#define BB_PATH_ROOT_PATH "PATH=/sbin;/usr/sbin;/bin;/usr/bin" BB_ADDITIONAL_PATH
2156#define PATH_SEP ';'
2157#endif
2093extern const char bb_PATH_root_path[] ALIGN1; /* BB_PATH_ROOT_PATH */ 2158extern const char bb_PATH_root_path[] ALIGN1; /* BB_PATH_ROOT_PATH */
2094#define bb_default_root_path (bb_PATH_root_path + sizeof("PATH")) 2159#define bb_default_root_path (bb_PATH_root_path + sizeof("PATH"))
2095/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, 2160/* 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..07f9857e9
--- /dev/null
+++ b/include/mingw.h
@@ -0,0 +1,533 @@
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 4095
12#define DEFAULT_GID DEFAULT_UID
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#ifdef LONG_MAX
59# if LONG_MAX == 2147483647
60# define LONG_BIT 32
61# else
62/* Safe assumption. */
63# define LONG_BIT 64
64# endif
65#elif defined __LONG_MAX__
66# if __LONG_MAX__ == 2147483647
67# define LONG_BIT 32
68# else
69/* Safe assumption. */
70# define LONG_BIT 64
71# endif
72#endif
73
74/*
75 * netdb.h
76 */
77
78typedef int sa_family_t;
79
80/*
81 * linux/un.h
82 */
83struct sockaddr_un {
84 sa_family_t sun_family;
85 char sun_path[1]; /* to make compiler happy, don't bother */
86};
87
88/*
89 * pwd.h
90 */
91struct passwd {
92 char *pw_name;
93 char *pw_passwd;
94 char *pw_gecos;
95 char *pw_dir;
96 char *pw_shell;
97 uid_t pw_uid;
98 gid_t pw_gid;
99};
100
101struct passwd *getpwnam(const char *name);
102struct passwd *getpwuid(uid_t uid);
103static inline void setpwent(void) {}
104static inline void endpwent(void) {}
105IMPL(getpwent_r,int,ENOENT,struct passwd *pwbuf UNUSED_PARAM,char *buf UNUSED_PARAM,size_t buflen UNUSED_PARAM,struct passwd **pwbufp UNUSED_PARAM);
106IMPL(getpwent,struct passwd *,NULL,void)
107
108/*
109 * signal.h
110 */
111#define SIGKILL 9
112#define SIGPIPE 13
113
114#define SIG_UNBLOCK 1
115
116NOIMPL(FAST_FUNC sigprocmask_allsigs, int how UNUSED_PARAM);
117typedef void (*sighandler_t)(int);
118sighandler_t winansi_signal(int signum, sighandler_t handler);
119#define signal(s, h) winansi_signal(s, h)
120
121/*
122 * stdio.h
123 */
124#undef fseeko
125#define fseeko(f,o,w) fseek(f,o,w)
126
127int fdprintf(int fd, const char *format, ...);
128FILE* mingw_fopen(const char *filename, const char *mode);
129int mingw_rename(const char*, const char*);
130#define fopen mingw_fopen
131#define rename mingw_rename
132
133FILE *mingw_popen(const char *cmd, const char *mode);
134int mingw_popen_fd(const char *cmd, const char *mode, int fd0, pid_t *pid);
135int mingw_pclose(FILE *fd);
136#undef popen
137#undef pclose
138#define popen mingw_popen
139#define pclose mingw_pclose
140
141#define setlinebuf(fd) setvbuf(fd, (char *) NULL, _IOLBF, 0)
142
143/*
144 * ANSI emulation wrappers
145 */
146
147void set_title(const char *str);
148void move_cursor_row(int n);
149void reset_screen(void);
150int winansi_putchar(int c);
151int winansi_puts(const char *s);
152size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
153int winansi_fputs(const char *str, FILE *stream);
154int winansi_vsnprintf(char *buf, size_t size, const char *format, va_list list);
155int winansi_vfprintf(FILE *stream, const char *format, va_list list);
156int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
157int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
158int winansi_write(int fd, const void *buf, size_t count);
159int winansi_read(int fd, void *buf, size_t count);
160int winansi_getc(FILE *stream);
161#define putchar winansi_putchar
162#define puts winansi_puts
163#define fwrite winansi_fwrite
164#define fputs winansi_fputs
165#if !defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO
166#define vsnprintf(buf, size, ...) winansi_vsnprintf(buf, size, __VA_ARGS__)
167#endif
168#define vfprintf(stream, ...) winansi_vfprintf(stream, __VA_ARGS__)
169#define vprintf(...) winansi_vfprintf(stdout, __VA_ARGS__)
170#define printf(...) winansi_printf(__VA_ARGS__)
171#define fprintf(...) winansi_fprintf(__VA_ARGS__)
172#define write winansi_write
173#define read winansi_read
174#define getc winansi_getc
175
176/*
177 * stdlib.h
178 */
179#define WTERMSIG(x) ((x) & 0x7f)
180#define WIFEXITED(x) (WTERMSIG(x) == 0)
181#define WEXITSTATUS(x) (((x) & 0xff00) >> 8)
182#define WIFSIGNALED(x) (((signed char) (((x) & 0x7f) + 1) >> 1) > 0)
183#define WCOREDUMP(x) 0
184#define WIFSTOPPED(x) 0
185
186int mingw_system(const char *cmd);
187#define system mingw_system
188
189int clearenv(void);
190char *mingw_getenv(const char *name);
191int mingw_putenv(const char *env);
192char *mingw_mktemp(char *template);
193int mkstemp(char *template);
194char *realpath(const char *path, char *resolved_path);
195int setenv(const char *name, const char *value, int replace);
196int unsetenv(const char *env);
197
198#define getenv mingw_getenv
199#define putenv mingw_putenv
200#define mktemp mingw_mktemp
201
202/*
203 * strings.h
204 */
205int ffs(int i);
206
207/*
208 * sys/ioctl.h
209 */
210
211#define TIOCGWINSZ 0x5413
212
213int ioctl(int fd, int code, ...);
214
215/*
216 * sys/socket.h
217 */
218#define hstrerror strerror
219
220#define SHUT_WR SD_SEND
221
222int mingw_socket(int domain, int type, int protocol);
223int mingw_connect(int sockfd, const struct sockaddr *sa, size_t sz);
224int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
225int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
226int mingw_shutdown(int sockfd, int how);
227int mingw_listen(int sockfd, int backlog);
228int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz);
229int mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
230 struct timeval *timeout);
231
232NOIMPL(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);
233
234#define socket mingw_socket
235#define connect mingw_connect
236#define sendto mingw_sendto
237#define listen mingw_listen
238#define bind mingw_bind
239#define setsockopt mingw_setsockopt
240#define shutdown mingw_shutdown
241#define accept mingw_accept
242#define select mingw_select
243
244/*
245 * sys/stat.h
246 */
247#define S_ISUID 04000
248#define S_ISGID 02000
249#define S_ISVTX 01000
250#ifndef S_IRWXU
251#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
252#endif
253#define S_IRWXG (S_IRWXU >> 3)
254#define S_IRWXO (S_IRWXG >> 3)
255
256#define S_IFSOCK 0140000
257#define S_IFLNK 0120000 /* Symbolic link */
258#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
259#define S_ISSOCK(x) 0
260
261#define S_IRGRP (S_IRUSR >> 3)
262#define S_IWGRP (S_IWUSR >> 3)
263#define S_IXGRP (S_IXUSR >> 3)
264#define S_IROTH (S_IRGRP >> 3)
265#define S_IWOTH (S_IWGRP >> 3)
266#define S_IXOTH (S_IXGRP >> 3)
267
268mode_t mingw_umask(mode_t mode);
269#define umask mingw_umask
270
271#define DEFAULT_UMASK 0002
272
273IMPL(fchmod,int,0,int fildes UNUSED_PARAM, mode_t mode UNUSED_PARAM);
274NOIMPL(fchown,int fd UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM);
275int mingw_mkdir(const char *path, int mode);
276int mingw_chdir(const char *path);
277int mingw_chmod(const char *path, int mode);
278
279#define mkdir mingw_mkdir
280#define chdir mingw_chdir
281#define chmod mingw_chmod
282
283#if ENABLE_LFS && !defined(__MINGW64_VERSION_MAJOR)
284# define off_t off64_t
285#endif
286
287typedef int nlink_t;
288typedef int blksize_t;
289typedef off_t blkcnt_t;
290#if ENABLE_FEATURE_EXTRA_FILE_DATA
291#define ino_t uint64_t
292#endif
293
294struct mingw_stat {
295 dev_t st_dev;
296 ino_t st_ino;
297 mode_t st_mode;
298 nlink_t st_nlink;
299 uid_t st_uid;
300 gid_t st_gid;
301 dev_t st_rdev;
302 off_t st_size;
303 time_t st_atime;
304 time_t st_mtime;
305 time_t st_ctime;
306 blksize_t st_blksize;
307 blkcnt_t st_blocks;
308};
309
310int mingw_lstat(const char *file_name, struct mingw_stat *buf);
311int mingw_stat(const char *file_name, struct mingw_stat *buf);
312int mingw_fstat(int fd, struct mingw_stat *buf);
313#undef lstat
314#undef stat
315#undef fstat
316#define lstat mingw_lstat
317#define stat mingw_stat
318#define fstat mingw_fstat
319
320/*
321 * sys/sysmacros.h
322 */
323#define makedev(a,b) 0*(a)*(b) /* avoid unused warning */
324#define minor(x) 0
325#define major(x) 0
326
327/*
328 * sys/time.h
329 */
330#ifndef _TIMESPEC_DEFINED
331#define _TIMESPEC_DEFINED
332struct timespec {
333 time_t tv_sec;
334 long int tv_nsec;
335};
336#endif
337
338int nanosleep(const struct timespec *req, struct timespec *rem);
339
340/*
341 * sys/wait.h
342 */
343#define WNOHANG 1
344#define WUNTRACED 2
345int waitpid(pid_t pid, int *status, int options);
346
347/*
348 * time.h
349 */
350struct tm *gmtime_r(const time_t *timep, struct tm *result);
351struct tm *localtime_r(const time_t *timep, struct tm *result);
352char *strptime(const char *s, const char *format, struct tm *tm);
353size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm);
354int stime(time_t *t);
355
356#define strftime mingw_strftime
357
358/*
359 * times.h
360 */
361#define clock_t long
362
363struct tms {
364 clock_t tms_utime; /* user CPU time */
365 clock_t tms_stime; /* system CPU time */
366 clock_t tms_cutime; /* user CPU time of children */
367 clock_t tms_cstime; /* system CPU time of children */
368};
369
370clock_t times(struct tms *buf);
371
372/*
373 * unistd.h
374 */
375#define PIPE_BUF 8192
376
377#define _SC_CLK_TCK 2
378
379#define TICKS_PER_SECOND 100
380#define MS_PER_TICK 10
381#define HNSEC_PER_TICK 100000
382
383IMPL(alarm,unsigned int,0,unsigned int seconds UNUSED_PARAM);
384IMPL(chown,int,0,const char *path UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM);
385NOIMPL(chroot,const char *root UNUSED_PARAM);
386NOIMPL(fchdir,int fd UNUSED_PARAM);
387int mingw_dup2 (int fd, int fdto);
388char *mingw_getcwd(char *pointer, int len);
389off_t mingw_lseek(int fd, off_t offset, int whence);
390
391
392int getuid(void);
393#define getgid getuid
394#define geteuid getuid
395#define getegid getuid
396int getgroups(int n, gid_t *groups);
397IMPL(getppid,int,1,void);
398NOIMPL(getsid,pid_t pid UNUSED_PARAM);
399int getlogin_r(char *buf, size_t len);
400int fcntl(int fd, int cmd, ...);
401int fsync(int fd);
402int kill(pid_t pid, int sig);
403int link(const char *oldpath, const char *newpath);
404NOIMPL(mknod,const char *name UNUSED_PARAM, mode_t mode UNUSED_PARAM, dev_t device UNUSED_PARAM);
405/* order of devices must match that in get_dev_type */
406enum {DEV_NULL, DEV_ZERO, DEV_URANDOM, NOT_DEVICE};
407int get_dev_type(const char *filename);
408void update_dev_fd(int dev, int fd);
409int mingw_open (const char *filename, int oflags, ...);
410int mingw_xopen(const char *filename, int oflags);
411ssize_t mingw_read(int fd, void *buf, size_t count);
412int mingw_close(int fd);
413int pipe(int filedes[2]);
414#if ENABLE_FEATURE_READLINK2
415ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
416#else
417NOIMPL(readlink,const char *path UNUSED_PARAM, char *buf UNUSED_PARAM, size_t bufsiz UNUSED_PARAM);
418#endif
419NOIMPL(setgid,gid_t gid UNUSED_PARAM);
420NOIMPL(setegid,gid_t gid UNUSED_PARAM);
421NOIMPL(setsid,void);
422NOIMPL(setuid,uid_t gid UNUSED_PARAM);
423NOIMPL(seteuid,uid_t gid UNUSED_PARAM);
424unsigned int sleep(unsigned int seconds);
425NOIMPL(symlink,const char *oldpath UNUSED_PARAM, const char *newpath UNUSED_PARAM);
426static inline void sync(void) {}
427long sysconf(int name);
428IMPL(getpagesize,int,4096,void);
429NOIMPL(ttyname_r,int fd UNUSED_PARAM, char *buf UNUSED_PARAM, int sz UNUSED_PARAM);
430int mingw_unlink(const char *pathname);
431pid_t vfork(void);
432int mingw_access(const char *name, int mode);
433int mingw_rmdir(const char *name);
434int mingw_isatty(int fd);
435
436#define dup2 mingw_dup2
437#define getcwd mingw_getcwd
438#define lchown chown
439#define open mingw_open
440#define close mingw_close
441#define unlink mingw_unlink
442#define rmdir mingw_rmdir
443#undef lseek
444#define lseek mingw_lseek
445
446#undef access
447#define access mingw_access
448#define isatty mingw_isatty
449
450/*
451 * utime.h
452 */
453int utimes(const char *file_name, const struct timeval times[2]);
454
455/*
456 * dirent.h
457 */
458DIR *mingw_opendir(const char *path);
459#define opendir mingw_opendir
460
461/*
462 * Functions with different prototypes in BusyBox and WIN32
463 */
464#define itoa bb_itoa
465#define strrev bb_strrev
466
467/*
468 * MinGW specific
469 */
470#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
471
472pid_t FAST_FUNC mingw_spawn(char **argv);
473intptr_t FAST_FUNC mingw_spawn_proc(const char **argv);
474int mingw_execv(const char *cmd, char *const *argv);
475int mingw_execvp(const char *cmd, char *const *argv);
476int mingw_execve(const char *cmd, char *const *argv, char *const *envp);
477#define spawn mingw_spawn
478#define execvp mingw_execvp
479#define execve mingw_execve
480#define execv mingw_execv
481
482#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
483#define is_absolute_path(path) ((path)[0] == '/' || (path)[0] == '\\' || has_dos_drive_prefix(path))
484
485int kill_SIGTERM_by_handle(HANDLE process, int exit_code);
486
487#define find_mount_point(n, s) find_mount_point(n)
488
489char *is_prefixed_with_case(const char *string, const char *key) FAST_FUNC;
490char *is_suffixed_with_case(const char *string, const char *key) FAST_FUNC;
491void qsort_string_vector_case(char **sv, unsigned count) FAST_FUNC;
492
493/*
494 * helpers
495 */
496
497const char *get_busybox_exec_path(void);
498void init_winsock(void);
499void init_codepage(void);
500
501int has_bat_suffix(const char *p);
502int has_exe_suffix(const char *p);
503int has_exe_suffix_or_dot(const char *name);
504char *alloc_win32_extension(const char *p);
505int add_win32_extension(char *p);
506
507static inline char *auto_win32_extension(const char *p)
508{
509 extern char *auto_string(char *str) FAST_FUNC;
510 char *s = alloc_win32_extension(p);
511 return s ? auto_string(s) : NULL;
512}
513
514void bs_to_slash(char *p) FAST_FUNC;
515void slash_to_bs(char *p) FAST_FUNC;
516size_t remove_cr(char *p, size_t len) FAST_FUNC;
517
518int err_win_to_posix(void);
519
520ULONGLONG CompatGetTickCount64(void);
521#define GetTickCount64 CompatGetTickCount64
522
523ssize_t get_random_bytes(void *buf, ssize_t count);
524int enumerate_links(const char *file, char *name);
525void hide_console(void);
526
527int unc_root_len(const char *dir);
528int root_len(const char *path);
529char *get_system_drive(void);
530int chdir_system_drive(void);
531char *xabsolute_path(char *path);
532char *get_drive_cwd(const char *path, char *buffer, int size);
533void fix_path_case(char *path);
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..799f6d01a 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,45 @@ 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_FEATURE_EXTRA_FILE_DATA) += inode_hash.o
102lib-$(CONFIG_PLATFORM_POSIX) += kernel_version.o
103lib-$(CONFIG_PLATFORM_POSIX) += login.o
104lib-$(CONFIG_PLATFORM_POSIX) += makedev.o
105lib-$(CONFIG_PLATFORM_POSIX) += perror_nomsg.o
106lib-$(CONFIG_PLATFORM_POSIX) += pidfile.o
107lib-$(CONFIG_PLATFORM_POSIX) += print_flags.o
108lib-$(CONFIG_PLATFORM_POSIX) += progress.o
109lib-$(CONFIG_PLATFORM_POSIX) += read_key.o
110lib-$(CONFIG_PLATFORM_POSIX) += setup_environment.o
111lib-$(CONFIG_PLATFORM_POSIX) += signals.o
112lib-$(CONFIG_PLATFORM_POSIX) += simplify_path.o
113lib-$(CONFIG_PLATFORM_POSIX) += speed_table.o
114lib-$(CONFIG_PLATFORM_POSIX) += udp_io.o
115lib-$(CONFIG_PLATFORM_POSIX) += warn_ignoring_args.o
116lib-$(CONFIG_PLATFORM_POSIX) += write.o
117lib-$(CONFIG_PLATFORM_POSIX) += xgethostbyname.o
118
115lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o 119lib-$(CONFIG_PLATFORM_LINUX) += match_fstype.o
116 120
117lib-$(CONFIG_FEATURE_UTMP) += utmp.o 121lib-$(CONFIG_FEATURE_UTMP) += utmp.o
@@ -123,7 +127,7 @@ lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
123lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o 127lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o
124lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o 128lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o
125 129
126lib-$(CONFIG_NC) += udp_io.o 130lib-$(CONFIG_NC_110_COMPAT) += udp_io.o
127lib-$(CONFIG_NETCAT) += udp_io.o 131lib-$(CONFIG_NETCAT) += udp_io.o
128lib-$(CONFIG_DNSD) += udp_io.o 132lib-$(CONFIG_DNSD) += udp_io.o
129lib-$(CONFIG_NTPD) += udp_io.o 133lib-$(CONFIG_NTPD) += udp_io.o
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index fa19e8488..1f1a18aab 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,11 @@ 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);
163#if ENABLE_PLATFORM_MINGW32
164 full_write2_str(" multi-call binary\n");
165#else
152 full_write2_str(" multi-call binary.\n"); /* common string */ 166 full_write2_str(" multi-call binary.\n"); /* common string */
167#endif
153 if (*p == '\b') 168 if (*p == '\b')
154 full_write2_str("\nNo help available\n"); 169 full_write2_str("\nNo help available\n");
155 else { 170 else {
@@ -312,6 +327,10 @@ void lbb_prepare(const char *applet
312 if (ENABLE_LOCALE_SUPPORT) 327 if (ENABLE_LOCALE_SUPPORT)
313 setlocale(LC_ALL, ""); 328 setlocale(LC_ALL, "");
314 329
330#if ENABLE_PLATFORM_MINGW32
331 init_winsock();
332#endif
333
315#if ENABLE_FEATURE_INDIVIDUAL 334#if ENABLE_FEATURE_INDIVIDUAL
316 /* Redundant for busybox (run_applet_and_exit covers that case) 335 /* Redundant for busybox (run_applet_and_exit covers that case)
317 * but needed for "individual applet" mode */ 336 * but needed for "individual applet" mode */
@@ -702,6 +721,7 @@ static void check_suid(int applet_no)
702 721
703 722
704# if ENABLE_FEATURE_INSTALLER 723# if ENABLE_FEATURE_INSTALLER
724# if !ENABLE_PLATFORM_MINGW32
705static const char usr_bin [] ALIGN1 = "/usr/bin/"; 725static const char usr_bin [] ALIGN1 = "/usr/bin/";
706static const char usr_sbin[] ALIGN1 = "/usr/sbin/"; 726static const char usr_sbin[] ALIGN1 = "/usr/sbin/";
707static const char *const install_dir[] = { 727static const char *const install_dir[] = {
@@ -746,6 +766,30 @@ static void install_links(const char *busybox, int use_symbolic_links,
746 continue; 766 continue;
747 } 767 }
748} 768}
769# else /* ENABLE_PLATFORM_MINGW32 */
770static void install_links(const char *busybox,
771 int use_symbolic_links UNUSED_PARAM, char *custom_install_dir)
772{
773 char *fpc;
774 const char *appname = applet_names;
775 int rc;
776
777 if (!is_directory(custom_install_dir, FALSE))
778 bb_error_msg_and_die("'%s' is not a directory", custom_install_dir);
779
780 while (*appname) {
781 fpc = xasprintf("%s/%s.exe", custom_install_dir, appname);
782 rc = link(busybox, fpc);
783 if (rc != 0 && (errno != EEXIST ||
784 strcmp("busybox.exe", bb_basename(fpc)) != 0)) {
785 bb_simple_perror_msg(fpc);
786 }
787 free(fpc);
788 while (*appname++ != '\0')
789 continue;
790 }
791}
792# endif
749# elif ENABLE_BUSYBOX 793# elif ENABLE_BUSYBOX
750static void install_links(const char *busybox UNUSED_PARAM, 794static void install_links(const char *busybox UNUSED_PARAM,
751 int use_symbolic_links UNUSED_PARAM, 795 int use_symbolic_links UNUSED_PARAM,
@@ -830,20 +874,35 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
830 874
831 dup2(1, 2); 875 dup2(1, 2);
832 full_write2_str(bb_banner); /* reuse const string */ 876 full_write2_str(bb_banner); /* reuse const string */
877#if ENABLE_PLATFORM_MINGW32
878 full_write2_str(" multi-call binary\n"); /* reuse */
879#else
833 full_write2_str(" multi-call binary.\n"); /* reuse */ 880 full_write2_str(" multi-call binary.\n"); /* reuse */
881#endif
882#if defined(MINGW_VER)
883 if (sizeof(MINGW_VER) > 5) {
884 full_write2_str(MINGW_VER "\n\n");
885 }
886#endif
834 full_write2_str( 887 full_write2_str(
835 "BusyBox is copyrighted by many authors between 1998-2015.\n" 888 "BusyBox is copyrighted by many authors between 1998-2019.\n"
836 "Licensed under GPLv2. See source distribution for detailed\n" 889 "Licensed under GPLv2. See source distribution for detailed\n"
837 "copyright notices.\n" 890 "copyright notices.\n"
838 "\n" 891 "\n"
839 "Usage: busybox [function [arguments]...]\n" 892 "Usage: busybox [function [arguments]...]\n"
840 " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n" 893 " or: busybox --list"IF_FULL_LIST_OPTION("[-full]")"\n"
841# if ENABLE_FEATURE_SHOW_SCRIPT && NUM_SCRIPTS > 0 894# if ENABLE_FEATURE_SHOW_SCRIPT && NUM_SCRIPTS > 0
842 " or: busybox --show SCRIPT\n" 895 " or: busybox --show SCRIPT\n"
843# endif 896# endif
844 IF_FEATURE_INSTALLER( 897 IF_FEATURE_INSTALLER(
898 IF_NOT_PLATFORM_MINGW32(
845 " or: busybox --install [-s] [DIR]\n" 899 " or: busybox --install [-s] [DIR]\n"
846 ) 900 )
901 IF_PLATFORM_MINGW32(
902 " or: busybox --install [DIR]\n"
903 " or: busybox --uninstall [-n] file\n"
904 )
905 )
847 " or: function [arguments]...\n" 906 " or: function [arguments]...\n"
848 "\n" 907 "\n"
849 IF_NOT_FEATURE_SH_STANDALONE( 908 IF_NOT_FEATURE_SH_STANDALONE(
@@ -860,6 +919,11 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
860 "\tTo run external program, use full path (/sbin/ip instead of ip).\n" 919 "\tTo run external program, use full path (/sbin/ip instead of ip).\n"
861 ) 920 )
862 "\n" 921 "\n"
922#if ENABLE_GLOBBING
923 "\tSupport for native Windows wildcards is enabled. In some\n"
924 "\tcases this may result in wildcards being processed twice.\n"
925 "\n"
926#endif
863 "Currently defined functions:\n" 927 "Currently defined functions:\n"
864 ); 928 );
865 col = 0; 929 col = 0;
@@ -904,9 +968,27 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
904 const char *a = applet_names; 968 const char *a = applet_names;
905 dup2(1, 2); 969 dup2(1, 2);
906 while (*a) { 970 while (*a) {
907# if ENABLE_FEATURE_INSTALLER 971# if ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32
908 if (argv[1][6]) /* --list-full? */ 972 if (argv[1][6]) /* --list-full? */
909 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); 973 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
974# elif ENABLE_PLATFORM_MINGW32 && (ENABLE_FEATURE_PREFER_APPLETS \
975 || ENABLE_FEATURE_SH_STANDALONE \
976 || ENABLE_FEATURE_SH_NOFORK)
977 if (argv[1][6]) { /* --list-full? */
978 const char *str;
979
980 if (APPLET_IS_NOFORK(i))
981 str = "NOFORK ";
982 else if (APPLET_IS_NOEXEC(i))
983 str = "noexec ";
984# if NUM_SCRIPTS > 0
985 else if (applet_main[i] == scripted_main)
986 str = "script ";
987# endif
988 else
989 str = " ";
990 full_write2_str(str);
991 }
910# endif 992# endif
911 full_write2_str(a); 993 full_write2_str(a);
912 full_write2_str("\n"); 994 full_write2_str("\n");
@@ -918,6 +1000,7 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
918 } 1000 }
919 1001
920 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { 1002 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
1003#if !ENABLE_PLATFORM_MINGW32
921 int use_symbolic_links; 1004 int use_symbolic_links;
922 const char *busybox; 1005 const char *busybox;
923 1006
@@ -938,9 +1021,40 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
938 */ 1021 */
939 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv); 1022 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv);
940 install_links(busybox, use_symbolic_links, argv[2]); 1023 install_links(busybox, use_symbolic_links, argv[2]);
1024#else
1025 /* busybox --install [DIR]
1026 * where DIR is the directory to install to. If DIR is not
1027 * provided put the links in the same directory as busybox.
1028 */
1029 install_links(bb_busybox_exec_path, FALSE, argv[2] ? argv[2] :
1030 dirname(xstrdup(bb_busybox_exec_path)));
1031#endif
941 return 0; 1032 return 0;
942 } 1033 }
943 1034
1035#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_INSTALLER
1036 if (strcmp(argv[1], "--uninstall") == 0) {
1037 char name[PATH_MAX];
1038 int dry_run = (argv[2] && strcmp(argv[2], "-n") == 0 && ++argv);
1039 const char *file = argv[2];
1040
1041 if (!argv[2])
1042 bb_error_msg_and_die(bb_msg_requires_arg, "--uninstall");
1043
1044 while (enumerate_links(file, name)) {
1045 if (dry_run) {
1046 full_write1_str(name);
1047 full_write1_str("\n");
1048 }
1049 else if (unlink(name) != 0) {
1050 bb_simple_perror_msg(name);
1051 }
1052 file = NULL;
1053 }
1054 return 0;
1055 }
1056#endif
1057
944 if (strcmp(argv[1], "--help") == 0) { 1058 if (strcmp(argv[1], "--help") == 0) {
945 /* "busybox --help [<applet>]" */ 1059 /* "busybox --help [<applet>]" */
946 if (!argv[2]) 1060 if (!argv[2])
@@ -960,15 +1074,42 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
960# endif 1074# endif
961 1075
962# if NUM_APPLETS > 0 1076# if NUM_APPLETS > 0
1077
1078# if ENABLE_PLATFORM_MINGW32
1079static int interp = 0;
1080char bb_comm[COMM_LEN];
1081char bb_command_line[128];
1082# endif
1083
963void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv) 1084void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **argv)
964{ 1085{
965 int argc = string_array_len(argv); 1086 int argc = string_array_len(argv);
1087# if ENABLE_PLATFORM_MINGW32
1088 int i;
1089 const char *vmask;
1090 unsigned int mask;
1091# endif
966 1092
967 /* 1093 /*
968 * We do not use argv[0]: do not want to repeat massaging of 1094 * We do not use argv[0]: do not want to repeat massaging of
969 * "-/sbin/halt" -> "halt", for example. 1095 * "-/sbin/halt" -> "halt", for example.
970 */ 1096 */
971 applet_name = name; 1097 applet_name = name;
1098# if ENABLE_PLATFORM_MINGW32
1099 safe_strncpy(bb_comm,
1100 interp ? bb_basename(argv[interp]) : applet_name,
1101 sizeof(bb_comm));
1102
1103 safe_strncpy(bb_command_line, applet_name, sizeof(bb_command_line));
1104 for (i=1; i < argc && argv[i] &&
1105 strlen(bb_command_line) + strlen(argv[i]) + 2 < 128; ++i) {
1106 strcat(strcat(bb_command_line, " "), argv[i]);
1107 }
1108
1109 vmask = getenv("BB_UMASK");
1110 if (vmask && sscanf(vmask, "%o", &mask) == 1)
1111 umask((mode_t)(mask&0777));
1112# endif
972 1113
973 /* Special case. POSIX says "test --help" 1114 /* Special case. POSIX says "test --help"
974 * should be no different from e.g. "test --foo". 1115 * should be no different from e.g. "test --foo".
@@ -994,6 +1135,7 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **ar
994 } 1135 }
995 if (ENABLE_FEATURE_SUID) 1136 if (ENABLE_FEATURE_SUID)
996 check_suid(applet_no); 1137 check_suid(applet_no);
1138
997 xfunc_error_retval = applet_main[applet_no](argc, argv); 1139 xfunc_error_retval = applet_main[applet_no](argc, argv);
998 /* Note: applet_main() may also not return (die on a xfunc or such) */ 1140 /* Note: applet_main() may also not return (die on a xfunc or such) */
999 xfunc_die(); 1141 xfunc_die();
@@ -1076,6 +1218,38 @@ int main(int argc UNUSED_PARAM, char **argv)
1076 argv[0][0] &= 0x7f; 1218 argv[0][0] &= 0x7f;
1077 } 1219 }
1078#endif 1220#endif
1221#if ENABLE_PLATFORM_MINGW32
1222 /* detect if we're running an interpreted script */
1223 if (argv[0][1] == ':' && argv[0][2] == '/') {
1224 switch (argv[0][0]) {
1225 case '2':
1226 ++interp;
1227 /* fall through */
1228 case '1':
1229 ++interp;
1230 argv[0] += 3;
1231 break;
1232 }
1233 }
1234# if ENABLE_FEATURE_EURO
1235 init_codepage();
1236# endif
1237 /* Ignore critical errors, such as calling GetVolumeInformation() on
1238 * a floppy or CDROM drive with no media. */
1239 SetErrorMode(SEM_FAILCRITICALERRORS);
1240#endif
1241
1242#if defined(__MINGW64_VERSION_MAJOR)
1243 if ( stdin ) {
1244 _setmode(fileno(stdin), _O_BINARY);
1245 }
1246 if ( stdout ) {
1247 _setmode(fileno(stdout), _O_BINARY);
1248 }
1249 if ( stderr ) {
1250 _setmode(fileno(stderr), _O_BINARY);
1251 }
1252#endif
1079 1253
1080#if defined(SINGLE_APPLET_MAIN) 1254#if defined(SINGLE_APPLET_MAIN)
1081 1255
@@ -1108,6 +1282,21 @@ int main(int argc UNUSED_PARAM, char **argv)
1108 applet_name = argv[0]; 1282 applet_name = argv[0];
1109 if (applet_name[0] == '-') 1283 if (applet_name[0] == '-')
1110 applet_name++; 1284 applet_name++;
1285# if ENABLE_PLATFORM_MINGW32
1286 if ( argv[1] && argv[2] && strcmp(argv[1], "--busybox") == 0 ) {
1287 argv += 2;
1288 applet_name = argv[0];
1289 }
1290 else {
1291 char *s;
1292
1293 str_tolower(argv[0]);
1294 bs_to_slash(argv[0]);
1295 if (has_exe_suffix_or_dot(argv[0]) && (s=strrchr(argv[0], '.'))) {
1296 *s = '\0';
1297 }
1298 }
1299# endif
1111 applet_name = bb_basename(applet_name); 1300 applet_name = bb_basename(applet_name);
1112 1301
1113 /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */ 1302 /* 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..1dc515b07 100644
--- a/libbb/copy_file.c
+++ b/libbb/copy_file.c
@@ -105,12 +105,18 @@ 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_POSIX || ENABLE_FEATURE_EXTRA_FILE_DATA
108 if (source_stat.st_dev == dest_stat.st_dev 109 if (source_stat.st_dev == dest_stat.st_dev
109 && source_stat.st_ino == dest_stat.st_ino 110 && source_stat.st_ino == dest_stat.st_ino
111# if ENABLE_FEATURE_EXTRA_FILE_DATA
112 /* ignore invalid inode numbers */
113 && source_stat.st_ino != 0
114# endif
110 ) { 115 ) {
111 bb_error_msg("'%s' and '%s' are the same file", source, dest); 116 bb_error_msg("'%s' and '%s' are the same file", source, dest);
112 return -1; 117 return -1;
113 } 118 }
119#endif
114 dest_exists = 1; 120 dest_exists = 1;
115 } 121 }
116 122
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 0e1be3820..edf734614 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 */
@@ -74,6 +88,35 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
74 } 88 }
75 } 89 }
76 endmntent(mtab_fp); 90 endmntent(mtab_fp);
91#else
92 mountEntry = NULL;
93 path = NULL;
94 current = NULL;
95
96 if ( isalpha(name[0]) && name[1] == ':' ) {
97 path = name;
98 }
99 else {
100 if ( (len=GetCurrentDirectory(0, NULL)) > 0 &&
101 (current=malloc(len+1)) != NULL &&
102 GetCurrentDirectory(len, current) ) {
103 path = current;
104 }
105 }
106
107 if ( path && isalpha(path[0]) && path[1] == ':' ) {
108 mnt_fsname[0] = path[0];
109 mnt_fsname[1] = path[1];
110 mnt_fsname[2] = '\0';
111 mnt_dir[0] = path[0];
112 mnt_dir[1] = path[1];
113 mnt_dir[2] = '\\';
114 mnt_dir[3] = '\0';
115
116 mountEntry = &my_mount_entry;
117 }
118 free(current);
119#endif
77 120
78 return mountEntry; 121 return mountEntry;
79} 122}
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/inode_hash.c b/libbb/inode_hash.c
index a125244ca..f2cc417bc 100644
--- a/libbb/inode_hash.c
+++ b/libbb/inode_hash.c
@@ -61,6 +61,11 @@ void FAST_FUNC add_to_ino_dev_hashtable(const struct stat *statbuf, const char *
61 int i; 61 int i;
62 ino_dev_hashtable_bucket_t *bucket; 62 ino_dev_hashtable_bucket_t *bucket;
63 63
64#if ENABLE_FEATURE_EXTRA_FILE_DATA
65 /* ignore invalid inode numbers */
66 if (statbuf->st_ino == 0)
67 return;
68#endif
64 if (!name) 69 if (!name)
65 name = ""; 70 name = "";
66 bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + strlen(name)); 71 bucket = xmalloc(sizeof(ino_dev_hashtable_bucket_t) + strlen(name));
diff --git a/libbb/lineedit.c b/libbb/lineedit.c
index fbabc6c12..9781b4a08 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -363,7 +363,7 @@ int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
363/* Put 'command_ps[cursor]', cursor++. 363/* Put 'command_ps[cursor]', cursor++.
364 * Advance cursor on screen. If we reached right margin, scroll text up 364 * Advance cursor on screen. If we reached right margin, scroll text up
365 * and remove terminal margin effect by printing 'next_char' */ 365 * and remove terminal margin effect by printing 'next_char' */
366#define HACK_FOR_WRONG_WIDTH 1 366#define HACK_FOR_WRONG_WIDTH 1 && !ENABLE_PLATFORM_MINGW32
367static void put_cur_glyph_and_inc_cursor(void) 367static void put_cur_glyph_and_inc_cursor(void)
368{ 368{
369 CHAR_T c = command_ps[cursor]; 369 CHAR_T c = command_ps[cursor];
@@ -426,6 +426,42 @@ static void put_cur_glyph_and_inc_cursor(void)
426 } 426 }
427} 427}
428 428
429#if ENABLE_PLATFORM_MINGW32
430static void inc_cursor(void)
431{
432 CHAR_T c = command_ps[cursor];
433 unsigned width = 0;
434 int ofs_to_right;
435
436 /* advance cursor */
437 cursor++;
438 if (unicode_status == UNICODE_ON) {
439 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
440 c = adjust_width_and_validate_wc(&cmdedit_x, c);
441 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
442 } else {
443 cmdedit_x++;
444 }
445
446 ofs_to_right = cmdedit_x - cmdedit_termw;
447 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
448 /* cursor remains on this line */
449 printf(ESC"[1C");
450 }
451
452 if (ofs_to_right >= 0) {
453 /* we go to the next line */
454 printf(ESC"[1B");
455 bb_putchar('\r');
456 cmdedit_y++;
457 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
458 width = 0;
459 }
460 cmdedit_x = width;
461 }
462}
463#endif
464
429/* Move to end of line (by printing all chars till the end) */ 465/* Move to end of line (by printing all chars till the end) */
430static void put_till_end_and_adv_cursor(void) 466static void put_till_end_and_adv_cursor(void)
431{ 467{
@@ -485,6 +521,7 @@ static void input_backward(unsigned num)
485 521
486 if (cmdedit_x >= num) { 522 if (cmdedit_x >= num) {
487 cmdedit_x -= num; 523 cmdedit_x -= num;
524#if !ENABLE_PLATFORM_MINGW32
488 if (num <= 4) { 525 if (num <= 4) {
489 /* This is longer by 5 bytes on x86. 526 /* This is longer by 5 bytes on x86.
490 * Also gets miscompiled for ARM users 527 * Also gets miscompiled for ARM users
@@ -497,6 +534,7 @@ static void input_backward(unsigned num)
497 } while (--num); 534 } while (--num);
498 return; 535 return;
499 } 536 }
537#endif
500 printf(ESC"[%uD", num); 538 printf(ESC"[%uD", num);
501 return; 539 return;
502 } 540 }
@@ -635,7 +673,11 @@ static void input_backspace(void)
635static void input_forward(void) 673static void input_forward(void)
636{ 674{
637 if (cursor < command_len) 675 if (cursor < command_len)
676#if !ENABLE_PLATFORM_MINGW32
638 put_cur_glyph_and_inc_cursor(); 677 put_cur_glyph_and_inc_cursor();
678#else
679 inc_cursor();
680#endif
639} 681}
640 682
641#if ENABLE_FEATURE_TAB_COMPLETION 683#if ENABLE_FEATURE_TAB_COMPLETION
@@ -646,6 +688,14 @@ static void input_forward(void)
646//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>, 688//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
647//not "foo bar <cursor>... 689//not "foo bar <cursor>...
648 690
691# if ENABLE_PLATFORM_MINGW32
692/* use case-insensitive comparisons for filenames */
693# define is_prefixed_with(s, k) is_prefixed_with_case(s, k)
694# define qsort_string_vector(s, c) qsort_string_vector_case(s, c)
695# define strcmp(s, t) strcasecmp(s, t)
696# define strncmp(s, t, n) strncasecmp(s, t, n)
697# endif
698
649static void free_tab_completion_data(void) 699static void free_tab_completion_data(void)
650{ 700{
651 if (matches) { 701 if (matches) {
@@ -662,8 +712,12 @@ static void add_match(char *matched)
662 while (*p) { 712 while (*p) {
663 /* ESC attack fix: drop any string with control chars */ 713 /* ESC attack fix: drop any string with control chars */
664 if (*p < ' ' 714 if (*p < ' '
715# if !ENABLE_PLATFORM_MINGW32
665 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f) 716 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f)
666 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f) 717 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f)
718# else
719 || *p == 0x7f
720# endif
667 ) { 721 ) {
668 free(matched); 722 free(matched);
669 return; 723 return;
@@ -682,13 +736,16 @@ static void add_match(char *matched)
682 */ 736 */
683static char *username_path_completion(char *ud) 737static char *username_path_completion(char *ud)
684{ 738{
739# if !ENABLE_PLATFORM_MINGW32
685 struct passwd *entry; 740 struct passwd *entry;
741#endif
686 char *tilde_name = ud; 742 char *tilde_name = ud;
687 char *home = NULL; 743 char *home = NULL;
688 744
689 ud++; /* skip ~ */ 745 ud++; /* skip ~ */
690 if (*ud == '/') { /* "~/..." */ 746 if (*ud == '/') { /* "~/..." */
691 home = home_pwd_buf; 747 home = home_pwd_buf;
748# if !ENABLE_PLATFORM_MINGW32
692 } else { 749 } else {
693 /* "~user/..." */ 750 /* "~user/..." */
694 ud = strchr(ud, '/'); 751 ud = strchr(ud, '/');
@@ -697,6 +754,7 @@ static char *username_path_completion(char *ud)
697 *ud = '/'; /* restore "~user/..." */ 754 *ud = '/'; /* restore "~user/..." */
698 if (entry) 755 if (entry)
699 home = entry->pw_dir; 756 home = entry->pw_dir;
757# endif
700 } 758 }
701 if (home) { 759 if (home) {
702 ud = concat_path_file(home, ud); 760 ud = concat_path_file(home, ud);
@@ -706,6 +764,7 @@ static char *username_path_completion(char *ud)
706 return tilde_name; 764 return tilde_name;
707} 765}
708 766
767# if !ENABLE_PLATFORM_MINGW32
709/* ~use<tab> - find all users with this prefix. 768/* ~use<tab> - find all users with this prefix.
710 * Return the length of the prefix used for matching. 769 * Return the length of the prefix used for matching.
711 */ 770 */
@@ -728,6 +787,7 @@ static NOINLINE unsigned complete_username(const char *ud)
728 787
729 return 1 + userlen; 788 return 1 + userlen;
730} 789}
790# endif
731# endif /* FEATURE_USERNAME_COMPLETION */ 791# endif /* FEATURE_USERNAME_COMPLETION */
732 792
733enum { 793enum {
@@ -755,7 +815,7 @@ static int path_parse(char ***p)
755 tmp = (char*)pth; 815 tmp = (char*)pth;
756 npth = 1; /* path component count */ 816 npth = 1; /* path component count */
757 while (1) { 817 while (1) {
758 tmp = strchr(tmp, ':'); 818 tmp = strchr(tmp, PATH_SEP);
759 if (!tmp) 819 if (!tmp)
760 break; 820 break;
761 tmp++; 821 tmp++;
@@ -768,7 +828,7 @@ static int path_parse(char ***p)
768 res[0] = tmp = xstrdup(pth); 828 res[0] = tmp = xstrdup(pth);
769 npth = 1; 829 npth = 1;
770 while (1) { 830 while (1) {
771 tmp = strchr(tmp, ':'); 831 tmp = strchr(tmp, PATH_SEP);
772 if (!tmp) 832 if (!tmp)
773 break; 833 break;
774 *tmp++ = '\0'; /* ':' -> '\0' */ 834 *tmp++ = '\0'; /* ':' -> '\0' */
@@ -796,6 +856,17 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
796 path1[0] = (char*)"."; 856 path1[0] = (char*)".";
797 857
798 pfind = strrchr(command, '/'); 858 pfind = strrchr(command, '/');
859#if ENABLE_PLATFORM_MINGW32
860 if (!pfind && has_dos_drive_prefix(command) && command[2] != '\0') {
861 char buffer[PATH_MAX];
862
863 /* path is of form c:path with no '/' */
864 if (get_drive_cwd(command, buffer, PATH_MAX)) {
865 pfind = command + 2;
866 path1[0] = xstrdup(buffer);
867 }
868 } else
869#endif
799 if (!pfind) { 870 if (!pfind) {
800 if (type == FIND_EXE_ONLY) 871 if (type == FIND_EXE_ONLY)
801 npaths = path_parse(&paths); 872 npaths = path_parse(&paths);
@@ -854,6 +925,12 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
854 if (stat(found, &st) && lstat(found, &st)) 925 if (stat(found, &st) && lstat(found, &st))
855 goto cont; /* hmm, remove in progress? */ 926 goto cont; /* hmm, remove in progress? */
856 927
928# if ENABLE_PLATFORM_MINGW32
929 if (type == FIND_EXE_ONLY && !S_ISDIR(st.st_mode) &&
930 !file_is_executable(found))
931 goto cont;
932# endif
933
857 /* Save only name */ 934 /* Save only name */
858 len = strlen(name_found); 935 len = strlen(name_found);
859 found = xrealloc(found, len + 2); /* +2: for slash and NUL */ 936 found = xrealloc(found, len + 2); /* +2: for slash and NUL */
@@ -1173,7 +1250,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1173 /* Free up any memory already allocated */ 1250 /* Free up any memory already allocated */
1174 free_tab_completion_data(); 1251 free_tab_completion_data();
1175 1252
1176# if ENABLE_FEATURE_USERNAME_COMPLETION 1253# if ENABLE_FEATURE_USERNAME_COMPLETION && !ENABLE_PLATFORM_MINGW32
1177 /* If the word starts with ~ and there is no slash in the word, 1254 /* If the word starts with ~ and there is no slash in the word,
1178 * then try completing this word as a username. */ 1255 * then try completing this word as a username. */
1179 if (state->flags & USERNAME_COMPLETION) 1256 if (state->flags & USERNAME_COMPLETION)
@@ -1224,7 +1301,11 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1224 for (cp = chosen_match; *cp; cp++) { 1301 for (cp = chosen_match; *cp; cp++) {
1225 unsigned n; 1302 unsigned n;
1226 for (n = 1; n < num_matches; n++) { 1303 for (n = 1; n < num_matches; n++) {
1304# if !ENABLE_PLATFORM_MINGW32
1227 if (matches[n][cp - chosen_match] != *cp) { 1305 if (matches[n][cp - chosen_match] != *cp) {
1306# else
1307 if (tolower(matches[n][cp - chosen_match]) != tolower(*cp)) {
1308# endif
1228 goto stop; 1309 goto stop;
1229 } 1310 }
1230 } 1311 }
@@ -1260,7 +1341,11 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1260 /* save tail */ 1341 /* save tail */
1261 strcpy(match_buf, &command_ps[cursor]); 1342 strcpy(match_buf, &command_ps[cursor]);
1262 /* add match and tail */ 1343 /* add match and tail */
1344#if ENABLE_PLATFORM_MINGW32
1345 sprintf(&command_ps[cursor-match_pfx_len], "%s%s", chosen_match, match_buf);
1346#else
1263 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); 1347 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1348#endif
1264 command_len = strlen(command_ps); 1349 command_len = strlen(command_ps);
1265 /* new pos */ 1350 /* new pos */
1266 pos = cursor + len_found - match_pfx_len; 1351 pos = cursor + len_found - match_pfx_len;
@@ -1297,6 +1382,13 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1297 free(match_buf); 1382 free(match_buf);
1298} 1383}
1299 1384
1385# if ENABLE_PLATFORM_MINGW32
1386# undef is_prefixed_with
1387# undef qsort_string_vector
1388# undef strcmp
1389# undef strncmp
1390# endif
1391
1300#endif /* FEATURE_TAB_COMPLETION */ 1392#endif /* FEATURE_TAB_COMPLETION */
1301 1393
1302 1394
@@ -2339,7 +2431,7 @@ static void sigaction2(int sig, struct sigaction *act)
2339 */ 2431 */
2340int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize) 2432int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize)
2341{ 2433{
2342 int len, n; 2434 int len IF_NOT_PLATFORM_MINGW32(, n);
2343 int timeout; 2435 int timeout;
2344#if ENABLE_FEATURE_TAB_COMPLETION 2436#if ENABLE_FEATURE_TAB_COMPLETION
2345 smallint lastWasTab = 0; 2437 smallint lastWasTab = 0;
@@ -2349,15 +2441,23 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2349 smallint vi_cmdmode = 0; 2441 smallint vi_cmdmode = 0;
2350#endif 2442#endif
2351 struct termios initial_settings; 2443 struct termios initial_settings;
2444#if !ENABLE_PLATFORM_MINGW32
2352 struct termios new_settings; 2445 struct termios new_settings;
2446#endif
2353 char read_key_buffer[KEYCODE_BUFFER_SIZE]; 2447 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2354 2448
2355 INIT_S(); 2449 INIT_S();
2356 2450
2451#if !ENABLE_PLATFORM_MINGW32
2357 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0 2452 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0
2358 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */ 2453 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */
2359 ); 2454 );
2360 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) { 2455 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) {
2456#else
2457 initial_settings.c_cc[VINTR] = CTRL('C');
2458 initial_settings.c_cc[VEOF] = CTRL('D');
2459 if (!isatty(0) || !isatty(1)) {
2460#endif
2361 /* Happens when e.g. stty -echo was run before. 2461 /* Happens when e.g. stty -echo was run before.
2362 * But if ICANON is not set, we don't come here. 2462 * But if ICANON is not set, we don't come here.
2363 * (example: interactive python ^Z-backgrounded, 2463 * (example: interactive python ^Z-backgrounded,
@@ -2411,7 +2511,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2411#endif 2511#endif
2412#define command command_must_not_be_used 2512#define command command_must_not_be_used
2413 2513
2514#if !ENABLE_PLATFORM_MINGW32
2414 tcsetattr_stdin_TCSANOW(&new_settings); 2515 tcsetattr_stdin_TCSANOW(&new_settings);
2516#endif
2415 2517
2416#if ENABLE_USERNAME_OR_HOMEDIR 2518#if ENABLE_USERNAME_OR_HOMEDIR
2417 { 2519 {
@@ -2464,6 +2566,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2464 } 2566 }
2465#endif 2567#endif
2466 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); 2568 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2569#if ENABLE_PLATFORM_MINGW32
2570 /* scroll to cursor position on any keypress */
2571 if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
2572 move_cursor_row(0);
2573#endif
2467 2574
2468#if ENABLE_FEATURE_REVERSE_SEARCH 2575#if ENABLE_FEATURE_REVERSE_SEARCH
2469 again: 2576 again:
@@ -2525,6 +2632,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2525 input_tab(&lastWasTab); 2632 input_tab(&lastWasTab);
2526 break; 2633 break;
2527#endif 2634#endif
2635#if ENABLE_PLATFORM_MINGW32
2636 case CTRL('Z'):
2637 command_ps[command_len] = '\0';
2638 bs_to_slash(command_ps);
2639 redraw(cmdedit_y, 0);
2640 break;
2641#endif
2528 case CTRL('K'): 2642 case CTRL('K'):
2529 /* Control-k -- clear to end of line */ 2643 /* Control-k -- clear to end of line */
2530 command_ps[cursor] = BB_NUL; 2644 command_ps[cursor] = BB_NUL;
@@ -2897,8 +3011,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2897 free_tab_completion_data(); 3011 free_tab_completion_data();
2898#endif 3012#endif
2899 3013
3014#if !ENABLE_PLATFORM_MINGW32
2900 /* restore initial_settings */ 3015 /* restore initial_settings */
2901 tcsetattr_stdin_TCSANOW(&initial_settings); 3016 tcsetattr_stdin_TCSANOW(&initial_settings);
3017#endif
2902#if ENABLE_FEATURE_EDITING_WINCH 3018#if ENABLE_FEATURE_EDITING_WINCH
2903 /* restore SIGWINCH handler */ 3019 /* restore SIGWINCH handler */
2904 sigaction_set(SIGWINCH, &S.SIGWINCH_handler); 3020 sigaction_set(SIGWINCH, &S.SIGWINCH_handler);
diff --git a/libbb/make_directory.c b/libbb/make_directory.c
index 9b03bb8d0..e0fd486d8 100644
--- a/libbb/make_directory.c
+++ b/libbb/make_directory.c
@@ -49,11 +49,19 @@ 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 bs_to_slash(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)
63 s += root_len(path);
64#endif
57 /* Bypass leading non-'/'s and then subsequent '/'s */ 65 /* Bypass leading non-'/'s and then subsequent '/'s */
58 while (*s) { 66 while (*s) {
59 if (*s == '/') { 67 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/parse_config.c b/libbb/parse_config.c
index 8701b010c..f7a2b81b9 100644
--- a/libbb/parse_config.c
+++ b/libbb/parse_config.c
@@ -115,6 +115,10 @@ static int get_line_with_continuation(parser_t *parser)
115 parser->lineno++; 115 parser->lineno++;
116 if (line[len - 1] == '\n') 116 if (line[len - 1] == '\n')
117 len--; 117 len--;
118#if ENABLE_PLATFORM_MINGW32
119 if (line[len - 1] == '\r')
120 len--;
121#endif
118 if (len == 0 || line[len - 1] != '\\') 122 if (len == 0 || line[len - 1] != '\\')
119 break; 123 break;
120 len--; 124 len--;
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 cb582c080..1e67d6542 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 65271e84f..26e1776a4 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{
@@ -314,3 +319,4 @@ void FAST_FUNC bb_sanitize_stdio(void)
314{ 319{
315 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL); 320 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
316} 321}
322#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/loginutils/su.c b/loginutils/su.c
index 41291ea8f..2e1b309b0 100644
--- a/loginutils/su.c
+++ b/loginutils/su.c
@@ -8,6 +8,7 @@
8//config: bool "su (19 kb)" 8//config: bool "su (19 kb)"
9//config: default y 9//config: default y
10//config: select FEATURE_SYSLOG 10//config: select FEATURE_SYSLOG
11//config: depends on PLATFORM_POSIX
11//config: help 12//config: help
12//config: su is used to become another user during a login session. 13//config: su is used to become another user during a login session.
13//config: Invoked without a username, su defaults to becoming the super user. 14//config: Invoked without a username, su defaults to becoming the super user.
diff --git a/loginutils/suw32.c b/loginutils/suw32.c
new file mode 100644
index 000000000..de29f423a
--- /dev/null
+++ b/loginutils/suw32.c
@@ -0,0 +1,67 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * Mini su implementation for busybox-w32
4 *
5 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
6 */
7//config:config SUW32
8//config: bool "su for Microsoft Windows"
9//config: default y
10//config: depends on PLATFORM_MINGW32 && ASH
11//config: help
12//config: su runs a shell with elevated privileges.
13
14//applet:IF_SUW32(APPLET_ODDNAME(su, suw32, BB_DIR_BIN, BB_SUID_DROP, suw32))
15
16//kbuild:lib-$(CONFIG_SUW32) += suw32.o
17
18//usage:#define suw32_trivial_usage
19//usage: "[-c \"CMD\"]"
20//usage:#define suw32_full_usage "\n\n"
21//usage: "Run shell with elevated privileges\n"
22//usage: "\n -c CMD Command to pass to 'sh -c'"
23
24#include "libbb.h"
25
26int suw32_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
27int suw32_main(int argc UNUSED_PARAM, char **argv)
28{
29 char *opt_command = NULL;
30 SHELLEXECUTEINFO info;
31 char *bb_path, *cwd;
32
33 getopt32(argv, "c:", &opt_command);
34 if (argv[optind])
35 bb_show_usage();
36
37 /* ShellExecuteEx() needs backslash as separator in UNC paths. */
38 bb_path = xstrdup(bb_busybox_exec_path);
39 slash_to_bs(bb_path);
40
41 memset(&info, 0, sizeof(SHELLEXECUTEINFO));
42 info.cbSize = sizeof(SHELLEXECUTEINFO);
43 /* info.fMask = SEE_MASK_DEFAULT; */
44 /* info.hwnd = NULL; */
45 info.lpVerb = "runas";
46 info.lpFile = bb_path;
47 /*
48 * It seems that when ShellExecuteEx() runs binaries residing in
49 * certain 'system' directories it sets the current directory of
50 * the process to %SYSTEMROOT%\System32. Override this by passing
51 * the directory we want to the shell.
52 *
53 * Canonicalise the directory now: if it's in a drive mapped to
54 * a network share it may not be available once we have elevated
55 * privileges.
56 */
57 cwd = xmalloc_realpath(getcwd(NULL, 0));
58 info.lpParameters =
59 xasprintf("--busybox ash -d \"%s\" -t \"BusyBox ash (Admin)\" ", cwd);
60 if (opt_command)
61 info.lpParameters =
62 xasprintf("%s -s -c \"%s\"", info.lpParameters, opt_command);
63 /* info.lpDirectory = NULL; */
64 info.nShow = SW_SHOWNORMAL;
65
66 return !ShellExecuteEx(&info);
67}
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 5aef64b60..c7ce2be0b 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..c3289982d
--- /dev/null
+++ b/miscutils/iconv.c
@@ -0,0 +1,1847 @@
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: depends on PLATFORM_MINGW32
20//config: help
21//config: 'iconv' converts text between character encodings.
22
23//applet:IF_ICONV(APPLET(iconv, BB_DIR_USR_BIN, BB_SUID_DROP))
24
25//kbuild:lib-$(CONFIG_ICONV) += iconv.o
26
27//usage:#define iconv_trivial_usage
28//usage: "[-lc] [-o outfile] -f from-enc -t to-enc [FILE]..."
29//usage:#define iconv_full_usage "\n\n"
30//usage: "Convert text between character encodings\n"
31//usage: "\n -l List all known character encodings"
32//usage: "\n -c Silently discard characters that cannot be converted"
33//usage: "\n -o Use outfile for output"
34//usage: "\n -f Use from-enc for input characters"
35//usage: "\n -t Use to-enc for output characters"
36
37#include "libbb.h"
38
39/* WORKAROUND: */
40#define GetProcAddressA GetProcAddress
41
42#define MB_CHAR_MAX 16
43
44#define UNICODE_MODE_BOM_DONE 1
45#define UNICODE_MODE_SWAPPED 2
46
47#define FLAG_USE_BOM 1
48#define FLAG_TRANSLIT 2 /* //TRANSLIT */
49#define FLAG_IGNORE 4 /* //IGNORE */
50
51typedef unsigned char uchar;
52typedef unsigned short ushort;
53typedef unsigned int uint;
54
55typedef void* iconv_t;
56
57iconv_t iconv_open(const char *tocode, const char *fromcode);
58int iconv_close(iconv_t cd);
59size_t iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
60
61typedef struct compat_t compat_t;
62typedef struct csconv_t csconv_t;
63typedef struct rec_iconv_t rec_iconv_t;
64
65typedef iconv_t (*f_iconv_open)(const char *tocode, const char *fromcode);
66typedef int (*f_iconv_close)(iconv_t cd);
67typedef size_t (*f_iconv)(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
68typedef int* (*f_errno)(void);
69typedef int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
70typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
71typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize);
72typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize);
73
74#define COMPAT_IN 1
75#define COMPAT_OUT 2
76
77/* unicode mapping for compatibility with other conversion table. */
78struct compat_t {
79 uint in;
80 uint out;
81 uint flag;
82};
83
84struct csconv_t {
85 int codepage;
86 int flags;
87 f_mbtowc mbtowc;
88 f_wctomb wctomb;
89 f_mblen mblen;
90 f_flush flush;
91 DWORD mode;
92 compat_t *compat;
93};
94
95struct rec_iconv_t {
96 iconv_t cd;
97 f_iconv_close iconv_close;
98 f_iconv iconv;
99 f_errno _errno;
100 csconv_t from;
101 csconv_t to;
102};
103
104static int win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode);
105static int win_iconv_close(iconv_t cd);
106static size_t win_iconv(iconv_t cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft);
107
108static int load_mlang(void);
109static int make_csconv(const char *name, csconv_t *cv);
110static int name_to_codepage(const char *name);
111static uint utf16_to_ucs4(const ushort *wbuf);
112static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize);
113static int mbtowc_flags(int codepage);
114static int must_use_null_useddefaultchar(int codepage);
115static int seterror(int err);
116
117static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
118static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
119static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
120static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize);
121static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize);
122
123static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
124static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
125static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
126static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
127static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
128static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
129static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
130static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
131static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
132static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
133static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize);
134
135static struct {
136 int codepage;
137 const char *name;
138} codepage_alias[] = {
139 {65001, "CP65001"},
140 {65001, "UTF8"},
141 {65001, "UTF-8"},
142
143 {1200, "CP1200"},
144 {1200, "UTF16LE"},
145 {1200, "UTF-16LE"},
146 {1200, "UCS2LE"},
147 {1200, "UCS-2LE"},
148 {1200, "UCS-2-INTERNAL"},
149
150 {1201, "CP1201"},
151 {1201, "UTF16BE"},
152 {1201, "UTF-16BE"},
153 {1201, "UCS2BE"},
154 {1201, "UCS-2BE"},
155 {1201, "unicodeFFFE"},
156
157 {12000, "CP12000"},
158 {12000, "UTF32LE"},
159 {12000, "UTF-32LE"},
160 {12000, "UCS4LE"},
161 {12000, "UCS-4LE"},
162
163 {12001, "CP12001"},
164 {12001, "UTF32BE"},
165 {12001, "UTF-32BE"},
166 {12001, "UCS4BE"},
167 {12001, "UCS-4BE"},
168
169#ifndef GLIB_COMPILATION
170 /*
171 * Default is big endian.
172 * See rfc2781 4.3 Interpreting text labelled as UTF-16.
173 */
174 {1201, "UTF16"},
175 {1201, "UTF-16"},
176 {1201, "UCS2"},
177 {1201, "UCS-2"},
178 {12001, "UTF32"},
179 {12001, "UTF-32"},
180 {12001, "UCS-4"},
181 {12001, "UCS4"},
182#else
183 /* Default is little endian, because the platform is */
184 {1200, "UTF16"},
185 {1200, "UTF-16"},
186 {1200, "UCS2"},
187 {1200, "UCS-2"},
188 {12000, "UTF32"},
189 {12000, "UTF-32"},
190 {12000, "UCS4"},
191 {12000, "UCS-4"},
192#endif
193
194 /* copy from libiconv `iconv -l` */
195 /* !IsValidCodePage(367) */
196 {20127, "ANSI_X3.4-1968"},
197 {20127, "ANSI_X3.4-1986"},
198 {20127, "ASCII"},
199 {20127, "CP367"},
200 {20127, "IBM367"},
201 {20127, "ISO-IR-6"},
202 {20127, "ISO646-US"},
203 {20127, "ISO_646.IRV:1991"},
204 {20127, "US"},
205 {20127, "US-ASCII"},
206 {20127, "CSASCII"},
207
208 /* !IsValidCodePage(819) */
209 {1252, "CP819"},
210 {1252, "IBM819"},
211 {28591, "ISO-8859-1"},
212 {28591, "ISO-IR-100"},
213 {28591, "ISO8859-1"},
214 {28591, "ISO_8859-1"},
215 {28591, "ISO_8859-1:1987"},
216 {28591, "L1"},
217 {28591, "LATIN1"},
218 {28591, "CSISOLATIN1"},
219
220 {1250, "CP1250"},
221 {1250, "MS-EE"},
222 {1250, "WINDOWS-1250"},
223
224 {1251, "CP1251"},
225 {1251, "MS-CYRL"},
226 {1251, "WINDOWS-1251"},
227
228 {1252, "CP1252"},
229 {1252, "MS-ANSI"},
230 {1252, "WINDOWS-1252"},
231
232 {1253, "CP1253"},
233 {1253, "MS-GREEK"},
234 {1253, "WINDOWS-1253"},
235
236 {1254, "CP1254"},
237 {1254, "MS-TURK"},
238 {1254, "WINDOWS-1254"},
239
240 {1255, "CP1255"},
241 {1255, "MS-HEBR"},
242 {1255, "WINDOWS-1255"},
243
244 {1256, "CP1256"},
245 {1256, "MS-ARAB"},
246 {1256, "WINDOWS-1256"},
247
248 {1257, "CP1257"},
249 {1257, "WINBALTRIM"},
250 {1257, "WINDOWS-1257"},
251
252 {1258, "CP1258"},
253 {1258, "WINDOWS-1258"},
254
255 {850, "850"},
256 {850, "CP850"},
257 {850, "IBM850"},
258 {850, "CSPC850MULTILINGUAL"},
259
260 /* !IsValidCodePage(862) */
261 {862, "862"},
262 {862, "CP862"},
263 {862, "IBM862"},
264 {862, "CSPC862LATINHEBREW"},
265
266 {866, "866"},
267 {866, "CP866"},
268 {866, "IBM866"},
269 {866, "CSIBM866"},
270
271 /* !IsValidCodePage(154) */
272 {154, "CP154"},
273 {154, "CYRILLIC-ASIAN"},
274 {154, "PT154"},
275 {154, "PTCP154"},
276 {154, "CSPTCP154"},
277
278 /* !IsValidCodePage(1133) */
279 {1133, "CP1133"},
280 {1133, "IBM-CP1133"},
281
282 {874, "CP874"},
283 {874, "WINDOWS-874"},
284
285 /* !IsValidCodePage(51932) */
286 {51932, "CP51932"},
287 {51932, "MS51932"},
288 {51932, "WINDOWS-51932"},
289 {51932, "EUC-JP"},
290
291 {932, "CP932"},
292 {932, "MS932"},
293 {932, "SHIFFT_JIS"},
294 {932, "SHIFFT_JIS-MS"},
295 {932, "SJIS"},
296 {932, "SJIS-MS"},
297 {932, "SJIS-OPEN"},
298 {932, "SJIS-WIN"},
299 {932, "WINDOWS-31J"},
300 {932, "WINDOWS-932"},
301 {932, "CSWINDOWS31J"},
302
303 {50221, "CP50221"},
304 {50221, "ISO-2022-JP"},
305 {50221, "ISO-2022-JP-MS"},
306 {50221, "ISO2022-JP"},
307 {50221, "ISO2022-JP-MS"},
308 {50221, "MS50221"},
309 {50221, "WINDOWS-50221"},
310
311 {936, "CP936"},
312 {936, "GBK"},
313 {936, "MS936"},
314 {936, "WINDOWS-936"},
315
316 {950, "CP950"},
317 {950, "BIG5"},
318 {950, "BIG5HKSCS"},
319 {950, "BIG5-HKSCS"},
320
321 {949, "CP949"},
322 {949, "UHC"},
323 {949, "EUC-KR"},
324
325 {1361, "CP1361"},
326 {1361, "JOHAB"},
327
328 {437, "437"},
329 {437, "CP437"},
330 {437, "IBM437"},
331 {437, "CSPC8CODEPAGE437"},
332
333 {737, "CP737"},
334
335 {775, "CP775"},
336 {775, "IBM775"},
337 {775, "CSPC775BALTIC"},
338
339 {852, "852"},
340 {852, "CP852"},
341 {852, "IBM852"},
342 {852, "CSPCP852"},
343
344 /* !IsValidCodePage(853) */
345 {853, "CP853"},
346
347 {855, "855"},
348 {855, "CP855"},
349 {855, "IBM855"},
350 {855, "CSIBM855"},
351
352 {857, "857"},
353 {857, "CP857"},
354 {857, "IBM857"},
355 {857, "CSIBM857"},
356
357 /* !IsValidCodePage(858) */
358 {858, "CP858"},
359
360 {860, "860"},
361 {860, "CP860"},
362 {860, "IBM860"},
363 {860, "CSIBM860"},
364
365 {861, "861"},
366 {861, "CP-IS"},
367 {861, "CP861"},
368 {861, "IBM861"},
369 {861, "CSIBM861"},
370
371 {863, "863"},
372 {863, "CP863"},
373 {863, "IBM863"},
374 {863, "CSIBM863"},
375
376 {864, "CP864"},
377 {864, "IBM864"},
378 {864, "CSIBM864"},
379
380 {865, "865"},
381 {865, "CP865"},
382 {865, "IBM865"},
383 {865, "CSIBM865"},
384
385 {869, "869"},
386 {869, "CP-GR"},
387 {869, "CP869"},
388 {869, "IBM869"},
389 {869, "CSIBM869"},
390
391 /* !IsValidCodePage(1152) */
392 {1125, "CP1125"},
393
394 /*
395 * Code Page Identifiers
396 * http://msdn2.microsoft.com/en-us/library/ms776446.aspx
397 */
398 {37, "IBM037"}, /* IBM EBCDIC US-Canada */
399 {437, "IBM437"}, /* OEM United States */
400 {500, "IBM500"}, /* IBM EBCDIC International */
401 {708, "ASMO-708"}, /* Arabic (ASMO 708) */
402 /* 709 Arabic (ASMO-449+, BCON V4) */
403 /* 710 Arabic - Transparent Arabic */
404 {720, "DOS-720"}, /* Arabic (Transparent ASMO); Arabic (DOS) */
405 {737, "ibm737"}, /* OEM Greek (formerly 437G); Greek (DOS) */
406 {775, "ibm775"}, /* OEM Baltic; Baltic (DOS) */
407 {850, "ibm850"}, /* OEM Multilingual Latin 1; Western European (DOS) */
408 {852, "ibm852"}, /* OEM Latin 2; Central European (DOS) */
409 {855, "IBM855"}, /* OEM Cyrillic (primarily Russian) */
410 {857, "ibm857"}, /* OEM Turkish; Turkish (DOS) */
411 {858, "IBM00858"}, /* OEM Multilingual Latin 1 + Euro symbol */
412 {860, "IBM860"}, /* OEM Portuguese; Portuguese (DOS) */
413 {861, "ibm861"}, /* OEM Icelandic; Icelandic (DOS) */
414 {862, "DOS-862"}, /* OEM Hebrew; Hebrew (DOS) */
415 {863, "IBM863"}, /* OEM French Canadian; French Canadian (DOS) */
416 {864, "IBM864"}, /* OEM Arabic; Arabic (864) */
417 {865, "IBM865"}, /* OEM Nordic; Nordic (DOS) */
418 {866, "cp866"}, /* OEM Russian; Cyrillic (DOS) */
419 {869, "ibm869"}, /* OEM Modern Greek; Greek, Modern (DOS) */
420 {870, "IBM870"}, /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */
421 {874, "windows-874"}, /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */
422 {875, "cp875"}, /* IBM EBCDIC Greek Modern */
423 {932, "shift_jis"}, /* ANSI/OEM Japanese; Japanese (Shift-JIS) */
424 {932, "shift-jis"}, /* alternative name for it */
425 {936, "gb2312"}, /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */
426 {949, "ks_c_5601-1987"}, /* ANSI/OEM Korean (Unified Hangul Code) */
427 {950, "big5"}, /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */
428 {950, "big5hkscs"}, /* ANSI/OEM Traditional Chinese (Hong Kong SAR); Chinese Traditional (Big5-HKSCS) */
429 {950, "big5-hkscs"}, /* alternative name for it */
430 {1026, "IBM1026"}, /* IBM EBCDIC Turkish (Latin 5) */
431 {1047, "IBM01047"}, /* IBM EBCDIC Latin 1/Open System */
432 {1140, "IBM01140"}, /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */
433 {1141, "IBM01141"}, /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */
434 {1142, "IBM01142"}, /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */
435 {1143, "IBM01143"}, /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */
436 {1144, "IBM01144"}, /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */
437 {1145, "IBM01145"}, /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */
438 {1146, "IBM01146"}, /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */
439 {1147, "IBM01147"}, /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */
440 {1148, "IBM01148"}, /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */
441 {1149, "IBM01149"}, /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */
442 {1250, "windows-1250"}, /* ANSI Central European; Central European (Windows) */
443 {1251, "windows-1251"}, /* ANSI Cyrillic; Cyrillic (Windows) */
444 {1252, "windows-1252"}, /* ANSI Latin 1; Western European (Windows) */
445 {1253, "windows-1253"}, /* ANSI Greek; Greek (Windows) */
446 {1254, "windows-1254"}, /* ANSI Turkish; Turkish (Windows) */
447 {1255, "windows-1255"}, /* ANSI Hebrew; Hebrew (Windows) */
448 {1256, "windows-1256"}, /* ANSI Arabic; Arabic (Windows) */
449 {1257, "windows-1257"}, /* ANSI Baltic; Baltic (Windows) */
450 {1258, "windows-1258"}, /* ANSI/OEM Vietnamese; Vietnamese (Windows) */
451 {1361, "Johab"}, /* Korean (Johab) */
452 {10000, "macintosh"}, /* MAC Roman; Western European (Mac) */
453 {10001, "x-mac-japanese"}, /* Japanese (Mac) */
454 {10002, "x-mac-chinesetrad"}, /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */
455 {10003, "x-mac-korean"}, /* Korean (Mac) */
456 {10004, "x-mac-arabic"}, /* Arabic (Mac) */
457 {10005, "x-mac-hebrew"}, /* Hebrew (Mac) */
458 {10006, "x-mac-greek"}, /* Greek (Mac) */
459 {10007, "x-mac-cyrillic"}, /* Cyrillic (Mac) */
460 {10008, "x-mac-chinesesimp"}, /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */
461 {10010, "x-mac-romanian"}, /* Romanian (Mac) */
462 {10017, "x-mac-ukrainian"}, /* Ukrainian (Mac) */
463 {10021, "x-mac-thai"}, /* Thai (Mac) */
464 {10029, "x-mac-ce"}, /* MAC Latin 2; Central European (Mac) */
465 {10079, "x-mac-icelandic"}, /* Icelandic (Mac) */
466 {10081, "x-mac-turkish"}, /* Turkish (Mac) */
467 {10082, "x-mac-croatian"}, /* Croatian (Mac) */
468 {20000, "x-Chinese_CNS"}, /* CNS Taiwan; Chinese Traditional (CNS) */
469 {20001, "x-cp20001"}, /* TCA Taiwan */
470 {20002, "x_Chinese-Eten"}, /* Eten Taiwan; Chinese Traditional (Eten) */
471 {20003, "x-cp20003"}, /* IBM5550 Taiwan */
472 {20004, "x-cp20004"}, /* TeleText Taiwan */
473 {20005, "x-cp20005"}, /* Wang Taiwan */
474 {20105, "x-IA5"}, /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */
475 {20106, "x-IA5-German"}, /* IA5 German (7-bit) */
476 {20107, "x-IA5-Swedish"}, /* IA5 Swedish (7-bit) */
477 {20108, "x-IA5-Norwegian"}, /* IA5 Norwegian (7-bit) */
478 {20127, "us-ascii"}, /* US-ASCII (7-bit) */
479 {20261, "x-cp20261"}, /* T.61 */
480 {20269, "x-cp20269"}, /* ISO 6937 Non-Spacing Accent */
481 {20273, "IBM273"}, /* IBM EBCDIC Germany */
482 {20277, "IBM277"}, /* IBM EBCDIC Denmark-Norway */
483 {20278, "IBM278"}, /* IBM EBCDIC Finland-Sweden */
484 {20280, "IBM280"}, /* IBM EBCDIC Italy */
485 {20284, "IBM284"}, /* IBM EBCDIC Latin America-Spain */
486 {20285, "IBM285"}, /* IBM EBCDIC United Kingdom */
487 {20290, "IBM290"}, /* IBM EBCDIC Japanese Katakana Extended */
488 {20297, "IBM297"}, /* IBM EBCDIC France */
489 {20420, "IBM420"}, /* IBM EBCDIC Arabic */
490 {20423, "IBM423"}, /* IBM EBCDIC Greek */
491 {20424, "IBM424"}, /* IBM EBCDIC Hebrew */
492 {20833, "x-EBCDIC-KoreanExtended"}, /* IBM EBCDIC Korean Extended */
493 {20838, "IBM-Thai"}, /* IBM EBCDIC Thai */
494 {20866, "koi8-r"}, /* Russian (KOI8-R); Cyrillic (KOI8-R) */
495 {20871, "IBM871"}, /* IBM EBCDIC Icelandic */
496 {20880, "IBM880"}, /* IBM EBCDIC Cyrillic Russian */
497 {20905, "IBM905"}, /* IBM EBCDIC Turkish */
498 {20924, "IBM00924"}, /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */
499 {20932, "EUC-JP"}, /* Japanese (JIS 0208-1990 and 0121-1990) */
500 {20936, "x-cp20936"}, /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */
501 {20949, "x-cp20949"}, /* Korean Wansung */
502 {21025, "cp1025"}, /* IBM EBCDIC Cyrillic Serbian-Bulgarian */
503 /* 21027 (deprecated) */
504 {21866, "koi8-u"}, /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */
505 {28591, "iso-8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */
506 {28591, "iso8859-1"}, /* ISO 8859-1 Latin 1; Western European (ISO) */
507 {28591, "iso_8859-1"},
508 {28591, "iso_8859_1"},
509 {28592, "iso-8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */
510 {28592, "iso8859-2"}, /* ISO 8859-2 Central European; Central European (ISO) */
511 {28592, "iso_8859-2"},
512 {28592, "iso_8859_2"},
513 {28593, "iso-8859-3"}, /* ISO 8859-3 Latin 3 */
514 {28593, "iso8859-3"}, /* ISO 8859-3 Latin 3 */
515 {28593, "iso_8859-3"},
516 {28593, "iso_8859_3"},
517 {28594, "iso-8859-4"}, /* ISO 8859-4 Baltic */
518 {28594, "iso8859-4"}, /* ISO 8859-4 Baltic */
519 {28594, "iso_8859-4"},
520 {28594, "iso_8859_4"},
521 {28595, "iso-8859-5"}, /* ISO 8859-5 Cyrillic */
522 {28595, "iso8859-5"}, /* ISO 8859-5 Cyrillic */
523 {28595, "iso_8859-5"},
524 {28595, "iso_8859_5"},
525 {28596, "iso-8859-6"}, /* ISO 8859-6 Arabic */
526 {28596, "iso8859-6"}, /* ISO 8859-6 Arabic */
527 {28596, "iso_8859-6"},
528 {28596, "iso_8859_6"},
529 {28597, "iso-8859-7"}, /* ISO 8859-7 Greek */
530 {28597, "iso8859-7"}, /* ISO 8859-7 Greek */
531 {28597, "iso_8859-7"},
532 {28597, "iso_8859_7"},
533 {28598, "iso-8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */
534 {28598, "iso8859-8"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */
535 {28598, "iso_8859-8"},
536 {28598, "iso_8859_8"},
537 {28599, "iso-8859-9"}, /* ISO 8859-9 Turkish */
538 {28599, "iso8859-9"}, /* ISO 8859-9 Turkish */
539 {28599, "iso_8859-9"},
540 {28599, "iso_8859_9"},
541 {28603, "iso-8859-13"}, /* ISO 8859-13 Estonian */
542 {28603, "iso8859-13"}, /* ISO 8859-13 Estonian */
543 {28603, "iso_8859-13"},
544 {28603, "iso_8859_13"},
545 {28605, "iso-8859-15"}, /* ISO 8859-15 Latin 9 */
546 {28605, "iso8859-15"}, /* ISO 8859-15 Latin 9 */
547 {28605, "iso_8859-15"},
548 {28605, "iso_8859_15"},
549 {29001, "x-Europa"}, /* Europa 3 */
550 {38598, "iso-8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */
551 {38598, "iso8859-8-i"}, /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */
552 {38598, "iso_8859-8-i"},
553 {38598, "iso_8859_8-i"},
554 {50220, "iso-2022-jp"}, /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */
555 {50221, "csISO2022JP"}, /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */
556 {50222, "iso-2022-jp"}, /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */
557 {50225, "iso-2022-kr"}, /* ISO 2022 Korean */
558 {50225, "iso2022-kr"}, /* ISO 2022 Korean */
559 {50227, "x-cp50227"}, /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */
560 /* 50229 ISO 2022 Traditional Chinese */
561 /* 50930 EBCDIC Japanese (Katakana) Extended */
562 /* 50931 EBCDIC US-Canada and Japanese */
563 /* 50933 EBCDIC Korean Extended and Korean */
564 /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */
565 /* 50936 EBCDIC Simplified Chinese */
566 /* 50937 EBCDIC US-Canada and Traditional Chinese */
567 /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */
568 {51932, "euc-jp"}, /* EUC Japanese */
569 {51936, "EUC-CN"}, /* EUC Simplified Chinese; Chinese Simplified (EUC) */
570 {51949, "euc-kr"}, /* EUC Korean */
571 /* 51950 EUC Traditional Chinese */
572 {52936, "hz-gb-2312"}, /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */
573 {54936, "GB18030"}, /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */
574 {57002, "x-iscii-de"}, /* ISCII Devanagari */
575 {57003, "x-iscii-be"}, /* ISCII Bengali */
576 {57004, "x-iscii-ta"}, /* ISCII Tamil */
577 {57005, "x-iscii-te"}, /* ISCII Telugu */
578 {57006, "x-iscii-as"}, /* ISCII Assamese */
579 {57007, "x-iscii-or"}, /* ISCII Oriya */
580 {57008, "x-iscii-ka"}, /* ISCII Kannada */
581 {57009, "x-iscii-ma"}, /* ISCII Malayalam */
582 {57010, "x-iscii-gu"}, /* ISCII Gujarati */
583 {57011, "x-iscii-pa"}, /* ISCII Punjabi */
584
585 {0, NULL}
586};
587
588/*
589 * SJIS SHIFTJIS table CP932 table
590 * ---- --------------------------- --------------------------------
591 * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS
592 * 7E U+203E OVERLINE U+007E TILDE
593 * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR
594 * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS
595 * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE
596 * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO
597 * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS
598 * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN
599 * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN
600 * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN
601 *
602 * EUC-JP and ISO-2022-JP should be compatible with CP932.
603 *
604 * Kernel and MLang have different Unicode mapping table. Make sure
605 * which API is used.
606 */
607static compat_t cp932_compat[] = {
608 {0x00A5, 0x005C, COMPAT_OUT},
609 {0x203E, 0x007E, COMPAT_OUT},
610 {0x2014, 0x2015, COMPAT_OUT},
611 {0x301C, 0xFF5E, COMPAT_OUT},
612 {0x2016, 0x2225, COMPAT_OUT},
613 {0x2212, 0xFF0D, COMPAT_OUT},
614 {0x00A2, 0xFFE0, COMPAT_OUT},
615 {0x00A3, 0xFFE1, COMPAT_OUT},
616 {0x00AC, 0xFFE2, COMPAT_OUT},
617 {0, 0, 0}
618};
619
620static compat_t cp20932_compat[] = {
621 {0x00A5, 0x005C, COMPAT_OUT},
622 {0x203E, 0x007E, COMPAT_OUT},
623 {0x2014, 0x2015, COMPAT_OUT},
624 {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN},
625 {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN},
626 {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN},
627 {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN},
628 {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN},
629 {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN},
630 {0, 0, 0}
631};
632
633static compat_t *cp51932_compat = cp932_compat;
634
635/* cp20932_compat for kernel. cp932_compat for mlang. */
636static compat_t *cp5022x_compat = cp932_compat;
637
638typedef HRESULT (WINAPI *CONVERTINETSTRING)(
639 LPDWORD lpdwMode,
640 DWORD dwSrcEncoding,
641 DWORD dwDstEncoding,
642 LPCSTR lpSrcStr,
643 LPINT lpnSrcSize,
644 LPBYTE lpDstStr,
645 LPINT lpnDstSize
646);
647typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)(
648 LPDWORD lpdwMode,
649 DWORD dwSrcEncoding,
650 LPCSTR lpSrcStr,
651 LPINT lpnMultiCharCount,
652 LPWSTR lpDstStr,
653 LPINT lpnWideCharCount
654);
655typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)(
656 LPDWORD lpdwMode,
657 DWORD dwEncoding,
658 LPCWSTR lpSrcStr,
659 LPINT lpnWideCharCount,
660 LPSTR lpDstStr,
661 LPINT lpnMultiCharCount
662);
663typedef HRESULT (WINAPI *ISCONVERTINETSTRINGAVAILABLE)(
664 DWORD dwSrcEncoding,
665 DWORD dwDstEncoding
666);
667typedef HRESULT (WINAPI *LCIDTORFC1766A)(
668 LCID Locale,
669 LPSTR pszRfc1766,
670 int nChar
671);
672typedef HRESULT (WINAPI *LCIDTORFC1766W)(
673 LCID Locale,
674 LPWSTR pszRfc1766,
675 int nChar
676);
677typedef HRESULT (WINAPI *RFC1766TOLCIDA)(
678 LCID *pLocale,
679 LPSTR pszRfc1766
680);
681typedef HRESULT (WINAPI *RFC1766TOLCIDW)(
682 LCID *pLocale,
683 LPWSTR pszRfc1766
684);
685static CONVERTINETSTRING ConvertINetString;
686static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode;
687static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte;
688static ISCONVERTINETSTRINGAVAILABLE IsConvertINetStringAvailable;
689static LCIDTORFC1766A LcidToRfc1766A;
690static RFC1766TOLCIDA Rfc1766ToLcidA;
691
692static int
693load_mlang(void)
694{
695 HMODULE h;
696 if (ConvertINetString != NULL)
697 return TRUE;
698 h = LoadLibrary(TEXT("mlang.dll"));
699 if (!h)
700 return FALSE;
701 ConvertINetString = (CONVERTINETSTRING)GetProcAddressA(h, "ConvertINetString");
702 ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddressA(h, "ConvertINetMultiByteToUnicode");
703 ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddressA(h, "ConvertINetUnicodeToMultiByte");
704 IsConvertINetStringAvailable = (ISCONVERTINETSTRINGAVAILABLE)GetProcAddressA(h, "IsConvertINetStringAvailable");
705 LcidToRfc1766A = (LCIDTORFC1766A)GetProcAddressA(h, "LcidToRfc1766A");
706 Rfc1766ToLcidA = (RFC1766TOLCIDA)GetProcAddressA(h, "Rfc1766ToLcidA");
707 return TRUE;
708}
709
710iconv_t
711iconv_open(const char *tocode, const char *fromcode)
712{
713 rec_iconv_t *cd;
714
715 cd = (rec_iconv_t *)xzalloc(sizeof(rec_iconv_t));
716
717 /* reset the errno to prevent reporting wrong error code.
718 * 0 for unsorted error. */
719 errno = 0;
720 if (win_iconv_open(cd, tocode, fromcode))
721 return (iconv_t)cd;
722
723 free(cd);
724
725 return (iconv_t)(-1);
726}
727
728int
729iconv_close(iconv_t _cd)
730{
731 rec_iconv_t *cd = (rec_iconv_t *)_cd;
732 int r = cd->iconv_close(cd->cd);
733 int e = *(cd->_errno());
734 free(cd);
735 errno = e;
736 return r;
737}
738
739size_t
740iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
741{
742 rec_iconv_t *cd = (rec_iconv_t *)_cd;
743 size_t r = cd->iconv(cd->cd, inbuf, inbytesleft, outbuf, outbytesleft);
744 errno = *(cd->_errno());
745 return r;
746}
747
748static int
749win_iconv_open(rec_iconv_t *cd, const char *tocode, const char *fromcode)
750{
751 if (!make_csconv(fromcode, &cd->from) || !make_csconv(tocode, &cd->to))
752 return FALSE;
753 cd->iconv_close = win_iconv_close;
754 cd->iconv = win_iconv;
755 cd->_errno = _errno;
756 cd->cd = (iconv_t)cd;
757 return TRUE;
758}
759
760static int
761win_iconv_close(iconv_t cd UNUSED_PARAM)
762{
763 return 0;
764}
765
766static size_t
767win_iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
768{
769 rec_iconv_t *cd = (rec_iconv_t *)_cd;
770 ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */
771 int insize;
772 int outsize;
773 int wsize;
774 DWORD frommode;
775 DWORD tomode;
776 uint wc;
777 compat_t *cp;
778 int i;
779
780 if (inbuf == NULL || *inbuf == NULL)
781 {
782 if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL)
783 {
784 tomode = cd->to.mode;
785 outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft);
786 if (outsize == -1)
787 {
788 if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG)
789 {
790 outsize = 0;
791 }
792 else
793 {
794 cd->to.mode = tomode;
795 return (size_t)(-1);
796 }
797 }
798 *outbuf += outsize;
799 *outbytesleft -= outsize;
800 }
801 cd->from.mode = 0;
802 cd->to.mode = 0;
803 return 0;
804 }
805
806 while (*inbytesleft != 0)
807 {
808 frommode = cd->from.mode;
809 tomode = cd->to.mode;
810 wsize = MB_CHAR_MAX;
811
812 insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize);
813 if (insize == -1)
814 {
815 if (cd->to.flags & FLAG_IGNORE)
816 {
817 cd->from.mode = frommode;
818 insize = 1;
819 wsize = 0;
820 }
821 else
822 {
823 cd->from.mode = frommode;
824 return (size_t)(-1);
825 }
826 }
827
828 if (wsize == 0)
829 {
830 *inbuf += insize;
831 *inbytesleft -= insize;
832 continue;
833 }
834
835 if (cd->from.compat != NULL)
836 {
837 wc = utf16_to_ucs4(wbuf);
838 cp = cd->from.compat;
839 for (i = 0; cp[i].in != 0; ++i)
840 {
841 if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc)
842 {
843 ucs4_to_utf16(cp[i].in, wbuf, &wsize);
844 break;
845 }
846 }
847 }
848
849 if (cd->to.compat != NULL)
850 {
851 wc = utf16_to_ucs4(wbuf);
852 cp = cd->to.compat;
853 for (i = 0; cp[i].in != 0; ++i)
854 {
855 if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc)
856 {
857 ucs4_to_utf16(cp[i].out, wbuf, &wsize);
858 break;
859 }
860 }
861 }
862
863 outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft);
864 if (outsize == -1)
865 {
866 if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG)
867 {
868 cd->to.mode = tomode;
869 outsize = 0;
870 }
871 else
872 {
873 cd->from.mode = frommode;
874 cd->to.mode = tomode;
875 return (size_t)(-1);
876 }
877 }
878
879 *inbuf += insize;
880 *outbuf += outsize;
881 *inbytesleft -= insize;
882 *outbytesleft -= outsize;
883 }
884
885 return 0;
886}
887
888static int
889make_csconv(const char *_name, csconv_t *cv)
890{
891 CPINFO cpinfo;
892 int use_compat = TRUE;
893 int flag = 0;
894 char *name;
895 char *p;
896
897 name = xstrndup(_name, strlen(_name));
898 if (name == NULL)
899 return FALSE;
900
901 /* check for option "enc_name//opt1//opt2" */
902 while ((p = strrstr(name, "//")) != NULL)
903 {
904 if (_stricmp(p + 2, "nocompat") == 0)
905 use_compat = FALSE;
906 else if (_stricmp(p + 2, "translit") == 0)
907 flag |= FLAG_TRANSLIT;
908 else if (_stricmp(p + 2, "ignore") == 0)
909 flag |= FLAG_IGNORE;
910 *p = 0;
911 }
912
913 cv->mode = 0;
914 cv->flags = flag;
915 cv->mblen = NULL;
916 cv->flush = NULL;
917 cv->compat = NULL;
918 cv->codepage = name_to_codepage(name);
919 if (cv->codepage == 1200 || cv->codepage == 1201)
920 {
921 cv->mbtowc = utf16_mbtowc;
922 cv->wctomb = utf16_wctomb;
923 if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0 ||
924 _stricmp(name, "UCS-2") == 0 || _stricmp(name, "UCS2") == 0 ||
925 _stricmp(name,"UCS-2-INTERNAL") == 0)
926 cv->flags |= FLAG_USE_BOM;
927 }
928 else if (cv->codepage == 12000 || cv->codepage == 12001)
929 {
930 cv->mbtowc = utf32_mbtowc;
931 cv->wctomb = utf32_wctomb;
932 if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0 ||
933 _stricmp(name, "UCS-4") == 0 || _stricmp(name, "UCS4") == 0)
934 cv->flags |= FLAG_USE_BOM;
935 }
936 else if (cv->codepage == 65001)
937 {
938 cv->mbtowc = kernel_mbtowc;
939 cv->wctomb = kernel_wctomb;
940 cv->mblen = utf8_mblen;
941 }
942 else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang())
943 {
944 cv->mbtowc = iso2022jp_mbtowc;
945 cv->wctomb = iso2022jp_wctomb;
946 cv->flush = iso2022jp_flush;
947 }
948 else if (cv->codepage == 51932 && load_mlang())
949 {
950 cv->mbtowc = mlang_mbtowc;
951 cv->wctomb = mlang_wctomb;
952 cv->mblen = eucjp_mblen;
953 }
954 else if (IsValidCodePage(cv->codepage)
955 && GetCPInfo(cv->codepage, &cpinfo) != 0)
956 {
957 cv->mbtowc = kernel_mbtowc;
958 cv->wctomb = kernel_wctomb;
959 if (cpinfo.MaxCharSize == 1)
960 cv->mblen = sbcs_mblen;
961 else if (cpinfo.MaxCharSize == 2)
962 cv->mblen = dbcs_mblen;
963 else
964 cv->mblen = mbcs_mblen;
965 }
966 else
967 {
968 /* not supported */
969 free(name);
970 errno = EINVAL;
971 return FALSE;
972 }
973
974 if (use_compat)
975 {
976 switch (cv->codepage)
977 {
978 case 932: cv->compat = cp932_compat; break;
979 case 20932: cv->compat = cp20932_compat; break;
980 case 51932: cv->compat = cp51932_compat; break;
981 case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break;
982 }
983 }
984
985 free(name);
986
987 return TRUE;
988}
989
990static int
991name_to_codepage(const char *name)
992{
993 int i;
994
995 if (*name == '\0' ||
996 strcmp(name, "char") == 0)
997 return GetACP();
998 else if (strcmp(name, "wchar_t") == 0)
999 return 1200;
1000 else if (_strnicmp(name, "cp", 2) == 0)
1001 return atoi(name + 2); /* CP123 */
1002 else if ('0' <= name[0] && name[0] <= '9')
1003 return atoi(name); /* 123 */
1004 else if (_strnicmp(name, "xx", 2) == 0)
1005 return atoi(name + 2); /* XX123 for debug */
1006
1007 for (i = 0; codepage_alias[i].name != NULL; ++i)
1008 if (_stricmp(name, codepage_alias[i].name) == 0)
1009 return codepage_alias[i].codepage;
1010 return -1;
1011}
1012
1013/*
1014 * http://www.faqs.org/rfcs/rfc2781.html
1015 */
1016static uint
1017utf16_to_ucs4(const ushort *wbuf)
1018{
1019 uint wc = wbuf[0];
1020 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1021 wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000;
1022 return wc;
1023}
1024
1025static void
1026ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize)
1027{
1028 if (wc < 0x10000)
1029 {
1030 wbuf[0] = wc;
1031 *wbufsize = 1;
1032 }
1033 else
1034 {
1035 wc -= 0x10000;
1036 wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF);
1037 wbuf[1] = 0xDC00 | (wc & 0x3FF);
1038 *wbufsize = 2;
1039 }
1040}
1041
1042/*
1043 * Check if codepage is one of those for which the dwFlags parameter
1044 * to MultiByteToWideChar() must be zero. Return zero or
1045 * MB_ERR_INVALID_CHARS. The docs in Platform SDK for Windows
1046 * Server 2003 R2 claims that also codepage 65001 is one of these, but
1047 * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave
1048 * out 65001 (UTF-8), and that indeed seems to be the case on XP, it
1049 * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting
1050 * from UTF-8.
1051 */
1052static int
1053mbtowc_flags(int codepage)
1054{
1055 return (codepage == 50220 || codepage == 50221 ||
1056 codepage == 50222 || codepage == 50225 ||
1057 codepage == 50227 || codepage == 50229 ||
1058 codepage == 52936 || codepage == 54936 ||
1059 (codepage >= 57002 && codepage <= 57011) ||
1060 codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS;
1061}
1062
1063/*
1064 * Check if codepage is one those for which the lpUsedDefaultChar
1065 * parameter to WideCharToMultiByte() must be NULL. The docs in
1066 * Platform SDK for Windows Server 2003 R2 claims that this is the
1067 * list below, while the MSDN docs for MSVS2008 claim that it is only
1068 * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform
1069 * SDK seems to be correct, at least for XP.
1070 */
1071static int
1072must_use_null_useddefaultchar(int codepage)
1073{
1074 return (codepage == 65000 || codepage == 65001 ||
1075 codepage == 50220 || codepage == 50221 ||
1076 codepage == 50222 || codepage == 50225 ||
1077 codepage == 50227 || codepage == 50229 ||
1078 codepage == 52936 || codepage == 54936 ||
1079 (codepage >= 57002 && codepage <= 57011) ||
1080 codepage == 42);
1081}
1082
1083static int
1084seterror(int err)
1085{
1086 errno = err;
1087 return -1;
1088}
1089
1090static int
1091sbcs_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf UNUSED_PARAM,
1092 int bufsize UNUSED_PARAM)
1093{
1094 return 1;
1095}
1096
1097static int
1098dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
1099{
1100 int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1;
1101 if (bufsize < len)
1102 return seterror(EINVAL);
1103 return len;
1104}
1105
1106static int
1107mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
1108{
1109 int len = 0;
1110
1111 if (cv->codepage == 54936) {
1112 if (buf[0] <= 0x7F)
1113 len = 1;
1114 else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
1115 bufsize >= 2 &&
1116 ((buf[1] >= 0x40 && buf[1] <= 0x7E) ||
1117 (buf[1] >= 0x80 && buf[1] <= 0xFE)))
1118 len = 2;
1119 else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
1120 bufsize >= 4 &&
1121 buf[1] >= 0x30 && buf[1] <= 0x39)
1122 len = 4;
1123 else
1124 return seterror(EINVAL);
1125 return len;
1126 }
1127 else
1128 return seterror(EINVAL);
1129}
1130
1131static int
1132utf8_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize)
1133{
1134 int len = 0;
1135
1136 if (buf[0] < 0x80) len = 1;
1137 else if ((buf[0] & 0xE0) == 0xC0) len = 2;
1138 else if ((buf[0] & 0xF0) == 0xE0) len = 3;
1139 else if ((buf[0] & 0xF8) == 0xF0) len = 4;
1140 else if ((buf[0] & 0xFC) == 0xF8) len = 5;
1141 else if ((buf[0] & 0xFE) == 0xFC) len = 6;
1142
1143 if (len == 0)
1144 return seterror(EILSEQ);
1145 else if (bufsize < len)
1146 return seterror(EINVAL);
1147 return len;
1148}
1149
1150static int
1151eucjp_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize)
1152{
1153 if (buf[0] < 0x80) /* ASCII */
1154 return 1;
1155 else if (buf[0] == 0x8E) /* JIS X 0201 */
1156 {
1157 if (bufsize < 2)
1158 return seterror(EINVAL);
1159 else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF))
1160 return seterror(EILSEQ);
1161 return 2;
1162 }
1163 else if (buf[0] == 0x8F) /* JIS X 0212 */
1164 {
1165 if (bufsize < 3)
1166 return seterror(EINVAL);
1167 else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE)
1168 || !(0xA1 <= buf[2] && buf[2] <= 0xFE))
1169 return seterror(EILSEQ);
1170 return 3;
1171 }
1172 else /* JIS X 0208 */
1173 {
1174 if (bufsize < 2)
1175 return seterror(EINVAL);
1176 else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE)
1177 || !(0xA1 <= buf[1] && buf[1] <= 0xFE))
1178 return seterror(EILSEQ);
1179 return 2;
1180 }
1181}
1182
1183static int
1184kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1185{
1186 int len;
1187
1188 len = cv->mblen(cv, buf, bufsize);
1189 if (len == -1)
1190 return -1;
1191 /* If converting from ASCII, reject 8bit
1192 * chars. MultiByteToWideChar() doesn't. Note that for ASCII we
1193 * know that the mblen function is sbcs_mblen() so len is 1.
1194 */
1195 if (cv->codepage == 20127 && buf[0] >= 0x80)
1196 return seterror(EILSEQ);
1197 *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage),
1198 (const char *)buf, len, (wchar_t *)wbuf, *wbufsize);
1199 if (*wbufsize == 0)
1200 return seterror(EILSEQ);
1201 return len;
1202}
1203
1204static int
1205kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1206{
1207 BOOL usedDefaultChar = 0;
1208 BOOL *p = NULL;
1209 int flags = 0;
1210 int len;
1211
1212 if (bufsize == 0)
1213 return seterror(E2BIG);
1214 if (!must_use_null_useddefaultchar(cv->codepage))
1215 {
1216 p = &usedDefaultChar;
1217#ifdef WC_NO_BEST_FIT_CHARS
1218 if (!(cv->flags & FLAG_TRANSLIT))
1219 flags |= WC_NO_BEST_FIT_CHARS;
1220#endif
1221 }
1222 len = WideCharToMultiByte(cv->codepage, flags,
1223 (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p);
1224 if (len == 0)
1225 {
1226 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1227 return seterror(E2BIG);
1228 return seterror(EILSEQ);
1229 }
1230 else if (usedDefaultChar && !(cv->flags & FLAG_TRANSLIT))
1231 return seterror(EILSEQ);
1232 else if (cv->mblen(cv, buf, len) != len) /* validate result */
1233 return seterror(EILSEQ);
1234 return len;
1235}
1236
1237/*
1238 * It seems that the mode (cv->mode) is fixnum.
1239 * For example, when converting iso-2022-jp(cp50221) to unicode:
1240 * in ascii sequence: mode=0xC42C0000
1241 * in jisx0208 sequence: mode=0xC42C0001
1242 * "C42C" is same for each convert session.
1243 * It should be: ((codepage-1)<<16)|state
1244 */
1245static int
1246mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1247{
1248 int len;
1249 int insize;
1250 HRESULT hr;
1251
1252 len = cv->mblen(cv, buf, bufsize);
1253 if (len == -1)
1254 return -1;
1255 insize = len;
1256 hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage,
1257 (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize);
1258 if (hr != S_OK || insize != len)
1259 return seterror(EILSEQ);
1260 return len;
1261}
1262
1263static int
1264mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1265{
1266 char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */
1267 int tmpsize = MB_CHAR_MAX;
1268 int insize = wbufsize;
1269 HRESULT hr;
1270
1271 hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage,
1272 (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize);
1273 if (hr != S_OK || insize != wbufsize)
1274 return seterror(EILSEQ);
1275 else if (bufsize < tmpsize)
1276 return seterror(E2BIG);
1277 else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize)
1278 return seterror(EILSEQ);
1279 memcpy(buf, tmpbuf, tmpsize);
1280 return tmpsize;
1281}
1282
1283static int
1284utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1285{
1286 int codepage = cv->codepage;
1287
1288 /* swap endian: 1200 <-> 1201 */
1289 if (cv->mode & UNICODE_MODE_SWAPPED)
1290 codepage ^= 1;
1291
1292 if (bufsize < 2)
1293 return seterror(EINVAL);
1294 if (codepage == 1200) /* little endian */
1295 wbuf[0] = (buf[1] << 8) | buf[0];
1296 else if (codepage == 1201) /* big endian */
1297 wbuf[0] = (buf[0] << 8) | buf[1];
1298
1299 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1300 {
1301 cv->mode |= UNICODE_MODE_BOM_DONE;
1302 if (wbuf[0] == 0xFFFE)
1303 {
1304 cv->mode |= UNICODE_MODE_SWAPPED;
1305 *wbufsize = 0;
1306 return 2;
1307 }
1308 else if (wbuf[0] == 0xFEFF)
1309 {
1310 *wbufsize = 0;
1311 return 2;
1312 }
1313 }
1314
1315 if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF)
1316 return seterror(EILSEQ);
1317 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1318 {
1319 if (bufsize < 4)
1320 return seterror(EINVAL);
1321 if (codepage == 1200) /* little endian */
1322 wbuf[1] = (buf[3] << 8) | buf[2];
1323 else if (codepage == 1201) /* big endian */
1324 wbuf[1] = (buf[2] << 8) | buf[3];
1325 if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF))
1326 return seterror(EILSEQ);
1327 *wbufsize = 2;
1328 return 4;
1329 }
1330 *wbufsize = 1;
1331 return 2;
1332}
1333
1334static int
1335utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1336{
1337 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1338 {
1339 int r;
1340
1341 cv->mode |= UNICODE_MODE_BOM_DONE;
1342 if (bufsize < 2)
1343 return seterror(E2BIG);
1344 if (cv->codepage == 1200) /* little endian */
1345 memcpy(buf, "\xFF\xFE", 2);
1346 else if (cv->codepage == 1201) /* big endian */
1347 memcpy(buf, "\xFE\xFF", 2);
1348
1349 r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2);
1350 if (r == -1)
1351 return -1;
1352 return r + 2;
1353 }
1354
1355 if (bufsize < 2)
1356 return seterror(E2BIG);
1357 if (cv->codepage == 1200) /* little endian */
1358 {
1359 buf[0] = (wbuf[0] & 0x00FF);
1360 buf[1] = (wbuf[0] & 0xFF00) >> 8;
1361 }
1362 else if (cv->codepage == 1201) /* big endian */
1363 {
1364 buf[0] = (wbuf[0] & 0xFF00) >> 8;
1365 buf[1] = (wbuf[0] & 0x00FF);
1366 }
1367 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1368 {
1369 if (bufsize < 4)
1370 return seterror(E2BIG);
1371 if (cv->codepage == 1200) /* little endian */
1372 {
1373 buf[2] = (wbuf[1] & 0x00FF);
1374 buf[3] = (wbuf[1] & 0xFF00) >> 8;
1375 }
1376 else if (cv->codepage == 1201) /* big endian */
1377 {
1378 buf[2] = (wbuf[1] & 0xFF00) >> 8;
1379 buf[3] = (wbuf[1] & 0x00FF);
1380 }
1381 return 4;
1382 }
1383 return 2;
1384}
1385
1386static int
1387utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1388{
1389 int codepage = cv->codepage;
1390 uint wc = 0xD800;
1391
1392 /* swap endian: 12000 <-> 12001 */
1393 if (cv->mode & UNICODE_MODE_SWAPPED)
1394 codepage ^= 1;
1395
1396 if (bufsize < 4)
1397 return seterror(EINVAL);
1398 if (codepage == 12000) /* little endian */
1399 wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
1400 else if (codepage == 12001) /* big endian */
1401 wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
1402
1403 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1404 {
1405 cv->mode |= UNICODE_MODE_BOM_DONE;
1406 if (wc == 0xFFFE0000)
1407 {
1408 cv->mode |= UNICODE_MODE_SWAPPED;
1409 *wbufsize = 0;
1410 return 4;
1411 }
1412 else if (wc == 0x0000FEFF)
1413 {
1414 *wbufsize = 0;
1415 return 4;
1416 }
1417 }
1418
1419 if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc)
1420 return seterror(EILSEQ);
1421 ucs4_to_utf16(wc, wbuf, wbufsize);
1422 return 4;
1423}
1424
1425static int
1426utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1427{
1428 uint wc;
1429
1430 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1431 {
1432 int r;
1433
1434 cv->mode |= UNICODE_MODE_BOM_DONE;
1435 if (bufsize < 4)
1436 return seterror(E2BIG);
1437 if (cv->codepage == 12000) /* little endian */
1438 memcpy(buf, "\xFF\xFE\x00\x00", 4);
1439 else if (cv->codepage == 12001) /* big endian */
1440 memcpy(buf, "\x00\x00\xFE\xFF", 4);
1441
1442 r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4);
1443 if (r == -1)
1444 return -1;
1445 return r + 4;
1446 }
1447
1448 if (bufsize < 4)
1449 return seterror(E2BIG);
1450 wc = utf16_to_ucs4(wbuf);
1451 if (cv->codepage == 12000) /* little endian */
1452 {
1453 buf[0] = wc & 0x000000FF;
1454 buf[1] = (wc & 0x0000FF00) >> 8;
1455 buf[2] = (wc & 0x00FF0000) >> 16;
1456 buf[3] = (wc & 0xFF000000) >> 24;
1457 }
1458 else if (cv->codepage == 12001) /* big endian */
1459 {
1460 buf[0] = (wc & 0xFF000000) >> 24;
1461 buf[1] = (wc & 0x00FF0000) >> 16;
1462 buf[2] = (wc & 0x0000FF00) >> 8;
1463 buf[3] = wc & 0x000000FF;
1464 }
1465 return 4;
1466}
1467
1468/*
1469 * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
1470 * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow
1471 * 1 byte Kana)
1472 * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte
1473 * Kana - SO/SI)
1474 *
1475 * MultiByteToWideChar() and WideCharToMultiByte() behave differently
1476 * depending on Windows version. On XP, WideCharToMultiByte() doesn't
1477 * terminate result sequence with ascii escape. But Vista does.
1478 * Use MLang instead.
1479 */
1480
1481#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift))
1482#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF)
1483#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF)
1484
1485#define ISO2022_SI 0
1486#define ISO2022_SO 1
1487
1488/* shift in */
1489static const char iso2022_SI_seq[] = "\x0F";
1490/* shift out */
1491static const char iso2022_SO_seq[] = "\x0E";
1492
1493typedef struct iso2022_esc_t iso2022_esc_t;
1494struct iso2022_esc_t {
1495 const char *esc;
1496 int esc_len;
1497 int len;
1498 int cs;
1499};
1500
1501#define ISO2022JP_CS_ASCII 0
1502#define ISO2022JP_CS_JISX0201_ROMAN 1
1503#define ISO2022JP_CS_JISX0201_KANA 2
1504#define ISO2022JP_CS_JISX0208_1978 3
1505#define ISO2022JP_CS_JISX0208_1983 4
1506#define ISO2022JP_CS_JISX0212 5
1507
1508static iso2022_esc_t iso2022jp_esc[] = {
1509 {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII},
1510 {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN},
1511 {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA},
1512 {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */
1513 {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983},
1514 {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212},
1515 {NULL, 0, 0, 0}
1516};
1517
1518static int
1519iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1520{
1521 iso2022_esc_t *iesc = iso2022jp_esc;
1522 char tmp[MB_CHAR_MAX];
1523 int insize;
1524 HRESULT hr;
1525 DWORD dummy = 0;
1526 int len;
1527 int esc_len;
1528 int cs;
1529 int shift;
1530 int i;
1531
1532 if (buf[0] == 0x1B)
1533 {
1534 for (i = 0; iesc[i].esc != NULL; ++i)
1535 {
1536 esc_len = iesc[i].esc_len;
1537 if (bufsize < esc_len)
1538 {
1539 if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0)
1540 return seterror(EINVAL);
1541 }
1542 else
1543 {
1544 if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0)
1545 {
1546 cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI);
1547 *wbufsize = 0;
1548 return esc_len;
1549 }
1550 }
1551 }
1552 /* not supported escape sequence */
1553 return seterror(EILSEQ);
1554 }
1555 else if (buf[0] == iso2022_SO_seq[0])
1556 {
1557 cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO);
1558 *wbufsize = 0;
1559 return 1;
1560 }
1561 else if (buf[0] == iso2022_SI_seq[0])
1562 {
1563 cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI);
1564 *wbufsize = 0;
1565 return 1;
1566 }
1567
1568 cs = ISO2022_MODE_CS(cv->mode);
1569 shift = ISO2022_MODE_SHIFT(cv->mode);
1570
1571 /* reset the mode for informal sequence */
1572 if (buf[0] < 0x20)
1573 {
1574 cs = ISO2022JP_CS_ASCII;
1575 shift = ISO2022_SI;
1576 }
1577
1578 len = iesc[cs].len;
1579 if (bufsize < len)
1580 return seterror(EINVAL);
1581 for (i = 0; i < len; ++i)
1582 if (!(buf[i] < 0x80))
1583 return seterror(EILSEQ);
1584 esc_len = iesc[cs].esc_len;
1585 memcpy(tmp, iesc[cs].esc, esc_len);
1586 if (shift == ISO2022_SO)
1587 {
1588 memcpy(tmp + esc_len, iso2022_SO_seq, 1);
1589 esc_len += 1;
1590 }
1591 memcpy(tmp + esc_len, buf, len);
1592
1593 if ((cv->codepage == 50220 || cv->codepage == 50221
1594 || cv->codepage == 50222) && shift == ISO2022_SO)
1595 {
1596 /* XXX: shift-out cannot be used for mbtowc (both kernel and
1597 * mlang) */
1598 esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len;
1599 memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len);
1600 memcpy(tmp + esc_len, buf, len);
1601 }
1602
1603 insize = len + esc_len;
1604 hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage,
1605 (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize);
1606 if (hr != S_OK || insize != len + esc_len)
1607 return seterror(EILSEQ);
1608
1609 /* Check for conversion error. Assuming defaultChar is 0x3F. */
1610 /* ascii should be converted from ascii */
1611 if (wbuf[0] == buf[0]
1612 && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
1613 return seterror(EILSEQ);
1614
1615 /* reset the mode for informal sequence */
1616 if (cv->mode != ISO2022_MODE(cs, shift))
1617 cv->mode = ISO2022_MODE(cs, shift);
1618
1619 return len;
1620}
1621
1622static int
1623iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1624{
1625 iso2022_esc_t *iesc = iso2022jp_esc;
1626 char tmp[MB_CHAR_MAX];
1627 int tmpsize = MB_CHAR_MAX;
1628 int insize = wbufsize;
1629 HRESULT hr;
1630 DWORD dummy = 0;
1631 int len;
1632 int esc_len;
1633 int cs;
1634 int shift;
1635 int i;
1636
1637 /*
1638 * MultiByte = [escape sequence] + character + [escape sequence]
1639 *
1640 * Whether trailing escape sequence is added depends on which API is
1641 * used (kernel or MLang, and its version).
1642 */
1643 hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage,
1644 (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize);
1645 if (hr != S_OK || insize != wbufsize)
1646 return seterror(EILSEQ);
1647 else if (bufsize < tmpsize)
1648 return seterror(E2BIG);
1649
1650 if (tmpsize == 1)
1651 {
1652 cs = ISO2022JP_CS_ASCII;
1653 esc_len = 0;
1654 }
1655 else
1656 {
1657 for (i = 1; iesc[i].esc != NULL; ++i)
1658 {
1659 esc_len = iesc[i].esc_len;
1660 if (strncmp(tmp, iesc[i].esc, esc_len) == 0)
1661 {
1662 cs = iesc[i].cs;
1663 break;
1664 }
1665 }
1666 if (iesc[i].esc == NULL)
1667 /* not supported escape sequence */
1668 return seterror(EILSEQ);
1669 }
1670
1671 shift = ISO2022_SI;
1672 if (tmp[esc_len] == iso2022_SO_seq[0])
1673 {
1674 shift = ISO2022_SO;
1675 esc_len += 1;
1676 }
1677
1678 len = iesc[cs].len;
1679
1680 /* Check for converting error. Assuming defaultChar is 0x3F. */
1681 /* ascii should be converted from ascii */
1682 if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80))
1683 return seterror(EILSEQ);
1684 else if (tmpsize < esc_len + len)
1685 return seterror(EILSEQ);
1686
1687 if (cv->mode == ISO2022_MODE(cs, shift))
1688 {
1689 /* remove escape sequence */
1690 if (esc_len != 0)
1691 memmove(tmp, tmp + esc_len, len);
1692 esc_len = 0;
1693 }
1694 else
1695 {
1696 if (cs == ISO2022JP_CS_ASCII)
1697 {
1698 esc_len = iesc[ISO2022JP_CS_ASCII].esc_len;
1699 memmove(tmp + esc_len, tmp, len);
1700 memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len);
1701 }
1702 if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO)
1703 {
1704 /* shift-in before changing to other mode */
1705 memmove(tmp + 1, tmp, len + esc_len);
1706 memcpy(tmp, iso2022_SI_seq, 1);
1707 esc_len += 1;
1708 }
1709 }
1710
1711 if (bufsize < len + esc_len)
1712 return seterror(E2BIG);
1713 memcpy(buf, tmp, len + esc_len);
1714 cv->mode = ISO2022_MODE(cs, shift);
1715 return len + esc_len;
1716}
1717
1718static int
1719iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize)
1720{
1721 iso2022_esc_t *iesc = iso2022jp_esc;
1722 int esc_len;
1723
1724 if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
1725 {
1726 esc_len = 0;
1727 if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
1728 esc_len += 1;
1729 if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
1730 esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
1731 if (bufsize < esc_len)
1732 return seterror(E2BIG);
1733
1734 esc_len = 0;
1735 if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
1736 {
1737 memcpy(buf, iso2022_SI_seq, 1);
1738 esc_len += 1;
1739 }
1740 if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
1741 {
1742 memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc,
1743 iesc[ISO2022JP_CS_ASCII].esc_len);
1744 esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
1745 }
1746 return esc_len;
1747 }
1748 return 0;
1749}
1750
1751static void process_file(iconv_t cd, FILE *in, FILE *out)
1752{
1753 char inbuf[BUFSIZ];
1754 char outbuf[BUFSIZ];
1755 const char *pin;
1756 char *pout;
1757 size_t inbytesleft;
1758 size_t outbytesleft;
1759 size_t rest = 0;
1760 size_t r;
1761
1762 while ((inbytesleft=fread(inbuf+rest, 1, sizeof(inbuf)-rest, in)) != 0
1763 || rest != 0) {
1764 inbytesleft += rest;
1765 pin = inbuf;
1766 pout = outbuf;
1767 outbytesleft = sizeof(outbuf);
1768 r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);
1769 fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out);
1770 if (r == (size_t)(-1) && errno != E2BIG &&
1771 (errno != EINVAL || feof(in)))
1772 bb_perror_msg_and_die("conversion error");
1773 memmove(inbuf, pin, inbytesleft);
1774 rest = inbytesleft;
1775 }
1776 pout = outbuf;
1777 outbytesleft = sizeof(outbuf);
1778 r = iconv(cd, NULL, NULL, &pout, &outbytesleft);
1779 fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out);
1780 if (r == (size_t)(-1))
1781 bb_perror_msg_and_die("conversion error");
1782}
1783
1784int iconv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1785int iconv_main(int argc, char **argv)
1786{
1787 char *fromcode = NULL;
1788 char *tocode = NULL;
1789 int i;
1790 iconv_t cd;
1791 FILE *in = stdin;
1792 FILE *out = stdout;
1793 int ignore = 0;
1794
1795 while ((i = getopt(argc, argv, "f:t:lco:")) != -1) {
1796 switch (i) {
1797 case 'l':
1798 for (i = 0; codepage_alias[i].name != NULL; ++i)
1799 printf("%s\n", codepage_alias[i].name);
1800 return 0;
1801
1802 case 'f':
1803 fromcode = optarg;
1804 break;
1805
1806 case 't':
1807 tocode = optarg;
1808 break;
1809
1810 case 'c':
1811 ignore = 1;
1812 break;
1813
1814 case 'o':
1815 out = xfopen(optarg, "wb");
1816 break;
1817
1818 default:
1819 bb_show_usage();
1820 }
1821 }
1822
1823 if (fromcode == NULL || tocode == NULL)
1824 bb_show_usage();
1825
1826 if (ignore)
1827 tocode = xasprintf("%s//IGNORE", tocode);
1828
1829 cd = iconv_open(tocode, fromcode);
1830 if (cd == (iconv_t)(-1))
1831 bb_perror_msg_and_die("iconv_open error");
1832
1833 if (optind == argc ||
1834 (optind == argc-1 && strcmp(argv[optind], "-") == 0)) {
1835 process_file(cd, in, out);
1836 }
1837 else {
1838 for (i=optind; i<argc; ++i) {
1839 in = xfopen(argv[i], "rb");
1840 process_file(cd, in, out);
1841 fclose(in);
1842 }
1843 }
1844
1845 iconv_close(cd);
1846 return 0;
1847}
diff --git a/miscutils/less.c b/miscutils/less.c
index 223c2558d..a5ce14c91 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
@@ -330,7 +338,11 @@ static void less_exit(int code)
330 set_tty_cooked(); 338 set_tty_cooked();
331 if (!(G.kbd_fd_orig_flags & O_NONBLOCK)) 339 if (!(G.kbd_fd_orig_flags & O_NONBLOCK))
332 ndelay_off(kbd_fd); 340 ndelay_off(kbd_fd);
341#if !ENABLE_PLATFORM_MINGW32
333 clear_line(); 342 clear_line();
343#else
344 printf(ESC"[?1049l");
345#endif
334 if (code < 0) 346 if (code < 0)
335 kill_myself_with_sig(- code); /* does not return */ 347 kill_myself_with_sig(- code); /* does not return */
336 exit(code); 348 exit(code);
@@ -575,6 +587,11 @@ static void read_lines(void)
575 last_line_pos = 0; 587 last_line_pos = 0;
576 break; 588 break;
577 } 589 }
590#if ENABLE_PLATFORM_MINGW32
591 if (c == '\r') {
592 continue;
593 }
594#endif
578 /* NUL is substituted by '\n'! */ 595 /* NUL is substituted by '\n'! */
579 if (c == '\0') c = '\n'; 596 if (c == '\0') c = '\n';
580 *p++ = c; 597 *p++ = c;
@@ -671,7 +688,12 @@ static void update_num_lines(void)
671 /* only do this for regular files */ 688 /* only do this for regular files */
672 if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) { 689 if (num_lines == REOPEN_AND_COUNT || num_lines == REOPEN_STDIN) {
673 count = 0; 690 count = 0;
691#if !ENABLE_PLATFORM_MINGW32
674 fd = open("/proc/self/fd/0", O_RDONLY); 692 fd = open("/proc/self/fd/0", O_RDONLY);
693#else
694 /* don't even try to access /proc on WIN32 */
695 fd = -1;
696#endif
675 if (fd < 0 && num_lines == REOPEN_AND_COUNT) { 697 if (fd < 0 && num_lines == REOPEN_AND_COUNT) {
676 /* "filename" is valid only if REOPEN_AND_COUNT */ 698 /* "filename" is valid only if REOPEN_AND_COUNT */
677 fd = open(filename, O_RDONLY); 699 fd = open(filename, O_RDONLY);
@@ -854,7 +876,12 @@ static void print_found(const char *line)
854 match_status = 1; 876 match_status = 1;
855 } 877 }
856 878
879#if !ENABLE_PLATFORM_MINGW32
857 printf("%s%s\n", growline ? growline : "", str); 880 printf("%s%s\n", growline ? growline : "", str);
881#else
882 /* skip newline, we use explicit positioning on WIN32 */
883 printf("%s%s", growline ? growline : "", str);
884#endif
858 free(growline); 885 free(growline);
859} 886}
860#else 887#else
@@ -890,7 +917,12 @@ static void print_ascii(const char *str)
890 *p = '\0'; 917 *p = '\0';
891 print_hilite(buf); 918 print_hilite(buf);
892 } 919 }
920#if !ENABLE_PLATFORM_MINGW32
893 puts(str); 921 puts(str);
922#else
923 /* skip newline, we use explicit positioning on WIN32 */
924 printf("%s", str);
925#endif
894} 926}
895 927
896/* Print the buffer */ 928/* Print the buffer */
@@ -900,6 +932,10 @@ static void buffer_print(void)
900 932
901 move_cursor(0, 0); 933 move_cursor(0, 0);
902 for (i = 0; i <= max_displayed_line; i++) { 934 for (i = 0; i <= max_displayed_line; i++) {
935#if ENABLE_PLATFORM_MINGW32
936 /* make sure we're on the right line */
937 move_cursor(i+1, 0);
938#endif
903 printf(CLEAR_2_EOL); 939 printf(CLEAR_2_EOL);
904 if (option_mask32 & FLAG_N) 940 if (option_mask32 & FLAG_N)
905 print_lineno(buffer[i]); 941 print_lineno(buffer[i]);
@@ -1087,9 +1123,13 @@ static void reinitialize(void)
1087 if (G.winsize_err) 1123 if (G.winsize_err)
1088 printf(ESC"[999;999H" ESC"[6n"); 1124 printf(ESC"[999;999H" ESC"[6n");
1089#endif 1125#endif
1126#if ENABLE_PLATFORM_MINGW32
1127 printf(ESC"[?1049h");
1128#endif
1090 buffer_fill_and_print(); 1129 buffer_fill_and_print();
1091} 1130}
1092 1131
1132#if !ENABLE_PLATFORM_MINGW32
1093static int64_t getch_nowait(void) 1133static int64_t getch_nowait(void)
1094{ 1134{
1095 int rd; 1135 int rd;
@@ -1151,6 +1191,46 @@ static int64_t getch_nowait(void)
1151 set_tty_cooked(); 1191 set_tty_cooked();
1152 return key64; 1192 return key64;
1153} 1193}
1194#else
1195static int64_t getch_nowait(void)
1196{
1197 int64_t c;
1198
1199retry:
1200 c = _getch();
1201 if (c == 0 || c == 0xe0) {
1202 switch (_getch()) {
1203 case 0x48:
1204 c = KEYCODE_UP;
1205 break;
1206 case 0x50:
1207 c = KEYCODE_DOWN;
1208 break;
1209 case 0x49:
1210 c = KEYCODE_PAGEUP;
1211 break;
1212 case 0x51:
1213 c = KEYCODE_PAGEDOWN;
1214 break;
1215 case 0x47:
1216 c = KEYCODE_HOME;
1217 break;
1218 case 0x4f:
1219 c = KEYCODE_END;
1220 break;
1221 default:
1222 goto retry;
1223 }
1224 }
1225
1226 /* Position cursor if line input is done */
1227 if (less_gets_pos >= 0)
1228 move_cursor(max_displayed_line + 2, less_gets_pos + 1);
1229 fflush_all();
1230
1231 return c;
1232}
1233#endif
1154 1234
1155/* Grab a character from input without requiring the return key. 1235/* Grab a character from input without requiring the return key.
1156 * May return KEYCODE_xxx values. 1236 * May return KEYCODE_xxx values.
@@ -1791,10 +1871,12 @@ static void keypress_process(int keypress)
1791 number_process(keypress); 1871 number_process(keypress);
1792} 1872}
1793 1873
1874#if !ENABLE_PLATFORM_MINGW32
1794static void sig_catcher(int sig) 1875static void sig_catcher(int sig)
1795{ 1876{
1796 less_exit(- sig); 1877 less_exit(- sig);
1797} 1878}
1879#endif
1798 1880
1799#if ENABLE_FEATURE_LESS_WINCH 1881#if ENABLE_FEATURE_LESS_WINCH
1800static void sigwinch_handler(int sig UNUSED_PARAM) 1882static void sigwinch_handler(int sig UNUSED_PARAM)
@@ -1806,7 +1888,9 @@ static void sigwinch_handler(int sig UNUSED_PARAM)
1806int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 1888int less_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1807int less_main(int argc, char **argv) 1889int less_main(int argc, char **argv)
1808{ 1890{
1891#if !ENABLE_PLATFORM_MINGW32
1809 char *tty_name; 1892 char *tty_name;
1893#endif
1810 int tty_fd; 1894 int tty_fd;
1811 1895
1812 INIT_G(); 1896 INIT_G();
@@ -1865,6 +1949,7 @@ int less_main(int argc, char **argv)
1865 if (option_mask32 & FLAG_TILDE) 1949 if (option_mask32 & FLAG_TILDE)
1866 empty_line_marker = ""; 1950 empty_line_marker = "";
1867 1951
1952#if !ENABLE_PLATFORM_MINGW32
1868 /* Some versions of less can survive w/o controlling tty, 1953 /* Some versions of less can survive w/o controlling tty,
1869 * try to do the same. This also allows to specify an alternative 1954 * try to do the same. This also allows to specify an alternative
1870 * tty via "less 1<>TTY". 1955 * tty via "less 1<>TTY".
@@ -1890,8 +1975,13 @@ int less_main(int argc, char **argv)
1890 } 1975 }
1891 G.kbd_fd_orig_flags = ndelay_on(tty_fd); 1976 G.kbd_fd_orig_flags = ndelay_on(tty_fd);
1892 kbd_fd = tty_fd; /* save in a global */ 1977 kbd_fd = tty_fd; /* save in a global */
1978#else
1979 kbd_fd = tty_fd = 0;
1980#endif
1893 1981
1982#if !ENABLE_PLATFORM_MINGW32
1894 get_termios_and_make_raw(tty_fd, &term_less, &term_orig, TERMIOS_RAW_CRNL_INPUT); 1983 get_termios_and_make_raw(tty_fd, &term_less, &term_orig, TERMIOS_RAW_CRNL_INPUT);
1984#endif
1895 1985
1896 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(tty_fd, &width, &max_displayed_line); 1986 IF_FEATURE_LESS_ASK_TERMINAL(G.winsize_err =) get_terminal_width_height(tty_fd, &width, &max_displayed_line);
1897 /* 20: two tabstops + 4 */ 1987 /* 20: two tabstops + 4 */
diff --git a/miscutils/man.c b/miscutils/man.c
index 61086612a..6724b4b5d 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -199,8 +199,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
199 if (path) while (*path) { 199 if (path) while (*path) {
200 char *next_path; 200 char *next_path;
201 char **path_element; 201 char **path_element;
202 202 next_path = strchr(path, PATH_SEP);
203 next_path = strchr(path, ':');
204 if (next_path) { 203 if (next_path) {
205 if (next_path == path) /* "::"? */ 204 if (next_path == path) /* "::"? */
206 goto next; 205 goto next;
@@ -223,7 +222,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
223 if (!next_path) 222 if (!next_path)
224 break; 223 break;
225 /* "path" may be a result of getenv(), be nice and don't mangle it */ 224 /* "path" may be a result of getenv(), be nice and don't mangle it */
226 *next_path = ':'; 225 *next_path = PATH_SEP;
227 next: 226 next:
228 path = next_path + 1; 227 path = next_path + 1;
229 } 228 }
@@ -249,11 +248,24 @@ int man_main(int argc UNUSED_PARAM, char **argv)
249 int count_mp; 248 int count_mp;
250 int opt, not_found; 249 int opt, not_found;
251 char *token[2]; 250 char *token[2];
251#if ENABLE_PLATFORM_MINGW32
252 char **ptr;
253 char *exepath, *relpath;
254 const char *mpl[] = { "/usr/man", "/usr/share/man", NULL, NULL };
255#endif
252 256
253 INIT_G(); 257 INIT_G();
254 258
255 opt = getopt32(argv, "^+" "aw" "\0" "-1"/*at least one arg*/); 259 opt = getopt32(argv, "^+" "aw" "\0" "-1"/*at least one arg*/);
256 argv += optind; 260 argv += optind;
261#if ENABLE_PLATFORM_MINGW32
262 /* add system drive prefix to filenames, if necessary */
263 for (ptr = argv; *ptr; ++ptr) {
264 if (strchr(*ptr, '/') || strchr(*ptr, '\\'))
265 *ptr = xabsolute_path(*ptr);
266 }
267 chdir_system_drive();
268#endif
257 269
258 sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); 270 sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9");
259 271
@@ -291,11 +303,25 @@ int man_main(int argc UNUSED_PARAM, char **argv)
291 } 303 }
292 config_close(parser); 304 config_close(parser);
293 305
306#if ENABLE_PLATFORM_MINGW32
307 /* allow man pages to be stored relative to the executable */
308 exepath = xstrdup(bb_busybox_exec_path);
309 relpath = concat_path_file(dirname(exepath), "man");
310
311 if (!man_path_list) {
312 mpl[2] = relpath;
313 man_path_list = (char**)mpl;
314 }
315 else {
316 man_path_list = add_MANPATH(man_path_list, &count_mp, relpath);
317 }
318#else
294 if (!man_path_list) { 319 if (!man_path_list) {
295 static const char *const mpl[] = { "/usr/man", "/usr/share/man", NULL }; 320 static const char *const mpl[] = { "/usr/man", "/usr/share/man", NULL };
296 man_path_list = (char**)mpl; 321 man_path_list = (char**)mpl;
297 /*count_mp = 2; - not used below anyway */ 322 /*count_mp = 2; - not used below anyway */
298 } 323 }
324#endif
299 325
300 { 326 {
301 /* environment overrides setting from man.config */ 327 /* environment overrides setting from man.config */
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 db7be07f3..d1a0204ed 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 b6f9d605a..44cec2cb5 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -486,6 +486,9 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp)
486 fprintf(stderr, "--> %s%s\n\n", s1, s2); 486 fprintf(stderr, "--> %s%s\n\n", s1, s2);
487 fflush(fp); 487 fflush(fp);
488 log_io("> %s%s", s1, s2); 488 log_io("> %s%s", s1, s2);
489#if ENABLE_PLATFORM_MINGW32
490 fseek(fp, 0L, SEEK_CUR);
491#endif
489 } 492 }
490 493
491 /* Read until "Nxx something" is received */ 494 /* Read until "Nxx something" is received */
@@ -493,6 +496,9 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp)
493 do { 496 do {
494 fgets_trim_sanitize(fp, "%s\n"); 497 fgets_trim_sanitize(fp, "%s\n");
495 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' '); 498 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
499#if ENABLE_PLATFORM_MINGW32
500 fseek(fp, 0L, SEEK_CUR);
501#endif
496 502
497 G.wget_buf[3] = '\0'; 503 G.wget_buf[3] = '\0';
498 result = xatoi_positive(G.wget_buf); 504 result = xatoi_positive(G.wget_buf);
@@ -716,6 +722,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port)
716#endif 722#endif
717 723
718#if ENABLE_FEATURE_WGET_HTTPS 724#if ENABLE_FEATURE_WGET_HTTPS
725# if !ENABLE_PLATFORM_MINGW32
719static void spawn_ssl_client(const char *host, int network_fd, int flags) 726static void spawn_ssl_client(const char *host, int network_fd, int flags)
720{ 727{
721 int sp[2]; 728 int sp[2];
@@ -770,6 +777,32 @@ static void spawn_ssl_client(const char *host, int network_fd, int flags)
770 close(sp[1]); 777 close(sp[1]);
771 xmove_fd(sp[0], network_fd); 778 xmove_fd(sp[0], network_fd);
772} 779}
780# else
781static void spawn_ssl_client(const char *host, int network_fd, int flags)
782{
783 int fd1;
784 char *servername, *p, *cmd;
785
786 servername = xstrdup(host);
787 p = strrchr(servername, ':');
788 if (p) *p = '\0';
789
790 fflush_all();
791
792 cmd = xasprintf("%s --busybox ssl_client -h %p -n %s%s",
793 bb_busybox_exec_path,
794 (void *)_get_osfhandle(network_fd), servername,
795 flags & TLSLOOP_EXIT_ON_LOCAL_EOF ? " -e" : "");
796
797 if ( (fd1=mingw_popen_fd(cmd, "b", -1, NULL)) == -1 ) {
798 bb_perror_msg_and_die("can't execute ssl_client");
799 }
800
801 free(cmd);
802 free(servername);
803 xmove_fd(fd1, network_fd);
804}
805# endif
773#endif 806#endif
774 807
775static FILE* prepare_ftp_session(FILE **dfpp, struct host_info *target, len_and_sockaddr *lsa) 808static 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 815c11578..a4de11379 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 924e17f32..b82c51029 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,25 @@
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.
176//config:
177//config:config ASH_NOCASEGLOB
178//config: bool "'nocaseglob' option"
179//config: default y
180//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
181//config: help
182//config: Enable support for the 'nocaseglob' option, which allows
183//config: case-insensitive filename globbing.
184//config:
151//config:endif # ash options 185//config:endif # ash options
152 186
153//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 187//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
@@ -284,6 +318,10 @@ typedef long arith_t;
284# define PIPE_BUF 4096 /* amount of buffering in a pipe */ 318# define PIPE_BUF 4096 /* amount of buffering in a pipe */
285#endif 319#endif
286 320
321#if !ENABLE_PLATFORM_MINGW32
322# define is_absolute_path(path) ((path)[0] == '/')
323#endif
324
287#if !BB_MMU 325#if !BB_MMU
288# error "Do not even bother, ash will not run on NOMMU machine" 326# error "Do not even bother, ash will not run on NOMMU machine"
289#endif 327#endif
@@ -301,6 +339,69 @@ typedef long arith_t;
301# define BB_GLOBAL_CONST const 339# define BB_GLOBAL_CONST const
302#endif 340#endif
303 341
342#define FORKSHELL_DEBUG 0
343#if ENABLE_PLATFORM_MINGW32
344union node;
345struct strlist;
346struct job;
347
348struct forkshell {
349 /* filled by forkshell_copy() */
350 struct globals_var *gvp;
351 struct globals_misc *gmp;
352 struct tblentry **cmdtable;
353 /* struct alias **atab; */
354 /* struct parsefile *g_parsefile; */
355 HANDLE hMapFile;
356 char *old_base;
357# if FORKSHELL_DEBUG
358 int nodeptrcount;
359 int funcblocksize;
360 int funcstringsize;
361# endif
362 int size;
363
364 /* type of forkshell */
365 int fpid;
366
367 /* generic data, used by forkshell_child */
368 int mode;
369 int nprocs;
370
371 /* optional data, used by forkshell_child */
372 int flags;
373 int fd[3];
374 union node *n;
375 char **argv;
376 char *path;
377 struct strlist *varlist;
378
379 /* start of data block */
380 char **nodeptr[1];
381};
382
383enum {
384 FS_OPENHERE,
385 FS_EVALBACKCMD,
386 FS_EVALSUBSHELL,
387 FS_EVALPIPE,
388 FS_SHELLEXEC
389};
390
391static struct forkshell* forkshell_prepare(struct forkshell *fs);
392static void forkshell_init(const char *idstr);
393static void forkshell_child(struct forkshell *fs);
394static void sticky_free(void *p);
395# define free(p) sticky_free(p)
396#if !JOBS
397#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
398#endif
399static void spawn_forkshell(struct forkshell *fs, struct job *jp,
400 union node *n, int mode);
401# if FORKSHELL_DEBUG
402static void forkshell_print(FILE *fp0, struct forkshell *fs, char **notes);
403# endif
404#endif
304 405
305/* ============ Hash table sizes. Configurable. */ 406/* ============ Hash table sizes. Configurable. */
306 407
@@ -333,6 +434,15 @@ static const char *const optletters_optnames[] = {
333 ,"\0" "nolog" 434 ,"\0" "nolog"
334 ,"\0" "debug" 435 ,"\0" "debug"
335#endif 436#endif
437#if ENABLE_PLATFORM_MINGW32
438 ,"X" "winxp"
439#endif
440#if ENABLE_ASH_NOCONSOLE
441 ,"\0" "noconsole"
442#endif
443#if ENABLE_ASH_NOCASEGLOB
444 ,"\0" "nocaseglob"
445#endif
336}; 446};
337 447
338#define optletters(n) optletters_optnames[n][0] 448#define optletters(n) optletters_optnames[n][0]
@@ -365,15 +475,25 @@ struct globals_misc {
365 int rootpid; /* pid of main shell */ 475 int rootpid; /* pid of main shell */
366 /* shell level: 0 for the main shell, 1 for its children, and so on */ 476 /* shell level: 0 for the main shell, 1 for its children, and so on */
367 int shlvl; 477 int shlvl;
478#if ENABLE_PLATFORM_MINGW32
479 int loopnest; /* current loop nesting level */
480#endif
368#define rootshell (!shlvl) 481#define rootshell (!shlvl)
369 int errlinno; 482 int errlinno;
370 483
371 char *minusc; /* argument to -c option */ 484 char *minusc; /* argument to -c option */
485#if ENABLE_PLATFORM_MINGW32
486 char *dirarg; /* argument to -d option */
487 char *title; /* argument to -t option */
488#endif
372 489
373 char *curdir; // = nullstr; /* current working directory */ 490 char *curdir; // = nullstr; /* current working directory */
374 char *physdir; // = nullstr; /* physical working directory */ 491 char *physdir; // = nullstr; /* physical working directory */
375 492
376 char *arg0; /* value of $0 */ 493 char *arg0; /* value of $0 */
494#if ENABLE_PLATFORM_MINGW32
495 char *commandname;
496#endif
377 497
378 struct jmploc *exception_handler; 498 struct jmploc *exception_handler;
379 499
@@ -413,6 +533,15 @@ struct globals_misc {
413# define nolog optlist[14 + BASH_PIPEFAIL] 533# define nolog optlist[14 + BASH_PIPEFAIL]
414# define debug optlist[15 + BASH_PIPEFAIL] 534# define debug optlist[15 + BASH_PIPEFAIL]
415#endif 535#endif
536#if ENABLE_PLATFORM_MINGW32
537# define winxp optlist[14 + BASH_PIPEFAIL + 2*DEBUG]
538#endif
539#if ENABLE_ASH_NOCONSOLE
540# define noconsole optlist[15 + BASH_PIPEFAIL + 2*DEBUG]
541#endif
542#if ENABLE_ASH_NOCASEGLOB
543# define nocaseglob optlist[15 + BASH_PIPEFAIL + 2*DEBUG + ENABLE_ASH_NOCONSOLE]
544#endif
416 545
417 /* trap handler commands */ 546 /* trap handler commands */
418 /* 547 /*
@@ -446,10 +575,20 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
446#define rootpid (G_misc.rootpid ) 575#define rootpid (G_misc.rootpid )
447#define shlvl (G_misc.shlvl ) 576#define shlvl (G_misc.shlvl )
448#define errlinno (G_misc.errlinno ) 577#define errlinno (G_misc.errlinno )
578#if ENABLE_PLATFORM_MINGW32
579#define loopnest (G_misc.loopnest )
580#endif
449#define minusc (G_misc.minusc ) 581#define minusc (G_misc.minusc )
582#if ENABLE_PLATFORM_MINGW32
583#define dirarg (G_misc.dirarg )
584#define title (G_misc.title )
585#endif
450#define curdir (G_misc.curdir ) 586#define curdir (G_misc.curdir )
451#define physdir (G_misc.physdir ) 587#define physdir (G_misc.physdir )
452#define arg0 (G_misc.arg0 ) 588#define arg0 (G_misc.arg0 )
589#if ENABLE_PLATFORM_MINGW32
590#define commandname (G_misc.commandname)
591#endif
453#define exception_handler (G_misc.exception_handler) 592#define exception_handler (G_misc.exception_handler)
454#define exception_type (G_misc.exception_type ) 593#define exception_type (G_misc.exception_type )
455#define suppress_int (G_misc.suppress_int ) 594#define suppress_int (G_misc.suppress_int )
@@ -1322,7 +1461,9 @@ struct parsefile {
1322 1461
1323static struct parsefile basepf; /* top level input file */ 1462static struct parsefile basepf; /* top level input file */
1324static struct parsefile *g_parsefile = &basepf; /* current input file */ 1463static struct parsefile *g_parsefile = &basepf; /* current input file */
1464#if ENABLE_PLATFORM_POSIX
1325static char *commandname; /* currently executing command */ 1465static char *commandname; /* currently executing command */
1466#endif
1326 1467
1327 1468
1328/* ============ Message printing */ 1469/* ============ Message printing */
@@ -2327,6 +2468,42 @@ bltinlookup(const char *name)
2327 return lookupvar(name); 2468 return lookupvar(name);
2328} 2469}
2329 2470
2471#if ENABLE_PLATFORM_MINGW32
2472static char *
2473fix_pathvar(const char *path, int len)
2474{
2475 char *newpath = xstrdup(path);
2476 char *p;
2477 int modified = FALSE;
2478
2479 p = newpath + len;
2480 while (*p) {
2481 if (*p != ':' && *p != ';') {
2482 /* skip drive */
2483 if (isalpha(*p) && p[1] == ':')
2484 p += 2;
2485 /* skip through path component */
2486 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2487 continue;
2488 }
2489 /* *p is ':', ';' or '\0' here */
2490 if (*p == ':') {
2491 *p++ = ';';
2492 modified = TRUE;
2493 }
2494 else if (*p == ';') {
2495 ++p;
2496 }
2497 }
2498
2499 if (!modified) {
2500 free(newpath);
2501 newpath = NULL;
2502 }
2503 return newpath;
2504}
2505#endif
2506
2330/* 2507/*
2331 * Same as setvar except that the variable and value are passed in 2508 * Same as setvar except that the variable and value are passed in
2332 * the first argument as name=value. Since the first argument will 2509 * the first argument as name=value. Since the first argument will
@@ -2339,6 +2516,27 @@ setvareq(char *s, int flags)
2339{ 2516{
2340 struct var *vp, **vpp; 2517 struct var *vp, **vpp;
2341 2518
2519#if ENABLE_PLATFORM_MINGW32
2520 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2521 const char *p;
2522 int len;
2523
2524 for (p = paths; *p; p += len + 1) {
2525 len = strlen(p);
2526 if (strncmp(s, p, len) == 0) {
2527 char *newpath = fix_pathvar(s, len);
2528 if (newpath) {
2529 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2530 free(s);
2531 flags |= VNOSAVE;
2532 flags &= ~(VTEXTFIXED|VSTACK);
2533 s = newpath;
2534 }
2535 break;
2536 }
2537 }
2538#endif
2539
2342 vpp = hashvar(s); 2540 vpp = hashvar(s);
2343 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2541 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2344 vpp = findvar(vpp, s); 2542 vpp = findvar(vpp, s);
@@ -2555,10 +2753,12 @@ path_advance(const char **path, const char *name)
2555 if (*path == NULL) 2753 if (*path == NULL)
2556 return NULL; 2754 return NULL;
2557 start = *path; 2755 start = *path;
2558 for (p = start; *p && *p != ':' && *p != '%'; p++) 2756 for (p = start; *p && *p != PATH_SEP && *p != '%'; p++)
2559 continue; 2757 continue;
2560 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 2758 len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */
2561 while (stackblocksize() < len) 2759
2760 /* reserve space for suffix on WIN32 */
2761 while (stackblocksize() < (ENABLE_PLATFORM_MINGW32 ? len+4 : len))
2562 growstackblock(); 2762 growstackblock();
2563 q = stackblock(); 2763 q = stackblock();
2564 if (p != start) { 2764 if (p != start) {
@@ -2569,10 +2769,10 @@ path_advance(const char **path, const char *name)
2569 pathopt = NULL; 2769 pathopt = NULL;
2570 if (*p == '%') { 2770 if (*p == '%') {
2571 pathopt = ++p; 2771 pathopt = ++p;
2572 while (*p && *p != ':') 2772 while (*p && *p != PATH_SEP)
2573 p++; 2773 p++;
2574 } 2774 }
2575 if (*p == ':') 2775 if (*p == PATH_SEP)
2576 *path = p + 1; 2776 *path = p + 1;
2577 else 2777 else
2578 *path = NULL; 2778 *path = NULL;
@@ -2656,6 +2856,7 @@ setprompt_if(smallint do_set, int whichprompt)
2656 2856
2657#define CD_PHYSICAL 1 2857#define CD_PHYSICAL 1
2658#define CD_PRINT 2 2858#define CD_PRINT 2
2859#define CD_PRINT_ALL 4
2659 2860
2660static int 2861static int
2661cdopt(void) 2862cdopt(void)
@@ -2664,7 +2865,14 @@ cdopt(void)
2664 int i, j; 2865 int i, j;
2665 2866
2666 j = 'L'; 2867 j = 'L';
2868#if ENABLE_PLATFORM_MINGW32
2869 while ((i = nextopt("LPa")) != '\0') {
2870 if (i == 'a')
2871 flags |= CD_PRINT_ALL;
2872 else
2873#else
2667 while ((i = nextopt("LP")) != '\0') { 2874 while ((i = nextopt("LP")) != '\0') {
2875#endif
2668 if (i != j) { 2876 if (i != j) {
2669 flags ^= CD_PHYSICAL; 2877 flags ^= CD_PHYSICAL;
2670 j = i; 2878 j = i;
@@ -2681,6 +2889,114 @@ cdopt(void)
2681static const char * 2889static const char *
2682updatepwd(const char *dir) 2890updatepwd(const char *dir)
2683{ 2891{
2892#if ENABLE_PLATFORM_MINGW32
2893# define is_path_sep(x) ((x) == '/' || (x) == '\\')
2894# define is_root(x) (is_path_sep(x[0]) && x[1] == '\0')
2895 /*
2896 * Due to Windows drive notion, getting pwd is a completely
2897 * different thing. Handle it in a separate routine
2898 */
2899
2900 char *new;
2901 char *p;
2902 char *cdcomppath;
2903 const char *lim;
2904 int len = 0;
2905 /*
2906 * There are five cases that make some kind of sense
2907 *
2908 * Absolute paths:
2909 * c:/path
2910 * //host/share
2911 *
2912 * Relative to current drive:
2913 * /path
2914 *
2915 * Relative to current working directory of current drive
2916 * path
2917 *
2918 * Relative to current working directory of other drive
2919 * c:path
2920 */
2921 int absdrive = has_dos_drive_prefix(dir);
2922 int curr_relpath = !absdrive && !is_path_sep(*dir);
2923 int other_relpath = absdrive && !is_path_sep(dir[2]);
2924 int relpath = curr_relpath || other_relpath;
2925
2926 cdcomppath = sstrdup(dir);
2927 STARTSTACKSTR(new);
2928
2929 /* prefix new path with current directory, if required */
2930 if (other_relpath) {
2931 /* c:path */
2932 char buffer[PATH_MAX];
2933
2934 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
2935 return 0;
2936 new = stack_putstr(buffer, new);
2937 }
2938 else if (curr_relpath || (is_root(dir) && unc_root_len(curdir))) {
2939 /* relative path on current drive or explicit root of UNC curdir */
2940 if (curdir == nullstr)
2941 return 0;
2942 new = stack_putstr(curdir, new);
2943 }
2944
2945 new = makestrspace(strlen(dir) + 2, new);
2946
2947 if ( (len=unc_root_len(dir)) || ((len=unc_root_len(curdir)) &&
2948 (is_root(dir) || curr_relpath)) ) {
2949 /* //host/share or path relative to //host/share */
2950 lim = (char *)stackblock() + len;
2951 }
2952 else {
2953 if (absdrive) {
2954 if (!relpath)
2955 new = stack_nputstr(dir, 2, new);
2956 cdcomppath += 2;
2957 dir += 2;
2958 }
2959 lim = (char *)stackblock() + 3;
2960 }
2961
2962 if (relpath) {
2963 if (!is_path_sep(new[-1]))
2964 USTPUTC('/', new);
2965 } else {
2966 USTPUTC('/', new);
2967 cdcomppath ++;
2968 if (is_path_sep(dir[1]) && !is_path_sep(dir[2])) {
2969 USTPUTC('/', new);
2970 cdcomppath++;
2971 }
2972 }
2973 p = strtok(cdcomppath, "/\\");
2974 while (p) {
2975 switch (*p) {
2976 case '.':
2977 if (p[1] == '.' && p[2] == '\0') {
2978 while (new > lim) {
2979 STUNPUTC(new);
2980 if (is_path_sep(new[-1]))
2981 break;
2982 }
2983 break;
2984 }
2985 if (p[1] == '\0')
2986 break;
2987 /* fall through */
2988 default:
2989 new = stack_putstr(p, new);
2990 USTPUTC('/', new);
2991 }
2992 p = strtok(0, "/\\");
2993 }
2994 if (new > lim)
2995 STUNPUTC(new);
2996 *new = 0;
2997 fix_path_case((char *)stackblock());
2998 return stackblock();
2999#else
2684 char *new; 3000 char *new;
2685 char *p; 3001 char *p;
2686 char *cdcomppath; 3002 char *cdcomppath;
@@ -2734,6 +3050,7 @@ updatepwd(const char *dir)
2734 STUNPUTC(new); 3050 STUNPUTC(new);
2735 *new = 0; 3051 *new = 0;
2736 return stackblock(); 3052 return stackblock();
3053#endif
2737} 3054}
2738 3055
2739/* 3056/*
@@ -2828,7 +3145,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2828 } 3145 }
2829 if (!dest) 3146 if (!dest)
2830 dest = nullstr; 3147 dest = nullstr;
2831 if (*dest == '/') 3148 if (is_absolute_path(dest))
2832 goto step6; 3149 goto step6;
2833 if (*dest == '.') { 3150 if (*dest == '.') {
2834 c = dest[1]; 3151 c = dest[1];
@@ -2850,7 +3167,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2850 c = *path; 3167 c = *path;
2851 p = path_advance(&path, dest); 3168 p = path_advance(&path, dest);
2852 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3169 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2853 if (c && c != ':') 3170 if (c && c != PATH_SEP)
2854 flags |= CD_PRINT; 3171 flags |= CD_PRINT;
2855 docd: 3172 docd:
2856 if (!docd(p, flags)) 3173 if (!docd(p, flags))
@@ -2872,6 +3189,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2872 return 0; 3189 return 0;
2873} 3190}
2874 3191
3192#if ENABLE_PLATFORM_MINGW32
3193static void
3194print_all_cwd(void)
3195{
3196 FILE *mnt;
3197 struct mntent *entry;
3198 char buffer[PATH_MAX];
3199
3200 mnt = setmntent(bb_path_mtab_file, "r");
3201 if (mnt) {
3202 while ((entry=getmntent(mnt)) != NULL) {
3203 entry->mnt_dir[2] = '\0';
3204 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3205 out1fmt("%s\n", buffer);
3206 }
3207 endmntent(mnt);
3208 }
3209}
3210#endif
3211
2875static int FAST_FUNC 3212static int FAST_FUNC
2876pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3213pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2877{ 3214{
@@ -2879,6 +3216,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2879 const char *dir = curdir; 3216 const char *dir = curdir;
2880 3217
2881 flags = cdopt(); 3218 flags = cdopt();
3219#if ENABLE_PLATFORM_MINGW32
3220 if (flags & CD_PRINT_ALL) {
3221 print_all_cwd();
3222 return 0;
3223 }
3224#endif
2882 if (flags) { 3225 if (flags) {
2883 if (physdir == nullstr) 3226 if (physdir == nullstr)
2884 setpwd(dir, 0); 3227 setpwd(dir, 0);
@@ -3530,6 +3873,9 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3530 */ 3873 */
3531struct procstat { 3874struct procstat {
3532 pid_t ps_pid; /* process id */ 3875 pid_t ps_pid; /* process id */
3876#if ENABLE_PLATFORM_MINGW32
3877 HANDLE ps_proc;
3878#endif
3533 int ps_status; /* last process status from wait() */ 3879 int ps_status; /* last process status from wait() */
3534 char *ps_cmd; /* text of command being run */ 3880 char *ps_cmd; /* text of command being run */
3535}; 3881};
@@ -3558,7 +3904,9 @@ struct job {
3558}; 3904};
3559 3905
3560static struct job *makejob(/*union node *,*/ int); 3906static struct job *makejob(/*union node *,*/ int);
3907#if !ENABLE_PLATFORM_MINGW32
3561static int forkshell(struct job *, union node *, int); 3908static int forkshell(struct job *, union node *, int);
3909#endif
3562static int waitforjob(struct job *); 3910static int waitforjob(struct job *);
3563 3911
3564#if !JOBS 3912#if !JOBS
@@ -3569,6 +3917,7 @@ static smallint doing_jobctl; //references:8
3569static void setjobctl(int); 3917static void setjobctl(int);
3570#endif 3918#endif
3571 3919
3920#if !ENABLE_PLATFORM_MINGW32
3572/* 3921/*
3573 * Ignore a signal. 3922 * Ignore a signal.
3574 */ 3923 */
@@ -3715,6 +4064,10 @@ setsignal(int signo)
3715 4064
3716 sigaction_set(signo, &act); 4065 sigaction_set(signo, &act);
3717} 4066}
4067#else
4068#define setsignal(s)
4069#define ignoresig(s)
4070#endif
3718 4071
3719/* mode flags for set_curjob */ 4072/* mode flags for set_curjob */
3720#define CUR_DELETE 2 4073#define CUR_DELETE 2
@@ -4213,6 +4566,122 @@ sprint_status48(char *s, int status, int sigonly)
4213 return col; 4566 return col;
4214} 4567}
4215 4568
4569#if ENABLE_PLATFORM_MINGW32
4570
4571HANDLE hSIGINT; /* Ctrl-C is pressed */
4572
4573static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4574{
4575 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4576 pending_int = 1;
4577 SetEvent(hSIGINT);
4578 return TRUE;
4579 }
4580 return FALSE;
4581}
4582
4583/*
4584 * Windows does not know about parent-child relationship
4585 * They don't support waitpid(-1)
4586 */
4587static pid_t
4588waitpid_child(int *status, int wait_flags)
4589{
4590 struct job *jb;
4591 struct procstat *ps;
4592 int pid_nr = 0;
4593 pid_t *pidlist;
4594 HANDLE *proclist;
4595 pid_t pid = -1;
4596 DWORD win_status, idx;
4597 int i, delay;
4598
4599 for (jb = curjob; jb; jb = jb->prev_job) {
4600 if (jb->state != JOBDONE)
4601 pid_nr += jb->nprocs;
4602 }
4603 if ( pid_nr++ == 0 )
4604 return -1;
4605
4606 pidlist = ckmalloc(sizeof(*pidlist)*pid_nr);
4607 proclist = ckmalloc(sizeof(*proclist)*pid_nr);
4608
4609 pidlist[0] = -1;
4610 proclist[0] = hSIGINT;
4611 pid_nr = 1;
4612 for (jb = curjob; jb; jb = jb->prev_job) {
4613 if (jb->state == JOBDONE)
4614 continue;
4615 ps = jb->ps;
4616 for (i = 0; i < jb->nprocs; ++i) {
4617 if (ps[i].ps_proc) {
4618 pidlist[pid_nr] = ps[i].ps_pid;
4619 proclist[pid_nr++] = ps[i].ps_proc;
4620 }
4621 }
4622 }
4623
4624 if (pid_nr == 1)
4625 goto done;
4626
4627 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4628 wait_flags&WNOHANG ? 1 : INFINITE);
4629 if (idx < pid_nr) {
4630 if (idx == 0) { /* hSIGINT */
4631 ResetEvent(hSIGINT);
4632
4633 ps = curjob->ps;
4634 for (i = 0; i < curjob->nprocs; ++i) {
4635 if (ps[i].ps_proc) {
4636 kill_SIGTERM_by_handle(ps[i].ps_proc, 128+SIGINT);
4637 pid = ps[i].ps_pid; /* remember last valid pid */
4638 }
4639 }
4640
4641 Sleep(200);
4642 delay = FALSE;
4643 for (i = 0; i < curjob->nprocs; ++i) {
4644 DWORD code;
4645 if (ps[i].ps_proc &&
4646 GetExitCodeProcess(ps[i].ps_proc, &code) &&
4647 code == STILL_ACTIVE) {
4648 TerminateProcess(ps[i].ps_proc, 128+SIGINT);
4649 delay = TRUE;
4650 }
4651 }
4652
4653 if (delay)
4654 Sleep(200);
4655 for (i = 0; i < curjob->nprocs; ++i) {
4656 /* mark all pids dead except the one we'll return */
4657 if (ps[i].ps_pid != pid) {
4658 ps[i].ps_status = 128 + SIGINT;
4659 ps[i].ps_pid = -1;
4660 CloseHandle(ps[i].ps_proc);
4661 ps[i].ps_proc = NULL;
4662 }
4663 }
4664
4665 *status = 128 + SIGINT; /* terminated by a signal */
4666 if (iflag)
4667 write(STDOUT_FILENO, "^C", 2);
4668 }
4669 else { /* valid pid index */
4670 GetExitCodeProcess(proclist[idx], &win_status);
4671 *status = (int)win_status << 8;
4672 pid = pidlist[idx];
4673 }
4674 }
4675 done:
4676 free(pidlist);
4677 free(proclist);
4678 return pid;
4679}
4680#define waitpid(p, s, f) waitpid_child(s, f)
4681#define wait_block_or_sig(s) waitpid_child(s, 0)
4682
4683#else
4684
4216static int 4685static int
4217wait_block_or_sig(int *status) 4686wait_block_or_sig(int *status)
4218{ 4687{
@@ -4245,6 +4714,7 @@ wait_block_or_sig(int *status)
4245 4714
4246 return pid; 4715 return pid;
4247} 4716}
4717#endif
4248 4718
4249#define DOWAIT_NONBLOCK 0 4719#define DOWAIT_NONBLOCK 0
4250#define DOWAIT_BLOCK 1 4720#define DOWAIT_BLOCK 1
@@ -4320,6 +4790,11 @@ dowait(int block, struct job *job)
4320 jobno(jp), pid, ps->ps_status, status)); 4790 jobno(jp), pid, ps->ps_status, status));
4321 ps->ps_status = status; 4791 ps->ps_status = status;
4322 thisjob = jp; 4792 thisjob = jp;
4793#if ENABLE_PLATFORM_MINGW32
4794 ps->ps_pid = -1;
4795 CloseHandle(ps->ps_proc);
4796 ps->ps_proc = NULL;
4797#endif
4323 } 4798 }
4324 if (ps->ps_status == -1) 4799 if (ps->ps_status == -1)
4325 jobstate = JOBRUNNING; 4800 jobstate = JOBRUNNING;
@@ -5036,6 +5511,7 @@ commandtext(union node *n)
5036 * 5511 *
5037 * Called with interrupts off. 5512 * Called with interrupts off.
5038 */ 5513 */
5514#if !ENABLE_PLATFORM_MINGW32
5039/* 5515/*
5040 * Clear traps on a fork. 5516 * Clear traps on a fork.
5041 */ 5517 */
@@ -5185,16 +5661,24 @@ forkchild(struct job *jp, union node *n, int mode)
5185 freejob(jp); 5661 freejob(jp);
5186 jobless = 0; 5662 jobless = 0;
5187} 5663}
5664#endif
5188 5665
5189/* Called after fork(), in parent */ 5666/* Called after fork(), in parent */
5190#if !JOBS 5667#if !JOBS
5191#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 5668#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5192#endif 5669#endif
5193static void 5670static void
5671#if !ENABLE_PLATFORM_MINGW32
5194forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5672forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5673#else
5674forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
5675#endif
5195{ 5676{
5677#if ENABLE_PLATFORM_MINGW32
5678 pid_t pid = GetProcessId(proc);
5679#endif
5196 TRACE(("In parent shell: child = %d\n", pid)); 5680 TRACE(("In parent shell: child = %d\n", pid));
5197 if (!jp) { 5681 if (!jp && !ENABLE_PLATFORM_MINGW32) { /* FIXME not quite understand this */
5198 /* jp is NULL when called by openhere() for heredoc support */ 5682 /* jp is NULL when called by openhere() for heredoc support */
5199 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0) 5683 while (jobless && dowait(DOWAIT_NONBLOCK, NULL) > 0)
5200 continue; 5684 continue;
@@ -5220,6 +5704,9 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5220 if (jp) { 5704 if (jp) {
5221 struct procstat *ps = &jp->ps[jp->nprocs++]; 5705 struct procstat *ps = &jp->ps[jp->nprocs++];
5222 ps->ps_pid = pid; 5706 ps->ps_pid = pid;
5707#if ENABLE_PLATFORM_MINGW32
5708 ps->ps_proc = proc;
5709#endif
5223 ps->ps_status = -1; 5710 ps->ps_status = -1;
5224 ps->ps_cmd = nullstr; 5711 ps->ps_cmd = nullstr;
5225#if JOBS 5712#if JOBS
@@ -5229,6 +5716,7 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5229 } 5716 }
5230} 5717}
5231 5718
5719#if !ENABLE_PLATFORM_MINGW32
5232/* jp and n are NULL when called by openhere() for heredoc support */ 5720/* jp and n are NULL when called by openhere() for heredoc support */
5233static int 5721static int
5234forkshell(struct job *jp, union node *n, int mode) 5722forkshell(struct job *jp, union node *n, int mode)
@@ -5251,6 +5739,7 @@ forkshell(struct job *jp, union node *n, int mode)
5251 } 5739 }
5252 return pid; 5740 return pid;
5253} 5741}
5742#endif
5254 5743
5255/* 5744/*
5256 * Wait for job to finish. 5745 * Wait for job to finish.
@@ -5382,6 +5871,7 @@ openhere(union node *redir)
5382{ 5871{
5383 int pip[2]; 5872 int pip[2];
5384 size_t len = 0; 5873 size_t len = 0;
5874 IF_PLATFORM_MINGW32(struct forkshell fs);
5385 5875
5386 if (pipe(pip) < 0) 5876 if (pipe(pip) < 0)
5387 ash_msg_and_raise_perror("can't create pipe"); 5877 ash_msg_and_raise_perror("can't create pipe");
@@ -5392,6 +5882,14 @@ openhere(union node *redir)
5392 goto out; 5882 goto out;
5393 } 5883 }
5394 } 5884 }
5885#if ENABLE_PLATFORM_MINGW32
5886 memset(&fs, 0, sizeof(fs));
5887 fs.fpid = FS_OPENHERE;
5888 fs.n = redir;
5889 fs.fd[0] = pip[0];
5890 fs.fd[1] = pip[1];
5891 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
5892#else
5395 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 5893 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5396 /* child */ 5894 /* child */
5397 close(pip[0]); 5895 close(pip[0]);
@@ -5406,6 +5904,7 @@ openhere(union node *redir)
5406 expandhere(redir->nhere.doc, pip[1]); 5904 expandhere(redir->nhere.doc, pip[1]);
5407 _exit(EXIT_SUCCESS); 5905 _exit(EXIT_SUCCESS);
5408 } 5906 }
5907#endif
5409 out: 5908 out:
5410 close(pip[1]); 5909 close(pip[1]);
5411 return pip[0]; 5910 return pip[0];
@@ -5483,6 +5982,9 @@ openredirect(union node *redir)
5483 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 5982 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5484 if (f < 0) 5983 if (f < 0)
5485 goto ecreate; 5984 goto ecreate;
5985#if ENABLE_PLATFORM_MINGW32
5986 lseek(f, 0, SEEK_END);
5987#endif
5486 break; 5988 break;
5487 } 5989 }
5488 5990
@@ -6462,6 +6964,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6462{ 6964{
6463 int pip[2]; 6965 int pip[2];
6464 struct job *jp; 6966 struct job *jp;
6967 IF_PLATFORM_MINGW32(struct forkshell fs);
6465 6968
6466 result->fd = -1; 6969 result->fd = -1;
6467 result->buf = NULL; 6970 result->buf = NULL;
@@ -6474,6 +6977,14 @@ evalbackcmd(union node *n, struct backcmd *result)
6474 if (pipe(pip) < 0) 6977 if (pipe(pip) < 0)
6475 ash_msg_and_raise_perror("can't create pipe"); 6978 ash_msg_and_raise_perror("can't create pipe");
6476 jp = makejob(/*n,*/ 1); 6979 jp = makejob(/*n,*/ 1);
6980#if ENABLE_PLATFORM_MINGW32
6981 memset(&fs, 0, sizeof(fs));
6982 fs.fpid = FS_EVALBACKCMD;
6983 fs.n = n;
6984 fs.fd[0] = pip[0];
6985 fs.fd[1] = pip[1];
6986 spawn_forkshell(&fs, jp, n, FORK_NOJOB);
6987#else
6477 if (forkshell(jp, n, FORK_NOJOB) == 0) { 6988 if (forkshell(jp, n, FORK_NOJOB) == 0) {
6478 /* child */ 6989 /* child */
6479 FORCE_INT_ON; 6990 FORCE_INT_ON;
@@ -6496,6 +7007,7 @@ evalbackcmd(union node *n, struct backcmd *result)
6496 evaltreenr(n, EV_EXIT); 7007 evaltreenr(n, EV_EXIT);
6497 /* NOTREACHED */ 7008 /* NOTREACHED */
6498 } 7009 }
7010#endif
6499 /* parent */ 7011 /* parent */
6500 close(pip[1]); 7012 close(pip[1]);
6501 result->fd = pip[0]; 7013 result->fd = pip[0];
@@ -6552,7 +7064,8 @@ expbackq(union node *cmd, int flag)
6552 7064
6553 /* Eat all trailing newlines */ 7065 /* Eat all trailing newlines */
6554 dest = expdest; 7066 dest = expdest;
6555 for (; dest > (char *)stackblock() && dest[-1] == '\n';) 7067 for (; dest > (char *)stackblock() && (dest[-1] == '\n' ||
7068 (ENABLE_PLATFORM_MINGW32 && dest[-1] == '\r'));)
6556 STUNPUTC(dest); 7069 STUNPUTC(dest);
6557 expdest = dest; 7070 expdest = dest;
6558 7071
@@ -7752,6 +8265,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7752 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8265 while (!pending_int && (dp = readdir(dirp)) != NULL) {
7753 if (dp->d_name[0] == '.' && !matchdot) 8266 if (dp->d_name[0] == '.' && !matchdot)
7754 continue; 8267 continue;
8268#if ENABLE_ASH_NOCASEGLOB
8269# undef pmatch
8270# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8271#endif
7755 if (pmatch(start, dp->d_name)) { 8272 if (pmatch(start, dp->d_name)) {
7756 if (atend) { 8273 if (atend) {
7757 strcpy(enddir, dp->d_name); 8274 strcpy(enddir, dp->d_name);
@@ -7781,6 +8298,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7781 endname[-esc - 1] = esc ? '\\' : '/'; 8298 endname[-esc - 1] = esc ? '\\' : '/';
7782#undef expdir 8299#undef expdir
7783#undef expdir_max 8300#undef expdir_max
8301#if ENABLE_ASH_NOCASEGLOB
8302# undef pmatch
8303# define pmatch(a, b) !fnmatch((a), (b), 0)
8304#endif
7784} 8305}
7785 8306
7786static struct strlist * 8307static struct strlist *
@@ -8075,6 +8596,11 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8075 } 8596 }
8076#endif 8597#endif
8077 8598
8599#if ENABLE_PLATFORM_MINGW32
8600 cmd = auto_win32_extension(cmd) ?: cmd;
8601 execve(cmd, argv, envp);
8602 /* skip POSIX-mandated retry on ENOEXEC */
8603#else
8078 repeat: 8604 repeat:
8079#ifdef SYSV 8605#ifdef SYSV
8080 do { 8606 do {
@@ -8110,6 +8636,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8110 argv[0] = (char*) "ash"; 8636 argv[0] = (char*) "ash";
8111 goto repeat; 8637 goto repeat;
8112 } 8638 }
8639#endif /* ENABLE_PLATFORM_MINGW32 */
8113} 8640}
8114 8641
8115/* 8642/*
@@ -8127,7 +8654,7 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8127 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 8654 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8128 8655
8129 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 8656 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
8130 if (strchr(prog, '/') != NULL 8657 if ((strchr(prog, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(prog, '\\')))
8131#if ENABLE_FEATURE_SH_STANDALONE 8658#if ENABLE_FEATURE_SH_STANDALONE
8132 || (applet_no = find_applet_by_name(prog)) >= 0 8659 || (applet_no = find_applet_by_name(prog)) >= 0
8133#endif 8660#endif
@@ -8186,6 +8713,9 @@ printentry(struct tblentry *cmdp)
8186 name = path_advance(&path, cmdp->cmdname); 8713 name = path_advance(&path, cmdp->cmdname);
8187 stunalloc(name); 8714 stunalloc(name);
8188 } while (--idx >= 0); 8715 } while (--idx >= 0);
8716#if ENABLE_PLATFORM_MINGW32
8717 add_win32_extension(name);
8718#endif
8189 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 8719 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8190} 8720}
8191 8721
@@ -8382,8 +8912,8 @@ changepath(const char *new)
8382 for (;;) { 8912 for (;;) {
8383 if (*old != *new) { 8913 if (*old != *new) {
8384 firstchange = idx; 8914 firstchange = idx;
8385 if ((*old == '\0' && *new == ':') 8915 if ((*old == '\0' && *new == PATH_SEP)
8386 || (*old == ':' && *new == '\0') 8916 || (*old == PATH_SEP && *new == '\0')
8387 ) { 8917 ) {
8388 firstchange++; 8918 firstchange++;
8389 } 8919 }
@@ -8393,7 +8923,7 @@ changepath(const char *new)
8393 break; 8923 break;
8394 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin")) 8924 if (*new == '%' && idx_bltin < 0 && prefix(new + 1, "builtin"))
8395 idx_bltin = idx; 8925 idx_bltin = idx;
8396 if (*new == ':') 8926 if (*new == PATH_SEP)
8397 idx++; 8927 idx++;
8398 new++; 8928 new++;
8399 old++; 8929 old++;
@@ -8582,6 +9112,9 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8582 stunalloc(p); 9112 stunalloc(p);
8583 } while (--j >= 0); 9113 } while (--j >= 0);
8584 } 9114 }
9115#if ENABLE_PLATFORM_MINGW32
9116 add_win32_extension(p);
9117#endif
8585 if (describe_command_verbose) { 9118 if (describe_command_verbose) {
8586 out1fmt(" is %s", p); 9119 out1fmt(" is %s", p);
8587 } else { 9120 } else {
@@ -8716,6 +9249,14 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8716/*static int funcstringsize; // size of strings in node */ 9249/*static int funcstringsize; // size of strings in node */
8717static void *funcblock; /* block to allocate function from */ 9250static void *funcblock; /* block to allocate function from */
8718static char *funcstring_end; /* end of block to allocate strings from */ 9251static char *funcstring_end; /* end of block to allocate strings from */
9252#if ENABLE_PLATFORM_MINGW32
9253static int nodeptrcount;
9254static char ***nodeptr;
9255# if FORKSHELL_DEBUG
9256static int annot_count;
9257static char **annot;
9258# endif
9259#endif
8719 9260
8720static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9261static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8721 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9262 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -8756,6 +9297,7 @@ sizenodelist(int funcblocksize, struct nodelist *lp)
8756{ 9297{
8757 while (lp) { 9298 while (lp) {
8758 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist)); 9299 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
9300 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8759 funcblocksize = calcsize(funcblocksize, lp->n); 9301 funcblocksize = calcsize(funcblocksize, lp->n);
8760 lp = lp->next; 9302 lp = lp->next;
8761 } 9303 }
@@ -8773,15 +9315,18 @@ calcsize(int funcblocksize, union node *n)
8773 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect); 9315 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8774 funcblocksize = calcsize(funcblocksize, n->ncmd.args); 9316 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8775 funcblocksize = calcsize(funcblocksize, n->ncmd.assign); 9317 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
9318 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8776 break; 9319 break;
8777 case NPIPE: 9320 case NPIPE:
8778 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist); 9321 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
9322 IF_PLATFORM_MINGW32(nodeptrcount++);
8779 break; 9323 break;
8780 case NREDIR: 9324 case NREDIR:
8781 case NBACKGND: 9325 case NBACKGND:
8782 case NSUBSHELL: 9326 case NSUBSHELL:
8783 funcblocksize = calcsize(funcblocksize, n->nredir.redirect); 9327 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8784 funcblocksize = calcsize(funcblocksize, n->nredir.n); 9328 funcblocksize = calcsize(funcblocksize, n->nredir.n);
9329 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8785 break; 9330 break;
8786 case NAND: 9331 case NAND:
8787 case NOR: 9332 case NOR:
@@ -8790,34 +9335,41 @@ calcsize(int funcblocksize, union node *n)
8790 case NUNTIL: 9335 case NUNTIL:
8791 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2); 9336 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8792 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1); 9337 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
9338 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8793 break; 9339 break;
8794 case NIF: 9340 case NIF:
8795 funcblocksize = calcsize(funcblocksize, n->nif.elsepart); 9341 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
8796 funcblocksize = calcsize(funcblocksize, n->nif.ifpart); 9342 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
8797 funcblocksize = calcsize(funcblocksize, n->nif.test); 9343 funcblocksize = calcsize(funcblocksize, n->nif.test);
9344 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8798 break; 9345 break;
8799 case NFOR: 9346 case NFOR:
8800 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */ 9347 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
8801 funcblocksize = calcsize(funcblocksize, n->nfor.body); 9348 funcblocksize = calcsize(funcblocksize, n->nfor.body);
8802 funcblocksize = calcsize(funcblocksize, n->nfor.args); 9349 funcblocksize = calcsize(funcblocksize, n->nfor.args);
9350 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8803 break; 9351 break;
8804 case NCASE: 9352 case NCASE:
8805 funcblocksize = calcsize(funcblocksize, n->ncase.cases); 9353 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
8806 funcblocksize = calcsize(funcblocksize, n->ncase.expr); 9354 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
9355 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8807 break; 9356 break;
8808 case NCLIST: 9357 case NCLIST:
8809 funcblocksize = calcsize(funcblocksize, n->nclist.body); 9358 funcblocksize = calcsize(funcblocksize, n->nclist.body);
8810 funcblocksize = calcsize(funcblocksize, n->nclist.pattern); 9359 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
8811 funcblocksize = calcsize(funcblocksize, n->nclist.next); 9360 funcblocksize = calcsize(funcblocksize, n->nclist.next);
9361 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8812 break; 9362 break;
8813 case NDEFUN: 9363 case NDEFUN:
8814 funcblocksize = calcsize(funcblocksize, n->ndefun.body); 9364 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
8815 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1); 9365 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
9366 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8816 break; 9367 break;
8817 case NARG: 9368 case NARG:
8818 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote); 9369 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
8819 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */ 9370 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
8820 funcblocksize = calcsize(funcblocksize, n->narg.next); 9371 funcblocksize = calcsize(funcblocksize, n->narg.next);
9372 IF_PLATFORM_MINGW32(nodeptrcount += 3);
8821 break; 9373 break;
8822 case NTO: 9374 case NTO:
8823#if BASH_REDIR_OUTPUT 9375#if BASH_REDIR_OUTPUT
@@ -8829,33 +9381,65 @@ calcsize(int funcblocksize, union node *n)
8829 case NAPPEND: 9381 case NAPPEND:
8830 funcblocksize = calcsize(funcblocksize, n->nfile.fname); 9382 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
8831 funcblocksize = calcsize(funcblocksize, n->nfile.next); 9383 funcblocksize = calcsize(funcblocksize, n->nfile.next);
9384 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8832 break; 9385 break;
8833 case NTOFD: 9386 case NTOFD:
8834 case NFROMFD: 9387 case NFROMFD:
8835 funcblocksize = calcsize(funcblocksize, n->ndup.vname); 9388 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
8836 funcblocksize = calcsize(funcblocksize, n->ndup.next); 9389 funcblocksize = calcsize(funcblocksize, n->ndup.next);
9390 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8837 break; 9391 break;
8838 case NHERE: 9392 case NHERE:
8839 case NXHERE: 9393 case NXHERE:
8840 funcblocksize = calcsize(funcblocksize, n->nhere.doc); 9394 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
8841 funcblocksize = calcsize(funcblocksize, n->nhere.next); 9395 funcblocksize = calcsize(funcblocksize, n->nhere.next);
9396 IF_PLATFORM_MINGW32(nodeptrcount += 2);
8842 break; 9397 break;
8843 case NNOT: 9398 case NNOT:
8844 funcblocksize = calcsize(funcblocksize, n->nnot.com); 9399 funcblocksize = calcsize(funcblocksize, n->nnot.com);
9400 IF_PLATFORM_MINGW32(nodeptrcount++);
8845 break; 9401 break;
8846 }; 9402 };
8847 return funcblocksize; 9403 return funcblocksize;
8848} 9404}
8849 9405
8850static char * 9406static char *
8851nodeckstrdup(char *s) 9407nodeckstrdup(const char *s)
8852{ 9408{
9409#if ENABLE_PLATFORM_MINGW32
9410 if(!s)
9411 return NULL;
9412#endif
8853 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 9413 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
8854 return strcpy(funcstring_end, s); 9414 return strcpy(funcstring_end, s);
8855} 9415}
8856 9416
8857static union node *copynode(union node *); 9417static union node *copynode(union node *);
8858 9418
9419#if ENABLE_PLATFORM_MINGW32
9420# define SAVE_PTR(dst) {if (nodeptr) *nodeptr++ = (char **)&(dst);}
9421# define SAVE_PTR2(dst1,dst2) {if (nodeptr) { *nodeptr++ = (char **)&(dst1);*nodeptr++ = (char **)&(dst2);}}
9422# define SAVE_PTR3(dst1,dst2,dst3) {if (nodeptr) { *nodeptr++ = (char **)&(dst1);*nodeptr++ = (char **)&(dst2);*nodeptr++ = (char **)&(dst3);}}
9423#else
9424# define SAVE_PTR(dst)
9425# define SAVE_PTR2(dst,dst2)
9426# define SAVE_PTR3(dst,dst2,dst3)
9427#endif
9428
9429#if ENABLE_PLATFORM_MINGW32 && FORKSHELL_DEBUG
9430# define ANNOT_NO_DUP(note) {if (annot) annot[annot_count++] = note;}
9431# define ANNOT(note) {ANNOT_NO_DUP(xstrdup(note));}
9432# define ANNOT2(n1,n2) {ANNOT(n1); ANNOT(n2)}
9433# define ANNOT3(n1,n2,n3) {ANNOT2(n1,n2); ANNOT(n3);}
9434# define ANNOT4(n1,n2,n3,n4) {ANNOT3(n1,n2,n3); ANNOT(n4);}
9435#else
9436# define ANNOT_NO_DUP(note)
9437# define ANNOT(note)
9438# define ANNOT2(n1,n2)
9439# define ANNOT3(n1,n2,n3)
9440# define ANNOT4(n1,n2,n3,n4)
9441#endif
9442
8859static struct nodelist * 9443static struct nodelist *
8860copynodelist(struct nodelist *lp) 9444copynodelist(struct nodelist *lp)
8861{ 9445{
@@ -8867,6 +9451,8 @@ copynodelist(struct nodelist *lp)
8867 *lpp = funcblock; 9451 *lpp = funcblock;
8868 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 9452 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8869 (*lpp)->n = copynode(lp->n); 9453 (*lpp)->n = copynode(lp->n);
9454 SAVE_PTR2((*lpp)->n, (*lpp)->next);
9455 ANNOT2("(*lpp)->n","(*lpp)->next");
8870 lp = lp->next; 9456 lp = lp->next;
8871 lpp = &(*lpp)->next; 9457 lpp = &(*lpp)->next;
8872 } 9458 }
@@ -8890,10 +9476,14 @@ copynode(union node *n)
8890 new->ncmd.args = copynode(n->ncmd.args); 9476 new->ncmd.args = copynode(n->ncmd.args);
8891 new->ncmd.assign = copynode(n->ncmd.assign); 9477 new->ncmd.assign = copynode(n->ncmd.assign);
8892 new->ncmd.linno = n->ncmd.linno; 9478 new->ncmd.linno = n->ncmd.linno;
9479 SAVE_PTR3(new->ncmd.redirect,new->ncmd.args, new->ncmd.assign);
9480 ANNOT3("ncmd.redirect","ncmd.args","ncmd.assign");
8893 break; 9481 break;
8894 case NPIPE: 9482 case NPIPE:
8895 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 9483 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
8896 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 9484 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
9485 SAVE_PTR(new->npipe.cmdlist);
9486 ANNOT("npipe.cmdlist");
8897 break; 9487 break;
8898 case NREDIR: 9488 case NREDIR:
8899 case NBACKGND: 9489 case NBACKGND:
@@ -8901,6 +9491,8 @@ copynode(union node *n)
8901 new->nredir.redirect = copynode(n->nredir.redirect); 9491 new->nredir.redirect = copynode(n->nredir.redirect);
8902 new->nredir.n = copynode(n->nredir.n); 9492 new->nredir.n = copynode(n->nredir.n);
8903 new->nredir.linno = n->nredir.linno; 9493 new->nredir.linno = n->nredir.linno;
9494 SAVE_PTR2(new->nredir.redirect,new->nredir.n);
9495 ANNOT2("nredir.redirect","nredir.n");
8904 break; 9496 break;
8905 case NAND: 9497 case NAND:
8906 case NOR: 9498 case NOR:
@@ -8909,37 +9501,54 @@ copynode(union node *n)
8909 case NUNTIL: 9501 case NUNTIL:
8910 new->nbinary.ch2 = copynode(n->nbinary.ch2); 9502 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8911 new->nbinary.ch1 = copynode(n->nbinary.ch1); 9503 new->nbinary.ch1 = copynode(n->nbinary.ch1);
9504 SAVE_PTR2(new->nbinary.ch1,new->nbinary.ch2);
9505 ANNOT2("nbinary.ch1","nbinary.ch2");
8912 break; 9506 break;
8913 case NIF: 9507 case NIF:
8914 new->nif.elsepart = copynode(n->nif.elsepart); 9508 new->nif.elsepart = copynode(n->nif.elsepart);
8915 new->nif.ifpart = copynode(n->nif.ifpart); 9509 new->nif.ifpart = copynode(n->nif.ifpart);
8916 new->nif.test = copynode(n->nif.test); 9510 new->nif.test = copynode(n->nif.test);
9511 SAVE_PTR3(new->nif.elsepart,new->nif.ifpart,new->nif.test);
9512 ANNOT3("nif.elsepart","nif.ifpart","nif.test");
8917 break; 9513 break;
8918 case NFOR: 9514 case NFOR:
8919 new->nfor.var = nodeckstrdup(n->nfor.var); 9515 new->nfor.var = nodeckstrdup(n->nfor.var);
8920 new->nfor.body = copynode(n->nfor.body); 9516 new->nfor.body = copynode(n->nfor.body);
8921 new->nfor.args = copynode(n->nfor.args); 9517 new->nfor.args = copynode(n->nfor.args);
8922 new->nfor.linno = n->nfor.linno; 9518 new->nfor.linno = n->nfor.linno;
9519 SAVE_PTR3(new->nfor.var,new->nfor.body,new->nfor.args);
9520 ANNOT_NO_DUP(xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"));
9521 ANNOT2("nfor.body","nfor.args");
8923 break; 9522 break;
8924 case NCASE: 9523 case NCASE:
8925 new->ncase.cases = copynode(n->ncase.cases); 9524 new->ncase.cases = copynode(n->ncase.cases);
8926 new->ncase.expr = copynode(n->ncase.expr); 9525 new->ncase.expr = copynode(n->ncase.expr);
8927 new->ncase.linno = n->ncase.linno; 9526 new->ncase.linno = n->ncase.linno;
9527 SAVE_PTR2(new->ncase.cases,new->ncase.expr);
9528 ANNOT2("ncase.cases","ncase.expr");
8928 break; 9529 break;
8929 case NCLIST: 9530 case NCLIST:
8930 new->nclist.body = copynode(n->nclist.body); 9531 new->nclist.body = copynode(n->nclist.body);
8931 new->nclist.pattern = copynode(n->nclist.pattern); 9532 new->nclist.pattern = copynode(n->nclist.pattern);
8932 new->nclist.next = copynode(n->nclist.next); 9533 new->nclist.next = copynode(n->nclist.next);
9534 SAVE_PTR3(new->nclist.body,new->nclist.pattern,new->nclist.next);
9535 ANNOT3("nclist.body","nclist.pattern","nclist.next");
8933 break; 9536 break;
8934 case NDEFUN: 9537 case NDEFUN:
8935 new->ndefun.body = copynode(n->ndefun.body); 9538 new->ndefun.body = copynode(n->ndefun.body);
8936 new->ndefun.text = nodeckstrdup(n->ndefun.text); 9539 new->ndefun.text = nodeckstrdup(n->ndefun.text);
8937 new->ndefun.linno = n->ndefun.linno; 9540 new->ndefun.linno = n->ndefun.linno;
9541 SAVE_PTR2(new->ndefun.body,new->ndefun.text);
9542 ANNOT("ndefun.body");
9543 ANNOT_NO_DUP(xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"));
8938 break; 9544 break;
8939 case NARG: 9545 case NARG:
8940 new->narg.backquote = copynodelist(n->narg.backquote); 9546 new->narg.backquote = copynodelist(n->narg.backquote);
8941 new->narg.text = nodeckstrdup(n->narg.text); 9547 new->narg.text = nodeckstrdup(n->narg.text);
8942 new->narg.next = copynode(n->narg.next); 9548 new->narg.next = copynode(n->narg.next);
9549 SAVE_PTR3(new->narg.backquote,new->narg.text,new->narg.next);
9550 ANNOT2("narg.backquote","narg.next");
9551 ANNOT_NO_DUP(xasprintf("narg.text '%s'", n->narg.text ?: "NULL"));
8943 break; 9552 break;
8944 case NTO: 9553 case NTO:
8945#if BASH_REDIR_OUTPUT 9554#if BASH_REDIR_OUTPUT
@@ -8952,6 +9561,8 @@ copynode(union node *n)
8952 new->nfile.fname = copynode(n->nfile.fname); 9561 new->nfile.fname = copynode(n->nfile.fname);
8953 new->nfile.fd = n->nfile.fd; 9562 new->nfile.fd = n->nfile.fd;
8954 new->nfile.next = copynode(n->nfile.next); 9563 new->nfile.next = copynode(n->nfile.next);
9564 SAVE_PTR2(new->nfile.fname,new->nfile.next);
9565 ANNOT2("nfile.fname","nfile.next");
8955 break; 9566 break;
8956 case NTOFD: 9567 case NTOFD:
8957 case NFROMFD: 9568 case NFROMFD:
@@ -8959,15 +9570,21 @@ copynode(union node *n)
8959 new->ndup.dupfd = n->ndup.dupfd; 9570 new->ndup.dupfd = n->ndup.dupfd;
8960 new->ndup.fd = n->ndup.fd; 9571 new->ndup.fd = n->ndup.fd;
8961 new->ndup.next = copynode(n->ndup.next); 9572 new->ndup.next = copynode(n->ndup.next);
9573 SAVE_PTR2(new->ndup.vname,new->ndup.next);
9574 ANNOT2("ndup.vname","ndup.next");
8962 break; 9575 break;
8963 case NHERE: 9576 case NHERE:
8964 case NXHERE: 9577 case NXHERE:
8965 new->nhere.doc = copynode(n->nhere.doc); 9578 new->nhere.doc = copynode(n->nhere.doc);
8966 new->nhere.fd = n->nhere.fd; 9579 new->nhere.fd = n->nhere.fd;
8967 new->nhere.next = copynode(n->nhere.next); 9580 new->nhere.next = copynode(n->nhere.next);
9581 SAVE_PTR2(new->nhere.doc,new->nhere.next);
9582 ANNOT2("nhere.doc","nhere.next");
8968 break; 9583 break;
8969 case NNOT: 9584 case NNOT:
8970 new->nnot.com = copynode(n->nnot.com); 9585 new->nnot.com = copynode(n->nnot.com);
9586 SAVE_PTR(new->nnot.com);
9587 ANNOT("nnot.com");
8971 break; 9588 break;
8972 }; 9589 };
8973 new->type = n->type; 9590 new->type = n->type;
@@ -8988,6 +9605,7 @@ copyfunc(union node *n)
8988 f = ckzalloc(blocksize /* + funcstringsize */); 9605 f = ckzalloc(blocksize /* + funcstringsize */);
8989 funcblock = (char *) f + offsetof(struct funcnode, n); 9606 funcblock = (char *) f + offsetof(struct funcnode, n);
8990 funcstring_end = (char *) f + blocksize; 9607 funcstring_end = (char *) f + blocksize;
9608 IF_PLATFORM_MINGW32(nodeptr = NULL);
8991 copynode(n); 9609 copynode(n);
8992 /* f->count = 0; - ckzalloc did it */ 9610 /* f->count = 0; - ckzalloc did it */
8993 return f; 9611 return f;
@@ -9014,7 +9632,9 @@ defun(union node *func)
9014#define SKIPFUNC (1 << 2) 9632#define SKIPFUNC (1 << 2)
9015static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 9633static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9016static int skipcount; /* number of levels to skip */ 9634static int skipcount; /* number of levels to skip */
9635#if ENABLE_PLATFORM_POSIX
9017static int loopnest; /* current loop nesting level */ 9636static int loopnest; /* current loop nesting level */
9637#endif
9018static int funcline; /* starting line number of current function, or 0 if not in a function */ 9638static int funcline; /* starting line number of current function, or 0 if not in a function */
9019 9639
9020/* Forward decl way out to parsing code - dotrap needs it */ 9640/* Forward decl way out to parsing code - dotrap needs it */
@@ -9333,6 +9953,7 @@ evalcase(union node *n, int flags)
9333static int 9953static int
9334evalsubshell(union node *n, int flags) 9954evalsubshell(union node *n, int flags)
9335{ 9955{
9956 IF_PLATFORM_MINGW32(struct forkshell fs;)
9336 struct job *jp; 9957 struct job *jp;
9337 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 9958 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9338 int status; 9959 int status;
@@ -9348,12 +9969,21 @@ evalsubshell(union node *n, int flags)
9348 if (backgnd == FORK_FG) 9969 if (backgnd == FORK_FG)
9349 get_tty_state(); 9970 get_tty_state();
9350 jp = makejob(/*n,*/ 1); 9971 jp = makejob(/*n,*/ 1);
9972#if ENABLE_PLATFORM_MINGW32
9973 memset(&fs, 0, sizeof(fs));
9974 fs.fpid = FS_EVALSUBSHELL;
9975 fs.n = n;
9976 fs.flags = flags;
9977 spawn_forkshell(&fs, jp, n, backgnd);
9978 if ( 0 ) {
9979#else
9351 if (forkshell(jp, n, backgnd) == 0) { 9980 if (forkshell(jp, n, backgnd) == 0) {
9352 /* child */ 9981 /* child */
9353 INT_ON; 9982 INT_ON;
9354 flags |= EV_EXIT; 9983 flags |= EV_EXIT;
9355 if (backgnd) 9984 if (backgnd)
9356 flags &= ~EV_TESTED; 9985 flags &= ~EV_TESTED;
9986#endif
9357 nofork: 9987 nofork:
9358 redirect(n->nredir.redirect, 0); 9988 redirect(n->nredir.redirect, 0);
9359 evaltreenr(n->nredir.n, flags); 9989 evaltreenr(n->nredir.n, flags);
@@ -9440,6 +10070,7 @@ expredir(union node *n)
9440static int 10070static int
9441evalpipe(union node *n, int flags) 10071evalpipe(union node *n, int flags)
9442{ 10072{
10073 IF_PLATFORM_MINGW32(struct forkshell fs;)
9443 struct job *jp; 10074 struct job *jp;
9444 struct nodelist *lp; 10075 struct nodelist *lp;
9445 int pipelen; 10076 int pipelen;
@@ -9466,6 +10097,16 @@ evalpipe(union node *n, int flags)
9466 ash_msg_and_raise_perror("can't create pipe"); 10097 ash_msg_and_raise_perror("can't create pipe");
9467 } 10098 }
9468 } 10099 }
10100#if ENABLE_PLATFORM_MINGW32
10101 memset(&fs, 0, sizeof(fs));
10102 fs.fpid = FS_EVALPIPE;
10103 fs.flags = flags;
10104 fs.n = lp->n;
10105 fs.fd[0] = pip[0];
10106 fs.fd[1] = pip[1];
10107 fs.fd[2] = prevfd;
10108 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
10109#else
9469 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 10110 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9470 /* child */ 10111 /* child */
9471 INT_ON; 10112 INT_ON;
@@ -9483,6 +10124,7 @@ evalpipe(union node *n, int flags)
9483 evaltreenr(lp->n, flags); 10124 evaltreenr(lp->n, flags);
9484 /* never returns */ 10125 /* never returns */
9485 } 10126 }
10127#endif
9486 /* parent */ 10128 /* parent */
9487 if (prevfd >= 0) 10129 if (prevfd >= 0)
9488 close(prevfd); 10130 close(prevfd);
@@ -10013,6 +10655,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10013 * as POSIX mandates */ 10655 * as POSIX mandates */
10014 return back_exitstatus; 10656 return back_exitstatus;
10015} 10657}
10658
10016static int 10659static int
10017evalcommand(union node *cmd, int flags) 10660evalcommand(union node *cmd, int flags)
10018{ 10661{
@@ -10036,6 +10679,9 @@ evalcommand(union node *cmd, int flags)
10036 int status; 10679 int status;
10037 char **nargv; 10680 char **nargv;
10038 smallint cmd_is_exec; 10681 smallint cmd_is_exec;
10682#if ENABLE_PLATFORM_MINGW32
10683 int cmdpath;
10684#endif
10039 10685
10040 errlinno = lineno = cmd->ncmd.linno; 10686 errlinno = lineno = cmd->ncmd.linno;
10041 if (funcline) 10687 if (funcline)
@@ -10103,6 +10749,7 @@ evalcommand(union node *cmd, int flags)
10103 } 10749 }
10104 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2); 10750 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
10105 10751
10752#if !ENABLE_PLATFORM_MINGW32
10106 path = vpath.var_text; 10753 path = vpath.var_text;
10107 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) { 10754 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10108 struct strlist **spp; 10755 struct strlist **spp;
@@ -10121,6 +10768,15 @@ evalcommand(union node *cmd, int flags)
10121 if (varcmp(p, path) == 0) 10768 if (varcmp(p, path) == 0)
10122 path = p; 10769 path = p;
10123 } 10770 }
10771#else
10772 /* Set path after any local PATH= has been processed. */
10773 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10774 struct strlist **spp = varlist.lastp;
10775 expandarg(argp, &varlist, EXP_VARTILDE);
10776 mklocal((*spp)->text);
10777 }
10778 path = vpath.var_text;
10779#endif
10124 10780
10125 /* Print the command if xflag is set. */ 10781 /* Print the command if xflag is set. */
10126 if (xflag) { 10782 if (xflag) {
@@ -10259,6 +10915,33 @@ evalcommand(union node *cmd, int flags)
10259 * in a script or a subshell does not need forking, 10915 * in a script or a subshell does not need forking,
10260 * we can just exec it. 10916 * we can just exec it.
10261 */ 10917 */
10918#if ENABLE_PLATFORM_MINGW32
10919 if (!(flags & EV_EXIT) || may_have_traps) {
10920 /* No, forking off a child is necessary */
10921 struct forkshell fs;
10922
10923 INT_OFF;
10924 memset(&fs, 0, sizeof(fs));
10925 fs.fpid = FS_SHELLEXEC;
10926 fs.argv = argv;
10927 fs.path = (char*)path;
10928 fs.fd[0] = cmdentry.u.index;
10929 fs.varlist = varlist.list;
10930 jp = makejob(/*cmd,*/ 1);
10931 spawn_forkshell(&fs, jp, cmd, FORK_FG);
10932 status = waitforjob(jp);
10933 INT_ON;
10934 TRACE(("forked child exited with %d\n", status));
10935 break;
10936 }
10937 /* If we're running 'command -p' we need to use the value stored
10938 * in path by parse_command_args(). If PATH is a local variable
10939 * listsetvar() will free the value currently in path so we need
10940 * to fetch the updated version. */
10941 cmdpath = (path != pathval());
10942 listsetvar(varlist.list, VEXPORT|VSTACK);
10943 shellexec(argv[0], argv, cmdpath ? path : pathval(), cmdentry.u.index);
10944#else
10262 if (!(flags & EV_EXIT) || may_have_traps) { 10945 if (!(flags & EV_EXIT) || may_have_traps) {
10263 /* No, forking off a child is necessary */ 10946 /* No, forking off a child is necessary */
10264 INT_OFF; 10947 INT_OFF;
@@ -10277,6 +10960,7 @@ evalcommand(union node *cmd, int flags)
10277 } 10960 }
10278 listsetvar(varlist.list, VEXPORT|VSTACK); 10961 listsetvar(varlist.list, VEXPORT|VSTACK);
10279 shellexec(argv[0], argv, path, cmdentry.u.index); 10962 shellexec(argv[0], argv, path, cmdentry.u.index);
10963#endif
10280 /* NOTREACHED */ 10964 /* NOTREACHED */
10281 } /* default */ 10965 } /* default */
10282 case CMDBUILTIN: 10966 case CMDBUILTIN:
@@ -10658,7 +11342,7 @@ preadbuffer(void)
10658 more--; 11342 more--;
10659 11343
10660 c = *q; 11344 c = *q;
10661 if (c == '\0') { 11345 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) {
10662 memmove(q, q + 1, more); 11346 memmove(q, q + 1, more);
10663 } else { 11347 } else {
10664 q++; 11348 q++;
@@ -10854,6 +11538,7 @@ popallfiles(void)
10854 unwindfiles(&basepf); 11538 unwindfiles(&basepf);
10855} 11539}
10856 11540
11541#if !ENABLE_PLATFORM_MINGW32
10857/* 11542/*
10858 * Close the file(s) that the shell is reading commands from. Called 11543 * Close the file(s) that the shell is reading commands from. Called
10859 * after a fork is done. 11544 * after a fork is done.
@@ -10867,6 +11552,7 @@ closescript(void)
10867 g_parsefile->pf_fd = 0; 11552 g_parsefile->pf_fd = 0;
10868 } 11553 }
10869} 11554}
11555#endif
10870 11556
10871/* 11557/*
10872 * Like setinputfile, but takes an open file descriptor. Call this with 11558 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11093,8 +11779,13 @@ options(int cmdline, int *login_sh)
11093 int val; 11779 int val;
11094 int c; 11780 int c;
11095 11781
11096 if (cmdline) 11782 if (cmdline) {
11097 minusc = NULL; 11783 minusc = NULL;
11784#if ENABLE_PLATFORM_MINGW32
11785 dirarg = NULL;
11786 title = NULL;
11787#endif
11788 }
11098 while ((p = *argptr) != NULL) { 11789 while ((p = *argptr) != NULL) {
11099 c = *p++; 11790 c = *p++;
11100 if (c != '-' && c != '+') 11791 if (c != '-' && c != '+')
@@ -11120,6 +11811,20 @@ options(int cmdline, int *login_sh)
11120 /* bash 3.2 indeed handles -c CMD and +c CMD the same */ 11811 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
11121 if (c == 'c' && cmdline) { 11812 if (c == 'c' && cmdline) {
11122 minusc = p; /* command is after shell args */ 11813 minusc = p; /* command is after shell args */
11814#if ENABLE_PLATFORM_MINGW32
11815 /* Undocumented flags;
11816 * -d force current directory
11817 * -t title to display in console window
11818 * Must appear before -s or -c. */
11819 } else if (c == 'd' && cmdline && val == 1) {
11820 if (*argptr == NULL)
11821 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
11822 dirarg = *argptr++;
11823 } else if (c == 't' && cmdline && val == 1) {
11824 if (*argptr == NULL)
11825 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
11826 title = *argptr++;
11827#endif
11123 } else if (c == 'o') { 11828 } else if (c == 'o') {
11124 if (plus_minus_o(*argptr, val)) { 11829 if (plus_minus_o(*argptr, val)) {
11125 /* it already printed err message */ 11830 /* it already printed err message */
@@ -13110,6 +13815,9 @@ evalstring(char *s, int flags)
13110 int status; 13815 int status;
13111 13816
13112 s = sstrdup(s); 13817 s = sstrdup(s);
13818#if ENABLE_PLATFORM_MINGW32
13819 remove_cr(s, strlen(s)+1);
13820#endif
13113 setinputstring(s); 13821 setinputstring(s);
13114 setstackmark(&smark); 13822 setstackmark(&smark);
13115 13823
@@ -13253,7 +13961,7 @@ find_dot_file(char *name)
13253 struct stat statb; 13961 struct stat statb;
13254 13962
13255 /* don't try this for absolute or relative paths */ 13963 /* don't try this for absolute or relative paths */
13256 if (strchr(name, '/')) 13964 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\')))
13257 return name; 13965 return name;
13258 13966
13259 while ((fullname = path_advance(&path, name)) != NULL) { 13967 while ((fullname = path_advance(&path, name)) != NULL) {
@@ -13375,14 +14083,18 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13375 struct builtincmd *bcmd; 14083 struct builtincmd *bcmd;
13376 14084
13377 /* If name contains a slash, don't use PATH or hash table */ 14085 /* If name contains a slash, don't use PATH or hash table */
13378 if (strchr(name, '/') != NULL) { 14086 if (strchr(name, '/') || (ENABLE_PLATFORM_MINGW32 && strchr(name, '\\'))) {
13379 entry->u.index = -1; 14087 entry->u.index = -1;
13380 if (act & DO_ABS) { 14088 if (act & DO_ABS) {
14089#if ENABLE_PLATFORM_MINGW32
14090 if (auto_win32_extension(name) == NULL && stat(name, &statb) < 0) {
14091#else
13381 while (stat(name, &statb) < 0) { 14092 while (stat(name, &statb) < 0) {
13382#ifdef SYSV 14093#ifdef SYSV
13383 if (errno == EINTR) 14094 if (errno == EINTR)
13384 continue; 14095 continue;
13385#endif 14096#endif
14097#endif
13386 entry->cmdtype = CMDUNKNOWN; 14098 entry->cmdtype = CMDUNKNOWN;
13387 return; 14099 return;
13388 } 14100 }
@@ -13482,12 +14194,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13482 } 14194 }
13483 } 14195 }
13484 /* if rehash, don't redo absolute path names */ 14196 /* if rehash, don't redo absolute path names */
13485 if (fullname[0] == '/' && idx <= prev) { 14197 if (is_absolute_path(fullname) && idx <= prev) {
13486 if (idx < prev) 14198 if (idx < prev)
13487 continue; 14199 continue;
13488 TRACE(("searchexec \"%s\": no change\n", name)); 14200 TRACE(("searchexec \"%s\": no change\n", name));
13489 goto success; 14201 goto success;
13490 } 14202 }
14203#if ENABLE_PLATFORM_MINGW32
14204 add_win32_extension(fullname);
14205#endif
13491 while (stat(fullname, &statb) < 0) { 14206 while (stat(fullname, &statb) < 0) {
13492#ifdef SYSV 14207#ifdef SYSV
13493 if (errno == EINTR) 14208 if (errno == EINTR)
@@ -13978,6 +14693,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13978 if (!isdigit(modestr[0])) 14693 if (!isdigit(modestr[0]))
13979 mask ^= 0777; 14694 mask ^= 0777;
13980 umask(mask); 14695 umask(mask);
14696#if ENABLE_PLATFORM_MINGW32
14697 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
14698#endif
13981 } 14699 }
13982 return 0; 14700 return 0;
13983} 14701}
@@ -14036,6 +14754,7 @@ init(void)
14036 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 14754 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
14037 basepf.linno = 1; 14755 basepf.linno = 1;
14038 14756
14757#if !ENABLE_PLATFORM_MINGW32
14039 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 14758 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14040 setsignal(SIGCHLD); 14759 setsignal(SIGCHLD);
14041 14760
@@ -14043,12 +14762,89 @@ init(void)
14043 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$" 14762 * Try: "trap '' HUP; bash; echo RET" and type "kill -HUP $$"
14044 */ 14763 */
14045 signal(SIGHUP, SIG_DFL); 14764 signal(SIGHUP, SIG_DFL);
14765#endif
14046 14766
14047 { 14767 {
14048 char **envp; 14768 char **envp;
14049 const char *p; 14769 const char *p;
14050 14770
14051 initvar(); 14771 initvar();
14772
14773#if ENABLE_PLATFORM_MINGW32
14774 /*
14775 * case insensitive env names from Windows world
14776 *
14777 * Some standard env names such as PATH is named Path and so on
14778 * ash itself is case sensitive, so "Path" will confuse it, as
14779 * MSVC getenv() is case insensitive.
14780 *
14781 * We may end up having both Path and PATH. Then Path will be chosen
14782 * because it appears first.
14783 */
14784 for (envp = environ; envp && *envp; envp++) {
14785 if (strncasecmp(*envp, "PATH=", 5) == 0 &&
14786 strncmp(*envp, "PATH=", 5) != 0) {
14787 break;
14788 }
14789 }
14790
14791 if (envp && *envp) {
14792 /*
14793 * If we get here it's because the environment contains a path
14794 * variable called something other than PATH. This suggests we
14795 * haven't been invoked from an earlier instance of BusyBox.
14796 */
14797 char *start, *end;
14798 struct passwd *pw;
14799
14800 for (envp = environ; envp && *envp; envp++) {
14801 if (!(end=strchr(*envp, '=')))
14802 continue;
14803
14804 /* make all variable names uppercase */
14805 for (start = *envp;start < end;start++)
14806 *start = toupper(*start);
14807
14808 /* Convert backslashes to forward slashes in value but
14809 * not if we're on Windows XP or for variables known to
14810 * cause problems */
14811 if ( !winxp && strncmp(*envp, "SYSTEMROOT=", 11) != 0 &&
14812 strncmp(*envp, "COMSPEC=", 8) != 0 ) {
14813 bs_to_slash(end+1);
14814 }
14815
14816 /* check for invalid characters in name */
14817 for (start = *envp;start < end;start++) {
14818 if (!isdigit(*start) && !isalpha(*start) && *start != '_') {
14819 break;
14820 }
14821 }
14822
14823 if (start != end) {
14824 /*
14825 * Make a copy of the variable, replacing invalid
14826 * characters in the name with underscores.
14827 */
14828 char *var = xstrdup(*envp);
14829
14830 for (start = var;*start != '=';start++) {
14831 if (!isdigit(*start) && !isalpha(*start)) {
14832 *start = '_';
14833 }
14834 }
14835 setvareq(var, VEXPORT|VNOSAVE);
14836 }
14837 }
14838
14839 /* Initialise some variables normally set at login, but
14840 * only if someone hasn't already set them. */
14841 pw = xgetpwuid(getuid());
14842 if (!getenv("USER")) xsetenv("USER", pw->pw_name);
14843 if (!getenv("LOGNAME")) xsetenv("LOGNAME", pw->pw_name);
14844 if (!getenv("HOME")) xsetenv("HOME", pw->pw_dir);
14845 if (!getenv("SHELL")) xsetenv("SHELL", DEFAULT_SHELL);
14846 }
14847#endif
14052 for (envp = environ; envp && *envp; envp++) { 14848 for (envp = environ; envp && *envp; envp++) {
14053/* Used to have 14849/* Used to have
14054 * p = endofname(*envp); 14850 * p = endofname(*envp);
@@ -14062,7 +14858,11 @@ init(void)
14062 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 14858 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14063 */ 14859 */
14064 if (strchr(*envp, '=')) { 14860 if (strchr(*envp, '=')) {
14861#if !ENABLE_PLATFORM_MINGW32
14065 setvareq(*envp, VEXPORT|VTEXTFIXED); 14862 setvareq(*envp, VEXPORT|VTEXTFIXED);
14863#else
14864 setvareq(*envp, VEXPORT);
14865#endif
14066 } 14866 }
14067 } 14867 }
14068 14868
@@ -14151,6 +14951,9 @@ procargs(char **argv)
14151 goto setarg0; 14951 goto setarg0;
14152 } else if (!sflag) { 14952 } else if (!sflag) {
14153 setinputfile(*xargv, 0); 14953 setinputfile(*xargv, 0);
14954#if ENABLE_PLATFORM_MINGW32
14955 bs_to_slash(*xargv);
14956#endif
14154 setarg0: 14957 setarg0:
14155 arg0 = *xargv++; 14958 arg0 = *xargv++;
14156 commandname = arg0; 14959 commandname = arg0;
@@ -14235,6 +15038,9 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14235 struct jmploc jmploc; 15038 struct jmploc jmploc;
14236 struct stackmark smark; 15039 struct stackmark smark;
14237 int login_sh; 15040 int login_sh;
15041#if ENABLE_PLATFORM_MINGW32
15042 char *sd;
15043#endif
14238 15044
14239 /* Initialize global data */ 15045 /* Initialize global data */
14240 INIT_G_misc(); 15046 INIT_G_misc();
@@ -14281,9 +15087,24 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14281 exception_handler = &jmploc; 15087 exception_handler = &jmploc;
14282 rootpid = getpid(); 15088 rootpid = getpid();
14283 15089
15090#if ENABLE_PLATFORM_MINGW32
15091 winxp = (argv[1] != NULL && strcmp(argv[1], "-X") == 0);
15092#endif
14284 init(); 15093 init();
14285 setstackmark(&smark); 15094 setstackmark(&smark);
14286 15095
15096#if ENABLE_PLATFORM_MINGW32
15097 hSIGINT = CreateEvent(NULL, TRUE, FALSE, NULL);
15098 SetConsoleCtrlHandler(ctrl_handler, TRUE);
15099
15100 if (argc == 3 && !strcmp(argv[1], "--fs")) {
15101 forkshell_init(argv[2]);
15102
15103 /* NOTREACHED */
15104 bb_error_msg_and_die("forkshell failed");
15105 }
15106#endif
15107
14287#if NUM_SCRIPTS > 0 15108#if NUM_SCRIPTS > 0
14288 if (argc < 0) 15109 if (argc < 0)
14289 /* Non-NULL minusc tells procargs that an embedded script is being run */ 15110 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14295,10 +15116,49 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14295 trace_puts_args(argv); 15116 trace_puts_args(argv);
14296#endif 15117#endif
14297 15118
15119#if ENABLE_ASH_NOCONSOLE
15120 if (noconsole)
15121 hide_console();
15122#endif
15123
15124#if ENABLE_PLATFORM_MINGW32
15125 if (dirarg) {
15126 chdir(dirarg);
15127 setpwd(NULL, 0);
15128 }
15129 else if (!login_sh && iflag) {
15130 char *cwd = getcwd(NULL, 0);
15131 if (cwd) {
15132 docd(cwd, 0);
15133 free(cwd);
15134 }
15135 }
15136
15137 if (title)
15138 set_title(title);
15139#endif
15140
14298 if (login_sh) { 15141 if (login_sh) {
14299 const char *hp; 15142 const char *hp;
14300 15143
15144#if ENABLE_PLATFORM_MINGW32
15145 if (!dirarg) {
15146 chdir(xgetpwuid(getuid())->pw_dir);
15147 setpwd(NULL, 0);
15148 }
15149#endif
15150
14301 state = 1; 15151 state = 1;
15152#if ENABLE_PLATFORM_MINGW32
15153 sd = get_system_drive();
15154 if (sd) {
15155 char *path = xasprintf("%s/etc/profile", sd);
15156 read_profile(path);
15157 free(sd);
15158 free(path);
15159 }
15160 else
15161#endif
14302 read_profile("/etc/profile"); 15162 read_profile("/etc/profile");
14303 state1: 15163 state1:
14304 state = 2; 15164 state = 2;
@@ -14309,9 +15169,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14309 state2: 15169 state2:
14310 state = 3; 15170 state = 3;
14311 if ( 15171 if (
15172#if ENABLE_PLATFORM_POSIX
14312#ifndef linux 15173#ifndef linux
14313 getuid() == geteuid() && getgid() == getegid() && 15174 getuid() == geteuid() && getgid() == getegid() &&
14314#endif 15175#endif
15176#endif
14315 iflag 15177 iflag
14316 ) { 15178 ) {
14317 const char *shinit = lookupvar("ENV"); 15179 const char *shinit = lookupvar("ENV");
@@ -14372,6 +15234,809 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14372 /* NOTREACHED */ 15234 /* NOTREACHED */
14373} 15235}
14374 15236
15237#if ENABLE_PLATFORM_MINGW32
15238static void
15239forkshell_openhere(struct forkshell *fs)
15240{
15241 union node *redir = fs->n;
15242 int pip[2];
15243
15244 pip[0] = fs->fd[0];
15245 pip[1] = fs->fd[1];
15246
15247 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15248
15249 close(pip[0]);
15250 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
15251 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
15252 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
15253 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
15254 signal(SIGPIPE, SIG_DFL);
15255 if (redir->type == NHERE) {
15256 size_t len = strlen(redir->nhere.doc->narg.text);
15257 full_write(pip[1], redir->nhere.doc->narg.text, len);
15258 } else /* NXHERE */
15259 expandhere(redir->nhere.doc, pip[1]);
15260 _exit(EXIT_SUCCESS);
15261}
15262
15263static void
15264forkshell_evalbackcmd(struct forkshell *fs)
15265{
15266 union node *n = fs->n;
15267 int pip[2] = {fs->fd[0], fs->fd[1]};
15268
15269 FORCE_INT_ON;
15270 close(pip[0]);
15271 if (pip[1] != 1) {
15272 /*close(1);*/
15273 dup2_or_raise(pip[1], 1);
15274 close(pip[1]);
15275 }
15276 eflag = 0;
15277 ifsfree();
15278 evaltreenr(n, EV_EXIT);
15279 /* NOTREACHED */
15280}
15281
15282static void
15283forkshell_evalsubshell(struct forkshell *fs)
15284{
15285 union node *n = fs->n;
15286 int flags = fs->flags;
15287
15288 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15289 INT_ON;
15290 flags |= EV_EXIT;
15291 expredir(n->nredir.redirect);
15292 redirect(n->nredir.redirect, 0);
15293 evaltreenr(n->nredir.n, flags);
15294 /* never returns */
15295}
15296
15297static void
15298forkshell_evalpipe(struct forkshell *fs)
15299{
15300 union node *n = fs->n;
15301 int flags = fs->flags;
15302 int prevfd = fs->fd[2];
15303 int pip[2] = {fs->fd[0], fs->fd[1]};
15304
15305 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15306 INT_ON;
15307 if (pip[1] >= 0) {
15308 close(pip[0]);
15309 }
15310 if (prevfd > 0) {
15311 dup2(prevfd, 0);
15312 close(prevfd);
15313 }
15314 if (pip[1] > 1) {
15315 dup2(pip[1], 1);
15316 close(pip[1]);
15317 }
15318 evaltreenr(n, flags);
15319}
15320
15321static void
15322forkshell_shellexec(struct forkshell *fs)
15323{
15324 int idx = fs->fd[0];
15325 struct strlist *varlist = fs->varlist;
15326 char **argv = fs->argv;
15327 char *path = fs->path;
15328
15329 FORCE_INT_ON;
15330 listsetvar(varlist, VEXPORT|VSTACK);
15331 shellexec(argv[0], argv, path, idx);
15332}
15333
15334static void
15335forkshell_child(struct forkshell *fs)
15336{
15337 switch ( fs->fpid ) {
15338 case FS_OPENHERE:
15339 forkshell_openhere(fs);
15340 break;
15341 case FS_EVALBACKCMD:
15342 forkshell_evalbackcmd(fs);
15343 break;
15344 case FS_EVALSUBSHELL:
15345 forkshell_evalsubshell(fs);
15346 break;
15347 case FS_EVALPIPE:
15348 forkshell_evalpipe(fs);
15349 break;
15350 case FS_SHELLEXEC:
15351 forkshell_shellexec(fs);
15352 break;
15353 }
15354}
15355
15356/*
15357 * Reinitialise the builtin environment variables in varinit. Their
15358 * current settings have been copied from the parent in vartab. Look
15359 * these up using the names from varinit_data, copy the details from
15360 * vartab to varinit and replace the old copy in vartab with the new
15361 * one in varinit.
15362 *
15363 * Also reinitialise the function pointers and line number variable.
15364 */
15365static void
15366reinitvar(void)
15367{
15368 int i;
15369 const char *name;
15370 struct var **vpp, **old;
15371
15372 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
15373 name = varinit_data[i].var_text ? varinit_data[i].var_text : "LINENO=";
15374 vpp = hashvar(name);
15375 if ( (old=findvar(vpp, name)) != NULL ) {
15376 varinit[i] = **old;
15377 *old = varinit+i;
15378 }
15379 varinit[i].var_func = varinit_data[i].var_func;
15380 }
15381 vlineno.var_text = linenovar;
15382}
15383
15384static void
15385spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
15386{
15387 struct forkshell *new;
15388 char buf[32];
15389 const char *argv[] = { "sh", "--fs", NULL, NULL };
15390 intptr_t ret;
15391
15392 new = forkshell_prepare(fs);
15393 new->mode = mode;
15394 new->nprocs = jp == NULL ? 0 : jp->nprocs;
15395 sprintf(buf, "%p", new->hMapFile);
15396 argv[2] = buf;
15397 ret = mingw_spawn_proc(argv);
15398 CloseHandle(new->hMapFile);
15399 UnmapViewOfFile(new);
15400 if (ret == -1) {
15401 if (jp)
15402 freejob(jp);
15403 ash_msg_and_raise_error("unable to spawn shell");
15404 }
15405 forkparent(jp, n, mode, (HANDLE)ret);
15406}
15407
15408/*
15409 * forkshell_prepare() and friends
15410 *
15411 * The sequence is as follows:
15412 * - funcblocksize, nodeptrcount are initialized
15413 * - forkshell_size(fs) is called to calculate the exact memory needed
15414 * - a new struct is allocated
15415 * - funcblock, funcstring, nodeptr are initialized from the new block
15416 * - forkshell_copy(fs) is called to copy recursively everything over
15417 * it will record all pointers along the way, to nodeptr
15418 *
15419 * When this memory is mapped elsewhere, pointer fixup will be needed
15420 */
15421
15422/* redefine without test that nodeptr is non-NULL */
15423#undef SAVE_PTR
15424#undef SAVE_PTR2
15425#undef SAVE_PTR3
15426#define SAVE_PTR(dst) {*nodeptr++ = (char **)&(dst);}
15427#define SAVE_PTR2(dst1,dst2) {SAVE_PTR(dst1); SAVE_PTR(dst2);}
15428#define SAVE_PTR3(dst1,dst2,dst3) {SAVE_PTR2(dst1,dst2); SAVE_PTR(dst3);}
15429#define SAVE_PTR4(dst1,dst2,dst3,dst4) {SAVE_PTR2(dst1,dst2); SAVE_PTR2(dst3,dst4);}
15430
15431static int align_len(const char *s)
15432{
15433 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
15434}
15435
15436#define SLIST_SIZE_BEGIN(name,type) \
15437static int \
15438name(int funcblocksize, type *p) \
15439{ \
15440 while (p) { \
15441 funcblocksize += sizeof(type);
15442 /* do something here with p */
15443#define SLIST_SIZE_END() \
15444 nodeptrcount++; \
15445 p = p->next; \
15446 } \
15447 return funcblocksize; \
15448}
15449
15450#define SLIST_COPY_BEGIN(name,type) \
15451static type * \
15452name(type *vp) \
15453{ \
15454 type *start; \
15455 type **vpp; \
15456 vpp = &start; \
15457 while (vp) { \
15458 *vpp = funcblock; \
15459 funcblock = (char *) funcblock + sizeof(type);
15460 /* do something here with vpp and vp */
15461#define SLIST_COPY_END() \
15462 SAVE_PTR((*vpp)->next); \
15463 ANNOT("(*vpp)->next"); \
15464 vp = vp->next; \
15465 vpp = &(*vpp)->next; \
15466 } \
15467 *vpp = NULL; \
15468 return start; \
15469}
15470
15471/*
15472 * struct var
15473 */
15474SLIST_SIZE_BEGIN(var_size,struct var)
15475funcblocksize += align_len(p->var_text);
15476nodeptrcount++; /* p->text */
15477SLIST_SIZE_END()
15478
15479SLIST_COPY_BEGIN(var_copy,struct var)
15480(*vpp)->var_text = nodeckstrdup(vp->var_text);
15481(*vpp)->flags = vp->flags;
15482(*vpp)->var_func = NULL;
15483SAVE_PTR((*vpp)->var_text);
15484ANNOT_NO_DUP(xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"));
15485SLIST_COPY_END()
15486
15487/*
15488 * struct strlist
15489 */
15490SLIST_SIZE_BEGIN(strlist_size,struct strlist)
15491funcblocksize += align_len(p->text);
15492nodeptrcount++; /* p->text */
15493SLIST_SIZE_END()
15494
15495SLIST_COPY_BEGIN(strlist_copy,struct strlist)
15496(*vpp)->text = nodeckstrdup(vp->text);
15497SAVE_PTR((*vpp)->text);
15498ANNOT_NO_DUP(xasprintf("(*vpp)->text '%s'", vp->text ?: "NULL"));
15499SLIST_COPY_END()
15500
15501/*
15502 * struct tblentry
15503 */
15504static int
15505tblentry_size(int funcblocksize, struct tblentry *tep)
15506{
15507 while (tep) {
15508 funcblocksize += sizeof(struct tblentry) + strlen(tep->cmdname);
15509 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
15510 if (tep->cmdtype == CMDFUNCTION) {
15511 funcblocksize += offsetof(struct funcnode, n);
15512 funcblocksize = calcsize(funcblocksize, &tep->param.func->n);
15513 nodeptrcount++; /* tep->param.func */
15514 }
15515 nodeptrcount++; /* tep->next */
15516 tep = tep->next;
15517 }
15518 return funcblocksize;
15519}
15520
15521static struct tblentry *
15522tblentry_copy(struct tblentry *tep)
15523{
15524 struct tblentry *start;
15525 struct tblentry **newp;
15526 int size;
15527
15528 newp = &start;
15529 while (tep) {
15530 *newp = funcblock;
15531 size = sizeof(struct tblentry) + strlen(tep->cmdname);
15532
15533 funcblock = (char *) funcblock + size;
15534 memcpy(*newp, tep, size);
15535 switch (tep->cmdtype) {
15536 case CMDBUILTIN:
15537 /* Save index of builtin, not pointer; fixed by forkshell_init() */
15538 (*newp)->param.index = tep->param.cmd - builtintab;
15539 break;
15540 case CMDFUNCTION:
15541 (*newp)->param.func = funcblock;
15542 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
15543 copynode(&tep->param.func->n);
15544 SAVE_PTR((*newp)->param.func);
15545 ANNOT("param.func");
15546 break;
15547 default:
15548 break;
15549 }
15550 SAVE_PTR((*newp)->next);
15551 ANNOT_NO_DUP(xasprintf("cmdname '%s'", tep->cmdname));
15552 tep = tep->next;
15553 newp = &(*newp)->next;
15554 }
15555 *newp = NULL;
15556 return start;
15557}
15558
15559static int
15560cmdtable_size(int funcblocksize, struct tblentry **cmdtablep)
15561{
15562 int i;
15563 nodeptrcount += CMDTABLESIZE;
15564 funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
15565 for (i = 0; i < CMDTABLESIZE; i++)
15566 funcblocksize = tblentry_size(funcblocksize, cmdtablep[i]);
15567 return funcblocksize;
15568}
15569
15570static struct tblentry **
15571cmdtable_copy(struct tblentry **cmdtablep)
15572{
15573 struct tblentry **new = funcblock;
15574 int i;
15575
15576 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
15577 for (i = 0; i < CMDTABLESIZE; i++) {
15578 new[i] = tblentry_copy(cmdtablep[i]);
15579 SAVE_PTR(new[i]);
15580 ANNOT_NO_DUP(xasprintf("cmdtablep[%d]", i));
15581 }
15582 return new;
15583}
15584
15585/*
15586 * char **
15587 */
15588static int
15589argv_size(int funcblocksize, char **p)
15590{
15591 while (p && *p) {
15592 funcblocksize += sizeof(char *);
15593 funcblocksize += align_len(*p);
15594 nodeptrcount++;
15595 p++;
15596 }
15597 funcblocksize += sizeof(char *);
15598 return funcblocksize;
15599}
15600
15601static char **
15602argv_copy(char **p)
15603{
15604 char **new, **start = funcblock;
15605#if FORKSHELL_DEBUG
15606 int i = 0;
15607#endif
15608
15609 while (p && *p) {
15610 new = funcblock;
15611 funcblock = (char *) funcblock + sizeof(char *);
15612 *new = nodeckstrdup(*p);
15613 SAVE_PTR(*new);
15614 ANNOT_NO_DUP(xasprintf("argv[%d] '%s'", i++, *p));
15615 p++;
15616 new++;
15617 }
15618 new = funcblock;
15619 funcblock = (char *) funcblock + sizeof(char *);
15620 *new = NULL;
15621 return start;
15622}
15623
15624/*
15625 * struct redirtab
15626 */
15627static int
15628redirtab_size(int funcblocksize, struct redirtab *rdtp)
15629{
15630 while (rdtp) {
15631 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
15632 rdtp = rdtp->next;
15633 nodeptrcount++; /* rdtp->next */
15634 }
15635 return funcblocksize;
15636}
15637
15638static struct redirtab *
15639redirtab_copy(struct redirtab *rdtp)
15640{
15641 struct redirtab *start;
15642 struct redirtab **vpp;
15643
15644 vpp = &start;
15645 while (rdtp) {
15646 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
15647 *vpp = funcblock;
15648 funcblock = (char *) funcblock + size;
15649 memcpy(*vpp, rdtp, size);
15650 SAVE_PTR((*vpp)->next);
15651 ANNOT("(*vpp)->next");
15652 rdtp = rdtp->next;
15653 vpp = &(*vpp)->next;
15654 }
15655 *vpp = NULL;
15656 return start;
15657}
15658
15659#undef shellparam
15660#undef redirlist
15661#undef vartab
15662static int
15663globals_var_size(int funcblocksize, struct globals_var *gvp)
15664{
15665 int i;
15666
15667 funcblocksize += sizeof(struct globals_var);
15668 funcblocksize = argv_size(funcblocksize, gvp->shellparam.p);
15669 funcblocksize = redirtab_size(funcblocksize, gvp->redirlist);
15670 for (i = 0; i < VTABSIZE; i++)
15671 funcblocksize = var_size(funcblocksize, gvp->vartab[i]);
15672 /* gvp->redirlist, gvp->shellparam.p, vartab */
15673 nodeptrcount += 2 + VTABSIZE;
15674 return funcblocksize;
15675}
15676
15677static struct globals_var *
15678globals_var_copy(struct globals_var *gvp)
15679{
15680 int i;
15681 struct globals_var *new;
15682
15683 new = funcblock;
15684 funcblock = (char *) funcblock + sizeof(struct globals_var);
15685 memcpy(new, gvp, sizeof(struct globals_var));
15686
15687 /* shparam */
15688 new->shellparam.malloced = 0;
15689 new->shellparam.p = argv_copy(gvp->shellparam.p);
15690 SAVE_PTR(new->shellparam.p);
15691 ANNOT("shellparam.p");
15692
15693 new->redirlist = redirtab_copy(gvp->redirlist);
15694 SAVE_PTR(new->redirlist);
15695 ANNOT("redirlist");
15696
15697 for (i = 0; i < VTABSIZE; i++) {
15698 new->vartab[i] = var_copy(gvp->vartab[i]);
15699 SAVE_PTR(new->vartab[i]);
15700 ANNOT_NO_DUP(xasprintf("vartab[%d]", i));
15701 }
15702
15703 return new;
15704}
15705
15706#undef minusc
15707#undef curdir
15708#undef physdir
15709#undef arg0
15710#undef commandname
15711#undef nullstr
15712static int
15713globals_misc_size(int funcblocksize, struct globals_misc *p)
15714{
15715 funcblocksize += sizeof(struct globals_misc);
15716 funcblocksize += align_len(p->minusc);
15717 if (p->curdir != p->nullstr)
15718 funcblocksize += align_len(p->curdir);
15719 if (p->physdir != p->nullstr)
15720 funcblocksize += align_len(p->physdir);
15721 funcblocksize += align_len(p->arg0);
15722 funcblocksize += align_len(p->commandname);
15723 nodeptrcount += 5; /* minusc, curdir, physdir, arg0, commandname */
15724 return funcblocksize;
15725}
15726
15727static struct globals_misc *
15728globals_misc_copy(struct globals_misc *p)
15729{
15730 struct globals_misc *new = funcblock;
15731
15732 funcblock = (char *) funcblock + sizeof(struct globals_misc);
15733 memcpy(new, p, sizeof(struct globals_misc));
15734
15735 new->minusc = nodeckstrdup(p->minusc);
15736 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
15737 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
15738 new->arg0 = nodeckstrdup(p->arg0);
15739 new->commandname = nodeckstrdup(p->commandname);
15740 SAVE_PTR4(new->minusc, new->curdir, new->physdir, new->arg0);
15741 SAVE_PTR(new->commandname);
15742 ANNOT_NO_DUP(xasprintf("minusc '%s'", p->minusc ?: "NULL"));
15743 ANNOT_NO_DUP(xasprintf("curdir '%s'", new->curdir ?: "NULL"));
15744 ANNOT_NO_DUP(xasprintf("physdir '%s'", new->physdir ?: "NULL"));
15745 ANNOT_NO_DUP(xasprintf("arg0 '%s'", p->arg0 ?: "NULL");)
15746 ANNOT_NO_DUP(xasprintf("commandname '%s'", p->commandname ?: "NULL"));
15747 return new;
15748}
15749
15750static int
15751forkshell_size(int funcblocksize, struct forkshell *fs)
15752{
15753 funcblocksize = globals_var_size(funcblocksize, fs->gvp);
15754 funcblocksize = globals_misc_size(funcblocksize, fs->gmp);
15755 funcblocksize = cmdtable_size(funcblocksize, fs->cmdtable);
15756 /* optlist_transfer(sending, fd); */
15757 /* misc_transfer(sending, fd); */
15758
15759 funcblocksize = calcsize(funcblocksize, fs->n);
15760 funcblocksize = argv_size(funcblocksize, fs->argv);
15761 funcblocksize += align_len(fs->path);
15762 funcblocksize = strlist_size(funcblocksize, fs->varlist);
15763
15764 nodeptrcount += 7; /* gvp, gmp, cmdtable, n, argv, string, strlist */
15765 return funcblocksize;
15766}
15767
15768static void
15769forkshell_copy(struct forkshell *fs, struct forkshell *new)
15770{
15771 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
15772 new->gvp = globals_var_copy(fs->gvp);
15773 new->gmp = globals_misc_copy(fs->gmp);
15774 new->cmdtable = cmdtable_copy(fs->cmdtable);
15775 SAVE_PTR3(new->gvp, new->gmp, new->cmdtable);
15776 ANNOT3("gvp", "gmp", "cmdtable");
15777
15778 new->n = copynode(fs->n);
15779 new->argv = argv_copy(fs->argv);
15780 new->path = nodeckstrdup(fs->path);
15781 new->varlist = strlist_copy(fs->varlist);
15782 SAVE_PTR4(new->n, new->argv, new->path, new->varlist);
15783 ANNOT2("n", "argv");
15784 ANNOT_NO_DUP(xasprintf("path '%s'", fs->path ?: "NULL"));
15785 ANNOT("varlist");
15786}
15787
15788#if FORKSHELL_DEBUG
15789/* fp and notes can each be NULL */
15790static void
15791forkshell_print(FILE *fp0, struct forkshell *fs, char **notes)
15792{
15793 FILE *fp;
15794 void *lfuncblock;
15795 char *lfuncstring;
15796 char ***lnodeptr;
15797 char *s;
15798 int count;
15799
15800 if (fp0 != NULL) {
15801 fp = fp0;
15802 }
15803 else {
15804 char name[32];
15805
15806 sprintf(name, "fs_%d.out", getpid());
15807 if ((fp=fopen(name, "w")) == NULL)
15808 return;
15809 }
15810
15811 fprintf(fp, "size %d = %d + %d*%d + %d + %d\n", fs->size,
15812 (int)sizeof(struct forkshell), fs->nodeptrcount,
15813 (int)sizeof(char *), fs->funcblocksize, fs->funcstringsize);
15814
15815 lnodeptr = fs->nodeptr;
15816 lfuncblock = (char *)lnodeptr + (fs->nodeptrcount+1)*sizeof(char *);
15817 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
15818
15819 fprintf(fp, "funcblocksize %d = %d + %d + %d\n\n", fs->funcblocksize,
15820 (int)((char *)fs->gmp-(char *)fs->gvp),
15821 (int)((char *)fs->cmdtable-(char *)fs->gmp),
15822 (int)(lfuncstring-(char *)fs->cmdtable));
15823
15824 fprintf(fp, "--- nodeptr ---\n");
15825 count = 0;
15826 if (notes == NULL) {
15827 while (*lnodeptr) {
15828 fprintf(fp, "%p ", *lnodeptr++);
15829 if ((count&3) == 3)
15830 fprintf(fp, "\n");
15831 ++count;
15832 }
15833 if (((count-1)&3) != 3)
15834 fprintf(fp, "\n");
15835 }
15836 else {
15837 while (*lnodeptr) {
15838 fprintf(fp, "%p %p %s\n", *lnodeptr, **lnodeptr, notes[count++]);
15839 lnodeptr++;
15840 }
15841 }
15842 if (count != fs->nodeptrcount)
15843 fprintf(fp, "--- %d pointers (expected %d) ---\n\n", count,
15844 fs->nodeptrcount);
15845 else
15846 fprintf(fp, "--- %d pointers ---\n\n", count);
15847
15848 fprintf(fp, "--- funcstring ---\n");
15849 count = 0;
15850 s = lfuncstring;
15851 while (s-lfuncstring < fs->funcstringsize) {
15852 if (!*s) {
15853 ++s;
15854 continue;
15855 }
15856 fprintf(fp, "%p '%s'\n", s, s);
15857 s += strlen(s)+1;
15858 ++count;
15859 }
15860 fprintf(fp, "--- %d strings ---\n", count);
15861
15862 if (fp0 == NULL)
15863 fclose(fp);
15864}
15865#endif
15866
15867static struct forkshell *
15868forkshell_prepare(struct forkshell *fs)
15869{
15870 int funcblocksize;
15871 struct forkshell *new;
15872 int size;
15873 HANDLE h;
15874 SECURITY_ATTRIBUTES sa;
15875#if FORKSHELL_DEBUG
15876 void *fb0;
15877 char name[32];
15878 FILE *fp;
15879#endif
15880
15881 /* Calculate size of "new" */
15882 fs->gvp = ash_ptr_to_globals_var;
15883 fs->gmp = ash_ptr_to_globals_misc;
15884 fs->cmdtable = cmdtable;
15885
15886 /*
15887 * Careful: much scope for off-by-one errors. nodeptrcount is the
15888 * number of actual pointers. There's also a terminating NULL pointer.
15889 * The array in the forkshell structure gives us one element for free.
15890 */
15891 nodeptrcount = 0;
15892 funcblocksize = forkshell_size(0, fs);
15893 size = sizeof(struct forkshell) + nodeptrcount*sizeof(char *) +
15894 funcblocksize;
15895
15896 /* Allocate, initialize pointers */
15897 memset(&sa, 0, sizeof(sa));
15898 sa.nLength = sizeof(sa);
15899 sa.lpSecurityDescriptor = NULL;
15900 sa.bInheritHandle = TRUE;
15901 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0, size, NULL);
15902 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
15903 nodeptr = new->nodeptr;
15904 funcblock = (char *)nodeptr + (nodeptrcount+1)*sizeof(char *);
15905 funcstring_end = (char *)new + size;
15906#if FORKSHELL_DEBUG
15907 fb0 = funcblock;
15908 annot = xmalloc(sizeof(char *)*nodeptrcount);
15909 annot_count = 0;
15910#endif
15911
15912 /* Now pack them all */
15913 forkshell_copy(fs, new);
15914
15915 /* Finish it up */
15916 *nodeptr = NULL;
15917 new->size = size;
15918 new->old_base = (char *)new;
15919 new->hMapFile = h;
15920#if FORKSHELL_DEBUG
15921 sprintf(name, "fs_%d.out", getpid());
15922 if ((fp=fopen(name, "w")) != NULL) {
15923 int i;
15924
15925 /* perform some sanity checks on pointers */
15926 fprintf(fp, "%p start of forkshell struct\n", new);
15927 fprintf(fp, "%p start of nodeptr\n", new->nodeptr);
15928 fprintf(fp, "%p start of funcblock", fb0);
15929 if ((char *)(nodeptr+1) != (char *)fb0)
15930 fprintf(fp, " != end nodeptr block + 1 %p\n", nodeptr+1);
15931 else
15932 fprintf(fp, "\n");
15933
15934 fprintf(fp, "%p start of funcstring", funcstring_end);
15935 if ((char *)funcblock != funcstring_end)
15936 fprintf(fp, " != end funcblock + 1 %p\n\n", funcblock);
15937 else
15938 fprintf(fp, "\n\n");
15939
15940 if (nodeptrcount != annot_count)
15941 fprintf(fp, "nodeptrcount (%d) != annot_count (%d)\n\n",
15942 nodeptrcount, annot_count);
15943
15944 new->nodeptrcount = nodeptrcount;
15945 new->funcblocksize = (char *)funcblock - (char *)fb0;
15946 new->funcstringsize = (char *)new + size - funcstring_end;
15947 forkshell_print(fp, new, nodeptrcount == annot_count ? annot : NULL);
15948
15949 for (i = 0; i < annot_count; ++i)
15950 free(annot[i]);
15951 free(annot);
15952 annot = NULL;
15953 annot_count = 0;
15954 fclose(fp);
15955 }
15956#endif
15957 return new;
15958}
15959
15960#undef exception_handler
15961#undef trap
15962#undef trap_ptr
15963static void *sticky_mem_start, *sticky_mem_end;
15964static void
15965forkshell_init(const char *idstr)
15966{
15967 struct forkshell *fs;
15968 void *map_handle;
15969 HANDLE h;
15970 struct globals_var **gvpp;
15971 struct globals_misc **gmpp;
15972 int i;
15973 char **ptr;
15974
15975 if (sscanf(idstr, "%p", &map_handle) != 1)
15976 bb_error_msg_and_die("invalid forkshell ID");
15977
15978 h = (HANDLE)map_handle;
15979 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
15980 if (!fs)
15981 bb_error_msg_and_die("Invalid forkshell memory");
15982
15983 /* this memory can't be freed */
15984 sticky_mem_start = fs;
15985 sticky_mem_end = (char *) fs + fs->size;
15986
15987 /* pointer fixup */
15988 for ( i=0; fs->nodeptr[i]; ++i ) {
15989 ptr = (char **)((char *)fs + ((char *)fs->nodeptr[i] - fs->old_base));
15990 if (*ptr)
15991 *ptr = (char *)fs + (*ptr - fs->old_base);
15992 }
15993
15994 /* Now fix up stuff that can't be transferred */
15995 for (i = 0; i < CMDTABLESIZE; i++) {
15996 struct tblentry *e = fs->cmdtable[i];
15997 while (e) {
15998 if (e->cmdtype == CMDBUILTIN)
15999 e->param.cmd = builtintab + e->param.index;
16000 e = e->next;
16001 }
16002 }
16003 fs->gmp->exception_handler = ash_ptr_to_globals_misc->exception_handler;
16004 for (i = 0; i < NSIG; i++)
16005 fs->gmp->trap[i] = ash_ptr_to_globals_misc->trap[i];
16006 fs->gmp->trap_ptr = ash_ptr_to_globals_misc->trap_ptr;
16007
16008 /* Switch global variables */
16009 gvpp = (struct globals_var **)&ash_ptr_to_globals_var;
16010 *gvpp = fs->gvp;
16011 gmpp = (struct globals_misc **)&ash_ptr_to_globals_misc;
16012 *gmpp = fs->gmp;
16013 cmdtable = fs->cmdtable;
16014
16015 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
16016
16017 reinitvar();
16018
16019 shlvl++;
16020 if (fs->mode == FORK_BG) {
16021 SetConsoleCtrlHandler(NULL, TRUE);
16022 if (fs->nprocs == 0) {
16023 close(0);
16024 if (open(bb_dev_null, O_RDONLY) != 0)
16025 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
16026 }
16027 }
16028 forkshell_child(fs);
16029}
16030
16031#undef free
16032static void
16033sticky_free(void *base)
16034{
16035 if (base >= sticky_mem_start && base < sticky_mem_end)
16036 return;
16037 free(base);
16038}
16039#endif
14375 16040
14376/*- 16041/*-
14377 * Copyright (c) 1989, 1991, 1993, 1994 16042 * Copyright (c) 1989, 1991, 1993, 1994
diff --git a/shell/math.h b/shell/math.h
index 41ef6e8df..a3fe51b6b 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#else 69#else
70typedef long arith_t; 70typedef long arith_t;
71# define ARITH_FMT "%ld" 71# define ARITH_FMT "%ld"
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 e0582adfb..d1df5888c 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/* read builtin */ 29/* read builtin */
@@ -43,7 +47,9 @@ shell_builtin_read(struct builtin_read_params *params)
43 char **pp; 47 char **pp;
44 char *buffer; 48 char *buffer;
45 char delim; 49 char delim;
50#if !ENABLE_PLATFORM_MINGW32
46 struct termios tty, old_tty; 51 struct termios tty, old_tty;
52#endif
47 const char *retval; 53 const char *retval;
48 int bufpos; /* need to be able to hold -1 */ 54 int bufpos; /* need to be able to hold -1 */
49 int startword; 55 int startword;
@@ -123,6 +129,18 @@ shell_builtin_read(struct builtin_read_params *params)
123 * bash seems to ignore -p PROMPT for this use case. 129 * bash seems to ignore -p PROMPT for this use case.
124 */ 130 */
125 int r; 131 int r;
132#if ENABLE_PLATFORM_MINGW32
133 HANDLE handle = (HANDLE)_get_osfhandle(fd);
134 DWORD filetype = FILE_TYPE_UNKNOWN;
135
136 if (handle != INVALID_HANDLE_VALUE)
137 filetype = GetFileType(handle);
138 /* poll uses WaitForSingleObject which can't handle disk files */
139 if (filetype == FILE_TYPE_DISK || filetype == FILE_TYPE_UNKNOWN)
140 return (const char *)(uintptr_t)(0);
141 if (isatty(fd))
142 return (const char *)(uintptr_t)(1);
143#endif
126 pfd[0].events = POLLIN; 144 pfd[0].events = POLLIN;
127 r = poll(pfd, 1, /*timeout:*/ 0); 145 r = poll(pfd, 1, /*timeout:*/ 0);
128 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */ 146 /* Return 0 only if poll returns 1 ("one fd ready"), else return 1: */
@@ -139,6 +157,7 @@ shell_builtin_read(struct builtin_read_params *params)
139 ifs = defifs; 157 ifs = defifs;
140 158
141 read_flags = params->read_flags; 159 read_flags = params->read_flags;
160#if !ENABLE_PLATFORM_MINGW32
142 if (nchars || (read_flags & BUILTIN_READ_SILENT)) { 161 if (nchars || (read_flags & BUILTIN_READ_SILENT)) {
143 tcgetattr(fd, &tty); 162 tcgetattr(fd, &tty);
144 old_tty = tty; 163 old_tty = tty;
@@ -161,6 +180,7 @@ shell_builtin_read(struct builtin_read_params *params)
161 * Ignoring, it's harmless. */ 180 * Ignoring, it's harmless. */
162 tcsetattr(fd, TCSANOW, &tty); 181 tcsetattr(fd, TCSANOW, &tty);
163 } 182 }
183#endif
164 184
165 retval = (const char *)(uintptr_t)0; 185 retval = (const char *)(uintptr_t)0;
166 startword = 1; 186 startword = 1;
@@ -190,6 +210,7 @@ shell_builtin_read(struct builtin_read_params *params)
190 } 210 }
191 } 211 }
192 212
213#if !ENABLE_PLATFORM_MINGW32
193 /* We must poll even if timeout is -1: 214 /* We must poll even if timeout is -1:
194 * we want to be interrupted if signal arrives, 215 * we want to be interrupted if signal arrives,
195 * regardless of SA_RESTART-ness of that signal! 216 * regardless of SA_RESTART-ness of that signal!
@@ -207,9 +228,34 @@ shell_builtin_read(struct builtin_read_params *params)
207 retval = (const char *)(uintptr_t)1; 228 retval = (const char *)(uintptr_t)1;
208 break; 229 break;
209 } 230 }
231#else
232 errno = 0;
233 if (isatty(fd)) {
234 int64_t key;
235
236 key = read_key(fd, NULL, timeout);
237 if (key == 0x03 || key == -1) {
238 /* ^C or timeout */
239 retval = (const char *)(uintptr_t)1;
240 goto ret;
241 }
242 buffer[bufpos] = key == '\r' ? '\n' : key;
243 if (!(read_flags & BUILTIN_READ_SILENT)) {
244 /* echo input if not in silent mode */
245 putchar(buffer[bufpos]);
246 }
247 }
248 else {
249 if (read(fd, &buffer[bufpos], 1) != 1) {
250 err = errno;
251 retval = (const char *)(uintptr_t)1;
252 break;
253 }
254 }
255#endif
210 256
211 c = buffer[bufpos]; 257 c = buffer[bufpos];
212 if (c == '\0') 258 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r'))
213 continue; 259 continue;
214 if (!(read_flags & BUILTIN_READ_RAW)) { 260 if (!(read_flags & BUILTIN_READ_RAW)) {
215 if (backslash) { 261 if (backslash) {
@@ -309,8 +355,10 @@ shell_builtin_read(struct builtin_read_params *params)
309 355
310 ret: 356 ret:
311 free(buffer); 357 free(buffer);
358#if !ENABLE_PLATFORM_MINGW32
312 if (read_flags & BUILTIN_READ_SILENT) 359 if (read_flags & BUILTIN_READ_SILENT)
313 tcsetattr(fd, TCSANOW, &old_tty); 360 tcsetattr(fd, TCSANOW, &old_tty);
361#endif
314 362
315 errno = err; 363 errno = err;
316 return retval; 364 return retval;
@@ -319,6 +367,7 @@ shell_builtin_read(struct builtin_read_params *params)
319 367
320/* ulimit builtin */ 368/* ulimit builtin */
321 369
370#if !ENABLE_PLATFORM_MINGW32
322struct limits { 371struct limits {
323 uint8_t cmd; /* RLIMIT_xxx fit into it */ 372 uint8_t cmd; /* RLIMIT_xxx fit into it */
324 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 373 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
@@ -633,3 +682,9 @@ shell_builtin_ulimit(char **argv)
633 682
634 return EXIT_SUCCESS; 683 return EXIT_SUCCESS;
635} 684}
685#else
686int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM)
687{
688 return 1;
689}
690#endif
diff --git a/testsuite/busybox.tests b/testsuite/busybox.tests
index beb17440c..a671fa082 100755
--- a/testsuite/busybox.tests
+++ b/testsuite/busybox.tests
@@ -7,11 +7,22 @@
7. ./testing.sh 7. ./testing.sh
8test -f "$bindir/.config" && . "$bindir/.config" 8test -f "$bindir/.config" && . "$bindir/.config"
9 9
10ln -s `which busybox` unknown 10if [ -f $bindir/busybox.exe ]; then
11 # Copy rather than link busybox.exe: we can only make hard
12 # links which can't be deleted because Windows sees the
13 # executable as running.
14 suffix=".exe"
15 lncmd="cp"
16else
17 suffix=""
18 lncmd="ln -s"
19fi
20
21$lncmd `which busybox` unknown$suffix
11 22
12testing "busybox as unknown name" "./unknown 2>&1" \ 23testing "busybox as unknown name" "./unknown 2>&1" \
13 "unknown: applet not found\n" "" "" 24 "unknown: applet not found\n" "" ""
14rm unknown 25rm unknown$suffix
15 26
16# We need busybox --help to be enabled for the rest of tests 27# We need busybox --help to be enabled for the rest of tests
17test x"$CONFIG_BUSYBOX" = x"y" \ 28test x"$CONFIG_BUSYBOX" = x"y" \
@@ -23,7 +34,7 @@ optional FEATURE_VERBOSE_USAGE
23testing "busybox --help busybox" "true | busybox --help busybox 2>&1 | cat" "$HELPDUMP\n" "" "" 34testing "busybox --help busybox" "true | busybox --help busybox 2>&1 | cat" "$HELPDUMP\n" "" ""
24SKIP= 35SKIP=
25 36
26ln -s `which busybox` busybox-suffix 37$lncmd `which busybox` busybox-suffix$suffix
27for i in busybox ./busybox-suffix 38for i in busybox ./busybox-suffix
28do 39do
29 testing "$i" "$i 2>&1 | cat" "$HELPDUMP\n" "" "" 40 testing "$i" "$i 2>&1 | cat" "$HELPDUMP\n" "" ""
@@ -42,6 +53,6 @@ do
42 testing "$i --help unknown" "$i --help unknown 2>&1" \ 53 testing "$i --help unknown" "$i --help unknown 2>&1" \
43 "unknown: applet not found\n" "" "" 54 "unknown: applet not found\n" "" ""
44done 55done
45rm busybox-suffix 56rm busybox-suffix$suffix
46 57
47exit $FAILCOUNT 58exit $FAILCOUNT
diff --git a/testsuite/runtest b/testsuite/runtest
index 44f9cd1a1..9f9e8d3bd 100755
--- a/testsuite/runtest
+++ b/testsuite/runtest
@@ -99,6 +99,20 @@ if [ x"$1" = x"-v" ]; then
99 shift 99 shift
100fi 100fi
101 101
102if [ -f $bindir/busybox.exe ]; then
103 suffix=".exe"
104 lnflag=""
105
106 # Some tests require /bin/echo and /bin/true exist
107 test ! -d /tmp && mkdir /tmp
108 test ! -d /bin && mkdir /bin
109 test ! -f /bin/echo.exe && cp "$bindir/busybox.exe" /bin/echo.exe
110 test ! -f /bin/true.exe && cp "$bindir/busybox.exe" /bin/true.exe
111else
112 suffix=""
113 lnflag="-s"
114fi
115
102implemented=$( 116implemented=$(
103 printf "busybox " # always implemented 117 printf "busybox " # always implemented
104 "$bindir/busybox" 2>&1 | 118 "$bindir/busybox" 2>&1 |
@@ -128,7 +142,7 @@ for i in $implemented; do
128 # Note: if $LINKSDIR/applet exists, we do not overwrite it. 142 # Note: if $LINKSDIR/applet exists, we do not overwrite it.
129 # Useful if one wants to run tests against a standard utility, 143 # Useful if one wants to run tests against a standard utility,
130 # not an applet. 144 # not an applet.
131 ln -s "$bindir/busybox" "$LINKSDIR/$i" 2>/dev/null 145 ln $lnflag "$bindir/busybox$suffix" "$LINKSDIR/$i$suffix" 2>/dev/null
132done 146done
133 147
134# Set up option flags so tests can be selective. 148# Set up option flags so tests can be selective.
@@ -146,7 +160,7 @@ for applet in $applets; do
146 160
147 # Is this a new-style test? 161 # Is this a new-style test?
148 if [ -f "$applet.tests" ]; then 162 if [ -f "$applet.tests" ]; then
149 if [ ! -e "$LINKSDIR/$applet" ]; then 163 if [ ! -e "$LINKSDIR/$applet$suffix" ]; then
150 # (avoiding bash'ism "${applet:0:4}") 164 # (avoiding bash'ism "${applet:0:4}")
151 if ! echo "$applet" | grep "^all_" >/dev/null; then 165 if ! echo "$applet" | grep "^all_" >/dev/null; then
152 echo "SKIPPED: $applet (not built)" 166 echo "SKIPPED: $applet (not built)"
diff --git a/testsuite/sh.tests b/testsuite/sh.tests
new file mode 100755
index 000000000..2a510fb25
--- /dev/null
+++ b/testsuite/sh.tests
@@ -0,0 +1,90 @@
1#!/bin/sh
2#
3# Test sh scripts
4#
5# Copyright 2019 by STMicroelectronics
6# Licensed under GPLv2, see file LICENSE in this source tree.
7
8. ./testing.sh
9
10test -f "$bindir/.config" && . "$bindir/.config"
11
12# testing "test name" "options" "expected result" "file input" "stdin"
13
14# Test case
15testing "shebang" \
16 "uudecode; sh -c './shebang.sh'; echo \$?" \
17 "Hello world
180
19" \
20"" "\
21begin-base64 755 shebang.sh
22IyEvYmluL3NoCmVjaG8gIkhlbGxvIHdvcmxkIgo=
23====
24"
25rm -f shebang.sh
26
27# Test case
28testing "shebang with whitespace" \
29 "uudecode; sh -c './shebang_trailing_space.sh'; echo \$?" \
30 "Hello world
310
32" \
33"" "\
34begin-base64 755 shebang_trailing_space.sh
35IyEvYmluL3NoIAplY2hvICJIZWxsbyB3b3JsZCIK
36====
37"
38rm -f shebang_trailing_space.sh
39
40# Test case
41testing "shebang with argument" \
42 "uudecode; sh -c './shebang_argument.sh'; echo \$?" \
43 "Hello world
440
45" \
46"" "\
47begin-base64 755 shebang_argument.sh
48IyEvYmluL3NoIC0KZWNobyAiSGVsbG8gd29ybGQiCg==
49====
50"
51rm -f shebang_argument.sh
52
53# Test case
54testing "shebang with leading whitespace and argument" \
55 "uudecode; sh -c './shebang_leading_space_argument.sh'; echo \$?" \
56 "Hello world
570
58" \
59"" "\
60begin-base64 755 shebang_leading_space_argument.sh
61IyEvYmluL3NoICAtCmVjaG8gIkhlbGxvIHdvcmxkIgo=
62====
63"
64rm -f shebang_leading_space_argument.sh
65
66# Test case
67testing "shebang with argument and trailing whitespace" \
68 "uudecode; sh -c './shebang_argument_trailing_space.sh'; echo \$?" \
69 "Hello world
700
71" \
72"" "\
73begin-base64 755 shebang_argument_trailing_space.sh
74IyEvYmluL3NoIC0gCmVjaG8gIkhlbGxvIHdvcmxkIgo=
75====
76"
77rm -f shebang_argument_trailing_space.sh
78
79# Test case
80testing "shebang with leading whitespace, argument and trailing whitespace" \
81 "uudecode; sh -c './shebang_leading_argument_trailing_space.sh'; echo \$?" \
82 "Hello world
830
84" \
85"" "\
86begin-base64 755 shebang_leading_argument_trailing_space.sh
87IyEvYmluL3NoICAtIAplY2hvICJIZWxsbyB3b3JsZCIK
88====
89"
90rm -f shebang_leading_argument_trailing_space.sh
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..3f80b53f1
--- /dev/null
+++ b/win32/mingw.c
@@ -0,0 +1,1742 @@
1#include "libbb.h"
2#include <userenv.h>
3#include "lazyload.h"
4#if ENABLE_FEATURE_EXTRA_FILE_DATA
5#include <aclapi.h>
6#endif
7#if ENABLE_FEATURE_READLINK2
8#include <ntdef.h>
9#endif
10
11#if defined(__MINGW64_VERSION_MAJOR)
12#if ENABLE_GLOBBING
13int _dowildcard = -1;
14#else
15int _dowildcard = 0;
16#endif
17
18#undef _fmode
19int _fmode = _O_BINARY;
20#endif
21
22#if !defined(__MINGW64_VERSION_MAJOR)
23#if ENABLE_GLOBBING
24int _CRT_glob = 1;
25#else
26int _CRT_glob = 0;
27#endif
28
29unsigned int _CRT_fmode = _O_BINARY;
30#endif
31
32smallint bb_got_signal;
33
34int err_win_to_posix(void)
35{
36 int error = ENOSYS;
37 switch(GetLastError()) {
38 case ERROR_ACCESS_DENIED: error = EACCES; break;
39 case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
40 case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
41 case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
42 case ERROR_ALREADY_EXISTS: error = EEXIST; break;
43 case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
44 case ERROR_BAD_COMMAND: error = EIO; break;
45 case ERROR_BAD_DEVICE: error = ENODEV; break;
46 case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
47 case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
48 case ERROR_BAD_FORMAT: error = ENOEXEC; break;
49 case ERROR_BAD_LENGTH: error = EINVAL; break;
50 case ERROR_BAD_PATHNAME: error = ENOENT; break;
51 case ERROR_BAD_PIPE: error = EPIPE; break;
52 case ERROR_BAD_UNIT: error = ENODEV; break;
53 case ERROR_BAD_USERNAME: error = EINVAL; break;
54 case ERROR_BROKEN_PIPE: error = EPIPE; break;
55 case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
56 case ERROR_BUSY: error = EBUSY; break;
57 case ERROR_BUSY_DRIVE: error = EBUSY; break;
58 case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
59 case ERROR_CANNOT_MAKE: error = EACCES; break;
60 case ERROR_CANTOPEN: error = EIO; break;
61 case ERROR_CANTREAD: error = EIO; break;
62 case ERROR_CANTWRITE: error = EIO; break;
63 case ERROR_CRC: error = EIO; break;
64 case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
65 case ERROR_DEVICE_IN_USE: error = EBUSY; break;
66 case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
67 case ERROR_DIRECTORY: error = EINVAL; break;
68 case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
69 case ERROR_DISK_CHANGE: error = EIO; break;
70 case ERROR_DISK_FULL: error = ENOSPC; break;
71 case ERROR_DRIVE_LOCKED: error = EBUSY; break;
72 case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
73 case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
74 case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
75 case ERROR_FILE_EXISTS: error = EEXIST; break;
76 case ERROR_FILE_INVALID: error = ENODEV; break;
77 case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
78 case ERROR_GEN_FAILURE: error = EIO; break;
79 case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
80 case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
81 case ERROR_INVALID_ACCESS: error = EACCES; break;
82 case ERROR_INVALID_ADDRESS: error = EFAULT; break;
83 case ERROR_INVALID_BLOCK: error = EFAULT; break;
84 case ERROR_INVALID_DATA: error = EINVAL; break;
85 case ERROR_INVALID_DRIVE: error = ENODEV; break;
86 case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
87 case ERROR_INVALID_FLAGS: error = EINVAL; break;
88 case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
89 case ERROR_INVALID_HANDLE: error = EBADF; break;
90 case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
91 case ERROR_INVALID_NAME: error = EINVAL; break;
92 case ERROR_INVALID_OWNER: error = EINVAL; break;
93 case ERROR_INVALID_PARAMETER: error = EINVAL; break;
94 case ERROR_INVALID_PASSWORD: error = EPERM; break;
95 case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
96 case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
97 case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
98 case ERROR_INVALID_WORKSTATION: error = EACCES; break;
99 case ERROR_IO_DEVICE: error = EIO; break;
100 case ERROR_IO_INCOMPLETE: error = EINTR; break;
101 case ERROR_LOCKED: error = EBUSY; break;
102 case ERROR_LOCK_VIOLATION: error = EACCES; break;
103 case ERROR_LOGON_FAILURE: error = EACCES; break;
104 case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
105 case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
106 case ERROR_MORE_DATA: error = EPIPE; break;
107 case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
108 case ERROR_NOACCESS: error = EFAULT; break;
109 case ERROR_NONE_MAPPED: error = EINVAL; break;
110 case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
111 case ERROR_NOT_READY: error = EAGAIN; break;
112 case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
113 case ERROR_NO_DATA: error = EPIPE; break;
114 case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
115 case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
116 case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
117 case ERROR_OPEN_FAILED: error = EIO; break;
118 case ERROR_OPEN_FILES: error = EBUSY; break;
119 case ERROR_OPERATION_ABORTED: error = EINTR; break;
120 case ERROR_OUTOFMEMORY: error = ENOMEM; break;
121 case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
122 case ERROR_PATH_BUSY: error = EBUSY; break;
123 case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
124 case ERROR_PIPE_BUSY: error = EBUSY; break;
125 case ERROR_PIPE_CONNECTED: error = EPIPE; break;
126 case ERROR_PIPE_LISTENING: error = EPIPE; break;
127 case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
128 case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
129 case ERROR_READ_FAULT: error = EIO; break;
130 case ERROR_SEEK: error = EIO; break;
131 case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
132 case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
133 case ERROR_SHARING_VIOLATION: error = EACCES; break;
134 case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
135 case ERROR_SWAPERROR: error = ENOENT; break;
136 case ERROR_TOO_MANY_LINKS: error = EMLINK; break;
137 case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
138 case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
139 case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
140 case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
141 case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
142 case ERROR_WRITE_FAULT: error = EIO; break;
143 case ERROR_WRITE_PROTECT: error = EROFS; break;
144 case ERROR_CANT_RESOLVE_FILENAME: error = ELOOP; break;
145 }
146 return error;
147}
148
149static int zero_fd = -1;
150static int rand_fd = -1;
151
152/*
153 * Determine if 'filename' corresponds to one of the supported
154 * device files. Constants for these are defined as an enum
155 * in mingw.h.
156 */
157int get_dev_type(const char *filename)
158{
159 int i;
160 const char *devname[NOT_DEVICE] = { "null", "zero", "urandom" };
161
162 if (filename && !strncmp(filename, "/dev/", 5)) {
163 for (i=0; i<NOT_DEVICE; ++i ) {
164 if (!strcmp(filename+5, devname[i])) {
165 return i;
166 }
167 }
168 }
169
170 return NOT_DEVICE;
171}
172
173void update_dev_fd(int dev, int fd)
174{
175 if (dev == DEV_ZERO)
176 zero_fd = fd;
177 else if (dev == DEV_URANDOM)
178 rand_fd = fd;
179}
180
181#undef open
182int mingw_open (const char *filename, int oflags, ...)
183{
184 va_list args;
185 unsigned mode;
186 int fd;
187 int special = (oflags & O_SPECIAL);
188 int dev = get_dev_type(filename);
189
190 /* /dev/null is always allowed, others only if O_SPECIAL is set */
191 if (dev != NOT_DEVICE && (dev == DEV_NULL || special)) {
192 filename = "nul";
193 oflags = O_RDWR;
194 }
195
196 va_start(args, oflags);
197 mode = va_arg(args, int);
198 va_end(args);
199
200 fd = open(filename, oflags&~O_SPECIAL, mode);
201 if (fd >= 0) {
202 update_dev_fd(dev, fd);
203 }
204 else if ((oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
205 DWORD attrs = GetFileAttributes(filename);
206 if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
207 errno = EISDIR;
208 }
209 return fd;
210}
211
212int mingw_xopen(const char *pathname, int flags)
213{
214 int ret;
215
216 /* allow use of special devices */
217 ret = mingw_open(pathname, flags|O_SPECIAL);
218 if (ret < 0) {
219 bb_perror_msg_and_die("can't open '%s'", pathname);
220 }
221 return ret;
222}
223
224#undef fopen
225FILE *mingw_fopen (const char *filename, const char *otype)
226{
227 if (filename && !strcmp(filename, "/dev/null"))
228 filename = "nul";
229 return fopen(filename, otype);
230}
231
232#undef read
233ssize_t mingw_read(int fd, void *buf, size_t count)
234{
235 if (fd == zero_fd) {
236 memset(buf, 0, count);
237 return count;
238 }
239 else if (fd == rand_fd) {
240 return get_random_bytes(buf, count);
241 }
242 return read(fd, buf, count);
243}
244
245#undef close
246int mingw_close(int fd)
247{
248 if (fd == zero_fd) {
249 zero_fd = -1;
250 }
251 if (fd == rand_fd) {
252 rand_fd = -1;
253 }
254 return close(fd);
255}
256
257#undef dup2
258int mingw_dup2 (int fd, int fdto)
259{
260 int ret = dup2(fd, fdto);
261 return ret != -1 ? fdto : -1;
262}
263
264/*
265 * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
266 * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
267 */
268static inline long long filetime_to_hnsec(const FILETIME *ft)
269{
270 long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
271 /* Windows to Unix Epoch conversion */
272 return winTime - 116444736000000000LL;
273}
274
275static inline time_t filetime_to_time_t(const FILETIME *ft)
276{
277 return (time_t)(filetime_to_hnsec(ft) / 10000000);
278}
279
280static inline mode_t file_attr_to_st_mode(DWORD attr)
281{
282 mode_t fMode = S_IRUSR|S_IRGRP|S_IROTH;
283 if (attr & FILE_ATTRIBUTE_DIRECTORY)
284 fMode |= S_IFDIR|S_IWUSR|S_IWGRP|S_IXUSR|S_IXGRP|S_IXOTH;
285 else
286 fMode |= S_IFREG;
287 if (!(attr & FILE_ATTRIBUTE_READONLY))
288 fMode |= S_IWUSR|S_IWGRP;
289 return fMode;
290}
291
292static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
293{
294 size_t len;
295
296 if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata))
297 return 0;
298
299 if (GetLastError() == ERROR_SHARING_VIOLATION) {
300 HANDLE hnd;
301 WIN32_FIND_DATA fd;
302
303 if ((hnd=FindFirstFile(fname, &fd)) != INVALID_HANDLE_VALUE) {
304 fdata->dwFileAttributes = fd.dwFileAttributes;
305 fdata->ftCreationTime = fd.ftCreationTime;
306 fdata->ftLastAccessTime = fd.ftLastAccessTime;
307 fdata->ftLastWriteTime = fd.ftLastWriteTime;
308 fdata->nFileSizeHigh = fd.nFileSizeHigh;
309 fdata->nFileSizeLow = fd.nFileSizeLow;
310 FindClose(hnd);
311 return 0;
312 }
313 }
314
315 switch (GetLastError()) {
316 case ERROR_ACCESS_DENIED:
317 case ERROR_SHARING_VIOLATION:
318 case ERROR_LOCK_VIOLATION:
319 case ERROR_SHARING_BUFFER_EXCEEDED:
320 return EACCES;
321 case ERROR_BUFFER_OVERFLOW:
322 return ENAMETOOLONG;
323 case ERROR_NOT_ENOUGH_MEMORY:
324 return ENOMEM;
325 case ERROR_INVALID_NAME:
326 len = strlen(fname);
327 if (len > 1 && (fname[len-1] == '/' || fname[len-1] == '\\'))
328 return ENOTDIR;
329 default:
330 return ENOENT;
331 }
332}
333
334#undef umask
335mode_t mingw_umask(mode_t new_mode)
336{
337 static mode_t old_mode = DEFAULT_UMASK;
338 mode_t tmp_mode;
339
340 tmp_mode = old_mode;
341 old_mode = new_mode;
342
343 umask((new_mode & S_IWUSR) ? _S_IWRITE : 0);
344
345 return tmp_mode;
346}
347
348/*
349 * Examine a file's contents to determine if it can be executed. This
350 * should be a last resort: in most cases it's much more efficient to
351 * check the file extension.
352 *
353 * We look for two types of file: shell scripts and binary executables.
354 */
355static int has_exec_format(const char *name)
356{
357 int n, sig;
358 unsigned int offset;
359 unsigned char buf[1024];
360
361 /* special case: skip DLLs, there are thousands of them! */
362 if (is_suffixed_with_case(name, ".dll"))
363 return 0;
364
365 n = open_read_close(name, buf, sizeof(buf));
366 if (n < 4) /* at least '#!/x' and not error */
367 return 0;
368
369 /* shell script */
370 if (buf[0] == '#' && buf[1] == '!') {
371 return 1;
372 }
373
374 /*
375 * Poke about in file to see if it's a PE binary. I've just copied
376 * the magic from the file command.
377 */
378 if (buf[0] == 'M' && buf[1] == 'Z') {
379 offset = (buf[0x19] << 8) + buf[0x18];
380 if (offset > 0x3f) {
381 offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) +
382 (buf[0x3d] << 8) + buf[0x3c];
383 if (offset < sizeof(buf)-100) {
384 if (memcmp(buf+offset, "PE\0\0", 4) == 0) {
385 sig = (buf[offset+25] << 8) + buf[offset+24];
386 if (sig == 0x10b || sig == 0x20b) {
387 sig = (buf[offset+23] << 8) + buf[offset+22];
388 if ((sig & 0x2000) != 0) {
389 /* DLL */
390 return 0;
391 }
392 sig = buf[offset+92];
393 return (sig == 1 || sig == 2 || sig == 3 || sig == 7);
394 }
395 }
396 }
397 }
398 }
399
400 return 0;
401}
402
403#if ENABLE_FEATURE_EXTRA_FILE_DATA
404static uid_t file_owner(HANDLE fh)
405{
406 PSID pSidOwner;
407 PSECURITY_DESCRIPTOR pSD;
408 static PTOKEN_USER user = NULL;
409 static int initialised = 0;
410 int equal;
411 uid_t uid = 0;
412 DWORD *ptr;
413 unsigned char prefix[] = {
414 0x01, 0x05, 0x00, 0x00,
415 0x00, 0x00, 0x00, 0x05,
416 0x15, 0x00, 0x00, 0x00
417 };
418
419 /* get SID of current user */
420 if (!initialised) {
421 HANDLE token;
422 DWORD ret = 0;
423
424 initialised = 1;
425 if (OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &token) ||
426 OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
427 GetTokenInformation(token, TokenUser, NULL, 0, &ret);
428 if (ret <= 0 || (user=malloc(ret)) == NULL ||
429 !GetTokenInformation(token, TokenUser, user, ret, &ret)) {
430 free(user);
431 user = NULL;
432 }
433 CloseHandle(token);
434 }
435 }
436
437 if (user == NULL)
438 return DEFAULT_UID;
439
440 /* get SID of file's owner */
441 if (GetSecurityInfo(fh, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION,
442 &pSidOwner, NULL, NULL, NULL, &pSD) != ERROR_SUCCESS)
443 return 0;
444
445 equal = EqualSid(pSidOwner, user->User.Sid);
446 LocalFree(pSD);
447
448 if (equal)
449 return DEFAULT_UID;
450
451 /* for local or domain users use the RID as uid */
452 if (memcmp(pSidOwner, prefix, sizeof(prefix)) == 0) {
453 ptr = (DWORD *)pSidOwner;
454 if (ptr[6] >= 500 && ptr[6] < DEFAULT_UID)
455 uid = (uid_t)ptr[6];
456 }
457 return uid;
458
459#if 0
460 /* this is how it would be done properly using the API */
461 {
462 PSID_IDENTIFIER_AUTHORITY auth;
463 unsigned char *count;
464 PDWORD subauth;
465 unsigned char nt_auth[] = {
466 0x00, 0x00, 0x00, 0x00, 0x00, 0x05
467 };
468
469 if (IsValidSid(pSidOwner) ) {
470 auth = GetSidIdentifierAuthority(pSidOwner);
471 count = GetSidSubAuthorityCount(pSidOwner);
472 subauth = GetSidSubAuthority(pSidOwner, 0);
473 if (memcmp(auth, nt_auth, sizeof(nt_auth)) == 0 &&
474 *count == 5 && *subauth == 21) {
475 subauth = GetSidSubAuthority(pSidOwner, 4);
476 if (*subauth >= 500 && *subauth < DEFAULT_UID)
477 uid = (uid_t)*subauth;
478 }
479 }
480 return uid;
481 }
482#endif
483}
484#endif
485
486static int is_symlink(DWORD attr, const char *pathname, WIN32_FIND_DATAA *fbuf)
487{
488 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
489 HANDLE handle = FindFirstFileA(pathname, fbuf);
490 if (handle != INVALID_HANDLE_VALUE) {
491 FindClose(handle);
492 return ((fbuf->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
493 fbuf->dwReserved0 == IO_REPARSE_TAG_SYMLINK);
494 }
495 }
496 return 0;
497}
498
499/* If follow is true then act like stat() and report on the link
500 * target. Otherwise report on the link itself.
501 */
502static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf)
503{
504 int err = EINVAL;
505 WIN32_FILE_ATTRIBUTE_DATA fdata;
506 WIN32_FIND_DATAA findbuf;
507#if ENABLE_FEATURE_EXTRA_FILE_DATA
508 DWORD flags;
509 BY_HANDLE_FILE_INFORMATION hdata;
510 HANDLE fh;
511#endif
512
513 while (file_name && !(err=get_file_attr(file_name, &fdata))) {
514 buf->st_ino = 0;
515 buf->st_uid = DEFAULT_UID;
516 buf->st_gid = DEFAULT_GID;
517 buf->st_dev = buf->st_rdev = 0;
518
519 if (is_symlink(fdata.dwFileAttributes, file_name, &findbuf)) {
520 char *name = auto_string(xmalloc_realpath(file_name));
521
522 if (follow) {
523 /* The file size and times are wrong when Windows follows
524 * a symlink. Use the canonicalized path to try again. */
525 err = errno;
526 file_name = name;
527 continue;
528 }
529
530 /* Get the contents of a symlink, not its target. */
531 buf->st_mode = S_IFLNK|S_IRWXU|S_IRWXG|S_IRWXO;
532 buf->st_size = name ? strlen(name) : 0; /* should use readlink */
533 buf->st_atime = filetime_to_time_t(&(findbuf.ftLastAccessTime));
534 buf->st_mtime = filetime_to_time_t(&(findbuf.ftLastWriteTime));
535 buf->st_ctime = filetime_to_time_t(&(findbuf.ftCreationTime));
536 }
537 else {
538 /* The file is not a symlink. */
539 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
540 if (S_ISREG(buf->st_mode) &&
541 (has_exe_suffix(file_name) || has_exec_format(file_name)))
542 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH;
543 buf->st_size = fdata.nFileSizeLow |
544 (((off64_t)fdata.nFileSizeHigh)<<32);
545 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
546 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
547 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
548 }
549 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1;
550
551#if ENABLE_FEATURE_EXTRA_FILE_DATA
552 flags = FILE_FLAG_BACKUP_SEMANTICS;
553 if (S_ISLNK(buf->st_mode))
554 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
555 fh = CreateFile(file_name, READ_CONTROL, 0, NULL,
556 OPEN_EXISTING, flags, NULL);
557 if (fh != INVALID_HANDLE_VALUE) {
558 if (GetFileInformationByHandle(fh, &hdata)) {
559 buf->st_dev = hdata.dwVolumeSerialNumber;
560 buf->st_ino = hdata.nFileIndexLow |
561 (((ino_t)hdata.nFileIndexHigh)<<32);
562 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 :
563 hdata.nNumberOfLinks;
564 }
565 buf->st_uid = buf->st_gid = file_owner(fh);
566 CloseHandle(fh);
567 }
568 else {
569 buf->st_uid = 0;
570 buf->st_gid = 0;
571 buf->st_mode &= ~(S_IROTH|S_IWOTH|S_IXOTH);
572 }
573#endif
574
575 /*
576 * Assume a block is 4096 bytes and calculate number of 512 byte
577 * sectors.
578 */
579 buf->st_blksize = 4096;
580 buf->st_blocks = ((buf->st_size+4095)>>12)<<3;
581 return 0;
582 }
583 errno = err;
584 return -1;
585}
586
587int mingw_lstat(const char *file_name, struct mingw_stat *buf)
588{
589 return do_lstat(0, file_name, buf);
590}
591
592int mingw_stat(const char *file_name, struct mingw_stat *buf)
593{
594 return do_lstat(1, file_name, buf);
595}
596
597int mingw_fstat(int fd, struct mingw_stat *buf)
598{
599 HANDLE fh = (HANDLE)_get_osfhandle(fd);
600 BY_HANDLE_FILE_INFORMATION fdata;
601
602 if (fh == INVALID_HANDLE_VALUE) {
603 errno = EBADF;
604 return -1;
605 }
606 /* direct non-file handles to MS's fstat() */
607 if (GetFileType(fh) != FILE_TYPE_DISK) {
608 struct _stati64 buf64;
609
610 if ( _fstati64(fd, &buf64) != 0 ) {
611 return -1;
612 }
613 buf->st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
614 buf->st_size = buf64.st_size;
615 buf->st_atime = buf64.st_atime;
616 buf->st_mtime = buf64.st_mtime;
617 buf->st_ctime = buf64.st_ctime;
618 buf->st_blocks = ((buf64.st_size+4095)>>12)<<3;
619#if ENABLE_FEATURE_EXTRA_FILE_DATA
620 buf->st_dev = 0;
621 buf->st_ino = 0;
622 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1;
623#endif
624 goto success;
625 }
626
627 if (GetFileInformationByHandle(fh, &fdata)) {
628 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
629 buf->st_size = fdata.nFileSizeLow |
630 (((off64_t)fdata.nFileSizeHigh)<<32);
631 buf->st_atime = filetime_to_time_t(&(fdata.ftLastAccessTime));
632 buf->st_mtime = filetime_to_time_t(&(fdata.ftLastWriteTime));
633 buf->st_ctime = filetime_to_time_t(&(fdata.ftCreationTime));
634 buf->st_blocks = ((buf->st_size+4095)>>12)<<3;
635#if ENABLE_FEATURE_EXTRA_FILE_DATA
636 buf->st_dev = fdata.dwVolumeSerialNumber;
637 buf->st_ino = fdata.nFileIndexLow |
638 (((uint64_t)fdata.nFileIndexHigh)<<32);
639 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : fdata.nNumberOfLinks;
640#endif
641 success:
642#if !ENABLE_FEATURE_EXTRA_FILE_DATA
643 buf->st_dev = 0;
644 buf->st_ino = 0;
645 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1;
646#endif
647 buf->st_rdev = 0;
648 buf->st_uid = DEFAULT_UID;
649 buf->st_gid = DEFAULT_GID;
650 buf->st_blksize = 4096;
651 return 0;
652 }
653
654 errno = EBADF;
655 return -1;
656}
657
658static inline void timeval_to_filetime(const struct timeval tv, FILETIME *ft)
659{
660 long long winTime = ((tv.tv_sec * 1000000LL) + tv.tv_usec) * 10LL + 116444736000000000LL;
661 ft->dwLowDateTime = winTime;
662 ft->dwHighDateTime = winTime >> 32;
663}
664
665int utimes(const char *file_name, const struct timeval tims[2])
666{
667 FILETIME mft, aft;
668 HANDLE fh;
669 int rc = 0;
670
671 fh = CreateFile(file_name, FILE_WRITE_ATTRIBUTES, 0,
672 NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
673 if ( fh == INVALID_HANDLE_VALUE ) {
674 errno = err_win_to_posix();
675 return -1;
676 }
677
678 if (tims) {
679 timeval_to_filetime(tims[0], &aft);
680 timeval_to_filetime(tims[1], &mft);
681 }
682 else {
683 GetSystemTimeAsFileTime(&mft);
684 aft = mft;
685 }
686
687 if (!SetFileTime(fh, NULL, &aft, &mft)) {
688 errno = EINVAL;
689 rc = -1;
690 }
691 CloseHandle(fh);
692 return rc;
693}
694
695unsigned int sleep (unsigned int seconds)
696{
697 Sleep(seconds*1000);
698 return 0;
699}
700
701int nanosleep(const struct timespec *req, struct timespec *rem)
702{
703 if (req->tv_nsec < 0 || 1000000000 <= req->tv_nsec) {
704 errno = EINVAL;
705 return -1;
706 }
707
708 Sleep(req->tv_sec*1000 + req->tv_nsec/1000000);
709
710 /* Sleep is not interruptible. So there is no remaining delay. */
711 if (rem != NULL) {
712 rem->tv_sec = 0;
713 rem->tv_nsec = 0;
714 }
715
716 return 0;
717}
718
719/*
720 * Windows' mktemp returns NULL on error whereas POSIX always returns the
721 * template and signals an error by making it an empty string.
722 */
723#undef mktemp
724char *mingw_mktemp(char *template)
725{
726 if ( mktemp(template) == NULL ) {
727 template[0] = '\0';
728 }
729
730 return template;
731}
732
733int mkstemp(char *template)
734{
735 char *filename = mktemp(template);
736 if (filename == NULL)
737 return -1;
738 return open(filename, O_RDWR | O_CREAT, 0600);
739}
740
741int gettimeofday(struct timeval *tv, void *tz UNUSED_PARAM)
742{
743 FILETIME ft;
744 long long hnsec;
745
746 GetSystemTimeAsFileTime(&ft);
747 hnsec = filetime_to_hnsec(&ft);
748 tv->tv_sec = hnsec / 10000000;
749 tv->tv_usec = (hnsec % 10000000) / 10;
750 return 0;
751}
752
753int pipe(int filedes[2])
754{
755 if (_pipe(filedes, PIPE_BUF, 0) < 0)
756 return -1;
757 return 0;
758}
759
760struct tm *gmtime_r(const time_t *timep, struct tm *result)
761{
762 /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
763 memcpy(result, gmtime(timep), sizeof(struct tm));
764 return result;
765}
766
767struct tm *localtime_r(const time_t *timep, struct tm *result)
768{
769 /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
770 memcpy(result, localtime(timep), sizeof(struct tm));
771 return result;
772}
773
774#undef getcwd
775char *mingw_getcwd(char *pointer, int len)
776{
777 char *ret = getcwd(pointer, len);
778 if (!ret)
779 return ret;
780 bs_to_slash(ret);
781 return ret;
782}
783
784#undef rename
785int mingw_rename(const char *pold, const char *pnew)
786{
787 DWORD attrs;
788
789 /*
790 * Try native rename() first to get errno right.
791 * It is based on MoveFile(), which cannot overwrite existing files.
792 */
793 if (!rename(pold, pnew))
794 return 0;
795 if (errno != EEXIST)
796 return -1;
797 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
798 return 0;
799 /* TODO: translate more errors */
800 if (GetLastError() == ERROR_ACCESS_DENIED &&
801 (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
802 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
803 errno = EISDIR;
804 return -1;
805 }
806 if ((attrs & FILE_ATTRIBUTE_READONLY) &&
807 SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
808 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
809 return 0;
810 /* revert file attributes on failure */
811 SetFileAttributes(pnew, attrs);
812 }
813 }
814 errno = EACCES;
815 return -1;
816}
817
818static char *gethomedir(void)
819{
820 static char *buf = NULL;
821 DWORD len = PATH_MAX;
822 HANDLE h;
823
824 if (!buf)
825 buf = xzalloc(PATH_MAX);
826
827 if (buf[0])
828 return buf;
829
830 if ( !OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h) )
831 return buf;
832
833 GetUserProfileDirectory(h, buf, &len);
834 CloseHandle(h);
835 bs_to_slash(buf);
836
837 return buf;
838}
839
840#define NAME_LEN 100
841static char *get_user_name(void)
842{
843 static char *user_name = NULL;
844 char *s;
845 DWORD len = NAME_LEN;
846
847 if ( user_name == NULL ) {
848 user_name = xzalloc(NAME_LEN);
849 }
850
851 if ( user_name[0] != '\0' ) {
852 return user_name;
853 }
854
855 if ( !GetUserName(user_name, &len) ) {
856 return NULL;
857 }
858
859 for ( s=user_name; *s; ++s ) {
860 if ( *s == ' ' ) {
861 *s = '_';
862 }
863 }
864
865 return user_name;
866}
867
868int getuid(void)
869{
870 int ret = DEFAULT_UID;
871 HANDLE h;
872
873 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h)) {
874 TOKEN_ELEVATION elevation;
875 DWORD size = sizeof(TOKEN_ELEVATION);
876
877 if (GetTokenInformation(h, TokenElevation, &elevation,
878 sizeof(elevation), &size)) {
879 if (elevation.TokenIsElevated)
880 ret = 0;
881 }
882 CloseHandle(h);
883 }
884 return ret;
885}
886
887struct passwd *getpwnam(const char *name)
888{
889 const char *myname;
890
891 if ( (myname=get_user_name()) != NULL &&
892 strcmp(myname, name) == 0 ) {
893 return getpwuid(DEFAULT_UID);
894 }
895 else if (strcmp(name, "root") == 0) {
896 return getpwuid(0);
897 }
898
899 return NULL;
900}
901
902struct passwd *getpwuid(uid_t uid)
903{
904 static struct passwd p;
905
906 if (uid == 0) {
907 static char *buf = NULL;
908 char dir[PATH_MAX];
909
910 if (!buf) {
911 buf = xzalloc(PATH_MAX);
912 GetSystemDirectory(dir, PATH_MAX);
913 realpath(dir, buf);
914 }
915
916 p.pw_name = (char *)"root";
917 p.pw_dir = buf;
918 }
919 else if (uid == DEFAULT_UID && (p.pw_name=get_user_name()) != NULL) {
920 p.pw_dir = gethomedir();
921 }
922 else {
923 return NULL;
924 }
925
926 p.pw_passwd = (char *)"";
927 p.pw_gecos = p.pw_name;
928 p.pw_shell = NULL;
929 p.pw_uid = uid;
930 p.pw_gid = uid;
931
932 return &p;
933}
934
935struct group *getgrgid(gid_t gid)
936{
937 static char *members[2] = { NULL, NULL };
938 static struct group g;
939
940 if (gid == 0) {
941 g.gr_name = (char *)"root";
942 }
943 else if (gid != DEFAULT_GID || (g.gr_name=get_user_name()) == NULL) {
944 return NULL;
945 }
946 g.gr_passwd = (char *)"";
947 g.gr_gid = gid;
948 members[0] = g.gr_name;
949 g.gr_mem = members;
950
951 return &g;
952}
953
954int getgrouplist(const char *user UNUSED_PARAM, gid_t group,
955 gid_t *groups, int *ngroups)
956{
957 if ( *ngroups == 0 ) {
958 *ngroups = 1;
959 return -1;
960 }
961
962 *ngroups = 1;
963 groups[0] = group;
964 return 1;
965}
966
967int getgroups(int n, gid_t *groups)
968{
969 if ( n == 0 ) {
970 return 1;
971 }
972
973 groups[0] = getgid();
974 return 1;
975}
976
977int getlogin_r(char *buf, size_t len)
978{
979 char *name;
980
981 if ( (name=get_user_name()) == NULL ) {
982 return -1;
983 }
984
985 if ( strlen(name) >= len ) {
986 errno = ERANGE;
987 return -1;
988 }
989
990 strcpy(buf, name);
991 return 0;
992}
993
994long sysconf(int name)
995{
996 if ( name == _SC_CLK_TCK ) {
997 return TICKS_PER_SECOND;
998 }
999 errno = EINVAL;
1000 return -1;
1001}
1002
1003clock_t times(struct tms *buf)
1004{
1005 memset(buf, 0, sizeof(*buf));
1006 return 0;
1007}
1008
1009int link(const char *oldpath, const char *newpath)
1010{
1011 DECLARE_PROC_ADDR(BOOL, CreateHardLinkA, LPCSTR, LPCSTR,
1012 LPSECURITY_ATTRIBUTES);
1013
1014 if (!INIT_PROC_ADDR(kernel32.dll, CreateHardLinkA)) {
1015 errno = ENOSYS;
1016 return -1;
1017 }
1018 if (!CreateHardLinkA(newpath, oldpath, NULL)) {
1019 errno = err_win_to_posix();
1020 return -1;
1021 }
1022 return 0;
1023}
1024
1025static char *normalize_ntpathA(char *buf)
1026{
1027 /* fix absolute path prefixes */
1028 if (buf[0] == '\\') {
1029 /* strip NT namespace prefixes */
1030 if (!strncmp(buf, "\\??\\", 4) ||
1031 !strncmp(buf, "\\\\?\\", 4))
1032 buf += 4;
1033 else if (!strncasecmp(buf, "\\DosDevices\\", 12))
1034 buf += 12;
1035 /* replace remaining '...UNC\' with '\\' */
1036 if (!strncasecmp(buf, "UNC\\", 4)) {
1037 buf += 2;
1038 *buf = '\\';
1039 }
1040 }
1041 return buf;
1042}
1043
1044static char *resolve_symlinks(char *path)
1045{
1046 HANDLE h;
1047 DWORD status;
1048 char *ptr = NULL;
1049 DECLARE_PROC_ADDR(DWORD, GetFinalPathNameByHandleA, HANDLE,
1050 LPSTR, DWORD, DWORD);
1051
1052 /* need a file handle to resolve symlinks */
1053 h = CreateFileA(path, 0, 0, NULL, OPEN_EXISTING,
1054 FILE_FLAG_BACKUP_SEMANTICS, NULL);
1055 if (h != INVALID_HANDLE_VALUE) {
1056 if (!INIT_PROC_ADDR(kernel32.dll, GetFinalPathNameByHandleA)) {
1057 ptr = path;
1058 goto end;
1059 }
1060
1061 /* normalize the path and return it on success */
1062 status = GetFinalPathNameByHandleA(h, path, MAX_PATH,
1063 FILE_NAME_NORMALIZED|VOLUME_NAME_DOS);
1064 if (status != 0 && status < MAX_PATH) {
1065 ptr = normalize_ntpathA(path);
1066 goto end;
1067 }
1068 }
1069
1070 errno = err_win_to_posix();
1071 end:
1072 CloseHandle(h);
1073 return ptr;
1074}
1075
1076/*
1077 * Emulate realpath in two stages:
1078 *
1079 * - _fullpath handles './', '../' and extra '/' characters. The
1080 * resulting path may not refer to an actual file.
1081 *
1082 * - resolve_symlinks checks that the file exists (by opening it) and
1083 * resolves symlinks by calling GetFinalPathNameByHandleA.
1084 */
1085char *realpath(const char *path, char *resolved_path)
1086{
1087 char buffer[MAX_PATH];
1088 char *real_path, *p;
1089
1090 /* enforce glibc pre-2.3 behaviour */
1091 if (path == NULL || resolved_path == NULL) {
1092 errno = EINVAL;
1093 return NULL;
1094 }
1095
1096 if (_fullpath(buffer, path, MAX_PATH) &&
1097 (real_path=resolve_symlinks(buffer))) {
1098 strcpy(resolved_path, real_path);
1099 bs_to_slash(resolved_path);
1100 p = last_char_is(resolved_path, '/');
1101 if (p && p > resolved_path && p[-1] != ':')
1102 *p = '\0';
1103 return resolved_path;
1104 }
1105 return NULL;
1106}
1107
1108#if ENABLE_FEATURE_READLINK2
1109static wchar_t *normalize_ntpath(wchar_t *wbuf)
1110{
1111 int i;
1112 /* fix absolute path prefixes */
1113 if (wbuf[0] == '\\') {
1114 /* strip NT namespace prefixes */
1115 if (!wcsncmp(wbuf, L"\\??\\", 4) ||
1116 !wcsncmp(wbuf, L"\\\\?\\", 4))
1117 wbuf += 4;
1118 else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
1119 wbuf += 12;
1120 /* replace remaining '...UNC\' with '\\' */
1121 if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
1122 wbuf += 2;
1123 *wbuf = '\\';
1124 }
1125 }
1126 /* convert backslashes to slashes */
1127 for (i = 0; wbuf[i]; i++)
1128 if (wbuf[i] == '\\')
1129 wbuf[i] = '/';
1130 return wbuf;
1131}
1132
1133#define SRPB rptr->SymbolicLinkReparseBuffer
1134ssize_t readlink(const char *pathname, char *buf, size_t bufsiz)
1135{
1136 HANDLE h;
1137
1138 h = CreateFile(pathname, 0, 0, NULL, OPEN_EXISTING,
1139 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
1140 if (h != INVALID_HANDLE_VALUE) {
1141 DWORD nbytes;
1142 BYTE rbuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1143 PREPARSE_DATA_BUFFER rptr = (PREPARSE_DATA_BUFFER)rbuf;
1144 BOOL status;
1145
1146 status = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0,
1147 rptr, sizeof(rbuf), &nbytes, NULL);
1148 CloseHandle(h);
1149
1150 if (status && rptr->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
1151 size_t len = SRPB.SubstituteNameLength/sizeof(WCHAR);
1152 WCHAR *name = SRPB.PathBuffer +
1153 SRPB.SubstituteNameOffset/sizeof(WCHAR);
1154
1155 name[len] = 0;
1156 name = normalize_ntpath(name);
1157 len = wcslen(name);
1158 if (len > bufsiz)
1159 len = bufsiz;
1160 if (WideCharToMultiByte(CP_ACP, 0, name, len, buf, bufsiz, 0, 0)) {
1161 return len;
1162 }
1163 }
1164 }
1165 errno = err_win_to_posix();
1166 return -1;
1167}
1168#endif
1169
1170const char *get_busybox_exec_path(void)
1171{
1172 static char *path = NULL;
1173
1174 if (!path) {
1175 path = xzalloc(PATH_MAX);
1176 }
1177
1178 if (!*path) {
1179 GetModuleFileName(NULL, path, PATH_MAX);
1180 bs_to_slash(path);
1181 }
1182 return path;
1183}
1184
1185#undef mkdir
1186int mingw_mkdir(const char *path, int mode UNUSED_PARAM)
1187{
1188 int ret;
1189 struct stat st;
1190 int lerrno = 0;
1191
1192 if ( (ret=mkdir(path)) < 0 ) {
1193 lerrno = errno;
1194 if ( lerrno == EACCES && stat(path, &st) == 0 ) {
1195 ret = 0;
1196 lerrno = 0;
1197 }
1198 }
1199
1200 errno = lerrno;
1201 return ret;
1202}
1203
1204#undef chdir
1205int mingw_chdir(const char *dirname)
1206{
1207 struct stat st;
1208 int ret = -1;
1209 const char *realdir = dirname;
1210
1211 if (lstat(dirname, &st) == 0 && S_ISLNK(st.st_mode)) {
1212 realdir = auto_string(xmalloc_readlink(dirname));
1213 if (realdir)
1214 fix_path_case((char *)realdir);
1215 }
1216
1217 if (realdir)
1218 ret = chdir(realdir);
1219
1220 return ret;
1221}
1222
1223#undef chmod
1224int mingw_chmod(const char *path, int mode)
1225{
1226 WIN32_FILE_ATTRIBUTE_DATA fdata;
1227
1228 if ( get_file_attr(path, &fdata) == 0 &&
1229 fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
1230 mode |= 0222;
1231 }
1232
1233 return chmod(path, mode);
1234}
1235
1236int fcntl(int fd, int cmd, ...)
1237{
1238 va_list arg;
1239 int result = -1;
1240 char *fds;
1241 int target, i, newfd;
1242
1243 va_start(arg, cmd);
1244
1245 switch (cmd) {
1246 case F_GETFD:
1247 case F_SETFD:
1248 case F_GETFL:
1249 /*
1250 * Our fake F_GETFL won't matter if the return value is used as
1251 * fcntl(fd, F_SETFL, ret|something);
1252 * because F_SETFL isn't supported either.
1253 */
1254 result = 0;
1255 break;
1256 case F_DUPFD:
1257 target = va_arg(arg, int);
1258 fds = xzalloc(target);
1259 while ((newfd = dup(fd)) < target && newfd >= 0) {
1260 fds[newfd] = 1;
1261 }
1262 for (i = 0; i < target; ++i) {
1263 if (fds[i]) {
1264 close(i);
1265 }
1266 }
1267 free(fds);
1268 result = newfd;
1269 break;
1270 default:
1271 errno = ENOSYS;
1272 break;
1273 }
1274
1275 va_end(arg);
1276 return result;
1277}
1278
1279#undef unlink
1280int mingw_unlink(const char *pathname)
1281{
1282 /* read-only files cannot be removed */
1283 chmod(pathname, 0666);
1284 return unlink(pathname);
1285}
1286
1287#undef strftime
1288size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm)
1289{
1290 size_t ret;
1291 char buffer[64];
1292 const char *replace;
1293 char *t;
1294 char *fmt;
1295 struct tm tm2;
1296
1297 /*
1298 * Emulate some formats that Windows' strftime lacks.
1299 * - '%e' day of the month with space padding
1300 * - '%s' number of seconds since the Unix epoch
1301 * - '%T' same as %H:%M:%S
1302 * - '%z' timezone offset
1303 * Also, permit the '-' modifier to omit padding. Windows uses '#'.
1304 */
1305 fmt = xstrdup(format);
1306 for ( t=fmt; *t; ++t ) {
1307 if ( *t == '%' ) {
1308 replace = NULL;
1309 if ( t[1] == 'e' ) {
1310 if ( tm->tm_mday >= 0 && tm->tm_mday <= 99 ) {
1311 sprintf(buffer, "%2d", tm->tm_mday);
1312 }
1313 else {
1314 strcpy(buffer, " ");
1315 }
1316 replace = buffer;
1317 }
1318 else if ( t[1] == 's' ) {
1319 tm2 = *tm;
1320 sprintf(buffer, "%d", (int)mktime(&tm2));
1321 replace = buffer;
1322 }
1323 else if ( t[1] == 'T' ) {
1324 replace = "%H:%M:%S";
1325 }
1326 else if ( t[1] == 'z' ) {
1327 _tzset();
1328 if ( tm->tm_isdst >= 0 ) {
1329 int offset = (int)_timezone - (tm->tm_isdst > 0 ? 3600 : 0);
1330 int hr, min;
1331
1332 if ( offset > 0 ) {
1333 buffer[0] = '-';
1334 }
1335 else {
1336 buffer[0] = '+';
1337 offset = -offset;
1338 }
1339
1340 hr = offset / 3600;
1341 min = (offset % 3600) / 60;
1342 sprintf(buffer+1, "%04d", hr*100 + min);
1343 }
1344 else {
1345 buffer[0] = '\0';
1346 }
1347 replace = buffer;
1348 }
1349 else if ( t[1] == '-' && t[2] != '\0' &&
1350 strchr("dHIjmMSUwWyY", t[2]) ) {
1351 /* Microsoft uses '#' rather than '-' to remove padding */
1352 t[1] = '#';
1353 }
1354 else if ( t[1] != '\0' ) {
1355 ++t;
1356 }
1357
1358 if (replace) {
1359 int m;
1360 char *newfmt;
1361
1362 *t = '\0';
1363 m = t - fmt;
1364 newfmt = xasprintf("%s%s%s", fmt, replace, t+2);
1365 free(fmt);
1366 t = newfmt + m + strlen(replace);
1367 fmt = newfmt;
1368 }
1369 }
1370 }
1371
1372 ret = strftime(buf, max, fmt, tm);
1373 free(fmt);
1374
1375 return ret;
1376}
1377
1378int stime(time_t *t UNUSED_PARAM)
1379{
1380 errno = EPERM;
1381 return -1;
1382}
1383
1384#undef access
1385int mingw_access(const char *name, int mode)
1386{
1387 int ret;
1388 struct stat s;
1389
1390 /* Windows can only handle test for existence, read or write */
1391 if (mode == F_OK || (mode & ~X_OK)) {
1392 ret = _access(name, mode & ~X_OK);
1393 if (ret < 0 || !(mode & X_OK)) {
1394 return ret;
1395 }
1396 }
1397
1398 if (!mingw_stat(name, &s)) {
1399 if ((s.st_mode&S_IXUSR)) {
1400 return 0;
1401 }
1402 errno = EACCES;
1403 }
1404
1405 return -1;
1406}
1407
1408#undef rmdir
1409int mingw_rmdir(const char *path)
1410{
1411 /* read-only directories cannot be removed */
1412 chmod(path, 0666);
1413 return rmdir(path);
1414}
1415
1416#define NUMEXT 5
1417static const char win_suffix[NUMEXT][4] = { "sh", "com", "exe", "bat", "cmd" };
1418
1419static int has_win_suffix(const char *name, int start)
1420{
1421 const char *dot = strrchr(bb_basename(name), '.');
1422 int i;
1423
1424 if (dot != NULL && strlen(dot) < 5) {
1425 for (i=start; i<NUMEXT; ++i) {
1426 if (!strcasecmp(dot+1, win_suffix[i])) {
1427 return 1;
1428 }
1429 }
1430 }
1431 return 0;
1432}
1433
1434int has_bat_suffix(const char *name)
1435{
1436 return has_win_suffix(name, 3);
1437}
1438
1439int has_exe_suffix(const char *name)
1440{
1441 return has_win_suffix(name, 0);
1442}
1443
1444int has_exe_suffix_or_dot(const char *name)
1445{
1446 return last_char_is(name, '.') || has_win_suffix(name, 0);
1447}
1448
1449/* Check if path can be made into an executable by adding a suffix.
1450 * The suffix is added to the end of the argument which must be
1451 * long enough to allow this.
1452 *
1453 * If the return value is TRUE the argument contains the new path,
1454 * if FALSE the argument is unchanged.
1455 */
1456int add_win32_extension(char *p)
1457{
1458 if (!has_exe_suffix_or_dot(p)) {
1459 int i, len = strlen(p);
1460
1461 p[len] = '.';
1462 for (i=0; i<NUMEXT; ++i) {
1463 strcpy(p+len+1, win_suffix[i]);
1464 if (file_is_executable(p))
1465 return TRUE;
1466 }
1467 p[len] = '\0';
1468 }
1469 return FALSE;
1470}
1471
1472/* Check if path can be made into an executable by adding a suffix.
1473 * Return an allocated string containing the path if it can;
1474 * return NULL if not.
1475 *
1476 * If path already has a suffix don't even bother trying.
1477 */
1478char *alloc_win32_extension(const char *p)
1479{
1480 if (!has_exe_suffix_or_dot(p)) {
1481 int len = strlen(p);
1482 char *path = strcpy(xmalloc(len+5), p);
1483
1484 if (add_win32_extension(path))
1485 return path;
1486 free(path);
1487 }
1488 return NULL;
1489}
1490
1491void FAST_FUNC bs_to_slash(char *p)
1492{
1493 for (; *p; ++p) {
1494 if ( *p == '\\' ) {
1495 *p = '/';
1496 }
1497 }
1498}
1499
1500void FAST_FUNC slash_to_bs(char *p)
1501{
1502 for (; *p; ++p) {
1503 if ( *p == '/' ) {
1504 *p = '\\';
1505 }
1506 }
1507}
1508
1509size_t FAST_FUNC remove_cr(char *p, size_t len)
1510{
1511 ssize_t i, j;
1512
1513 for (i=j=0; i<len; ++i) {
1514 if (p[i] != '\r')
1515 p[j++] = p[i];
1516 }
1517 return j;
1518}
1519
1520#undef opendir
1521DIR *mingw_opendir(const char *path)
1522{
1523 char name[4];
1524
1525 if (isalpha(path[0]) && path[1] == ':' && path[2] == '\0') {
1526 strcpy(name, path);
1527 name[2] = '/';
1528 name[3] = '\0';
1529 path = name;
1530 }
1531
1532 return opendir(path);
1533}
1534
1535off_t mingw_lseek(int fd, off_t offset, int whence)
1536{
1537 HANDLE h = (HANDLE)_get_osfhandle(fd);
1538 if (h == INVALID_HANDLE_VALUE) {
1539 errno = EBADF;
1540 return -1;
1541 }
1542 if (GetFileType(h) != FILE_TYPE_DISK) {
1543 errno = ESPIPE;
1544 return -1;
1545 }
1546 return _lseeki64(fd, offset, whence);
1547}
1548
1549#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
1550#undef GetTickCount64
1551
1552ULONGLONG CompatGetTickCount64(void)
1553{
1554 DECLARE_PROC_ADDR(ULONGLONG, GetTickCount64, void);
1555
1556 if (!INIT_PROC_ADDR(kernel32.dll, GetTickCount64)) {
1557 return (ULONGLONG)GetTickCount();
1558 }
1559
1560 return GetTickCount64();
1561}
1562#endif
1563
1564#if ENABLE_FEATURE_INSTALLER
1565/*
1566 * Enumerate the names of all hard links to a file. The first call
1567 * provides the file name as the first argument; subsequent calls must
1568 * set the first argument to NULL. Returns 0 on error or when there are
1569 * no more links.
1570 */
1571int enumerate_links(const char *file, char *name)
1572{
1573 static HANDLE h = INVALID_HANDLE_VALUE;
1574 char aname[PATH_MAX];
1575 wchar_t wname[PATH_MAX];
1576 DWORD length = PATH_MAX;
1577 DECLARE_PROC_ADDR(HANDLE, FindFirstFileNameW, LPCWSTR, DWORD, LPDWORD,
1578 PWSTR);
1579 DECLARE_PROC_ADDR(BOOL, FindNextFileNameW, HANDLE, LPDWORD, PWSTR);
1580
1581 if (!INIT_PROC_ADDR(kernel32.dll, FindFirstFileNameW) ||
1582 !INIT_PROC_ADDR(kernel32.dll, FindNextFileNameW))
1583 return 0;
1584
1585 if (file != NULL) {
1586 wchar_t wfile[PATH_MAX];
1587 MultiByteToWideChar(CP_ACP, 0, file, -1, wfile, PATH_MAX);
1588 h = FindFirstFileNameW(wfile, 0, &length, wname);
1589 if (h == INVALID_HANDLE_VALUE)
1590 return 0;
1591 }
1592 else if (!FindNextFileNameW(h, &length, wname)) {
1593 FindClose(h);
1594 h = INVALID_HANDLE_VALUE;
1595 return 0;
1596 }
1597 WideCharToMultiByte(CP_ACP, 0, wname, -1, aname, PATH_MAX, NULL, NULL);
1598 realpath(aname, name);
1599 return 1;
1600}
1601#endif
1602
1603#if ENABLE_ASH_NOCONSOLE
1604void hide_console(void)
1605{
1606 DWORD dummy;
1607 DECLARE_PROC_ADDR(DWORD, GetConsoleProcessList, LPDWORD, DWORD);
1608 DECLARE_PROC_ADDR(BOOL, ShowWindow, HWND, int);
1609
1610 if (INIT_PROC_ADDR(kernel32.dll, GetConsoleProcessList) &&
1611 INIT_PROC_ADDR(user32.dll, ShowWindow)) {
1612 if (GetConsoleProcessList(&dummy, 1) == 1) {
1613 ShowWindow(GetConsoleWindow(), SW_HIDE);
1614 }
1615 }
1616}
1617#endif
1618
1619#define is_path_sep(x) ((x) == '/' || (x) == '\\')
1620#define is_unc_path(x) (is_path_sep(x[0]) && is_path_sep(x[1]))
1621
1622/* Return the length of the root of a UNC path, i.e. the '//host/share'
1623 * component, or 0 if the path doesn't look like that. */
1624int unc_root_len(const char *dir)
1625{
1626 const char *s = dir + 2;
1627 int len;
1628
1629 if (!is_unc_path(dir))
1630 return 0;
1631 len = strcspn(s, "/\\");
1632 if (len == 0)
1633 return 0;
1634 s += len + 1;
1635 len = strcspn(s, "/\\");
1636 if (len == 0)
1637 return 0;
1638 s += len;
1639
1640 return s - dir;
1641}
1642
1643/* Return the length of the root of a path, i.e. either the drive or
1644 * UNC '//host/share', or 0 if the path doesn't look like that. */
1645int root_len(const char *path)
1646{
1647 if (path == NULL)
1648 return 0;
1649 if (isalpha(*path) && path[1] == ':')
1650 return 2;
1651 return unc_root_len(path);
1652}
1653
1654char *get_system_drive(void)
1655{
1656 struct passwd *pwd;
1657 char *drive = NULL;
1658 int len;
1659
1660 pwd = getpwuid(0);
1661 if (pwd != NULL && (len=root_len(pwd->pw_dir))) {
1662 drive = xstrdup(pwd->pw_dir);
1663 drive[len] = '\0';
1664 }
1665
1666 return drive;
1667}
1668
1669int chdir_system_drive(void)
1670{
1671 char *sd = get_system_drive();
1672 int ret = -1;
1673
1674 if (sd) {
1675 strcat(sd, "/");
1676 ret = chdir(sd);
1677 }
1678 free(sd);
1679 return ret;
1680}
1681
1682/*
1683 * This function is used to make relative paths absolute before a call
1684 * to chdir_system_drive(). It's unlikely to be useful in other cases.
1685 *
1686 * If the argument is an absolute path or a relative path which resolves
1687 * to a path on the system drive return 'path'. If it's a relative path
1688 * which resolves to a path that isn't on the system drive return an
1689 * allocated string containing the resolved path. Die on failure,
1690 * which is most likely because the file doesn't exist.
1691 */
1692char *xabsolute_path(char *path)
1693{
1694 char *rpath, *sd;
1695
1696 if (root_len(path) != 0)
1697 return path; // absolute path
1698 rpath = xmalloc_realpath(path);
1699 if (rpath) {
1700 sd = auto_string(get_system_drive());
1701 if (sd && is_prefixed_with_case(rpath, sd)) {
1702 free(rpath);
1703 return path; // resolved path is on system drive
1704 }
1705 return rpath;
1706 }
1707 bb_perror_msg_and_die("can't open '%s'", path);
1708}
1709
1710char *get_drive_cwd(const char *path, char *buffer, int size)
1711{
1712 char drive[3] = { *path, ':', '\0' };
1713 DWORD ret;
1714
1715 ret = GetFullPathName(drive, size, buffer, NULL);
1716 if (ret == 0 || ret > size)
1717 return NULL;
1718 bs_to_slash(buffer);
1719 return buffer;
1720}
1721
1722void fix_path_case(char *path)
1723{
1724 char resolved[PATH_MAX];
1725 int len;
1726
1727 // Canonicalise path: for physical drives this makes case match
1728 // what's stored on disk. For mapped drives, not so much.
1729 if (realpath(path, resolved) && strcasecmp(path, resolved) == 0)
1730 strcpy(path, resolved);
1731
1732 // make drive letter or UNC hostname uppercase
1733 len = root_len(path);
1734 if (len == 2) {
1735 *path = toupper(*path);
1736 }
1737 else if (len != 0) {
1738 for (path+=2; !is_path_sep(*path); ++path) {
1739 *path = toupper(*path);
1740 }
1741 }
1742}
diff --git a/win32/mntent.c b/win32/mntent.c
new file mode 100644
index 000000000..7ab51239d
--- /dev/null
+++ b/win32/mntent.c
@@ -0,0 +1,86 @@
1/*
2 * A simple WIN32 implementation of mntent routines. It only handles
3 * logical drives.
4 */
5#include "libbb.h"
6
7struct mntdata {
8 DWORD flags;
9 int index;
10 struct mntent me;
11 char mnt_fsname[PATH_MAX];
12 char mnt_dir[4];
13 char mnt_type[100];
14 char mnt_opts[4];
15};
16
17FILE *mingw_setmntent(void)
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 UINT drive_type;
36 char buf[PATH_MAX];
37
38 data->me.mnt_fsname = data->mnt_fsname;
39 data->me.mnt_dir = data->mnt_dir;
40 data->me.mnt_type = data->mnt_type;
41 data->me.mnt_opts = data->mnt_opts;
42 data->me.mnt_freq = 0;
43 data->me.mnt_passno = 0;
44
45 entry = NULL;
46 while ( ++data->index < 26 ) {
47 if ( (data->flags & 1<<data->index) != 0 ) {
48 data->mnt_fsname[0] = 'A' + data->index;
49 data->mnt_fsname[1] = ':';
50 data->mnt_fsname[2] = '\0';
51 data->mnt_dir[0] = 'A' + data->index;
52 data->mnt_dir[1] = ':';
53 data->mnt_dir[2] = '/';
54 data->mnt_dir[3] = '\0';
55 data->mnt_type[0] = '\0';
56 data->mnt_opts[0] = '\0';
57
58 drive_type = GetDriveType(data->mnt_dir);
59 if ( drive_type == DRIVE_FIXED || drive_type == DRIVE_CDROM ||
60 drive_type == DRIVE_REMOVABLE ||
61 drive_type == DRIVE_REMOTE ) {
62 if ( !GetVolumeInformation(data->mnt_dir, NULL, 0, NULL, NULL,
63 NULL, data->mnt_type, 100) ) {
64 continue;
65 }
66
67 if (realpath(data->mnt_dir, buf) != NULL) {
68 if (isalpha(buf[0]) && strcmp(buf+1, ":/") == 0)
69 buf[2] = '\0';
70 strcpy(data->mnt_fsname, buf);
71 }
72
73 entry = &data->me;
74 break;
75 }
76 }
77 }
78
79 return entry;
80}
81
82int endmntent(FILE *stream)
83{
84 free(stream);
85 return 0;
86}
diff --git a/win32/mntent.h b/win32/mntent.h
new file mode 100644
index 000000000..8bdf3d45e
--- /dev/null
+++ b/win32/mntent.h
@@ -0,0 +1,21 @@
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 *mingw_setmntent(void);
16extern struct mntent *getmntent(FILE *stream);
17extern int endmntent(FILE *stream);
18
19#define setmntent(f, m) mingw_setmntent()
20
21#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..8546c6e2f
--- /dev/null
+++ b/win32/process.c
@@ -0,0 +1,756 @@
1#include "libbb.h"
2#include <tlhelp32.h>
3#include <psapi.h>
4#include "lazyload.h"
5
6int waitpid(pid_t pid, int *status, int options)
7{
8 HANDLE proc;
9 intptr_t ret;
10
11 /* Windows does not understand parent-child */
12 if (pid > 0 && options == 0) {
13 if ( (proc=OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
14 FALSE, pid)) != NULL ) {
15 ret = _cwait(status, (intptr_t)proc, 0);
16 CloseHandle(proc);
17 if (ret == -1) {
18 return -1;
19 }
20 *status <<= 8;
21 return pid;
22 }
23 }
24 errno = pid < 0 ? ENOSYS : EINVAL;
25 return -1;
26}
27
28typedef struct {
29 char *path;
30 char *name;
31 char *opts;
32 char buf[100];
33} interp_t;
34
35static int
36parse_interpreter(const char *cmd, interp_t *interp)
37{
38 char *path, *t;
39 int n;
40
41 while (TRUE) {
42 n = open_read_close(cmd, interp->buf, sizeof(interp->buf)-1);
43 if (n < 4) /* at least '#!/x' and not error */
44 break;
45
46 /*
47 * See http://www.in-ulm.de/~mascheck/various/shebang/ for trivia
48 * relating to '#!'.
49 */
50 if (interp->buf[0] != '#' || interp->buf[1] != '!')
51 break;
52 interp->buf[n] = '\0';
53 if ((t=strchr(interp->buf, '\n')) == NULL)
54 break;
55 t[1] = '\0';
56
57 if ((path=strtok(interp->buf+2, " \t\r\n")) == NULL)
58 break;
59
60 t = (char *)bb_basename(path);
61 if (*t == '\0')
62 break;
63
64 interp->path = path;
65 interp->name = t;
66 interp->opts = strtok(NULL, "\r\n");
67 /* Trim leading and trailing whitespace from the options.
68 * If the resulting string is empty return a NULL pointer. */
69 if (interp->opts && trim(interp->opts) == interp->opts)
70 interp->opts = NULL;
71 return 1;
72 }
73
74 if (n >= 0 && is_suffixed_with_case(cmd, ".sh")) {
75 interp->path = (char *)DEFAULT_SHELL;
76 interp->name = (char *)DEFAULT_SHELL_SHORT_NAME;
77 interp->opts = NULL;
78 return 1;
79 }
80 return 0;
81}
82
83/*
84 * See http://msdn2.microsoft.com/en-us/library/17w5ykft(vs.71).aspx
85 * (Parsing C++ Command-Line Arguments)
86 */
87static char *
88quote_arg(const char *arg)
89{
90 int len = 0, n = 0;
91 int force_quotes = 0;
92 char *q, *d;
93 const char *p = arg;
94
95 /* empty arguments must be quoted */
96 if (!*p) {
97 force_quotes = 1;
98 }
99
100 while (*p) {
101 if (isspace(*p)) {
102 /* arguments containing whitespace must be quoted */
103 force_quotes = 1;
104 }
105 else if (*p == '"') {
106 /* double quotes in arguments need to be escaped */
107 n++;
108 }
109 else if (*p == '\\') {
110 /* count contiguous backslashes */
111 int count = 0;
112 while (*p == '\\') {
113 count++;
114 p++;
115 len++;
116 }
117
118 /*
119 * Only escape backslashes before explicit double quotes or
120 * or where the backslashes are at the end of an argument
121 * that is scheduled to be quoted.
122 */
123 if (*p == '"' || (force_quotes && *p == '\0')) {
124 n += count*2 + 1;
125 }
126
127 if (*p == '\0') {
128 break;
129 }
130 continue;
131 }
132 len++;
133 p++;
134 }
135
136 if (!force_quotes && n == 0) {
137 return (char*)arg;
138 }
139
140 /* insert double quotes and backslashes where necessary */
141 d = q = xmalloc(len+n+3);
142 if (force_quotes) {
143 *d++ = '"';
144 }
145
146 while (*arg) {
147 if (*arg == '"') {
148 *d++ = '\\';
149 }
150 else if (*arg == '\\') {
151 int count = 0;
152 while (*arg == '\\') {
153 count++;
154 *d++ = *arg++;
155 }
156
157 if (*arg == '"' || (force_quotes && *arg == '\0')) {
158 while (count-- > 0) {
159 *d++ = '\\';
160 }
161 if (*arg == '"') {
162 *d++ = '\\';
163 }
164 }
165 }
166 if (*arg != '\0') {
167 *d++ = *arg++;
168 }
169 }
170 if (force_quotes) {
171 *d++ = '"';
172 }
173 *d = '\0';
174
175 return q;
176}
177
178static char *
179find_first_executable(const char *name)
180{
181 char *tmp, *path = getenv("PATH");
182 char *exe_path = NULL;
183
184 if (path) {
185 tmp = path = xstrdup(path);
186 exe_path = find_executable(name, &tmp);
187 free(path);
188 }
189
190 return exe_path;
191}
192
193static intptr_t
194spawnveq(int mode, const char *path, char *const *argv, char *const *env)
195{
196 char **new_argv;
197 char *new_path = NULL;
198 int i, argc;
199 intptr_t ret;
200 struct stat st;
201
202 /*
203 * Require that the file exists, is a regular file and is executable.
204 * It may still contain garbage but we let spawnve deal with that.
205 */
206 if (stat(path, &st) == 0) {
207 if (!S_ISREG(st.st_mode) || !(st.st_mode&S_IXUSR)) {
208 errno = EACCES;
209 return -1;
210 }
211 }
212 else {
213 return -1;
214 }
215
216 argc = string_array_len((char **)argv);
217 new_argv = xmalloc(sizeof(*argv)*(argc+1));
218 for (i = 0;i < argc;i++)
219 new_argv[i] = quote_arg(argv[i]);
220 new_argv[argc] = NULL;
221
222 /* Special case: spawnve won't execute a batch file if the first
223 * argument is a relative path containing forward slashes. Absolute
224 * paths are fine but there's no harm in converting them too. */
225 if (has_bat_suffix(path)) {
226 slash_to_bs(new_argv[0]);
227
228 /* Another special case: spawnve returns ENOEXEC when passed an
229 * empty batch file. Pretend it worked. */
230 if (st.st_size == 0) {
231 ret = 0;
232 goto done;
233 }
234 }
235
236 /*
237 * Another special case: if a file doesn't have an extension add
238 * a '.' at the end. This forces spawnve to use precisely the
239 * file specified without trying to add an extension.
240 */
241 if (!strchr(bb_basename(path), '.')) {
242 new_path = xasprintf("%s.", path);
243 }
244
245 ret = spawnve(mode, new_path ? new_path : path, new_argv, env);
246
247 done:
248 for (i = 0;i < argc;i++)
249 if (new_argv[i] != argv[i])
250 free(new_argv[i]);
251 free(new_argv);
252 free(new_path);
253
254 return ret;
255}
256
257#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
258static intptr_t
259mingw_spawn_applet(int mode,
260 char *const *argv,
261 char *const *envp)
262{
263 return spawnveq(mode, bb_busybox_exec_path, argv, envp);
264}
265#endif
266
267static intptr_t
268mingw_spawn_interpreter(int mode, const char *prog, char *const *argv, char *const *envp)
269{
270 intptr_t ret;
271 int nopts;
272 interp_t interp;
273 char **new_argv;
274 int argc;
275 char *fullpath = NULL;
276
277 if (!parse_interpreter(prog, &interp))
278 return spawnveq(mode, prog, argv, envp);
279
280 nopts = interp.opts != NULL;
281 argc = string_array_len((char **)argv);
282 new_argv = xmalloc(sizeof(*argv)*(argc+nopts+2));
283 new_argv[1] = interp.opts;
284 new_argv[nopts+1] = (char *)prog; /* pass absolute path */
285 memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc);
286
287 if ((fullpath=alloc_win32_extension(interp.path)) != NULL ||
288 file_is_executable(interp.path)) {
289 new_argv[0] = fullpath ? fullpath : interp.path;
290 ret = spawnveq(mode, new_argv[0], new_argv, envp);
291 } else
292#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
293 if (find_applet_by_name(interp.name) >= 0) {
294 /* the fake path indicates the index of the script */
295 new_argv[0] = fullpath = xasprintf("%d:/%s", nopts+1, interp.name);
296 ret = mingw_spawn_applet(mode, new_argv, envp);
297 } else
298#endif
299 if ((fullpath=find_first_executable(interp.name)) != NULL) {
300 new_argv[0] = fullpath;
301 ret = spawnveq(mode, fullpath, new_argv, envp);
302 }
303 else {
304 errno = ENOENT;
305 ret = -1;
306 }
307
308 free(fullpath);
309 free(new_argv);
310 return ret;
311}
312
313static intptr_t
314mingw_spawn_1(int mode, const char *cmd, char *const *argv, char *const *envp)
315{
316 char *prog;
317
318#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
319 if (find_applet_by_name(cmd) >= 0)
320 return mingw_spawn_applet(mode, argv, envp);
321 else
322#endif
323 if (strchr(cmd, '/') || strchr(cmd, '\\')) {
324 return mingw_spawn_interpreter(mode, cmd, argv, envp);
325 }
326 else if ((prog=find_first_executable(cmd)) != NULL) {
327 intptr_t ret = mingw_spawn_interpreter(mode, prog, argv, envp);
328 free(prog);
329 return ret;
330 }
331
332 errno = ENOENT;
333 return -1;
334}
335
336pid_t FAST_FUNC
337mingw_spawn(char **argv)
338{
339 intptr_t ret;
340
341 ret = mingw_spawn_proc((const char **)argv);
342
343 return ret == -1 ? -1 : GetProcessId((HANDLE)ret);
344}
345
346intptr_t FAST_FUNC
347mingw_spawn_proc(const char **argv)
348{
349 return mingw_spawn_1(P_NOWAIT, argv[0], (char *const *)argv, environ);
350}
351
352int
353mingw_execvp(const char *cmd, char *const *argv)
354{
355 int ret = (int)mingw_spawn_1(P_WAIT, cmd, argv, environ);
356 if (ret != -1)
357 exit(ret);
358 return ret;
359}
360
361int
362mingw_execve(const char *cmd, char *const *argv, char *const *envp)
363{
364 int ret = (int)mingw_spawn_interpreter(P_WAIT, cmd, argv, envp);
365 if (ret != -1)
366 exit(ret);
367 return ret;
368}
369
370int
371mingw_execv(const char *cmd, char *const *argv)
372{
373 return mingw_execve(cmd, argv, environ);
374}
375
376static inline long long filetime_to_ticks(const FILETIME *ft)
377{
378 return (((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime)/
379 HNSEC_PER_TICK;
380}
381
382/*
383 * Attempt to get a string from another instance of busybox.exe.
384 * This will only work if the other process is using the same binary
385 * as the current process. If anything goes wrong just give up.
386 */
387static char *get_bb_string(DWORD pid, const char *exe, char *string)
388{
389 HANDLE proc;
390 HMODULE mlist[32];
391 DWORD needed;
392 void *address;
393 char *my_base;
394 char buffer[128];
395 char exepath[PATH_MAX];
396 char *name = NULL;
397 int i;
398
399 if (!(proc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
400 FALSE, pid))) {
401 return NULL;
402 }
403
404 if (exe == NULL) {
405 if (GetProcessImageFileName(proc, exepath, PATH_MAX) != 0) {
406 exe = bb_basename(exepath);
407 }
408 }
409
410 /*
411 * Search for the module that matches the name of the executable.
412 * The values returned in mlist are actually the base address of
413 * the module in the other process (as noted in the documentation
414 * for the MODULEINFO structure).
415 */
416 if (!EnumProcessModules(proc, mlist, sizeof(mlist), &needed)) {
417 goto finish;
418 }
419
420 for (i=0; exe != NULL && i<needed/sizeof(HMODULE); ++i) {
421 char modname[MAX_PATH];
422 if (GetModuleFileNameEx(proc, mlist[i], modname, sizeof(modname))) {
423 if (strcasecmp(bb_basename(modname), exe) == 0) {
424 break;
425 }
426 }
427 }
428
429 if (i == needed/sizeof(HMODULE)) {
430 goto finish;
431 }
432
433 /* attempt to read the BusyBox version string */
434 my_base = (char *)GetModuleHandle(NULL);
435 address = (char *)mlist[i] + ((char *)bb_banner - my_base);
436 if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) {
437 goto finish;
438 }
439
440 if (memcmp(buffer, bb_banner, strlen(bb_banner)) != 0) {
441 /* version mismatch (or not BusyBox at all) */
442 goto finish;
443 }
444
445 /* attempt to read the required string */
446 address = (char *)mlist[i] + ((char *)string - my_base);
447 if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) {
448 goto finish;
449 }
450
451 buffer[127] = '\0';
452 name = auto_string(xstrdup(buffer));
453
454 finish:
455 CloseHandle(proc);
456 return name;
457}
458
459/* POSIX version in libbb/procps.c */
460procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags
461#if !ENABLE_FEATURE_PS_TIME && !ENABLE_FEATURE_PS_LONG
462UNUSED_PARAM
463#endif
464)
465{
466 PROCESSENTRY32 pe;
467 HANDLE proc;
468 const char *comm, *name;
469 BOOL ret;
470
471 pe.dwSize = sizeof(pe);
472 if (!sp) {
473 sp = xzalloc(sizeof(struct procps_status_t));
474 sp->snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
475 if (sp->snapshot == INVALID_HANDLE_VALUE) {
476 free(sp);
477 return NULL;
478 }
479 ret = Process32First(sp->snapshot, &pe);
480 }
481 else {
482 ret = Process32Next(sp->snapshot, &pe);
483 }
484
485 if (!ret) {
486 CloseHandle(sp->snapshot);
487 free(sp);
488 return NULL;
489 }
490
491 memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
492#if !ENABLE_DESKTOP
493 strcpy(sp->state, " ");
494#endif
495
496#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
497 if (flags & (PSSCAN_STIME|PSSCAN_UTIME|PSSCAN_START_TIME)) {
498 FILETIME crTime, exTime, keTime, usTime;
499
500 if ((proc=OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
501 FALSE, pe.th32ProcessID))) {
502 if (GetProcessTimes(proc, &crTime, &exTime, &keTime, &usTime)) {
503 long long ticks_since_boot, boot_time, create_time;
504 FILETIME now;
505
506 ticks_since_boot = GetTickCount64()/MS_PER_TICK;
507 GetSystemTimeAsFileTime(&now);
508 boot_time = filetime_to_ticks(&now) - ticks_since_boot;
509 create_time = filetime_to_ticks(&crTime);
510
511 sp->start_time = (unsigned long)(create_time - boot_time);
512 sp->stime = (unsigned long)filetime_to_ticks(&keTime);
513 sp->utime = (unsigned long)filetime_to_ticks(&usTime);
514 }
515 CloseHandle(proc);
516 }
517 }
518#endif
519
520 if (flags & PSSCAN_UIDGID) {
521 /* if we can open the process it belongs to us */
522 if ((proc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID))) {
523 sp->uid = DEFAULT_UID;
524 sp->gid = DEFAULT_GID;
525 CloseHandle(proc);
526 }
527 }
528
529 sp->pid = pe.th32ProcessID;
530 sp->ppid = pe.th32ParentProcessID;
531
532 if (sp->pid == GetProcessId(GetCurrentProcess())) {
533 comm = applet_name;
534 }
535 else if ((name=get_bb_string(sp->pid, pe.szExeFile, bb_comm)) != NULL) {
536 comm = name;
537 }
538 else {
539 comm = pe.szExeFile;
540 }
541 safe_strncpy(sp->comm, comm, COMM_LEN);
542
543 return sp;
544}
545
546void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
547{
548 const char *str, *cmdline;
549
550 *buf = '\0';
551 if (pid == GetProcessId(GetCurrentProcess()))
552 cmdline = bb_command_line;
553 else if ((str=get_bb_string(pid, NULL, bb_command_line)) != NULL)
554 cmdline = str;
555 else
556 cmdline = comm;
557 safe_strncpy(buf, cmdline, col);
558}
559
560/**
561 * If the process ID is positive invoke the callback for that process
562 * only. If negative or zero invoke the callback for all descendants
563 * of the indicated process. Zero indicates the current process; negative
564 * indicates the process with process ID -pid.
565 */
566typedef int (*kill_callback)(pid_t pid, int exit_code);
567
568static int kill_pids(pid_t pid, int exit_code, kill_callback killer)
569{
570 DWORD pids[16384];
571 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0;
572
573 if(pid > 0)
574 pids[0] = (DWORD)pid;
575 else if (pid == 0)
576 pids[0] = (DWORD)getpid();
577 else
578 pids[0] = (DWORD)-pid;
579 len = 1;
580
581 /*
582 * Even if Process32First()/Process32Next() seem to traverse the
583 * processes in topological order (i.e. parent processes before
584 * child processes), there is nothing in the Win32 API documentation
585 * suggesting that this is guaranteed.
586 *
587 * Therefore, run through them at least twice and stop when no more
588 * process IDs were added to the list.
589 */
590 if (pid <= 0) {
591 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
592
593 if (snapshot == INVALID_HANDLE_VALUE) {
594 errno = err_win_to_posix();
595 return -1;
596 }
597
598 for (;;) {
599 PROCESSENTRY32 entry;
600 int orig_len = len;
601
602 memset(&entry, 0, sizeof(entry));
603 entry.dwSize = sizeof(entry);
604
605 if (!Process32First(snapshot, &entry))
606 break;
607
608 do {
609 for (i = len - 1; i >= 0; i--) {
610 if (pids[i] == entry.th32ProcessID)
611 break;
612 if (pids[i] == entry.th32ParentProcessID)
613 pids[len++] = entry.th32ProcessID;
614 }
615 } while (len < max_len && Process32Next(snapshot, &entry));
616
617 if (orig_len == len || len >= max_len)
618 break;
619 }
620
621 CloseHandle(snapshot);
622 }
623
624 for (i = len - 1; i >= 0; i--) {
625 SetLastError(0);
626 if (killer(pids[i], exit_code)) {
627 errno = err_win_to_posix();
628 ret = -1;
629 }
630 }
631
632 return ret;
633}
634
635/**
636 * Determine whether a process runs in the same architecture as the current
637 * one. That test is required before we assume that GetProcAddress() returns
638 * a valid address *for the target process*.
639 */
640static inline int process_architecture_matches_current(HANDLE process)
641{
642 static BOOL current_is_wow = -1;
643 BOOL is_wow;
644
645 if (current_is_wow == -1 &&
646 !IsWow64Process (GetCurrentProcess(), &current_is_wow))
647 current_is_wow = -2;
648 if (current_is_wow == -2)
649 return 0; /* could not determine current process' WoW-ness */
650 if (!IsWow64Process (process, &is_wow))
651 return 0; /* cannot determine */
652 return is_wow == current_is_wow;
653}
654
655/**
656 * This function tries to terminate a Win32 process, as gently as possible,
657 * by injecting a thread that calls ExitProcess().
658 *
659 * Note: as kernel32.dll is loaded before any process, the other process and
660 * this process will have ExitProcess() at the same address.
661 *
662 * The idea comes from the Dr Dobb's article "A Safer Alternative to
663 * TerminateProcess()" by Andrew Tucker (July 1, 1999),
664 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
665 *
666 */
667int kill_SIGTERM_by_handle(HANDLE process, int exit_code)
668{
669 DWORD code;
670 int ret = 0;
671
672 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) {
673 DECLARE_PROC_ADDR(DWORD, ExitProcess, LPVOID);
674 PVOID arg = (PVOID)(intptr_t)exit_code;
675 DWORD thread_id;
676 HANDLE thread;
677
678 if (!INIT_PROC_ADDR(kernel32, ExitProcess) ||
679 !process_architecture_matches_current(process)) {
680 SetLastError(ERROR_ACCESS_DENIED);
681 ret = -1;
682 goto finish;
683 }
684
685 if ((thread = CreateRemoteThread(process, NULL, 0,
686 ExitProcess, arg, 0, &thread_id))) {
687 CloseHandle(thread);
688 }
689 }
690
691 finish:
692 CloseHandle(process);
693 return ret;
694}
695
696static int kill_SIGTERM(pid_t pid, int exit_code)
697{
698 HANDLE process;
699
700 if (!(process = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD |
701 PROCESS_QUERY_INFORMATION |
702 PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
703 PROCESS_VM_READ, FALSE, pid))) {
704 return -1;
705 }
706
707 return kill_SIGTERM_by_handle(process, exit_code);
708}
709
710/*
711 * This way of terminating processes is not gentle: they get no chance to
712 * clean up after themselves (closing file handles, removing .lock files,
713 * terminating spawned processes (if any), etc).
714 */
715static int kill_SIGKILL(pid_t pid, int exit_code)
716{
717 HANDLE process;
718 int ret;
719
720 if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) {
721 return -1;
722 }
723
724 ret = !TerminateProcess(process, exit_code);
725 CloseHandle(process);
726
727 return ret;
728}
729
730static int kill_SIGTEST(pid_t pid, int exit_code UNUSED_PARAM)
731{
732 HANDLE process;
733
734 if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) {
735 return -1;
736 }
737
738 CloseHandle(process);
739 return 0;
740}
741
742int kill(pid_t pid, int sig)
743{
744 if (sig == SIGTERM) {
745 return kill_pids(pid, 128+sig, kill_SIGTERM);
746 }
747 else if (sig == SIGKILL) {
748 return kill_pids(pid, 128+sig, kill_SIGKILL);
749 }
750 else if (sig == 0) {
751 return kill_pids(pid, 128+sig, kill_SIGTEST);
752 }
753
754 errno = EINVAL;
755 return -1;
756}
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..1900d1872
--- /dev/null
+++ b/win32/resources/Kbuild.src
@@ -0,0 +1,27 @@
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
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/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..92e640951
--- /dev/null
+++ b/win32/resources/resources.rc
@@ -0,0 +1,34 @@
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-2019 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
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..22fc591ec
--- /dev/null
+++ b/win32/statfs.c
@@ -0,0 +1,83 @@
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();
25 return -1;
26 }
27
28 if ( !GetVolumeInformation(file, NULL, 0, &serial, &namelen, &flags,
29 fsname, 100) ) {
30 errno = err_win_to_posix();
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 (dlls/kernel32/volume.c).
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 if ( strcmp(fsname, "UDF") == 0 ) {
66 buf->f_type = 0x15013346;
67 }
68 else {
69 buf->f_type = 0;
70 }
71
72 buf->f_frsize = buf->f_bsize;
73 buf->f_blocks = total_number_of_bytes / buf->f_bsize;
74 buf->f_bfree = total_number_of_free_bytes / buf->f_bsize;
75 buf->f_bavail = free_bytes_available / buf->f_bsize;
76 buf->f_files = 0;
77 buf->f_ffree = 0;
78 buf->f_fsid = serial;
79 buf->f_flag = 0;
80 buf->f_namelen = namelen;
81
82 return 0;
83}
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/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/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..25f2ea48c
--- /dev/null
+++ b/win32/termios.c
@@ -0,0 +1,119 @@
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#if ENABLE_FEATURE_EURO
10 wchar_t uchar;
11 char achar;
12#else
13 char *s;
14#endif
15 int alt_pressed = FALSE;
16 DWORD state;
17
18 if (fd != 0)
19 bb_error_msg_and_die("read_key only works on stdin");
20 if (cin == INVALID_HANDLE_VALUE)
21 return -1;
22 GetConsoleMode(cin, &mode);
23 SetConsoleMode(cin, 0);
24
25 while (1) {
26 if (timeout > 0) {
27 if (WaitForSingleObject(cin, timeout) != WAIT_OBJECT_0)
28 goto done;
29 }
30#if ENABLE_FEATURE_EURO
31 if (!ReadConsoleInputW(cin, &record, 1, &nevent_out))
32#else
33 if (!ReadConsoleInput(cin, &record, 1, &nevent_out))
34#endif
35 goto done;
36
37 if (record.EventType != KEY_EVENT)
38 continue;
39
40 state = record.Event.KeyEvent.dwControlKeyState;
41 if (!record.Event.KeyEvent.bKeyDown) {
42 /* ignore all key up events except Alt */
43 if (alt_pressed && !(state & LEFT_ALT_PRESSED))
44 alt_pressed = FALSE;
45 else
46 continue;
47 }
48 else {
49 alt_pressed = ((state & LEFT_ALT_PRESSED) != 0);
50 }
51
52#if ENABLE_FEATURE_EURO
53 if (!record.Event.KeyEvent.uChar.UnicodeChar) {
54#else
55 if (!record.Event.KeyEvent.uChar.AsciiChar) {
56#endif
57 if (alt_pressed) {
58 switch (record.Event.KeyEvent.wVirtualKeyCode) {
59 case VK_MENU:
60 case VK_INSERT:
61 case VK_END:
62 case VK_DOWN:
63 case VK_NEXT:
64 case VK_LEFT:
65 case VK_CLEAR:
66 case VK_RIGHT:
67 case VK_HOME:
68 case VK_UP:
69 case VK_PRIOR:
70 case VK_KANA:
71 continue;
72 }
73 }
74
75 switch (record.Event.KeyEvent.wVirtualKeyCode) {
76 case VK_DELETE: ret = KEYCODE_DELETE; goto done;
77 case VK_INSERT: ret = KEYCODE_INSERT; goto done;
78 case VK_UP: ret = KEYCODE_UP; goto done;
79 case VK_DOWN: ret = KEYCODE_DOWN; goto done;
80 case VK_RIGHT:
81 if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) {
82 ret = KEYCODE_CTRL_RIGHT;
83 goto done;
84 }
85 ret = KEYCODE_RIGHT;
86 goto done;
87 case VK_LEFT:
88 if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED)) {
89 ret = KEYCODE_CTRL_LEFT;
90 goto done;
91 }
92 ret = KEYCODE_LEFT;
93 goto done;
94 case VK_HOME: ret = KEYCODE_HOME; goto done;
95 case VK_END: ret = KEYCODE_END; goto done;
96 case VK_PRIOR: ret = KEYCODE_PAGEUP; goto done;
97 case VK_NEXT: ret = KEYCODE_PAGEDOWN; goto done;
98 }
99 continue;
100 }
101#if ENABLE_FEATURE_EURO
102 uchar = record.Event.KeyEvent.uChar.UnicodeChar;
103 achar = uchar & 0x7f;
104 if (achar != uchar)
105 WideCharToMultiByte(CP_ACP, 0, &uchar, 1, &achar, 1, NULL, NULL);
106 ret = achar;
107#else
108 if ( (record.Event.KeyEvent.uChar.AsciiChar & 0x80) == 0x80 ) {
109 s = &record.Event.KeyEvent.uChar.AsciiChar;
110 OemToCharBuff(s, s, 1);
111 }
112 ret = record.Event.KeyEvent.uChar.AsciiChar;
113#endif
114 break;
115 }
116 done:
117 SetConsoleMode(cin, mode);
118 return ret;
119}
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..516aa5ea2
--- /dev/null
+++ b/win32/uname.c
@@ -0,0 +1,47 @@
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 GetSystemInfo(&sys_info);
29 switch (sys_info.wProcessorArchitecture) {
30 case PROCESSOR_ARCHITECTURE_AMD64:
31 strcpy(name->machine, "x86_64");
32 break;
33 case PROCESSOR_ARCHITECTURE_INTEL:
34 if (sys_info.wProcessorLevel < 6) {
35 strcpy(name->machine, "i386");
36 }
37 else {
38 strcpy(name->machine, "i686");
39 }
40 break;
41 default:
42 strcpy(name->machine, unk);
43 break;
44 }
45
46 return 0;
47}
diff --git a/win32/winansi.c b/win32/winansi.c
new file mode 100644
index 000000000..f5d836bd2
--- /dev/null
+++ b/win32/winansi.c
@@ -0,0 +1,908 @@
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
24static HANDLE console = INVALID_HANDLE_VALUE;
25static HANDLE console_in = INVALID_HANDLE_VALUE;
26static WORD plain_attr;
27static WORD attr;
28static int negative;
29
30static void init(void)
31{
32 CONSOLE_SCREEN_BUFFER_INFO sbi;
33
34 if (console != INVALID_HANDLE_VALUE || console_in != INVALID_HANDLE_VALUE)
35 return;
36
37 console = GetStdHandle(STD_OUTPUT_HANDLE);
38 console_in = GetStdHandle(STD_INPUT_HANDLE);
39
40 if (GetConsoleScreenBufferInfo(console, &sbi)) {
41 attr = plain_attr = sbi.wAttributes;
42 negative = 0;
43 }
44}
45
46static int is_console(int fd)
47{
48 init();
49 return isatty(fd) && console != INVALID_HANDLE_VALUE;
50}
51
52static int is_console_in(int fd)
53{
54 init();
55 return isatty(fd) && console_in != INVALID_HANDLE_VALUE;
56}
57
58static int skip_ansi_emulation(void)
59{
60 static char *var = NULL;
61 static int got_var = FALSE;
62
63 if (!got_var) {
64 var = getenv("BB_SKIP_ANSI_EMULATION");
65 got_var = TRUE;
66 }
67
68 return var != NULL;
69}
70
71void set_title(const char *str)
72{
73 if (is_console(STDOUT_FILENO))
74 SetConsoleTitle(str);
75}
76
77static HANDLE dup_handle(HANDLE h)
78{
79 HANDLE h2;
80
81 if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(),
82 &h2, 0, TRUE, DUPLICATE_SAME_ACCESS))
83 return INVALID_HANDLE_VALUE;
84 return h2;
85}
86
87static void use_alt_buffer(int flag)
88{
89 static HANDLE console_orig = INVALID_HANDLE_VALUE;
90 static int initialised = FALSE;
91 CONSOLE_SCREEN_BUFFER_INFO sbi;
92 HANDLE h;
93
94 init();
95
96 if (console == INVALID_HANDLE_VALUE)
97 return;
98
99 if (!initialised) {
100 console_orig = dup_handle(console);
101 initialised = TRUE;
102 }
103
104 if (console_orig == INVALID_HANDLE_VALUE)
105 return;
106
107 if (flag) {
108 SECURITY_ATTRIBUTES sa;
109
110 // handle should be inheritable
111 memset(&sa, 0, sizeof(sa));
112 sa.nLength = sizeof(sa);
113 /* sa.lpSecurityDescriptor = NULL; - memset did it */
114 sa.bInheritHandle = TRUE;
115
116 // create new alternate buffer
117 h = CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE,
118 FILE_SHARE_READ|FILE_SHARE_WRITE, &sa,
119 CONSOLE_TEXTMODE_BUFFER, NULL);
120 if (h == INVALID_HANDLE_VALUE)
121 return;
122
123 if (GetConsoleScreenBufferInfo(console, &sbi))
124 SetConsoleScreenBufferSize(h, sbi.dwSize);
125 }
126 else {
127 // revert to original buffer
128 h = dup_handle(console_orig);
129 if (h == INVALID_HANDLE_VALUE)
130 return;
131 }
132
133 console = h;
134 SetConsoleActiveScreenBuffer(console);
135 close(STDOUT_FILENO);
136 _open_osfhandle((intptr_t)console, O_RDWR|O_BINARY);
137}
138
139#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
140#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
141
142static void set_console_attr(void)
143{
144 WORD attributes = attr;
145 if (negative) {
146 attributes &= ~FOREGROUND_ALL;
147 attributes &= ~BACKGROUND_ALL;
148
149 /* This could probably use a bitmask
150 instead of a series of ifs */
151 if (attr & FOREGROUND_RED)
152 attributes |= BACKGROUND_RED;
153 if (attr & FOREGROUND_GREEN)
154 attributes |= BACKGROUND_GREEN;
155 if (attr & FOREGROUND_BLUE)
156 attributes |= BACKGROUND_BLUE;
157
158 if (attr & BACKGROUND_RED)
159 attributes |= FOREGROUND_RED;
160 if (attr & BACKGROUND_GREEN)
161 attributes |= FOREGROUND_GREEN;
162 if (attr & BACKGROUND_BLUE)
163 attributes |= FOREGROUND_BLUE;
164 }
165 SetConsoleTextAttribute(console, attributes);
166}
167
168static void clear_buffer(DWORD len, COORD pos)
169{
170 DWORD dummy;
171
172 FillConsoleOutputCharacterA(console, ' ', len, pos, &dummy);
173 FillConsoleOutputAttribute(console, plain_attr, len, pos, &dummy);
174}
175
176static void erase_in_line(void)
177{
178 CONSOLE_SCREEN_BUFFER_INFO sbi;
179
180 if (!GetConsoleScreenBufferInfo(console, &sbi))
181 return;
182 clear_buffer(sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition);
183}
184
185static void erase_till_end_of_screen(void)
186{
187 CONSOLE_SCREEN_BUFFER_INFO sbi;
188 DWORD len;
189
190 if(!GetConsoleScreenBufferInfo(console, &sbi))
191 return;
192 len = sbi.dwSize.X - sbi.dwCursorPosition.X +
193 sbi.dwSize.X * (sbi.srWindow.Bottom - sbi.dwCursorPosition.Y);
194 clear_buffer(len, sbi.dwCursorPosition);
195}
196
197void reset_screen(void)
198{
199 CONSOLE_SCREEN_BUFFER_INFO sbi;
200 COORD pos = { 0, 0 };
201
202 /* move to start of screen buffer and clear it all */
203 if (!GetConsoleScreenBufferInfo(console, &sbi))
204 return;
205 SetConsoleCursorPosition(console, pos);
206 clear_buffer(sbi.dwSize.X * sbi.dwSize.Y, pos);
207}
208
209void move_cursor_row(int n)
210{
211 CONSOLE_SCREEN_BUFFER_INFO sbi;
212
213 if(!GetConsoleScreenBufferInfo(console, &sbi))
214 return;
215 sbi.dwCursorPosition.Y += n;
216 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
217}
218
219static void move_cursor_column(int n)
220{
221 CONSOLE_SCREEN_BUFFER_INFO sbi;
222
223 if (!GetConsoleScreenBufferInfo(console, &sbi))
224 return;
225 sbi.dwCursorPosition.X += n;
226 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
227}
228
229static void move_cursor(int x, int y)
230{
231 COORD pos;
232 CONSOLE_SCREEN_BUFFER_INFO sbi;
233
234 if (!GetConsoleScreenBufferInfo(console, &sbi))
235 return;
236 pos.X = sbi.srWindow.Left + x;
237 pos.Y = sbi.srWindow.Top + y;
238 SetConsoleCursorPosition(console, pos);
239}
240
241/* On input pos points to the start of a suspected escape sequence.
242 * If a valid sequence is found return a pointer to the character
243 * following it, otherwise return the original pointer. */
244static char *process_escape(char *pos)
245{
246 const char *str, *func;
247 char *bel;
248 size_t len;
249
250 switch (pos[1]) {
251 case '[':
252 /* go ahead and process "\033[" sequence */
253 break;
254 case ']':
255 if ((pos[2] == '0' || pos[2] == '2') && pos[3] == ';' &&
256 (bel=strchr(pos+4, '\007')) && bel - pos < 260) {
257 /* set console title */
258 *bel++ = '\0';
259 CharToOem(pos+4, pos+4);
260 SetConsoleTitle(pos+4);
261 return bel;
262 }
263 /* invalid "\033]" sequence, fall through */
264 default:
265 return pos;
266 }
267
268 str = pos + 2;
269 len = strspn(str, "0123456789;");
270 func = str + len;
271 switch (*func) {
272 case 'm':
273 do {
274 long val = strtol(str, (char **)&str, 10);
275 switch (val) {
276 case 0: /* reset */
277 attr = plain_attr;
278 negative = 0;
279 break;
280 case 1: /* bold */
281 attr |= FOREGROUND_INTENSITY;
282 break;
283 case 2: /* faint */
284 case 22: /* normal */
285 attr &= ~FOREGROUND_INTENSITY;
286 break;
287 case 3: /* italic */
288 /* Unsupported */
289 break;
290 case 4: /* underline */
291 case 21: /* double underline */
292 /* Wikipedia says this flag does nothing */
293 /* Furthermore, mingw doesn't define this flag
294 attr |= COMMON_LVB_UNDERSCORE; */
295 break;
296 case 24: /* no underline */
297 /* attr &= ~COMMON_LVB_UNDERSCORE; */
298 break;
299 case 5: /* slow blink */
300 case 6: /* fast blink */
301 /* We don't have blink, but we do have
302 background intensity */
303 attr |= BACKGROUND_INTENSITY;
304 break;
305 case 25: /* no blink */
306 attr &= ~BACKGROUND_INTENSITY;
307 break;
308 case 7: /* negative */
309 negative = 1;
310 break;
311 case 27: /* positive */
312 negative = 0;
313 break;
314 case 8: /* conceal */
315 case 28: /* reveal */
316 /* Unsupported */
317 break;
318 case 30: /* Black */
319 attr &= ~FOREGROUND_ALL;
320 break;
321 case 31: /* Red */
322 attr &= ~FOREGROUND_ALL;
323 attr |= FOREGROUND_RED;
324 break;
325 case 32: /* Green */
326 attr &= ~FOREGROUND_ALL;
327 attr |= FOREGROUND_GREEN;
328 break;
329 case 33: /* Yellow */
330 attr &= ~FOREGROUND_ALL;
331 attr |= FOREGROUND_RED | FOREGROUND_GREEN;
332 break;
333 case 34: /* Blue */
334 attr &= ~FOREGROUND_ALL;
335 attr |= FOREGROUND_BLUE;
336 break;
337 case 35: /* Magenta */
338 attr &= ~FOREGROUND_ALL;
339 attr |= FOREGROUND_RED | FOREGROUND_BLUE;
340 break;
341 case 36: /* Cyan */
342 attr &= ~FOREGROUND_ALL;
343 attr |= FOREGROUND_GREEN | FOREGROUND_BLUE;
344 break;
345 case 37: /* White */
346 attr |= FOREGROUND_RED |
347 FOREGROUND_GREEN |
348 FOREGROUND_BLUE;
349 break;
350 case 38: /* Unknown */
351 break;
352 case 39: /* reset */
353 attr &= ~FOREGROUND_ALL;
354 attr |= (plain_attr & FOREGROUND_ALL);
355 break;
356 case 40: /* Black */
357 attr &= ~BACKGROUND_ALL;
358 break;
359 case 41: /* Red */
360 attr &= ~BACKGROUND_ALL;
361 attr |= BACKGROUND_RED;
362 break;
363 case 42: /* Green */
364 attr &= ~BACKGROUND_ALL;
365 attr |= BACKGROUND_GREEN;
366 break;
367 case 43: /* Yellow */
368 attr &= ~BACKGROUND_ALL;
369 attr |= BACKGROUND_RED | BACKGROUND_GREEN;
370 break;
371 case 44: /* Blue */
372 attr &= ~BACKGROUND_ALL;
373 attr |= BACKGROUND_BLUE;
374 break;
375 case 45: /* Magenta */
376 attr &= ~BACKGROUND_ALL;
377 attr |= BACKGROUND_RED | BACKGROUND_BLUE;
378 break;
379 case 46: /* Cyan */
380 attr &= ~BACKGROUND_ALL;
381 attr |= BACKGROUND_GREEN | BACKGROUND_BLUE;
382 break;
383 case 47: /* White */
384 attr |= BACKGROUND_RED |
385 BACKGROUND_GREEN |
386 BACKGROUND_BLUE;
387 break;
388 case 48: /* Unknown */
389 break;
390 case 49: /* reset */
391 attr &= ~BACKGROUND_ALL;
392 attr |= (plain_attr & BACKGROUND_ALL);
393 break;
394 default:
395 /* Unsupported code */
396 return pos;
397 }
398 str++;
399 } while (*(str-1) == ';');
400
401 set_console_attr();
402 break;
403 case 'A': /* up */
404 move_cursor_row(-strtol(str, (char **)&str, 10));
405 break;
406 case 'B': /* down */
407 move_cursor_row(strtol(str, (char **)&str, 10));
408 break;
409 case 'C': /* forward */
410 move_cursor_column(strtol(str, (char **)&str, 10));
411 break;
412 case 'D': /* back */
413 move_cursor_column(-strtol(str, (char **)&str, 10));
414 break;
415 case 'H':
416 if (!len)
417 move_cursor(0, 0);
418 else {
419 int row, col = 1;
420
421 row = strtol(str, (char **)&str, 10);
422 if (*str == ';') {
423 col = strtol(str+1, (char **)&str, 10);
424 }
425 move_cursor(col > 0 ? col-1 : 0, row > 0 ? row-1 : 0);
426 }
427 break;
428 case 'J':
429 erase_till_end_of_screen();
430 break;
431 case 'K':
432 erase_in_line();
433 break;
434 case '?':
435 if (strncmp(str+1, "1049", 4) == 0 &&
436 (str[5] == 'h' || str[5] == 'l') ) {
437 use_alt_buffer(str[5] == 'h');
438 func = str + 5;
439 break;
440 }
441 /* fall through */
442 default:
443 /* Unsupported code */
444 return pos;
445 }
446
447 return (char *)func + 1;
448}
449
450#if ENABLE_FEATURE_EURO
451void init_codepage(void)
452{
453 if (GetConsoleCP() == 850 && GetConsoleOutputCP() == 850) {
454 SetConsoleCP(858);
455 SetConsoleOutputCP(858);
456 }
457}
458
459static BOOL winansi_CharToOemBuff(LPCSTR s, LPSTR d, DWORD len)
460{
461 WCHAR *buf;
462 int i;
463
464 if (!s || !d)
465 return FALSE;
466
467 buf = xmalloc(len*sizeof(WCHAR));
468 MultiByteToWideChar(CP_ACP, 0, s, len, buf, len);
469 WideCharToMultiByte(CP_OEMCP, 0, buf, len, d, len, NULL, NULL);
470 if (GetConsoleOutputCP() == 858) {
471 for (i=0; i<len; ++i) {
472 if (buf[i] == 0x20ac) {
473 d[i] = 0xd5;
474 }
475 }
476 }
477 free(buf);
478 return TRUE;
479}
480
481static BOOL winansi_CharToOem(LPCSTR s, LPSTR d)
482{
483 if (!s || !d)
484 return FALSE;
485 return winansi_CharToOemBuff(s, d, strlen(s)+1);
486}
487
488static BOOL winansi_OemToCharBuff(LPCSTR s, LPSTR d, DWORD len)
489{
490 WCHAR *buf;
491 int i;
492
493 if (!s || !d)
494 return FALSE;
495
496 buf = xmalloc(len*sizeof(WCHAR));
497 MultiByteToWideChar(CP_OEMCP, 0, s, len, buf, len);
498 WideCharToMultiByte(CP_ACP, 0, buf, len, d, len, NULL, NULL);
499 if (GetConsoleOutputCP() == 858) {
500 for (i=0; i<len; ++i) {
501 if (buf[i] == 0x0131) {
502 d[i] = 0x80;
503 }
504 }
505 }
506 free(buf);
507 return TRUE;
508}
509
510# undef CharToOemBuff
511# undef CharToOem
512# undef OemToCharBuff
513# define CharToOemBuff winansi_CharToOemBuff
514# define CharToOem winansi_CharToOem
515# define OemToCharBuff winansi_OemToCharBuff
516#endif
517
518static int ansi_emulate(const char *s, FILE *stream)
519{
520 int rv = 0;
521 const unsigned char *t;
522 char *pos, *str;
523 size_t cur_len;
524 static size_t max_len = 0;
525 static char *mem = NULL;
526
527 /* if no special treatment is required output the string as-is */
528 for ( t=(unsigned char *)s; *t; ++t ) {
529 if ( *t == '\033' || *t > 0x7f ) {
530 break;
531 }
532 }
533
534 if ( *t == '\0' ) {
535 return fputs(s, stream) == EOF ? EOF : strlen(s);
536 }
537
538 /*
539 * Make a writable copy of the string and retain array for reuse.
540 * The test above guarantees that the string length won't be zero
541 * so the array will always be allocated.
542 */
543 cur_len = strlen(s);
544 if ( cur_len > max_len ) {
545 free(mem);
546 mem = strdup(s);
547 max_len = cur_len;
548 }
549 else {
550 strcpy(mem, s);
551 }
552 pos = str = mem;
553
554 while (*pos) {
555 pos = strchr(str, '\033');
556 if (pos && !skip_ansi_emulation()) {
557 size_t len = pos - str;
558
559 if (len) {
560 *pos = '\0'; /* NB, '\033' has been overwritten */
561 CharToOem(str, str);
562 if (fputs(str, stream) == EOF)
563 return EOF;
564 rv += len;
565 }
566
567 if (fflush(stream) == EOF)
568 return EOF;
569
570 str = process_escape(pos);
571 if (str == pos) {
572 if (fputc('\033', stream) == EOF)
573 return EOF;
574 ++str;
575 }
576 rv += str - pos;
577 pos = str;
578
579 if (fflush(stream) == EOF)
580 return EOF;
581
582 } else {
583 rv += strlen(str);
584 CharToOem(str, str);
585 return fputs(str, stream) == EOF ? EOF : rv;
586 }
587 }
588 return rv;
589}
590
591int winansi_putchar(int c)
592{
593 char t = c;
594 char *s = &t;
595
596 if (!is_console(STDOUT_FILENO))
597 return putchar(c);
598
599 CharToOemBuff(s, s, 1);
600 return putchar(t) == EOF ? EOF : c;
601}
602
603int winansi_puts(const char *s)
604{
605 return (winansi_fputs(s, stdout) == EOF || putchar('\n') == EOF) ? EOF : 0;
606}
607
608static sighandler_t sigpipe_handler = SIG_DFL;
609
610#undef signal
611sighandler_t winansi_signal(int signum, sighandler_t handler)
612{
613 sighandler_t old;
614
615 if (signum == SIGPIPE) {
616 old = sigpipe_handler;
617 sigpipe_handler = handler;
618 return old;
619 }
620 return signal(signum, handler);
621}
622
623static void check_pipe_fd(int fd)
624{
625 int error = GetLastError();
626
627 if ((error == ERROR_NO_DATA &&
628 GetFileType((HANDLE)_get_osfhandle(fd)) == FILE_TYPE_PIPE) ||
629 error == ERROR_BROKEN_PIPE) {
630 if (sigpipe_handler == SIG_DFL)
631 exit(128+SIGPIPE);
632 else /* SIG_IGN */
633 errno = EPIPE;
634 }
635}
636
637static void check_pipe(FILE *stream)
638{
639 int fd = fileno(stream);
640
641 if (fd != -1 && ferror(stream)) {
642 check_pipe_fd(fd);
643 }
644}
645
646size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
647{
648 size_t lsize, lmemb, ret;
649 char *str;
650 int rv;
651
652 lsize = MIN(size, nmemb);
653 lmemb = MAX(size, nmemb);
654 if (lsize != 1 || !is_console(fileno(stream))) {
655 SetLastError(0);
656 if ((ret=fwrite(ptr, size, nmemb, stream)) < nmemb)
657 check_pipe(stream);
658 return ret;
659 }
660
661 str = xmalloc(lmemb+1);
662 memcpy(str, ptr, lmemb);
663 str[lmemb] = '\0';
664
665 rv = ansi_emulate(str, stream);
666 free(str);
667
668 return rv == EOF ? 0 : nmemb;
669}
670
671int winansi_fputs(const char *str, FILE *stream)
672{
673 int ret;
674
675 if (!is_console(fileno(stream))) {
676 SetLastError(0);
677 if ((ret=fputs(str, stream)) == EOF)
678 check_pipe(stream);
679 return ret;
680 }
681
682 return ansi_emulate(str, stream) == EOF ? EOF : 0;
683}
684
685#if !defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO
686/*
687 * Prior to Windows 10 vsnprintf was incompatible with the C99 standard.
688 * Implement a replacement using _vsnprintf.
689 */
690int winansi_vsnprintf(char *buf, size_t size, const char *format, va_list list)
691{
692 size_t len;
693 va_list list2;
694
695 va_copy(list2, list);
696 len = _vsnprintf(NULL, 0, format, list2);
697 if (len < 0)
698 return -1;
699
700 _vsnprintf(buf, size, format, list);
701 buf[size-1] = '\0';
702 return len;
703}
704#endif
705
706int winansi_vfprintf(FILE *stream, const char *format, va_list list)
707{
708 int len, rv;
709 char small_buf[256];
710 char *buf = small_buf;
711 va_list cp;
712
713 if (!is_console(fileno(stream)))
714 goto abort;
715
716 va_copy(cp, list);
717 len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
718 va_end(cp);
719
720 if (len > sizeof(small_buf) - 1) {
721 buf = malloc(len + 1);
722 if (!buf)
723 goto abort;
724
725 va_copy(cp, list);
726 len = vsnprintf(buf, len + 1, format, cp);
727 va_end(cp);
728 }
729
730 if (len == -1)
731 goto abort;
732
733 rv = ansi_emulate(buf, stream);
734
735 if (buf != small_buf)
736 free(buf);
737 return rv;
738
739abort:
740 SetLastError(0);
741 if ((rv=vfprintf(stream, format, list)) == EOF)
742 check_pipe(stream);
743 return rv;
744}
745
746int winansi_fprintf(FILE *stream, const char *format, ...)
747{
748 va_list list;
749 int rv;
750
751 va_start(list, format);
752 rv = winansi_vfprintf(stream, format, list);
753 va_end(list);
754
755 return rv;
756}
757
758int winansi_printf(const char *format, ...)
759{
760 va_list list;
761 int rv;
762
763 va_start(list, format);
764 rv = winansi_vfprintf(stdout, format, list);
765 va_end(list);
766
767 return rv;
768}
769
770static int ansi_emulate_write(int fd, const void *buf, size_t count)
771{
772 int rv = 0, i;
773 int special = FALSE, has_null = FALSE;
774 const unsigned char *s = (const unsigned char *)buf;
775 char *pos, *str;
776 size_t len, out_len;
777 static size_t max_len = 0;
778 static char *mem = NULL;
779
780 for ( i=0; i<count; ++i ) {
781 if ( s[i] == '\033' || s[i] > 0x7f ) {
782 special = TRUE;
783 }
784 else if ( !s[i] ) {
785 has_null = TRUE;
786 }
787 }
788
789 /*
790 * If no special treatment is required or the data contains NUL
791 * characters output the string as-is.
792 */
793 if ( !special || has_null ) {
794 return write(fd, buf, count);
795 }
796
797 /* make a writable copy of the data and retain array for reuse */
798 if ( count > max_len ) {
799 free(mem);
800 mem = malloc(count+1);
801 max_len = count;
802 }
803 memcpy(mem, buf, count);
804 mem[count] = '\0';
805 pos = str = mem;
806
807 /* we've checked the data doesn't contain any NULs */
808 while (*pos) {
809 pos = strchr(str, '\033');
810 if (pos && !skip_ansi_emulation()) {
811 len = pos - str;
812
813 if (len) {
814 CharToOemBuff(str, str, len);
815 out_len = write(fd, str, len);
816 if (out_len == -1)
817 return -1;
818 rv += out_len;
819 }
820
821 str = process_escape(pos);
822 if (str == pos) {
823 if (write(fd, pos, 1) == -1)
824 return -1;
825 ++str;
826 }
827 rv += str - pos;
828 pos = str;
829 } else {
830 len = strlen(str);
831 CharToOem(str, str);
832 out_len = write(fd, str, len);
833 return (out_len == -1) ? -1 : rv+out_len;
834 }
835 }
836 return rv;
837}
838
839int winansi_write(int fd, const void *buf, size_t count)
840{
841 if (!is_console(fd)) {
842 int ret;
843
844 SetLastError(0);
845 if ((ret=write(fd, buf, count)) == -1) {
846 check_pipe_fd(fd);
847 }
848 return ret;
849 }
850
851 return ansi_emulate_write(fd, buf, count);
852}
853
854int winansi_read(int fd, void *buf, size_t count)
855{
856 int rv;
857
858 rv = mingw_read(fd, buf, count);
859 if (!is_console_in(fd))
860 return rv;
861
862 if ( rv > 0 ) {
863 OemToCharBuff(buf, buf, rv);
864 }
865
866 return rv;
867}
868
869int winansi_getc(FILE *stream)
870{
871 int rv;
872
873 rv = getc(stream);
874 if (!is_console_in(fileno(stream)))
875 return rv;
876
877 if ( rv != EOF ) {
878 unsigned char c = (unsigned char)rv;
879 char *s = (char *)&c;
880 OemToCharBuff(s, s, 1);
881 rv = (int)c;
882 }
883
884 return rv;
885}
886
887/* Ensure that isatty(fd) returns 0 for the NUL device */
888int mingw_isatty(int fd)
889{
890 int result = _isatty(fd);
891
892 if (result) {
893 HANDLE handle = (HANDLE) _get_osfhandle(fd);
894 DWORD mode;
895
896 if (handle == INVALID_HANDLE_VALUE)
897 return 0;
898
899 /* check if its a device (i.e. console, printer, serial port) */
900 if (GetFileType(handle) != FILE_TYPE_CHAR)
901 return 0;
902
903 if (!GetConsoleMode(handle, &mode))
904 return 0;
905 }
906
907 return result;
908}