aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--Config.in135
-rw-r--r--Makefile26
-rw-r--r--Makefile.custom22
-rw-r--r--Makefile.flags9
-rw-r--r--README1
-rw-r--r--README.md39
-rw-r--r--applets/.gitignore3
-rw-r--r--applets/applet_tables.c9
-rw-r--r--archival/ar.c19
-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.c7
-rw-r--r--archival/libarchive/unsafe_symlink_target.c19
-rw-r--r--archival/rpm.c16
-rw-r--r--archival/tar.c25
-rw-r--r--archival/unzip.c5
-rw-r--r--configs/mingw32_defconfig1217
-rw-r--r--configs/mingw64_defconfig1217
-rw-r--r--console-tools/reset.c15
-rw-r--r--coreutils/date.c25
-rw-r--r--coreutils/dd.c38
-rw-r--r--coreutils/du.c2
-rw-r--r--coreutils/expr.c2
-rw-r--r--coreutils/factor.c4
-rw-r--r--coreutils/ls.c42
-rw-r--r--coreutils/nproc.c18
-rw-r--r--coreutils/od_bloaty.c7
-rw-r--r--coreutils/printf.c68
-rw-r--r--coreutils/shred.c10
-rw-r--r--coreutils/shuf.c2
-rw-r--r--coreutils/split.c8
-rw-r--r--coreutils/stat.c1
-rw-r--r--coreutils/sum.c4
-rw-r--r--coreutils/timeout.c71
-rw-r--r--coreutils/yes.c4
-rw-r--r--debianutils/pipe_progress.c7
-rw-r--r--debianutils/which.c48
-rw-r--r--e2fsprogs/chattr.c65
-rw-r--r--e2fsprogs/e2fs_lib.c64
-rw-r--r--e2fsprogs/e2fs_lib.h12
-rw-r--r--e2fsprogs/lsattr.c18
-rw-r--r--editors/awk.c27
-rw-r--r--editors/diff.c26
-rw-r--r--editors/sed.c21
-rw-r--r--editors/vi.c164
-rw-r--r--findutils/find.c10
-rw-r--r--findutils/xargs.c95
-rw-r--r--include/bb_archive.h14
-rw-r--r--include/libbb.h79
-rw-r--r--include/mingw.h585
-rw-r--r--include/platform.h46
-rw-r--r--libbb/Kbuild.src50
-rw-r--r--libbb/appletlib.c207
-rw-r--r--libbb/bb_qsort.c12
-rw-r--r--libbb/compare_string_array.c41
-rw-r--r--libbb/concat_path_file.c6
-rw-r--r--libbb/copy_file.c6
-rw-r--r--libbb/dump.c36
-rw-r--r--libbb/executable.c18
-rw-r--r--libbb/find_mount_point.c31
-rw-r--r--libbb/find_pid_by_name.c6
-rw-r--r--libbb/get_last_path_component.c43
-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.c153
-rw-r--r--libbb/make_directory.c8
-rw-r--r--libbb/messages.c4
-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/signals.c2
-rw-r--r--libbb/time.c9
-rw-r--r--libbb/u_signal_names.c15
-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.c8
-rw-r--r--libbb/xreadlink.c43
-rw-r--r--loginutils/su.c1
-rw-r--r--loginutils/suw32.c72
-rw-r--r--miscutils/bbconfig.c1
-rw-r--r--miscutils/bc.c15
-rw-r--r--miscutils/dc.c2
-rw-r--r--miscutils/iconv.c1761
-rw-r--r--miscutils/inotifyd.c211
-rw-r--r--miscutils/less.c90
-rw-r--r--miscutils/man.c32
-rw-r--r--miscutils/time.c55
-rw-r--r--miscutils/ts.c8
-rw-r--r--networking/ftpgetput.c6
-rw-r--r--networking/httpd.c176
-rw-r--r--networking/nc.c57
-rw-r--r--networking/ssl_client.c21
-rw-r--r--networking/tls.c2
-rw-r--r--networking/wget.c33
-rw-r--r--procps/free.c9
-rw-r--r--procps/iostat.c2
-rw-r--r--procps/kill.c2
-rw-r--r--procps/mpstat.c2
-rw-r--r--procps/ps.c12
-rw-r--r--procps/smemcap.c1
-rw-r--r--scripts/basic/.gitignore3
-rw-r--r--scripts/basic/docproc.c15
-rw-r--r--scripts/basic/fixdep.c68
-rw-r--r--scripts/basic/split-include.c13
-rwxr-xr-xscripts/bloat-o-meter25
-rw-r--r--scripts/kconfig/.gitignore1
-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.c25
-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.c2116
-rw-r--r--shell/math.h2
-rw-r--r--shell/random.c11
-rw-r--r--shell/random.h3
-rw-r--r--shell/shell_common.c75
-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/Kbuild31
-rw-r--r--win32/arpa/inet.h0
-rw-r--r--win32/dirent.c96
-rw-r--r--win32/dirent.h19
-rw-r--r--win32/env.c107
-rw-r--r--win32/fnmatch.c522
-rw-r--r--win32/fnmatch.h84
-rw-r--r--win32/fsync.c75
-rw-r--r--win32/grp.h0
-rw-r--r--win32/inet_pton.c95
-rw-r--r--win32/ioctl.c46
-rw-r--r--win32/isaac.c222
-rw-r--r--win32/lazyload.h27
-rw-r--r--win32/match_class.c7
-rw-r--r--win32/match_class.h11
-rw-r--r--win32/mingw.c2107
-rw-r--r--win32/mntent.c94
-rw-r--r--win32/mntent.h33
-rw-r--r--win32/net.c146
-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.c602
-rw-r--r--win32/poll.h53
-rw-r--r--win32/popen.c336
-rw-r--r--win32/process.c806
-rw-r--r--win32/pwd.h0
-rw-r--r--win32/regcomp.c3936
-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.src28
-rw-r--r--win32/resources/README9
-rw-r--r--win32/resources/aterm.icobin0 -> 15086 bytes
-rw-r--r--win32/resources/dummy.c2
-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.c592
-rw-r--r--win32/sh_random.c76
-rw-r--r--win32/statfs.c70
-rw-r--r--win32/strndup.c36
-rw-r--r--win32/strptime.c603
-rw-r--r--win32/sys/inotify.h0
-rw-r--r--win32/sys/ioctl.h0
-rw-r--r--win32/sys/mman.h0
-rw-r--r--win32/sys/resource.h11
-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/timegm.c134
-rw-r--r--win32/uname.c42
-rw-r--r--win32/winansi.c1121
197 files changed, 30343 insertions, 184 deletions
diff --git a/.gitignore b/.gitignore
index becd9bf6d..764cc058a 100644
--- a/.gitignore
+++ b/.gitignore
@@ -18,8 +18,11 @@ Config.in
18# Normal output 18# Normal output
19# 19#
20/busybox 20/busybox
21/busybox.exe
21/busybox_old 22/busybox_old
23/busybox_old.exe
22/busybox_unstripped* 24/busybox_unstripped*
25win32/resources/busybox-w32.manifest
23 26
24# 27#
25# Backups / patches 28# Backups / patches
diff --git a/Config.in b/Config.in
index a98a8b15b..da8a72a8e 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
@@ -357,6 +371,127 @@ config FEATURE_SYSLOG
357 #This option is auto-selected when you select any applet which may 371 #This option is auto-selected when you select any applet which may
358 #send its output to syslog. You do not need to select it manually. 372 #send its output to syslog. You do not need to select it manually.
359 373
374comment 'Settings for MINGW32'
375
376config GLOBBING
377 bool "Allow busybox.exe to expand wildcards"
378 default y
379 depends on PLATFORM_MINGW32
380 help
381 In Microsoft Windows expansion of wildcards on the command line
382 ('globbing') is handled by the C runtime while the BusyBox shell
383 does its own wildcard expansion. In most circumstances BusyBox
384 will do the right thing. If it doesn't or if you don't need to
385 use BusyBox applets from the Windows Command Prompt you can stop
386 busybox.exe from expanding wildcards by setting this to 'N'.
387
388choice
389 prompt "Random number generator"
390 default FEATURE_PRNG_SHELL
391 depends on PLATFORM_MINGW32
392 help
393 BusyBox on Microsoft Windows uses a pseudo-random number
394 generator to emulate the Linux /dev/urandom device. There
395 are two options:
396 - The shell's built-in PRNG.
397 - Bob Jenkins' ISAAC. This is intended to be a secure PRNG. It's
398 slightly faster than the shell's PRNG but is larger both in terms
399 of code and runtime memory.
400
401config FEATURE_PRNG_SHELL
402 bool "Use shell PRNG"
403
404config FEATURE_PRNG_ISAAC
405 bool "Use ISAAC PRNG"
406
407endchoice
408
409config FEATURE_RESOURCES
410 bool "Include resources in binary"
411 default y
412 depends on PLATFORM_MINGW32
413 help
414 Microsoft Windows applications can contain non-executable resources
415 of various sorts.
416
417config FEATURE_VERSIONINFO
418 bool "Include version information in binary (1.0 kb)"
419 default y
420 depends on FEATURE_RESOURCES
421 help
422 Include version information in the application.
423
424config FEATURE_ICON
425 bool "Include application icon in binary"
426 default y
427 depends on FEATURE_RESOURCES
428 help
429 Microsoft Windows applications can contain icons which are used in
430 various places in the user interface. Each icon adds 15 Kbytes to
431 the size of the binary.
432
433choice
434 prompt "Application icon"
435 default FEATURE_ICON_ALL
436 depends on FEATURE_ICON
437
438config FEATURE_ICON_ATERM
439 bool "Adwaita terminal"
440
441config FEATURE_ICON_STERM
442 bool "Adwaita terminal (symbolic)"
443
444config FEATURE_ICON_ALL
445 bool "All available icons"
446
447endchoice
448
449config FEATURE_EURO
450 bool "Support the euro currency symbol (0.5 kb)"
451 default y
452 depends on PLATFORM_MINGW32
453 help
454 Support the entry and display of the euro currency symbol. This
455 requires the OEM code page to be 858. If the OEM code page of
456 the console is 850 when BusyBox starts it's changed to 858.
457
458config SKIP_ANSI_EMULATION_DEFAULT
459 int "Default setting for ANSI escape sequences"
460 default 2
461 range 0 2
462 depends on PLATFORM_MINGW32
463 help
464 Control how ANSI escape sequences are handled. Possible values
465 are:
466
467 0 Always emulate escape sequences.
468 1 Always emit escape sequences.
469 2 Emit escape sequences if the terminal supports them, otherwise
470 emulate them.
471
472 Setting the environment variable BB_SKIP_ANSI_EMULATION overrides
473 this default.
474
475config FEATURE_IMPROVED_COLOUR_MAPPING
476 bool "More accurate colour mapping for ANSI emulation (0.6 kb)"
477 default y
478 depends on PLATFORM_MINGW32
479 help
480 Use a more accurate technique to map RGB colours to the standard
481 Windows console colours.
482
483config FEATURE_EXTRA_FILE_DATA
484 bool "Read additional file metadata (2.1 kb)"
485 default y
486 depends on PLATFORM_MINGW32
487 help
488 Read additional file metadata: device id, inode number and number
489 of hard links. This may slow down some file operations but it
490 permits extra features such as warning of attempts to copy a file
491 onto itself or to store a tar archive in itself. Also try to
492 determine the ownership of files so that, for example, 'ls' can
493 distinguish files belonging to the current user.
494
360comment 'Build Options' 495comment 'Build Options'
361 496
362config STATIC 497config STATIC
diff --git a/Makefile b/Makefile
index b2ce46c7c..783f55f87 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
@@ -305,6 +306,15 @@ KALLSYMS = scripts/kallsyms
305PERL = perl 306PERL = perl
306CHECK = sparse 307CHECK = sparse
307 308
309# Handle MSYS2 weirdness
310ifneq ($(CROSS_COMPILE),)
311ifeq ($(shell _= command -v $(AR)),)
312AR := $(CROSS_COMPILE)gcc-ar
313STRIP := strip
314WINDRES := windres
315endif
316endif
317
308CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF) 318CHECKFLAGS := -D__linux__ -Dlinux -D__STDC__ -Dunix -D__unix__ -Wbitwise $(CF)
309MODFLAGS = -DMODULE 319MODFLAGS = -DMODULE
310CFLAGS_MODULE = $(MODFLAGS) 320CFLAGS_MODULE = $(MODFLAGS)
@@ -312,6 +322,7 @@ AFLAGS_MODULE = $(MODFLAGS)
312LDFLAGS_MODULE = -r 322LDFLAGS_MODULE = -r
313CFLAGS_KERNEL = 323CFLAGS_KERNEL =
314AFLAGS_KERNEL = 324AFLAGS_KERNEL =
325EXEEXT =
315 326
316 327
317# Use LINUXINCLUDE when you must reference the include/ directory. 328# Use LINUXINCLUDE when you must reference the include/ directory.
@@ -330,7 +341,7 @@ KERNELVERSION = $(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
330 341
331export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \ 342export VERSION PATCHLEVEL SUBLEVEL KERNELRELEASE KERNELVERSION \
332 ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \ 343 ARCH CONFIG_SHELL HOSTCC HOSTCFLAGS CROSS_COMPILE AS LD CC \
333 CPP AR NM STRIP OBJCOPY OBJDUMP MAKE AWK GENKSYMS PERL UTS_MACHINE \ 344 CPP AR NM STRIP OBJCOPY OBJDUMP WINDRES MAKE AWK GENKSYMS PERL UTS_MACHINE \
334 HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS 345 HOSTCXX HOSTCXXFLAGS LDFLAGS_MODULE CHECK CHECKFLAGS
335 346
336export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS 347export CPPFLAGS NOSTDINC_FLAGS LINUXINCLUDE OBJCOPYFLAGS LDFLAGS
@@ -462,6 +473,7 @@ scripts_basic: include/autoconf.h
462# Objects we will link into busybox / subdirs we need to visit 473# Objects we will link into busybox / subdirs we need to visit
463core-y := \ 474core-y := \
464 applets/ \ 475 applets/ \
476 win32/resources/ \
465 477
466libs-y := \ 478libs-y := \
467 archival/ \ 479 archival/ \
@@ -492,6 +504,7 @@ libs-y := \
492 sysklogd/ \ 504 sysklogd/ \
493 util-linux/ \ 505 util-linux/ \
494 util-linux/volume_id/ \ 506 util-linux/volume_id/ \
507 win32/ \
495 508
496endif # KBUILD_EXTMOD 509endif # KBUILD_EXTMOD
497 510
@@ -532,7 +545,7 @@ endif
532# command line. 545# command line.
533# This allow a user to issue only 'make' to build a kernel including modules 546# 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 547# Defaults busybox but it is usually overridden in the arch makefile
535all: busybox doc 548all: busybox$(EXEEXT) doc
536 549
537# arch Makefile may override CC so keep this after arch Makefile is included 550# 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) 551#bbox# NOSTDINC_FLAGS += -nostdinc -isystem $(shell $(CC) -print-file-name=include)
@@ -715,16 +728,16 @@ debug_kallsyms: .tmp_map$(last_kallsyms)
715endif # ifdef CONFIG_KALLSYMS 728endif # ifdef CONFIG_KALLSYMS
716 729
717# busybox image - including updated kernel symbols 730# busybox image - including updated kernel symbols
718busybox_unstripped: $(busybox-all) FORCE 731busybox_unstripped$(EXEEXT): $(busybox-all) FORCE
719 $(call if_changed_rule,busybox__) 732 $(call if_changed_rule,busybox__)
720 $(Q)rm -f .old_version 733 $(Q)rm -f .old_version
721 734
722busybox: busybox_unstripped 735busybox$(EXEEXT): busybox_unstripped$(EXEEXT)
723ifeq ($(SKIP_STRIP),y) 736ifeq ($(SKIP_STRIP),y)
724 $(Q)cp $< $@ 737 $(Q)cp $< $@
725else 738else
726 $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \ 739 $(Q)$(STRIP) -s --remove-section=.note --remove-section=.comment \
727 busybox_unstripped -o $@ 740 busybox_unstripped$(EXEEXT) -o $@
728# strip is confused by PIE executable and does not set exec bits 741# strip is confused by PIE executable and does not set exec bits
729 $(Q)chmod a+x $@ 742 $(Q)chmod a+x $@
730endif 743endif
@@ -966,7 +979,7 @@ endif # CONFIG_MODULES
966 979
967# Directories & files removed with 'make clean' 980# Directories & files removed with 'make clean'
968CLEAN_DIRS += $(MODVERDIR) _install 0_lib 981CLEAN_DIRS += $(MODVERDIR) _install 0_lib
969CLEAN_FILES += busybox busybox_unstripped* busybox.links \ 982CLEAN_FILES += busybox$(EXEEXT) busybox_unstripped* busybox.links \
970 System.map .kernelrelease \ 983 System.map .kernelrelease \
971 .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map 984 .tmp_kallsyms* .tmp_version .tmp_busybox* .tmp_System.map
972 985
@@ -984,6 +997,7 @@ MRPROPER_FILES += .config .config.old include/asm .version .old_version \
984 include/applets.h \ 997 include/applets.h \
985 include/usage.h \ 998 include/usage.h \
986 applets/usage \ 999 applets/usage \
1000 win32/resources/busybox-w32.manifest \
987 .kernelrelease Module.symvers tags TAGS cscope* \ 1001 .kernelrelease Module.symvers tags TAGS cscope* \
988 busybox_old 1002 busybox_old
989 1003
diff --git a/Makefile.custom b/Makefile.custom
index 6f679c4e1..e2f6d1c2f 100644
--- a/Makefile.custom
+++ b/Makefile.custom
@@ -104,29 +104,29 @@ checkhelp:
104 $(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./))) 104 $(patsubst %,$(srctree)/%,$(wildcard $(patsubst %,%/Config.in,$(busybox-dirs) ./)))
105 105
106.PHONY: sizes 106.PHONY: sizes
107sizes: busybox_unstripped 107sizes: busybox_unstripped$(EXEEXT)
108 $(NM) --size-sort $(<) 108 $(NM) --size-sort $(<)
109 109
110.PHONY: bloatcheck 110.PHONY: bloatcheck
111bloatcheck: busybox_old busybox_unstripped 111bloatcheck: busybox_old$(EXEEXT) busybox_unstripped$(EXEEXT)
112 @$(srctree)/scripts/bloat-o-meter busybox_old busybox_unstripped 112 @$(srctree)/scripts/bloat-o-meter busybox_old$(EXEEXT) busybox_unstripped$(EXEEXT)
113 @$(CROSS_COMPILE)size busybox_old busybox_unstripped 113 @$(CROSS_COMPILE)size busybox_old$(EXEEXT) busybox_unstripped$(EXEEXT)
114 114
115.PHONY: baseline 115.PHONY: baseline
116baseline: busybox_unstripped 116baseline: busybox_unstripped$(EXEEXT)
117 @mv busybox_unstripped busybox_old 117 @mv busybox_unstripped$(EXEEXT) busybox_old$(EXEEXT)
118 118
119.PHONY: objsizes 119.PHONY: objsizes
120objsizes: busybox_unstripped 120objsizes: busybox_unstripped$(EXEEXT)
121 $(srctree)/scripts/objsizes 121 $(srctree)/scripts/objsizes
122 122
123.PHONY: stksizes 123.PHONY: stksizes
124stksizes: busybox_unstripped 124stksizes: busybox_unstripped$(EXEEXT)
125 $(CROSS_COMPILE)objdump -d busybox_unstripped | $(srctree)/scripts/checkstack.pl $(ARCH) | uniq 125 $(CROSS_COMPILE)objdump -d busybox_unstripped$(EXEEXT) | $(srctree)/scripts/checkstack.pl $(ARCH) | uniq
126 126
127.PHONY: bigdata 127.PHONY: bigdata
128bigdata: busybox_unstripped 128bigdata: busybox_unstripped$(EXEEXT)
129 $(CROSS_COMPILE)nm --size-sort busybox_unstripped | grep -vi ' [trw] ' 129 $(CROSS_COMPILE)nm --size-sort busybox_unstripped$(EXEEXT) | grep -vi ' [trw] '
130 130
131# Documentation Targets 131# Documentation Targets
132.PHONY: doc 132.PHONY: doc
diff --git a/Makefile.flags b/Makefile.flags
index c34356230..f202a5f54 100644
--- a/Makefile.flags
+++ b/Makefile.flags
@@ -15,6 +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 -DMINGW_VER=$(squote)$(quote)$(MINGW_VER)$(quote)$(squote) \
18 -DBB_VER=$(squote)$(quote)$(BB_VER)$(quote)$(squote) 19 -DBB_VER=$(squote)$(quote)$(BB_VER)$(quote)$(squote)
19 20
20CFLAGS += $(call cc-option,-Wall,) 21CFLAGS += $(call cc-option,-Wall,)
@@ -145,6 +146,13 @@ CFLAGS += --sysroot=$(CONFIG_SYSROOT)
145export SYSROOT=$(CONFIG_SYSROOT) 146export SYSROOT=$(CONFIG_SYSROOT)
146endif 147endif
147 148
149ifeq ($(CONFIG_PLATFORM_MINGW32),y)
150CFLAGS += -Iwin32 -DHAVE_STRING_H=1 -DHAVE_CONFIG_H=0 -fno-builtin-stpcpy -fno-builtin-stpncpy -fno-ident -fno-builtin-strndup
151EXEEXT = .exe
152LDLIBS += ws2_32
153endif
154
155ifneq ($(CONFIG_PLATFORM_MINGW32),y)
148# libm may be needed for dc, awk, ntpd 156# libm may be needed for dc, awk, ntpd
149LDLIBS += m 157LDLIBS += m
150# Android has no separate crypt library 158# Android has no separate crypt library
@@ -160,6 +168,7 @@ endif
160ifeq ($(RT_AVAILABLE),y) 168ifeq ($(RT_AVAILABLE),y)
161LDLIBS += rt 169LDLIBS += rt
162endif 170endif
171endif
163 172
164# libpam may use libpthread, libdl and/or libaudit. 173# libpam may use libpthread, libdl and/or libaudit.
165# On some platforms that requires an explicit -lpthread, -ldl, -laudit. 174# On some platforms that requires an explicit -lpthread, -ldl, -laudit.
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..9e7ffbbf8
--- /dev/null
+++ b/README.md
@@ -0,0 +1,39 @@
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 or you'd prefer to communicate privately you can email [rmy@pobox.com](mailto:rmy@pobox.com).
4
5Additional information and downloads of precompiled binaries are available from [frippery.org](https://frippery.org/busybox).
6
7### Building
8
9You need a MinGW compiler and a POSIX environment. I cross-compile on Linux. On Fedora the following should pull in everything required:
10
11`dnf install gcc make ncurses-devel perl-Pod-Html`
12
13`dnf install mingw64-gcc mingw64-windows-default-manifest` (for a 64-bit build)
14
15`dnf install mingw32-gcc mingw32-windows-default-manifest` (for a 32-bit build)
16
17On Microsoft Windows you can install MSYS2 and a 64-bit toolchain by following [these instructions](https://www.msys2.org/#installation). To obtain a 32-bit toolchain run:
18
19`pacman -S --needed mingw-w64-i686-toolchain`
20
21Run `mingw64.exe` or `mingw32.exe` from the installation directory.
22
23On either Linux or Windows the commands `make mingw64_defconfig` or `make mingw32_defconfig` will pick up the default configuration. You can then customize your build with `make menuconfig` (Linux only) or by editing `.config`, if you know what you're doing.
24
25Then just `make` or `make CROSS_COMPILE=""` on Windows.
26
27### Limitations
28
29 - Use forward slashes in paths: Windows doesn't mind and the shell will be happier.
30 - Windows paths are different from Unix ([more detail](https://frippery.org/busybox/paths.html)):
31 * Absolute paths: `c:/path` or `//host/share`
32 * Relative to current directory of other drive: `c:path`
33 * Relative to current root (drive or share): `/path`
34 * Relative to current directory of current root (drive or share): `path`
35 - 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.
36 - 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.
37 - 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.
38 - The system tries to detect the best way to handle ANSI escape sequences for the terminal being used. If this doesn't work you can try setting the environment variable `BB_SKIP_ANSI_EMULATION=1` to force the use of literal ANSI escapes or `BB_SKIP_ANSI_EMULATION=0` to emulate them using the Windows console API.
39 - 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/.gitignore b/applets/.gitignore
index 459938d67..970bb90a9 100644
--- a/applets/.gitignore
+++ b/applets/.gitignore
@@ -1,3 +1,6 @@
1/applet_tables 1/applet_tables
2/usage 2/usage
3/usage_pod 3/usage_pod
4/applet_tables.exe
5/usage.exe
6/usage_pod.exe
diff --git a/applets/applet_tables.c b/applets/applet_tables.c
index 66ef7e4ac..fe26a5109 100644
--- a/applets/applet_tables.c
+++ b/applets/applet_tables.c
@@ -103,6 +103,9 @@ int main(int argc, char **argv)
103 if (i < 0) 103 if (i < 0)
104 return 1; 104 return 1;
105 dup2(i, 1); 105 dup2(i, 1);
106#ifdef __MINGW32__
107 close(i);
108#endif
106 109
107 /* Keep in sync with include/busybox.h! */ 110 /* Keep in sync with include/busybox.h! */
108 111
@@ -236,8 +239,14 @@ int main(int argc, char **argv)
236 239
237 if (fclose(stdout)) 240 if (fclose(stdout))
238 return 1; 241 return 1;
242#ifdef __MINGW32__
243 unlink(argv[1]);
244#endif
239 if (rename(tmp1, argv[1])) 245 if (rename(tmp1, argv[1]))
240 return 1; 246 return 1;
247#ifdef __MINGW32__
248 unlink(argv[2]);
249#endif
241 if (rename(tmp2, argv[2])) 250 if (rename(tmp2, argv[2]))
242 return 1; 251 return 1;
243 return 0; 252 return 0;
diff --git a/archival/ar.c b/archival/ar.c
index 320cbae72..beccab217 100644
--- a/archival/ar.c
+++ b/archival/ar.c
@@ -153,6 +153,9 @@ static int write_ar_archive(archive_handle_t *handle)
153{ 153{
154 struct stat st; 154 struct stat st;
155 archive_handle_t *out_handle; 155 archive_handle_t *out_handle;
156#if ENABLE_PLATFORM_MINGW32
157 char *temp_fn = NULL;
158#endif
156 159
157 xfstat(handle->src_fd, &st, handle->ar__name); 160 xfstat(handle->src_fd, &st, handle->ar__name);
158 161
@@ -161,8 +164,14 @@ static int write_ar_archive(archive_handle_t *handle)
161 */ 164 */
162 if (st.st_size != 0) { 165 if (st.st_size != 0) {
163 out_handle = init_handle(); 166 out_handle = init_handle();
167#if !ENABLE_PLATFORM_MINGW32
164 xunlink(handle->ar__name); 168 xunlink(handle->ar__name);
165 out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC); 169 out_handle->src_fd = xopen(handle->ar__name, O_WRONLY | O_CREAT | O_TRUNC);
170#else
171 /* can't unlink open file, create temporary output file */
172 temp_fn = xasprintf("%sXXXXXX", handle->ar__name);
173 out_handle->src_fd = xmkstemp(temp_fn);
174#endif
166 out_handle->accept = handle->accept; 175 out_handle->accept = handle->accept;
167 } else { 176 } else {
168 out_handle = handle; 177 out_handle = handle;
@@ -184,12 +193,20 @@ static int write_ar_archive(archive_handle_t *handle)
184 continue; 193 continue;
185 194
186 /* optional, since we exit right after we return */ 195 /* optional, since we exit right after we return */
187 if (ENABLE_FEATURE_CLEAN_UP) { 196 if (ENABLE_FEATURE_CLEAN_UP || ENABLE_PLATFORM_MINGW32) {
188 close(handle->src_fd); 197 close(handle->src_fd);
189 if (out_handle->src_fd != handle->src_fd) 198 if (out_handle->src_fd != handle->src_fd)
190 close(out_handle->src_fd); 199 close(out_handle->src_fd);
191 } 200 }
192 201
202#if ENABLE_PLATFORM_MINGW32
203 if (temp_fn != NULL) {
204 xrename(temp_fn, handle->ar__name);
205 if (ENABLE_FEATURE_CLEAN_UP)
206 free(temp_fn);
207 }
208#endif
209
193 return EXIT_SUCCESS; 210 return EXIT_SUCCESS;
194} 211}
195#endif /* FEATURE_AR_CREATE */ 212#endif /* FEATURE_AR_CREATE */
diff --git a/archival/bbunzip.c b/archival/bbunzip.c
index 0ac059c19..467996071 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 bf070a001..8e1f9431d 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,
@@ -1770,6 +1775,10 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
1770 int state_status; 1775 int state_status;
1771 int status_num; 1776 int status_num;
1772 int i; 1777 int i;
1778#if ENABLE_PLATFORM_MINGW32
1779 char **ptr, *path;
1780 int fd;
1781#endif
1773#if ENABLE_LONG_OPTS 1782#if ENABLE_LONG_OPTS
1774 static const char dpkg_longopts[] ALIGN1 = 1783 static const char dpkg_longopts[] ALIGN1 =
1775// FIXME: we use -C non-compatibly, should be: 1784// FIXME: we use -C non-compatibly, should be:
@@ -1814,6 +1823,26 @@ int dpkg_main(int argc UNUSED_PARAM, char **argv)
1814 bb_show_usage(); 1823 bb_show_usage();
1815 } 1824 }
1816 1825
1826#if ENABLE_PLATFORM_MINGW32
1827 if (opt & OPT_install) {
1828 /* add system drive prefix to filenames, if necessary */
1829 for (ptr = argv; *ptr; ++ptr) {
1830 *ptr = xabsolute_path(*ptr);
1831 }
1832 }
1833
1834 chdir_system_drive();
1835
1836 /* initialise data store */
1837 path = xstrdup("/var/lib/dpkg/info");
1838 bb_make_directory(path, -1, FILEUTILS_RECUR);
1839 free(path);
1840
1841 fd = open("/var/lib/dpkg/status", O_RDWR|O_CREAT, 0666);
1842 if (fd >= 0)
1843 xclose(fd);
1844#endif
1845
1817/* puts("(Reading database ... xxxxx files and directories installed.)"); */ 1846/* puts("(Reading database ... xxxxx files and directories installed.)"); */
1818 index_status_file("/var/lib/dpkg/status"); 1847 index_status_file("/var/lib/dpkg/status");
1819 1848
diff --git a/archival/libarchive/decompress_gunzip.c b/archival/libarchive/decompress_gunzip.c
index d051ecb81..d2f7a9309 100644
--- a/archival/libarchive/decompress_gunzip.c
+++ b/archival/libarchive/decompress_gunzip.c
@@ -1137,6 +1137,9 @@ static uint32_t buffer_read_le_u32(STATE_PARAM_ONLY)
1137 return res; 1137 return res;
1138} 1138}
1139 1139
1140#if ENABLE_PLATFORM_MINGW32 && __GNUC__
1141#pragma pack(2)
1142#endif
1140static int check_header_gzip(STATE_PARAM transformer_state_t *xstate) 1143static int check_header_gzip(STATE_PARAM transformer_state_t *xstate)
1141{ 1144{
1142 union { 1145 union {
@@ -1208,6 +1211,9 @@ static int check_header_gzip(STATE_PARAM transformer_state_t *xstate)
1208 } 1211 }
1209 return 1; 1212 return 1;
1210} 1213}
1214#if ENABLE_PLATFORM_MINGW32 && __GNUC__
1215#pragma pack()
1216#endif
1211 1217
1212IF_DESKTOP(long long) int FAST_FUNC 1218IF_DESKTOP(long long) int FAST_FUNC
1213unpack_gz_stream(transformer_state_t *xstate) 1219unpack_gz_stream(transformer_state_t *xstate)
diff --git a/archival/libarchive/open_transformer.c b/archival/libarchive/open_transformer.c
index 44715ef25..3d202ad26 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,12 @@ 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 mingw_fork_compressor(fd, transform_prog, "r");
158}
159#endif
153 160
154 161
155#if SEAMLESS_COMPRESSION 162#if SEAMLESS_COMPRESSION
diff --git a/archival/libarchive/unsafe_symlink_target.c b/archival/libarchive/unsafe_symlink_target.c
index f8dc8033d..8e4cd4380 100644
--- a/archival/libarchive/unsafe_symlink_target.c
+++ b/archival/libarchive/unsafe_symlink_target.c
@@ -5,6 +5,12 @@
5#include "libbb.h" 5#include "libbb.h"
6#include "bb_archive.h" 6#include "bb_archive.h"
7 7
8/* symlink may not be available for WIN32, just issue a warning */
9#if ENABLE_PLATFORM_MINGW32
10# undef bb_perror_msg_and_die
11# define bb_perror_msg_and_die(...) bb_perror_msg(__VA_ARGS__)
12#endif
13
8void FAST_FUNC create_or_remember_link(llist_t **link_placeholders, 14void FAST_FUNC create_or_remember_link(llist_t **link_placeholders,
9 const char *target, 15 const char *target,
10 const char *linkname, 16 const char *linkname,
@@ -31,11 +37,24 @@ void FAST_FUNC create_links_from_list(llist_t *list)
31 37
32 target = list->data + 1 + strlen(list->data + 1) + 1; 38 target = list->data + 1 + strlen(list->data + 1) + 1;
33 if ((*list->data ? link : symlink) (target, list->data + 1)) { 39 if ((*list->data ? link : symlink) (target, list->data + 1)) {
40#if !ENABLE_PLATFORM_MINGW32
34 /* shared message */ 41 /* shared message */
35 bb_error_msg_and_die("can't create %slink '%s' to '%s'", 42 bb_error_msg_and_die("can't create %slink '%s' to '%s'",
36 *list->data ? "hard" : "sym", 43 *list->data ? "hard" : "sym",
37 list->data + 1, target 44 list->data + 1, target
38 ); 45 );
46#else
47 if (!*list->data)
48 bb_error_msg("can't create %slink '%s' to '%s'",
49 "sym",
50 list->data + 1, target
51 );
52 else
53 bb_error_msg_and_die("can't create %slink '%s' to '%s'",
54 "hard",
55 list->data + 1, target
56 );
57#endif
39 } 58 }
40 list = list->link; 59 list = list->link;
41 } 60 }
diff --git a/archival/rpm.c b/archival/rpm.c
index af8db99a6..d83c33137 100644
--- a/archival/rpm.c
+++ b/archival/rpm.c
@@ -142,6 +142,7 @@ static int rpm_gettags(const char *filename)
142 } 142 }
143 G.mytags = tags; 143 G.mytags = tags;
144 144
145#if !ENABLE_PLATFORM_MINGW32
145 /* Map the store */ 146 /* Map the store */
146 storepos = (storepos + G_pagesize) & -(int)G_pagesize; 147 storepos = (storepos + G_pagesize) & -(int)G_pagesize;
147 /* remember size for munmap */ 148 /* remember size for munmap */
@@ -150,6 +151,14 @@ static int rpm_gettags(const char *filename)
150 G.map = mmap_read(fd, storepos); 151 G.map = mmap_read(fd, storepos);
151 if (G.map == MAP_FAILED) 152 if (G.map == MAP_FAILED)
152 bb_perror_msg_and_die("mmap '%s'", filename); 153 bb_perror_msg_and_die("mmap '%s'", filename);
154#else
155# undef munmap
156# define munmap(p, l) free(p)
157 /* Allocate memory for the store */
158 G.map = xmalloc(storepos);
159 xlseek(fd, 0, SEEK_SET);
160 full_read(fd, G.map, storepos);
161#endif
153 162
154 return fd; 163 return fd;
155} 164}
@@ -240,6 +249,7 @@ static void fileaction_dobackup(char *filename, int fileref)
240 } 249 }
241} 250}
242 251
252#if !ENABLE_PLATFORM_MINGW32
243static void fileaction_setowngrp(char *filename, int fileref) 253static void fileaction_setowngrp(char *filename, int fileref)
244{ 254{
245 /* real rpm warns: "user foo does not exist - using <you>" */ 255 /* real rpm warns: "user foo does not exist - using <you>" */
@@ -249,6 +259,7 @@ static void fileaction_setowngrp(char *filename, int fileref)
249 int gid = gr ? gr->gr_gid : getgid(); 259 int gid = gr ? gr->gr_gid : getgid();
250 chown(filename, uid, gid); 260 chown(filename, uid, gid);
251} 261}
262#endif
252 263
253static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref)) 264static void loop_through_files(int filetag, void (*fileaction)(char *filename, int fileref))
254{ 265{
@@ -296,6 +307,9 @@ static void extract_cpio(int fd, const char *source_rpm)
296 307
297 if (source_rpm != NULL) { 308 if (source_rpm != NULL) {
298 /* Binary rpm (it was built from some SRPM), install to root */ 309 /* Binary rpm (it was built from some SRPM), install to root */
310#if ENABLE_PLATFORM_MINGW32
311 if (chdir_system_drive())
312#endif
299 xchdir("/"); 313 xchdir("/");
300 } /* else: SRPM, install to current dir */ 314 } /* else: SRPM, install to current dir */
301 315
@@ -410,8 +424,10 @@ int rpm_main(int argc, char **argv)
410 loop_through_files(TAG_BASENAMES, fileaction_dobackup); 424 loop_through_files(TAG_BASENAMES, fileaction_dobackup);
411 /* Extact the archive */ 425 /* Extact the archive */
412 extract_cpio(rpm_fd, source_rpm); 426 extract_cpio(rpm_fd, source_rpm);
427#if !ENABLE_PLATFORM_MINGW32
413 /* Set the correct file uid/gid's */ 428 /* Set the correct file uid/gid's */
414 loop_through_files(TAG_BASENAMES, fileaction_setowngrp); 429 loop_through_files(TAG_BASENAMES, fileaction_setowngrp);
430#endif
415 } 431 }
416 else 432 else
417 if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) { 433 if ((func & (rpm_query|rpm_query_package)) == (rpm_query|rpm_query_package)) {
diff --git a/archival/tar.c b/archival/tar.c
index 9de37592e..054640c01 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 */
@@ -506,15 +508,21 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
506 } 508 }
507 } 509 }
508 510
511#if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_EXTRA_FILE_DATA
509 /* It is a bad idea to store the archive we are in the process of creating, 512 /* It is a bad idea to store the archive we are in the process of creating,
510 * so check the device and inode to be sure that this particular file isn't 513 * so check the device and inode to be sure that this particular file isn't
511 * the new tarball */ 514 * the new tarball */
512 if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev 515 if (tbInfo->tarFileStatBuf.st_dev == statbuf->st_dev
513 && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino 516 && tbInfo->tarFileStatBuf.st_ino == statbuf->st_ino
517# if ENABLE_FEATURE_EXTRA_FILE_DATA
518 /* ignore invalid inode numbers */
519 && statbuf->st_ino != 0
520# endif
514 ) { 521 ) {
515 bb_error_msg("%s: file is the archive; skipping", fileName); 522 bb_error_msg("%s: file is the archive; skipping", fileName);
516 return TRUE; 523 return TRUE;
517 } 524 }
525#endif
518 526
519# if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS 527# if !ENABLE_FEATURE_TAR_GNU_EXTENSIONS
520 if (strlen(header_name) >= NAME_SIZE) { 528 if (strlen(header_name) >= NAME_SIZE) {
@@ -568,7 +576,7 @@ static int FAST_FUNC writeFileToTarball(struct recursive_state *state,
568 return TRUE; 576 return TRUE;
569} 577}
570 578
571# if SEAMLESS_COMPRESSION 579# if SEAMLESS_COMPRESSION && !ENABLE_PLATFORM_MINGW32
572/* Don't inline: vfork scares gcc and pessimizes code */ 580/* Don't inline: vfork scares gcc and pessimizes code */
573static void NOINLINE vfork_compressor(int tar_fd, const char *gzip) 581static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
574{ 582{
@@ -645,6 +653,10 @@ static void NOINLINE vfork_compressor(int tar_fd, const char *gzip)
645} 653}
646# endif /* SEAMLESS_COMPRESSION */ 654# endif /* SEAMLESS_COMPRESSION */
647 655
656# if ENABLE_PLATFORM_MINGW32
657# define vfork_compressor(f, g) mingw_fork_compressor((f), (g), "w")
658# endif
659
648 660
649# if !SEAMLESS_COMPRESSION 661# if !SEAMLESS_COMPRESSION
650/* Do not pass gzip flag to writeTarFile() */ 662/* Do not pass gzip flag to writeTarFile() */
@@ -659,16 +671,21 @@ static NOINLINE int writeTarFile(
659 const char *gzip) 671 const char *gzip)
660{ 672{
661 int errorFlag = FALSE; 673 int errorFlag = FALSE;
674# if SEAMLESS_COMPRESSION && ENABLE_PLATFORM_MINGW32
675 pid_t pid = 0;
676# endif
662 677
663 /*tbInfo->hlInfoHead = NULL; - already is */ 678 /*tbInfo->hlInfoHead = NULL; - already is */
664 679
680# if ENABLE_PLATFORM_POSIX || ENABLE_FEATURE_EXTRA_FILE_DATA
665 /* Store the stat info for the tarball's file, so 681 /* Store the stat info for the tarball's file, so
666 * can avoid including the tarball into itself.... */ 682 * can avoid including the tarball into itself.... */
667 xfstat(tbInfo->tarFd, &tbInfo->tarFileStatBuf, "can't stat tar file"); 683 xfstat(tbInfo->tarFd, &tbInfo->tarFileStatBuf, "can't stat tar file");
684# endif
668 685
669# if SEAMLESS_COMPRESSION 686# if SEAMLESS_COMPRESSION
670 if (gzip) 687 if (gzip)
671 vfork_compressor(tbInfo->tarFd, gzip); 688 IF_PLATFORM_MINGW32(pid = )vfork_compressor(tbInfo->tarFd, gzip);
672# endif 689# endif
673 690
674 /* Read the directory/files and iterate over them one at a time */ 691 /* Read the directory/files and iterate over them one at a time */
@@ -702,7 +719,11 @@ static NOINLINE int writeTarFile(
702# if SEAMLESS_COMPRESSION 719# if SEAMLESS_COMPRESSION
703 if (gzip) { 720 if (gzip) {
704 int status; 721 int status;
722# if !ENABLE_PLATFORM_MINGW32
705 if (safe_waitpid(-1, &status, 0) == -1) 723 if (safe_waitpid(-1, &status, 0) == -1)
724# else
725 if (safe_waitpid(pid, &status, 0) == -1)
726# endif
706 bb_simple_perror_msg("waitpid"); 727 bb_simple_perror_msg("waitpid");
707 else if (!WIFEXITED(status) || WEXITSTATUS(status)) 728 else if (!WIFEXITED(status) || WEXITSTATUS(status))
708 /* gzip was killed or has exited with nonzero! */ 729 /* gzip was killed or has exited with nonzero! */
diff --git a/archival/unzip.c b/archival/unzip.c
index fc92ac661..edfd73652 100644
--- a/archival/unzip.c
+++ b/archival/unzip.c
@@ -71,6 +71,9 @@
71 71
72#include "libbb.h" 72#include "libbb.h"
73#include "bb_archive.h" 73#include "bb_archive.h"
74#if ENABLE_PLATFORM_MINGW32 && __GNUC__
75#pragma pack(2)
76#endif
74 77
75#if 0 78#if 0
76# define dbg(...) bb_error_msg(__VA_ARGS__) 79# define dbg(...) bb_error_msg(__VA_ARGS__)
@@ -620,7 +623,7 @@ int unzip_main(int argc, char **argv)
620 } 623 }
621 } 624 }
622 625
623#ifndef __GLIBC__ 626#if !defined(__GLIBC__) && !ENABLE_PLATFORM_MINGW32
624 /* 627 /*
625 * This code is needed for non-GNU getopt 628 * This code is needed for non-GNU getopt
626 * which doesn't understand "-" in option string. 629 * which doesn't understand "-" in option string.
diff --git a/configs/mingw32_defconfig b/configs/mingw32_defconfig
new file mode 100644
index 000000000..d41899fdc
--- /dev/null
+++ b/configs/mingw32_defconfig
@@ -0,0 +1,1217 @@
1#
2# Automatically generated make config: don't edit
3# Busybox version: 1.35.0.git
4# Fri Sep 17 09:17:23 2021
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_INFO is not set
40# CONFIG_FEATURE_SYSLOG is not set
41
42#
43# Settings for MINGW32
44#
45CONFIG_GLOBBING=y
46CONFIG_FEATURE_PRNG_SHELL=y
47# CONFIG_FEATURE_PRNG_ISAAC is not set
48CONFIG_FEATURE_RESOURCES=y
49CONFIG_FEATURE_VERSIONINFO=y
50CONFIG_FEATURE_ICON=y
51# CONFIG_FEATURE_ICON_ATERM is not set
52# CONFIG_FEATURE_ICON_STERM is not set
53CONFIG_FEATURE_ICON_ALL=y
54CONFIG_FEATURE_EURO=y
55CONFIG_SKIP_ANSI_EMULATION_DEFAULT=2
56CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
57CONFIG_FEATURE_EXTRA_FILE_DATA=y
58
59#
60# Build Options
61#
62# CONFIG_STATIC is not set
63# CONFIG_PIE is not set
64# CONFIG_NOMMU is not set
65# CONFIG_BUILD_LIBBUSYBOX is not set
66# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set
67# CONFIG_FEATURE_INDIVIDUAL is not set
68# CONFIG_FEATURE_SHARED_BUSYBOX is not set
69CONFIG_CROSS_COMPILER_PREFIX="i686-w64-mingw32-"
70CONFIG_SYSROOT=""
71CONFIG_EXTRA_CFLAGS=""
72CONFIG_EXTRA_LDFLAGS=""
73CONFIG_EXTRA_LDLIBS=""
74CONFIG_USE_PORTABLE_CODE=y
75CONFIG_STACK_OPTIMIZATION_386=y
76CONFIG_STATIC_LIBGCC=y
77
78#
79# Installation Options ("make install" behavior)
80#
81CONFIG_INSTALL_APPLET_SYMLINKS=y
82# CONFIG_INSTALL_APPLET_HARDLINKS is not set
83# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
84# CONFIG_INSTALL_APPLET_DONT is not set
85# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
86# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
87# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
88CONFIG_PREFIX=""
89
90#
91# Debugging Options
92#
93# CONFIG_DEBUG is not set
94# CONFIG_DEBUG_PESSIMIZE is not set
95# CONFIG_DEBUG_SANITIZE is not set
96# CONFIG_UNIT_TEST is not set
97# CONFIG_WERROR is not set
98# CONFIG_WARN_SIMPLE_MSG is not set
99CONFIG_NO_DEBUG_LIB=y
100# CONFIG_DMALLOC is not set
101# CONFIG_EFENCE is not set
102
103#
104# Library Tuning
105#
106# CONFIG_FEATURE_USE_BSS_TAIL is not set
107CONFIG_FLOAT_DURATION=y
108# CONFIG_FEATURE_RTMINMAX is not set
109# CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS is not set
110CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
111# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
112# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
113CONFIG_PASSWORD_MINLEN=6
114CONFIG_MD5_SMALL=1
115CONFIG_SHA3_SMALL=1
116# CONFIG_FEATURE_FAST_TOP is not set
117# CONFIG_FEATURE_ETC_NETWORKS is not set
118# CONFIG_FEATURE_ETC_SERVICES is not set
119CONFIG_FEATURE_EDITING=y
120CONFIG_FEATURE_EDITING_MAX_LEN=1024
121CONFIG_FEATURE_EDITING_VI=y
122CONFIG_FEATURE_EDITING_HISTORY=255
123CONFIG_FEATURE_EDITING_SAVEHISTORY=y
124# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
125CONFIG_FEATURE_REVERSE_SEARCH=y
126CONFIG_FEATURE_TAB_COMPLETION=y
127CONFIG_FEATURE_USERNAME_COMPLETION=y
128CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
129# CONFIG_FEATURE_EDITING_WINCH is not set
130# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
131# CONFIG_LOCALE_SUPPORT is not set
132# CONFIG_UNICODE_SUPPORT is not set
133# CONFIG_UNICODE_USING_LOCALE is not set
134# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
135CONFIG_SUBST_WCHAR=0
136CONFIG_LAST_SUPPORTED_WCHAR=0
137# CONFIG_UNICODE_COMBINING_WCHARS is not set
138# CONFIG_UNICODE_WIDE_WCHARS is not set
139# CONFIG_UNICODE_BIDI_SUPPORT is not set
140# CONFIG_UNICODE_NEUTRAL_TABLE is not set
141# CONFIG_UNICODE_PRESERVE_BROKEN is not set
142CONFIG_FEATURE_NON_POSIX_CP=y
143# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
144# CONFIG_FEATURE_USE_SENDFILE is not set
145CONFIG_FEATURE_COPYBUF_KB=4
146# CONFIG_FEATURE_SKIP_ROOTFS is not set
147# CONFIG_MONOTONIC_SYSCALL is not set
148# CONFIG_IOCTL_HEX2STR_ERROR is not set
149# CONFIG_FEATURE_HWIB is not set
150CONFIG_FEATURE_TIMEZONE=y
151
152#
153# Applets
154#
155
156#
157# Archival Utilities
158#
159CONFIG_FEATURE_SEAMLESS_XZ=y
160CONFIG_FEATURE_SEAMLESS_LZMA=y
161CONFIG_FEATURE_SEAMLESS_BZ2=y
162CONFIG_FEATURE_SEAMLESS_GZ=y
163CONFIG_FEATURE_SEAMLESS_Z=y
164CONFIG_AR=y
165CONFIG_FEATURE_AR_LONG_FILENAMES=y
166CONFIG_FEATURE_AR_CREATE=y
167CONFIG_UNCOMPRESS=y
168CONFIG_GUNZIP=y
169CONFIG_ZCAT=y
170CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
171CONFIG_BUNZIP2=y
172CONFIG_BZCAT=y
173CONFIG_UNLZMA=y
174CONFIG_LZCAT=y
175CONFIG_LZMA=y
176CONFIG_UNXZ=y
177CONFIG_XZCAT=y
178CONFIG_XZ=y
179CONFIG_BZIP2=y
180CONFIG_BZIP2_SMALL=8
181CONFIG_FEATURE_BZIP2_DECOMPRESS=y
182CONFIG_CPIO=y
183CONFIG_FEATURE_CPIO_O=y
184# CONFIG_FEATURE_CPIO_P is not set
185CONFIG_DPKG=y
186CONFIG_DPKG_DEB=y
187CONFIG_GZIP=y
188CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
189CONFIG_GZIP_FAST=2
190CONFIG_FEATURE_GZIP_LEVELS=y
191CONFIG_FEATURE_GZIP_DECOMPRESS=y
192CONFIG_LZOP=y
193CONFIG_UNLZOP=y
194CONFIG_LZOPCAT=y
195# CONFIG_LZOP_COMPR_HIGH is not set
196CONFIG_RPM=y
197CONFIG_RPM2CPIO=y
198CONFIG_TAR=y
199CONFIG_FEATURE_TAR_LONG_OPTIONS=y
200CONFIG_FEATURE_TAR_CREATE=y
201CONFIG_FEATURE_TAR_AUTODETECT=y
202CONFIG_FEATURE_TAR_FROM=y
203CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
204# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
205CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
206# CONFIG_FEATURE_TAR_TO_COMMAND is not set
207# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
208CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
209# CONFIG_FEATURE_TAR_SELINUX is not set
210CONFIG_UNZIP=y
211CONFIG_FEATURE_UNZIP_CDF=y
212CONFIG_FEATURE_UNZIP_BZIP2=y
213CONFIG_FEATURE_UNZIP_LZMA=y
214CONFIG_FEATURE_UNZIP_XZ=y
215CONFIG_FEATURE_LZMA_FAST=y
216
217#
218# Coreutils
219#
220CONFIG_BASENAME=y
221CONFIG_CAT=y
222CONFIG_FEATURE_CATN=y
223CONFIG_FEATURE_CATV=y
224# CONFIG_CHGRP is not set
225CONFIG_CHMOD=y
226# CONFIG_CHOWN is not set
227# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
228# CONFIG_CHROOT is not set
229CONFIG_CKSUM=y
230CONFIG_CRC32=y
231CONFIG_COMM=y
232CONFIG_CP=y
233CONFIG_FEATURE_CP_LONG_OPTIONS=y
234# CONFIG_FEATURE_CP_REFLINK is not set
235CONFIG_CUT=y
236CONFIG_FEATURE_CUT_REGEX=y
237CONFIG_DATE=y
238CONFIG_FEATURE_DATE_ISOFMT=y
239# CONFIG_FEATURE_DATE_NANO is not set
240CONFIG_FEATURE_DATE_COMPAT=y
241CONFIG_DD=y
242# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set
243# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
244CONFIG_FEATURE_DD_IBS_OBS=y
245CONFIG_FEATURE_DD_STATUS=y
246CONFIG_DF=y
247# CONFIG_FEATURE_DF_FANCY is not set
248CONFIG_DIRNAME=y
249CONFIG_DOS2UNIX=y
250CONFIG_UNIX2DOS=y
251CONFIG_DU=y
252CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
253CONFIG_ECHO=y
254CONFIG_FEATURE_FANCY_ECHO=y
255CONFIG_ENV=y
256CONFIG_EXPAND=y
257CONFIG_UNEXPAND=y
258CONFIG_EXPR=y
259CONFIG_EXPR_MATH_SUPPORT_64=y
260CONFIG_FACTOR=y
261CONFIG_FALSE=y
262CONFIG_FOLD=y
263CONFIG_HEAD=y
264CONFIG_FEATURE_FANCY_HEAD=y
265# CONFIG_HOSTID is not set
266CONFIG_ID=y
267CONFIG_GROUPS=y
268CONFIG_INSTALL=y
269CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
270CONFIG_LINK=y
271CONFIG_LN=y
272CONFIG_LOGNAME=y
273CONFIG_LS=y
274CONFIG_FEATURE_LS_FILETYPES=y
275CONFIG_FEATURE_LS_FOLLOWLINKS=y
276CONFIG_FEATURE_LS_RECURSIVE=y
277CONFIG_FEATURE_LS_WIDTH=y
278CONFIG_FEATURE_LS_SORTFILES=y
279CONFIG_FEATURE_LS_TIMESTAMPS=y
280CONFIG_FEATURE_LS_USERNAME=y
281CONFIG_FEATURE_LS_COLOR=y
282CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
283CONFIG_MD5SUM=y
284CONFIG_SHA1SUM=y
285CONFIG_SHA256SUM=y
286CONFIG_SHA512SUM=y
287CONFIG_SHA3SUM=y
288
289#
290# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
291#
292CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
293CONFIG_MKDIR=y
294# CONFIG_MKFIFO is not set
295# CONFIG_MKNOD is not set
296CONFIG_MKTEMP=y
297CONFIG_MV=y
298# CONFIG_NICE is not set
299CONFIG_NL=y
300# CONFIG_NOHUP is not set
301CONFIG_NPROC=y
302CONFIG_OD=y
303CONFIG_PASTE=y
304CONFIG_PRINTENV=y
305CONFIG_PRINTF=y
306CONFIG_PWD=y
307CONFIG_READLINK=y
308CONFIG_FEATURE_READLINK_FOLLOW=y
309CONFIG_REALPATH=y
310CONFIG_RM=y
311CONFIG_RMDIR=y
312CONFIG_SEQ=y
313CONFIG_SHRED=y
314CONFIG_SHUF=y
315CONFIG_SLEEP=y
316CONFIG_FEATURE_FANCY_SLEEP=y
317CONFIG_SORT=y
318CONFIG_FEATURE_SORT_BIG=y
319# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set
320CONFIG_SPLIT=y
321CONFIG_FEATURE_SPLIT_FANCY=y
322CONFIG_STAT=y
323CONFIG_FEATURE_STAT_FORMAT=y
324CONFIG_FEATURE_STAT_FILESYSTEM=y
325# CONFIG_STTY is not set
326CONFIG_SUM=y
327CONFIG_SYNC=y
328# CONFIG_FEATURE_SYNC_FANCY is not set
329CONFIG_FSYNC=y
330CONFIG_TAC=y
331CONFIG_TAIL=y
332CONFIG_FEATURE_FANCY_TAIL=y
333CONFIG_TEE=y
334CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
335CONFIG_TEST=y
336CONFIG_TEST1=y
337CONFIG_TEST2=y
338CONFIG_FEATURE_TEST_64=y
339CONFIG_TIMEOUT=y
340CONFIG_TOUCH=y
341CONFIG_FEATURE_TOUCH_SUSV3=y
342CONFIG_TR=y
343CONFIG_FEATURE_TR_CLASSES=y
344CONFIG_FEATURE_TR_EQUIV=y
345CONFIG_TRUE=y
346CONFIG_TRUNCATE=y
347# CONFIG_TTY is not set
348CONFIG_UNAME=y
349CONFIG_UNAME_OSNAME="MS/Windows"
350CONFIG_BB_ARCH=y
351CONFIG_UNIQ=y
352CONFIG_UNLINK=y
353CONFIG_USLEEP=y
354CONFIG_UUDECODE=y
355CONFIG_BASE32=y
356CONFIG_BASE64=y
357CONFIG_UUENCODE=y
358CONFIG_WC=y
359CONFIG_FEATURE_WC_LARGE=y
360# CONFIG_WHO is not set
361# CONFIG_W is not set
362# CONFIG_USERS is not set
363CONFIG_WHOAMI=y
364CONFIG_YES=y
365
366#
367# Common options
368#
369CONFIG_FEATURE_VERBOSE=y
370
371#
372# Common options for cp and mv
373#
374# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
375
376#
377# Common options for df, du, ls
378#
379CONFIG_FEATURE_HUMAN_READABLE=y
380
381#
382# Console Utilities
383#
384# CONFIG_CHVT is not set
385CONFIG_CLEAR=y
386# CONFIG_DEALLOCVT is not set
387# CONFIG_DUMPKMAP is not set
388# CONFIG_FGCONSOLE is not set
389# CONFIG_KBD_MODE is not set
390# CONFIG_LOADFONT is not set
391# CONFIG_SETFONT is not set
392# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
393CONFIG_DEFAULT_SETFONT_DIR=""
394# CONFIG_FEATURE_LOADFONT_PSF2 is not set
395# CONFIG_FEATURE_LOADFONT_RAW is not set
396# CONFIG_LOADKMAP is not set
397# CONFIG_OPENVT is not set
398CONFIG_RESET=y
399# CONFIG_RESIZE is not set
400# CONFIG_FEATURE_RESIZE_PRINT is not set
401# CONFIG_SETCONSOLE is not set
402# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
403# CONFIG_SETKEYCODES is not set
404# CONFIG_SETLOGCONS is not set
405# CONFIG_SHOWKEY is not set
406
407#
408# Debian Utilities
409#
410CONFIG_PIPE_PROGRESS=y
411# CONFIG_RUN_PARTS is not set
412# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
413# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
414# CONFIG_START_STOP_DAEMON is not set
415# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
416# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
417CONFIG_WHICH=y
418
419#
420# klibc-utils
421#
422# CONFIG_MINIPS is not set
423# CONFIG_NUKE is not set
424# CONFIG_RESUME is not set
425# CONFIG_RUN_INIT is not set
426
427#
428# Editors
429#
430CONFIG_AWK=y
431CONFIG_FEATURE_AWK_LIBM=y
432CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
433CONFIG_CMP=y
434CONFIG_DIFF=y
435CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
436CONFIG_FEATURE_DIFF_DIR=y
437CONFIG_ED=y
438CONFIG_PATCH=y
439CONFIG_SED=y
440CONFIG_VI=y
441CONFIG_FEATURE_VI_MAX_LEN=4096
442CONFIG_FEATURE_VI_8BIT=y
443CONFIG_FEATURE_VI_COLON=y
444CONFIG_FEATURE_VI_COLON_EXPAND=y
445CONFIG_FEATURE_VI_YANKMARK=y
446CONFIG_FEATURE_VI_SEARCH=y
447CONFIG_FEATURE_VI_REGEX_SEARCH=y
448# CONFIG_FEATURE_VI_USE_SIGNALS is not set
449CONFIG_FEATURE_VI_DOT_CMD=y
450CONFIG_FEATURE_VI_READONLY=y
451CONFIG_FEATURE_VI_SETOPTS=y
452CONFIG_FEATURE_VI_FILE_FORMAT=y
453CONFIG_FEATURE_VI_SET=y
454CONFIG_FEATURE_VI_WIN_RESIZE=y
455# CONFIG_FEATURE_VI_ASK_TERMINAL is not set
456CONFIG_FEATURE_VI_UNDO=y
457CONFIG_FEATURE_VI_UNDO_QUEUE=y
458CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
459CONFIG_FEATURE_VI_VERBOSE_STATUS=y
460CONFIG_FEATURE_ALLOW_EXEC=y
461
462#
463# Finding Utilities
464#
465CONFIG_FIND=y
466CONFIG_FEATURE_FIND_PRINT0=y
467CONFIG_FEATURE_FIND_MTIME=y
468CONFIG_FEATURE_FIND_MMIN=y
469CONFIG_FEATURE_FIND_PERM=y
470CONFIG_FEATURE_FIND_TYPE=y
471CONFIG_FEATURE_FIND_EXECUTABLE=y
472CONFIG_FEATURE_FIND_XDEV=y
473CONFIG_FEATURE_FIND_MAXDEPTH=y
474CONFIG_FEATURE_FIND_NEWER=y
475CONFIG_FEATURE_FIND_INUM=y
476CONFIG_FEATURE_FIND_EXEC=y
477CONFIG_FEATURE_FIND_EXEC_PLUS=y
478# CONFIG_FEATURE_FIND_USER is not set
479# CONFIG_FEATURE_FIND_GROUP is not set
480CONFIG_FEATURE_FIND_NOT=y
481CONFIG_FEATURE_FIND_DEPTH=y
482CONFIG_FEATURE_FIND_PAREN=y
483CONFIG_FEATURE_FIND_SIZE=y
484CONFIG_FEATURE_FIND_PRUNE=y
485CONFIG_FEATURE_FIND_QUIT=y
486CONFIG_FEATURE_FIND_DELETE=y
487CONFIG_FEATURE_FIND_EMPTY=y
488CONFIG_FEATURE_FIND_PATH=y
489CONFIG_FEATURE_FIND_REGEX=y
490# CONFIG_FEATURE_FIND_CONTEXT is not set
491CONFIG_FEATURE_FIND_LINKS=y
492CONFIG_GREP=y
493CONFIG_EGREP=y
494CONFIG_FGREP=y
495CONFIG_FEATURE_GREP_CONTEXT=y
496CONFIG_XARGS=y
497CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
498CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
499CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
500CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
501CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
502CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y
503CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y
504
505#
506# Init Utilities
507#
508# CONFIG_BOOTCHARTD is not set
509# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
510# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
511# CONFIG_HALT is not set
512# CONFIG_POWEROFF is not set
513# CONFIG_REBOOT is not set
514# CONFIG_FEATURE_WAIT_FOR_INIT is not set
515# CONFIG_FEATURE_CALL_TELINIT is not set
516CONFIG_TELINIT_PATH=""
517# CONFIG_INIT is not set
518# CONFIG_LINUXRC is not set
519# CONFIG_FEATURE_USE_INITTAB is not set
520# CONFIG_FEATURE_KILL_REMOVED is not set
521CONFIG_FEATURE_KILL_DELAY=0
522# CONFIG_FEATURE_INIT_SCTTY is not set
523# CONFIG_FEATURE_INIT_SYSLOG is not set
524# CONFIG_FEATURE_INIT_QUIET is not set
525# CONFIG_FEATURE_INIT_COREDUMPS is not set
526CONFIG_INIT_TERMINAL_TYPE=""
527# CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set
528
529#
530# Login/Password Management Utilities
531#
532# CONFIG_FEATURE_SHADOWPASSWDS is not set
533# CONFIG_USE_BB_PWD_GRP is not set
534# CONFIG_USE_BB_SHADOW is not set
535# CONFIG_USE_BB_CRYPT is not set
536# CONFIG_USE_BB_CRYPT_SHA is not set
537# CONFIG_ADD_SHELL is not set
538# CONFIG_REMOVE_SHELL is not set
539# CONFIG_ADDGROUP is not set
540# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
541# CONFIG_ADDUSER is not set
542# CONFIG_FEATURE_CHECK_NAMES is not set
543CONFIG_LAST_ID=0
544CONFIG_FIRST_SYSTEM_ID=0
545CONFIG_LAST_SYSTEM_ID=0
546# CONFIG_CHPASSWD is not set
547CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
548# CONFIG_CRYPTPW is not set
549# CONFIG_MKPASSWD is not set
550# CONFIG_DELUSER is not set
551# CONFIG_DELGROUP is not set
552# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
553# CONFIG_GETTY is not set
554# CONFIG_LOGIN is not set
555# CONFIG_LOGIN_SESSION_AS_CHILD is not set
556# CONFIG_LOGIN_SCRIPTS is not set
557# CONFIG_FEATURE_NOLOGIN is not set
558# CONFIG_FEATURE_SECURETTY is not set
559# CONFIG_PASSWD is not set
560# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
561# CONFIG_SU is not set
562# CONFIG_FEATURE_SU_SYSLOG is not set
563# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
564# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set
565# CONFIG_SULOGIN is not set
566CONFIG_SUW32=y
567# CONFIG_VLOCK is not set
568
569#
570# Linux Ext2 FS Progs
571#
572CONFIG_CHATTR=y
573# CONFIG_FSCK is not set
574CONFIG_LSATTR=y
575# CONFIG_TUNE2FS is not set
576
577#
578# Linux Module Utilities
579#
580# CONFIG_MODPROBE_SMALL is not set
581# CONFIG_DEPMOD is not set
582# CONFIG_INSMOD is not set
583# CONFIG_LSMOD is not set
584# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
585# CONFIG_MODINFO is not set
586# CONFIG_MODPROBE is not set
587# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
588# CONFIG_RMMOD is not set
589
590#
591# Options common to multiple modutils
592#
593# CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set
594# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
595# CONFIG_FEATURE_2_4_MODULES is not set
596# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
597# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
598# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
599# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
600# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
601# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
602# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
603# CONFIG_FEATURE_MODUTILS_ALIAS is not set
604# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
605CONFIG_DEFAULT_MODULES_DIR=""
606CONFIG_DEFAULT_DEPMOD_FILE=""
607
608#
609# Linux System Utilities
610#
611# CONFIG_ACPID is not set
612# CONFIG_FEATURE_ACPID_COMPAT is not set
613# CONFIG_BLKDISCARD is not set
614# CONFIG_BLKID is not set
615# CONFIG_FEATURE_BLKID_TYPE is not set
616# CONFIG_BLOCKDEV is not set
617CONFIG_CAL=y
618# CONFIG_CHRT is not set
619# CONFIG_DMESG is not set
620# CONFIG_FEATURE_DMESG_PRETTY is not set
621# CONFIG_EJECT is not set
622# CONFIG_FEATURE_EJECT_SCSI is not set
623# CONFIG_FALLOCATE is not set
624# CONFIG_FATATTR is not set
625# CONFIG_FBSET is not set
626# CONFIG_FEATURE_FBSET_FANCY is not set
627# CONFIG_FEATURE_FBSET_READMODE is not set
628# CONFIG_FDFORMAT is not set
629# CONFIG_FDISK is not set
630# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
631# CONFIG_FEATURE_FDISK_WRITABLE is not set
632# CONFIG_FEATURE_AIX_LABEL is not set
633# CONFIG_FEATURE_SGI_LABEL is not set
634# CONFIG_FEATURE_SUN_LABEL is not set
635# CONFIG_FEATURE_OSF_LABEL is not set
636# CONFIG_FEATURE_GPT_LABEL is not set
637# CONFIG_FEATURE_FDISK_ADVANCED is not set
638# CONFIG_FINDFS is not set
639# CONFIG_FLOCK is not set
640# CONFIG_FDFLUSH is not set
641# CONFIG_FREERAMDISK is not set
642# CONFIG_FSCK_MINIX is not set
643# CONFIG_FSFREEZE is not set
644# CONFIG_FSTRIM is not set
645CONFIG_GETOPT=y
646CONFIG_FEATURE_GETOPT_LONG=y
647CONFIG_HEXDUMP=y
648CONFIG_HD=y
649CONFIG_XXD=y
650# CONFIG_HWCLOCK is not set
651# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
652# CONFIG_IONICE is not set
653# CONFIG_IPCRM is not set
654# CONFIG_IPCS is not set
655# CONFIG_LAST is not set
656# CONFIG_FEATURE_LAST_FANCY is not set
657# CONFIG_LOSETUP is not set
658# CONFIG_LSPCI is not set
659# CONFIG_LSUSB is not set
660# CONFIG_MDEV is not set
661# CONFIG_FEATURE_MDEV_CONF is not set
662# CONFIG_FEATURE_MDEV_RENAME is not set
663# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
664# CONFIG_FEATURE_MDEV_EXEC is not set
665# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
666# CONFIG_FEATURE_MDEV_DAEMON is not set
667# CONFIG_MESG is not set
668# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
669# CONFIG_MKE2FS is not set
670# CONFIG_MKFS_EXT2 is not set
671# CONFIG_MKFS_MINIX is not set
672# CONFIG_FEATURE_MINIX2 is not set
673# CONFIG_MKFS_REISER is not set
674# CONFIG_MKDOSFS is not set
675# CONFIG_MKFS_VFAT is not set
676# CONFIG_MKSWAP is not set
677# CONFIG_FEATURE_MKSWAP_UUID is not set
678# CONFIG_MORE is not set
679# CONFIG_MOUNT is not set
680# CONFIG_FEATURE_MOUNT_FAKE is not set
681# CONFIG_FEATURE_MOUNT_VERBOSE is not set
682# CONFIG_FEATURE_MOUNT_HELPERS is not set
683# CONFIG_FEATURE_MOUNT_LABEL is not set
684# CONFIG_FEATURE_MOUNT_NFS is not set
685# CONFIG_FEATURE_MOUNT_CIFS is not set
686# CONFIG_FEATURE_MOUNT_FLAGS is not set
687# CONFIG_FEATURE_MOUNT_FSTAB is not set
688# CONFIG_FEATURE_MOUNT_OTHERTAB is not set
689# CONFIG_MOUNTPOINT is not set
690# CONFIG_NOLOGIN is not set
691# CONFIG_NOLOGIN_DEPENDENCIES is not set
692# CONFIG_NSENTER is not set
693# CONFIG_PIVOT_ROOT is not set
694# CONFIG_RDATE is not set
695# CONFIG_RDEV is not set
696# CONFIG_READPROFILE is not set
697# CONFIG_RENICE is not set
698CONFIG_REV=y
699# CONFIG_RTCWAKE is not set
700# CONFIG_SCRIPT is not set
701# CONFIG_SCRIPTREPLAY is not set
702# CONFIG_SETARCH is not set
703# CONFIG_LINUX32 is not set
704# CONFIG_LINUX64 is not set
705# CONFIG_SETPRIV is not set
706# CONFIG_FEATURE_SETPRIV_DUMP is not set
707# CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set
708# CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set
709# CONFIG_SETSID is not set
710# CONFIG_SWAPON is not set
711# CONFIG_FEATURE_SWAPON_DISCARD is not set
712# CONFIG_FEATURE_SWAPON_PRI is not set
713# CONFIG_SWAPOFF is not set
714# CONFIG_FEATURE_SWAPONOFF_LABEL is not set
715# CONFIG_SWITCH_ROOT is not set
716# CONFIG_TASKSET is not set
717# CONFIG_FEATURE_TASKSET_FANCY is not set
718# CONFIG_FEATURE_TASKSET_CPULIST is not set
719# CONFIG_UEVENT is not set
720# CONFIG_UMOUNT is not set
721# CONFIG_FEATURE_UMOUNT_ALL is not set
722# CONFIG_UNSHARE is not set
723# CONFIG_WALL is not set
724# CONFIG_FEATURE_MOUNT_LOOP is not set
725# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
726# CONFIG_FEATURE_MTAB_SUPPORT is not set
727# CONFIG_VOLUMEID is not set
728# CONFIG_FEATURE_VOLUMEID_BCACHE is not set
729# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
730# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
731# CONFIG_FEATURE_VOLUMEID_EROFS is not set
732# CONFIG_FEATURE_VOLUMEID_EXFAT is not set
733# CONFIG_FEATURE_VOLUMEID_EXT is not set
734# CONFIG_FEATURE_VOLUMEID_F2FS is not set
735# CONFIG_FEATURE_VOLUMEID_FAT is not set
736# CONFIG_FEATURE_VOLUMEID_HFS is not set
737# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
738# CONFIG_FEATURE_VOLUMEID_JFS is not set
739# CONFIG_FEATURE_VOLUMEID_LFS is not set
740# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
741# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
742# CONFIG_FEATURE_VOLUMEID_LUKS is not set
743# CONFIG_FEATURE_VOLUMEID_MINIX is not set
744# CONFIG_FEATURE_VOLUMEID_NILFS is not set
745# CONFIG_FEATURE_VOLUMEID_NTFS is not set
746# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
747# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
748# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
749# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set
750# CONFIG_FEATURE_VOLUMEID_SYSV is not set
751# CONFIG_FEATURE_VOLUMEID_UBIFS is not set
752# CONFIG_FEATURE_VOLUMEID_UDF is not set
753# CONFIG_FEATURE_VOLUMEID_XFS is not set
754
755#
756# Miscellaneous Utilities
757#
758# CONFIG_ADJTIMEX is not set
759CONFIG_ASCII=y
760# CONFIG_BBCONFIG is not set
761# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
762CONFIG_BC=y
763CONFIG_DC=y
764CONFIG_FEATURE_DC_BIG=y
765# CONFIG_FEATURE_DC_LIBM is not set
766CONFIG_FEATURE_BC_INTERACTIVE=y
767CONFIG_FEATURE_BC_LONG_OPTIONS=y
768# CONFIG_BEEP is not set
769CONFIG_FEATURE_BEEP_FREQ=0
770CONFIG_FEATURE_BEEP_LENGTH_MS=0
771# CONFIG_CHAT is not set
772# CONFIG_FEATURE_CHAT_NOFAIL is not set
773# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
774# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
775# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
776# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
777# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
778# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
779# CONFIG_CONSPY is not set
780# CONFIG_CROND is not set
781# CONFIG_FEATURE_CROND_D is not set
782# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
783# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set
784CONFIG_FEATURE_CROND_DIR=""
785# CONFIG_CRONTAB is not set
786# CONFIG_DEVFSD is not set
787# CONFIG_DEVFSD_MODLOAD is not set
788# CONFIG_DEVFSD_FG_NP is not set
789# CONFIG_DEVFSD_VERBOSE is not set
790# CONFIG_FEATURE_DEVFS is not set
791# CONFIG_DEVMEM is not set
792# CONFIG_FBSPLASH is not set
793# CONFIG_FLASH_ERASEALL is not set
794# CONFIG_FLASH_LOCK is not set
795# CONFIG_FLASH_UNLOCK is not set
796# CONFIG_FLASHCP is not set
797# CONFIG_HDPARM is not set
798# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
799# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
800# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
801# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
802# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
803# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
804# CONFIG_HEXEDIT is not set
805# CONFIG_I2CGET is not set
806# CONFIG_I2CSET is not set
807# CONFIG_I2CDUMP is not set
808# CONFIG_I2CDETECT is not set
809# CONFIG_I2CTRANSFER is not set
810CONFIG_ICONV=y
811CONFIG_INOTIFYD=y
812CONFIG_LESS=y
813CONFIG_FEATURE_LESS_MAXLINES=9999999
814CONFIG_FEATURE_LESS_BRACKETS=y
815CONFIG_FEATURE_LESS_FLAGS=y
816CONFIG_FEATURE_LESS_TRUNCATE=y
817CONFIG_FEATURE_LESS_MARKS=y
818CONFIG_FEATURE_LESS_REGEXP=y
819# CONFIG_FEATURE_LESS_WINCH is not set
820# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set
821CONFIG_FEATURE_LESS_DASHCMD=y
822CONFIG_FEATURE_LESS_LINENUMS=y
823CONFIG_FEATURE_LESS_RAW=y
824CONFIG_FEATURE_LESS_ENV=y
825# CONFIG_LSSCSI is not set
826# CONFIG_MAKEDEVS is not set
827# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
828# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
829CONFIG_MAN=y
830# CONFIG_MICROCOM is not set
831# CONFIG_MIM is not set
832# CONFIG_MT is not set
833# CONFIG_NANDWRITE is not set
834# CONFIG_NANDDUMP is not set
835# CONFIG_PARTPROBE is not set
836# CONFIG_RAIDAUTORUN is not set
837# CONFIG_READAHEAD is not set
838# CONFIG_RFKILL is not set
839# CONFIG_RUNLEVEL is not set
840# CONFIG_RX is not set
841# CONFIG_SETFATTR is not set
842# CONFIG_SETSERIAL is not set
843CONFIG_STRINGS=y
844CONFIG_TIME=y
845CONFIG_TS=y
846CONFIG_TTYSIZE=y
847# CONFIG_UBIATTACH is not set
848# CONFIG_UBIDETACH is not set
849# CONFIG_UBIMKVOL is not set
850# CONFIG_UBIRMVOL is not set
851# CONFIG_UBIRSVOL is not set
852# CONFIG_UBIUPDATEVOL is not set
853# CONFIG_UBIRENAME is not set
854# CONFIG_VOLNAME is not set
855# CONFIG_WATCHDOG is not set
856# CONFIG_FEATURE_WATCHDOG_OPEN_TWICE is not set
857
858#
859# Networking Utilities
860#
861CONFIG_FEATURE_IPV6=y
862# CONFIG_FEATURE_UNIX_LOCAL is not set
863CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
864# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
865# CONFIG_FEATURE_TLS_SHA1 is not set
866# CONFIG_ARP is not set
867# CONFIG_ARPING is not set
868# CONFIG_BRCTL is not set
869# CONFIG_FEATURE_BRCTL_FANCY is not set
870# CONFIG_FEATURE_BRCTL_SHOW is not set
871# CONFIG_DNSD is not set
872# CONFIG_ETHER_WAKE is not set
873# CONFIG_FTPD is not set
874# CONFIG_FEATURE_FTPD_WRITE is not set
875# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
876# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set
877CONFIG_FTPGET=y
878CONFIG_FTPPUT=y
879CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
880# CONFIG_HOSTNAME is not set
881# CONFIG_DNSDOMAINNAME is not set
882CONFIG_HTTPD=y
883CONFIG_FEATURE_HTTPD_PORT_DEFAULT=80
884CONFIG_FEATURE_HTTPD_RANGES=y
885# CONFIG_FEATURE_HTTPD_SETUID is not set
886CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
887# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
888# CONFIG_FEATURE_HTTPD_CGI is not set
889# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
890# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
891CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
892CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
893# CONFIG_FEATURE_HTTPD_PROXY is not set
894CONFIG_FEATURE_HTTPD_GZIP=y
895CONFIG_FEATURE_HTTPD_ETAG=y
896CONFIG_FEATURE_HTTPD_LAST_MODIFIED=y
897CONFIG_FEATURE_HTTPD_DATE=y
898CONFIG_FEATURE_HTTPD_ACL_IP=y
899# CONFIG_IFCONFIG is not set
900# CONFIG_FEATURE_IFCONFIG_STATUS is not set
901# CONFIG_FEATURE_IFCONFIG_SLIP is not set
902# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
903# CONFIG_FEATURE_IFCONFIG_HW is not set
904# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
905# CONFIG_IFENSLAVE is not set
906# CONFIG_IFPLUGD is not set
907# CONFIG_IFUP is not set
908# CONFIG_IFDOWN is not set
909CONFIG_IFUPDOWN_IFSTATE_PATH=""
910# CONFIG_FEATURE_IFUPDOWN_IP is not set
911# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
912# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
913# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
914# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
915# CONFIG_INETD is not set
916# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
917# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
918# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
919# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
920# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
921# CONFIG_FEATURE_INETD_RPC is not set
922# CONFIG_IP is not set
923# CONFIG_IPADDR is not set
924# CONFIG_IPLINK is not set
925# CONFIG_IPROUTE is not set
926# CONFIG_IPTUNNEL is not set
927# CONFIG_IPRULE is not set
928# CONFIG_IPNEIGH is not set
929# CONFIG_FEATURE_IP_ADDRESS is not set
930# CONFIG_FEATURE_IP_LINK is not set
931# CONFIG_FEATURE_IP_ROUTE is not set
932CONFIG_FEATURE_IP_ROUTE_DIR=""
933# CONFIG_FEATURE_IP_TUNNEL is not set
934# CONFIG_FEATURE_IP_RULE is not set
935# CONFIG_FEATURE_IP_NEIGH is not set
936# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
937CONFIG_IPCALC=y
938CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
939CONFIG_FEATURE_IPCALC_FANCY=y
940# CONFIG_FAKEIDENTD is not set
941# CONFIG_NAMEIF is not set
942# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
943# CONFIG_NBDCLIENT is not set
944CONFIG_NC=y
945# CONFIG_NETCAT is not set
946CONFIG_NC_SERVER=y
947# CONFIG_NC_EXTRA is not set
948# CONFIG_NC_110_COMPAT is not set
949# CONFIG_NETSTAT is not set
950# CONFIG_FEATURE_NETSTAT_WIDE is not set
951# CONFIG_FEATURE_NETSTAT_PRG is not set
952# CONFIG_NSLOOKUP is not set
953# CONFIG_FEATURE_NSLOOKUP_BIG is not set
954# CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS is not set
955# CONFIG_NTPD is not set
956# CONFIG_FEATURE_NTPD_SERVER is not set
957# CONFIG_FEATURE_NTPD_CONF is not set
958# CONFIG_FEATURE_NTP_AUTH is not set
959# CONFIG_PING is not set
960# CONFIG_PING6 is not set
961# CONFIG_FEATURE_FANCY_PING is not set
962# CONFIG_PSCAN is not set
963# CONFIG_ROUTE is not set
964# CONFIG_SLATTACH is not set
965CONFIG_SSL_CLIENT=y
966# CONFIG_TC is not set
967# CONFIG_FEATURE_TC_INGRESS is not set
968# CONFIG_TCPSVD is not set
969# CONFIG_UDPSVD is not set
970# CONFIG_TELNET is not set
971# CONFIG_FEATURE_TELNET_TTYPE is not set
972# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
973# CONFIG_FEATURE_TELNET_WIDTH is not set
974# CONFIG_TELNETD is not set
975# CONFIG_FEATURE_TELNETD_STANDALONE is not set
976CONFIG_FEATURE_TELNETD_PORT_DEFAULT=0
977# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
978# CONFIG_TFTP is not set
979# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
980# CONFIG_FEATURE_TFTP_HPA_COMPAT is not set
981# CONFIG_TFTPD is not set
982# CONFIG_FEATURE_TFTP_GET is not set
983# CONFIG_FEATURE_TFTP_PUT is not set
984# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
985# CONFIG_TFTP_DEBUG is not set
986CONFIG_TLS=y
987# CONFIG_TRACEROUTE is not set
988# CONFIG_TRACEROUTE6 is not set
989# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
990# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
991# CONFIG_TUNCTL is not set
992# CONFIG_FEATURE_TUNCTL_UG is not set
993# CONFIG_VCONFIG is not set
994CONFIG_WGET=y
995CONFIG_FEATURE_WGET_LONG_OPTIONS=y
996# CONFIG_FEATURE_WGET_STATUSBAR is not set
997CONFIG_FEATURE_WGET_FTP=y
998CONFIG_FEATURE_WGET_AUTHENTICATION=y
999# CONFIG_FEATURE_WGET_TIMEOUT is not set
1000CONFIG_FEATURE_WGET_HTTPS=y
1001# CONFIG_FEATURE_WGET_OPENSSL is not set
1002CONFIG_WHOIS=y
1003# CONFIG_ZCIP is not set
1004# CONFIG_UDHCPD is not set
1005# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
1006# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
1007CONFIG_DHCPD_LEASES_FILE=""
1008# CONFIG_DUMPLEASES is not set
1009# CONFIG_DHCPRELAY is not set
1010# CONFIG_UDHCPC is not set
1011# CONFIG_FEATURE_UDHCPC_ARPING is not set
1012# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set
1013CONFIG_UDHCPC_DEFAULT_SCRIPT=""
1014# CONFIG_UDHCPC6 is not set
1015# CONFIG_FEATURE_UDHCPC6_RFC3646 is not set
1016# CONFIG_FEATURE_UDHCPC6_RFC4704 is not set
1017# CONFIG_FEATURE_UDHCPC6_RFC4833 is not set
1018# CONFIG_FEATURE_UDHCPC6_RFC5970 is not set
1019CONFIG_UDHCPC_DEFAULT_INTERFACE=""
1020# CONFIG_FEATURE_UDHCP_PORT is not set
1021CONFIG_UDHCP_DEBUG=0
1022CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
1023# CONFIG_FEATURE_UDHCP_RFC3397 is not set
1024# CONFIG_FEATURE_UDHCP_8021Q is not set
1025CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
1026
1027#
1028# Print Utilities
1029#
1030# CONFIG_LPD is not set
1031# CONFIG_LPR is not set
1032# CONFIG_LPQ is not set
1033
1034#
1035# Mail Utilities
1036#
1037# CONFIG_MAKEMIME is not set
1038# CONFIG_POPMAILDIR is not set
1039# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
1040# CONFIG_REFORMIME is not set
1041# CONFIG_FEATURE_REFORMIME_COMPAT is not set
1042# CONFIG_SENDMAIL is not set
1043CONFIG_FEATURE_MIME_CHARSET=""
1044
1045#
1046# Process Utilities
1047#
1048CONFIG_FREE=y
1049# CONFIG_FUSER is not set
1050# CONFIG_IOSTAT is not set
1051CONFIG_KILL=y
1052CONFIG_KILLALL=y
1053# CONFIG_KILLALL5 is not set
1054# CONFIG_LSOF is not set
1055# CONFIG_MPSTAT is not set
1056# CONFIG_NMETER is not set
1057CONFIG_PGREP=y
1058CONFIG_PKILL=y
1059CONFIG_PIDOF=y
1060CONFIG_FEATURE_PIDOF_SINGLE=y
1061CONFIG_FEATURE_PIDOF_OMIT=y
1062# CONFIG_PMAP is not set
1063# CONFIG_POWERTOP is not set
1064# CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set
1065CONFIG_PS=y
1066# CONFIG_FEATURE_PS_WIDE is not set
1067# CONFIG_FEATURE_PS_LONG is not set
1068CONFIG_FEATURE_PS_TIME=y
1069# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
1070# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
1071# CONFIG_PSTREE is not set
1072# CONFIG_PWDX is not set
1073# CONFIG_SMEMCAP is not set
1074# CONFIG_BB_SYSCTL is not set
1075# CONFIG_TOP is not set
1076# CONFIG_FEATURE_TOP_INTERACTIVE is not set
1077# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
1078# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
1079# CONFIG_FEATURE_TOP_SMP_CPU is not set
1080# CONFIG_FEATURE_TOP_DECIMALS is not set
1081# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
1082# CONFIG_FEATURE_TOPMEM is not set
1083CONFIG_UPTIME=y
1084# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
1085CONFIG_WATCH=y
1086# CONFIG_FEATURE_SHOW_THREADS is not set
1087
1088#
1089# Runit Utilities
1090#
1091# CONFIG_CHPST is not set
1092# CONFIG_SETUIDGID is not set
1093# CONFIG_ENVUIDGID is not set
1094# CONFIG_ENVDIR is not set
1095# CONFIG_SOFTLIMIT is not set
1096# CONFIG_RUNSV is not set
1097# CONFIG_RUNSVDIR is not set
1098# CONFIG_FEATURE_RUNSVDIR_LOG is not set
1099# CONFIG_SV is not set
1100CONFIG_SV_DEFAULT_SERVICE_DIR=""
1101# CONFIG_SVC is not set
1102# CONFIG_SVOK is not set
1103# CONFIG_SVLOGD is not set
1104# CONFIG_CHCON is not set
1105# CONFIG_GETENFORCE is not set
1106# CONFIG_GETSEBOOL is not set
1107# CONFIG_LOAD_POLICY is not set
1108# CONFIG_MATCHPATHCON is not set
1109# CONFIG_RUNCON is not set
1110# CONFIG_SELINUXENABLED is not set
1111# CONFIG_SESTATUS is not set
1112# CONFIG_SETENFORCE is not set
1113# CONFIG_SETFILES is not set
1114# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
1115# CONFIG_RESTORECON is not set
1116# CONFIG_SETSEBOOL is not set
1117
1118#
1119# Shells
1120#
1121CONFIG_SH_IS_ASH=y
1122# CONFIG_SH_IS_HUSH is not set
1123# CONFIG_SH_IS_NONE is not set
1124CONFIG_BASH_IS_ASH=y
1125# CONFIG_BASH_IS_HUSH is not set
1126# CONFIG_BASH_IS_NONE is not set
1127CONFIG_SHELL_ASH=y
1128CONFIG_ASH=y
1129# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
1130CONFIG_ASH_INTERNAL_GLOB=y
1131CONFIG_ASH_BASH_COMPAT=y
1132# CONFIG_ASH_BASH_SOURCE_CURDIR is not set
1133CONFIG_ASH_BASH_NOT_FOUND_HOOK=y
1134CONFIG_ASH_JOB_CONTROL=y
1135CONFIG_ASH_ALIAS=y
1136CONFIG_ASH_RANDOM_SUPPORT=y
1137CONFIG_ASH_EXPAND_PRMT=y
1138# CONFIG_ASH_IDLE_TIMEOUT is not set
1139# CONFIG_ASH_MAIL is not set
1140CONFIG_ASH_ECHO=y
1141CONFIG_ASH_PRINTF=y
1142CONFIG_ASH_TEST=y
1143CONFIG_ASH_HELP=y
1144CONFIG_ASH_GETOPTS=y
1145CONFIG_ASH_CMDCMD=y
1146CONFIG_ASH_NOCONSOLE=y
1147CONFIG_ASH_NOCASEGLOB=y
1148# CONFIG_CTTYHACK is not set
1149# CONFIG_HUSH is not set
1150# CONFIG_SHELL_HUSH is not set
1151# CONFIG_HUSH_BASH_COMPAT is not set
1152# CONFIG_HUSH_BRACE_EXPANSION is not set
1153# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set
1154# CONFIG_HUSH_LINENO_VAR is not set
1155# CONFIG_HUSH_INTERACTIVE is not set
1156# CONFIG_HUSH_SAVEHISTORY is not set
1157# CONFIG_HUSH_JOB is not set
1158# CONFIG_HUSH_TICK is not set
1159# CONFIG_HUSH_IF is not set
1160# CONFIG_HUSH_LOOPS is not set
1161# CONFIG_HUSH_CASE is not set
1162# CONFIG_HUSH_FUNCTIONS is not set
1163# CONFIG_HUSH_LOCAL is not set
1164# CONFIG_HUSH_RANDOM_SUPPORT is not set
1165# CONFIG_HUSH_MODE_X is not set
1166# CONFIG_HUSH_ECHO is not set
1167# CONFIG_HUSH_PRINTF is not set
1168# CONFIG_HUSH_TEST is not set
1169# CONFIG_HUSH_HELP is not set
1170# CONFIG_HUSH_EXPORT is not set
1171# CONFIG_HUSH_EXPORT_N is not set
1172# CONFIG_HUSH_READONLY is not set
1173# CONFIG_HUSH_KILL is not set
1174# CONFIG_HUSH_WAIT is not set
1175# CONFIG_HUSH_COMMAND is not set
1176# CONFIG_HUSH_TRAP is not set
1177# CONFIG_HUSH_TYPE is not set
1178# CONFIG_HUSH_TIMES is not set
1179# CONFIG_HUSH_READ is not set
1180# CONFIG_HUSH_SET is not set
1181# CONFIG_HUSH_UNSET is not set
1182# CONFIG_HUSH_ULIMIT is not set
1183# CONFIG_HUSH_UMASK is not set
1184# CONFIG_HUSH_GETOPTS is not set
1185# CONFIG_HUSH_MEMLEAK is not set
1186
1187#
1188# Options common to all shells
1189#
1190CONFIG_FEATURE_SH_MATH=y
1191CONFIG_FEATURE_SH_MATH_64=y
1192CONFIG_FEATURE_SH_MATH_BASE=y
1193CONFIG_FEATURE_SH_EXTRA_QUIET=y
1194CONFIG_FEATURE_SH_STANDALONE=y
1195CONFIG_FEATURE_SH_NOFORK=y
1196# CONFIG_FEATURE_SH_READ_FRAC is not set
1197CONFIG_FEATURE_SH_HISTFILESIZE=y
1198CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y
1199
1200#
1201# System Logging Utilities
1202#
1203# CONFIG_KLOGD is not set
1204# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
1205# CONFIG_LOGGER is not set
1206# CONFIG_LOGREAD is not set
1207# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
1208# CONFIG_SYSLOGD is not set
1209# CONFIG_FEATURE_ROTATE_LOGFILE is not set
1210# CONFIG_FEATURE_REMOTE_LOG is not set
1211# CONFIG_FEATURE_SYSLOGD_DUP is not set
1212# CONFIG_FEATURE_SYSLOGD_CFG is not set
1213# CONFIG_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS is not set
1214CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
1215# CONFIG_FEATURE_IPC_SYSLOG is not set
1216CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
1217# CONFIG_FEATURE_KMSG_SYSLOG is not set
diff --git a/configs/mingw64_defconfig b/configs/mingw64_defconfig
new file mode 100644
index 000000000..449f16ae6
--- /dev/null
+++ b/configs/mingw64_defconfig
@@ -0,0 +1,1217 @@
1#
2# Automatically generated make config: don't edit
3# Busybox version: 1.35.0.git
4# Fri Sep 17 09:17:23 2021
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_INFO is not set
40# CONFIG_FEATURE_SYSLOG is not set
41
42#
43# Settings for MINGW32
44#
45CONFIG_GLOBBING=y
46CONFIG_FEATURE_PRNG_SHELL=y
47# CONFIG_FEATURE_PRNG_ISAAC is not set
48CONFIG_FEATURE_RESOURCES=y
49CONFIG_FEATURE_VERSIONINFO=y
50CONFIG_FEATURE_ICON=y
51# CONFIG_FEATURE_ICON_ATERM is not set
52# CONFIG_FEATURE_ICON_STERM is not set
53CONFIG_FEATURE_ICON_ALL=y
54CONFIG_FEATURE_EURO=y
55CONFIG_SKIP_ANSI_EMULATION_DEFAULT=2
56CONFIG_FEATURE_IMPROVED_COLOUR_MAPPING=y
57CONFIG_FEATURE_EXTRA_FILE_DATA=y
58
59#
60# Build Options
61#
62# CONFIG_STATIC is not set
63# CONFIG_PIE is not set
64# CONFIG_NOMMU is not set
65# CONFIG_BUILD_LIBBUSYBOX is not set
66# CONFIG_FEATURE_LIBBUSYBOX_STATIC is not set
67# CONFIG_FEATURE_INDIVIDUAL is not set
68# CONFIG_FEATURE_SHARED_BUSYBOX is not set
69CONFIG_CROSS_COMPILER_PREFIX="x86_64-w64-mingw32-"
70CONFIG_SYSROOT=""
71CONFIG_EXTRA_CFLAGS=""
72CONFIG_EXTRA_LDFLAGS=""
73CONFIG_EXTRA_LDLIBS=""
74CONFIG_USE_PORTABLE_CODE=y
75CONFIG_STACK_OPTIMIZATION_386=y
76CONFIG_STATIC_LIBGCC=y
77
78#
79# Installation Options ("make install" behavior)
80#
81CONFIG_INSTALL_APPLET_SYMLINKS=y
82# CONFIG_INSTALL_APPLET_HARDLINKS is not set
83# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
84# CONFIG_INSTALL_APPLET_DONT is not set
85# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
86# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
87# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
88CONFIG_PREFIX=""
89
90#
91# Debugging Options
92#
93# CONFIG_DEBUG is not set
94# CONFIG_DEBUG_PESSIMIZE is not set
95# CONFIG_DEBUG_SANITIZE is not set
96# CONFIG_UNIT_TEST is not set
97# CONFIG_WERROR is not set
98# CONFIG_WARN_SIMPLE_MSG is not set
99CONFIG_NO_DEBUG_LIB=y
100# CONFIG_DMALLOC is not set
101# CONFIG_EFENCE is not set
102
103#
104# Library Tuning
105#
106# CONFIG_FEATURE_USE_BSS_TAIL is not set
107CONFIG_FLOAT_DURATION=y
108# CONFIG_FEATURE_RTMINMAX is not set
109# CONFIG_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS is not set
110CONFIG_FEATURE_BUFFERS_USE_MALLOC=y
111# CONFIG_FEATURE_BUFFERS_GO_ON_STACK is not set
112# CONFIG_FEATURE_BUFFERS_GO_IN_BSS is not set
113CONFIG_PASSWORD_MINLEN=6
114CONFIG_MD5_SMALL=1
115CONFIG_SHA3_SMALL=1
116# CONFIG_FEATURE_FAST_TOP is not set
117# CONFIG_FEATURE_ETC_NETWORKS is not set
118# CONFIG_FEATURE_ETC_SERVICES is not set
119CONFIG_FEATURE_EDITING=y
120CONFIG_FEATURE_EDITING_MAX_LEN=1024
121CONFIG_FEATURE_EDITING_VI=y
122CONFIG_FEATURE_EDITING_HISTORY=255
123CONFIG_FEATURE_EDITING_SAVEHISTORY=y
124# CONFIG_FEATURE_EDITING_SAVE_ON_EXIT is not set
125CONFIG_FEATURE_REVERSE_SEARCH=y
126CONFIG_FEATURE_TAB_COMPLETION=y
127CONFIG_FEATURE_USERNAME_COMPLETION=y
128CONFIG_FEATURE_EDITING_FANCY_PROMPT=y
129# CONFIG_FEATURE_EDITING_WINCH is not set
130# CONFIG_FEATURE_EDITING_ASK_TERMINAL is not set
131# CONFIG_LOCALE_SUPPORT is not set
132# CONFIG_UNICODE_SUPPORT is not set
133# CONFIG_UNICODE_USING_LOCALE is not set
134# CONFIG_FEATURE_CHECK_UNICODE_IN_ENV is not set
135CONFIG_SUBST_WCHAR=0
136CONFIG_LAST_SUPPORTED_WCHAR=0
137# CONFIG_UNICODE_COMBINING_WCHARS is not set
138# CONFIG_UNICODE_WIDE_WCHARS is not set
139# CONFIG_UNICODE_BIDI_SUPPORT is not set
140# CONFIG_UNICODE_NEUTRAL_TABLE is not set
141# CONFIG_UNICODE_PRESERVE_BROKEN is not set
142CONFIG_FEATURE_NON_POSIX_CP=y
143# CONFIG_FEATURE_VERBOSE_CP_MESSAGE is not set
144# CONFIG_FEATURE_USE_SENDFILE is not set
145CONFIG_FEATURE_COPYBUF_KB=4
146# CONFIG_FEATURE_SKIP_ROOTFS is not set
147# CONFIG_MONOTONIC_SYSCALL is not set
148# CONFIG_IOCTL_HEX2STR_ERROR is not set
149# CONFIG_FEATURE_HWIB is not set
150CONFIG_FEATURE_TIMEZONE=y
151
152#
153# Applets
154#
155
156#
157# Archival Utilities
158#
159CONFIG_FEATURE_SEAMLESS_XZ=y
160CONFIG_FEATURE_SEAMLESS_LZMA=y
161CONFIG_FEATURE_SEAMLESS_BZ2=y
162CONFIG_FEATURE_SEAMLESS_GZ=y
163CONFIG_FEATURE_SEAMLESS_Z=y
164CONFIG_AR=y
165CONFIG_FEATURE_AR_LONG_FILENAMES=y
166CONFIG_FEATURE_AR_CREATE=y
167CONFIG_UNCOMPRESS=y
168CONFIG_GUNZIP=y
169CONFIG_ZCAT=y
170CONFIG_FEATURE_GUNZIP_LONG_OPTIONS=y
171CONFIG_BUNZIP2=y
172CONFIG_BZCAT=y
173CONFIG_UNLZMA=y
174CONFIG_LZCAT=y
175CONFIG_LZMA=y
176CONFIG_UNXZ=y
177CONFIG_XZCAT=y
178CONFIG_XZ=y
179CONFIG_BZIP2=y
180CONFIG_BZIP2_SMALL=8
181CONFIG_FEATURE_BZIP2_DECOMPRESS=y
182CONFIG_CPIO=y
183CONFIG_FEATURE_CPIO_O=y
184# CONFIG_FEATURE_CPIO_P is not set
185CONFIG_DPKG=y
186CONFIG_DPKG_DEB=y
187CONFIG_GZIP=y
188CONFIG_FEATURE_GZIP_LONG_OPTIONS=y
189CONFIG_GZIP_FAST=2
190CONFIG_FEATURE_GZIP_LEVELS=y
191CONFIG_FEATURE_GZIP_DECOMPRESS=y
192CONFIG_LZOP=y
193CONFIG_UNLZOP=y
194CONFIG_LZOPCAT=y
195# CONFIG_LZOP_COMPR_HIGH is not set
196CONFIG_RPM=y
197CONFIG_RPM2CPIO=y
198CONFIG_TAR=y
199CONFIG_FEATURE_TAR_LONG_OPTIONS=y
200CONFIG_FEATURE_TAR_CREATE=y
201CONFIG_FEATURE_TAR_AUTODETECT=y
202CONFIG_FEATURE_TAR_FROM=y
203CONFIG_FEATURE_TAR_OLDGNU_COMPATIBILITY=y
204# CONFIG_FEATURE_TAR_OLDSUN_COMPATIBILITY is not set
205CONFIG_FEATURE_TAR_GNU_EXTENSIONS=y
206# CONFIG_FEATURE_TAR_TO_COMMAND is not set
207# CONFIG_FEATURE_TAR_UNAME_GNAME is not set
208CONFIG_FEATURE_TAR_NOPRESERVE_TIME=y
209# CONFIG_FEATURE_TAR_SELINUX is not set
210CONFIG_UNZIP=y
211CONFIG_FEATURE_UNZIP_CDF=y
212CONFIG_FEATURE_UNZIP_BZIP2=y
213CONFIG_FEATURE_UNZIP_LZMA=y
214CONFIG_FEATURE_UNZIP_XZ=y
215CONFIG_FEATURE_LZMA_FAST=y
216
217#
218# Coreutils
219#
220CONFIG_BASENAME=y
221CONFIG_CAT=y
222CONFIG_FEATURE_CATN=y
223CONFIG_FEATURE_CATV=y
224# CONFIG_CHGRP is not set
225CONFIG_CHMOD=y
226# CONFIG_CHOWN is not set
227# CONFIG_FEATURE_CHOWN_LONG_OPTIONS is not set
228# CONFIG_CHROOT is not set
229CONFIG_CKSUM=y
230CONFIG_CRC32=y
231CONFIG_COMM=y
232CONFIG_CP=y
233CONFIG_FEATURE_CP_LONG_OPTIONS=y
234# CONFIG_FEATURE_CP_REFLINK is not set
235CONFIG_CUT=y
236CONFIG_FEATURE_CUT_REGEX=y
237CONFIG_DATE=y
238CONFIG_FEATURE_DATE_ISOFMT=y
239# CONFIG_FEATURE_DATE_NANO is not set
240CONFIG_FEATURE_DATE_COMPAT=y
241CONFIG_DD=y
242# CONFIG_FEATURE_DD_SIGNAL_HANDLING is not set
243# CONFIG_FEATURE_DD_THIRD_STATUS_LINE is not set
244CONFIG_FEATURE_DD_IBS_OBS=y
245CONFIG_FEATURE_DD_STATUS=y
246CONFIG_DF=y
247# CONFIG_FEATURE_DF_FANCY is not set
248CONFIG_DIRNAME=y
249CONFIG_DOS2UNIX=y
250CONFIG_UNIX2DOS=y
251CONFIG_DU=y
252CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K=y
253CONFIG_ECHO=y
254CONFIG_FEATURE_FANCY_ECHO=y
255CONFIG_ENV=y
256CONFIG_EXPAND=y
257CONFIG_UNEXPAND=y
258CONFIG_EXPR=y
259CONFIG_EXPR_MATH_SUPPORT_64=y
260CONFIG_FACTOR=y
261CONFIG_FALSE=y
262CONFIG_FOLD=y
263CONFIG_HEAD=y
264CONFIG_FEATURE_FANCY_HEAD=y
265# CONFIG_HOSTID is not set
266CONFIG_ID=y
267CONFIG_GROUPS=y
268CONFIG_INSTALL=y
269CONFIG_FEATURE_INSTALL_LONG_OPTIONS=y
270CONFIG_LINK=y
271CONFIG_LN=y
272CONFIG_LOGNAME=y
273CONFIG_LS=y
274CONFIG_FEATURE_LS_FILETYPES=y
275CONFIG_FEATURE_LS_FOLLOWLINKS=y
276CONFIG_FEATURE_LS_RECURSIVE=y
277CONFIG_FEATURE_LS_WIDTH=y
278CONFIG_FEATURE_LS_SORTFILES=y
279CONFIG_FEATURE_LS_TIMESTAMPS=y
280CONFIG_FEATURE_LS_USERNAME=y
281CONFIG_FEATURE_LS_COLOR=y
282CONFIG_FEATURE_LS_COLOR_IS_DEFAULT=y
283CONFIG_MD5SUM=y
284CONFIG_SHA1SUM=y
285CONFIG_SHA256SUM=y
286CONFIG_SHA512SUM=y
287CONFIG_SHA3SUM=y
288
289#
290# Common options for md5sum, sha1sum, sha256sum, sha512sum, sha3sum
291#
292CONFIG_FEATURE_MD5_SHA1_SUM_CHECK=y
293CONFIG_MKDIR=y
294# CONFIG_MKFIFO is not set
295# CONFIG_MKNOD is not set
296CONFIG_MKTEMP=y
297CONFIG_MV=y
298# CONFIG_NICE is not set
299CONFIG_NL=y
300# CONFIG_NOHUP is not set
301CONFIG_NPROC=y
302CONFIG_OD=y
303CONFIG_PASTE=y
304CONFIG_PRINTENV=y
305CONFIG_PRINTF=y
306CONFIG_PWD=y
307CONFIG_READLINK=y
308CONFIG_FEATURE_READLINK_FOLLOW=y
309CONFIG_REALPATH=y
310CONFIG_RM=y
311CONFIG_RMDIR=y
312CONFIG_SEQ=y
313CONFIG_SHRED=y
314CONFIG_SHUF=y
315CONFIG_SLEEP=y
316CONFIG_FEATURE_FANCY_SLEEP=y
317CONFIG_SORT=y
318CONFIG_FEATURE_SORT_BIG=y
319# CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY is not set
320CONFIG_SPLIT=y
321CONFIG_FEATURE_SPLIT_FANCY=y
322CONFIG_STAT=y
323CONFIG_FEATURE_STAT_FORMAT=y
324CONFIG_FEATURE_STAT_FILESYSTEM=y
325# CONFIG_STTY is not set
326CONFIG_SUM=y
327CONFIG_SYNC=y
328# CONFIG_FEATURE_SYNC_FANCY is not set
329CONFIG_FSYNC=y
330CONFIG_TAC=y
331CONFIG_TAIL=y
332CONFIG_FEATURE_FANCY_TAIL=y
333CONFIG_TEE=y
334CONFIG_FEATURE_TEE_USE_BLOCK_IO=y
335CONFIG_TEST=y
336CONFIG_TEST1=y
337CONFIG_TEST2=y
338CONFIG_FEATURE_TEST_64=y
339CONFIG_TIMEOUT=y
340CONFIG_TOUCH=y
341CONFIG_FEATURE_TOUCH_SUSV3=y
342CONFIG_TR=y
343CONFIG_FEATURE_TR_CLASSES=y
344CONFIG_FEATURE_TR_EQUIV=y
345CONFIG_TRUE=y
346CONFIG_TRUNCATE=y
347# CONFIG_TTY is not set
348CONFIG_UNAME=y
349CONFIG_UNAME_OSNAME="MS/Windows"
350CONFIG_BB_ARCH=y
351CONFIG_UNIQ=y
352CONFIG_UNLINK=y
353CONFIG_USLEEP=y
354CONFIG_UUDECODE=y
355CONFIG_BASE32=y
356CONFIG_BASE64=y
357CONFIG_UUENCODE=y
358CONFIG_WC=y
359CONFIG_FEATURE_WC_LARGE=y
360# CONFIG_WHO is not set
361# CONFIG_W is not set
362# CONFIG_USERS is not set
363CONFIG_WHOAMI=y
364CONFIG_YES=y
365
366#
367# Common options
368#
369CONFIG_FEATURE_VERBOSE=y
370
371#
372# Common options for cp and mv
373#
374# CONFIG_FEATURE_PRESERVE_HARDLINKS is not set
375
376#
377# Common options for df, du, ls
378#
379CONFIG_FEATURE_HUMAN_READABLE=y
380
381#
382# Console Utilities
383#
384# CONFIG_CHVT is not set
385CONFIG_CLEAR=y
386# CONFIG_DEALLOCVT is not set
387# CONFIG_DUMPKMAP is not set
388# CONFIG_FGCONSOLE is not set
389# CONFIG_KBD_MODE is not set
390# CONFIG_LOADFONT is not set
391# CONFIG_SETFONT is not set
392# CONFIG_FEATURE_SETFONT_TEXTUAL_MAP is not set
393CONFIG_DEFAULT_SETFONT_DIR=""
394# CONFIG_FEATURE_LOADFONT_PSF2 is not set
395# CONFIG_FEATURE_LOADFONT_RAW is not set
396# CONFIG_LOADKMAP is not set
397# CONFIG_OPENVT is not set
398CONFIG_RESET=y
399# CONFIG_RESIZE is not set
400# CONFIG_FEATURE_RESIZE_PRINT is not set
401# CONFIG_SETCONSOLE is not set
402# CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS is not set
403# CONFIG_SETKEYCODES is not set
404# CONFIG_SETLOGCONS is not set
405# CONFIG_SHOWKEY is not set
406
407#
408# Debian Utilities
409#
410CONFIG_PIPE_PROGRESS=y
411# CONFIG_RUN_PARTS is not set
412# CONFIG_FEATURE_RUN_PARTS_LONG_OPTIONS is not set
413# CONFIG_FEATURE_RUN_PARTS_FANCY is not set
414# CONFIG_START_STOP_DAEMON is not set
415# CONFIG_FEATURE_START_STOP_DAEMON_LONG_OPTIONS is not set
416# CONFIG_FEATURE_START_STOP_DAEMON_FANCY is not set
417CONFIG_WHICH=y
418
419#
420# klibc-utils
421#
422# CONFIG_MINIPS is not set
423# CONFIG_NUKE is not set
424# CONFIG_RESUME is not set
425# CONFIG_RUN_INIT is not set
426
427#
428# Editors
429#
430CONFIG_AWK=y
431CONFIG_FEATURE_AWK_LIBM=y
432CONFIG_FEATURE_AWK_GNU_EXTENSIONS=y
433CONFIG_CMP=y
434CONFIG_DIFF=y
435CONFIG_FEATURE_DIFF_LONG_OPTIONS=y
436CONFIG_FEATURE_DIFF_DIR=y
437CONFIG_ED=y
438CONFIG_PATCH=y
439CONFIG_SED=y
440CONFIG_VI=y
441CONFIG_FEATURE_VI_MAX_LEN=4096
442CONFIG_FEATURE_VI_8BIT=y
443CONFIG_FEATURE_VI_COLON=y
444CONFIG_FEATURE_VI_COLON_EXPAND=y
445CONFIG_FEATURE_VI_YANKMARK=y
446CONFIG_FEATURE_VI_SEARCH=y
447CONFIG_FEATURE_VI_REGEX_SEARCH=y
448# CONFIG_FEATURE_VI_USE_SIGNALS is not set
449CONFIG_FEATURE_VI_DOT_CMD=y
450CONFIG_FEATURE_VI_READONLY=y
451CONFIG_FEATURE_VI_SETOPTS=y
452CONFIG_FEATURE_VI_FILE_FORMAT=y
453CONFIG_FEATURE_VI_SET=y
454CONFIG_FEATURE_VI_WIN_RESIZE=y
455# CONFIG_FEATURE_VI_ASK_TERMINAL is not set
456CONFIG_FEATURE_VI_UNDO=y
457CONFIG_FEATURE_VI_UNDO_QUEUE=y
458CONFIG_FEATURE_VI_UNDO_QUEUE_MAX=256
459CONFIG_FEATURE_VI_VERBOSE_STATUS=y
460CONFIG_FEATURE_ALLOW_EXEC=y
461
462#
463# Finding Utilities
464#
465CONFIG_FIND=y
466CONFIG_FEATURE_FIND_PRINT0=y
467CONFIG_FEATURE_FIND_MTIME=y
468CONFIG_FEATURE_FIND_MMIN=y
469CONFIG_FEATURE_FIND_PERM=y
470CONFIG_FEATURE_FIND_TYPE=y
471CONFIG_FEATURE_FIND_EXECUTABLE=y
472CONFIG_FEATURE_FIND_XDEV=y
473CONFIG_FEATURE_FIND_MAXDEPTH=y
474CONFIG_FEATURE_FIND_NEWER=y
475CONFIG_FEATURE_FIND_INUM=y
476CONFIG_FEATURE_FIND_EXEC=y
477CONFIG_FEATURE_FIND_EXEC_PLUS=y
478# CONFIG_FEATURE_FIND_USER is not set
479# CONFIG_FEATURE_FIND_GROUP is not set
480CONFIG_FEATURE_FIND_NOT=y
481CONFIG_FEATURE_FIND_DEPTH=y
482CONFIG_FEATURE_FIND_PAREN=y
483CONFIG_FEATURE_FIND_SIZE=y
484CONFIG_FEATURE_FIND_PRUNE=y
485CONFIG_FEATURE_FIND_QUIT=y
486CONFIG_FEATURE_FIND_DELETE=y
487CONFIG_FEATURE_FIND_EMPTY=y
488CONFIG_FEATURE_FIND_PATH=y
489CONFIG_FEATURE_FIND_REGEX=y
490# CONFIG_FEATURE_FIND_CONTEXT is not set
491CONFIG_FEATURE_FIND_LINKS=y
492CONFIG_GREP=y
493CONFIG_EGREP=y
494CONFIG_FGREP=y
495CONFIG_FEATURE_GREP_CONTEXT=y
496CONFIG_XARGS=y
497CONFIG_FEATURE_XARGS_SUPPORT_CONFIRMATION=y
498CONFIG_FEATURE_XARGS_SUPPORT_QUOTES=y
499CONFIG_FEATURE_XARGS_SUPPORT_TERMOPT=y
500CONFIG_FEATURE_XARGS_SUPPORT_ZERO_TERM=y
501CONFIG_FEATURE_XARGS_SUPPORT_REPL_STR=y
502CONFIG_FEATURE_XARGS_SUPPORT_PARALLEL=y
503CONFIG_FEATURE_XARGS_SUPPORT_ARGS_FILE=y
504
505#
506# Init Utilities
507#
508# CONFIG_BOOTCHARTD is not set
509# CONFIG_FEATURE_BOOTCHARTD_BLOATED_HEADER is not set
510# CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE is not set
511# CONFIG_HALT is not set
512# CONFIG_POWEROFF is not set
513# CONFIG_REBOOT is not set
514# CONFIG_FEATURE_WAIT_FOR_INIT is not set
515# CONFIG_FEATURE_CALL_TELINIT is not set
516CONFIG_TELINIT_PATH=""
517# CONFIG_INIT is not set
518# CONFIG_LINUXRC is not set
519# CONFIG_FEATURE_USE_INITTAB is not set
520# CONFIG_FEATURE_KILL_REMOVED is not set
521CONFIG_FEATURE_KILL_DELAY=0
522# CONFIG_FEATURE_INIT_SCTTY is not set
523# CONFIG_FEATURE_INIT_SYSLOG is not set
524# CONFIG_FEATURE_INIT_QUIET is not set
525# CONFIG_FEATURE_INIT_COREDUMPS is not set
526CONFIG_INIT_TERMINAL_TYPE=""
527# CONFIG_FEATURE_INIT_MODIFY_CMDLINE is not set
528
529#
530# Login/Password Management Utilities
531#
532# CONFIG_FEATURE_SHADOWPASSWDS is not set
533# CONFIG_USE_BB_PWD_GRP is not set
534# CONFIG_USE_BB_SHADOW is not set
535# CONFIG_USE_BB_CRYPT is not set
536# CONFIG_USE_BB_CRYPT_SHA is not set
537# CONFIG_ADD_SHELL is not set
538# CONFIG_REMOVE_SHELL is not set
539# CONFIG_ADDGROUP is not set
540# CONFIG_FEATURE_ADDUSER_TO_GROUP is not set
541# CONFIG_ADDUSER is not set
542# CONFIG_FEATURE_CHECK_NAMES is not set
543CONFIG_LAST_ID=0
544CONFIG_FIRST_SYSTEM_ID=0
545CONFIG_LAST_SYSTEM_ID=0
546# CONFIG_CHPASSWD is not set
547CONFIG_FEATURE_DEFAULT_PASSWD_ALGO=""
548# CONFIG_CRYPTPW is not set
549# CONFIG_MKPASSWD is not set
550# CONFIG_DELUSER is not set
551# CONFIG_DELGROUP is not set
552# CONFIG_FEATURE_DEL_USER_FROM_GROUP is not set
553# CONFIG_GETTY is not set
554# CONFIG_LOGIN is not set
555# CONFIG_LOGIN_SESSION_AS_CHILD is not set
556# CONFIG_LOGIN_SCRIPTS is not set
557# CONFIG_FEATURE_NOLOGIN is not set
558# CONFIG_FEATURE_SECURETTY is not set
559# CONFIG_PASSWD is not set
560# CONFIG_FEATURE_PASSWD_WEAK_CHECK is not set
561# CONFIG_SU is not set
562# CONFIG_FEATURE_SU_SYSLOG is not set
563# CONFIG_FEATURE_SU_CHECKS_SHELLS is not set
564# CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY is not set
565# CONFIG_SULOGIN is not set
566CONFIG_SUW32=y
567# CONFIG_VLOCK is not set
568
569#
570# Linux Ext2 FS Progs
571#
572CONFIG_CHATTR=y
573# CONFIG_FSCK is not set
574CONFIG_LSATTR=y
575# CONFIG_TUNE2FS is not set
576
577#
578# Linux Module Utilities
579#
580# CONFIG_MODPROBE_SMALL is not set
581# CONFIG_DEPMOD is not set
582# CONFIG_INSMOD is not set
583# CONFIG_LSMOD is not set
584# CONFIG_FEATURE_LSMOD_PRETTY_2_6_OUTPUT is not set
585# CONFIG_MODINFO is not set
586# CONFIG_MODPROBE is not set
587# CONFIG_FEATURE_MODPROBE_BLACKLIST is not set
588# CONFIG_RMMOD is not set
589
590#
591# Options common to multiple modutils
592#
593# CONFIG_FEATURE_CMDLINE_MODULE_OPTIONS is not set
594# CONFIG_FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED is not set
595# CONFIG_FEATURE_2_4_MODULES is not set
596# CONFIG_FEATURE_INSMOD_VERSION_CHECKING is not set
597# CONFIG_FEATURE_INSMOD_KSYMOOPS_SYMBOLS is not set
598# CONFIG_FEATURE_INSMOD_LOADINKMEM is not set
599# CONFIG_FEATURE_INSMOD_LOAD_MAP is not set
600# CONFIG_FEATURE_INSMOD_LOAD_MAP_FULL is not set
601# CONFIG_FEATURE_CHECK_TAINTED_MODULE is not set
602# CONFIG_FEATURE_INSMOD_TRY_MMAP is not set
603# CONFIG_FEATURE_MODUTILS_ALIAS is not set
604# CONFIG_FEATURE_MODUTILS_SYMBOLS is not set
605CONFIG_DEFAULT_MODULES_DIR=""
606CONFIG_DEFAULT_DEPMOD_FILE=""
607
608#
609# Linux System Utilities
610#
611# CONFIG_ACPID is not set
612# CONFIG_FEATURE_ACPID_COMPAT is not set
613# CONFIG_BLKDISCARD is not set
614# CONFIG_BLKID is not set
615# CONFIG_FEATURE_BLKID_TYPE is not set
616# CONFIG_BLOCKDEV is not set
617CONFIG_CAL=y
618# CONFIG_CHRT is not set
619# CONFIG_DMESG is not set
620# CONFIG_FEATURE_DMESG_PRETTY is not set
621# CONFIG_EJECT is not set
622# CONFIG_FEATURE_EJECT_SCSI is not set
623# CONFIG_FALLOCATE is not set
624# CONFIG_FATATTR is not set
625# CONFIG_FBSET is not set
626# CONFIG_FEATURE_FBSET_FANCY is not set
627# CONFIG_FEATURE_FBSET_READMODE is not set
628# CONFIG_FDFORMAT is not set
629# CONFIG_FDISK is not set
630# CONFIG_FDISK_SUPPORT_LARGE_DISKS is not set
631# CONFIG_FEATURE_FDISK_WRITABLE is not set
632# CONFIG_FEATURE_AIX_LABEL is not set
633# CONFIG_FEATURE_SGI_LABEL is not set
634# CONFIG_FEATURE_SUN_LABEL is not set
635# CONFIG_FEATURE_OSF_LABEL is not set
636# CONFIG_FEATURE_GPT_LABEL is not set
637# CONFIG_FEATURE_FDISK_ADVANCED is not set
638# CONFIG_FINDFS is not set
639# CONFIG_FLOCK is not set
640# CONFIG_FDFLUSH is not set
641# CONFIG_FREERAMDISK is not set
642# CONFIG_FSCK_MINIX is not set
643# CONFIG_FSFREEZE is not set
644# CONFIG_FSTRIM is not set
645CONFIG_GETOPT=y
646CONFIG_FEATURE_GETOPT_LONG=y
647CONFIG_HEXDUMP=y
648CONFIG_HD=y
649CONFIG_XXD=y
650# CONFIG_HWCLOCK is not set
651# CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS is not set
652# CONFIG_IONICE is not set
653# CONFIG_IPCRM is not set
654# CONFIG_IPCS is not set
655# CONFIG_LAST is not set
656# CONFIG_FEATURE_LAST_FANCY is not set
657# CONFIG_LOSETUP is not set
658# CONFIG_LSPCI is not set
659# CONFIG_LSUSB is not set
660# CONFIG_MDEV is not set
661# CONFIG_FEATURE_MDEV_CONF is not set
662# CONFIG_FEATURE_MDEV_RENAME is not set
663# CONFIG_FEATURE_MDEV_RENAME_REGEXP is not set
664# CONFIG_FEATURE_MDEV_EXEC is not set
665# CONFIG_FEATURE_MDEV_LOAD_FIRMWARE is not set
666# CONFIG_FEATURE_MDEV_DAEMON is not set
667# CONFIG_MESG is not set
668# CONFIG_FEATURE_MESG_ENABLE_ONLY_GROUP is not set
669# CONFIG_MKE2FS is not set
670# CONFIG_MKFS_EXT2 is not set
671# CONFIG_MKFS_MINIX is not set
672# CONFIG_FEATURE_MINIX2 is not set
673# CONFIG_MKFS_REISER is not set
674# CONFIG_MKDOSFS is not set
675# CONFIG_MKFS_VFAT is not set
676# CONFIG_MKSWAP is not set
677# CONFIG_FEATURE_MKSWAP_UUID is not set
678# CONFIG_MORE is not set
679# CONFIG_MOUNT is not set
680# CONFIG_FEATURE_MOUNT_FAKE is not set
681# CONFIG_FEATURE_MOUNT_VERBOSE is not set
682# CONFIG_FEATURE_MOUNT_HELPERS is not set
683# CONFIG_FEATURE_MOUNT_LABEL is not set
684# CONFIG_FEATURE_MOUNT_NFS is not set
685# CONFIG_FEATURE_MOUNT_CIFS is not set
686# CONFIG_FEATURE_MOUNT_FLAGS is not set
687# CONFIG_FEATURE_MOUNT_FSTAB is not set
688# CONFIG_FEATURE_MOUNT_OTHERTAB is not set
689# CONFIG_MOUNTPOINT is not set
690# CONFIG_NOLOGIN is not set
691# CONFIG_NOLOGIN_DEPENDENCIES is not set
692# CONFIG_NSENTER is not set
693# CONFIG_PIVOT_ROOT is not set
694# CONFIG_RDATE is not set
695# CONFIG_RDEV is not set
696# CONFIG_READPROFILE is not set
697# CONFIG_RENICE is not set
698CONFIG_REV=y
699# CONFIG_RTCWAKE is not set
700# CONFIG_SCRIPT is not set
701# CONFIG_SCRIPTREPLAY is not set
702# CONFIG_SETARCH is not set
703# CONFIG_LINUX32 is not set
704# CONFIG_LINUX64 is not set
705# CONFIG_SETPRIV is not set
706# CONFIG_FEATURE_SETPRIV_DUMP is not set
707# CONFIG_FEATURE_SETPRIV_CAPABILITIES is not set
708# CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES is not set
709# CONFIG_SETSID is not set
710# CONFIG_SWAPON is not set
711# CONFIG_FEATURE_SWAPON_DISCARD is not set
712# CONFIG_FEATURE_SWAPON_PRI is not set
713# CONFIG_SWAPOFF is not set
714# CONFIG_FEATURE_SWAPONOFF_LABEL is not set
715# CONFIG_SWITCH_ROOT is not set
716# CONFIG_TASKSET is not set
717# CONFIG_FEATURE_TASKSET_FANCY is not set
718# CONFIG_FEATURE_TASKSET_CPULIST is not set
719# CONFIG_UEVENT is not set
720# CONFIG_UMOUNT is not set
721# CONFIG_FEATURE_UMOUNT_ALL is not set
722# CONFIG_UNSHARE is not set
723# CONFIG_WALL is not set
724# CONFIG_FEATURE_MOUNT_LOOP is not set
725# CONFIG_FEATURE_MOUNT_LOOP_CREATE is not set
726# CONFIG_FEATURE_MTAB_SUPPORT is not set
727# CONFIG_VOLUMEID is not set
728# CONFIG_FEATURE_VOLUMEID_BCACHE is not set
729# CONFIG_FEATURE_VOLUMEID_BTRFS is not set
730# CONFIG_FEATURE_VOLUMEID_CRAMFS is not set
731# CONFIG_FEATURE_VOLUMEID_EROFS is not set
732# CONFIG_FEATURE_VOLUMEID_EXFAT is not set
733# CONFIG_FEATURE_VOLUMEID_EXT is not set
734# CONFIG_FEATURE_VOLUMEID_F2FS is not set
735# CONFIG_FEATURE_VOLUMEID_FAT is not set
736# CONFIG_FEATURE_VOLUMEID_HFS is not set
737# CONFIG_FEATURE_VOLUMEID_ISO9660 is not set
738# CONFIG_FEATURE_VOLUMEID_JFS is not set
739# CONFIG_FEATURE_VOLUMEID_LFS is not set
740# CONFIG_FEATURE_VOLUMEID_LINUXRAID is not set
741# CONFIG_FEATURE_VOLUMEID_LINUXSWAP is not set
742# CONFIG_FEATURE_VOLUMEID_LUKS is not set
743# CONFIG_FEATURE_VOLUMEID_MINIX is not set
744# CONFIG_FEATURE_VOLUMEID_NILFS is not set
745# CONFIG_FEATURE_VOLUMEID_NTFS is not set
746# CONFIG_FEATURE_VOLUMEID_OCFS2 is not set
747# CONFIG_FEATURE_VOLUMEID_REISERFS is not set
748# CONFIG_FEATURE_VOLUMEID_ROMFS is not set
749# CONFIG_FEATURE_VOLUMEID_SQUASHFS is not set
750# CONFIG_FEATURE_VOLUMEID_SYSV is not set
751# CONFIG_FEATURE_VOLUMEID_UBIFS is not set
752# CONFIG_FEATURE_VOLUMEID_UDF is not set
753# CONFIG_FEATURE_VOLUMEID_XFS is not set
754
755#
756# Miscellaneous Utilities
757#
758# CONFIG_ADJTIMEX is not set
759CONFIG_ASCII=y
760# CONFIG_BBCONFIG is not set
761# CONFIG_FEATURE_COMPRESS_BBCONFIG is not set
762CONFIG_BC=y
763CONFIG_DC=y
764CONFIG_FEATURE_DC_BIG=y
765# CONFIG_FEATURE_DC_LIBM is not set
766CONFIG_FEATURE_BC_INTERACTIVE=y
767CONFIG_FEATURE_BC_LONG_OPTIONS=y
768# CONFIG_BEEP is not set
769CONFIG_FEATURE_BEEP_FREQ=0
770CONFIG_FEATURE_BEEP_LENGTH_MS=0
771# CONFIG_CHAT is not set
772# CONFIG_FEATURE_CHAT_NOFAIL is not set
773# CONFIG_FEATURE_CHAT_TTY_HIFI is not set
774# CONFIG_FEATURE_CHAT_IMPLICIT_CR is not set
775# CONFIG_FEATURE_CHAT_SWALLOW_OPTS is not set
776# CONFIG_FEATURE_CHAT_SEND_ESCAPES is not set
777# CONFIG_FEATURE_CHAT_VAR_ABORT_LEN is not set
778# CONFIG_FEATURE_CHAT_CLR_ABORT is not set
779# CONFIG_CONSPY is not set
780# CONFIG_CROND is not set
781# CONFIG_FEATURE_CROND_D is not set
782# CONFIG_FEATURE_CROND_CALL_SENDMAIL is not set
783# CONFIG_FEATURE_CROND_SPECIAL_TIMES is not set
784CONFIG_FEATURE_CROND_DIR=""
785# CONFIG_CRONTAB is not set
786# CONFIG_DEVFSD is not set
787# CONFIG_DEVFSD_MODLOAD is not set
788# CONFIG_DEVFSD_FG_NP is not set
789# CONFIG_DEVFSD_VERBOSE is not set
790# CONFIG_FEATURE_DEVFS is not set
791# CONFIG_DEVMEM is not set
792# CONFIG_FBSPLASH is not set
793# CONFIG_FLASH_ERASEALL is not set
794# CONFIG_FLASH_LOCK is not set
795# CONFIG_FLASH_UNLOCK is not set
796# CONFIG_FLASHCP is not set
797# CONFIG_HDPARM is not set
798# CONFIG_FEATURE_HDPARM_GET_IDENTITY is not set
799# CONFIG_FEATURE_HDPARM_HDIO_SCAN_HWIF is not set
800# CONFIG_FEATURE_HDPARM_HDIO_UNREGISTER_HWIF is not set
801# CONFIG_FEATURE_HDPARM_HDIO_DRIVE_RESET is not set
802# CONFIG_FEATURE_HDPARM_HDIO_TRISTATE_HWIF is not set
803# CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA is not set
804# CONFIG_HEXEDIT is not set
805# CONFIG_I2CGET is not set
806# CONFIG_I2CSET is not set
807# CONFIG_I2CDUMP is not set
808# CONFIG_I2CDETECT is not set
809# CONFIG_I2CTRANSFER is not set
810CONFIG_ICONV=y
811CONFIG_INOTIFYD=y
812CONFIG_LESS=y
813CONFIG_FEATURE_LESS_MAXLINES=9999999
814CONFIG_FEATURE_LESS_BRACKETS=y
815CONFIG_FEATURE_LESS_FLAGS=y
816CONFIG_FEATURE_LESS_TRUNCATE=y
817CONFIG_FEATURE_LESS_MARKS=y
818CONFIG_FEATURE_LESS_REGEXP=y
819# CONFIG_FEATURE_LESS_WINCH is not set
820# CONFIG_FEATURE_LESS_ASK_TERMINAL is not set
821CONFIG_FEATURE_LESS_DASHCMD=y
822CONFIG_FEATURE_LESS_LINENUMS=y
823CONFIG_FEATURE_LESS_RAW=y
824CONFIG_FEATURE_LESS_ENV=y
825# CONFIG_LSSCSI is not set
826# CONFIG_MAKEDEVS is not set
827# CONFIG_FEATURE_MAKEDEVS_LEAF is not set
828# CONFIG_FEATURE_MAKEDEVS_TABLE is not set
829CONFIG_MAN=y
830# CONFIG_MICROCOM is not set
831# CONFIG_MIM is not set
832# CONFIG_MT is not set
833# CONFIG_NANDWRITE is not set
834# CONFIG_NANDDUMP is not set
835# CONFIG_PARTPROBE is not set
836# CONFIG_RAIDAUTORUN is not set
837# CONFIG_READAHEAD is not set
838# CONFIG_RFKILL is not set
839# CONFIG_RUNLEVEL is not set
840# CONFIG_RX is not set
841# CONFIG_SETFATTR is not set
842# CONFIG_SETSERIAL is not set
843CONFIG_STRINGS=y
844CONFIG_TIME=y
845CONFIG_TS=y
846CONFIG_TTYSIZE=y
847# CONFIG_UBIATTACH is not set
848# CONFIG_UBIDETACH is not set
849# CONFIG_UBIMKVOL is not set
850# CONFIG_UBIRMVOL is not set
851# CONFIG_UBIRSVOL is not set
852# CONFIG_UBIUPDATEVOL is not set
853# CONFIG_UBIRENAME is not set
854# CONFIG_VOLNAME is not set
855# CONFIG_WATCHDOG is not set
856# CONFIG_FEATURE_WATCHDOG_OPEN_TWICE is not set
857
858#
859# Networking Utilities
860#
861CONFIG_FEATURE_IPV6=y
862# CONFIG_FEATURE_UNIX_LOCAL is not set
863CONFIG_FEATURE_PREFER_IPV4_ADDRESS=y
864# CONFIG_VERBOSE_RESOLUTION_ERRORS is not set
865# CONFIG_FEATURE_TLS_SHA1 is not set
866# CONFIG_ARP is not set
867# CONFIG_ARPING is not set
868# CONFIG_BRCTL is not set
869# CONFIG_FEATURE_BRCTL_FANCY is not set
870# CONFIG_FEATURE_BRCTL_SHOW is not set
871# CONFIG_DNSD is not set
872# CONFIG_ETHER_WAKE is not set
873# CONFIG_FTPD is not set
874# CONFIG_FEATURE_FTPD_WRITE is not set
875# CONFIG_FEATURE_FTPD_ACCEPT_BROKEN_LIST is not set
876# CONFIG_FEATURE_FTPD_AUTHENTICATION is not set
877CONFIG_FTPGET=y
878CONFIG_FTPPUT=y
879CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
880# CONFIG_HOSTNAME is not set
881# CONFIG_DNSDOMAINNAME is not set
882CONFIG_HTTPD=y
883CONFIG_FEATURE_HTTPD_PORT_DEFAULT=80
884CONFIG_FEATURE_HTTPD_RANGES=y
885# CONFIG_FEATURE_HTTPD_SETUID is not set
886CONFIG_FEATURE_HTTPD_BASIC_AUTH=y
887# CONFIG_FEATURE_HTTPD_AUTH_MD5 is not set
888# CONFIG_FEATURE_HTTPD_CGI is not set
889# CONFIG_FEATURE_HTTPD_CONFIG_WITH_SCRIPT_INTERPR is not set
890# CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV is not set
891CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
892CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
893# CONFIG_FEATURE_HTTPD_PROXY is not set
894CONFIG_FEATURE_HTTPD_GZIP=y
895CONFIG_FEATURE_HTTPD_ETAG=y
896CONFIG_FEATURE_HTTPD_LAST_MODIFIED=y
897CONFIG_FEATURE_HTTPD_DATE=y
898CONFIG_FEATURE_HTTPD_ACL_IP=y
899# CONFIG_IFCONFIG is not set
900# CONFIG_FEATURE_IFCONFIG_STATUS is not set
901# CONFIG_FEATURE_IFCONFIG_SLIP is not set
902# CONFIG_FEATURE_IFCONFIG_MEMSTART_IOADDR_IRQ is not set
903# CONFIG_FEATURE_IFCONFIG_HW is not set
904# CONFIG_FEATURE_IFCONFIG_BROADCAST_PLUS is not set
905# CONFIG_IFENSLAVE is not set
906# CONFIG_IFPLUGD is not set
907# CONFIG_IFUP is not set
908# CONFIG_IFDOWN is not set
909CONFIG_IFUPDOWN_IFSTATE_PATH=""
910# CONFIG_FEATURE_IFUPDOWN_IP is not set
911# CONFIG_FEATURE_IFUPDOWN_IPV4 is not set
912# CONFIG_FEATURE_IFUPDOWN_IPV6 is not set
913# CONFIG_FEATURE_IFUPDOWN_MAPPING is not set
914# CONFIG_FEATURE_IFUPDOWN_EXTERNAL_DHCP is not set
915# CONFIG_INETD is not set
916# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_ECHO is not set
917# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DISCARD is not set
918# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_TIME is not set
919# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_DAYTIME is not set
920# CONFIG_FEATURE_INETD_SUPPORT_BUILTIN_CHARGEN is not set
921# CONFIG_FEATURE_INETD_RPC is not set
922# CONFIG_IP is not set
923# CONFIG_IPADDR is not set
924# CONFIG_IPLINK is not set
925# CONFIG_IPROUTE is not set
926# CONFIG_IPTUNNEL is not set
927# CONFIG_IPRULE is not set
928# CONFIG_IPNEIGH is not set
929# CONFIG_FEATURE_IP_ADDRESS is not set
930# CONFIG_FEATURE_IP_LINK is not set
931# CONFIG_FEATURE_IP_ROUTE is not set
932CONFIG_FEATURE_IP_ROUTE_DIR=""
933# CONFIG_FEATURE_IP_TUNNEL is not set
934# CONFIG_FEATURE_IP_RULE is not set
935# CONFIG_FEATURE_IP_NEIGH is not set
936# CONFIG_FEATURE_IP_RARE_PROTOCOLS is not set
937CONFIG_IPCALC=y
938CONFIG_FEATURE_IPCALC_LONG_OPTIONS=y
939CONFIG_FEATURE_IPCALC_FANCY=y
940# CONFIG_FAKEIDENTD is not set
941# CONFIG_NAMEIF is not set
942# CONFIG_FEATURE_NAMEIF_EXTENDED is not set
943# CONFIG_NBDCLIENT is not set
944CONFIG_NC=y
945# CONFIG_NETCAT is not set
946CONFIG_NC_SERVER=y
947# CONFIG_NC_EXTRA is not set
948# CONFIG_NC_110_COMPAT is not set
949# CONFIG_NETSTAT is not set
950# CONFIG_FEATURE_NETSTAT_WIDE is not set
951# CONFIG_FEATURE_NETSTAT_PRG is not set
952# CONFIG_NSLOOKUP is not set
953# CONFIG_FEATURE_NSLOOKUP_BIG is not set
954# CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS is not set
955# CONFIG_NTPD is not set
956# CONFIG_FEATURE_NTPD_SERVER is not set
957# CONFIG_FEATURE_NTPD_CONF is not set
958# CONFIG_FEATURE_NTP_AUTH is not set
959# CONFIG_PING is not set
960# CONFIG_PING6 is not set
961# CONFIG_FEATURE_FANCY_PING is not set
962# CONFIG_PSCAN is not set
963# CONFIG_ROUTE is not set
964# CONFIG_SLATTACH is not set
965CONFIG_SSL_CLIENT=y
966# CONFIG_TC is not set
967# CONFIG_FEATURE_TC_INGRESS is not set
968# CONFIG_TCPSVD is not set
969# CONFIG_UDPSVD is not set
970# CONFIG_TELNET is not set
971# CONFIG_FEATURE_TELNET_TTYPE is not set
972# CONFIG_FEATURE_TELNET_AUTOLOGIN is not set
973# CONFIG_FEATURE_TELNET_WIDTH is not set
974# CONFIG_TELNETD is not set
975# CONFIG_FEATURE_TELNETD_STANDALONE is not set
976CONFIG_FEATURE_TELNETD_PORT_DEFAULT=0
977# CONFIG_FEATURE_TELNETD_INETD_WAIT is not set
978# CONFIG_TFTP is not set
979# CONFIG_FEATURE_TFTP_PROGRESS_BAR is not set
980# CONFIG_FEATURE_TFTP_HPA_COMPAT is not set
981# CONFIG_TFTPD is not set
982# CONFIG_FEATURE_TFTP_GET is not set
983# CONFIG_FEATURE_TFTP_PUT is not set
984# CONFIG_FEATURE_TFTP_BLOCKSIZE is not set
985# CONFIG_TFTP_DEBUG is not set
986CONFIG_TLS=y
987# CONFIG_TRACEROUTE is not set
988# CONFIG_TRACEROUTE6 is not set
989# CONFIG_FEATURE_TRACEROUTE_VERBOSE is not set
990# CONFIG_FEATURE_TRACEROUTE_USE_ICMP is not set
991# CONFIG_TUNCTL is not set
992# CONFIG_FEATURE_TUNCTL_UG is not set
993# CONFIG_VCONFIG is not set
994CONFIG_WGET=y
995CONFIG_FEATURE_WGET_LONG_OPTIONS=y
996# CONFIG_FEATURE_WGET_STATUSBAR is not set
997CONFIG_FEATURE_WGET_FTP=y
998CONFIG_FEATURE_WGET_AUTHENTICATION=y
999# CONFIG_FEATURE_WGET_TIMEOUT is not set
1000CONFIG_FEATURE_WGET_HTTPS=y
1001# CONFIG_FEATURE_WGET_OPENSSL is not set
1002CONFIG_WHOIS=y
1003# CONFIG_ZCIP is not set
1004# CONFIG_UDHCPD is not set
1005# CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC is not set
1006# CONFIG_FEATURE_UDHCPD_WRITE_LEASES_EARLY is not set
1007CONFIG_DHCPD_LEASES_FILE=""
1008# CONFIG_DUMPLEASES is not set
1009# CONFIG_DHCPRELAY is not set
1010# CONFIG_UDHCPC is not set
1011# CONFIG_FEATURE_UDHCPC_ARPING is not set
1012# CONFIG_FEATURE_UDHCPC_SANITIZEOPT is not set
1013CONFIG_UDHCPC_DEFAULT_SCRIPT=""
1014# CONFIG_UDHCPC6 is not set
1015# CONFIG_FEATURE_UDHCPC6_RFC3646 is not set
1016# CONFIG_FEATURE_UDHCPC6_RFC4704 is not set
1017# CONFIG_FEATURE_UDHCPC6_RFC4833 is not set
1018# CONFIG_FEATURE_UDHCPC6_RFC5970 is not set
1019CONFIG_UDHCPC_DEFAULT_INTERFACE=""
1020# CONFIG_FEATURE_UDHCP_PORT is not set
1021CONFIG_UDHCP_DEBUG=0
1022CONFIG_UDHCPC_SLACK_FOR_BUGGY_SERVERS=0
1023# CONFIG_FEATURE_UDHCP_RFC3397 is not set
1024# CONFIG_FEATURE_UDHCP_8021Q is not set
1025CONFIG_IFUPDOWN_UDHCPC_CMD_OPTIONS=""
1026
1027#
1028# Print Utilities
1029#
1030# CONFIG_LPD is not set
1031# CONFIG_LPR is not set
1032# CONFIG_LPQ is not set
1033
1034#
1035# Mail Utilities
1036#
1037# CONFIG_MAKEMIME is not set
1038# CONFIG_POPMAILDIR is not set
1039# CONFIG_FEATURE_POPMAILDIR_DELIVERY is not set
1040# CONFIG_REFORMIME is not set
1041# CONFIG_FEATURE_REFORMIME_COMPAT is not set
1042# CONFIG_SENDMAIL is not set
1043CONFIG_FEATURE_MIME_CHARSET=""
1044
1045#
1046# Process Utilities
1047#
1048CONFIG_FREE=y
1049# CONFIG_FUSER is not set
1050# CONFIG_IOSTAT is not set
1051CONFIG_KILL=y
1052CONFIG_KILLALL=y
1053# CONFIG_KILLALL5 is not set
1054# CONFIG_LSOF is not set
1055# CONFIG_MPSTAT is not set
1056# CONFIG_NMETER is not set
1057CONFIG_PGREP=y
1058CONFIG_PKILL=y
1059CONFIG_PIDOF=y
1060CONFIG_FEATURE_PIDOF_SINGLE=y
1061CONFIG_FEATURE_PIDOF_OMIT=y
1062# CONFIG_PMAP is not set
1063# CONFIG_POWERTOP is not set
1064# CONFIG_FEATURE_POWERTOP_INTERACTIVE is not set
1065CONFIG_PS=y
1066# CONFIG_FEATURE_PS_WIDE is not set
1067# CONFIG_FEATURE_PS_LONG is not set
1068CONFIG_FEATURE_PS_TIME=y
1069# CONFIG_FEATURE_PS_UNUSUAL_SYSTEMS is not set
1070# CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS is not set
1071# CONFIG_PSTREE is not set
1072# CONFIG_PWDX is not set
1073# CONFIG_SMEMCAP is not set
1074# CONFIG_BB_SYSCTL is not set
1075# CONFIG_TOP is not set
1076# CONFIG_FEATURE_TOP_INTERACTIVE is not set
1077# CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE is not set
1078# CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS is not set
1079# CONFIG_FEATURE_TOP_SMP_CPU is not set
1080# CONFIG_FEATURE_TOP_DECIMALS is not set
1081# CONFIG_FEATURE_TOP_SMP_PROCESS is not set
1082# CONFIG_FEATURE_TOPMEM is not set
1083CONFIG_UPTIME=y
1084# CONFIG_FEATURE_UPTIME_UTMP_SUPPORT is not set
1085CONFIG_WATCH=y
1086# CONFIG_FEATURE_SHOW_THREADS is not set
1087
1088#
1089# Runit Utilities
1090#
1091# CONFIG_CHPST is not set
1092# CONFIG_SETUIDGID is not set
1093# CONFIG_ENVUIDGID is not set
1094# CONFIG_ENVDIR is not set
1095# CONFIG_SOFTLIMIT is not set
1096# CONFIG_RUNSV is not set
1097# CONFIG_RUNSVDIR is not set
1098# CONFIG_FEATURE_RUNSVDIR_LOG is not set
1099# CONFIG_SV is not set
1100CONFIG_SV_DEFAULT_SERVICE_DIR=""
1101# CONFIG_SVC is not set
1102# CONFIG_SVOK is not set
1103# CONFIG_SVLOGD is not set
1104# CONFIG_CHCON is not set
1105# CONFIG_GETENFORCE is not set
1106# CONFIG_GETSEBOOL is not set
1107# CONFIG_LOAD_POLICY is not set
1108# CONFIG_MATCHPATHCON is not set
1109# CONFIG_RUNCON is not set
1110# CONFIG_SELINUXENABLED is not set
1111# CONFIG_SESTATUS is not set
1112# CONFIG_SETENFORCE is not set
1113# CONFIG_SETFILES is not set
1114# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
1115# CONFIG_RESTORECON is not set
1116# CONFIG_SETSEBOOL is not set
1117
1118#
1119# Shells
1120#
1121CONFIG_SH_IS_ASH=y
1122# CONFIG_SH_IS_HUSH is not set
1123# CONFIG_SH_IS_NONE is not set
1124CONFIG_BASH_IS_ASH=y
1125# CONFIG_BASH_IS_HUSH is not set
1126# CONFIG_BASH_IS_NONE is not set
1127CONFIG_SHELL_ASH=y
1128CONFIG_ASH=y
1129# CONFIG_ASH_OPTIMIZE_FOR_SIZE is not set
1130CONFIG_ASH_INTERNAL_GLOB=y
1131CONFIG_ASH_BASH_COMPAT=y
1132# CONFIG_ASH_BASH_SOURCE_CURDIR is not set
1133CONFIG_ASH_BASH_NOT_FOUND_HOOK=y
1134CONFIG_ASH_JOB_CONTROL=y
1135CONFIG_ASH_ALIAS=y
1136CONFIG_ASH_RANDOM_SUPPORT=y
1137CONFIG_ASH_EXPAND_PRMT=y
1138# CONFIG_ASH_IDLE_TIMEOUT is not set
1139# CONFIG_ASH_MAIL is not set
1140CONFIG_ASH_ECHO=y
1141CONFIG_ASH_PRINTF=y
1142CONFIG_ASH_TEST=y
1143CONFIG_ASH_HELP=y
1144CONFIG_ASH_GETOPTS=y
1145CONFIG_ASH_CMDCMD=y
1146CONFIG_ASH_NOCONSOLE=y
1147CONFIG_ASH_NOCASEGLOB=y
1148# CONFIG_CTTYHACK is not set
1149# CONFIG_HUSH is not set
1150# CONFIG_SHELL_HUSH is not set
1151# CONFIG_HUSH_BASH_COMPAT is not set
1152# CONFIG_HUSH_BRACE_EXPANSION is not set
1153# CONFIG_HUSH_BASH_SOURCE_CURDIR is not set
1154# CONFIG_HUSH_LINENO_VAR is not set
1155# CONFIG_HUSH_INTERACTIVE is not set
1156# CONFIG_HUSH_SAVEHISTORY is not set
1157# CONFIG_HUSH_JOB is not set
1158# CONFIG_HUSH_TICK is not set
1159# CONFIG_HUSH_IF is not set
1160# CONFIG_HUSH_LOOPS is not set
1161# CONFIG_HUSH_CASE is not set
1162# CONFIG_HUSH_FUNCTIONS is not set
1163# CONFIG_HUSH_LOCAL is not set
1164# CONFIG_HUSH_RANDOM_SUPPORT is not set
1165# CONFIG_HUSH_MODE_X is not set
1166# CONFIG_HUSH_ECHO is not set
1167# CONFIG_HUSH_PRINTF is not set
1168# CONFIG_HUSH_TEST is not set
1169# CONFIG_HUSH_HELP is not set
1170# CONFIG_HUSH_EXPORT is not set
1171# CONFIG_HUSH_EXPORT_N is not set
1172# CONFIG_HUSH_READONLY is not set
1173# CONFIG_HUSH_KILL is not set
1174# CONFIG_HUSH_WAIT is not set
1175# CONFIG_HUSH_COMMAND is not set
1176# CONFIG_HUSH_TRAP is not set
1177# CONFIG_HUSH_TYPE is not set
1178# CONFIG_HUSH_TIMES is not set
1179# CONFIG_HUSH_READ is not set
1180# CONFIG_HUSH_SET is not set
1181# CONFIG_HUSH_UNSET is not set
1182# CONFIG_HUSH_ULIMIT is not set
1183# CONFIG_HUSH_UMASK is not set
1184# CONFIG_HUSH_GETOPTS is not set
1185# CONFIG_HUSH_MEMLEAK is not set
1186
1187#
1188# Options common to all shells
1189#
1190CONFIG_FEATURE_SH_MATH=y
1191CONFIG_FEATURE_SH_MATH_64=y
1192CONFIG_FEATURE_SH_MATH_BASE=y
1193CONFIG_FEATURE_SH_EXTRA_QUIET=y
1194CONFIG_FEATURE_SH_STANDALONE=y
1195CONFIG_FEATURE_SH_NOFORK=y
1196# CONFIG_FEATURE_SH_READ_FRAC is not set
1197CONFIG_FEATURE_SH_HISTFILESIZE=y
1198CONFIG_FEATURE_SH_EMBEDDED_SCRIPTS=y
1199
1200#
1201# System Logging Utilities
1202#
1203# CONFIG_KLOGD is not set
1204# CONFIG_FEATURE_KLOGD_KLOGCTL is not set
1205# CONFIG_LOGGER is not set
1206# CONFIG_LOGREAD is not set
1207# CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING is not set
1208# CONFIG_SYSLOGD is not set
1209# CONFIG_FEATURE_ROTATE_LOGFILE is not set
1210# CONFIG_FEATURE_REMOTE_LOG is not set
1211# CONFIG_FEATURE_SYSLOGD_DUP is not set
1212# CONFIG_FEATURE_SYSLOGD_CFG is not set
1213# CONFIG_FEATURE_SYSLOGD_PRECISE_TIMESTAMPS is not set
1214CONFIG_FEATURE_SYSLOGD_READ_BUFFER_SIZE=0
1215# CONFIG_FEATURE_IPC_SYSLOG is not set
1216CONFIG_FEATURE_IPC_SYSLOG_BUFFER_SIZE=0
1217# 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/date.c b/coreutils/date.c
index abcc37c33..4e62a6fb0 100644
--- a/coreutils/date.c
+++ b/coreutils/date.c
@@ -97,7 +97,9 @@
97//usage:#define date_full_usage "\n\n" 97//usage:#define date_full_usage "\n\n"
98//usage: "Display time (using +FMT), or set time\n" 98//usage: "Display time (using +FMT), or set time\n"
99//usage: "\n -u Work in UTC (don't convert to local time)" 99//usage: "\n -u Work in UTC (don't convert to local time)"
100//usage: IF_NOT_PLATFORM_MINGW32(
100//usage: "\n [-s] TIME Set time to TIME" 101//usage: "\n [-s] TIME Set time to TIME"
102//usage: )
101//usage: "\n -d TIME Display TIME, not 'now'" 103//usage: "\n -d TIME Display TIME, not 'now'"
102//usage: IF_FEATURE_DATE_ISOFMT( 104//usage: IF_FEATURE_DATE_ISOFMT(
103//usage: "\n -D FMT FMT (strptime format) for -s/-d TIME conversion" 105//usage: "\n -D FMT FMT (strptime format) for -s/-d TIME conversion"
@@ -132,19 +134,30 @@
132 134
133enum { 135enum {
134 OPT_RFC2822 = (1 << 0), /* R */ 136 OPT_RFC2822 = (1 << 0), /* R */
137#if !ENABLE_PLATFORM_MINGW32
135 OPT_SET = (1 << 1), /* s */ 138 OPT_SET = (1 << 1), /* s */
136 OPT_UTC = (1 << 2), /* u */ 139 OPT_UTC = (1 << 2), /* u */
137 OPT_DATE = (1 << 3), /* d */ 140 OPT_DATE = (1 << 3), /* d */
138 OPT_REFERENCE = (1 << 4), /* r */ 141 OPT_REFERENCE = (1 << 4), /* r */
139 OPT_ISO8601 = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */ 142 OPT_ISO8601 = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* I */
140 OPT_STR2DT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */ 143 OPT_STR2DT = (1 << 6) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
144#else
145 OPT_SET = (0), /* s */
146 OPT_UTC = (1 << 1), /* u */
147 OPT_DATE = (1 << 2), /* d */
148 OPT_REFERENCE = (1 << 3), /* r */
149 OPT_ISO8601 = (1 << 4) * ENABLE_FEATURE_DATE_ISOFMT, /* I */
150 OPT_STR2DT = (1 << 5) * ENABLE_FEATURE_DATE_ISOFMT, /* D */
151#endif
141}; 152};
142 153
143#if ENABLE_LONG_OPTS 154#if ENABLE_LONG_OPTS
144static const char date_longopts[] ALIGN1 = 155static const char date_longopts[] ALIGN1 =
145 "rfc-822\0" No_argument "R" 156 "rfc-822\0" No_argument "R"
146 "rfc-2822\0" No_argument "R" 157 "rfc-2822\0" No_argument "R"
158#if !ENABLE_PLATFORM_MINGW32
147 "set\0" Required_argument "s" 159 "set\0" Required_argument "s"
160#endif
148 "utc\0" No_argument "u" 161 "utc\0" No_argument "u"
149 /* "universal\0" No_argument "u" */ 162 /* "universal\0" No_argument "u" */
150 "date\0" Required_argument "d" 163 "date\0" Required_argument "d"
@@ -174,13 +187,25 @@ int date_main(int argc UNUSED_PARAM, char **argv)
174 char *isofmt_arg = NULL; 187 char *isofmt_arg = NULL;
175 188
176 opt = getopt32long(argv, "^" 189 opt = getopt32long(argv, "^"
190#if !ENABLE_PLATFORM_MINGW32
177 "Rs:ud:r:" 191 "Rs:ud:r:"
192#else
193 "Rud:r:"
194#endif
178 IF_FEATURE_DATE_ISOFMT("I::D:") 195 IF_FEATURE_DATE_ISOFMT("I::D:")
196#if !ENABLE_PLATFORM_MINGW32
179 "\0" 197 "\0"
180 "d--s:s--d" 198 "d--s:s--d"
181 IF_FEATURE_DATE_ISOFMT(":R--I:I--R"), 199 IF_FEATURE_DATE_ISOFMT(":R--I:I--R"),
200#else
201 IF_FEATURE_DATE_ISOFMT("\0R--I:I--R"),
202#endif
182 date_longopts, 203 date_longopts,
204#if !ENABLE_PLATFORM_MINGW32
183 &date_str, &date_str, &filename 205 &date_str, &date_str, &filename
206#else
207 &date_str, &filename
208#endif
184 IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt) 209 IF_FEATURE_DATE_ISOFMT(, &isofmt_arg, &fmt_str2dt)
185 ); 210 );
186 argv += optind; 211 argv += optind;
diff --git a/coreutils/dd.c b/coreutils/dd.c
index 06c1b7b9c..a3c9ababf 100644
--- a/coreutils/dd.c
+++ b/coreutils/dd.c
@@ -94,6 +94,9 @@
94//usage: "\n status=none Suppress all output" 94//usage: "\n status=none Suppress all output"
95//usage: ) 95//usage: )
96//usage: "\n" 96//usage: "\n"
97//usage: IF_PLATFORM_MINGW32(
98//usage: "\nif=/dev/zero and if=/dev/urandom are supported"
99//usage: )
97//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G" 100//usage: "\nN may be suffixed by c (1), w (2), b (512), kB (1000), k (1024), MB, M, GB, G"
98//usage: 101//usage:
99//usage:#define dd_example_usage 102//usage:#define dd_example_usage
@@ -301,9 +304,11 @@ static int parse_comma_flags(char *val, const char *words, const char *error_in)
301 304
302static void *alloc_buf(size_t size) 305static void *alloc_buf(size_t size)
303{ 306{
307#if !ENABLE_PLATFORM_MINGW32
304 /* Important for "{i,o}flag=direct" - buffers must be page aligned */ 308 /* Important for "{i,o}flag=direct" - buffers must be page aligned */
305 if (size >= bb_getpagesize()) 309 if (size >= bb_getpagesize())
306 return xmmap_anon(size); 310 return xmmap_anon(size);
311#endif
307 return xmalloc(size); 312 return xmalloc(size);
308} 313}
309 314
@@ -423,11 +428,11 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
423#if ENABLE_FEATURE_DD_IBS_OBS 428#if ENABLE_FEATURE_DD_IBS_OBS
424 if (what == OP_ibs) { 429 if (what == OP_ibs) {
425 /* Must fit into positive ssize_t */ 430 /* Must fit into positive ssize_t */
426 ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 431 ibs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
427 /*continue;*/ 432 /*continue;*/
428 } 433 }
429 if (what == OP_obs) { 434 if (what == OP_obs) {
430 obs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 435 obs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
431 /*continue;*/ 436 /*continue;*/
432 } 437 }
433 if (what == OP_conv) { 438 if (what == OP_conv) {
@@ -444,7 +449,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
444 } 449 }
445#endif 450#endif
446 if (what == OP_bs) { 451 if (what == OP_bs) {
447 ibs = xatoul_range_sfx(val, 1, ((size_t)-1L)/2, cwbkMG_suffixes); 452 ibs = xatoul_range_sfx(val, 1, ULONG_MAX/2, cwbkMG_suffixes);
448 obs = ibs; 453 obs = ibs;
449 /*continue;*/ 454 /*continue;*/
450 } 455 }
@@ -504,7 +509,10 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
504 if (G.flags & FLAG_IDIRECT) 509 if (G.flags & FLAG_IDIRECT)
505 iflag |= O_DIRECT; 510 iflag |= O_DIRECT;
506#endif 511#endif
507 xmove_fd(xopen(infile, iflag), ifd); 512 xmove_fd(MINGW_SPECIAL(xopen)(infile, iflag), ifd);
513#if ENABLE_PLATFORM_MINGW32
514 update_special_fd(get_dev_type(infile), ifd);
515#endif
508 } else { 516 } else {
509 infile = bb_msg_standard_input; 517 infile = bb_msg_standard_input;
510 } 518 }
@@ -521,6 +529,27 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
521#endif 529#endif
522 xmove_fd(xopen(outfile, oflag), ofd); 530 xmove_fd(xopen(outfile, oflag), ofd);
523 531
532#if ENABLE_PLATFORM_MINGW32
533 {
534 off_t len = (off_t)seek * ((G.flags & FLAG_SEEK_BYTES) ? 1 : obs);
535 struct stat st;
536 int ret = fstat(ofd, &st);
537
538 if (ret == 0 && !(G.flags & FLAG_APPEND) && len > st.st_size)
539 make_sparse(ofd, st.st_size, len);
540
541 if (seek && !(G.flags & FLAG_NOTRUNC)) {
542 if (ftruncate(ofd, len) < 0) {
543 if (ret < 0
544 || S_ISREG(st.st_mode)
545 || S_ISDIR(st.st_mode)
546 ) {
547 goto die_outfile;
548 }
549 }
550 }
551 }
552#else
524 if (seek && !(G.flags & FLAG_NOTRUNC)) { 553 if (seek && !(G.flags & FLAG_NOTRUNC)) {
525 size_t blocksz = (G.flags & FLAG_SEEK_BYTES) ? 1 : obs; 554 size_t blocksz = (G.flags & FLAG_SEEK_BYTES) ? 1 : obs;
526 if (ftruncate(ofd, seek * blocksz) < 0) { 555 if (ftruncate(ofd, seek * blocksz) < 0) {
@@ -534,6 +563,7 @@ int dd_main(int argc UNUSED_PARAM, char **argv)
534 } 563 }
535 } 564 }
536 } 565 }
566#endif
537 } else { 567 } else {
538 outfile = bb_msg_standard_output; 568 outfile = bb_msg_standard_output;
539 } 569 }
diff --git a/coreutils/du.c b/coreutils/du.c
index 832dd7594..092647468 100644
--- a/coreutils/du.c
+++ b/coreutils/du.c
@@ -137,7 +137,7 @@ static void print(unsigned long long size, const char *filename)
137 size >>= 10; 137 size >>= 10;
138 } 138 }
139 } 139 }
140 printf("%llu\t%s\n", size, filename); 140 printf("%"LL_FMT"u\t%s\n", size, filename);
141#endif 141#endif
142} 142}
143 143
diff --git a/coreutils/expr.c b/coreutils/expr.c
index 760b081f9..b896acd44 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 de8ea4e11..a7a5a5030 100644
--- a/coreutils/factor.c
+++ b/coreutils/factor.c
@@ -125,7 +125,7 @@ static NOINLINE void print_w(wide_t n)
125{ 125{
126 unsigned rep = square_count; 126 unsigned rep = square_count;
127 do 127 do
128 printf(" %llu", n); 128 printf(" %"LL_FMT"u", n);
129 while (--rep != 0); 129 while (--rep != 0);
130} 130}
131static NOINLINE void print_h(half_t n) 131static NOINLINE void print_h(half_t n)
@@ -227,7 +227,7 @@ static void factorize_numstr(const char *numstr)
227 N = bb_strtoull(numstr, NULL, 10); 227 N = bb_strtoull(numstr, NULL, 10);
228 if (errno) 228 if (errno)
229 bb_show_usage(); 229 bb_show_usage();
230 printf("%llu:", N); 230 printf("%"LL_FMT"u:", N);
231 square_count = 1; 231 square_count = 1;
232 factorize(N); 232 factorize(N);
233} 233}
diff --git a/coreutils/ls.c b/coreutils/ls.c
index 48f5eb482..58b8fde75 100644
--- a/coreutils/ls.c
+++ b/coreutils/ls.c
@@ -316,6 +316,9 @@ struct dnode {
316 int dn_rdev_min; 316 int dn_rdev_min;
317// dev_t dn_dev; 317// dev_t dn_dev;
318// blksize_t dn_blksize; 318// blksize_t dn_blksize;
319#if ENABLE_PLATFORM_MINGW32
320 DWORD dn_attr;
321#endif
319}; 322};
320 323
321struct globals { 324struct globals {
@@ -497,7 +500,11 @@ static NOINLINE unsigned display_single(const struct dnode *dn)
497 lpath = xmalloc_readlink_or_warn(dn->fullname); 500 lpath = xmalloc_readlink_or_warn(dn->fullname);
498 501
499 if (opt & OPT_i) /* show inode# */ 502 if (opt & OPT_i) /* show inode# */
500 column += printf("%7llu ", (long long) dn->dn_ino); 503#if !ENABLE_FEATURE_EXTRA_FILE_DATA
504 column += printf("%7"LL_FMT"u ", (long long) dn->dn_ino);
505#else
506 column += printf("%19"LL_FMT"u ", (long long) dn->dn_ino);
507#endif
501//TODO: -h should affect -s too: 508//TODO: -h should affect -s too:
502 if (opt & OPT_s) /* show allocated blocks */ 509 if (opt & OPT_s) /* show allocated blocks */
503 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1)); 510 column += printf("%6"OFF_FMT"u ", (off_t) (dn->dn_blocks >> 1));
@@ -652,7 +659,11 @@ static void display_files(struct dnode **dn, unsigned nfiles)
652 } 659 }
653 column_width += 2 660 column_width += 2
654 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */ 661 + ((option_mask32 & OPT_Z) ? 33 : 0) /* context width */
662#if !ENABLE_FEATURE_EXTRA_FILE_DATA
655 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */ 663 + ((option_mask32 & OPT_i) ? 8 : 0) /* inode# width */
664#else
665 + ((option_mask32 & OPT_i) ? 20 : 0) /* inode# width */
666#endif
656 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */ 667 + ((option_mask32 & OPT_s) ? 5 : 0) /* "alloc block" width */
657 ; 668 ;
658 ncols = (unsigned)G_terminal_width / column_width; 669 ncols = (unsigned)G_terminal_width / column_width;
@@ -733,6 +744,9 @@ static struct dnode *my_stat(const char *fullname, const char *name, int force_f
733 744
734 /* cur->dstat = statbuf: */ 745 /* cur->dstat = statbuf: */
735 cur->dn_mode = statbuf.st_mode ; 746 cur->dn_mode = statbuf.st_mode ;
747#if ENABLE_PLATFORM_MINGW32
748 cur->dn_attr = statbuf.st_attr ;
749#endif
736 cur->dn_size = statbuf.st_size ; 750 cur->dn_size = statbuf.st_size ;
737#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES 751#if ENABLE_FEATURE_LS_TIMESTAMPS || ENABLE_FEATURE_LS_SORTFILES
738 cur->dn_time = statbuf.st_mtime ; 752 cur->dn_time = statbuf.st_mtime ;
@@ -942,9 +956,20 @@ static struct dnode **scan_one_dir(const char *path, unsigned *nfiles_p)
942 continue; /* if only -A, skip . and .. but show other dotfiles */ 956 continue; /* if only -A, skip . and .. but show other dotfiles */
943 } 957 }
944 } 958 }
959#if ENABLE_PLATFORM_MINGW32
960 if (has_dos_drive_prefix(path) && path[2] == '\0')
961 fullname = xasprintf("%s%s", path, entry->d_name);
962 else
963#endif
945 fullname = concat_path_file(path, entry->d_name); 964 fullname = concat_path_file(path, entry->d_name);
946 cur = my_stat(fullname, bb_basename(fullname), 0); 965 cur = my_stat(fullname, bb_basename(fullname), 0);
966#if !ENABLE_PLATFORM_MINGW32
947 if (!cur) { 967 if (!cur) {
968#else
969 if (!cur || ((cur->dn_attr & FILE_ATTRIBUTE_HIDDEN) &&
970 !(option_mask32 & (OPT_a|OPT_A)))) {
971 /* skip invalid or hidden files */
972#endif
948 free(fullname); 973 free(fullname);
949 continue; 974 continue;
950 } 975 }
@@ -1053,6 +1078,18 @@ static void scan_and_display_dirs_recur(struct dnode **dn, int first)
1053 } 1078 }
1054} 1079}
1055 1080
1081#if ENABLE_PLATFORM_MINGW32
1082static char *fix_backslash(char *p)
1083{
1084 const char *flag = getenv("BB_FIX_BACKSLASH");
1085 int value = flag ? atoi(flag) : 0;
1086
1087 if (value == 1)
1088 bs_to_slash(p);
1089 return p;
1090}
1091#endif
1092
1056 1093
1057int ls_main(int argc UNUSED_PARAM, char **argv) 1094int ls_main(int argc UNUSED_PARAM, char **argv)
1058{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */ 1095{ /* ^^^^^^^^^^^^^^^^^ note: if FTPD, argc can be wrong, see ftpd.c */
@@ -1208,6 +1245,9 @@ int ls_main(int argc UNUSED_PARAM, char **argv)
1208 dn = NULL; 1245 dn = NULL;
1209 nfiles = 0; 1246 nfiles = 0;
1210 do { 1247 do {
1248#if ENABLE_PLATFORM_MINGW32
1249 *argv = fix_backslash(*argv);
1250#endif
1211 cur = my_stat(*argv, *argv, 1251 cur = my_stat(*argv, *argv,
1212 /* follow links on command line unless -l, -i, -s or -F: */ 1252 /* follow links on command line unless -l, -i, -s or -F: */
1213 !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F)) 1253 !(option_mask32 & (OPT_l|OPT_i|OPT_s|OPT_F))
diff --git a/coreutils/nproc.c b/coreutils/nproc.c
index bb9bc56c5..fb673a210 100644
--- a/coreutils/nproc.c
+++ b/coreutils/nproc.c
@@ -29,7 +29,11 @@
29int nproc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 29int nproc_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
30int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 30int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
31{ 31{
32#if !ENABLE_PLATFORM_MINGW32
32 unsigned long mask[1024]; 33 unsigned long mask[1024];
34#else
35 DWORD_PTR affinity, process_affinity, system_affinity;
36#endif
33 int count = 0; 37 int count = 0;
34#if ENABLE_LONG_OPTS 38#if ENABLE_LONG_OPTS
35 int ignore = 0; 39 int ignore = 0;
@@ -38,7 +42,10 @@ int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
38 "all\0" No_argument "\xff" 42 "all\0" No_argument "\xff"
39 , &ignore 43 , &ignore
40 ); 44 );
45#endif
41 46
47#if !ENABLE_PLATFORM_MINGW32
48#if ENABLE_LONG_OPTS
42 if (opts & (1 << 1)) { 49 if (opts & (1 << 1)) {
43 DIR *cpusd = opendir("/sys/devices/system/cpu"); 50 DIR *cpusd = opendir("/sys/devices/system/cpu");
44 if (cpusd) { 51 if (cpusd) {
@@ -63,6 +70,17 @@ int nproc_main(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
63 } 70 }
64 } 71 }
65 } 72 }
73#else /* ENABLE_PLATFORM_MINGW32 */
74 if (GetProcessAffinityMask(GetCurrentProcess(), &process_affinity,
75 &system_affinity)) {
76 affinity = (ENABLE_LONG_OPTS && opts & (1 << 1)) ?
77 system_affinity : process_affinity;
78 while (affinity) {
79 count += affinity & 1;
80 affinity >>= 1;
81 }
82 }
83#endif /* ENABLE_PLATFORM_MINGW32 */
66 84
67 IF_LONG_OPTS(count -= ignore;) 85 IF_LONG_OPTS(count -= ignore;)
68 if (count <= 0) 86 if (count <= 0)
diff --git a/coreutils/od_bloaty.c b/coreutils/od_bloaty.c
index 5b5e56a21..1830aca83 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/printf.c b/coreutils/printf.c
index 2e672d15f..da129f909 100644
--- a/coreutils/printf.c
+++ b/coreutils/printf.c
@@ -152,6 +152,71 @@ static double my_xstrtod(const char *arg)
152 return result; 152 return result;
153} 153}
154 154
155#if ENABLE_PLATFORM_MINGW32
156static int buflen = 0;
157static int bufmax = 0;
158static char *buffer = NULL;
159
160static void my_flush(void)
161{
162 if (buffer)
163 full_write(STDOUT_FILENO, buffer, buflen);
164 free(buffer);
165 buffer = NULL;
166 buflen = bufmax = 0;
167}
168
169static void copy_to_buffer(const char *str, int len)
170{
171 char *newbuf;
172
173 if (buflen + len >= bufmax) {
174 bufmax += 256 + len;
175 if ((newbuf = realloc(buffer, bufmax)) == NULL) {
176 my_flush();
177 return;
178 }
179 buffer = newbuf;
180 }
181 memcpy(buffer + buflen, str, len);
182 buflen += len;
183
184 if (buflen > 40 && buffer[buflen-1] == '\n')
185 my_flush();
186}
187
188static int my_putchar(int ch)
189{
190 char str[1] = { (char)ch };
191 copy_to_buffer(str, 1);
192 return ch;
193}
194
195static int my_printf(const char *format, ...)
196{
197 va_list list;
198 char *str;
199 int len;
200
201 va_start(list, format);
202 len = vasprintf(&str, format, list);
203 va_end(list);
204
205 if (len >= 0) {
206 copy_to_buffer(str, len);
207 free(str);
208 }
209 return len;
210}
211
212#undef bb_putchar
213#undef putchar
214#undef printf
215#define bb_putchar(c) my_putchar(c)
216#define putchar(c) my_putchar(c)
217#define printf(...) my_printf(__VA_ARGS__)
218#endif
219
155/* Handles %b; return 1 if output is to be short-circuited by \c */ 220/* Handles %b; return 1 if output is to be short-circuited by \c */
156static int print_esc_string(const char *str) 221static int print_esc_string(const char *str)
157{ 222{
@@ -444,6 +509,9 @@ int printf_main(int argc UNUSED_PARAM, char **argv)
444 do { 509 do {
445 argv = argv2; 510 argv = argv2;
446 argv2 = print_formatted(format, argv, &conv_err); 511 argv2 = print_formatted(format, argv, &conv_err);
512#if ENABLE_PLATFORM_MINGW32
513 my_flush();
514#endif
447 } while (argv2 > argv && *argv2); 515 } while (argv2 > argv && *argv2);
448 516
449 /* coreutils compat (bash doesn't do this): 517 /* coreutils compat (bash doesn't do this):
diff --git a/coreutils/shred.c b/coreutils/shred.c
index 04bf87229..794d7b815 100644
--- a/coreutils/shred.c
+++ b/coreutils/shred.c
@@ -60,9 +60,9 @@ int shred_main(int argc UNUSED_PARAM, char **argv)
60 opt = getopt32(argv, "^" "fuzn:+vxs:" "\0" "-1"/*min 1 arg*/, &num_iter, &opt_s); 60 opt = getopt32(argv, "^" "fuzn:+vxs:" "\0" "-1"/*min 1 arg*/, &num_iter, &opt_s);
61 argv += optind; 61 argv += optind;
62 62
63 zero_fd = xopen("/dev/zero", O_RDONLY); 63 zero_fd = MINGW_SPECIAL(xopen)("/dev/zero", O_RDONLY);
64 if (num_iter != 0) 64 if (num_iter != 0)
65 rand_fd = xopen("/dev/urandom", O_RDONLY); 65 rand_fd = MINGW_SPECIAL(xopen)("/dev/urandom", O_RDONLY);
66 66
67 for (;;) { 67 for (;;) {
68 struct stat sb; 68 struct stat sb;
@@ -102,8 +102,14 @@ int shred_main(int argc UNUSED_PARAM, char **argv)
102 } 102 }
103 if (opt & OPT_u) { 103 if (opt & OPT_u) {
104 ftruncate(fd, 0); 104 ftruncate(fd, 0);
105#if ENABLE_PLATFORM_MINGW32
106 xclose(fd);
107#endif
105 xunlink(fname); 108 xunlink(fname);
106 } 109 }
110#if ENABLE_PLATFORM_MINGW32
111 else
112#endif
107 xclose(fd); 113 xclose(fd);
108 } 114 }
109 115
diff --git a/coreutils/shuf.c b/coreutils/shuf.c
index 3def3d80f..81b0df453 100644
--- a/coreutils/shuf.c
+++ b/coreutils/shuf.c
@@ -166,7 +166,7 @@ int shuf_main(int argc, char **argv)
166 166
167 for (i = numlines - outlines; i < numlines; i++) { 167 for (i = numlines - outlines; i < numlines; i++) {
168 if (opts & OPT_i) 168 if (opts & OPT_i)
169 printf("%llu%c", lo + (uintptr_t)lines[i], eol); 169 printf("%"LL_FMT"u%c", lo + (uintptr_t)lines[i], eol);
170 else 170 else
171 printf("%s%c", lines[i], eol); 171 printf("%s%c", lines[i], eol);
172 } 172 }
diff --git a/coreutils/split.c b/coreutils/split.c
index 3fcfd95f2..9393423a1 100644
--- a/coreutils/split.c
+++ b/coreutils/split.c
@@ -78,8 +78,10 @@ static char *next_file(char *old, unsigned suffix_len)
78 return old; 78 return old;
79} 79}
80 80
81#if !ENABLE_PLATFORM_MINGW32
81#define read_buffer bb_common_bufsiz1 82#define read_buffer bb_common_bufsiz1
82enum { READ_BUFFER_SIZE = COMMON_BUFSIZE - 1 }; 83enum { READ_BUFFER_SIZE = COMMON_BUFSIZE - 1 };
84#endif
83 85
84#define SPLIT_OPT_l (1<<0) 86#define SPLIT_OPT_l (1<<0)
85#define SPLIT_OPT_b (1<<1) 87#define SPLIT_OPT_b (1<<1)
@@ -97,8 +99,12 @@ int split_main(int argc UNUSED_PARAM, char **argv)
97 unsigned opt; 99 unsigned opt;
98 ssize_t bytes_read, to_write; 100 ssize_t bytes_read, to_write;
99 char *src; 101 char *src;
100 102#if ENABLE_PLATFORM_MINGW32
103 size_t READ_BUFFER_SIZE = 16*1024;
104 char *read_buffer = xmalloc(16*1024);
105#else
101 setup_common_bufsiz(); 106 setup_common_bufsiz();
107#endif
102 108
103 opt = getopt32(argv, "^" 109 opt = getopt32(argv, "^"
104 "l:b:a:+" /* -a N */ 110 "l:b:a:+" /* -a N */
diff --git a/coreutils/stat.c b/coreutils/stat.c
index 2c2909e7e..940ade89a 100644
--- a/coreutils/stat.c
+++ b/coreutils/stat.c
@@ -190,6 +190,7 @@ FS_TYPE(0x012FF7B4, "xenix") \
190FS_TYPE(0x012FF7B5, "sysv4") \ 190FS_TYPE(0x012FF7B5, "sysv4") \
191FS_TYPE(0x012FF7B6, "sysv2") \ 191FS_TYPE(0x012FF7B6, "sysv2") \
192FS_TYPE(0x012FF7B7, "coh") \ 192FS_TYPE(0x012FF7B7, "coh") \
193IF_PLATFORM_MINGW32(FS_TYPE(0x15013346, "udf")) \
193FS_TYPE(0x00011954, "ufs") \ 194FS_TYPE(0x00011954, "ufs") \
194FS_TYPE(0x012FD16D, "xia") \ 195FS_TYPE(0x012FD16D, "xia") \
195FS_TYPE(0x5346544e, "ntfs") \ 196FS_TYPE(0x5346544e, "ntfs") \
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 06108f315..74df31d10 100644
--- a/coreutils/timeout.c
+++ b/coreutils/timeout.c
@@ -42,11 +42,38 @@
42//usage: "[-s SIG] [-k KILL_SECS] SECS PROG ARGS" 42//usage: "[-s SIG] [-k KILL_SECS] SECS PROG ARGS"
43//usage:#define timeout_full_usage "\n\n" 43//usage:#define timeout_full_usage "\n\n"
44//usage: "Run PROG. Send SIG to it if it is not gone in SECS seconds.\n" 44//usage: "Run PROG. Send SIG to it if it is not gone in SECS seconds.\n"
45//usage: "Default SIG: TERM." 45//usage: "Default SIG: TERM.\n"
46//usage: "If it still exists in KILL_SECS seconds, send KILL.\n" 46//usage: "If it still exists in KILL_SECS seconds, send KILL.\n"
47 47
48#include "libbb.h" 48#include "libbb.h"
49 49
50#if ENABLE_PLATFORM_MINGW32
51static HANDLE child = INVALID_HANDLE_VALUE;
52
53static void kill_child(void)
54{
55 if (child != INVALID_HANDLE_VALUE) {
56 kill_SIGTERM_by_handle(child);
57 }
58}
59
60/* Return TRUE if child exits before timeout expires */
61static NOINLINE int timeout_wait(int timeout, HANDLE proc, DWORD *status)
62{
63 /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */
64 while (1) {
65 sleep1();
66 if (--timeout <= 0)
67 break;
68 if (WaitForSingleObject(proc, 0) == WAIT_OBJECT_0) {
69 /* process is gone */
70 GetExitCodeProcess(proc, status);
71 return TRUE;
72 }
73 }
74 return FALSE;
75}
76#else
50static NOINLINE int timeout_wait(int timeout, pid_t pid) 77static NOINLINE int timeout_wait(int timeout, pid_t pid)
51{ 78{
52 /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */ 79 /* Just sleep(HUGE_NUM); kill(parent) may kill wrong process! */
@@ -61,12 +88,18 @@ static NOINLINE int timeout_wait(int timeout, pid_t pid)
61 } 88 }
62 return EXIT_FAILURE; 89 return EXIT_FAILURE;
63} 90}
91#endif
64 92
65int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 93int timeout_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
66int timeout_main(int argc UNUSED_PARAM, char **argv) 94int timeout_main(int argc UNUSED_PARAM, char **argv)
67{ 95{
68 int signo; 96 int signo;
97#if !ENABLE_PLATFORM_MINGW32
69 int status; 98 int status;
99#else
100 intptr_t ret;
101 DWORD status = EXIT_SUCCESS;
102#endif
70 int parent = 0; 103 int parent = 0;
71 int timeout; 104 int timeout;
72 int kill_timeout; 105 int kill_timeout;
@@ -77,6 +110,10 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
77 const char *opt_s = "TERM"; 110 const char *opt_s = "TERM";
78 char *opt_k = NULL; 111 char *opt_k = NULL;
79 112
113#if ENABLE_PLATFORM_MINGW32
114 xfunc_error_retval = 125;
115#endif
116
80 /* -p option is not documented, it is needed to support NOMMU. */ 117 /* -p option is not documented, it is needed to support NOMMU. */
81 118
82 /* -t SECONDS; -p PARENT_PID */ 119 /* -t SECONDS; -p PARENT_PID */
@@ -85,7 +122,11 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
85 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */ 122 /*argv += optind; - no, wait for bb_daemonize_or_rexec! */
86 123
87 signo = get_signum(opt_s); 124 signo = get_signum(opt_s);
125#if !ENABLE_PLATFORM_MINGW32
88 if (signo < 0) 126 if (signo < 0)
127#else
128 if (signo != SIGTERM && signo != SIGKILL && signo != 0)
129#endif
89 bb_error_msg_and_die("unknown signal '%s'", opt_s); 130 bb_error_msg_and_die("unknown signal '%s'", opt_s);
90 131
91 kill_timeout = 0; 132 kill_timeout = 0;
@@ -98,6 +139,7 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
98 if (!argv[optind]) /* no PROG? */ 139 if (!argv[optind]) /* no PROG? */
99 bb_show_usage(); 140 bb_show_usage();
100 141
142#if !ENABLE_PLATFORM_MINGW32
101 /* We want to create a grandchild which will watch 143 /* We want to create a grandchild which will watch
102 * and kill the grandparent. Other methods: 144 * and kill the grandparent. Other methods:
103 * making parent watch child disrupts parent<->child link 145 * making parent watch child disrupts parent<->child link
@@ -150,4 +192,31 @@ int timeout_main(int argc UNUSED_PARAM, char **argv)
150 argv[1] = sv2; 192 argv[1] = sv2;
151#endif 193#endif
152 BB_EXECVP_or_die(argv); 194 BB_EXECVP_or_die(argv);
195#else /* ENABLE_PLATFORM_MINGW32 */
196 argv += optind;
197 if ((ret=mingw_spawn_proc((const char **)argv)) == -1) {
198 xfunc_error_retval = errno == EACCES ? 126 : 127;
199 bb_perror_msg_and_die("can't execute '%s'", argv[0]);
200 }
201
202 child = (HANDLE)ret;
203 atexit(kill_child);
204 if (timeout_wait(timeout, child, &status))
205 goto finish;
206 status = signo == SIGKILL ? 137 : 124;
207
208 pid = (pid_t)GetProcessId(child);
209 kill(pid, signo);
210
211 if (kill_timeout > 0) {
212 if (timeout_wait(kill_timeout, child, &status))
213 goto finish;
214 kill(parent, SIGKILL);
215 status = 137;
216 }
217 finish:
218 CloseHandle(child);
219 child = INVALID_HANDLE_VALUE;
220 return status;
221#endif
153} 222}
diff --git a/coreutils/yes.c b/coreutils/yes.c
index 161db82c0..64dfa500c 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_stdout(*pp); 50 fputs_stdout(*pp);
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 23692dc6f..815ac71da 100644
--- a/debianutils/which.c
+++ b/debianutils/which.c
@@ -35,6 +35,11 @@ int which_main(int argc UNUSED_PARAM, char **argv)
35 int status = 0; 35 int status = 0;
36 /* This sizeof(): bb_default_root_path is shorter than BB_PATH_ROOT_PATH */ 36 /* This sizeof(): bb_default_root_path is shorter than BB_PATH_ROOT_PATH */
37 char buf[sizeof(BB_PATH_ROOT_PATH)]; 37 char buf[sizeof(BB_PATH_ROOT_PATH)];
38#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
39 /* If we were run as 'which.exe' skip standalone shell behaviour */
40 int sh_standalone =
41 is_suffixed_with_case(bb_busybox_exec_path, "which.exe") == NULL;
42#endif
38 43
39 env_path = getenv("PATH"); 44 env_path = getenv("PATH");
40 if (!env_path) 45 if (!env_path)
@@ -47,12 +52,51 @@ int which_main(int argc UNUSED_PARAM, char **argv)
47 do { 52 do {
48 int missing = 1; 53 int missing = 1;
49 54
55#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
56 if (sh_standalone) {
57 if (strcmp(*argv, "busybox") == 0 &&
58 is_prefixed_with_case(bb_basename(bb_busybox_exec_path),
59 "busybox")) {
60 missing = 0;
61 puts(bb_busybox_exec_path);
62 if (!option_mask32) /* -a not set */
63 break;
64 }
65 else if (find_applet_by_name(*argv) >= 0) {
66 missing = 0;
67 puts(*argv);
68 if (!option_mask32) /* -a not set */
69 break;
70 }
71 }
72#endif
73
74#if !ENABLE_PLATFORM_MINGW32
50 /* If file contains a slash don't use PATH */ 75 /* If file contains a slash don't use PATH */
51 if (strchr(*argv, '/')) { 76 if (strchr(*argv, '/')) {
52 if (file_is_executable(*argv)) { 77 if (file_is_executable(*argv)) {
53 missing = 0; 78 missing = 0;
54 puts(*argv); 79 puts(*argv);
55 } 80 }
81#else
82 if (has_path(*argv)) {
83 char *path = alloc_system_drive(*argv);
84
85 if (add_win32_extension(path) || file_is_executable(path)) {
86 missing = 0;
87 puts(bs_to_slash(path));
88 }
89# if ENABLE_FEATURE_SH_STANDALONE
90 else if (sh_standalone) {
91 const char *name = bb_basename(*argv);
92
93 if (unix_path(*argv) && find_applet_by_name(name) >= 0) {
94 missing = 0;
95 puts(name);
96 }
97 }
98# endif
99#endif
56 } else { 100 } else {
57 char *path; 101 char *path;
58 char *p; 102 char *p;
@@ -61,7 +105,11 @@ int which_main(int argc UNUSED_PARAM, char **argv)
61 /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */ 105 /* NOFORK NB: xmalloc inside find_executable(), must have no allocs above! */
62 while ((p = find_executable(*argv, &path)) != NULL) { 106 while ((p = find_executable(*argv, &path)) != NULL) {
63 missing = 0; 107 missing = 0;
108#if ENABLE_PLATFORM_MINGW32
109 puts(bs_to_slash(p));
110#else
64 puts(p); 111 puts(p);
112#endif
65 free(p); 113 free(p);
66 if (!option_mask32) /* -a not set */ 114 if (!option_mask32) /* -a not set */
67 break; 115 break;
diff --git a/e2fsprogs/chattr.c b/e2fsprogs/chattr.c
index f436cd91e..c1e90d13f 100644
--- a/e2fsprogs/chattr.c
+++ b/e2fsprogs/chattr.c
@@ -20,14 +20,27 @@
20//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o 20//kbuild:lib-$(CONFIG_CHATTR) += chattr.o e2fs_lib.o
21 21
22//usage:#define chattr_trivial_usage 22//usage:#define chattr_trivial_usage
23//usage: IF_NOT_PLATFORM_MINGW32(
23//usage: "[-R] [-v VERSION] [-p PROJID] [-+=AacDdijsStTu] FILE..." 24//usage: "[-R] [-v VERSION] [-p PROJID] [-+=AacDdijsStTu] FILE..."
25//usage: )
26//usage: IF_PLATFORM_MINGW32(
27//usage: "[-R] [-+rhsatn] FILE..."
28//usage: )
24//usage:#define chattr_full_usage "\n\n" 29//usage:#define chattr_full_usage "\n\n"
30//usage: IF_NOT_PLATFORM_MINGW32(
25//usage: "Change ext2 file attributes\n" 31//usage: "Change ext2 file attributes\n"
32//usage: )
33//usage: IF_PLATFORM_MINGW32(
34//usage: "Change file attributes\n"
35//usage: )
26//usage: "\n -R Recurse" 36//usage: "\n -R Recurse"
37//usage: IF_NOT_PLATFORM_MINGW32(
27//usage: "\n -v NUM Set version/generation number" 38//usage: "\n -v NUM Set version/generation number"
28//usage: "\n -p NUM Set project number" 39//usage: "\n -p NUM Set project number"
40//usage: )
29//-V, -f accepted but ignored 41//-V, -f accepted but ignored
30//usage: "\nModifiers:" 42//usage: "\nModifiers:"
43//usage: IF_NOT_PLATFORM_MINGW32(
31//usage: "\n -,+,= Remove/add/set attributes" 44//usage: "\n -,+,= Remove/add/set attributes"
32//usage: "\nAttributes:" 45//usage: "\nAttributes:"
33//usage: "\n A No atime" 46//usage: "\n A No atime"
@@ -50,6 +63,17 @@
50//usage: "\n t Don't tail-merge with other files" 63//usage: "\n t Don't tail-merge with other files"
51//usage: "\n u Allow undelete" 64//usage: "\n u Allow undelete"
52//usage: "\n V Verity" 65//usage: "\n V Verity"
66//usage: )
67//usage: IF_PLATFORM_MINGW32(
68//usage: "\n -,+ Remove/add attributes"
69//usage: "\nAttributes:"
70//usage: "\n r Read only"
71//usage: "\n h Hidden"
72//usage: "\n s System"
73//usage: "\n a Archive"
74//usage: "\n t Temporary"
75//usage: "\n n Not indexed"
76//usage: )
53 77
54#include "libbb.h" 78#include "libbb.h"
55#include "e2fs_lib.h" 79#include "e2fs_lib.h"
@@ -61,11 +85,15 @@
61#define OPT_SET_PROJ (1 << 4) 85#define OPT_SET_PROJ (1 << 4)
62 86
63struct globals { 87struct globals {
88#if !ENABLE_PLATFORM_MINGW32
64 unsigned version; 89 unsigned version;
90#endif
65 unsigned af; 91 unsigned af;
66 unsigned rf; 92 unsigned rf;
67 int flags; 93 int flags;
94#if !ENABLE_PLATFORM_MINGW32
68 uint32_t projid; 95 uint32_t projid;
96#endif
69 smallint recursive; 97 smallint recursive;
70}; 98};
71 99
@@ -89,11 +117,17 @@ static char** decode_arg(char **argv, struct globals *gp)
89 /* testcase: chattr =ae -R FILE should not complain "= is incompatible with - and +" */ 117 /* testcase: chattr =ae -R FILE should not complain "= is incompatible with - and +" */
90 /* (and should not read flags, with =FLAGS they can be just set directly) */ 118 /* (and should not read flags, with =FLAGS they can be just set directly) */
91 fl = &gp->rf; 119 fl = &gp->rf;
120#if ENABLE_PLATFORM_MINGW32
121 } else { /* if (opt == '+') */
122 gp->flags |= OPT_ADD;
123 }
124#else
92 } else if (opt == '+') { 125 } else if (opt == '+') {
93 gp->flags |= OPT_ADD; 126 gp->flags |= OPT_ADD;
94 } else { /* if (opt == '=') */ 127 } else { /* if (opt == '=') */
95 gp->flags |= OPT_SET; 128 gp->flags |= OPT_SET;
96 } 129 }
130#endif
97 131
98 while (*++arg) { 132 while (*++arg) {
99 if (opt == '-') { 133 if (opt == '-') {
@@ -106,6 +140,7 @@ static char** decode_arg(char **argv, struct globals *gp)
106 gp->recursive = 1; 140 gp->recursive = 1;
107 continue; 141 continue;
108 } 142 }
143#if !ENABLE_PLATFORM_MINGW32
109 if (*arg == 'V') { 144 if (*arg == 'V') {
110 /*"verbose and print program version" (nop for now) */; 145 /*"verbose and print program version" (nop for now) */;
111 continue; 146 continue;
@@ -128,6 +163,7 @@ static char** decode_arg(char **argv, struct globals *gp)
128 gp->flags |= OPT_SET_PROJ; 163 gp->flags |= OPT_SET_PROJ;
129 continue; 164 continue;
130 } 165 }
166#endif
131 /* not a known option, try as an attribute */ 167 /* not a known option, try as an attribute */
132 gp->flags |= OPT_REM; 168 gp->flags |= OPT_REM;
133 } 169 }
@@ -155,7 +191,9 @@ static int FAST_FUNC chattr_dir_proc(const char *dir_name, struct dirent *de, vo
155static void change_attributes(const char *name, struct globals *gp) 191static void change_attributes(const char *name, struct globals *gp)
156{ 192{
157 unsigned fsflags; 193 unsigned fsflags;
194#if !ENABLE_PLATFORM_MINGW32
158 int fd; 195 int fd;
196#endif
159 struct stat st; 197 struct stat st;
160 198
161 if (lstat(name, &st) != 0) { 199 if (lstat(name, &st) != 0) {
@@ -170,6 +208,7 @@ static void change_attributes(const char *name, struct globals *gp)
170 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode)) 208 if (!S_ISREG(st.st_mode) && !S_ISLNK(st.st_mode) && !S_ISDIR(st.st_mode))
171 return; 209 return;
172 210
211#if !ENABLE_PLATFORM_MINGW32
173 /* There is no way to run needed ioctls on a symlink. 212 /* There is no way to run needed ioctls on a symlink.
174 * open(O_PATH | O_NOFOLLOW) _can_ be used to get a fd referring to the symlink, 213 * open(O_PATH | O_NOFOLLOW) _can_ be used to get a fd referring to the symlink,
175 * but ioctls fail on such a fd (tried on 4.12.0 kernel). 214 * but ioctls fail on such a fd (tried on 4.12.0 kernel).
@@ -221,6 +260,24 @@ static void change_attributes(const char *name, struct globals *gp)
221 skip_setflags: 260 skip_setflags:
222 close(fd); 261 close(fd);
223 } 262 }
263#else /* ENABLE_PLATFORM_MINGW32 */
264 if (gp->flags & OPT_SET) {
265 fsflags = gp->af;
266 } else {
267 if (fgetflags(name, &fsflags) != 0) {
268 bb_perror_msg("reading flags on %s", name);
269 goto skip_setflags;
270 }
271 /*if (gp->flags & OPT_REM) - not needed, rf is zero otherwise */
272 fsflags &= ~gp->rf;
273 /*if (gp->flags & OPT_ADD) - not needed, af is zero otherwise */
274 fsflags |= gp->af;
275 }
276 if (fsetflags(name, fsflags) != 0)
277 bb_perror_msg("setting flags on %s", name);
278
279 skip_setflags:
280#endif
224 281
225 if (gp->recursive && S_ISDIR(st.st_mode)) 282 if (gp->recursive && S_ISDIR(st.st_mode))
226 iterate_on_dir(name, chattr_dir_proc, gp); 283 iterate_on_dir(name, chattr_dir_proc, gp);
@@ -238,7 +295,11 @@ int chattr_main(int argc UNUSED_PARAM, char **argv)
238 char *arg = *++argv; 295 char *arg = *++argv;
239 if (!arg) 296 if (!arg)
240 bb_show_usage(); 297 bb_show_usage();
298#if ENABLE_PLATFORM_MINGW32
299 if (arg[0] != '-' && arg[0] != '+')
300#else
241 if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=') 301 if (arg[0] != '-' && arg[0] != '+' && arg[0] != '=')
302#endif
242 break; 303 break;
243 304
244 argv = decode_arg(argv, &g); 305 argv = decode_arg(argv, &g);
@@ -251,7 +312,11 @@ int chattr_main(int argc UNUSED_PARAM, char **argv)
251 if (g.rf & g.af) 312 if (g.rf & g.af)
252 bb_simple_error_msg_and_die("can't set and unset a flag"); 313 bb_simple_error_msg_and_die("can't set and unset a flag");
253 if (!g.flags) 314 if (!g.flags)
315#if ENABLE_PLATFORM_MINGW32
316 bb_simple_error_msg_and_die("must use - or +");
317#else
254 bb_simple_error_msg_and_die("must use -v, -p, =, - or +"); 318 bb_simple_error_msg_and_die("must use -v, -p, =, - or +");
319#endif
255 320
256 /* now run chattr on all the files passed to us */ 321 /* now run chattr on all the files passed to us */
257 do change_attributes(*argv, &g); while (*++argv); 322 do change_attributes(*argv, &g); while (*++argv);
diff --git a/e2fsprogs/e2fs_lib.c b/e2fsprogs/e2fs_lib.c
index 9b68d8901..48c3b68b5 100644
--- a/e2fsprogs/e2fs_lib.c
+++ b/e2fsprogs/e2fs_lib.c
@@ -8,6 +8,37 @@
8#include "libbb.h" 8#include "libbb.h"
9#include "e2fs_lib.h" 9#include "e2fs_lib.h"
10 10
11#if ENABLE_PLATFORM_MINGW32
12/* Only certain attributes can be set using SetFileAttributes() */
13#define CHATTR_MASK (FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | \
14 FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_ARCHIVE | \
15 FILE_ATTRIBUTE_TEMPORARY | FILE_ATTRIBUTE_NOT_CONTENT_INDEXED | \
16 FILE_ATTRIBUTE_OFFLINE)
17
18/* Get/set file attributes on a Windows file system */
19int fgetsetflags(const char *name, unsigned *get_flags, unsigned set_flags)
20{
21 struct stat buf;
22
23 if (stat(name, &buf) == 0 /* stat is ok */
24 && !S_ISREG(buf.st_mode) && !S_ISDIR(buf.st_mode)
25 ) {
26 errno = EOPNOTSUPP;
27 return -1;
28 }
29
30 if (get_flags) {
31 *get_flags = (unsigned long)buf.st_attr;
32 }
33 else if (!SetFileAttributes(name, set_flags & CHATTR_MASK)) {
34 errno = err_win_to_posix();
35 return -1;
36 }
37 return 0;
38}
39#endif
40
41#if !ENABLE_PLATFORM_MINGW32
11/* Print file attributes on an ext2 file system */ 42/* Print file attributes on an ext2 file system */
12const uint32_t e2attr_flags_value[] ALIGN4 = { 43const uint32_t e2attr_flags_value[] ALIGN4 = {
13#ifdef ENABLE_COMPRESSION 44#ifdef ENABLE_COMPRESSION
@@ -70,6 +101,39 @@ static const char e2attr_flags_lname[] ALIGN1 =
70 "Project_Hierarchy" "\0" 101 "Project_Hierarchy" "\0"
71 "Verity" "\0" 102 "Verity" "\0"
72 /* Another trailing NUL is added by compiler */; 103 /* Another trailing NUL is added by compiler */;
104#else /* ENABLE_PLATFORM_MINGW32 */
105/* Print file attributes on a Windows file system */
106const uint32_t e2attr_flags_value[] = {
107 FILE_ATTRIBUTE_REPARSE_POINT,
108 FILE_ATTRIBUTE_OFFLINE,
109 FILE_ATTRIBUTE_ENCRYPTED,
110 FILE_ATTRIBUTE_COMPRESSED,
111 FILE_ATTRIBUTE_SPARSE_FILE,
112 FILE_ATTRIBUTE_READONLY,
113 FILE_ATTRIBUTE_HIDDEN,
114 FILE_ATTRIBUTE_SYSTEM,
115 FILE_ATTRIBUTE_ARCHIVE,
116 FILE_ATTRIBUTE_TEMPORARY,
117 FILE_ATTRIBUTE_NOT_CONTENT_INDEXED,
118};
119
120const char e2attr_flags_sname[] ALIGN1 =
121 "roecSrhsatn";
122
123static const char e2attr_flags_lname[] ALIGN1 =
124 "Reparse" "\0"
125 "Offline" "\0"
126 "Encrypted" "\0"
127 "Compressed" "\0"
128 "Sparse" "\0"
129 "Readonly" "\0"
130 "Hidden" "\0"
131 "System" "\0"
132 "Archive" "\0"
133 "Temporary" "\0"
134 "Notindexed" "\0"
135 /* Another trailing NUL is added by compiler */;
136#endif
73 137
74void print_e2flags_long(unsigned flags) 138void print_e2flags_long(unsigned flags)
75{ 139{
diff --git a/e2fsprogs/e2fs_lib.h b/e2fsprogs/e2fs_lib.h
index bab447a94..aa92e63af 100644
--- a/e2fsprogs/e2fs_lib.h
+++ b/e2fsprogs/e2fs_lib.h
@@ -11,6 +11,13 @@
11 11
12PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN 12PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
13 13
14#if ENABLE_PLATFORM_MINGW32
15/* Get/set a file flags */
16int fgetsetflags(const char *name, unsigned *get_flags, unsigned set_flags);
17#define fgetflags(name, flags) fgetsetflags(name, flags, 0)
18#define fsetflags(name, flags) fgetsetflags(name, NULL, flags)
19#endif
20
14/* Print file attributes on an ext2 file system */ 21/* Print file attributes on an ext2 file system */
15void print_e2flags_long(unsigned flags); 22void print_e2flags_long(unsigned flags);
16void print_e2flags(unsigned flags); 23void print_e2flags(unsigned flags);
@@ -20,6 +27,7 @@ extern const char e2attr_flags_sname[];
20 27
21/* If you plan to ENABLE_COMPRESSION, see e2fs_lib.c and chattr.c - */ 28/* If you plan to ENABLE_COMPRESSION, see e2fs_lib.c and chattr.c - */
22/* make sure that chattr doesn't accept bad options! */ 29/* make sure that chattr doesn't accept bad options! */
30#if !ENABLE_PLATFORM_MINGW32
23#ifdef ENABLE_COMPRESSION 31#ifdef ENABLE_COMPRESSION
24#define e2attr_flags_value_chattr (&e2attr_flags_value[5]) 32#define e2attr_flags_value_chattr (&e2attr_flags_value[5])
25#define e2attr_flags_sname_chattr (&e2attr_flags_sname[5]) 33#define e2attr_flags_sname_chattr (&e2attr_flags_sname[5])
@@ -27,5 +35,9 @@ extern const char e2attr_flags_sname[];
27#define e2attr_flags_value_chattr (&e2attr_flags_value[1]) 35#define e2attr_flags_value_chattr (&e2attr_flags_value[1])
28#define e2attr_flags_sname_chattr (&e2attr_flags_sname[1]) 36#define e2attr_flags_sname_chattr (&e2attr_flags_sname[1])
29#endif 37#endif
38#else
39#define e2attr_flags_value_chattr (&e2attr_flags_value[5])
40#define e2attr_flags_sname_chattr (&e2attr_flags_sname[5])
41#endif
30 42
31POP_SAVED_FUNCTION_VISIBILITY 43POP_SAVED_FUNCTION_VISIBILITY
diff --git a/e2fsprogs/lsattr.c b/e2fsprogs/lsattr.c
index c9f353ce5..d82861e14 100644
--- a/e2fsprogs/lsattr.c
+++ b/e2fsprogs/lsattr.c
@@ -21,7 +21,12 @@
21//kbuild:lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o 21//kbuild:lib-$(CONFIG_LSATTR) += lsattr.o e2fs_lib.o
22 22
23//usage:#define lsattr_trivial_usage 23//usage:#define lsattr_trivial_usage
24//usage: IF_NOT_PLATFORM_MINGW32(
24//usage: "[-Radlpv] [FILE]..." 25//usage: "[-Radlpv] [FILE]..."
26//usage: )
27//usage: IF_PLATFORM_MINGW32(
28//usage: "[-Radl] [FILE]..."
29//usage: )
25//usage:#define lsattr_full_usage "\n\n" 30//usage:#define lsattr_full_usage "\n\n"
26//usage: "List ext2 file attributes\n" 31//usage: "List ext2 file attributes\n"
27//usage: "\n -R Recurse" 32//usage: "\n -R Recurse"
@@ -29,8 +34,10 @@
29//usage: "\n -d List directory names, not contents" 34//usage: "\n -d List directory names, not contents"
30// -a,-d text should match ls --help 35// -a,-d text should match ls --help
31//usage: "\n -l List long flag names" 36//usage: "\n -l List long flag names"
37//usage: IF_NOT_PLATFORM_MINGW32(
32//usage: "\n -p List project ID" 38//usage: "\n -p List project ID"
33//usage: "\n -v List version/generation number" 39//usage: "\n -v List version/generation number"
40//usage: )
34 41
35#include "libbb.h" 42#include "libbb.h"
36#include "e2fs_lib.h" 43#include "e2fs_lib.h"
@@ -47,6 +54,7 @@ enum {
47static void list_attributes(const char *name) 54static void list_attributes(const char *name)
48{ 55{
49 unsigned fsflags; 56 unsigned fsflags;
57#if !ENABLE_PLATFORM_MINGW32
50 int fd, r; 58 int fd, r;
51 59
52 /* There is no way to run needed ioctls on a symlink. 60 /* There is no way to run needed ioctls on a symlink.
@@ -80,6 +88,10 @@ static void list_attributes(const char *name)
80 goto read_err; 88 goto read_err;
81 89
82 close(fd); 90 close(fd);
91#else /* ENABLE_PLATFORM_MINGW32 */
92 if (fgetflags(name, &fsflags) != 0)
93 goto read_err;
94#endif
83 95
84 if (option_mask32 & OPT_PF_LONG) { 96 if (option_mask32 & OPT_PF_LONG) {
85 printf("%-28s ", name); 97 printf("%-28s ", name);
@@ -93,7 +105,9 @@ static void list_attributes(const char *name)
93 return; 105 return;
94 read_err: 106 read_err:
95 bb_perror_msg("reading %s", name); 107 bb_perror_msg("reading %s", name);
108#if !ENABLE_PLATFORM_MINGW32
96 close(fd); 109 close(fd);
110#endif
97} 111}
98 112
99static int FAST_FUNC lsattr_dir_proc(const char *dir_name, 113static int FAST_FUNC lsattr_dir_proc(const char *dir_name,
@@ -142,7 +156,11 @@ static void lsattr_args(const char *name)
142int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 156int lsattr_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
143int lsattr_main(int argc UNUSED_PARAM, char **argv) 157int lsattr_main(int argc UNUSED_PARAM, char **argv)
144{ 158{
159#if ENABLE_PLATFORM_MINGW32
160 getopt32(argv, "Radl");
161#else
145 getopt32(argv, "Radlvp"); 162 getopt32(argv, "Radlvp");
163#endif
146 argv += optind; 164 argv += optind;
147 165
148 if (!*argv) 166 if (!*argv)
diff --git a/editors/awk.c b/editors/awk.c
index f6314ac72..17710e57e 100644
--- a/editors/awk.c
+++ b/editors/awk.c
@@ -826,7 +826,11 @@ static char *skip_spaces(char *p)
826 if (*p == '\\' && p[1] == '\n') { 826 if (*p == '\\' && p[1] == '\n') {
827 p++; 827 p++;
828 t_lineno++; 828 t_lineno++;
829#if !ENABLE_PLATFORM_MINGW32
829 } else if (*p != ' ' && *p != '\t') { 830 } else if (*p != ' ' && *p != '\t') {
831#else
832 } else if (*p != ' ' && *p != '\t' && *p != '\r') {
833#endif
830 break; 834 break;
831 } 835 }
832 p++; 836 p++;
@@ -907,7 +911,7 @@ static double my_strtod(char **pp)
907static void fmt_num(const char *format, double n) 911static void fmt_num(const char *format, double n)
908{ 912{
909 if (n == (long long)n) { 913 if (n == (long long)n) {
910 snprintf(g_buf, MAXVARFMT, "%lld", (long long)n); 914 snprintf(g_buf, MAXVARFMT, "%"LL_FMT"d", (long long)n);
911 } else { 915 } else {
912 const char *s = format; 916 const char *s = format;
913 char c; 917 char c;
@@ -2213,6 +2217,21 @@ static int ptest(node *pattern)
2213 return istrue(evaluate(pattern, &G.ptest__tmpvar)); 2217 return istrue(evaluate(pattern, &G.ptest__tmpvar));
2214} 2218}
2215 2219
2220#if ENABLE_PLATFORM_MINGW32
2221static ssize_t FAST_FUNC safe_read_strip_cr(int fd, void *buf, size_t count)
2222{
2223 ssize_t n;
2224
2225 do {
2226 n = safe_read(fd, buf, count);
2227 } while (n > 0 && (n=remove_cr((char *)buf, n)) == 0);
2228
2229 return n;
2230}
2231
2232#define safe_read safe_read_strip_cr
2233#endif
2234
2216/* read next record from stream rsm into a variable v */ 2235/* read next record from stream rsm into a variable v */
2217static int awk_getline(rstream *rsm, var *v) 2236static int awk_getline(rstream *rsm, var *v)
2218{ 2237{
@@ -3246,6 +3265,12 @@ static var *evaluate(node *op, var *res)
3246 v &= 0x7fffffffffffffffULL; 3265 v &= 0x7fffffffffffffffULL;
3247# endif 3266# endif
3248 R_d = (double)v / 0x8000000000000000ULL; 3267 R_d = (double)v / 0x8000000000000000ULL;
3268#elif ENABLE_PLATFORM_MINGW32 && RAND_MAX == 0x7fff
3269 /* 45 bits of randomness ought to be enough for anyone */
3270 uint64_t v = ((uint64_t)rand() << 48) |
3271 ((uint64_t)rand() << 33) |
3272 ((uint64_t)rand() << 18);
3273 R_d = (double)v / 0x8000000000000000ULL;
3249#else 3274#else
3250# error Not implemented for this value of RAND_MAX 3275# error Not implemented for this value of RAND_MAX
3251#endif 3276#endif
diff --git a/editors/diff.c b/editors/diff.c
index 1adc4cbc7..17d41af8d 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 e8c82ac63..73034438a 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;
@@ -1023,6 +1029,11 @@ static char *get_next_line(char *gets_char, char *last_puts_char)
1023 char c = temp[len-1]; 1029 char c = temp[len-1];
1024 if (c == '\n' || c == '\0') { 1030 if (c == '\n' || c == '\0') {
1025 temp[len-1] = '\0'; 1031 temp[len-1] = '\0';
1032#if ENABLE_PLATFORM_MINGW32
1033 if (!G.keep_cr && c == '\n' && len > 1 && temp[len-2] == '\r') {
1034 temp[len-2] = '\0';
1035 }
1036#endif
1026 gc = c; 1037 gc = c;
1027 if (c == '\0') { 1038 if (c == '\0') {
1028 int ch = fgetc(fp); 1039 int ch = fgetc(fp);
@@ -1499,7 +1510,12 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1499 "quiet\0" No_argument "n" 1510 "quiet\0" No_argument "n"
1500 "silent\0" No_argument "n" 1511 "silent\0" No_argument "n"
1501 "expression\0" Required_argument "e" 1512 "expression\0" Required_argument "e"
1513# if !ENABLE_PLATFORM_MINGW32
1502 "file\0" Required_argument "f"; 1514 "file\0" Required_argument "f";
1515# else
1516 "file\0" Required_argument "f"
1517 "binary\0" No_argument "b";
1518# endif
1503#endif 1519#endif
1504 1520
1505 INIT_G(); 1521 INIT_G();
@@ -1523,6 +1539,7 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1523 */ 1539 */
1524 opt = getopt32long(argv, "^" 1540 opt = getopt32long(argv, "^"
1525 "i::rEne:*f:*" 1541 "i::rEne:*f:*"
1542 IF_PLATFORM_MINGW32("b")
1526 "\0" "nn"/*count -n*/, 1543 "\0" "nn"/*count -n*/,
1527 sed_longopts, 1544 sed_longopts,
1528 &opt_i, &opt_e, &opt_f, 1545 &opt_i, &opt_e, &opt_f,
@@ -1536,6 +1553,10 @@ int sed_main(int argc UNUSED_PARAM, char **argv)
1536 G.regex_type |= REG_EXTENDED; // -r or -E 1553 G.regex_type |= REG_EXTENDED; // -r or -E
1537 //if (opt & 8) 1554 //if (opt & 8)
1538 // G.be_quiet++; // -n (implemented with a counter instead) 1555 // G.be_quiet++; // -n (implemented with a counter instead)
1556#if ENABLE_PLATFORM_MINGW32
1557 if (opt & 0x40)
1558 G.keep_cr = 1;
1559#endif
1539 while (opt_e) { // -e 1560 while (opt_e) { // -e
1540 add_cmd_block(llist_pop(&opt_e)); 1561 add_cmd_block(llist_pop(&opt_e));
1541 } 1562 }
diff --git a/editors/vi.c b/editors/vi.c
index 3dbe5b471..b973cc056 100644
--- a/editors/vi.c
+++ b/editors/vi.c
@@ -112,6 +112,15 @@
112//config: help 112//config: help
113//config: Enable the editor to set some (ai, ic, showmatch) options. 113//config: Enable the editor to set some (ai, ic, showmatch) options.
114//config: 114//config:
115//config:config FEATURE_VI_FILE_FORMAT
116//config: bool "Enable detection of file format"
117//config: default y
118//config: depends on VI && FEATURE_VI_SETOPTS && PLATFORM_MINGW32
119//config: help
120//config: Enable the editor to detect the format of files it reads
121//config: so they can be written out with the appropriate line
122//config: endings. Enable the 'fileformats' option.
123//config:
115//config:config FEATURE_VI_SET 124//config:config FEATURE_VI_SET
116//config: bool "Support :set" 125//config: bool "Support :set"
117//config: default y 126//config: default y
@@ -297,6 +306,7 @@ struct globals {
297 306
298 // the rest 307 // the rest
299#if ENABLE_FEATURE_VI_SETOPTS 308#if ENABLE_FEATURE_VI_SETOPTS
309#if !ENABLE_FEATURE_VI_FILE_FORMAT
300 smallint vi_setops; // set by setops() 310 smallint vi_setops; // set by setops()
301#define VI_AUTOINDENT (1 << 0) 311#define VI_AUTOINDENT (1 << 0)
302#define VI_EXPANDTAB (1 << 1) 312#define VI_EXPANDTAB (1 << 1)
@@ -304,6 +314,17 @@ struct globals {
304#define VI_IGNORECASE (1 << 3) 314#define VI_IGNORECASE (1 << 3)
305#define VI_SHOWMATCH (1 << 4) 315#define VI_SHOWMATCH (1 << 4)
306#define VI_TABSTOP (1 << 5) 316#define VI_TABSTOP (1 << 5)
317#else
318 smalluint vi_setops; // set by setops()
319#define VI_AUTOINDENT (1 << 0)
320#define VI_EXPANDTAB (1 << 1)
321#define VI_FILEFORMAT (1 << 2)
322#define VI_FILEFORMATS (1 << 3)
323#define VI_ERR_METHOD (1 << 4)
324#define VI_IGNORECASE (1 << 5)
325#define VI_SHOWMATCH (1 << 6)
326#define VI_TABSTOP (1 << 7)
327#endif
307#define autoindent (vi_setops & VI_AUTOINDENT) 328#define autoindent (vi_setops & VI_AUTOINDENT)
308#define expandtab (vi_setops & VI_EXPANDTAB ) 329#define expandtab (vi_setops & VI_EXPANDTAB )
309#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash 330#define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash
@@ -313,10 +334,18 @@ struct globals {
313#define OPTS_STR \ 334#define OPTS_STR \
314 "ai\0""autoindent\0" \ 335 "ai\0""autoindent\0" \
315 "et\0""expandtab\0" \ 336 "et\0""expandtab\0" \
337 IF_FEATURE_VI_FILE_FORMAT("ff\0""fileformat\0") \
338 IF_FEATURE_VI_FILE_FORMAT("ffs\0""fileformats\0") \
316 "fl\0""flash\0" \ 339 "fl\0""flash\0" \
317 "ic\0""ignorecase\0" \ 340 "ic\0""ignorecase\0" \
318 "sm\0""showmatch\0" \ 341 "sm\0""showmatch\0" \
319 "ts\0""tabstop\0" 342 "ts\0""tabstop\0"
343
344#define FF_DOS_UNIX 0
345#define FF_UNIX_DOS 1
346#define FORMATS_STR \
347 "dos,unix\0" \
348 "unix,dos\0"
320#else 349#else
321#define autoindent (0) 350#define autoindent (0)
322#define expandtab (0) 351#define expandtab (0)
@@ -380,6 +409,10 @@ struct globals {
380#if ENABLE_FEATURE_VI_SETOPTS 409#if ENABLE_FEATURE_VI_SETOPTS
381 int indentcol; // column of recently autoindent, 0 or -1 410 int indentcol; // column of recently autoindent, 0 or -1
382#endif 411#endif
412#if ENABLE_FEATURE_VI_FILE_FORMAT
413 smallint fileformat;
414 smallint fileformats;
415#endif
383 smallint cmd_error; 416 smallint cmd_error;
384 417
385 // former statics 418 // former statics
@@ -400,7 +433,9 @@ struct globals {
400#if ENABLE_FEATURE_VI_USE_SIGNALS 433#if ENABLE_FEATURE_VI_USE_SIGNALS
401 sigjmp_buf restart; // int_handler() jumps to location remembered here 434 sigjmp_buf restart; // int_handler() jumps to location remembered here
402#endif 435#endif
436#if !ENABLE_PLATFORM_MINGW32
403 struct termios term_orig; // remember what the cooked mode was 437 struct termios term_orig; // remember what the cooked mode was
438#endif
404 int cindex; // saved character index for up/down motion 439 int cindex; // saved character index for up/down motion
405 smallint keep_index; // retain saved character index 440 smallint keep_index; // retain saved character index
406#if ENABLE_FEATURE_VI_COLON 441#if ENABLE_FEATURE_VI_COLON
@@ -505,6 +540,8 @@ struct globals {
505#define dotcnt (G.dotcnt ) 540#define dotcnt (G.dotcnt )
506#define last_search_pattern (G.last_search_pattern) 541#define last_search_pattern (G.last_search_pattern)
507#define indentcol (G.indentcol ) 542#define indentcol (G.indentcol )
543#define fileformat (G.fileformat )
544#define fileformats (G.fileformats )
508#define cmd_error (G.cmd_error ) 545#define cmd_error (G.cmd_error )
509 546
510#define edit_file__cur_line (G.edit_file__cur_line) 547#define edit_file__cur_line (G.edit_file__cur_line)
@@ -608,6 +645,23 @@ static ALWAYS_INLINE int query_screen_dimensions(void)
608// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready) 645// sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
609static int mysleep(int hund) 646static int mysleep(int hund)
610{ 647{
648#if ENABLE_PLATFORM_MINGW32
649 HANDLE h = GetStdHandle(STD_INPUT_HANDLE);
650 DWORD ret;
651
652 if (hund == 0) {
653 // Allow two events in the queue. Otherwise pasted test isn't
654 // displayed because there's still a key release event waiting
655 // after the last character is processed.
656 DWORD nevent_out;
657
658 ret = GetNumberOfConsoleInputEvents(h, &nevent_out);
659 return ret != 0 ? (nevent_out > 2) : 0;
660 }
661 fflush_all();
662 ret = WaitForSingleObject(h, hund*10);
663 return ret != WAIT_TIMEOUT;
664#else
611 struct pollfd pfd[1]; 665 struct pollfd pfd[1];
612 666
613 if (hund != 0) 667 if (hund != 0)
@@ -616,9 +670,11 @@ static int mysleep(int hund)
616 pfd[0].fd = STDIN_FILENO; 670 pfd[0].fd = STDIN_FILENO;
617 pfd[0].events = POLLIN; 671 pfd[0].events = POLLIN;
618 return safe_poll(pfd, 1, hund*10) > 0; 672 return safe_poll(pfd, 1, hund*10) > 0;
673#endif
619} 674}
620 675
621//----- Set terminal attributes -------------------------------- 676//----- Set terminal attributes --------------------------------
677#if !ENABLE_PLATFORM_MINGW32
622static void rawmode(void) 678static void rawmode(void)
623{ 679{
624 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals 680 // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
@@ -630,6 +686,10 @@ static void cookmode(void)
630 fflush_all(); 686 fflush_all();
631 tcsetattr_stdin_TCSANOW(&term_orig); 687 tcsetattr_stdin_TCSANOW(&term_orig);
632} 688}
689#else
690#define rawmode() ((void)0)
691#define cookmode fflush_all
692#endif
633 693
634//----- Terminal Drawing --------------------------------------- 694//----- Terminal Drawing ---------------------------------------
635// The terminal is made up of 'rows' line of 'columns' columns. 695// The terminal is made up of 'rows' line of 'columns' columns.
@@ -1212,7 +1272,11 @@ static char *get_input_line(const char *prompt)
1212 c = get_one_char(); 1272 c = get_one_char();
1213 if (c == '\n' || c == '\r' || c == 27) 1273 if (c == '\n' || c == '\r' || c == 27)
1214 break; // this is end of input 1274 break; // this is end of input
1275#if !ENABLE_PLATFORM_MINGW32
1215 if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { 1276 if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) {
1277#else
1278 if (c == 8 || c == 127) {
1279#endif
1216 // user wants to erase prev char 1280 // user wants to erase prev char
1217 write1("\b \b"); // erase char on screen 1281 write1("\b \b"); // erase char on screen
1218 buf[--i] = '\0'; 1282 buf[--i] = '\0';
@@ -1970,6 +2034,18 @@ static char *yank_delete(char *start, char *stop, int buftype, int yf, int undo)
1970 return p; 2034 return p;
1971} 2035}
1972 2036
2037#if ENABLE_PLATFORM_MINGW32
2038static int count_cr(char *p, int len)
2039{
2040 int i, cnt;
2041
2042 for (i = cnt = 0; i < len; ++i)
2043 if (p[i] == '\n')
2044 ++cnt;
2045 return cnt;
2046}
2047#endif
2048
1973// might reallocate text[]! 2049// might reallocate text[]!
1974static int file_insert(const char *fn, char *p, int initial) 2050static int file_insert(const char *fn, char *p, int initial)
1975{ 2051{
@@ -1982,7 +2058,11 @@ static int file_insert(const char *fn, char *p, int initial)
1982 if (p > end) 2058 if (p > end)
1983 p = end; 2059 p = end;
1984 2060
2061#if !ENABLE_PLATFORM_MINGW32
1985 fd = open(fn, O_RDONLY); 2062 fd = open(fn, O_RDONLY);
2063#else
2064 fd = open(fn, O_RDONLY | _O_TEXT);
2065#endif
1986 if (fd < 0) { 2066 if (fd < 0) {
1987 if (!initial) 2067 if (!initial)
1988 status_line_bold_errno(fn); 2068 status_line_bold_errno(fn);
@@ -2005,8 +2085,15 @@ static int file_insert(const char *fn, char *p, int initial)
2005 status_line_bold_errno(fn); 2085 status_line_bold_errno(fn);
2006 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert 2086 p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert
2007 } else if (cnt < size) { 2087 } else if (cnt < size) {
2088#if ENABLE_PLATFORM_MINGW32
2089 // On WIN32 a partial read might just mean CRs have been removed
2090 int cnt_cr = cnt + count_cr(p, cnt);
2091#endif
2008 // There was a partial read, shrink unused space 2092 // There was a partial read, shrink unused space
2009 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO); 2093 p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
2094#if ENABLE_PLATFORM_MINGW32
2095 if (cnt_cr < size)
2096#endif
2010 status_line_bold("can't read '%s'", fn); 2097 status_line_bold("can't read '%s'", fn);
2011 } 2098 }
2012# if ENABLE_FEATURE_VI_UNDO 2099# if ENABLE_FEATURE_VI_UNDO
@@ -2014,6 +2101,10 @@ static int file_insert(const char *fn, char *p, int initial)
2014 undo_push_insert(p, size, ALLOW_UNDO); 2101 undo_push_insert(p, size, ALLOW_UNDO);
2015 } 2102 }
2016# endif 2103# endif
2104#if ENABLE_FEATURE_VI_FILE_FORMAT
2105 if (initial && cnt > 0)
2106 fileformat = cnt == size ? FF_UNIX_DOS : FF_DOS_UNIX;
2107#endif
2017 fi: 2108 fi:
2018 close(fd); 2109 close(fd);
2019 2110
@@ -2174,7 +2265,11 @@ static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
2174 p += 1 + stupid_insert(p, ' '); 2265 p += 1 + stupid_insert(p, ' ');
2175 } 2266 }
2176#endif 2267#endif
2268#if !ENABLE_PLATFORM_MINGW32
2177 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS 2269 } else if (c == term_orig.c_cc[VERASE] || c == 8 || c == 127) { // Is this a BS
2270#else
2271 } else if (c == 8 || c == 127) { // Is this a BS
2272#endif
2178 if (p > text) { 2273 if (p > text) {
2179 p--; 2274 p--;
2180 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char 2275 p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char
@@ -2286,6 +2381,9 @@ static int init_text_buffer(char *fn)
2286 free(text); 2381 free(text);
2287 text_size = 10240; 2382 text_size = 10240;
2288 screenbegin = dot = end = text = xzalloc(text_size); 2383 screenbegin = dot = end = text = xzalloc(text_size);
2384#if ENABLE_FEATURE_VI_FILE_FORMAT
2385 fileformat = fileformats;
2386#endif
2289 2387
2290 update_filename(fn); 2388 update_filename(fn);
2291 rc = file_insert(fn, text, 1); 2389 rc = file_insert(fn, text, 1);
@@ -2331,6 +2429,13 @@ static uintptr_t string_insert(char *p, const char *s, int undo) // insert the s
2331static int file_write(char *fn, char *first, char *last) 2429static int file_write(char *fn, char *first, char *last)
2332{ 2430{
2333 int fd, cnt, charcnt; 2431 int fd, cnt, charcnt;
2432#if ENABLE_PLATFORM_MINGW32
2433# if ENABLE_FEATURE_VI_FILE_FORMAT
2434# define dos (fileformat == FF_DOS_UNIX)
2435# else
2436# define dos (1)
2437# endif
2438#endif
2334 2439
2335 if (fn == 0) { 2440 if (fn == 0) {
2336 status_line_bold("No current filename"); 2441 status_line_bold("No current filename");
@@ -2339,12 +2444,22 @@ static int file_write(char *fn, char *first, char *last)
2339 // By popular request we do not open file with O_TRUNC, 2444 // By popular request we do not open file with O_TRUNC,
2340 // but instead ftruncate() it _after_ successful write. 2445 // but instead ftruncate() it _after_ successful write.
2341 // Might reduce amount of data lost on power fail etc. 2446 // Might reduce amount of data lost on power fail etc.
2447#if !ENABLE_PLATFORM_MINGW32
2342 fd = open(fn, (O_WRONLY | O_CREAT), 0666); 2448 fd = open(fn, (O_WRONLY | O_CREAT), 0666);
2449#else
2450 fd = open(fn, (O_WRONLY | O_CREAT | (dos ? _O_TEXT : 0)), 0666);
2451#endif
2343 if (fd < 0) 2452 if (fd < 0)
2344 return -1; 2453 return -1;
2345 cnt = last - first + 1; 2454 cnt = last - first + 1;
2346 charcnt = full_write(fd, first, cnt); 2455 charcnt = full_write(fd, first, cnt);
2456#if !ENABLE_PLATFORM_MINGW32
2347 ftruncate(fd, charcnt); 2457 ftruncate(fd, charcnt);
2458#else
2459 // If file was written in text mode it will be bigger so adjust
2460 // the truncation to match.
2461 ftruncate(fd, charcnt + (dos ? count_cr(first, cnt) : 0));
2462#endif
2348 if (charcnt == cnt) { 2463 if (charcnt == cnt) {
2349 // good write 2464 // good write
2350 //modified_count = FALSE; 2465 //modified_count = FALSE;
@@ -2633,6 +2748,7 @@ static void setops(char *args, int flg_no)
2633 2748
2634 index = 1 << (index >> 1); // convert to VI_bit 2749 index = 1 << (index >> 1); // convert to VI_bit
2635 2750
2751#if !ENABLE_FEATURE_VI_FILE_FORMAT
2636 if (index & VI_TABSTOP) { 2752 if (index & VI_TABSTOP) {
2637 int t; 2753 int t;
2638 if (!eq || flg_no) // no "=NNN" or it is "notabstop"? 2754 if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
@@ -2643,6 +2759,27 @@ static void setops(char *args, int flg_no)
2643 tabstop = t; 2759 tabstop = t;
2644 return; 2760 return;
2645 } 2761 }
2762#else
2763 if (index & VI_FILEFORMAT)
2764 goto bad;
2765 if (index & (VI_TABSTOP | VI_FILEFORMATS)) {
2766 if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
2767 goto bad;
2768 if (index & VI_TABSTOP) {
2769 int t = bb_strtou(eq + 1, NULL, 10);
2770 if (t <= 0 || t > MAX_TABSTOP)
2771 goto bad;
2772 tabstop = t;
2773 return;
2774 } else { // VI_FILEFORMATS
2775 int t = index_in_strings(FORMATS_STR, eq + 1);
2776 if (t < 0)
2777 goto bad;
2778 fileformats = t;
2779 return;
2780 }
2781 }
2782#endif
2646 if (eq) goto bad; // boolean option has "="? 2783 if (eq) goto bad; // boolean option has "="?
2647 if (flg_no) { 2784 if (flg_no) {
2648 vi_setops &= ~index; 2785 vi_setops &= ~index;
@@ -3139,6 +3276,7 @@ static void colon(char *buf)
3139 if (!args[0] || strcmp(args, "all") == 0) { 3276 if (!args[0] || strcmp(args, "all") == 0) {
3140 // print out values of all options 3277 // print out values of all options
3141# if ENABLE_FEATURE_VI_SETOPTS 3278# if ENABLE_FEATURE_VI_SETOPTS
3279# if !ENABLE_FEATURE_VI_FILE_FORMAT
3142 status_line_bold( 3280 status_line_bold(
3143 "%sautoindent " 3281 "%sautoindent "
3144 "%sexpandtab " 3282 "%sexpandtab "
@@ -3153,6 +3291,28 @@ static void colon(char *buf)
3153 showmatch ? "" : "no", 3291 showmatch ? "" : "no",
3154 tabstop 3292 tabstop
3155 ); 3293 );
3294# else // ENABLE_FEATURE_VI_FILE_FORMAT
3295 unsigned int mask = 1, j = 0;
3296 go_bottom_and_clear_to_eol();
3297 for (;;) {
3298 const char *name = nth_string(OPTS_STR, 2 * j + 1);
3299 if (!name[0])
3300 break;
3301 if (mask == VI_FILEFORMAT)
3302 printf("%s=%s ", name, nth_string("dos\0unix\0", fileformat));
3303 else if (mask == VI_FILEFORMATS)
3304 printf("%s=%s ", name, nth_string(FORMATS_STR, fileformats));
3305 else if (mask == VI_TABSTOP)
3306 printf("%s=%u ", name, tabstop);
3307 else
3308 printf("%s%s ", vi_setops & mask ? "" : "no", name);
3309 if (j++ == 4)
3310 bb_putchar('\n');
3311 mask <<= 1;
3312 }
3313 bb_putchar('\n');
3314 Hit_Return();
3315# endif
3156# endif 3316# endif
3157 goto ret; 3317 goto ret;
3158 } 3318 }
@@ -4967,7 +5127,11 @@ int vi_main(int argc, char **argv)
4967 5127
4968 // .exrc must belong to and only be writable by user 5128 // .exrc must belong to and only be writable by user
4969 if (stat(exrc, &st) == 0) { 5129 if (stat(exrc, &st) == 0) {
5130# if !ENABLE_PLATFORM_MINGW32
4970 if ((st.st_mode & (S_IWGRP|S_IWOTH)) == 0 5131 if ((st.st_mode & (S_IWGRP|S_IWOTH)) == 0
5132# else
5133 if (1
5134# endif
4971 && st.st_uid == getuid() 5135 && st.st_uid == getuid()
4972 ) { 5136 ) {
4973 cmds = xmalloc_open_read_close(exrc, NULL); 5137 cmds = xmalloc_open_read_close(exrc, NULL);
diff --git a/findutils/find.c b/findutils/find.c
index bb6ad31e5..7dbd32dac 100644
--- a/findutils/find.c
+++ b/findutils/find.c
@@ -135,7 +135,7 @@
135//config:config FEATURE_FIND_XDEV 135//config:config FEATURE_FIND_XDEV
136//config: bool "Enable -xdev: 'stay in filesystem'" 136//config: bool "Enable -xdev: 'stay in filesystem'"
137//config: default y 137//config: default y
138//config: depends on FIND 138//config: depends on FIND && (PLATFORM_POSIX || FEATURE_EXTRA_FILE_DATA)
139//config: 139//config:
140//config:config FEATURE_FIND_MAXDEPTH 140//config:config FEATURE_FIND_MAXDEPTH
141//config: bool "Enable -mindepth N and -maxdepth N" 141//config: bool "Enable -mindepth N and -maxdepth N"
@@ -153,7 +153,7 @@
153//config:config FEATURE_FIND_INUM 153//config:config FEATURE_FIND_INUM
154//config: bool "Enable -inum: inode number matching" 154//config: bool "Enable -inum: inode number matching"
155//config: default y 155//config: default y
156//config: depends on FIND 156//config: depends on FIND && (PLATFORM_POSIX || FEATURE_EXTRA_FILE_DATA)
157//config: 157//config:
158//config:config FEATURE_FIND_SAMEFILE 158//config:config FEATURE_FIND_SAMEFILE
159//config: bool "Enable -samefile: reference file matching" 159//config: bool "Enable -samefile: reference file matching"
@@ -274,7 +274,7 @@
274//config:config FEATURE_FIND_LINKS 274//config:config FEATURE_FIND_LINKS
275//config: bool "Enable -links: link count matching" 275//config: bool "Enable -links: link count matching"
276//config: default y 276//config: default y
277//config: depends on FIND 277//config: depends on FIND && (PLATFORM_POSIX || FEATURE_EXTRA_FILE_DATA)
278//config: help 278//config: help
279//config: Support the 'find -links' option for matching number of links. 279//config: Support the 'find -links' option for matching number of links.
280 280
@@ -1528,7 +1528,11 @@ static action*** parse_params(char **argv)
1528 action_inum *ap; 1528 action_inum *ap;
1529 dbg("%d", __LINE__); 1529 dbg("%d", __LINE__);
1530 ap = ALLOC_ACTION(inum); 1530 ap = ALLOC_ACTION(inum);
1531# if !ENABLE_FEATURE_EXTRA_FILE_DATA
1531 ap->inode_num = xatoul(arg1); 1532 ap->inode_num = xatoul(arg1);
1533# else
1534 ap->inode_num = xatoull(arg1);
1535# endif
1532 } 1536 }
1533#endif 1537#endif
1534#if ENABLE_FEATURE_FIND_SAMEFILE 1538#if ENABLE_FEATURE_FIND_SAMEFILE
diff --git a/findutils/xargs.c b/findutils/xargs.c
index 90ff05986..a0ba89c1e 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
@@ -114,6 +117,9 @@ struct globals {
114#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL 117#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
115 int running_procs; 118 int running_procs;
116 int max_procs; 119 int max_procs;
120#if ENABLE_PLATFORM_MINGW32
121 HANDLE *procs;
122#endif
117#endif 123#endif
118 smalluint xargs_exitcode; 124 smalluint xargs_exitcode;
119#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES 125#if ENABLE_FEATURE_XARGS_SUPPORT_QUOTES
@@ -135,12 +141,61 @@ struct globals {
135 G.idx = 0; \ 141 G.idx = 0; \
136 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \ 142 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.running_procs = 0;) \
137 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \ 143 IF_FEATURE_XARGS_SUPPORT_PARALLEL(G.max_procs = 1;) \
144 IF_FEATURE_XARGS_SUPPORT_PARALLEL(IF_PLATFORM_MINGW32(G.procs = NULL;)) \
138 G.xargs_exitcode = 0; \ 145 G.xargs_exitcode = 0; \
139 IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__state = NORM;) \ 146 IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__state = NORM;) \
140 IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__q = '\0';) \ 147 IF_FEATURE_XARGS_SUPPORT_QUOTES(G.process_stdin__q = '\0';) \
141} while (0) 148} while (0)
142 149
143 150
151#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL && ENABLE_PLATFORM_MINGW32
152static int wait_for_slot(int *idx)
153{
154 int i;
155
156 /* if less than max_procs running, set status to 0, return next free slot */
157 if (G.running_procs < G.max_procs) {
158 *idx = G.running_procs++;
159 return 0;
160 }
161
162check_exit_codes:
163 for (i = G.running_procs - 1; i >= 0; i--) {
164 DWORD status = 0;
165 if (!GetExitCodeProcess(G.procs[i], &status) ||
166 status != STILL_ACTIVE) {
167 CloseHandle(G.procs[i]);
168 if (i + 1 < G.running_procs)
169 G.procs[i] = G.procs[G.running_procs - 1];
170 *idx = G.running_procs - 1;
171 if (!G.max_procs)
172 G.running_procs--;
173 return status;
174 }
175 }
176
177 if (G.running_procs < MAXIMUM_WAIT_OBJECTS)
178 WaitForMultipleObjects((DWORD)G.running_procs, G.procs, FALSE,
179 INFINITE);
180 else {
181 /* Fall back to polling */
182 for (;;) {
183 DWORD nr = i + MAXIMUM_WAIT_OBJECTS > G.running_procs ?
184 MAXIMUM_WAIT_OBJECTS : (DWORD)(G.running_procs - i);
185 DWORD ret = WaitForMultipleObjects(nr, G.procs + i, FALSE, 100);
186
187 if (ret != WAIT_TIMEOUT)
188 break;
189 i += MAXIMUM_WAIT_OBJECTS;
190 if (i > G.running_procs)
191 i = 0;
192 }
193 }
194
195 goto check_exit_codes;
196}
197#endif /* SUPPORT_PARALLEL && PLATFORM_MINGW32 */
198
144/* 199/*
145 * Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123). 200 * Returns 0 if xargs should continue (but may set G.xargs_exitcode to 123).
146 * Else sets G.xargs_exitcode to error code and returns nonzero. 201 * Else sets G.xargs_exitcode to error code and returns nonzero.
@@ -157,6 +212,23 @@ static int xargs_exec(void)
157 if (G.max_procs == 1) { 212 if (G.max_procs == 1) {
158 status = spawn_and_wait(G.args); 213 status = spawn_and_wait(G.args);
159 } else { 214 } else {
215#if ENABLE_PLATFORM_MINGW32
216 int idx;
217 status = !G.running_procs && !G.max_procs ? 0 : wait_for_slot(&idx);
218 if (G.max_procs) {
219 HANDLE p = (HANDLE)mingw_spawn_proc((const char **)G.args);
220 if (p < 0)
221 status = -1;
222 else
223 G.procs[idx] = p;
224 } else {
225 while (G.running_procs) {
226 int status2 = wait_for_slot(&idx);
227 if (status2 && !status)
228 status = status2;
229 }
230 }
231#else
160 pid_t pid; 232 pid_t pid;
161 int wstat; 233 int wstat;
162 again: 234 again:
@@ -197,6 +269,7 @@ static int xargs_exec(void)
197 /* final waitpid() loop: must be ECHILD "no more children" */ 269 /* final waitpid() loop: must be ECHILD "no more children" */
198 status = 0; 270 status = 0;
199 } 271 }
272#endif
200 } 273 }
201#endif 274#endif
202 /* Manpage: 275 /* Manpage:
@@ -516,6 +589,7 @@ static char* FAST_FUNC process_stdin_with_replace(int n_max_chars, int n_max_arg
516 */ 589 */
517static int xargs_ask_confirmation(void) 590static int xargs_ask_confirmation(void)
518{ 591{
592#if !ENABLE_PLATFORM_MINGW32
519 FILE *tty_stream; 593 FILE *tty_stream;
520 int r; 594 int r;
521 595
@@ -525,6 +599,18 @@ static int xargs_ask_confirmation(void)
525 r = bb_ask_y_confirmation_FILE(tty_stream); 599 r = bb_ask_y_confirmation_FILE(tty_stream);
526 600
527 fclose(tty_stream); 601 fclose(tty_stream);
602#else
603 int r, c, savec;
604
605 fputs(" ?...", stderr);
606 fflush_all();
607 c = savec = getche();
608 while (c != EOF && c != '\r')
609 c = getche();
610 fputs("\n", stderr);
611 fflush_all();
612 r = (savec == 'y' || savec == 'Y');
613#endif
528 614
529 return r; 615 return r;
530} 616}
@@ -628,7 +714,12 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
628 714
629#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL 715#if ENABLE_FEATURE_XARGS_SUPPORT_PARALLEL
630 if (G.max_procs <= 0) /* -P0 means "run lots of them" */ 716 if (G.max_procs <= 0) /* -P0 means "run lots of them" */
717#if !ENABLE_PLATFORM_MINGW32
631 G.max_procs = 100; /* let's not go crazy high */ 718 G.max_procs = 100; /* let's not go crazy high */
719#else
720 G.max_procs = MAXIMUM_WAIT_OBJECTS;
721 G.procs = xmalloc(sizeof(G.procs[0]) * G.max_procs);
722#endif
632#endif 723#endif
633 724
634#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE 725#if ENABLE_FEATURE_XARGS_SUPPORT_ARGS_FILE
@@ -750,7 +841,11 @@ int xargs_main(int argc UNUSED_PARAM, char **argv)
750 fmt = " %s"; 841 fmt = " %s";
751 } 842 }
752 if (!(opt & OPT_INTERACTIVE)) 843 if (!(opt & OPT_INTERACTIVE))
844#if !ENABLE_PLATFORM_MINGW32
753 bb_putchar_stderr('\n'); 845 bb_putchar_stderr('\n');
846#else
847 fprintf(stderr, "\n");
848#endif
754 } 849 }
755 850
756 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) { 851 if (!(opt & OPT_INTERACTIVE) || xargs_ask_confirmation()) {
diff --git a/include/bb_archive.h b/include/bb_archive.h
index e0ef8fc4e..3422c9656 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 {
@@ -277,7 +287,11 @@ enum {
277 BBUNPK_SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION, 287 BBUNPK_SEAMLESS_MAGIC = (1 << 31) * ENABLE_ZCAT * SEAMLESS_COMPRESSION,
278}; 288};
279 289
290#if !ENABLE_PLATFORM_MINGW32
280void check_errors_in_children(int signo); 291void check_errors_in_children(int signo);
292#else
293#define check_errors_in_children(s) ((void)0)
294#endif
281#if BB_MMU 295#if BB_MMU
282void fork_transformer(int fd, 296void fork_transformer(int fd,
283 int signature_skipped, 297 int signature_skipped,
diff --git a/include/libbb.h b/include/libbb.h
index a48782832..9aff42e67 100644
--- a/include/libbb.h
+++ b/include/libbb.h
@@ -143,6 +143,9 @@
143# include <arpa/inet.h> 143# include <arpa/inet.h>
144#elif defined __APPLE__ 144#elif defined __APPLE__
145# include <netinet/in.h> 145# include <netinet/in.h>
146#elif ENABLE_PLATFORM_MINGW32
147# include <winsock2.h>
148# include <ws2tcpip.h>
146#else 149#else
147# include <arpa/inet.h> 150# include <arpa/inet.h>
148//This breaks on bionic: 151//This breaks on bionic:
@@ -182,7 +185,9 @@
182 185
183/* Some libc's forget to declare these, do it ourself */ 186/* Some libc's forget to declare these, do it ourself */
184 187
188#if !ENABLE_PLATFORM_MINGW32
185extern char **environ; 189extern char **environ;
190#endif
186/* klogctl is in libc's klog.h, but we cheat and not #include that */ 191/* klogctl is in libc's klog.h, but we cheat and not #include that */
187int klogctl(int type, char *b, int len); 192int klogctl(int type, char *b, int len);
188#ifndef PATH_MAX 193#ifndef PATH_MAX
@@ -192,6 +197,13 @@ int klogctl(int type, char *b, int len);
192# define BUFSIZ 4096 197# define BUFSIZ 4096
193#endif 198#endif
194 199
200#if ENABLE_PLATFORM_MINGW32
201# include "mingw.h"
202# define MINGW_SPECIAL(a) mingw_ ## a
203#else
204# define MINGW_SPECIAL(a) a
205#endif
206
195#if __GNUC_PREREQ(5,0) 207#if __GNUC_PREREQ(5,0)
196/* Since musl is apparently unable to get it right and would use 208/* Since musl is apparently unable to get it right and would use
197 * a function call to a single-instruction function of "bswap %eax", 209 * a function call to a single-instruction function of "bswap %eax",
@@ -276,6 +288,19 @@ PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN
276 : ((T)1 << (sizeof(T)*8-1)) \ 288 : ((T)1 << (sizeof(T)*8-1)) \
277 ) 289 )
278 290
291#if ENABLE_PLATFORM_MINGW32 && \
292 (!defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO)
293#define LL_FMT "I64"
294#else
295#define LL_FMT "ll"
296#endif
297
298#if ENABLE_PLATFORM_MINGW32 && defined(_WIN64)
299#define PID_FMT "I64"
300#else
301#define PID_FMT
302#endif
303
279/* Large file support */ 304/* Large file support */
280/* Note that CONFIG_LFS=y forces bbox to be built with all common ops 305/* Note that CONFIG_LFS=y forces bbox to be built with all common ops
281 * (stat, lseek etc) mapped to "largefile" variants by libc. 306 * (stat, lseek etc) mapped to "largefile" variants by libc.
@@ -301,7 +326,7 @@ typedef unsigned long long uoff_t;
301# define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX) 326# define XATOOFF(a) xatoull_range((a), 0, LLONG_MAX)
302# define BB_STRTOOFF bb_strtoull 327# define BB_STRTOOFF bb_strtoull
303# define STRTOOFF strtoull 328# define STRTOOFF strtoull
304# define OFF_FMT "ll" 329# define OFF_FMT LL_FMT
305# endif 330# endif
306#else 331#else
307/* CONFIG_LFS is off */ 332/* CONFIG_LFS is off */
@@ -603,20 +628,37 @@ enum {
603 * Dance around with long long to guard against that... 628 * Dance around with long long to guard against that...
604 */ 629 */
605 BB_FATAL_SIGS = (int)(0 630 BB_FATAL_SIGS = (int)(0
631#ifdef SIGHUP
606 + (1LL << SIGHUP) 632 + (1LL << SIGHUP)
633#endif
607 + (1LL << SIGINT) 634 + (1LL << SIGINT)
608 + (1LL << SIGTERM) 635 + (1LL << SIGTERM)
609 + (1LL << SIGPIPE) // Write to pipe with no readers 636 + (1LL << SIGPIPE) // Write to pipe with no readers
637#ifdef SIGQUIT
610 + (1LL << SIGQUIT) // Quit from keyboard 638 + (1LL << SIGQUIT) // Quit from keyboard
639#endif
611 + (1LL << SIGABRT) // Abort signal from abort(3) 640 + (1LL << SIGABRT) // Abort signal from abort(3)
641#ifdef SIGALRM
612 + (1LL << SIGALRM) // Timer signal from alarm(2) 642 + (1LL << SIGALRM) // Timer signal from alarm(2)
643#endif
644#ifdef SIGVTALRM
613 + (1LL << SIGVTALRM) // Virtual alarm clock 645 + (1LL << SIGVTALRM) // Virtual alarm clock
646#endif
647#ifdef SIGXCPU
614 + (1LL << SIGXCPU) // CPU time limit exceeded 648 + (1LL << SIGXCPU) // CPU time limit exceeded
649#endif
650#ifdef SIGXFSZ
615 + (1LL << SIGXFSZ) // File size limit exceeded 651 + (1LL << SIGXFSZ) // File size limit exceeded
652#endif
653#ifdef SIGUSR1
616 + (1LL << SIGUSR1) // Yes kids, these are also fatal! 654 + (1LL << SIGUSR1) // Yes kids, these are also fatal!
655#endif
656#ifdef SIGUSR1
617 + (1LL << SIGUSR2) 657 + (1LL << SIGUSR2)
658#endif
618 + 0), 659 + 0),
619}; 660};
661#if !ENABLE_PLATFORM_MINGW32
620void bb_signals(int sigs, void (*f)(int)) FAST_FUNC; 662void bb_signals(int sigs, void (*f)(int)) FAST_FUNC;
621/* Unlike signal() and bb_signals, sets handler with sigaction() 663/* Unlike signal() and bb_signals, sets handler with sigaction()
622 * and in a way that while signal handler is run, no other signals 664 * and in a way that while signal handler is run, no other signals
@@ -636,6 +678,10 @@ int sigaction_set(int sig, const struct sigaction *act) FAST_FUNC;
636int sigprocmask_allsigs(int how) FAST_FUNC; 678int sigprocmask_allsigs(int how) FAST_FUNC;
637/* Return old set in the same set: */ 679/* Return old set in the same set: */
638int sigprocmask2(int how, sigset_t *set) FAST_FUNC; 680int sigprocmask2(int how, sigset_t *set) FAST_FUNC;
681#else
682#define bb_signals(s, f)
683#define kill_myself_with_sig(s)
684#endif
639/* Standard handler which just records signo */ 685/* Standard handler which just records signo */
640extern smallint bb_got_signal; 686extern smallint bb_got_signal;
641void record_signo(int signo); /* not FAST_FUNC! */ 687void record_signo(int signo); /* not FAST_FUNC! */
@@ -718,7 +764,7 @@ void xsettimeofday(const struct timeval *tv) FAST_FUNC;
718int xsocket(int domain, int type, int protocol) FAST_FUNC; 764int xsocket(int domain, int type, int protocol) FAST_FUNC;
719void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC; 765void xbind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen) FAST_FUNC;
720void xlisten(int s, int backlog) FAST_FUNC; 766void xlisten(int s, int backlog) FAST_FUNC;
721void xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) FAST_FUNC; 767void xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen) FAST_FUNC;
722ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, 768ssize_t xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
723 socklen_t tolen) FAST_FUNC; 769 socklen_t tolen) FAST_FUNC;
724 770
@@ -1227,6 +1273,7 @@ void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
1227 1273
1228/* xvfork() can't be a _function_, return after vfork in child mangles stack 1274/* xvfork() can't be a _function_, return after vfork in child mangles stack
1229 * in the parent. It must be a macro. */ 1275 * in the parent. It must be a macro. */
1276#if !ENABLE_PLATFORM_MINGW32
1230#define xvfork() \ 1277#define xvfork() \
1231({ \ 1278({ \
1232 pid_t bb__xvfork_pid = vfork(); \ 1279 pid_t bb__xvfork_pid = vfork(); \
@@ -1234,6 +1281,9 @@ void BB_EXECVP_or_die(char **argv) NORETURN FAST_FUNC;
1234 bb_simple_perror_msg_and_die("vfork"); \ 1281 bb_simple_perror_msg_and_die("vfork"); \
1235 bb__xvfork_pid; \ 1282 bb__xvfork_pid; \
1236}) 1283})
1284#else
1285#define xvfork() vfork()
1286#endif
1237#if BB_MMU 1287#if BB_MMU
1238pid_t xfork(void) FAST_FUNC; 1288pid_t xfork(void) FAST_FUNC;
1239#endif 1289#endif
@@ -1824,9 +1874,15 @@ int bb_xioctl(int fd, unsigned request, void *argp) FAST_FUNC;
1824#define xioctl(fd,request,argp) bb_xioctl(fd,request,argp) 1874#define xioctl(fd,request,argp) bb_xioctl(fd,request,argp)
1825#endif 1875#endif
1826 1876
1877#if !ENABLE_PLATFORM_MINGW32 || ENABLE_FEATURE_EXTRA_FILE_DATA
1827char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC; 1878char *is_in_ino_dev_hashtable(const struct stat *statbuf) FAST_FUNC;
1828void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC; 1879void add_to_ino_dev_hashtable(const struct stat *statbuf, const char *name) FAST_FUNC;
1829void reset_ino_dev_hashtable(void) FAST_FUNC; 1880void reset_ino_dev_hashtable(void) FAST_FUNC;
1881#else
1882#define add_to_ino_dev_hashtable(s, n) (void)0
1883#define is_in_ino_dev_hashtable(s) NULL
1884#define reset_ino_dev_hashtable()
1885#endif
1830#ifdef __GLIBC__ 1886#ifdef __GLIBC__
1831/* At least glibc has horrendously large inline for this, so wrap it */ 1887/* At least glibc has horrendously large inline for this, so wrap it */
1832unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC; 1888unsigned long long bb_makedev(unsigned major, unsigned minor) FAST_FUNC;
@@ -2024,7 +2080,11 @@ int FAST_FUNC procps_read_smaps(pid_t pid, struct smaprec *total,
2024 void (*cb)(struct smaprec *, void *), void *data); 2080 void (*cb)(struct smaprec *, void *), void *data);
2025 2081
2026typedef struct procps_status_t { 2082typedef struct procps_status_t {
2083#if !ENABLE_PLATFORM_MINGW32
2027 DIR *dir; 2084 DIR *dir;
2085#else
2086 HANDLE snapshot;
2087#endif
2028 IF_FEATURE_SHOW_THREADS(DIR *task_dir;) 2088 IF_FEATURE_SHOW_THREADS(DIR *task_dir;)
2029 uint8_t shift_pages_to_bytes; 2089 uint8_t shift_pages_to_bytes;
2030 uint8_t shift_pages_to_kb; 2090 uint8_t shift_pages_to_kb;
@@ -2258,12 +2318,27 @@ extern const char bb_path_wtmp_file[] ALIGN1;
2258#define bb_path_motd_file "/etc/motd" 2318#define bb_path_motd_file "/etc/motd"
2259 2319
2260#define bb_dev_null "/dev/null" 2320#define bb_dev_null "/dev/null"
2321#if ENABLE_PLATFORM_MINGW32
2322#define bb_busybox_exec_path get_busybox_exec_path()
2323extern char bb_comm[];
2324extern char bb_command_line[];
2325#else
2261extern const char bb_busybox_exec_path[] ALIGN1; 2326extern const char bb_busybox_exec_path[] ALIGN1;
2327#endif
2262/* allow default system PATH to be extended via CFLAGS */ 2328/* allow default system PATH to be extended via CFLAGS */
2263#ifndef BB_ADDITIONAL_PATH 2329#ifndef BB_ADDITIONAL_PATH
2264#define BB_ADDITIONAL_PATH "" 2330#define BB_ADDITIONAL_PATH ""
2265#endif 2331#endif
2332#if !ENABLE_PLATFORM_MINGW32
2266#define BB_PATH_ROOT_PATH "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH 2333#define BB_PATH_ROOT_PATH "PATH=/sbin:/usr/sbin:/bin:/usr/bin" BB_ADDITIONAL_PATH
2334#define PATH_SEP ':'
2335#define PATH_SEP_STR ":"
2336#else
2337#define BB_PATH_ROOT_PATH "PATH=/sbin;/usr/sbin;/bin;/usr/bin" BB_ADDITIONAL_PATH
2338#define PATH_SEP ';'
2339#define PATH_SEP_STR ";"
2340extern const char bb_skip_ansi_emulation[] ALIGN1;
2341#endif
2267extern const char bb_PATH_root_path[] ALIGN1; /* BB_PATH_ROOT_PATH */ 2342extern const char bb_PATH_root_path[] ALIGN1; /* BB_PATH_ROOT_PATH */
2268#define bb_default_root_path (bb_PATH_root_path + sizeof("PATH")) 2343#define bb_default_root_path (bb_PATH_root_path + sizeof("PATH"))
2269/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, 2344/* 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..06fb3b289
--- /dev/null
+++ b/include/mingw.h
@@ -0,0 +1,585 @@
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_DIRECT 0
35#define O_SPECIAL 0x800000
36
37#define AT_FDCWD -100
38#define AT_SYMLINK_NOFOLLOW 0x100
39
40/*
41 * grp.h
42 */
43
44struct group {
45 char *gr_name;
46 char *gr_passwd;
47 gid_t gr_gid;
48 char **gr_mem;
49};
50IMPL(getgrnam,struct group *,NULL,const char *name UNUSED_PARAM);
51struct group *getgrgid(gid_t gid);
52NOIMPL(initgroups,const char *group UNUSED_PARAM,gid_t gid UNUSED_PARAM);
53static inline void endgrent(void) {}
54int getgrouplist(const char *user, gid_t group, gid_t *groups, int *ngroups);
55
56/*
57 * limits.h
58 */
59#define NAME_MAX 255
60#define MAXSYMLINKS 20
61
62#ifdef LONG_MAX
63# if LONG_MAX == 2147483647
64# define LONG_BIT 32
65# else
66/* Safe assumption. */
67# define LONG_BIT 64
68# endif
69#elif defined __LONG_MAX__
70# if __LONG_MAX__ == 2147483647
71# define LONG_BIT 32
72# else
73/* Safe assumption. */
74# define LONG_BIT 64
75# endif
76#endif
77
78/*
79 * netdb.h
80 */
81
82typedef int sa_family_t;
83
84/*
85 * linux/un.h
86 */
87struct sockaddr_un {
88 sa_family_t sun_family;
89 char sun_path[1]; /* to make compiler happy, don't bother */
90};
91
92/*
93 * pwd.h
94 */
95struct passwd {
96 char *pw_name;
97 char *pw_passwd;
98 char *pw_gecos;
99 char *pw_dir;
100 char *pw_shell;
101 uid_t pw_uid;
102 gid_t pw_gid;
103};
104
105struct passwd *getpwnam(const char *name);
106struct passwd *getpwuid(uid_t uid);
107static inline void setpwent(void) {}
108static inline void endpwent(void) {}
109IMPL(getpwent_r,int,ENOENT,struct passwd *pwbuf UNUSED_PARAM,char *buf UNUSED_PARAM,size_t buflen UNUSED_PARAM,struct passwd **pwbufp UNUSED_PARAM);
110IMPL(getpwent,struct passwd *,NULL,void)
111
112/*
113 * signal.h
114 */
115#define SIGKILL 9
116#define SIGPIPE 13
117
118#define SIG_UNBLOCK 1
119
120typedef void (*sighandler_t)(int);
121sighandler_t winansi_signal(int signum, sighandler_t handler);
122#define signal(s, h) winansi_signal(s, h)
123
124/*
125 * stdio.h
126 */
127#undef fseeko
128#define fseeko(f,o,w) fseek(f,o,w)
129
130int fdprintf(int fd, const char *format, ...);
131FILE* mingw_fopen(const char *filename, const char *mode);
132int mingw_rename(const char*, const char*);
133#define fopen mingw_fopen
134#define rename mingw_rename
135
136FILE *mingw_popen(const char *cmd, const char *mode);
137int mingw_popen_fd(const char *cmd, const char *mode, int fd0, pid_t *pid);
138int mingw_pclose(FILE *fd);
139pid_t mingw_fork_compressor(int fd, const char *compressor, const char *mode);
140#undef popen
141#undef pclose
142#define popen mingw_popen
143#define pclose mingw_pclose
144
145#define setlinebuf(fd) setvbuf(fd, (char *) NULL, _IOLBF, 0)
146
147/*
148 * ANSI emulation wrappers
149 */
150
151void set_title(const char *str);
152void move_cursor_row(int n);
153void reset_screen(void);
154int winansi_putchar(int c);
155int winansi_puts(const char *s);
156size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);
157int winansi_fputs(const char *str, FILE *stream);
158int winansi_fputc(int c, FILE *stream);
159int winansi_vsnprintf(char *buf, size_t size, const char *format, va_list list);
160int winansi_vfprintf(FILE *stream, const char *format, va_list list);
161int winansi_printf(const char *format, ...) __attribute__((format (printf, 1, 2)));
162int winansi_fprintf(FILE *stream, const char *format, ...) __attribute__((format (printf, 2, 3)));
163int winansi_write(int fd, const void *buf, size_t count);
164int winansi_read(int fd, void *buf, size_t count);
165int winansi_getc(FILE *stream);
166#define putchar winansi_putchar
167#define puts winansi_puts
168#define fwrite winansi_fwrite
169#define fputs winansi_fputs
170#define fputc winansi_fputc
171#if !defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO
172#define vsnprintf(buf, size, ...) winansi_vsnprintf(buf, size, __VA_ARGS__)
173#endif
174#define vfprintf(stream, ...) winansi_vfprintf(stream, __VA_ARGS__)
175#define vprintf(...) winansi_vfprintf(stdout, __VA_ARGS__)
176#define printf(...) winansi_printf(__VA_ARGS__)
177#define fprintf(...) winansi_fprintf(__VA_ARGS__)
178#define write winansi_write
179#define read winansi_read
180#define getc winansi_getc
181
182/*
183 * stdlib.h
184 */
185#define WTERMSIG(x) ((x) & 0x7f)
186#define WIFEXITED(x) (WTERMSIG(x) == 0)
187#define WEXITSTATUS(x) (((x) & 0xff00) >> 8)
188#define WIFSIGNALED(x) (((signed char) (((x) & 0x7f) + 1) >> 1) > 0)
189#define WCOREDUMP(x) 0
190#define WIFSTOPPED(x) 0
191
192int mingw_system(const char *cmd);
193#define system mingw_system
194
195int clearenv(void);
196char *mingw_getenv(const char *name);
197int mingw_putenv(const char *env);
198char *mingw_mktemp(char *template);
199int mkstemp(char *template);
200char *realpath(const char *path, char *resolved_path);
201int setenv(const char *name, const char *value, int replace);
202int unsetenv(const char *env);
203
204#define getenv mingw_getenv
205#define putenv mingw_putenv
206#define mktemp mingw_mktemp
207
208/*
209 * string.h
210 */
211char *strndup(char const *s, size_t n);
212char *mingw_strerror(int errnum);
213char *strsignal(int sig);
214
215#define strerror mingw_strerror
216
217/*
218 * strings.h
219 */
220int ffs(int i);
221
222/*
223 * sys/ioctl.h
224 */
225
226#define TIOCGWINSZ 0x5413
227
228int ioctl(int fd, int code, ...);
229
230/*
231 * sys/socket.h
232 */
233#define hstrerror strerror
234
235#define SHUT_WR SD_SEND
236
237int mingw_socket(int domain, int type, int protocol);
238int mingw_connect(int sockfd, const struct sockaddr *sa, size_t sz);
239int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz);
240int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen);
241int mingw_shutdown(int sockfd, int how);
242int mingw_listen(int sockfd, int backlog);
243int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz);
244int mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
245 struct timeval *timeout);
246int mingw_getpeername(int fd, struct sockaddr *sa, socklen_t *sz);
247int mingw_gethostname(char *host, int namelen);
248int mingw_getaddrinfo(const char *node, const char *service,
249 const struct addrinfo *hints, struct addrinfo **res);
250struct hostent *mingw_gethostbyaddr(const void *addr, socklen_t len, int type);
251
252#define socket mingw_socket
253#define connect mingw_connect
254#define listen mingw_listen
255#define bind mingw_bind
256#define setsockopt mingw_setsockopt
257#define shutdown mingw_shutdown
258#define accept mingw_accept
259#define select mingw_select
260#define getpeername mingw_getpeername
261#define gethostname mingw_gethostname
262#define getaddrinfo mingw_getaddrinfo
263#define gethostbyaddr mingw_gethostbyaddr
264
265/*
266 * sys/time.h
267 */
268#ifndef _TIMESPEC_DEFINED
269#define _TIMESPEC_DEFINED
270struct timespec {
271 time_t tv_sec;
272 long int tv_nsec;
273};
274#endif
275
276time_t timegm(struct tm *tm);
277
278int nanosleep(const struct timespec *req, struct timespec *rem);
279
280/*
281 * sys/stat.h
282 */
283#define S_ISUID 04000
284#define S_ISGID 02000
285#define S_ISVTX 01000
286#ifndef S_IRWXU
287#define S_IRWXU (S_IRUSR | S_IWUSR | S_IXUSR)
288#endif
289#define S_IRWXG (S_IRWXU >> 3)
290#define S_IRWXO (S_IRWXG >> 3)
291
292#define S_IFSOCK 0140000
293#define S_IFLNK 0120000 /* Symbolic link */
294#define S_ISLNK(x) (((x) & S_IFMT) == S_IFLNK)
295#define S_ISSOCK(x) 0
296
297#define S_IRGRP (S_IRUSR >> 3)
298#define S_IWGRP (S_IWUSR >> 3)
299#define S_IXGRP (S_IXUSR >> 3)
300#define S_IROTH (S_IRGRP >> 3)
301#define S_IWOTH (S_IWGRP >> 3)
302#define S_IXOTH (S_IXGRP >> 3)
303
304mode_t mingw_umask(mode_t mode);
305#define umask mingw_umask
306
307#define DEFAULT_UMASK 0002
308
309IMPL(fchmod,int,0,int fildes UNUSED_PARAM, mode_t mode UNUSED_PARAM);
310NOIMPL(fchown,int fd UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM);
311int mingw_mkdir(const char *path, int mode);
312int mingw_chdir(const char *path);
313int mingw_chmod(const char *path, int mode);
314
315#define mkdir mingw_mkdir
316#define chdir mingw_chdir
317#define chmod mingw_chmod
318
319#if ENABLE_LFS && !defined(__MINGW64_VERSION_MAJOR)
320# define off_t off64_t
321#endif
322
323typedef int nlink_t;
324typedef int blksize_t;
325typedef off_t blkcnt_t;
326#if ENABLE_FEATURE_EXTRA_FILE_DATA
327#define ino_t uint64_t
328#endif
329
330struct mingw_stat {
331 dev_t st_dev;
332 ino_t st_ino;
333 mode_t st_mode;
334 nlink_t st_nlink;
335 uid_t st_uid;
336 gid_t st_gid;
337 dev_t st_rdev;
338 off_t st_size;
339 struct timespec st_atim;
340 struct timespec st_mtim;
341 struct timespec st_ctim;
342 blksize_t st_blksize;
343 blkcnt_t st_blocks;
344 DWORD st_attr;
345};
346#define st_atime st_atim.tv_sec
347#define st_mtime st_mtim.tv_sec
348#define st_ctime st_ctim.tv_sec
349
350int mingw_lstat(const char *file_name, struct mingw_stat *buf);
351int mingw_stat(const char *file_name, struct mingw_stat *buf);
352int mingw_fstat(int fd, struct mingw_stat *buf);
353#undef lstat
354#undef stat
355#undef fstat
356#define lstat mingw_lstat
357#define stat mingw_stat
358#define fstat mingw_fstat
359
360#define UTIME_NOW ((1l << 30) - 1l)
361#define UTIME_OMIT ((1l << 30) - 2l)
362
363int utimensat(int fd, const char *path, const struct timespec times[2],
364 int flags);
365int futimens(int fd, const struct timespec times[2]);
366
367/*
368 * sys/sysinfo.h
369 */
370struct sysinfo {
371 long uptime; /* Seconds since boot */
372 unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
373 unsigned long totalram; /* Total usable main memory size */
374 unsigned long freeram; /* Available memory size */
375 unsigned long sharedram; /* Amount of shared memory */
376 unsigned long bufferram; /* Memory used by buffers */
377 unsigned long totalswap; /* Total swap space size */
378 unsigned long freeswap; /* Swap space still available */
379 unsigned short procs; /* Number of current processes */
380 unsigned long totalhigh; /* Total high memory size */
381 unsigned long freehigh; /* Available high memory size */
382 unsigned int mem_unit; /* Memory unit size in bytes */
383};
384
385int sysinfo(struct sysinfo *info);
386
387/*
388 * sys/sysmacros.h
389 */
390#define makedev(a,b) 0*(a)*(b) /* avoid unused warning */
391#define minor(x) 0
392#define major(x) 0
393
394/*
395 * sys/wait.h
396 */
397#define WNOHANG 1
398#define WUNTRACED 2
399pid_t waitpid(pid_t pid, int *status, int options);
400pid_t mingw_wait3(pid_t pid, int *status, int options, struct rusage *rusage);
401
402/*
403 * time.h
404 */
405struct tm *gmtime_r(const time_t *timep, struct tm *result);
406struct tm *localtime_r(const time_t *timep, struct tm *result);
407char *strptime(const char *s, const char *format, struct tm *tm);
408char *mingw_strptime(const char *s, const char *format, struct tm *tm, long *gmt);
409size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm);
410
411#define strftime mingw_strftime
412
413/*
414 * times.h
415 */
416#define clock_t long
417
418struct tms {
419 clock_t tms_utime; /* user CPU time */
420 clock_t tms_stime; /* system CPU time */
421 clock_t tms_cutime; /* user CPU time of children */
422 clock_t tms_cstime; /* system CPU time of children */
423};
424
425clock_t times(struct tms *buf);
426
427/*
428 * unistd.h
429 */
430#define PIPE_BUF 8192
431
432#define _SC_CLK_TCK 2
433
434#define TICKS_PER_SECOND 100
435#define MS_PER_TICK 10
436#define HNSEC_PER_TICK 100000
437
438IMPL(alarm,unsigned int,0,unsigned int seconds UNUSED_PARAM);
439IMPL(chown,int,0,const char *path UNUSED_PARAM, uid_t uid UNUSED_PARAM, gid_t gid UNUSED_PARAM);
440NOIMPL(chroot,const char *root UNUSED_PARAM);
441NOIMPL(fchdir,int fd UNUSED_PARAM);
442int mingw_dup2 (int fd, int fdto);
443char *mingw_getcwd(char *pointer, int len);
444off_t mingw_lseek(int fd, off_t offset, int whence);
445
446
447int getuid(void);
448#define getgid getuid
449#define geteuid getuid
450#define getegid getuid
451int getgroups(int n, gid_t *groups);
452IMPL(getppid,int,1,void);
453NOIMPL(getsid,pid_t pid UNUSED_PARAM);
454int getlogin_r(char *buf, size_t len);
455int fcntl(int fd, int cmd, ...);
456int fsync(int fd);
457int kill(pid_t pid, int sig);
458int link(const char *oldpath, const char *newpath);
459NOIMPL(mknod,const char *name UNUSED_PARAM, mode_t mode UNUSED_PARAM, dev_t device UNUSED_PARAM);
460/* order of devices must match that in get_dev_type */
461enum {DEV_NULL, DEV_ZERO, DEV_URANDOM, NOT_DEVICE = -1};
462int get_dev_type(const char *filename);
463void update_special_fd(int dev, int fd);
464int mingw_open (const char *filename, int oflags, ...);
465
466/* functions which add O_SPECIAL to open(2) to allow access to devices */
467int mingw_xopen(const char *filename, int oflags);
468ssize_t mingw_open_read_close(const char *fn, void *buf, size_t size) FAST_FUNC;
469
470ssize_t mingw_read(int fd, void *buf, size_t count);
471int mingw_close(int fd);
472int pipe(int filedes[2]);
473ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
474NOIMPL(setgid,gid_t gid UNUSED_PARAM);
475NOIMPL(setegid,gid_t gid UNUSED_PARAM);
476NOIMPL(setsid,void);
477NOIMPL(setuid,uid_t gid UNUSED_PARAM);
478NOIMPL(seteuid,uid_t gid UNUSED_PARAM);
479unsigned int sleep(unsigned int seconds);
480int symlink(const char *target, const char *linkpath);
481long sysconf(int name);
482IMPL(getpagesize,int,4096,void);
483NOIMPL(ttyname_r,int fd UNUSED_PARAM, char *buf UNUSED_PARAM, int sz UNUSED_PARAM);
484int mingw_unlink(const char *pathname);
485pid_t vfork(void);
486int mingw_access(const char *name, int mode);
487int mingw_rmdir(const char *name);
488void mingw_sync(void);
489int mingw_isatty(int fd);
490
491#define dup2 mingw_dup2
492#define getcwd mingw_getcwd
493#define lchown chown
494#define open mingw_open
495#define close mingw_close
496#define unlink mingw_unlink
497#define rmdir mingw_rmdir
498#define sync mingw_sync
499#undef lseek
500#define lseek mingw_lseek
501
502#undef access
503#define access mingw_access
504#define isatty mingw_isatty
505
506/*
507 * utime.h
508 */
509int utimes(const char *file_name, const struct timeval times[2]);
510
511/*
512 * Functions with different prototypes in BusyBox and WIN32
513 */
514#define itoa bb_itoa
515#define strrev bb_strrev
516
517/*
518 * MinGW specific
519 */
520#define is_dir_sep(c) ((c) == '/' || (c) == '\\')
521#define is_unc_path(x) (strlen(x) > 4 && is_dir_sep(x[0]) && \
522 is_dir_sep(x[1]) && !is_dir_sep(x[2]))
523
524pid_t FAST_FUNC mingw_spawn(char **argv);
525pid_t FAST_FUNC mingw_spawn_detach(char **argv);
526intptr_t FAST_FUNC mingw_spawn_proc(const char **argv);
527int mingw_execv(const char *cmd, char *const *argv);
528int mingw_execvp(const char *cmd, char *const *argv);
529int mingw_execve(const char *cmd, char *const *argv, char *const *envp);
530#define spawn mingw_spawn
531#define execvp mingw_execvp
532#define execve mingw_execve
533#define execv mingw_execv
534
535#define has_dos_drive_prefix(path) (isalpha(*(path)) && (path)[1] == ':')
536
537int kill_SIGTERM_by_handle(HANDLE process);
538
539#define find_mount_point(n, s) find_mount_point(n)
540
541char *is_prefixed_with_case(const char *string, const char *key) FAST_FUNC;
542char *is_suffixed_with_case(const char *string, const char *key) FAST_FUNC;
543void qsort_string_vector_case(char **sv, unsigned count) FAST_FUNC;
544
545/*
546 * helpers
547 */
548
549const char *get_busybox_exec_path(void);
550void init_winsock(void);
551void init_codepage(void);
552
553int has_bat_suffix(const char *p);
554int has_exe_suffix(const char *p);
555int has_exe_suffix_or_dot(const char *name);
556int add_win32_extension(char *p);
557
558char *bs_to_slash(char *p) FAST_FUNC;
559void slash_to_bs(char *p) FAST_FUNC;
560size_t remove_cr(char *p, size_t len) FAST_FUNC;
561
562int err_win_to_posix(void);
563
564ULONGLONG CompatGetTickCount64(void);
565#define GetTickCount64 CompatGetTickCount64
566
567ssize_t get_random_bytes(void *buf, ssize_t count);
568int enumerate_links(const char *file, char *name);
569void hide_console(void);
570
571int unc_root_len(const char *dir);
572int root_len(const char *path);
573const char *get_system_drive(void);
574const char *need_system_drive(const char *path);
575char *alloc_system_drive(const char *path);
576int chdir_system_drive(void);
577char *xabsolute_path(char *path);
578char *get_drive_cwd(const char *path, char *buffer, int size);
579void fix_path_case(char *path);
580void make_sparse(int fd, off_t start, off_t end);
581int skip_ansi_emulation(int reset);
582int unix_path(const char *path);
583int has_path(const char *file);
584int is_relative_path(const char *path);
585char *get_last_slash(const char *path);
diff --git a/include/platform.h b/include/platform.h
index ad27bb31a..3fb1a2dc8 100644
--- a/include/platform.h
+++ b/include/platform.h
@@ -7,6 +7,20 @@
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# else
14# undef __USE_MINGW_ANSI_STDIO
15# define __USE_MINGW_ANSI_STDIO 0
16# undef _WIN32_WINNT
17# define _WIN32_WINNT 0x502
18# endif
19#else
20# if defined(__MINGW32__)
21# error "You must select target platform MS Windows, or it won't build"
22# endif
23#endif
10 24
11/* Convenience macros to test the version of gcc. */ 25/* Convenience macros to test the version of gcc. */
12#undef __GNUC_PREREQ 26#undef __GNUC_PREREQ
@@ -135,7 +149,7 @@
135 149
136/* Make all declarations hidden (-fvisibility flag only affects definitions) */ 150/* Make all declarations hidden (-fvisibility flag only affects definitions) */
137/* (don't include system headers after this until corresponding pop!) */ 151/* (don't include system headers after this until corresponding pop!) */
138#if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__) 152#if __GNUC_PREREQ(4,1) && !defined(__CYGWIN__) && !ENABLE_PLATFORM_MINGW32
139# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)") 153# define PUSH_AND_SET_FUNCTION_VISIBILITY_TO_HIDDEN _Pragma("GCC visibility push(hidden)")
140# define POP_SAVED_FUNCTION_VISIBILITY _Pragma("GCC visibility pop") 154# define POP_SAVED_FUNCTION_VISIBILITY _Pragma("GCC visibility pop")
141#else 155#else
@@ -164,6 +178,13 @@
164# define bswap_64 __bswap64 178# define bswap_64 __bswap64
165# define bswap_32 __bswap32 179# define bswap_32 __bswap32
166# define bswap_16 __bswap16 180# define bswap_16 __bswap16
181#elif ENABLE_PLATFORM_MINGW32
182# define __BIG_ENDIAN 0
183# define __LITTLE_ENDIAN 1
184# define __BYTE_ORDER __LITTLE_ENDIAN
185# define bswap_16(x) ((((x) & 0xFF00) >> 8) | (((x) & 0xFF) << 8))
186# define bswap_32(x) ((bswap_16(((x) & 0xFFFF0000L) >> 16)) | (bswap_16((x) & 0xFFFFL) << 16))
187# define bswap_64(x) ((bswap_32(((x) & 0xFFFFFFFF00000000LL) >> 32)) | (bswap_32((x) & 0xFFFFFFFFLL) << 32))
167#else 188#else
168# include <byteswap.h> 189# include <byteswap.h>
169# include <endian.h> 190# include <endian.h>
@@ -442,6 +463,27 @@ typedef unsigned smalluint;
442# endif 463# endif
443#endif 464#endif
444 465
466#if ENABLE_PLATFORM_MINGW32
467# undef HAVE_FDATASYNC
468# undef HAVE_DPRINTF
469# undef HAVE_GETLINE
470# undef HAVE_MEMRCHR
471# undef HAVE_MKDTEMP
472# undef HAVE_SETBIT
473# undef HAVE_STPCPY
474# undef HAVE_STPNCPY
475# undef HAVE_STRCASESTR
476# undef HAVE_STRCHRNUL
477# undef HAVE_STRSEP
478# undef HAVE_STRVERSCMP
479#if !defined(__MINGW64_VERSION_MAJOR)
480# undef HAVE_VASPRINTF
481#endif
482# undef HAVE_UNLOCKED_STDIO
483# undef HAVE_UNLOCKED_LINE_OPS
484# undef HAVE_PRINTF_PERCENTM
485#endif
486
445#if defined(__WATCOMC__) 487#if defined(__WATCOMC__)
446# undef HAVE_DPRINTF 488# undef HAVE_DPRINTF
447# undef HAVE_GETLINE 489# undef HAVE_GETLINE
@@ -562,6 +604,7 @@ extern int dprintf(int fd, const char *format, ...);
562#endif 604#endif
563 605
564#ifndef HAVE_MEMRCHR 606#ifndef HAVE_MEMRCHR
607#include <stddef.h>
565extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC; 608extern void *memrchr(const void *s, int c, size_t n) FAST_FUNC;
566#endif 609#endif
567 610
@@ -625,6 +668,7 @@ extern int usleep(unsigned) FAST_FUNC;
625#endif 668#endif
626 669
627#ifndef HAVE_VASPRINTF 670#ifndef HAVE_VASPRINTF
671# include <stdarg.h>
628extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC; 672extern int vasprintf(char **string_ptr, const char *format, va_list p) FAST_FUNC;
629#endif 673#endif
630 674
diff --git a/libbb/Kbuild.src b/libbb/Kbuild.src
index 2fa239857..9b37b174d 100644
--- a/libbb/Kbuild.src
+++ b/libbb/Kbuild.src
@@ -12,14 +12,12 @@ 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 16lib-y += bb_do_delay.o
18lib-y += bb_pwd.o 17lib-y += bb_pwd.o
19lib-y += bb_qsort.o 18lib-y += bb_qsort.o
20#lib-y += bb_strtod.o 19#lib-y += bb_strtod.o
21lib-y += bb_strtonum.o 20lib-y += bb_strtonum.o
22lib-y += change_identity.o
23lib-y += chomp.o 21lib-y += chomp.o
24lib-y += compare_string_array.o 22lib-y += compare_string_array.o
25lib-y += concat_path_file.o 23lib-y += concat_path_file.o
@@ -29,32 +27,23 @@ lib-y += copy_file.o
29lib-y += copyfd.o 27lib-y += copyfd.o
30lib-y += crc32.o 28lib-y += crc32.o
31lib-y += default_error_retval.o 29lib-y += default_error_retval.o
32lib-y += device_open.o
33lib-y += dump.o 30lib-y += dump.o
34lib-y += executable.o 31lib-y += executable.o
35lib-y += fclose_nonstdin.o 32lib-y += fclose_nonstdin.o
36lib-y += fflush_stdout_and_exit.o 33lib-y += fflush_stdout_and_exit.o
37lib-y += fgets_str.o 34lib-y += fgets_str.o
38lib-y += find_pid_by_name.o 35lib-y += find_pid_by_name.o
39lib-y += find_root_device.o
40lib-y += full_write.o 36lib-y += full_write.o
41lib-y += get_console.o
42lib-y += get_last_path_component.o 37lib-y += get_last_path_component.o
43lib-y += get_line_from_file.o 38lib-y += get_line_from_file.o
44lib-y += getpty.o 39lib-y += getopt32.o
45lib-y += get_volsize.o
46lib-y += herror_msg.o 40lib-y += herror_msg.o
47lib-y += human_readable.o 41lib-y += human_readable.o
48lib-y += inet_common.o
49lib-y += inode_hash.o
50lib-y += isdirectory.o 42lib-y += isdirectory.o
51lib-y += kernel_version.o
52lib-y += last_char_is.o 43lib-y += last_char_is.o
53lib-y += lineedit.o lineedit_ptr_hack.o 44lib-y += lineedit.o lineedit_ptr_hack.o
54lib-y += llist.o 45lib-y += llist.o
55lib-y += login.o
56lib-y += make_directory.o 46lib-y += make_directory.o
57lib-y += makedev.o
58lib-y += hash_md5_sha.o 47lib-y += hash_md5_sha.o
59# Alternative (disabled) MD5 implementation 48# Alternative (disabled) MD5 implementation
60#lib-y += hash_md5prime.o 49#lib-y += hash_md5prime.o
@@ -62,20 +51,15 @@ lib-y += messages.o
62lib-y += mode_string.o 51lib-y += mode_string.o
63lib-y += parse_mode.o 52lib-y += parse_mode.o
64lib-y += perror_msg.o 53lib-y += perror_msg.o
65lib-y += perror_nomsg.o
66lib-y += perror_nomsg_and_die.o 54lib-y += perror_nomsg_and_die.o
67lib-y += pidfile.o
68lib-y += platform.o 55lib-y += platform.o
69lib-y += printable.o 56lib-y += printable.o
70lib-y += printable_string.o 57lib-y += printable_string.o
71lib-y += print_flags.o
72lib-y += process_escape_sequence.o 58lib-y += process_escape_sequence.o
73lib-y += procps.o 59lib-y += procps.o
74lib-y += progress.o
75lib-y += ptr_to_globals.o 60lib-y += ptr_to_globals.o
76lib-y += read.o 61lib-y += read.o
77lib-y += read_printf.o 62lib-y += read_printf.o
78lib-y += read_key.o
79lib-y += recursive_action.o 63lib-y += recursive_action.o
80lib-y += remove_file.o 64lib-y += remove_file.o
81lib-y += run_shell.o 65lib-y += run_shell.o
@@ -84,12 +68,9 @@ lib-y += safe_poll.o
84lib-y += safe_strncpy.o 68lib-y += safe_strncpy.o
85lib-y += safe_write.o 69lib-y += safe_write.o
86lib-y += securetty.o 70lib-y += securetty.o
87lib-y += setup_environment.o
88lib-y += signals.o
89lib-y += simplify_path.o 71lib-y += simplify_path.o
90lib-y += single_argv.o 72lib-y += single_argv.o
91lib-y += skip_whitespace.o 73lib-y += skip_whitespace.o
92lib-y += speed_table.o
93lib-y += str_tolower.o 74lib-y += str_tolower.o
94lib-y += strrstr.o 75lib-y += strrstr.o
95lib-y += sysconf.o 76lib-y += sysconf.o
@@ -102,17 +83,40 @@ lib-y += vfork_daemon_rexec.o
102lib-y += warn_ignoring_args.o 83lib-y += warn_ignoring_args.o
103lib-y += wfopen.o 84lib-y += wfopen.o
104lib-y += wfopen_input.o 85lib-y += wfopen_input.o
105lib-y += write.o
106lib-y += xatonum.o 86lib-y += xatonum.o
107lib-y += xconnect.o 87lib-y += xconnect.o
108lib-y += xfuncs.o 88lib-y += xfuncs.o
109lib-y += xfuncs_printf.o 89lib-y += xfuncs_printf.o
110lib-y += xfunc_die.o 90lib-y += xfunc_die.o
111lib-y += xgetcwd.o 91lib-y += xgetcwd.o
112lib-y += xgethostbyname.o
113lib-y += xreadlink.o 92lib-y += xreadlink.o
114lib-y += xrealloc_vector.o 93lib-y += xrealloc_vector.o
115 94
95lib-$(CONFIG_PLATFORM_POSIX) += bb_askpass.o
96lib-$(CONFIG_PLATFORM_POSIX) += change_identity.o
97lib-$(CONFIG_PLATFORM_POSIX) += device_open.o
98lib-$(CONFIG_PLATFORM_POSIX) += find_root_device.o
99lib-$(CONFIG_PLATFORM_POSIX) += get_console.o
100lib-$(CONFIG_PLATFORM_POSIX) += getpty.o
101lib-$(CONFIG_PLATFORM_POSIX) += get_volsize.o
102lib-$(CONFIG_PLATFORM_POSIX) += inet_common.o
103lib-$(CONFIG_PLATFORM_POSIX) += inode_hash.o
104lib-$(CONFIG_FEATURE_EXTRA_FILE_DATA) += inode_hash.o
105lib-$(CONFIG_PLATFORM_POSIX) += kernel_version.o
106lib-$(CONFIG_PLATFORM_POSIX) += login.o
107lib-$(CONFIG_PLATFORM_POSIX) += makedev.o
108lib-$(CONFIG_PLATFORM_POSIX) += perror_nomsg.o
109lib-$(CONFIG_PLATFORM_POSIX) += pidfile.o
110lib-$(CONFIG_PLATFORM_POSIX) += print_flags.o
111lib-$(CONFIG_PLATFORM_POSIX) += progress.o
112lib-$(CONFIG_PLATFORM_POSIX) += read_key.o
113lib-$(CONFIG_PLATFORM_POSIX) += setup_environment.o
114lib-$(CONFIG_PLATFORM_POSIX) += signals.o
115lib-$(CONFIG_PLATFORM_POSIX) += speed_table.o
116lib-$(CONFIG_PLATFORM_POSIX) += udp_io.o
117lib-$(CONFIG_PLATFORM_POSIX) += write.o
118lib-$(CONFIG_PLATFORM_POSIX) += xgethostbyname.o
119
116lib-$(CONFIG_MOUNT) += match_fstype.o 120lib-$(CONFIG_MOUNT) += match_fstype.o
117lib-$(CONFIG_UMOUNT) += match_fstype.o 121lib-$(CONFIG_UMOUNT) += match_fstype.o
118 122
@@ -125,7 +129,7 @@ lib-$(CONFIG_FEATURE_MTAB_SUPPORT) += mtab.o
125lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o 129lib-$(CONFIG_UNICODE_SUPPORT) += unicode.o
126lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o 130lib-$(CONFIG_FEATURE_CHECK_NAMES) += die_if_bad_username.o
127 131
128lib-$(CONFIG_NC) += udp_io.o 132lib-$(CONFIG_NC_110_COMPAT) += udp_io.o
129lib-$(CONFIG_NETCAT) += udp_io.o 133lib-$(CONFIG_NETCAT) += udp_io.o
130lib-$(CONFIG_DNSD) += udp_io.o 134lib-$(CONFIG_DNSD) += udp_io.o
131lib-$(CONFIG_NTPD) += udp_io.o 135lib-$(CONFIG_NTPD) += udp_io.o
diff --git a/libbb/appletlib.c b/libbb/appletlib.c
index 03389f541..6c0be4a83 100644
--- a/libbb/appletlib.c
+++ b/libbb/appletlib.c
@@ -53,6 +53,15 @@ static inline int *get_perrno(void) { return &errno; }
53# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__ 53# define IF_FEATURE_INDIVIDUAL(...) __VA_ARGS__
54#endif 54#endif
55 55
56#if (ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32) || \
57 (ENABLE_PLATFORM_MINGW32 && (ENABLE_FEATURE_PREFER_APPLETS \
58 || ENABLE_FEATURE_SH_STANDALONE \
59 || ENABLE_FEATURE_SH_NOFORK))
60# define IF_FULL_LIST_OPTION(...) __VA_ARGS__
61#else
62# define IF_FULL_LIST_OPTION(...)
63#endif
64
56#include "usage_compressed.h" 65#include "usage_compressed.h"
57 66
58#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS 67#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS
@@ -62,6 +71,7 @@ static inline int *get_perrno(void) { return &errno; }
62# define NUM_SCRIPTS 0 71# define NUM_SCRIPTS 0
63#endif 72#endif
64#if NUM_SCRIPTS > 0 73#if NUM_SCRIPTS > 0
74# define BB_ARCHIVE_PUBLIC
65# include "bb_archive.h" 75# include "bb_archive.h"
66static const char packed_scripts[] ALIGN1 = { PACKED_SCRIPTS }; 76static const char packed_scripts[] ALIGN1 = { PACKED_SCRIPTS };
67#endif 77#endif
@@ -111,6 +121,7 @@ static const char usage_messages[] ALIGN1 = UNPACKED_USAGE;
111#if ENABLE_FEATURE_COMPRESS_USAGE 121#if ENABLE_FEATURE_COMPRESS_USAGE
112 122
113static const char packed_usage[] ALIGN1 = { PACKED_USAGE }; 123static const char packed_usage[] ALIGN1 = { PACKED_USAGE };
124# define BB_ARCHIVE_PUBLIC
114# include "bb_archive.h" 125# include "bb_archive.h"
115# define unpack_usage_messages() \ 126# define unpack_usage_messages() \
116 unpack_bz2_data(packed_usage, sizeof(packed_usage), sizeof(UNPACKED_USAGE)) 127 unpack_bz2_data(packed_usage, sizeof(packed_usage), sizeof(UNPACKED_USAGE))
@@ -154,7 +165,11 @@ void FAST_FUNC bb_show_usage(void)
154 ap--; 165 ap--;
155 } 166 }
156 full_write2_str(bb_banner); 167 full_write2_str(bb_banner);
168#if ENABLE_PLATFORM_MINGW32
169 full_write2_str("\n");
170#else
157 full_write2_str(" multi-call binary.\n"); /* common string */ 171 full_write2_str(" multi-call binary.\n"); /* common string */
172#endif
158 if (*p == '\b') 173 if (*p == '\b')
159 full_write2_str("\nNo help available\n"); 174 full_write2_str("\nNo help available\n");
160 else { 175 else {
@@ -288,6 +303,11 @@ const char *applet_name;
288#if !BB_MMU 303#if !BB_MMU
289bool re_execed; 304bool re_execed;
290#endif 305#endif
306#if ENABLE_PLATFORM_MINGW32
307static int interp = 0;
308char bb_comm[COMM_LEN];
309char bb_command_line[128];
310#endif
291 311
292 312
293/* If not built as a single-applet executable... */ 313/* If not built as a single-applet executable... */
@@ -673,15 +693,36 @@ static void install_links(const char *busybox, int use_symbolic_links,
673 const char *appname = applet_names; 693 const char *appname = applet_names;
674 unsigned i; 694 unsigned i;
675 int rc; 695 int rc;
696# if ENABLE_PLATFORM_MINGW32
697 const char *sd = NULL;
698
699 if (custom_install_dir != NULL) {
700 bb_make_directory(custom_install_dir, 0755, FILEUTILS_RECUR);
701 }
702 else {
703 sd = get_system_drive();
704 for (i=1; i<ARRAY_SIZE(install_dir); ++i) {
705 fpc = xasprintf("%s%s", sd ?: "", install_dir[i]);
706 bb_make_directory(fpc, 0755, FILEUTILS_RECUR);
707 free(fpc);
708 }
709 }
710# endif
676 711
677 lf = link; 712 lf = link;
678 if (use_symbolic_links) 713 if (use_symbolic_links)
679 lf = symlink; 714 lf = symlink;
680 715
681 for (i = 0; i < ARRAY_SIZE(applet_main); i++) { 716 for (i = 0; i < ARRAY_SIZE(applet_main); i++) {
717# if ENABLE_PLATFORM_MINGW32
718 fpc = xasprintf("%s%s/%s.exe", sd ?: "",
719 custom_install_dir ?: install_dir[APPLET_INSTALL_LOC(i)],
720 appname);
721# else
682 fpc = concat_path_file( 722 fpc = concat_path_file(
683 custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)], 723 custom_install_dir ? custom_install_dir : install_dir[APPLET_INSTALL_LOC(i)],
684 appname); 724 appname);
725# endif
685 // debug: bb_error_msg("%slinking %s to busybox", 726 // debug: bb_error_msg("%slinking %s to busybox",
686 // use_symbolic_links ? "sym" : "", fpc); 727 // use_symbolic_links ? "sym" : "", fpc);
687 rc = lf(busybox, fpc); 728 rc = lf(busybox, fpc);
@@ -779,20 +820,35 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
779 820
780 dup2(1, 2); 821 dup2(1, 2);
781 full_write2_str(bb_banner); /* reuse const string */ 822 full_write2_str(bb_banner); /* reuse const string */
823#if ENABLE_PLATFORM_MINGW32
824 full_write2_str("\n");
825#else
782 full_write2_str(" multi-call binary.\n"); /* reuse */ 826 full_write2_str(" multi-call binary.\n"); /* reuse */
827#endif
828#if defined(MINGW_VER)
829 if (sizeof(MINGW_VER) > 5) {
830 full_write2_str(MINGW_VER "\n\n");
831 }
832#endif
783 full_write2_str( 833 full_write2_str(
784 "BusyBox is copyrighted by many authors between 1998-2015.\n" 834 "BusyBox is copyrighted by many authors between 1998-2021.\n"
785 "Licensed under GPLv2. See source distribution for detailed\n" 835 "Licensed under GPLv2. See source distribution for detailed\n"
786 "copyright notices.\n" 836 "copyright notices.\n"
787 "\n" 837 "\n"
788 "Usage: busybox [function [arguments]...]\n" 838 "Usage: busybox [function [arguments]...]\n"
789 " or: busybox --list"IF_FEATURE_INSTALLER("[-full]")"\n" 839 " or: busybox --list"IF_FULL_LIST_OPTION("[-full]")"\n"
790# if ENABLE_FEATURE_SHOW_SCRIPT && NUM_SCRIPTS > 0 840# if ENABLE_FEATURE_SHOW_SCRIPT && NUM_SCRIPTS > 0
791 " or: busybox --show SCRIPT\n" 841 " or: busybox --show SCRIPT\n"
792# endif 842# endif
793 IF_FEATURE_INSTALLER( 843 IF_FEATURE_INSTALLER(
844 IF_NOT_PLATFORM_MINGW32(
794 " or: busybox --install [-s] [DIR]\n" 845 " or: busybox --install [-s] [DIR]\n"
795 ) 846 )
847 IF_PLATFORM_MINGW32(
848 " or: busybox --install [-s] [-u|DIR]\n"
849 " or: busybox --uninstall [-n] file\n"
850 )
851 )
796 " or: function [arguments]...\n" 852 " or: function [arguments]...\n"
797 "\n" 853 "\n"
798 IF_NOT_FEATURE_SH_STANDALONE( 854 IF_NOT_FEATURE_SH_STANDALONE(
@@ -853,9 +909,28 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
853 const char *a = applet_names; 909 const char *a = applet_names;
854 dup2(1, 2); 910 dup2(1, 2);
855 while (*a) { 911 while (*a) {
856# if ENABLE_FEATURE_INSTALLER 912# if ENABLE_FEATURE_INSTALLER && !ENABLE_PLATFORM_MINGW32
857 if (argv[1][6]) /* --list-full? */ 913 if (argv[1][6]) /* --list-full? */
858 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1); 914 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
915# elif ENABLE_PLATFORM_MINGW32 && (ENABLE_FEATURE_PREFER_APPLETS \
916 || ENABLE_FEATURE_SH_STANDALONE \
917 || ENABLE_FEATURE_SH_NOFORK)
918 if (argv[1][6]) { /* --list-full? */
919 const char *str;
920
921 if (APPLET_IS_NOFORK(i))
922 str = "NOFORK ";
923 else if (APPLET_IS_NOEXEC(i))
924 str = "noexec ";
925# if NUM_SCRIPTS > 0
926 else if (applet_main[i] == scripted_main)
927 str = "script ";
928# endif
929 else
930 str = " ";
931 full_write2_str(str);
932 full_write2_str(install_dir[APPLET_INSTALL_LOC(i)] + 1);
933 }
859# endif 934# endif
860 full_write2_str(a); 935 full_write2_str(a);
861 full_write2_str("\n"); 936 full_write2_str("\n");
@@ -868,6 +943,7 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
868 943
869 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) { 944 if (ENABLE_FEATURE_INSTALLER && strcmp(argv[1], "--install") == 0) {
870 int use_symbolic_links; 945 int use_symbolic_links;
946#if !ENABLE_PLATFORM_MINGW32
871 const char *busybox; 947 const char *busybox;
872 948
873 busybox = xmalloc_readlink(bb_busybox_exec_path); 949 busybox = xmalloc_readlink(bb_busybox_exec_path);
@@ -887,9 +963,63 @@ int busybox_main(int argc UNUSED_PARAM, char **argv)
887 */ 963 */
888 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv); 964 use_symbolic_links = (argv[2] && strcmp(argv[2], "-s") == 0 && ++argv);
889 install_links(busybox, use_symbolic_links, argv[2]); 965 install_links(busybox, use_symbolic_links, argv[2]);
966#else
967 char *target;
968 uint32_t opt;
969 enum { OPT_s = (1 << 0), OPT_u = (1 << 1) };
970
971 /* busybox --install [-s] [-u|DIR]
972 * -s: make symlinks
973 * -u: install to Unix-style directories in system drive
974 * DIR: directory to install links to
975 * If no argument is provided put the links in the same directory
976 * as busybox.
977 */
978 argv += 1;
979 opt = getopt32(argv, "!su");
980 argv += optind;
981
982 if (opt == (uint32_t)-1 ||
983 (*argv != NULL && (opt & OPT_u || *(argv + 1) != NULL)))
984 bb_simple_error_msg_and_die("busybox --install [-s] [-u|DIR]");
985
986 if (opt & OPT_u)
987 target = NULL;
988 else if (*argv != NULL)
989 target = *argv;
990 else
991 target = dirname(xstrdup(bb_busybox_exec_path));
992
993 use_symbolic_links = opt & OPT_s;
994 /* NULL target -> install to Unix-style dirs */
995 install_links(bb_busybox_exec_path, use_symbolic_links, target);
996#endif
890 return 0; 997 return 0;
891 } 998 }
892 999
1000#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_INSTALLER
1001 if (strcmp(argv[1], "--uninstall") == 0) {
1002 char name[PATH_MAX];
1003 int dry_run = (argv[2] && strcmp(argv[2], "-n") == 0 && ++argv);
1004 const char *file = argv[2];
1005
1006 if (!argv[2])
1007 bb_error_msg_and_die(bb_msg_requires_arg, "--uninstall");
1008
1009 while (enumerate_links(file, name)) {
1010 if (dry_run) {
1011 full_write1_str(name);
1012 full_write1_str("\n");
1013 }
1014 else if (unlink(name) != 0) {
1015 bb_simple_perror_msg(name);
1016 }
1017 file = NULL;
1018 }
1019 return 0;
1020 }
1021#endif
1022
893 if (strcmp(argv[1], "--help") == 0) { 1023 if (strcmp(argv[1], "--help") == 0) {
894 /* "busybox --help [<applet>]" */ 1024 /* "busybox --help [<applet>]" */
895 if (!argv[2] 1025 if (!argv[2]
@@ -939,6 +1069,9 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv)
939# if defined APPLET_NO_echo 1069# if defined APPLET_NO_echo
940 && applet_no != APPLET_NO_echo 1070 && applet_no != APPLET_NO_echo
941# endif 1071# endif
1072# if ENABLE_PLATFORM_MINGW32 && defined APPLET_NO_busybox
1073 && applet_no != APPLET_NO_busybox
1074# endif
942 ) { 1075 ) {
943 if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) { 1076 if (argv[1] && !argv[2] && strcmp(argv[1], "--help") == 0) {
944 /* Make "foo --help" exit with 0: */ 1077 /* Make "foo --help" exit with 0: */
@@ -950,7 +1083,14 @@ void FAST_FUNC show_usage_if_dash_dash_help(int applet_no, char **argv)
950 1083
951void 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)
952{ 1085{
1086# if ENABLE_PLATFORM_MINGW32
1087 int argc = string_array_len(argv);
1088 int i;
1089 const char *vmask;
1090 unsigned int mask;
1091# else
953 int argc; 1092 int argc;
1093# endif
954 1094
955 /* 1095 /*
956 * We do not use argv[0]: do not want to repeat massaging of 1096 * We do not use argv[0]: do not want to repeat massaging of
@@ -963,7 +1103,23 @@ void FAST_FUNC run_applet_no_and_exit(int applet_no, const char *name, char **ar
963 if (ENABLE_FEATURE_SUID) 1103 if (ENABLE_FEATURE_SUID)
964 check_suid(applet_no); 1104 check_suid(applet_no);
965 1105
1106# if ENABLE_PLATFORM_MINGW32
1107 safe_strncpy(bb_comm,
1108 interp ? bb_basename(argv[interp]) : applet_name,
1109 sizeof(bb_comm));
1110
1111 safe_strncpy(bb_command_line, applet_name, sizeof(bb_command_line));
1112 for (i=1; i < argc && argv[i] &&
1113 strlen(bb_command_line) + strlen(argv[i]) + 2 < 128; ++i) {
1114 strcat(strcat(bb_command_line, " "), argv[i]);
1115 }
1116
1117 vmask = getenv("BB_UMASK");
1118 if (vmask && sscanf(vmask, "%o", &mask) == 1)
1119 umask((mode_t)(mask&0777));
1120# else
966 argc = string_array_len(argv); 1121 argc = string_array_len(argv);
1122# endif
967 xfunc_error_retval = applet_main[applet_no](argc, argv); 1123 xfunc_error_retval = applet_main[applet_no](argc, argv);
968 1124
969 /* Note: applet_main() may also not return (die on a xfunc or such) */ 1125 /* Note: applet_main() may also not return (die on a xfunc or such) */
@@ -1073,6 +1229,38 @@ int main(int argc UNUSED_PARAM, char **argv)
1073 argv[0][0] &= 0x7f; 1229 argv[0][0] &= 0x7f;
1074 } 1230 }
1075#endif 1231#endif
1232#if ENABLE_PLATFORM_MINGW32
1233 /* detect if we're running an interpreted script */
1234 if (argv[0][1] == ':' && argv[0][2] == '/') {
1235 switch (argv[0][0]) {
1236 case '2':
1237 ++interp;
1238 /* fall through */
1239 case '1':
1240 ++interp;
1241 argv[0] += 3;
1242 break;
1243 }
1244 }
1245# if ENABLE_FEATURE_EURO
1246 init_codepage();
1247# endif
1248 /* Ignore critical errors, such as calling GetVolumeInformation() on
1249 * a floppy or CDROM drive with no media. */
1250 SetErrorMode(SEM_FAILCRITICALERRORS);
1251#endif
1252
1253#if defined(__MINGW64_VERSION_MAJOR)
1254 if ( stdin ) {
1255 _setmode(fileno(stdin), _O_BINARY);
1256 }
1257 if ( stdout ) {
1258 _setmode(fileno(stdout), _O_BINARY);
1259 }
1260 if ( stderr ) {
1261 _setmode(fileno(stderr), _O_BINARY);
1262 }
1263#endif
1076 1264
1077#if defined(SINGLE_APPLET_MAIN) 1265#if defined(SINGLE_APPLET_MAIN)
1078 1266
@@ -1097,6 +1285,10 @@ int main(int argc UNUSED_PARAM, char **argv)
1097 1285
1098#else 1286#else
1099 1287
1288# if ENABLE_PLATFORM_MINGW32
1289 if (argv[1] && argv[2] && strcmp(argv[1], "--busybox") == 0)
1290 argv += 2;
1291# endif
1100 lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv)); 1292 lbb_prepare("busybox" IF_FEATURE_INDIVIDUAL(, argv));
1101# if !ENABLE_BUSYBOX 1293# if !ENABLE_BUSYBOX
1102 if (argv[1] && is_prefixed_with(bb_basename(argv[0]), "busybox")) 1294 if (argv[1] && is_prefixed_with(bb_basename(argv[0]), "busybox"))
@@ -1105,6 +1297,15 @@ int main(int argc UNUSED_PARAM, char **argv)
1105 applet_name = argv[0]; 1297 applet_name = argv[0];
1106 if (applet_name[0] == '-') 1298 if (applet_name[0] == '-')
1107 applet_name++; 1299 applet_name++;
1300# if ENABLE_PLATFORM_MINGW32
1301 str_tolower(argv[0]);
1302 bs_to_slash(argv[0]);
1303 if (has_exe_suffix_or_dot(argv[0])) {
1304 char *s = strrchr(argv[0], '.');
1305 if (s)
1306 *s = '\0';
1307 }
1308# endif
1108 applet_name = bb_basename(applet_name); 1309 applet_name = bb_basename(applet_name);
1109 1310
1110 /* If we are a result of execv("/proc/self/exe"), fix ugly comm of "exe" */ 1311 /* 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 d8cd033a3..70a4c29cf 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/concat_path_file.c b/libbb/concat_path_file.c
index 5b4b7f113..81c7481ca 100644
--- a/libbb/concat_path_file.c
+++ b/libbb/concat_path_file.c
@@ -21,8 +21,14 @@ char* FAST_FUNC concat_path_file(const char *path, const char *filename)
21 21
22 if (!path) 22 if (!path)
23 path = ""; 23 path = "";
24#if ENABLE_PLATFORM_MINGW32
25 lc = last_char_is(path, '/') ?: last_char_is(path, '\\');
26 while (*filename == '/' || *filename == '\\')
27 filename++;
28#else
24 lc = last_char_is(path, '/'); 29 lc = last_char_is(path, '/');
25 while (*filename == '/') 30 while (*filename == '/')
26 filename++; 31 filename++;
32#endif
27 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename); 33 return xasprintf("%s%s%s", path, (lc==NULL ? "/" : ""), filename);
28} 34}
diff --git a/libbb/copy_file.c b/libbb/copy_file.c
index 044bc3c20..c0928a5a8 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 if (flags & FILEUTILS_NO_OVERWRITE) /* cp -n */ 120 if (flags & FILEUTILS_NO_OVERWRITE) /* cp -n */
115 return 0; 121 return 0;
116 dest_exists = 1; 122 dest_exists = 1;
diff --git a/libbb/dump.c b/libbb/dump.c
index fcdee8343..d24057325 100644
--- a/libbb/dump.c
+++ b/libbb/dump.c
@@ -36,6 +36,9 @@ typedef struct priv_dumper_t {
36 off_t eaddress; /* end address */ 36 off_t eaddress; /* end address */
37 int blocksize; 37 int blocksize;
38 smallint exitval; /* final exit value */ 38 smallint exitval; /* final exit value */
39#if ENABLE_PLATFORM_MINGW32
40 FILE *fd;
41#endif
39 42
40 /* former statics */ 43 /* former statics */
41 smallint next__done; 44 smallint next__done;
@@ -57,6 +60,9 @@ dumper_t* FAST_FUNC alloc_dumper(void)
57 dumper->pub.dump_length = -1; 60 dumper->pub.dump_length = -1;
58 dumper->pub.dump_vflag = FIRST; 61 dumper->pub.dump_vflag = FIRST;
59 dumper->get__ateof = 1; 62 dumper->get__ateof = 1;
63#if ENABLE_PLATFORM_MINGW32
64 dumper->fd = stdin;
65#endif
60 return &dumper->pub; 66 return &dumper->pub;
61} 67}
62 68
@@ -328,7 +334,11 @@ static void do_skip(priv_dumper_t *dumper, const char *fname)
328{ 334{
329 struct stat sbuf; 335 struct stat sbuf;
330 336
337#if ENABLE_PLATFORM_MINGW32
338 xfstat(fileno(dumper->fd), &sbuf, fname);
339#else
331 xfstat(STDIN_FILENO, &sbuf, fname); 340 xfstat(STDIN_FILENO, &sbuf, fname);
341#endif
332 if (S_ISREG(sbuf.st_mode) 342 if (S_ISREG(sbuf.st_mode)
333 && dumper->pub.dump_skip >= sbuf.st_size 343 && dumper->pub.dump_skip >= sbuf.st_size
334 ) { 344 ) {
@@ -337,7 +347,11 @@ static void do_skip(priv_dumper_t *dumper, const char *fname)
337 dumper->pub.address += sbuf.st_size; 347 dumper->pub.address += sbuf.st_size;
338 return; 348 return;
339 } 349 }
350#if ENABLE_PLATFORM_MINGW32
351 if (fseeko(dumper->fd, dumper->pub.dump_skip, SEEK_SET)) {
352#else
340 if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) { 353 if (fseeko(stdin, dumper->pub.dump_skip, SEEK_SET)) {
354#endif
341 bb_simple_perror_msg_and_die(fname); 355 bb_simple_perror_msg_and_die(fname);
342 } 356 }
343 dumper->pub.address += dumper->pub.dump_skip; 357 dumper->pub.address += dumper->pub.dump_skip;
@@ -353,13 +367,23 @@ static NOINLINE int next(priv_dumper_t *dumper)
353 if (fname) { 367 if (fname) {
354 dumper->argv++; 368 dumper->argv++;
355 if (NOT_LONE_DASH(fname)) { 369 if (NOT_LONE_DASH(fname)) {
370#if ENABLE_PLATFORM_MINGW32
371 dumper->fd = fopen(fname, "r");
372 if (!dumper->fd) {
373#else
356 if (!freopen(fname, "r", stdin)) { 374 if (!freopen(fname, "r", stdin)) {
375#endif
357 bb_simple_perror_msg(fname); 376 bb_simple_perror_msg(fname);
358 dumper->exitval = 1; 377 dumper->exitval = 1;
359 dumper->next__done = 1; 378 dumper->next__done = 1;
360 continue; 379 continue;
361 } 380 }
362 } 381 }
382#if ENABLE_PLATFORM_MINGW32
383 else {
384 dumper->fd = stdin;
385 }
386#endif
363 } else { 387 } else {
364 if (dumper->next__done) 388 if (dumper->next__done)
365 return 0; /* no next file */ 389 return 0; /* no next file */
@@ -414,13 +438,25 @@ static unsigned char *get(priv_dumper_t *dumper)
414 dumper->eaddress = dumper->pub.address + nread; 438 dumper->eaddress = dumper->pub.address + nread;
415 return dumper->get__curp; 439 return dumper->get__curp;
416 } 440 }
441#if ENABLE_PLATFORM_MINGW32
442 n = fread(dumper->get__curp + nread, sizeof(unsigned char),
443 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), dumper->fd);
444#else
417 n = fread(dumper->get__curp + nread, sizeof(unsigned char), 445 n = fread(dumper->get__curp + nread, sizeof(unsigned char),
418 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin); 446 dumper->pub.dump_length == -1 ? need : MIN(dumper->pub.dump_length, need), stdin);
447#endif
419 if (n == 0) { 448 if (n == 0) {
449#if ENABLE_PLATFORM_MINGW32
450 if (ferror(dumper->fd)) {
451#else
420 if (ferror(stdin)) { 452 if (ferror(stdin)) {
453#endif
421 bb_simple_perror_msg(dumper->argv[-1]); 454 bb_simple_perror_msg(dumper->argv[-1]);
422 } 455 }
423 dumper->get__ateof = 1; 456 dumper->get__ateof = 1;
457#if ENABLE_PLATFORM_MINGW32
458 fclose(dumper->fd);
459#endif
424 continue; 460 continue;
425 } 461 }
426 dumper->get__ateof = 0; 462 dumper->get__ateof = 0;
diff --git a/libbb/executable.c b/libbb/executable.c
index a033b74d9..32b37f29d 100644
--- a/libbb/executable.c
+++ b/libbb/executable.c
@@ -15,7 +15,12 @@
15int FAST_FUNC file_is_executable(const char *name) 15int FAST_FUNC file_is_executable(const char *name)
16{ 16{
17 struct stat s; 17 struct stat s;
18#if !ENABLE_PLATFORM_MINGW32
18 return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode)); 19 return (!access(name, X_OK) && !stat(name, &s) && S_ISREG(s.st_mode));
20#else
21 /* expand WIN32 implementation of access(2) */
22 return (!stat(name, &s) && S_ISREG(s.st_mode) && (s.st_mode & S_IXUSR));
23#endif
19} 24}
20 25
21/* search (*PATHp) for an executable file; 26/* search (*PATHp) for an executable file;
@@ -44,14 +49,23 @@ char* FAST_FUNC find_executable(const char *filename, char **PATHp)
44 while (p) { 49 while (p) {
45 int ex; 50 int ex;
46 51
47 n = strchr(p, ':'); 52 n = strchr(p, PATH_SEP);
48 if (n) *n = '\0'; 53 if (n) *n = '\0';
49 p = concat_path_file( 54 p = concat_path_file(
50 p[0] ? p : ".", /* handle "::" case */ 55 p[0] ? p : ".", /* handle "::" case */
51 filename 56 filename
52 ); 57 );
58#if ENABLE_PLATFORM_MINGW32
59 {
60 char *w = alloc_system_drive(p);
61 ex = add_win32_extension(w) || file_is_executable(w);
62 free(p);
63 p = w;
64 }
65#else
53 ex = file_is_executable(p); 66 ex = file_is_executable(p);
54 if (n) *n++ = ':'; 67#endif
68 if (n) *n++ = PATH_SEP;
55 if (ex) { 69 if (ex) {
56 *PATHp = n; 70 *PATHp = n;
57 return p; 71 return p;
diff --git a/libbb/find_mount_point.c b/libbb/find_mount_point.c
index 0e1be3820..cf06c6e18 100644
--- a/libbb/find_mount_point.c
+++ b/libbb/find_mount_point.c
@@ -6,6 +6,9 @@
6 * 6 *
7 * Licensed under GPLv2 or later, see file LICENSE in this source tree. 7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
8 */ 8 */
9#if ENABLE_PLATFORM_MINGW32
10# define MNTENT_PRIVATE
11#endif
9#include "libbb.h" 12#include "libbb.h"
10#include <mntent.h> 13#include <mntent.h>
11 14
@@ -19,14 +22,22 @@
19struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too) 22struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
20{ 23{
21 struct stat s; 24 struct stat s;
25#if !ENABLE_PLATFORM_MINGW32
22 FILE *mtab_fp; 26 FILE *mtab_fp;
23 struct mntent *mountEntry; 27 struct mntent *mountEntry;
24 dev_t devno_of_name; 28 dev_t devno_of_name;
25 bool block_dev; 29 bool block_dev;
30#else
31 struct mntent *mountEntry;
32 static struct mntdata *data = NULL;
33 char *current;
34 const char *path;
35#endif
26 36
27 if (stat(name, &s) != 0) 37 if (stat(name, &s) != 0)
28 return NULL; 38 return NULL;
29 39
40#if !ENABLE_PLATFORM_MINGW32
30 devno_of_name = s.st_dev; 41 devno_of_name = s.st_dev;
31 block_dev = 0; 42 block_dev = 0;
32 /* Why S_ISCHR? - UBI volumes use char devices, not block */ 43 /* Why S_ISCHR? - UBI volumes use char devices, not block */
@@ -74,6 +85,26 @@ struct mntent* FAST_FUNC find_mount_point(const char *name, int subdir_too)
74 } 85 }
75 } 86 }
76 endmntent(mtab_fp); 87 endmntent(mtab_fp);
88#else
89 mountEntry = NULL;
90 path = NULL;
91 current = NULL;
92
93 if ( isalpha(name[0]) && name[1] == ':' ) {
94 path = name;
95 } else {
96 path = current = xrealloc_getcwd_or_warn(NULL);
97 }
98
99 if ( path && isalpha(path[0]) && path[1] == ':' ) {
100 if (data == NULL)
101 data = xmalloc(sizeof(*data));
102
103 fill_mntdata(data, toupper(path[0]) - 'A');
104 mountEntry = &data->me;
105 }
106 free(current);
107#endif
77 108
78 return mountEntry; 109 return mountEntry;
79} 110}
diff --git a/libbb/find_pid_by_name.c b/libbb/find_pid_by_name.c
index fe13f7211..0b6d5a206 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,6 +91,7 @@ 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 */
@@ -95,6 +100,7 @@ pid_t* FAST_FUNC find_pid_by_name(const char *procName)
95 : bb_basename(p->exe), 100 : bb_basename(p->exe),
96 procName 101 procName
97 ) == 0) 102 ) == 0)
103#endif
98 ) { 104 ) {
99 pidList = xrealloc_vector(pidList, 2, i); 105 pidList = xrealloc_vector(pidList, 2, i);
100 pidList[i++] = p->pid; 106 pidList[i++] = p->pid;
diff --git a/libbb/get_last_path_component.c b/libbb/get_last_path_component.c
index 04fdf2a3e..254aabafd 100644
--- a/libbb/get_last_path_component.c
+++ b/libbb/get_last_path_component.c
@@ -10,12 +10,34 @@
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 == '\\' || *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#if ENABLE_PLATFORM_MINGW32
27char *get_last_slash(const char *path)
28{
29 const char *start = path + root_len(path);
30 char *slash = strrchr(start, '/');
31 char *bslash = strrchr(start, '\\');
32
33 if (slash && bslash)
34 slash = MAX(slash, bslash);
35 else if (!slash)
36 slash = bslash;
37 return slash;
38}
39#endif
40
19/* 41/*
20 * "/" -> "/" 42 * "/" -> "/"
21 * "abc" -> "abc" 43 * "abc" -> "abc"
@@ -24,10 +46,20 @@ const char* FAST_FUNC bb_basename(const char *name)
24 */ 46 */
25char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path) 47char* FAST_FUNC bb_get_last_path_component_nostrip(const char *path)
26{ 48{
49#if ENABLE_PLATFORM_MINGW32
50 const char *start = path + root_len(path);
51 char *slash = get_last_slash(path);
52
53 if (!slash && has_dos_drive_prefix(path) && path[2] != '\0')
54 return (char *)path + 2;
55 if (!slash || (slash == start && !slash[1]))
56 return (char*)path;
57#else
27 char *slash = strrchr(path, '/'); 58 char *slash = strrchr(path, '/');
28 59
29 if (!slash || (slash == path && !slash[1])) 60 if (!slash || (slash == path && !slash[1]))
30 return (char*)path; 61 return (char*)path;
62#endif
31 63
32 return slash + 1; 64 return slash + 1;
33} 65}
@@ -42,9 +74,20 @@ char* FAST_FUNC bb_get_last_path_component_strip(char *path)
42{ 74{
43 char *slash = last_char_is(path, '/'); 75 char *slash = last_char_is(path, '/');
44 76
77#if ENABLE_PLATFORM_MINGW32
78 const char *start = has_dos_drive_prefix(path) ? path+2 : path;
79
80 if (!slash)
81 slash = last_char_is(path, '\\');
82
83 if (slash)
84 while ((*slash == '/' || *slash == '\\') && slash != start)
85 *slash-- = '\0';
86#else
45 if (slash) 87 if (slash)
46 while (*slash == '/' && slash != path) 88 while (*slash == '/' && slash != path)
47 *slash-- = '\0'; 89 *slash-- = '\0';
90#endif
48 91
49 return bb_get_last_path_component_nostrip(path); 92 return bb_get_last_path_component_nostrip(path);
50} 93}
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 e14c78707..8abc87976 100644
--- a/libbb/lineedit.c
+++ b/libbb/lineedit.c
@@ -401,7 +401,7 @@ int adjust_width_and_validate_wc(unsigned *width_adj, int wc);
401/* Put 'command_ps[cursor]', cursor++. 401/* Put 'command_ps[cursor]', cursor++.
402 * Advance cursor on screen. If we reached right margin, scroll text up 402 * Advance cursor on screen. If we reached right margin, scroll text up
403 * and remove terminal margin effect by printing 'next_char' */ 403 * and remove terminal margin effect by printing 'next_char' */
404#define HACK_FOR_WRONG_WIDTH 1 404#define HACK_FOR_WRONG_WIDTH 1 && !ENABLE_PLATFORM_MINGW32
405static void put_cur_glyph_and_inc_cursor(void) 405static void put_cur_glyph_and_inc_cursor(void)
406{ 406{
407 CHAR_T c = command_ps[cursor]; 407 CHAR_T c = command_ps[cursor];
@@ -464,6 +464,42 @@ static void put_cur_glyph_and_inc_cursor(void)
464 } 464 }
465} 465}
466 466
467#if ENABLE_PLATFORM_MINGW32
468static void inc_cursor(void)
469{
470 CHAR_T c = command_ps[cursor];
471 unsigned width = 0;
472 int ofs_to_right;
473
474 /* advance cursor */
475 cursor++;
476 if (unicode_status == UNICODE_ON) {
477 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x;)
478 c = adjust_width_and_validate_wc(&cmdedit_x, c);
479 IF_UNICODE_WIDE_WCHARS(width = cmdedit_x - width;)
480 } else {
481 cmdedit_x++;
482 }
483
484 ofs_to_right = cmdedit_x - cmdedit_termw;
485 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right <= 0) {
486 /* cursor remains on this line */
487 printf(ESC"[1C");
488 }
489
490 if (ofs_to_right >= 0) {
491 /* we go to the next line */
492 printf(ESC"[1B");
493 bb_putchar('\r');
494 cmdedit_y++;
495 if (!ENABLE_UNICODE_WIDE_WCHARS || ofs_to_right == 0) {
496 width = 0;
497 }
498 cmdedit_x = width;
499 }
500}
501#endif
502
467/* Move to end of line (by printing all chars till the end) */ 503/* Move to end of line (by printing all chars till the end) */
468static void put_till_end_and_adv_cursor(void) 504static void put_till_end_and_adv_cursor(void)
469{ 505{
@@ -523,6 +559,7 @@ static void input_backward(unsigned num)
523 559
524 if (cmdedit_x >= num) { 560 if (cmdedit_x >= num) {
525 cmdedit_x -= num; 561 cmdedit_x -= num;
562#if !ENABLE_PLATFORM_MINGW32
526 if (num <= 4) { 563 if (num <= 4) {
527 /* This is longer by 5 bytes on x86. 564 /* This is longer by 5 bytes on x86.
528 * Also gets miscompiled for ARM users 565 * Also gets miscompiled for ARM users
@@ -535,6 +572,7 @@ static void input_backward(unsigned num)
535 } while (--num); 572 } while (--num);
536 return; 573 return;
537 } 574 }
575#endif
538 printf(ESC"[%uD", num); 576 printf(ESC"[%uD", num);
539 return; 577 return;
540 } 578 }
@@ -673,7 +711,11 @@ static void input_backspace(void)
673static void input_forward(void) 711static void input_forward(void)
674{ 712{
675 if (cursor < command_len) 713 if (cursor < command_len)
714#if !ENABLE_PLATFORM_MINGW32
676 put_cur_glyph_and_inc_cursor(); 715 put_cur_glyph_and_inc_cursor();
716#else
717 inc_cursor();
718#endif
677} 719}
678 720
679#if ENABLE_FEATURE_TAB_COMPLETION 721#if ENABLE_FEATURE_TAB_COMPLETION
@@ -684,6 +726,14 @@ static void input_forward(void)
684//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>, 726//Also, perhaps "foo b<TAB> needs to complete to "foo bar" <cursor>,
685//not "foo bar <cursor>... 727//not "foo bar <cursor>...
686 728
729# if ENABLE_PLATFORM_MINGW32
730/* use case-insensitive comparisons for filenames */
731# define is_prefixed_with(s, k) is_prefixed_with_case(s, k)
732# define qsort_string_vector(s, c) qsort_string_vector_case(s, c)
733# define strcmp(s, t) strcasecmp(s, t)
734# define strncmp(s, t, n) strncasecmp(s, t, n)
735# endif
736
687static void free_tab_completion_data(void) 737static void free_tab_completion_data(void)
688{ 738{
689 if (matches) { 739 if (matches) {
@@ -700,8 +750,12 @@ static void add_match(char *matched)
700 while (*p) { 750 while (*p) {
701 /* ESC attack fix: drop any string with control chars */ 751 /* ESC attack fix: drop any string with control chars */
702 if (*p < ' ' 752 if (*p < ' '
753# if !ENABLE_PLATFORM_MINGW32
703 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f) 754 || (!ENABLE_UNICODE_SUPPORT && *p >= 0x7f)
704 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f) 755 || (ENABLE_UNICODE_SUPPORT && *p == 0x7f)
756# else
757 || *p == 0x7f
758# endif
705 ) { 759 ) {
706 free(matched); 760 free(matched);
707 return; 761 return;
@@ -720,13 +774,16 @@ static void add_match(char *matched)
720 */ 774 */
721static char *username_path_completion(char *ud) 775static char *username_path_completion(char *ud)
722{ 776{
777# if !ENABLE_PLATFORM_MINGW32
723 struct passwd *entry; 778 struct passwd *entry;
779#endif
724 char *tilde_name = ud; 780 char *tilde_name = ud;
725 const char *home = NULL; 781 const char *home = NULL;
726 782
727 ud++; /* skip ~ */ 783 ud++; /* skip ~ */
728 if (*ud == '/') { /* "~/..." */ 784 if (*ud == '/') { /* "~/..." */
729 home = get_homedir_or_NULL(); 785 home = get_homedir_or_NULL();
786# if !ENABLE_PLATFORM_MINGW32
730 } else { 787 } else {
731 /* "~user/..." */ 788 /* "~user/..." */
732 ud = strchr(ud, '/'); 789 ud = strchr(ud, '/');
@@ -735,6 +792,7 @@ static char *username_path_completion(char *ud)
735 *ud = '/'; /* restore "~user/..." */ 792 *ud = '/'; /* restore "~user/..." */
736 if (entry) 793 if (entry)
737 home = entry->pw_dir; 794 home = entry->pw_dir;
795# endif
738 } 796 }
739 if (home) { 797 if (home) {
740 ud = concat_path_file(home, ud); 798 ud = concat_path_file(home, ud);
@@ -744,6 +802,7 @@ static char *username_path_completion(char *ud)
744 return tilde_name; 802 return tilde_name;
745} 803}
746 804
805# if !ENABLE_PLATFORM_MINGW32
747/* ~use<tab> - find all users with this prefix. 806/* ~use<tab> - find all users with this prefix.
748 * Return the length of the prefix used for matching. 807 * Return the length of the prefix used for matching.
749 */ 808 */
@@ -766,6 +825,7 @@ static NOINLINE unsigned complete_username(const char *ud)
766 825
767 return 1 + userlen; 826 return 1 + userlen;
768} 827}
828# endif
769# endif /* FEATURE_USERNAME_COMPLETION */ 829# endif /* FEATURE_USERNAME_COMPLETION */
770 830
771enum { 831enum {
@@ -795,7 +855,7 @@ static unsigned path_parse(char ***p)
795 tmp = (char*)pth; 855 tmp = (char*)pth;
796 npth = 1; /* path component count */ 856 npth = 1; /* path component count */
797 while (1) { 857 while (1) {
798 tmp = strchr(tmp, ':'); 858 tmp = strchr(tmp, PATH_SEP);
799 if (!tmp) 859 if (!tmp)
800 break; 860 break;
801 tmp++; 861 tmp++;
@@ -806,7 +866,7 @@ static unsigned path_parse(char ***p)
806 res[0] = tmp = xstrdup(pth); 866 res[0] = tmp = xstrdup(pth);
807 npth = 1; 867 npth = 1;
808 while (1) { 868 while (1) {
809 tmp = strchr(tmp, ':'); 869 tmp = strchr(tmp, PATH_SEP);
810 if (!tmp) 870 if (!tmp)
811 break; 871 break;
812 *tmp++ = '\0'; /* ':' -> '\0' */ 872 *tmp++ = '\0'; /* ':' -> '\0' */
@@ -834,6 +894,17 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
834 path1[0] = (char*)"."; 894 path1[0] = (char*)".";
835 895
836 basecmd = strrchr(command, '/'); 896 basecmd = strrchr(command, '/');
897#if ENABLE_PLATFORM_MINGW32
898 if (!basecmd && has_dos_drive_prefix(command) && command[2] != '\0') {
899 char buffer[PATH_MAX];
900
901 /* path is of form c:path with no '/' */
902 if (get_drive_cwd(command, buffer, PATH_MAX)) {
903 basecmd = command + 2;
904 path1[0] = dirbuf = xstrdup(buffer);
905 }
906 } else
907#endif
837 if (!basecmd) { 908 if (!basecmd) {
838 if (type == FIND_EXE_ONLY) 909 if (type == FIND_EXE_ONLY)
839 npaths = path_parse(&paths); 910 npaths = path_parse(&paths);
@@ -891,6 +962,9 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
891 } 962 }
892 963
893 lpath = *paths[i] ? paths[i] : "."; 964 lpath = *paths[i] ? paths[i] : ".";
965#if ENABLE_PLATFORM_MINGW32
966 lpath = auto_string(alloc_system_drive(lpath));
967#endif
894 dir = opendir(lpath); 968 dir = opendir(lpath);
895 if (!dir) 969 if (!dir)
896 continue; /* don't print an error */ 970 continue; /* don't print an error */
@@ -913,6 +987,12 @@ static NOINLINE unsigned complete_cmd_dir_file(const char *command, int type)
913 if (stat(found, &st) && lstat(found, &st)) 987 if (stat(found, &st) && lstat(found, &st))
914 goto cont; /* hmm, remove in progress? */ 988 goto cont; /* hmm, remove in progress? */
915 989
990# if ENABLE_PLATFORM_MINGW32
991 if (type == FIND_EXE_ONLY && S_ISREG(st.st_mode) &&
992 !(st.st_mode & S_IXUSR))
993 goto cont;
994# endif
995
916 /* Save only name */ 996 /* Save only name */
917 len = strlen(name_found); 997 len = strlen(name_found);
918 found = xrealloc(found, len + 2); /* +2: for slash and NUL */ 998 found = xrealloc(found, len + 2); /* +2: for slash and NUL */
@@ -996,7 +1076,13 @@ static NOINLINE int build_match_prefix(char *match_buf)
996 1076
997 /* Mark every \c as "quoted c" */ 1077 /* Mark every \c as "quoted c" */
998 for (i = 0; int_buf[i]; i++) { 1078 for (i = 0; int_buf[i]; i++) {
1079#if ENABLE_PLATFORM_MINGW32
1080 /* Trailing backslash is effectively removed which confuses
1081 * the code to display case-preserved filenames. */
1082 if (int_buf[i] == '\\' && int_buf[i+1] != '\0') {
1083#else
999 if (int_buf[i] == '\\') { 1084 if (int_buf[i] == '\\') {
1085#endif
1000 remove_chunk(int_buf, i, i + 1); 1086 remove_chunk(int_buf, i, i + 1);
1001 int_buf[i] |= QUOT; 1087 int_buf[i] |= QUOT;
1002 } 1088 }
@@ -1197,6 +1283,11 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1197 size_t len_found; 1283 size_t len_found;
1198 /* Length of string used for matching */ 1284 /* Length of string used for matching */
1199 unsigned match_pfx_len = match_pfx_len; 1285 unsigned match_pfx_len = match_pfx_len;
1286#if ENABLE_PLATFORM_MINGW32
1287 unsigned orig_pfx_len;
1288 char *target;
1289 const char *source;
1290#endif
1200 int find_type; 1291 int find_type;
1201# if ENABLE_UNICODE_SUPPORT 1292# if ENABLE_UNICODE_SUPPORT
1202 /* cursor pos in command converted to multibyte form */ 1293 /* cursor pos in command converted to multibyte form */
@@ -1244,7 +1335,7 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1244 /* Free up any memory already allocated */ 1335 /* Free up any memory already allocated */
1245 free_tab_completion_data(); 1336 free_tab_completion_data();
1246 1337
1247# if ENABLE_FEATURE_USERNAME_COMPLETION 1338# if ENABLE_FEATURE_USERNAME_COMPLETION && !ENABLE_PLATFORM_MINGW32
1248 /* If the word starts with ~ and there is no slash in the word, 1339 /* If the word starts with ~ and there is no slash in the word,
1249 * then try completing this word as a username. */ 1340 * then try completing this word as a username. */
1250 if (state->flags & USERNAME_COMPLETION) 1341 if (state->flags & USERNAME_COMPLETION)
@@ -1261,6 +1352,9 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1261 { 1352 {
1262 const char *e = match_buf + strlen(match_buf); 1353 const char *e = match_buf + strlen(match_buf);
1263 const char *s = e - match_pfx_len; 1354 const char *s = e - match_pfx_len;
1355#if ENABLE_PLATFORM_MINGW32
1356 orig_pfx_len = match_pfx_len;
1357#endif
1264 while (s < e) 1358 while (s < e)
1265 if (is_special_char(*s++)) 1359 if (is_special_char(*s++))
1266 match_pfx_len++; 1360 match_pfx_len++;
@@ -1295,7 +1389,11 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1295 for (cp = chosen_match; *cp; cp++) { 1389 for (cp = chosen_match; *cp; cp++) {
1296 unsigned n; 1390 unsigned n;
1297 for (n = 1; n < num_matches; n++) { 1391 for (n = 1; n < num_matches; n++) {
1392# if !ENABLE_PLATFORM_MINGW32
1298 if (matches[n][cp - chosen_match] != *cp) { 1393 if (matches[n][cp - chosen_match] != *cp) {
1394# else
1395 if (tolower(matches[n][cp - chosen_match]) != tolower(*cp)) {
1396# endif
1299 goto stop; 1397 goto stop;
1300 } 1398 }
1301 } 1399 }
@@ -1331,7 +1429,21 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1331 /* save tail */ 1429 /* save tail */
1332 strcpy(match_buf, &command_ps[cursor]); 1430 strcpy(match_buf, &command_ps[cursor]);
1333 /* add match and tail */ 1431 /* add match and tail */
1432# if ENABLE_PLATFORM_MINGW32
1433 if (match_pfx_len == orig_pfx_len) {
1434 /* replace match prefix to allow for altered case */
1435 target = &command_ps[cursor-match_pfx_len];
1436 source = chosen_match;
1437 }
1438 else {
1439 /* only replace tail of match if special characters are quoted */
1440 target = &command_ps[cursor];
1441 source = chosen_match + match_pfx_len;
1442 }
1443 strcpy(stpcpy(target, source), match_buf);
1444# else
1334 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf); 1445 sprintf(&command_ps[cursor], "%s%s", chosen_match + match_pfx_len, match_buf);
1446# endif
1335 command_len = strlen(command_ps); 1447 command_len = strlen(command_ps);
1336 /* new pos */ 1448 /* new pos */
1337 pos = cursor + len_found - match_pfx_len; 1449 pos = cursor + len_found - match_pfx_len;
@@ -1368,6 +1480,13 @@ static NOINLINE void input_tab(smallint *lastWasTab)
1368 free(match_buf); 1480 free(match_buf);
1369} 1481}
1370 1482
1483# if ENABLE_PLATFORM_MINGW32
1484# undef is_prefixed_with
1485# undef qsort_string_vector
1486# undef strcmp
1487# undef strncmp
1488# endif
1489
1371#endif /* FEATURE_TAB_COMPLETION */ 1490#endif /* FEATURE_TAB_COMPLETION */
1372 1491
1373 1492
@@ -2414,7 +2533,7 @@ static void sigaction2(int sig, struct sigaction *act)
2414 */ 2533 */
2415int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize) 2534int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *command, int maxsize)
2416{ 2535{
2417 int len, n; 2536 int len IF_NOT_PLATFORM_MINGW32(, n);
2418 int timeout; 2537 int timeout;
2419#if ENABLE_FEATURE_TAB_COMPLETION 2538#if ENABLE_FEATURE_TAB_COMPLETION
2420 smallint lastWasTab = 0; 2539 smallint lastWasTab = 0;
@@ -2424,7 +2543,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2424 smallint vi_cmdmode = 0; 2543 smallint vi_cmdmode = 0;
2425#endif 2544#endif
2426 struct termios initial_settings; 2545 struct termios initial_settings;
2546#if !ENABLE_PLATFORM_MINGW32
2427 struct termios new_settings; 2547 struct termios new_settings;
2548#endif
2428 char read_key_buffer[KEYCODE_BUFFER_SIZE]; 2549 char read_key_buffer[KEYCODE_BUFFER_SIZE];
2429 2550
2430 INIT_S(); 2551 INIT_S();
@@ -2433,10 +2554,16 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2433 cmdedit_termw = 80; 2554 cmdedit_termw = 80;
2434 IF_FEATURE_EDITING_VI(delptr = delbuf;) 2555 IF_FEATURE_EDITING_VI(delptr = delbuf;)
2435 2556
2557#if !ENABLE_PLATFORM_MINGW32
2436 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0 2558 n = get_termios_and_make_raw(STDIN_FILENO, &new_settings, &initial_settings, 0
2437 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */ 2559 | TERMIOS_CLEAR_ISIG /* turn off INTR (ctrl-C), QUIT, SUSP */
2438 ); 2560 );
2439 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) { 2561 if (n != 0 || (initial_settings.c_lflag & (ECHO|ICANON)) == ICANON) {
2562#else
2563 initial_settings.c_cc[VINTR] = CTRL('C');
2564 initial_settings.c_cc[VEOF] = CTRL('D');
2565 if (!isatty(0) || !isatty(1)) {
2566#endif
2440 /* Happens when e.g. stty -echo was run before. 2567 /* Happens when e.g. stty -echo was run before.
2441 * But if ICANON is not set, we don't come here. 2568 * But if ICANON is not set, we don't come here.
2442 * (example: interactive python ^Z-backgrounded, 2569 * (example: interactive python ^Z-backgrounded,
@@ -2488,7 +2615,9 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2488#endif 2615#endif
2489#define command command_must_not_be_used 2616#define command command_must_not_be_used
2490 2617
2618#if !ENABLE_PLATFORM_MINGW32
2491 tcsetattr_stdin_TCSANOW(&new_settings); 2619 tcsetattr_stdin_TCSANOW(&new_settings);
2620#endif
2492 2621
2493#if 0 2622#if 0
2494 for (i = 0; i <= state->max_history; i++) 2623 for (i = 0; i <= state->max_history; i++)
@@ -2529,6 +2658,11 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2529 } 2658 }
2530#endif 2659#endif
2531 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout); 2660 ic = ic_raw = lineedit_read_key(read_key_buffer, timeout);
2661#if ENABLE_PLATFORM_MINGW32
2662 /* scroll to cursor position on any keypress */
2663 if (isatty(fileno(stdin)) && isatty(fileno(stdout)))
2664 move_cursor_row(0);
2665#endif
2532 2666
2533#if ENABLE_FEATURE_REVERSE_SEARCH 2667#if ENABLE_FEATURE_REVERSE_SEARCH
2534 again: 2668 again:
@@ -2590,6 +2724,13 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2590 input_tab(&lastWasTab); 2724 input_tab(&lastWasTab);
2591 break; 2725 break;
2592#endif 2726#endif
2727#if ENABLE_PLATFORM_MINGW32
2728 case CTRL('Z'):
2729 command_ps[command_len] = '\0';
2730 bs_to_slash(command_ps);
2731 redraw(cmdedit_y, 0);
2732 break;
2733#endif
2593 case CTRL('K'): 2734 case CTRL('K'):
2594 /* Control-k -- clear to end of line */ 2735 /* Control-k -- clear to end of line */
2595 command_ps[cursor] = BB_NUL; 2736 command_ps[cursor] = BB_NUL;
@@ -2963,8 +3104,10 @@ int FAST_FUNC read_line_input(line_input_t *st, const char *prompt, char *comman
2963 free_tab_completion_data(); 3104 free_tab_completion_data();
2964#endif 3105#endif
2965 3106
3107#if !ENABLE_PLATFORM_MINGW32
2966 /* restore initial_settings */ 3108 /* restore initial_settings */
2967 tcsetattr_stdin_TCSANOW(&initial_settings); 3109 tcsetattr_stdin_TCSANOW(&initial_settings);
3110#endif
2968#if ENABLE_FEATURE_EDITING_WINCH 3111#if ENABLE_FEATURE_EDITING_WINCH
2969 /* restore SIGWINCH handler */ 3112 /* restore SIGWINCH handler */
2970 sigaction_set(SIGWINCH, &S.SIGWINCH_handler); 3113 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..04863855a 100644
--- a/libbb/messages.c
+++ b/libbb/messages.c
@@ -27,7 +27,11 @@ 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#else
33const char bb_skip_ansi_emulation[] ALIGN1 = "BB_SKIP_ANSI_EMULATION";
34#endif
31const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL; 35const char bb_default_login_shell[] ALIGN1 = LIBBB_DEFAULT_LOGIN_SHELL;
32/* util-linux manpage says /sbin:/bin:/usr/sbin:/usr/bin, 36/* 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! */ 37 * 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 52abe66f7..906c03964 100644
--- a/libbb/mode_string.c
+++ b/libbb/mode_string.c
@@ -19,7 +19,7 @@
19/* Generate ls-style "mode string" like "-rwsr-xr-x" or "drwxrwxrwt" */ 19/* Generate ls-style "mode string" like "-rwsr-xr-x" or "drwxrwxrwt" */
20 20
21#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \ 21#if ( S_IFSOCK!= 0140000 ) || ( S_IFLNK != 0120000 ) \
22 || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 ) \ 22 || ( S_IFREG != 0100000 ) || ( S_IFBLK != 0060000 && S_IFBLK != 0030000 ) \
23 || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \ 23 || ( S_IFDIR != 0040000 ) || ( S_IFCHR != 0020000 ) \
24 || ( S_IFIFO != 0010000 ) 24 || ( S_IFIFO != 0010000 )
25# warning mode type bitflag value assumption(s) violated! falling back to larger version 25# 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 f56b71b21..8c9cac125 100644
--- a/libbb/procps.c
+++ b/libbb/procps.c
@@ -63,6 +63,7 @@ const char* FAST_FUNC get_cached_groupname(gid_t gid)
63 return get_cached(1, gid, gid2group_utoa); 63 return get_cached(1, gid, gid2group_utoa);
64} 64}
65 65
66#if !ENABLE_PLATFORM_MINGW32
66 67
67#define PROCPS_BUFSIZE 1024 68#define PROCPS_BUFSIZE 1024
68 69
@@ -618,6 +619,8 @@ void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
618 } 619 }
619} 620}
620 621
622#endif /* ENABLE_PLATFORM_MINGW32 */
623
621/* from kernel: 624/* from kernel:
622 // pid comm S ppid pgid sid tty_nr tty_pgrp flg 625 // pid comm S ppid pgid sid tty_nr tty_pgrp flg
623 sprintf(buffer,"%d (%s) %c %d %d %d %d %d %lu %lu \ 626 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 0cd04ab7b..379dd2448 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/signals.c b/libbb/signals.c
index 0bebc847d..c09a562ed 100644
--- a/libbb/signals.c
+++ b/libbb/signals.c
@@ -18,6 +18,7 @@ void record_signo(int signo)
18 bb_got_signal = signo; 18 bb_got_signal = signo;
19} 19}
20 20
21#if !ENABLE_PLATFORM_MINGW32
21/* Saves 2 bytes on x86! Oh my... */ 22/* Saves 2 bytes on x86! Oh my... */
22int FAST_FUNC sigaction_set(int signum, const struct sigaction *act) 23int FAST_FUNC sigaction_set(int signum, const struct sigaction *act)
23{ 24{
@@ -40,6 +41,7 @@ int FAST_FUNC sigprocmask2(int how, sigset_t *set)
40 oset = set; 41 oset = set;
41 return sigprocmask(how, set, oset); 42 return sigprocmask(how, set, oset);
42} 43}
44#endif
43 45
44void FAST_FUNC bb_signals(int sigs, void (*f)(int)) 46void FAST_FUNC bb_signals(int sigs, void (*f)(int))
45{ 47{
diff --git a/libbb/time.c b/libbb/time.c
index f09ef5d52..e7c9fa65e 100644
--- a/libbb/time.c
+++ b/libbb/time.c
@@ -44,12 +44,21 @@ int FAST_FUNC parse_datestr(const char *date_str, struct tm *ptm)
44 save = *ptm; 44 save = *ptm;
45 fmt = fmt_str; 45 fmt = fmt_str;
46 while (*fmt) { 46 while (*fmt) {
47#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_TIMEZONE
48 long gmtoff;
49 endp = mingw_strptime(date_str, fmt, ptm, &gmtoff);
50#else
47 endp = strptime(date_str, fmt, ptm); 51 endp = strptime(date_str, fmt, ptm);
52#endif
48 if (endp && *endp == '\0') { 53 if (endp && *endp == '\0') {
49# if ENABLE_FEATURE_TIMEZONE 54# if ENABLE_FEATURE_TIMEZONE
50 if (strchr(fmt, 'z')) { 55 if (strchr(fmt, 'z')) {
51 /* we have timezone offset: obtain Unix time_t */ 56 /* we have timezone offset: obtain Unix time_t */
57#if ENABLE_PLATFORM_MINGW32
58 ptm->tm_sec -= gmtoff;
59#else
52 ptm->tm_sec -= ptm->tm_gmtoff; 60 ptm->tm_sec -= ptm->tm_gmtoff;
61#endif
53 ptm->tm_isdst = 0; 62 ptm->tm_isdst = 0;
54 t = timegm(ptm); 63 t = timegm(ptm);
55 if (t == (time_t)-1) 64 if (t == (time_t)-1)
diff --git a/libbb/u_signal_names.c b/libbb/u_signal_names.c
index f7d598c7a..ef2b6f891 100644
--- a/libbb/u_signal_names.c
+++ b/libbb/u_signal_names.c
@@ -27,10 +27,23 @@
27 27
28#include "libbb.h" 28#include "libbb.h"
29 29
30#if ENABLE_PLATFORM_MINGW32
31# undef SIGPIPE
32#endif
33
34#if ENABLE_PLATFORM_POSIX || defined(SIGSTKFLT) || defined(SIGVTALRM)
35# define SIGLEN 7
36#elif defined(SIGWINCH) || (ENABLE_FEATURE_RTMINMAX && \
37 !ENABLE_FEATURE_RTMINMAX_USE_LIBC_DEFINITIONS && defined(__SIGRTMIN))
38# define SIGLEN 6
39#else
40# define SIGLEN 5
41#endif
42
30/* Believe it or not, but some arches have more than 32 SIGs! 43/* Believe it or not, but some arches have more than 32 SIGs!
31 * HPPA: SIGSTKFLT == 36. */ 44 * HPPA: SIGSTKFLT == 36. */
32 45
33static const char signals[][7] ALIGN1 = { 46static const char signals[][SIGLEN] ALIGN1 = {
34 // SUSv3 says kill must support these, and specifies the numerical values, 47 // SUSv3 says kill must support these, and specifies the numerical values,
35 // http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html 48 // http://www.opengroup.org/onlinepubs/009695399/utilities/kill.html
36 // {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"}, 49 // {0, "EXIT"}, {1, "HUP"}, {2, "INT"}, {3, "QUIT"},
diff --git a/libbb/vfork_daemon_rexec.c b/libbb/vfork_daemon_rexec.c
index 31e97051f..151739ae2 100644
--- a/libbb/vfork_daemon_rexec.c
+++ b/libbb/vfork_daemon_rexec.c
@@ -171,6 +171,7 @@ void FAST_FUNC run_noexec_applet_and_exit(int a, const char *name, char **argv)
171 * Higher-level code, hiding optional NOFORK/NOEXEC trickery. 171 * Higher-level code, hiding optional NOFORK/NOEXEC trickery.
172 */ 172 */
173 173
174#if !ENABLE_PLATFORM_MINGW32
174/* This does a fork/exec in one call, using vfork(). Returns PID of new child, 175/* This does a fork/exec in one call, using vfork(). Returns PID of new child,
175 * -1 for failure. Runs argv[0], searching path if that has no / in it. */ 176 * -1 for failure. Runs argv[0], searching path if that has no / in it. */
176pid_t FAST_FUNC spawn(char **argv) 177pid_t FAST_FUNC spawn(char **argv)
@@ -212,6 +213,7 @@ pid_t FAST_FUNC spawn(char **argv)
212 } 213 }
213 return pid; 214 return pid;
214} 215}
216#endif
215 217
216/* Die with an error message if we can't spawn a child process. */ 218/* Die with an error message if we can't spawn a child process. */
217pid_t FAST_FUNC xspawn(char **argv) 219pid_t FAST_FUNC xspawn(char **argv)
@@ -232,6 +234,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
232 if (APPLET_IS_NOFORK(a)) 234 if (APPLET_IS_NOFORK(a))
233 return run_nofork_applet(a, argv); 235 return run_nofork_applet(a, argv);
234# if BB_MMU /* NOEXEC needs fork(), thus this is done only on MMU machines: */ 236# if BB_MMU /* NOEXEC needs fork(), thus this is done only on MMU machines: */
237# if !ENABLE_PLATFORM_MINGW32 /* and then only if not on Microsoft Windows */
235 if (APPLET_IS_NOEXEC(a)) { 238 if (APPLET_IS_NOEXEC(a)) {
236 fflush_all(); 239 fflush_all();
237 rc = fork(); 240 rc = fork();
@@ -241,6 +244,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
241 /* child */ 244 /* child */
242 run_noexec_applet_and_exit(a, argv[0], argv); 245 run_noexec_applet_and_exit(a, argv[0], argv);
243 } 246 }
247# endif
244# endif 248# endif
245 } 249 }
246#endif 250#endif
@@ -248,6 +252,7 @@ int FAST_FUNC spawn_and_wait(char **argv)
248 return wait4pid(rc); 252 return wait4pid(rc);
249} 253}
250 254
255#if !ENABLE_PLATFORM_MINGW32
251#if !BB_MMU 256#if !BB_MMU
252void FAST_FUNC re_exec(char **argv) 257void FAST_FUNC re_exec(char **argv)
253{ 258{
@@ -335,3 +340,4 @@ void FAST_FUNC bb_sanitize_stdio(void)
335{ 340{
336 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL); 341 bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE, NULL);
337} 342}
343#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 0e0b247b8..65b1cb8de 100644
--- a/libbb/xconnect.c
+++ b/libbb/xconnect.c
@@ -71,6 +71,7 @@ int FAST_FUNC setsockopt_bindtodevice(int fd UNUSED_PARAM,
71} 71}
72#endif 72#endif
73 73
74#if !ENABLE_PLATFORM_MINGW32
74static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen)) 75static len_and_sockaddr* get_lsa(int fd, int (*get_name)(int fd, struct sockaddr *addr, socklen_t *addrlen))
75{ 76{
76 len_and_sockaddr lsa; 77 len_and_sockaddr lsa;
@@ -99,16 +100,17 @@ len_and_sockaddr* FAST_FUNC get_peer_lsa(int fd)
99{ 100{
100 return get_lsa(fd, getpeername); 101 return get_lsa(fd, getpeername);
101} 102}
103#endif
102 104
103void FAST_FUNC xconnect(int s, const struct sockaddr *s_addr, socklen_t addrlen) 105void FAST_FUNC xconnect(int s, const struct sockaddr *saddr, socklen_t addrlen)
104{ 106{
105 if (connect(s, s_addr, addrlen) < 0) { 107 if (connect(s, saddr, addrlen) < 0) {
106 if (ENABLE_FEATURE_CLEAN_UP) 108 if (ENABLE_FEATURE_CLEAN_UP)
107 close(s); 109 close(s);
108 if (s_addr->sa_family == AF_INET) 110 if (saddr->sa_family == AF_INET)
109 bb_perror_msg_and_die("%s (%s)", 111 bb_perror_msg_and_die("%s (%s)",
110 "can't connect to remote host", 112 "can't connect to remote host",
111 inet_ntoa(((struct sockaddr_in *)s_addr)->sin_addr)); 113 inet_ntoa(((struct sockaddr_in *)saddr)->sin_addr));
112 bb_simple_perror_msg_and_die("can't connect to remote host"); 114 bb_simple_perror_msg_and_die("can't connect to remote host");
113 } 115 }
114} 116}
@@ -348,6 +350,10 @@ int FAST_FUNC xsocket_type(len_and_sockaddr **lsap, int family, int sock_type)
348#if ENABLE_FEATURE_IPV6 350#if ENABLE_FEATURE_IPV6
349 fd = socket(AF_INET6, sock_type, 0); 351 fd = socket(AF_INET6, sock_type, 0);
350 if (fd >= 0) { 352 if (fd >= 0) {
353#if ENABLE_PLATFORM_MINGW32
354 DWORD buffer = 0;
355 setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &buffer, sizeof(DWORD));
356#endif
351 family = AF_INET6; 357 family = AF_INET6;
352 goto done; 358 goto done;
353 } 359 }
diff --git a/libbb/xfuncs.c b/libbb/xfuncs.c
index c40dcb706..102b5a227 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;
@@ -309,6 +311,7 @@ int FAST_FUNC is_TERM_dumb(void)
309 return term && strcmp(term, "dumb") == 0; 311 return term && strcmp(term, "dumb") == 0;
310} 312}
311 313
314#if !ENABLE_PLATFORM_MINGW32
312int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp) 315int FAST_FUNC tcsetattr_stdin_TCSANOW(const struct termios *tp)
313{ 316{
314 return tcsetattr(STDIN_FILENO, TCSANOW, tp); 317 return tcsetattr(STDIN_FILENO, TCSANOW, tp);
@@ -389,6 +392,7 @@ int FAST_FUNC set_termios_to_raw(int fd, struct termios *oldterm, int flags)
389 get_termios_and_make_raw(fd, &newterm, oldterm, flags); 392 get_termios_and_make_raw(fd, &newterm, oldterm, flags);
390 return tcsetattr(fd, TCSANOW, &newterm); 393 return tcsetattr(fd, TCSANOW, &newterm);
391} 394}
395#endif
392 396
393pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options) 397pid_t FAST_FUNC safe_waitpid(pid_t pid, int *wstat, int options)
394{ 398{
diff --git a/libbb/xfuncs_printf.c b/libbb/xfuncs_printf.c
index d29acebcd..d7d8b1092 100644
--- a/libbb/xfuncs_printf.c
+++ b/libbb/xfuncs_printf.c
@@ -111,6 +111,7 @@ void* FAST_FUNC xmemdup(const void *s, int n)
111 return memcpy(xmalloc(n), s, n); 111 return memcpy(xmalloc(n), s, n);
112} 112}
113 113
114#if !ENABLE_PLATFORM_MINGW32
114void* FAST_FUNC mmap_read(int fd, size_t size) 115void* FAST_FUNC mmap_read(int fd, size_t size)
115{ 116{
116 return mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); 117 return mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0);
@@ -131,6 +132,7 @@ void* FAST_FUNC xmmap_anon(size_t size)
131 bb_die_memory_exhausted(); 132 bb_die_memory_exhausted();
132 return p; 133 return p;
133} 134}
135#endif
134 136
135// Die if we can't open a file and return a FILE* to it. 137// Die if we can't open a file and return a FILE* to it.
136// Notice we haven't got xfread(), This is for use with fscanf() and friends. 138// Notice we haven't got xfread(), This is for use with fscanf() and friends.
@@ -497,6 +499,7 @@ void FAST_FUNC xlisten(int s, int backlog)
497 if (listen(s, backlog)) bb_simple_perror_msg_and_die("listen"); 499 if (listen(s, backlog)) bb_simple_perror_msg_and_die("listen");
498} 500}
499 501
502#if !ENABLE_PLATFORM_MINGW32
500/* Die with an error message if sendto failed. 503/* Die with an error message if sendto failed.
501 * Return bytes sent otherwise */ 504 * Return bytes sent otherwise */
502ssize_t FAST_FUNC xsendto(int s, const void *buf, size_t len, const struct sockaddr *to, 505ssize_t FAST_FUNC xsendto(int s, const void *buf, size_t len, const struct sockaddr *to,
@@ -510,6 +513,7 @@ ssize_t FAST_FUNC xsendto(int s, const void *buf, size_t len, const struct socka
510 } 513 }
511 return ret; 514 return ret;
512} 515}
516#endif
513 517
514// xstat() - a stat() which dies on failure with meaningful error message 518// xstat() - a stat() which dies on failure with meaningful error message
515void FAST_FUNC xstat(const char *name, struct stat *stat_buf) 519void FAST_FUNC xstat(const char *name, struct stat *stat_buf)
@@ -543,6 +547,7 @@ void FAST_FUNC selinux_or_die(void)
543/* not defined, other code must have no calls to it */ 547/* not defined, other code must have no calls to it */
544#endif 548#endif
545 549
550#if !ENABLE_PLATFORM_MINGW32
546int FAST_FUNC ioctl_or_perror_and_die(int fd, unsigned request, void *argp, const char *fmt,...) 551int FAST_FUNC ioctl_or_perror_and_die(int fd, unsigned request, void *argp, const char *fmt,...)
547{ 552{
548 int ret; 553 int ret;
@@ -707,6 +712,7 @@ void FAST_FUNC xvfork_parent_waits_and_exits(void)
707 } 712 }
708 /* Child continues */ 713 /* Child continues */
709} 714}
715#endif /* !ENABLE_PLATFORM_MINGW32 */
710 716
711// Useful when we do know that pid is valid, and we just want to wait 717// Useful when we do know that pid is valid, and we just want to wait
712// for it to exit. Not existing pid is fatal. waitpid() status is not returned. 718// for it to exit. Not existing pid is fatal. waitpid() status is not returned.
@@ -720,11 +726,13 @@ int FAST_FUNC wait_for_exitstatus(pid_t pid)
720 return exit_status; 726 return exit_status;
721} 727}
722 728
729#if !ENABLE_PLATFORM_MINGW32
723void FAST_FUNC xsettimeofday(const struct timeval *tv) 730void FAST_FUNC xsettimeofday(const struct timeval *tv)
724{ 731{
725 if (settimeofday(tv, NULL)) 732 if (settimeofday(tv, NULL))
726 bb_simple_perror_msg_and_die("settimeofday"); 733 bb_simple_perror_msg_and_die("settimeofday");
727} 734}
735#endif
728 736
729void FAST_FUNC xgettimeofday(struct timeval *tv) 737void FAST_FUNC xgettimeofday(struct timeval *tv)
730{ 738{
diff --git a/libbb/xreadlink.c b/libbb/xreadlink.c
index 2682f6975..e6cf90310 100644
--- a/libbb/xreadlink.c
+++ b/libbb/xreadlink.c
@@ -64,19 +64,26 @@ 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 }
71 71
72 if (!--looping) { 72 if (!--looping) {
73#if ENABLE_PLATFORM_MINGW32
74 errno = ELOOP;
75#endif
73 free(linkpath); 76 free(linkpath);
74 free_buf_ret_null: 77 free_buf_ret_null:
75 free(buf); 78 free(buf);
76 return NULL; 79 return NULL;
77 } 80 }
78 81
82#if ENABLE_PLATFORM_MINGW32
83 if (is_relative_path(linkpath)) {
84#else
79 if (*linkpath != '/') { 85 if (*linkpath != '/') {
86#endif
80 bufsize += strlen(linkpath); 87 bufsize += strlen(linkpath);
81 buf = xrealloc(buf, bufsize); 88 buf = xrealloc(buf, bufsize);
82 lpc = bb_get_last_path_component_strip(buf); 89 lpc = bb_get_last_path_component_strip(buf);
@@ -149,7 +156,11 @@ char* FAST_FUNC xmalloc_realpath_coreutils(char *path)
149 * $ realpath symlink 156 * $ realpath symlink
150 * /usr/bin/qwe 157 * /usr/bin/qwe
151 */ 158 */
159#if ENABLE_PLATFORM_MINGW32
160 if (is_relative_path(target)) {
161#else
152 if (target[0] != '/') { 162 if (target[0] != '/') {
163#endif
153 /* 164 /*
154 * $ ln -s target_does_not_exist symlink 165 * $ ln -s target_does_not_exist symlink
155 * $ readlink -f symlink 166 * $ readlink -f symlink
@@ -168,6 +179,35 @@ char* FAST_FUNC xmalloc_realpath_coreutils(char *path)
168 return buf; 179 return buf;
169 } 180 }
170 181
182#if ENABLE_PLATFORM_MINGW32
183 /* ignore leading and trailing slashes */
184 /* but keep leading slashes of UNC path */
185 if (!is_unc_path(path)) {
186 while (is_dir_sep(path[0]) && is_dir_sep(path[1]))
187 ++path;
188 }
189 i = strlen(path) - 1;
190 while (i > 0 && is_dir_sep(path[i]))
191 i--;
192 c = path[i + 1];
193 path[i + 1] = '\0';
194
195 last_slash = get_last_slash(path);
196 if (last_slash == path + root_len(path))
197 buf = xstrdup(path);
198 else if (last_slash) {
199 char c2 = *last_slash;
200 *last_slash = '\0';
201 buf = xmalloc_realpath(path);
202 *last_slash++ = c2;
203 if (buf) {
204 unsigned len = strlen(buf);
205 buf = xrealloc(buf, len + strlen(last_slash) + 2);
206 buf[len++] = c2;
207 strcpy(buf + len, last_slash);
208 }
209 }
210#else
171 /* ignore leading and trailing slashes */ 211 /* ignore leading and trailing slashes */
172 while (path[0] == '/' && path[1] == '/') 212 while (path[0] == '/' && path[1] == '/')
173 ++path; 213 ++path;
@@ -191,6 +231,7 @@ char* FAST_FUNC xmalloc_realpath_coreutils(char *path)
191 strcpy(buf + len, last_slash); 231 strcpy(buf + len, last_slash);
192 } 232 }
193 } 233 }
234#endif
194 path[i + 1] = c; 235 path[i + 1] = c;
195 } 236 }
196 237
diff --git a/loginutils/su.c b/loginutils/su.c
index e1db7590f..647c97fb1 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..3500c08db
--- /dev/null
+++ b/loginutils/suw32.c
@@ -0,0 +1,72 @@
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#include "lazyload.h"
26
27int suw32_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
28int suw32_main(int argc UNUSED_PARAM, char **argv)
29{
30 char *opt_command = NULL;
31 SHELLEXECUTEINFO info;
32 char *bb_path, *cwd;
33 DECLARE_PROC_ADDR(BOOL, ShellExecuteExA, SHELLEXECUTEINFOA *);
34
35 getopt32(argv, "c:", &opt_command);
36 if (argv[optind])
37 bb_show_usage();
38
39 /* ShellExecuteEx() needs backslash as separator in UNC paths. */
40 bb_path = xstrdup(bb_busybox_exec_path);
41 slash_to_bs(bb_path);
42
43 memset(&info, 0, sizeof(SHELLEXECUTEINFO));
44 info.cbSize = sizeof(SHELLEXECUTEINFO);
45 /* info.fMask = SEE_MASK_DEFAULT; */
46 /* info.hwnd = NULL; */
47 info.lpVerb = "runas";
48 info.lpFile = bb_path;
49 /*
50 * It seems that when ShellExecuteEx() runs binaries residing in
51 * certain 'system' directories it sets the current directory of
52 * the process to %SYSTEMROOT%\System32. Override this by passing
53 * the directory we want to the shell.
54 *
55 * Canonicalise the directory now: if it's in a drive mapped to
56 * a network share it may not be available once we have elevated
57 * privileges.
58 */
59 cwd = xmalloc_realpath(getcwd(NULL, 0));
60 info.lpParameters =
61 xasprintf("--busybox ash -d \"%s\" -t \"BusyBox ash (Admin)\" ", cwd);
62 if (opt_command)
63 info.lpParameters =
64 xasprintf("%s -s -c \"%s\"", info.lpParameters, opt_command);
65 /* info.lpDirectory = NULL; */
66 info.nShow = SW_SHOWNORMAL;
67
68 if (!INIT_PROC_ADDR(shell32.dll, ShellExecuteExA))
69 return -1;
70
71 return !ShellExecuteExA(&info);
72}
diff --git a/miscutils/bbconfig.c b/miscutils/bbconfig.c
index fe02516a8..077e03c5d 100644
--- a/miscutils/bbconfig.c
+++ b/miscutils/bbconfig.c
@@ -35,6 +35,7 @@
35#include "libbb.h" 35#include "libbb.h"
36#include "bbconfigopts.h" 36#include "bbconfigopts.h"
37#if ENABLE_FEATURE_COMPRESS_BBCONFIG 37#if ENABLE_FEATURE_COMPRESS_BBCONFIG
38#define BB_ARCHIVE_PUBLIC
38# include "bb_archive.h" 39# include "bb_archive.h"
39# include "bbconfigopts_bz2.h" 40# include "bbconfigopts_bz2.h"
40#endif 41#endif
diff --git a/miscutils/bc.c b/miscutils/bc.c
index ae370ff55..e3f7573c9 100644
--- a/miscutils/bc.c
+++ b/miscutils/bc.c
@@ -7464,6 +7464,17 @@ static unsigned xc_vm_envLen(const char *var)
7464 return len; 7464 return len;
7465} 7465}
7466 7466
7467#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_BC_INTERACTIVE
7468static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
7469{
7470 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
7471 bb_got_signal = SIGINT;
7472 return TRUE;
7473 }
7474 return FALSE;
7475}
7476#endif
7477
7467static int xc_vm_init(const char *env_len) 7478static int xc_vm_init(const char *env_len)
7468{ 7479{
7469 G.prog.len = xc_vm_envLen(env_len); 7480 G.prog.len = xc_vm_envLen(env_len);
@@ -7489,7 +7500,11 @@ static int xc_vm_init(const char *env_len)
7489 // from stdin is not interrupted by ^C either, 7500 // from stdin is not interrupted by ^C either,
7490 // it restarts, thus fgetc() does not return on ^C. 7501 // it restarts, thus fgetc() does not return on ^C.
7491 // (This problem manifests only if line editing is disabled) 7502 // (This problem manifests only if line editing is disabled)
7503# if !ENABLE_PLATFORM_MINGW32
7492 signal_SA_RESTART_empty_mask(SIGINT, record_signo); 7504 signal_SA_RESTART_empty_mask(SIGINT, record_signo);
7505# else
7506 SetConsoleCtrlHandler(ctrl_handler, TRUE);
7507# endif
7493 7508
7494 // Without SA_RESTART, this exhibits a bug: 7509 // Without SA_RESTART, this exhibits a bug:
7495 // "while (1) print 1" and try ^C-ing it. 7510 // "while (1) print 1" and try ^C-ing it.
diff --git a/miscutils/dc.c b/miscutils/dc.c
index 42baa67ad..d6369fd15 100644
--- a/miscutils/dc.c
+++ b/miscutils/dc.c
@@ -17,7 +17,7 @@ typedef unsigned long data_t;
17#define DATA_FMT "l" 17#define DATA_FMT "l"
18#else 18#else
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..ec3c0c92d
--- /dev/null
+++ b/miscutils/iconv.c
@@ -0,0 +1,1761 @@
1/*
2 * iconv implementation using Win32 API to convert.
3 *
4 * This file is placed in the public domain.
5 */
6
7/*
8 * This code was obtained from:
9 *
10 * https://github.com/win-iconv/win-iconv
11 *
12 * Modified for busybox-w32 by Ronald M Yorston. These modifications
13 * are also dedicated to the public domain.
14 */
15
16//config:config ICONV
17//config: bool "iconv (11.4 kb)"
18//config: default y
19//config: depends on PLATFORM_MINGW32
20//config: help
21//config: 'iconv' converts text between character encodings.
22
23//applet:IF_ICONV(APPLET(iconv, BB_DIR_USR_BIN, BB_SUID_DROP))
24
25//kbuild:lib-$(CONFIG_ICONV) += iconv.o
26
27//usage:#define iconv_trivial_usage
28//usage: "[-lc] [-o outfile] [-f from-enc] [-t to-enc] [FILE]..."
29//usage:#define iconv_full_usage "\n\n"
30//usage: "Convert text between character encodings\n"
31//usage: "\n -l List all known character encodings"
32//usage: "\n -c Silently discard characters that cannot be converted"
33//usage: "\n -o Use outfile for output"
34//usage: "\n -f Use from-enc for input characters"
35//usage: "\n -t Use to-enc for output characters"
36
37#include "libbb.h"
38
39/* WORKAROUND: */
40#define GetProcAddressA GetProcAddress
41
42#define MB_CHAR_MAX 16
43
44#define UNICODE_MODE_BOM_DONE 1
45#define UNICODE_MODE_SWAPPED 2
46
47#define FLAG_USE_BOM 1
48#define FLAG_TRANSLIT 2 /* //TRANSLIT */
49#define FLAG_IGNORE 4 /* //IGNORE */
50
51typedef unsigned char uchar;
52typedef unsigned short ushort;
53typedef unsigned int uint;
54
55typedef void* iconv_t;
56
57static iconv_t iconv_open(const char *tocode, const char *fromcode);
58static int iconv_close(iconv_t cd);
59static size_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 int (*f_mbtowc)(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
66typedef int (*f_wctomb)(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
67typedef int (*f_mblen)(csconv_t *cv, const uchar *buf, int bufsize);
68typedef int (*f_flush)(csconv_t *cv, uchar *buf, int bufsize);
69
70#define COMPAT_IN 1
71#define COMPAT_OUT 2
72
73/* unicode mapping for compatibility with other conversion table. */
74struct compat_t {
75 uint in;
76 uint out;
77 uint flag;
78};
79
80struct csconv_t {
81 int codepage;
82 int flags;
83 f_mbtowc mbtowc;
84 f_wctomb wctomb;
85 f_mblen mblen;
86 f_flush flush;
87 DWORD mode;
88 compat_t *compat;
89};
90
91struct rec_iconv_t {
92 iconv_t cd;
93 csconv_t from;
94 csconv_t to;
95};
96
97static int load_mlang(void);
98static int make_csconv(const char *name, csconv_t *cv);
99static int name_to_codepage(const char *name);
100static uint utf16_to_ucs4(const ushort *wbuf);
101static void ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize);
102static int mbtowc_flags(int codepage);
103static int must_use_null_useddefaultchar(int codepage);
104static int seterror(int err);
105
106static int sbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
107static int dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
108static int mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize);
109static int utf8_mblen(csconv_t *cv, const uchar *buf, int bufsize);
110static int eucjp_mblen(csconv_t *cv, const uchar *buf, int bufsize);
111
112static int kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
113static int kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
114static int mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
115static int mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
116static int utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
117static int utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
118static int utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
119static int utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
120static int iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize);
121static int iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize);
122static int iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize);
123
124#define CP_ALIAS_LIST \
125 CP_ALIAS(65001, "CP65001") \
126 CP_ALIAS(65001, "UTF8") \
127 CP_ALIAS(65001, "UTF-8") \
128\
129 CP_ALIAS(1200, "CP1200") \
130 CP_ALIAS(1200, "UTF16LE") \
131 CP_ALIAS(1200, "UTF-16LE") \
132 CP_ALIAS(1200, "UCS2LE") \
133 CP_ALIAS(1200, "UCS-2LE") \
134 CP_ALIAS(1200, "UCS-2-INTERNAL") \
135\
136 CP_ALIAS(1201, "CP1201") \
137 CP_ALIAS(1201, "UTF16BE") \
138 CP_ALIAS(1201, "UTF-16BE") \
139 CP_ALIAS(1201, "UCS2BE") \
140 CP_ALIAS(1201, "UCS-2BE") \
141 CP_ALIAS(1201, "unicodeFFFE") \
142\
143 CP_ALIAS(12000, "CP12000") \
144 CP_ALIAS(12000, "UTF32LE") \
145 CP_ALIAS(12000, "UTF-32LE") \
146 CP_ALIAS(12000, "UCS4LE") \
147 CP_ALIAS(12000, "UCS-4LE") \
148\
149 CP_ALIAS(12001, "CP12001") \
150 CP_ALIAS(12001, "UTF32BE") \
151 CP_ALIAS(12001, "UTF-32BE") \
152 CP_ALIAS(12001, "UCS4BE") \
153 CP_ALIAS(12001, "UCS-4BE") \
154\
155 /* Default is little endian, because the platform is */ \
156 CP_ALIAS(1200, "UTF16") \
157 CP_ALIAS(1200, "UTF-16") \
158 CP_ALIAS(1200, "UCS2") \
159 CP_ALIAS(1200, "UCS-2") \
160 CP_ALIAS(12000, "UTF32") \
161 CP_ALIAS(12000, "UTF-32") \
162 CP_ALIAS(12000, "UCS4") \
163 CP_ALIAS(12000, "UCS-4") \
164\
165 /* copy from libiconv `iconv -l` */ \
166 /* !IsValidCodePage(367) */ \
167 CP_ALIAS(20127, "ANSI_X3.4-1968") \
168 CP_ALIAS(20127, "ANSI_X3.4-1986") \
169 CP_ALIAS(20127, "ASCII") \
170 CP_ALIAS(20127, "CP367") \
171 CP_ALIAS(20127, "IBM367") \
172 CP_ALIAS(20127, "ISO-IR-6") \
173 CP_ALIAS(20127, "ISO646-US") \
174 CP_ALIAS(20127, "ISO_646.IRV:1991") \
175 CP_ALIAS(20127, "US") \
176 CP_ALIAS(20127, "US-ASCII") \
177 CP_ALIAS(20127, "CSASCII") \
178\
179 /* !IsValidCodePage(819) */ \
180 CP_ALIAS(1252, "CP819") \
181 CP_ALIAS(1252, "IBM819") \
182 CP_ALIAS(28591, "ISO-8859-1") \
183 CP_ALIAS(28591, "ISO-IR-100") \
184 CP_ALIAS(28591, "ISO8859-1") \
185 CP_ALIAS(28591, "ISO_8859-1") \
186 CP_ALIAS(28591, "ISO_8859-1:1987") \
187 CP_ALIAS(28591, "L1") \
188 CP_ALIAS(28591, "LATIN1") \
189 CP_ALIAS(28591, "CSISOLATIN1") \
190\
191 CP_ALIAS(1250, "CP1250") \
192 CP_ALIAS(1250, "MS-EE") \
193 CP_ALIAS(1250, "WINDOWS-1250") \
194\
195 CP_ALIAS(1251, "CP1251") \
196 CP_ALIAS(1251, "MS-CYRL") \
197 CP_ALIAS(1251, "WINDOWS-1251") \
198\
199 CP_ALIAS(1252, "CP1252") \
200 CP_ALIAS(1252, "MS-ANSI") \
201 CP_ALIAS(1252, "WINDOWS-1252") \
202\
203 CP_ALIAS(1253, "CP1253") \
204 CP_ALIAS(1253, "MS-GREEK") \
205 CP_ALIAS(1253, "WINDOWS-1253") \
206\
207 CP_ALIAS(1254, "CP1254") \
208 CP_ALIAS(1254, "MS-TURK") \
209 CP_ALIAS(1254, "WINDOWS-1254") \
210\
211 CP_ALIAS(1255, "CP1255") \
212 CP_ALIAS(1255, "MS-HEBR") \
213 CP_ALIAS(1255, "WINDOWS-1255") \
214\
215 CP_ALIAS(1256, "CP1256") \
216 CP_ALIAS(1256, "MS-ARAB") \
217 CP_ALIAS(1256, "WINDOWS-1256") \
218\
219 CP_ALIAS(1257, "CP1257") \
220 CP_ALIAS(1257, "WINBALTRIM") \
221 CP_ALIAS(1257, "WINDOWS-1257") \
222\
223 CP_ALIAS(1258, "CP1258") \
224 CP_ALIAS(1258, "WINDOWS-1258") \
225\
226 CP_ALIAS(850, "850") \
227 CP_ALIAS(850, "CP850") \
228 CP_ALIAS(850, "IBM850") \
229 CP_ALIAS(850, "CSPC850MULTILINGUAL") \
230\
231 /* !IsValidCodePage(862) */ \
232 CP_ALIAS(862, "862") \
233 CP_ALIAS(862, "CP862") \
234 CP_ALIAS(862, "IBM862") \
235 CP_ALIAS(862, "CSPC862LATINHEBREW") \
236\
237 CP_ALIAS(866, "866") \
238 CP_ALIAS(866, "CP866") \
239 CP_ALIAS(866, "IBM866") \
240 CP_ALIAS(866, "CSIBM866") \
241\
242 /* !IsValidCodePage(154) */ \
243 CP_ALIAS(154, "CP154") \
244 CP_ALIAS(154, "CYRILLIC-ASIAN") \
245 CP_ALIAS(154, "PT154") \
246 CP_ALIAS(154, "PTCP154") \
247 CP_ALIAS(154, "CSPTCP154") \
248\
249 /* !IsValidCodePage(1133) */ \
250 CP_ALIAS(1133, "CP1133") \
251 CP_ALIAS(1133, "IBM-CP1133") \
252\
253 CP_ALIAS(874, "CP874") \
254 CP_ALIAS(874, "WINDOWS-874") \
255\
256 /* !IsValidCodePage(51932) */ \
257 CP_ALIAS(51932, "CP51932") \
258 CP_ALIAS(51932, "MS51932") \
259 CP_ALIAS(51932, "WINDOWS-51932") \
260 CP_ALIAS(51932, "EUC-JP") \
261\
262 CP_ALIAS(932, "CP932") \
263 CP_ALIAS(932, "MS932") \
264 CP_ALIAS(932, "SHIFFT_JIS") \
265 CP_ALIAS(932, "SHIFFT_JIS-MS") \
266 CP_ALIAS(932, "SJIS") \
267 CP_ALIAS(932, "SJIS-MS") \
268 CP_ALIAS(932, "SJIS-OPEN") \
269 CP_ALIAS(932, "SJIS-WIN") \
270 CP_ALIAS(932, "WINDOWS-31J") \
271 CP_ALIAS(932, "WINDOWS-932") \
272 CP_ALIAS(932, "CSWINDOWS31J") \
273\
274 CP_ALIAS(50221, "CP50221") \
275 CP_ALIAS(50221, "ISO-2022-JP") \
276 CP_ALIAS(50221, "ISO-2022-JP-MS") \
277 CP_ALIAS(50221, "ISO2022-JP") \
278 CP_ALIAS(50221, "ISO2022-JP-MS") \
279 CP_ALIAS(50221, "MS50221") \
280 CP_ALIAS(50221, "WINDOWS-50221") \
281\
282 CP_ALIAS(936, "CP936") \
283 CP_ALIAS(936, "GBK") \
284 CP_ALIAS(936, "MS936") \
285 CP_ALIAS(936, "WINDOWS-936") \
286\
287 CP_ALIAS(950, "CP950") \
288 CP_ALIAS(950, "BIG5") \
289 CP_ALIAS(950, "BIG5HKSCS") \
290 CP_ALIAS(950, "BIG5-HKSCS") \
291\
292 CP_ALIAS(949, "CP949") \
293 CP_ALIAS(949, "UHC") \
294 CP_ALIAS(949, "EUC-KR") \
295\
296 CP_ALIAS(1361, "CP1361") \
297 CP_ALIAS(1361, "JOHAB") \
298\
299 CP_ALIAS(437, "437") \
300 CP_ALIAS(437, "CP437") \
301 CP_ALIAS(437, "IBM437") \
302 CP_ALIAS(437, "CSPC8CODEPAGE437") \
303\
304 CP_ALIAS(737, "CP737") \
305\
306 CP_ALIAS(775, "CP775") \
307 CP_ALIAS(775, "IBM775") \
308 CP_ALIAS(775, "CSPC775BALTIC") \
309\
310 CP_ALIAS(852, "852") \
311 CP_ALIAS(852, "CP852") \
312 CP_ALIAS(852, "IBM852") \
313 CP_ALIAS(852, "CSPCP852") \
314\
315 /* !IsValidCodePage(853) */ \
316 CP_ALIAS(853, "CP853") \
317\
318 CP_ALIAS(855, "855") \
319 CP_ALIAS(855, "CP855") \
320 CP_ALIAS(855, "IBM855") \
321 CP_ALIAS(855, "CSIBM855") \
322\
323 CP_ALIAS(857, "857") \
324 CP_ALIAS(857, "CP857") \
325 CP_ALIAS(857, "IBM857") \
326 CP_ALIAS(857, "CSIBM857") \
327\
328 /* !IsValidCodePage(858) */ \
329 CP_ALIAS(858, "CP858") \
330\
331 CP_ALIAS(860, "860") \
332 CP_ALIAS(860, "CP860") \
333 CP_ALIAS(860, "IBM860") \
334 CP_ALIAS(860, "CSIBM860") \
335\
336 CP_ALIAS(861, "861") \
337 CP_ALIAS(861, "CP-IS") \
338 CP_ALIAS(861, "CP861") \
339 CP_ALIAS(861, "IBM861") \
340 CP_ALIAS(861, "CSIBM861") \
341\
342 CP_ALIAS(863, "863") \
343 CP_ALIAS(863, "CP863") \
344 CP_ALIAS(863, "IBM863") \
345 CP_ALIAS(863, "CSIBM863") \
346\
347 CP_ALIAS(864, "CP864") \
348 CP_ALIAS(864, "IBM864") \
349 CP_ALIAS(864, "CSIBM864") \
350\
351 CP_ALIAS(865, "865") \
352 CP_ALIAS(865, "CP865") \
353 CP_ALIAS(865, "IBM865") \
354 CP_ALIAS(865, "CSIBM865") \
355\
356 CP_ALIAS(869, "869") \
357 CP_ALIAS(869, "CP-GR") \
358 CP_ALIAS(869, "CP869") \
359 CP_ALIAS(869, "IBM869") \
360 CP_ALIAS(869, "CSIBM869") \
361\
362 /* !IsValidCodePage(1152) */ \
363 CP_ALIAS(1125, "CP1125") \
364\
365 /* \
366 * Code Page Identifiers \
367 * http://msdn2.microsoft.com/en-us/library/ms776446.aspx \
368 */ \
369 CP_ALIAS(37, "IBM037") /* IBM EBCDIC US-Canada */ \
370 CP_ALIAS(437, "IBM437") /* OEM United States */ \
371 CP_ALIAS(500, "IBM500") /* IBM EBCDIC International */ \
372 CP_ALIAS(708, "ASMO-708") /* Arabic (ASMO 708) */ \
373 /* 709 Arabic (ASMO-449+, BCON V4) */ \
374 /* 710 Arabic - Transparent Arabic */ \
375 CP_ALIAS(720, "DOS-720") /* Arabic (Transparent ASMO); Arabic (DOS) */ \
376 CP_ALIAS(737, "ibm737") /* OEM Greek (formerly 437G); Greek (DOS) */ \
377 CP_ALIAS(775, "ibm775") /* OEM Baltic; Baltic (DOS) */ \
378 CP_ALIAS(850, "ibm850") /* OEM Multilingual Latin 1; Western European (DOS) */ \
379 CP_ALIAS(852, "ibm852") /* OEM Latin 2; Central European (DOS) */ \
380 CP_ALIAS(855, "IBM855") /* OEM Cyrillic (primarily Russian) */ \
381 CP_ALIAS(857, "ibm857") /* OEM Turkish; Turkish (DOS) */ \
382 CP_ALIAS(858, "IBM00858") /* OEM Multilingual Latin 1 + Euro symbol */ \
383 CP_ALIAS(860, "IBM860") /* OEM Portuguese; Portuguese (DOS) */ \
384 CP_ALIAS(861, "ibm861") /* OEM Icelandic; Icelandic (DOS) */ \
385 CP_ALIAS(862, "DOS-862") /* OEM Hebrew; Hebrew (DOS) */ \
386 CP_ALIAS(863, "IBM863") /* OEM French Canadian; French Canadian (DOS) */ \
387 CP_ALIAS(864, "IBM864") /* OEM Arabic; Arabic (864) */ \
388 CP_ALIAS(865, "IBM865") /* OEM Nordic; Nordic (DOS) */ \
389 CP_ALIAS(866, "cp866") /* OEM Russian; Cyrillic (DOS) */ \
390 CP_ALIAS(869, "ibm869") /* OEM Modern Greek; Greek, Modern (DOS) */ \
391 CP_ALIAS(870, "IBM870") /* IBM EBCDIC Multilingual/ROECE (Latin 2); IBM EBCDIC Multilingual Latin 2 */ \
392 CP_ALIAS(874, "windows-874") /* ANSI/OEM Thai (same as 28605, ISO 8859-15); Thai (Windows) */ \
393 CP_ALIAS(875, "cp875") /* IBM EBCDIC Greek Modern */ \
394 CP_ALIAS(932, "shift_jis") /* ANSI/OEM Japanese; Japanese (Shift-JIS) */ \
395 CP_ALIAS(932, "shift-jis") /* alternative name for it */ \
396 CP_ALIAS(936, "gb2312") /* ANSI/OEM Simplified Chinese (PRC, Singapore); Chinese Simplified (GB2312) */ \
397 CP_ALIAS(949, "ks_c_5601-1987") /* ANSI/OEM Korean (Unified Hangul Code) */ \
398 CP_ALIAS(950, "big5") /* ANSI/OEM Traditional Chinese (Taiwan; Hong Kong SAR, PRC); Chinese Traditional (Big5) */ \
399 CP_ALIAS(950, "big5hkscs") /* ANSI/OEM Traditional Chinese (Hong Kong SAR); Chinese Traditional (Big5-HKSCS) */ \
400 CP_ALIAS(950, "big5-hkscs") /* alternative name for it */ \
401 CP_ALIAS(1026, "IBM1026") /* IBM EBCDIC Turkish (Latin 5) */ \
402 CP_ALIAS(1047, "IBM01047") /* IBM EBCDIC Latin 1/Open System */ \
403 CP_ALIAS(1140, "IBM01140") /* IBM EBCDIC US-Canada (037 + Euro symbol); IBM EBCDIC (US-Canada-Euro) */ \
404 CP_ALIAS(1141, "IBM01141") /* IBM EBCDIC Germany (20273 + Euro symbol); IBM EBCDIC (Germany-Euro) */ \
405 CP_ALIAS(1142, "IBM01142") /* IBM EBCDIC Denmark-Norway (20277 + Euro symbol); IBM EBCDIC (Denmark-Norway-Euro) */ \
406 CP_ALIAS(1143, "IBM01143") /* IBM EBCDIC Finland-Sweden (20278 + Euro symbol); IBM EBCDIC (Finland-Sweden-Euro) */ \
407 CP_ALIAS(1144, "IBM01144") /* IBM EBCDIC Italy (20280 + Euro symbol); IBM EBCDIC (Italy-Euro) */ \
408 CP_ALIAS(1145, "IBM01145") /* IBM EBCDIC Latin America-Spain (20284 + Euro symbol); IBM EBCDIC (Spain-Euro) */ \
409 CP_ALIAS(1146, "IBM01146") /* IBM EBCDIC United Kingdom (20285 + Euro symbol); IBM EBCDIC (UK-Euro) */ \
410 CP_ALIAS(1147, "IBM01147") /* IBM EBCDIC France (20297 + Euro symbol); IBM EBCDIC (France-Euro) */ \
411 CP_ALIAS(1148, "IBM01148") /* IBM EBCDIC International (500 + Euro symbol); IBM EBCDIC (International-Euro) */ \
412 CP_ALIAS(1149, "IBM01149") /* IBM EBCDIC Icelandic (20871 + Euro symbol); IBM EBCDIC (Icelandic-Euro) */ \
413 CP_ALIAS(1250, "windows-1250") /* ANSI Central European; Central European (Windows) */ \
414 CP_ALIAS(1251, "windows-1251") /* ANSI Cyrillic; Cyrillic (Windows) */ \
415 CP_ALIAS(1252, "windows-1252") /* ANSI Latin 1; Western European (Windows) */ \
416 CP_ALIAS(1253, "windows-1253") /* ANSI Greek; Greek (Windows) */ \
417 CP_ALIAS(1254, "windows-1254") /* ANSI Turkish; Turkish (Windows) */ \
418 CP_ALIAS(1255, "windows-1255") /* ANSI Hebrew; Hebrew (Windows) */ \
419 CP_ALIAS(1256, "windows-1256") /* ANSI Arabic; Arabic (Windows) */ \
420 CP_ALIAS(1257, "windows-1257") /* ANSI Baltic; Baltic (Windows) */ \
421 CP_ALIAS(1258, "windows-1258") /* ANSI/OEM Vietnamese; Vietnamese (Windows) */ \
422 CP_ALIAS(1361, "Johab") /* Korean (Johab) */ \
423 CP_ALIAS(10000, "macintosh") /* MAC Roman; Western European (Mac) */ \
424 CP_ALIAS(10001, "x-mac-japanese") /* Japanese (Mac) */ \
425 CP_ALIAS(10002, "x-mac-chinesetrad") /* MAC Traditional Chinese (Big5); Chinese Traditional (Mac) */ \
426 CP_ALIAS(10003, "x-mac-korean") /* Korean (Mac) */ \
427 CP_ALIAS(10004, "x-mac-arabic") /* Arabic (Mac) */ \
428 CP_ALIAS(10005, "x-mac-hebrew") /* Hebrew (Mac) */ \
429 CP_ALIAS(10006, "x-mac-greek") /* Greek (Mac) */ \
430 CP_ALIAS(10007, "x-mac-cyrillic") /* Cyrillic (Mac) */ \
431 CP_ALIAS(10008, "x-mac-chinesesimp") /* MAC Simplified Chinese (GB 2312); Chinese Simplified (Mac) */ \
432 CP_ALIAS(10010, "x-mac-romanian") /* Romanian (Mac) */ \
433 CP_ALIAS(10017, "x-mac-ukrainian") /* Ukrainian (Mac) */ \
434 CP_ALIAS(10021, "x-mac-thai") /* Thai (Mac) */ \
435 CP_ALIAS(10029, "x-mac-ce") /* MAC Latin 2; Central European (Mac) */ \
436 CP_ALIAS(10079, "x-mac-icelandic") /* Icelandic (Mac) */ \
437 CP_ALIAS(10081, "x-mac-turkish") /* Turkish (Mac) */ \
438 CP_ALIAS(10082, "x-mac-croatian") /* Croatian (Mac) */ \
439 CP_ALIAS(20000, "x-Chinese_CNS") /* CNS Taiwan; Chinese Traditional (CNS) */ \
440 CP_ALIAS(20001, "x-cp20001") /* TCA Taiwan */ \
441 CP_ALIAS(20002, "x_Chinese-Eten") /* Eten Taiwan; Chinese Traditional (Eten) */ \
442 CP_ALIAS(20003, "x-cp20003") /* IBM5550 Taiwan */ \
443 CP_ALIAS(20004, "x-cp20004") /* TeleText Taiwan */ \
444 CP_ALIAS(20005, "x-cp20005") /* Wang Taiwan */ \
445 CP_ALIAS(20105, "x-IA5") /* IA5 (IRV International Alphabet No. 5, 7-bit); Western European (IA5) */ \
446 CP_ALIAS(20106, "x-IA5-German") /* IA5 German (7-bit) */ \
447 CP_ALIAS(20107, "x-IA5-Swedish") /* IA5 Swedish (7-bit) */ \
448 CP_ALIAS(20108, "x-IA5-Norwegian") /* IA5 Norwegian (7-bit) */ \
449 CP_ALIAS(20127, "us-ascii") /* US-ASCII (7-bit) */ \
450 CP_ALIAS(20261, "x-cp20261") /* T.61 */ \
451 CP_ALIAS(20269, "x-cp20269") /* ISO 6937 Non-Spacing Accent */ \
452 CP_ALIAS(20273, "IBM273") /* IBM EBCDIC Germany */ \
453 CP_ALIAS(20277, "IBM277") /* IBM EBCDIC Denmark-Norway */ \
454 CP_ALIAS(20278, "IBM278") /* IBM EBCDIC Finland-Sweden */ \
455 CP_ALIAS(20280, "IBM280") /* IBM EBCDIC Italy */ \
456 CP_ALIAS(20284, "IBM284") /* IBM EBCDIC Latin America-Spain */ \
457 CP_ALIAS(20285, "IBM285") /* IBM EBCDIC United Kingdom */ \
458 CP_ALIAS(20290, "IBM290") /* IBM EBCDIC Japanese Katakana Extended */ \
459 CP_ALIAS(20297, "IBM297") /* IBM EBCDIC France */ \
460 CP_ALIAS(20420, "IBM420") /* IBM EBCDIC Arabic */ \
461 CP_ALIAS(20423, "IBM423") /* IBM EBCDIC Greek */ \
462 CP_ALIAS(20424, "IBM424") /* IBM EBCDIC Hebrew */ \
463 CP_ALIAS(20833, "x-EBCDIC-KoreanExtended") /* IBM EBCDIC Korean Extended */ \
464 CP_ALIAS(20838, "IBM-Thai") /* IBM EBCDIC Thai */ \
465 CP_ALIAS(20866, "koi8-r") /* Russian (KOI8-R); Cyrillic (KOI8-R) */ \
466 CP_ALIAS(20871, "IBM871") /* IBM EBCDIC Icelandic */ \
467 CP_ALIAS(20880, "IBM880") /* IBM EBCDIC Cyrillic Russian */ \
468 CP_ALIAS(20905, "IBM905") /* IBM EBCDIC Turkish */ \
469 CP_ALIAS(20924, "IBM00924") /* IBM EBCDIC Latin 1/Open System (1047 + Euro symbol) */ \
470 CP_ALIAS(20932, "EUC-JP") /* Japanese (JIS 0208-1990 and 0121-1990) */ \
471 CP_ALIAS(20936, "x-cp20936") /* Simplified Chinese (GB2312); Chinese Simplified (GB2312-80) */ \
472 CP_ALIAS(20949, "x-cp20949") /* Korean Wansung */ \
473 CP_ALIAS(21025, "cp1025") /* IBM EBCDIC Cyrillic Serbian-Bulgarian */ \
474 /* 21027 (deprecated) */ \
475 CP_ALIAS(21866, "koi8-u") /* Ukrainian (KOI8-U); Cyrillic (KOI8-U) */ \
476 CP_ALIAS(28591, "iso-8859-1") /* ISO 8859-1 Latin 1; Western European (ISO) */ \
477 CP_ALIAS(28591, "iso8859-1") /* ISO 8859-1 Latin 1; Western European (ISO) */ \
478 CP_ALIAS(28591, "iso_8859-1") \
479 CP_ALIAS(28591, "iso_8859_1") \
480 CP_ALIAS(28592, "iso-8859-2") /* ISO 8859-2 Central European; Central European (ISO) */ \
481 CP_ALIAS(28592, "iso8859-2") /* ISO 8859-2 Central European; Central European (ISO) */ \
482 CP_ALIAS(28592, "iso_8859-2") \
483 CP_ALIAS(28592, "iso_8859_2") \
484 CP_ALIAS(28593, "iso-8859-3") /* ISO 8859-3 Latin 3 */ \
485 CP_ALIAS(28593, "iso8859-3") /* ISO 8859-3 Latin 3 */ \
486 CP_ALIAS(28593, "iso_8859-3") \
487 CP_ALIAS(28593, "iso_8859_3") \
488 CP_ALIAS(28594, "iso-8859-4") /* ISO 8859-4 Baltic */ \
489 CP_ALIAS(28594, "iso8859-4") /* ISO 8859-4 Baltic */ \
490 CP_ALIAS(28594, "iso_8859-4") \
491 CP_ALIAS(28594, "iso_8859_4") \
492 CP_ALIAS(28595, "iso-8859-5") /* ISO 8859-5 Cyrillic */ \
493 CP_ALIAS(28595, "iso8859-5") /* ISO 8859-5 Cyrillic */ \
494 CP_ALIAS(28595, "iso_8859-5") \
495 CP_ALIAS(28595, "iso_8859_5") \
496 CP_ALIAS(28596, "iso-8859-6") /* ISO 8859-6 Arabic */ \
497 CP_ALIAS(28596, "iso8859-6") /* ISO 8859-6 Arabic */ \
498 CP_ALIAS(28596, "iso_8859-6") \
499 CP_ALIAS(28596, "iso_8859_6") \
500 CP_ALIAS(28597, "iso-8859-7") /* ISO 8859-7 Greek */ \
501 CP_ALIAS(28597, "iso8859-7") /* ISO 8859-7 Greek */ \
502 CP_ALIAS(28597, "iso_8859-7") \
503 CP_ALIAS(28597, "iso_8859_7") \
504 CP_ALIAS(28598, "iso-8859-8") /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ \
505 CP_ALIAS(28598, "iso8859-8") /* ISO 8859-8 Hebrew; Hebrew (ISO-Visual) */ \
506 CP_ALIAS(28598, "iso_8859-8") \
507 CP_ALIAS(28598, "iso_8859_8") \
508 CP_ALIAS(28599, "iso-8859-9") /* ISO 8859-9 Turkish */ \
509 CP_ALIAS(28599, "iso8859-9") /* ISO 8859-9 Turkish */ \
510 CP_ALIAS(28599, "iso_8859-9") \
511 CP_ALIAS(28599, "iso_8859_9") \
512 CP_ALIAS(28603, "iso-8859-13") /* ISO 8859-13 Estonian */ \
513 CP_ALIAS(28603, "iso8859-13") /* ISO 8859-13 Estonian */ \
514 CP_ALIAS(28603, "iso_8859-13") \
515 CP_ALIAS(28603, "iso_8859_13") \
516 CP_ALIAS(28605, "iso-8859-15") /* ISO 8859-15 Latin 9 */ \
517 CP_ALIAS(28605, "iso8859-15") /* ISO 8859-15 Latin 9 */ \
518 CP_ALIAS(28605, "iso_8859-15") \
519 CP_ALIAS(28605, "iso_8859_15") \
520 CP_ALIAS(29001, "x-Europa") /* Europa 3 */ \
521 CP_ALIAS(38598, "iso-8859-8-i") /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ \
522 CP_ALIAS(38598, "iso8859-8-i") /* ISO 8859-8 Hebrew; Hebrew (ISO-Logical) */ \
523 CP_ALIAS(38598, "iso_8859-8-i") \
524 CP_ALIAS(38598, "iso_8859_8-i") \
525 CP_ALIAS(50220, "iso-2022-jp") /* ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS) */ \
526 CP_ALIAS(50221, "csISO2022JP") /* ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow 1 byte Kana) */ \
527 CP_ALIAS(50222, "iso-2022-jp") /* ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte Kana - SO/SI) */ \
528 CP_ALIAS(50225, "iso-2022-kr") /* ISO 2022 Korean */ \
529 CP_ALIAS(50225, "iso2022-kr") /* ISO 2022 Korean */ \
530 CP_ALIAS(50227, "x-cp50227") /* ISO 2022 Simplified Chinese; Chinese Simplified (ISO 2022) */ \
531 /* 50229 ISO 2022 Traditional Chinese */ \
532 /* 50930 EBCDIC Japanese (Katakana) Extended */ \
533 /* 50931 EBCDIC US-Canada and Japanese */ \
534 /* 50933 EBCDIC Korean Extended and Korean */ \
535 /* 50935 EBCDIC Simplified Chinese Extended and Simplified Chinese */ \
536 /* 50936 EBCDIC Simplified Chinese */ \
537 /* 50937 EBCDIC US-Canada and Traditional Chinese */ \
538 /* 50939 EBCDIC Japanese (Latin) Extended and Japanese */ \
539 CP_ALIAS(51932, "euc-jp") /* EUC Japanese */ \
540 CP_ALIAS(51936, "EUC-CN") /* EUC Simplified Chinese; Chinese Simplified (EUC) */ \
541 CP_ALIAS(51949, "euc-kr") /* EUC Korean */ \
542 /* 51950 EUC Traditional Chinese */ \
543 CP_ALIAS(52936, "hz-gb-2312") /* HZ-GB2312 Simplified Chinese; Chinese Simplified (HZ) */ \
544 CP_ALIAS(54936, "GB18030") /* Windows XP and later: GB18030 Simplified Chinese (4 byte); Chinese Simplified (GB18030) */ \
545 CP_ALIAS(57002, "x-iscii-de") /* ISCII Devanagari */ \
546 CP_ALIAS(57003, "x-iscii-be") /* ISCII Bengali */ \
547 CP_ALIAS(57004, "x-iscii-ta") /* ISCII Tamil */ \
548 CP_ALIAS(57005, "x-iscii-te") /* ISCII Telugu */ \
549 CP_ALIAS(57006, "x-iscii-as") /* ISCII Assamese */ \
550 CP_ALIAS(57007, "x-iscii-or") /* ISCII Oriya */ \
551 CP_ALIAS(57008, "x-iscii-ka") /* ISCII Kannada */ \
552 CP_ALIAS(57009, "x-iscii-ma") /* ISCII Malayalam */ \
553 CP_ALIAS(57010, "x-iscii-gu") /* ISCII Gujarati */ \
554 CP_ALIAS(57011, "x-iscii-pa") /* ISCII Punjabi */
555
556#define CP_ALIAS(codepage, alias) codepage,
557static const int cp_codepage[] = {
558 CP_ALIAS_LIST
559};
560#undef CP_ALIAS
561
562#define CP_ALIAS(codepage, alias) alias"\0"
563static const char cp_alias[] ALIGN1 =
564 CP_ALIAS_LIST;
565#undef CP_ALIAS
566
567/*
568 * SJIS SHIFTJIS table CP932 table
569 * ---- --------------------------- --------------------------------
570 * 5C U+00A5 YEN SIGN U+005C REVERSE SOLIDUS
571 * 7E U+203E OVERLINE U+007E TILDE
572 * 815C U+2014 EM DASH U+2015 HORIZONTAL BAR
573 * 815F U+005C REVERSE SOLIDUS U+FF3C FULLWIDTH REVERSE SOLIDUS
574 * 8160 U+301C WAVE DASH U+FF5E FULLWIDTH TILDE
575 * 8161 U+2016 DOUBLE VERTICAL LINE U+2225 PARALLEL TO
576 * 817C U+2212 MINUS SIGN U+FF0D FULLWIDTH HYPHEN-MINUS
577 * 8191 U+00A2 CENT SIGN U+FFE0 FULLWIDTH CENT SIGN
578 * 8192 U+00A3 POUND SIGN U+FFE1 FULLWIDTH POUND SIGN
579 * 81CA U+00AC NOT SIGN U+FFE2 FULLWIDTH NOT SIGN
580 *
581 * EUC-JP and ISO-2022-JP should be compatible with CP932.
582 *
583 * Kernel and MLang have different Unicode mapping table. Make sure
584 * which API is used.
585 */
586static compat_t cp932_compat[] = {
587 {0x00A5, 0x005C, COMPAT_OUT},
588 {0x203E, 0x007E, COMPAT_OUT},
589 {0x2014, 0x2015, COMPAT_OUT},
590 {0x301C, 0xFF5E, COMPAT_OUT},
591 {0x2016, 0x2225, COMPAT_OUT},
592 {0x2212, 0xFF0D, COMPAT_OUT},
593 {0x00A2, 0xFFE0, COMPAT_OUT},
594 {0x00A3, 0xFFE1, COMPAT_OUT},
595 {0x00AC, 0xFFE2, COMPAT_OUT},
596 {0, 0, 0}
597};
598
599static compat_t cp20932_compat[] = {
600 {0x00A5, 0x005C, COMPAT_OUT},
601 {0x203E, 0x007E, COMPAT_OUT},
602 {0x2014, 0x2015, COMPAT_OUT},
603 {0xFF5E, 0x301C, COMPAT_OUT|COMPAT_IN},
604 {0x2225, 0x2016, COMPAT_OUT|COMPAT_IN},
605 {0xFF0D, 0x2212, COMPAT_OUT|COMPAT_IN},
606 {0xFFE0, 0x00A2, COMPAT_OUT|COMPAT_IN},
607 {0xFFE1, 0x00A3, COMPAT_OUT|COMPAT_IN},
608 {0xFFE2, 0x00AC, COMPAT_OUT|COMPAT_IN},
609 {0, 0, 0}
610};
611
612static compat_t *cp51932_compat = cp932_compat;
613
614/* cp20932_compat for kernel. cp932_compat for mlang. */
615static compat_t *cp5022x_compat = cp932_compat;
616
617typedef HRESULT (WINAPI *CONVERTINETMULTIBYTETOUNICODE)(
618 LPDWORD lpdwMode,
619 DWORD dwSrcEncoding,
620 LPCSTR lpSrcStr,
621 LPINT lpnMultiCharCount,
622 LPWSTR lpDstStr,
623 LPINT lpnWideCharCount
624);
625
626typedef HRESULT (WINAPI *CONVERTINETUNICODETOMULTIBYTE)(
627 LPDWORD lpdwMode,
628 DWORD dwEncoding,
629 LPCWSTR lpSrcStr,
630 LPINT lpnWideCharCount,
631 LPSTR lpDstStr,
632 LPINT lpnMultiCharCount
633);
634
635static CONVERTINETMULTIBYTETOUNICODE ConvertINetMultiByteToUnicode;
636static CONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte;
637
638static int
639load_mlang(void)
640{
641 HMODULE h;
642 if (ConvertINetMultiByteToUnicode != NULL)
643 return TRUE;
644 h = LoadLibrary(TEXT("mlang.dll"));
645 if (!h)
646 return FALSE;
647 ConvertINetMultiByteToUnicode = (CONVERTINETMULTIBYTETOUNICODE)GetProcAddressA(h, "ConvertINetMultiByteToUnicode");
648 ConvertINetUnicodeToMultiByte = (CONVERTINETUNICODETOMULTIBYTE)GetProcAddressA(h, "ConvertINetUnicodeToMultiByte");
649 return TRUE;
650}
651
652static iconv_t
653iconv_open(const char *tocode, const char *fromcode)
654{
655 rec_iconv_t *cd;
656
657 cd = (rec_iconv_t *)xzalloc(sizeof(rec_iconv_t));
658
659 /* reset the errno to prevent reporting wrong error code.
660 * 0 for unsorted error. */
661 errno = 0;
662 if (make_csconv(fromcode, &cd->from) && make_csconv(tocode, &cd->to)) {
663 cd->cd = (iconv_t)cd;
664 return (iconv_t)cd;
665 }
666
667 free(cd);
668 return (iconv_t)(-1);
669}
670
671static int
672iconv_close(iconv_t _cd)
673{
674 free(_cd);
675 return 0;
676}
677
678static size_t
679iconv(iconv_t _cd, const char **inbuf, size_t *inbytesleft, char **outbuf, size_t *outbytesleft)
680{
681 rec_iconv_t *cd = (rec_iconv_t *)_cd;
682 ushort wbuf[MB_CHAR_MAX]; /* enough room for one character */
683 int insize;
684 int outsize;
685 int wsize;
686 DWORD frommode;
687 DWORD tomode;
688 uint wc;
689 compat_t *cp;
690 int i;
691
692 if (inbuf == NULL || *inbuf == NULL)
693 {
694 if (outbuf != NULL && *outbuf != NULL && cd->to.flush != NULL)
695 {
696 tomode = cd->to.mode;
697 outsize = cd->to.flush(&cd->to, (uchar *)*outbuf, *outbytesleft);
698 if (outsize == -1)
699 {
700 if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG)
701 {
702 outsize = 0;
703 }
704 else
705 {
706 cd->to.mode = tomode;
707 return (size_t)(-1);
708 }
709 }
710 *outbuf += outsize;
711 *outbytesleft -= outsize;
712 }
713 cd->from.mode = 0;
714 cd->to.mode = 0;
715 return 0;
716 }
717
718 while (*inbytesleft != 0)
719 {
720 frommode = cd->from.mode;
721 tomode = cd->to.mode;
722 wsize = MB_CHAR_MAX;
723
724 insize = cd->from.mbtowc(&cd->from, (const uchar *)*inbuf, *inbytesleft, wbuf, &wsize);
725 if (insize == -1)
726 {
727 if (cd->to.flags & FLAG_IGNORE)
728 {
729 cd->from.mode = frommode;
730 insize = 1;
731 wsize = 0;
732 }
733 else
734 {
735 cd->from.mode = frommode;
736 return (size_t)(-1);
737 }
738 }
739
740 if (wsize == 0)
741 {
742 *inbuf += insize;
743 *inbytesleft -= insize;
744 continue;
745 }
746
747 if (cd->from.compat != NULL)
748 {
749 wc = utf16_to_ucs4(wbuf);
750 cp = cd->from.compat;
751 for (i = 0; cp[i].in != 0; ++i)
752 {
753 if ((cp[i].flag & COMPAT_IN) && cp[i].out == wc)
754 {
755 ucs4_to_utf16(cp[i].in, wbuf, &wsize);
756 break;
757 }
758 }
759 }
760
761 if (cd->to.compat != NULL)
762 {
763 wc = utf16_to_ucs4(wbuf);
764 cp = cd->to.compat;
765 for (i = 0; cp[i].in != 0; ++i)
766 {
767 if ((cp[i].flag & COMPAT_OUT) && cp[i].in == wc)
768 {
769 ucs4_to_utf16(cp[i].out, wbuf, &wsize);
770 break;
771 }
772 }
773 }
774
775 outsize = cd->to.wctomb(&cd->to, wbuf, wsize, (uchar *)*outbuf, *outbytesleft);
776 if (outsize == -1)
777 {
778 if ((cd->to.flags & FLAG_IGNORE) && errno != E2BIG)
779 {
780 cd->to.mode = tomode;
781 outsize = 0;
782 }
783 else
784 {
785 cd->from.mode = frommode;
786 cd->to.mode = tomode;
787 return (size_t)(-1);
788 }
789 }
790
791 *inbuf += insize;
792 *outbuf += outsize;
793 *inbytesleft -= insize;
794 *outbytesleft -= outsize;
795 }
796
797 return 0;
798}
799
800static int
801make_csconv(const char *_name, csconv_t *cv)
802{
803 CPINFO cpinfo;
804 int use_compat = TRUE;
805 int flag = 0;
806 char *name;
807 char *p, *s;
808
809 name = xstrdup(_name);
810
811 /* check for option "enc_name//opt1//opt2" */
812 while ((p = strrstr(name, "//")) != NULL)
813 {
814 for (s = p + 2; *s; ++s)
815 *s = tolower(*s);
816 switch (index_in_strings("nocompat\0translit\0ignore\0", p + 2)) {
817 case 0:
818 use_compat = FALSE;
819 break;
820 case 1:
821 flag |= FLAG_TRANSLIT;
822 break;
823 case 2:
824 flag |= FLAG_IGNORE;
825 break;
826 }
827 *p = 0;
828 }
829
830 cv->mode = 0;
831 cv->flags = flag;
832 cv->mblen = NULL;
833 cv->flush = NULL;
834 cv->compat = NULL;
835 cv->codepage = name_to_codepage(name);
836 if (cv->codepage == 1200 || cv->codepage == 1201)
837 {
838 cv->mbtowc = utf16_mbtowc;
839 cv->wctomb = utf16_wctomb;
840 if (_stricmp(name, "UTF-16") == 0 || _stricmp(name, "UTF16") == 0 ||
841 _stricmp(name, "UCS-2") == 0 || _stricmp(name, "UCS2") == 0 ||
842 _stricmp(name,"UCS-2-INTERNAL") == 0)
843 cv->flags |= FLAG_USE_BOM;
844 }
845 else if (cv->codepage == 12000 || cv->codepage == 12001)
846 {
847 cv->mbtowc = utf32_mbtowc;
848 cv->wctomb = utf32_wctomb;
849 if (_stricmp(name, "UTF-32") == 0 || _stricmp(name, "UTF32") == 0 ||
850 _stricmp(name, "UCS-4") == 0 || _stricmp(name, "UCS4") == 0)
851 cv->flags |= FLAG_USE_BOM;
852 }
853 else if (cv->codepage == 65001)
854 {
855 cv->mbtowc = kernel_mbtowc;
856 cv->wctomb = kernel_wctomb;
857 cv->mblen = utf8_mblen;
858 }
859 else if ((cv->codepage == 50220 || cv->codepage == 50221 || cv->codepage == 50222) && load_mlang())
860 {
861 cv->mbtowc = iso2022jp_mbtowc;
862 cv->wctomb = iso2022jp_wctomb;
863 cv->flush = iso2022jp_flush;
864 }
865 else if (cv->codepage == 51932 && load_mlang())
866 {
867 cv->mbtowc = mlang_mbtowc;
868 cv->wctomb = mlang_wctomb;
869 cv->mblen = eucjp_mblen;
870 }
871 else if (IsValidCodePage(cv->codepage)
872 && GetCPInfo(cv->codepage, &cpinfo) != 0)
873 {
874 cv->mbtowc = kernel_mbtowc;
875 cv->wctomb = kernel_wctomb;
876 if (cpinfo.MaxCharSize == 1)
877 cv->mblen = sbcs_mblen;
878 else if (cpinfo.MaxCharSize == 2)
879 cv->mblen = dbcs_mblen;
880 else
881 cv->mblen = mbcs_mblen;
882 }
883 else
884 {
885 /* not supported */
886 free(name);
887 errno = EINVAL;
888 return FALSE;
889 }
890
891 if (use_compat)
892 {
893 switch (cv->codepage)
894 {
895 case 932: cv->compat = cp932_compat; break;
896 case 20932: cv->compat = cp20932_compat; break;
897 case 51932: cv->compat = cp51932_compat; break;
898 case 50220: case 50221: case 50222: cv->compat = cp5022x_compat; break;
899 }
900 }
901
902 free(name);
903
904 return TRUE;
905}
906
907static int
908name_to_codepage(const char *name)
909{
910 int i;
911 const char *alias;
912
913 if (*name == '\0' || strcmp(name, "char") == 0)
914 return GetACP();
915 else if (strcmp(name, "wchar_t") == 0)
916 return 1200;
917 else if (_strnicmp(name, "cp", 2) == 0)
918 return atoi(name + 2); /* CP123 */
919 else if ('0' <= name[0] && name[0] <= '9')
920 return atoi(name); /* 123 */
921 else if (_strnicmp(name, "xx", 2) == 0)
922 return atoi(name + 2); /* XX123 for debug */
923
924 i = 0;
925 alias = cp_alias;
926 while (*alias) {
927 if (_stricmp(alias, name) == 0) {
928 return cp_codepage[i];
929 }
930 alias += strlen(alias) + 1;
931 ++i;
932 }
933 return -1;
934}
935
936/*
937 * http://www.faqs.org/rfcs/rfc2781.html
938 */
939static uint
940utf16_to_ucs4(const ushort *wbuf)
941{
942 uint wc = wbuf[0];
943 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
944 wc = ((wbuf[0] & 0x3FF) << 10) + (wbuf[1] & 0x3FF) + 0x10000;
945 return wc;
946}
947
948static void
949ucs4_to_utf16(uint wc, ushort *wbuf, int *wbufsize)
950{
951 if (wc < 0x10000)
952 {
953 wbuf[0] = wc;
954 *wbufsize = 1;
955 }
956 else
957 {
958 wc -= 0x10000;
959 wbuf[0] = 0xD800 | ((wc >> 10) & 0x3FF);
960 wbuf[1] = 0xDC00 | (wc & 0x3FF);
961 *wbufsize = 2;
962 }
963}
964
965/*
966 * Check if codepage is one of those for which the dwFlags parameter
967 * to MultiByteToWideChar() must be zero. Return zero or
968 * MB_ERR_INVALID_CHARS. The docs in Platform SDK for Windows
969 * Server 2003 R2 claims that also codepage 65001 is one of these, but
970 * that doesn't seem to be the case. The MSDN docs for MSVS2008 leave
971 * out 65001 (UTF-8), and that indeed seems to be the case on XP, it
972 * works fine to pass MB_ERR_INVALID_CHARS in dwFlags when converting
973 * from UTF-8.
974 */
975static int
976mbtowc_flags(int codepage)
977{
978 return (codepage == 50220 || codepage == 50221 ||
979 codepage == 50222 || codepage == 50225 ||
980 codepage == 50227 || codepage == 50229 ||
981 codepage == 52936 || codepage == 54936 ||
982 (codepage >= 57002 && codepage <= 57011) ||
983 codepage == 65000 || codepage == 42) ? 0 : MB_ERR_INVALID_CHARS;
984}
985
986/*
987 * Check if codepage is one those for which the lpUsedDefaultChar
988 * parameter to WideCharToMultiByte() must be NULL. The docs in
989 * Platform SDK for Windows Server 2003 R2 claims that this is the
990 * list below, while the MSDN docs for MSVS2008 claim that it is only
991 * for 65000 (UTF-7) and 65001 (UTF-8). This time the earlier Platform
992 * SDK seems to be correct, at least for XP.
993 */
994static int
995must_use_null_useddefaultchar(int codepage)
996{
997 return (codepage == 65000 || codepage == 65001 ||
998 codepage == 50220 || codepage == 50221 ||
999 codepage == 50222 || codepage == 50225 ||
1000 codepage == 50227 || codepage == 50229 ||
1001 codepage == 52936 || codepage == 54936 ||
1002 (codepage >= 57002 && codepage <= 57011) ||
1003 codepage == 42);
1004}
1005
1006static int
1007seterror(int err)
1008{
1009 errno = err;
1010 return -1;
1011}
1012
1013static int
1014sbcs_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf UNUSED_PARAM,
1015 int bufsize UNUSED_PARAM)
1016{
1017 return 1;
1018}
1019
1020static int
1021dbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
1022{
1023 int len = IsDBCSLeadByteEx(cv->codepage, buf[0]) ? 2 : 1;
1024 if (bufsize < len)
1025 return seterror(EINVAL);
1026 return len;
1027}
1028
1029static int
1030mbcs_mblen(csconv_t *cv, const uchar *buf, int bufsize)
1031{
1032 int len = 0;
1033
1034 if (cv->codepage == 54936) {
1035 if (buf[0] <= 0x7F)
1036 len = 1;
1037 else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
1038 bufsize >= 2 &&
1039 ((buf[1] >= 0x40 && buf[1] <= 0x7E) ||
1040 (buf[1] >= 0x80 && buf[1] <= 0xFE)))
1041 len = 2;
1042 else if (buf[0] >= 0x81 && buf[0] <= 0xFE &&
1043 bufsize >= 4 &&
1044 buf[1] >= 0x30 && buf[1] <= 0x39)
1045 len = 4;
1046 else
1047 return seterror(EINVAL);
1048 return len;
1049 }
1050 else
1051 return seterror(EINVAL);
1052}
1053
1054static int
1055utf8_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize)
1056{
1057 int len = 0;
1058
1059 if (buf[0] < 0x80) len = 1;
1060 else if ((buf[0] & 0xE0) == 0xC0) len = 2;
1061 else if ((buf[0] & 0xF0) == 0xE0) len = 3;
1062 else if ((buf[0] & 0xF8) == 0xF0) len = 4;
1063 else if ((buf[0] & 0xFC) == 0xF8) len = 5;
1064 else if ((buf[0] & 0xFE) == 0xFC) len = 6;
1065
1066 if (len == 0)
1067 return seterror(EILSEQ);
1068 else if (bufsize < len)
1069 return seterror(EINVAL);
1070 return len;
1071}
1072
1073static int
1074eucjp_mblen(csconv_t *cv UNUSED_PARAM, const uchar *buf, int bufsize)
1075{
1076 if (buf[0] < 0x80) /* ASCII */
1077 return 1;
1078 else if (buf[0] == 0x8E) /* JIS X 0201 */
1079 {
1080 if (bufsize < 2)
1081 return seterror(EINVAL);
1082 else if (!(0xA1 <= buf[1] && buf[1] <= 0xDF))
1083 return seterror(EILSEQ);
1084 return 2;
1085 }
1086 else if (buf[0] == 0x8F) /* JIS X 0212 */
1087 {
1088 if (bufsize < 3)
1089 return seterror(EINVAL);
1090 else if (!(0xA1 <= buf[1] && buf[1] <= 0xFE)
1091 || !(0xA1 <= buf[2] && buf[2] <= 0xFE))
1092 return seterror(EILSEQ);
1093 return 3;
1094 }
1095 else /* JIS X 0208 */
1096 {
1097 if (bufsize < 2)
1098 return seterror(EINVAL);
1099 else if (!(0xA1 <= buf[0] && buf[0] <= 0xFE)
1100 || !(0xA1 <= buf[1] && buf[1] <= 0xFE))
1101 return seterror(EILSEQ);
1102 return 2;
1103 }
1104}
1105
1106static int
1107kernel_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1108{
1109 int len;
1110
1111 len = cv->mblen(cv, buf, bufsize);
1112 if (len == -1)
1113 return -1;
1114 /* If converting from ASCII, reject 8bit
1115 * chars. MultiByteToWideChar() doesn't. Note that for ASCII we
1116 * know that the mblen function is sbcs_mblen() so len is 1.
1117 */
1118 if (cv->codepage == 20127 && buf[0] >= 0x80)
1119 return seterror(EILSEQ);
1120 *wbufsize = MultiByteToWideChar(cv->codepage, mbtowc_flags (cv->codepage),
1121 (const char *)buf, len, (wchar_t *)wbuf, *wbufsize);
1122 if (*wbufsize == 0)
1123 return seterror(EILSEQ);
1124 return len;
1125}
1126
1127static int
1128kernel_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1129{
1130 BOOL usedDefaultChar = 0;
1131 BOOL *p = NULL;
1132 int flags = 0;
1133 int len;
1134
1135 if (bufsize == 0)
1136 return seterror(E2BIG);
1137 if (!must_use_null_useddefaultchar(cv->codepage))
1138 {
1139 p = &usedDefaultChar;
1140#ifdef WC_NO_BEST_FIT_CHARS
1141 if (!(cv->flags & FLAG_TRANSLIT))
1142 flags |= WC_NO_BEST_FIT_CHARS;
1143#endif
1144 }
1145 len = WideCharToMultiByte(cv->codepage, flags,
1146 (const wchar_t *)wbuf, wbufsize, (char *)buf, bufsize, NULL, p);
1147 if (len == 0)
1148 {
1149 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
1150 return seterror(E2BIG);
1151 return seterror(EILSEQ);
1152 }
1153 else if (usedDefaultChar && !(cv->flags & FLAG_TRANSLIT))
1154 return seterror(EILSEQ);
1155 else if (cv->mblen(cv, buf, len) != len) /* validate result */
1156 return seterror(EILSEQ);
1157 return len;
1158}
1159
1160/*
1161 * It seems that the mode (cv->mode) is fixnum.
1162 * For example, when converting iso-2022-jp(cp50221) to unicode:
1163 * in ascii sequence: mode=0xC42C0000
1164 * in jisx0208 sequence: mode=0xC42C0001
1165 * "C42C" is same for each convert session.
1166 * It should be: ((codepage-1)<<16)|state
1167 */
1168static int
1169mlang_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1170{
1171 int len;
1172 int insize;
1173 HRESULT hr;
1174
1175 len = cv->mblen(cv, buf, bufsize);
1176 if (len == -1)
1177 return -1;
1178 insize = len;
1179 hr = ConvertINetMultiByteToUnicode(&cv->mode, cv->codepage,
1180 (const char *)buf, &insize, (wchar_t *)wbuf, wbufsize);
1181 if (hr != S_OK || insize != len)
1182 return seterror(EILSEQ);
1183 return len;
1184}
1185
1186static int
1187mlang_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1188{
1189 char tmpbuf[MB_CHAR_MAX]; /* enough room for one character */
1190 int tmpsize = MB_CHAR_MAX;
1191 int insize = wbufsize;
1192 HRESULT hr;
1193
1194 hr = ConvertINetUnicodeToMultiByte(&cv->mode, cv->codepage,
1195 (const wchar_t *)wbuf, &wbufsize, tmpbuf, &tmpsize);
1196 if (hr != S_OK || insize != wbufsize)
1197 return seterror(EILSEQ);
1198 else if (bufsize < tmpsize)
1199 return seterror(E2BIG);
1200 else if (cv->mblen(cv, (uchar *)tmpbuf, tmpsize) != tmpsize)
1201 return seterror(EILSEQ);
1202 memcpy(buf, tmpbuf, tmpsize);
1203 return tmpsize;
1204}
1205
1206static int
1207utf16_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1208{
1209 int codepage = cv->codepage;
1210
1211 /* swap endian: 1200 <-> 1201 */
1212 if (cv->mode & UNICODE_MODE_SWAPPED)
1213 codepage ^= 1;
1214
1215 if (bufsize < 2)
1216 return seterror(EINVAL);
1217 if (codepage == 1200) /* little endian */
1218 wbuf[0] = (buf[1] << 8) | buf[0];
1219 else if (codepage == 1201) /* big endian */
1220 wbuf[0] = (buf[0] << 8) | buf[1];
1221
1222 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1223 {
1224 cv->mode |= UNICODE_MODE_BOM_DONE;
1225 if (wbuf[0] == 0xFFFE)
1226 {
1227 cv->mode |= UNICODE_MODE_SWAPPED;
1228 *wbufsize = 0;
1229 return 2;
1230 }
1231 else if (wbuf[0] == 0xFEFF)
1232 {
1233 *wbufsize = 0;
1234 return 2;
1235 }
1236 }
1237
1238 if (0xDC00 <= wbuf[0] && wbuf[0] <= 0xDFFF)
1239 return seterror(EILSEQ);
1240 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1241 {
1242 if (bufsize < 4)
1243 return seterror(EINVAL);
1244 if (codepage == 1200) /* little endian */
1245 wbuf[1] = (buf[3] << 8) | buf[2];
1246 else if (codepage == 1201) /* big endian */
1247 wbuf[1] = (buf[2] << 8) | buf[3];
1248 if (!(0xDC00 <= wbuf[1] && wbuf[1] <= 0xDFFF))
1249 return seterror(EILSEQ);
1250 *wbufsize = 2;
1251 return 4;
1252 }
1253 *wbufsize = 1;
1254 return 2;
1255}
1256
1257static int
1258utf16_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1259{
1260 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1261 {
1262 int r;
1263
1264 cv->mode |= UNICODE_MODE_BOM_DONE;
1265 if (bufsize < 2)
1266 return seterror(E2BIG);
1267 if (cv->codepage == 1200) /* little endian */
1268 memcpy(buf, "\xFF\xFE", 2);
1269 else if (cv->codepage == 1201) /* big endian */
1270 memcpy(buf, "\xFE\xFF", 2);
1271
1272 r = utf16_wctomb(cv, wbuf, wbufsize, buf + 2, bufsize - 2);
1273 if (r == -1)
1274 return -1;
1275 return r + 2;
1276 }
1277
1278 if (bufsize < 2)
1279 return seterror(E2BIG);
1280 if (cv->codepage == 1200) /* little endian */
1281 {
1282 buf[0] = (wbuf[0] & 0x00FF);
1283 buf[1] = (wbuf[0] & 0xFF00) >> 8;
1284 }
1285 else if (cv->codepage == 1201) /* big endian */
1286 {
1287 buf[0] = (wbuf[0] & 0xFF00) >> 8;
1288 buf[1] = (wbuf[0] & 0x00FF);
1289 }
1290 if (0xD800 <= wbuf[0] && wbuf[0] <= 0xDBFF)
1291 {
1292 if (bufsize < 4)
1293 return seterror(E2BIG);
1294 if (cv->codepage == 1200) /* little endian */
1295 {
1296 buf[2] = (wbuf[1] & 0x00FF);
1297 buf[3] = (wbuf[1] & 0xFF00) >> 8;
1298 }
1299 else if (cv->codepage == 1201) /* big endian */
1300 {
1301 buf[2] = (wbuf[1] & 0xFF00) >> 8;
1302 buf[3] = (wbuf[1] & 0x00FF);
1303 }
1304 return 4;
1305 }
1306 return 2;
1307}
1308
1309static int
1310utf32_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1311{
1312 int codepage = cv->codepage;
1313 uint wc = 0xD800;
1314
1315 /* swap endian: 12000 <-> 12001 */
1316 if (cv->mode & UNICODE_MODE_SWAPPED)
1317 codepage ^= 1;
1318
1319 if (bufsize < 4)
1320 return seterror(EINVAL);
1321 if (codepage == 12000) /* little endian */
1322 wc = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
1323 else if (codepage == 12001) /* big endian */
1324 wc = (buf[0] << 24) | (buf[1] << 16) | (buf[2] << 8) | buf[3];
1325
1326 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1327 {
1328 cv->mode |= UNICODE_MODE_BOM_DONE;
1329 if (wc == 0xFFFE0000)
1330 {
1331 cv->mode |= UNICODE_MODE_SWAPPED;
1332 *wbufsize = 0;
1333 return 4;
1334 }
1335 else if (wc == 0x0000FEFF)
1336 {
1337 *wbufsize = 0;
1338 return 4;
1339 }
1340 }
1341
1342 if ((0xD800 <= wc && wc <= 0xDFFF) || 0x10FFFF < wc)
1343 return seterror(EILSEQ);
1344 ucs4_to_utf16(wc, wbuf, wbufsize);
1345 return 4;
1346}
1347
1348static int
1349utf32_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1350{
1351 uint wc;
1352
1353 if ((cv->flags & FLAG_USE_BOM) && !(cv->mode & UNICODE_MODE_BOM_DONE))
1354 {
1355 int r;
1356
1357 cv->mode |= UNICODE_MODE_BOM_DONE;
1358 if (bufsize < 4)
1359 return seterror(E2BIG);
1360 if (cv->codepage == 12000) /* little endian */
1361 memcpy(buf, "\xFF\xFE\x00\x00", 4);
1362 else if (cv->codepage == 12001) /* big endian */
1363 memcpy(buf, "\x00\x00\xFE\xFF", 4);
1364
1365 r = utf32_wctomb(cv, wbuf, wbufsize, buf + 4, bufsize - 4);
1366 if (r == -1)
1367 return -1;
1368 return r + 4;
1369 }
1370
1371 if (bufsize < 4)
1372 return seterror(E2BIG);
1373 wc = utf16_to_ucs4(wbuf);
1374 if (cv->codepage == 12000) /* little endian */
1375 {
1376 buf[0] = wc & 0x000000FF;
1377 buf[1] = (wc & 0x0000FF00) >> 8;
1378 buf[2] = (wc & 0x00FF0000) >> 16;
1379 buf[3] = (wc & 0xFF000000) >> 24;
1380 }
1381 else if (cv->codepage == 12001) /* big endian */
1382 {
1383 buf[0] = (wc & 0xFF000000) >> 24;
1384 buf[1] = (wc & 0x00FF0000) >> 16;
1385 buf[2] = (wc & 0x0000FF00) >> 8;
1386 buf[3] = wc & 0x000000FF;
1387 }
1388 return 4;
1389}
1390
1391/*
1392 * 50220: ISO 2022 Japanese with no halfwidth Katakana; Japanese (JIS)
1393 * 50221: ISO 2022 Japanese with halfwidth Katakana; Japanese (JIS-Allow
1394 * 1 byte Kana)
1395 * 50222: ISO 2022 Japanese JIS X 0201-1989; Japanese (JIS-Allow 1 byte
1396 * Kana - SO/SI)
1397 *
1398 * MultiByteToWideChar() and WideCharToMultiByte() behave differently
1399 * depending on Windows version. On XP, WideCharToMultiByte() doesn't
1400 * terminate result sequence with ascii escape. But Vista does.
1401 * Use MLang instead.
1402 */
1403
1404#define ISO2022_MODE(cs, shift) (((cs) << 8) | (shift))
1405#define ISO2022_MODE_CS(mode) (((mode) >> 8) & 0xFF)
1406#define ISO2022_MODE_SHIFT(mode) ((mode) & 0xFF)
1407
1408#define ISO2022_SI 0
1409#define ISO2022_SO 1
1410
1411/* shift in */
1412static const char iso2022_SI_seq[] = "\x0F";
1413/* shift out */
1414static const char iso2022_SO_seq[] = "\x0E";
1415
1416typedef struct iso2022_esc_t iso2022_esc_t;
1417struct iso2022_esc_t {
1418 const char *esc;
1419 int esc_len;
1420 int len;
1421 int cs;
1422};
1423
1424#define ISO2022JP_CS_ASCII 0
1425#define ISO2022JP_CS_JISX0201_ROMAN 1
1426#define ISO2022JP_CS_JISX0201_KANA 2
1427#define ISO2022JP_CS_JISX0208_1978 3
1428#define ISO2022JP_CS_JISX0208_1983 4
1429#define ISO2022JP_CS_JISX0212 5
1430
1431static iso2022_esc_t iso2022jp_esc[] = {
1432 {"\x1B\x28\x42", 3, 1, ISO2022JP_CS_ASCII},
1433 {"\x1B\x28\x4A", 3, 1, ISO2022JP_CS_JISX0201_ROMAN},
1434 {"\x1B\x28\x49", 3, 1, ISO2022JP_CS_JISX0201_KANA},
1435 {"\x1B\x24\x40", 3, 2, ISO2022JP_CS_JISX0208_1983}, /* unify 1978 with 1983 */
1436 {"\x1B\x24\x42", 3, 2, ISO2022JP_CS_JISX0208_1983},
1437 {"\x1B\x24\x28\x44", 4, 2, ISO2022JP_CS_JISX0212},
1438 {NULL, 0, 0, 0}
1439};
1440
1441static int
1442iso2022jp_mbtowc(csconv_t *cv, const uchar *buf, int bufsize, ushort *wbuf, int *wbufsize)
1443{
1444 iso2022_esc_t *iesc = iso2022jp_esc;
1445 char tmp[MB_CHAR_MAX];
1446 int insize;
1447 HRESULT hr;
1448 DWORD dummy = 0;
1449 int len;
1450 int esc_len;
1451 int cs;
1452 int shift;
1453 int i;
1454
1455 if (buf[0] == 0x1B)
1456 {
1457 for (i = 0; iesc[i].esc != NULL; ++i)
1458 {
1459 esc_len = iesc[i].esc_len;
1460 if (bufsize < esc_len)
1461 {
1462 if (strncmp((char *)buf, iesc[i].esc, bufsize) == 0)
1463 return seterror(EINVAL);
1464 }
1465 else
1466 {
1467 if (strncmp((char *)buf, iesc[i].esc, esc_len) == 0)
1468 {
1469 cv->mode = ISO2022_MODE(iesc[i].cs, ISO2022_SI);
1470 *wbufsize = 0;
1471 return esc_len;
1472 }
1473 }
1474 }
1475 /* not supported escape sequence */
1476 return seterror(EILSEQ);
1477 }
1478 else if (buf[0] == iso2022_SO_seq[0])
1479 {
1480 cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SO);
1481 *wbufsize = 0;
1482 return 1;
1483 }
1484 else if (buf[0] == iso2022_SI_seq[0])
1485 {
1486 cv->mode = ISO2022_MODE(ISO2022_MODE_CS(cv->mode), ISO2022_SI);
1487 *wbufsize = 0;
1488 return 1;
1489 }
1490
1491 cs = ISO2022_MODE_CS(cv->mode);
1492 shift = ISO2022_MODE_SHIFT(cv->mode);
1493
1494 /* reset the mode for informal sequence */
1495 if (buf[0] < 0x20)
1496 {
1497 cs = ISO2022JP_CS_ASCII;
1498 shift = ISO2022_SI;
1499 }
1500
1501 len = iesc[cs].len;
1502 if (bufsize < len)
1503 return seterror(EINVAL);
1504 for (i = 0; i < len; ++i)
1505 if (!(buf[i] < 0x80))
1506 return seterror(EILSEQ);
1507 esc_len = iesc[cs].esc_len;
1508 memcpy(tmp, iesc[cs].esc, esc_len);
1509 if (shift == ISO2022_SO)
1510 {
1511 memcpy(tmp + esc_len, iso2022_SO_seq, 1);
1512 esc_len += 1;
1513 }
1514 memcpy(tmp + esc_len, buf, len);
1515
1516 if ((cv->codepage == 50220 || cv->codepage == 50221
1517 || cv->codepage == 50222) && shift == ISO2022_SO)
1518 {
1519 /* XXX: shift-out cannot be used for mbtowc (both kernel and
1520 * mlang) */
1521 esc_len = iesc[ISO2022JP_CS_JISX0201_KANA].esc_len;
1522 memcpy(tmp, iesc[ISO2022JP_CS_JISX0201_KANA].esc, esc_len);
1523 memcpy(tmp + esc_len, buf, len);
1524 }
1525
1526 insize = len + esc_len;
1527 hr = ConvertINetMultiByteToUnicode(&dummy, cv->codepage,
1528 (const char *)tmp, &insize, (wchar_t *)wbuf, wbufsize);
1529 if (hr != S_OK || insize != len + esc_len)
1530 return seterror(EILSEQ);
1531
1532 /* Check for conversion error. Assuming defaultChar is 0x3F. */
1533 /* ascii should be converted from ascii */
1534 if (wbuf[0] == buf[0]
1535 && cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
1536 return seterror(EILSEQ);
1537
1538 /* reset the mode for informal sequence */
1539 if (cv->mode != ISO2022_MODE(cs, shift))
1540 cv->mode = ISO2022_MODE(cs, shift);
1541
1542 return len;
1543}
1544
1545static int
1546iso2022jp_wctomb(csconv_t *cv, ushort *wbuf, int wbufsize, uchar *buf, int bufsize)
1547{
1548 iso2022_esc_t *iesc = iso2022jp_esc;
1549 char tmp[MB_CHAR_MAX];
1550 int tmpsize = MB_CHAR_MAX;
1551 int insize = wbufsize;
1552 HRESULT hr;
1553 DWORD dummy = 0;
1554 int len;
1555 int esc_len;
1556 int cs;
1557 int shift;
1558 int i;
1559
1560 /*
1561 * MultiByte = [escape sequence] + character + [escape sequence]
1562 *
1563 * Whether trailing escape sequence is added depends on which API is
1564 * used (kernel or MLang, and its version).
1565 */
1566 hr = ConvertINetUnicodeToMultiByte(&dummy, cv->codepage,
1567 (const wchar_t *)wbuf, &wbufsize, tmp, &tmpsize);
1568 if (hr != S_OK || insize != wbufsize)
1569 return seterror(EILSEQ);
1570 else if (bufsize < tmpsize)
1571 return seterror(E2BIG);
1572
1573 if (tmpsize == 1)
1574 {
1575 cs = ISO2022JP_CS_ASCII;
1576 esc_len = 0;
1577 }
1578 else
1579 {
1580 for (i = 1; iesc[i].esc != NULL; ++i)
1581 {
1582 esc_len = iesc[i].esc_len;
1583 if (strncmp(tmp, iesc[i].esc, esc_len) == 0)
1584 {
1585 cs = iesc[i].cs;
1586 break;
1587 }
1588 }
1589 if (iesc[i].esc == NULL)
1590 /* not supported escape sequence */
1591 return seterror(EILSEQ);
1592 }
1593
1594 shift = ISO2022_SI;
1595 if (tmp[esc_len] == iso2022_SO_seq[0])
1596 {
1597 shift = ISO2022_SO;
1598 esc_len += 1;
1599 }
1600
1601 len = iesc[cs].len;
1602
1603 /* Check for converting error. Assuming defaultChar is 0x3F. */
1604 /* ascii should be converted from ascii */
1605 if (cs == ISO2022JP_CS_ASCII && !(wbuf[0] < 0x80))
1606 return seterror(EILSEQ);
1607 else if (tmpsize < esc_len + len)
1608 return seterror(EILSEQ);
1609
1610 if (cv->mode == ISO2022_MODE(cs, shift))
1611 {
1612 /* remove escape sequence */
1613 if (esc_len != 0)
1614 memmove(tmp, tmp + esc_len, len);
1615 esc_len = 0;
1616 }
1617 else
1618 {
1619 if (cs == ISO2022JP_CS_ASCII)
1620 {
1621 esc_len = iesc[ISO2022JP_CS_ASCII].esc_len;
1622 memmove(tmp + esc_len, tmp, len);
1623 memcpy(tmp, iesc[ISO2022JP_CS_ASCII].esc, esc_len);
1624 }
1625 if (ISO2022_MODE_SHIFT(cv->mode) == ISO2022_SO)
1626 {
1627 /* shift-in before changing to other mode */
1628 memmove(tmp + 1, tmp, len + esc_len);
1629 memcpy(tmp, iso2022_SI_seq, 1);
1630 esc_len += 1;
1631 }
1632 }
1633
1634 if (bufsize < len + esc_len)
1635 return seterror(E2BIG);
1636 memcpy(buf, tmp, len + esc_len);
1637 cv->mode = ISO2022_MODE(cs, shift);
1638 return len + esc_len;
1639}
1640
1641static int
1642iso2022jp_flush(csconv_t *cv, uchar *buf, int bufsize)
1643{
1644 iso2022_esc_t *iesc = iso2022jp_esc;
1645 int esc_len;
1646
1647 if (cv->mode != ISO2022_MODE(ISO2022JP_CS_ASCII, ISO2022_SI))
1648 {
1649 esc_len = 0;
1650 if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
1651 esc_len += 1;
1652 if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
1653 esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
1654 if (bufsize < esc_len)
1655 return seterror(E2BIG);
1656
1657 esc_len = 0;
1658 if (ISO2022_MODE_SHIFT(cv->mode) != ISO2022_SI)
1659 {
1660 memcpy(buf, iso2022_SI_seq, 1);
1661 esc_len += 1;
1662 }
1663 if (ISO2022_MODE_CS(cv->mode) != ISO2022JP_CS_ASCII)
1664 {
1665 memcpy(buf + esc_len, iesc[ISO2022JP_CS_ASCII].esc,
1666 iesc[ISO2022JP_CS_ASCII].esc_len);
1667 esc_len += iesc[ISO2022JP_CS_ASCII].esc_len;
1668 }
1669 return esc_len;
1670 }
1671 return 0;
1672}
1673
1674static void process_file(iconv_t cd, FILE *in, FILE *out)
1675{
1676 char inbuf[BUFSIZ];
1677 char outbuf[BUFSIZ];
1678 const char *pin;
1679 char *pout;
1680 size_t inbytesleft = 0;
1681 size_t outbytesleft;
1682 size_t rest = 0;
1683 size_t r;
1684
1685 while ((!feof(in) &&
1686 (inbytesleft=fread(inbuf+rest, 1, sizeof(inbuf)-rest, in)) != 0)
1687 || rest != 0) {
1688 inbytesleft += rest;
1689 pin = inbuf;
1690 pout = outbuf;
1691 outbytesleft = sizeof(outbuf);
1692 r = iconv(cd, &pin, &inbytesleft, &pout, &outbytesleft);
1693 if (r == (size_t)(-1) && errno != E2BIG &&
1694 (errno != EINVAL || feof(in)))
1695 bb_perror_msg_and_die("conversion error");
1696 fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out);
1697 memmove(inbuf, pin, inbytesleft);
1698 rest = inbytesleft;
1699 }
1700 pout = outbuf;
1701 outbytesleft = sizeof(outbuf);
1702 r = iconv(cd, NULL, NULL, &pout, &outbytesleft);
1703 fwrite(outbuf, 1, sizeof(outbuf) - outbytesleft, out);
1704 if (r == (size_t)(-1))
1705 bb_perror_msg_and_die("conversion error");
1706}
1707
1708enum {
1709 OPT_f = (1 << 0),
1710 OPT_t = (1 << 1),
1711 OPT_l = (1 << 2),
1712 OPT_c = (1 << 3),
1713 OPT_o = (1 << 4),
1714};
1715
1716int iconv_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
1717int iconv_main(int argc, char **argv)
1718{
1719 const char *fromcode = "", *tocode = "", *outfile;
1720 int i, opt;
1721 iconv_t cd;
1722 FILE *in;
1723 FILE *out = stdout;
1724
1725 opt = getopt32(argv, "f:t:lco:", &fromcode, &tocode, &outfile);
1726
1727 if (opt & OPT_l) {
1728 const char *alias = cp_alias;
1729 while (*alias) {
1730 printf("%s\n", alias);
1731 alias += strlen(alias) + 1;
1732 }
1733 return 0;
1734 }
1735
1736 if (opt & OPT_o)
1737 out = xfopen(outfile, "wb");
1738
1739 if (opt & OPT_c)
1740 tocode = xasprintf("%s//IGNORE", tocode);
1741
1742 cd = iconv_open(tocode, fromcode);
1743 if (cd == (iconv_t)(-1))
1744 bb_perror_msg_and_die("iconv_open error");
1745
1746 if (optind == argc)
1747 argv[argc++] = (char *)"-";
1748
1749 for (i=optind; i<argc; ++i) {
1750 if (argv[i][0] == '-' && argv[i][1] == '\0')
1751 in = stdin;
1752 else
1753 in = xfopen(argv[optind], "rb");
1754 process_file(cd, in, out);
1755 fclose(in);
1756 }
1757
1758 if (ENABLE_FEATURE_CLEAN_UP)
1759 iconv_close(cd);
1760 return 0;
1761}
diff --git a/miscutils/inotifyd.c b/miscutils/inotifyd.c
index 8bff86ae5..fdd04c292 100644
--- a/miscutils/inotifyd.c
+++ b/miscutils/inotifyd.c
@@ -45,8 +45,11 @@
45//usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run." 45//usage: "\nPROG ACTUAL_EVENTS FILEn [SUBFILE] is run."
46//usage: "\nIf PROG is -, events are sent to stdout." 46//usage: "\nIf PROG is -, events are sent to stdout."
47//usage: "\nEvents:" 47//usage: "\nEvents:"
48//usage: IF_NOT_PLATFORM_MINGW32(
48//usage: "\n a File is accessed" 49//usage: "\n a File is accessed"
50//usage: )
49//usage: "\n c File is modified" 51//usage: "\n c File is modified"
52//usage: IF_NOT_PLATFORM_MINGW32(
50//usage: "\n e Metadata changed" 53//usage: "\n e Metadata changed"
51//usage: "\n w Writable file is closed" 54//usage: "\n w Writable file is closed"
52//usage: "\n 0 Unwritable file is closed" 55//usage: "\n 0 Unwritable file is closed"
@@ -55,8 +58,11 @@
55//usage: "\n M File is moved" 58//usage: "\n M File is moved"
56//usage: "\n u Backing fs is unmounted" 59//usage: "\n u Backing fs is unmounted"
57//usage: "\n o Event queue overflowed" 60//usage: "\n o Event queue overflowed"
61//usage: )
58//usage: "\n x File can't be watched anymore" 62//usage: "\n x File can't be watched anymore"
63//usage: IF_NOT_PLATFORM_MINGW32(
59//usage: "\nIf watching a directory:" 64//usage: "\nIf watching a directory:"
65//usage: )
60//usage: "\n y Subfile is moved into dir" 66//usage: "\n y Subfile is moved into dir"
61//usage: "\n m Subfile is moved out of dir" 67//usage: "\n m Subfile is moved out of dir"
62//usage: "\n n Subfile is created" 68//usage: "\n n Subfile is created"
@@ -69,6 +75,7 @@
69#include "common_bufsiz.h" 75#include "common_bufsiz.h"
70#include <sys/inotify.h> 76#include <sys/inotify.h>
71 77
78#if !ENABLE_PLATFORM_MINGW32
72static const char mask_names[] ALIGN1 = 79static const char mask_names[] ALIGN1 =
73 "a" // 0x00000001 File was accessed 80 "a" // 0x00000001 File was accessed
74 "c" // 0x00000002 File was modified 81 "c" // 0x00000002 File was modified
@@ -222,3 +229,207 @@ int inotifyd_main(int argc, char **argv)
222 done: 229 done:
223 return bb_got_signal; 230 return bb_got_signal;
224} 231}
232#else /* ENABLE_PLATFORM_MINGW32 */
233/*
234 * Order is important: the indices match the values taken by the
235 * Action member of the FILE_NOTIFY_INFORMATION structure, including
236 * the undocumented zero value when the directory itself is deleted.
237 */
238static const char mask_names[] ALIGN1 =
239 "x" // File is no longer watched (usually deleted)
240 "n" // Subfile was created
241 "d" // Subfile was deleted
242 "c" // File was modified
243 "m" // File was moved from X
244 "y" // File was moved to Y
245;
246
247enum {
248 MASK_BITS = sizeof(mask_names) - 1
249};
250
251static const unsigned mask_values[] = {
252 0x000, // File is no longer watched (usually deleted)
253 0x003, // Subfile was created
254 0x003, // Subfile was deleted
255 0x1fc, // File was modified (everything except create/delete/move)
256 0x003, // File was moved from X
257 0x003, // File was moved to Y
258};
259
260struct watch {
261 HANDLE hdir;
262 HANDLE hevent;
263 DWORD mask; // notification filter
264 DWORD bits; // events to report
265 OVERLAPPED overlap;
266 const char *dirname;
267 char buf[2048];
268};
269
270static void run_agent(const char *agent, FILE_NOTIFY_INFORMATION *info,
271 struct watch *w)
272{
273 int len;
274 char filename[MAX_PATH];
275 char event[2];
276 const char *args[5];
277
278 memset(filename, 0, sizeof(filename));
279 len = WideCharToMultiByte(CP_ACP, 0, info->FileName,
280 info->FileNameLength/2, filename, sizeof(filename),
281 NULL, NULL);
282
283 if (info->Action >= 0 && info->Action < 6 &&
284 ((1 << info->Action) & w->bits)) {
285 event[0] = mask_names[info->Action];
286 event[1] = '\0';
287
288 if (LONE_CHAR(agent, '-')) {
289 /* "inotifyd - FILE": built-in echo */
290 printf(len ? "%s\t%s\t%s\n" : "%s\t%s\n",
291 event, w->dirname, filename);
292 fflush(stdout);
293 }
294 else {
295 args[0] = agent;
296 args[1] = event;
297 args[2] = w->dirname;
298 args[3] = len ? filename : NULL;
299 args[4] = NULL;
300 spawn_and_wait((char **)args);
301 }
302 }
303}
304
305static BOOL start_watch(struct watch *w)
306{
307 DWORD nret;
308
309 memset(w->buf, 0, sizeof(w->buf));
310 memset(&w->overlap, 0, sizeof(OVERLAPPED));
311 w->overlap.hEvent = w->hevent;
312 ResetEvent(w->hevent);
313
314 return ReadDirectoryChangesW(w->hdir, w->buf, sizeof(w->buf),
315 FALSE, w->mask, &nret, &w->overlap, NULL);
316}
317
318int inotifyd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
319int inotifyd_main(int argc, char **argv)
320{
321 int n;
322 unsigned mask, bits;
323 const char *agent;
324 HANDLE *hevent;
325 struct watch *watch;
326
327 // sanity check: agent and at least one watch must be given
328 if (!argv[1] || !argv[2])
329 bb_show_usage();
330
331 argv++;
332 agent = *argv;
333 argc -= 2; // number of files we watch
334
335 watch = (struct watch *)xzalloc(argc * sizeof(struct watch));
336 hevent = (HANDLE *)xzalloc(argc * sizeof(HANDLE));
337
338 // setup watches
339 for (n = 0; *++argv; ++n) {
340 char *masks;
341
342 masks = strrchr(*argv, ':');
343 // don't confuse a drive prefix with a mask
344 if (masks && masks != (*argv)+1)
345 *masks = '\0';
346
347 mask = 0x01ff; // assuming we want all notifications
348 bits = 0x3f; // assuming we want to report everything
349 // if mask is specified ->
350 if (masks && *masks == '\0') {
351 // convert names to notification filter and report bitmask
352 mask = bits = 0;
353 while (*++masks) {
354 const char *found;
355 found = memchr(mask_names, *masks, MASK_BITS);
356 if (found) {
357 mask |= mask_values[(found - mask_names)];
358 bits |= (1 << (found - mask_names));
359 }
360 }
361 }
362
363 if (mask == 0)
364 bb_error_msg_and_die("%s: invalid mask\n", *argv);
365
366 if (!is_directory(*argv, FALSE))
367 bb_error_msg_and_die("%s: not a directory", *argv);
368
369 watch[n].hdir = CreateFile(*argv, GENERIC_READ|FILE_LIST_DIRECTORY,
370 FILE_SHARE_READ|FILE_SHARE_WRITE|FILE_SHARE_DELETE,
371 NULL, OPEN_EXISTING,
372 FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_OVERLAPPED, NULL);
373 if (watch[n].hdir == INVALID_HANDLE_VALUE)
374 break;
375
376 watch[n].dirname = *argv;
377 watch[n].mask = mask;
378 watch[n].bits = bits;
379 watch[n].hevent = hevent[n] = CreateEvent(NULL, TRUE, FALSE, NULL);
380
381 if (!start_watch(watch+n))
382 break;
383 }
384
385 if (*argv != NULL) {
386 errno = err_win_to_posix();
387 bb_perror_msg_and_die("add watch (%s) failed", *argv);
388 }
389
390 while (1) {
391 DWORD status;
392
393 status = WaitForMultipleObjects(n, hevent, FALSE, INFINITE);
394 if (WAIT_OBJECT_0 <= status && status < WAIT_OBJECT_0 + n) {
395 FILE_NOTIFY_INFORMATION *info;
396 int index = status - WAIT_OBJECT_0;
397 int offset = 0;
398 struct watch *w = watch + index;
399 int got_zero = 0;
400
401 do {
402 info = (FILE_NOTIFY_INFORMATION *)(w->buf + offset);
403 got_zero += (info->Action == 0);
404 run_agent(agent, info, w);
405 offset += info->NextEntryOffset;
406 } while (info->NextEntryOffset);
407
408 if (!start_watch(w)) {
409 // directory was deleted?
410 int i, count;
411
412 if (!got_zero) {
413 // we haven't seen an 'x' event, fake one
414 memset(info, 0, sizeof(FILE_NOTIFY_INFORMATION));
415 run_agent(agent, info, w);
416 }
417
418 // mark watch as dead, terminate if all are dead
419 w->mask = 0;
420 for (count = i = 0; i<n; ++i)
421 if (watch[i].mask)
422 ++count;
423 if (count == 0)
424 break;
425 }
426 }
427 else {
428 errno = err_win_to_posix();
429 bb_perror_msg_and_die("watch failed");
430 }
431 }
432
433 return EXIT_SUCCESS;
434}
435#endif
diff --git a/miscutils/less.c b/miscutils/less.c
index 26983f40d..063c4d378 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 d319e8bba..be3b2a000 100644
--- a/miscutils/man.c
+++ b/miscutils/man.c
@@ -199,8 +199,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
199 if (path) while (*path) { 199 if (path) while (*path) {
200 char *next_path; 200 char *next_path;
201 char **path_element; 201 char **path_element;
202 202 next_path = strchr(path, PATH_SEP);
203 next_path = strchr(path, ':');
204 if (next_path) { 203 if (next_path) {
205 if (next_path == path) /* "::"? */ 204 if (next_path == path) /* "::"? */
206 goto next; 205 goto next;
@@ -223,7 +222,7 @@ static char **add_MANPATH(char **man_path_list, int *count_mp, char *path)
223 if (!next_path) 222 if (!next_path)
224 break; 223 break;
225 /* "path" may be a result of getenv(), be nice and don't mangle it */ 224 /* "path" may be a result of getenv(), be nice and don't mangle it */
226 *next_path = ':'; 225 *next_path = PATH_SEP;
227 next: 226 next:
228 path = next_path + 1; 227 path = next_path + 1;
229 } 228 }
@@ -260,11 +259,24 @@ int man_main(int argc UNUSED_PARAM, char **argv)
260 int count_mp; 259 int count_mp;
261 int opt, not_found; 260 int opt, not_found;
262 char *token[2]; 261 char *token[2];
262#if ENABLE_PLATFORM_MINGW32
263 char **ptr;
264 char *exepath, *relpath;
265 const char *mpl[] = { "/usr/man", "/usr/share/man", NULL, NULL };
266#endif
263 267
264 INIT_G(); 268 INIT_G();
265 269
266 opt = getopt32(argv, "^+" "aw" "\0" "-1"/*at least one arg*/); 270 opt = getopt32(argv, "^+" "aw" "\0" "-1"/*at least one arg*/);
267 argv += optind; 271 argv += optind;
272#if ENABLE_PLATFORM_MINGW32
273 /* add system drive prefix to filenames, if necessary */
274 for (ptr = argv; *ptr; ++ptr) {
275 if (strchr(*ptr, '/') || strchr(*ptr, '\\'))
276 *ptr = xabsolute_path(*ptr);
277 }
278 chdir_system_drive();
279#endif
268 280
269 conf_sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9"); 281 conf_sec_list = xstrdup("0p:1:1p:2:3:3p:4:5:6:7:8:9");
270 282
@@ -302,11 +314,25 @@ int man_main(int argc UNUSED_PARAM, char **argv)
302 } 314 }
303 config_close(parser); 315 config_close(parser);
304 316
317#if ENABLE_PLATFORM_MINGW32
318 /* allow man pages to be stored relative to the executable */
319 exepath = xstrdup(bb_busybox_exec_path);
320 relpath = concat_path_file(dirname(exepath), "man");
321
322 if (!man_path_list) {
323 mpl[2] = relpath;
324 man_path_list = (char**)mpl;
325 }
326 else {
327 man_path_list = add_MANPATH(man_path_list, &count_mp, relpath);
328 }
329#else
305 if (!man_path_list) { 330 if (!man_path_list) {
306 static const char *const mpl[] = { "/usr/man", "/usr/share/man", NULL }; 331 static const char *const mpl[] = { "/usr/man", "/usr/share/man", NULL };
307 man_path_list = (char**)mpl; 332 man_path_list = (char**)mpl;
308 /*count_mp = 2; - not used below anyway */ 333 /*count_mp = 2; - not used below anyway */
309 } 334 }
335#endif
310 336
311 { 337 {
312 /* environment overrides setting from man.config */ 338 /* environment overrides setting from man.config */
diff --git a/miscutils/time.c b/miscutils/time.c
index c4bcbcbc9..ac37a1375 100644
--- a/miscutils/time.c
+++ b/miscutils/time.c
@@ -22,12 +22,21 @@
22//kbuild:lib-$(CONFIG_TIME) += time.o 22//kbuild:lib-$(CONFIG_TIME) += time.o
23 23
24//usage:#define time_trivial_usage 24//usage:#define time_trivial_usage
25//usage: IF_NOT_PLATFORM_MINGW32(
25//usage: "[-vpa] [-o FILE] PROG ARGS" 26//usage: "[-vpa] [-o FILE] PROG ARGS"
27//usage: )
28//usage: IF_PLATFORM_MINGW32(
29//usage: "[-pa] [-o FILE] PROG ARGS"
30//usage: )
26//usage:#define time_full_usage "\n\n" 31//usage:#define time_full_usage "\n\n"
27//usage: "Run PROG, display resource usage when it exits\n" 32//usage: "Run PROG, display resource usage when it exits\n"
33//usage: IF_NOT_PLATFORM_MINGW32(
28//usage: "\n -v Verbose" 34//usage: "\n -v Verbose"
35//usage: )
29//usage: "\n -p POSIX output format" 36//usage: "\n -p POSIX output format"
37//usage: IF_NOT_PLATFORM_MINGW32(
30//usage: "\n -f FMT Custom format" 38//usage: "\n -f FMT Custom format"
39//usage: )
31//usage: "\n -o FILE Write result to FILE" 40//usage: "\n -o FILE Write result to FILE"
32//usage: "\n -a Append (else overwrite)" 41//usage: "\n -a Append (else overwrite)"
33 42
@@ -57,6 +66,7 @@ static const char default_format[] ALIGN1 = "real\t%E\nuser\t%u\nsys\t%T";
57/* The output format for the -p option .*/ 66/* The output format for the -p option .*/
58static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S"; 67static const char posix_format[] ALIGN1 = "real %e\nuser %U\nsys %S";
59 68
69#if !ENABLE_PLATFORM_MINGW32
60/* Format string for printing all statistics verbosely. 70/* Format string for printing all statistics verbosely.
61 Keep this output to 24 lines so users on terminals can see it all.*/ 71 Keep this output to 24 lines so users on terminals can see it all.*/
62static const char long_format[] ALIGN1 = 72static const char long_format[] ALIGN1 =
@@ -83,6 +93,7 @@ static const char long_format[] ALIGN1 =
83 "\tSignals delivered: %k\n" 93 "\tSignals delivered: %k\n"
84 "\tPage size (bytes): %Z\n" 94 "\tPage size (bytes): %Z\n"
85 "\tExit status: %x"; 95 "\tExit status: %x";
96#endif
86 97
87/* Wait for and fill in data on child process PID. 98/* Wait for and fill in data on child process PID.
88 Return 0 on error, 1 if ok. */ 99 Return 0 on error, 1 if ok. */
@@ -93,7 +104,11 @@ static void resuse_end(pid_t pid, resource_t *resp)
93 104
94 /* Ignore signals, but don't ignore the children. When wait3 105 /* Ignore signals, but don't ignore the children. When wait3
95 * returns the child process, set the time the command finished. */ 106 * returns the child process, set the time the command finished. */
107#if !ENABLE_PLATFORM_MINGW32
96 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) { 108 while ((caught = wait3(&resp->waitstatus, 0, &resp->ru)) != pid) {
109#else
110 while ((caught=mingw_wait3(pid, &resp->waitstatus, 0, &resp->ru)) != pid) {
111#endif
97 if (caught == -1 && errno != EINTR) { 112 if (caught == -1 && errno != EINTR) {
98 bb_simple_perror_msg("wait"); 113 bb_simple_perror_msg("wait");
99 return; 114 return;
@@ -102,6 +117,7 @@ static void resuse_end(pid_t pid, resource_t *resp)
102 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms; 117 resp->elapsed_ms = monotonic_ms() - resp->elapsed_ms;
103} 118}
104 119
120#if !ENABLE_PLATFORM_MINGW32
105static void printargv(char *const *argv) 121static void printargv(char *const *argv)
106{ 122{
107 const char *fmt = " %s" + 1; 123 const char *fmt = " %s" + 1;
@@ -136,6 +152,7 @@ static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
136 return tmp / 1024; /* then smaller. */ 152 return tmp / 1024; /* then smaller. */
137} 153}
138#undef pagesize 154#undef pagesize
155#endif
139 156
140/* summarize: Report on the system use of a command. 157/* summarize: Report on the system use of a command.
141 158
@@ -185,11 +202,17 @@ static unsigned long ptok(const unsigned pagesize, const unsigned long pages)
185#define TICKS_PER_SEC 100 202#define TICKS_PER_SEC 100
186#endif 203#endif
187 204
205#if ENABLE_PLATFORM_MINGW32
206#define summarize(f, c, r) summarize(f, r)
207#endif
208
188static void summarize(const char *fmt, char **command, resource_t *resp) 209static void summarize(const char *fmt, char **command, resource_t *resp)
189{ 210{
211#if !ENABLE_PLATFORM_MINGW32
190 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */ 212 unsigned vv_ms; /* Elapsed virtual (CPU) milliseconds */
191 unsigned cpu_ticks; /* Same, in "CPU ticks" */ 213 unsigned cpu_ticks; /* Same, in "CPU ticks" */
192 unsigned pagesize = bb_getpagesize(); 214 unsigned pagesize = bb_getpagesize();
215#endif
193 216
194 /* Impossible: we do not use WUNTRACED flag in wait()... 217 /* Impossible: we do not use WUNTRACED flag in wait()...
195 if (WIFSTOPPED(resp->waitstatus)) 218 if (WIFSTOPPED(resp->waitstatus))
@@ -203,6 +226,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
203 printf("Command exited with non-zero status %u\n", 226 printf("Command exited with non-zero status %u\n",
204 WEXITSTATUS(resp->waitstatus)); 227 WEXITSTATUS(resp->waitstatus));
205 228
229#if !ENABLE_PLATFORM_MINGW32
206 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000 230 vv_ms = (resp->ru.ru_utime.tv_sec + resp->ru.ru_stime.tv_sec) * 1000
207 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000; 231 + (resp->ru.ru_utime.tv_usec + resp->ru.ru_stime.tv_usec) / 1000;
208 232
@@ -213,6 +237,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
213 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000; 237 cpu_ticks = vv_ms * (unsigned long long)TICKS_PER_SEC / 1000;
214#endif 238#endif
215 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */ 239 if (!cpu_ticks) cpu_ticks = 1; /* we divide by it, must be nonzero */
240#endif
216 241
217 while (*fmt) { 242 while (*fmt) {
218 /* Handle leading literal part */ 243 /* Handle leading literal part */
@@ -246,6 +271,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
246 bb_putchar(*fmt); 271 bb_putchar(*fmt);
247 break; 272 break;
248#endif 273#endif
274#if !ENABLE_PLATFORM_MINGW32
249 case 'C': /* The command that got timed. */ 275 case 'C': /* The command that got timed. */
250 printargv(command); 276 printargv(command);
251 break; 277 break;
@@ -254,6 +280,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
254 (ptok(pagesize, (UL) resp->ru.ru_idrss) + 280 (ptok(pagesize, (UL) resp->ru.ru_idrss) +
255 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks); 281 ptok(pagesize, (UL) resp->ru.ru_isrss)) / cpu_ticks);
256 break; 282 break;
283#endif
257 case 'E': { /* Elapsed real (wall clock) time. */ 284 case 'E': { /* Elapsed real (wall clock) time. */
258 unsigned seconds = resp->elapsed_ms / 1000; 285 unsigned seconds = resp->elapsed_ms / 1000;
259 if (seconds >= 3600) /* One hour -> h:m:s. */ 286 if (seconds >= 3600) /* One hour -> h:m:s. */
@@ -268,6 +295,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
268 (unsigned)(resp->elapsed_ms / 10) % 100); 295 (unsigned)(resp->elapsed_ms / 10) % 100);
269 break; 296 break;
270 } 297 }
298#if !ENABLE_PLATFORM_MINGW32
271 case 'F': /* Major page faults. */ 299 case 'F': /* Major page faults. */
272 printf("%lu", resp->ru.ru_majflt); 300 printf("%lu", resp->ru.ru_majflt);
273 break; 301 break;
@@ -296,6 +324,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
296 case 'R': /* Minor page faults (reclaims). */ 324 case 'R': /* Minor page faults (reclaims). */
297 printf("%lu", resp->ru.ru_minflt); 325 printf("%lu", resp->ru.ru_minflt);
298 break; 326 break;
327#endif
299 case 'S': /* System time. */ 328 case 'S': /* System time. */
300 printf("%u.%02u", 329 printf("%u.%02u",
301 (unsigned)resp->ru.ru_stime.tv_sec, 330 (unsigned)resp->ru.ru_stime.tv_sec,
@@ -330,6 +359,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
330 (unsigned)(resp->ru.ru_utime.tv_sec % 60), 359 (unsigned)(resp->ru.ru_utime.tv_sec % 60),
331 (unsigned)(resp->ru.ru_utime.tv_usec / 10000)); 360 (unsigned)(resp->ru.ru_utime.tv_usec / 10000));
332 break; 361 break;
362#if !ENABLE_PLATFORM_MINGW32
333 case 'W': /* Times swapped out. */ 363 case 'W': /* Times swapped out. */
334 printf("%lu", resp->ru.ru_nswap); 364 printf("%lu", resp->ru.ru_nswap);
335 break; 365 break;
@@ -342,11 +372,13 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
342 case 'c': /* Involuntary context switches. */ 372 case 'c': /* Involuntary context switches. */
343 printf("%lu", resp->ru.ru_nivcsw); 373 printf("%lu", resp->ru.ru_nivcsw);
344 break; 374 break;
375#endif
345 case 'e': /* Elapsed real time in seconds. */ 376 case 'e': /* Elapsed real time in seconds. */
346 printf("%u.%02u", 377 printf("%u.%02u",
347 (unsigned)resp->elapsed_ms / 1000, 378 (unsigned)resp->elapsed_ms / 1000,
348 (unsigned)(resp->elapsed_ms / 10) % 100); 379 (unsigned)(resp->elapsed_ms / 10) % 100);
349 break; 380 break;
381#if !ENABLE_PLATFORM_MINGW32
350 case 'k': /* Signals delivered. */ 382 case 'k': /* Signals delivered. */
351 printf("%lu", resp->ru.ru_nsignals); 383 printf("%lu", resp->ru.ru_nsignals);
352 break; 384 break;
@@ -368,6 +400,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
368 case 'x': /* Exit status. */ 400 case 'x': /* Exit status. */
369 printf("%u", WEXITSTATUS(resp->waitstatus)); 401 printf("%u", WEXITSTATUS(resp->waitstatus));
370 break; 402 break;
403#endif
371 } 404 }
372 break; 405 break;
373 406
@@ -402,6 +435,7 @@ static void summarize(const char *fmt, char **command, resource_t *resp)
402static void run_command(char *const *cmd, resource_t *resp) 435static void run_command(char *const *cmd, resource_t *resp)
403{ 436{
404 pid_t pid; 437 pid_t pid;
438#if !ENABLE_PLATFORM_MINGW32
405 void (*interrupt_signal)(int); 439 void (*interrupt_signal)(int);
406 void (*quit_signal)(int); 440 void (*quit_signal)(int);
407 441
@@ -422,6 +456,13 @@ static void run_command(char *const *cmd, resource_t *resp)
422 /* Re-enable signals. */ 456 /* Re-enable signals. */
423 signal(SIGINT, interrupt_signal); 457 signal(SIGINT, interrupt_signal);
424 signal(SIGQUIT, quit_signal); 458 signal(SIGQUIT, quit_signal);
459#else
460 resp->elapsed_ms = monotonic_ms();
461 if ((pid=spawn((char **)cmd)) == -1)
462 bb_perror_msg_and_die("can't execute %s", cmd[0]);
463
464 resuse_end(pid, resp);
465#endif
425} 466}
426 467
427int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 468int time_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
@@ -435,20 +476,34 @@ int time_main(int argc UNUSED_PARAM, char **argv)
435 int opt; 476 int opt;
436 int ex; 477 int ex;
437 enum { 478 enum {
479#if !ENABLE_PLATFORM_MINGW32
438 OPT_v = (1 << 0), 480 OPT_v = (1 << 0),
439 OPT_p = (1 << 1), 481 OPT_p = (1 << 1),
440 OPT_a = (1 << 2), 482 OPT_a = (1 << 2),
441 OPT_o = (1 << 3), 483 OPT_o = (1 << 3),
442 OPT_f = (1 << 4), 484 OPT_f = (1 << 4),
485#else
486 OPT_p = (1 << 0),
487 OPT_a = (1 << 1),
488 OPT_o = (1 << 2),
489#endif
443 }; 490 };
444 491
445 /* "+": stop on first non-option */ 492 /* "+": stop on first non-option */
493#if !ENABLE_PLATFORM_MINGW32
446 opt = getopt32(argv, "^+" "vpao:f:" "\0" "-1"/*at least one arg*/, 494 opt = getopt32(argv, "^+" "vpao:f:" "\0" "-1"/*at least one arg*/,
447 &output_filename, &output_format 495 &output_filename, &output_format
448 ); 496 );
497#else
498 opt = getopt32(argv, "^+" "pao:" "\0" "-1"/*at least one arg*/,
499 &output_filename
500 );
501#endif
449 argv += optind; 502 argv += optind;
503#if !ENABLE_PLATFORM_MINGW32
450 if (opt & OPT_v) 504 if (opt & OPT_v)
451 output_format = long_format; 505 output_format = long_format;
506#endif
452 if (opt & OPT_p) 507 if (opt & OPT_p)
453 output_format = posix_format; 508 output_format = posix_format;
454 output_fd = STDERR_FILENO; 509 output_fd = STDERR_FILENO;
diff --git a/miscutils/ts.c b/miscutils/ts.c
index c7e477cc5..af6677fc7 100644
--- a/miscutils/ts.c
+++ b/miscutils/ts.c
@@ -25,6 +25,9 @@ int ts_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
25int ts_main(int argc UNUSED_PARAM, char **argv) 25int ts_main(int argc UNUSED_PARAM, char **argv)
26{ 26{
27 struct timeval base; 27 struct timeval base;
28#if ENABLE_PLATFORM_MINGW32 && !defined(_USE_32BIT_TIME_T)
29 time_t t;
30#endif
28 unsigned opt; 31 unsigned opt;
29 char *frac; 32 char *frac;
30 char *fmt_dt2str; 33 char *fmt_dt2str;
@@ -73,7 +76,12 @@ int ts_main(int argc UNUSED_PARAM, char **argv)
73 if (opt & 1) /* -i */ 76 if (opt & 1) /* -i */
74 base = ts1; 77 base = ts1;
75 } 78 }
79#if ENABLE_PLATFORM_MINGW32 && !defined(_USE_32BIT_TIME_T)
80 t = ts.tv_sec;
81 localtime_r(&t, &tm_time);
82#else
76 localtime_r(&ts.tv_sec, &tm_time); 83 localtime_r(&ts.tv_sec, &tm_time);
84#endif
77 strftime(date_buf, COMMON_BUFSIZE, fmt_dt2str, &tm_time); 85 strftime(date_buf, COMMON_BUFSIZE, fmt_dt2str, &tm_time);
78 if (!frac) { 86 if (!frac) {
79 printf("%s %s", date_buf, line); 87 printf("%s %s", date_buf, line);
diff --git a/networking/ftpgetput.c b/networking/ftpgetput.c
index 4c92f34a1..ef0973a84 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/httpd.c b/networking/httpd.c
index 33045163f..c9daa0638 100644
--- a/networking/httpd.c
+++ b/networking/httpd.c
@@ -264,7 +264,12 @@
264//kbuild:lib-$(CONFIG_HTTPD) += httpd.o 264//kbuild:lib-$(CONFIG_HTTPD) += httpd.o
265 265
266//usage:#define httpd_trivial_usage 266//usage:#define httpd_trivial_usage
267//usage: IF_NOT_PLATFORM_MINGW32(
267//usage: "[-ifv[v]]" 268//usage: "[-ifv[v]]"
269//usage: )
270//usage: IF_PLATFORM_MINGW32(
271//usage: "[-fv[v]]"
272//usage: )
268//usage: " [-c CONFFILE]" 273//usage: " [-c CONFFILE]"
269//usage: " [-p [IP:]PORT]" 274//usage: " [-p [IP:]PORT]"
270//usage: IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]") 275//usage: IF_FEATURE_HTTPD_SETUID(" [-u USER[:GRP]]")
@@ -273,7 +278,9 @@
273//usage: "or httpd -d/-e" IF_FEATURE_HTTPD_AUTH_MD5("/-m") " STRING" 278//usage: "or httpd -d/-e" IF_FEATURE_HTTPD_AUTH_MD5("/-m") " STRING"
274//usage:#define httpd_full_usage "\n\n" 279//usage:#define httpd_full_usage "\n\n"
275//usage: "Listen for incoming HTTP requests\n" 280//usage: "Listen for incoming HTTP requests\n"
281//usage: IF_NOT_PLATFORM_MINGW32(
276//usage: "\n -i Inetd mode" 282//usage: "\n -i Inetd mode"
283//usage: )
277//usage: "\n -f Run in foreground" 284//usage: "\n -f Run in foreground"
278//usage: "\n -v[v] Verbose" 285//usage: "\n -v[v] Verbose"
279//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:"STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT)")" 286//usage: "\n -p [IP:]PORT Bind to IP:PORT (default *:"STR(CONFIG_FEATURE_HTTPD_PORT_DEFAULT)")"
@@ -322,6 +329,8 @@
322 329
323#define HEADER_READ_TIMEOUT 60 330#define HEADER_READ_TIMEOUT 60
324 331
332static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
333
325#define STR1(s) #s 334#define STR1(s) #s
326#define STR(s) STR1(s) 335#define STR(s) STR1(s)
327 336
@@ -557,7 +566,12 @@ enum {
557} while (0) 566} while (0)
558 567
559 568
569#if !ENABLE_PLATFORM_MINGW32
560#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1) 570#define STRNCASECMP(a, str) strncasecmp((a), (str), sizeof(str)-1)
571#else
572/* Not exactly equivalent to strncasecmp(), but OK for its use here */
573#define STRNCASECMP(a, str) (!is_prefixed_with_case((a), (str)))
574#endif
561 575
562/* Prototypes */ 576/* Prototypes */
563enum { 577enum {
@@ -721,8 +735,15 @@ static int parse_conf(const char *path, int flag)
721 735
722 filename = opt_c_configFile; 736 filename = opt_c_configFile;
723 if (flag == SUBDIR_PARSE || filename == NULL) { 737 if (flag == SUBDIR_PARSE || filename == NULL) {
738#if !ENABLE_PLATFORM_MINGW32
724 filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2); 739 filename = alloca(strlen(path) + sizeof(HTTPD_CONF) + 2);
725 sprintf((char *)filename, "%s/%s", path, HTTPD_CONF); 740 sprintf((char *)filename, "%s/%s", path, HTTPD_CONF);
741#else
742 const char *sd = need_system_drive(path);
743
744 filename = auto_string(xasprintf("%s%s/%s", sd ? sd : "",
745 path, HTTPD_CONF));
746#endif
726 } 747 }
727 748
728 while ((f = fopen_for_read(filename)) == NULL) { 749 while ((f = fopen_for_read(filename)) == NULL) {
@@ -771,6 +792,7 @@ static int parse_conf(const char *path, int flag)
771 * without needless copying, therefore we don't merge 792 * without needless copying, therefore we don't merge
772 * this operation into next while loop. */ 793 * this operation into next while loop. */
773 while ((ch = *p0) != '\0' && ch != '\n' && ch != '#' 794 while ((ch = *p0) != '\0' && ch != '\n' && ch != '#'
795 IF_PLATFORM_MINGW32(&& ch != '\r')
774 && ch != ' ' && ch != '\t' 796 && ch != ' ' && ch != '\t'
775 ) { 797 ) {
776 p0++; 798 p0++;
@@ -778,7 +800,11 @@ static int parse_conf(const char *path, int flag)
778 p = p0; 800 p = p0;
779 /* if we enter this loop, we have some whitespace. 801 /* if we enter this loop, we have some whitespace.
780 * discard it */ 802 * discard it */
803#if !ENABLE_PLATFORM_MINGW32
781 while (ch != '\0' && ch != '\n' && ch != '#') { 804 while (ch != '\0' && ch != '\n' && ch != '#') {
805#else
806 while (ch != '\0' && ch != '\n' && ch != '\r' && ch != '#') {
807#endif
782 if (ch != ' ' && ch != '\t') { 808 if (ch != ' ' && ch != '\t') {
783 *p++ = ch; 809 *p++ = ch;
784 } 810 }
@@ -1311,7 +1337,21 @@ static unsigned get_line(void)
1311 count = 0; 1337 count = 0;
1312 while (1) { 1338 while (1) {
1313 if (hdr_cnt <= 0) { 1339 if (hdr_cnt <= 0) {
1340#if ENABLE_PLATFORM_MINGW32
1341 int nfds = 1;
1342 struct pollfd fds = {STDIN_FILENO, POLLIN, 0};
1343
1344 switch (poll(&fds, nfds, HEADER_READ_TIMEOUT*1000)) {
1345 case 0:
1346 send_REQUEST_TIMEOUT_and_exit(0);
1347 break;
1348 case -1:
1349 bb_simple_perror_msg_and_die("poll");
1350 break;
1351 }
1352#else
1314 alarm(HEADER_READ_TIMEOUT); 1353 alarm(HEADER_READ_TIMEOUT);
1354#endif
1315 hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf); 1355 hdr_cnt = safe_read(STDIN_FILENO, hdr_buf, sizeof_hdr_buf);
1316 if (hdr_cnt <= 0) 1356 if (hdr_cnt <= 0)
1317 goto ret; 1357 goto ret;
@@ -1771,7 +1811,7 @@ static NOINLINE void send_file_and_exit(const char *url, int what)
1771 } 1811 }
1772#if ENABLE_FEATURE_HTTPD_ETAG 1812#if ENABLE_FEATURE_HTTPD_ETAG
1773 /* ETag is "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417" */ 1813 /* ETag is "hex(last_mod)-hex(file_size)" e.g. "5e132e20-417" */
1774 sprintf(G.etag, "\"%llx-%llx\"", (unsigned long long)last_mod, (unsigned long long)file_size); 1814 sprintf(G.etag, "\"%"LL_FMT"x-%"LL_FMT"x\"", (unsigned long long)last_mod, (unsigned long long)file_size);
1775 1815
1776 if (G.if_none_match) { 1816 if (G.if_none_match) {
1777 dbg("If-None-Match:'%s' file's ETag:'%s'\n", G.if_none_match, G.etag); 1817 dbg("If-None-Match:'%s' file's ETag:'%s'\n", G.if_none_match, G.etag);
@@ -2026,7 +2066,11 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
2026 2066
2027 /* WHY? */ 2067 /* WHY? */
2028 /* If already saw a match, don't accept other different matches */ 2068 /* If already saw a match, don't accept other different matches */
2069#if !ENABLE_PLATFORM_MINGW32
2029 if (prev && strcmp(prev, dir_prefix) != 0) 2070 if (prev && strcmp(prev, dir_prefix) != 0)
2071#else
2072 if (prev && strcasecmp(prev, dir_prefix) != 0)
2073#endif
2030 continue; 2074 continue;
2031 2075
2032 dbg("checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd); 2076 dbg("checkPerm: '%s' ? '%s'\n", dir_prefix, user_and_passwd);
@@ -2034,7 +2078,11 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
2034 /* If it's not a prefix match, continue searching */ 2078 /* If it's not a prefix match, continue searching */
2035 len = strlen(dir_prefix); 2079 len = strlen(dir_prefix);
2036 if (len != 1 /* dir_prefix "/" matches all, don't need to check */ 2080 if (len != 1 /* dir_prefix "/" matches all, don't need to check */
2081#if !ENABLE_PLATFORM_MINGW32
2037 && (strncmp(dir_prefix, path, len) != 0 2082 && (strncmp(dir_prefix, path, len) != 0
2083#else
2084 && (strncasecmp(dir_prefix, path, len) != 0
2085#endif
2038 || (path[len] != '/' && path[len] != '\0') 2086 || (path[len] != '/' && path[len] != '\0')
2039 ) 2087 )
2040 ) { 2088 ) {
@@ -2056,6 +2104,7 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
2056 goto bad_input; 2104 goto bad_input;
2057 2105
2058 /* compare "user:" */ 2106 /* compare "user:" */
2107# if !ENABLE_PLATFORM_MINGW32
2059 if (cur->after_colon[0] != '*' 2108 if (cur->after_colon[0] != '*'
2060 && strncmp(cur->after_colon, user_and_passwd, 2109 && strncmp(cur->after_colon, user_and_passwd,
2061 colon_after_user - user_and_passwd + 1) != 0 2110 colon_after_user - user_and_passwd + 1) != 0
@@ -2063,11 +2112,20 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
2063 continue; 2112 continue;
2064 } 2113 }
2065 /* this cfg entry is '*' or matches username from peer */ 2114 /* this cfg entry is '*' or matches username from peer */
2115# else
2116 if (strncmp(cur->after_colon, user_and_passwd,
2117 colon_after_user - user_and_passwd + 1) != 0
2118 ) {
2119 continue;
2120 }
2121 /* this cfg entry matches username from peer */
2122# endif
2066 2123
2067 passwd = strchr(cur->after_colon, ':'); 2124 passwd = strchr(cur->after_colon, ':');
2068 if (!passwd) 2125 if (!passwd)
2069 goto bad_input; 2126 goto bad_input;
2070 passwd++; 2127 passwd++;
2128# if !ENABLE_PLATFORM_MINGW32
2071 if (passwd[0] == '*') { 2129 if (passwd[0] == '*') {
2072# if ENABLE_PAM 2130# if ENABLE_PAM
2073 struct pam_userinfo userinfo; 2131 struct pam_userinfo userinfo;
@@ -2115,11 +2173,12 @@ static int check_user_passwd(const char *path, char *user_and_passwd)
2115 goto check_encrypted; 2173 goto check_encrypted;
2116# endif /* ENABLE_PAM */ 2174# endif /* ENABLE_PAM */
2117 } 2175 }
2176# endif /* !ENABLE_PLATFORM_MINGW32 */
2118 /* Else: passwd is from httpd.conf, it is either plaintext or encrypted */ 2177 /* Else: passwd is from httpd.conf, it is either plaintext or encrypted */
2119 2178
2120 if (passwd[0] == '$' && isdigit(passwd[1])) { 2179 if (passwd[0] == '$' && isdigit(passwd[1])) {
2121 char *encrypted; 2180 char *encrypted;
2122# if !ENABLE_PAM 2181# if !ENABLE_PAM && !ENABLE_PLATFORM_MINGW32
2123 check_encrypted: 2182 check_encrypted:
2124# endif 2183# endif
2125 /* encrypt pwd from peer and check match with local one */ 2184 /* encrypt pwd from peer and check match with local one */
@@ -2168,7 +2227,6 @@ static Htaccess_Proxy *find_proxy_entry(const char *url)
2168/* 2227/*
2169 * Handle timeouts 2228 * Handle timeouts
2170 */ 2229 */
2171static void send_REQUEST_TIMEOUT_and_exit(int sig) NORETURN;
2172static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM) 2230static void send_REQUEST_TIMEOUT_and_exit(int sig UNUSED_PARAM)
2173{ 2231{
2174 send_headers_and_exit(HTTP_REQUEST_TIMEOUT); 2232 send_headers_and_exit(HTTP_REQUEST_TIMEOUT);
@@ -2232,17 +2290,29 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2232 remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr); 2290 remote_ip = ntohl(fromAddr->u.sin.sin_addr.s_addr);
2233 } 2291 }
2234# if ENABLE_FEATURE_IPV6 2292# if ENABLE_FEATURE_IPV6
2293# if !ENABLE_PLATFORM_MINGW32
2235 if (fromAddr->u.sa.sa_family == AF_INET6 2294 if (fromAddr->u.sa.sa_family == AF_INET6
2236 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0 2295 && fromAddr->u.sin6.sin6_addr.s6_addr32[0] == 0
2237 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0 2296 && fromAddr->u.sin6.sin6_addr.s6_addr32[1] == 0
2238 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff) 2297 && ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[2]) == 0xffff)
2239 remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]); 2298 remote_ip = ntohl(fromAddr->u.sin6.sin6_addr.s6_addr32[3]);
2299# else
2300 if (fromAddr->u.sa.sa_family == AF_INET6
2301 && fromAddr->u.sin6.sin6_addr.s6_words[0] == 0
2302 && fromAddr->u.sin6.sin6_addr.s6_words[1] == 0
2303 && fromAddr->u.sin6.sin6_addr.s6_words[2] == 0
2304 && fromAddr->u.sin6.sin6_addr.s6_words[3] == 0
2305 && ntohl(*(uint32_t *)(fromAddr->u.sin6.sin6_addr.s6_words+4)) == 0xffff)
2306 remote_ip = ntohl(*(uint32_t *)(fromAddr->u.sin6.sin6_addr.s6_words+6));
2307# endif
2240# endif 2308# endif
2241 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip); 2309 if_ip_denied_send_HTTP_FORBIDDEN_and_exit(remote_ip);
2242#endif 2310#endif
2243 2311
2312#ifdef SIGALRM
2244 /* Install timeout handler. get_line() needs it. */ 2313 /* Install timeout handler. get_line() needs it. */
2245 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit); 2314 signal(SIGALRM, send_REQUEST_TIMEOUT_and_exit);
2315#endif
2246 2316
2247 if (!get_line()) { /* EOF or error or empty line */ 2317 if (!get_line()) { /* EOF or error or empty line */
2248 /* Observed Firefox to "speculatively" open 2318 /* Observed Firefox to "speculatively" open
@@ -2579,7 +2649,11 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2579 /* We are done reading headers, disable peer timeout */ 2649 /* We are done reading headers, disable peer timeout */
2580 alarm(0); 2650 alarm(0);
2581 2651
2652#if !ENABLE_PLATFORM_MINGW32
2582 if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0) { 2653 if (strcmp(bb_basename(urlcopy), HTTPD_CONF) == 0) {
2654#else
2655 if (strcasecmp(bb_basename(urlcopy), HTTPD_CONF) == 0) {
2656#endif
2583 /* protect listing [/path]/httpd.conf or IP deny */ 2657 /* protect listing [/path]/httpd.conf or IP deny */
2584 send_headers_and_exit(HTTP_FORBIDDEN); 2658 send_headers_and_exit(HTTP_FORBIDDEN);
2585 } 2659 }
@@ -2625,12 +2699,14 @@ static void handle_incoming_and_exit(const len_and_sockaddr *fromAddr)
2625 ); 2699 );
2626} 2700}
2627 2701
2702
2628/* 2703/*
2629 * The main http server function. 2704 * The main http server function.
2630 * Given a socket, listen for new connections and farm out 2705 * Given a socket, listen for new connections and farm out
2631 * the processing as a [v]forked process. 2706 * the processing as a [v]forked process.
2632 * Never returns. 2707 * Never returns.
2633 */ 2708 */
2709# if !ENABLE_PLATFORM_MINGW32
2634#if BB_MMU 2710#if BB_MMU
2635static void mini_httpd(int server_socket) NORETURN; 2711static void mini_httpd(int server_socket) NORETURN;
2636static void mini_httpd(int server_socket) 2712static void mini_httpd(int server_socket)
@@ -2719,6 +2795,39 @@ static void mini_httpd_nommu(int server_socket, int argc, char **argv)
2719 /* never reached */ 2795 /* never reached */
2720} 2796}
2721#endif 2797#endif
2798#else /* ENABLE_PLATFORM_MINGW32 */
2799static void mini_httpd_win32(int fg, int sock, int argc, char **argv) NORETURN;
2800static void mini_httpd_win32(int fg, int sock, int argc, char **argv)
2801{
2802 char *argv_copy[argc + 5];
2803
2804 argv_copy[0] = (char *)bb_busybox_exec_path;
2805 argv_copy[1] = (char *)"--busybox";
2806 argv_copy[2] = (char *)"httpd";
2807 argv_copy[3] = (char *)"-I";
2808 memcpy(&argv_copy[5], &argv[1], argc * sizeof(argv[0]));
2809
2810 while (1) {
2811 int n;
2812
2813 /* Wait for connections... */
2814 n = accept(sock, NULL, NULL);
2815 if (n < 0)
2816 continue;
2817
2818 /* set the KEEPALIVE option to cull dead connections */
2819 setsockopt_keepalive(n);
2820
2821 argv_copy[4] = itoa(n);
2822 if ((fg ? spawn(argv_copy) : mingw_spawn_detach(argv_copy)) == -1)
2823 bb_perror_msg_and_die("can't execute 'httpd'");
2824
2825 /* parent, or spawn failed */
2826 close(n);
2827 } /* while (1) */
2828 /* never reached */
2829}
2830#endif
2722 2831
2723/* 2832/*
2724 * Process a HTTP connection on stdin/out. 2833 * Process a HTTP connection on stdin/out.
@@ -2736,12 +2845,39 @@ static void mini_httpd_inetd(void)
2736 handle_incoming_and_exit(&fromAddr); 2845 handle_incoming_and_exit(&fromAddr);
2737} 2846}
2738 2847
2848#if ENABLE_PLATFORM_MINGW32
2849static void mingw_daemonize(char **argv)
2850{
2851 char **new_argv;
2852 int argc, fd;
2853
2854 argc = string_array_len((char **)argv);
2855 new_argv = xmalloc(sizeof(*argv)*(argc+3));
2856 new_argv[0] = (char *)bb_busybox_exec_path;
2857 new_argv[1] = (char *)"--busybox";
2858 new_argv[2] = (char *)"-httpd";
2859 memcpy(&new_argv[3], &argv[1], sizeof(*argv)*argc);
2860
2861 fd = xopen(bb_dev_null, O_RDWR);
2862 xdup2(fd, 0);
2863 xdup2(fd, 1);
2864 xdup2(fd, 2);
2865 close(fd);
2866
2867 if (mingw_spawn_detach(new_argv))
2868 exit(EXIT_SUCCESS); /* parent */
2869 exit(EXIT_FAILURE); /* parent */
2870}
2871#endif
2872
2873#ifdef SIGHUP
2739static void sighup_handler(int sig UNUSED_PARAM) 2874static void sighup_handler(int sig UNUSED_PARAM)
2740{ 2875{
2741 int sv = errno; 2876 int sv = errno;
2742 parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE); 2877 parse_conf(DEFAULT_PATH_HTTPD_CONF, SIGNALED_PARSE);
2743 errno = sv; 2878 errno = sv;
2744} 2879}
2880#endif
2745 2881
2746enum { 2882enum {
2747 c_opt_config_file = 0, 2883 c_opt_config_file = 0,
@@ -2779,6 +2915,7 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2779 IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;) 2915 IF_FEATURE_HTTPD_SETUID(const char *s_ugid = NULL;)
2780 IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;) 2916 IF_FEATURE_HTTPD_SETUID(struct bb_uidgid_t ugid;)
2781 IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;) 2917 IF_FEATURE_HTTPD_AUTH_MD5(const char *pass;)
2918 IF_PLATFORM_MINGW32(int fd;)
2782 2919
2783 INIT_G(); 2920 INIT_G();
2784 2921
@@ -2797,16 +2934,19 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2797 IF_FEATURE_HTTPD_BASIC_AUTH("r:") 2934 IF_FEATURE_HTTPD_BASIC_AUTH("r:")
2798 IF_FEATURE_HTTPD_AUTH_MD5("m:") 2935 IF_FEATURE_HTTPD_AUTH_MD5("m:")
2799 IF_FEATURE_HTTPD_SETUID("u:") 2936 IF_FEATURE_HTTPD_SETUID("u:")
2800 "p:ifv" 2937 IF_NOT_PLATFORM_MINGW32("p:ifv")
2938 IF_PLATFORM_MINGW32("p:I:+fv")
2801 "\0" 2939 "\0"
2802 /* -v counts, -i implies -f */ 2940 /* -v counts, -i implies -f */
2803 "vv:if", 2941 IF_NOT_PLATFORM_MINGW32("vv:if",)
2942 IF_PLATFORM_MINGW32("vv:If",)
2804 &opt_c_configFile, &url_for_decode, &home_httpd 2943 &opt_c_configFile, &url_for_decode, &home_httpd
2805 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode) 2944 IF_FEATURE_HTTPD_ENCODE_URL_STR(, &url_for_encode)
2806 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm) 2945 IF_FEATURE_HTTPD_BASIC_AUTH(, &g_realm)
2807 IF_FEATURE_HTTPD_AUTH_MD5(, &pass) 2946 IF_FEATURE_HTTPD_AUTH_MD5(, &pass)
2808 IF_FEATURE_HTTPD_SETUID(, &s_ugid) 2947 IF_FEATURE_HTTPD_SETUID(, &s_ugid)
2809 , &bind_addr_or_port 2948 , &bind_addr_or_port
2949 IF_PLATFORM_MINGW32(, &fd)
2810 , &verbose 2950 , &verbose
2811 ); 2951 );
2812 if (opt & OPT_DECODE_URL) { 2952 if (opt & OPT_DECODE_URL) {
@@ -2836,20 +2976,32 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2836 } 2976 }
2837#endif 2977#endif
2838 2978
2979#if !ENABLE_PLATFORM_MINGW32
2839#if !BB_MMU 2980#if !BB_MMU
2840 if (!(opt & OPT_FOREGROUND)) { 2981 if (!(opt & OPT_FOREGROUND)) {
2841 bb_daemonize_or_rexec(0, argv); /* don't change current directory */ 2982 bb_daemonize_or_rexec(0, argv); /* don't change current directory */
2842 re_execed = 0; /* for the following chdir to work */ 2983 re_execed = 0; /* for the following chdir to work */
2843 } 2984 }
2844#endif 2985#endif
2986#else /* ENABLE_PLATFORM_MINGW32 */
2987 if (!(opt & OPT_FOREGROUND) && argv[0][0] != '-')
2988 mingw_daemonize(argv);
2989#endif
2990
2845 /* Chdir to home (unless we were re_exec()ed for NOMMU case 2991 /* Chdir to home (unless we were re_exec()ed for NOMMU case
2846 * in mini_httpd_nommu(): we are already in the home dir then). 2992 * in mini_httpd_nommu(): we are already in the home dir then).
2847 */ 2993 */
2994#if ENABLE_PLATFORM_MINGW32
2995 if (!(opt & OPT_INETD))
2996#else
2848 if (!re_execed) 2997 if (!re_execed)
2998#endif
2849 xchdir(home_httpd); 2999 xchdir(home_httpd);
2850 3000
2851 if (!(opt & OPT_INETD)) { 3001 if (!(opt & OPT_INETD)) {
3002#ifdef SIGCHLD
2852 signal(SIGCHLD, SIG_IGN); 3003 signal(SIGCHLD, SIG_IGN);
3004#endif
2853 server_socket = openServer(); 3005 server_socket = openServer();
2854#if ENABLE_FEATURE_HTTPD_SETUID 3006#if ENABLE_FEATURE_HTTPD_SETUID
2855 /* drop privileges */ 3007 /* drop privileges */
@@ -2883,12 +3035,23 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2883#endif 3035#endif
2884 3036
2885 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE); 3037 parse_conf(DEFAULT_PATH_HTTPD_CONF, FIRST_PARSE);
3038#ifdef SIGHUP
2886 if (!(opt & OPT_INETD)) 3039 if (!(opt & OPT_INETD))
2887 signal(SIGHUP, sighup_handler); 3040 signal(SIGHUP, sighup_handler);
3041#endif
2888 3042
2889 xfunc_error_retval = 0; 3043 xfunc_error_retval = 0;
3044#if ENABLE_PLATFORM_MINGW32
3045 if (opt & OPT_INETD) {
3046 xmove_fd(fd, 0);
3047 xdup2(0, 1);
3048 while (--fd > 2)
3049 close(fd);
3050 }
3051#endif
2890 if (opt & OPT_INETD) 3052 if (opt & OPT_INETD)
2891 mini_httpd_inetd(); /* never returns */ 3053 mini_httpd_inetd(); /* never returns */
3054#if !ENABLE_PLATFORM_MINGW32
2892#if BB_MMU 3055#if BB_MMU
2893 if (!(opt & OPT_FOREGROUND)) 3056 if (!(opt & OPT_FOREGROUND))
2894 bb_daemonize(0); /* don't change current directory */ 3057 bb_daemonize(0); /* don't change current directory */
@@ -2896,5 +3059,8 @@ int httpd_main(int argc UNUSED_PARAM, char **argv)
2896#else 3059#else
2897 mini_httpd_nommu(server_socket, argc, argv); /* never returns */ 3060 mini_httpd_nommu(server_socket, argc, argv); /* never returns */
2898#endif 3061#endif
3062#else /* ENABLE_PLATFORM_MINGW32 */
3063 mini_httpd_win32(opt & OPT_FOREGROUND, server_socket, argc, argv);
3064#endif
2899 /* return 0; */ 3065 /* return 0; */
2900} 3066}
diff --git a/networking/nc.c b/networking/nc.c
index d351bf72a..5525e41f9 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_PLATFORM_MINGW32
113static void timeout(int signum UNUSED_PARAM) 114static void timeout(int signum UNUSED_PARAM)
114{ 115{
115 bb_simple_error_msg_and_die("timed out"); 116 bb_simple_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)
@@ -123,11 +125,15 @@ int nc_main(int argc, char **argv)
123 int cfd = 0; 125 int cfd = 0;
124 unsigned lport = 0; 126 unsigned lport = 0;
125 IF_NOT_NC_SERVER(const) unsigned do_listen = 0; 127 IF_NOT_NC_SERVER(const) unsigned do_listen = 0;
128#if !ENABLE_PLATFORM_MINGW32
126 IF_NOT_NC_EXTRA (const) unsigned wsecs = 0; 129 IF_NOT_NC_EXTRA (const) unsigned wsecs = 0;
127 IF_NOT_NC_EXTRA (const) unsigned delay = 0; 130 IF_NOT_NC_EXTRA (const) unsigned delay = 0;
128 IF_NOT_NC_EXTRA (const int execparam = 0;) 131 IF_NOT_NC_EXTRA (const int execparam = 0;)
129 IF_NC_EXTRA (char **execparam = NULL;) 132 IF_NC_EXTRA (char **execparam = NULL;)
130 struct pollfd pfds[2]; 133 struct pollfd pfds[2];
134#else
135 fd_set readfds, testfds;
136#endif
131 int opt; /* must be signed (getopt returns -1) */ 137 int opt; /* must be signed (getopt returns -1) */
132 138
133 if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) { 139 if (ENABLE_NC_SERVER || ENABLE_NC_EXTRA) {
@@ -187,10 +193,12 @@ int nc_main(int argc, char **argv)
187 argv++; 193 argv++;
188 } 194 }
189 195
196#if !ENABLE_PLATFORM_MINGW32
190 if (wsecs) { 197 if (wsecs) {
191 signal(SIGALRM, timeout); 198 signal(SIGALRM, timeout);
192 alarm(wsecs); 199 alarm(wsecs);
193 } 200 }
201#endif
194 202
195 if (!cfd) { 203 if (!cfd) {
196 if (do_listen) { 204 if (do_listen) {
@@ -208,11 +216,13 @@ int nc_main(int argc, char **argv)
208 } 216 }
209#endif 217#endif
210 close_on_exec_on(sfd); 218 close_on_exec_on(sfd);
211 accept_again: 219 IF_NOT_PLATFORM_MINGW32(accept_again:)
212 cfd = accept(sfd, NULL, 0); 220 cfd = accept(sfd, NULL, 0);
213 if (cfd < 0) 221 if (cfd < 0)
214 bb_simple_perror_msg_and_die("accept"); 222 bb_simple_perror_msg_and_die("accept");
223#if !ENABLE_PLATFORM_MINGW32
215 if (!execparam) 224 if (!execparam)
225#endif
216 close(sfd); 226 close(sfd);
217 } else { 227 } else {
218 cfd = create_and_connect_stream_or_die(argv[0], 228 cfd = create_and_connect_stream_or_die(argv[0],
@@ -220,6 +230,7 @@ int nc_main(int argc, char **argv)
220 } 230 }
221 } 231 }
222 232
233#if !ENABLE_PLATFORM_MINGW32
223 if (wsecs) { 234 if (wsecs) {
224 alarm(0); 235 alarm(0);
225 /* Non-ignored signals revert to SIG_DFL on exec anyway */ 236 /* Non-ignored signals revert to SIG_DFL on exec anyway */
@@ -244,16 +255,24 @@ int nc_main(int argc, char **argv)
244 IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);) 255 IF_NC_EXTRA(BB_EXECVP(execparam[0], execparam);)
245 IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);) 256 IF_NC_EXTRA(bb_perror_msg_and_die("can't execute '%s'", execparam[0]);)
246 } 257 }
258#endif
247 259
248 /* loop copying stdin to cfd, and cfd to stdout */ 260 /* loop copying stdin to cfd, and cfd to stdout */
249 261
262#if !ENABLE_PLATFORM_MINGW32
250 pfds[0].fd = STDIN_FILENO; 263 pfds[0].fd = STDIN_FILENO;
251 pfds[0].events = POLLIN; 264 pfds[0].events = POLLIN;
252 pfds[1].fd = cfd; 265 pfds[1].fd = cfd;
253 pfds[1].events = POLLIN; 266 pfds[1].events = POLLIN;
267#else
268 FD_ZERO(&readfds);
269 FD_SET(cfd, &readfds);
270 FD_SET(STDIN_FILENO, &readfds);
271#endif
254 272
255#define iobuf bb_common_bufsiz1 273#define iobuf bb_common_bufsiz1
256 setup_common_bufsiz(); 274 setup_common_bufsiz();
275#if !ENABLE_PLATFORM_MINGW32
257 for (;;) { 276 for (;;) {
258 int fdidx; 277 int fdidx;
259 int ofd; 278 int ofd;
@@ -288,5 +307,41 @@ int nc_main(int argc, char **argv)
288 fdidx++; 307 fdidx++;
289 } 308 }
290 } 309 }
310#else
311 for (;;) {
312 int fd;
313 int ofd;
314 int nread;
315
316 testfds = readfds;
317
318 if (select(cfd + 1, &testfds, NULL, NULL, NULL) < 0)
319 bb_simple_perror_msg_and_die("select");
320
321 fd = STDIN_FILENO;
322 while (1) {
323 if (FD_ISSET(fd, &testfds)) {
324 nread = safe_read(fd, iobuf, COMMON_BUFSIZE);
325 if (fd == cfd) {
326 if (nread < 1)
327 exit(EXIT_SUCCESS);
328 ofd = STDOUT_FILENO;
329 } else {
330 if (nread < 1) {
331 /* Close outgoing half-connection so they get EOF,
332 * but leave incoming alone so we can see response */
333 shutdown(cfd, SHUT_WR);
334 FD_CLR(STDIN_FILENO, &readfds);
335 }
336 ofd = cfd;
337 }
338 xwrite(ofd, iobuf, nread);
339 }
340 if (fd == cfd)
341 break;
342 fd = cfd;
343 }
344 }
345#endif
291} 346}
292#endif 347#endif
diff --git a/networking/ssl_client.c b/networking/ssl_client.c
index 397aad297..27575a2bf 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,14 @@ 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 init_winsock();
68 tls->ifd = tls->ofd = _open_osfhandle((intptr_t)h, _O_RDWR|_O_BINARY);
69 }
70#endif
50 71
51 tls_handshake(tls, sni); 72 tls_handshake(tls, sni);
52 73
diff --git a/networking/tls.c b/networking/tls.c
index 415952f16..5f40aec70 100644
--- a/networking/tls.c
+++ b/networking/tls.c
@@ -331,7 +331,7 @@ static void dump_tls_record(const void *vp, int len)
331 331
332void FAST_FUNC tls_get_random(void *buf, unsigned len) 332void FAST_FUNC tls_get_random(void *buf, unsigned len)
333{ 333{
334 if (len != open_read_close("/dev/urandom", buf, len)) 334 if (len != MINGW_SPECIAL(open_read_close)("/dev/urandom", buf, len))
335 xfunc_die(); 335 xfunc_die();
336} 336}
337 337
diff --git a/networking/wget.c b/networking/wget.c
index 9ec0e67b9..5470502aa 100644
--- a/networking/wget.c
+++ b/networking/wget.c
@@ -517,6 +517,9 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp)
517 fprintf(stderr, "--> %s%s\n\n", s1, s2); 517 fprintf(stderr, "--> %s%s\n\n", s1, s2);
518 fflush(fp); 518 fflush(fp);
519 log_io("> %s%s", s1, s2); 519 log_io("> %s%s", s1, s2);
520#if ENABLE_PLATFORM_MINGW32
521 fseek(fp, 0L, SEEK_CUR);
522#endif
520 } 523 }
521 524
522 /* Read until "Nxx something" is received */ 525 /* Read until "Nxx something" is received */
@@ -524,6 +527,9 @@ static int ftpcmd(const char *s1, const char *s2, FILE *fp)
524 do { 527 do {
525 fgets_trim_sanitize(fp, "%s\n"); 528 fgets_trim_sanitize(fp, "%s\n");
526 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' '); 529 } while (!isdigit(G.wget_buf[0]) || G.wget_buf[3] != ' ');
530#if ENABLE_PLATFORM_MINGW32
531 fseek(fp, 0L, SEEK_CUR);
532#endif
527 533
528 G.wget_buf[3] = '\0'; 534 G.wget_buf[3] = '\0';
529 result = xatoi_positive(G.wget_buf); 535 result = xatoi_positive(G.wget_buf);
@@ -766,6 +772,7 @@ static int spawn_https_helper_openssl(const char *host, unsigned port)
766#endif 772#endif
767 773
768#if ENABLE_FEATURE_WGET_HTTPS 774#if ENABLE_FEATURE_WGET_HTTPS
775# if !ENABLE_PLATFORM_MINGW32
769static void spawn_ssl_client(const char *host, int network_fd, int flags) 776static void spawn_ssl_client(const char *host, int network_fd, int flags)
770{ 777{
771 int sp[2]; 778 int sp[2];
@@ -820,6 +827,32 @@ static void spawn_ssl_client(const char *host, int network_fd, int flags)
820 close(sp[1]); 827 close(sp[1]);
821 xmove_fd(sp[0], network_fd); 828 xmove_fd(sp[0], network_fd);
822} 829}
830# else
831static void spawn_ssl_client(const char *host, int network_fd, int flags)
832{
833 int fd1;
834 char *servername, *p, *cmd;
835
836 servername = xstrdup(host);
837 p = strrchr(servername, ':');
838 if (p) *p = '\0';
839
840 fflush_all();
841
842 cmd = xasprintf("%s --busybox ssl_client -h %p -n %s%s",
843 bb_busybox_exec_path,
844 (void *)_get_osfhandle(network_fd), servername,
845 flags & TLSLOOP_EXIT_ON_LOCAL_EOF ? " -e" : "");
846
847 if ( (fd1=mingw_popen_fd(cmd, "b", -1, NULL)) == -1 ) {
848 bb_perror_msg_and_die("can't execute ssl_client");
849 }
850
851 free(cmd);
852 free(servername);
853 xmove_fd(fd1, network_fd);
854}
855# endif
823#endif 856#endif
824 857
825#if ENABLE_FEATURE_WGET_FTP 858#if ENABLE_FEATURE_WGET_FTP
diff --git a/procps/free.c b/procps/free.c
index 0b68e1b88..f19c38dd5 100644
--- a/procps/free.c
+++ b/procps/free.c
@@ -77,6 +77,7 @@ static const char *scale(struct globals *g, unsigned long d)
77 return make_human_readable_str(d, g->mem_unit, G_unit); 77 return make_human_readable_str(d, g->mem_unit, G_unit);
78} 78}
79 79
80#if !ENABLE_PLATFORM_MINGW32
80/* NOINLINE reduces main() stack usage, which makes code smaller (on x86 at least) */ 81/* NOINLINE reduces main() stack usage, which makes code smaller (on x86 at least) */
81static NOINLINE unsigned int parse_meminfo(struct globals *g) 82static NOINLINE unsigned int parse_meminfo(struct globals *g)
82{ 83{
@@ -103,6 +104,14 @@ static NOINLINE unsigned int parse_meminfo(struct globals *g)
103 104
104 return seen_cached_and_available_and_reclaimable == 0; 105 return seen_cached_and_available_and_reclaimable == 0;
105} 106}
107#else
108static NOINLINE unsigned int parse_meminfo(struct globals *g)
109{
110 g->cached_kb = g->available_kb = g->reclaimable_kb = 0;
111
112 return 1;
113}
114#endif
106 115
107int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE; 116int free_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
108int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM)) 117int free_main(int argc UNUSED_PARAM, char **argv IF_NOT_DESKTOP(UNUSED_PARAM))
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 8f10e21ab..8c2bc2b6f 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 c78c1f0a0..883c4d52d 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 03b9c418c..4c6e07e8b 100644
--- a/procps/ps.c
+++ b/procps/ps.c
@@ -120,7 +120,7 @@ enum { MAX_WIDTH = 2*1024 };
120#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG 120#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
121static unsigned long get_uptime(void) 121static unsigned long get_uptime(void)
122{ 122{
123#ifdef __linux__ 123#if defined __linux__ || ENABLE_PLATFORM_MINGW32
124 struct sysinfo info; 124 struct sysinfo info;
125 if (sysinfo(&info) < 0) 125 if (sysinfo(&info) < 0)
126 return 0; 126 return 0;
@@ -237,10 +237,12 @@ static void func_comm(char *buf, int size, const procps_status_t *ps)
237 safe_strncpy(buf, ps->comm, size+1); 237 safe_strncpy(buf, ps->comm, size+1);
238} 238}
239 239
240#if !ENABLE_PLATFORM_MINGW32
240static void func_state(char *buf, int size, const procps_status_t *ps) 241static void func_state(char *buf, int size, const procps_status_t *ps)
241{ 242{
242 safe_strncpy(buf, ps->state, size+1); 243 safe_strncpy(buf, ps->state, size+1);
243} 244}
245#endif
244 246
245static void func_args(char *buf, int size, const procps_status_t *ps) 247static void func_args(char *buf, int size, const procps_status_t *ps)
246{ 248{
@@ -257,6 +259,7 @@ static void func_ppid(char *buf, int size, const procps_status_t *ps)
257 sprintf(buf, "%*u", size, ps->ppid); 259 sprintf(buf, "%*u", size, ps->ppid);
258} 260}
259 261
262#if !ENABLE_PLATFORM_MINGW32
260static void func_pgid(char *buf, int size, const procps_status_t *ps) 263static void func_pgid(char *buf, int size, const procps_status_t *ps)
261{ 264{
262 sprintf(buf, "%*u", size, ps->pgid); 265 sprintf(buf, "%*u", size, ps->pgid);
@@ -293,6 +296,7 @@ static void func_tty(char *buf, int size, const procps_status_t *ps)
293 if (ps->tty_major) /* tty field of "0" means "no tty" */ 296 if (ps->tty_major) /* tty field of "0" means "no tty" */
294 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor); 297 snprintf(buf, size+1, "%u,%u", ps->tty_major, ps->tty_minor);
295} 298}
299#endif
296 300
297#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS 301#if ENABLE_FEATURE_PS_ADDITIONAL_COLUMNS
298static void func_rgroup(char *buf, int size, const procps_status_t *ps) 302static void func_rgroup(char *buf, int size, const procps_status_t *ps)
@@ -383,7 +387,9 @@ static const ps_out_t out_spec[] ALIGN_PTR = {
383 { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM }, 387 { MAX_WIDTH , "args" ,"COMMAND",func_args ,PSSCAN_COMM },
384 { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID }, 388 { 5 , "pid" ,"PID" ,func_pid ,PSSCAN_PID },
385 { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID }, 389 { 5 , "ppid" ,"PPID" ,func_ppid ,PSSCAN_PPID },
390#if !ENABLE_PLATFORM_MINGW32
386 { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID }, 391 { 5 , "pgid" ,"PGID" ,func_pgid ,PSSCAN_PGID },
392#endif
387#if ENABLE_FEATURE_PS_TIME 393#if ENABLE_FEATURE_PS_TIME
388 { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME }, 394 { sizeof("ELAPSED")-1, "etime" ,"ELAPSED",func_etime ,PSSCAN_START_TIME },
389#endif 395#endif
@@ -396,12 +402,14 @@ static const ps_out_t out_spec[] ALIGN_PTR = {
396#if ENABLE_FEATURE_PS_TIME 402#if ENABLE_FEATURE_PS_TIME
397 { 5 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME }, 403 { 5 , "time" ,"TIME" ,func_time ,PSSCAN_STIME | PSSCAN_UTIME },
398#endif 404#endif
405#if !ENABLE_PLATFORM_MINGW32
399 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY }, 406 { 6 , "tty" ,"TT" ,func_tty ,PSSCAN_TTY },
400 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ }, 407 { 4 , "vsz" ,"VSZ" ,func_vsz ,PSSCAN_VSZ },
401/* Not mandated, but useful: */ 408/* Not mandated, but useful: */
402 { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID }, 409 { 5 , "sid" ,"SID" ,func_sid ,PSSCAN_SID },
403 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE }, 410 { 4 , "stat" ,"STAT" ,func_state ,PSSCAN_STATE },
404 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS }, 411 { 4 , "rss" ,"RSS" ,func_rss ,PSSCAN_RSS },
412#endif
405#if ENABLE_SELINUX 413#if ENABLE_SELINUX
406 { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT }, 414 { 35 , "label" ,"LABEL" ,func_label ,PSSCAN_CONTEXT },
407#endif 415#endif
@@ -544,6 +552,8 @@ static void format_process(const procps_status_t *ps)
544#if ENABLE_SELINUX 552#if ENABLE_SELINUX
545# define SELINUX_O_PREFIX "label," 553# define SELINUX_O_PREFIX "label,"
546# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args") 554# define DEFAULT_O_STR (SELINUX_O_PREFIX "pid,user" IF_FEATURE_PS_TIME(",time") ",args")
555#elif ENABLE_PLATFORM_MINGW32
556# define DEFAULT_O_STR ("pid,ppid,user" IF_FEATURE_PS_TIME(",time,etime") ",args")
547#else 557#else
548# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args") 558# define DEFAULT_O_STR ("pid,user" IF_FEATURE_PS_TIME(",time") ",args")
549#endif 559#endif
diff --git a/procps/smemcap.c b/procps/smemcap.c
index 2f1897dae..429cc8a3e 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/.gitignore b/scripts/basic/.gitignore
index d91e941a4..3d20e4f15 100644
--- a/scripts/basic/.gitignore
+++ b/scripts/basic/.gitignore
@@ -2,3 +2,6 @@ hash
2fixdep 2fixdep
3docproc 3docproc
4split-include 4split-include
5/docproc.exe
6/fixdep.exe
7/split-include.exe
diff --git a/scripts/basic/docproc.c b/scripts/basic/docproc.c
index 4464e1874..2c7a19b83 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 426b4888b..64fd92f06 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((unsigned char)*p)); 394 do p--; while (p != m && !isalnum((unsigned char)*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 6ef29195e..290bea2fb 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/bloat-o-meter b/scripts/bloat-o-meter
index b4a1d2811..26474595f 100755
--- a/scripts/bloat-o-meter
+++ b/scripts/bloat-o-meter
@@ -43,6 +43,8 @@ if f1 is None or f2 is None:
43 43
44sym_args = " ".join(sys.argv[3 + flag_timing + dashes:]) 44sym_args = " ".join(sys.argv[3 + flag_timing + dashes:])
45def getsizes(file): 45def getsizes(file):
46 if file.endswith(".exe"):
47 return getsizes_pe(file)
46 sym, alias, lut, section = {}, {}, {}, {} 48 sym, alias, lut, section = {}, {}, {}, {}
47 for l in os.popen("readelf -W -S " + file).readlines(): 49 for l in os.popen("readelf -W -S " + file).readlines():
48 x = l.replace("[ ", "[", 1).split() 50 x = l.replace("[ ", "[", 1).split()
@@ -80,6 +82,29 @@ def getsizes(file):
80 sym[alias[(addr, sz)]["name"]] = {"addr" : addr, "size": sz} 82 sym[alias[(addr, sz)]["name"]] = {"addr" : addr, "size": sz}
81 return sym 83 return sym
82 84
85def getsizes_pe(file):
86 sym, sections = {}, {}
87 prefix = os.getenv("CROSS_COMPILE", "")
88 for l in os.popen(prefix + "objdump -h " + file).readlines():
89 x = l.split()
90 if len(x) != 7: continue
91 sections[x[1]] = 1
92 if x[1] not in [".rdata"]: continue
93 sym[x[1]] = {"addr" : int(x[3], 16), "size" : int(x[2], 16)}
94 for l in os.popen(prefix + "nm -S --size-sort %s %s" % (sym_args, file)).readlines():
95 if len(l.split()) != 4: continue
96 value, size, typ, name = l.split()
97 if typ in ["N"]: continue # skip debug symbols
98 if name in sections: continue # bare reference to section
99 value = int(value, 16)
100 size = int(size, 16)
101 if "$" in name:
102 section, name = name.split("$")
103 if section in sym:
104 sym[section]["size"] -= size
105 sym[name] = {"addr" : value, "size": size}
106 return sym
107
83if flag_timing: 108if flag_timing:
84 start_t1 = int(time.time() * 1e9) 109 start_t1 = int(time.time() * 1e9)
85old = getsizes(f1) 110old = getsizes(f1)
diff --git a/scripts/kconfig/.gitignore b/scripts/kconfig/.gitignore
index b49584c93..aa9c7d637 100644
--- a/scripts/kconfig/.gitignore
+++ b/scripts/kconfig/.gitignore
@@ -17,3 +17,4 @@ mconf
17qconf 17qconf
18gconf 18gconf
19kxgettext 19kxgettext
20/conf.exe
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 39ec1cdb6..4680932d7 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 249a3195e..2f7fa6618 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -584,15 +584,24 @@ int conf_write(const char *name)
584 fclose(out); 584 fclose(out);
585 if (out_h) { 585 if (out_h) {
586 fclose(out_h); 586 fclose(out_h);
587#ifdef __MINGW32__
588 unlink("include/autoconf.h");
589#endif
587 rename(".tmpconfig.h", "include/autoconf.h"); 590 rename(".tmpconfig.h", "include/autoconf.h");
588 } 591 }
589 if (!name || basename != conf_def_filename) { 592 if (!name || basename != conf_def_filename) {
590 if (!name) 593 if (!name)
591 name = conf_def_filename; 594 name = conf_def_filename;
592 sprintf(tmpname, "%s.old", name); 595 sprintf(tmpname, "%s.old", name);
596#ifdef __MINGW32__
597 unlink(tmpname);
598#endif
593 rename(name, tmpname); 599 rename(name, tmpname);
594 } 600 }
595 sprintf(tmpname, "%s%s", dirname, basename); 601 sprintf(tmpname, "%s%s", dirname, basename);
602#ifdef __MINGW32__
603 unlink(tmpname);
604#endif
596 if (rename(newname, tmpname)) 605 if (rename(newname, tmpname))
597 return 1; 606 return 1;
598 607
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 aaf82820e..55afeb763 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,13 +25,17 @@
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
34#ifndef __MINGW32__
30#ifndef SIGWINCH 35#ifndef SIGWINCH
31#define SIGWINCH 28 36#define SIGWINCH 28
32#endif 37#endif
38#endif
33 39
34#define LKC_DIRECT_LINK 40#define LKC_DIRECT_LINK
35#include "lkc.h" 41#include "lkc.h"
@@ -270,11 +276,15 @@ static char input_buf[4096];
270static const char filename[] = ".config"; 276static const char filename[] = ".config";
271static char *args[1024], **argptr = args; 277static char *args[1024], **argptr = args;
272static int indent; 278static int indent;
279#ifndef __MINGW32__
273static struct termios ios_org; 280static struct termios ios_org;
281#endif
274static int rows = 0, cols = 0; 282static int rows = 0, cols = 0;
275static struct menu *current_menu; 283static struct menu *current_menu;
276static int child_count; 284static int child_count;
285#ifndef __MINGW32__
277static int do_resize; 286static int do_resize;
287#endif
278static int single_menu_mode; 288static int single_menu_mode;
279 289
280static void conf(struct menu *menu); 290static void conf(struct menu *menu);
@@ -294,6 +304,9 @@ static int cprint(const char *fmt, ...);
294 304
295static void init_wsize(void) 305static void init_wsize(void)
296{ 306{
307#ifdef __MINGW32__
308 fprintf(stderr, "Skipping attempt to change window size\n");
309#else
297 struct winsize ws; 310 struct winsize ws;
298 char *env; 311 char *env;
299 312
@@ -325,6 +338,7 @@ static void init_wsize(void)
325 338
326 rows -= 4; 339 rows -= 4;
327 cols -= 5; 340 cols -= 5;
341#endif
328} 342}
329 343
330static void cprint_init(void) 344static void cprint_init(void)
@@ -461,6 +475,10 @@ static void winch_handler(int sig)
461 475
462static int exec_conf(void) 476static int exec_conf(void)
463{ 477{
478#ifdef __MINGW32__
479 fprintf(stderr, "exec_conf not implemented\n");
480 exit(1);
481#else
464 int pipefd[2], stat, size; 482 int pipefd[2], stat, size;
465 sigset_t sset, osset; 483 sigset_t sset, osset;
466 484
@@ -535,6 +553,7 @@ static int exec_conf(void)
535 sigprocmask(SIG_SETMASK, &osset, NULL); 553 sigprocmask(SIG_SETMASK, &osset, NULL);
536 554
537 return WEXITSTATUS(stat); 555 return WEXITSTATUS(stat);
556#endif
538} 557}
539 558
540static void search_conf(void) 559static void search_conf(void)
@@ -788,7 +807,7 @@ static void conf(struct menu *menu)
788 switch (type) { 807 switch (type) {
789 case 'm': 808 case 'm':
790 if (single_menu_mode) 809 if (single_menu_mode)
791 submenu->data = (void *) (long) !submenu->data; 810 submenu->data = (void *) (intptr_t) !submenu->data;
792 else 811 else
793 conf(submenu); 812 conf(submenu);
794 break; 813 break;
@@ -1051,7 +1070,9 @@ static void conf_save(void)
1051 1070
1052static void conf_cleanup(void) 1071static void conf_cleanup(void)
1053{ 1072{
1073#ifndef __MINGW32__
1054 tcsetattr(1, TCSAFLUSH, &ios_org); 1074 tcsetattr(1, TCSAFLUSH, &ios_org);
1075#endif
1055 unlink(".help.tmp"); 1076 unlink(".help.tmp");
1056 unlink("lxdialog.scrltmp"); 1077 unlink("lxdialog.scrltmp");
1057} 1078}
@@ -1080,7 +1101,9 @@ int main(int ac, char **av)
1080 single_menu_mode = 1; 1101 single_menu_mode = 1;
1081 } 1102 }
1082 1103
1104#ifndef __MINGW32__
1083 tcgetattr(1, &ios_org); 1105 tcgetattr(1, &ios_org);
1106#endif
1084 atexit(conf_cleanup); 1107 atexit(conf_cleanup);
1085 init_wsize(); 1108 init_wsize();
1086 conf(&rootmenu); 1109 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 827643808..ad1e5ba7e 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 * - both / and \ are supported in PATH. Usually you must use /
27 * - job control doesn't work, though the jobs builtin is available
28 * - trap doesn't work for signals, only EXIT
29 * - /dev/null is supported for redirection
30 * - fake $PPID
31 */
32
18//config:config SHELL_ASH 33//config:config SHELL_ASH
19//config: bool #hidden option 34//config: bool #hidden option
20//config: depends on !NOMMU 35//config: depends on !NOMMU
@@ -153,6 +168,25 @@
153//config: you to run the specified command or builtin, 168//config: you to run the specified command or builtin,
154//config: even when there is a function with the same name. 169//config: even when there is a function with the same name.
155//config: 170//config:
171//config:
172//config:config ASH_NOCONSOLE
173//config: bool "'noconsole' option"
174//config: default y
175//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
176//config: help
177//config: Enable support for the 'noconsole' option, which attempts to
178//config: hide the console normally associated with a command line
179//config: application. This may be useful when running a shell script
180//config: from a GUI application.
181//config:
182//config:config ASH_NOCASEGLOB
183//config: bool "'nocaseglob' option"
184//config: default y
185//config: depends on (ASH || SH_IS_ASH || BASH_IS_ASH) && PLATFORM_MINGW32
186//config: help
187//config: Enable support for the 'nocaseglob' option, which allows
188//config: case-insensitive filename globbing.
189//config:
156//config:endif # ash options 190//config:endif # ash options
157 191
158//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP)) 192//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
@@ -178,7 +212,17 @@
178 212
179#define PROFILE 0 213#define PROFILE 0
180 214
215/*
216 * Only one of JOBS or JOBS_WIN32 is enabled at a time (or neither).
217 * JOBS_WIN32 doesn't enable job control, just some job-related features.
218 */
219#if ENABLE_PLATFORM_MINGW32
220#define JOBS_WIN32 ENABLE_ASH_JOB_CONTROL
221#define JOBS 0
222#else
223#define JOBS_WIN32 0
181#define JOBS ENABLE_ASH_JOB_CONTROL 224#define JOBS ENABLE_ASH_JOB_CONTROL
225#endif
182 226
183#include <fnmatch.h> 227#include <fnmatch.h>
184#include <sys/times.h> 228#include <sys/times.h>
@@ -299,10 +343,81 @@ typedef long arith_t;
299# define unlikely(cond) (cond) 343# define unlikely(cond) (cond)
300#endif 344#endif
301 345
346#if !ENABLE_PLATFORM_MINGW32
347# define is_relative_path(path) ((path)[0] != '/')
348#endif
349
302#if !BB_MMU 350#if !BB_MMU
303# error "Do not even bother, ash will not run on NOMMU machine" 351# error "Do not even bother, ash will not run on NOMMU machine"
304#endif 352#endif
305 353
354#if ENABLE_PLATFORM_MINGW32
355# define FORKSHELL_DEBUG 0
356
357union node;
358struct strlist;
359struct job;
360
361struct forkshell {
362 /* filled by forkshell_copy() */
363 struct globals_var *gvp;
364 struct globals_misc *gmp;
365 struct tblentry **cmdtable;
366#if ENABLE_ASH_ALIAS
367 struct alias **atab;
368#endif
369#if MAX_HISTORY
370 char **history;
371 int cnt_history;
372#endif
373 /* struct parsefile *g_parsefile; */
374 HANDLE hMapFile;
375 char *old_base;
376 int size;
377# if FORKSHELL_DEBUG
378 int funcblocksize;
379 int funcstringsize;
380# endif
381 int relocatesize;
382
383 /* type of forkshell */
384 int fpid;
385
386 /* generic data, used by forkshell_child */
387 int mode;
388 int nprocs;
389
390 /* optional data, used by forkshell_child */
391 int flags;
392 int fd[3];
393 union node *n;
394 char **argv;
395 char *path;
396};
397
398enum {
399 FS_OPENHERE,
400 FS_EVALBACKCMD,
401 FS_EVALSUBSHELL,
402 FS_EVALPIPE,
403 FS_SHELLEXEC
404};
405
406static struct forkshell* forkshell_prepare(struct forkshell *fs);
407static void forkshell_init(const char *idstr);
408static void *sticky_mem_start, *sticky_mem_end;
409static void sticky_free(void *p);
410# define free(p) sticky_free(p)
411#if !JOBS && !JOBS_WIN32
412#define spawn_forkshell(fs, jp, n, mode) spawn_forkshell(fs, jp, mode)
413#endif
414static void spawn_forkshell(struct forkshell *fs, struct job *jp,
415 union node *n, int mode);
416# if FORKSHELL_DEBUG
417static void forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes);
418# endif
419#endif
420
306/* ============ Hash table sizes. Configurable. */ 421/* ============ Hash table sizes. Configurable. */
307 422
308#define VTABSIZE 39 423#define VTABSIZE 39
@@ -330,7 +445,11 @@ static const char *const optletters_optnames[] = {
330 "m" "monitor", 445 "m" "monitor",
331 "n" "noexec", 446 "n" "noexec",
332/* Ditto: bash has no "set -s", "set -c" */ 447/* Ditto: bash has no "set -s", "set -c" */
448#if !ENABLE_PLATFORM_MINGW32
333 "s" "", 449 "s" "",
450#else
451 "s" "stdin",
452#endif
334 "c" "", 453 "c" "",
335 "x" "xtrace", 454 "x" "xtrace",
336 "v" "verbose", 455 "v" "verbose",
@@ -347,6 +466,15 @@ static const char *const optletters_optnames[] = {
347 ,"\0" "nolog" 466 ,"\0" "nolog"
348 ,"\0" "debug" 467 ,"\0" "debug"
349#endif 468#endif
469#if ENABLE_PLATFORM_MINGW32
470 ,"X" "winxp"
471#endif
472#if ENABLE_ASH_NOCONSOLE
473 ,"\0" "noconsole"
474#endif
475#if ENABLE_ASH_NOCASEGLOB
476 ,"\0" "nocaseglob"
477#endif
350}; 478};
351//bash 4.4.23 also has these opts (with these defaults): 479//bash 4.4.23 also has these opts (with these defaults):
352//braceexpand on 480//braceexpand on
@@ -389,28 +517,42 @@ struct jmploc {
389struct globals_misc { 517struct globals_misc {
390 uint8_t exitstatus; /* exit status of last command */ 518 uint8_t exitstatus; /* exit status of last command */
391 uint8_t back_exitstatus;/* exit status of backquoted command */ 519 uint8_t back_exitstatus;/* exit status of backquoted command */
520#if !ENABLE_PLATFORM_MINGW32
392 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */ 521 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
522#endif
393 smallint inps4; /* Prevent PS4 nesting. */ 523 smallint inps4; /* Prevent PS4 nesting. */
394 int savestatus; /* exit status of last command outside traps */ 524 int savestatus; /* exit status of last command outside traps */
395 int rootpid; /* pid of main shell */ 525 int rootpid; /* pid of main shell */
396 /* shell level: 0 for the main shell, 1 for its children, and so on */ 526 /* shell level: 0 for the main shell, 1 for its children, and so on */
397 int shlvl; 527 int shlvl;
528#if ENABLE_PLATFORM_MINGW32
529 int loopnest; /* current loop nesting level */
530#endif
398#define rootshell (!shlvl) 531#define rootshell (!shlvl)
399 int errlinno; 532 int errlinno;
400 533
401 char *minusc; /* argument to -c option */ 534 char *minusc; /* argument to -c option */
535#if ENABLE_PLATFORM_MINGW32
536 char *dirarg; /* argument to -d option */
537 char *title; /* argument to -t option */
538#endif
402 539
403 char *curdir; // = nullstr; /* current working directory */ 540 char *curdir; // = nullstr; /* current working directory */
404 char *physdir; // = nullstr; /* physical working directory */ 541 char *physdir; // = nullstr; /* physical working directory */
405 542
406 char *arg0; /* value of $0 */ 543 char *arg0; /* value of $0 */
544#if ENABLE_PLATFORM_MINGW32
545 char *commandname;
546#endif
407 547
408 struct jmploc *exception_handler; 548 struct jmploc *exception_handler;
409 549
410 volatile int suppress_int; /* counter */ 550 volatile int suppress_int; /* counter */
411 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */ 551 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
552#if !ENABLE_PLATFORM_MINGW32
412 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */ 553 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
413 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */ 554 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
555#endif
414 smallint exception_type; /* kind of exception: */ 556 smallint exception_type; /* kind of exception: */
415#define EXINT 0 /* SIGINT received */ 557#define EXINT 0 /* SIGINT received */
416#define EXERROR 1 /* a generic error */ 558#define EXERROR 1 /* a generic error */
@@ -445,8 +587,18 @@ struct globals_misc {
445# define nolog optlist[16 + BASH_PIPEFAIL] 587# define nolog optlist[16 + BASH_PIPEFAIL]
446# define debug optlist[17 + BASH_PIPEFAIL] 588# define debug optlist[17 + BASH_PIPEFAIL]
447#endif 589#endif
590#if ENABLE_PLATFORM_MINGW32
591# define winxp optlist[16 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
592# if ENABLE_ASH_NOCONSOLE
593# define noconsole optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0)]
594# endif
595# if ENABLE_ASH_NOCASEGLOB
596# define nocaseglob optlist[17 + BASH_PIPEFAIL + 2*(DEBUG != 0) + ENABLE_ASH_NOCONSOLE]
597# endif
598#endif
448 599
449 /* trap handler commands */ 600 /* trap handler commands */
601#if !ENABLE_PLATFORM_MINGW32
450 /* 602 /*
451 * Sigmode records the current value of the signal handlers for the various 603 * Sigmode records the current value of the signal handlers for the various
452 * modes. A value of zero means that the current handler is not known. 604 * modes. A value of zero means that the current handler is not known.
@@ -461,12 +613,15 @@ struct globals_misc {
461 /* indicates specified signal received */ 613 /* indicates specified signal received */
462 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */ 614 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
463 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */ 615 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
616#endif
464 char *trap[NSIG + 1]; 617 char *trap[NSIG + 1];
465/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */ 618/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
466#define NTRAP_ERR NSIG 619#define NTRAP_ERR NSIG
467#define NTRAP_LAST NSIG 620#define NTRAP_LAST NSIG
468 621
622#if !ENABLE_PLATFORM_MINGW32
469 char **trap_ptr; /* used only by "trap hack" */ 623 char **trap_ptr; /* used only by "trap hack" */
624#endif
470 625
471 /* Rarely referenced stuff */ 626 /* Rarely referenced stuff */
472#if ENABLE_ASH_RANDOM_SUPPORT 627#if ENABLE_ASH_RANDOM_SUPPORT
@@ -484,10 +639,20 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
484#define rootpid (G_misc.rootpid ) 639#define rootpid (G_misc.rootpid )
485#define shlvl (G_misc.shlvl ) 640#define shlvl (G_misc.shlvl )
486#define errlinno (G_misc.errlinno ) 641#define errlinno (G_misc.errlinno )
642#if ENABLE_PLATFORM_MINGW32
643#define loopnest (G_misc.loopnest )
644#endif
487#define minusc (G_misc.minusc ) 645#define minusc (G_misc.minusc )
646#if ENABLE_PLATFORM_MINGW32
647#define dirarg (G_misc.dirarg )
648#define title (G_misc.title )
649#endif
488#define curdir (G_misc.curdir ) 650#define curdir (G_misc.curdir )
489#define physdir (G_misc.physdir ) 651#define physdir (G_misc.physdir )
490#define arg0 (G_misc.arg0 ) 652#define arg0 (G_misc.arg0 )
653#if ENABLE_PLATFORM_MINGW32
654#define commandname (G_misc.commandname)
655#endif
491#define exception_handler (G_misc.exception_handler) 656#define exception_handler (G_misc.exception_handler)
492#define exception_type (G_misc.exception_type ) 657#define exception_type (G_misc.exception_type )
493#define suppress_int (G_misc.suppress_int ) 658#define suppress_int (G_misc.suppress_int )
@@ -503,12 +668,23 @@ extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
503#define trap_ptr (G_misc.trap_ptr ) 668#define trap_ptr (G_misc.trap_ptr )
504#define random_gen (G_misc.random_gen ) 669#define random_gen (G_misc.random_gen )
505#define backgndpid (G_misc.backgndpid ) 670#define backgndpid (G_misc.backgndpid )
671
672#if ENABLE_PLATFORM_MINGW32
673#undef got_sigchld
674#undef pending_sig
675#undef may_have_traps
676#undef trap_ptr
677#define pending_sig (0)
678#define may_have_traps (0)
679#define trap_ptr trap
680#endif
681
506#define INIT_G_misc() do { \ 682#define INIT_G_misc() do { \
507 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \ 683 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
508 savestatus = -1; \ 684 savestatus = -1; \
509 curdir = nullstr; \ 685 curdir = nullstr; \
510 physdir = nullstr; \ 686 physdir = nullstr; \
511 trap_ptr = trap; \ 687 IF_NOT_PLATFORM_MINGW32(trap_ptr = trap;) \
512} while (0) 688} while (0)
513 689
514 690
@@ -518,6 +694,9 @@ static void trace_printf(const char *fmt, ...);
518static void trace_vprintf(const char *fmt, va_list va); 694static void trace_vprintf(const char *fmt, va_list va);
519# define TRACE(param) trace_printf param 695# define TRACE(param) trace_printf param
520# define TRACEV(param) trace_vprintf param 696# define TRACEV(param) trace_vprintf param
697# if ENABLE_PLATFORM_MINGW32 && defined(close)
698# undef close
699# endif
521# define close(fd) do { \ 700# define close(fd) do { \
522 int dfd = (fd); \ 701 int dfd = (fd); \
523 if (close(dfd) < 0) \ 702 if (close(dfd) < 0) \
@@ -611,7 +790,9 @@ struct parsefile {
611 790
612static struct parsefile basepf; /* top level input file */ 791static struct parsefile basepf; /* top level input file */
613static struct parsefile *g_parsefile = &basepf; /* current input file */ 792static struct parsefile *g_parsefile = &basepf; /* current input file */
793#if ENABLE_PLATFORM_POSIX
614static char *commandname; /* currently executing command */ 794static char *commandname; /* currently executing command */
795#endif
615 796
616 797
617/* ============ Interrupts / exceptions */ 798/* ============ Interrupts / exceptions */
@@ -673,9 +854,11 @@ static void
673raise_interrupt(void) 854raise_interrupt(void)
674{ 855{
675 pending_int = 0; 856 pending_int = 0;
857#if !ENABLE_PLATFORM_MINGW32
676 /* Signal is not automatically unmasked after it is raised, 858 /* Signal is not automatically unmasked after it is raised,
677 * do it ourself - unmask all signals */ 859 * do it ourself - unmask all signals */
678 sigprocmask_allsigs(SIG_UNBLOCK); 860 sigprocmask_allsigs(SIG_UNBLOCK);
861#endif
679 /* pending_sig = 0; - now done in signal_handler() */ 862 /* pending_sig = 0; - now done in signal_handler() */
680 863
681 if (!(rootshell && iflag)) { 864 if (!(rootshell && iflag)) {
@@ -683,6 +866,10 @@ raise_interrupt(void)
683 signal(SIGINT, SIG_DFL); 866 signal(SIGINT, SIG_DFL);
684 raise(SIGINT); 867 raise(SIGINT);
685 } 868 }
869#if ENABLE_PLATFORM_MINGW32
870 if (iflag)
871 write(STDOUT_FILENO, "^C", 2);
872#endif
686 /* bash: ^C even on empty command line sets $? */ 873 /* bash: ^C even on empty command line sets $? */
687 exitstatus = SIGINT + 128; 874 exitstatus = SIGINT + 128;
688 raise_exception(EXINT); 875 raise_exception(EXINT);
@@ -1966,6 +2153,22 @@ maybe_single_quote(const char *s)
1966 return single_quote(s); 2153 return single_quote(s);
1967} 2154}
1968 2155
2156#if ENABLE_PLATFORM_MINGW32
2157/*
2158 * Place 'path' in a string on the stack, adding the system drive prefix
2159 * if necessary and leaving room for an optional extension.
2160 */
2161static char *
2162stack_add_system_drive(const char *path)
2163{
2164 const char *sd = need_system_drive(path);
2165 char *p = growstackto(strlen(path) + 5 + (sd ? strlen(sd) : 0));
2166
2167 strcpy(stpcpy(p, sd ?: ""), path);
2168 return p;
2169}
2170#endif
2171
1969 2172
1970/* ============ nextopt */ 2173/* ============ nextopt */
1971 2174
@@ -2120,6 +2323,17 @@ static void change_seconds(const char *) FAST_FUNC;
2120static void change_realtime(const char *) FAST_FUNC; 2323static void change_realtime(const char *) FAST_FUNC;
2121#endif 2324#endif
2122 2325
2326#if ENABLE_PLATFORM_MINGW32
2327static void FAST_FUNC
2328change_skip_ansi(const char *newval UNUSED_PARAM)
2329{
2330 skip_ansi_emulation(TRUE);
2331}
2332
2333# define LINENO_INDEX (5 + 2 * ENABLE_ASH_MAIL + ENABLE_ASH_GETOPTS)
2334# define FUNCNAME_INDEX (LINENO_INDEX + 1)
2335#endif
2336
2123static const struct { 2337static const struct {
2124 int flags; 2338 int flags;
2125 const char *var_text; 2339 const char *var_text;
@@ -2157,6 +2371,9 @@ static const struct {
2157#if ENABLE_FEATURE_EDITING_SAVEHISTORY 2371#if ENABLE_FEATURE_EDITING_SAVEHISTORY
2158 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL }, 2372 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
2159#endif 2373#endif
2374#if ENABLE_PLATFORM_MINGW32
2375 { VSTRFIXED|VTEXTFIXED|VUNSET, bb_skip_ansi_emulation, change_skip_ansi },
2376#endif
2160}; 2377};
2161 2378
2162struct redirtab; 2379struct redirtab;
@@ -2402,6 +2619,42 @@ bltinlookup(const char *name)
2402 return lookupvar(name); 2619 return lookupvar(name);
2403} 2620}
2404 2621
2622#if ENABLE_PLATFORM_MINGW32
2623static char *
2624fix_pathvar(const char *path, int len)
2625{
2626 char *newpath = xstrdup(path);
2627 char *p;
2628 int modified = FALSE;
2629
2630 p = newpath + len;
2631 while (*p) {
2632 if (*p != ':' && *p != ';') {
2633 /* skip drive */
2634 if (isalpha(*p) && p[1] == ':')
2635 p += 2;
2636 /* skip through path component */
2637 for (; *p != '\0' && *p != ':' && *p != ';'; ++p)
2638 continue;
2639 }
2640 /* *p is ':', ';' or '\0' here */
2641 if (*p == ':') {
2642 *p++ = ';';
2643 modified = TRUE;
2644 }
2645 else if (*p == ';') {
2646 ++p;
2647 }
2648 }
2649
2650 if (!modified) {
2651 free(newpath);
2652 newpath = NULL;
2653 }
2654 return newpath;
2655}
2656#endif
2657
2405/* 2658/*
2406 * Same as setvar except that the variable and value are passed in 2659 * Same as setvar except that the variable and value are passed in
2407 * the first argument as name=value. Since the first argument will 2660 * the first argument as name=value. Since the first argument will
@@ -2414,6 +2667,27 @@ setvareq(char *s, int flags)
2414{ 2667{
2415 struct var *vp, **vpp; 2668 struct var *vp, **vpp;
2416 2669
2670#if ENABLE_PLATFORM_MINGW32
2671 const char *paths = "PATH=\0""CDPATH=\0""MANPATH=\0";
2672 const char *p;
2673 int len;
2674
2675 for (p = paths; *p; p += len + 1) {
2676 len = strlen(p);
2677 if (strncmp(s, p, len) == 0) {
2678 char *newpath = fix_pathvar(s, len);
2679 if (newpath) {
2680 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2681 free(s);
2682 flags |= VNOSAVE;
2683 flags &= ~(VTEXTFIXED|VSTACK);
2684 s = newpath;
2685 }
2686 break;
2687 }
2688 }
2689#endif
2690
2417 vpp = hashvar(s); 2691 vpp = hashvar(s);
2418 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1)); 2692 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
2419 vpp = findvar(vpp, s); 2693 vpp = findvar(vpp, s);
@@ -2631,13 +2905,17 @@ static const char *pathopt; /* set by padvance */
2631static int 2905static int
2632padvance_magic(const char **path, const char *name, int magic) 2906padvance_magic(const char **path, const char *name, int magic)
2633{ 2907{
2634 const char *term = "%:"; 2908 const char *term = "%"PATH_SEP_STR;
2635 const char *lpathopt; 2909 const char *lpathopt;
2636 const char *p; 2910 const char *p;
2637 char *q; 2911 char *q;
2638 const char *start; 2912 const char *start;
2639 size_t qlen; 2913 size_t qlen;
2640 size_t len; 2914 size_t len;
2915#if ENABLE_PLATFORM_MINGW32
2916 size_t sdlen = 0;
2917 const char *sd;
2918#endif
2641 2919
2642 if (*path == NULL) 2920 if (*path == NULL)
2643 return -1; 2921 return -1;
@@ -2648,14 +2926,14 @@ padvance_magic(const char **path, const char *name, int magic)
2648 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) { 2926 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2649 lpathopt = start + 1; 2927 lpathopt = start + 1;
2650 start = p; 2928 start = p;
2651 term = ":"; 2929 term = PATH_SEP_STR;
2652 } 2930 }
2653 2931
2654 len = strcspn(start, term); 2932 len = strcspn(start, term);
2655 p = start + len; 2933 p = start + len;
2656 2934
2657 if (*p == '%') { 2935 if (*p == '%') {
2658 size_t extra = strchrnul(p, ':') - p; 2936 size_t extra = strchrnul(p, PATH_SEP) - p;
2659 2937
2660 if (legal_pathopt(p + 1, term, magic)) 2938 if (legal_pathopt(p + 1, term, magic))
2661 lpathopt = p + 1; 2939 lpathopt = p + 1;
@@ -2666,14 +2944,27 @@ padvance_magic(const char **path, const char *name, int magic)
2666 } 2944 }
2667 2945
2668 pathopt = lpathopt; 2946 pathopt = lpathopt;
2669 *path = *p == ':' ? p + 1 : NULL; 2947 *path = *p == PATH_SEP ? p + 1 : NULL;
2670 2948
2671 /* "2" is for '/' and '\0' */ 2949 /* "2" is for '/' and '\0' */
2672 qlen = len + strlen(name) + 2; 2950 qlen = len + strlen(name) + 2;
2951#if ENABLE_PLATFORM_MINGW32
2952 /* reserve space for system drive prefix and extension */
2953 sd = need_system_drive(start);
2954 if (sd != NULL)
2955 sdlen = strlen(sd);
2956 qlen += 4 + sdlen;
2957#endif
2673 q = growstackto(qlen); 2958 q = growstackto(qlen);
2674 2959
2675 if (len) { 2960 if (len) {
2961#if ENABLE_PLATFORM_MINGW32
2962 q = mempcpy(q, sd, sdlen);
2963#endif
2676 q = mempcpy(q, start, len); 2964 q = mempcpy(q, start, len);
2965#if ENABLE_PLATFORM_MINGW32
2966 if (q[-1] != '/' && q[-1] != '\\')
2967#endif
2677 *q++ = '/'; 2968 *q++ = '/';
2678 } 2969 }
2679 strcpy(q, name); 2970 strcpy(q, name);
@@ -2750,6 +3041,9 @@ setprompt_if(smallint do_set, int whichprompt)
2750 default: /* 0 */ 3041 default: /* 0 */
2751 prompt = nullstr; 3042 prompt = nullstr;
2752 } 3043 }
3044#if ENABLE_PLATFORM_MINGW32
3045 skip_ansi_emulation(TRUE);
3046#endif
2753#if ENABLE_ASH_EXPAND_PRMT 3047#if ENABLE_ASH_EXPAND_PRMT
2754 pushstackmark(&smark, stackblocksize()); 3048 pushstackmark(&smark, stackblocksize());
2755 putprompt(expandstr(prompt, PSSYNTAX)); 3049 putprompt(expandstr(prompt, PSSYNTAX));
@@ -2764,6 +3058,7 @@ setprompt_if(smallint do_set, int whichprompt)
2764 3058
2765#define CD_PHYSICAL 1 3059#define CD_PHYSICAL 1
2766#define CD_PRINT 2 3060#define CD_PRINT 2
3061#define CD_PRINT_ALL 4
2767 3062
2768static int 3063static int
2769cdopt(void) 3064cdopt(void)
@@ -2772,7 +3067,14 @@ cdopt(void)
2772 int i, j; 3067 int i, j;
2773 3068
2774 j = 'L'; 3069 j = 'L';
3070#if ENABLE_PLATFORM_MINGW32
3071 while ((i = nextopt("LPa")) != '\0') {
3072 if (i == 'a')
3073 flags |= CD_PRINT_ALL;
3074 else
3075#else
2775 while ((i = nextopt("LP")) != '\0') { 3076 while ((i = nextopt("LP")) != '\0') {
3077#endif
2776 if (i != j) { 3078 if (i != j) {
2777 flags ^= CD_PHYSICAL; 3079 flags ^= CD_PHYSICAL;
2778 j = i; 3080 j = i;
@@ -2789,6 +3091,129 @@ cdopt(void)
2789static const char * 3091static const char *
2790updatepwd(const char *dir) 3092updatepwd(const char *dir)
2791{ 3093{
3094#if ENABLE_PLATFORM_MINGW32
3095 /*
3096 * Due to Windows drive notion, getting pwd is a completely
3097 * different thing. Handle it in a separate routine
3098 */
3099
3100 char *new;
3101 char *p;
3102 char *cdcomppath;
3103 const char *lim;
3104 int len;
3105 char buffer[PATH_MAX];
3106 /*
3107 * There are five cases that make some kind of sense
3108 *
3109 * Absolute paths:
3110 * c:/path
3111 * //host/share
3112 *
3113 * Relative to current working directory of other drive:
3114 * c:path
3115 *
3116 * Relative to current root (drive/share):
3117 * /path
3118 *
3119 * Relative to current working directory of current root (drive/share):
3120 * path
3121 */
3122 enum {ABS_DRIVE, ABS_SHARE, REL_OTHER, REL_ROOT, REL_CWD} target;
3123
3124 /* skip multiple leading separators unless dir is a UNC path */
3125 if (is_dir_sep(*dir) && unc_root_len(dir) == 0) {
3126 while (is_dir_sep(dir[1]))
3127 ++dir;
3128 }
3129
3130 len = strlen(dir);
3131 if (len >= 2 && has_dos_drive_prefix(dir))
3132 target = len >= 3 && is_dir_sep(dir[2]) ? ABS_DRIVE : REL_OTHER;
3133 else if (unc_root_len(dir) != 0)
3134 target = ABS_SHARE;
3135 else if (is_dir_sep(*dir))
3136 target = REL_ROOT;
3137 else
3138 target = REL_CWD;
3139
3140 cdcomppath = sstrdup(dir);
3141 STARTSTACKSTR(new);
3142
3143 switch (target) {
3144 case REL_OTHER:
3145 /* c:path */
3146 if (get_drive_cwd(dir, buffer, PATH_MAX) == NULL)
3147 return 0;
3148 new = stack_putstr(buffer, new);
3149 len = 2;
3150 cdcomppath += len;
3151 dir += len;
3152 break;
3153 case REL_CWD:
3154 case REL_ROOT:
3155 /* path or /path */
3156 len = root_len(curdir);
3157 if (len == 0)
3158 return 0;
3159 new = target == REL_CWD ? stack_putstr(curdir, new) :
3160 stnputs(curdir, len, new);
3161 break;
3162 default:
3163 /* //host/share or c:/path */
3164 len = root_len(dir);
3165 if (len == 0)
3166 return 0;
3167 new = stnputs(dir, len, new);
3168 cdcomppath += len;
3169 dir += len;
3170 break;
3171 }
3172
3173 new = makestrspace(strlen(dir) + 2, new);
3174 lim = (char *)stackblock() + len + 1;
3175
3176 if (!is_dir_sep(*dir)) {
3177 if (!is_dir_sep(new[-1]))
3178 USTPUTC('/', new);
3179 if (new > lim && is_dir_sep(*lim))
3180 lim++;
3181 } else {
3182 USTPUTC('/', new);
3183 cdcomppath++;
3184 if (is_dir_sep(dir[1]) && !is_dir_sep(dir[2])) {
3185 USTPUTC('/', new);
3186 cdcomppath++;
3187 lim++;
3188 }
3189 }
3190 p = strtok(cdcomppath, "/\\");
3191 while (p) {
3192 switch (*p) {
3193 case '.':
3194 if (p[1] == '.' && p[2] == '\0') {
3195 while (new > lim) {
3196 STUNPUTC(new);
3197 if (is_dir_sep(new[-1]))
3198 break;
3199 }
3200 break;
3201 }
3202 if (p[1] == '\0')
3203 break;
3204 /* fall through */
3205 default:
3206 new = stack_putstr(p, new);
3207 USTPUTC('/', new);
3208 }
3209 p = strtok(NULL, "/\\");
3210 }
3211 if (new > lim)
3212 STUNPUTC(new);
3213 *new = 0;
3214 fix_path_case((char *)stackblock());
3215 return bs_to_slash((char *)stackblock());
3216#else
2792 char *new; 3217 char *new;
2793 char *p; 3218 char *p;
2794 char *cdcomppath; 3219 char *cdcomppath;
@@ -2842,6 +3267,7 @@ updatepwd(const char *dir)
2842 STUNPUTC(new); 3267 STUNPUTC(new);
2843 *new = 0; 3268 *new = 0;
2844 return stackblock(); 3269 return stackblock();
3270#endif
2845} 3271}
2846 3272
2847/* 3273/*
@@ -2937,7 +3363,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2937 } 3363 }
2938 if (!dest) 3364 if (!dest)
2939 dest = nullstr; 3365 dest = nullstr;
2940 if (*dest == '/') 3366 if (!is_relative_path(dest))
2941 goto step6; 3367 goto step6;
2942 if (*dest == '.') { 3368 if (*dest == '.') {
2943 c = dest[1]; 3369 c = dest[1];
@@ -2960,7 +3386,7 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2960 p = stalloc(len); 3386 p = stalloc(len);
2961 3387
2962 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) { 3388 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2963 if (c && c != ':') 3389 if (c && c != PATH_SEP)
2964 flags |= CD_PRINT; 3390 flags |= CD_PRINT;
2965 docd: 3391 docd:
2966 if (!docd(p, flags)) 3392 if (!docd(p, flags))
@@ -2982,6 +3408,26 @@ cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2982 return 0; 3408 return 0;
2983} 3409}
2984 3410
3411#if ENABLE_PLATFORM_MINGW32
3412static void
3413print_all_cwd(void)
3414{
3415 FILE *mnt;
3416 struct mntent *entry;
3417 char buffer[PATH_MAX];
3418
3419 mnt = setmntent(bb_path_mtab_file, "r");
3420 if (mnt) {
3421 while ((entry=getmntent(mnt)) != NULL) {
3422 entry->mnt_dir[2] = '\0';
3423 if (get_drive_cwd(entry->mnt_dir, buffer, PATH_MAX) != NULL)
3424 out1fmt("%s\n", buffer);
3425 }
3426 endmntent(mnt);
3427 }
3428}
3429#endif
3430
2985static int FAST_FUNC 3431static int FAST_FUNC
2986pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM) 3432pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2987{ 3433{
@@ -2989,6 +3435,12 @@ pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
2989 const char *dir = curdir; 3435 const char *dir = curdir;
2990 3436
2991 flags = cdopt(); 3437 flags = cdopt();
3438#if ENABLE_PLATFORM_MINGW32
3439 if (flags & CD_PRINT_ALL) {
3440 print_all_cwd();
3441 return 0;
3442 }
3443#endif
2992 if (flags) { 3444 if (flags) {
2993 if (physdir == nullstr) 3445 if (physdir == nullstr)
2994 setpwd(dir, 0); 3446 setpwd(dir, 0);
@@ -3617,7 +4069,12 @@ unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
3617struct procstat { 4069struct procstat {
3618 pid_t ps_pid; /* process id */ 4070 pid_t ps_pid; /* process id */
3619 int ps_status; /* last process status from wait() */ 4071 int ps_status; /* last process status from wait() */
4072#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3620 char *ps_cmd; /* text of command being run */ 4073 char *ps_cmd; /* text of command being run */
4074#endif
4075#if ENABLE_PLATFORM_MINGW32
4076 HANDLE ps_proc;
4077#endif
3621}; 4078};
3622 4079
3623struct job { 4080struct job {
@@ -3644,17 +4101,23 @@ struct job {
3644}; 4101};
3645 4102
3646static struct job *makejob(/*union node *,*/ int); 4103static struct job *makejob(/*union node *,*/ int);
4104#if !ENABLE_PLATFORM_MINGW32
3647static int forkshell(struct job *, union node *, int); 4105static int forkshell(struct job *, union node *, int);
4106#endif
3648static int waitforjob(struct job *); 4107static int waitforjob(struct job *);
3649 4108
3650#if !JOBS 4109#if !JOBS && !JOBS_WIN32
3651enum { doing_jobctl = 0 }; 4110enum { doing_jobctl = 0 };
3652#define setjobctl(on) do {} while (0) 4111#define setjobctl(on) do {} while (0)
3653#else 4112#elif JOBS_WIN32
4113static smallint doing_jobctl; //references:8
4114#define setjobctl(on) do { if (rootshell) doing_jobctl = on; } while (0)
4115#else /* JOBS */
3654static smallint doing_jobctl; //references:8 4116static smallint doing_jobctl; //references:8
3655static void setjobctl(int); 4117static void setjobctl(int);
3656#endif 4118#endif
3657 4119
4120#if !ENABLE_PLATFORM_MINGW32
3658/* 4121/*
3659 * Ignore a signal. 4122 * Ignore a signal.
3660 */ 4123 */
@@ -3801,6 +4264,10 @@ setsignal(int signo)
3801 4264
3802 sigaction_set(signo, &act); 4265 sigaction_set(signo, &act);
3803} 4266}
4267#else
4268#define setsignal(s)
4269#define ignoresig(s)
4270#endif
3804 4271
3805/* mode flags for set_curjob */ 4272/* mode flags for set_curjob */
3806#define CUR_DELETE 2 4273#define CUR_DELETE 2
@@ -3934,7 +4401,7 @@ set_curjob(struct job *jp, unsigned mode)
3934 } 4401 }
3935} 4402}
3936 4403
3937#if JOBS || DEBUG 4404#if JOBS || ENABLE_PLATFORM_MINGW32 || DEBUG
3938static int 4405static int
3939jobno(const struct job *jp) 4406jobno(const struct job *jp)
3940{ 4407{
@@ -3952,12 +4419,16 @@ static struct job *
3952getjob(const char *name, int getctl) 4419getjob(const char *name, int getctl)
3953{ 4420{
3954 struct job *jp; 4421 struct job *jp;
4422#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3955 struct job *found; 4423 struct job *found;
4424#endif
3956 const char *err_msg = "%s: no such job"; 4425 const char *err_msg = "%s: no such job";
3957 unsigned num; 4426 unsigned num;
3958 int c; 4427 int c;
3959 const char *p; 4428 const char *p;
4429#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
3960 char *(*match)(const char *, const char *); 4430 char *(*match)(const char *, const char *);
4431#endif
3961 4432
3962 jp = curjob; 4433 jp = curjob;
3963 p = name; 4434 p = name;
@@ -3998,6 +4469,7 @@ getjob(const char *name, int getctl)
3998 } 4469 }
3999 } 4470 }
4000 4471
4472#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4001 match = prefix; 4473 match = prefix;
4002 if (*p == '?') { 4474 if (*p == '?') {
4003 match = strstr; 4475 match = strstr;
@@ -4017,6 +4489,9 @@ getjob(const char *name, int getctl)
4017 if (!found) 4489 if (!found)
4018 goto err; 4490 goto err;
4019 jp = found; 4491 jp = found;
4492#else
4493 goto err;
4494#endif
4020 4495
4021 gotit: 4496 gotit:
4022#if JOBS 4497#if JOBS
@@ -4035,14 +4510,18 @@ getjob(const char *name, int getctl)
4035static void 4510static void
4036freejob(struct job *jp) 4511freejob(struct job *jp)
4037{ 4512{
4513#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4038 struct procstat *ps; 4514 struct procstat *ps;
4039 int i; 4515 int i;
4516#endif
4040 4517
4041 INT_OFF; 4518 INT_OFF;
4519#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4042 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) { 4520 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
4043 if (ps->ps_cmd != nullstr) 4521 if (ps->ps_cmd != nullstr)
4044 free(ps->ps_cmd); 4522 free(ps->ps_cmd);
4045 } 4523 }
4524#endif
4046 if (jp->ps != &jp->ps0) 4525 if (jp->ps != &jp->ps0)
4047 free(jp->ps); 4526 free(jp->ps);
4048 jp->used = 0; 4527 jp->used = 0;
@@ -4135,7 +4614,9 @@ setjobctl(int on)
4135 ttyfd = fd; 4614 ttyfd = fd;
4136 doing_jobctl = on; 4615 doing_jobctl = on;
4137} 4616}
4617#endif
4138 4618
4619#if JOBS || JOBS_WIN32
4139static int FAST_FUNC 4620static int FAST_FUNC
4140killcmd(int argc, char **argv) 4621killcmd(int argc, char **argv)
4141{ 4622{
@@ -4165,8 +4646,10 @@ killcmd(int argc, char **argv)
4165 * sh -c 'true|sleep 1 & sleep 2; kill %1' 4646 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4166 */ 4647 */
4167 n = jp->nprocs; /* can't be 0 (I hope) */ 4648 n = jp->nprocs; /* can't be 0 (I hope) */
4649#if !ENABLE_PLATFORM_MINGW32
4168 if (jp->jobctl) 4650 if (jp->jobctl)
4169 n = 1; 4651 n = 1;
4652#endif
4170 dst = alloca(n * sizeof(int)*4); 4653 dst = alloca(n * sizeof(int)*4);
4171 argv[i] = dst; 4654 argv[i] = dst;
4172 for (j = 0; j < n; j++) { 4655 for (j = 0; j < n; j++) {
@@ -4181,7 +4664,11 @@ killcmd(int argc, char **argv)
4181 * leading space. Needed to not confuse 4664 * leading space. Needed to not confuse
4182 * negative pids with "kill -SIGNAL_NO" syntax 4665 * negative pids with "kill -SIGNAL_NO" syntax
4183 */ 4666 */
4667#if !ENABLE_PLATFORM_MINGW32
4184 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid); 4668 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4669#else
4670 dst += sprintf(dst, " -%u", (int)ps->ps_pid);
4671#endif
4185 } 4672 }
4186 *dst = '\0'; 4673 *dst = '\0';
4187 } 4674 }
@@ -4189,7 +4676,9 @@ killcmd(int argc, char **argv)
4189 } 4676 }
4190 return kill_main(argc, argv); 4677 return kill_main(argc, argv);
4191} 4678}
4679#endif
4192 4680
4681#if JOBS
4193static void 4682static void
4194showpipe(struct job *jp /*, FILE *out*/) 4683showpipe(struct job *jp /*, FILE *out*/)
4195{ 4684{
@@ -4294,6 +4783,77 @@ sprint_status48(char *os, int status, int sigonly)
4294 return s - os; 4783 return s - os;
4295} 4784}
4296 4785
4786#if ENABLE_PLATFORM_MINGW32
4787static BOOL WINAPI ctrl_handler(DWORD dwCtrlType)
4788{
4789 if (dwCtrlType == CTRL_C_EVENT || dwCtrlType == CTRL_BREAK_EVENT) {
4790 if (!suppress_int)
4791 raise_interrupt(); /* does not return */
4792 pending_int = 1;
4793 return TRUE;
4794 }
4795 return FALSE;
4796}
4797
4798/*
4799 * Windows does not know about parent-child relationship
4800 * They don't support waitpid(-1)
4801 */
4802static pid_t
4803waitpid_child(int *status, int wait_flags)
4804{
4805 struct job *jb;
4806 struct procstat *ps;
4807 int pid_nr = 0;
4808 pid_t *pidlist;
4809 HANDLE *proclist;
4810 pid_t pid = -1;
4811 DWORD win_status, idx;
4812 int i;
4813
4814 for (jb = curjob; jb; jb = jb->prev_job) {
4815 if (jb->state != JOBDONE)
4816 pid_nr += jb->nprocs;
4817 }
4818 if (pid_nr == 0)
4819 return -1;
4820
4821 pidlist = ckmalloc(sizeof(*pidlist)*pid_nr);
4822 proclist = ckmalloc(sizeof(*proclist)*pid_nr);
4823
4824 pid_nr = 0;
4825 for (jb = curjob; jb; jb = jb->prev_job) {
4826 if (jb->state == JOBDONE)
4827 continue;
4828 ps = jb->ps;
4829 for (i = 0; i < jb->nprocs; ++i) {
4830 if (ps[i].ps_proc) {
4831 pidlist[pid_nr] = ps[i].ps_pid;
4832 proclist[pid_nr++] = ps[i].ps_proc;
4833 }
4834 }
4835 }
4836
4837 if (pid_nr == 0)
4838 goto done;
4839
4840 idx = WaitForMultipleObjects(pid_nr, proclist, FALSE,
4841 wait_flags&WNOHANG ? 1 : INFINITE);
4842 if (idx < pid_nr) {
4843 GetExitCodeProcess(proclist[idx], &win_status);
4844 *status = (int)win_status << 8;
4845 if (win_status == 128 + SIGTERM || win_status == 128 + SIGKILL)
4846 *status += win_status - 128;
4847 pid = pidlist[idx];
4848 }
4849 done:
4850 free(pidlist);
4851 free(proclist);
4852 return pid;
4853}
4854#define waitpid(p, s, f) waitpid_child(s, f)
4855#endif
4856
4297#define DOWAIT_NONBLOCK 0 4857#define DOWAIT_NONBLOCK 0
4298#define DOWAIT_BLOCK 1 4858#define DOWAIT_BLOCK 1
4299#define DOWAIT_BLOCK_OR_SIG 2 4859#define DOWAIT_BLOCK_OR_SIG 2
@@ -4304,6 +4864,7 @@ sprint_status48(char *os, int status, int sigonly)
4304static int 4864static int
4305waitproc(int block, int *status) 4865waitproc(int block, int *status)
4306{ 4866{
4867#if !ENABLE_PLATFORM_MINGW32
4307 sigset_t oldmask; 4868 sigset_t oldmask;
4308 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG; 4869 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4309 int err; 4870 int err;
@@ -4334,6 +4895,11 @@ waitproc(int block, int *status)
4334 } while (got_sigchld); 4895 } while (got_sigchld);
4335 4896
4336 return err; 4897 return err;
4898#else
4899 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4900 *status = 0;
4901 return waitpid(-1, status, flags);
4902#endif
4337} 4903}
4338 4904
4339static int 4905static int
@@ -4390,6 +4956,11 @@ waitone(int block, struct job *job)
4390 jobno(jp), pid, ps->ps_status, status)); 4956 jobno(jp), pid, ps->ps_status, status));
4391 ps->ps_status = status; 4957 ps->ps_status = status;
4392 thisjob = jp; 4958 thisjob = jp;
4959#if ENABLE_PLATFORM_MINGW32
4960 ps->ps_pid = -1;
4961 CloseHandle(ps->ps_proc);
4962 ps->ps_proc = NULL;
4963#endif
4393 } 4964 }
4394 if (ps->ps_status == -1) 4965 if (ps->ps_status == -1)
4395 jobstate = JOBRUNNING; 4966 jobstate = JOBRUNNING;
@@ -4452,6 +5023,7 @@ waitone(int block, struct job *job)
4452static int 5023static int
4453dowait(int block, struct job *jp) 5024dowait(int block, struct job *jp)
4454{ 5025{
5026#if !ENABLE_PLATFORM_MINGW32
4455 smallint gotchld = *(volatile smallint *)&got_sigchld; 5027 smallint gotchld = *(volatile smallint *)&got_sigchld;
4456 int rpid; 5028 int rpid;
4457 int pid; 5029 int pid;
@@ -4473,9 +5045,17 @@ dowait(int block, struct job *jp)
4473 } while (pid >= 0); 5045 } while (pid >= 0);
4474 5046
4475 return rpid; 5047 return rpid;
5048#else
5049 int pid = 1;
5050
5051 while (jp ? jp->state == JOBRUNNING : pid > 0)
5052 pid = waitone(block, jp);
5053
5054 return pid;
5055#endif
4476} 5056}
4477 5057
4478#if JOBS 5058#if JOBS || JOBS_WIN32
4479static void 5059static void
4480showjob(struct job *jp, int mode) 5060showjob(struct job *jp, int mode)
4481{ 5061{
@@ -4490,7 +5070,7 @@ showjob(struct job *jp, int mode)
4490 5070
4491 if (mode & SHOW_ONLY_PGID) { /* jobs -p */ 5071 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
4492 /* just output process (group) id of pipeline */ 5072 /* just output process (group) id of pipeline */
4493 fprintf(out, "%d\n", ps->ps_pid); 5073 fprintf(out, "%"PID_FMT"d\n", ps->ps_pid);
4494 return; 5074 return;
4495 } 5075 }
4496 5076
@@ -4503,7 +5083,7 @@ showjob(struct job *jp, int mode)
4503 s[col - 3] = '-'; 5083 s[col - 3] = '-';
4504 5084
4505 if (mode & SHOW_PIDS) 5085 if (mode & SHOW_PIDS)
4506 col += fmtstr(s + col, 16, "%d ", ps->ps_pid); 5086 col += fmtstr(s + col, 16, "%"PID_FMT"d ", ps->ps_pid);
4507 5087
4508 psend = ps + jp->nprocs; 5088 psend = ps + jp->nprocs;
4509 5089
@@ -4512,8 +5092,10 @@ showjob(struct job *jp, int mode)
4512 col += sizeof("Running") - 1; 5092 col += sizeof("Running") - 1;
4513 } else { 5093 } else {
4514 int status = psend[-1].ps_status; 5094 int status = psend[-1].ps_status;
5095#if !ENABLE_PLATFORM_MINGW32
4515 if (jp->state == JOBSTOPPED) 5096 if (jp->state == JOBSTOPPED)
4516 status = jp->stopstatus; 5097 status = jp->stopstatus;
5098#endif
4517 col += sprint_status48(s + col, status, 0); 5099 col += sprint_status48(s + col, status, 0);
4518 } 5100 }
4519 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */ 5101 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
@@ -4532,14 +5114,18 @@ showjob(struct job *jp, int mode)
4532 s[0] = '\0'; 5114 s[0] = '\0';
4533 col = 33; 5115 col = 33;
4534 if (mode & SHOW_PIDS) 5116 if (mode & SHOW_PIDS)
4535 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1; 5117 col = fmtstr(s, 48, "\n%*c%"PID_FMT"d ", indent_col, ' ', ps->ps_pid) - 1;
4536 start: 5118 start:
5119#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
4537 fprintf(out, "%s%*c%s%s", 5120 fprintf(out, "%s%*c%s%s",
4538 s, 5121 s,
4539 33 - col >= 0 ? 33 - col : 0, ' ', 5122 33 - col >= 0 ? 33 - col : 0, ' ',
4540 ps == jp->ps ? "" : "| ", 5123 ps == jp->ps ? "" : "| ",
4541 ps->ps_cmd 5124 ps->ps_cmd
4542 ); 5125 );
5126#else
5127 fprintf(out, "%s", s);
5128#endif
4543 } while (++ps != psend); 5129 } while (++ps != psend);
4544 newline_and_flush(out); 5130 newline_and_flush(out);
4545 5131
@@ -4796,7 +5382,7 @@ makejob(/*union node *node,*/ int nprocs)
4796 break; 5382 break;
4797 if (jp->state != JOBDONE || !jp->waited) 5383 if (jp->state != JOBDONE || !jp->waited)
4798 continue; 5384 continue;
4799#if JOBS 5385#if JOBS || JOBS_WIN32
4800 if (doing_jobctl) 5386 if (doing_jobctl)
4801 continue; 5387 continue;
4802#endif 5388#endif
@@ -4822,7 +5408,7 @@ makejob(/*union node *node,*/ int nprocs)
4822 return jp; 5408 return jp;
4823} 5409}
4824 5410
4825#if JOBS 5411#if JOBS || JOBS_WIN32
4826/* 5412/*
4827 * Return a string identifying a command (to be printed by the 5413 * Return a string identifying a command (to be printed by the
4828 * jobs command). 5414 * jobs command).
@@ -5141,6 +5727,7 @@ commandtext(union node *n)
5141 * 5727 *
5142 * Called with interrupts off. 5728 * Called with interrupts off.
5143 */ 5729 */
5730#if !ENABLE_PLATFORM_MINGW32
5144/* 5731/*
5145 * Clear traps on a fork. 5732 * Clear traps on a fork.
5146 */ 5733 */
@@ -5289,14 +5876,22 @@ forkchild(struct job *jp, union node *n, int mode)
5289 for (jp = curjob; jp; jp = jp->prev_job) 5876 for (jp = curjob; jp; jp = jp->prev_job)
5290 freejob(jp); 5877 freejob(jp);
5291} 5878}
5879#endif
5292 5880
5293/* Called after fork(), in parent */ 5881/* Called after fork(), in parent */
5294#if !JOBS 5882#if !JOBS && !JOBS_WIN32
5295#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid) 5883#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5296#endif 5884#endif
5297static void 5885static void
5886#if !ENABLE_PLATFORM_MINGW32
5298forkparent(struct job *jp, union node *n, int mode, pid_t pid) 5887forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5888#else
5889forkparent(struct job *jp, union node *n, int mode, HANDLE proc)
5890#endif
5299{ 5891{
5892#if ENABLE_PLATFORM_MINGW32
5893 pid_t pid = GetProcessId(proc);
5894#endif
5300 TRACE(("In parent shell: child = %d\n", pid)); 5895 TRACE(("In parent shell: child = %d\n", pid));
5301 if (!jp) /* jp is NULL when called by openhere() for heredoc support */ 5896 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
5302 return; 5897 return;
@@ -5315,19 +5910,29 @@ forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5315 if (mode == FORK_BG) { 5910 if (mode == FORK_BG) {
5316 backgndpid = pid; /* set $! */ 5911 backgndpid = pid; /* set $! */
5317 set_curjob(jp, CUR_RUNNING); 5912 set_curjob(jp, CUR_RUNNING);
5913#if ENABLE_PLATFORM_MINGW32
5914 if (iflag && jp && jp->nprocs == 0)
5915 fprintf(stderr, "[%d] %"PID_FMT"d\n", jobno(jp), pid);
5916#endif
5318 } 5917 }
5319 if (jp) { 5918 if (jp) {
5320 struct procstat *ps = &jp->ps[jp->nprocs++]; 5919 struct procstat *ps = &jp->ps[jp->nprocs++];
5321 ps->ps_pid = pid; 5920 ps->ps_pid = pid;
5322 ps->ps_status = -1; 5921 ps->ps_status = -1;
5922#if ENABLE_PLATFORM_POSIX || JOBS_WIN32
5323 ps->ps_cmd = nullstr; 5923 ps->ps_cmd = nullstr;
5324#if JOBS 5924#endif
5925#if ENABLE_PLATFORM_MINGW32
5926 ps->ps_proc = proc;
5927#endif
5928#if JOBS || JOBS_WIN32
5325 if (doing_jobctl && n) 5929 if (doing_jobctl && n)
5326 ps->ps_cmd = commandtext(n); 5930 ps->ps_cmd = commandtext(n);
5327#endif 5931#endif
5328 } 5932 }
5329} 5933}
5330 5934
5935#if !ENABLE_PLATFORM_MINGW32
5331/* jp and n are NULL when called by openhere() for heredoc support */ 5936/* jp and n are NULL when called by openhere() for heredoc support */
5332static int 5937static int
5333forkshell(struct job *jp, union node *n, int mode) 5938forkshell(struct job *jp, union node *n, int mode)
@@ -5350,6 +5955,7 @@ forkshell(struct job *jp, union node *n, int mode)
5350 } 5955 }
5351 return pid; 5956 return pid;
5352} 5957}
5958#endif
5353 5959
5354/* 5960/*
5355 * Wait for job to finish. 5961 * Wait for job to finish.
@@ -5438,6 +6044,7 @@ waitforjob(struct job *jp)
5438/* 6044/*
5439 * return 1 if there are stopped jobs, otherwise 0 6045 * return 1 if there are stopped jobs, otherwise 0
5440 */ 6046 */
6047#if !ENABLE_PLATFORM_MINGW32
5441static int 6048static int
5442stoppedjobs(void) 6049stoppedjobs(void)
5443{ 6050{
@@ -5456,6 +6063,17 @@ stoppedjobs(void)
5456 out: 6063 out:
5457 return retval; 6064 return retval;
5458} 6065}
6066#else
6067static int
6068stoppedjobs(void)
6069{
6070 if (iflag && curjob) {
6071 out2str("You have background jobs.\n");
6072 return 1;
6073 }
6074 return 0;
6075}
6076#endif
5459 6077
5460 6078
5461/* 6079/*
@@ -5480,6 +6098,7 @@ openhere(union node *redir)
5480 char *p; 6098 char *p;
5481 int pip[2]; 6099 int pip[2];
5482 size_t len = 0; 6100 size_t len = 0;
6101 IF_PLATFORM_MINGW32(struct forkshell fs);
5483 6102
5484 if (pipe(pip) < 0) 6103 if (pipe(pip) < 0)
5485 ash_msg_and_raise_perror("can't create pipe"); 6104 ash_msg_and_raise_perror("can't create pipe");
@@ -5496,6 +6115,14 @@ openhere(union node *redir)
5496 goto out; 6115 goto out;
5497 } 6116 }
5498 6117
6118#if ENABLE_PLATFORM_MINGW32
6119 memset(&fs, 0, sizeof(fs));
6120 fs.fpid = FS_OPENHERE;
6121 fs.fd[0] = pip[0];
6122 fs.fd[1] = pip[1];
6123 fs.path = p;
6124 spawn_forkshell(&fs, NULL, NULL, FORK_NOJOB);
6125#else
5499 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { 6126 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
5500 /* child */ 6127 /* child */
5501 close(pip[0]); 6128 close(pip[0]);
@@ -5507,6 +6134,7 @@ openhere(union node *redir)
5507 xwrite(pip[1], p, len); 6134 xwrite(pip[1], p, len);
5508 _exit(EXIT_SUCCESS); 6135 _exit(EXIT_SUCCESS);
5509 } 6136 }
6137#endif
5510 out: 6138 out:
5511 close(pip[1]); 6139 close(pip[1]);
5512 return pip[0]; 6140 return pip[0];
@@ -5584,6 +6212,9 @@ openredirect(union node *redir)
5584 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666); 6212 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5585 if (f < 0) 6213 if (f < 0)
5586 goto ecreate; 6214 goto ecreate;
6215#if ENABLE_PLATFORM_MINGW32
6216 lseek(f, 0, SEEK_END);
6217#endif
5587 break; 6218 break;
5588 } 6219 }
5589 6220
@@ -6592,6 +7223,7 @@ evalbackcmd(union node *n, struct backcmd *result
6592 const int ip = 0; 7223 const int ip = 0;
6593 const int ic = 1; 7224 const int ic = 1;
6594#endif 7225#endif
7226 IF_PLATFORM_MINGW32(struct forkshell fs);
6595 7227
6596 result->fd = -1; 7228 result->fd = -1;
6597 result->buf = NULL; 7229 result->buf = NULL;
@@ -6605,6 +7237,15 @@ evalbackcmd(union node *n, struct backcmd *result
6605 ash_msg_and_raise_perror("can't create pipe"); 7237 ash_msg_and_raise_perror("can't create pipe");
6606 /* process substitution uses NULL job/node, like openhere() */ 7238 /* process substitution uses NULL job/node, like openhere() */
6607 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL; 7239 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
7240#if ENABLE_PLATFORM_MINGW32
7241 memset(&fs, 0, sizeof(fs));
7242 fs.fpid = FS_EVALBACKCMD;
7243 fs.n = n;
7244 fs.fd[0] = pip[0];
7245 fs.fd[1] = pip[1];
7246 fs.fd[2] = ctl;
7247 spawn_forkshell(&fs, jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB);
7248#else
6608 if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) { 7249 if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) {
6609 /* child */ 7250 /* child */
6610 FORCE_INT_ON; 7251 FORCE_INT_ON;
@@ -6628,6 +7269,7 @@ evalbackcmd(union node *n, struct backcmd *result
6628 evaltreenr(n, EV_EXIT); 7269 evaltreenr(n, EV_EXIT);
6629 /* NOTREACHED */ 7270 /* NOTREACHED */
6630 } 7271 }
7272#endif
6631 /* parent */ 7273 /* parent */
6632#if BASH_PROCESS_SUBST 7274#if BASH_PROCESS_SUBST
6633 if (ctl != CTLBACKQ) { 7275 if (ctl != CTLBACKQ) {
@@ -6706,7 +7348,8 @@ expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
6706 7348
6707 /* Eat all trailing newlines */ 7349 /* Eat all trailing newlines */
6708 dest = expdest; 7350 dest = expdest;
6709 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';) 7351 for (; dest > ((char *)stackblock() + startloc) && (dest[-1] == '\n'
7352 IF_PLATFORM_MINGW32(|| dest[-1] == '\r'));)
6710 STUNPUTC(dest); 7353 STUNPUTC(dest);
6711 expdest = dest; 7354 expdest = dest;
6712 7355
@@ -7853,6 +8496,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7853 8496
7854 metaflag = 0; 8497 metaflag = 0;
7855 start = name; 8498 start = name;
8499#if ENABLE_PLATFORM_MINGW32
8500 if (expdir_len == 0 && has_dos_drive_prefix(start) && start[2] != '/')
8501 start += 2;
8502#endif
7856 for (p = name; esc = 0, *p; p += esc + 1) { 8503 for (p = name; esc = 0, *p; p += esc + 1) {
7857 if (*p == '*' || *p == '?') 8504 if (*p == '*' || *p == '?')
7858 metaflag = 1; 8505 metaflag = 1;
@@ -7927,6 +8574,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7927 while (!pending_int && (dp = readdir(dirp)) != NULL) { 8574 while (!pending_int && (dp = readdir(dirp)) != NULL) {
7928 if (dp->d_name[0] == '.' && !matchdot) 8575 if (dp->d_name[0] == '.' && !matchdot)
7929 continue; 8576 continue;
8577#if ENABLE_ASH_NOCASEGLOB
8578# undef pmatch
8579# define pmatch(a, b) !fnmatch((a), (b), nocaseglob ? FNM_CASEFOLD : 0)
8580#endif
7930 if (pmatch(start, dp->d_name)) { 8581 if (pmatch(start, dp->d_name)) {
7931 if (atend) { 8582 if (atend) {
7932 strcpy(enddir, dp->d_name); 8583 strcpy(enddir, dp->d_name);
@@ -7956,6 +8607,10 @@ expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
7956 endname[-esc - 1] = esc ? '\\' : '/'; 8607 endname[-esc - 1] = esc ? '\\' : '/';
7957#undef expdir 8608#undef expdir
7958#undef expdir_max 8609#undef expdir_max
8610#if ENABLE_ASH_NOCASEGLOB
8611# undef pmatch
8612# define pmatch(a, b) !fnmatch((a), (b), 0)
8613#endif
7959} 8614}
7960 8615
7961static struct strlist * 8616static struct strlist *
@@ -8232,7 +8887,17 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8232{ 8887{
8233#if ENABLE_FEATURE_SH_STANDALONE 8888#if ENABLE_FEATURE_SH_STANDALONE
8234 if (applet_no >= 0) { 8889 if (applet_no >= 0) {
8890# if ENABLE_PLATFORM_MINGW32
8891 /* Treat all applets as NOEXEC, including the shell itself if
8892 * this is a FS_SHELLEXEC shell. */
8893 struct forkshell *fs = (struct forkshell *)sticky_mem_start;
8894 if (applet_main[applet_no] != ash_main ||
8895 (fs && fs->fpid == FS_SHELLEXEC)) {
8896 /* mingw-w64's getopt() uses __argv[0] as the program name */
8897 __argv[0] = (char *)cmd;
8898# else
8235 if (APPLET_IS_NOEXEC(applet_no)) { 8899 if (APPLET_IS_NOEXEC(applet_no)) {
8900# endif
8236 clearenv(); 8901 clearenv();
8237 while (*envp) 8902 while (*envp)
8238 putenv(*envp++); 8903 putenv(*envp++);
@@ -8246,6 +8911,12 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8246 } 8911 }
8247#endif 8912#endif
8248 8913
8914#if ENABLE_PLATFORM_MINGW32
8915 /* cmd was allocated on the stack with room for an extension */
8916 add_win32_extension((char *)cmd);
8917 execve(cmd, argv, envp);
8918 /* skip POSIX-mandated retry on ENOEXEC */
8919#else /* !ENABLE_PLATFORM_MINGW32 */
8249 repeat: 8920 repeat:
8250#ifdef SYSV 8921#ifdef SYSV
8251 do { 8922 do {
@@ -8281,6 +8952,7 @@ tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, c
8281 argv[0] = (char*) "ash"; 8952 argv[0] = (char*) "ash";
8282 goto repeat; 8953 goto repeat;
8283 } 8954 }
8955#endif /* !ENABLE_PLATFORM_MINGW32 */
8284} 8956}
8285 8957
8286/* 8958/*
@@ -8298,11 +8970,21 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8298 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */ 8970 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
8299 8971
8300 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL); 8972 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
8973#if !ENABLE_PLATFORM_MINGW32
8301 if (strchr(prog, '/') != NULL 8974 if (strchr(prog, '/') != NULL
8975#else
8976 if (has_path(prog)
8977#endif
8302#if ENABLE_FEATURE_SH_STANDALONE 8978#if ENABLE_FEATURE_SH_STANDALONE
8303 || (applet_no = find_applet_by_name(prog)) >= 0 8979 || (applet_no = find_applet_by_name(prog)) >= 0
8304#endif 8980#endif
8305 ) { 8981 ) {
8982#if ENABLE_PLATFORM_MINGW32
8983# if ENABLE_FEATURE_SH_STANDALONE
8984 char *oldprog = prog;
8985# endif
8986 prog = stack_add_system_drive(prog);
8987#endif
8306 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp); 8988 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
8307 if (applet_no >= 0) { 8989 if (applet_no >= 0) {
8308 /* We tried execing ourself, but it didn't work. 8990 /* We tried execing ourself, but it didn't work.
@@ -8312,6 +8994,18 @@ static void shellexec(char *prog, char **argv, const char *path, int idx)
8312 goto try_PATH; 8994 goto try_PATH;
8313 } 8995 }
8314 e = errno; 8996 e = errno;
8997#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
8998 if (unix_path(oldprog)) {
8999 const char *name = bb_basename(oldprog);
9000 if ((applet_no = find_applet_by_name(name)) >= 0) {
9001 tryexec(applet_no, name, argv, envp);
9002 e = errno;
9003 }
9004 else {
9005 e = ENOENT;
9006 }
9007 }
9008#endif
8315 } else { 9009 } else {
8316 try_PATH: 9010 try_PATH:
8317 e = ENOENT; 9011 e = ENOENT;
@@ -8357,6 +9051,9 @@ printentry(struct tblentry *cmdp)
8357 padvance(&path, cmdp->cmdname); 9051 padvance(&path, cmdp->cmdname);
8358 } while (--idx >= 0); 9052 } while (--idx >= 0);
8359 name = stackblock(); 9053 name = stackblock();
9054#if ENABLE_PLATFORM_MINGW32
9055 add_win32_extension(name);
9056#endif
8360 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr)); 9057 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8361} 9058}
8362 9059
@@ -8557,7 +9254,7 @@ changepath(const char *newval)
8557 bltin = idx; 9254 bltin = idx;
8558 break; 9255 break;
8559 } 9256 }
8560 new = strchr(new, ':'); 9257 new = strchr(new, PATH_SEP);
8561 if (!new) 9258 if (!new)
8562 break; 9259 break;
8563 idx++; 9260 idx++;
@@ -8735,14 +9432,29 @@ describe_command(char *command, const char *path, int describe_command_verbose)
8735 case CMDNORMAL: { 9432 case CMDNORMAL: {
8736 int j = entry.u.index; 9433 int j = entry.u.index;
8737 char *p; 9434 char *p;
9435#if ENABLE_PLATFORM_MINGW32 && ENABLE_FEATURE_SH_STANDALONE
9436 if (j == INT_MIN) {
9437 p = (char *)bb_basename(command);
9438 goto describe;
9439 }
9440#endif
8738 if (j < 0) { 9441 if (j < 0) {
9442#if ENABLE_PLATFORM_MINGW32
9443 p = stack_add_system_drive(command);
9444#else
8739 p = command; 9445 p = command;
9446#endif
8740 } else { 9447 } else {
8741 do { 9448 do {
8742 padvance(&path, command); 9449 padvance(&path, command);
8743 } while (--j >= 0); 9450 } while (--j >= 0);
8744 p = stackblock(); 9451 p = stackblock();
8745 } 9452 }
9453#if ENABLE_PLATFORM_MINGW32
9454 add_win32_extension(p);
9455 bs_to_slash(p);
9456 IF_FEATURE_SH_STANDALONE(describe:)
9457#endif
8746 if (describe_command_verbose) { 9458 if (describe_command_verbose) {
8747 out1fmt(" is %s", p); 9459 out1fmt(" is %s", p);
8748 } else { 9460 } else {
@@ -8898,6 +9610,13 @@ commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
8898/*static int funcstringsize; // size of strings in node */ 9610/*static int funcstringsize; // size of strings in node */
8899static void *funcblock; /* block to allocate function from */ 9611static void *funcblock; /* block to allocate function from */
8900static char *funcstring_end; /* end of block to allocate strings from */ 9612static char *funcstring_end; /* end of block to allocate strings from */
9613#if ENABLE_PLATFORM_MINGW32
9614static int fs_size;
9615# if FORKSHELL_DEBUG
9616static void *fs_start;
9617static const char **annot;
9618# endif
9619#endif
8901 9620
8902static const uint8_t nodesize[N_NUMBER] ALIGN1 = { 9621static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
8903 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)), 9622 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
@@ -9030,14 +9749,60 @@ calcsize(int funcblocksize, union node *n)
9030} 9749}
9031 9750
9032static char * 9751static char *
9033nodeckstrdup(char *s) 9752nodeckstrdup(const char *s)
9034{ 9753{
9754#if ENABLE_PLATFORM_MINGW32
9755 if(!s)
9756 return NULL;
9757#endif
9035 funcstring_end -= SHELL_ALIGN(strlen(s) + 1); 9758 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
9036 return strcpy(funcstring_end, s); 9759 return strcpy(funcstring_end, s);
9037} 9760}
9038 9761
9039static union node *copynode(union node *); 9762static union node *copynode(union node *);
9040 9763
9764#if ENABLE_PLATFORM_MINGW32
9765# if FORKSHELL_DEBUG
9766# define FREE 1
9767# define NO_FREE 2
9768# define ANNOT(dst,note) { \
9769 if (annot) \
9770 annot[(char *)&dst - (char *)fs_start] = note; \
9771 }
9772# else
9773# define FREE 1
9774# define NO_FREE 1
9775# define ANNOT(dst,note)
9776# endif
9777
9778/* The relocation map is offset from the start of the forkshell data
9779 * block by 'fs_size' bytes. The flag relating to a particular destination
9780 * pointer is thus at (dst+fs_size). */
9781# define MARK_PTR(dst,flag) {*((char *)&dst + fs_size) = flag;}
9782
9783# define SAVE_PTR(dst,note,flag) { \
9784 if (fs_size) { \
9785 MARK_PTR(dst,flag); ANNOT(dst,note); \
9786 } \
9787}
9788# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2) { \
9789 if (fs_size) { \
9790 MARK_PTR(dst1,flag1); MARK_PTR(dst2,flag2); \
9791 ANNOT(dst1,note1); ANNOT(dst2,note2); \
9792 } \
9793}
9794# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3) { \
9795 if (fs_size) { \
9796 MARK_PTR(dst1,flag1); MARK_PTR(dst2,flag2); MARK_PTR(dst3,flag3); \
9797 ANNOT(dst1,note1); ANNOT(dst2,note2); ANNOT(dst3,note3); \
9798 } \
9799}
9800#else
9801# define SAVE_PTR(dst,note,flag)
9802# define SAVE_PTR2(dst1,note1,flag1,dst2,note2,flag2)
9803# define SAVE_PTR3(dst1,note1,flag1,dst2,note2,flag2,dst3,note3,flag3)
9804#endif
9805
9041static struct nodelist * 9806static struct nodelist *
9042copynodelist(struct nodelist *lp) 9807copynodelist(struct nodelist *lp)
9043{ 9808{
@@ -9049,6 +9814,8 @@ copynodelist(struct nodelist *lp)
9049 *lpp = funcblock; 9814 *lpp = funcblock;
9050 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist)); 9815 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9051 (*lpp)->n = copynode(lp->n); 9816 (*lpp)->n = copynode(lp->n);
9817 SAVE_PTR2((*lpp)->n, "(*lpp)->next", NO_FREE,
9818 (*lpp)->next, "(*lpp)->next", NO_FREE);
9052 lp = lp->next; 9819 lp = lp->next;
9053 lpp = &(*lpp)->next; 9820 lpp = &(*lpp)->next;
9054 } 9821 }
@@ -9072,10 +9839,14 @@ copynode(union node *n)
9072 new->ncmd.args = copynode(n->ncmd.args); 9839 new->ncmd.args = copynode(n->ncmd.args);
9073 new->ncmd.assign = copynode(n->ncmd.assign); 9840 new->ncmd.assign = copynode(n->ncmd.assign);
9074 new->ncmd.linno = n->ncmd.linno; 9841 new->ncmd.linno = n->ncmd.linno;
9842 SAVE_PTR3(new->ncmd.redirect, "ncmd.redirect", NO_FREE,
9843 new->ncmd.args, "ncmd.args", NO_FREE,
9844 new->ncmd.assign, "ncmd.assign", NO_FREE);
9075 break; 9845 break;
9076 case NPIPE: 9846 case NPIPE:
9077 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist); 9847 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
9078 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd; 9848 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
9849 SAVE_PTR(new->npipe.cmdlist, "npipe.cmdlist", NO_FREE);
9079 break; 9850 break;
9080 case NREDIR: 9851 case NREDIR:
9081 case NBACKGND: 9852 case NBACKGND:
@@ -9083,6 +9854,8 @@ copynode(union node *n)
9083 new->nredir.redirect = copynode(n->nredir.redirect); 9854 new->nredir.redirect = copynode(n->nredir.redirect);
9084 new->nredir.n = copynode(n->nredir.n); 9855 new->nredir.n = copynode(n->nredir.n);
9085 new->nredir.linno = n->nredir.linno; 9856 new->nredir.linno = n->nredir.linno;
9857 SAVE_PTR2(new->nredir.redirect, "nredir.redirect", NO_FREE,
9858 new->nredir.n, "nredir.n", NO_FREE);
9086 break; 9859 break;
9087 case NAND: 9860 case NAND:
9088 case NOR: 9861 case NOR:
@@ -9091,37 +9864,58 @@ copynode(union node *n)
9091 case NUNTIL: 9864 case NUNTIL:
9092 new->nbinary.ch2 = copynode(n->nbinary.ch2); 9865 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9093 new->nbinary.ch1 = copynode(n->nbinary.ch1); 9866 new->nbinary.ch1 = copynode(n->nbinary.ch1);
9867 SAVE_PTR2(new->nbinary.ch1, "nbinary.ch1", NO_FREE,
9868 new->nbinary.ch2, "nbinary.ch2", NO_FREE);
9094 break; 9869 break;
9095 case NIF: 9870 case NIF:
9096 new->nif.elsepart = copynode(n->nif.elsepart); 9871 new->nif.elsepart = copynode(n->nif.elsepart);
9097 new->nif.ifpart = copynode(n->nif.ifpart); 9872 new->nif.ifpart = copynode(n->nif.ifpart);
9098 new->nif.test = copynode(n->nif.test); 9873 new->nif.test = copynode(n->nif.test);
9874 SAVE_PTR3(new->nif.elsepart, "nif.elsepart", NO_FREE,
9875 new->nif.ifpart, "nif.ifpart", NO_FREE,
9876 new->nif.test, "nif.test", NO_FREE);
9099 break; 9877 break;
9100 case NFOR: 9878 case NFOR:
9101 new->nfor.var = nodeckstrdup(n->nfor.var); 9879 new->nfor.var = nodeckstrdup(n->nfor.var);
9102 new->nfor.body = copynode(n->nfor.body); 9880 new->nfor.body = copynode(n->nfor.body);
9103 new->nfor.args = copynode(n->nfor.args); 9881 new->nfor.args = copynode(n->nfor.args);
9104 new->nfor.linno = n->nfor.linno; 9882 new->nfor.linno = n->nfor.linno;
9883 SAVE_PTR3(new->nfor.var,
9884 xasprintf("nfor.var '%s'", n->nfor.var ?: "NULL"), FREE,
9885 new->nfor.body, "nfor.body", NO_FREE,
9886 new->nfor.args, "nfor.args", NO_FREE);
9105 break; 9887 break;
9106 case NCASE: 9888 case NCASE:
9107 new->ncase.cases = copynode(n->ncase.cases); 9889 new->ncase.cases = copynode(n->ncase.cases);
9108 new->ncase.expr = copynode(n->ncase.expr); 9890 new->ncase.expr = copynode(n->ncase.expr);
9109 new->ncase.linno = n->ncase.linno; 9891 new->ncase.linno = n->ncase.linno;
9892 SAVE_PTR2(new->ncase.cases, "ncase.cases", NO_FREE,
9893 new->ncase.expr, "ncase.expr", NO_FREE);
9110 break; 9894 break;
9111 case NCLIST: 9895 case NCLIST:
9112 new->nclist.body = copynode(n->nclist.body); 9896 new->nclist.body = copynode(n->nclist.body);
9113 new->nclist.pattern = copynode(n->nclist.pattern); 9897 new->nclist.pattern = copynode(n->nclist.pattern);
9114 new->nclist.next = copynode(n->nclist.next); 9898 new->nclist.next = copynode(n->nclist.next);
9899 SAVE_PTR3(new->nclist.body, "nclist.body", NO_FREE,
9900 new->nclist.pattern, "nclist.pattern", NO_FREE,
9901 new->nclist.next, "nclist.next", NO_FREE);
9115 break; 9902 break;
9116 case NDEFUN: 9903 case NDEFUN:
9117 new->ndefun.body = copynode(n->ndefun.body); 9904 new->ndefun.body = copynode(n->ndefun.body);
9118 new->ndefun.text = nodeckstrdup(n->ndefun.text); 9905 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9119 new->ndefun.linno = n->ndefun.linno; 9906 new->ndefun.linno = n->ndefun.linno;
9907 SAVE_PTR2(new->ndefun.body, "ndefun.body", NO_FREE,
9908 new->ndefun.text,
9909 xasprintf("ndefun.text '%s'", n->ndefun.text ?: "NULL"), FREE);
9120 break; 9910 break;
9121 case NARG: 9911 case NARG:
9122 new->narg.backquote = copynodelist(n->narg.backquote); 9912 new->narg.backquote = copynodelist(n->narg.backquote);
9123 new->narg.text = nodeckstrdup(n->narg.text); 9913 new->narg.text = nodeckstrdup(n->narg.text);
9124 new->narg.next = copynode(n->narg.next); 9914 new->narg.next = copynode(n->narg.next);
9915 SAVE_PTR3(new->narg.backquote, "narg.backquote", NO_FREE,
9916 new->narg.text,
9917 xasprintf("narg.text '%s'", n->narg.text ?: "NULL"), FREE,
9918 new->narg.next, "narg.next", NO_FREE);
9125 break; 9919 break;
9126 case NTO: 9920 case NTO:
9127#if BASH_REDIR_OUTPUT 9921#if BASH_REDIR_OUTPUT
@@ -9134,6 +9928,8 @@ copynode(union node *n)
9134 new->nfile.fname = copynode(n->nfile.fname); 9928 new->nfile.fname = copynode(n->nfile.fname);
9135 new->nfile.fd = n->nfile.fd; 9929 new->nfile.fd = n->nfile.fd;
9136 new->nfile.next = copynode(n->nfile.next); 9930 new->nfile.next = copynode(n->nfile.next);
9931 SAVE_PTR2(new->nfile.fname, "nfile.fname", NO_FREE,
9932 new->nfile.next, "nfile.next", NO_FREE);
9137 break; 9933 break;
9138 case NTOFD: 9934 case NTOFD:
9139 case NFROMFD: 9935 case NFROMFD:
@@ -9141,15 +9937,20 @@ copynode(union node *n)
9141 new->ndup.dupfd = n->ndup.dupfd; 9937 new->ndup.dupfd = n->ndup.dupfd;
9142 new->ndup.fd = n->ndup.fd; 9938 new->ndup.fd = n->ndup.fd;
9143 new->ndup.next = copynode(n->ndup.next); 9939 new->ndup.next = copynode(n->ndup.next);
9940 SAVE_PTR2(new->ndup.vname, "ndup.vname", NO_FREE,
9941 new->ndup.next, "ndup.next", NO_FREE);
9144 break; 9942 break;
9145 case NHERE: 9943 case NHERE:
9146 case NXHERE: 9944 case NXHERE:
9147 new->nhere.doc = copynode(n->nhere.doc); 9945 new->nhere.doc = copynode(n->nhere.doc);
9148 new->nhere.fd = n->nhere.fd; 9946 new->nhere.fd = n->nhere.fd;
9149 new->nhere.next = copynode(n->nhere.next); 9947 new->nhere.next = copynode(n->nhere.next);
9948 SAVE_PTR2(new->nhere.doc, "nhere.doc", NO_FREE,
9949 new->nhere.next, "nhere.next", NO_FREE);
9150 break; 9950 break;
9151 case NNOT: 9951 case NNOT:
9152 new->nnot.com = copynode(n->nnot.com); 9952 new->nnot.com = copynode(n->nnot.com);
9953 SAVE_PTR(new->nnot.com, "nnot.com", NO_FREE);
9153 break; 9954 break;
9154 }; 9955 };
9155 new->type = n->type; 9956 new->type = n->type;
@@ -9170,6 +9971,7 @@ copyfunc(union node *n)
9170 f = ckzalloc(blocksize /* + funcstringsize */); 9971 f = ckzalloc(blocksize /* + funcstringsize */);
9171 funcblock = (char *) f + offsetof(struct funcnode, n); 9972 funcblock = (char *) f + offsetof(struct funcnode, n);
9172 funcstring_end = (char *) f + blocksize; 9973 funcstring_end = (char *) f + blocksize;
9974 IF_PLATFORM_MINGW32(fs_size = 0);
9173 copynode(n); 9975 copynode(n);
9174 /* f->count = 0; - ckzalloc did it */ 9976 /* f->count = 0; - ckzalloc did it */
9175 return f; 9977 return f;
@@ -9197,12 +9999,15 @@ defun(union node *func)
9197#define SKIPFUNCDEF (1 << 3) 9999#define SKIPFUNCDEF (1 << 3)
9198static smallint evalskip; /* set to SKIPxxx if we are skipping commands */ 10000static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
9199static int skipcount; /* number of levels to skip */ 10001static int skipcount; /* number of levels to skip */
10002#if ENABLE_PLATFORM_POSIX
9200static int loopnest; /* current loop nesting level */ 10003static int loopnest; /* current loop nesting level */
10004#endif
9201static int funcline; /* starting line number of current function, or 0 if not in a function */ 10005static int funcline; /* starting line number of current function, or 0 if not in a function */
9202 10006
9203/* Forward decl way out to parsing code - dotrap needs it */ 10007/* Forward decl way out to parsing code - dotrap needs it */
9204static int evalstring(char *s, int flags); 10008static int evalstring(char *s, int flags);
9205 10009
10010#if !ENABLE_PLATFORM_MINGW32
9206/* Called to execute a trap. 10011/* Called to execute a trap.
9207 * Single callsite - at the end of evaltree(). 10012 * Single callsite - at the end of evaltree().
9208 * If we return non-zero, evaltree raises EXEXIT exception. 10013 * If we return non-zero, evaltree raises EXEXIT exception.
@@ -9261,6 +10066,9 @@ dotrap(void)
9261 savestatus = last_status; 10066 savestatus = last_status;
9262 TRACE(("dotrap returns\n")); 10067 TRACE(("dotrap returns\n"));
9263} 10068}
10069#else
10070# define dotrap()
10071#endif
9264 10072
9265/* forward declarations - evaluation is fairly recursive business... */ 10073/* forward declarations - evaluation is fairly recursive business... */
9266static int evalloop(union node *, int); 10074static int evalloop(union node *, int);
@@ -9546,6 +10354,7 @@ evalcase(union node *n, int flags)
9546static int 10354static int
9547evalsubshell(union node *n, int flags) 10355evalsubshell(union node *n, int flags)
9548{ 10356{
10357 IF_PLATFORM_MINGW32(struct forkshell fs;)
9549 struct job *jp; 10358 struct job *jp;
9550 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */ 10359 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
9551 int status; 10360 int status;
@@ -9559,12 +10368,21 @@ evalsubshell(union node *n, int flags)
9559 if (backgnd == FORK_FG) 10368 if (backgnd == FORK_FG)
9560 get_tty_state(); 10369 get_tty_state();
9561 jp = makejob(/*n,*/ 1); 10370 jp = makejob(/*n,*/ 1);
10371#if ENABLE_PLATFORM_MINGW32
10372 memset(&fs, 0, sizeof(fs));
10373 fs.fpid = FS_EVALSUBSHELL;
10374 fs.n = n;
10375 fs.flags = flags;
10376 spawn_forkshell(&fs, jp, n, backgnd);
10377 if ( 0 ) {
10378#else
9562 if (forkshell(jp, n, backgnd) == 0) { 10379 if (forkshell(jp, n, backgnd) == 0) {
9563 /* child */ 10380 /* child */
9564 INT_ON; 10381 INT_ON;
9565 flags |= EV_EXIT; 10382 flags |= EV_EXIT;
9566 if (backgnd) 10383 if (backgnd)
9567 flags &= ~EV_TESTED; 10384 flags &= ~EV_TESTED;
10385#endif
9568 nofork: 10386 nofork:
9569 redirect(n->nredir.redirect, 0); 10387 redirect(n->nredir.redirect, 0);
9570 evaltreenr(n->nredir.n, flags); 10388 evaltreenr(n->nredir.n, flags);
@@ -9650,6 +10468,7 @@ expredir(union node *n)
9650static int 10468static int
9651evalpipe(union node *n, int flags) 10469evalpipe(union node *n, int flags)
9652{ 10470{
10471 IF_PLATFORM_MINGW32(struct forkshell fs;)
9653 struct job *jp; 10472 struct job *jp;
9654 struct nodelist *lp; 10473 struct nodelist *lp;
9655 int pipelen; 10474 int pipelen;
@@ -9676,6 +10495,16 @@ evalpipe(union node *n, int flags)
9676 ash_msg_and_raise_perror("can't create pipe"); 10495 ash_msg_and_raise_perror("can't create pipe");
9677 } 10496 }
9678 } 10497 }
10498#if ENABLE_PLATFORM_MINGW32
10499 memset(&fs, 0, sizeof(fs));
10500 fs.fpid = FS_EVALPIPE;
10501 fs.flags = flags;
10502 fs.n = lp->n;
10503 fs.fd[0] = pip[0];
10504 fs.fd[1] = pip[1];
10505 fs.fd[2] = prevfd;
10506 spawn_forkshell(&fs, jp, lp->n, n->npipe.pipe_backgnd);
10507#else
9679 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) { 10508 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
9680 /* child */ 10509 /* child */
9681 INT_ON; 10510 INT_ON;
@@ -9693,6 +10522,7 @@ evalpipe(union node *n, int flags)
9693 evaltreenr(lp->n, flags); 10522 evaltreenr(lp->n, flags);
9694 /* never returns */ 10523 /* never returns */
9695 } 10524 }
10525#endif
9696 /* parent */ 10526 /* parent */
9697 if (prevfd >= 0) 10527 if (prevfd >= 0)
9698 close(prevfd); 10528 close(prevfd);
@@ -10127,12 +10957,35 @@ static int ulimitcmd(int, char **) FAST_FUNC;
10127#define BUILTIN_SPEC_REG_ASSG "7" 10957#define BUILTIN_SPEC_REG_ASSG "7"
10128 10958
10129/* Stubs for calling non-FAST_FUNC's */ 10959/* Stubs for calling non-FAST_FUNC's */
10960#if !ENABLE_PLATFORM_MINGW32
10130#if ENABLE_ASH_ECHO 10961#if ENABLE_ASH_ECHO
10131static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); } 10962static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
10132#endif 10963#endif
10133#if ENABLE_ASH_PRINTF 10964#if ENABLE_ASH_PRINTF
10134static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); } 10965static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
10135#endif 10966#endif
10967#else
10968#if ENABLE_ASH_ECHO
10969static int FAST_FUNC echocmd(int argc, char **argv)
10970{
10971 int ret;
10972 INT_OFF;
10973 ret = echo_main(argc, argv);
10974 INT_ON;
10975 return ret;
10976}
10977#endif
10978#if ENABLE_ASH_PRINTF
10979static int FAST_FUNC printfcmd(int argc, char **argv)
10980{
10981 int ret;
10982 INT_OFF;
10983 ret = printf_main(argc, argv);
10984 INT_ON;
10985 return ret;
10986}
10987#endif
10988#endif
10136#if ENABLE_ASH_TEST || BASH_TEST2 10989#if ENABLE_ASH_TEST || BASH_TEST2
10137static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); } 10990static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
10138#endif 10991#endif
@@ -10181,7 +11034,7 @@ static const struct builtincmd builtintab[] = {
10181#if MAX_HISTORY 11034#if MAX_HISTORY
10182 { BUILTIN_NOSPEC "history" , historycmd }, 11035 { BUILTIN_NOSPEC "history" , historycmd },
10183#endif 11036#endif
10184#if JOBS 11037#if JOBS || JOBS_WIN32
10185 { BUILTIN_REGULAR "jobs" , jobscmd }, 11038 { BUILTIN_REGULAR "jobs" , jobscmd },
10186 { BUILTIN_REGULAR "kill" , killcmd }, 11039 { BUILTIN_REGULAR "kill" , killcmd },
10187#endif 11040#endif
@@ -10223,7 +11076,7 @@ static const struct builtincmd builtintab[] = {
10223 /* [ */ 1 * ENABLE_ASH_TEST + \ 11076 /* [ */ 1 * ENABLE_ASH_TEST + \
10224 /* [[ */ 1 * BASH_TEST2 + \ 11077 /* [[ */ 1 * BASH_TEST2 + \
10225 /* alias */ 1 * ENABLE_ASH_ALIAS + \ 11078 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10226 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \ 11079 /* bg */ 1 * JOBS + \
10227 /* break cd cddir */ 3) 11080 /* break cd cddir */ 3)
10228#define EVALCMD (COMMANDCMD + \ 11081#define EVALCMD (COMMANDCMD + \
10229 /* command */ 1 * ENABLE_ASH_CMDCMD + \ 11082 /* command */ 1 * ENABLE_ASH_CMDCMD + \
@@ -10280,6 +11133,7 @@ bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
10280 * as POSIX mandates */ 11133 * as POSIX mandates */
10281 return back_exitstatus; 11134 return back_exitstatus;
10282} 11135}
11136
10283static int 11137static int
10284evalcommand(union node *cmd, int flags) 11138evalcommand(union node *cmd, int flags)
10285{ 11139{
@@ -10533,6 +11387,22 @@ evalcommand(union node *cmd, int flags)
10533 * in a script or a subshell does not need forking, 11387 * in a script or a subshell does not need forking,
10534 * we can just exec it. 11388 * we can just exec it.
10535 */ 11389 */
11390#if ENABLE_PLATFORM_MINGW32
11391 if (!(flags & EV_EXIT) || may_have_traps) {
11392 /* No, forking off a child is necessary */
11393 struct forkshell fs;
11394
11395 INT_OFF;
11396 memset(&fs, 0, sizeof(fs));
11397 fs.fpid = FS_SHELLEXEC;
11398 fs.argv = argv;
11399 fs.path = (char*)path;
11400 fs.fd[0] = cmdentry.u.index;
11401 jp = makejob(/*cmd,*/ 1);
11402 spawn_forkshell(&fs, jp, cmd, FORK_FG);
11403 break;
11404 }
11405#else
10536 if (!(flags & EV_EXIT) || may_have_traps) { 11406 if (!(flags & EV_EXIT) || may_have_traps) {
10537 /* No, forking off a child is necessary */ 11407 /* No, forking off a child is necessary */
10538 INT_OFF; 11408 INT_OFF;
@@ -10546,6 +11416,7 @@ evalcommand(union node *cmd, int flags)
10546 FORCE_INT_ON; 11416 FORCE_INT_ON;
10547 /* fall through to exec'ing external program */ 11417 /* fall through to exec'ing external program */
10548 } 11418 }
11419#endif
10549 shellexec(argv[0], argv, path, cmdentry.u.index); 11420 shellexec(argv[0], argv, path, cmdentry.u.index);
10550 /* NOTREACHED */ 11421 /* NOTREACHED */
10551 } /* default */ 11422 } /* default */
@@ -10787,8 +11658,12 @@ preadfd(void)
10787 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ); 11658 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
10788 if (nr == 0) { 11659 if (nr == 0) {
10789 /* ^C pressed, "convert" to SIGINT */ 11660 /* ^C pressed, "convert" to SIGINT */
11661# if !ENABLE_PLATFORM_MINGW32
10790 write(STDOUT_FILENO, "^C", 2); 11662 write(STDOUT_FILENO, "^C", 2);
10791 raise(SIGINT); 11663 raise(SIGINT);
11664# else
11665 raise_interrupt();
11666# endif
10792 if (trap[SIGINT]) { 11667 if (trap[SIGINT]) {
10793 buf[0] = '\n'; 11668 buf[0] = '\n';
10794 buf[1] = '\0'; 11669 buf[1] = '\0';
@@ -10882,7 +11757,7 @@ preadbuffer(void)
10882 more--; 11757 more--;
10883 11758
10884 c = *q; 11759 c = *q;
10885 if (c == '\0') { 11760 if (c == '\0' || (ENABLE_PLATFORM_MINGW32 && c == '\r')) {
10886 memmove(q, q + 1, more); 11761 memmove(q, q + 1, more);
10887 } else { 11762 } else {
10888 q++; 11763 q++;
@@ -11110,6 +11985,7 @@ popallfiles(void)
11110 unwindfiles(&basepf); 11985 unwindfiles(&basepf);
11111} 11986}
11112 11987
11988#if !ENABLE_PLATFORM_MINGW32
11113/* 11989/*
11114 * Close the file(s) that the shell is reading commands from. Called 11990 * Close the file(s) that the shell is reading commands from. Called
11115 * after a fork is done. 11991 * after a fork is done.
@@ -11123,6 +11999,7 @@ closescript(void)
11123 g_parsefile->pf_fd = 0; 11999 g_parsefile->pf_fd = 0;
11124 } 12000 }
11125} 12001}
12002#endif
11126 12003
11127/* 12004/*
11128 * Like setinputfile, but takes an open file descriptor. Call this with 12005 * Like setinputfile, but takes an open file descriptor. Call this with
@@ -11358,8 +12235,13 @@ options(int *login_sh)
11358 int val; 12235 int val;
11359 int c; 12236 int c;
11360 12237
11361 if (login_sh) 12238 if (login_sh) {
11362 minusc = NULL; 12239 minusc = NULL;
12240#if ENABLE_PLATFORM_MINGW32
12241 dirarg = NULL;
12242 title = NULL;
12243#endif
12244 }
11363 while ((p = *argptr) != NULL) { 12245 while ((p = *argptr) != NULL) {
11364 c = *p++; 12246 c = *p++;
11365 if (c != '-' && c != '+') 12247 if (c != '-' && c != '+')
@@ -11389,6 +12271,24 @@ options(int *login_sh)
11389 cflag = 1; 12271 cflag = 1;
11390 continue; 12272 continue;
11391 } 12273 }
12274#if ENABLE_PLATFORM_MINGW32
12275 /* Undocumented flags;
12276 * -d force current directory
12277 * -t title to display in console window
12278 * Must appear before -s or -c. */
12279 if (c == 'd' && val == 1) {
12280 if (*argptr == NULL)
12281 ash_msg_and_raise_error(bb_msg_requires_arg, "-d");
12282 dirarg = *argptr++;
12283 continue;
12284 }
12285 if (c == 't' && val == 1) {
12286 if (*argptr == NULL)
12287 ash_msg_and_raise_error(bb_msg_requires_arg, "-t");
12288 title = *argptr++;
12289 continue;
12290 }
12291#endif
11392 if (c == 's') { /* -s, +s */ 12292 if (c == 's') { /* -s, +s */
11393 sflag = 1; 12293 sflag = 1;
11394 continue; 12294 continue;
@@ -13412,6 +14312,9 @@ evalstring(char *s, int flags)
13412 int status; 14312 int status;
13413 14313
13414 s = sstrdup(s); 14314 s = sstrdup(s);
14315#if ENABLE_PLATFORM_MINGW32
14316 remove_cr(s, strlen(s)+1);
14317#endif
13415 setinputstring(s); 14318 setinputstring(s);
13416 setstackmark(&smark); 14319 setstackmark(&smark);
13417 14320
@@ -13499,7 +14402,7 @@ cmdloop(int top)
13499 int skip; 14402 int skip;
13500 14403
13501 setstackmark(&smark); 14404 setstackmark(&smark);
13502#if JOBS 14405#if JOBS || JOBS_WIN32
13503 if (doing_jobctl) 14406 if (doing_jobctl)
13504 showjobs(SHOW_CHANGED|SHOW_STDERR); 14407 showjobs(SHOW_CHANGED|SHOW_STDERR);
13505#endif 14408#endif
@@ -13530,8 +14433,10 @@ cmdloop(int top)
13530 } else { 14433 } else {
13531 int i; 14434 int i;
13532 14435
14436#if !ENABLE_PLATFORM_MINGW32
13533 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */ 14437 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13534 job_warning >>= 1; 14438 job_warning >>= 1;
14439#endif
13535 numeof = 0; 14440 numeof = 0;
13536 i = evaltree(n, 0); 14441 i = evaltree(n, 0);
13537 if (n) 14442 if (n)
@@ -13561,7 +14466,7 @@ find_dot_file(char *basename)
13561 int len; 14466 int len;
13562 14467
13563 /* don't try this for absolute or relative paths */ 14468 /* don't try this for absolute or relative paths */
13564 if (strchr(basename, '/')) 14469 if (strchr(basename, '/') IF_PLATFORM_MINGW32(|| strchr(basename, '\\')))
13565 return basename; 14470 return basename;
13566 14471
13567 while ((len = padvance(&path, basename)) >= 0) { 14472 while ((len = padvance(&path, basename)) >= 0) {
@@ -13689,6 +14594,7 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13689 struct builtincmd *bcmd; 14594 struct builtincmd *bcmd;
13690 int len; 14595 int len;
13691 14596
14597#if !ENABLE_PLATFORM_MINGW32
13692 /* If name contains a slash, don't use PATH or hash table */ 14598 /* If name contains a slash, don't use PATH or hash table */
13693 if (strchr(name, '/') != NULL) { 14599 if (strchr(name, '/') != NULL) {
13694 entry->u.index = -1; 14600 entry->u.index = -1;
@@ -13705,6 +14611,29 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13705 entry->cmdtype = CMDNORMAL; 14611 entry->cmdtype = CMDNORMAL;
13706 return; 14612 return;
13707 } 14613 }
14614#else /* ENABLE_PLATFORM_MINGW32 */
14615 /* If name contains a slash or drive prefix, don't use PATH or hash table */
14616 if (has_path(name)) {
14617 fullname = stack_add_system_drive(name);
14618 entry->u.index = -1;
14619 if (act & DO_ABS) {
14620 if (!add_win32_extension(fullname) && stat(fullname, &statb) < 0) {
14621# if ENABLE_FEATURE_SH_STANDALONE
14622 if (unix_path(name) &&
14623 find_applet_by_name(bb_basename(name)) >= 0) {
14624 entry->cmdtype = CMDNORMAL;
14625 entry->u.index = INT_MIN;
14626 return;
14627 }
14628# endif
14629 entry->cmdtype = CMDUNKNOWN;
14630 return;
14631 }
14632 }
14633 entry->cmdtype = CMDNORMAL;
14634 return;
14635 }
14636#endif /* ENABLE_PLATFORM_MINGW32 */
13708 14637
13709/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */ 14638/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
13710 14639
@@ -13798,12 +14727,15 @@ find_command(char *name, struct cmdentry *entry, int act, const char *path)
13798 } 14727 }
13799 } 14728 }
13800 /* if rehash, don't redo absolute path names */ 14729 /* if rehash, don't redo absolute path names */
13801 if (fullname[0] == '/' && idx <= prev) { 14730 if (!is_relative_path(fullname) && idx <= prev) {
13802 if (idx < prev) 14731 if (idx < prev)
13803 continue; 14732 continue;
13804 TRACE(("searchexec \"%s\": no change\n", name)); 14733 TRACE(("searchexec \"%s\": no change\n", name));
13805 goto success; 14734 goto success;
13806 } 14735 }
14736#if ENABLE_PLATFORM_MINGW32
14737 add_win32_extension(fullname);
14738#endif
13807 while (stat(fullname, &statb) < 0) { 14739 while (stat(fullname, &statb) < 0) {
13808#ifdef SYSV 14740#ifdef SYSV
13809 if (errno == EINTR) 14741 if (errno == EINTR)
@@ -13942,8 +14874,10 @@ trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13942 if (LONE_DASH(action)) 14874 if (LONE_DASH(action))
13943 action = NULL; 14875 action = NULL;
13944 else { 14876 else {
14877#if !ENABLE_PLATFORM_MINGW32
13945 if (action[0]) /* not NULL and not "" and not "-" */ 14878 if (action[0]) /* not NULL and not "" and not "-" */
13946 may_have_traps = 1; 14879 may_have_traps = 1;
14880#endif
13947 action = ckstrdup(action); 14881 action = ckstrdup(action);
13948 } 14882 }
13949 } 14883 }
@@ -14239,6 +15173,20 @@ readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14239 goto again; 15173 goto again;
14240 } 15174 }
14241 15175
15176#if ENABLE_PLATFORM_MINGW32
15177 if ((uintptr_t)r == 2) {
15178 /* ^C pressed, propagate event */
15179 if (iflag) {
15180 raise_interrupt();
15181 }
15182 else {
15183 GenerateConsoleCtrlEvent(CTRL_C_EVENT, 0);
15184 exitshell();
15185 }
15186 return (uintptr_t)r;
15187 }
15188#endif
15189
14242 if ((uintptr_t)r > 1) 15190 if ((uintptr_t)r > 1)
14243 ash_msg_and_raise_error(r); 15191 ash_msg_and_raise_error(r);
14244 15192
@@ -14299,6 +15247,9 @@ umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14299 if (!isdigit(modestr[0])) 15247 if (!isdigit(modestr[0]))
14300 mask ^= 0777; 15248 mask ^= 0777;
14301 umask(mask); 15249 umask(mask);
15250#if ENABLE_PLATFORM_MINGW32
15251 setvareq(xasprintf("BB_UMASK=0%o", mask), VEXPORT|VNOSAVE);
15252#endif
14302 } 15253 }
14303 return 0; 15254 return 0;
14304} 15255}
@@ -14392,22 +15343,108 @@ exitshell(void)
14392 /* NOTREACHED */ 15343 /* NOTREACHED */
14393} 15344}
14394 15345
15346#if ENABLE_PLATFORM_MINGW32
15347static void xsetenv_if_unset(const char *key, const char *value)
15348{
15349 if (!getenv(key))
15350 xsetenv(key, value);
15351}
15352#endif
15353
14395/* Don't inline: conserve stack of caller from having our locals too */ 15354/* Don't inline: conserve stack of caller from having our locals too */
14396static NOINLINE void 15355static NOINLINE void
14397init(void) 15356init(void)
14398{ 15357{
15358#if !ENABLE_PLATFORM_MINGW32
14399 /* we will never free this */ 15359 /* we will never free this */
14400 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ); 15360 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
14401 basepf.linno = 1; 15361 basepf.linno = 1;
14402 15362
14403 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */ 15363 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
14404 setsignal(SIGCHLD); 15364 setsignal(SIGCHLD);
15365#endif
14405 15366
14406 { 15367 {
14407 char **envp; 15368 char **envp;
14408 const char *p; 15369 const char *p;
14409 15370
14410 initvar(); 15371 initvar();
15372
15373#if ENABLE_PLATFORM_MINGW32
15374 /*
15375 * case insensitive env names from Windows world
15376 *
15377 * Some standard env names such as PATH is named Path and so on
15378 * ash itself is case sensitive, so "Path" will confuse it, as
15379 * MSVC getenv() is case insensitive.
15380 *
15381 * We may end up having both Path and PATH. Then Path will be chosen
15382 * because it appears first.
15383 */
15384 for (envp = environ; envp && *envp; envp++) {
15385 if (is_prefixed_with_case(*envp, "PATH=") &&
15386 !is_prefixed_with(*envp, "PATH=")) {
15387 break;
15388 }
15389 }
15390
15391 if (envp && *envp) {
15392 /*
15393 * If we get here it's because the environment contains a path
15394 * variable called something other than PATH. This suggests we
15395 * haven't been invoked from an earlier instance of BusyBox.
15396 */
15397 char *start, *end;
15398 struct passwd *pw;
15399
15400 for (envp = environ; envp && *envp; envp++) {
15401 if (!(end=strchr(*envp, '=')))
15402 continue;
15403
15404 /* make all variable names uppercase */
15405 for (start = *envp;start < end;start++)
15406 *start = toupper(*start);
15407
15408 /* Convert backslashes to forward slashes in value but
15409 * not if we're on Windows XP or for variables known to
15410 * cause problems */
15411 if (!winxp && !is_prefixed_with(*envp, "SYSTEMROOT=") &&
15412 !is_prefixed_with(*envp, "COMSPEC=")) {
15413 bs_to_slash(end+1);
15414 }
15415
15416 /* check for invalid characters in name */
15417 for (start = *envp;start < end;start++) {
15418 if (!isdigit(*start) && !isalpha(*start) && *start != '_') {
15419 break;
15420 }
15421 }
15422
15423 if (start != end) {
15424 /*
15425 * Make a copy of the variable, replacing invalid
15426 * characters in the name with underscores.
15427 */
15428 char *var = xstrdup(*envp);
15429
15430 for (start = var;*start != '=';start++) {
15431 if (!isdigit(*start) && !isalpha(*start)) {
15432 *start = '_';
15433 }
15434 }
15435 setvareq(var, VEXPORT|VNOSAVE);
15436 }
15437 }
15438
15439 /* Initialise some variables normally set at login, but
15440 * only if someone hasn't already set them. */
15441 pw = xgetpwuid(getuid());
15442 xsetenv_if_unset("USER", pw->pw_name);
15443 xsetenv_if_unset("LOGNAME", pw->pw_name);
15444 xsetenv_if_unset("HOME", pw->pw_dir);
15445 xsetenv_if_unset("SHELL", DEFAULT_SHELL);
15446 }
15447#endif
14411 for (envp = environ; envp && *envp; envp++) { 15448 for (envp = environ; envp && *envp; envp++) {
14412/* Used to have 15449/* Used to have
14413 * p = endofname(*envp); 15450 * p = endofname(*envp);
@@ -14421,7 +15458,11 @@ init(void)
14421 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this 15458 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14422 */ 15459 */
14423 if (strchr(*envp, '=')) { 15460 if (strchr(*envp, '=')) {
15461#if !ENABLE_PLATFORM_MINGW32
14424 setvareq(*envp, VEXPORT|VTEXTFIXED); 15462 setvareq(*envp, VEXPORT|VTEXTFIXED);
15463#else
15464 setvareq(*envp, VEXPORT);
15465#endif
14425 } 15466 }
14426 } 15467 }
14427 15468
@@ -14496,7 +15537,9 @@ procargs(char **argv)
14496 } 15537 }
14497 if (iflag == 2 /* no explicit -i given */ 15538 if (iflag == 2 /* no explicit -i given */
14498 && sflag == 1 /* -s given (or implied) */ 15539 && sflag == 1 /* -s given (or implied) */
15540#if !ENABLE_PLATFORM_MINGW32
14499 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */ 15541 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
15542#endif
14500 && isatty(0) && isatty(1) /* we are on tty */ 15543 && isatty(0) && isatty(1) /* we are on tty */
14501 ) { 15544 ) {
14502 iflag = 1; 15545 iflag = 1;
@@ -14516,6 +15559,9 @@ procargs(char **argv)
14516 goto setarg0; 15559 goto setarg0;
14517 } else if (!sflag) { 15560 } else if (!sflag) {
14518 setinputfile(*xargv, 0); 15561 setinputfile(*xargv, 0);
15562#if ENABLE_PLATFORM_MINGW32
15563 bs_to_slash(*xargv);
15564#endif
14519 setarg0: 15565 setarg0:
14520 arg0 = *xargv++; 15566 arg0 = *xargv++;
14521 commandname = arg0; 15567 commandname = arg0;
@@ -14539,8 +15585,10 @@ procargs(char **argv)
14539 * NB: must do it before setting up signals (in optschanged()) 15585 * NB: must do it before setting up signals (in optschanged())
14540 * and reading .profile etc (after we return from here): 15586 * and reading .profile etc (after we return from here):
14541 */ 15587 */
15588#if !ENABLE_PLATFORM_MINGW32
14542 if (iflag) 15589 if (iflag)
14543 signal(SIGHUP, SIG_DFL); 15590 signal(SIGHUP, SIG_DFL);
15591#endif
14544 15592
14545 optschanged(); 15593 optschanged();
14546 15594
@@ -14585,9 +15633,25 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14585 struct stackmark smark; 15633 struct stackmark smark;
14586 int login_sh; 15634 int login_sh;
14587 15635
15636#if ENABLE_PLATFORM_MINGW32
15637 INIT_G_memstack();
15638
15639 /* from init() */
15640 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
15641 basepf.linno = 1;
15642
15643 if (argc == 3 && !strcmp(argv[1], "--fs")) {
15644 forkshell_init(argv[2]);
15645 /* only reached in case of error */
15646 bb_error_msg_and_die("forkshell failed");
15647 }
15648#endif
15649
14588 /* Initialize global data */ 15650 /* Initialize global data */
14589 INIT_G_misc(); 15651 INIT_G_misc();
15652#if !ENABLE_PLATFORM_MINGW32
14590 INIT_G_memstack(); 15653 INIT_G_memstack();
15654#endif
14591 INIT_G_var(); 15655 INIT_G_var();
14592#if ENABLE_ASH_ALIAS 15656#if ENABLE_ASH_ALIAS
14593 INIT_G_alias(); 15657 INIT_G_alias();
@@ -14630,9 +15694,16 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14630 exception_handler = &jmploc; 15694 exception_handler = &jmploc;
14631 rootpid = getpid(); 15695 rootpid = getpid();
14632 15696
15697#if ENABLE_PLATFORM_MINGW32
15698 winxp = (argv[1] != NULL && strcmp(argv[1], "-X") == 0);
15699#endif
14633 init(); 15700 init();
14634 setstackmark(&smark); 15701 setstackmark(&smark);
14635 15702
15703#if ENABLE_PLATFORM_MINGW32
15704 SetConsoleCtrlHandler(ctrl_handler, TRUE);
15705#endif
15706
14636#if NUM_SCRIPTS > 0 15707#if NUM_SCRIPTS > 0
14637 if (argc < 0) 15708 if (argc < 0)
14638 /* Non-NULL minusc tells procargs that an embedded script is being run */ 15709 /* Non-NULL minusc tells procargs that an embedded script is being run */
@@ -14644,11 +15715,46 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14644 trace_puts_args(argv); 15715 trace_puts_args(argv);
14645#endif 15716#endif
14646 15717
15718#if ENABLE_ASH_NOCONSOLE
15719 if (noconsole)
15720 hide_console();
15721#endif
15722
15723#if ENABLE_PLATFORM_MINGW32
15724 if (dirarg) {
15725 chdir(dirarg);
15726 setpwd(NULL, 0);
15727 }
15728 else if (!login_sh && iflag) {
15729 char *cwd = getcwd(NULL, 0);
15730 if (cwd) {
15731 docd(cwd, 0);
15732 free(cwd);
15733 }
15734 }
15735
15736 if (title)
15737 set_title(title);
15738#endif
15739
14647 if (login_sh) { 15740 if (login_sh) {
14648 const char *hp; 15741 const char *hp;
14649 15742
15743#if ENABLE_PLATFORM_MINGW32
15744 if (!dirarg) {
15745 chdir(xgetpwuid(getuid())->pw_dir);
15746 setpwd(NULL, 0);
15747 }
15748#endif
15749
14650 state = 1; 15750 state = 1;
15751#if ENABLE_PLATFORM_MINGW32
15752 hp = xasprintf("%s/etc/profile", get_system_drive() ?: "");
15753 read_profile(hp);
15754 free((void *)hp);
15755#else
14651 read_profile("/etc/profile"); 15756 read_profile("/etc/profile");
15757#endif
14652 state1: 15758 state1:
14653 state = 2; 15759 state = 2;
14654 hp = lookupvar("HOME"); 15760 hp = lookupvar("HOME");
@@ -14658,9 +15764,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14658 state2: 15764 state2:
14659 state = 3; 15765 state = 3;
14660 if (iflag 15766 if (iflag
15767#if ENABLE_PLATFORM_POSIX
14661#ifndef linux 15768#ifndef linux
14662 && getuid() == geteuid() && getgid() == getegid() 15769 && getuid() == geteuid() && getgid() == getegid()
14663#endif 15770#endif
15771#endif
14664 ) { 15772 ) {
14665 const char *shinit = lookupvar("ENV"); 15773 const char *shinit = lookupvar("ENV");
14666 if (shinit != NULL && *shinit != '\0') 15774 if (shinit != NULL && *shinit != '\0')
@@ -14685,7 +15793,11 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14685 // ash -sc 'echo $-' 15793 // ash -sc 'echo $-'
14686 // continue reading input from stdin after running 'echo'. 15794 // continue reading input from stdin after running 'echo'.
14687 // bash does not do this: it prints "hBcs" and exits. 15795 // bash does not do this: it prints "hBcs" and exits.
15796#if !ENABLE_PLATFORM_MINGW32
14688 evalstring(minusc, EV_EXIT); 15797 evalstring(minusc, EV_EXIT);
15798#else
15799 evalstring(minusc, sflag ? 0 : EV_EXIT);
15800#endif
14689 } 15801 }
14690 15802
14691 if (sflag || minusc == NULL) { 15803 if (sflag || minusc == NULL) {
@@ -14728,6 +15840,954 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
14728 /* NOTREACHED */ 15840 /* NOTREACHED */
14729} 15841}
14730 15842
15843#if ENABLE_PLATFORM_MINGW32
15844static void
15845forkshell_openhere(struct forkshell *fs)
15846{
15847 const char *p = fs->path;
15848 size_t len = strlen(p);
15849 int pip[2];
15850
15851 pip[0] = fs->fd[0];
15852 pip[1] = fs->fd[1];
15853
15854 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15855
15856 close(pip[0]);
15857 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
15858 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
15859 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
15860 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
15861 signal(SIGPIPE, SIG_DFL);
15862 xwrite(pip[1], p, len);
15863 _exit(EXIT_SUCCESS);
15864}
15865
15866static void
15867forkshell_evalbackcmd(struct forkshell *fs)
15868{
15869#if BASH_PROCESS_SUBST
15870 /* determine end of pipe used by parent (ip) and child (ic) */
15871 const int ctl = fs->fd[2];
15872 const int ip = (ctl == CTLTOPROC);
15873 const int ic = !(ctl == CTLTOPROC);
15874#else
15875 const int ip = 0;
15876 const int ic = 1;
15877#endif
15878 union node *n = fs->n;
15879 int pip[2];
15880
15881 pip[ip] = fs->fd[ip];
15882 pip[ic] = fs->fd[ic];
15883
15884 FORCE_INT_ON;
15885 close(pip[ip]);
15886 if (pip[ic] != ic) {
15887 /*close(ic);*/
15888 dup2_or_raise(pip[ic], ic);
15889 close(pip[ic]);
15890 }
15891 eflag = 0;
15892 ifsfree();
15893 evaltreenr(n, EV_EXIT);
15894 /* NOTREACHED */
15895}
15896
15897static void
15898forkshell_evalsubshell(struct forkshell *fs)
15899{
15900 union node *n = fs->n;
15901 int flags = fs->flags;
15902
15903 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15904 INT_ON;
15905 flags |= EV_EXIT;
15906 expredir(n->nredir.redirect);
15907 redirect(n->nredir.redirect, 0);
15908 evaltreenr(n->nredir.n, flags);
15909 /* never returns */
15910}
15911
15912static void
15913forkshell_evalpipe(struct forkshell *fs)
15914{
15915 union node *n = fs->n;
15916 int flags = fs->flags;
15917 int prevfd = fs->fd[2];
15918 int pip[2] = {fs->fd[0], fs->fd[1]};
15919
15920 TRACE(("ash: subshell: %s\n",__PRETTY_FUNCTION__));
15921 INT_ON;
15922 if (pip[1] >= 0) {
15923 close(pip[0]);
15924 }
15925 if (prevfd > 0) {
15926 dup2(prevfd, 0);
15927 close(prevfd);
15928 }
15929 if (pip[1] > 1) {
15930 dup2(pip[1], 1);
15931 close(pip[1]);
15932 }
15933 evaltreenr(n, flags);
15934}
15935
15936static void
15937forkshell_shellexec(struct forkshell *fs)
15938{
15939 int idx = fs->fd[0];
15940 char **argv = fs->argv;
15941 char *path = fs->path;
15942
15943 FORCE_INT_ON;
15944 shellexec(argv[0], argv, path, idx);
15945}
15946
15947static void
15948forkshell_child(struct forkshell *fs)
15949{
15950 switch ( fs->fpid ) {
15951 case FS_OPENHERE:
15952 forkshell_openhere(fs);
15953 break;
15954 case FS_EVALBACKCMD:
15955 forkshell_evalbackcmd(fs);
15956 break;
15957 case FS_EVALSUBSHELL:
15958 forkshell_evalsubshell(fs);
15959 break;
15960 case FS_EVALPIPE:
15961 forkshell_evalpipe(fs);
15962 break;
15963 case FS_SHELLEXEC:
15964 forkshell_shellexec(fs);
15965 break;
15966 }
15967}
15968
15969/*
15970 * Reinitialise the builtin environment variables in varinit. Their
15971 * current settings have been copied from the parent in vartab. Look
15972 * these up using the names from varinit_data, copy the details from
15973 * vartab to varinit and replace the old copy in vartab with the new
15974 * one in varinit.
15975 *
15976 * Also reinitialise the function pointers and line number variable.
15977 */
15978static void
15979reinitvar(void)
15980{
15981 int i;
15982 const char *name;
15983 struct var **vpp, **old;
15984
15985 for (i=0; i<ARRAY_SIZE(varinit); ++i) {
15986 if (i == LINENO_INDEX)
15987 name = "LINENO=";
15988 else if (i == FUNCNAME_INDEX)
15989 name = "FUNCNAME=";
15990 else
15991 name = varinit_data[i].var_text;
15992 vpp = hashvar(name);
15993 if ( (old=findvar(vpp, name)) != NULL ) {
15994 varinit[i] = **old;
15995 *old = varinit+i;
15996 }
15997 varinit[i].var_func = varinit_data[i].var_func;
15998 }
15999 vlineno.var_text = linenovar;
16000 vfuncname.var_text = funcnamevar;
16001}
16002
16003static void
16004spawn_forkshell(struct forkshell *fs, struct job *jp, union node *n, int mode)
16005{
16006 struct forkshell *new;
16007 char buf[32];
16008 const char *argv[] = { "sh", "--fs", NULL, NULL };
16009 intptr_t ret;
16010
16011 new = forkshell_prepare(fs);
16012 if (new == NULL)
16013 goto fail;
16014
16015 new->mode = mode;
16016 new->nprocs = jp == NULL ? 0 : jp->nprocs;
16017 sprintf(buf, "%p", new->hMapFile);
16018 argv[2] = buf;
16019 ret = spawnve(P_NOWAIT, bb_busybox_exec_path, (char *const *)argv, NULL);
16020 CloseHandle(new->hMapFile);
16021 UnmapViewOfFile(new);
16022 if (ret == -1) {
16023 fail:
16024 if (jp)
16025 freejob(jp);
16026 ash_msg_and_raise_error("unable to spawn shell");
16027 }
16028 forkparent(jp, n, mode, (HANDLE)ret);
16029}
16030
16031/*
16032 * forkshell_prepare() and friends
16033 *
16034 * The sequence is as follows:
16035 * - funcblocksize is initialized
16036 * - forkshell_size(fs) is called to calculate the exact memory needed
16037 * - a new struct is allocated
16038 * - funcblock, funcstring, relocate are initialized from the new block
16039 * - forkshell_copy(fs) is called to copy recursively everything over
16040 * it will record all relocations along the way
16041 *
16042 * When this memory is mapped elsewhere, pointer fixup will be needed
16043 */
16044
16045/* redefine without test that fs_size is nonzero */
16046#undef SAVE_PTR
16047#undef SAVE_PTR2
16048#undef SAVE_PTR3
16049#define SAVE_PTR(dst,note,flag) {MARK_PTR(dst,flag); ANNOT(dst,note);}
16050
16051static int align_len(const char *s)
16052{
16053 return s ? SHELL_ALIGN(strlen(s)+1) : 0;
16054}
16055
16056struct datasize {
16057 int funcblocksize;
16058 int funcstringsize;
16059};
16060
16061#define SLIST_SIZE_BEGIN(name,type) \
16062static struct datasize \
16063name(struct datasize ds, type *p) \
16064{ \
16065 while (p) { \
16066 ds.funcblocksize += sizeof(type);
16067 /* do something here with p */
16068#define SLIST_SIZE_END() \
16069 p = p->next; \
16070 } \
16071 return ds; \
16072}
16073
16074#define SLIST_COPY_BEGIN(name,type) \
16075static type * \
16076name(type *vp) \
16077{ \
16078 type *start; \
16079 type **vpp; \
16080 vpp = &start; \
16081 while (vp) { \
16082 *vpp = funcblock; \
16083 funcblock = (char *) funcblock + sizeof(type);
16084 /* do something here with vpp and vp */
16085#define SLIST_COPY_END() \
16086 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE); \
16087 vp = vp->next; \
16088 vpp = &(*vpp)->next; \
16089 } \
16090 *vpp = NULL; \
16091 return start; \
16092}
16093
16094/*
16095 * struct var
16096 */
16097SLIST_SIZE_BEGIN(var_size,struct var)
16098ds.funcstringsize += align_len(p->var_text);
16099SLIST_SIZE_END()
16100
16101SLIST_COPY_BEGIN(var_copy,struct var)
16102(*vpp)->var_text = nodeckstrdup(vp->var_text);
16103(*vpp)->flags = vp->flags;
16104(*vpp)->var_func = NULL;
16105SAVE_PTR((*vpp)->var_text, xasprintf("(*vpp)->var_text '%s'", vp->var_text ?: "NULL"), FREE);
16106SLIST_COPY_END()
16107
16108/*
16109 * struct tblentry
16110 */
16111static struct datasize
16112tblentry_size(struct datasize ds, struct tblentry *tep)
16113{
16114 while (tep) {
16115 ds.funcblocksize += sizeof(struct tblentry) + align_len(tep->cmdname);
16116 /* CMDBUILTIN, e->param.cmd needs no pointer relocation */
16117 if (tep->cmdtype == CMDFUNCTION) {
16118 ds.funcblocksize += offsetof(struct funcnode, n);
16119 ds.funcblocksize = calcsize(ds.funcblocksize, &tep->param.func->n);
16120 }
16121 tep = tep->next;
16122 }
16123 return ds;
16124}
16125
16126static struct tblentry *
16127tblentry_copy(struct tblentry *tep)
16128{
16129 struct tblentry *start;
16130 struct tblentry **newp;
16131 int size;
16132
16133 newp = &start;
16134 while (tep) {
16135 *newp = funcblock;
16136 size = sizeof(struct tblentry) + align_len(tep->cmdname);
16137
16138 funcblock = (char *) funcblock + size;
16139 memcpy(*newp, tep, sizeof(struct tblentry)+strlen(tep->cmdname));
16140 switch (tep->cmdtype) {
16141 case CMDBUILTIN:
16142 /* Save index of builtin, not pointer; fixed by forkshell_init() */
16143 (*newp)->param.index = tep->param.cmd - builtintab;
16144 break;
16145 case CMDFUNCTION:
16146 (*newp)->param.func = funcblock;
16147 funcblock = (char *) funcblock + offsetof(struct funcnode, n);
16148 copynode(&tep->param.func->n);
16149 SAVE_PTR((*newp)->param.func, "param.func", NO_FREE);
16150 break;
16151 default:
16152 break;
16153 }
16154 SAVE_PTR((*newp)->next, xasprintf("cmdname '%s'", tep->cmdname), FREE);
16155 tep = tep->next;
16156 newp = &(*newp)->next;
16157 }
16158 *newp = NULL;
16159 return start;
16160}
16161
16162static struct datasize
16163cmdtable_size(struct datasize ds, struct tblentry **cmdtablep)
16164{
16165 int i;
16166 ds.funcblocksize += sizeof(struct tblentry *)*CMDTABLESIZE;
16167 for (i = 0; i < CMDTABLESIZE; i++)
16168 ds = tblentry_size(ds, cmdtablep[i]);
16169 return ds;
16170}
16171
16172static struct tblentry **
16173cmdtable_copy(struct tblentry **cmdtablep)
16174{
16175 struct tblentry **new = funcblock;
16176 int i;
16177
16178 funcblock = (char *) funcblock + sizeof(struct tblentry *)*CMDTABLESIZE;
16179 for (i = 0; i < CMDTABLESIZE; i++) {
16180 new[i] = tblentry_copy(cmdtablep[i]);
16181 SAVE_PTR(new[i], xasprintf("cmdtable[%d]", i), FREE);
16182 }
16183 return new;
16184}
16185
16186#if ENABLE_ASH_ALIAS
16187/*
16188 * struct alias
16189 */
16190SLIST_SIZE_BEGIN(alias_size,struct alias)
16191ds.funcstringsize += align_len(p->name);
16192ds.funcstringsize += align_len(p->val);
16193SLIST_SIZE_END()
16194
16195SLIST_COPY_BEGIN(alias_copy,struct alias)
16196(*vpp)->name = nodeckstrdup(vp->name);
16197(*vpp)->val = nodeckstrdup(vp->val);
16198(*vpp)->flag = vp->flag;
16199SAVE_PTR((*vpp)->name, xasprintf("(*vpp)->name '%s'", vp->name ?: "NULL"), FREE);
16200SAVE_PTR((*vpp)->val, xasprintf("(*vpp)->val '%s'", vp->val ?: "NULL"), FREE);
16201SLIST_COPY_END()
16202
16203static struct datasize
16204atab_size(struct datasize ds, struct alias **atabp)
16205{
16206 int i;
16207 ds.funcblocksize += sizeof(struct alias *)*ATABSIZE;
16208 for (i = 0; i < ATABSIZE; i++)
16209 ds = alias_size(ds, atabp[i]);
16210 return ds;
16211}
16212
16213static struct alias **
16214atab_copy(struct alias **atabp)
16215{
16216 struct alias **new = funcblock;
16217 int i;
16218
16219 funcblock = (char *) funcblock + sizeof(struct alias *)*ATABSIZE;
16220 for (i = 0; i < ATABSIZE; i++) {
16221 new[i] = alias_copy(atabp[i]);
16222 SAVE_PTR(new[i], xasprintf("atab[%d]", i), FREE);
16223 }
16224 return new;
16225}
16226#endif
16227
16228/*
16229 * char **
16230 */
16231static struct datasize
16232argv_size(struct datasize ds, char **p)
16233{
16234 if (p) {
16235 while (*p) {
16236 ds.funcblocksize += sizeof(char *);
16237 ds.funcstringsize += align_len(*p);
16238 p++;
16239 }
16240 ds.funcblocksize += sizeof(char *);
16241 }
16242 return ds;
16243}
16244
16245static char **
16246argv_copy(char **p)
16247{
16248 char **new, **start = funcblock;
16249#if FORKSHELL_DEBUG
16250 int i = 0;
16251#endif
16252
16253 if (p) {
16254 while (*p) {
16255 new = funcblock;
16256 funcblock = (char *) funcblock + sizeof(char *);
16257 *new = nodeckstrdup(*p);
16258 SAVE_PTR(*new, xasprintf("argv[%d] '%s'", i++, *p), FREE);
16259 p++;
16260 }
16261 new = funcblock;
16262 funcblock = (char *) funcblock + sizeof(char *);
16263 *new = NULL;
16264 return start;
16265 }
16266 return NULL;
16267}
16268
16269#if MAX_HISTORY
16270static struct datasize
16271history_size(struct datasize ds, line_input_t *st)
16272{
16273 int i;
16274
16275 ds.funcblocksize += sizeof(char *) * st->cnt_history;
16276 for (i = 0; i < st->cnt_history; i++) {
16277 ds.funcstringsize += align_len(st->history[i]);
16278 }
16279 return ds;
16280}
16281
16282static char **
16283history_copy(line_input_t *st)
16284{
16285 char **new = funcblock;
16286 int i;
16287
16288 funcblock = (char *)funcblock + sizeof(char *) * st->cnt_history;
16289 for (i = 0; i < st->cnt_history; i++) {
16290 new[i] = nodeckstrdup(st->history[i]);
16291 SAVE_PTR(new[i],
16292 xasprintf("history[%d] '%s'", i, st->history[i]), FREE);
16293 }
16294 return new;
16295}
16296#endif
16297
16298/*
16299 * struct redirtab
16300 */
16301static int
16302redirtab_size(int funcblocksize, struct redirtab *rdtp)
16303{
16304 while (rdtp) {
16305 funcblocksize += sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
16306 rdtp = rdtp->next;
16307 }
16308 return funcblocksize;
16309}
16310
16311static struct redirtab *
16312redirtab_copy(struct redirtab *rdtp)
16313{
16314 struct redirtab *start;
16315 struct redirtab **vpp;
16316
16317 vpp = &start;
16318 while (rdtp) {
16319 int size = sizeof(*rdtp)+sizeof(rdtp->two_fd[0])*rdtp->pair_count;
16320 *vpp = funcblock;
16321 funcblock = (char *) funcblock + size;
16322 memcpy(*vpp, rdtp, size);
16323 SAVE_PTR((*vpp)->next, "(*vpp)->next", NO_FREE);
16324 rdtp = rdtp->next;
16325 vpp = &(*vpp)->next;
16326 }
16327 *vpp = NULL;
16328 return start;
16329}
16330
16331#undef funcname
16332#undef shellparam
16333#undef redirlist
16334#undef vartab
16335static struct datasize
16336globals_var_size(struct datasize ds, struct globals_var *gvp)
16337{
16338 int i;
16339
16340 ds.funcblocksize += sizeof(struct globals_var);
16341 ds.funcstringsize += align_len(gvp->funcname);
16342 ds = argv_size(ds, gvp->shellparam.p);
16343 ds.funcblocksize = redirtab_size(ds.funcblocksize, gvp->redirlist);
16344 for (i = 0; i < VTABSIZE; i++)
16345 ds = var_size(ds, gvp->vartab[i]);
16346 return ds;
16347}
16348
16349static struct globals_var *
16350globals_var_copy(struct globals_var *gvp)
16351{
16352 int i;
16353 struct globals_var *new;
16354
16355 new = funcblock;
16356 funcblock = (char *) funcblock + sizeof(struct globals_var);
16357 memcpy(new, gvp, sizeof(struct globals_var));
16358
16359 new->funcname = nodeckstrdup(gvp->funcname);
16360 SAVE_PTR(new->funcname, xasprintf("funcname '%s'", gvp->funcname ?: "NULL"), FREE);
16361
16362 /* shparam */
16363 new->shellparam.malloced = 0;
16364 new->shellparam.p = argv_copy(gvp->shellparam.p);
16365 SAVE_PTR(new->shellparam.p, "shellparam.p", NO_FREE);
16366
16367 new->redirlist = redirtab_copy(gvp->redirlist);
16368 SAVE_PTR(new->redirlist, "redirlist", NO_FREE);
16369
16370 for (i = 0; i < VTABSIZE; i++) {
16371 new->vartab[i] = var_copy(gvp->vartab[i]);
16372 SAVE_PTR(new->vartab[i], xasprintf("vartab[%d]", i), FREE);
16373 }
16374
16375 return new;
16376}
16377
16378#undef minusc
16379#undef curdir
16380#undef physdir
16381#undef arg0
16382#undef commandname
16383#undef nullstr
16384static struct datasize
16385globals_misc_size(struct datasize ds, struct globals_misc *p)
16386{
16387 ds.funcblocksize += sizeof(struct globals_misc);
16388 ds.funcstringsize += align_len(p->minusc);
16389 if (p->curdir != p->nullstr)
16390 ds.funcstringsize += align_len(p->curdir);
16391 if (p->physdir != p->nullstr)
16392 ds.funcstringsize += align_len(p->physdir);
16393 ds.funcstringsize += align_len(p->arg0);
16394 ds.funcstringsize += align_len(p->commandname);
16395 return ds;
16396}
16397
16398static struct globals_misc *
16399globals_misc_copy(struct globals_misc *p)
16400{
16401 struct globals_misc *new = funcblock;
16402
16403 funcblock = (char *) funcblock + sizeof(struct globals_misc);
16404 memcpy(new, p, sizeof(struct globals_misc));
16405
16406 new->minusc = nodeckstrdup(p->minusc);
16407 new->curdir = p->curdir != p->nullstr ? nodeckstrdup(p->curdir) : new->nullstr;
16408 new->physdir = p->physdir != p->nullstr ? nodeckstrdup(p->physdir) : new->nullstr;
16409 new->arg0 = nodeckstrdup(p->arg0);
16410 new->commandname = nodeckstrdup(p->commandname);
16411 SAVE_PTR(new->minusc, xasprintf("minusc '%s'", p->minusc ?: "NULL"), FREE);
16412 SAVE_PTR(new->curdir,
16413 xasprintf("curdir '%s'", new->curdir ?: "NULL"), FREE);
16414 SAVE_PTR(new->physdir,
16415 xasprintf("physdir '%s'", new->physdir ?: "NULL"), FREE);
16416 SAVE_PTR(new->arg0, xasprintf("arg0 '%s'", p->arg0 ?: "NULL"), FREE);
16417 SAVE_PTR(new->commandname,
16418 xasprintf("commandname '%s'", p->commandname ?: "NULL"), FREE);
16419 return new;
16420}
16421
16422static struct datasize
16423forkshell_size(struct forkshell *fs)
16424{
16425 struct datasize ds = {0, 0};
16426
16427 ds.funcstringsize += align_len(fs->path);
16428 if (fs->fpid == FS_OPENHERE)
16429 return ds;
16430
16431 ds = globals_var_size(ds, ash_ptr_to_globals_var);
16432 ds = globals_misc_size(ds, ash_ptr_to_globals_misc);
16433 ds = cmdtable_size(ds, cmdtable);
16434
16435 ds.funcblocksize = calcsize(ds.funcblocksize, fs->n);
16436 ds = argv_size(ds, fs->argv);
16437
16438 if ((ENABLE_ASH_ALIAS || MAX_HISTORY) && fs->fpid != FS_SHELLEXEC) {
16439#if ENABLE_ASH_ALIAS
16440 ds = atab_size(ds, atab);
16441#endif
16442#if MAX_HISTORY
16443 if (line_input_state)
16444 ds = history_size(ds, line_input_state);
16445#endif
16446 }
16447 return ds;
16448}
16449
16450static void
16451forkshell_copy(struct forkshell *fs, struct forkshell *new)
16452{
16453 memcpy(new, fs, sizeof(struct forkshell)); /* non-pointer stuff */
16454
16455 new->path = nodeckstrdup(fs->path);
16456 SAVE_PTR(new->path, xasprintf("path '%s'", fs->path ?: "NULL"), FREE);
16457 if (fs->fpid == FS_OPENHERE)
16458 return;
16459
16460 new->gvp = globals_var_copy(ash_ptr_to_globals_var);
16461 new->gmp = globals_misc_copy(ash_ptr_to_globals_misc);
16462 new->cmdtable = cmdtable_copy(cmdtable);
16463 SAVE_PTR(new->gvp, "gvp", NO_FREE);
16464 SAVE_PTR(new->gmp, "gmp", NO_FREE);
16465 SAVE_PTR(new->cmdtable, "cmdtable", NO_FREE);
16466
16467 new->n = copynode(fs->n);
16468 new->argv = argv_copy(fs->argv);
16469 SAVE_PTR(new->n, "n", NO_FREE);
16470 SAVE_PTR(new->argv, "argv", NO_FREE);
16471
16472 if ((ENABLE_ASH_ALIAS || MAX_HISTORY) && fs->fpid != FS_SHELLEXEC) {
16473#if ENABLE_ASH_ALIAS
16474 new->atab = atab_copy(atab);
16475 SAVE_PTR(new->atab, "atab", NO_FREE);
16476#endif
16477#if MAX_HISTORY
16478 if (line_input_state) {
16479 new->history = history_copy(line_input_state);
16480 SAVE_PTR(new->history, "history", NO_FREE);
16481 new->cnt_history = line_input_state->cnt_history;
16482 }
16483#endif
16484 }
16485}
16486
16487#if FORKSHELL_DEBUG
16488/* fp and notes can each be NULL */
16489#define NUM_BLOCKS 7
16490static void
16491forkshell_print(FILE *fp0, struct forkshell *fs, const char **notes)
16492{
16493 FILE *fp;
16494 void *lfuncblock;
16495 char *lfuncstring;
16496 char *lrelocate;
16497 char *s;
16498 int count, i, total;
16499 int size[NUM_BLOCKS];
16500 char *lptr[NUM_BLOCKS+1];
16501 enum {GVP, GMP, CMDTABLE, NODE, ARGV, ATAB, HISTORY, FUNCSTRING};
16502 const char *fsname[] = {
16503 "FS_OPENHERE",
16504 "FS_EVALBACKCMD",
16505 "FS_EVALSUBSHELL",
16506 "FS_EVALPIPE",
16507 "FS_SHELLEXEC"
16508 };
16509
16510 if (fp0 != NULL) {
16511 fp = fp0;
16512 }
16513 else {
16514 char name[64];
16515 static int num = 0;
16516
16517 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
16518 if ((fp=fopen(name, "w")) == NULL)
16519 return;
16520 }
16521
16522 total = sizeof(struct forkshell) + fs->funcblocksize +
16523 fs->funcstringsize + fs->relocatesize;
16524 fprintf(fp, "total size %6d = %d + %d + %d + %d = %d\n",
16525 fs->size + fs->relocatesize,
16526 (int)sizeof(struct forkshell), fs->funcblocksize,
16527 fs->funcstringsize, fs->relocatesize, total);
16528
16529 lfuncblock = (char *)(fs + 1);
16530 lfuncstring = (char *)lfuncblock + fs->funcblocksize;
16531 lrelocate = (char *)lfuncstring + fs->funcstringsize;
16532
16533 /* funcblocksize is zero for FS_OPENHERE */
16534 if (fs->funcblocksize != 0) {
16535 /* Depending on the configuration and the type of forkshell
16536 * some items may not be present. */
16537 lptr[FUNCSTRING] = lfuncstring;
16538#if MAX_HISTORY
16539 lptr[HISTORY] = fs->history ? (char *)fs->history : lptr[FUNCSTRING];
16540#else
16541 lptr[HISTORY] = lptr[FUNCSTRING];
16542#endif
16543 lptr[ATAB] = IF_ASH_ALIAS(fs->atab ? (char *)fs->atab :) lptr[HISTORY];
16544 lptr[ARGV] = fs->argv ? (char *)fs->argv : lptr[ATAB];
16545 lptr[NODE] = fs->n ? (char *)fs->n : lptr[ARGV];
16546 lptr[CMDTABLE] = (char *)fs->cmdtable;
16547 lptr[GMP] = (char *)fs->gmp;
16548 lptr[GVP] = (char *)fs->gvp;
16549
16550 fprintf(fp, "funcblocksize %6d = ", fs->funcblocksize);
16551 total = 0;
16552 for (i=0; i<NUM_BLOCKS; ++i) {
16553 size[i] = (int)(lptr[i+1] - lptr[i]);
16554 total += size[i];
16555 fprintf(fp, "%d %c ", size[i], i == NUM_BLOCKS - 1 ? '=' : '+');
16556 }
16557 fprintf(fp, "%d\n\n", total);
16558 }
16559 else {
16560 fprintf(fp, "\n");
16561 }
16562
16563 fprintf(fp, "%s\n\n", fsname[fs->fpid]);
16564 fprintf(fp, "--- relocate ---\n");
16565 count = 0;
16566 for (i = 0; i < fs->relocatesize; ++i) {
16567 if (lrelocate[i]) {
16568 char **ptr = (char **)((char *)fs + i);
16569 fprintf(fp, "%p %p %s\n", ptr, *ptr,
16570 notes && notes[i] ? notes[i] : "");
16571 ++count;
16572 }
16573 }
16574 fprintf(fp, "--- %d relocations ---\n\n", count);
16575
16576 fprintf(fp, "--- funcstring ---\n");
16577 count = 0;
16578 s = lfuncstring;
16579 while (s-lfuncstring < fs->funcstringsize) {
16580 if (!*s) {
16581 ++s;
16582 continue;
16583 }
16584 fprintf(fp, "%p '%s'\n", s, s);
16585 s += strlen(s)+1;
16586 ++count;
16587 }
16588 fprintf(fp, "--- %d strings ---\n", count);
16589
16590 if (fp0 == NULL)
16591 fclose(fp);
16592}
16593#endif
16594
16595static struct forkshell *
16596forkshell_prepare(struct forkshell *fs)
16597{
16598 struct forkshell *new;
16599 struct datasize ds;
16600 int size, relocatesize;
16601 HANDLE h;
16602 SECURITY_ATTRIBUTES sa;
16603#if FORKSHELL_DEBUG
16604 char *relocate;
16605 char name[64];
16606 FILE *fp;
16607 static int num = 0;
16608#endif
16609
16610 /* calculate size of structure, funcblock and funcstring */
16611 ds = forkshell_size(fs);
16612 size = sizeof(struct forkshell) + ds.funcblocksize + ds.funcstringsize;
16613 relocatesize = sizeof(struct forkshell) + ds.funcblocksize;
16614
16615 /* Allocate shared memory region */
16616 memset(&sa, 0, sizeof(sa));
16617 sa.nLength = sizeof(sa);
16618 sa.lpSecurityDescriptor = NULL;
16619 sa.bInheritHandle = TRUE;
16620 h = CreateFileMapping(INVALID_HANDLE_VALUE, &sa, PAGE_READWRITE, 0,
16621 size+relocatesize, NULL);
16622
16623 /* Initialise pointers */
16624 new = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
16625 if (new == NULL)
16626 return NULL;
16627 fs_size = size;
16628 funcblock = (char *)(new + 1);
16629 funcstring_end = (char *)new + size;
16630#if FORKSHELL_DEBUG
16631 fs_start = new;
16632 relocate = (char *)new + size;
16633 annot = (const char **)xzalloc(sizeof(char *)*relocatesize);
16634#endif
16635
16636 /* Now pack them all */
16637 forkshell_copy(fs, new);
16638
16639 /* Finish it up */
16640 new->size = size;
16641 new->relocatesize = relocatesize;
16642 new->old_base = (char *)new;
16643 new->hMapFile = h;
16644#if FORKSHELL_DEBUG
16645 sprintf(name, "fs_%d_%03d.out", getpid(), ++num % 100);
16646 if ((fp=fopen(name, "w")) != NULL) {
16647 int i;
16648
16649 new->funcblocksize = (char *)funcblock - (char *)(new + 1);
16650 new->funcstringsize = (char *)new + size - funcstring_end;
16651
16652 /* perform some sanity checks on pointers */
16653 fprintf(fp, "forkshell %p %6d\n", new, (int)sizeof(*new));
16654 fprintf(fp, "funcblock %p %6d\n", new+1, new->funcblocksize);
16655 fprintf(fp, "funcstring %p %6d\n", funcstring_end,
16656 new->funcstringsize);
16657 if ((char *)funcblock != funcstring_end)
16658 fprintf(fp, " funcstring != end funcblock + 1 %p\n", funcblock);
16659 fprintf(fp, "relocate %p %6d\n\n", relocate, new->relocatesize);
16660
16661 forkshell_print(fp, new, annot);
16662
16663 for (i = 0; i < relocatesize; ++i) {
16664 /* check relocations are only present for structure and funcblock */
16665 if (i >= sizeof(*new)+new->funcblocksize && annot[i] != NULL) {
16666 fprintf(fp, "\nnon-NULL annotation at offset %d (> %d) %s\n",
16667 i, (int)sizeof(*new)+new->funcblocksize, annot[i]);
16668 break;
16669 }
16670 if (relocate[i] == FREE) {
16671 free((void *)annot[i]);
16672 }
16673 }
16674 free(annot);
16675 annot = NULL;
16676 fclose(fp);
16677 }
16678#endif
16679 return new;
16680}
16681
16682#undef trap
16683#undef trap_ptr
16684static void
16685forkshell_init(const char *idstr)
16686{
16687 struct forkshell *fs;
16688 void *map_handle;
16689 HANDLE h;
16690 struct globals_var **gvpp;
16691 struct globals_misc **gmpp;
16692 int i;
16693 char **ptr;
16694 char *lrelocate;
16695 struct jmploc jmploc;
16696
16697 if (sscanf(idstr, "%p", &map_handle) != 1)
16698 return;
16699
16700 h = (HANDLE)map_handle;
16701 fs = (struct forkshell *)MapViewOfFile(h, FILE_MAP_WRITE, 0,0, 0);
16702 if (!fs)
16703 return;
16704
16705 /* this memory can't be freed */
16706 sticky_mem_start = fs;
16707 sticky_mem_end = (char *) fs + fs->size;
16708
16709 /* pointer fixup */
16710 lrelocate = (char *)fs + fs->size;
16711 for (i = 0; i < fs->relocatesize; i++) {
16712 if (lrelocate[i]) {
16713 ptr = (char **)((char *)fs + i);
16714 if (*ptr)
16715 *ptr = (char *)fs + (*ptr - fs->old_base);
16716 }
16717 }
16718
16719 if (fs->fpid == FS_OPENHERE)
16720 goto end;
16721
16722 /* Now fix up stuff that can't be transferred */
16723 for (i = 0; i < CMDTABLESIZE; i++) {
16724 struct tblentry *e = fs->cmdtable[i];
16725 while (e) {
16726 if (e->cmdtype == CMDBUILTIN)
16727 e->param.cmd = builtintab + e->param.index;
16728 e = e->next;
16729 }
16730 }
16731 memset(fs->gmp->trap, 0, sizeof(fs->gmp->trap[0])*NSIG);
16732 /* fs->gmp->trap_ptr = fs->gmp->trap; */
16733
16734 /* Set global variables */
16735 gvpp = (struct globals_var **)&ash_ptr_to_globals_var;
16736 *gvpp = fs->gvp;
16737 gmpp = (struct globals_misc **)&ash_ptr_to_globals_misc;
16738 *gmpp = fs->gmp;
16739 cmdtable = fs->cmdtable;
16740#if ENABLE_ASH_ALIAS
16741 atab = fs->atab; /* will be NULL for FS_SHELLEXEC */
16742#endif
16743#if MAX_HISTORY
16744 if (fs->cnt_history) {
16745 line_input_state = new_line_input_t(FOR_SHELL);
16746 line_input_state->cnt_history = fs->cnt_history;
16747 for (i = 0; i < line_input_state->cnt_history; i++)
16748 line_input_state->history[i] = fs->history[i];
16749 }
16750#endif
16751
16752 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
16753
16754 reinitvar();
16755
16756 if (setjmp(jmploc.loc)) {
16757 exitreset();
16758 exitshell();
16759 }
16760 exception_handler = &jmploc;
16761
16762 shlvl++;
16763 if (fs->mode == FORK_BG) {
16764 SetConsoleCtrlHandler(NULL, TRUE);
16765 if (fs->nprocs == 0) {
16766 close(0);
16767 if (open(bb_dev_null, O_RDONLY) != 0)
16768 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
16769 }
16770 }
16771 else {
16772 SetConsoleCtrlHandler(ctrl_handler, TRUE);
16773 }
16774#if JOBS_WIN32
16775 /* do job control only in root shell */
16776 doing_jobctl = 0;
16777#endif
16778 end:
16779 forkshell_child(fs);
16780}
16781
16782#undef free
16783static void
16784sticky_free(void *base)
16785{
16786 if (base >= sticky_mem_start && base < sticky_mem_end)
16787 return;
16788 free(base);
16789}
16790#endif
14731 16791
14732/*- 16792/*-
14733 * Copyright (c) 1989, 1991, 1993, 1994 16793 * 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 2e36d9208..fff356c04 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;
@@ -177,6 +197,7 @@ shell_builtin_read(struct builtin_read_params *params)
177 if ((bufpos & 0xff) == 0) 197 if ((bufpos & 0xff) == 0)
178 buffer = xrealloc(buffer, bufpos + 0x101); 198 buffer = xrealloc(buffer, bufpos + 0x101);
179 199
200 IF_PLATFORM_MINGW32(loop:)
180 timeout = -1; 201 timeout = -1;
181 if (params->opt_t) { 202 if (params->opt_t) {
182 timeout = end_ms - (unsigned)monotonic_ms(); 203 timeout = end_ms - (unsigned)monotonic_ms();
@@ -190,6 +211,7 @@ shell_builtin_read(struct builtin_read_params *params)
190 } 211 }
191 } 212 }
192 213
214#if !ENABLE_PLATFORM_MINGW32
193 /* We must poll even if timeout is -1: 215 /* We must poll even if timeout is -1:
194 * we want to be interrupted if signal arrives, 216 * we want to be interrupted if signal arrives,
195 * regardless of SA_RESTART-ness of that signal! 217 * regardless of SA_RESTART-ness of that signal!
@@ -207,8 +229,52 @@ shell_builtin_read(struct builtin_read_params *params)
207 retval = (const char *)(uintptr_t)1; 229 retval = (const char *)(uintptr_t)1;
208 break; 230 break;
209 } 231 }
232#else
233 errno = 0;
234 if (isatty(fd)) {
235 int64_t key;
236
237 key = read_key(fd, NULL, timeout);
238 if (key == 0x03) {
239 /* ^C pressed */
240 retval = (const char *)(uintptr_t)2;
241 goto ret;
242 }
243 else if (key == -1 || (key == 0x1a && bufpos == 0)) {
244 /* timeout or ^Z at start of buffer */
245 retval = (const char *)(uintptr_t)1;
246 goto ret;
247 }
248 else if (key == '\b') {
249 if (bufpos > 0) {
250 --bufpos;
251 ++nchars;
252 if (!(read_flags & BUILTIN_READ_SILENT)) {
253 printf("\b \b");
254 }
255 }
256 goto loop;
257 }
258 buffer[bufpos] = key == '\r' ? '\n' : key;
259 if (!(read_flags & BUILTIN_READ_SILENT)) {
260 /* echo input if not in silent mode */
261 putchar(buffer[bufpos]);
262 }
263 }
264 else {
265 if (read(fd, &buffer[bufpos], 1) != 1) {
266 err = errno;
267 retval = (const char *)(uintptr_t)1;
268 break;
269 }
270 }
271#endif
210 272
211 c = buffer[bufpos]; 273 c = buffer[bufpos];
274#if ENABLE_PLATFORM_MINGW32
275 if (c == '\r')
276 continue;
277#endif
212 if (!(read_flags & BUILTIN_READ_RAW)) { 278 if (!(read_flags & BUILTIN_READ_RAW)) {
213 if (backslash) { 279 if (backslash) {
214 backslash = 0; 280 backslash = 0;
@@ -309,8 +375,10 @@ shell_builtin_read(struct builtin_read_params *params)
309 375
310 ret: 376 ret:
311 free(buffer); 377 free(buffer);
378#if !ENABLE_PLATFORM_MINGW32
312 if (read_flags & BUILTIN_READ_SILENT) 379 if (read_flags & BUILTIN_READ_SILENT)
313 tcsetattr(fd, TCSANOW, &old_tty); 380 tcsetattr(fd, TCSANOW, &old_tty);
381#endif
314 382
315 errno = err; 383 errno = err;
316 return retval; 384 return retval;
@@ -319,6 +387,7 @@ shell_builtin_read(struct builtin_read_params *params)
319 387
320/* ulimit builtin */ 388/* ulimit builtin */
321 389
390#if !ENABLE_PLATFORM_MINGW32
322struct limits { 391struct limits {
323 uint8_t cmd; /* RLIMIT_xxx fit into it */ 392 uint8_t cmd; /* RLIMIT_xxx fit into it */
324 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */ 393 uint8_t factor_shift; /* shift by to get rlim_{cur,max} values */
@@ -687,3 +756,9 @@ shell_builtin_ulimit(char **argv)
687 756
688 return EXIT_SUCCESS; 757 return EXIT_SUCCESS;
689} 758}
759#else
760int FAST_FUNC shell_builtin_ulimit(char **argv UNUSED_PARAM)
761{
762 return 1;
763}
764#endif
diff --git a/testsuite/busybox.tests b/testsuite/busybox.tests
index beb17440c..2ce09b281 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..adbcb1178 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 63b005c67..5f8e60cac 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..9f486d5e9
--- /dev/null
+++ b/win32/Kbuild
@@ -0,0 +1,31 @@
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) += dirent.o
8lib-$(CONFIG_PLATFORM_MINGW32) += env.o
9lib-$(CONFIG_PLATFORM_MINGW32) += fnmatch.o
10lib-$(CONFIG_PLATFORM_MINGW32) += fsync.o
11lib-$(CONFIG_PLATFORM_MINGW32) += inet_pton.o
12lib-$(CONFIG_PLATFORM_MINGW32) += ioctl.o
13lib-$(CONFIG_FEATURE_PRNG_ISAAC) += isaac.o
14lib-$(CONFIG_PLATFORM_MINGW32) += mingw.o
15lib-$(CONFIG_PLATFORM_MINGW32) += process.o
16lib-$(CONFIG_PLATFORM_MINGW32) += match_class.o
17lib-$(CONFIG_PLATFORM_MINGW32) += mntent.o
18lib-$(CONFIG_PLATFORM_MINGW32) += net.o
19lib-$(CONFIG_PLATFORM_MINGW32) += poll.o
20lib-$(CONFIG_PLATFORM_MINGW32) += popen.o
21lib-$(CONFIG_PLATFORM_MINGW32) += regex.o
22lib-$(CONFIG_PLATFORM_MINGW32) += select.o
23lib-$(CONFIG_FEATURE_PRNG_SHELL) += sh_random.o
24lib-$(CONFIG_PLATFORM_MINGW32) += statfs.o
25lib-$(CONFIG_PLATFORM_MINGW32) += strndup.o
26lib-$(CONFIG_PLATFORM_MINGW32) += strptime.o
27lib-$(CONFIG_PLATFORM_MINGW32) += system.o
28lib-$(CONFIG_PLATFORM_MINGW32) += termios.o
29lib-$(CONFIG_PLATFORM_MINGW32) += timegm.o
30lib-$(CONFIG_PLATFORM_MINGW32) += uname.o
31lib-$(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/dirent.c b/win32/dirent.c
new file mode 100644
index 000000000..b7de19391
--- /dev/null
+++ b/win32/dirent.c
@@ -0,0 +1,96 @@
1#include "libbb.h"
2
3struct DIR {
4 struct dirent dd_dir;
5 HANDLE dd_handle; /* FindFirstFile handle */
6 int dd_stat; /* 0-based index */
7};
8
9static inline void finddata2dirent(struct dirent *ent, WIN32_FIND_DATAA *fdata)
10{
11 /* copy file name from WIN32_FIND_DATA to dirent */
12 strcpy(ent->d_name, fdata->cFileName);
13}
14
15DIR *opendir(const char *name)
16{
17 char pattern[MAX_PATH];
18 WIN32_FIND_DATAA fdata;
19 HANDLE h;
20 int len;
21 DIR *dir;
22
23 /* check that name is not NULL */
24 if (!name) {
25 errno = EINVAL;
26 return NULL;
27 }
28 /* check that the pattern won't be too long for FindFirstFileA */
29 len = strlen(name);
30 if (len + 2 >= MAX_PATH) {
31 errno = ENAMETOOLONG;
32 return NULL;
33 }
34 /* copy name to temp buffer */
35 strcpy(pattern, name);
36
37 /* append optional '/' and wildcard '*' */
38 if (len && !is_dir_sep(pattern[len - 1]))
39 pattern[len++] = '/';
40 pattern[len++] = '*';
41 pattern[len] = 0;
42
43 /* open find handle */
44 h = FindFirstFileA(pattern, &fdata);
45 if (h == INVALID_HANDLE_VALUE) {
46 DWORD err = GetLastError();
47 errno = (err == ERROR_DIRECTORY) ? ENOTDIR : err_win_to_posix();
48 return NULL;
49 }
50
51 /* initialize DIR structure and copy first dir entry */
52 dir = xmalloc(sizeof(DIR));
53 dir->dd_handle = h;
54 dir->dd_stat = 0;
55 finddata2dirent(&dir->dd_dir, &fdata);
56 return dir;
57}
58
59struct dirent *readdir(DIR *dir)
60{
61 if (!dir) {
62 errno = EBADF; /* No set_errno for mingw */
63 return NULL;
64 }
65
66 /* if first entry, dirent has already been set up by opendir */
67 if (dir->dd_stat) {
68 /* get next entry and convert from WIN32_FIND_DATA to dirent */
69 WIN32_FIND_DATAA fdata;
70 if (FindNextFileA(dir->dd_handle, &fdata)) {
71 finddata2dirent(&dir->dd_dir, &fdata);
72 } else {
73 DWORD lasterr = GetLastError();
74 /* POSIX says you shouldn't set errno when readdir can't
75 find any more files; so, if another error we leave it set. */
76 if (lasterr != ERROR_NO_MORE_FILES)
77 errno = err_win_to_posix();
78 return NULL;
79 }
80 }
81
82 ++dir->dd_stat;
83 return &dir->dd_dir;
84}
85
86int closedir(DIR *dir)
87{
88 if (!dir) {
89 errno = EBADF;
90 return -1;
91 }
92
93 FindClose(dir->dd_handle);
94 free(dir);
95 return 0;
96}
diff --git a/win32/dirent.h b/win32/dirent.h
new file mode 100644
index 000000000..b38d0d133
--- /dev/null
+++ b/win32/dirent.h
@@ -0,0 +1,19 @@
1#ifndef DIRENT_H
2#define DIRENT_H
3
4typedef struct DIR DIR;
5
6#define DT_UNKNOWN 0
7#define DT_DIR 1
8#define DT_REG 2
9#define DT_LNK 3
10
11struct dirent {
12 char d_name[PATH_MAX]; // file name
13};
14
15DIR *opendir(const char *dirname);
16struct dirent *readdir(DIR *dir);
17int closedir(DIR *dir);
18
19#endif /* DIRENT_H */
diff --git a/win32/env.c b/win32/env.c
new file mode 100644
index 000000000..4d4e9c8fd
--- /dev/null
+++ b/win32/env.c
@@ -0,0 +1,107 @@
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 = xasprintf("%s=", name);
53 ret = _putenv(envstr);
54 free(envstr);
55
56 return ret;
57}
58
59int clearenv(void)
60{
61 char *envp, *name, *s;
62
63 while ( environ && (envp=*environ) ) {
64 if ( (s=strchr(envp, '=')) != NULL ) {
65 name = xstrndup(envp, s-envp+1);
66 if (_putenv(name) == -1) {
67 free(name);
68 return -1;
69 }
70 free(name);
71 }
72 else {
73 return -1;
74 }
75 }
76 return 0;
77}
78
79int mingw_putenv(const char *env)
80{
81 char *s, **envp;
82 int ret = 0;
83
84 if ( (s=strchr(env, '=')) == NULL ) {
85 return unsetenv(env);
86 }
87
88 if (s[1] != '\0') {
89 /* setting non-empty value is fine */
90 return _putenv(env);
91 }
92 else {
93 /* set empty value by setting a non-empty one then truncating */
94 char *envstr = xasprintf("%s0", env);
95 ret = _putenv(envstr);
96
97 for (envp = environ; *envp; ++envp) {
98 if (strcmp(*envp, envstr) == 0) {
99 (*envp)[s - env + 1] = '\0';
100 break;
101 }
102 }
103 free(envstr);
104 }
105
106 return ret;
107}
diff --git a/win32/fnmatch.c b/win32/fnmatch.c
new file mode 100644
index 000000000..7d8fde6a2
--- /dev/null
+++ b/win32/fnmatch.c
@@ -0,0 +1,522 @@
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#include "match_class.h"
21
22#if HAVE_CONFIG_H
23# include <config.h>
24#endif
25
26/* Enable GNU extensions in fnmatch.h. */
27#ifndef _GNU_SOURCE
28# define _GNU_SOURCE 1
29#endif
30
31#include <errno.h>
32#include <fnmatch.h>
33#include <ctype.h>
34
35#if HAVE_STRING_H || defined _LIBC
36# include <string.h>
37#else
38# include <strings.h>
39#endif
40
41#if defined STDC_HEADERS || defined _LIBC
42# include <stdlib.h>
43#endif
44
45/* For platform which support the ISO C amendement 1 functionality we
46 support user defined character classes. */
47#if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
48/* Solaris 2.5 has a bug: <wchar.h> must be included before <wctype.h>. */
49# include <wchar.h>
50# include <wctype.h>
51#endif
52
53/* Comment out all this code if we are using the GNU C Library, and are not
54 actually compiling the library itself. This code is part of the GNU C
55 Library, but also included in many other GNU distributions. Compiling
56 and linking in this code is a waste when using the GNU C library
57 (especially if it is a shared library). Rather than having every GNU
58 program understand `configure --with-gnu-libc' and omit the object files,
59 it is simpler to just do this in the source for each such file. */
60
61#if defined _LIBC || !defined __GNU_LIBRARY__
62
63
64# if defined STDC_HEADERS || !defined isascii
65# define ISASCII(c) 1
66# else
67# define ISASCII(c) isascii(c)
68# endif
69
70# ifdef isblank
71# define ISBLANK(c) (ISASCII (c) && isblank (c))
72# else
73# define ISBLANK(c) ((c) == ' ' || (c) == '\t')
74# endif
75# ifdef isgraph
76# define ISGRAPH(c) (ISASCII (c) && isgraph (c))
77# else
78# define ISGRAPH(c) (ISASCII (c) && isprint (c) && !isspace (c))
79# endif
80
81# define ISPRINT(c) (ISASCII (c) && isprint (c))
82# define ISDIGIT(c) (ISASCII (c) && isdigit (c))
83# define ISALNUM(c) (ISASCII (c) && isalnum (c))
84# define ISALPHA(c) (ISASCII (c) && isalpha (c))
85# define ISCNTRL(c) (ISASCII (c) && iscntrl (c))
86# define ISLOWER(c) (ISASCII (c) && islower (c))
87# define ISPUNCT(c) (ISASCII (c) && ispunct (c))
88# define ISSPACE(c) (ISASCII (c) && isspace (c))
89# define ISUPPER(c) (ISASCII (c) && isupper (c))
90# define ISXDIGIT(c) (ISASCII (c) && isxdigit (c))
91
92# define STREQ(s1, s2) ((strcmp (s1, s2) == 0))
93
94# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
95/* The GNU C library provides support for user-defined character classes
96 and the functions from ISO C amendement 1. */
97# ifdef CHARCLASS_NAME_MAX
98# define CHAR_CLASS_MAX_LENGTH CHARCLASS_NAME_MAX
99# else
100/* This shouldn't happen but some implementation might still have this
101 problem. Use a reasonable default value. */
102# define CHAR_CLASS_MAX_LENGTH 256
103# endif
104
105# ifdef _LIBC
106# define IS_CHAR_CLASS(string) __wctype (string)
107# else
108# define IS_CHAR_CLASS(string) wctype (string)
109# endif
110# else
111# define CHAR_CLASS_MAX_LENGTH 7 /* Namely, `xdigit'. */
112
113# define IS_CHAR_CLASS(string) \
114 (STREQ (string, "alpha") || STREQ (string, "upper") \
115 || STREQ (string, "lower") || STREQ (string, "digit") \
116 || STREQ (string, "alnum") || STREQ (string, "xdigit") \
117 || STREQ (string, "space") || STREQ (string, "print") \
118 || STREQ (string, "punct") || STREQ (string, "graph") \
119 || STREQ (string, "cntrl") || STREQ (string, "blank"))
120# endif
121
122/* Avoid depending on library functions or files
123 whose names are inconsistent. */
124
125# if !defined _LIBC && !defined getenv
126extern char *getenv (const char *);
127# endif
128
129# ifndef errno
130extern int errno;
131# endif
132
133/* This function doesn't exist on most systems. */
134
135# if !defined HAVE___STRCHRNUL && !defined _LIBC && 0
136static char *
137__strchrnul (const char *s, int c)
138{
139 char *result = strchr (s, c);
140 if (result == NULL)
141 result = strchr (s, '\0');
142 return result;
143}
144# else
145# define __strchrnul strchrnul
146# endif
147
148# ifndef internal_function
149/* Inside GNU libc we mark some function in a special way. In other
150 environments simply ignore the marking. */
151# define internal_function
152# endif
153
154/* Match STRING against the filename pattern PATTERN, returning zero if
155 it matches, nonzero if not. */
156static int internal_fnmatch __P ((const char *pattern, const char *string,
157 int no_leading_period, int flags))
158 internal_function;
159static int
160internal_function
161internal_fnmatch (const char *pattern, const char *string,
162 int no_leading_period, int flags)
163{
164 register const char *p = pattern, *n = string;
165 register unsigned char c;
166
167/* Note that this evaluates C many times. */
168# ifdef _LIBC
169# define FOLD(c) ((flags & FNM_CASEFOLD) ? tolower (c) : (c))
170# else
171# define FOLD(c) ((flags & FNM_CASEFOLD) && ISUPPER (c) ? tolower (c) : (c))
172# endif
173
174 while ((c = *p++) != '\0')
175 {
176 c = FOLD (c);
177
178 switch (c)
179 {
180 case '?':
181 if (*n == '\0')
182 return FNM_NOMATCH;
183 else if (*n == '/' && (flags & FNM_FILE_NAME))
184 return FNM_NOMATCH;
185 else if (*n == '.' && no_leading_period
186 && (n == string
187 || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
188 return FNM_NOMATCH;
189 break;
190
191 case '\\':
192 if (!(flags & FNM_NOESCAPE))
193 {
194 c = *p++;
195 if (c == '\0')
196 /* Trailing \ loses. */
197 return FNM_NOMATCH;
198 c = FOLD (c);
199 }
200 if (FOLD ((unsigned char) *n) != c)
201 return FNM_NOMATCH;
202 break;
203
204 case '*':
205 if (*n == '.' && no_leading_period
206 && (n == string
207 || (n[-1] == '/' && (flags & FNM_FILE_NAME))))
208 return FNM_NOMATCH;
209
210 for (c = *p++; c == '?' || c == '*'; c = *p++)
211 {
212 if (*n == '/' && (flags & FNM_FILE_NAME))
213 /* A slash does not match a wildcard under FNM_FILE_NAME. */
214 return FNM_NOMATCH;
215 else if (c == '?')
216 {
217 /* A ? needs to match one character. */
218 if (*n == '\0')
219 /* There isn't another character; no match. */
220 return FNM_NOMATCH;
221 else
222 /* One character of the string is consumed in matching
223 this ? wildcard, so *??? won't match if there are
224 less than three characters. */
225 ++n;
226 }
227 }
228
229 if (c == '\0')
230 /* The wildcard(s) is/are the last element of the pattern.
231 If the name is a file name and contains another slash
232 this does mean it cannot match. */
233 return ((flags & FNM_FILE_NAME) && strchr (n, '/') != NULL
234 ? FNM_NOMATCH : 0);
235 else
236 {
237 const char *endp;
238
239 endp = __strchrnul (n, (flags & FNM_FILE_NAME) ? '/' : '\0');
240
241 if (c == '[')
242 {
243 int flags2 = ((flags & FNM_FILE_NAME)
244 ? flags : (flags & ~FNM_PERIOD));
245
246 for (--p; n < endp; ++n)
247 if (internal_fnmatch (p, n,
248 (no_leading_period
249 && (n == string
250 || (n[-1] == '/'
251 && (flags
252 & FNM_FILE_NAME)))),
253 flags2)
254 == 0)
255 return 0;
256 }
257 else if (c == '/' && (flags & FNM_FILE_NAME))
258 {
259 while (*n != '\0' && *n != '/')
260 ++n;
261 if (*n == '/'
262 && (internal_fnmatch (p, n + 1, flags & FNM_PERIOD,
263 flags) == 0))
264 return 0;
265 }
266 else
267 {
268 int flags2 = ((flags & FNM_FILE_NAME)
269 ? flags : (flags & ~FNM_PERIOD));
270
271 if (c == '\\' && !(flags & FNM_NOESCAPE))
272 c = *p;
273 c = FOLD (c);
274 for (--p; n < endp; ++n)
275 if (FOLD ((unsigned char) *n) == c
276 && (internal_fnmatch (p, n,
277 (no_leading_period
278 && (n == string
279 || (n[-1] == '/'
280 && (flags
281 & FNM_FILE_NAME)))),
282 flags2) == 0))
283 return 0;
284 }
285 }
286
287 /* If we come here no match is possible with the wildcard. */
288 return FNM_NOMATCH;
289
290 case '[':
291 {
292 /* Nonzero if the sense of the character class is inverted. */
293 static int posixly_correct;
294 register int not;
295 char cold;
296
297 if (posixly_correct == 0)
298 posixly_correct = getenv ("POSIXLY_CORRECT") != NULL ? 1 : -1;
299
300 if (*n == '\0')
301 return FNM_NOMATCH;
302
303 if (*n == '.' && no_leading_period && (n == string
304 || (n[-1] == '/'
305 && (flags
306 & FNM_FILE_NAME))))
307 return FNM_NOMATCH;
308
309 if (*n == '/' && (flags & FNM_FILE_NAME))
310 /* `/' cannot be matched. */
311 return FNM_NOMATCH;
312
313 not = (*p == '!' || (posixly_correct < 0 && *p == '^'));
314 if (not)
315 ++p;
316
317 c = *p++;
318 for (;;)
319 {
320 unsigned char fn = FOLD ((unsigned char) *n);
321
322 if (!(flags & FNM_NOESCAPE) && c == '\\')
323 {
324 if (*p == '\0')
325 return FNM_NOMATCH;
326 c = FOLD ((unsigned char) *p);
327 ++p;
328
329 if (c == fn)
330 goto matched;
331 }
332 else if (c == '[' && *p == ':')
333 {
334 /* Leave room for the null. */
335 char str[CHAR_CLASS_MAX_LENGTH + 1];
336 size_t c1 = 0;
337# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
338 wctype_t wt;
339# endif
340 const char *startp = p;
341
342 for (;;)
343 {
344 if (c1 == CHAR_CLASS_MAX_LENGTH)
345 /* The name is too long and therefore the pattern
346 is ill-formed. */
347 return FNM_NOMATCH;
348
349 c = *++p;
350 if (c == ':' && p[1] == ']')
351 {
352 p += 2;
353 break;
354 }
355 if (c < 'a' || c >= 'z')
356 {
357 /* This cannot possibly be a character class name.
358 Match it as a normal range. */
359 p = startp;
360 c = '[';
361 goto normal_bracket;
362 }
363 str[c1++] = c;
364 }
365 str[c1] = '\0';
366
367# if defined _LIBC || (defined HAVE_WCTYPE_H && defined HAVE_WCHAR_H)
368 wt = IS_CHAR_CLASS (str);
369 if (wt == 0)
370 /* Invalid character class name. */
371 return FNM_NOMATCH;
372
373 if (__iswctype (__btowc ((unsigned char) *n), wt))
374 goto matched;
375# else
376 switch (match_class(str)) {
377 case CCLASS_ALNUM:
378 if (ISALNUM ((unsigned char) *n))
379 goto matched;
380 break;
381 case CCLASS_ALPHA:
382 if (ISALPHA ((unsigned char) *n))
383 goto matched;
384 break;
385 case CCLASS_BLANK:
386 if (ISBLANK ((unsigned char) *n))
387 goto matched;
388 break;
389 case CCLASS_CNTRL:
390 if (ISCNTRL ((unsigned char) *n))
391 goto matched;
392 break;
393 case CCLASS_DIGIT:
394 if (ISDIGIT ((unsigned char) *n))
395 goto matched;
396 break;
397 case CCLASS_GRAPH:
398 if (ISGRAPH ((unsigned char) *n))
399 goto matched;
400 break;
401 case CCLASS_LOWER:
402 if (ISLOWER ((unsigned char) *n))
403 goto matched;
404 break;
405 case CCLASS_PRINT:
406 if (ISPRINT ((unsigned char) *n))
407 goto matched;
408 break;
409 case CCLASS_PUNCT:
410 if (ISPUNCT ((unsigned char) *n))
411 goto matched;
412 break;
413 case CCLASS_SPACE:
414 if (ISSPACE ((unsigned char) *n))
415 goto matched;
416 break;
417 case CCLASS_UPPER:
418 if (ISUPPER ((unsigned char) *n))
419 goto matched;
420 break;
421 case CCLASS_XDIGIT:
422 if (ISXDIGIT ((unsigned char) *n))
423 goto matched;
424 break;
425 }
426# endif
427 }
428 else if (c == '\0')
429 /* [ (unterminated) loses. */
430 return FNM_NOMATCH;
431 else
432 {
433 normal_bracket:
434 if (FOLD (c) == fn)
435 goto matched;
436
437 cold = c;
438 c = *p++;
439
440 if (c == '-' && *p != ']')
441 {
442 /* It is a range. */
443 unsigned char cend = *p++;
444 if (!(flags & FNM_NOESCAPE) && cend == '\\')
445 cend = *p++;
446 if (cend == '\0')
447 return FNM_NOMATCH;
448
449 if (cold <= fn && fn <= FOLD (cend))
450 goto matched;
451
452 c = *p++;
453 }
454 }
455
456 if (c == ']')
457 break;
458 }
459
460 if (!not)
461 return FNM_NOMATCH;
462 break;
463
464 matched:
465 /* Skip the rest of the [...] that already matched. */
466 while (c != ']')
467 {
468 if (c == '\0')
469 /* [... (unterminated) loses. */
470 return FNM_NOMATCH;
471
472 c = *p++;
473 if (!(flags & FNM_NOESCAPE) && c == '\\')
474 {
475 if (*p == '\0')
476 return FNM_NOMATCH;
477 /* XXX 1003.2d11 is unclear if this is right. */
478 ++p;
479 }
480 else if (c == '[' && *p == ':')
481 {
482 do
483 if (*++p == '\0')
484 return FNM_NOMATCH;
485 while (*p != ':' || p[1] == ']');
486 p += 2;
487 c = *p;
488 }
489 }
490 if (not)
491 return FNM_NOMATCH;
492 }
493 break;
494
495 default:
496 if (c != FOLD ((unsigned char) *n))
497 return FNM_NOMATCH;
498 }
499
500 ++n;
501 }
502
503 if (*n == '\0')
504 return 0;
505
506 if ((flags & FNM_LEADING_DIR) && *n == '/')
507 /* The FNM_LEADING_DIR flag says that "foo*" matches "foobar/frobozz". */
508 return 0;
509
510 return FNM_NOMATCH;
511
512# undef FOLD
513}
514
515
516int
517fnmatch (const char *pattern, const char *string, int flags)
518{
519 return internal_fnmatch (pattern, string, flags & FNM_PERIOD, flags);
520}
521
522#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..f229a9355
--- /dev/null
+++ b/win32/inet_pton.c
@@ -0,0 +1,95 @@
1/*
2 inet_pton from musl (https://www.musl-libc.org/).
3
4 MIT licensed:
5
6----------------------------------------------------------------------
7Copyright © 2005-2020 Rich Felker, et al.
8
9Permission is hereby granted, free of charge, to any person obtaining
10a copy of this software and associated documentation files (the
11"Software"), to deal in the Software without restriction, including
12without limitation the rights to use, copy, modify, merge, publish,
13distribute, sublicense, and/or sell copies of the Software, and to
14permit persons to whom the Software is furnished to do so, subject to
15the following conditions:
16
17The above copyright notice and this permission notice shall be
18included in all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27----------------------------------------------------------------------
28*/
29#include "libbb.h"
30
31static int hexval(unsigned c)
32{
33 if (c-'0'<10) return c-'0';
34 c |= 32;
35 if (c-'a'<6) return c-'a'+10;
36 return -1;
37}
38
39int inet_pton(int af, const char *restrict s, void *restrict a0)
40{
41 uint16_t ip[8];
42 unsigned char *a = a0;
43 int i, j, v, d, brk=-1, need_v4=0;
44
45 if (af==AF_INET) {
46 for (i=0; i<4; i++) {
47 for (v=j=0; j<3 && isdigit(s[j]); j++)
48 v = 10*v + s[j]-'0';
49 if (j==0 || (j>1 && s[0]=='0') || v>255) return 0;
50 a[i] = v;
51 if (s[j]==0 && i==3) return 1;
52 if (s[j]!='.') return 0;
53 s += j+1;
54 }
55 return 0;
56 } else if (af!=AF_INET6) {
57 errno = EAFNOSUPPORT;
58 return -1;
59 }
60
61 if (*s==':' && *++s!=':') return 0;
62
63 for (i=0; ; i++) {
64 if (s[0]==':' && brk<0) {
65 brk=i;
66 ip[i&7]=0;
67 if (!*++s) break;
68 if (i==7) return 0;
69 continue;
70 }
71 for (v=j=0; j<4 && (d=hexval(s[j]))>=0; j++)
72 v=16*v+d;
73 if (j==0) return 0;
74 ip[i&7] = v;
75 if (!s[j] && (brk>=0 || i==7)) break;
76 if (i==7) return 0;
77 if (s[j]!=':') {
78 if (s[j]!='.' || (i<6 && brk<0)) return 0;
79 need_v4=1;
80 i++;
81 break;
82 }
83 s += j+1;
84 }
85 if (brk>=0) {
86 memmove(ip+brk+7-i, ip+brk, 2*(i+1-brk));
87 for (j=0; j<7-i; j++) ip[brk+j] = 0;
88 }
89 for (j=0; j<8; j++) {
90 *a++ = ip[j]>>8;
91 *a++ = ip[j];
92 }
93 if (need_v4 && inet_pton(AF_INET, (void *)s, a-4) <= 0) return 0;
94 return 1;
95}
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..6b174b69e
--- /dev/null
+++ b/win32/isaac.c
@@ -0,0 +1,222 @@
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++ % 256] = *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;
132 FILETIME tm;
133 MEMORYSTATUS ms;
134 SYSTEM_INFO si;
135 LARGE_INTEGER pc;
136 uint32_t *u;
137 char *env, *s;
138 unsigned char *p;
139
140 i = 0;
141 t->randrsl[i++] = (uint32_t)GetProcessId(GetCurrentProcess());
142 t->randrsl[i++] = (uint32_t)GetCurrentThreadId();
143 t->randrsl[i++] = (uint32_t)GetTickCount();
144
145 GET_DATA(GetSystemTimeAsFileTime, tm)
146 GET_DATA(GlobalMemoryStatus, ms)
147 GET_DATA(GetSystemInfo, si)
148 GET_DATA(QueryPerformanceCounter, pc)
149
150 env = GetEnvironmentStringsA();
151
152 /* environment ends with two nuls */
153 p = (unsigned char *)t->randrsl;
154 i *= sizeof(uint32_t);
155 for (s=env; *s || *(s+1); ++s)
156 p[i++ % (256 * sizeof(uint32_t))] ^= *s;
157
158 FreeEnvironmentStringsA(env);
159
160#if 0
161 {
162 for (j=0; j<256; ++j) {
163 fprintf(stderr, "%02x", p[j]);
164 if ((j&31) == 31) {
165 fprintf(stderr, "\n");
166 }
167 else if ((j&3) == 3) {
168 fprintf(stderr, " ");
169 }
170 }
171 fprintf(stderr, "\n");
172 }
173#endif
174}
175
176#define RAND_BYTES sizeof(t->randrsl)
177#define RAND_WORDS (sizeof(t->randrsl)/sizeof(t->randrsl[0]))
178
179/*
180 * Place 'count' random bytes in the buffer 'buf'. You're responsible
181 * for ensuring the buffer is big enough.
182 */
183ssize_t get_random_bytes(void *buf, ssize_t count)
184{
185 static isaac_t *t = NULL;
186 static int rand_index = 0;
187 ssize_t save_count = count;
188 unsigned char *ptr;
189
190 if (buf == NULL || count < 0) {
191 errno = EINVAL;
192 return -1;
193 }
194
195 if (!t) {
196 t = xzalloc(sizeof(isaac_t));
197
198 get_entropy(t);
199 randinit(t, 1);
200 isaac(t);
201 rand_index = 0;
202 }
203
204 ptr = (unsigned char *)t->randrsl;
205 while (count > 0) {
206 int bytes_left = RAND_BYTES - rand_index;
207 ssize_t delta = MIN(bytes_left, count);
208
209 memcpy(buf, ptr+rand_index, delta);
210 buf += delta;
211 count -= delta;
212 rand_index += delta;
213
214 if (rand_index >= RAND_BYTES) {
215 /* generate more */
216 isaac(t);
217 rand_index = 0;
218 }
219 }
220
221 return save_count;
222}
diff --git a/win32/lazyload.h b/win32/lazyload.h
new file mode 100644
index 000000000..034bc7e45
--- /dev/null
+++ b/win32/lazyload.h
@@ -0,0 +1,27 @@
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
24void *get_proc_addr(const char *dll, const char *function,
25 struct proc_addr *proc);
26
27#endif
diff --git a/win32/match_class.c b/win32/match_class.c
new file mode 100644
index 000000000..789e0df02
--- /dev/null
+++ b/win32/match_class.c
@@ -0,0 +1,7 @@
1#include "libbb.h"
2#include "match_class.h"
3
4int match_class(const char *name)
5{
6 return index_in_strings(CHAR_CLASSES, name);
7}
diff --git a/win32/match_class.h b/win32/match_class.h
new file mode 100644
index 000000000..92fd1323f
--- /dev/null
+++ b/win32/match_class.h
@@ -0,0 +1,11 @@
1#define CHAR_CLASSES \
2 "alnum\0alpha\0blank\0cntrl\0digit\0graph\0" \
3 "lower\0print\0punct\0space\0upper\0xdigit\0"
4
5enum {
6 CCLASS_ALNUM, CCLASS_ALPHA, CCLASS_BLANK, CCLASS_CNTRL,
7 CCLASS_DIGIT, CCLASS_GRAPH, CCLASS_LOWER, CCLASS_PRINT,
8 CCLASS_PUNCT, CCLASS_SPACE, CCLASS_UPPER, CCLASS_XDIGIT
9};
10
11extern int match_class(const char *name);
diff --git a/win32/mingw.c b/win32/mingw.c
new file mode 100644
index 000000000..06da37040
--- /dev/null
+++ b/win32/mingw.c
@@ -0,0 +1,2107 @@
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#include <ntdef.h>
8#include <psapi.h>
9
10#if defined(__MINGW64_VERSION_MAJOR)
11#if ENABLE_GLOBBING
12extern int _setargv(void);
13int _setargv(void)
14{
15 extern int _dowildcard;
16 char *glob;
17
18 _dowildcard = -1;
19 glob = getenv("BB_GLOBBING");
20 if (glob) {
21 if (strcmp(glob, "0") == 0)
22 _dowildcard = 0;
23 }
24 else {
25 setenv("BB_GLOBBING", "0", TRUE);
26 }
27 return 0;
28}
29#else
30int _dowildcard = 0;
31#endif
32
33#undef _fmode
34int _fmode = _O_BINARY;
35#endif
36
37#if !defined(__MINGW64_VERSION_MAJOR)
38#if ENABLE_GLOBBING
39int _CRT_glob = 1;
40#else
41int _CRT_glob = 0;
42#endif
43
44unsigned int _CRT_fmode = _O_BINARY;
45#endif
46
47smallint bb_got_signal;
48
49#pragma GCC optimize ("no-if-conversion")
50int err_win_to_posix(void)
51{
52 int error = ENOSYS;
53 switch(GetLastError()) {
54 case ERROR_ACCESS_DENIED: error = EACCES; break;
55 case ERROR_ACCOUNT_DISABLED: error = EACCES; break;
56 case ERROR_ACCOUNT_RESTRICTION: error = EACCES; break;
57 case ERROR_ALREADY_ASSIGNED: error = EBUSY; break;
58 case ERROR_ALREADY_EXISTS: error = EEXIST; break;
59 case ERROR_ARITHMETIC_OVERFLOW: error = ERANGE; break;
60 case ERROR_BAD_COMMAND: error = EIO; break;
61 case ERROR_BAD_DEVICE: error = ENODEV; break;
62 case ERROR_BAD_DRIVER_LEVEL: error = ENXIO; break;
63 case ERROR_BAD_EXE_FORMAT: error = ENOEXEC; break;
64 case ERROR_BAD_FORMAT: error = ENOEXEC; break;
65 case ERROR_BAD_LENGTH: error = EINVAL; break;
66 case ERROR_BAD_PATHNAME: error = ENOENT; break;
67 case ERROR_BAD_NET_NAME: error = ENOENT; break;
68 case ERROR_BAD_NETPATH: error = ENOENT; break;
69 case ERROR_BAD_PIPE: error = EPIPE; break;
70 case ERROR_BAD_UNIT: error = ENODEV; break;
71 case ERROR_BAD_USERNAME: error = EINVAL; break;
72 case ERROR_BROKEN_PIPE: error = EPIPE; break;
73 case ERROR_BUFFER_OVERFLOW: error = ENAMETOOLONG; break;
74 case ERROR_BUSY: error = EBUSY; break;
75 case ERROR_BUSY_DRIVE: error = EBUSY; break;
76 case ERROR_CALL_NOT_IMPLEMENTED: error = ENOSYS; break;
77 case ERROR_CANNOT_MAKE: error = EACCES; break;
78 case ERROR_CANTOPEN: error = EIO; break;
79 case ERROR_CANTREAD: error = EIO; break;
80 case ERROR_CANTWRITE: error = EIO; break;
81 case ERROR_CRC: error = EIO; break;
82 case ERROR_CURRENT_DIRECTORY: error = EACCES; break;
83 case ERROR_DEVICE_IN_USE: error = EBUSY; break;
84 case ERROR_DEV_NOT_EXIST: error = ENODEV; break;
85 case ERROR_DIRECTORY: error = EINVAL; break;
86 case ERROR_DIR_NOT_EMPTY: error = ENOTEMPTY; break;
87 case ERROR_DISK_CHANGE: error = EIO; break;
88 case ERROR_DISK_FULL: error = ENOSPC; break;
89 case ERROR_DRIVE_LOCKED: error = EBUSY; break;
90 case ERROR_ENVVAR_NOT_FOUND: error = EINVAL; break;
91 case ERROR_EXE_MARKED_INVALID: error = ENOEXEC; break;
92 case ERROR_FILENAME_EXCED_RANGE: error = ENAMETOOLONG; break;
93 case ERROR_FILE_EXISTS: error = EEXIST; break;
94 case ERROR_FILE_INVALID: error = ENODEV; break;
95 case ERROR_FILE_NOT_FOUND: error = ENOENT; break;
96 case ERROR_GEN_FAILURE: error = EIO; break;
97 case ERROR_HANDLE_DISK_FULL: error = ENOSPC; break;
98 case ERROR_INSUFFICIENT_BUFFER: error = ENOMEM; break;
99 case ERROR_INVALID_ACCESS: error = EACCES; break;
100 case ERROR_INVALID_ADDRESS: error = EFAULT; break;
101 case ERROR_INVALID_BLOCK: error = EFAULT; break;
102 case ERROR_INVALID_DATA: error = EINVAL; break;
103 case ERROR_INVALID_DRIVE: error = ENODEV; break;
104 case ERROR_INVALID_EXE_SIGNATURE: error = ENOEXEC; break;
105 case ERROR_INVALID_FLAGS: error = EINVAL; break;
106 case ERROR_INVALID_FUNCTION: error = ENOSYS; break;
107 case ERROR_INVALID_HANDLE: error = EBADF; break;
108 case ERROR_INVALID_LOGON_HOURS: error = EACCES; break;
109 case ERROR_INVALID_NAME: error = EINVAL; break;
110 case ERROR_INVALID_OWNER: error = EINVAL; break;
111 case ERROR_INVALID_PARAMETER: error = EINVAL; break;
112 case ERROR_INVALID_PASSWORD: error = EPERM; break;
113 case ERROR_INVALID_PRIMARY_GROUP: error = EINVAL; break;
114 case ERROR_INVALID_SIGNAL_NUMBER: error = EINVAL; break;
115 case ERROR_INVALID_TARGET_HANDLE: error = EIO; break;
116 case ERROR_INVALID_WORKSTATION: error = EACCES; break;
117 case ERROR_IO_DEVICE: error = EIO; break;
118 case ERROR_IO_INCOMPLETE: error = EINTR; break;
119 case ERROR_LOCKED: error = EBUSY; break;
120 case ERROR_LOCK_VIOLATION: error = EACCES; break;
121 case ERROR_LOGON_FAILURE: error = EACCES; break;
122 case ERROR_MAPPED_ALIGNMENT: error = EINVAL; break;
123 case ERROR_META_EXPANSION_TOO_LONG: error = E2BIG; break;
124 case ERROR_MORE_DATA: error = EPIPE; break;
125 case ERROR_NEGATIVE_SEEK: error = ESPIPE; break;
126 case ERROR_NOACCESS: error = EFAULT; break;
127 case ERROR_NONE_MAPPED: error = EINVAL; break;
128 case ERROR_NOT_ENOUGH_MEMORY: error = ENOMEM; break;
129 case ERROR_NOT_READY: error = EAGAIN; break;
130 case ERROR_NOT_SAME_DEVICE: error = EXDEV; break;
131 case ERROR_NO_DATA: error = EPIPE; break;
132 case ERROR_NO_MORE_SEARCH_HANDLES: error = EIO; break;
133 case ERROR_NO_PROC_SLOTS: error = EAGAIN; break;
134 case ERROR_NO_SUCH_PRIVILEGE: error = EACCES; break;
135 case ERROR_OPEN_FAILED: error = EIO; break;
136 case ERROR_OPEN_FILES: error = EBUSY; break;
137 case ERROR_OPERATION_ABORTED: error = EINTR; break;
138 case ERROR_OUTOFMEMORY: error = ENOMEM; break;
139 case ERROR_PASSWORD_EXPIRED: error = EACCES; break;
140 case ERROR_PATH_BUSY: error = EBUSY; break;
141 case ERROR_PATH_NOT_FOUND: error = ENOENT; break;
142 case ERROR_PIPE_BUSY: error = EBUSY; break;
143 case ERROR_PIPE_CONNECTED: error = EPIPE; break;
144 case ERROR_PIPE_LISTENING: error = EPIPE; break;
145 case ERROR_PIPE_NOT_CONNECTED: error = EPIPE; break;
146 case ERROR_PRIVILEGE_NOT_HELD: error = EACCES; break;
147 case ERROR_READ_FAULT: error = EIO; break;
148 case ERROR_SEEK: error = EIO; break;
149 case ERROR_SEEK_ON_DEVICE: error = ESPIPE; break;
150 case ERROR_SHARING_BUFFER_EXCEEDED: error = ENFILE; break;
151 case ERROR_SHARING_VIOLATION: error = EACCES; break;
152 case ERROR_STACK_OVERFLOW: error = ENOMEM; break;
153 case ERROR_SWAPERROR: error = ENOENT; break;
154 case ERROR_TOO_MANY_LINKS: error = EMLINK; break;
155 case ERROR_TOO_MANY_MODULES: error = EMFILE; break;
156 case ERROR_TOO_MANY_OPEN_FILES: error = EMFILE; break;
157 case ERROR_UNRECOGNIZED_MEDIA: error = ENXIO; break;
158 case ERROR_UNRECOGNIZED_VOLUME: error = ENODEV; break;
159 case ERROR_WAIT_NO_CHILDREN: error = ECHILD; break;
160 case ERROR_WRITE_FAULT: error = EIO; break;
161 case ERROR_WRITE_PROTECT: error = EROFS; break;
162 case ERROR_CANT_RESOLVE_FILENAME: error = ELOOP; break;
163 }
164 return error;
165}
166#pragma GCC reset_options
167
168#undef strerror
169char *mingw_strerror(int errnum)
170{
171 if (errnum == ELOOP)
172 return (char *)"Too many levels of symbolic links";
173 return strerror(errnum);
174}
175
176char *strsignal(int sig)
177{
178 if (sig == SIGTERM)
179 return (char *)"Terminated";
180 else if (sig == SIGKILL)
181 return (char *)"Killed";
182 return (char *)get_signame(sig);
183}
184
185static int zero_fd = -1;
186static int rand_fd = -1;
187
188/*
189 * Determine if 'filename' corresponds to one of the supported
190 * device files. Constants for these are defined as an enum
191 * in mingw.h.
192 */
193int get_dev_type(const char *filename)
194{
195 if (filename && is_prefixed_with(filename, "/dev/"))
196 return index_in_strings("null\0zero\0urandom\0", filename+5);
197
198 return NOT_DEVICE;
199}
200
201void update_special_fd(int dev, int fd)
202{
203 if (dev == DEV_ZERO)
204 zero_fd = fd;
205 else if (dev == DEV_URANDOM)
206 rand_fd = fd;
207}
208
209#define PREFIX_LEN (sizeof(DEV_FD_PREFIX)-1)
210static int get_dev_fd(const char *filename)
211{
212 int fd;
213
214 if (filename && is_prefixed_with(filename, DEV_FD_PREFIX)) {
215 fd = bb_strtou(filename+PREFIX_LEN, NULL, 10);
216 if (errno == 0 && (HANDLE)_get_osfhandle(fd) != INVALID_HANDLE_VALUE)
217 return fd;
218 }
219 return -1;
220}
221
222#undef open
223int mingw_open (const char *filename, int oflags, ...)
224{
225 va_list args;
226 unsigned mode;
227 int fd;
228 int special = (oflags & O_SPECIAL);
229 int dev = get_dev_type(filename);
230
231 /* /dev/null is always allowed, others only if O_SPECIAL is set */
232 if (dev == DEV_NULL || (special && dev != NOT_DEVICE)) {
233 filename = "nul";
234 oflags = O_RDWR;
235 }
236 else if ((fd=get_dev_fd(filename)) >= 0) {
237 return fd;
238 }
239
240 va_start(args, oflags);
241 mode = va_arg(args, int);
242 va_end(args);
243
244 fd = open(filename, oflags&~O_SPECIAL, mode);
245 if (fd >= 0) {
246 update_special_fd(dev, fd);
247 }
248 else if ((oflags & O_ACCMODE) != O_RDONLY && errno == EACCES) {
249 DWORD attrs = GetFileAttributes(filename);
250 if (attrs != INVALID_FILE_ATTRIBUTES && (attrs & FILE_ATTRIBUTE_DIRECTORY))
251 errno = EISDIR;
252 }
253 return fd;
254}
255
256int mingw_xopen(const char *pathname, int flags)
257{
258 int ret;
259
260 /* allow use of special devices */
261 ret = mingw_open(pathname, flags|O_SPECIAL);
262 if (ret < 0) {
263 bb_perror_msg_and_die("can't open '%s'", pathname);
264 }
265 return ret;
266}
267
268ssize_t FAST_FUNC mingw_open_read_close(const char *fn, void *buf, size_t size)
269{
270 /* allow use of special devices */
271 int fd = mingw_open(fn, O_RDONLY|O_SPECIAL);
272 if (fd < 0)
273 return fd;
274 return read_close(fd, buf, size);
275}
276
277#undef fopen
278FILE *mingw_fopen (const char *filename, const char *otype)
279{
280 int fd;
281
282 if (get_dev_type(filename) == DEV_NULL)
283 filename = "nul";
284 else if ((fd=get_dev_fd(filename)) >= 0)
285 return fdopen(fd, otype);
286 return fopen(filename, otype);
287}
288
289#undef read
290ssize_t mingw_read(int fd, void *buf, size_t count)
291{
292 if (fd == zero_fd) {
293 memset(buf, 0, count);
294 return count;
295 }
296 else if (fd == rand_fd) {
297 return get_random_bytes(buf, count);
298 }
299 return read(fd, buf, count);
300}
301
302#undef close
303int mingw_close(int fd)
304{
305 if (fd == zero_fd) {
306 zero_fd = -1;
307 }
308 if (fd == rand_fd) {
309 rand_fd = -1;
310 }
311 return close(fd);
312}
313
314#undef dup2
315int mingw_dup2 (int fd, int fdto)
316{
317 int ret = dup2(fd, fdto);
318 return ret != -1 ? fdto : -1;
319}
320
321/*
322 * The unit of FILETIME is 100-nanoseconds since January 1, 1601, UTC.
323 * Returns the 100-nanoseconds ("hekto nanoseconds") since the epoch.
324 */
325static inline long long filetime_to_hnsec(const FILETIME *ft)
326{
327 long long winTime = ((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime;
328 /* Windows to Unix Epoch conversion */
329 return winTime - 116444736000000000LL;
330}
331
332static inline struct timespec filetime_to_timespec(const FILETIME *ft)
333{
334 struct timespec ts;
335 long long winTime = filetime_to_hnsec(ft);
336
337 ts.tv_sec = (time_t)(winTime / 10000000);
338 ts.tv_nsec = (long)(winTime % 10000000) * 100;
339
340 return ts;
341}
342
343static inline mode_t file_attr_to_st_mode(DWORD attr)
344{
345 mode_t fMode = S_IRUSR|S_IRGRP|S_IROTH;
346 if (attr & FILE_ATTRIBUTE_DIRECTORY)
347 fMode |= S_IFDIR|S_IXUSR|S_IXGRP|S_IXOTH;
348 else if (attr & FILE_ATTRIBUTE_DEVICE)
349 fMode |= S_IFCHR|S_IWOTH;
350 else
351 fMode |= S_IFREG;
352 if (!(attr & FILE_ATTRIBUTE_READONLY))
353 fMode |= S_IWUSR|S_IWGRP;
354 return fMode;
355}
356
357static inline int get_file_attr(const char *fname, WIN32_FILE_ATTRIBUTE_DATA *fdata)
358{
359 size_t len;
360
361 if (get_dev_type(fname) != NOT_DEVICE || get_dev_fd(fname) >= 0) {
362 /* Fake attributes for special devices */
363 FILETIME epoch = {0xd53e8000, 0x019db1de}; // Unix epoch as FILETIME
364 fdata->dwFileAttributes = FILE_ATTRIBUTE_DEVICE;
365 fdata->ftCreationTime = fdata->ftLastAccessTime =
366 fdata->ftLastWriteTime = epoch;
367 fdata->nFileSizeHigh = fdata->nFileSizeLow = 0;
368 return 0;
369 }
370
371 if (GetFileAttributesExA(fname, GetFileExInfoStandard, fdata)) {
372 fdata->dwFileAttributes &= ~FILE_ATTRIBUTE_DEVICE;
373 return 0;
374 }
375
376 if (GetLastError() == ERROR_SHARING_VIOLATION) {
377 HANDLE hnd;
378 WIN32_FIND_DATA fd;
379
380 if ((hnd=FindFirstFile(fname, &fd)) != INVALID_HANDLE_VALUE) {
381 fdata->dwFileAttributes =
382 fd.dwFileAttributes & ~FILE_ATTRIBUTE_DEVICE;
383 fdata->ftCreationTime = fd.ftCreationTime;
384 fdata->ftLastAccessTime = fd.ftLastAccessTime;
385 fdata->ftLastWriteTime = fd.ftLastWriteTime;
386 fdata->nFileSizeHigh = fd.nFileSizeHigh;
387 fdata->nFileSizeLow = fd.nFileSizeLow;
388 FindClose(hnd);
389 return 0;
390 }
391 }
392
393 switch (GetLastError()) {
394 case ERROR_ACCESS_DENIED:
395 case ERROR_SHARING_VIOLATION:
396 case ERROR_LOCK_VIOLATION:
397 case ERROR_SHARING_BUFFER_EXCEEDED:
398 return EACCES;
399 case ERROR_BUFFER_OVERFLOW:
400 return ENAMETOOLONG;
401 case ERROR_NOT_ENOUGH_MEMORY:
402 return ENOMEM;
403 case ERROR_INVALID_NAME:
404 len = strlen(fname);
405 if (len > 1 && (fname[len-1] == '/' || fname[len-1] == '\\'))
406 return ENOTDIR;
407 default:
408 return ENOENT;
409 }
410}
411
412#undef umask
413mode_t mingw_umask(mode_t new_mode)
414{
415 static mode_t old_mode = DEFAULT_UMASK;
416 mode_t tmp_mode;
417
418 tmp_mode = old_mode;
419 old_mode = new_mode;
420
421 umask((new_mode & S_IWUSR) ? _S_IWRITE : 0);
422
423 return tmp_mode;
424}
425
426/*
427 * Examine a file's contents to determine if it can be executed. This
428 * should be a last resort: in most cases it's much more efficient to
429 * check the file extension.
430 *
431 * We look for two types of file: shell scripts and binary executables.
432 */
433static int has_exec_format(const char *name)
434{
435 int n, sig;
436 unsigned int offset;
437 unsigned char buf[1024];
438
439 /* special case: skip DLLs, there are thousands of them! */
440 if (is_suffixed_with_case(name, ".dll"))
441 return 0;
442
443 n = open_read_close(name, buf, sizeof(buf));
444 if (n < 4) /* at least '#!/x' and not error */
445 return 0;
446
447 /* shell script */
448 if (buf[0] == '#' && buf[1] == '!') {
449 return 1;
450 }
451
452 /*
453 * Poke about in file to see if it's a PE binary. I've just copied
454 * the magic from the file command.
455 */
456 if (buf[0] == 'M' && buf[1] == 'Z') {
457 offset = (buf[0x19] << 8) + buf[0x18];
458 if (offset > 0x3f) {
459 offset = (buf[0x3f] << 24) + (buf[0x3e] << 16) +
460 (buf[0x3d] << 8) + buf[0x3c];
461 if (offset < sizeof(buf)-100) {
462 if (memcmp(buf+offset, "PE\0\0", 4) == 0) {
463 sig = (buf[offset+25] << 8) + buf[offset+24];
464 if (sig == 0x10b || sig == 0x20b) {
465 sig = (buf[offset+23] << 8) + buf[offset+22];
466 if ((sig & 0x2000) != 0) {
467 /* DLL */
468 return 0;
469 }
470 sig = buf[offset+92];
471 return (sig == 1 || sig == 2 || sig == 3 || sig == 7);
472 }
473 }
474 }
475 }
476 }
477
478 return 0;
479}
480
481#if ENABLE_FEATURE_EXTRA_FILE_DATA
482static uid_t file_owner(HANDLE fh)
483{
484 PSID pSidOwner;
485 PSECURITY_DESCRIPTOR pSD;
486 static PTOKEN_USER user = NULL;
487 static int initialised = 0;
488 uid_t uid = 0;
489 DWORD *ptr;
490 unsigned char prefix[] = {
491 0x01, 0x05, 0x00, 0x00,
492 0x00, 0x00, 0x00, 0x05,
493 0x15, 0x00, 0x00, 0x00
494 };
495
496 /* get SID of current user */
497 if (!initialised) {
498 HANDLE token;
499 DWORD ret = 0;
500
501 initialised = 1;
502 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token)) {
503 GetTokenInformation(token, TokenUser, NULL, 0, &ret);
504 if (ret <= 0 || (user=malloc(ret)) == NULL ||
505 !GetTokenInformation(token, TokenUser, user, ret, &ret)) {
506 free(user);
507 user = NULL;
508 }
509 CloseHandle(token);
510 }
511 }
512
513 if (user == NULL)
514 return DEFAULT_UID;
515
516 /* get SID of file's owner */
517 if (GetSecurityInfo(fh, SE_FILE_OBJECT, OWNER_SECURITY_INFORMATION,
518 &pSidOwner, NULL, NULL, NULL, &pSD) != ERROR_SUCCESS)
519 return 0;
520
521 if (EqualSid(pSidOwner, user->User.Sid)) {
522 uid = DEFAULT_UID;
523 }
524 else if (memcmp(pSidOwner, prefix, sizeof(prefix)) == 0) {
525 /* for local or domain users use the RID as uid */
526 ptr = (DWORD *)pSidOwner;
527 if (ptr[6] >= 500 && ptr[6] < DEFAULT_UID)
528 uid = (uid_t)ptr[6];
529 }
530 LocalFree(pSD);
531 return uid;
532
533#if 0
534 /* this is how it would be done properly using the API */
535 {
536 PSID_IDENTIFIER_AUTHORITY auth;
537 unsigned char *count;
538 PDWORD subauth;
539 unsigned char nt_auth[] = {
540 0x00, 0x00, 0x00, 0x00, 0x00, 0x05
541 };
542
543 if (IsValidSid(pSidOwner) ) {
544 auth = GetSidIdentifierAuthority(pSidOwner);
545 count = GetSidSubAuthorityCount(pSidOwner);
546 subauth = GetSidSubAuthority(pSidOwner, 0);
547 if (memcmp(auth, nt_auth, sizeof(nt_auth)) == 0 &&
548 *count == 5 && *subauth == 21) {
549 subauth = GetSidSubAuthority(pSidOwner, 4);
550 if (*subauth >= 500 && *subauth < DEFAULT_UID)
551 uid = (uid_t)*subauth;
552 }
553 }
554 return uid;
555 }
556#endif
557}
558#endif
559
560static int get_symlink_data(DWORD attr, const char *pathname,
561 WIN32_FIND_DATAA *fbuf)
562{
563 if (attr & FILE_ATTRIBUTE_REPARSE_POINT) {
564 HANDLE handle = FindFirstFileA(pathname, fbuf);
565 if (handle != INVALID_HANDLE_VALUE) {
566 FindClose(handle);
567 return ((fbuf->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) &&
568 fbuf->dwReserved0 == IO_REPARSE_TAG_SYMLINK);
569 }
570 }
571 return 0;
572}
573
574static int is_symlink(const char *pathname)
575{
576 WIN32_FILE_ATTRIBUTE_DATA fdata;
577 WIN32_FIND_DATAA fbuf;
578
579 if (!get_file_attr(pathname, &fdata))
580 return get_symlink_data(fdata.dwFileAttributes, pathname, &fbuf);
581 return 0;
582}
583
584/* If follow is true then act like stat() and report on the link
585 * target. Otherwise report on the link itself.
586 */
587static int do_lstat(int follow, const char *file_name, struct mingw_stat *buf)
588{
589 int err = EINVAL;
590 WIN32_FILE_ATTRIBUTE_DATA fdata;
591 WIN32_FIND_DATAA findbuf;
592 DWORD low, high;
593 off64_t size;
594#if ENABLE_FEATURE_EXTRA_FILE_DATA
595 DWORD flags;
596 BY_HANDLE_FILE_INFORMATION hdata;
597 HANDLE fh;
598#endif
599
600 while (file_name && !(err=get_file_attr(file_name, &fdata))) {
601 buf->st_ino = 0;
602 buf->st_uid = DEFAULT_UID;
603 buf->st_gid = DEFAULT_GID;
604 buf->st_dev = buf->st_rdev = 0;
605
606 if (get_symlink_data(fdata.dwFileAttributes, file_name, &findbuf)) {
607 char *name = auto_string(xmalloc_realpath(file_name));
608
609 if (follow) {
610 /* The file size and times are wrong when Windows follows
611 * a symlink. Use the canonicalized path to try again. */
612 err = errno;
613 file_name = name;
614 continue;
615 }
616
617 /* Get the contents of a symlink, not its target. */
618 buf->st_mode = S_IFLNK|S_IRWXU|S_IRWXG|S_IRWXO;
619 buf->st_attr = fdata.dwFileAttributes;
620 buf->st_size = name ? strlen(name) : 0; /* should use readlink */
621 buf->st_atim = filetime_to_timespec(&(findbuf.ftLastAccessTime));
622 buf->st_mtim = filetime_to_timespec(&(findbuf.ftLastWriteTime));
623 buf->st_ctim = filetime_to_timespec(&(findbuf.ftCreationTime));
624 }
625 else {
626 /* The file is not a symlink. */
627 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
628 buf->st_attr = fdata.dwFileAttributes;
629 if (S_ISREG(buf->st_mode) &&
630 !(buf->st_attr & FILE_ATTRIBUTE_DEVICE) &&
631 (has_exe_suffix(file_name) || has_exec_format(file_name)))
632 buf->st_mode |= S_IXUSR|S_IXGRP|S_IXOTH;
633 buf->st_size = fdata.nFileSizeLow |
634 (((off64_t)fdata.nFileSizeHigh)<<32);
635 buf->st_atim = filetime_to_timespec(&(fdata.ftLastAccessTime));
636 buf->st_mtim = filetime_to_timespec(&(fdata.ftLastWriteTime));
637 buf->st_ctim = filetime_to_timespec(&(fdata.ftCreationTime));
638 }
639 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1;
640
641#if ENABLE_FEATURE_EXTRA_FILE_DATA
642 flags = FILE_FLAG_BACKUP_SEMANTICS;
643 if (S_ISLNK(buf->st_mode))
644 flags |= FILE_FLAG_OPEN_REPARSE_POINT;
645 fh = CreateFile(file_name, READ_CONTROL, 0, NULL,
646 OPEN_EXISTING, flags, NULL);
647 if (fh != INVALID_HANDLE_VALUE) {
648 if (GetFileInformationByHandle(fh, &hdata)) {
649 buf->st_dev = hdata.dwVolumeSerialNumber;
650 buf->st_ino = hdata.nFileIndexLow |
651 (((ino_t)hdata.nFileIndexHigh)<<32);
652 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 :
653 hdata.nNumberOfLinks;
654 }
655 buf->st_uid = buf->st_gid = file_owner(fh);
656 CloseHandle(fh);
657 }
658 else {
659 buf->st_uid = 0;
660 buf->st_gid = 0;
661 if (!(buf->st_attr & FILE_ATTRIBUTE_DEVICE))
662 buf->st_mode &= ~(S_IROTH|S_IWOTH|S_IXOTH);
663 }
664#endif
665
666 /* Get actual size of compressed/sparse files */
667 low = GetCompressedFileSize(file_name, &high);
668 if ((low == INVALID_FILE_SIZE && GetLastError() != NO_ERROR) ||
669 S_ISDIR(buf->st_mode)) {
670 size = buf->st_size;
671 }
672 else {
673 size = low | (((off64_t)high)<<32);
674 }
675
676 /*
677 * Assume a block is 4096 bytes and calculate number of 512 byte
678 * sectors.
679 */
680 buf->st_blksize = 4096;
681 buf->st_blocks = ((size+4095)>>12)<<3;
682 return 0;
683 }
684 errno = err;
685 return -1;
686}
687
688int mingw_lstat(const char *file_name, struct mingw_stat *buf)
689{
690 return do_lstat(0, file_name, buf);
691}
692
693int mingw_stat(const char *file_name, struct mingw_stat *buf)
694{
695 return do_lstat(1, file_name, buf);
696}
697
698#undef st_atime
699#undef st_mtime
700#undef st_ctime
701int mingw_fstat(int fd, struct mingw_stat *buf)
702{
703 HANDLE fh = (HANDLE)_get_osfhandle(fd);
704 BY_HANDLE_FILE_INFORMATION fdata;
705
706 if (fh == INVALID_HANDLE_VALUE) {
707 errno = EBADF;
708 return -1;
709 }
710 /* direct non-file handles to MS's fstat() */
711 if (GetFileType(fh) != FILE_TYPE_DISK) {
712 struct _stati64 buf64;
713
714 if ( _fstati64(fd, &buf64) != 0 ) {
715 return -1;
716 }
717 buf->st_mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH;
718 buf->st_attr = FILE_ATTRIBUTE_NORMAL;
719 buf->st_size = buf64.st_size;
720 buf->st_atim.tv_sec = buf64.st_atime;
721 buf->st_atim.tv_nsec = 0;
722 buf->st_mtim.tv_sec = buf64.st_mtime;
723 buf->st_mtim.tv_nsec = 0;
724 buf->st_ctim.tv_sec = buf64.st_ctime;
725 buf->st_ctim.tv_nsec = 0;
726 buf->st_blocks = ((buf64.st_size+4095)>>12)<<3;
727#if ENABLE_FEATURE_EXTRA_FILE_DATA
728 buf->st_dev = 0;
729 buf->st_ino = 0;
730 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1;
731#endif
732 goto success;
733 }
734
735 if (GetFileInformationByHandle(fh, &fdata)) {
736 buf->st_mode = file_attr_to_st_mode(fdata.dwFileAttributes);
737 buf->st_attr = fdata.dwFileAttributes;
738 buf->st_size = fdata.nFileSizeLow |
739 (((off64_t)fdata.nFileSizeHigh)<<32);
740 buf->st_atim = filetime_to_timespec(&(fdata.ftLastAccessTime));
741 buf->st_mtim = filetime_to_timespec(&(fdata.ftLastWriteTime));
742 buf->st_ctim = filetime_to_timespec(&(fdata.ftCreationTime));
743 buf->st_blocks = ((buf->st_size+4095)>>12)<<3;
744#if ENABLE_FEATURE_EXTRA_FILE_DATA
745 buf->st_dev = fdata.dwVolumeSerialNumber;
746 buf->st_ino = fdata.nFileIndexLow |
747 (((uint64_t)fdata.nFileIndexHigh)<<32);
748 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : fdata.nNumberOfLinks;
749#endif
750 success:
751#if !ENABLE_FEATURE_EXTRA_FILE_DATA
752 buf->st_dev = 0;
753 buf->st_ino = 0;
754 buf->st_nlink = S_ISDIR(buf->st_mode) ? 2 : 1;
755#endif
756 buf->st_rdev = 0;
757 buf->st_uid = DEFAULT_UID;
758 buf->st_gid = DEFAULT_GID;
759 buf->st_blksize = 4096;
760 return 0;
761 }
762
763 errno = EBADF;
764 return -1;
765}
766
767static inline void timespec_to_filetime(const struct timespec tv, FILETIME *ft)
768{
769 long long winTime = tv.tv_sec * 10000000LL + tv.tv_nsec / 100LL +
770 116444736000000000LL;
771 ft->dwLowDateTime = winTime;
772 ft->dwHighDateTime = winTime >> 32;
773}
774
775static int hutimens(HANDLE fh, const struct timespec times[2])
776{
777 FILETIME now, aft, mft;
778 FILETIME *pft[2] = {&aft, &mft};
779 int i;
780
781 GetSystemTimeAsFileTime(&now);
782
783 if (times) {
784 for (i = 0; i < 2; ++i) {
785 if (times[i].tv_nsec == UTIME_NOW)
786 *pft[i] = now;
787 else if (times[i].tv_nsec == UTIME_OMIT)
788 pft[i] = NULL;
789 else if (times[i].tv_nsec >= 0 && times[i].tv_nsec < 1000000000L)
790 timespec_to_filetime(times[i], pft[i]);
791 else {
792 errno = EINVAL;
793 return -1;
794 }
795 }
796 } else {
797 aft = mft = now;
798 }
799
800 if (!SetFileTime(fh, NULL, pft[0], pft[1])) {
801 errno = err_win_to_posix();
802 return -1;
803 }
804 return 0;
805}
806
807int futimens(int fd, const struct timespec times[2])
808{
809 HANDLE fh;
810
811 fh = (HANDLE)_get_osfhandle(fd);
812 if (fh == INVALID_HANDLE_VALUE) {
813 errno = EBADF;
814 return -1;
815 }
816
817 return hutimens(fh, times);
818}
819
820int utimensat(int fd, const char *path, const struct timespec times[2],
821 int flags)
822{
823 int rc = -1;
824 HANDLE fh;
825 DWORD cflag = FILE_FLAG_BACKUP_SEMANTICS;
826
827 if (is_relative_path(path) && fd != AT_FDCWD) {
828 errno = ENOSYS; // partial implementation
829 return rc;
830 }
831
832 if (flags & AT_SYMLINK_NOFOLLOW)
833 cflag |= FILE_FLAG_OPEN_REPARSE_POINT;
834
835 fh = CreateFile(path, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING,
836 cflag, NULL);
837 if (fh == INVALID_HANDLE_VALUE) {
838 errno = err_win_to_posix();
839 return rc;
840 }
841
842 rc = hutimens(fh, times);
843 CloseHandle(fh);
844 return rc;
845}
846
847int utimes(const char *file_name, const struct timeval tv[2])
848{
849 struct timespec ts[2];
850
851 if (tv) {
852 if (tv[0].tv_usec < 0 || tv[0].tv_usec >= 1000000 ||
853 tv[1].tv_usec < 0 || tv[1].tv_usec >= 1000000) {
854 errno = EINVAL;
855 return -1;
856 }
857 ts[0].tv_sec = tv[0].tv_sec;
858 ts[0].tv_nsec = tv[0].tv_usec * 1000;
859 ts[1].tv_sec = tv[1].tv_sec;
860 ts[1].tv_nsec = tv[1].tv_usec * 1000;
861 }
862 return utimensat(AT_FDCWD, file_name, tv ? ts : NULL, 0);
863}
864
865unsigned int sleep (unsigned int seconds)
866{
867 Sleep(seconds*1000);
868 return 0;
869}
870
871int nanosleep(const struct timespec *req, struct timespec *rem)
872{
873 if (req->tv_nsec < 0 || 1000000000 <= req->tv_nsec) {
874 errno = EINVAL;
875 return -1;
876 }
877
878 Sleep(req->tv_sec*1000 + req->tv_nsec/1000000);
879
880 /* Sleep is not interruptible. So there is no remaining delay. */
881 if (rem != NULL) {
882 rem->tv_sec = 0;
883 rem->tv_nsec = 0;
884 }
885
886 return 0;
887}
888
889/*
890 * Windows' mktemp returns NULL on error whereas POSIX always returns the
891 * template and signals an error by making it an empty string.
892 */
893#undef mktemp
894char *mingw_mktemp(char *template)
895{
896 if ( mktemp(template) == NULL ) {
897 template[0] = '\0';
898 }
899
900 return template;
901}
902
903int mkstemp(char *template)
904{
905 char *filename = mktemp(template);
906 if (filename == NULL)
907 return -1;
908 return open(filename, O_RDWR | O_CREAT, 0600);
909}
910
911int gettimeofday(struct timeval *tv, void *tz UNUSED_PARAM)
912{
913 FILETIME ft;
914 long long hnsec;
915
916 GetSystemTimeAsFileTime(&ft);
917 hnsec = filetime_to_hnsec(&ft);
918 tv->tv_sec = hnsec / 10000000;
919 tv->tv_usec = (hnsec % 10000000) / 10;
920 return 0;
921}
922
923int pipe(int filedes[2])
924{
925 if (_pipe(filedes, PIPE_BUF, 0) < 0)
926 return -1;
927 return 0;
928}
929
930struct tm *gmtime_r(const time_t *timep, struct tm *result)
931{
932 /* gmtime() in MSVCRT.DLL is thread-safe, but not reentrant */
933 memcpy(result, gmtime(timep), sizeof(struct tm));
934 return result;
935}
936
937struct tm *localtime_r(const time_t *timep, struct tm *result)
938{
939 /* localtime() in MSVCRT.DLL is thread-safe, but not reentrant */
940 memcpy(result, localtime(timep), sizeof(struct tm));
941 return result;
942}
943
944#undef getcwd
945char *mingw_getcwd(char *pointer, int len)
946{
947 char *ret = getcwd(pointer, len);
948 if (!ret)
949 return ret;
950 return bs_to_slash(ret);
951}
952
953#undef rename
954int mingw_rename(const char *pold, const char *pnew)
955{
956 DWORD attrs;
957
958 /*
959 * For non-symlinks, try native rename() first to get errno right.
960 * It is based on MoveFile(), which cannot overwrite existing files.
961 */
962 if (!is_symlink(pold)) {
963 if (!rename(pold, pnew))
964 return 0;
965 if (errno != EEXIST)
966 return -1;
967 }
968 if (MoveFileEx(pold, pnew,
969 MOVEFILE_REPLACE_EXISTING | MOVEFILE_COPY_ALLOWED))
970 return 0;
971 /* TODO: translate more errors */
972 if (GetLastError() == ERROR_ACCESS_DENIED &&
973 (attrs = GetFileAttributes(pnew)) != INVALID_FILE_ATTRIBUTES) {
974 if (attrs & FILE_ATTRIBUTE_DIRECTORY) {
975 errno = EISDIR;
976 return -1;
977 }
978 if ((attrs & FILE_ATTRIBUTE_READONLY) &&
979 SetFileAttributes(pnew, attrs & ~FILE_ATTRIBUTE_READONLY)) {
980 if (MoveFileEx(pold, pnew, MOVEFILE_REPLACE_EXISTING))
981 return 0;
982 /* revert file attributes on failure */
983 SetFileAttributes(pnew, attrs);
984 }
985 }
986 errno = EACCES;
987 return -1;
988}
989
990static char *gethomedir(void)
991{
992 static char *buf = NULL;
993 DECLARE_PROC_ADDR(BOOL, GetUserProfileDirectoryA, HANDLE, LPSTR, LPDWORD);
994
995 if (!buf) {
996 DWORD len = PATH_MAX;
997 HANDLE h;
998
999 buf = xzalloc(len);
1000 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h)) {
1001 if (INIT_PROC_ADDR(userenv.dll, GetUserProfileDirectoryA)) {
1002 GetUserProfileDirectoryA(h, buf, &len);
1003 bs_to_slash(buf);
1004 }
1005 CloseHandle(h);
1006 }
1007 }
1008 return buf;
1009}
1010
1011static char *getsysdir(void)
1012{
1013 static char *buf = NULL;
1014 char dir[PATH_MAX];
1015
1016 if (!buf) {
1017 buf = xzalloc(PATH_MAX);
1018 GetSystemDirectory(dir, PATH_MAX);
1019 realpath(dir, buf);
1020 }
1021 return buf;
1022}
1023
1024#define NAME_LEN 100
1025static char *get_user_name(void)
1026{
1027 static char *user_name = NULL;
1028 char *s;
1029 DWORD len = NAME_LEN;
1030
1031 if ( user_name == NULL ) {
1032 user_name = xzalloc(NAME_LEN);
1033 }
1034
1035 if ( user_name[0] != '\0' ) {
1036 return user_name;
1037 }
1038
1039 if ( !GetUserName(user_name, &len) ) {
1040 return NULL;
1041 }
1042
1043 for ( s=user_name; *s; ++s ) {
1044 if ( *s == ' ' ) {
1045 *s = '_';
1046 }
1047 }
1048
1049 return user_name;
1050}
1051
1052int getuid(void)
1053{
1054 int ret = DEFAULT_UID;
1055 HANDLE h;
1056
1057 if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &h)) {
1058 TOKEN_ELEVATION elevation;
1059 DWORD size = sizeof(TOKEN_ELEVATION);
1060
1061 if (GetTokenInformation(h, TokenElevation, &elevation,
1062 sizeof(elevation), &size)) {
1063 if (elevation.TokenIsElevated)
1064 ret = 0;
1065 }
1066 CloseHandle(h);
1067 }
1068 return ret;
1069}
1070
1071struct passwd *getpwnam(const char *name)
1072{
1073 const char *myname;
1074
1075 if ( (myname=get_user_name()) != NULL &&
1076 strcmp(myname, name) == 0 ) {
1077 return getpwuid(DEFAULT_UID);
1078 }
1079 else if (strcmp(name, "root") == 0) {
1080 return getpwuid(0);
1081 }
1082
1083 return NULL;
1084}
1085
1086struct passwd *getpwuid(uid_t uid)
1087{
1088 static struct passwd p;
1089
1090 if (uid == 0) {
1091 p.pw_name = (char *)"root";
1092 p.pw_dir = getsysdir();
1093 }
1094 else if (uid == DEFAULT_UID && (p.pw_name=get_user_name()) != NULL) {
1095 p.pw_dir = gethomedir();
1096 }
1097 else {
1098 return NULL;
1099 }
1100
1101 p.pw_passwd = (char *)"";
1102 p.pw_gecos = p.pw_name;
1103 p.pw_shell = NULL;
1104 p.pw_uid = uid;
1105 p.pw_gid = uid;
1106
1107 return &p;
1108}
1109
1110struct group *getgrgid(gid_t gid)
1111{
1112 static char *members[2] = { NULL, NULL };
1113 static struct group g;
1114
1115 if (gid == 0) {
1116 g.gr_name = (char *)"root";
1117 }
1118 else if (gid != DEFAULT_GID || (g.gr_name=get_user_name()) == NULL) {
1119 return NULL;
1120 }
1121 g.gr_passwd = (char *)"";
1122 g.gr_gid = gid;
1123 members[0] = g.gr_name;
1124 g.gr_mem = members;
1125
1126 return &g;
1127}
1128
1129int getgrouplist(const char *user UNUSED_PARAM, gid_t group,
1130 gid_t *groups, int *ngroups)
1131{
1132 if ( *ngroups == 0 ) {
1133 *ngroups = 1;
1134 return -1;
1135 }
1136
1137 *ngroups = 1;
1138 groups[0] = group;
1139 return 1;
1140}
1141
1142int getgroups(int n, gid_t *groups)
1143{
1144 if ( n == 0 ) {
1145 return 1;
1146 }
1147
1148 groups[0] = getgid();
1149 return 1;
1150}
1151
1152int getlogin_r(char *buf, size_t len)
1153{
1154 char *name;
1155
1156 if ( (name=get_user_name()) == NULL ) {
1157 return -1;
1158 }
1159
1160 if ( strlen(name) >= len ) {
1161 errno = ERANGE;
1162 return -1;
1163 }
1164
1165 strcpy(buf, name);
1166 return 0;
1167}
1168
1169long sysconf(int name)
1170{
1171 if ( name == _SC_CLK_TCK ) {
1172 return TICKS_PER_SECOND;
1173 }
1174 errno = EINVAL;
1175 return -1;
1176}
1177
1178clock_t times(struct tms *buf)
1179{
1180 memset(buf, 0, sizeof(*buf));
1181 return 0;
1182}
1183
1184int link(const char *oldpath, const char *newpath)
1185{
1186 DECLARE_PROC_ADDR(BOOL, CreateHardLinkA, LPCSTR, LPCSTR,
1187 LPSECURITY_ATTRIBUTES);
1188
1189 if (!INIT_PROC_ADDR(kernel32.dll, CreateHardLinkA)) {
1190 return -1;
1191 }
1192 if (!CreateHardLinkA(newpath, oldpath, NULL)) {
1193 errno = err_win_to_posix();
1194 return -1;
1195 }
1196 return 0;
1197}
1198
1199#ifndef SYMBOLIC_LINK_FLAG_DIRECTORY
1200# define SYMBOLIC_LINK_FLAG_DIRECTORY (0x1)
1201#endif
1202#ifndef SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE
1203# define SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE (0x2)
1204#endif
1205
1206int symlink(const char *target, const char *linkpath)
1207{
1208 DWORD flag = SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1209 struct stat st;
1210 DECLARE_PROC_ADDR(BOOLEAN, CreateSymbolicLinkA, LPCSTR, LPCSTR, DWORD);
1211 char *targ, *relative = NULL;
1212
1213 if (!INIT_PROC_ADDR(kernel32.dll, CreateSymbolicLinkA)) {
1214 return -1;
1215 }
1216
1217 if (is_relative_path(target) && has_path(linkpath)) {
1218 /* make target's path relative to current directory */
1219 const char *name = bb_get_last_path_component_nostrip(linkpath);
1220 relative = xasprintf("%.*s%s",
1221 (int)(name - linkpath), linkpath, target);
1222 }
1223
1224 if (stat(relative ?: target, &st) != -1 && S_ISDIR(st.st_mode))
1225 flag |= SYMBOLIC_LINK_FLAG_DIRECTORY;
1226 free(relative);
1227
1228 targ = auto_string(strdup(target));
1229 slash_to_bs(targ);
1230
1231 retry:
1232 if (!CreateSymbolicLinkA(linkpath, targ, flag)) {
1233 /* Old Windows versions see 'UNPRIVILEGED_CREATE' as an invalid
1234 * parameter. Retry without it. */
1235 if ((flag & SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE) &&
1236 GetLastError() == ERROR_INVALID_PARAMETER) {
1237 flag &= ~SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE;
1238 goto retry;
1239 }
1240 errno = err_win_to_posix();
1241 return -1;
1242 }
1243 return 0;
1244}
1245
1246static char *normalize_ntpathA(char *buf)
1247{
1248 /* fix absolute path prefixes */
1249 if (buf[0] == '\\') {
1250 /* strip NT namespace prefixes */
1251 if (is_prefixed_with(buf, "\\??\\") ||
1252 is_prefixed_with(buf, "\\\\?\\"))
1253 buf += 4;
1254 else if (is_prefixed_with_case(buf, "\\DosDevices\\"))
1255 buf += 12;
1256 /* replace remaining '...UNC\' with '\\' */
1257 if (is_prefixed_with_case(buf, "UNC\\")) {
1258 buf += 2;
1259 *buf = '\\';
1260 }
1261 }
1262 return buf;
1263}
1264
1265static char *resolve_symlinks(char *path)
1266{
1267 HANDLE h;
1268 DWORD status;
1269 char *ptr = NULL;
1270 DECLARE_PROC_ADDR(DWORD, GetFinalPathNameByHandleA, HANDLE,
1271 LPSTR, DWORD, DWORD);
1272 char *resolve = NULL;
1273
1274 if (GetFileAttributesA(path) & FILE_ATTRIBUTE_REPARSE_POINT) {
1275 resolve = xmalloc_follow_symlinks(path);
1276 if (!resolve)
1277 return NULL;
1278 }
1279
1280 /* need a file handle to resolve symlinks */
1281 h = CreateFileA(resolve ?: path, 0, 0, NULL, OPEN_EXISTING,
1282 FILE_FLAG_BACKUP_SEMANTICS, NULL);
1283 if (h != INVALID_HANDLE_VALUE) {
1284 if (!INIT_PROC_ADDR(kernel32.dll, GetFinalPathNameByHandleA)) {
1285 goto end;
1286 }
1287
1288 /* normalize the path and return it on success */
1289 status = GetFinalPathNameByHandleA(h, path, MAX_PATH,
1290 FILE_NAME_NORMALIZED|VOLUME_NAME_DOS);
1291 if (status != 0 && status < MAX_PATH) {
1292 ptr = normalize_ntpathA(path);
1293 goto end;
1294 }
1295 }
1296
1297 errno = err_win_to_posix();
1298 end:
1299 CloseHandle(h);
1300 free(resolve);
1301 return ptr;
1302}
1303
1304/*
1305 * Emulate realpath in two stages:
1306 *
1307 * - _fullpath handles './', '../' and extra '/' characters. The
1308 * resulting path may not refer to an actual file.
1309 *
1310 * - resolve_symlinks checks that the file exists (by opening it) and
1311 * resolves symlinks by calling GetFinalPathNameByHandleA.
1312 */
1313char *realpath(const char *path, char *resolved_path)
1314{
1315 char buffer[MAX_PATH];
1316 char *real_path, *p;
1317
1318 /* enforce glibc pre-2.3 behaviour */
1319 if (path == NULL || resolved_path == NULL) {
1320 errno = EINVAL;
1321 return NULL;
1322 }
1323
1324 if (_fullpath(buffer, path, MAX_PATH) &&
1325 (real_path=resolve_symlinks(buffer))) {
1326 bs_to_slash(strcpy(resolved_path, real_path));
1327 p = last_char_is(resolved_path, '/');
1328 if (p && p > resolved_path && p[-1] != ':')
1329 *p = '\0';
1330 return resolved_path;
1331 }
1332 return NULL;
1333}
1334
1335static wchar_t *normalize_ntpath(wchar_t *wbuf)
1336{
1337 int i;
1338 /* fix absolute path prefixes */
1339 if (wbuf[0] == '\\') {
1340 /* strip NT namespace prefixes */
1341 if (!wcsncmp(wbuf, L"\\??\\", 4) ||
1342 !wcsncmp(wbuf, L"\\\\?\\", 4))
1343 wbuf += 4;
1344 else if (!wcsnicmp(wbuf, L"\\DosDevices\\", 12))
1345 wbuf += 12;
1346 /* replace remaining '...UNC\' with '\\' */
1347 if (!wcsnicmp(wbuf, L"UNC\\", 4)) {
1348 wbuf += 2;
1349 *wbuf = '\\';
1350 }
1351 }
1352 /* convert backslashes to slashes */
1353 for (i = 0; wbuf[i]; i++)
1354 if (wbuf[i] == '\\')
1355 wbuf[i] = '/';
1356 return wbuf;
1357}
1358
1359#define SRPB rptr->SymbolicLinkReparseBuffer
1360ssize_t readlink(const char *pathname, char *buf, size_t bufsiz)
1361{
1362 HANDLE h;
1363
1364 h = CreateFile(pathname, 0, 0, NULL, OPEN_EXISTING,
1365 FILE_FLAG_OPEN_REPARSE_POINT|FILE_FLAG_BACKUP_SEMANTICS, NULL);
1366 if (h != INVALID_HANDLE_VALUE) {
1367 DWORD nbytes;
1368 BYTE rbuf[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
1369 PREPARSE_DATA_BUFFER rptr = (PREPARSE_DATA_BUFFER)rbuf;
1370 BOOL status;
1371
1372 status = DeviceIoControl(h, FSCTL_GET_REPARSE_POINT, NULL, 0,
1373 rptr, sizeof(rbuf), &nbytes, NULL);
1374 CloseHandle(h);
1375
1376 if (status && rptr->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
1377 size_t len = SRPB.SubstituteNameLength/sizeof(WCHAR);
1378 WCHAR *name = SRPB.PathBuffer +
1379 SRPB.SubstituteNameOffset/sizeof(WCHAR);
1380
1381 name[len] = 0;
1382 name = normalize_ntpath(name);
1383 len = wcslen(name);
1384 if (len > bufsiz)
1385 len = bufsiz;
1386 if (WideCharToMultiByte(CP_ACP, 0, name, len, buf, bufsiz, 0, 0)) {
1387 return len;
1388 }
1389 }
1390 }
1391 errno = err_win_to_posix();
1392 return -1;
1393}
1394
1395const char *get_busybox_exec_path(void)
1396{
1397 static char *path = NULL;
1398
1399 if (!path) {
1400 path = xzalloc(PATH_MAX);
1401 }
1402
1403 if (!*path) {
1404 GetModuleFileName(NULL, path, PATH_MAX);
1405 bs_to_slash(path);
1406 }
1407 return path;
1408}
1409
1410#undef mkdir
1411int mingw_mkdir(const char *path, int mode UNUSED_PARAM)
1412{
1413 int ret;
1414 struct stat st;
1415 int lerrno = 0;
1416
1417 if ( (ret=mkdir(path)) < 0 ) {
1418 lerrno = errno;
1419 if ( lerrno == EACCES && stat(path, &st) == 0 ) {
1420 ret = 0;
1421 lerrno = 0;
1422 }
1423 }
1424
1425 errno = lerrno;
1426 return ret;
1427}
1428
1429#undef chdir
1430int mingw_chdir(const char *dirname)
1431{
1432 int ret = -1;
1433 const char *realdir = dirname;
1434
1435 if (is_symlink(dirname)) {
1436 realdir = auto_string(xmalloc_readlink(dirname));
1437 if (realdir)
1438 fix_path_case((char *)realdir);
1439 }
1440
1441 if (realdir)
1442 ret = chdir(realdir);
1443
1444 return ret;
1445}
1446
1447#undef chmod
1448int mingw_chmod(const char *path, int mode)
1449{
1450 WIN32_FILE_ATTRIBUTE_DATA fdata;
1451
1452 if ( get_file_attr(path, &fdata) == 0 &&
1453 fdata.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
1454 mode |= 0222;
1455 }
1456
1457 return chmod(path, mode);
1458}
1459
1460int fcntl(int fd, int cmd, ...)
1461{
1462 va_list arg;
1463 int result = -1;
1464 char *fds;
1465 int target, i, newfd;
1466
1467 va_start(arg, cmd);
1468
1469 switch (cmd) {
1470 case F_GETFD:
1471 case F_SETFD:
1472 case F_GETFL:
1473 /*
1474 * Our fake F_GETFL won't matter if the return value is used as
1475 * fcntl(fd, F_SETFL, ret|something);
1476 * because F_SETFL isn't supported either.
1477 */
1478 result = 0;
1479 break;
1480 case F_DUPFD:
1481 target = va_arg(arg, int);
1482 fds = xzalloc(target);
1483 while ((newfd = dup(fd)) < target && newfd >= 0) {
1484 fds[newfd] = 1;
1485 }
1486 for (i = 0; i < target; ++i) {
1487 if (fds[i]) {
1488 close(i);
1489 }
1490 }
1491 free(fds);
1492 result = newfd;
1493 break;
1494 default:
1495 errno = ENOSYS;
1496 break;
1497 }
1498
1499 va_end(arg);
1500 return result;
1501}
1502
1503#undef unlink
1504#undef rmdir
1505int mingw_unlink(const char *pathname)
1506{
1507 int ret;
1508
1509 /* read-only files cannot be removed */
1510 chmod(pathname, 0666);
1511
1512 ret = unlink(pathname);
1513 if (ret == -1 && errno == EACCES) {
1514 /* a symlink to a directory needs to be removed by calling rmdir */
1515 /* (the *real* Windows rmdir, not mingw_rmdir) */
1516 if (is_symlink(pathname)) {
1517 return rmdir(pathname);
1518 }
1519 }
1520 return ret;
1521}
1522
1523struct pagefile_info {
1524 SIZE_T total;
1525 SIZE_T in_use;
1526};
1527
1528static BOOL CALLBACK
1529pagefile_cb(LPVOID context, PENUM_PAGE_FILE_INFORMATION info,
1530 LPCSTR name UNUSED_PARAM)
1531{
1532 struct pagefile_info *pfinfo = (struct pagefile_info *)context;
1533
1534 pfinfo->total += info->TotalSize;
1535 pfinfo->in_use += info->TotalInUse;
1536 return TRUE;
1537}
1538
1539int sysinfo(struct sysinfo *info)
1540{
1541 PERFORMANCE_INFORMATION perf;
1542 struct pagefile_info pfinfo;
1543 DECLARE_PROC_ADDR(BOOL, GetPerformanceInfo, PPERFORMANCE_INFORMATION,
1544 DWORD);
1545 DECLARE_PROC_ADDR(BOOL, EnumPageFilesA, PENUM_PAGE_FILE_CALLBACKA, LPVOID);
1546
1547 memset((void *)info, 0, sizeof(struct sysinfo));
1548 memset((void *)&perf, 0, sizeof(PERFORMANCE_INFORMATION));
1549 memset((void *)&pfinfo, 0, sizeof(struct pagefile_info));
1550 info->mem_unit = 4096;
1551
1552 if (INIT_PROC_ADDR(psapi.dll, GetPerformanceInfo)) {
1553 perf.cb = sizeof(PERFORMANCE_INFORMATION);
1554 GetPerformanceInfo(&perf, perf.cb);
1555 }
1556
1557 if (INIT_PROC_ADDR(psapi.dll, EnumPageFilesA)) {
1558 EnumPageFilesA((PENUM_PAGE_FILE_CALLBACK)pagefile_cb, (LPVOID)&pfinfo);
1559 }
1560
1561 info->totalram = perf.PhysicalTotal * perf.PageSize / 4096;
1562 info->bufferram = perf.SystemCache * perf.PageSize / 4096;
1563 if (perf.PhysicalAvailable > perf.SystemCache)
1564 info->freeram = perf.PhysicalAvailable * perf.PageSize / 4096 -
1565 info->bufferram;
1566 info->totalswap = pfinfo.total * perf.PageSize / 4096;
1567 info->freeswap = (pfinfo.total - pfinfo.in_use) * perf.PageSize / 4096;
1568
1569 info->uptime = GetTickCount64() / 1000;
1570 info->procs = perf.ProcessCount;
1571
1572 return 0;
1573}
1574
1575#undef strftime
1576size_t mingw_strftime(char *buf, size_t max, const char *format, const struct tm *tm)
1577{
1578 size_t ret;
1579 char buffer[64];
1580 const char *replace;
1581 char *t;
1582 char *fmt;
1583 struct tm tm2;
1584
1585 /*
1586 * Emulate some formats that Windows' strftime lacks.
1587 * - '%e' day of the month with space padding
1588 * - '%s' number of seconds since the Unix epoch
1589 * - '%T' same as %H:%M:%S
1590 * - '%z' timezone offset
1591 * Also, permit the '-' modifier to omit padding. Windows uses '#'.
1592 */
1593 fmt = xstrdup(format);
1594 for ( t=fmt; *t; ++t ) {
1595 if ( *t == '%' ) {
1596 replace = NULL;
1597 if ( t[1] == 'e' ) {
1598 if ( tm->tm_mday >= 0 && tm->tm_mday <= 99 ) {
1599 sprintf(buffer, "%2d", tm->tm_mday);
1600 }
1601 else {
1602 strcpy(buffer, " ");
1603 }
1604 replace = buffer;
1605 }
1606 else if ( t[1] == 's' ) {
1607 tm2 = *tm;
1608 sprintf(buffer, "%d", (int)mktime(&tm2));
1609 replace = buffer;
1610 }
1611 else if ( t[1] == 'T' ) {
1612 replace = "%H:%M:%S";
1613 }
1614 else if ( t[1] == 'z' ) {
1615 _tzset();
1616 if ( tm->tm_isdst >= 0 ) {
1617 int offset = (int)_timezone - (tm->tm_isdst > 0 ? 3600 : 0);
1618 int hr, min;
1619
1620 if ( offset > 0 ) {
1621 buffer[0] = '-';
1622 }
1623 else {
1624 buffer[0] = '+';
1625 offset = -offset;
1626 }
1627
1628 hr = offset / 3600;
1629 min = (offset % 3600) / 60;
1630 sprintf(buffer+1, "%04d", hr*100 + min);
1631 }
1632 else {
1633 buffer[0] = '\0';
1634 }
1635 replace = buffer;
1636 }
1637 else if ( t[1] == '-' && t[2] != '\0' &&
1638 strchr("dHIjmMSUwWyY", t[2]) ) {
1639 /* Microsoft uses '#' rather than '-' to remove padding */
1640 t[1] = '#';
1641 }
1642 else if ( t[1] != '\0' ) {
1643 ++t;
1644 }
1645
1646 if (replace) {
1647 int m;
1648 char *newfmt;
1649
1650 *t = '\0';
1651 m = t - fmt;
1652 newfmt = xasprintf("%s%s%s", fmt, replace, t+2);
1653 free(fmt);
1654 t = newfmt + m + strlen(replace) - 1;
1655 fmt = newfmt;
1656 }
1657 }
1658 }
1659
1660 ret = strftime(buf, max, fmt, tm);
1661 free(fmt);
1662
1663 return ret;
1664}
1665
1666#undef access
1667int mingw_access(const char *name, int mode)
1668{
1669 int ret;
1670 struct stat s;
1671
1672 /* Windows can only handle test for existence, read or write */
1673 if (mode == F_OK || (mode & ~X_OK)) {
1674 ret = _access(name, mode & ~X_OK);
1675 if (ret < 0 || !(mode & X_OK)) {
1676 return ret;
1677 }
1678 }
1679
1680 if (!mingw_stat(name, &s)) {
1681 if ((s.st_mode&S_IXUSR)) {
1682 return 0;
1683 }
1684 errno = EACCES;
1685 }
1686
1687 return -1;
1688}
1689
1690int mingw_rmdir(const char *path)
1691{
1692 /* On Linux rmdir(2) doesn't remove symlinks */
1693 if (is_symlink(path)) {
1694 errno = ENOTDIR;
1695 return -1;
1696 }
1697
1698 /* read-only directories cannot be removed */
1699 chmod(path, 0666);
1700 return rmdir(path);
1701}
1702
1703void mingw_sync(void)
1704{
1705 HANDLE h;
1706 FILE *mnt;
1707 struct mntent *entry;
1708 char name[] = "\\\\.\\C:";
1709
1710 mnt = setmntent(bb_path_mtab_file, "r");
1711 if (mnt) {
1712 while ((entry=getmntent(mnt)) != NULL) {
1713 name[4] = entry->mnt_dir[0];
1714 h = CreateFile(name, GENERIC_READ | GENERIC_WRITE,
1715 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
1716 OPEN_EXISTING, 0, NULL);
1717 if (h != INVALID_HANDLE_VALUE) {
1718 FlushFileBuffers(h);
1719 CloseHandle(h);
1720 }
1721 }
1722 endmntent(mnt);
1723 }
1724}
1725
1726#define NUMEXT 5
1727static const char win_suffix[NUMEXT][4] = { "sh", "com", "exe", "bat", "cmd" };
1728
1729static int has_win_suffix(const char *name, int start)
1730{
1731 const char *dot = strrchr(bb_basename(name), '.');
1732 int i;
1733
1734 if (dot != NULL && strlen(dot) < 5) {
1735 for (i=start; i<NUMEXT; ++i) {
1736 if (!strcasecmp(dot+1, win_suffix[i])) {
1737 return 1;
1738 }
1739 }
1740 }
1741 return 0;
1742}
1743
1744int has_bat_suffix(const char *name)
1745{
1746 return has_win_suffix(name, 3);
1747}
1748
1749int has_exe_suffix(const char *name)
1750{
1751 return has_win_suffix(name, 0);
1752}
1753
1754int has_exe_suffix_or_dot(const char *name)
1755{
1756 return last_char_is(name, '.') || has_win_suffix(name, 0);
1757}
1758
1759/* Check if path can be made into an executable by adding a suffix.
1760 * The suffix is added to the end of the argument which must be
1761 * long enough to allow this.
1762 *
1763 * If the return value is TRUE the argument contains the new path,
1764 * if FALSE the argument is unchanged.
1765 */
1766int add_win32_extension(char *p)
1767{
1768 if (!has_exe_suffix_or_dot(p)) {
1769 int i, len = strlen(p);
1770
1771 p[len] = '.';
1772 for (i=0; i<NUMEXT; ++i) {
1773 strcpy(p+len+1, win_suffix[i]);
1774 if (file_is_executable(p))
1775 return TRUE;
1776 }
1777 p[len] = '\0';
1778 }
1779 return FALSE;
1780}
1781
1782char * FAST_FUNC bs_to_slash(char *str)
1783{
1784 char *p;
1785
1786 for (p=str; *p; ++p) {
1787 if ( *p == '\\' ) {
1788 *p = '/';
1789 }
1790 }
1791 return str;
1792}
1793
1794void FAST_FUNC slash_to_bs(char *p)
1795{
1796 for (; *p; ++p) {
1797 if ( *p == '/' ) {
1798 *p = '\\';
1799 }
1800 }
1801}
1802
1803size_t FAST_FUNC remove_cr(char *p, size_t len)
1804{
1805 ssize_t i, j;
1806
1807 for (i=j=0; i<len; ++i) {
1808 if (p[i] != '\r')
1809 p[j++] = p[i];
1810 }
1811 return j;
1812}
1813
1814off_t mingw_lseek(int fd, off_t offset, int whence)
1815{
1816 HANDLE h = (HANDLE)_get_osfhandle(fd);
1817 if (h == INVALID_HANDLE_VALUE) {
1818 errno = EBADF;
1819 return -1;
1820 }
1821 if (GetFileType(h) != FILE_TYPE_DISK) {
1822 errno = ESPIPE;
1823 return -1;
1824 }
1825 return _lseeki64(fd, offset, whence);
1826}
1827
1828#undef GetTickCount64
1829ULONGLONG CompatGetTickCount64(void)
1830{
1831 DECLARE_PROC_ADDR(ULONGLONG, GetTickCount64, void);
1832
1833 if (!INIT_PROC_ADDR(kernel32.dll, GetTickCount64)) {
1834 return (ULONGLONG)GetTickCount();
1835 }
1836
1837 return GetTickCount64();
1838}
1839
1840#if ENABLE_FEATURE_INSTALLER
1841/*
1842 * Enumerate the names of all hard links to a file. The first call
1843 * provides the file name as the first argument; subsequent calls must
1844 * set the first argument to NULL. Returns 0 on error or when there are
1845 * no more links.
1846 */
1847int enumerate_links(const char *file, char *name)
1848{
1849 static HANDLE h = INVALID_HANDLE_VALUE;
1850 char aname[PATH_MAX];
1851 wchar_t wname[PATH_MAX];
1852 DWORD length = PATH_MAX;
1853 DECLARE_PROC_ADDR(HANDLE, FindFirstFileNameW, LPCWSTR, DWORD, LPDWORD,
1854 PWSTR);
1855 DECLARE_PROC_ADDR(BOOL, FindNextFileNameW, HANDLE, LPDWORD, PWSTR);
1856
1857 if (!INIT_PROC_ADDR(kernel32.dll, FindFirstFileNameW) ||
1858 !INIT_PROC_ADDR(kernel32.dll, FindNextFileNameW))
1859 return 0;
1860
1861 if (file != NULL) {
1862 wchar_t wfile[PATH_MAX];
1863 MultiByteToWideChar(CP_ACP, 0, file, -1, wfile, PATH_MAX);
1864 h = FindFirstFileNameW(wfile, 0, &length, wname);
1865 if (h == INVALID_HANDLE_VALUE)
1866 return 0;
1867 }
1868 else if (!FindNextFileNameW(h, &length, wname)) {
1869 FindClose(h);
1870 h = INVALID_HANDLE_VALUE;
1871 return 0;
1872 }
1873 WideCharToMultiByte(CP_ACP, 0, wname, -1, aname, PATH_MAX, NULL, NULL);
1874 realpath(aname, name);
1875 return 1;
1876}
1877#endif
1878
1879#if ENABLE_ASH_NOCONSOLE
1880void hide_console(void)
1881{
1882 DWORD dummy;
1883 DECLARE_PROC_ADDR(DWORD, GetConsoleProcessList, LPDWORD, DWORD);
1884 DECLARE_PROC_ADDR(BOOL, ShowWindow, HWND, int);
1885
1886 if (INIT_PROC_ADDR(kernel32.dll, GetConsoleProcessList) &&
1887 INIT_PROC_ADDR(user32.dll, ShowWindow)) {
1888 if (GetConsoleProcessList(&dummy, 1) == 1) {
1889 ShowWindow(GetConsoleWindow(), SW_HIDE);
1890 }
1891 }
1892}
1893#endif
1894
1895/* Return the length of the root of a UNC path, i.e. the '//host/share'
1896 * component, or 0 if the path doesn't look like that. */
1897int unc_root_len(const char *dir)
1898{
1899 const char *s = dir + 2;
1900 int len;
1901
1902 if (!is_unc_path(dir))
1903 return 0;
1904 len = strcspn(s, "/\\");
1905 if (len == 0)
1906 return 0;
1907 s += len + 1;
1908 len = strcspn(s, "/\\");
1909 if (len == 0)
1910 return 0;
1911 s += len;
1912
1913 return s - dir;
1914}
1915
1916/* Return the length of the root of a path, i.e. either the drive or
1917 * UNC '//host/share', or 0 if the path doesn't look like that. */
1918int root_len(const char *path)
1919{
1920 if (path == NULL)
1921 return 0;
1922 if (isalpha(*path) && path[1] == ':')
1923 return 2;
1924 return unc_root_len(path);
1925}
1926
1927const char *get_system_drive(void)
1928{
1929 static char *drive = NULL;
1930 int len;
1931
1932 if (drive == NULL) {
1933 const char *sysdir = getsysdir();
1934 if ((len=root_len(sysdir))) {
1935 drive = xstrndup(sysdir, len);
1936 }
1937 }
1938
1939 return getenv("BB_SYSTEMROOT") ?: drive;
1940}
1941
1942/* Return pointer to system drive if path is of form '/file', else NULL */
1943const char *need_system_drive(const char *path)
1944{
1945 if (root_len(path) == 0 && (path[0] == '/' || path[0] == '\\'))
1946 return get_system_drive();
1947 return NULL;
1948}
1949
1950/* Allocate a string long enough to allow a system drive prefix and
1951 * file extension to be added to path. Add the prefix if necessary. */
1952char *alloc_system_drive(const char *path)
1953{
1954 const char *sd = need_system_drive(path);
1955 char *s = xmalloc(strlen(path) + 5 + (sd ? strlen(sd) : 0));
1956 strcpy(stpcpy(s, sd ?: ""), path);
1957 return s;
1958}
1959
1960int chdir_system_drive(void)
1961{
1962 const char *sd = get_system_drive();
1963 int ret = -1;
1964
1965 if (sd)
1966 ret = chdir(auto_string(concat_path_file(sd, "")));
1967 return ret;
1968}
1969
1970/*
1971 * This function is used to make relative paths absolute before a call
1972 * to chdir_system_drive(). It's unlikely to be useful in other cases.
1973 *
1974 * If the argument is an absolute path or a relative path which resolves
1975 * to a path on the system drive return 'path'. If it's a relative path
1976 * which resolves to a path that isn't on the system drive return an
1977 * allocated string containing the resolved path. Die on failure,
1978 * which is most likely because the file doesn't exist.
1979 */
1980char *xabsolute_path(char *path)
1981{
1982 char *rpath;
1983 const char *sd;
1984
1985 if (root_len(path) != 0)
1986 return path; // absolute path
1987 rpath = xmalloc_realpath(path);
1988 if (rpath) {
1989 sd = get_system_drive();
1990 if (sd && is_prefixed_with_case(rpath, sd)) {
1991 free(rpath);
1992 return path; // resolved path is on system drive
1993 }
1994 return rpath;
1995 }
1996 bb_perror_msg_and_die("can't open '%s'", path);
1997}
1998
1999char *get_drive_cwd(const char *path, char *buffer, int size)
2000{
2001 char drive[3] = { *path, ':', '\0' };
2002 DWORD ret;
2003
2004 ret = GetFullPathName(drive, size, buffer, NULL);
2005 if (ret == 0 || ret > size)
2006 return NULL;
2007 return bs_to_slash(buffer);
2008}
2009
2010void fix_path_case(char *path)
2011{
2012 char resolved[PATH_MAX];
2013 int len;
2014
2015 // Canonicalise path: for physical drives this makes case match
2016 // what's stored on disk. For mapped drives, not so much.
2017 if (realpath(path, resolved) && strcasecmp(path, resolved) == 0)
2018 strcpy(path, resolved);
2019
2020 // make drive letter or UNC hostname uppercase
2021 len = root_len(path);
2022 if (len == 2) {
2023 *path = toupper(*path);
2024 }
2025 else if (len != 0) {
2026 for (path+=2; !is_dir_sep(*path); ++path) {
2027 *path = toupper(*path);
2028 }
2029 }
2030}
2031
2032void make_sparse(int fd, off_t start, off_t end)
2033{
2034 DWORD dwTemp;
2035 HANDLE fh;
2036 FILE_ZERO_DATA_INFORMATION fzdi;
2037
2038 if ((fh=(HANDLE)_get_osfhandle(fd)) == INVALID_HANDLE_VALUE)
2039 return;
2040
2041 DeviceIoControl(fh, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwTemp, NULL);
2042
2043 fzdi.FileOffset.QuadPart = start;
2044 fzdi.BeyondFinalZero.QuadPart = end;
2045 DeviceIoControl(fh, FSCTL_SET_ZERO_DATA, &fzdi, sizeof(fzdi),
2046 NULL, 0, &dwTemp, NULL);
2047}
2048
2049void *get_proc_addr(const char *dll, const char *function,
2050 struct proc_addr *proc)
2051{
2052 /* only do this once */
2053 if (!proc->initialized) {
2054 HANDLE hnd = LoadLibraryExA(dll, NULL, LOAD_LIBRARY_SEARCH_SYSTEM32);
2055
2056 /* The documentation for LoadLibraryEx says the above may fail
2057 * on Windows 7. If it does, retry using LoadLibrary with an
2058 * explicit, backslash-separated path. */
2059 if (!hnd) {
2060 char dir[PATH_MAX], *path;
2061
2062 GetSystemDirectory(dir, PATH_MAX);
2063 path = concat_path_file(dir, dll);
2064 slash_to_bs(path);
2065 hnd = LoadLibrary(path);
2066 free(path);
2067 }
2068
2069 if (hnd)
2070 proc->pfunction = GetProcAddress(hnd, function);
2071 proc->initialized = 1;
2072 }
2073 /* set ENOSYS if DLL or function was not found */
2074 if (!proc->pfunction)
2075 errno = ENOSYS;
2076 return proc->pfunction;
2077}
2078
2079#if ENABLE_FEATURE_SH_STANDALONE || ENABLE_FEATURE_PREFER_APPLETS
2080int unix_path(const char *path)
2081{
2082 int i;
2083 char *p = xstrdup(path);
2084
2085#define UNIX_PATHS "/bin\0/usr/bin\0/sbin\0/usr/sbin\0"
2086 i = index_in_strings(UNIX_PATHS, dirname(p));
2087 free(p);
2088 return i >= 0;
2089}
2090#endif
2091
2092/* Return true if file is referenced using a path. This means a path
2093 * look-up isn't required. */
2094int has_path(const char *file)
2095{
2096 return strchr(file, '/') || strchr(file, '\\') ||
2097 has_dos_drive_prefix(file);
2098}
2099
2100/* Test whether a path is relative to a known location (usually the
2101 * current working directory or a symlink). On Unix this is a path
2102 * that doesn't start with a slash but on Windows we also need to
2103 * exclude paths that start with a backslash or a drive letter. */
2104int is_relative_path(const char *path)
2105{
2106 return !is_dir_sep(path[0]) && !has_dos_drive_prefix(path);
2107}
diff --git a/win32/mntent.c b/win32/mntent.c
new file mode 100644
index 000000000..7f142b485
--- /dev/null
+++ b/win32/mntent.c
@@ -0,0 +1,94 @@
1/*
2 * A simple WIN32 implementation of mntent routines. It only handles
3 * logical drives.
4 */
5#define MNTENT_PRIVATE
6#include "libbb.h"
7
8struct mntstate {
9 DWORD drives;
10 int index;
11};
12
13int fill_mntdata(struct mntdata *data, int index)
14{
15 UINT drive_type;
16 char buf[PATH_MAX];
17
18 // initialise pointers and scalar data
19 data->me.mnt_fsname = data->mnt_fsname;
20 data->me.mnt_dir = data->mnt_dir;
21 data->me.mnt_type = data->mnt_type;
22 data->me.mnt_opts = data->mnt_opts;
23 data->me.mnt_freq = 0;
24 data->me.mnt_passno = 0;
25
26 // initialise strings
27 data->mnt_fsname[0] = 'A' + index;
28 data->mnt_fsname[1] = ':';
29 data->mnt_fsname[2] = '\0';
30 data->mnt_dir[0] = 'A' + index;
31 data->mnt_dir[1] = ':';
32 data->mnt_dir[2] = '/';
33 data->mnt_dir[3] = '\0';
34 data->mnt_type[0] = '\0';
35 data->mnt_opts[0] = '\0';
36
37 drive_type = GetDriveType(data->mnt_dir);
38 if (drive_type == DRIVE_FIXED || drive_type == DRIVE_CDROM ||
39 drive_type == DRIVE_REMOVABLE || drive_type == DRIVE_REMOTE) {
40 if (!GetVolumeInformation(data->mnt_dir, NULL, 0, NULL, NULL,
41 NULL, data->mnt_type, 100)) {
42 return FALSE;
43 }
44
45 if (realpath(data->mnt_dir, buf) != NULL) {
46 if (isalpha(buf[0]) && strcmp(buf+1, ":/") == 0)
47 buf[2] = '\0';
48 strcpy(data->mnt_fsname, buf);
49 }
50 return TRUE;
51 }
52 return FALSE;
53}
54
55FILE *mingw_setmntent(void)
56{
57 struct mntstate *state;
58
59 if ( (state=malloc(sizeof(struct mntstate))) == NULL ) {
60 return NULL;
61 }
62
63 state->drives = GetLogicalDrives();
64 state->index = -1;
65
66 return (FILE *)state;
67}
68
69struct mntent *getmntent(FILE *stream)
70{
71 struct mntstate *state = (struct mntstate *)stream;
72 static struct mntdata *data = NULL;
73 struct mntent *entry = NULL;
74
75 while (++state->index < 26) {
76 if ((state->drives & 1 << state->index) != 0) {
77 if (data == NULL)
78 data = xmalloc(sizeof(*data));
79
80 if (fill_mntdata(data, state->index)) {
81 entry = &data->me;
82 break;
83 }
84 }
85 }
86
87 return entry;
88}
89
90int endmntent(FILE *stream)
91{
92 free(stream);
93 return 0;
94}
diff --git a/win32/mntent.h b/win32/mntent.h
new file mode 100644
index 000000000..029f18b96
--- /dev/null
+++ b/win32/mntent.h
@@ -0,0 +1,33 @@
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# if defined(MNTENT_PRIVATE)
20struct mntdata {
21 struct mntent me;
22 char mnt_fsname[PATH_MAX];
23 char mnt_dir[4];
24 char mnt_type[100];
25 char mnt_opts[4];
26};
27
28extern int fill_mntdata(struct mntdata *data, int index);
29# endif
30
31#define setmntent(f, m) mingw_setmntent()
32
33#endif
diff --git a/win32/net.c b/win32/net.c
new file mode 100644
index 000000000..33dc837fa
--- /dev/null
+++ b/win32/net.c
@@ -0,0 +1,146 @@
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 static int initialized = 0;
17
18 if (initialized)
19 return;
20
21 if (WSAStartup(MAKEWORD(2,2), &wsa))
22 bb_error_msg_and_die("WSAStartup failed, error %d", WSAGetLastError());
23
24 atexit((void(*)(void)) WSACleanup);
25 initialized = 1;
26}
27
28#undef gethostname
29int mingw_gethostname(char *name, int namelen)
30{
31 init_winsock();
32 return gethostname(name, namelen);
33}
34
35#undef gethostbyaddr
36struct hostent *mingw_gethostbyaddr(const void *addr, socklen_t len, int type)
37{
38 init_winsock();
39 return gethostbyaddr(addr, len, type);
40}
41
42#undef getaddrinfo
43int mingw_getaddrinfo(const char *node, const char *service,
44 const struct addrinfo *hints, struct addrinfo **res)
45{
46 init_winsock();
47 return getaddrinfo(node, service, hints, res);
48}
49
50int mingw_socket(int domain, int type, int protocol)
51{
52 int sockfd;
53 SOCKET s;
54
55 init_winsock();
56 s = WSASocket(domain, type, protocol, NULL, 0, 0);
57 if (s == INVALID_SOCKET) {
58 /*
59 * WSAGetLastError() values are regular BSD error codes
60 * biased by WSABASEERR.
61 * However, strerror() does not know about networking
62 * specific errors, which are values beginning at 38 or so.
63 * Therefore, we choose to leave the biased error code
64 * in errno so that _if_ someone looks up the code somewhere,
65 * then it is at least the number that are usually listed.
66 */
67 errno = WSAGetLastError();
68 return -1;
69 }
70 /* convert into a file descriptor */
71 if ((sockfd = _open_osfhandle((intptr_t)s, O_RDWR|O_BINARY)) < 0) {
72 closesocket(s);
73 bb_error_msg("unable to make a socket file descriptor: %s",
74 strerror(errno));
75 return -1;
76 }
77 return sockfd;
78}
79
80#undef connect
81int mingw_connect(int sockfd, const struct sockaddr *sa, size_t sz)
82{
83 SOCKET s = (SOCKET)_get_osfhandle(sockfd);
84 return connect(s, sa, sz);
85}
86
87#undef bind
88int mingw_bind(int sockfd, struct sockaddr *sa, size_t sz)
89{
90 SOCKET s = (SOCKET)_get_osfhandle(sockfd);
91 return bind(s, sa, sz);
92}
93
94#undef setsockopt
95int mingw_setsockopt(int sockfd, int lvl, int optname, void *optval, int optlen)
96{
97 SOCKET s = (SOCKET)_get_osfhandle(sockfd);
98 return setsockopt(s, lvl, optname, (const char*)optval, optlen);
99}
100
101#undef shutdown
102int mingw_shutdown(int sockfd, int how)
103{
104 SOCKET s = (SOCKET)_get_osfhandle(sockfd);
105 return shutdown(s, how);
106}
107
108#undef listen
109int mingw_listen(int sockfd, int backlog)
110{
111 SOCKET s = (SOCKET)_get_osfhandle(sockfd);
112 return listen(s, backlog);
113}
114
115#undef accept
116int mingw_accept(int sockfd1, struct sockaddr *sa, socklen_t *sz)
117{
118 int sockfd2;
119
120 SOCKET s1 = (SOCKET)_get_osfhandle(sockfd1);
121 SOCKET s2 = accept(s1, sa, sz);
122
123 /* convert into a file descriptor */
124 if ((sockfd2 = _open_osfhandle((intptr_t)s2, O_RDWR|O_BINARY)) < 0) {
125 int err = errno;
126 closesocket(s2);
127 bb_error_msg("unable to make a socket file descriptor: %s",
128 strerror(err));
129 return -1;
130 }
131 return sockfd2;
132}
133
134#undef getpeername
135int mingw_getpeername(int fd, struct sockaddr *sa, socklen_t *sz)
136{
137 SOCKET sock;
138
139 init_winsock();
140 sock = (SOCKET)_get_osfhandle(fd);
141 if (sock == INVALID_SOCKET) {
142 errno = EBADF;
143 return -1;
144 }
145 return getpeername(sock, sa, sz);
146}
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..8f16d93e9
--- /dev/null
+++ b/win32/poll.c
@@ -0,0 +1,602 @@
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 "libbb.h"
27#include <malloc.h>
28
29#include <sys/types.h>
30
31/* Specification. */
32#include <poll.h>
33
34#include <errno.h>
35#include <limits.h>
36#include <assert.h>
37
38#if (defined _WIN32 || defined __WIN32__) && ! defined __CYGWIN__
39# define WINDOWS_NATIVE
40# define WIN32_NATIVE
41# include <winsock2.h>
42# include <windows.h>
43# include <io.h>
44# include <stdio.h>
45# include <conio.h>
46#else
47# include <sys/time.h>
48# include <unistd.h>
49#endif
50
51#include <sys/select.h>
52#include <sys/socket.h>
53
54#ifdef HAVE_SYS_IOCTL_H
55# include <sys/ioctl.h>
56#endif
57#ifdef HAVE_SYS_FILIO_H
58# include <sys/filio.h>
59#endif
60
61#include <time.h>
62
63#ifndef INFTIM
64# define INFTIM (-1)
65#endif
66
67/* BeOS does not have MSG_PEEK. */
68#ifndef MSG_PEEK
69# define MSG_PEEK 0
70#endif
71
72#ifdef WINDOWS_NATIVE
73
74/* Here we need the recv() function from Windows, that takes a SOCKET as
75 first argument, not any possible gnulib override. */
76# undef recv
77
78/* Here we need the select() function from Windows, because we pass bit masks
79 of SOCKETs, not bit masks of FDs. */
80# undef select
81
82static BOOL IsConsoleHandle (HANDLE h)
83{
84 DWORD mode;
85 return GetConsoleMode (h, &mode) != 0;
86}
87
88static BOOL
89IsSocketHandle (HANDLE h)
90{
91 WSANETWORKEVENTS ev;
92
93 if (IsConsoleHandle (h))
94 return FALSE;
95
96 /* Under Wine, it seems that getsockopt returns 0 for pipes too.
97 WSAEnumNetworkEvents instead distinguishes the two correctly. */
98 ev.lNetworkEvents = 0xDEADBEEF;
99 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
100 return ev.lNetworkEvents != 0xDEADBEEF;
101}
102
103/* Declare data structures for ntdll functions. */
104typedef struct _FILE_PIPE_LOCAL_INFORMATION {
105 ULONG NamedPipeType;
106 ULONG NamedPipeConfiguration;
107 ULONG MaximumInstances;
108 ULONG CurrentInstances;
109 ULONG InboundQuota;
110 ULONG ReadDataAvailable;
111 ULONG OutboundQuota;
112 ULONG WriteQuotaAvailable;
113 ULONG NamedPipeState;
114 ULONG NamedPipeEnd;
115} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
116
117typedef struct _IO_STATUS_BLOCK
118{
119 union {
120 DWORD Status;
121 PVOID Pointer;
122 } u;
123 ULONG_PTR Information;
124} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
125
126typedef enum _FILE_INFORMATION_CLASS {
127 FilePipeLocalInformation = 24
128} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
129
130typedef DWORD (WINAPI *PNtQueryInformationFile)
131 (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
132
133# ifndef PIPE_BUF
134# define PIPE_BUF 512
135# endif
136
137/* Compute revents values for file handle H. If some events cannot happen
138 for the handle, eliminate them from *P_SOUGHT. */
139
140static int
141windows_compute_revents (HANDLE h, int *p_sought)
142{
143 int i, ret, happened;
144 INPUT_RECORD *irbuffer;
145 DWORD avail, nbuffer;
146 BOOL bRet;
147 IO_STATUS_BLOCK iosb;
148 FILE_PIPE_LOCAL_INFORMATION fpli;
149 static PNtQueryInformationFile NtQueryInformationFile;
150 static BOOL once_only;
151
152 switch (GetFileType (h))
153 {
154 case FILE_TYPE_PIPE:
155 if (!once_only)
156 {
157 NtQueryInformationFile = (PNtQueryInformationFile)
158 GetProcAddress (GetModuleHandle ("ntdll.dll"),
159 "NtQueryInformationFile");
160 once_only = TRUE;
161 }
162
163 happened = 0;
164 if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
165 {
166 if (avail)
167 happened |= *p_sought & (POLLIN | POLLRDNORM);
168 }
169 else if (GetLastError () == ERROR_BROKEN_PIPE)
170 happened |= POLLHUP;
171
172 else
173 {
174 /* It was the write-end of the pipe. Check if it is writable.
175 If NtQueryInformationFile fails, optimistically assume the pipe is
176 writable. This could happen on Windows 9x, where
177 NtQueryInformationFile is not available, or if we inherit a pipe
178 that doesn't permit FILE_READ_ATTRIBUTES access on the write end
179 (I think this should not happen since Windows XP SP2; WINE seems
180 fine too). Otherwise, ensure that enough space is available for
181 atomic writes. */
182 memset (&iosb, 0, sizeof (iosb));
183 memset (&fpli, 0, sizeof (fpli));
184
185 if (!NtQueryInformationFile
186 || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
187 FilePipeLocalInformation)
188 || fpli.WriteQuotaAvailable >= PIPE_BUF
189 || (fpli.OutboundQuota < PIPE_BUF &&
190 fpli.WriteQuotaAvailable == fpli.OutboundQuota))
191 happened |= *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
192 }
193 return happened;
194
195 case FILE_TYPE_CHAR:
196 ret = WaitForSingleObject (h, 0);
197 if (!IsConsoleHandle (h))
198 return ret == WAIT_OBJECT_0 ? *p_sought & ~(POLLPRI | POLLRDBAND) : 0;
199
200 nbuffer = avail = 0;
201 bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
202 if (bRet)
203 {
204 /* Input buffer. */
205 *p_sought &= POLLIN | POLLRDNORM;
206 if (nbuffer == 0)
207 return POLLHUP;
208 if (!*p_sought)
209 return 0;
210
211 irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
212 bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
213 if (!bRet || avail == 0)
214 return POLLHUP;
215
216 for (i = 0; i < avail; i++)
217 if (irbuffer[i].EventType == KEY_EVENT)
218 return *p_sought;
219 return 0;
220 }
221 else
222 {
223 /* Screen buffer. */
224 *p_sought &= POLLOUT | POLLWRNORM | POLLWRBAND;
225 return *p_sought;
226 }
227
228 default:
229 ret = WaitForSingleObject (h, 0);
230 if (ret == WAIT_OBJECT_0)
231 return *p_sought & ~(POLLPRI | POLLRDBAND);
232
233 return *p_sought & (POLLOUT | POLLWRNORM | POLLWRBAND);
234 }
235}
236
237/* Convert fd_sets returned by select into revents values. */
238
239static int
240windows_compute_revents_socket (SOCKET h, int sought, long lNetworkEvents)
241{
242 int happened = 0;
243
244 if ((lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE)) == FD_ACCEPT)
245 happened |= (POLLIN | POLLRDNORM) & sought;
246
247 else if (lNetworkEvents & (FD_READ | FD_ACCEPT | FD_CLOSE))
248 {
249 int r, error;
250
251 char data[64];
252 WSASetLastError (0);
253 r = recv (h, data, sizeof (data), MSG_PEEK);
254 error = WSAGetLastError ();
255 WSASetLastError (0);
256
257 if (r > 0 || error == WSAENOTCONN)
258 happened |= (POLLIN | POLLRDNORM) & sought;
259
260 /* Distinguish hung-up sockets from other errors. */
261 else if (r == 0 || error == WSAESHUTDOWN || error == WSAECONNRESET
262 || error == WSAECONNABORTED || error == WSAENETRESET)
263 happened |= POLLHUP;
264
265 else
266 happened |= POLLERR;
267 }
268
269 if (lNetworkEvents & (FD_WRITE | FD_CONNECT))
270 happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
271
272 if (lNetworkEvents & FD_OOB)
273 happened |= (POLLPRI | POLLRDBAND) & sought;
274
275 return happened;
276}
277
278#else /* !MinGW */
279
280/* Convert select(2) returned fd_sets into poll(2) revents values. */
281static int
282compute_revents (int fd, int sought, fd_set *rfds, fd_set *wfds, fd_set *efds)
283{
284 int happened = 0;
285 if (FD_ISSET (fd, rfds))
286 {
287 int r;
288 int socket_errno;
289
290# if defined __MACH__ && defined __APPLE__
291 /* There is a bug in Mac OS X that causes it to ignore MSG_PEEK
292 for some kinds of descriptors. Detect if this descriptor is a
293 connected socket, a server socket, or something else using a
294 0-byte recv, and use ioctl(2) to detect POLLHUP. */
295 r = recv (fd, NULL, 0, MSG_PEEK);
296 socket_errno = (r < 0) ? errno : 0;
297 if (r == 0 || socket_errno == ENOTSOCK)
298 ioctl (fd, FIONREAD, &r);
299# else
300 char data[64];
301 r = recv (fd, data, sizeof (data), MSG_PEEK);
302 socket_errno = (r < 0) ? errno : 0;
303# endif
304 if (r == 0)
305 happened |= POLLHUP;
306
307 /* If the event happened on an unconnected server socket,
308 that's fine. */
309 else if (r > 0 || ( /* (r == -1) && */ socket_errno == ENOTCONN))
310 happened |= (POLLIN | POLLRDNORM) & sought;
311
312 /* Distinguish hung-up sockets from other errors. */
313 else if (socket_errno == ESHUTDOWN || socket_errno == ECONNRESET
314 || socket_errno == ECONNABORTED || socket_errno == ENETRESET)
315 happened |= POLLHUP;
316
317 /* some systems can't use recv() on non-socket, including HP NonStop */
318 else if (socket_errno == ENOTSOCK)
319 happened |= (POLLIN | POLLRDNORM) & sought;
320
321 else
322 happened |= POLLERR;
323 }
324
325 if (FD_ISSET (fd, wfds))
326 happened |= (POLLOUT | POLLWRNORM | POLLWRBAND) & sought;
327
328 if (FD_ISSET (fd, efds))
329 happened |= (POLLPRI | POLLRDBAND) & sought;
330
331 return happened;
332}
333#endif /* !MinGW */
334
335int
336poll (struct pollfd *pfd, nfds_t nfd, int timeout)
337{
338#ifndef WINDOWS_NATIVE
339 fd_set rfds, wfds, efds;
340 struct timeval tv;
341 struct timeval *ptv;
342 int maxfd, rc;
343 nfds_t i;
344
345 if (nfd > INT_MAX)
346 {
347 errno = EINVAL;
348 return -1;
349 }
350 /* Don't check directly for NFD greater than OPEN_MAX. Any practical use
351 of a too-large NFD is caught by one of the other checks below, and
352 checking directly for getdtablesize is too much of a portability
353 and/or performance and/or correctness hassle. */
354
355 /* EFAULT is not necessary to implement, but let's do it in the
356 simplest case. */
357 if (!pfd && nfd)
358 {
359 errno = EFAULT;
360 return -1;
361 }
362
363 /* convert timeout number into a timeval structure */
364 if (timeout == 0)
365 {
366 ptv = &tv;
367 ptv->tv_sec = 0;
368 ptv->tv_usec = 0;
369 }
370 else if (timeout > 0)
371 {
372 ptv = &tv;
373 ptv->tv_sec = timeout / 1000;
374 ptv->tv_usec = (timeout % 1000) * 1000;
375 }
376 else if (timeout == INFTIM)
377 /* wait forever */
378 ptv = NULL;
379 else
380 {
381 errno = EINVAL;
382 return -1;
383 }
384
385 /* create fd sets and determine max fd */
386 maxfd = -1;
387 FD_ZERO (&rfds);
388 FD_ZERO (&wfds);
389 FD_ZERO (&efds);
390 for (i = 0; i < nfd; i++)
391 {
392 if (pfd[i].fd < 0)
393 continue;
394 if (maxfd < pfd[i].fd)
395 {
396 maxfd = pfd[i].fd;
397 if (FD_SETSIZE <= maxfd)
398 {
399 errno = EINVAL;
400 return -1;
401 }
402 }
403 if (pfd[i].events & (POLLIN | POLLRDNORM))
404 FD_SET (pfd[i].fd, &rfds);
405
406 /* see select(2): "the only exceptional condition detectable
407 is out-of-band data received on a socket", hence we push
408 POLLWRBAND events onto wfds instead of efds. */
409 if (pfd[i].events & (POLLOUT | POLLWRNORM | POLLWRBAND))
410 FD_SET (pfd[i].fd, &wfds);
411 if (pfd[i].events & (POLLPRI | POLLRDBAND))
412 FD_SET (pfd[i].fd, &efds);
413 }
414
415 /* examine fd sets */
416 rc = select (maxfd + 1, &rfds, &wfds, &efds, ptv);
417 if (rc < 0)
418 return rc;
419
420 /* establish results */
421 rc = 0;
422 for (i = 0; i < nfd; i++)
423 {
424 pfd[i].revents = (pfd[i].fd < 0
425 ? 0
426 : compute_revents (pfd[i].fd, pfd[i].events,
427 &rfds, &wfds, &efds));
428 rc += pfd[i].revents != 0;
429 }
430
431 return rc;
432#else
433 static struct timeval tv0;
434 static HANDLE hEvent;
435 WSANETWORKEVENTS ev;
436 HANDLE h, handle_array[FD_SETSIZE + 2];
437 DWORD ret, wait_timeout, nhandles;
438 fd_set rfds, wfds, xfds;
439 BOOL poll_again;
440 MSG msg;
441 int rc = 0;
442 nfds_t i;
443
444 if (nfd > INT_MAX || timeout < -1)
445 {
446 errno = EINVAL;
447 return -1;
448 }
449
450 if (!hEvent)
451 hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
452
453restart:
454 handle_array[0] = hEvent;
455 nhandles = 1;
456 FD_ZERO (&rfds);
457 FD_ZERO (&wfds);
458 FD_ZERO (&xfds);
459
460 /* Classify socket handles and create fd sets. */
461 for (i = 0; i < nfd; i++)
462 {
463 int sought = pfd[i].events;
464 pfd[i].revents = 0;
465 if (pfd[i].fd < 0)
466 continue;
467 if (!(sought & (POLLIN | POLLRDNORM | POLLOUT | POLLWRNORM | POLLWRBAND
468 | POLLPRI | POLLRDBAND)))
469 continue;
470
471 h = (HANDLE) _get_osfhandle (pfd[i].fd);
472 assert (h != NULL);
473 if (IsSocketHandle (h))
474 {
475 int requested = FD_CLOSE;
476
477 /* see above; socket handles are mapped onto select. */
478 if (sought & (POLLIN | POLLRDNORM))
479 {
480 requested |= FD_READ | FD_ACCEPT;
481 FD_SET ((SOCKET) h, &rfds);
482 }
483 if (sought & (POLLOUT | POLLWRNORM | POLLWRBAND))
484 {
485 requested |= FD_WRITE | FD_CONNECT;
486 FD_SET ((SOCKET) h, &wfds);
487 }
488 if (sought & (POLLPRI | POLLRDBAND))
489 {
490 requested |= FD_OOB;
491 FD_SET ((SOCKET) h, &xfds);
492 }
493
494 if (requested)
495 WSAEventSelect ((SOCKET) h, hEvent, requested);
496 }
497 else
498 {
499 /* Poll now. If we get an event, do not poll again. Also,
500 screen buffer handles are waitable, and they'll block until
501 a character is available. windows_compute_revents eliminates
502 bits for the "wrong" direction. */
503 pfd[i].revents = windows_compute_revents (h, &sought);
504 if (sought)
505 handle_array[nhandles++] = h;
506 if (pfd[i].revents)
507 timeout = 0;
508 }
509 }
510
511 if (select (0, &rfds, &wfds, &xfds, &tv0) > 0)
512 {
513 /* Do MsgWaitForMultipleObjects anyway to dispatch messages, but
514 no need to call select again. */
515 poll_again = FALSE;
516 wait_timeout = 0;
517 }
518 else
519 {
520 poll_again = TRUE;
521 if (timeout == INFTIM)
522 wait_timeout = INFINITE;
523 else
524 wait_timeout = timeout;
525 }
526
527 for (;;)
528 {
529 ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
530 wait_timeout, QS_ALLINPUT);
531
532 if (ret == WAIT_OBJECT_0 + nhandles)
533 {
534 /* new input of some other kind */
535 BOOL bRet;
536 while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
537 {
538 TranslateMessage (&msg);
539 DispatchMessage (&msg);
540 }
541 }
542 else
543 break;
544 }
545
546 if (poll_again)
547 select (0, &rfds, &wfds, &xfds, &tv0);
548
549 /* Place a sentinel at the end of the array. */
550 handle_array[nhandles] = NULL;
551 nhandles = 1;
552 for (i = 0; i < nfd; i++)
553 {
554 int happened;
555
556 if (pfd[i].fd < 0)
557 continue;
558 if (!(pfd[i].events & (POLLIN | POLLRDNORM |
559 POLLOUT | POLLWRNORM | POLLWRBAND)))
560 continue;
561
562 h = (HANDLE) _get_osfhandle (pfd[i].fd);
563 if (h != handle_array[nhandles])
564 {
565 /* It's a socket. */
566 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
567 WSAEventSelect ((SOCKET) h, 0, 0);
568
569 /* If we're lucky, WSAEnumNetworkEvents already provided a way
570 to distinguish FD_READ and FD_ACCEPT; this saves a recv later. */
571 if (FD_ISSET ((SOCKET) h, &rfds)
572 && !(ev.lNetworkEvents & (FD_READ | FD_ACCEPT)))
573 ev.lNetworkEvents |= FD_READ | FD_ACCEPT;
574 if (FD_ISSET ((SOCKET) h, &wfds))
575 ev.lNetworkEvents |= FD_WRITE | FD_CONNECT;
576 if (FD_ISSET ((SOCKET) h, &xfds))
577 ev.lNetworkEvents |= FD_OOB;
578
579 happened = windows_compute_revents_socket ((SOCKET) h, pfd[i].events,
580 ev.lNetworkEvents);
581 }
582 else
583 {
584 /* Not a socket. */
585 int sought = pfd[i].events;
586 happened = windows_compute_revents (h, &sought);
587 nhandles++;
588 }
589
590 if ((pfd[i].revents |= happened) != 0)
591 rc++;
592 }
593
594 if (!rc && timeout == INFTIM)
595 {
596 SleepEx (1, TRUE);
597 goto restart;
598 }
599
600 return rc;
601#endif
602}
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..c9ea00ffd
--- /dev/null
+++ b/win32/popen.c
@@ -0,0 +1,336 @@
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}
309
310/* Used with mode "w" and a compressor when creating a compressed tar
311 * file; with mode "r" and a decompressor in open_transformer. */
312pid_t mingw_fork_compressor(int fd, const char *compressor, const char *mode)
313{
314 char *cmd;
315 int fd1;
316 pid_t pid;
317
318 if (find_applet_by_name(compressor) < 0
319#if ENABLE_XZ || ENABLE_LZMA
320 /* xz and lzma applets don't support compression, try using
321 * an external program */
322 || (mode[0] == 'w' && index_in_strings("lzma\0xz\0", compressor) >= 0)
323#endif
324 )
325 cmd = xasprintf("%s -cf -", compressor);
326 else
327 cmd = xasprintf("%s --busybox %s -cf -", bb_busybox_exec_path,
328 compressor);
329
330 if ((fd1 = mingw_popen_fd(cmd, mode, fd, &pid)) == -1)
331 bb_perror_msg_and_die("can't execute '%s'", compressor);
332
333 free(cmd);
334 xmove_fd(fd1, fd);
335 return pid;
336}
diff --git a/win32/process.c b/win32/process.c
new file mode 100644
index 000000000..fb3c5b369
--- /dev/null
+++ b/win32/process.c
@@ -0,0 +1,806 @@
1#include "libbb.h"
2#include <tlhelp32.h>
3#include <psapi.h>
4#include "lazyload.h"
5
6pid_t waitpid(pid_t pid, int *status, int options)
7#if ENABLE_TIME
8{
9 return mingw_wait3(pid, status, options, NULL);
10}
11#endif
12
13#if ENABLE_TIME
14pid_t mingw_wait3(pid_t pid, int *status, int options, struct rusage *rusage)
15#endif
16{
17 HANDLE proc;
18 DWORD code;
19
20 /* Windows does not understand parent-child */
21 if (pid > 0 && options == 0) {
22 if ( (proc=OpenProcess(SYNCHRONIZE|PROCESS_QUERY_INFORMATION,
23 FALSE, pid)) != NULL ) {
24 WaitForSingleObject(proc, INFINITE);
25 GetExitCodeProcess(proc, &code);
26#if ENABLE_TIME
27 if (rusage != NULL) {
28 FILETIME crTime, exTime, keTime, usTime;
29
30 memset(rusage, 0, sizeof(*rusage));
31 if (GetProcessTimes(proc, &crTime, &exTime, &keTime, &usTime)) {
32 uint64_t kernel_usec =
33 (((uint64_t)keTime.dwHighDateTime << 32)
34 | (uint64_t)keTime.dwLowDateTime)/10;
35 uint64_t user_usec =
36 (((uint64_t)usTime.dwHighDateTime << 32)
37 | (uint64_t)usTime.dwLowDateTime)/10;
38
39 rusage->ru_utime.tv_sec = user_usec / 1000000U;
40 rusage->ru_utime.tv_usec = user_usec % 1000000U;
41 rusage->ru_stime.tv_sec = kernel_usec / 1000000U;
42 rusage->ru_stime.tv_usec = kernel_usec % 1000000U;
43 }
44 }
45#endif
46 CloseHandle(proc);
47 *status = code << 8;
48 return pid;
49 }
50 }
51 errno = pid < 0 ? ENOSYS : EINVAL;
52 return -1;
53}
54
55typedef struct {
56 char *path;
57 char *name;
58 char *opts;
59 char buf[100];
60} interp_t;
61
62static int
63parse_interpreter(const char *cmd, interp_t *interp)
64{
65 char *path, *t;
66 int n;
67
68 while (TRUE) {
69 n = open_read_close(cmd, interp->buf, sizeof(interp->buf)-1);
70 if (n < 4) /* at least '#!/x' and not error */
71 break;
72
73 /*
74 * See http://www.in-ulm.de/~mascheck/various/shebang/ for trivia
75 * relating to '#!'. See also https://lwn.net/Articles/630727/
76 * for Linux-specific details.
77 */
78 if (interp->buf[0] != '#' || interp->buf[1] != '!')
79 break;
80 interp->buf[n] = '\0';
81 if ((t=strchr(interp->buf, '\n')) == NULL)
82 break;
83 t[1] = '\0';
84
85 if ((path=strtok(interp->buf+2, " \t\r\n")) == NULL)
86 break;
87
88 t = (char *)bb_basename(path);
89 if (*t == '\0')
90 break;
91
92 interp->path = path;
93 interp->name = t;
94 interp->opts = strtok(NULL, "\r\n");
95 /* Trim leading and trailing whitespace from the options.
96 * If the resulting string is empty return a NULL pointer. */
97 if (interp->opts && trim(interp->opts) == interp->opts)
98 interp->opts = NULL;
99 return 1;
100 }
101
102 if (n >= 0 && is_suffixed_with_case(cmd, ".sh")) {
103 interp->path = (char *)DEFAULT_SHELL;
104 interp->name = (char *)DEFAULT_SHELL_SHORT_NAME;
105 interp->opts = NULL;
106 return 1;
107 }
108 return 0;
109}
110
111/*
112 * See https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=vs-2019#parsing-c-command-line-arguments
113 * (Parsing C++ Command-Line Arguments)
114 */
115static char *
116quote_arg(const char *arg)
117{
118 int len = 0, n = 0;
119 int force_quotes = 0;
120 char *q, *d;
121 const char *p = arg;
122
123 /* empty arguments must be quoted */
124 if (!*p) {
125 force_quotes = 1;
126 }
127
128 while (*p) {
129 if (isspace(*p)) {
130 /* arguments containing whitespace must be quoted */
131 force_quotes = 1;
132 }
133 else if (*p == '"') {
134 /* double quotes in arguments need to be escaped */
135 n++;
136 }
137 else if (*p == '\\') {
138 /* count contiguous backslashes */
139 int count = 0;
140 while (*p == '\\') {
141 count++;
142 p++;
143 len++;
144 }
145
146 /*
147 * Only escape backslashes before explicit double quotes or
148 * or where the backslashes are at the end of an argument
149 * that is scheduled to be quoted.
150 */
151 if (*p == '"' || (force_quotes && *p == '\0')) {
152 n += count*2 + 1;
153 }
154
155 if (*p == '\0') {
156 break;
157 }
158 continue;
159 }
160 len++;
161 p++;
162 }
163
164 if (!force_quotes && n == 0) {
165 return (char*)arg;
166 }
167
168 /* insert double quotes and backslashes where necessary */
169 d = q = xmalloc(len+n+3);
170 if (force_quotes) {
171 *d++ = '"';
172 }
173
174 while (*arg) {
175 if (*arg == '"') {
176 *d++ = '\\';
177 }
178 else if (*arg == '\\') {
179 int count = 0;
180 while (*arg == '\\') {
181 count++;
182 *d++ = *arg++;
183 }
184
185 if (*arg == '"' || (force_quotes && *arg == '\0')) {
186 while (count-- > 0) {
187 *d++ = '\\';
188 }
189 if (*arg == '"') {
190 *d++ = '\\';
191 }
192 }
193 }
194 if (*arg != '\0') {
195 *d++ = *arg++;
196 }
197 }
198 if (force_quotes) {
199 *d++ = '"';
200 }
201 *d = '\0';
202
203 return q;
204}
205
206static char *
207find_first_executable(const char *name)
208{
209 char *tmp, *path = getenv("PATH");
210 char *exe_path = NULL;
211
212 if (path) {
213 tmp = path = xstrdup(path);
214 exe_path = find_executable(name, &tmp);
215 free(path);
216 }
217
218 return exe_path;
219}
220
221static intptr_t
222spawnveq(int mode, const char *path, char *const *argv, char *const *env)
223{
224 char **new_argv;
225 char *new_path = NULL;
226 int i, argc;
227 intptr_t ret;
228 struct stat st;
229
230 /*
231 * Require that the file exists, is a regular file and is executable.
232 * It may still contain garbage but we let spawnve deal with that.
233 */
234 if (stat(path, &st) == 0) {
235 if (!S_ISREG(st.st_mode) || !(st.st_mode&S_IXUSR)) {
236 errno = EACCES;
237 return -1;
238 }
239 }
240 else {
241 return -1;
242 }
243
244 argc = string_array_len((char **)argv);
245 new_argv = xzalloc(sizeof(*argv)*(argc+1));
246 for (i = 0;i < argc;i++)
247 new_argv[i] = quote_arg(argv[i]);
248
249 /* Special case: spawnve won't execute a batch file if the first
250 * argument is a relative path containing forward slashes. Absolute
251 * paths are fine but there's no harm in converting them too. */
252 if (has_bat_suffix(path)) {
253 slash_to_bs(new_argv[0]);
254
255 /* Another special case: spawnve returns ENOEXEC when passed an
256 * empty batch file. Pretend it worked. */
257 if (st.st_size == 0) {
258 ret = 0;
259 goto done;
260 }
261 }
262
263 /*
264 * Another special case: if a file doesn't have an extension add
265 * a '.' at the end. This forces spawnve to use precisely the
266 * file specified without trying to add an extension.
267 */
268 if (!strchr(bb_basename(path), '.')) {
269 new_path = xasprintf("%s.", path);
270 }
271
272 errno = 0;
273 ret = spawnve(mode, new_path ? new_path : path, new_argv, env);
274
275 done:
276 for (i = 0;i < argc;i++)
277 if (new_argv[i] != argv[i])
278 free(new_argv[i]);
279 free(new_argv);
280 free(new_path);
281
282 return ret;
283}
284
285#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
286static intptr_t
287mingw_spawn_applet(int mode,
288 char *const *argv,
289 char *const *envp)
290{
291 return spawnveq(mode, bb_busybox_exec_path, argv, envp);
292}
293#endif
294
295static intptr_t
296mingw_spawn_interpreter(int mode, const char *prog, char *const *argv,
297 char *const *envp, int level)
298{
299 intptr_t ret;
300 int nopts;
301 interp_t interp;
302 char **new_argv;
303 int argc;
304 char *fullpath = NULL;
305
306 if (!parse_interpreter(prog, &interp))
307 return spawnveq(mode, prog, argv, envp);
308
309 if (++level > 4) {
310 errno = ELOOP;
311 return -1;
312 }
313
314 nopts = interp.opts != NULL;
315 argc = string_array_len((char **)argv);
316 new_argv = xmalloc(sizeof(*argv)*(argc+nopts+2));
317 new_argv[1] = interp.opts;
318 new_argv[nopts+1] = (char *)prog; /* pass absolute path */
319 memcpy(new_argv+nopts+2, argv+1, sizeof(*argv)*argc);
320
321 fullpath = alloc_system_drive(interp.path);
322 if (add_win32_extension(fullpath) || file_is_executable(fullpath)) {
323 new_argv[0] = fullpath;
324 ret = mingw_spawn_interpreter(mode, new_argv[0], new_argv, envp, level);
325 } else
326#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
327 if (find_applet_by_name(interp.name) >= 0) {
328 /* the fake path indicates the index of the script */
329 new_argv[0] = fullpath = xasprintf("%d:/%s", nopts+1, interp.name);
330 ret = mingw_spawn_applet(mode, new_argv, envp);
331 } else
332#endif
333 {
334 errno = ENOENT;
335 ret = -1;
336 }
337
338 free(fullpath);
339 free(new_argv);
340 return ret;
341}
342
343static intptr_t
344mingw_spawnvp(int mode, const char *cmd, char *const *argv)
345{
346 char *prog;
347 intptr_t ret;
348
349#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
350 if (find_applet_by_name(cmd) >= 0)
351 return mingw_spawn_applet(mode, argv, NULL);
352 else
353#endif
354 if (has_path(cmd)) {
355 char *path = alloc_system_drive(cmd);
356 add_win32_extension(path);
357 ret = mingw_spawn_interpreter(mode, path, argv, NULL, 0);
358 free(path);
359#if ENABLE_FEATURE_PREFER_APPLETS || ENABLE_FEATURE_SH_STANDALONE
360 if (ret == -1 && unix_path(cmd) &&
361 find_applet_by_name(bb_basename(cmd)) >= 0) {
362 return mingw_spawn_applet(mode, argv, NULL);
363 }
364#endif
365 return ret;
366 }
367 else if ((prog=find_first_executable(cmd)) != NULL) {
368 ret = mingw_spawn_interpreter(mode, prog, argv, NULL, 0);
369 free(prog);
370 return ret;
371 }
372
373 errno = ENOENT;
374 return -1;
375}
376
377static pid_t
378mingw_spawn_pid(int mode, char **argv)
379{
380 intptr_t ret;
381
382 ret = mingw_spawnvp(mode, argv[0], (char *const *)argv);
383
384 return ret == -1 ? (pid_t)-1 : (pid_t)GetProcessId((HANDLE)ret);
385}
386
387pid_t FAST_FUNC
388mingw_spawn(char **argv)
389{
390 return mingw_spawn_pid(P_NOWAIT, argv);
391}
392
393pid_t FAST_FUNC
394mingw_spawn_detach(char **argv)
395{
396 return mingw_spawn_pid(P_DETACH, argv);
397}
398
399intptr_t FAST_FUNC
400mingw_spawn_proc(const char **argv)
401{
402 return mingw_spawnvp(P_NOWAIT, argv[0], (char *const *)argv);
403}
404
405int
406mingw_execvp(const char *cmd, char *const *argv)
407{
408 int ret = (int)mingw_spawnvp(P_WAIT, cmd, argv);
409 if (ret != -1 || errno == 0)
410 exit(ret);
411 return ret;
412}
413
414int
415mingw_execve(const char *cmd, char *const *argv, char *const *envp)
416{
417 int ret = (int)mingw_spawn_interpreter(P_WAIT, cmd, argv, envp, 0);
418 if (ret != -1 || errno == 0)
419 exit(ret);
420 return ret;
421}
422
423int
424mingw_execv(const char *cmd, char *const *argv)
425{
426 return mingw_execve(cmd, argv, NULL);
427}
428
429static inline long long filetime_to_ticks(const FILETIME *ft)
430{
431 return (((long long)ft->dwHighDateTime << 32) + ft->dwLowDateTime)/
432 HNSEC_PER_TICK;
433}
434
435/*
436 * Attempt to get a string from another instance of busybox.exe.
437 * This will only work if the other process is using the same binary
438 * as the current process. If anything goes wrong just give up.
439 */
440static char *get_bb_string(DWORD pid, const char *exe, char *string)
441{
442 HANDLE proc;
443 HMODULE mlist[32];
444 DWORD needed;
445 void *address;
446 char *my_base;
447 char buffer[128];
448 char exepath[PATH_MAX];
449 char *name = NULL;
450 int i;
451 DECLARE_PROC_ADDR(DWORD, GetProcessImageFileNameA, HANDLE,
452 LPSTR, DWORD);
453 DECLARE_PROC_ADDR(BOOL, EnumProcessModules, HANDLE, HMODULE *,
454 DWORD, LPDWORD);
455 DECLARE_PROC_ADDR(DWORD, GetModuleFileNameExA, HANDLE, HMODULE,
456 LPSTR, DWORD);
457
458 if (!INIT_PROC_ADDR(psapi.dll, GetProcessImageFileNameA) ||
459 !INIT_PROC_ADDR(psapi.dll, EnumProcessModules) ||
460 !INIT_PROC_ADDR(psapi.dll, GetModuleFileNameExA))
461 return NULL;
462
463 if (!(proc=OpenProcess(PROCESS_QUERY_INFORMATION|PROCESS_VM_READ,
464 FALSE, pid))) {
465 return NULL;
466 }
467
468 if (exe == NULL) {
469 if (GetProcessImageFileNameA(proc, exepath, PATH_MAX) != 0) {
470 exe = bb_basename(exepath);
471 }
472 }
473
474 /*
475 * Search for the module that matches the name of the executable.
476 * The values returned in mlist are actually the base address of
477 * the module in the other process (as noted in the documentation
478 * for the MODULEINFO structure).
479 */
480 if (!EnumProcessModules(proc, mlist, sizeof(mlist), &needed)) {
481 goto finish;
482 }
483
484 for (i=0; exe != NULL && i<needed/sizeof(HMODULE); ++i) {
485 char modname[MAX_PATH];
486 if (GetModuleFileNameExA(proc, mlist[i], modname, sizeof(modname))) {
487 if (strcasecmp(bb_basename(modname), exe) == 0) {
488 break;
489 }
490 }
491 }
492
493 if (i == needed/sizeof(HMODULE)) {
494 goto finish;
495 }
496
497 /* attempt to read the BusyBox version string */
498 my_base = (char *)GetModuleHandle(NULL);
499 address = (char *)mlist[i] + ((char *)bb_banner - my_base);
500 if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) {
501 goto finish;
502 }
503
504 if (memcmp(buffer, bb_banner, strlen(bb_banner)) != 0) {
505 /* version mismatch (or not BusyBox at all) */
506 goto finish;
507 }
508
509 /* attempt to read the required string */
510 address = (char *)mlist[i] + ((char *)string - my_base);
511 if (!ReadProcessMemory(proc, address, buffer, 128, NULL)) {
512 goto finish;
513 }
514
515 buffer[127] = '\0';
516 name = auto_string(xstrdup(buffer));
517
518 finish:
519 CloseHandle(proc);
520 return name;
521}
522
523/* POSIX version in libbb/procps.c */
524procps_status_t* FAST_FUNC procps_scan(procps_status_t* sp, int flags
525#if !ENABLE_FEATURE_PS_TIME && !ENABLE_FEATURE_PS_LONG
526UNUSED_PARAM
527#endif
528)
529{
530 PROCESSENTRY32 pe;
531 HANDLE proc;
532 const char *comm, *name;
533 BOOL ret;
534
535 pe.dwSize = sizeof(pe);
536 if (!sp) {
537 sp = xzalloc(sizeof(struct procps_status_t));
538 sp->snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
539 if (sp->snapshot == INVALID_HANDLE_VALUE) {
540 free(sp);
541 return NULL;
542 }
543 ret = Process32First(sp->snapshot, &pe);
544 }
545 else {
546 ret = Process32Next(sp->snapshot, &pe);
547 }
548
549 if (!ret) {
550 CloseHandle(sp->snapshot);
551 free(sp);
552 return NULL;
553 }
554
555 memset(&sp->vsz, 0, sizeof(*sp) - offsetof(procps_status_t, vsz));
556#if !ENABLE_DESKTOP
557 strcpy(sp->state, " ");
558#endif
559
560#if ENABLE_FEATURE_PS_TIME || ENABLE_FEATURE_PS_LONG
561 if (flags & (PSSCAN_STIME|PSSCAN_UTIME|PSSCAN_START_TIME)) {
562 FILETIME crTime, exTime, keTime, usTime;
563
564 if ((proc=OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION,
565 FALSE, pe.th32ProcessID))) {
566 if (GetProcessTimes(proc, &crTime, &exTime, &keTime, &usTime)) {
567 long long ticks_since_boot, boot_time, create_time;
568 FILETIME now;
569
570 ticks_since_boot = GetTickCount64()/MS_PER_TICK;
571 GetSystemTimeAsFileTime(&now);
572 boot_time = filetime_to_ticks(&now) - ticks_since_boot;
573 create_time = filetime_to_ticks(&crTime);
574
575 sp->start_time = (unsigned long)(create_time - boot_time);
576 sp->stime = (unsigned long)filetime_to_ticks(&keTime);
577 sp->utime = (unsigned long)filetime_to_ticks(&usTime);
578 }
579 CloseHandle(proc);
580 }
581 }
582#endif
583
584 if (flags & PSSCAN_UIDGID) {
585 /* if we can open the process it belongs to us */
586 if ((proc=OpenProcess(PROCESS_ALL_ACCESS, FALSE, pe.th32ProcessID))) {
587 sp->uid = DEFAULT_UID;
588 sp->gid = DEFAULT_GID;
589 CloseHandle(proc);
590 }
591 }
592
593 sp->pid = pe.th32ProcessID;
594 sp->ppid = pe.th32ParentProcessID;
595
596 if (sp->pid == GetProcessId(GetCurrentProcess())) {
597 comm = applet_name;
598 }
599 else if ((name=get_bb_string(sp->pid, pe.szExeFile, bb_comm)) != NULL) {
600 comm = name;
601 }
602 else {
603 comm = pe.szExeFile;
604 }
605 safe_strncpy(sp->comm, comm, COMM_LEN);
606
607 return sp;
608}
609
610void FAST_FUNC read_cmdline(char *buf, int col, unsigned pid, const char *comm)
611{
612 const char *str, *cmdline;
613
614 *buf = '\0';
615 if (pid == GetProcessId(GetCurrentProcess()))
616 cmdline = bb_command_line;
617 else if ((str=get_bb_string(pid, NULL, bb_command_line)) != NULL)
618 cmdline = str;
619 else
620 cmdline = comm;
621 safe_strncpy(buf, cmdline, col);
622}
623
624/**
625 * If the process ID is positive invoke the callback for that process
626 * only. If negative or zero invoke the callback for all descendants
627 * of the indicated process. Zero indicates the current process; negative
628 * indicates the process with process ID -pid.
629 */
630typedef int (*kill_callback)(pid_t pid, int sig);
631
632static int kill_pids(pid_t pid, int sig, kill_callback killer)
633{
634 DWORD pids[16384];
635 int max_len = sizeof(pids) / sizeof(*pids), i, len, ret = 0;
636
637 if(pid > 0)
638 pids[0] = (DWORD)pid;
639 else if (pid == 0)
640 pids[0] = (DWORD)getpid();
641 else
642 pids[0] = (DWORD)-pid;
643 len = 1;
644
645 /*
646 * Even if Process32First()/Process32Next() seem to traverse the
647 * processes in topological order (i.e. parent processes before
648 * child processes), there is nothing in the Win32 API documentation
649 * suggesting that this is guaranteed.
650 *
651 * Therefore, run through them at least twice and stop when no more
652 * process IDs were added to the list.
653 */
654 if (pid <= 0) {
655 HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
656
657 if (snapshot == INVALID_HANDLE_VALUE) {
658 errno = err_win_to_posix();
659 return -1;
660 }
661
662 for (;;) {
663 PROCESSENTRY32 entry;
664 int orig_len = len;
665
666 memset(&entry, 0, sizeof(entry));
667 entry.dwSize = sizeof(entry);
668
669 if (!Process32First(snapshot, &entry))
670 break;
671
672 do {
673 for (i = len - 1; i >= 0; i--) {
674 if (pids[i] == entry.th32ProcessID)
675 break;
676 if (pids[i] == entry.th32ParentProcessID)
677 pids[len++] = entry.th32ProcessID;
678 }
679 } while (len < max_len && Process32Next(snapshot, &entry));
680
681 if (orig_len == len || len >= max_len)
682 break;
683 }
684
685 CloseHandle(snapshot);
686 }
687
688 for (i = len - 1; i >= 0; i--) {
689 SetLastError(0);
690 if (killer(pids[i], sig)) {
691 errno = err_win_to_posix();
692 ret = -1;
693 }
694 }
695
696 return ret;
697}
698
699/**
700 * Determine whether a process runs in the same architecture as the current
701 * one. That test is required before we assume that GetProcAddress() returns
702 * a valid address *for the target process*.
703 */
704static inline int process_architecture_matches_current(HANDLE process)
705{
706 static BOOL current_is_wow = -1;
707 BOOL is_wow;
708
709 if (current_is_wow == -1 &&
710 !IsWow64Process (GetCurrentProcess(), &current_is_wow))
711 current_is_wow = -2;
712 if (current_is_wow == -2)
713 return 0; /* could not determine current process' WoW-ness */
714 if (!IsWow64Process (process, &is_wow))
715 return 0; /* cannot determine */
716 return is_wow == current_is_wow;
717}
718
719/**
720 * This function tries to terminate a Win32 process, as gently as possible,
721 * by injecting a thread that calls ExitProcess().
722 *
723 * Note: as kernel32.dll is loaded before any process, the other process and
724 * this process will have ExitProcess() at the same address.
725 *
726 * The idea comes from the Dr Dobb's article "A Safer Alternative to
727 * TerminateProcess()" by Andrew Tucker (July 1, 1999),
728 * http://www.drdobbs.com/a-safer-alternative-to-terminateprocess/184416547
729 *
730 */
731int kill_SIGTERM_by_handle(HANDLE process)
732{
733 DWORD code;
734 int ret = 0;
735
736 if (GetExitCodeProcess(process, &code) && code == STILL_ACTIVE) {
737 DECLARE_PROC_ADDR(DWORD, ExitProcess, LPVOID);
738 PVOID arg = (PVOID)(intptr_t)(128 + SIGTERM);
739 DWORD thread_id;
740 HANDLE thread;
741
742 if (!INIT_PROC_ADDR(kernel32, ExitProcess) ||
743 !process_architecture_matches_current(process)) {
744 SetLastError(ERROR_ACCESS_DENIED);
745 ret = -1;
746 goto finish;
747 }
748
749 if ((thread = CreateRemoteThread(process, NULL, 0,
750 ExitProcess, arg, 0, &thread_id))) {
751 CloseHandle(thread);
752 }
753 }
754
755 finish:
756 CloseHandle(process);
757 return ret;
758}
759
760static int kill_SIGTERM(pid_t pid, int sig UNUSED_PARAM)
761{
762 HANDLE process;
763
764 if (!(process = OpenProcess(SYNCHRONIZE | PROCESS_CREATE_THREAD |
765 PROCESS_QUERY_INFORMATION |
766 PROCESS_VM_OPERATION | PROCESS_VM_WRITE |
767 PROCESS_VM_READ, FALSE, pid))) {
768 return -1;
769 }
770
771 return kill_SIGTERM_by_handle(process);
772}
773
774/*
775 * This way of terminating processes is not gentle: they get no chance to
776 * clean up after themselves (closing file handles, removing .lock files,
777 * terminating spawned processes (if any), etc).
778 *
779 * If the signal isn't SIGKILL just check if the target process exists.
780 */
781static int kill_SIGKILL(pid_t pid, int sig)
782{
783 HANDLE process;
784 int ret = 0;
785
786 if (!(process=OpenProcess(PROCESS_TERMINATE, FALSE, pid))) {
787 return -1;
788 }
789
790 if (sig == SIGKILL)
791 ret = !TerminateProcess(process, 128 + SIGKILL);
792 CloseHandle(process);
793
794 return ret;
795}
796
797int kill(pid_t pid, int sig)
798{
799 if (sig == SIGTERM)
800 return kill_pids(pid, sig, kill_SIGTERM);
801 else if (sig == SIGKILL || sig == 0)
802 return kill_pids(pid, sig, kill_SIGKILL);
803
804 errno = EINVAL;
805 return -1;
806}
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..e1692d341
--- /dev/null
+++ b/win32/regcomp.c
@@ -0,0 +1,3936 @@
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#include "match_class.h"
22
23#define UNUSED_PARAM __attribute__ ((__unused__))
24
25static reg_errcode_t re_compile_internal (regex_t *preg, const char * pattern,
26 size_t length, reg_syntax_t syntax);
27static void re_compile_fastmap_iter (regex_t *bufp,
28 const re_dfastate_t *init_state,
29 char *fastmap);
30static reg_errcode_t init_dfa (re_dfa_t *dfa, size_t pat_len);
31#ifdef RE_ENABLE_I18N
32static void free_charset (re_charset_t *cset);
33#endif /* RE_ENABLE_I18N */
34static void free_workarea_compile (regex_t *preg);
35static reg_errcode_t create_initial_state (re_dfa_t *dfa);
36#ifdef RE_ENABLE_I18N
37static void optimize_utf8 (re_dfa_t *dfa);
38#endif
39static reg_errcode_t analyze (regex_t *preg);
40static reg_errcode_t preorder (bin_tree_t *root,
41 reg_errcode_t (fn (void *, bin_tree_t *)),
42 void *extra);
43static reg_errcode_t postorder (bin_tree_t *root,
44 reg_errcode_t (fn (void *, bin_tree_t *)),
45 void *extra);
46static reg_errcode_t optimize_subexps (void *extra, bin_tree_t *node);
47static reg_errcode_t lower_subexps (void *extra, bin_tree_t *node);
48static bin_tree_t *lower_subexp (reg_errcode_t *err, regex_t *preg,
49 bin_tree_t *node);
50static reg_errcode_t calc_first (void *extra, bin_tree_t *node);
51static reg_errcode_t calc_next (void *extra, bin_tree_t *node);
52static reg_errcode_t link_nfa_nodes (void *extra, bin_tree_t *node);
53static int duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint);
54static int search_duplicated_node (const re_dfa_t *dfa, int org_node,
55 unsigned int constraint);
56static reg_errcode_t calc_eclosure (re_dfa_t *dfa);
57static reg_errcode_t calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa,
58 int node, int root);
59static reg_errcode_t calc_inveclosure (re_dfa_t *dfa);
60static int fetch_number (re_string_t *input, re_token_t *token,
61 reg_syntax_t syntax);
62static int peek_token (re_token_t *token, re_string_t *input,
63 reg_syntax_t syntax) internal_function;
64static bin_tree_t *parse (re_string_t *regexp, regex_t *preg,
65 reg_syntax_t syntax, reg_errcode_t *err);
66static bin_tree_t *parse_reg_exp (re_string_t *regexp, regex_t *preg,
67 re_token_t *token, reg_syntax_t syntax,
68 int nest, reg_errcode_t *err);
69static bin_tree_t *parse_branch (re_string_t *regexp, regex_t *preg,
70 re_token_t *token, reg_syntax_t syntax,
71 int nest, reg_errcode_t *err);
72static bin_tree_t *parse_expression (re_string_t *regexp, regex_t *preg,
73 re_token_t *token, reg_syntax_t syntax,
74 int nest, reg_errcode_t *err);
75static bin_tree_t *parse_sub_exp (re_string_t *regexp, regex_t *preg,
76 re_token_t *token, reg_syntax_t syntax,
77 int nest, reg_errcode_t *err);
78static bin_tree_t *parse_dup_op (bin_tree_t *dup_elem, re_string_t *regexp,
79 re_dfa_t *dfa, re_token_t *token,
80 reg_syntax_t syntax, reg_errcode_t *err);
81static bin_tree_t *parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa,
82 re_token_t *token, reg_syntax_t syntax,
83 reg_errcode_t *err);
84static reg_errcode_t parse_bracket_element (bracket_elem_t *elem,
85 re_string_t *regexp,
86 re_token_t *token, int token_len,
87 re_dfa_t *dfa,
88 reg_syntax_t syntax,
89 int accept_hyphen);
90static reg_errcode_t parse_bracket_symbol (bracket_elem_t *elem,
91 re_string_t *regexp,
92 re_token_t *token);
93#ifdef RE_ENABLE_I18N
94static reg_errcode_t build_equiv_class (bitset_t sbcset,
95 re_charset_t *mbcset,
96 int *equiv_class_alloc,
97 const unsigned char *name);
98static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
99 bitset_t sbcset,
100 re_charset_t *mbcset,
101 int *char_class_alloc,
102 const char *class_name,
103 reg_syntax_t syntax);
104#else /* not RE_ENABLE_I18N */
105static reg_errcode_t build_equiv_class (bitset_t sbcset,
106 const unsigned char *name);
107static reg_errcode_t build_charclass (RE_TRANSLATE_TYPE trans,
108 bitset_t sbcset,
109 const char *class_name,
110 reg_syntax_t syntax);
111#endif /* not RE_ENABLE_I18N */
112static bin_tree_t *build_charclass_op (re_dfa_t *dfa,
113 RE_TRANSLATE_TYPE trans,
114 const char *class_name,
115 const char *extra,
116 int non_match, reg_errcode_t *err);
117static bin_tree_t *create_tree (re_dfa_t *dfa,
118 bin_tree_t *left, bin_tree_t *right,
119 re_token_type_t type);
120static bin_tree_t *create_token_tree (re_dfa_t *dfa,
121 bin_tree_t *left, bin_tree_t *right,
122 const re_token_t *token);
123static bin_tree_t *duplicate_tree (const bin_tree_t *src, re_dfa_t *dfa);
124static void free_token (re_token_t *node);
125static reg_errcode_t free_tree (void *extra, bin_tree_t *node);
126static reg_errcode_t mark_opt_subexp (void *extra, bin_tree_t *node);
127
128/* This table gives an error message for each of the error codes listed
129 in regex.h. Obviously the order here has to be same as there.
130 POSIX doesn't require that we do anything for REG_NOERROR,
131 but why not be nice? */
132
133const char __re_error_msgid[] attribute_hidden =
134 {
135#define REG_NOERROR_IDX 0
136 gettext_noop ("Success") /* REG_NOERROR */
137 "\0"
138#define REG_NOMATCH_IDX (REG_NOERROR_IDX + sizeof "Success")
139 gettext_noop ("No match") /* REG_NOMATCH */
140 "\0"
141#define REG_BADPAT_IDX (REG_NOMATCH_IDX + sizeof "No match")
142 gettext_noop ("Invalid regular expression") /* REG_BADPAT */
143 "\0"
144#define REG_ECOLLATE_IDX (REG_BADPAT_IDX + sizeof "Invalid regular expression")
145 gettext_noop ("Invalid collation character") /* REG_ECOLLATE */
146 "\0"
147#define REG_ECTYPE_IDX (REG_ECOLLATE_IDX + sizeof "Invalid collation character")
148 gettext_noop ("Invalid character class name") /* REG_ECTYPE */
149 "\0"
150#define REG_EESCAPE_IDX (REG_ECTYPE_IDX + sizeof "Invalid character class name")
151 gettext_noop ("Trailing backslash") /* REG_EESCAPE */
152 "\0"
153#define REG_ESUBREG_IDX (REG_EESCAPE_IDX + sizeof "Trailing backslash")
154 gettext_noop ("Invalid back reference") /* REG_ESUBREG */
155 "\0"
156#define REG_EBRACK_IDX (REG_ESUBREG_IDX + sizeof "Invalid back reference")
157 gettext_noop ("Unmatched [ or [^") /* REG_EBRACK */
158 "\0"
159#define REG_EPAREN_IDX (REG_EBRACK_IDX + sizeof "Unmatched [ or [^")
160 gettext_noop ("Unmatched ( or \\(") /* REG_EPAREN */
161 "\0"
162#define REG_EBRACE_IDX (REG_EPAREN_IDX + sizeof "Unmatched ( or \\(")
163 gettext_noop ("Unmatched \\{") /* REG_EBRACE */
164 "\0"
165#define REG_BADBR_IDX (REG_EBRACE_IDX + sizeof "Unmatched \\{")
166 gettext_noop ("Invalid content of \\{\\}") /* REG_BADBR */
167 "\0"
168#define REG_ERANGE_IDX (REG_BADBR_IDX + sizeof "Invalid content of \\{\\}")
169 gettext_noop ("Invalid range end") /* REG_ERANGE */
170 "\0"
171#define REG_ESPACE_IDX (REG_ERANGE_IDX + sizeof "Invalid range end")
172 gettext_noop ("Memory exhausted") /* REG_ESPACE */
173 "\0"
174#define REG_BADRPT_IDX (REG_ESPACE_IDX + sizeof "Memory exhausted")
175 gettext_noop ("Invalid preceding regular expression") /* REG_BADRPT */
176 "\0"
177#define REG_EEND_IDX (REG_BADRPT_IDX + sizeof "Invalid preceding regular expression")
178 gettext_noop ("Premature end of regular expression") /* REG_EEND */
179 "\0"
180#define REG_ESIZE_IDX (REG_EEND_IDX + sizeof "Premature end of regular expression")
181 gettext_noop ("Regular expression too big") /* REG_ESIZE */
182 "\0"
183#define REG_ERPAREN_IDX (REG_ESIZE_IDX + sizeof "Regular expression too big")
184 gettext_noop ("Unmatched ) or \\)") /* REG_ERPAREN */
185 };
186
187const size_t __re_error_msgid_idx[] attribute_hidden =
188 {
189 REG_NOERROR_IDX,
190 REG_NOMATCH_IDX,
191 REG_BADPAT_IDX,
192 REG_ECOLLATE_IDX,
193 REG_ECTYPE_IDX,
194 REG_EESCAPE_IDX,
195 REG_ESUBREG_IDX,
196 REG_EBRACK_IDX,
197 REG_EPAREN_IDX,
198 REG_EBRACE_IDX,
199 REG_BADBR_IDX,
200 REG_ERANGE_IDX,
201 REG_ESPACE_IDX,
202 REG_BADRPT_IDX,
203 REG_EEND_IDX,
204 REG_ESIZE_IDX,
205 REG_ERPAREN_IDX
206 };
207
208/* Entry points for GNU code. */
209
210
211#ifdef ZOS_USS
212
213/* For ZOS USS we must define btowc */
214
215wchar_t
216btowc (int c)
217{
218 wchar_t wtmp[2];
219 char tmp[2];
220
221 tmp[0] = c;
222 tmp[1] = 0;
223
224 mbtowc (wtmp, tmp, 1);
225 return wtmp[0];
226}
227#endif
228
229/* re_compile_pattern is the GNU regular expression compiler: it
230 compiles PATTERN (of length LENGTH) and puts the result in BUFP.
231 Returns 0 if the pattern was valid, otherwise an error string.
232
233 Assumes the `allocated' (and perhaps `buffer') and `translate' fields
234 are set in BUFP on entry. */
235
236const char *
237re_compile_pattern (const char *pattern,
238 size_t length,
239 struct re_pattern_buffer *bufp)
240{
241 reg_errcode_t ret;
242
243 /* And GNU code determines whether or not to get register information
244 by passing null for the REGS argument to re_match, etc., not by
245 setting no_sub, unless RE_NO_SUB is set. */
246 bufp->no_sub = !!(re_syntax_options & RE_NO_SUB);
247
248 /* Match anchors at newline. */
249 bufp->newline_anchor = 1;
250
251 ret = re_compile_internal (bufp, pattern, length, re_syntax_options);
252
253 if (!ret)
254 return NULL;
255 return gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
256}
257#ifdef _LIBC
258weak_alias (__re_compile_pattern, re_compile_pattern)
259#endif
260
261/* Set by `re_set_syntax' to the current regexp syntax to recognize. Can
262 also be assigned to arbitrarily: each pattern buffer stores its own
263 syntax, so it can be changed between regex compilations. */
264/* This has no initializer because initialized variables in Emacs
265 become read-only after dumping. */
266reg_syntax_t re_syntax_options;
267
268
269/* Specify the precise syntax of regexps for compilation. This provides
270 for compatibility for various utilities which historically have
271 different, incompatible syntaxes.
272
273 The argument SYNTAX is a bit mask comprised of the various bits
274 defined in regex.h. We return the old syntax. */
275
276reg_syntax_t
277re_set_syntax (reg_syntax_t syntax)
278{
279 reg_syntax_t ret = re_syntax_options;
280
281 re_syntax_options = syntax;
282 return ret;
283}
284#ifdef _LIBC
285weak_alias (__re_set_syntax, re_set_syntax)
286#endif
287
288int
289re_compile_fastmap (struct re_pattern_buffer *bufp)
290{
291 re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
292 char *fastmap = bufp->fastmap;
293
294 memset (fastmap, '\0', sizeof (char) * SBC_MAX);
295 re_compile_fastmap_iter (bufp, dfa->init_state, fastmap);
296 if (dfa->init_state != dfa->init_state_word)
297 re_compile_fastmap_iter (bufp, dfa->init_state_word, fastmap);
298 if (dfa->init_state != dfa->init_state_nl)
299 re_compile_fastmap_iter (bufp, dfa->init_state_nl, fastmap);
300 if (dfa->init_state != dfa->init_state_begbuf)
301 re_compile_fastmap_iter (bufp, dfa->init_state_begbuf, fastmap);
302 bufp->fastmap_accurate = 1;
303 return 0;
304}
305#ifdef _LIBC
306weak_alias (__re_compile_fastmap, re_compile_fastmap)
307#endif
308
309static inline void
310__attribute ((always_inline))
311re_set_fastmap (char *fastmap, int icase, int ch)
312{
313 fastmap[ch] = 1;
314 if (icase)
315 fastmap[tolower (ch)] = 1;
316}
317
318/* Helper function for re_compile_fastmap.
319 Compile fastmap for the initial_state INIT_STATE. */
320
321static void
322re_compile_fastmap_iter (regex_t *bufp, const re_dfastate_t *init_state,
323 char *fastmap)
324{
325 volatile re_dfa_t *dfa = (re_dfa_t *) bufp->buffer;
326 int node_cnt;
327 int icase = (dfa->mb_cur_max == 1 && (bufp->syntax & RE_ICASE));
328 for (node_cnt = 0; node_cnt < init_state->nodes.nelem; ++node_cnt)
329 {
330 int node = init_state->nodes.elems[node_cnt];
331 re_token_type_t type = dfa->nodes[node].type;
332
333 if (type == CHARACTER)
334 {
335 re_set_fastmap (fastmap, icase, dfa->nodes[node].opr.c);
336#ifdef RE_ENABLE_I18N
337 if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
338 {
339 unsigned char *buf = re_malloc (unsigned char, dfa->mb_cur_max), *p;
340 wchar_t wc;
341 mbstate_t state;
342
343 p = buf;
344 *p++ = dfa->nodes[node].opr.c;
345 while (++node < dfa->nodes_len
346 && dfa->nodes[node].type == CHARACTER
347 && dfa->nodes[node].mb_partial)
348 *p++ = dfa->nodes[node].opr.c;
349 memset (&state, '\0', sizeof (state));
350 if (__mbrtowc (&wc, (const char *) buf, p - buf,
351 &state) == p - buf
352 && (__wcrtomb ((char *) buf, towlower (wc), &state)
353 != (size_t) -1))
354 re_set_fastmap (fastmap, 0, buf[0]);
355 re_free (buf);
356 }
357#endif
358 }
359 else if (type == SIMPLE_BRACKET)
360 {
361 int i, ch;
362 for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
363 {
364 int j;
365 bitset_word_t w = dfa->nodes[node].opr.sbcset[i];
366 for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
367 if (w & ((bitset_word_t) 1 << j))
368 re_set_fastmap (fastmap, icase, ch);
369 }
370 }
371#ifdef RE_ENABLE_I18N
372 else if (type == COMPLEX_BRACKET)
373 {
374 re_charset_t *cset = dfa->nodes[node].opr.mbcset;
375 int i;
376
377# ifdef _LIBC
378 /* See if we have to try all bytes which start multiple collation
379 elements.
380 e.g. In da_DK, we want to catch 'a' since "aa" is a valid
381 collation element, and don't catch 'b' since 'b' is
382 the only collation element which starts from 'b' (and
383 it is caught by SIMPLE_BRACKET). */
384 if (_NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES) != 0
385 && (cset->ncoll_syms || cset->nranges))
386 {
387 const int32_t *table = (const int32_t *)
388 _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
389 for (i = 0; i < SBC_MAX; ++i)
390 if (table[i] < 0)
391 re_set_fastmap (fastmap, icase, i);
392 }
393# endif /* _LIBC */
394
395 /* See if we have to start the match at all multibyte characters,
396 i.e. where we would not find an invalid sequence. This only
397 applies to multibyte character sets; for single byte character
398 sets, the SIMPLE_BRACKET again suffices. */
399 if (dfa->mb_cur_max > 1
400 && (cset->nchar_classes || cset->non_match || cset->nranges
401# ifdef _LIBC
402 || cset->nequiv_classes
403# endif /* _LIBC */
404 ))
405 {
406 unsigned char c = 0;
407 do
408 {
409 mbstate_t mbs;
410 memset (&mbs, 0, sizeof (mbs));
411 if (__mbrtowc (NULL, (char *) &c, 1, &mbs) == (size_t) -2)
412 re_set_fastmap (fastmap, false, (int) c);
413 }
414 while (++c != 0);
415 }
416
417 else
418 {
419 /* ... Else catch all bytes which can start the mbchars. */
420 for (i = 0; i < cset->nmbchars; ++i)
421 {
422 char buf[256];
423 mbstate_t state;
424 memset (&state, '\0', sizeof (state));
425 if (__wcrtomb (buf, cset->mbchars[i], &state) != (size_t) -1)
426 re_set_fastmap (fastmap, icase, *(unsigned char *) buf);
427 if ((bufp->syntax & RE_ICASE) && dfa->mb_cur_max > 1)
428 {
429 if (__wcrtomb (buf, towlower (cset->mbchars[i]), &state)
430 != (size_t) -1)
431 re_set_fastmap (fastmap, false, *(unsigned char *) buf);
432 }
433 }
434 }
435 }
436#endif /* RE_ENABLE_I18N */
437 else if (type == OP_PERIOD
438#ifdef RE_ENABLE_I18N
439 || type == OP_UTF8_PERIOD
440#endif /* RE_ENABLE_I18N */
441 || type == END_OF_RE)
442 {
443 memset (fastmap, '\1', sizeof (char) * SBC_MAX);
444 if (type == END_OF_RE)
445 bufp->can_be_null = 1;
446 return;
447 }
448 }
449}
450
451/* Entry point for POSIX code. */
452/* regcomp takes a regular expression as a string and compiles it.
453
454 PREG is a regex_t *. We do not expect any fields to be initialized,
455 since POSIX says we shouldn't. Thus, we set
456
457 `buffer' to the compiled pattern;
458 `used' to the length of the compiled pattern;
459 `syntax' to RE_SYNTAX_POSIX_EXTENDED if the
460 REG_EXTENDED bit in CFLAGS is set; otherwise, to
461 RE_SYNTAX_POSIX_BASIC;
462 `newline_anchor' to REG_NEWLINE being set in CFLAGS;
463 `fastmap' to an allocated space for the fastmap;
464 `fastmap_accurate' to zero;
465 `re_nsub' to the number of subexpressions in PATTERN.
466
467 PATTERN is the address of the pattern string.
468
469 CFLAGS is a series of bits which affect compilation.
470
471 If REG_EXTENDED is set, we use POSIX extended syntax; otherwise, we
472 use POSIX basic syntax.
473
474 If REG_NEWLINE is set, then . and [^...] don't match newline.
475 Also, regexec will try a match beginning after every newline.
476
477 If REG_ICASE is set, then we considers upper- and lowercase
478 versions of letters to be equivalent when matching.
479
480 If REG_NOSUB is set, then when PREG is passed to regexec, that
481 routine will report only success or failure, and nothing about the
482 registers.
483
484 It returns 0 if it succeeds, nonzero if it doesn't. (See regex.h for
485 the return codes and their meanings.) */
486
487int
488regcomp (regex_t *__restrict preg,
489 const char *__restrict pattern,
490 int cflags)
491{
492 reg_errcode_t ret;
493 reg_syntax_t syntax = ((cflags & REG_EXTENDED) ? RE_SYNTAX_POSIX_EXTENDED
494 : RE_SYNTAX_POSIX_BASIC);
495
496 preg->buffer = NULL;
497 preg->allocated = 0;
498 preg->used = 0;
499
500 /* Try to allocate space for the fastmap. */
501 preg->fastmap = re_malloc (char, SBC_MAX);
502 if (BE (preg->fastmap == NULL, 0))
503 return REG_ESPACE;
504
505 syntax |= (cflags & REG_ICASE) ? RE_ICASE : 0;
506
507 /* If REG_NEWLINE is set, newlines are treated differently. */
508 if (cflags & REG_NEWLINE)
509 { /* REG_NEWLINE implies neither . nor [^...] match newline. */
510 syntax &= ~RE_DOT_NEWLINE;
511 syntax |= RE_HAT_LISTS_NOT_NEWLINE;
512 /* It also changes the matching behavior. */
513 preg->newline_anchor = 1;
514 }
515 else
516 preg->newline_anchor = 0;
517 preg->no_sub = !!(cflags & REG_NOSUB);
518 preg->translate = NULL;
519
520 ret = re_compile_internal (preg, pattern, strlen (pattern), syntax);
521
522 /* POSIX doesn't distinguish between an unmatched open-group and an
523 unmatched close-group: both are REG_EPAREN. */
524 if (ret == REG_ERPAREN)
525 ret = REG_EPAREN;
526
527 /* We have already checked preg->fastmap != NULL. */
528 if (BE (ret == REG_NOERROR, 1))
529 /* Compute the fastmap now, since regexec cannot modify the pattern
530 buffer. This function never fails in this implementation. */
531 (void) re_compile_fastmap (preg);
532 else
533 {
534 /* Some error occurred while compiling the expression. */
535 re_free (preg->fastmap);
536 preg->fastmap = NULL;
537 }
538
539 return (int) ret;
540}
541#ifdef _LIBC
542weak_alias (__regcomp, regcomp)
543#endif
544
545/* Returns a message corresponding to an error code, ERRCODE, returned
546 from either regcomp or regexec. We don't use PREG here. */
547
548size_t
549regerror(int errcode, UNUSED_PARAM const regex_t *__restrict preg,
550 char *__restrict errbuf, size_t errbuf_size)
551{
552 const char *msg;
553 size_t msg_size;
554
555 if (BE (errcode < 0
556 || errcode >= (int) (sizeof (__re_error_msgid_idx)
557 / sizeof (__re_error_msgid_idx[0])), 0))
558 /* Only error codes returned by the rest of the code should be passed
559 to this routine. If we are given anything else, or if other regex
560 code generates an invalid error code, then the program has a bug.
561 Dump core so we can fix it. */
562 abort ();
563
564 msg = gettext (__re_error_msgid + __re_error_msgid_idx[errcode]);
565
566 msg_size = strlen (msg) + 1; /* Includes the null. */
567
568 if (BE (errbuf_size != 0, 1))
569 {
570 if (BE (msg_size > errbuf_size, 0))
571 {
572 memcpy (errbuf, msg, errbuf_size - 1);
573 errbuf[errbuf_size - 1] = 0;
574 }
575 else
576 memcpy (errbuf, msg, msg_size);
577 }
578
579 return msg_size;
580}
581#ifdef _LIBC
582weak_alias (__regerror, regerror)
583#endif
584
585
586#ifdef RE_ENABLE_I18N
587/* This static array is used for the map to single-byte characters when
588 UTF-8 is used. Otherwise we would allocate memory just to initialize
589 it the same all the time. UTF-8 is the preferred encoding so this is
590 a worthwhile optimization. */
591#if __GNUC__ >= 3
592static const bitset_t utf8_sb_map = {
593 /* Set the first 128 bits. */
594 [0 ... 0x80 / BITSET_WORD_BITS - 1] = BITSET_WORD_MAX
595};
596#else /* ! (__GNUC__ >= 3) */
597static bitset_t utf8_sb_map;
598#endif /* __GNUC__ >= 3 */
599#endif /* RE_ENABLE_I18N */
600
601
602static void
603free_dfa_content (re_dfa_t *dfa)
604{
605 int i, j;
606
607 if (dfa->nodes)
608 for (i = 0; i < dfa->nodes_len; ++i)
609 free_token (dfa->nodes + i);
610 re_free (dfa->nexts);
611 for (i = 0; i < dfa->nodes_len; ++i)
612 {
613 if (dfa->eclosures != NULL)
614 re_node_set_free (dfa->eclosures + i);
615 if (dfa->inveclosures != NULL)
616 re_node_set_free (dfa->inveclosures + i);
617 if (dfa->edests != NULL)
618 re_node_set_free (dfa->edests + i);
619 }
620 re_free (dfa->edests);
621 re_free (dfa->eclosures);
622 re_free (dfa->inveclosures);
623 re_free (dfa->nodes);
624
625 if (dfa->state_table)
626 for (i = 0; i <= dfa->state_hash_mask; ++i)
627 {
628 struct re_state_table_entry *entry = dfa->state_table + i;
629 for (j = 0; j < entry->num; ++j)
630 {
631 re_dfastate_t *state = entry->array[j];
632 free_state (state);
633 }
634 re_free (entry->array);
635 }
636 re_free (dfa->state_table);
637#ifdef RE_ENABLE_I18N
638 if (dfa->sb_char != utf8_sb_map)
639 re_free (dfa->sb_char);
640#endif
641 re_free (dfa->subexp_map);
642#ifdef DEBUG
643 re_free (dfa->re_str);
644#endif
645
646 re_free (dfa);
647}
648
649
650/* Free dynamically allocated space used by PREG. */
651
652void
653regfree (regex_t *preg)
654{
655 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
656 if (BE (dfa != NULL, 1))
657 free_dfa_content (dfa);
658 preg->buffer = NULL;
659 preg->allocated = 0;
660
661 re_free (preg->fastmap);
662 preg->fastmap = NULL;
663
664 re_free (preg->translate);
665 preg->translate = NULL;
666}
667#ifdef _LIBC
668weak_alias (__regfree, regfree)
669#endif
670
671/* Entry points compatible with 4.2 BSD regex library. We don't define
672 them unless specifically requested. */
673
674#if defined _REGEX_RE_COMP || defined _LIBC
675
676/* BSD has one and only one pattern buffer. */
677static struct re_pattern_buffer re_comp_buf;
678
679char *
680# ifdef _LIBC
681/* Make these definitions weak in libc, so POSIX programs can redefine
682 these names if they don't use our functions, and still use
683 regcomp/regexec above without link errors. */
684weak_function
685# endif
686re_comp (s)
687 const char *s;
688{
689 reg_errcode_t ret;
690 char *fastmap;
691
692 if (!s)
693 {
694 if (!re_comp_buf.buffer)
695 return gettext ("No previous regular expression");
696 return 0;
697 }
698
699 if (re_comp_buf.buffer)
700 {
701 fastmap = re_comp_buf.fastmap;
702 re_comp_buf.fastmap = NULL;
703 __regfree (&re_comp_buf);
704 memset (&re_comp_buf, '\0', sizeof (re_comp_buf));
705 re_comp_buf.fastmap = fastmap;
706 }
707
708 if (re_comp_buf.fastmap == NULL)
709 {
710 re_comp_buf.fastmap = (char *) malloc (SBC_MAX);
711 if (re_comp_buf.fastmap == NULL)
712 return (char *) gettext (__re_error_msgid
713 + __re_error_msgid_idx[(int) REG_ESPACE]);
714 }
715
716 /* Since `re_exec' always passes NULL for the `regs' argument, we
717 don't need to initialize the pattern buffer fields which affect it. */
718
719 /* Match anchors at newlines. */
720 re_comp_buf.newline_anchor = 1;
721
722 ret = re_compile_internal (&re_comp_buf, s, strlen (s), re_syntax_options);
723
724 if (!ret)
725 return NULL;
726
727 /* Yes, we're discarding `const' here if !HAVE_LIBINTL. */
728 return (char *) gettext (__re_error_msgid + __re_error_msgid_idx[(int) ret]);
729}
730
731#ifdef _LIBC
732libc_freeres_fn (free_mem)
733{
734 __regfree (&re_comp_buf);
735}
736#endif
737
738#endif /* _REGEX_RE_COMP */
739
740/* Internal entry point.
741 Compile the regular expression PATTERN, whose length is LENGTH.
742 SYNTAX indicate regular expression's syntax. */
743
744static reg_errcode_t
745re_compile_internal (regex_t *preg, const char * pattern, size_t length,
746 reg_syntax_t syntax)
747{
748 reg_errcode_t err = REG_NOERROR;
749 re_dfa_t *dfa;
750 re_string_t regexp;
751
752 /* Initialize the pattern buffer. */
753 preg->fastmap_accurate = 0;
754 preg->syntax = syntax;
755 preg->not_bol = preg->not_eol = 0;
756 preg->used = 0;
757 preg->re_nsub = 0;
758 preg->can_be_null = 0;
759 preg->regs_allocated = REGS_UNALLOCATED;
760
761 /* Initialize the dfa. */
762 dfa = (re_dfa_t *) preg->buffer;
763 if (BE (preg->allocated < sizeof (re_dfa_t), 0))
764 {
765 /* If zero allocated, but buffer is non-null, try to realloc
766 enough space. This loses if buffer's address is bogus, but
767 that is the user's responsibility. If ->buffer is NULL this
768 is a simple allocation. */
769 dfa = re_realloc (preg->buffer, re_dfa_t, 1);
770 if (dfa == NULL)
771 return REG_ESPACE;
772 preg->allocated = sizeof (re_dfa_t);
773 preg->buffer = (unsigned char *) dfa;
774 }
775 preg->used = sizeof (re_dfa_t);
776
777 err = init_dfa (dfa, length);
778 if (BE (err != REG_NOERROR, 0))
779 {
780 free_dfa_content (dfa);
781 preg->buffer = NULL;
782 preg->allocated = 0;
783 return err;
784 }
785#ifdef DEBUG
786 /* Note: length+1 will not overflow since it is checked in init_dfa. */
787 dfa->re_str = re_malloc (char, length + 1);
788 strncpy (dfa->re_str, pattern, length + 1);
789#endif
790
791 __libc_lock_init (dfa->lock);
792
793 err = re_string_construct (&regexp, pattern, length, preg->translate,
794 syntax & RE_ICASE, dfa);
795 if (BE (err != REG_NOERROR, 0))
796 {
797 re_compile_internal_free_return:
798 free_workarea_compile (preg);
799 re_string_destruct (&regexp);
800 free_dfa_content (dfa);
801 preg->buffer = NULL;
802 preg->allocated = 0;
803 return err;
804 }
805
806 /* Parse the regular expression, and build a structure tree. */
807 preg->re_nsub = 0;
808 dfa->str_tree = parse (&regexp, preg, syntax, &err);
809 if (BE (dfa->str_tree == NULL, 0))
810 goto re_compile_internal_free_return;
811
812 /* Analyze the tree and create the nfa. */
813 err = analyze (preg);
814 if (BE (err != REG_NOERROR, 0))
815 goto re_compile_internal_free_return;
816
817#ifdef RE_ENABLE_I18N
818 /* If possible, do searching in single byte encoding to speed things up. */
819 if (dfa->is_utf8 && !(syntax & RE_ICASE) && preg->translate == NULL)
820 optimize_utf8 (dfa);
821#endif
822
823 /* Then create the initial state of the dfa. */
824 err = create_initial_state (dfa);
825
826 /* Release work areas. */
827 free_workarea_compile (preg);
828 re_string_destruct (&regexp);
829
830 if (BE (err != REG_NOERROR, 0))
831 {
832 free_dfa_content (dfa);
833 preg->buffer = NULL;
834 preg->allocated = 0;
835 }
836
837 return err;
838}
839
840/* Initialize DFA. We use the length of the regular expression PAT_LEN
841 as the initial length of some arrays. */
842
843static reg_errcode_t
844init_dfa (re_dfa_t *dfa, size_t pat_len)
845{
846 unsigned int table_size;
847#ifndef _LIBC
848 const char *codeset_name;
849#endif
850
851 memset (dfa, '\0', sizeof (re_dfa_t));
852
853 /* Force allocation of str_tree_storage the first time. */
854 dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
855
856 /* Avoid overflows. */
857 if (pat_len == SIZE_MAX)
858 return REG_ESPACE;
859
860 dfa->nodes_alloc = pat_len + 1;
861 dfa->nodes = re_malloc (re_token_t, dfa->nodes_alloc);
862
863 /* table_size = 2 ^ ceil(log pat_len) */
864 for (table_size = 1; ; table_size <<= 1)
865 if (table_size > pat_len)
866 break;
867
868 dfa->state_table = calloc (sizeof (struct re_state_table_entry), table_size);
869 dfa->state_hash_mask = table_size - 1;
870
871 dfa->mb_cur_max = MB_CUR_MAX;
872#ifdef _LIBC
873 if (dfa->mb_cur_max == 6
874 && strcmp (_NL_CURRENT (LC_CTYPE, _NL_CTYPE_CODESET_NAME), "UTF-8") == 0)
875 dfa->is_utf8 = 1;
876 dfa->map_notascii = (_NL_CURRENT_WORD (LC_CTYPE, _NL_CTYPE_MAP_TO_NONASCII)
877 != 0);
878#else
879# ifdef HAVE_LANGINFO_CODESET
880 codeset_name = nl_langinfo (CODESET);
881# else
882 codeset_name = getenv ("LC_ALL");
883 if (codeset_name == NULL || codeset_name[0] == '\0')
884 codeset_name = getenv ("LC_CTYPE");
885 if (codeset_name == NULL || codeset_name[0] == '\0')
886 codeset_name = getenv ("LANG");
887 if (codeset_name == NULL)
888 codeset_name = "";
889 else if (strchr (codeset_name, '.') != NULL)
890 codeset_name = strchr (codeset_name, '.') + 1;
891# endif
892
893 /* strcasecmp isn't a standard interface. brute force check */
894#if 0
895 if (strcasecmp (codeset_name, "UTF-8") == 0
896 || strcasecmp (codeset_name, "UTF8") == 0)
897 dfa->is_utf8 = 1;
898#else
899 if ( (codeset_name[0] == 'U' || codeset_name[0] == 'u')
900 && (codeset_name[1] == 'T' || codeset_name[1] == 't')
901 && (codeset_name[2] == 'F' || codeset_name[2] == 'f')
902 && (codeset_name[3] == '-'
903 ? codeset_name[4] == '8' && codeset_name[5] == '\0'
904 : codeset_name[3] == '8' && codeset_name[4] == '\0'))
905 dfa->is_utf8 = 1;
906#endif
907
908 /* We check exhaustively in the loop below if this charset is a
909 superset of ASCII. */
910 dfa->map_notascii = 0;
911#endif
912
913#ifdef RE_ENABLE_I18N
914 if (dfa->mb_cur_max > 1)
915 {
916 if (dfa->is_utf8)
917 {
918#if !defined(__GNUC__) || __GNUC__ < 3
919 static short utf8_sb_map_inited = 0;
920
921 if (! utf8_sb_map_inited)
922 {
923 int i;
924
925 utf8_sb_map_inited = 0;
926 for (i = 0; i <= 0x80 / BITSET_WORD_BITS - 1; i++)
927 utf8_sb_map[i] = BITSET_WORD_MAX;
928 }
929#endif
930 dfa->sb_char = (re_bitset_ptr_t) utf8_sb_map;
931 }
932 else
933 {
934 int i, j, ch;
935
936 dfa->sb_char = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
937 if (BE (dfa->sb_char == NULL, 0))
938 return REG_ESPACE;
939
940 /* Set the bits corresponding to single byte chars. */
941 for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
942 for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
943 {
944 wint_t wch = __btowc (ch);
945 if (wch != WEOF)
946 dfa->sb_char[i] |= (bitset_word_t) 1 << j;
947# ifndef _LIBC
948 if (isascii (ch) && wch != ch)
949 dfa->map_notascii = 1;
950# endif
951 }
952 }
953 }
954#endif
955
956 if (BE (dfa->nodes == NULL || dfa->state_table == NULL, 0))
957 return REG_ESPACE;
958 return REG_NOERROR;
959}
960
961/* Initialize WORD_CHAR table, which indicate which character is
962 "word". In this case "word" means that it is the word construction
963 character used by some operators like "\<", "\>", etc. */
964
965static void
966internal_function
967init_word_char (re_dfa_t *dfa)
968{
969 int i, j, ch;
970 dfa->word_ops_used = 1;
971 for (i = 0, ch = 0; i < BITSET_WORDS; ++i)
972 for (j = 0; j < BITSET_WORD_BITS; ++j, ++ch)
973 if (isalnum (ch) || ch == '_')
974 dfa->word_char[i] |= (bitset_word_t) 1 << j;
975}
976
977/* Free the work area which are only used while compiling. */
978
979static void
980free_workarea_compile (regex_t *preg)
981{
982 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
983 bin_tree_storage_t *storage, *next;
984 for (storage = dfa->str_tree_storage; storage; storage = next)
985 {
986 next = storage->next;
987 re_free (storage);
988 }
989 dfa->str_tree_storage = NULL;
990 dfa->str_tree_storage_idx = BIN_TREE_STORAGE_SIZE;
991 dfa->str_tree = NULL;
992 re_free (dfa->org_indices);
993 dfa->org_indices = NULL;
994}
995
996/* Create initial states for all contexts. */
997
998static reg_errcode_t
999create_initial_state (re_dfa_t *dfa)
1000{
1001 int first, i;
1002 reg_errcode_t err;
1003 re_node_set init_nodes;
1004
1005 /* Initial states have the epsilon closure of the node which is
1006 the first node of the regular expression. */
1007 first = dfa->str_tree->first->node_idx;
1008 dfa->init_node = first;
1009 err = re_node_set_init_copy (&init_nodes, dfa->eclosures + first);
1010 if (BE (err != REG_NOERROR, 0))
1011 return err;
1012
1013 /* The back-references which are in initial states can epsilon transit,
1014 since in this case all of the subexpressions can be null.
1015 Then we add epsilon closures of the nodes which are the next nodes of
1016 the back-references. */
1017 if (dfa->nbackref > 0)
1018 for (i = 0; i < init_nodes.nelem; ++i)
1019 {
1020 int node_idx = init_nodes.elems[i];
1021 re_token_type_t type = dfa->nodes[node_idx].type;
1022
1023 int clexp_idx;
1024 if (type != OP_BACK_REF)
1025 continue;
1026 for (clexp_idx = 0; clexp_idx < init_nodes.nelem; ++clexp_idx)
1027 {
1028 re_token_t *clexp_node;
1029 clexp_node = dfa->nodes + init_nodes.elems[clexp_idx];
1030 if (clexp_node->type == OP_CLOSE_SUBEXP
1031 && clexp_node->opr.idx == dfa->nodes[node_idx].opr.idx)
1032 break;
1033 }
1034 if (clexp_idx == init_nodes.nelem)
1035 continue;
1036
1037 if (type == OP_BACK_REF)
1038 {
1039 int dest_idx = dfa->edests[node_idx].elems[0];
1040 if (!re_node_set_contains (&init_nodes, dest_idx))
1041 {
1042 err = re_node_set_merge (&init_nodes,
1043 dfa->eclosures + dest_idx);
1044 if (err != REG_NOERROR)
1045 return err;
1046 i = 0;
1047 }
1048 }
1049 }
1050
1051 /* It must be the first time to invoke acquire_state. */
1052 dfa->init_state = re_acquire_state_context (&err, dfa, &init_nodes, 0);
1053 /* We don't check ERR here, since the initial state must not be NULL. */
1054 if (BE (dfa->init_state == NULL, 0))
1055 return err;
1056 if (dfa->init_state->has_constraint)
1057 {
1058 dfa->init_state_word = re_acquire_state_context (&err, dfa, &init_nodes,
1059 CONTEXT_WORD);
1060 dfa->init_state_nl = re_acquire_state_context (&err, dfa, &init_nodes,
1061 CONTEXT_NEWLINE);
1062 dfa->init_state_begbuf = re_acquire_state_context (&err, dfa,
1063 &init_nodes,
1064 CONTEXT_NEWLINE
1065 | CONTEXT_BEGBUF);
1066 if (BE (dfa->init_state_word == NULL || dfa->init_state_nl == NULL
1067 || dfa->init_state_begbuf == NULL, 0))
1068 return err;
1069 }
1070 else
1071 dfa->init_state_word = dfa->init_state_nl
1072 = dfa->init_state_begbuf = dfa->init_state;
1073
1074 re_node_set_free (&init_nodes);
1075 return REG_NOERROR;
1076}
1077
1078#ifdef RE_ENABLE_I18N
1079/* If it is possible to do searching in single byte encoding instead of UTF-8
1080 to speed things up, set dfa->mb_cur_max to 1, clear is_utf8 and change
1081 DFA nodes where needed. */
1082
1083static void
1084optimize_utf8 (re_dfa_t *dfa)
1085{
1086 int node, i, mb_chars = 0, has_period = 0;
1087
1088 for (node = 0; node < dfa->nodes_len; ++node)
1089 switch (dfa->nodes[node].type)
1090 {
1091 case CHARACTER:
1092 if (dfa->nodes[node].opr.c >= 0x80)
1093 mb_chars = 1;
1094 break;
1095 case ANCHOR:
1096 switch (dfa->nodes[node].opr.ctx_type)
1097 {
1098 case LINE_FIRST:
1099 case LINE_LAST:
1100 case BUF_FIRST:
1101 case BUF_LAST:
1102 break;
1103 default:
1104 /* Word anchors etc. cannot be handled. It's okay to test
1105 opr.ctx_type since constraints (for all DFA nodes) are
1106 created by ORing one or more opr.ctx_type values. */
1107 return;
1108 }
1109 break;
1110 case OP_PERIOD:
1111 has_period = 1;
1112 break;
1113 case OP_BACK_REF:
1114 case OP_ALT:
1115 case END_OF_RE:
1116 case OP_DUP_ASTERISK:
1117 case OP_OPEN_SUBEXP:
1118 case OP_CLOSE_SUBEXP:
1119 break;
1120 case COMPLEX_BRACKET:
1121 return;
1122 case SIMPLE_BRACKET:
1123 /* Just double check. The non-ASCII range starts at 0x80. */
1124 assert (0x80 % BITSET_WORD_BITS == 0);
1125 for (i = 0x80 / BITSET_WORD_BITS; i < BITSET_WORDS; ++i)
1126 if (dfa->nodes[node].opr.sbcset[i])
1127 return;
1128 break;
1129 default:
1130 abort ();
1131 }
1132
1133 if (mb_chars || has_period)
1134 for (node = 0; node < dfa->nodes_len; ++node)
1135 {
1136 if (dfa->nodes[node].type == CHARACTER
1137 && dfa->nodes[node].opr.c >= 0x80)
1138 dfa->nodes[node].mb_partial = 0;
1139 else if (dfa->nodes[node].type == OP_PERIOD)
1140 dfa->nodes[node].type = OP_UTF8_PERIOD;
1141 }
1142
1143 /* The search can be in single byte locale. */
1144 dfa->mb_cur_max = 1;
1145 dfa->is_utf8 = 0;
1146 dfa->has_mb_node = dfa->nbackref > 0 || has_period;
1147}
1148#endif
1149
1150/* Analyze the structure tree, and calculate "first", "next", "edest",
1151 "eclosure", and "inveclosure". */
1152
1153static reg_errcode_t
1154analyze (regex_t *preg)
1155{
1156 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
1157 reg_errcode_t ret;
1158
1159 /* Allocate arrays. */
1160 dfa->nexts = re_malloc (int, dfa->nodes_alloc);
1161 dfa->org_indices = re_malloc (int, dfa->nodes_alloc);
1162 dfa->edests = re_malloc (re_node_set, dfa->nodes_alloc);
1163 dfa->eclosures = re_malloc (re_node_set, dfa->nodes_alloc);
1164 if (BE (dfa->nexts == NULL || dfa->org_indices == NULL || dfa->edests == NULL
1165 || dfa->eclosures == NULL, 0))
1166 return REG_ESPACE;
1167
1168 dfa->subexp_map = re_malloc (int, preg->re_nsub);
1169 if (dfa->subexp_map != NULL)
1170 {
1171 int i;
1172 for (i = 0; i < preg->re_nsub; i++)
1173 dfa->subexp_map[i] = i;
1174 preorder (dfa->str_tree, optimize_subexps, dfa);
1175 for (i = 0; i < preg->re_nsub; i++)
1176 if (dfa->subexp_map[i] != i)
1177 break;
1178 if (i == preg->re_nsub)
1179 {
1180 free (dfa->subexp_map);
1181 dfa->subexp_map = NULL;
1182 }
1183 }
1184
1185 ret = postorder (dfa->str_tree, lower_subexps, preg);
1186 if (BE (ret != REG_NOERROR, 0))
1187 return ret;
1188 ret = postorder (dfa->str_tree, calc_first, dfa);
1189 if (BE (ret != REG_NOERROR, 0))
1190 return ret;
1191 preorder (dfa->str_tree, calc_next, dfa);
1192 ret = preorder (dfa->str_tree, link_nfa_nodes, dfa);
1193 if (BE (ret != REG_NOERROR, 0))
1194 return ret;
1195 ret = calc_eclosure (dfa);
1196 if (BE (ret != REG_NOERROR, 0))
1197 return ret;
1198
1199 /* We only need this during the prune_impossible_nodes pass in regexec.c;
1200 skip it if p_i_n will not run, as calc_inveclosure can be quadratic. */
1201 if ((!preg->no_sub && preg->re_nsub > 0 && dfa->has_plural_match)
1202 || dfa->nbackref)
1203 {
1204 dfa->inveclosures = re_malloc (re_node_set, dfa->nodes_len);
1205 if (BE (dfa->inveclosures == NULL, 0))
1206 return REG_ESPACE;
1207 ret = calc_inveclosure (dfa);
1208 }
1209
1210 return ret;
1211}
1212
1213/* Our parse trees are very unbalanced, so we cannot use a stack to
1214 implement parse tree visits. Instead, we use parent pointers and
1215 some hairy code in these two functions. */
1216static reg_errcode_t
1217postorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
1218 void *extra)
1219{
1220 bin_tree_t *node, *prev;
1221
1222 for (node = root; ; )
1223 {
1224 /* Descend down the tree, preferably to the left (or to the right
1225 if that's the only child). */
1226 while (node->left || node->right)
1227 if (node->left)
1228 node = node->left;
1229 else
1230 node = node->right;
1231
1232 do
1233 {
1234 reg_errcode_t err = fn (extra, node);
1235 if (BE (err != REG_NOERROR, 0))
1236 return err;
1237 if (node->parent == NULL)
1238 return REG_NOERROR;
1239 prev = node;
1240 node = node->parent;
1241 }
1242 /* Go up while we have a node that is reached from the right. */
1243 while (node->right == prev || node->right == NULL);
1244 node = node->right;
1245 }
1246}
1247
1248static reg_errcode_t
1249preorder (bin_tree_t *root, reg_errcode_t (fn (void *, bin_tree_t *)),
1250 void *extra)
1251{
1252 bin_tree_t *node;
1253
1254 for (node = root; ; )
1255 {
1256 reg_errcode_t err = fn (extra, node);
1257 if (BE (err != REG_NOERROR, 0))
1258 return err;
1259
1260 /* Go to the left node, or up and to the right. */
1261 if (node->left)
1262 node = node->left;
1263 else
1264 {
1265 bin_tree_t *prev = NULL;
1266 while (node->right == prev || node->right == NULL)
1267 {
1268 prev = node;
1269 node = node->parent;
1270 if (!node)
1271 return REG_NOERROR;
1272 }
1273 node = node->right;
1274 }
1275 }
1276}
1277
1278/* Optimization pass: if a SUBEXP is entirely contained, strip it and tell
1279 re_search_internal to map the inner one's opr.idx to this one's. Adjust
1280 backreferences as well. Requires a preorder visit. */
1281static reg_errcode_t
1282optimize_subexps (void *extra, bin_tree_t *node)
1283{
1284 re_dfa_t *dfa = (re_dfa_t *) extra;
1285
1286 if (node->token.type == OP_BACK_REF && dfa->subexp_map)
1287 {
1288 int idx = node->token.opr.idx;
1289 node->token.opr.idx = dfa->subexp_map[idx];
1290 dfa->used_bkref_map |= 1 << node->token.opr.idx;
1291 }
1292
1293 else if (node->token.type == SUBEXP
1294 && node->left && node->left->token.type == SUBEXP)
1295 {
1296 int other_idx = node->left->token.opr.idx;
1297
1298 node->left = node->left->left;
1299 if (node->left)
1300 node->left->parent = node;
1301
1302 dfa->subexp_map[other_idx] = dfa->subexp_map[node->token.opr.idx];
1303 if (other_idx < BITSET_WORD_BITS)
1304 dfa->used_bkref_map &= ~((bitset_word_t) 1 << other_idx);
1305 }
1306
1307 return REG_NOERROR;
1308}
1309
1310/* Lowering pass: Turn each SUBEXP node into the appropriate concatenation
1311 of OP_OPEN_SUBEXP, the body of the SUBEXP (if any) and OP_CLOSE_SUBEXP. */
1312static reg_errcode_t
1313lower_subexps (void *extra, bin_tree_t *node)
1314{
1315 regex_t *preg = (regex_t *) extra;
1316 reg_errcode_t err = REG_NOERROR;
1317
1318 if (node->left && node->left->token.type == SUBEXP)
1319 {
1320 node->left = lower_subexp (&err, preg, node->left);
1321 if (node->left)
1322 node->left->parent = node;
1323 }
1324 if (node->right && node->right->token.type == SUBEXP)
1325 {
1326 node->right = lower_subexp (&err, preg, node->right);
1327 if (node->right)
1328 node->right->parent = node;
1329 }
1330
1331 return err;
1332}
1333
1334static bin_tree_t *
1335lower_subexp (reg_errcode_t *err, regex_t *preg, bin_tree_t *node)
1336{
1337 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
1338 bin_tree_t *body = node->left;
1339 bin_tree_t *op, *cls, *tree1, *tree;
1340
1341 if (preg->no_sub
1342 /* We do not optimize empty subexpressions, because otherwise we may
1343 have bad CONCAT nodes with NULL children. This is obviously not
1344 very common, so we do not lose much. An example that triggers
1345 this case is the sed "script" /\(\)/x. */
1346 && node->left != NULL
1347 && (node->token.opr.idx >= BITSET_WORD_BITS
1348 || !(dfa->used_bkref_map
1349 & ((bitset_word_t) 1 << node->token.opr.idx))))
1350 return node->left;
1351
1352 /* Convert the SUBEXP node to the concatenation of an
1353 OP_OPEN_SUBEXP, the contents, and an OP_CLOSE_SUBEXP. */
1354 op = create_tree (dfa, NULL, NULL, OP_OPEN_SUBEXP);
1355 cls = create_tree (dfa, NULL, NULL, OP_CLOSE_SUBEXP);
1356 tree1 = body ? create_tree (dfa, body, cls, CONCAT) : cls;
1357 tree = create_tree (dfa, op, tree1, CONCAT);
1358 if (BE (tree == NULL || tree1 == NULL || op == NULL || cls == NULL, 0))
1359 {
1360 *err = REG_ESPACE;
1361 return NULL;
1362 }
1363
1364 op->token.opr.idx = cls->token.opr.idx = node->token.opr.idx;
1365 op->token.opt_subexp = cls->token.opt_subexp = node->token.opt_subexp;
1366 return tree;
1367}
1368
1369/* Pass 1 in building the NFA: compute FIRST and create unlinked automaton
1370 nodes. Requires a postorder visit. */
1371static reg_errcode_t
1372calc_first (void *extra, bin_tree_t *node)
1373{
1374 re_dfa_t *dfa = (re_dfa_t *) extra;
1375 if (node->token.type == CONCAT)
1376 {
1377 node->first = node->left->first;
1378 node->node_idx = node->left->node_idx;
1379 }
1380 else
1381 {
1382 node->first = node;
1383 node->node_idx = re_dfa_add_node (dfa, node->token);
1384 if (BE (node->node_idx == -1, 0))
1385 return REG_ESPACE;
1386 if (node->token.type == ANCHOR)
1387 dfa->nodes[node->node_idx].constraint = node->token.opr.ctx_type;
1388 }
1389 return REG_NOERROR;
1390}
1391
1392/* Pass 2: compute NEXT on the tree. Preorder visit. */
1393static reg_errcode_t
1394calc_next (UNUSED_PARAM void *extra, bin_tree_t *node)
1395{
1396 switch (node->token.type)
1397 {
1398 case OP_DUP_ASTERISK:
1399 node->left->next = node;
1400 break;
1401 case CONCAT:
1402 node->left->next = node->right->first;
1403 node->right->next = node->next;
1404 break;
1405 default:
1406 if (node->left)
1407 node->left->next = node->next;
1408 if (node->right)
1409 node->right->next = node->next;
1410 break;
1411 }
1412 return REG_NOERROR;
1413}
1414
1415/* Pass 3: link all DFA nodes to their NEXT node (any order will do). */
1416static reg_errcode_t
1417link_nfa_nodes (void *extra, bin_tree_t *node)
1418{
1419 re_dfa_t *dfa = (re_dfa_t *) extra;
1420 int idx = node->node_idx;
1421 reg_errcode_t err = REG_NOERROR;
1422
1423 switch (node->token.type)
1424 {
1425 case CONCAT:
1426 break;
1427
1428 case END_OF_RE:
1429 assert (node->next == NULL);
1430 break;
1431
1432 case OP_DUP_ASTERISK:
1433 case OP_ALT:
1434 {
1435 int left, right;
1436 dfa->has_plural_match = 1;
1437 if (node->left != NULL)
1438 left = node->left->first->node_idx;
1439 else
1440 left = node->next->node_idx;
1441 if (node->right != NULL)
1442 right = node->right->first->node_idx;
1443 else
1444 right = node->next->node_idx;
1445 assert (left > -1);
1446 assert (right > -1);
1447 err = re_node_set_init_2 (dfa->edests + idx, left, right);
1448 }
1449 break;
1450
1451 case ANCHOR:
1452 case OP_OPEN_SUBEXP:
1453 case OP_CLOSE_SUBEXP:
1454 err = re_node_set_init_1 (dfa->edests + idx, node->next->node_idx);
1455 break;
1456
1457 case OP_BACK_REF:
1458 dfa->nexts[idx] = node->next->node_idx;
1459 if (node->token.type == OP_BACK_REF)
1460 err = re_node_set_init_1 (dfa->edests + idx, dfa->nexts[idx]);
1461 break;
1462
1463 default:
1464 assert (!IS_EPSILON_NODE (node->token.type));
1465 dfa->nexts[idx] = node->next->node_idx;
1466 break;
1467 }
1468
1469 return err;
1470}
1471
1472/* Duplicate the epsilon closure of the node ROOT_NODE.
1473 Note that duplicated nodes have constraint INIT_CONSTRAINT in addition
1474 to their own constraint. */
1475
1476static reg_errcode_t
1477internal_function
1478duplicate_node_closure (re_dfa_t *dfa, int top_org_node, int top_clone_node,
1479 int root_node, unsigned int init_constraint)
1480{
1481 int org_node, clone_node, ret;
1482 unsigned int constraint = init_constraint;
1483 for (org_node = top_org_node, clone_node = top_clone_node;;)
1484 {
1485 int org_dest, clone_dest;
1486 if (dfa->nodes[org_node].type == OP_BACK_REF)
1487 {
1488 /* If the back reference epsilon-transit, its destination must
1489 also have the constraint. Then duplicate the epsilon closure
1490 of the destination of the back reference, and store it in
1491 edests of the back reference. */
1492 org_dest = dfa->nexts[org_node];
1493 re_node_set_empty (dfa->edests + clone_node);
1494 clone_dest = duplicate_node (dfa, org_dest, constraint);
1495 if (BE (clone_dest == -1, 0))
1496 return REG_ESPACE;
1497 dfa->nexts[clone_node] = dfa->nexts[org_node];
1498 ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
1499 if (BE (ret < 0, 0))
1500 return REG_ESPACE;
1501 }
1502 else if (dfa->edests[org_node].nelem == 0)
1503 {
1504 /* In case of the node can't epsilon-transit, don't duplicate the
1505 destination and store the original destination as the
1506 destination of the node. */
1507 dfa->nexts[clone_node] = dfa->nexts[org_node];
1508 break;
1509 }
1510 else if (dfa->edests[org_node].nelem == 1)
1511 {
1512 /* In case of the node can epsilon-transit, and it has only one
1513 destination. */
1514 org_dest = dfa->edests[org_node].elems[0];
1515 re_node_set_empty (dfa->edests + clone_node);
1516 /* If the node is root_node itself, it means the epsilon clsoure
1517 has a loop. Then tie it to the destination of the root_node. */
1518 if (org_node == root_node && clone_node != org_node)
1519 {
1520 ret = re_node_set_insert (dfa->edests + clone_node, org_dest);
1521 if (BE (ret < 0, 0))
1522 return REG_ESPACE;
1523 break;
1524 }
1525 /* In case of the node has another constraint, add it. */
1526 constraint |= dfa->nodes[org_node].constraint;
1527 clone_dest = duplicate_node (dfa, org_dest, constraint);
1528 if (BE (clone_dest == -1, 0))
1529 return REG_ESPACE;
1530 ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
1531 if (BE (ret < 0, 0))
1532 return REG_ESPACE;
1533 }
1534 else /* dfa->edests[org_node].nelem == 2 */
1535 {
1536 /* In case of the node can epsilon-transit, and it has two
1537 destinations. In the bin_tree_t and DFA, that's '|' and '*'. */
1538 org_dest = dfa->edests[org_node].elems[0];
1539 re_node_set_empty (dfa->edests + clone_node);
1540 /* Search for a duplicated node which satisfies the constraint. */
1541 clone_dest = search_duplicated_node (dfa, org_dest, constraint);
1542 if (clone_dest == -1)
1543 {
1544 /* There is no such duplicated node, create a new one. */
1545 reg_errcode_t err;
1546 clone_dest = duplicate_node (dfa, org_dest, constraint);
1547 if (BE (clone_dest == -1, 0))
1548 return REG_ESPACE;
1549 ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
1550 if (BE (ret < 0, 0))
1551 return REG_ESPACE;
1552 err = duplicate_node_closure (dfa, org_dest, clone_dest,
1553 root_node, constraint);
1554 if (BE (err != REG_NOERROR, 0))
1555 return err;
1556 }
1557 else
1558 {
1559 /* There is a duplicated node which satisfies the constraint,
1560 use it to avoid infinite loop. */
1561 ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
1562 if (BE (ret < 0, 0))
1563 return REG_ESPACE;
1564 }
1565
1566 org_dest = dfa->edests[org_node].elems[1];
1567 clone_dest = duplicate_node (dfa, org_dest, constraint);
1568 if (BE (clone_dest == -1, 0))
1569 return REG_ESPACE;
1570 ret = re_node_set_insert (dfa->edests + clone_node, clone_dest);
1571 if (BE (ret < 0, 0))
1572 return REG_ESPACE;
1573 }
1574 org_node = org_dest;
1575 clone_node = clone_dest;
1576 }
1577 return REG_NOERROR;
1578}
1579
1580/* Search for a node which is duplicated from the node ORG_NODE, and
1581 satisfies the constraint CONSTRAINT. */
1582
1583static int
1584search_duplicated_node (const re_dfa_t *dfa, int org_node,
1585 unsigned int constraint)
1586{
1587 int idx;
1588 for (idx = dfa->nodes_len - 1; dfa->nodes[idx].duplicated && idx > 0; --idx)
1589 {
1590 if (org_node == dfa->org_indices[idx]
1591 && constraint == dfa->nodes[idx].constraint)
1592 return idx; /* Found. */
1593 }
1594 return -1; /* Not found. */
1595}
1596
1597/* Duplicate the node whose index is ORG_IDX and set the constraint CONSTRAINT.
1598 Return the index of the new node, or -1 if insufficient storage is
1599 available. */
1600
1601static int
1602duplicate_node (re_dfa_t *dfa, int org_idx, unsigned int constraint)
1603{
1604 int dup_idx = re_dfa_add_node (dfa, dfa->nodes[org_idx]);
1605 if (BE (dup_idx != -1, 1))
1606 {
1607 dfa->nodes[dup_idx].constraint = constraint;
1608 dfa->nodes[dup_idx].constraint |= dfa->nodes[org_idx].constraint;
1609 dfa->nodes[dup_idx].duplicated = 1;
1610
1611 /* Store the index of the original node. */
1612 dfa->org_indices[dup_idx] = org_idx;
1613 }
1614 return dup_idx;
1615}
1616
1617static reg_errcode_t
1618calc_inveclosure (re_dfa_t *dfa)
1619{
1620 int src, idx, ret;
1621 for (idx = 0; idx < dfa->nodes_len; ++idx)
1622 re_node_set_init_empty (dfa->inveclosures + idx);
1623
1624 for (src = 0; src < dfa->nodes_len; ++src)
1625 {
1626 int *elems = dfa->eclosures[src].elems;
1627 for (idx = 0; idx < dfa->eclosures[src].nelem; ++idx)
1628 {
1629 ret = re_node_set_insert_last (dfa->inveclosures + elems[idx], src);
1630 if (BE (ret == -1, 0))
1631 return REG_ESPACE;
1632 }
1633 }
1634
1635 return REG_NOERROR;
1636}
1637
1638/* Calculate "eclosure" for all the node in DFA. */
1639
1640static reg_errcode_t
1641calc_eclosure (re_dfa_t *dfa)
1642{
1643 int node_idx, incomplete;
1644#ifdef DEBUG
1645 assert (dfa->nodes_len > 0);
1646#endif
1647 incomplete = 0;
1648 /* For each nodes, calculate epsilon closure. */
1649 for (node_idx = 0; ; ++node_idx)
1650 {
1651 reg_errcode_t err;
1652 re_node_set eclosure_elem;
1653 if (node_idx == dfa->nodes_len)
1654 {
1655 if (!incomplete)
1656 break;
1657 incomplete = 0;
1658 node_idx = 0;
1659 }
1660
1661#ifdef DEBUG
1662 assert (dfa->eclosures[node_idx].nelem != -1);
1663#endif
1664
1665 /* If we have already calculated, skip it. */
1666 if (dfa->eclosures[node_idx].nelem != 0)
1667 continue;
1668 /* Calculate epsilon closure of `node_idx'. */
1669 err = calc_eclosure_iter (&eclosure_elem, dfa, node_idx, 1);
1670 if (BE (err != REG_NOERROR, 0))
1671 return err;
1672
1673 if (dfa->eclosures[node_idx].nelem == 0)
1674 {
1675 incomplete = 1;
1676 re_node_set_free (&eclosure_elem);
1677 }
1678 }
1679 return REG_NOERROR;
1680}
1681
1682/* Calculate epsilon closure of NODE. */
1683
1684static reg_errcode_t
1685calc_eclosure_iter (re_node_set *new_set, re_dfa_t *dfa, int node, int root)
1686{
1687 reg_errcode_t err;
1688 int i;
1689 re_node_set eclosure;
1690 int ret;
1691 int incomplete = 0;
1692 err = re_node_set_alloc (&eclosure, dfa->edests[node].nelem + 1);
1693 if (BE (err != REG_NOERROR, 0))
1694 return err;
1695
1696 /* This indicates that we are calculating this node now.
1697 We reference this value to avoid infinite loop. */
1698 dfa->eclosures[node].nelem = -1;
1699
1700 /* If the current node has constraints, duplicate all nodes
1701 since they must inherit the constraints. */
1702 if (dfa->nodes[node].constraint
1703 && dfa->edests[node].nelem
1704 && !dfa->nodes[dfa->edests[node].elems[0]].duplicated)
1705 {
1706 err = duplicate_node_closure (dfa, node, node, node,
1707 dfa->nodes[node].constraint);
1708 if (BE (err != REG_NOERROR, 0))
1709 return err;
1710 }
1711
1712 /* Expand each epsilon destination nodes. */
1713 if (IS_EPSILON_NODE(dfa->nodes[node].type))
1714 for (i = 0; i < dfa->edests[node].nelem; ++i)
1715 {
1716 re_node_set eclosure_elem;
1717 int edest = dfa->edests[node].elems[i];
1718 /* If calculating the epsilon closure of `edest' is in progress,
1719 return intermediate result. */
1720 if (dfa->eclosures[edest].nelem == -1)
1721 {
1722 incomplete = 1;
1723 continue;
1724 }
1725 /* If we haven't calculated the epsilon closure of `edest' yet,
1726 calculate now. Otherwise use calculated epsilon closure. */
1727 if (dfa->eclosures[edest].nelem == 0)
1728 {
1729 err = calc_eclosure_iter (&eclosure_elem, dfa, edest, 0);
1730 if (BE (err != REG_NOERROR, 0))
1731 return err;
1732 }
1733 else
1734 eclosure_elem = dfa->eclosures[edest];
1735 /* Merge the epsilon closure of `edest'. */
1736 err = re_node_set_merge (&eclosure, &eclosure_elem);
1737 if (BE (err != REG_NOERROR, 0))
1738 return err;
1739 /* If the epsilon closure of `edest' is incomplete,
1740 the epsilon closure of this node is also incomplete. */
1741 if (dfa->eclosures[edest].nelem == 0)
1742 {
1743 incomplete = 1;
1744 re_node_set_free (&eclosure_elem);
1745 }
1746 }
1747
1748 /* An epsilon closure includes itself. */
1749 ret = re_node_set_insert (&eclosure, node);
1750 if (BE (ret < 0, 0))
1751 return REG_ESPACE;
1752 if (incomplete && !root)
1753 dfa->eclosures[node].nelem = 0;
1754 else
1755 dfa->eclosures[node] = eclosure;
1756 *new_set = eclosure;
1757 return REG_NOERROR;
1758}
1759
1760/* Functions for token which are used in the parser. */
1761
1762/* Fetch a token from INPUT.
1763 We must not use this function inside bracket expressions. */
1764
1765static void
1766internal_function
1767fetch_token (re_token_t *result, re_string_t *input, reg_syntax_t syntax)
1768{
1769 re_string_skip_bytes (input, peek_token (result, input, syntax));
1770}
1771
1772/* Peek a token from INPUT, and return the length of the token.
1773 We must not use this function inside bracket expressions. */
1774
1775static int
1776internal_function
1777peek_token (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
1778{
1779 unsigned char c;
1780
1781 if (re_string_eoi (input))
1782 {
1783 token->type = END_OF_RE;
1784 return 0;
1785 }
1786
1787 c = re_string_peek_byte (input, 0);
1788 token->opr.c = c;
1789
1790 token->word_char = 0;
1791#ifdef RE_ENABLE_I18N
1792 token->mb_partial = 0;
1793 if (input->mb_cur_max > 1 &&
1794 !re_string_first_byte (input, re_string_cur_idx (input)))
1795 {
1796 token->type = CHARACTER;
1797 token->mb_partial = 1;
1798 return 1;
1799 }
1800#endif
1801 if (c == '\\')
1802 {
1803 unsigned char c2;
1804 if (re_string_cur_idx (input) + 1 >= re_string_length (input))
1805 {
1806 token->type = BACK_SLASH;
1807 return 1;
1808 }
1809
1810 c2 = re_string_peek_byte_case (input, 1);
1811 token->opr.c = c2;
1812 token->type = CHARACTER;
1813#ifdef RE_ENABLE_I18N
1814 if (input->mb_cur_max > 1)
1815 {
1816 wint_t wc = re_string_wchar_at (input,
1817 re_string_cur_idx (input) + 1);
1818 token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
1819 }
1820 else
1821#endif
1822 token->word_char = IS_WORD_CHAR (c2) != 0;
1823
1824 switch (c2)
1825 {
1826 case '|':
1827 if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_NO_BK_VBAR))
1828 token->type = OP_ALT;
1829 break;
1830 case '1': case '2': case '3': case '4': case '5':
1831 case '6': case '7': case '8': case '9':
1832 if (!(syntax & RE_NO_BK_REFS))
1833 {
1834 token->type = OP_BACK_REF;
1835 token->opr.idx = c2 - '1';
1836 }
1837 break;
1838 case '<':
1839 if (!(syntax & RE_NO_GNU_OPS))
1840 {
1841 token->type = ANCHOR;
1842 token->opr.ctx_type = WORD_FIRST;
1843 }
1844 break;
1845 case '>':
1846 if (!(syntax & RE_NO_GNU_OPS))
1847 {
1848 token->type = ANCHOR;
1849 token->opr.ctx_type = WORD_LAST;
1850 }
1851 break;
1852 case 'b':
1853 if (!(syntax & RE_NO_GNU_OPS))
1854 {
1855 token->type = ANCHOR;
1856 token->opr.ctx_type = WORD_DELIM;
1857 }
1858 break;
1859 case 'B':
1860 if (!(syntax & RE_NO_GNU_OPS))
1861 {
1862 token->type = ANCHOR;
1863 token->opr.ctx_type = NOT_WORD_DELIM;
1864 }
1865 break;
1866 case 'w':
1867 if (!(syntax & RE_NO_GNU_OPS))
1868 token->type = OP_WORD;
1869 break;
1870 case 'W':
1871 if (!(syntax & RE_NO_GNU_OPS))
1872 token->type = OP_NOTWORD;
1873 break;
1874 case 's':
1875 if (!(syntax & RE_NO_GNU_OPS))
1876 token->type = OP_SPACE;
1877 break;
1878 case 'S':
1879 if (!(syntax & RE_NO_GNU_OPS))
1880 token->type = OP_NOTSPACE;
1881 break;
1882 case '`':
1883 if (!(syntax & RE_NO_GNU_OPS))
1884 {
1885 token->type = ANCHOR;
1886 token->opr.ctx_type = BUF_FIRST;
1887 }
1888 break;
1889 case '\'':
1890 if (!(syntax & RE_NO_GNU_OPS))
1891 {
1892 token->type = ANCHOR;
1893 token->opr.ctx_type = BUF_LAST;
1894 }
1895 break;
1896 case '(':
1897 if (!(syntax & RE_NO_BK_PARENS))
1898 token->type = OP_OPEN_SUBEXP;
1899 break;
1900 case ')':
1901 if (!(syntax & RE_NO_BK_PARENS))
1902 token->type = OP_CLOSE_SUBEXP;
1903 break;
1904 case '+':
1905 if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
1906 token->type = OP_DUP_PLUS;
1907 break;
1908 case '?':
1909 if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_BK_PLUS_QM))
1910 token->type = OP_DUP_QUESTION;
1911 break;
1912 case '{':
1913 if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
1914 token->type = OP_OPEN_DUP_NUM;
1915 break;
1916 case '}':
1917 if ((syntax & RE_INTERVALS) && (!(syntax & RE_NO_BK_BRACES)))
1918 token->type = OP_CLOSE_DUP_NUM;
1919 break;
1920 default:
1921 break;
1922 }
1923 return 2;
1924 }
1925
1926 token->type = CHARACTER;
1927#ifdef RE_ENABLE_I18N
1928 if (input->mb_cur_max > 1)
1929 {
1930 wint_t wc = re_string_wchar_at (input, re_string_cur_idx (input));
1931 token->word_char = IS_WIDE_WORD_CHAR (wc) != 0;
1932 }
1933 else
1934#endif
1935 token->word_char = IS_WORD_CHAR (token->opr.c);
1936
1937 switch (c)
1938 {
1939 case '\n':
1940 if (syntax & RE_NEWLINE_ALT)
1941 token->type = OP_ALT;
1942 break;
1943 case '|':
1944 if (!(syntax & RE_LIMITED_OPS) && (syntax & RE_NO_BK_VBAR))
1945 token->type = OP_ALT;
1946 break;
1947 case '*':
1948 token->type = OP_DUP_ASTERISK;
1949 break;
1950 case '+':
1951 if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
1952 token->type = OP_DUP_PLUS;
1953 break;
1954 case '?':
1955 if (!(syntax & RE_LIMITED_OPS) && !(syntax & RE_BK_PLUS_QM))
1956 token->type = OP_DUP_QUESTION;
1957 break;
1958 case '{':
1959 if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
1960 token->type = OP_OPEN_DUP_NUM;
1961 break;
1962 case '}':
1963 if ((syntax & RE_INTERVALS) && (syntax & RE_NO_BK_BRACES))
1964 token->type = OP_CLOSE_DUP_NUM;
1965 break;
1966 case '(':
1967 if (syntax & RE_NO_BK_PARENS)
1968 token->type = OP_OPEN_SUBEXP;
1969 break;
1970 case ')':
1971 if (syntax & RE_NO_BK_PARENS)
1972 token->type = OP_CLOSE_SUBEXP;
1973 break;
1974 case '[':
1975 token->type = OP_OPEN_BRACKET;
1976 break;
1977 case '.':
1978 token->type = OP_PERIOD;
1979 break;
1980 case '^':
1981 if (!(syntax & (RE_CONTEXT_INDEP_ANCHORS | RE_CARET_ANCHORS_HERE)) &&
1982 re_string_cur_idx (input) != 0)
1983 {
1984 char prev = re_string_peek_byte (input, -1);
1985 if (!(syntax & RE_NEWLINE_ALT) || prev != '\n')
1986 break;
1987 }
1988 token->type = ANCHOR;
1989 token->opr.ctx_type = LINE_FIRST;
1990 break;
1991 case '$':
1992 if (!(syntax & RE_CONTEXT_INDEP_ANCHORS) &&
1993 re_string_cur_idx (input) + 1 != re_string_length (input))
1994 {
1995 re_token_t next;
1996 re_string_skip_bytes (input, 1);
1997 peek_token (&next, input, syntax);
1998 re_string_skip_bytes (input, -1);
1999 if (next.type != OP_ALT && next.type != OP_CLOSE_SUBEXP)
2000 break;
2001 }
2002 token->type = ANCHOR;
2003 token->opr.ctx_type = LINE_LAST;
2004 break;
2005 default:
2006 break;
2007 }
2008 return 1;
2009}
2010
2011/* Peek a token from INPUT, and return the length of the token.
2012 We must not use this function out of bracket expressions. */
2013
2014static int
2015internal_function
2016peek_token_bracket (re_token_t *token, re_string_t *input, reg_syntax_t syntax)
2017{
2018 unsigned char c;
2019 if (re_string_eoi (input))
2020 {
2021 token->type = END_OF_RE;
2022 return 0;
2023 }
2024 c = re_string_peek_byte (input, 0);
2025 token->opr.c = c;
2026
2027#ifdef RE_ENABLE_I18N
2028 if (input->mb_cur_max > 1 &&
2029 !re_string_first_byte (input, re_string_cur_idx (input)))
2030 {
2031 token->type = CHARACTER;
2032 return 1;
2033 }
2034#endif /* RE_ENABLE_I18N */
2035
2036 if (c == '\\' && (syntax & RE_BACKSLASH_ESCAPE_IN_LISTS)
2037 && re_string_cur_idx (input) + 1 < re_string_length (input))
2038 {
2039 /* In this case, '\' escape a character. */
2040 unsigned char c2;
2041 re_string_skip_bytes (input, 1);
2042 c2 = re_string_peek_byte (input, 0);
2043 token->opr.c = c2;
2044 token->type = CHARACTER;
2045 return 1;
2046 }
2047 if (c == '[') /* '[' is a special char in a bracket exps. */
2048 {
2049 unsigned char c2;
2050 int token_len;
2051 if (re_string_cur_idx (input) + 1 < re_string_length (input))
2052 c2 = re_string_peek_byte (input, 1);
2053 else
2054 c2 = 0;
2055 token->opr.c = c2;
2056 token_len = 2;
2057 switch (c2)
2058 {
2059 case '.':
2060 token->type = OP_OPEN_COLL_ELEM;
2061 break;
2062 case '=':
2063 token->type = OP_OPEN_EQUIV_CLASS;
2064 break;
2065 case ':':
2066 if (syntax & RE_CHAR_CLASSES)
2067 {
2068 token->type = OP_OPEN_CHAR_CLASS;
2069 break;
2070 }
2071 /* else fall through. */
2072 default:
2073 token->type = CHARACTER;
2074 token->opr.c = c;
2075 token_len = 1;
2076 break;
2077 }
2078 return token_len;
2079 }
2080 switch (c)
2081 {
2082 case '-':
2083 token->type = OP_CHARSET_RANGE;
2084 break;
2085 case ']':
2086 token->type = OP_CLOSE_BRACKET;
2087 break;
2088 case '^':
2089 token->type = OP_NON_MATCH_LIST;
2090 break;
2091 default:
2092 token->type = CHARACTER;
2093 }
2094 return 1;
2095}
2096
2097/* Functions for parser. */
2098
2099/* Entry point of the parser.
2100 Parse the regular expression REGEXP and return the structure tree.
2101 If an error has occurred, ERR is set by error code, and return NULL.
2102 This function build the following tree, from regular expression <reg_exp>:
2103 CAT
2104 / \
2105 / \
2106 <reg_exp> EOR
2107
2108 CAT means concatenation.
2109 EOR means end of regular expression. */
2110
2111static bin_tree_t *
2112parse (re_string_t *regexp, regex_t *preg, reg_syntax_t syntax,
2113 reg_errcode_t *err)
2114{
2115 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
2116 bin_tree_t *tree, *eor, *root;
2117 re_token_t current_token;
2118 dfa->syntax = syntax;
2119 fetch_token (&current_token, regexp, syntax | RE_CARET_ANCHORS_HERE);
2120 tree = parse_reg_exp (regexp, preg, &current_token, syntax, 0, err);
2121 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2122 return NULL;
2123 eor = create_tree (dfa, NULL, NULL, END_OF_RE);
2124 if (tree != NULL)
2125 root = create_tree (dfa, tree, eor, CONCAT);
2126 else
2127 root = eor;
2128 if (BE (eor == NULL || root == NULL, 0))
2129 {
2130 *err = REG_ESPACE;
2131 return NULL;
2132 }
2133 return root;
2134}
2135
2136/* This function build the following tree, from regular expression
2137 <branch1>|<branch2>:
2138 ALT
2139 / \
2140 / \
2141 <branch1> <branch2>
2142
2143 ALT means alternative, which represents the operator `|'. */
2144
2145static bin_tree_t *
2146parse_reg_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
2147 reg_syntax_t syntax, int nest, reg_errcode_t *err)
2148{
2149 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
2150 bin_tree_t *tree, *branch = NULL;
2151 tree = parse_branch (regexp, preg, token, syntax, nest, err);
2152 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2153 return NULL;
2154
2155 while (token->type == OP_ALT)
2156 {
2157 fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
2158 if (token->type != OP_ALT && token->type != END_OF_RE
2159 && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
2160 {
2161 branch = parse_branch (regexp, preg, token, syntax, nest, err);
2162 if (BE (*err != REG_NOERROR && branch == NULL, 0))
2163 return NULL;
2164 }
2165 else
2166 branch = NULL;
2167 tree = create_tree (dfa, tree, branch, OP_ALT);
2168 if (BE (tree == NULL, 0))
2169 {
2170 *err = REG_ESPACE;
2171 return NULL;
2172 }
2173 }
2174 return tree;
2175}
2176
2177/* This function build the following tree, from regular expression
2178 <exp1><exp2>:
2179 CAT
2180 / \
2181 / \
2182 <exp1> <exp2>
2183
2184 CAT means concatenation. */
2185
2186static bin_tree_t *
2187parse_branch (re_string_t *regexp, regex_t *preg, re_token_t *token,
2188 reg_syntax_t syntax, int nest, reg_errcode_t *err)
2189{
2190 bin_tree_t *tree, *exp;
2191 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
2192 tree = parse_expression (regexp, preg, token, syntax, nest, err);
2193 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2194 return NULL;
2195
2196 while (token->type != OP_ALT && token->type != END_OF_RE
2197 && (nest == 0 || token->type != OP_CLOSE_SUBEXP))
2198 {
2199 exp = parse_expression (regexp, preg, token, syntax, nest, err);
2200 if (BE (*err != REG_NOERROR && exp == NULL, 0))
2201 {
2202 return NULL;
2203 }
2204 if (tree != NULL && exp != NULL)
2205 {
2206 tree = create_tree (dfa, tree, exp, CONCAT);
2207 if (tree == NULL)
2208 {
2209 *err = REG_ESPACE;
2210 return NULL;
2211 }
2212 }
2213 else if (tree == NULL)
2214 tree = exp;
2215 /* Otherwise exp == NULL, we don't need to create new tree. */
2216 }
2217 return tree;
2218}
2219
2220/* This function build the following tree, from regular expression a*:
2221 *
2222 |
2223 a
2224*/
2225
2226static bin_tree_t *
2227parse_expression (re_string_t *regexp, regex_t *preg, re_token_t *token,
2228 reg_syntax_t syntax, int nest, reg_errcode_t *err)
2229{
2230 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
2231 bin_tree_t *tree;
2232 switch (token->type)
2233 {
2234 case CHARACTER:
2235 tree = create_token_tree (dfa, NULL, NULL, token);
2236 if (BE (tree == NULL, 0))
2237 {
2238 *err = REG_ESPACE;
2239 return NULL;
2240 }
2241#ifdef RE_ENABLE_I18N
2242 if (dfa->mb_cur_max > 1)
2243 {
2244 while (!re_string_eoi (regexp)
2245 && !re_string_first_byte (regexp, re_string_cur_idx (regexp)))
2246 {
2247 bin_tree_t *mbc_remain;
2248 fetch_token (token, regexp, syntax);
2249 mbc_remain = create_token_tree (dfa, NULL, NULL, token);
2250 tree = create_tree (dfa, tree, mbc_remain, CONCAT);
2251 if (BE (mbc_remain == NULL || tree == NULL, 0))
2252 {
2253 *err = REG_ESPACE;
2254 return NULL;
2255 }
2256 }
2257 }
2258#endif
2259 break;
2260 case OP_OPEN_SUBEXP:
2261 tree = parse_sub_exp (regexp, preg, token, syntax, nest + 1, err);
2262 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2263 return NULL;
2264 break;
2265 case OP_OPEN_BRACKET:
2266 tree = parse_bracket_exp (regexp, dfa, token, syntax, err);
2267 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2268 return NULL;
2269 break;
2270 case OP_BACK_REF:
2271 if (!BE (dfa->completed_bkref_map & (1 << token->opr.idx), 1))
2272 {
2273 *err = REG_ESUBREG;
2274 return NULL;
2275 }
2276 dfa->used_bkref_map |= 1 << token->opr.idx;
2277 tree = create_token_tree (dfa, NULL, NULL, token);
2278 if (BE (tree == NULL, 0))
2279 {
2280 *err = REG_ESPACE;
2281 return NULL;
2282 }
2283 ++dfa->nbackref;
2284 dfa->has_mb_node = 1;
2285 break;
2286 case OP_OPEN_DUP_NUM:
2287 if (syntax & RE_CONTEXT_INVALID_DUP)
2288 {
2289 *err = REG_BADRPT;
2290 return NULL;
2291 }
2292 /* FALLTHROUGH */
2293 case OP_DUP_ASTERISK:
2294 case OP_DUP_PLUS:
2295 case OP_DUP_QUESTION:
2296 if (syntax & RE_CONTEXT_INVALID_OPS)
2297 {
2298 *err = REG_BADRPT;
2299 return NULL;
2300 }
2301 else if (syntax & RE_CONTEXT_INDEP_OPS)
2302 {
2303 fetch_token (token, regexp, syntax);
2304 return parse_expression (regexp, preg, token, syntax, nest, err);
2305 }
2306 /* else fall through */
2307 case OP_CLOSE_SUBEXP:
2308 if ((token->type == OP_CLOSE_SUBEXP) &&
2309 !(syntax & RE_UNMATCHED_RIGHT_PAREN_ORD))
2310 {
2311 *err = REG_ERPAREN;
2312 return NULL;
2313 }
2314 /* else fall through */
2315 case OP_CLOSE_DUP_NUM:
2316 /* We treat it as a normal character. */
2317
2318 /* Then we can these characters as normal characters. */
2319 token->type = CHARACTER;
2320 /* mb_partial and word_char bits should be initialized already
2321 by peek_token. */
2322 tree = create_token_tree (dfa, NULL, NULL, token);
2323 if (BE (tree == NULL, 0))
2324 {
2325 *err = REG_ESPACE;
2326 return NULL;
2327 }
2328 break;
2329 case ANCHOR:
2330 if ((token->opr.ctx_type
2331 & (WORD_DELIM | NOT_WORD_DELIM | WORD_FIRST | WORD_LAST))
2332 && dfa->word_ops_used == 0)
2333 init_word_char (dfa);
2334 if (token->opr.ctx_type == WORD_DELIM
2335 || token->opr.ctx_type == NOT_WORD_DELIM)
2336 {
2337 bin_tree_t *tree_first, *tree_last;
2338 if (token->opr.ctx_type == WORD_DELIM)
2339 {
2340 token->opr.ctx_type = WORD_FIRST;
2341 tree_first = create_token_tree (dfa, NULL, NULL, token);
2342 token->opr.ctx_type = WORD_LAST;
2343 }
2344 else
2345 {
2346 token->opr.ctx_type = INSIDE_WORD;
2347 tree_first = create_token_tree (dfa, NULL, NULL, token);
2348 token->opr.ctx_type = INSIDE_NOTWORD;
2349 }
2350 tree_last = create_token_tree (dfa, NULL, NULL, token);
2351 tree = create_tree (dfa, tree_first, tree_last, OP_ALT);
2352 if (BE (tree_first == NULL || tree_last == NULL || tree == NULL, 0))
2353 {
2354 *err = REG_ESPACE;
2355 return NULL;
2356 }
2357 }
2358 else
2359 {
2360 tree = create_token_tree (dfa, NULL, NULL, token);
2361 if (BE (tree == NULL, 0))
2362 {
2363 *err = REG_ESPACE;
2364 return NULL;
2365 }
2366 }
2367 /* We must return here, since ANCHORs can't be followed
2368 by repetition operators.
2369 eg. RE"^*" is invalid or "<ANCHOR(^)><CHAR(*)>",
2370 it must not be "<ANCHOR(^)><REPEAT(*)>". */
2371 fetch_token (token, regexp, syntax);
2372 return tree;
2373 case OP_PERIOD:
2374 tree = create_token_tree (dfa, NULL, NULL, token);
2375 if (BE (tree == NULL, 0))
2376 {
2377 *err = REG_ESPACE;
2378 return NULL;
2379 }
2380 if (dfa->mb_cur_max > 1)
2381 dfa->has_mb_node = 1;
2382 break;
2383 case OP_WORD:
2384 case OP_NOTWORD:
2385 tree = build_charclass_op (dfa, regexp->trans,
2386 "alnum",
2387 "_",
2388 token->type == OP_NOTWORD, err);
2389 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2390 return NULL;
2391 break;
2392 case OP_SPACE:
2393 case OP_NOTSPACE:
2394 tree = build_charclass_op (dfa, regexp->trans,
2395 "space",
2396 "",
2397 token->type == OP_NOTSPACE, err);
2398 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2399 return NULL;
2400 break;
2401 case OP_ALT:
2402 case END_OF_RE:
2403 return NULL;
2404 case BACK_SLASH:
2405 *err = REG_EESCAPE;
2406 return NULL;
2407 default:
2408 /* Must not happen? */
2409#ifdef DEBUG
2410 assert (0);
2411#endif
2412 return NULL;
2413 }
2414 fetch_token (token, regexp, syntax);
2415
2416 while (token->type == OP_DUP_ASTERISK || token->type == OP_DUP_PLUS
2417 || token->type == OP_DUP_QUESTION || token->type == OP_OPEN_DUP_NUM)
2418 {
2419 tree = parse_dup_op (tree, regexp, dfa, token, syntax, err);
2420 if (BE (*err != REG_NOERROR && tree == NULL, 0))
2421 return NULL;
2422 /* In BRE consecutive duplications are not allowed. */
2423 if ((syntax & RE_CONTEXT_INVALID_DUP)
2424 && (token->type == OP_DUP_ASTERISK
2425 || token->type == OP_OPEN_DUP_NUM))
2426 {
2427 *err = REG_BADRPT;
2428 return NULL;
2429 }
2430 }
2431
2432 return tree;
2433}
2434
2435/* This function build the following tree, from regular expression
2436 (<reg_exp>):
2437 SUBEXP
2438 |
2439 <reg_exp>
2440*/
2441
2442static bin_tree_t *
2443parse_sub_exp (re_string_t *regexp, regex_t *preg, re_token_t *token,
2444 reg_syntax_t syntax, int nest, reg_errcode_t *err)
2445{
2446 re_dfa_t *dfa = (re_dfa_t *) preg->buffer;
2447 bin_tree_t *tree;
2448 size_t cur_nsub;
2449 cur_nsub = preg->re_nsub++;
2450
2451 fetch_token (token, regexp, syntax | RE_CARET_ANCHORS_HERE);
2452
2453 /* The subexpression may be a null string. */
2454 if (token->type == OP_CLOSE_SUBEXP)
2455 tree = NULL;
2456 else
2457 {
2458 tree = parse_reg_exp (regexp, preg, token, syntax, nest, err);
2459 if (BE (*err == REG_NOERROR && token->type != OP_CLOSE_SUBEXP, 0))
2460 *err = REG_EPAREN;
2461 if (BE (*err != REG_NOERROR, 0))
2462 return NULL;
2463 }
2464
2465 if (cur_nsub <= '9' - '1')
2466 dfa->completed_bkref_map |= 1 << cur_nsub;
2467
2468 tree = create_tree (dfa, tree, NULL, SUBEXP);
2469 if (BE (tree == NULL, 0))
2470 {
2471 *err = REG_ESPACE;
2472 return NULL;
2473 }
2474 tree->token.opr.idx = cur_nsub;
2475 return tree;
2476}
2477
2478/* This function parse repetition operators like "*", "+", "{1,3}" etc. */
2479
2480static bin_tree_t *
2481parse_dup_op (bin_tree_t *elem, re_string_t *regexp, re_dfa_t *dfa,
2482 re_token_t *token, reg_syntax_t syntax, reg_errcode_t *err)
2483{
2484 bin_tree_t *tree = NULL, *old_tree = NULL;
2485 int i, start, end, start_idx = re_string_cur_idx (regexp);
2486#ifndef RE_TOKEN_INIT_BUG
2487 re_token_t start_token = *token;
2488#else
2489 re_token_t start_token;
2490
2491 memcpy ((void *) &start_token, (void *) token, sizeof start_token);
2492#endif
2493
2494 if (token->type == OP_OPEN_DUP_NUM)
2495 {
2496 end = 0;
2497 start = fetch_number (regexp, token, syntax);
2498 if (start == -1)
2499 {
2500 if (token->type == CHARACTER && token->opr.c == ',')
2501 start = 0; /* We treat "{,m}" as "{0,m}". */
2502 else
2503 {
2504 *err = REG_BADBR; /* <re>{} is invalid. */
2505 return NULL;
2506 }
2507 }
2508 if (BE (start != -2, 1))
2509 {
2510 /* We treat "{n}" as "{n,n}". */
2511 end = ((token->type == OP_CLOSE_DUP_NUM) ? start
2512 : ((token->type == CHARACTER && token->opr.c == ',')
2513 ? fetch_number (regexp, token, syntax) : -2));
2514 }
2515 if (BE (start == -2 || end == -2, 0))
2516 {
2517 /* Invalid sequence. */
2518 if (BE (!(syntax & RE_INVALID_INTERVAL_ORD), 0))
2519 {
2520 if (token->type == END_OF_RE)
2521 *err = REG_EBRACE;
2522 else
2523 *err = REG_BADBR;
2524
2525 return NULL;
2526 }
2527
2528 /* If the syntax bit is set, rollback. */
2529 re_string_set_index (regexp, start_idx);
2530 *token = start_token;
2531 token->type = CHARACTER;
2532 /* mb_partial and word_char bits should be already initialized by
2533 peek_token. */
2534 return elem;
2535 }
2536
2537 if (BE ((end != -1 && start > end) || token->type != OP_CLOSE_DUP_NUM, 0))
2538 {
2539 /* First number greater than second. */
2540 *err = REG_BADBR;
2541 return NULL;
2542 }
2543 }
2544 else
2545 {
2546 start = (token->type == OP_DUP_PLUS) ? 1 : 0;
2547 end = (token->type == OP_DUP_QUESTION) ? 1 : -1;
2548 }
2549
2550 fetch_token (token, regexp, syntax);
2551
2552 if (BE (elem == NULL, 0))
2553 return NULL;
2554 if (BE (start == 0 && end == 0, 0))
2555 {
2556 postorder (elem, free_tree, NULL);
2557 return NULL;
2558 }
2559
2560 /* Extract "<re>{n,m}" to "<re><re>...<re><re>{0,<m-n>}". */
2561 if (BE (start > 0, 0))
2562 {
2563 tree = elem;
2564 for (i = 2; i <= start; ++i)
2565 {
2566 elem = duplicate_tree (elem, dfa);
2567 tree = create_tree (dfa, tree, elem, CONCAT);
2568 if (BE (elem == NULL || tree == NULL, 0))
2569 goto parse_dup_op_espace;
2570 }
2571
2572 if (start == end)
2573 return tree;
2574
2575 /* Duplicate ELEM before it is marked optional. */
2576 elem = duplicate_tree (elem, dfa);
2577 old_tree = tree;
2578 }
2579 else
2580 old_tree = NULL;
2581
2582 if (elem->token.type == SUBEXP)
2583 postorder (elem, mark_opt_subexp, (void *) (intptr_t) elem->token.opr.idx);
2584
2585 tree = create_tree (dfa, elem, NULL, (end == -1 ? OP_DUP_ASTERISK : OP_ALT));
2586 if (BE (tree == NULL, 0))
2587 goto parse_dup_op_espace;
2588
2589 /* This loop is actually executed only when end != -1,
2590 to rewrite <re>{0,n} as (<re>(<re>...<re>?)?)?... We have
2591 already created the start+1-th copy. */
2592 for (i = start + 2; i <= end; ++i)
2593 {
2594 elem = duplicate_tree (elem, dfa);
2595 tree = create_tree (dfa, tree, elem, CONCAT);
2596 if (BE (elem == NULL || tree == NULL, 0))
2597 goto parse_dup_op_espace;
2598
2599 tree = create_tree (dfa, tree, NULL, OP_ALT);
2600 if (BE (tree == NULL, 0))
2601 goto parse_dup_op_espace;
2602 }
2603
2604 if (old_tree)
2605 tree = create_tree (dfa, old_tree, tree, CONCAT);
2606
2607 return tree;
2608
2609 parse_dup_op_espace:
2610 *err = REG_ESPACE;
2611 return NULL;
2612}
2613
2614/* Size of the names for collating symbol/equivalence_class/character_class.
2615 I'm not sure, but maybe enough. */
2616#define BRACKET_NAME_BUF_SIZE 32
2617
2618#ifndef _LIBC
2619 /* Local function for parse_bracket_exp only used in case of NOT _LIBC.
2620 Build the range expression which starts from START_ELEM, and ends
2621 at END_ELEM. The result are written to MBCSET and SBCSET.
2622 RANGE_ALLOC is the allocated size of mbcset->range_starts, and
2623 mbcset->range_ends, is a pointer argument since we may
2624 update it. */
2625
2626static reg_errcode_t
2627internal_function
2628# ifdef RE_ENABLE_I18N
2629build_range_exp (bitset_t sbcset, re_charset_t *mbcset, int *range_alloc,
2630 bracket_elem_t *start_elem, bracket_elem_t *end_elem)
2631# else /* not RE_ENABLE_I18N */
2632build_range_exp (bitset_t sbcset, bracket_elem_t *start_elem,
2633 bracket_elem_t *end_elem)
2634# endif /* not RE_ENABLE_I18N */
2635{
2636 unsigned int start_ch, end_ch;
2637 /* Equivalence Classes and Character Classes can't be a range start/end. */
2638 if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
2639 || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
2640 0))
2641 return REG_ERANGE;
2642
2643 /* We can handle no multi character collating elements without libc
2644 support. */
2645 if (BE ((start_elem->type == COLL_SYM
2646 && strlen ((char *) start_elem->opr.name) > 1)
2647 || (end_elem->type == COLL_SYM
2648 && strlen ((char *) end_elem->opr.name) > 1), 0))
2649 return REG_ECOLLATE;
2650
2651# ifdef RE_ENABLE_I18N
2652 {
2653 wchar_t wc;
2654 wint_t start_wc;
2655 wint_t end_wc;
2656 wchar_t cmp_buf[6] = {L'\0', L'\0', L'\0', L'\0', L'\0', L'\0'};
2657
2658 start_ch = ((start_elem->type == SB_CHAR) ? start_elem->opr.ch
2659 : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
2660 : 0));
2661 end_ch = ((end_elem->type == SB_CHAR) ? end_elem->opr.ch
2662 : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
2663 : 0));
2664#ifdef GAWK
2665 /*
2666 * Fedora Core 2, maybe others, have broken `btowc' that returns -1
2667 * for any value > 127. Sigh. Note that `start_ch' and `end_ch' are
2668 * unsigned, so we don't have sign extension problems.
2669 */
2670 start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
2671 ? start_ch : start_elem->opr.wch);
2672 end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
2673 ? end_ch : end_elem->opr.wch);
2674#else
2675 start_wc = ((start_elem->type == SB_CHAR || start_elem->type == COLL_SYM)
2676 ? __btowc (start_ch) : start_elem->opr.wch);
2677 end_wc = ((end_elem->type == SB_CHAR || end_elem->type == COLL_SYM)
2678 ? __btowc (end_ch) : end_elem->opr.wch);
2679#endif
2680 if (start_wc == WEOF || end_wc == WEOF)
2681 return REG_ECOLLATE;
2682 cmp_buf[0] = start_wc;
2683 cmp_buf[4] = end_wc;
2684 if (wcscoll (cmp_buf, cmp_buf + 4) > 0)
2685 return REG_ERANGE;
2686
2687 /* Got valid collation sequence values, add them as a new entry.
2688 However, for !_LIBC we have no collation elements: if the
2689 character set is single byte, the single byte character set
2690 that we build below suffices. parse_bracket_exp passes
2691 no MBCSET if dfa->mb_cur_max == 1. */
2692 if (mbcset)
2693 {
2694 /* Check the space of the arrays. */
2695 if (BE (*range_alloc == mbcset->nranges, 0))
2696 {
2697 /* There is not enough space, need realloc. */
2698 wchar_t *new_array_start, *new_array_end;
2699 int new_nranges;
2700
2701 /* +1 in case of mbcset->nranges is 0. */
2702 new_nranges = 2 * mbcset->nranges + 1;
2703 /* Use realloc since mbcset->range_starts and mbcset->range_ends
2704 are NULL if *range_alloc == 0. */
2705 new_array_start = re_realloc (mbcset->range_starts, wchar_t,
2706 new_nranges);
2707 new_array_end = re_realloc (mbcset->range_ends, wchar_t,
2708 new_nranges);
2709
2710 if (BE (new_array_start == NULL || new_array_end == NULL, 0))
2711 return REG_ESPACE;
2712
2713 mbcset->range_starts = new_array_start;
2714 mbcset->range_ends = new_array_end;
2715 *range_alloc = new_nranges;
2716 }
2717
2718 mbcset->range_starts[mbcset->nranges] = start_wc;
2719 mbcset->range_ends[mbcset->nranges++] = end_wc;
2720 }
2721
2722 /* Build the table for single byte characters. */
2723 for (wc = 0; wc < SBC_MAX; ++wc)
2724 {
2725 cmp_buf[2] = wc;
2726 if (wcscoll (cmp_buf, cmp_buf + 2) <= 0
2727 && wcscoll (cmp_buf + 2, cmp_buf + 4) <= 0)
2728 bitset_set (sbcset, wc);
2729 }
2730 }
2731# else /* not RE_ENABLE_I18N */
2732 {
2733 unsigned int ch;
2734 start_ch = ((start_elem->type == SB_CHAR ) ? start_elem->opr.ch
2735 : ((start_elem->type == COLL_SYM) ? start_elem->opr.name[0]
2736 : 0));
2737 end_ch = ((end_elem->type == SB_CHAR ) ? end_elem->opr.ch
2738 : ((end_elem->type == COLL_SYM) ? end_elem->opr.name[0]
2739 : 0));
2740 if (start_ch > end_ch)
2741 return REG_ERANGE;
2742 /* Build the table for single byte characters. */
2743 for (ch = 0; ch < SBC_MAX; ++ch)
2744 if (start_ch <= ch && ch <= end_ch)
2745 bitset_set (sbcset, ch);
2746 }
2747# endif /* not RE_ENABLE_I18N */
2748 return REG_NOERROR;
2749}
2750#endif /* not _LIBC */
2751
2752#ifndef _LIBC
2753/* Helper function for parse_bracket_exp only used in case of NOT _LIBC..
2754 Build the collating element which is represented by NAME.
2755 The result are written to MBCSET and SBCSET.
2756 COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
2757 pointer argument since we may update it. */
2758
2759static reg_errcode_t
2760internal_function
2761# ifdef RE_ENABLE_I18N
2762build_collating_symbol (bitset_t sbcset, re_charset_t *mbcset,
2763 int *coll_sym_alloc, const unsigned char *name)
2764# else /* not RE_ENABLE_I18N */
2765build_collating_symbol (bitset_t sbcset, const unsigned char *name)
2766# endif /* not RE_ENABLE_I18N */
2767{
2768 size_t name_len = strlen ((const char *) name);
2769 if (BE (name_len != 1, 0))
2770 return REG_ECOLLATE;
2771 else
2772 {
2773 bitset_set (sbcset, name[0]);
2774 return REG_NOERROR;
2775 }
2776}
2777#endif /* not _LIBC */
2778
2779/* This function parse bracket expression like "[abc]", "[a-c]",
2780 "[[.a-a.]]" etc. */
2781
2782static bin_tree_t *
2783parse_bracket_exp (re_string_t *regexp, re_dfa_t *dfa, re_token_t *token,
2784 reg_syntax_t syntax, reg_errcode_t *err)
2785{
2786#ifdef _LIBC
2787 const unsigned char *collseqmb;
2788 const char *collseqwc;
2789 uint32_t nrules;
2790 int32_t table_size;
2791 const int32_t *symb_table;
2792 const unsigned char *extra;
2793
2794 /* Local function for parse_bracket_exp used in _LIBC environment.
2795 Seek the collating symbol entry correspondings to NAME.
2796 Return the index of the symbol in the SYMB_TABLE. */
2797
2798 auto inline int32_t
2799 __attribute ((always_inline))
2800 seek_collating_symbol_entry (name, name_len)
2801 const unsigned char *name;
2802 size_t name_len;
2803 {
2804 int32_t hash = elem_hash ((const char *) name, name_len);
2805 int32_t elem = hash % table_size;
2806 if (symb_table[2 * elem] != 0)
2807 {
2808 int32_t second = hash % (table_size - 2) + 1;
2809
2810 do
2811 {
2812 /* First compare the hashing value. */
2813 if (symb_table[2 * elem] == hash
2814 /* Compare the length of the name. */
2815 && name_len == extra[symb_table[2 * elem + 1]]
2816 /* Compare the name. */
2817 && memcmp (name, &extra[symb_table[2 * elem + 1] + 1],
2818 name_len) == 0)
2819 {
2820 /* Yep, this is the entry. */
2821 break;
2822 }
2823
2824 /* Next entry. */
2825 elem += second;
2826 }
2827 while (symb_table[2 * elem] != 0);
2828 }
2829 return elem;
2830 }
2831
2832 /* Local function for parse_bracket_exp used in _LIBC environment.
2833 Look up the collation sequence value of BR_ELEM.
2834 Return the value if succeeded, UINT_MAX otherwise. */
2835
2836 auto inline unsigned int
2837 __attribute ((always_inline))
2838 lookup_collation_sequence_value (br_elem)
2839 bracket_elem_t *br_elem;
2840 {
2841 if (br_elem->type == SB_CHAR)
2842 {
2843 /*
2844 if (MB_CUR_MAX == 1)
2845 */
2846 if (nrules == 0)
2847 return collseqmb[br_elem->opr.ch];
2848 else
2849 {
2850 wint_t wc = __btowc (br_elem->opr.ch);
2851 return __collseq_table_lookup (collseqwc, wc);
2852 }
2853 }
2854 else if (br_elem->type == MB_CHAR)
2855 {
2856 if (nrules != 0)
2857 return __collseq_table_lookup (collseqwc, br_elem->opr.wch);
2858 }
2859 else if (br_elem->type == COLL_SYM)
2860 {
2861 size_t sym_name_len = strlen ((char *) br_elem->opr.name);
2862 if (nrules != 0)
2863 {
2864 int32_t elem, idx;
2865 elem = seek_collating_symbol_entry (br_elem->opr.name,
2866 sym_name_len);
2867 if (symb_table[2 * elem] != 0)
2868 {
2869 /* We found the entry. */
2870 idx = symb_table[2 * elem + 1];
2871 /* Skip the name of collating element name. */
2872 idx += 1 + extra[idx];
2873 /* Skip the byte sequence of the collating element. */
2874 idx += 1 + extra[idx];
2875 /* Adjust for the alignment. */
2876 idx = (idx + 3) & ~3;
2877 /* Skip the multibyte collation sequence value. */
2878 idx += sizeof (unsigned int);
2879 /* Skip the wide char sequence of the collating element. */
2880 idx += sizeof (unsigned int) *
2881 (1 + *(unsigned int *) (extra + idx));
2882 /* Return the collation sequence value. */
2883 return *(unsigned int *) (extra + idx);
2884 }
2885 else if (symb_table[2 * elem] == 0 && sym_name_len == 1)
2886 {
2887 /* No valid character. Match it as a single byte
2888 character. */
2889 return collseqmb[br_elem->opr.name[0]];
2890 }
2891 }
2892 else if (sym_name_len == 1)
2893 return collseqmb[br_elem->opr.name[0]];
2894 }
2895 return UINT_MAX;
2896 }
2897
2898 /* Local function for parse_bracket_exp used in _LIBC environment.
2899 Build the range expression which starts from START_ELEM, and ends
2900 at END_ELEM. The result are written to MBCSET and SBCSET.
2901 RANGE_ALLOC is the allocated size of mbcset->range_starts, and
2902 mbcset->range_ends, is a pointer argument since we may
2903 update it. */
2904
2905 auto inline reg_errcode_t
2906 __attribute ((always_inline))
2907 build_range_exp (sbcset, mbcset, range_alloc, start_elem, end_elem)
2908 re_charset_t *mbcset;
2909 int *range_alloc;
2910 bitset_t sbcset;
2911 bracket_elem_t *start_elem, *end_elem;
2912 {
2913 unsigned int ch;
2914 uint32_t start_collseq;
2915 uint32_t end_collseq;
2916
2917 /* Equivalence Classes and Character Classes can't be a range
2918 start/end. */
2919 if (BE (start_elem->type == EQUIV_CLASS || start_elem->type == CHAR_CLASS
2920 || end_elem->type == EQUIV_CLASS || end_elem->type == CHAR_CLASS,
2921 0))
2922 return REG_ERANGE;
2923
2924 start_collseq = lookup_collation_sequence_value (start_elem);
2925 end_collseq = lookup_collation_sequence_value (end_elem);
2926 /* Check start/end collation sequence values. */
2927 if (BE (start_collseq == UINT_MAX || end_collseq == UINT_MAX, 0))
2928 return REG_ECOLLATE;
2929 if (BE ((syntax & RE_NO_EMPTY_RANGES) && start_collseq > end_collseq, 0))
2930 return REG_ERANGE;
2931
2932 /* Got valid collation sequence values, add them as a new entry.
2933 However, if we have no collation elements, and the character set
2934 is single byte, the single byte character set that we
2935 build below suffices. */
2936 if (nrules > 0 || dfa->mb_cur_max > 1)
2937 {
2938 /* Check the space of the arrays. */
2939 if (BE (*range_alloc == mbcset->nranges, 0))
2940 {
2941 /* There is not enough space, need realloc. */
2942 uint32_t *new_array_start;
2943 uint32_t *new_array_end;
2944 int new_nranges;
2945
2946 /* +1 in case of mbcset->nranges is 0. */
2947 new_nranges = 2 * mbcset->nranges + 1;
2948 new_array_start = re_realloc (mbcset->range_starts, uint32_t,
2949 new_nranges);
2950 new_array_end = re_realloc (mbcset->range_ends, uint32_t,
2951 new_nranges);
2952
2953 if (BE (new_array_start == NULL || new_array_end == NULL, 0))
2954 return REG_ESPACE;
2955
2956 mbcset->range_starts = new_array_start;
2957 mbcset->range_ends = new_array_end;
2958 *range_alloc = new_nranges;
2959 }
2960
2961 mbcset->range_starts[mbcset->nranges] = start_collseq;
2962 mbcset->range_ends[mbcset->nranges++] = end_collseq;
2963 }
2964
2965 /* Build the table for single byte characters. */
2966 for (ch = 0; ch < SBC_MAX; ch++)
2967 {
2968 uint32_t ch_collseq;
2969 /*
2970 if (MB_CUR_MAX == 1)
2971 */
2972 if (nrules == 0)
2973 ch_collseq = collseqmb[ch];
2974 else
2975 ch_collseq = __collseq_table_lookup (collseqwc, __btowc (ch));
2976 if (start_collseq <= ch_collseq && ch_collseq <= end_collseq)
2977 bitset_set (sbcset, ch);
2978 }
2979 return REG_NOERROR;
2980 }
2981
2982 /* Local function for parse_bracket_exp used in _LIBC environment.
2983 Build the collating element which is represented by NAME.
2984 The result are written to MBCSET and SBCSET.
2985 COLL_SYM_ALLOC is the allocated size of mbcset->coll_sym, is a
2986 pointer argument since we may update it. */
2987
2988 auto inline reg_errcode_t
2989 __attribute ((always_inline))
2990 build_collating_symbol (sbcset, mbcset, coll_sym_alloc, name)
2991 re_charset_t *mbcset;
2992 int *coll_sym_alloc;
2993 bitset_t sbcset;
2994 const unsigned char *name;
2995 {
2996 int32_t elem, idx;
2997 size_t name_len = strlen ((const char *) name);
2998 if (nrules != 0)
2999 {
3000 elem = seek_collating_symbol_entry (name, name_len);
3001 if (symb_table[2 * elem] != 0)
3002 {
3003 /* We found the entry. */
3004 idx = symb_table[2 * elem + 1];
3005 /* Skip the name of collating element name. */
3006 idx += 1 + extra[idx];
3007 }
3008 else if (symb_table[2 * elem] == 0 && name_len == 1)
3009 {
3010 /* No valid character, treat it as a normal
3011 character. */
3012 bitset_set (sbcset, name[0]);
3013 return REG_NOERROR;
3014 }
3015 else
3016 return REG_ECOLLATE;
3017
3018 /* Got valid collation sequence, add it as a new entry. */
3019 /* Check the space of the arrays. */
3020 if (BE (*coll_sym_alloc == mbcset->ncoll_syms, 0))
3021 {
3022 /* Not enough, realloc it. */
3023 /* +1 in case of mbcset->ncoll_syms is 0. */
3024 int new_coll_sym_alloc = 2 * mbcset->ncoll_syms + 1;
3025 /* Use realloc since mbcset->coll_syms is NULL
3026 if *alloc == 0. */
3027 int32_t *new_coll_syms = re_realloc (mbcset->coll_syms, int32_t,
3028 new_coll_sym_alloc);
3029 if (BE (new_coll_syms == NULL, 0))
3030 return REG_ESPACE;
3031 mbcset->coll_syms = new_coll_syms;
3032 *coll_sym_alloc = new_coll_sym_alloc;
3033 }
3034 mbcset->coll_syms[mbcset->ncoll_syms++] = idx;
3035 return REG_NOERROR;
3036 }
3037 else
3038 {
3039 if (BE (name_len != 1, 0))
3040 return REG_ECOLLATE;
3041 else
3042 {
3043 bitset_set (sbcset, name[0]);
3044 return REG_NOERROR;
3045 }
3046 }
3047 }
3048#endif
3049
3050 re_token_t br_token;
3051 re_bitset_ptr_t sbcset;
3052#ifdef RE_ENABLE_I18N
3053 re_charset_t *mbcset;
3054 int coll_sym_alloc = 0, range_alloc = 0, mbchar_alloc = 0;
3055 int equiv_class_alloc = 0, char_class_alloc = 0;
3056#endif /* not RE_ENABLE_I18N */
3057 int non_match = 0;
3058 bin_tree_t *work_tree;
3059 int token_len;
3060 int first_round = 1;
3061#ifdef _LIBC
3062 collseqmb = (const unsigned char *)
3063 _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQMB);
3064 nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
3065 if (nrules)
3066 {
3067 /*
3068 if (MB_CUR_MAX > 1)
3069 */
3070 collseqwc = _NL_CURRENT (LC_COLLATE, _NL_COLLATE_COLLSEQWC);
3071 table_size = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_SYMB_HASH_SIZEMB);
3072 symb_table = (const int32_t *) _NL_CURRENT (LC_COLLATE,
3073 _NL_COLLATE_SYMB_TABLEMB);
3074 extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
3075 _NL_COLLATE_SYMB_EXTRAMB);
3076 }
3077#endif
3078 sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
3079#ifdef RE_ENABLE_I18N
3080 mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
3081#endif /* RE_ENABLE_I18N */
3082#ifdef RE_ENABLE_I18N
3083 if (BE (sbcset == NULL || mbcset == NULL, 0))
3084#else
3085 if (BE (sbcset == NULL, 0))
3086#endif /* RE_ENABLE_I18N */
3087 {
3088 *err = REG_ESPACE;
3089 return NULL;
3090 }
3091
3092 token_len = peek_token_bracket (token, regexp, syntax);
3093 if (BE (token->type == END_OF_RE, 0))
3094 {
3095 *err = REG_BADPAT;
3096 goto parse_bracket_exp_free_return;
3097 }
3098 if (token->type == OP_NON_MATCH_LIST)
3099 {
3100#ifdef RE_ENABLE_I18N
3101 mbcset->non_match = 1;
3102#endif /* not RE_ENABLE_I18N */
3103 non_match = 1;
3104 if (syntax & RE_HAT_LISTS_NOT_NEWLINE)
3105 bitset_set (sbcset, '\n');
3106 re_string_skip_bytes (regexp, token_len); /* Skip a token. */
3107 token_len = peek_token_bracket (token, regexp, syntax);
3108 if (BE (token->type == END_OF_RE, 0))
3109 {
3110 *err = REG_BADPAT;
3111 goto parse_bracket_exp_free_return;
3112 }
3113 }
3114
3115 /* We treat the first ']' as a normal character. */
3116 if (token->type == OP_CLOSE_BRACKET)
3117 token->type = CHARACTER;
3118
3119 while (1)
3120 {
3121 bracket_elem_t start_elem, end_elem;
3122 unsigned char start_name_buf[BRACKET_NAME_BUF_SIZE];
3123 unsigned char end_name_buf[BRACKET_NAME_BUF_SIZE];
3124 reg_errcode_t ret;
3125 int token_len2 = 0, is_range_exp = 0;
3126 re_token_t token2;
3127
3128 start_elem.opr.name = start_name_buf;
3129 ret = parse_bracket_element (&start_elem, regexp, token, token_len, dfa,
3130 syntax, first_round);
3131 if (BE (ret != REG_NOERROR, 0))
3132 {
3133 *err = ret;
3134 goto parse_bracket_exp_free_return;
3135 }
3136 first_round = 0;
3137
3138 /* Get information about the next token. We need it in any case. */
3139 token_len = peek_token_bracket (token, regexp, syntax);
3140
3141 /* Do not check for ranges if we know they are not allowed. */
3142 if (start_elem.type != CHAR_CLASS && start_elem.type != EQUIV_CLASS)
3143 {
3144 if (BE (token->type == END_OF_RE, 0))
3145 {
3146 *err = REG_EBRACK;
3147 goto parse_bracket_exp_free_return;
3148 }
3149 if (token->type == OP_CHARSET_RANGE)
3150 {
3151 re_string_skip_bytes (regexp, token_len); /* Skip '-'. */
3152 token_len2 = peek_token_bracket (&token2, regexp, syntax);
3153 if (BE (token2.type == END_OF_RE, 0))
3154 {
3155 *err = REG_EBRACK;
3156 goto parse_bracket_exp_free_return;
3157 }
3158 if (token2.type == OP_CLOSE_BRACKET)
3159 {
3160 /* We treat the last '-' as a normal character. */
3161 re_string_skip_bytes (regexp, -token_len);
3162 token->type = CHARACTER;
3163 }
3164 else
3165 is_range_exp = 1;
3166 }
3167 }
3168
3169 if (is_range_exp == 1)
3170 {
3171 end_elem.opr.name = end_name_buf;
3172 ret = parse_bracket_element (&end_elem, regexp, &token2, token_len2,
3173 dfa, syntax, 1);
3174 if (BE (ret != REG_NOERROR, 0))
3175 {
3176 *err = ret;
3177 goto parse_bracket_exp_free_return;
3178 }
3179
3180 token_len = peek_token_bracket (token, regexp, syntax);
3181
3182#ifdef _LIBC
3183 *err = build_range_exp (sbcset, mbcset, &range_alloc,
3184 &start_elem, &end_elem);
3185#else
3186# ifdef RE_ENABLE_I18N
3187 *err = build_range_exp (sbcset,
3188 dfa->mb_cur_max > 1 ? mbcset : NULL,
3189 &range_alloc, &start_elem, &end_elem);
3190# else
3191 *err = build_range_exp (sbcset, &start_elem, &end_elem);
3192# endif
3193#endif /* RE_ENABLE_I18N */
3194 if (BE (*err != REG_NOERROR, 0))
3195 goto parse_bracket_exp_free_return;
3196 }
3197 else
3198 {
3199 switch (start_elem.type)
3200 {
3201 case SB_CHAR:
3202 bitset_set (sbcset, start_elem.opr.ch);
3203 break;
3204#ifdef RE_ENABLE_I18N
3205 case MB_CHAR:
3206 /* Check whether the array has enough space. */
3207 if (BE (mbchar_alloc == mbcset->nmbchars, 0))
3208 {
3209 wchar_t *new_mbchars;
3210 /* Not enough, realloc it. */
3211 /* +1 in case of mbcset->nmbchars is 0. */
3212 mbchar_alloc = 2 * mbcset->nmbchars + 1;
3213 /* Use realloc since array is NULL if *alloc == 0. */
3214 new_mbchars = re_realloc (mbcset->mbchars, wchar_t,
3215 mbchar_alloc);
3216 if (BE (new_mbchars == NULL, 0))
3217 goto parse_bracket_exp_espace;
3218 mbcset->mbchars = new_mbchars;
3219 }
3220 mbcset->mbchars[mbcset->nmbchars++] = start_elem.opr.wch;
3221 break;
3222#endif /* RE_ENABLE_I18N */
3223 case EQUIV_CLASS:
3224 *err = build_equiv_class (sbcset,
3225#ifdef RE_ENABLE_I18N
3226 mbcset, &equiv_class_alloc,
3227#endif /* RE_ENABLE_I18N */
3228 start_elem.opr.name);
3229 if (BE (*err != REG_NOERROR, 0))
3230 goto parse_bracket_exp_free_return;
3231 break;
3232 case COLL_SYM:
3233 *err = build_collating_symbol (sbcset,
3234#ifdef RE_ENABLE_I18N
3235 mbcset, &coll_sym_alloc,
3236#endif /* RE_ENABLE_I18N */
3237 start_elem.opr.name);
3238 if (BE (*err != REG_NOERROR, 0))
3239 goto parse_bracket_exp_free_return;
3240 break;
3241 case CHAR_CLASS:
3242 *err = build_charclass (regexp->trans, sbcset,
3243#ifdef RE_ENABLE_I18N
3244 mbcset, &char_class_alloc,
3245#endif /* RE_ENABLE_I18N */
3246 (const char *) start_elem.opr.name, syntax);
3247 if (BE (*err != REG_NOERROR, 0))
3248 goto parse_bracket_exp_free_return;
3249 break;
3250 default:
3251 assert (0);
3252 break;
3253 }
3254 }
3255 if (BE (token->type == END_OF_RE, 0))
3256 {
3257 *err = REG_EBRACK;
3258 goto parse_bracket_exp_free_return;
3259 }
3260 if (token->type == OP_CLOSE_BRACKET)
3261 break;
3262 }
3263
3264 re_string_skip_bytes (regexp, token_len); /* Skip a token. */
3265
3266 /* If it is non-matching list. */
3267 if (non_match)
3268 bitset_not (sbcset);
3269
3270#ifdef RE_ENABLE_I18N
3271 /* Ensure only single byte characters are set. */
3272 if (dfa->mb_cur_max > 1)
3273 bitset_mask (sbcset, dfa->sb_char);
3274
3275 if (mbcset->nmbchars || mbcset->ncoll_syms || mbcset->nequiv_classes
3276 || mbcset->nranges || (dfa->mb_cur_max > 1 && (mbcset->nchar_classes
3277 || mbcset->non_match)))
3278 {
3279 bin_tree_t *mbc_tree;
3280 int sbc_idx;
3281 /* Build a tree for complex bracket. */
3282 dfa->has_mb_node = 1;
3283 br_token.type = COMPLEX_BRACKET;
3284 br_token.opr.mbcset = mbcset;
3285 mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
3286 if (BE (mbc_tree == NULL, 0))
3287 goto parse_bracket_exp_espace;
3288 for (sbc_idx = 0; sbc_idx < BITSET_WORDS; ++sbc_idx)
3289 if (sbcset[sbc_idx])
3290 break;
3291 /* If there are no bits set in sbcset, there is no point
3292 of having both SIMPLE_BRACKET and COMPLEX_BRACKET. */
3293 if (sbc_idx < BITSET_WORDS)
3294 {
3295 /* Build a tree for simple bracket. */
3296 br_token.type = SIMPLE_BRACKET;
3297 br_token.opr.sbcset = sbcset;
3298 work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
3299 if (BE (work_tree == NULL, 0))
3300 goto parse_bracket_exp_espace;
3301
3302 /* Then join them by ALT node. */
3303 work_tree = create_tree (dfa, work_tree, mbc_tree, OP_ALT);
3304 if (BE (work_tree == NULL, 0))
3305 goto parse_bracket_exp_espace;
3306 }
3307 else
3308 {
3309 re_free (sbcset);
3310 work_tree = mbc_tree;
3311 }
3312 }
3313 else
3314#endif /* not RE_ENABLE_I18N */
3315 {
3316#ifdef RE_ENABLE_I18N
3317 free_charset (mbcset);
3318#endif
3319 /* Build a tree for simple bracket. */
3320 br_token.type = SIMPLE_BRACKET;
3321 br_token.opr.sbcset = sbcset;
3322 work_tree = create_token_tree (dfa, NULL, NULL, &br_token);
3323 if (BE (work_tree == NULL, 0))
3324 goto parse_bracket_exp_espace;
3325 }
3326 return work_tree;
3327
3328 parse_bracket_exp_espace:
3329 *err = REG_ESPACE;
3330 parse_bracket_exp_free_return:
3331 re_free (sbcset);
3332#ifdef RE_ENABLE_I18N
3333 free_charset (mbcset);
3334#endif /* RE_ENABLE_I18N */
3335 return NULL;
3336}
3337
3338/* Parse an element in the bracket expression. */
3339
3340static reg_errcode_t
3341parse_bracket_element (bracket_elem_t *elem, re_string_t *regexp,
3342 re_token_t *token, int token_len,
3343 UNUSED_PARAM re_dfa_t *dfa, reg_syntax_t syntax,
3344 int accept_hyphen)
3345{
3346#ifdef RE_ENABLE_I18N
3347 int cur_char_size;
3348 cur_char_size = re_string_char_size_at (regexp, re_string_cur_idx (regexp));
3349 if (cur_char_size > 1)
3350 {
3351 elem->type = MB_CHAR;
3352 elem->opr.wch = re_string_wchar_at (regexp, re_string_cur_idx (regexp));
3353 re_string_skip_bytes (regexp, cur_char_size);
3354 return REG_NOERROR;
3355 }
3356#endif /* RE_ENABLE_I18N */
3357 re_string_skip_bytes (regexp, token_len); /* Skip a token. */
3358 if (token->type == OP_OPEN_COLL_ELEM || token->type == OP_OPEN_CHAR_CLASS
3359 || token->type == OP_OPEN_EQUIV_CLASS)
3360 return parse_bracket_symbol (elem, regexp, token);
3361 if (BE (token->type == OP_CHARSET_RANGE, 0) && !accept_hyphen)
3362 {
3363 /* A '-' must only appear as anything but a range indicator before
3364 the closing bracket. Everything else is an error. */
3365 re_token_t token2;
3366 (void) peek_token_bracket (&token2, regexp, syntax);
3367 if (token2.type != OP_CLOSE_BRACKET)
3368 /* The actual error value is not standardized since this whole
3369 case is undefined. But ERANGE makes good sense. */
3370 return REG_ERANGE;
3371 }
3372 elem->type = SB_CHAR;
3373 elem->opr.ch = token->opr.c;
3374 return REG_NOERROR;
3375}
3376
3377/* Parse a bracket symbol in the bracket expression. Bracket symbols are
3378 such as [:<character_class>:], [.<collating_element>.], and
3379 [=<equivalent_class>=]. */
3380
3381static reg_errcode_t
3382parse_bracket_symbol (bracket_elem_t *elem, re_string_t *regexp,
3383 re_token_t *token)
3384{
3385 unsigned char ch, delim = token->opr.c;
3386 int i = 0;
3387 if (re_string_eoi(regexp))
3388 return REG_EBRACK;
3389 for (;; ++i)
3390 {
3391 if (i >= BRACKET_NAME_BUF_SIZE)
3392 return REG_EBRACK;
3393 if (token->type == OP_OPEN_CHAR_CLASS)
3394 ch = re_string_fetch_byte_case (regexp);
3395 else
3396 ch = re_string_fetch_byte (regexp);
3397 if (re_string_eoi(regexp))
3398 return REG_EBRACK;
3399 if (ch == delim && re_string_peek_byte (regexp, 0) == ']')
3400 break;
3401 elem->opr.name[i] = ch;
3402 }
3403 re_string_skip_bytes (regexp, 1);
3404 elem->opr.name[i] = '\0';
3405 switch (token->type)
3406 {
3407 case OP_OPEN_COLL_ELEM:
3408 elem->type = COLL_SYM;
3409 break;
3410 case OP_OPEN_EQUIV_CLASS:
3411 elem->type = EQUIV_CLASS;
3412 break;
3413 case OP_OPEN_CHAR_CLASS:
3414 elem->type = CHAR_CLASS;
3415 break;
3416 default:
3417 break;
3418 }
3419 return REG_NOERROR;
3420}
3421
3422 /* Helper function for parse_bracket_exp.
3423 Build the equivalence class which is represented by NAME.
3424 The result are written to MBCSET and SBCSET.
3425 EQUIV_CLASS_ALLOC is the allocated size of mbcset->equiv_classes,
3426 is a pointer argument since we may update it. */
3427
3428static reg_errcode_t
3429#ifdef RE_ENABLE_I18N
3430build_equiv_class (bitset_t sbcset, re_charset_t *mbcset,
3431 int *equiv_class_alloc, const unsigned char *name)
3432#else /* not RE_ENABLE_I18N */
3433build_equiv_class (bitset_t sbcset, const unsigned char *name)
3434#endif /* not RE_ENABLE_I18N */
3435{
3436#ifdef _LIBC
3437 uint32_t nrules = _NL_CURRENT_WORD (LC_COLLATE, _NL_COLLATE_NRULES);
3438 if (nrules != 0)
3439 {
3440 const int32_t *table, *indirect;
3441 const unsigned char *weights, *extra, *cp;
3442 unsigned char char_buf[2];
3443 int32_t idx1, idx2;
3444 unsigned int ch;
3445 size_t len;
3446 /* This #include defines a local function! */
3447# include <locale/weight.h>
3448 /* Calculate the index for equivalence class. */
3449 cp = name;
3450 table = (const int32_t *) _NL_CURRENT (LC_COLLATE, _NL_COLLATE_TABLEMB);
3451 weights = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
3452 _NL_COLLATE_WEIGHTMB);
3453 extra = (const unsigned char *) _NL_CURRENT (LC_COLLATE,
3454 _NL_COLLATE_EXTRAMB);
3455 indirect = (const int32_t *) _NL_CURRENT (LC_COLLATE,
3456 _NL_COLLATE_INDIRECTMB);
3457 idx1 = findidx (&cp);
3458 if (BE (idx1 == 0 || cp < name + strlen ((const char *) name), 0))
3459 /* This isn't a valid character. */
3460 return REG_ECOLLATE;
3461
3462 /* Build single byte matcing table for this equivalence class. */
3463 char_buf[1] = (unsigned char) '\0';
3464 len = weights[idx1 & 0xffffff];
3465 for (ch = 0; ch < SBC_MAX; ++ch)
3466 {
3467 char_buf[0] = ch;
3468 cp = char_buf;
3469 idx2 = findidx (&cp);
3470/*
3471 idx2 = table[ch];
3472*/
3473 if (idx2 == 0)
3474 /* This isn't a valid character. */
3475 continue;
3476 /* Compare only if the length matches and the collation rule
3477 index is the same. */
3478 if (len == weights[idx2 & 0xffffff] && (idx1 >> 24) == (idx2 >> 24))
3479 {
3480 int cnt = 0;
3481
3482 while (cnt <= len &&
3483 weights[(idx1 & 0xffffff) + 1 + cnt]
3484 == weights[(idx2 & 0xffffff) + 1 + cnt])
3485 ++cnt;
3486
3487 if (cnt > len)
3488 bitset_set (sbcset, ch);
3489 }
3490 }
3491 /* Check whether the array has enough space. */
3492 if (BE (*equiv_class_alloc == mbcset->nequiv_classes, 0))
3493 {
3494 /* Not enough, realloc it. */
3495 /* +1 in case of mbcset->nequiv_classes is 0. */
3496 int new_equiv_class_alloc = 2 * mbcset->nequiv_classes + 1;
3497 /* Use realloc since the array is NULL if *alloc == 0. */
3498 int32_t *new_equiv_classes = re_realloc (mbcset->equiv_classes,
3499 int32_t,
3500 new_equiv_class_alloc);
3501 if (BE (new_equiv_classes == NULL, 0))
3502 return REG_ESPACE;
3503 mbcset->equiv_classes = new_equiv_classes;
3504 *equiv_class_alloc = new_equiv_class_alloc;
3505 }
3506 mbcset->equiv_classes[mbcset->nequiv_classes++] = idx1;
3507 }
3508 else
3509#endif /* _LIBC */
3510 {
3511 if (BE (strlen ((const char *) name) != 1, 0))
3512 return REG_ECOLLATE;
3513 bitset_set (sbcset, *name);
3514 }
3515 return REG_NOERROR;
3516}
3517
3518 /* Helper function for parse_bracket_exp.
3519 Build the character class which is represented by NAME.
3520 The result are written to MBCSET and SBCSET.
3521 CHAR_CLASS_ALLOC is the allocated size of mbcset->char_classes,
3522 is a pointer argument since we may update it. */
3523
3524static reg_errcode_t
3525#ifdef RE_ENABLE_I18N
3526build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
3527 re_charset_t *mbcset, int *char_class_alloc,
3528 const char *class_name, reg_syntax_t syntax)
3529#else /* not RE_ENABLE_I18N */
3530build_charclass (RE_TRANSLATE_TYPE trans, bitset_t sbcset,
3531 const char *class_name, reg_syntax_t syntax)
3532#endif /* not RE_ENABLE_I18N */
3533{
3534 int i;
3535
3536 /* In case of REG_ICASE "upper" and "lower" match the both of
3537 upper and lower cases. */
3538 if ((syntax & RE_ICASE)
3539 && (strcmp (class_name, "upper") == 0 || strcmp (class_name, "lower") == 0))
3540 class_name = "alpha";
3541
3542#ifdef RE_ENABLE_I18N
3543 /* Check the space of the arrays. */
3544 if (BE (*char_class_alloc == mbcset->nchar_classes, 0))
3545 {
3546 /* Not enough, realloc it. */
3547 /* +1 in case of mbcset->nchar_classes is 0. */
3548 int new_char_class_alloc = 2 * mbcset->nchar_classes + 1;
3549 /* Use realloc since array is NULL if *alloc == 0. */
3550 wctype_t *new_char_classes = re_realloc (mbcset->char_classes, wctype_t,
3551 new_char_class_alloc);
3552 if (BE (new_char_classes == NULL, 0))
3553 return REG_ESPACE;
3554 mbcset->char_classes = new_char_classes;
3555 *char_class_alloc = new_char_class_alloc;
3556 }
3557 mbcset->char_classes[mbcset->nchar_classes++] = __wctype (class_name);
3558#endif /* RE_ENABLE_I18N */
3559
3560#define BUILD_CHARCLASS_LOOP(ctype_func) \
3561 do { \
3562 if (BE (trans != NULL, 0)) \
3563 { \
3564 for (i = 0; i < SBC_MAX; ++i) \
3565 if (ctype_func (i)) \
3566 bitset_set (sbcset, trans[i]); \
3567 } \
3568 else \
3569 { \
3570 for (i = 0; i < SBC_MAX; ++i) \
3571 if (ctype_func (i)) \
3572 bitset_set (sbcset, i); \
3573 } \
3574 } while (0)
3575
3576#if 0
3577 if (strcmp (class_name, "alnum") == 0)
3578 BUILD_CHARCLASS_LOOP (isalnum);
3579 else if (strcmp (class_name, "cntrl") == 0)
3580 BUILD_CHARCLASS_LOOP (iscntrl);
3581 else if (strcmp (class_name, "lower") == 0)
3582 BUILD_CHARCLASS_LOOP (islower);
3583 else if (strcmp (class_name, "space") == 0)
3584 BUILD_CHARCLASS_LOOP (isspace);
3585 else if (strcmp (class_name, "alpha") == 0)
3586 BUILD_CHARCLASS_LOOP (isalpha);
3587 else if (strcmp (class_name, "digit") == 0)
3588 BUILD_CHARCLASS_LOOP (isdigit);
3589 else if (strcmp (class_name, "print") == 0)
3590 BUILD_CHARCLASS_LOOP (isprint);
3591 else if (strcmp (class_name, "upper") == 0)
3592 BUILD_CHARCLASS_LOOP (isupper);
3593 else if (strcmp (class_name, "blank") == 0)
3594#ifndef GAWK
3595 BUILD_CHARCLASS_LOOP (isblank);
3596#else
3597 /* see comments above */
3598 BUILD_CHARCLASS_LOOP (is_blank);
3599#endif
3600 else if (strcmp (class_name, "graph") == 0)
3601 BUILD_CHARCLASS_LOOP (isgraph);
3602 else if (strcmp (class_name, "punct") == 0)
3603 BUILD_CHARCLASS_LOOP (ispunct);
3604 else if (strcmp (class_name, "xdigit") == 0)
3605 BUILD_CHARCLASS_LOOP (isxdigit);
3606 else
3607 return REG_ECTYPE;
3608#else
3609 switch (match_class(class_name)) {
3610 case CCLASS_ALNUM:
3611 BUILD_CHARCLASS_LOOP (isalnum);
3612 break;
3613 case CCLASS_CNTRL:
3614 BUILD_CHARCLASS_LOOP (iscntrl);
3615 break;
3616 case CCLASS_LOWER:
3617 BUILD_CHARCLASS_LOOP (islower);
3618 break;
3619 case CCLASS_SPACE:
3620 BUILD_CHARCLASS_LOOP (isspace);
3621 break;
3622 case CCLASS_ALPHA:
3623 BUILD_CHARCLASS_LOOP (isalpha);
3624 break;
3625 case CCLASS_DIGIT:
3626 BUILD_CHARCLASS_LOOP (isdigit);
3627 break;
3628 case CCLASS_PRINT:
3629 BUILD_CHARCLASS_LOOP (isprint);
3630 break;
3631 case CCLASS_UPPER:
3632 BUILD_CHARCLASS_LOOP (isupper);
3633 break;
3634 case CCLASS_BLANK:
3635#ifndef GAWK
3636 BUILD_CHARCLASS_LOOP (isblank);
3637#else
3638 /* see comments above */
3639 BUILD_CHARCLASS_LOOP (is_blank);
3640#endif
3641 break;
3642 case CCLASS_GRAPH:
3643 BUILD_CHARCLASS_LOOP (isgraph);
3644 break;
3645 case CCLASS_PUNCT:
3646 BUILD_CHARCLASS_LOOP (ispunct);
3647 break;
3648 case CCLASS_XDIGIT:
3649 BUILD_CHARCLASS_LOOP (isxdigit);
3650 break;
3651 default:
3652 return REG_ECTYPE;
3653 }
3654#endif
3655
3656 return REG_NOERROR;
3657}
3658
3659static bin_tree_t *
3660build_charclass_op (re_dfa_t *dfa, RE_TRANSLATE_TYPE trans,
3661 const char *class_name,
3662 const char *extra, int non_match,
3663 reg_errcode_t *err)
3664{
3665 re_bitset_ptr_t sbcset;
3666#ifdef RE_ENABLE_I18N
3667 re_charset_t *mbcset;
3668 int alloc = 0;
3669#endif /* not RE_ENABLE_I18N */
3670 reg_errcode_t ret;
3671 re_token_t br_token;
3672 bin_tree_t *tree;
3673
3674 sbcset = (re_bitset_ptr_t) calloc (sizeof (bitset_t), 1);
3675#ifdef RE_ENABLE_I18N
3676 mbcset = (re_charset_t *) calloc (sizeof (re_charset_t), 1);
3677#endif /* RE_ENABLE_I18N */
3678
3679#ifdef RE_ENABLE_I18N
3680 if (BE (sbcset == NULL || mbcset == NULL, 0))
3681#else /* not RE_ENABLE_I18N */
3682 if (BE (sbcset == NULL, 0))
3683#endif /* not RE_ENABLE_I18N */
3684 {
3685 *err = REG_ESPACE;
3686 return NULL;
3687 }
3688
3689 if (non_match)
3690 {
3691#ifdef RE_ENABLE_I18N
3692 mbcset->non_match = 1;
3693#endif /* not RE_ENABLE_I18N */
3694 }
3695
3696 /* We don't care the syntax in this case. */
3697 ret = build_charclass (trans, sbcset,
3698#ifdef RE_ENABLE_I18N
3699 mbcset, &alloc,
3700#endif /* RE_ENABLE_I18N */
3701 class_name, 0);
3702
3703 if (BE (ret != REG_NOERROR, 0))
3704 {
3705 re_free (sbcset);
3706#ifdef RE_ENABLE_I18N
3707 free_charset (mbcset);
3708#endif /* RE_ENABLE_I18N */
3709 *err = ret;
3710 return NULL;
3711 }
3712 /* \w match '_' also. */
3713 for (; *extra; extra++)
3714 bitset_set (sbcset, *extra);
3715
3716 /* If it is non-matching list. */
3717 if (non_match)
3718 bitset_not (sbcset);
3719
3720#ifdef RE_ENABLE_I18N
3721 /* Ensure only single byte characters are set. */
3722 if (dfa->mb_cur_max > 1)
3723 bitset_mask (sbcset, dfa->sb_char);
3724#endif
3725
3726 /* Build a tree for simple bracket. */
3727 br_token.type = SIMPLE_BRACKET;
3728 br_token.opr.sbcset = sbcset;
3729 tree = create_token_tree (dfa, NULL, NULL, &br_token);
3730 if (BE (tree == NULL, 0))
3731 goto build_word_op_espace;
3732
3733#ifdef RE_ENABLE_I18N
3734 if (dfa->mb_cur_max > 1)
3735 {
3736 bin_tree_t *mbc_tree;
3737 /* Build a tree for complex bracket. */
3738 br_token.type = COMPLEX_BRACKET;
3739 br_token.opr.mbcset = mbcset;
3740 dfa->has_mb_node = 1;
3741 mbc_tree = create_token_tree (dfa, NULL, NULL, &br_token);
3742 if (BE (mbc_tree == NULL, 0))
3743 goto build_word_op_espace;
3744 /* Then join them by ALT node. */
3745 tree = create_tree (dfa, tree, mbc_tree, OP_ALT);
3746 if (BE (mbc_tree != NULL, 1))
3747 return tree;
3748 }
3749 else
3750 {
3751 free_charset (mbcset);
3752 return tree;
3753 }
3754#else /* not RE_ENABLE_I18N */
3755 return tree;
3756#endif /* not RE_ENABLE_I18N */
3757
3758 build_word_op_espace:
3759 re_free (sbcset);
3760#ifdef RE_ENABLE_I18N
3761 free_charset (mbcset);
3762#endif /* RE_ENABLE_I18N */
3763 *err = REG_ESPACE;
3764 return NULL;
3765}
3766
3767/* This is intended for the expressions like "a{1,3}".
3768 Fetch a number from `input', and return the number.
3769 Return -1, if the number field is empty like "{,1}".
3770 Return -2, if an error has occurred. */
3771
3772static int
3773fetch_number (re_string_t *input, re_token_t *token, reg_syntax_t syntax)
3774{
3775 int num = -1;
3776 unsigned char c;
3777 while (1)
3778 {
3779 fetch_token (token, input, syntax);
3780 c = token->opr.c;
3781 if (BE (token->type == END_OF_RE, 0))
3782 return -2;
3783 if (token->type == OP_CLOSE_DUP_NUM || c == ',')
3784 break;
3785 num = ((token->type != CHARACTER || c < '0' || '9' < c || num == -2)
3786 ? -2 : ((num == -1) ? c - '0' : num * 10 + c - '0'));
3787 num = (num > RE_DUP_MAX) ? -2 : num;
3788 }
3789 return num;
3790}
3791
3792#ifdef RE_ENABLE_I18N
3793static void
3794free_charset (re_charset_t *cset)
3795{
3796 re_free (cset->mbchars);
3797# ifdef _LIBC
3798 re_free (cset->coll_syms);
3799 re_free (cset->equiv_classes);
3800 re_free (cset->range_starts);
3801 re_free (cset->range_ends);
3802# endif
3803 re_free (cset->char_classes);
3804 re_free (cset);
3805}
3806#endif /* RE_ENABLE_I18N */
3807
3808/* Functions for binary tree operation. */
3809
3810/* Create a tree node. */
3811
3812static bin_tree_t *
3813create_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
3814 re_token_type_t type)
3815{
3816 re_token_t t;
3817 t.type = type;
3818 return create_token_tree (dfa, left, right, &t);
3819}
3820
3821static bin_tree_t *
3822create_token_tree (re_dfa_t *dfa, bin_tree_t *left, bin_tree_t *right,
3823 const re_token_t *token)
3824{
3825 bin_tree_t *tree;
3826 if (BE (dfa->str_tree_storage_idx == BIN_TREE_STORAGE_SIZE, 0))
3827 {
3828 bin_tree_storage_t *storage = re_malloc (bin_tree_storage_t, 1);
3829
3830 if (storage == NULL)
3831 return NULL;
3832 storage->next = dfa->str_tree_storage;
3833 dfa->str_tree_storage = storage;
3834 dfa->str_tree_storage_idx = 0;
3835 }
3836 tree = &dfa->str_tree_storage->data[dfa->str_tree_storage_idx++];
3837
3838 tree->parent = NULL;
3839 tree->left = left;
3840 tree->right = right;
3841 tree->token = *token;
3842 tree->token.duplicated = 0;
3843 tree->token.opt_subexp = 0;
3844 tree->first = NULL;
3845 tree->next = NULL;
3846 tree->node_idx = -1;
3847
3848 if (left != NULL)
3849 left->parent = tree;
3850 if (right != NULL)
3851 right->parent = tree;
3852 return tree;
3853}
3854
3855/* Mark the tree SRC as an optional subexpression.
3856 To be called from preorder or postorder. */
3857
3858static reg_errcode_t
3859mark_opt_subexp (void *extra, bin_tree_t *node)
3860{
3861 int idx = (int) (intptr_t) extra;
3862 if (node->token.type == SUBEXP && node->token.opr.idx == idx)
3863 node->token.opt_subexp = 1;
3864
3865 return REG_NOERROR;
3866}
3867
3868/* Free the allocated memory inside NODE. */
3869
3870static void
3871free_token (re_token_t *node)
3872{
3873#ifdef RE_ENABLE_I18N
3874 if (node->type == COMPLEX_BRACKET && node->duplicated == 0)
3875 free_charset (node->opr.mbcset);
3876 else
3877#endif /* RE_ENABLE_I18N */
3878 if (node->type == SIMPLE_BRACKET && node->duplicated == 0)
3879 re_free (node->opr.sbcset);
3880}
3881
3882/* Worker function for tree walking. Free the allocated memory inside NODE
3883 and its children. */
3884
3885static reg_errcode_t
3886free_tree (UNUSED_PARAM void *extra, bin_tree_t *node)
3887{
3888 free_token (&node->token);
3889 return REG_NOERROR;
3890}
3891
3892
3893/* Duplicate the node SRC, and return new node. This is a preorder
3894 visit similar to the one implemented by the generic visitor, but
3895 we need more infrastructure to maintain two parallel trees --- so,
3896 it's easier to duplicate. */
3897
3898static bin_tree_t *
3899duplicate_tree (const bin_tree_t *root, re_dfa_t *dfa)
3900{
3901 const bin_tree_t *node;
3902 bin_tree_t *dup_root;
3903 bin_tree_t **p_new = &dup_root, *dup_node = root->parent;
3904
3905 for (node = root; ; )
3906 {
3907 /* Create a new tree and link it back to the current parent. */
3908 *p_new = create_token_tree (dfa, NULL, NULL, &node->token);
3909 if (*p_new == NULL)
3910 return NULL;
3911 (*p_new)->parent = dup_node;
3912 (*p_new)->token.duplicated = 1;
3913 dup_node = *p_new;
3914
3915 /* Go to the left node, or up and to the right. */
3916 if (node->left)
3917 {
3918 node = node->left;
3919 p_new = &dup_node->left;
3920 }
3921 else
3922 {
3923 const bin_tree_t *prev = NULL;
3924 while (node->right == prev || node->right == NULL)
3925 {
3926 prev = node;
3927 node = node->parent;
3928 dup_node = dup_node->parent;
3929 if (!node)
3930 return dup_root;
3931 }
3932 node = node->right;
3933 p_new = &dup_node->right;
3934 }
3935 }
3936}
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..550eddaa0
--- /dev/null
+++ b/win32/resources/Kbuild.src
@@ -0,0 +1,28 @@
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
10obj-$(CONFIG_FEATURE_RESOURCES) += dummy.o
11
12# return commit level if available or 0
13bb_level = $(or $(word 2,$(subst -, ,$1)),0)
14
15WRFLAGS := -D"KBUILD_STR(s)=\#s" -D"BB_VER=KBUILD_STR($(BB_VER))" \
16 -D"BB_VERSION=$(VERSION)" -D"BB_PATCHLEVEL=$(PATCHLEVEL)" \
17 -D"BB_SUBLEVEL=$(SUBLEVEL)" \
18 -D"BB_EXTRAVERSION=$(call bb_level,$(EXTRAVERSION))" \
19 --include-dir=$(objtree)/include --include-dir=$(objtree)/win32/resources
20
21quiet_cmd_windres = WINDRES $@
22 cmd_windres = $(WINDRES) $(WRFLAGS) $< $@
23
24%.o: %.rc FORCE
25 $(call if_changed,windres)
26
27win32/resources/resources.o: win32/resources/resources.rc .config
28win32/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..2d4b87680
--- /dev/null
+++ b/win32/resources/dummy.c
@@ -0,0 +1,2 @@
1extern void wibble(void);
2void wibble(void) {}
diff --git a/win32/resources/resources.rc b/win32/resources/resources.rc
new file mode 100644
index 000000000..b4ec9c2d7
--- /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-2021 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..2be221ac8
--- /dev/null
+++ b/win32/select.c
@@ -0,0 +1,592 @@
1/* Emulation for select(2)
2 Contributed by Paolo Bonzini.
3
4 Copyright 2008-2021 Free Software Foundation, Inc.
5
6 This file is part of gnulib.
7
8 This file is free software: you can redistribute it and/or modify
9 it under the terms of the GNU Lesser General Public License as
10 published by the Free Software Foundation; either version 2.1 of the
11 License, or (at your option) any later version.
12
13 This file 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 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with this program. If not, see <https://www.gnu.org/licenses/>. */
20
21#include "libbb.h"
22
23/* Specification. */
24#include <sys/select.h>
25
26#if defined _WIN32 && ! defined __CYGWIN__
27/* Native Windows. */
28
29#include <malloc.h>
30#include <assert.h>
31#include <sys/types.h>
32#include <errno.h>
33#include <limits.h>
34
35#include <winsock2.h>
36#include <windows.h>
37#include <io.h>
38#include <stdio.h>
39#include <conio.h>
40#include <time.h>
41
42/* Get the overridden 'struct timeval'. */
43
44#undef select
45
46/* Don't assume that UNICODE is not defined. */
47#undef GetModuleHandle
48#define GetModuleHandle GetModuleHandleA
49#undef PeekConsoleInput
50#define PeekConsoleInput PeekConsoleInputA
51#undef CreateEvent
52#define CreateEvent CreateEventA
53#undef PeekMessage
54#define PeekMessage PeekMessageA
55#undef DispatchMessage
56#define DispatchMessage DispatchMessageA
57
58/* Avoid warnings from gcc -Wcast-function-type. */
59#define GetProcAddress \
60 (void *) GetProcAddress
61
62struct bitset {
63 unsigned char in[FD_SETSIZE / CHAR_BIT];
64 unsigned char out[FD_SETSIZE / CHAR_BIT];
65};
66
67/* Declare data structures for ntdll functions. */
68typedef struct _FILE_PIPE_LOCAL_INFORMATION {
69 ULONG NamedPipeType;
70 ULONG NamedPipeConfiguration;
71 ULONG MaximumInstances;
72 ULONG CurrentInstances;
73 ULONG InboundQuota;
74 ULONG ReadDataAvailable;
75 ULONG OutboundQuota;
76 ULONG WriteQuotaAvailable;
77 ULONG NamedPipeState;
78 ULONG NamedPipeEnd;
79} FILE_PIPE_LOCAL_INFORMATION, *PFILE_PIPE_LOCAL_INFORMATION;
80
81typedef struct _IO_STATUS_BLOCK
82{
83 union {
84 DWORD Status;
85 PVOID Pointer;
86 } u;
87 ULONG_PTR Information;
88} IO_STATUS_BLOCK, *PIO_STATUS_BLOCK;
89
90typedef enum _FILE_INFORMATION_CLASS {
91 FilePipeLocalInformation = 24
92} FILE_INFORMATION_CLASS, *PFILE_INFORMATION_CLASS;
93
94typedef DWORD (WINAPI *PNtQueryInformationFile)
95 (HANDLE, IO_STATUS_BLOCK *, VOID *, ULONG, FILE_INFORMATION_CLASS);
96
97#ifndef PIPE_BUF
98#define PIPE_BUF 512
99#endif
100
101static BOOL IsConsoleHandle (HANDLE h)
102{
103 DWORD mode;
104 return GetConsoleMode (h, &mode) != 0;
105}
106
107static BOOL
108IsSocketHandle (HANDLE h)
109{
110 WSANETWORKEVENTS ev;
111
112 if (IsConsoleHandle (h))
113 return FALSE;
114
115 /* Under Wine, it seems that getsockopt returns 0 for pipes too.
116 WSAEnumNetworkEvents instead distinguishes the two correctly. */
117 ev.lNetworkEvents = 0xDEADBEEF;
118 WSAEnumNetworkEvents ((SOCKET) h, NULL, &ev);
119 return ev.lNetworkEvents != 0xDEADBEEF;
120}
121
122/* Compute output fd_sets for libc descriptor FD (whose Windows handle is
123 H). */
124
125static int
126windows_poll_handle (HANDLE h, int fd,
127 struct bitset *rbits,
128 struct bitset *wbits,
129 struct bitset *xbits)
130{
131 BOOL read, write, except;
132 int i, ret;
133 INPUT_RECORD *irbuffer;
134 DWORD avail, nbuffer;
135 BOOL bRet;
136 IO_STATUS_BLOCK iosb;
137 FILE_PIPE_LOCAL_INFORMATION fpli;
138 static PNtQueryInformationFile NtQueryInformationFile;
139 static BOOL once_only;
140
141 read = write = except = FALSE;
142 switch (GetFileType (h))
143 {
144 case FILE_TYPE_DISK:
145 read = TRUE;
146 write = TRUE;
147 break;
148
149 case FILE_TYPE_PIPE:
150 if (!once_only)
151 {
152 NtQueryInformationFile = (PNtQueryInformationFile)
153 GetProcAddress (GetModuleHandle ("ntdll.dll"),
154 "NtQueryInformationFile");
155 once_only = TRUE;
156 }
157
158 if (PeekNamedPipe (h, NULL, 0, NULL, &avail, NULL) != 0)
159 {
160 if (avail)
161 read = TRUE;
162 }
163 else if (GetLastError () == ERROR_BROKEN_PIPE)
164 read = TRUE;
165
166 else
167 {
168 /* It was the write-end of the pipe. Check if it is writable.
169 If NtQueryInformationFile fails, optimistically assume the pipe is
170 writable. This could happen on Windows 9x, where
171 NtQueryInformationFile is not available, or if we inherit a pipe
172 that doesn't permit FILE_READ_ATTRIBUTES access on the write end
173 (I think this should not happen since Windows XP SP2; WINE seems
174 fine too). Otherwise, ensure that enough space is available for
175 atomic writes. */
176 memset (&iosb, 0, sizeof (iosb));
177 memset (&fpli, 0, sizeof (fpli));
178
179 if (!NtQueryInformationFile
180 || NtQueryInformationFile (h, &iosb, &fpli, sizeof (fpli),
181 FilePipeLocalInformation)
182 || fpli.WriteQuotaAvailable >= PIPE_BUF
183 || (fpli.OutboundQuota < PIPE_BUF &&
184 fpli.WriteQuotaAvailable == fpli.OutboundQuota))
185 write = TRUE;
186 }
187 break;
188
189 case FILE_TYPE_CHAR:
190 write = TRUE;
191 if (!(rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
192 break;
193
194 ret = WaitForSingleObject (h, 0);
195 if (ret == WAIT_OBJECT_0)
196 {
197 if (!IsConsoleHandle (h))
198 {
199 read = TRUE;
200 break;
201 }
202
203 nbuffer = avail = 0;
204 bRet = GetNumberOfConsoleInputEvents (h, &nbuffer);
205
206 /* Screen buffers handles are filtered earlier. */
207 assert (bRet);
208 if (nbuffer == 0)
209 {
210 except = TRUE;
211 break;
212 }
213
214 irbuffer = (INPUT_RECORD *) alloca (nbuffer * sizeof (INPUT_RECORD));
215 bRet = PeekConsoleInput (h, irbuffer, nbuffer, &avail);
216 if (!bRet || avail == 0)
217 {
218 except = TRUE;
219 break;
220 }
221
222 for (i = 0; i < avail; i++)
223 if (irbuffer[i].EventType == KEY_EVENT &&
224 irbuffer[i].Event.KeyEvent.bKeyDown)
225 read = TRUE;
226 }
227 break;
228
229 default:
230 ret = WaitForSingleObject (h, 0);
231 write = TRUE;
232 if (ret == WAIT_OBJECT_0)
233 read = TRUE;
234
235 break;
236 }
237
238 ret = 0;
239 if (read && (rbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
240 {
241 rbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
242 ret++;
243 }
244
245 if (write && (wbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
246 {
247 wbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
248 ret++;
249 }
250
251 if (except && (xbits->in[fd / CHAR_BIT] & (1 << (fd & (CHAR_BIT - 1)))))
252 {
253 xbits->out[fd / CHAR_BIT] |= (1 << (fd & (CHAR_BIT - 1)));
254 ret++;
255 }
256
257 return ret;
258}
259
260int
261mingw_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
262 struct timeval *timeout)
263#undef timeval
264{
265 static struct timeval tv0;
266 static HANDLE hEvent;
267 HANDLE h, handle_array[FD_SETSIZE + 2];
268 fd_set handle_rfds, handle_wfds, handle_xfds;
269 struct bitset rbits, wbits, xbits;
270 unsigned char anyfds_in[FD_SETSIZE / CHAR_BIT];
271 DWORD ret, wait_timeout, nhandles, nsock, nbuffer;
272 MSG msg;
273 int i, fd, rc;
274 clock_t tend = 0;
275
276 if (nfds > FD_SETSIZE)
277 nfds = FD_SETSIZE;
278
279 if (!timeout)
280 wait_timeout = INFINITE;
281 else
282 {
283 wait_timeout = timeout->tv_sec * 1000 + timeout->tv_usec / 1000;
284
285 /* select is also used as a portable usleep. */
286 if (!rfds && !wfds && !xfds)
287 {
288 Sleep (wait_timeout);
289 return 0;
290 }
291 }
292
293 if (!hEvent)
294 hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);
295
296 handle_array[0] = hEvent;
297 nhandles = 1;
298 nsock = 0;
299
300 /* Copy descriptors to bitsets. At the same time, eliminate
301 bits in the "wrong" direction for console input buffers
302 and screen buffers, because screen buffers are waitable
303 and they will block until a character is available. */
304 memset (&rbits, 0, sizeof (rbits));
305 memset (&wbits, 0, sizeof (wbits));
306 memset (&xbits, 0, sizeof (xbits));
307 memset (anyfds_in, 0, sizeof (anyfds_in));
308 if (rfds)
309 for (i = 0; i < rfds->fd_count; i++)
310 {
311 fd = rfds->fd_array[i];
312 h = (HANDLE) _get_osfhandle (fd);
313 if (IsConsoleHandle (h)
314 && !GetNumberOfConsoleInputEvents (h, &nbuffer))
315 continue;
316
317 rbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
318 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
319 }
320 else
321 rfds = (fd_set *) alloca (sizeof (fd_set));
322
323 if (wfds)
324 for (i = 0; i < wfds->fd_count; i++)
325 {
326 fd = wfds->fd_array[i];
327 h = (HANDLE) _get_osfhandle (fd);
328 if (IsConsoleHandle (h)
329 && GetNumberOfConsoleInputEvents (h, &nbuffer))
330 continue;
331
332 wbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
333 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
334 }
335 else
336 wfds = (fd_set *) alloca (sizeof (fd_set));
337
338 if (xfds)
339 for (i = 0; i < xfds->fd_count; i++)
340 {
341 fd = xfds->fd_array[i];
342 xbits.in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
343 anyfds_in[fd / CHAR_BIT] |= 1 << (fd & (CHAR_BIT - 1));
344 }
345 else
346 xfds = (fd_set *) alloca (sizeof (fd_set));
347
348 /* Zero all the fd_sets, including the application's. */
349 FD_ZERO (rfds);
350 FD_ZERO (wfds);
351 FD_ZERO (xfds);
352 FD_ZERO (&handle_rfds);
353 FD_ZERO (&handle_wfds);
354 FD_ZERO (&handle_xfds);
355
356 /* Classify handles. Create fd sets for sockets, poll the others. */
357 for (i = 0; i < nfds; i++)
358 {
359 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
360 continue;
361
362 h = (HANDLE) _get_osfhandle (i);
363 if (!h)
364 {
365 errno = EBADF;
366 return -1;
367 }
368
369 if (IsSocketHandle (h))
370 {
371 int requested = FD_CLOSE;
372
373 /* See above; socket handles are mapped onto select, but we
374 need to map descriptors to handles. */
375 if (rbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
376 {
377 requested |= FD_READ | FD_ACCEPT;
378 FD_SET ((SOCKET) h, rfds);
379 FD_SET ((SOCKET) h, &handle_rfds);
380 }
381 if (wbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
382 {
383 requested |= FD_WRITE | FD_CONNECT;
384 FD_SET ((SOCKET) h, wfds);
385 FD_SET ((SOCKET) h, &handle_wfds);
386 }
387 if (xbits.in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
388 {
389 requested |= FD_OOB;
390 FD_SET ((SOCKET) h, xfds);
391 FD_SET ((SOCKET) h, &handle_xfds);
392 }
393
394 WSAEventSelect ((SOCKET) h, hEvent, requested);
395 nsock++;
396 }
397 else
398 {
399 handle_array[nhandles++] = h;
400
401 /* Poll now. If we get an event, do not wait below. */
402 if (wait_timeout != 0
403 && windows_poll_handle (h, i, &rbits, &wbits, &xbits))
404 wait_timeout = 0;
405 }
406 }
407
408 /* Place a sentinel at the end of the array. */
409 handle_array[nhandles] = NULL;
410
411 /* When will the waiting period expire? */
412 if (wait_timeout != INFINITE)
413 tend = clock () + wait_timeout;
414
415restart:
416 if (wait_timeout == 0 || nsock == 0)
417 rc = 0;
418 else
419 {
420 /* See if we need to wait in the loop below. If any select is ready,
421 do MsgWaitForMultipleObjects anyway to dispatch messages, but
422 no need to call select again. */
423 rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
424 if (rc == 0)
425 {
426 /* Restore the fd_sets for the other select we do below. */
427 memcpy (&handle_rfds, rfds, sizeof (fd_set));
428 memcpy (&handle_wfds, wfds, sizeof (fd_set));
429 memcpy (&handle_xfds, xfds, sizeof (fd_set));
430 }
431 else
432 wait_timeout = 0;
433 }
434
435 /* How much is left to wait? */
436 if (wait_timeout != INFINITE)
437 {
438 clock_t tnow = clock ();
439 if (tend >= tnow)
440 wait_timeout = tend - tnow;
441 else
442 wait_timeout = 0;
443 }
444
445 for (;;)
446 {
447 ret = MsgWaitForMultipleObjects (nhandles, handle_array, FALSE,
448 wait_timeout, QS_ALLINPUT);
449
450 if (ret == WAIT_OBJECT_0 + nhandles)
451 {
452 /* new input of some other kind */
453 BOOL bRet;
454 while ((bRet = PeekMessage (&msg, NULL, 0, 0, PM_REMOVE)) != 0)
455 {
456 TranslateMessage (&msg);
457 DispatchMessage (&msg);
458 }
459 }
460 else
461 break;
462 }
463
464 /* If we haven't done it yet, check the status of the sockets. */
465 if (rc == 0 && nsock > 0)
466 rc = select (0, &handle_rfds, &handle_wfds, &handle_xfds, &tv0);
467
468 if (nhandles > 1)
469 {
470 /* Count results that are not counted in the return value of select. */
471 nhandles = 1;
472 for (i = 0; i < nfds; i++)
473 {
474 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
475 continue;
476
477 h = (HANDLE) _get_osfhandle (i);
478 if (h == handle_array[nhandles])
479 {
480 /* Not a socket. */
481 nhandles++;
482 windows_poll_handle (h, i, &rbits, &wbits, &xbits);
483 if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
484 || wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))
485 || xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
486 rc++;
487 }
488 }
489
490 if (rc == 0
491 && (wait_timeout == INFINITE
492 /* If NHANDLES > 1, but no bits are set, it means we've
493 been told incorrectly that some handle was signaled.
494 This happens with anonymous pipes, which always cause
495 MsgWaitForMultipleObjects to exit immediately, but no
496 data is found ready to be read by windows_poll_handle.
497 To avoid a total failure (whereby we return zero and
498 don't wait at all), let's poll in a more busy loop. */
499 || (wait_timeout != 0 && nhandles > 1)))
500 {
501 /* Sleep 1 millisecond to avoid busy wait and retry with the
502 original fd_sets. */
503 memcpy (&handle_rfds, rfds, sizeof (fd_set));
504 memcpy (&handle_wfds, wfds, sizeof (fd_set));
505 memcpy (&handle_xfds, xfds, sizeof (fd_set));
506 SleepEx (1, TRUE);
507 goto restart;
508 }
509 if (timeout && wait_timeout == 0 && rc == 0)
510 timeout->tv_sec = timeout->tv_usec = 0;
511 }
512
513 /* Now fill in the results. */
514 FD_ZERO (rfds);
515 FD_ZERO (wfds);
516 FD_ZERO (xfds);
517 nhandles = 1;
518 for (i = 0; i < nfds; i++)
519 {
520 if ((anyfds_in[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1)))) == 0)
521 continue;
522
523 h = (HANDLE) _get_osfhandle (i);
524 if (h != handle_array[nhandles])
525 {
526 /* Perform handle->descriptor mapping. */
527 SOCKET s = (SOCKET) h;
528 WSAEventSelect (s, NULL, 0);
529 if (FD_ISSET (s, &handle_rfds))
530 FD_SET (i, rfds);
531 if (FD_ISSET (s, &handle_wfds))
532 FD_SET (i, wfds);
533 if (FD_ISSET (s, &handle_xfds))
534 FD_SET (i, xfds);
535 }
536 else
537 {
538 /* Not a socket. */
539 nhandles++;
540 if (rbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
541 FD_SET (i, rfds);
542 if (wbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
543 FD_SET (i, wfds);
544 if (xbits.out[i / CHAR_BIT] & (1 << (i & (CHAR_BIT - 1))))
545 FD_SET (i, xfds);
546 }
547 }
548
549 return rc;
550}
551
552#else /* ! Native Windows. */
553
554#include <stddef.h> /* NULL */
555#include <errno.h>
556#include <unistd.h>
557
558#undef select
559
560int
561rpl_select (int nfds, fd_set *rfds, fd_set *wfds, fd_set *xfds,
562 struct timeval *timeout)
563{
564 int i;
565
566 /* FreeBSD 8.2 has a bug: it does not always detect invalid fds. */
567 if (nfds < 0 || nfds > FD_SETSIZE)
568 {
569 errno = EINVAL;
570 return -1;
571 }
572 for (i = 0; i < nfds; i++)
573 {
574 if (((rfds && FD_ISSET (i, rfds))
575 || (wfds && FD_ISSET (i, wfds))
576 || (xfds && FD_ISSET (i, xfds)))
577 && dup2 (i, i) != i)
578 return -1;
579 }
580
581 /* Interix 3.5 has a bug: it does not support nfds == 0. */
582 if (nfds == 0)
583 {
584 nfds = 1;
585 rfds = NULL;
586 wfds = NULL;
587 xfds = NULL;
588 }
589 return select (nfds, rfds, wfds, xfds, timeout);
590}
591
592#endif
diff --git a/win32/sh_random.c b/win32/sh_random.c
new file mode 100644
index 000000000..912793269
--- /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 FILETIME 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)GetProcessId(GetCurrentProcess());
28 state[i++%2] ^= (uint32_t)GetCurrentThreadId();
29 state[i++%2] ^= (uint32_t)GetTickCount();
30
31 GET_DATA(GetSystemTimeAsFileTime, 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..97b3ce679
--- /dev/null
+++ b/win32/statfs.c
@@ -0,0 +1,70 @@
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 /* Valid filesystem names don't seem to be documented. The following
16 * are present in Wine (dlls/kernel32/volume.c). */
17#define FS_NAMES "NTFS\0FAT\0FAT32\0CDFS\0UDF\0"
18 int fstypes[] = {0, 0x5346544e, 0x4006, 0x4006, 0x9660, 0x15013346};
19
20 if ( (mnt=find_mount_point(file, 0)) == NULL ) {
21 return -1;
22 }
23
24 file = mnt->mnt_dir;
25 if ( !GetDiskFreeSpaceEx(file, (PULARGE_INTEGER) &free_bytes_available,
26 (PULARGE_INTEGER) &total_number_of_bytes,
27 (PULARGE_INTEGER) &total_number_of_free_bytes) ) {
28 errno = err_win_to_posix();
29 return -1;
30 }
31
32 if ( !GetVolumeInformation(file, NULL, 0, &serial, &namelen, &flags,
33 fsname, 100) ) {
34 errno = err_win_to_posix();
35 return -1;
36 }
37
38 memset(buf, 0, sizeof(*buf));
39
40 /* XXX I couldn't determine how to get block size. MSDN has a
41 * unhelpful hard-coded list here:
42 * http://support.microsoft.com/kb/140365
43 * but this depends on the filesystem type, the size of the disk and
44 * the version of Windows. So this code assumes the disk is NTFS
45 * and the version of Windows is >= Win2K.
46 */
47 if (total_number_of_bytes < UINT64_C(16) * 1024 * 1024 * 1024 * 1024)
48 buf->f_bsize = 4096;
49 else if (total_number_of_bytes < UINT64_C(32) * 1024 * 1024 * 1024 * 1024)
50 buf->f_bsize = 8192;
51 else if (total_number_of_bytes < UINT64_C(64) * 1024 * 1024 * 1024 * 1024)
52 buf->f_bsize = 16384;
53 else if (total_number_of_bytes < UINT64_C(128) * 1024 * 1024 * 1024 * 1024)
54 buf->f_bsize = 32768;
55 else
56 buf->f_bsize = 65536;
57
58 buf->f_type = fstypes[index_in_strings(FS_NAMES, fsname)+1];
59 buf->f_frsize = buf->f_bsize;
60 buf->f_blocks = total_number_of_bytes / buf->f_bsize;
61 buf->f_bfree = total_number_of_free_bytes / buf->f_bsize;
62 buf->f_bavail = free_bytes_available / buf->f_bsize;
63 //buf->f_files = 0;
64 //buf->f_ffree = 0;
65 buf->f_fsid = serial;
66 //buf->f_flag = 0;
67 buf->f_namelen = namelen;
68
69 return 0;
70}
diff --git a/win32/strndup.c b/win32/strndup.c
new file mode 100644
index 000000000..4d04609f6
--- /dev/null
+++ b/win32/strndup.c
@@ -0,0 +1,36 @@
1/* A replacement function, for systems that lack strndup.
2
3 Copyright (C) 1996-1998, 2001-2003, 2005-2007, 2009-2020 Free Software
4 Foundation, Inc.
5
6 This program is free software; you can redistribute it and/or modify it
7 under the terms of the GNU General Public License as published by the
8 Free Software Foundation; either version 2, or (at your option) any
9 later version.
10
11 This program 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
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, see <https://www.gnu.org/licenses/>. */
18
19#include "libbb.h"
20
21#include <string.h>
22
23#include <stdlib.h>
24
25char *
26strndup (char const *s, size_t n)
27{
28 size_t len = strnlen (s, n);
29 char *new = malloc (len + 1);
30
31 if (new == NULL)
32 return NULL;
33
34 new[len] = '\0';
35 return memcpy (new, s, len);
36}
diff --git a/win32/strptime.c b/win32/strptime.c
new file mode 100644
index 000000000..3205b95a2
--- /dev/null
+++ b/win32/strptime.c
@@ -0,0 +1,603 @@
1/* Copyright (C) 2002, 2004-2005, 2007, 2009-2020 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 <https://www.gnu.org/licenses/>. */
17
18/*
19 * File from gnulib (https://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 * A form of support for tm_gmtoff was later restored.
24 */
25
26#include "libbb.h"
27#include <time.h>
28
29#include <assert.h>
30#include <ctype.h>
31#include <limits.h>
32#include <string.h>
33#include <stdbool.h>
34
35
36enum ptime_locale_status { not, loc, raw };
37
38
39
40#define match_char(ch1, ch2) if (ch1 != ch2) return NULL
41/* Oh come on. Get a reasonable compiler. */
42# define match_string(cs1, s2) \
43 (strncasecmp ((cs1), (s2), strlen (cs1)) ? 0 : ((s2) += strlen (cs1), 1))
44/* We intentionally do not use isdigit() for testing because this will
45 lead to problems with the wide character version. */
46#define get_number(from, to, n) \
47 do { \
48 int __n = n; \
49 val = 0; \
50 while (*rp == ' ') \
51 ++rp; \
52 if (*rp < '0' || *rp > '9') \
53 return NULL; \
54 do { \
55 val *= 10; \
56 val += *rp++ - '0'; \
57 } while (--__n > 0 && val * 10 <= to && *rp >= '0' && *rp <= '9'); \
58 if (val < from || val > to) \
59 return NULL; \
60 } while (0)
61# define get_alt_number(from, to, n) \
62 /* We don't have the alternate representation. */ \
63 get_number(from, to, n)
64#define recursive(new_fmt) \
65 (*(new_fmt) != '\0' \
66 && (rp = __strptime_internal (rp, (new_fmt), tm, \
67 decided, era_cnt, gmtoff)) != NULL)
68
69
70static char const weekday_name[][10] =
71 {
72 "Sunday", "Monday", "Tuesday", "Wednesday",
73 "Thursday", "Friday", "Saturday"
74 };
75static char const ab_weekday_name[][4] =
76 {
77 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
78 };
79static char const month_name[][10] =
80 {
81 "January", "February", "March", "April", "May", "June",
82 "July", "August", "September", "October", "November", "December"
83 };
84static char const ab_month_name[][4] =
85 {
86 "Jan", "Feb", "Mar", "Apr", "May", "Jun",
87 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
88 };
89# define HERE_D_T_FMT "%a %b %e %H:%M:%S %Y"
90# define HERE_D_FMT "%m/%d/%y"
91# define HERE_AM_STR "AM"
92# define HERE_PM_STR "PM"
93# define HERE_T_FMT_AMPM "%I:%M:%S %p"
94# define HERE_T_FMT "%H:%M:%S"
95
96static const unsigned short int __mon_yday[2][13] =
97 {
98 /* Normal years. */
99 { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
100 /* Leap years. */
101 { 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
102 };
103
104# define ISSPACE(Ch) isspace (Ch)
105
106
107
108
109#ifndef __isleap
110/* Nonzero if YEAR is a leap year (every 4 years,
111 except every 100th isn't, and every 400th is). */
112# define __isleap(year) \
113 ((year) % 4 == 0 && ((year) % 100 != 0 || (year) % 400 == 0))
114#endif
115
116/* Compute the day of the week. */
117static void
118day_of_the_week (struct tm *tm)
119{
120 /* We know that January 1st 1970 was a Thursday (= 4). Compute the
121 difference between this data in the one on TM and so determine
122 the weekday. */
123 int corr_year = 1900 + tm->tm_year - (tm->tm_mon < 2);
124 int corr_quad = corr_year / 4;
125 int wday = (-473
126 + (365 * (tm->tm_year - 70))
127 + corr_quad
128 - ((corr_quad + (corr_quad < 0)) / 25 - (corr_quad < 0))
129 + ((corr_quad / 25) / 4)
130 + __mon_yday[0][tm->tm_mon]
131 + tm->tm_mday - 1);
132 tm->tm_wday = ((wday % 7) + 7) % 7;
133}
134
135/* Compute the day of the year. */
136static void
137day_of_the_year (struct tm *tm)
138{
139 tm->tm_yday = (__mon_yday[__isleap (1900 + tm->tm_year)][tm->tm_mon]
140 + (tm->tm_mday - 1));
141}
142
143
144static char *
145__strptime_internal (const char *rp, const char *fmt, struct tm *tm,
146 enum ptime_locale_status *decided, int era_cnt,
147 long *gmtoff)
148{
149
150 int cnt;
151 size_t val;
152 int have_I, is_pm;
153 int century, want_century;
154 int want_era;
155 int have_wday, want_xday;
156 int have_yday;
157 int have_mon, have_mday;
158 int have_uweek, have_wweek;
159 int week_no;
160
161 have_I = is_pm = 0;
162 century = -1;
163 want_century = 0;
164 want_era = 0;
165 week_no = 0;
166
167 have_wday = want_xday = have_yday = have_mon = have_mday = have_uweek = 0;
168 have_wweek = 0;
169
170 while (*fmt != '\0')
171 {
172 /* A white space in the format string matches 0 more or white
173 space in the input string. */
174 if (ISSPACE (*fmt))
175 {
176 while (ISSPACE (*rp))
177 ++rp;
178 ++fmt;
179 continue;
180 }
181
182 /* Any character but '%' must be matched by the same character
183 in the input string. */
184 if (*fmt != '%')
185 {
186 match_char (*fmt++, *rp++);
187 continue;
188 }
189
190 ++fmt;
191 /* We need this for handling the 'E' modifier. */
192 start_over:
193
194 switch (*fmt++)
195 {
196 case '%':
197 /* Match the '%' character itself. */
198 match_char ('%', *rp++);
199 break;
200 case 'a':
201 case 'A':
202 /* Match day of week. */
203 for (cnt = 0; cnt < 7; ++cnt)
204 {
205 if (*decided != loc
206 && (match_string (weekday_name[cnt], rp)
207 || match_string (ab_weekday_name[cnt], rp)))
208 {
209 *decided = raw;
210 break;
211 }
212 }
213 if (cnt == 7)
214 /* Does not match a weekday name. */
215 return NULL;
216 tm->tm_wday = cnt;
217 have_wday = 1;
218 break;
219 case 'b':
220 case 'B':
221 case 'h':
222 /* Match month name. */
223 for (cnt = 0; cnt < 12; ++cnt)
224 {
225 if (match_string (month_name[cnt], rp)
226 || match_string (ab_month_name[cnt], rp))
227 {
228 *decided = raw;
229 break;
230 }
231 }
232 if (cnt == 12)
233 /* Does not match a month name. */
234 return NULL;
235 tm->tm_mon = cnt;
236 want_xday = 1;
237 break;
238 case 'c':
239 /* Match locale's date and time format. */
240 if (!recursive (HERE_D_T_FMT))
241 return NULL;
242 want_xday = 1;
243 break;
244 case 'C':
245 /* Match century number. */
246 get_number (0, 99, 2);
247 century = val;
248 want_xday = 1;
249 break;
250 case 'd':
251 case 'e':
252 /* Match day of month. */
253 get_number (1, 31, 2);
254 tm->tm_mday = val;
255 have_mday = 1;
256 want_xday = 1;
257 break;
258 case 'F':
259 if (!recursive ("%Y-%m-%d"))
260 return NULL;
261 want_xday = 1;
262 break;
263 case 'x':
264 /* Fall through. */
265 case 'D':
266 /* Match standard day format. */
267 if (!recursive (HERE_D_FMT))
268 return NULL;
269 want_xday = 1;
270 break;
271 case 'k':
272 case 'H':
273 /* Match hour in 24-hour clock. */
274 get_number (0, 23, 2);
275 tm->tm_hour = val;
276 have_I = 0;
277 break;
278 case 'l':
279 /* Match hour in 12-hour clock. GNU extension. */
280 case 'I':
281 /* Match hour in 12-hour clock. */
282 get_number (1, 12, 2);
283 tm->tm_hour = val % 12;
284 have_I = 1;
285 break;
286 case 'j':
287 /* Match day number of year. */
288 get_number (1, 366, 3);
289 tm->tm_yday = val - 1;
290 have_yday = 1;
291 break;
292 case 'm':
293 /* Match number of month. */
294 get_number (1, 12, 2);
295 tm->tm_mon = val - 1;
296 have_mon = 1;
297 want_xday = 1;
298 break;
299 case 'M':
300 /* Match minute. */
301 get_number (0, 59, 2);
302 tm->tm_min = val;
303 break;
304 case 'n':
305 case 't':
306 /* Match any white space. */
307 while (ISSPACE (*rp))
308 ++rp;
309 break;
310 case 'p':
311 /* Match locale's equivalent of AM/PM. */
312 if (!match_string (HERE_AM_STR, rp))
313 {
314 if (match_string (HERE_PM_STR, rp))
315 is_pm = 1;
316 else
317 return NULL;
318 }
319 break;
320 case 'q':
321 /* Match quarter of year. GNU extension. */
322 get_number (1, 4, 1);
323 tm->tm_mon = (val - 1) * 3;
324 tm->tm_mday = 1;
325 have_mon = 1;
326 have_mday = 1;
327 want_xday = 1;
328 break;
329 case 'r':
330 if (!recursive (HERE_T_FMT_AMPM))
331 return NULL;
332 break;
333 case 'R':
334 if (!recursive ("%H:%M"))
335 return NULL;
336 break;
337 case 's':
338 {
339 /* The number of seconds may be very high so we cannot use
340 the 'get_number' macro. Instead read the number
341 character for character and construct the result while
342 doing this. */
343 time_t secs = 0;
344 if (*rp < '0' || *rp > '9')
345 /* We need at least one digit. */
346 return NULL;
347
348 do
349 {
350 secs *= 10;
351 secs += *rp++ - '0';
352 }
353 while (*rp >= '0' && *rp <= '9');
354
355 if (localtime_r (&secs, tm) == NULL)
356 /* Error in function. */
357 return NULL;
358 }
359 break;
360 case 'S':
361 get_number (0, 61, 2);
362 tm->tm_sec = val;
363 break;
364 case 'X':
365 /* Fall through. */
366 case 'T':
367 if (!recursive (HERE_T_FMT))
368 return NULL;
369 break;
370 case 'u':
371 get_number (1, 7, 1);
372 tm->tm_wday = val % 7;
373 have_wday = 1;
374 break;
375 case 'g':
376 get_number (0, 99, 2);
377 /* XXX This cannot determine any field in TM. */
378 break;
379 case 'G':
380 if (*rp < '0' || *rp > '9')
381 return NULL;
382 /* XXX Ignore the number since we would need some more
383 information to compute a real date. */
384 do
385 ++rp;
386 while (*rp >= '0' && *rp <= '9');
387 break;
388 case 'U':
389 get_number (0, 53, 2);
390 week_no = val;
391 have_uweek = 1;
392 break;
393 case 'W':
394 get_number (0, 53, 2);
395 week_no = val;
396 have_wweek = 1;
397 break;
398 case 'V':
399 get_number (0, 53, 2);
400 /* XXX This cannot determine any field in TM without some
401 information. */
402 break;
403 case 'w':
404 /* Match number of weekday. */
405 get_number (0, 6, 1);
406 tm->tm_wday = val;
407 have_wday = 1;
408 break;
409 case 'y':
410 /* Match year within century. */
411 get_number (0, 99, 2);
412 /* The "Year 2000: The Millennium Rollover" paper suggests that
413 values in the range 69-99 refer to the twentieth century. */
414 tm->tm_year = val >= 69 ? val : val + 100;
415 /* Indicate that we want to use the century, if specified. */
416 want_century = 1;
417 want_xday = 1;
418 break;
419 case 'Y':
420 /* Match year including century number. */
421 get_number (0, 9999, 4);
422 tm->tm_year = val - 1900;
423 want_century = 0;
424 want_xday = 1;
425 break;
426 case 'Z':
427 /* XXX How to handle this? */
428 break;
429 case 'z':
430 /* We recognize two formats: if two digits are given, these
431 specify hours. If fours digits are used, minutes are
432 also specified. And 'Z'.
433
434 Three formats! We recognize three formats... */
435 {
436 bool neg;
437 int n;
438
439 val = 0;
440 while (*rp == ' ')
441 ++rp;
442 if (*rp == 'Z') {
443 ++rp;
444 if (gmtoff)
445 *gmtoff = 0;
446 break;
447 }
448 if (*rp != '+' && *rp != '-')
449 return NULL;
450 neg = *rp++ == '-';
451 n = 0;
452 while (n < 4 && *rp >= '0' && *rp <= '9')
453 {
454 val = val * 10 + *rp++ - '0';
455 ++n;
456 }
457 if (n == 2)
458 val *= 100;
459 else if (n != 4)
460 /* Only two or four digits recognized. */
461 return NULL;
462 else
463 {
464 /* We have to convert the minutes into decimal. */
465 if (val % 100 >= 60)
466 return NULL;
467 val = (val / 100) * 100 + ((val % 100) * 50) / 30;
468 }
469 if (val > 1200)
470 return NULL;
471 if (gmtoff) {
472 *gmtoff = (val * 3600) / 100;
473 if (neg)
474 *gmtoff = -*gmtoff;
475 }
476 }
477 break;
478 case 'E':
479 /* We have no information about the era format. Just use
480 the normal format. */
481 if (strchr("cCyYxX", *fmt) == NULL)
482 /* This is an illegal format. */
483 return NULL;
484
485 goto start_over;
486 case 'O':
487 /* We don't have an alternative number format. Just use
488 the normal format. */
489 if (strchr("deHImMqSUWVwy", *fmt) == NULL)
490 /* This is an illegal format. */
491 return NULL;
492
493 goto start_over;
494 default:
495 return NULL;
496 }
497 }
498
499 if (have_I && is_pm)
500 tm->tm_hour += 12;
501
502 if (century != -1)
503 {
504 if (want_century)
505 tm->tm_year = tm->tm_year % 100 + (century - 19) * 100;
506 else
507 /* Only the century, but not the year. Strange, but so be it. */
508 tm->tm_year = (century - 19) * 100;
509 }
510
511 if (era_cnt != -1)
512 {
513 }
514 else
515 if (want_era)
516 {
517 /* No era found but we have seen an E modifier. Rectify some
518 values. */
519 if (want_century && century == -1 && tm->tm_year < 69)
520 tm->tm_year += 100;
521 }
522
523 if (want_xday && !have_wday)
524 {
525 if ( !(have_mon && have_mday) && have_yday)
526 {
527 /* We don't have tm_mon and/or tm_mday, compute them. */
528 int t_mon = 0;
529 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon] <= tm->tm_yday)
530 t_mon++;
531 if (!have_mon)
532 tm->tm_mon = t_mon - 1;
533 if (!have_mday)
534 tm->tm_mday =
535 (tm->tm_yday
536 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
537 }
538 day_of_the_week (tm);
539 }
540
541 if (want_xday && !have_yday)
542 day_of_the_year (tm);
543
544 if ((have_uweek || have_wweek) && have_wday)
545 {
546 int save_wday = tm->tm_wday;
547 int save_mday = tm->tm_mday;
548 int save_mon = tm->tm_mon;
549 int w_offset = have_uweek ? 0 : 1;
550
551 tm->tm_mday = 1;
552 tm->tm_mon = 0;
553 day_of_the_week (tm);
554 if (have_mday)
555 tm->tm_mday = save_mday;
556 if (have_mon)
557 tm->tm_mon = save_mon;
558
559 if (!have_yday)
560 tm->tm_yday = ((7 - (tm->tm_wday - w_offset)) % 7
561 + (week_no - 1) *7
562 + save_wday - w_offset);
563
564 if (!have_mday || !have_mon)
565 {
566 int t_mon = 0;
567 while (__mon_yday[__isleap(1900 + tm->tm_year)][t_mon]
568 <= tm->tm_yday)
569 t_mon++;
570 if (!have_mon)
571 tm->tm_mon = t_mon - 1;
572 if (!have_mday)
573 tm->tm_mday =
574 (tm->tm_yday
575 - __mon_yday[__isleap(1900 + tm->tm_year)][t_mon - 1] + 1);
576 }
577
578 tm->tm_wday = save_wday;
579 }
580
581 return (char *) rp;
582}
583
584
585char *
586strptime (const char *buf, const char *format, struct tm *tm)
587{
588 enum ptime_locale_status decided;
589
590 decided = raw;
591 return __strptime_internal (buf, format, tm, &decided, -1, NULL);
592}
593
594char *
595mingw_strptime (const char *buf, const char *format, struct tm *tm,
596 long *gmtoff)
597{
598 enum ptime_locale_status decided;
599
600 decided = raw;
601 return __strptime_internal (buf, format, tm, &decided, -1, gmtoff);
602}
603
diff --git a/win32/sys/inotify.h b/win32/sys/inotify.h
new file mode 100644
index 000000000..e69de29bb
--- /dev/null
+++ b/win32/sys/inotify.h
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..3220d8112
--- /dev/null
+++ b/win32/sys/resource.h
@@ -0,0 +1,11 @@
1#ifndef _SYS_RESOURCE_H
2#define _SYS_RESOURCE_H 1
3
4#include <time.h>
5
6struct rusage {
7 struct timeval ru_utime;
8 struct timeval ru_stime;
9};
10
11#endif
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..45d4e7740
--- /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#endif
13 DWORD alt_pressed = FALSE;
14 DWORD state;
15
16 if (fd != 0)
17 bb_error_msg_and_die("read_key only works on stdin");
18 if (cin == INVALID_HANDLE_VALUE)
19 return -1;
20 GetConsoleMode(cin, &mode);
21 SetConsoleMode(cin, 0);
22
23 while (1) {
24 if (timeout > 0) {
25 if (WaitForSingleObject(cin, timeout) != WAIT_OBJECT_0)
26 goto done;
27 }
28#if ENABLE_FEATURE_EURO
29 if (!ReadConsoleInputW(cin, &record, 1, &nevent_out))
30#else
31 if (!ReadConsoleInput(cin, &record, 1, &nevent_out))
32#endif
33 goto done;
34
35 if (record.EventType != KEY_EVENT)
36 continue;
37
38 state = record.Event.KeyEvent.dwControlKeyState;
39 if (!record.Event.KeyEvent.bKeyDown) {
40 /* ignore all key up events except Alt */
41 if (!(alt_pressed && (state & LEFT_ALT_PRESSED) == 0 &&
42 record.Event.KeyEvent.wVirtualKeyCode == VK_MENU))
43 continue;
44 }
45 alt_pressed = state & LEFT_ALT_PRESSED;
46
47#if ENABLE_FEATURE_EURO
48 if (!record.Event.KeyEvent.uChar.UnicodeChar) {
49#else
50 if (!record.Event.KeyEvent.uChar.AsciiChar) {
51#endif
52 if (alt_pressed && !(state & ENHANCED_KEY)) {
53 /* keys on numeric pad used to enter character codes */
54 switch (record.Event.KeyEvent.wVirtualKeyCode) {
55 case VK_NUMPAD0: case VK_INSERT:
56 case VK_NUMPAD1: case VK_END:
57 case VK_NUMPAD2: case VK_DOWN:
58 case VK_NUMPAD3: case VK_NEXT:
59 case VK_NUMPAD4: case VK_LEFT:
60 case VK_NUMPAD5: case VK_CLEAR:
61 case VK_NUMPAD6: case VK_RIGHT:
62 case VK_NUMPAD7: case VK_HOME:
63 case VK_NUMPAD8: case VK_UP:
64 case VK_NUMPAD9: case VK_PRIOR:
65 continue;
66 }
67 }
68
69 switch (record.Event.KeyEvent.wVirtualKeyCode) {
70 case VK_DELETE: ret = KEYCODE_DELETE; break;
71 case VK_INSERT: ret = KEYCODE_INSERT; break;
72 case VK_UP: ret = KEYCODE_UP; break;
73 case VK_DOWN: ret = KEYCODE_DOWN; break;
74 case VK_RIGHT: ret = KEYCODE_RIGHT; break;
75 case VK_LEFT: ret = KEYCODE_LEFT; break;
76 case VK_HOME: ret = KEYCODE_HOME; break;
77 case VK_END: ret = KEYCODE_END; break;
78 case VK_PRIOR: ret = KEYCODE_PAGEUP; break;
79 case VK_NEXT: ret = KEYCODE_PAGEDOWN; break;
80 default:
81 alt_pressed = FALSE;
82 continue;
83 }
84
85 if (state & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
86 ret &= ~0x20;
87 if (state & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
88 ret &= ~0x40;
89 if (state & SHIFT_PRESSED)
90 ret &= ~0x80;
91 goto done;
92 }
93#if ENABLE_FEATURE_EURO
94 uchar = record.Event.KeyEvent.uChar.UnicodeChar;
95 achar = uchar & 0x7f;
96 if (achar != uchar)
97 WideCharToMultiByte(CP_ACP, 0, &uchar, 1, &achar, 1, NULL, NULL);
98 ret = achar;
99#else
100 if ( (record.Event.KeyEvent.uChar.AsciiChar & 0x80) == 0x80 ) {
101 char *s = &record.Event.KeyEvent.uChar.AsciiChar;
102 OemToCharBuff(s, s, 1);
103 }
104 ret = record.Event.KeyEvent.uChar.AsciiChar;
105#endif
106 if (state & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED)) {
107 switch (ret) {
108 case '\b': ret = KEYCODE_ALT_BACKSPACE; goto done;
109 case 'b': ret = KEYCODE_ALT_LEFT; goto done;
110 case 'd': ret = KEYCODE_ALT_D; goto done;
111 case 'f': ret = KEYCODE_ALT_RIGHT; goto done;
112 }
113 }
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/timegm.c b/win32/timegm.c
new file mode 100644
index 000000000..8bfa041c4
--- /dev/null
+++ b/win32/timegm.c
@@ -0,0 +1,134 @@
1/*
2 timegm from musl (https://www.musl-libc.org/).
3
4 MIT licensed:
5
6----------------------------------------------------------------------
7Copyright © 2005-2020 Rich Felker, et al.
8
9Permission is hereby granted, free of charge, to any person obtaining
10a copy of this software and associated documentation files (the
11"Software"), to deal in the Software without restriction, including
12without limitation the rights to use, copy, modify, merge, publish,
13distribute, sublicense, and/or sell copies of the Software, and to
14permit persons to whom the Software is furnished to do so, subject to
15the following conditions:
16
17The above copyright notice and this permission notice shall be
18included in all copies or substantial portions of the Software.
19
20THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
27----------------------------------------------------------------------
28*/
29#include "libbb.h"
30
31static long long __year_to_secs(long long year, int *is_leap)
32{
33 int cycles, centuries, leaps, rem;
34
35 if (year-2ULL <= 136) {
36 int y = year;
37 leaps = (y-68)>>2;
38 if (!((y-68)&3)) {
39 leaps--;
40 if (is_leap) *is_leap = 1;
41 } else if (is_leap) *is_leap = 0;
42 return 31536000*(y-70) + 86400*leaps;
43 }
44
45 if (!is_leap) is_leap = &(int){0};
46 cycles = (year-100) / 400;
47 rem = (year-100) % 400;
48 if (rem < 0) {
49 cycles--;
50 rem += 400;
51 }
52 if (!rem) {
53 *is_leap = 1;
54 centuries = 0;
55 leaps = 0;
56 } else {
57 if (rem >= 200) {
58 if (rem >= 300) centuries = 3, rem -= 300;
59 else centuries = 2, rem -= 200;
60 } else {
61 if (rem >= 100) centuries = 1, rem -= 100;
62 else centuries = 0;
63 }
64 if (!rem) {
65 *is_leap = 0;
66 leaps = 0;
67 } else {
68 leaps = rem / 4U;
69 rem %= 4U;
70 *is_leap = !rem;
71 }
72 }
73
74 leaps += 97*cycles + 24*centuries - *is_leap;
75
76 return (year-100) * 31536000LL + leaps * 86400LL + 946684800 + 86400;
77}
78
79static int __month_to_secs(int month, int is_leap)
80{
81 static const int secs_through_month[] = {
82 0, 31*86400, 59*86400, 90*86400,
83 120*86400, 151*86400, 181*86400, 212*86400,
84 243*86400, 273*86400, 304*86400, 334*86400 };
85 int t = secs_through_month[month];
86 if (is_leap && month >= 2) t+=86400;
87 return t;
88}
89
90static long long __tm_to_secs(const struct tm *tm)
91{
92 int is_leap;
93 long long t;
94 long long year = tm->tm_year;
95 int month = tm->tm_mon;
96 if (month >= 12 || month < 0) {
97 int adj = month / 12;
98 month %= 12;
99 if (month < 0) {
100 adj--;
101 month += 12;
102 }
103 year += adj;
104 }
105 t = __year_to_secs(year, &is_leap);
106 t += __month_to_secs(month, is_leap);
107 t += 86400LL * (tm->tm_mday-1);
108 t += 3600LL * tm->tm_hour;
109 t += 60LL * tm->tm_min;
110 t += tm->tm_sec;
111 return t;
112}
113
114/*
115 * Restricted version of timegm:
116 *
117 * it doesn't normalise its argument
118 * its return value is limited to the range Microsoft supports
119 */
120time_t timegm(struct tm *tm)
121{
122 long long t = __tm_to_secs(tm);
123 if (t < 0 ||
124#ifdef _USE_32BIT_TIME_T
125 t > INT_MAX /* 2038-01-19 03:14:07Z */
126#else
127 t > 32535215999 /* 3000-12-31 23:59:59Z */
128#endif
129 ) {
130 errno = EOVERFLOW;
131 return -1;
132 }
133 return t;
134}
diff --git a/win32/uname.c b/win32/uname.c
new file mode 100644
index 000000000..357a6fc64
--- /dev/null
+++ b/win32/uname.c
@@ -0,0 +1,42 @@
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 GetVersionEx(&os_info);
21 sprintf(name->release, "%u.%u", (unsigned int)os_info.dwMajorVersion,
22 (unsigned int)os_info.dwMinorVersion);
23 sprintf(name->version, "%u", (unsigned int)os_info.dwBuildNumber);
24
25 GetSystemInfo(&sys_info);
26 switch (sys_info.wProcessorArchitecture) {
27 case PROCESSOR_ARCHITECTURE_AMD64:
28 strcpy(name->machine, "x86_64");
29 break;
30 case PROCESSOR_ARCHITECTURE_INTEL:
31 strcpy(name->machine, "i686");
32 if (sys_info.wProcessorLevel < 6) {
33 name->machine[1] = '3';
34 }
35 break;
36 default:
37 strcpy(name->machine, unk);
38 break;
39 }
40
41 return 0;
42}
diff --git a/win32/winansi.c b/win32/winansi.c
new file mode 100644
index 000000000..622ba1c77
--- /dev/null
+++ b/win32/winansi.c
@@ -0,0 +1,1121 @@
1/*
2 * Copyright 2008 Peter Harris <git@peter.is-a-geek.org>
3 */
4
5#include "libbb.h"
6#include <windows.h>
7#include "lazyload.h"
8#undef PACKED
9
10/*
11 Functions to be wrapped:
12*/
13#undef vfprintf
14#undef vprintf
15#undef printf
16#undef fprintf
17#undef fputs
18#undef fputc
19#undef putchar
20#undef fwrite
21#undef puts
22#undef write
23#undef read
24#undef getc
25
26#define FOREGROUND_ALL (FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE)
27#define BACKGROUND_ALL (BACKGROUND_RED | BACKGROUND_GREEN | BACKGROUND_BLUE)
28
29static WORD plain_attr = 0xffff;
30static WORD current_attr;
31
32static HANDLE get_console(void)
33{
34 return GetStdHandle(STD_OUTPUT_HANDLE);
35}
36
37static WORD get_console_attr(void)
38{
39 CONSOLE_SCREEN_BUFFER_INFO sbi;
40
41 if (GetConsoleScreenBufferInfo(get_console(), &sbi))
42 return sbi.wAttributes;
43
44 return FOREGROUND_ALL;
45}
46
47static int is_console(int fd)
48{
49 if (plain_attr == 0xffff)
50 current_attr = plain_attr = get_console_attr();
51 return isatty(fd) && get_console() != INVALID_HANDLE_VALUE;
52}
53
54static int is_console_in(int fd)
55{
56 return isatty(fd) && GetStdHandle(STD_INPUT_HANDLE) != INVALID_HANDLE_VALUE;
57}
58
59static int is_wine(void)
60{
61 DECLARE_PROC_ADDR(const char *, wine_get_version, void);
62
63 return INIT_PROC_ADDR(ntdll.dll, wine_get_version) != NULL;
64}
65
66#ifndef ENABLE_VIRTUAL_TERMINAL_PROCESSING
67#define ENABLE_VIRTUAL_TERMINAL_PROCESSING 0x0004
68#endif
69
70#ifndef DISABLE_NEWLINE_AUTO_RETURN
71#define DISABLE_NEWLINE_AUTO_RETURN 0x0008
72#endif
73
74int skip_ansi_emulation(int reset)
75{
76 static int skip = -1;
77
78 if (skip < 0 || reset) {
79 const char *var = getenv(bb_skip_ansi_emulation);
80 int dflt = is_wine() ? 0 : CONFIG_SKIP_ANSI_EMULATION_DEFAULT;
81 skip = var == NULL ? dflt : atoi(var);
82 if (skip < 0 || skip > 2)
83 skip = 0;
84
85 if (is_console(STDOUT_FILENO)) {
86 HANDLE h = get_console();
87 DWORD mode;
88
89 if (GetConsoleMode(h, &mode)) {
90 if (skip)
91 mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
92 else
93 mode &= ~ENABLE_VIRTUAL_TERMINAL_PROCESSING;
94 mode &= ~DISABLE_NEWLINE_AUTO_RETURN;
95 if (!SetConsoleMode(h, mode) && skip == 2)
96 skip = 0;
97 }
98 }
99 }
100
101 return skip;
102}
103
104void set_title(const char *str)
105{
106 if (is_console(STDOUT_FILENO))
107 SetConsoleTitle(str);
108}
109
110static HANDLE dup_handle(HANDLE h)
111{
112 HANDLE h2;
113
114 if (!DuplicateHandle(GetCurrentProcess(), h, GetCurrentProcess(),
115 &h2, 0, TRUE, DUPLICATE_SAME_ACCESS))
116 return INVALID_HANDLE_VALUE;
117 return h2;
118}
119
120static void use_alt_buffer(int flag)
121{
122 static HANDLE console_orig = INVALID_HANDLE_VALUE;
123 const char *var;
124 HANDLE console, h;
125
126 var = getenv("BB_ALT_BUFFER");
127 if (var ? strcmp(var, "0") == 0 : is_wine()) {
128 reset_screen();
129 return;
130 }
131
132 if (flag) {
133 SECURITY_ATTRIBUTES sa;
134 CONSOLE_SCREEN_BUFFER_INFO sbi;
135
136 if (console_orig != INVALID_HANDLE_VALUE)
137 return;
138
139 console = get_console();
140 console_orig = dup_handle(console);
141
142 // handle should be inheritable
143 memset(&sa, 0, sizeof(sa));
144 sa.nLength = sizeof(sa);
145 /* sa.lpSecurityDescriptor = NULL; - memset did it */
146 sa.bInheritHandle = TRUE;
147
148 // create new alternate buffer
149 h = CreateConsoleScreenBuffer(GENERIC_READ|GENERIC_WRITE,
150 FILE_SHARE_READ|FILE_SHARE_WRITE, &sa,
151 CONSOLE_TEXTMODE_BUFFER, NULL);
152 if (h == INVALID_HANDLE_VALUE)
153 return;
154
155 if (GetConsoleScreenBufferInfo(console, &sbi))
156 SetConsoleScreenBufferSize(h, sbi.dwSize);
157 }
158 else {
159 if (console_orig == INVALID_HANDLE_VALUE)
160 return;
161
162 // revert to original buffer
163 h = dup_handle(console_orig);
164 console_orig = INVALID_HANDLE_VALUE;
165 if (h == INVALID_HANDLE_VALUE)
166 return;
167 }
168
169 console = h;
170 SetConsoleActiveScreenBuffer(console);
171 close(STDOUT_FILENO);
172 _open_osfhandle((intptr_t)console, O_RDWR|O_BINARY);
173}
174
175static void clear_buffer(DWORD len, COORD pos)
176{
177 HANDLE console = get_console();
178 DWORD dummy;
179
180 FillConsoleOutputCharacterA(console, ' ', len, pos, &dummy);
181 FillConsoleOutputAttribute(console, plain_attr, len, pos, &dummy);
182}
183
184static void erase_in_line(void)
185{
186 HANDLE console = get_console();
187 CONSOLE_SCREEN_BUFFER_INFO sbi;
188
189 if (!GetConsoleScreenBufferInfo(console, &sbi))
190 return;
191 clear_buffer(sbi.dwSize.X - sbi.dwCursorPosition.X, sbi.dwCursorPosition);
192}
193
194static void erase_till_end_of_screen(void)
195{
196 HANDLE console = get_console();
197 CONSOLE_SCREEN_BUFFER_INFO sbi;
198 DWORD len;
199
200 if(!GetConsoleScreenBufferInfo(console, &sbi))
201 return;
202 len = sbi.dwSize.X - sbi.dwCursorPosition.X +
203 sbi.dwSize.X * (sbi.srWindow.Bottom - sbi.dwCursorPosition.Y);
204 clear_buffer(len, sbi.dwCursorPosition);
205}
206
207void reset_screen(void)
208{
209 HANDLE console = get_console();
210 CONSOLE_SCREEN_BUFFER_INFO sbi;
211 COORD pos = { 0, 0 };
212
213 /* move to start of screen buffer and clear it all */
214 if (!GetConsoleScreenBufferInfo(console, &sbi))
215 return;
216 SetConsoleCursorPosition(console, pos);
217 clear_buffer(sbi.dwSize.X * sbi.dwSize.Y, pos);
218}
219
220void move_cursor_row(int n)
221{
222 HANDLE console = get_console();
223 CONSOLE_SCREEN_BUFFER_INFO sbi;
224
225 if(!GetConsoleScreenBufferInfo(console, &sbi))
226 return;
227 sbi.dwCursorPosition.Y += n;
228 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
229}
230
231static void move_cursor_column(int n)
232{
233 HANDLE console = get_console();
234 CONSOLE_SCREEN_BUFFER_INFO sbi;
235
236 if (!GetConsoleScreenBufferInfo(console, &sbi))
237 return;
238 sbi.dwCursorPosition.X += n;
239 SetConsoleCursorPosition(console, sbi.dwCursorPosition);
240}
241
242static void move_cursor(int x, int y)
243{
244 HANDLE console = get_console();
245 COORD pos;
246 CONSOLE_SCREEN_BUFFER_INFO sbi;
247
248 if (!GetConsoleScreenBufferInfo(console, &sbi))
249 return;
250 pos.X = sbi.srWindow.Left + x;
251 pos.Y = sbi.srWindow.Top + y;
252 SetConsoleCursorPosition(console, pos);
253}
254
255static const unsigned char colour_1bit[16] = {
256 /* Black */ 0,
257 /* Red */ FOREGROUND_RED,
258 /* Green */ FOREGROUND_GREEN,
259 /* Yellow */ FOREGROUND_RED | FOREGROUND_GREEN,
260 /* Blue */ FOREGROUND_BLUE,
261 /* Magenta */ FOREGROUND_RED | FOREGROUND_BLUE,
262 /* Cyan */ FOREGROUND_GREEN | FOREGROUND_BLUE,
263 /* White */ FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE,
264 /* ... and again but brighter */
265 FOREGROUND_INTENSITY,
266 FOREGROUND_RED | FOREGROUND_INTENSITY,
267 FOREGROUND_GREEN | FOREGROUND_INTENSITY,
268 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY,
269 FOREGROUND_BLUE | FOREGROUND_INTENSITY,
270 FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
271 FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY,
272 FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
273};
274
275#if !ENABLE_FEATURE_IMPROVED_COLOUR_MAPPING
276static WORD rgb_to_console(int *rgb)
277{
278 int dark = 0, bright;
279 WORD attr = 0;
280
281 if (rgb[0] > 85)
282 attr |= FOREGROUND_RED;
283 else
284 ++dark;
285
286 if (rgb[1] > 85)
287 attr |= FOREGROUND_GREEN;
288 else
289 ++dark;
290
291 if (rgb[2] > 85)
292 attr |= FOREGROUND_BLUE;
293 else
294 ++dark;
295
296 /* increase intensity if all components are either bright or
297 * dark and at least one is bright */
298 bright = (rgb[0] > 171) + (rgb[1] > 171) + (rgb[2] > 171);
299 if (bright + dark == 3 && dark != 3) {
300 attr |= FOREGROUND_INTENSITY;
301 }
302
303 return attr;
304}
305#else
306#include <math.h>
307
308/* Standard console colours in LAB colour space */
309static float colour_lab[16][3] = {
310 {-0.000000, 0.000000, 0.000000},
311 {25.530788, 48.055233, 38.059635},
312 {46.228817, -51.699638, 49.897949},
313 {51.868336, -12.930751, 56.677288},
314 {12.975313, 47.507763, -64.704285},
315 {29.782101, 58.939846, -36.497940},
316 {48.256081, -28.841570, -8.481050},
317 {77.704361, 0.004262, -0.008416},
318 {53.585018, 0.003129, -0.006235},
319 {53.232883, 80.109299, 67.220078},
320 {87.737038, -86.184654, 83.181168},
321 {97.138245, -21.555901, 94.482483},
322 {32.302586, 79.196678, -107.863686},
323 {60.319931, 98.254234, -60.842991},
324 {91.116524, -48.079609, -14.138126},
325 {100.000000, 0.005245, -0.010419},
326};
327
328/* Convert RGB to XYZ and XYZ to LAB. See:
329 * http://www.easyrgb.com/en/math.php#text1 */
330static void rgb2lab(const int *rgb, float *lab)
331{
332 float var_RGB[3], var_XYZ[3];
333 int i;
334
335 for (i = 0; i < 3; ++i) {
336 var_RGB[i] = rgb[i]/255.0f;
337 if (var_RGB[i] > 0.04045f)
338 var_RGB[i] = pow((var_RGB[i] + 0.055f) / 1.055f, 2.4f);
339 else
340 var_RGB[i] /= 12.92f;
341 }
342
343 /* use equal energy reference values */
344 var_XYZ[0] = var_RGB[0]*0.4124f + var_RGB[1]*0.3576f + var_RGB[2]*0.1805f;
345 var_XYZ[1] = var_RGB[0]*0.2126f + var_RGB[1]*0.7152f + var_RGB[2]*0.0722f;
346 var_XYZ[2] = var_RGB[0]*0.0193f + var_RGB[1]*0.1192f + var_RGB[2]*0.9505f;
347
348 for (i = 0; i < 3; ++i) {
349 if (var_XYZ[i] > 0.008856f)
350 var_XYZ[i] = pow(var_XYZ[i], 1.0f / 3.0f);
351 else
352 var_XYZ[i] = 7.787f * var_XYZ[i] + 16.0f / 116.0f;
353 }
354
355 lab[0] = 116.0f * var_XYZ[1] - 16.0f;
356 lab[1] = 500.0f * (var_XYZ[0] - var_XYZ[1]);
357 lab[2] = 200.0f * (var_XYZ[1] - var_XYZ[2]);
358}
359
360static WORD rgb_to_console(int *rgb)
361{
362 int i, imin = 0;
363 float deltamin = 1.0e20;
364
365 /* Use 1976 CIE deltaE to find closest console colour. See:
366 * https://zschuessler.github.io/DeltaE/learn */
367 for (i = 0; i < 16; ++i) {
368 float lab[3], dl, da, db, delta;
369
370 rgb2lab(rgb, lab);
371 dl = colour_lab[i][0] - lab[0];
372 da = colour_lab[i][1] - lab[1];
373 db = colour_lab[i][2] - lab[2];
374 delta = dl * dl + da * da + db *db;
375 if (delta < deltamin) {
376 imin = i;
377 deltamin = delta;
378 }
379 }
380 return colour_1bit[imin];
381}
382#endif
383
384/* 24-bit colour */
385static char *process_24bit(char *str, WORD *attr)
386{
387 int count;
388 int rgb[3];
389
390 for (count = 0; count < 3; ++count) {
391 rgb[count] = strtol(str, (char **)&str, 10);
392 if (*str == ';')
393 ++str;
394 }
395
396 *attr = rgb_to_console(rgb);
397
398 return *(str - 1) == ';' ? str - 1 : str;
399}
400
401/* 8-bit colour */
402static char *process_8bit(char *str, WORD *attr)
403{
404 int val = strtol(str, &str, 10);
405
406 if (val < 16) {
407 *attr = colour_1bit[val];
408 }
409 else if (val < 232) {
410 int i, rgb[3];
411
412 val -= 16;
413 for (i = 2; i >= 0; --i) {
414 rgb[i] = (val % 6) * 42 + 21;
415 val /= 6;
416 }
417
418 *attr = rgb_to_console(rgb);
419 }
420 else if (val < 238) {
421 /* black */
422 *attr = 0;
423 }
424 else if (val < 244) {
425 /* bright black */
426 *attr = FOREGROUND_INTENSITY;
427 }
428 else if (val < 250) {
429 /* white */
430 *attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
431 }
432 else if (val < 256) {
433 /* bright white */
434 *attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE |
435 FOREGROUND_INTENSITY;
436 }
437
438 return str;
439}
440
441static char *process_colour(char *str, WORD *attr)
442{
443 long val = strtol(str, (char **)&str, 10);
444
445 *attr = -1; /* error return */
446 switch (val) {
447 case 2:
448 str = process_24bit(str + 1, attr);
449 break;
450 case 5:
451 str = process_8bit(str + 1, attr);
452 break;
453 default:
454 break;
455 }
456
457 return str;
458}
459
460/* On input pos points to the start of a suspected escape sequence.
461 * If a valid sequence is found return a pointer to the character
462 * following it, otherwise return the original pointer. */
463static char *process_escape(char *pos)
464{
465 char *str, *func;
466 char *bel;
467 size_t len;
468 WORD t, attr = current_attr;
469 static int reverse = 0;
470
471 switch (pos[1]) {
472 case '[':
473 /* go ahead and process "\033[" sequence */
474 break;
475 case ']':
476 if ((pos[2] == '0' || pos[2] == '2') && pos[3] == ';' &&
477 (bel=strchr(pos+4, '\007')) && bel - pos < 260) {
478 /* set console title */
479 *bel++ = '\0';
480 CharToOem(pos+4, pos+4);
481 SetConsoleTitle(pos+4);
482 return bel;
483 }
484 /* invalid "\033]" sequence, fall through */
485 default:
486 return pos;
487 }
488
489 str = pos + 2;
490 len = strspn(str, "0123456789;");
491 func = str + len;
492 switch (*func) {
493 case 'm':
494 do {
495 long val = strtol(str, (char **)&str, 10);
496 switch (val) {
497 case 0: /* reset */
498 attr = plain_attr;
499 reverse = 0;
500 break;
501 case 1: /* bold */
502 attr |= FOREGROUND_INTENSITY;
503 break;
504 case 2: /* faint */
505 case 22: /* normal */
506 attr &= ~FOREGROUND_INTENSITY;
507 break;
508 case 3: /* italic */
509 /* Unsupported */
510 break;
511 case 4: /* underline */
512 case 21: /* double underline */
513 /* Wikipedia says this flag does nothing */
514 /* Furthermore, mingw doesn't define this flag
515 attr |= COMMON_LVB_UNDERSCORE; */
516 break;
517 case 24: /* no underline */
518 /* attr &= ~COMMON_LVB_UNDERSCORE; */
519 break;
520 case 5: /* slow blink */
521 case 6: /* fast blink */
522 /* We don't have blink, but we do have
523 background intensity */
524 attr |= BACKGROUND_INTENSITY;
525 break;
526 case 25: /* no blink */
527 attr &= ~BACKGROUND_INTENSITY;
528 break;
529 case 7: /* reverse video on */
530 reverse = 1;
531 break;
532 case 27: /* reverse video off */
533 reverse = 0;
534 break;
535 case 8: /* conceal */
536 case 9: /* strike through */
537 case 28: /* reveal */
538 /* Unsupported */
539 break;
540
541 /* Foreground colours */
542 case 30: /* Black */
543 case 31: /* Red */
544 case 32: /* Green */
545 case 33: /* Yellow */
546 case 34: /* Blue */
547 case 35: /* Magenta */
548 case 36: /* Cyan */
549 case 37: /* White */
550 attr &= ~FOREGROUND_ALL;
551 attr |= colour_1bit[val - 30];
552 break;
553 case 38: /* 8/24 bit */
554 str = process_colour(str + 1, &t);
555 if (t != -1) {
556 attr &= ~(FOREGROUND_ALL|FOREGROUND_INTENSITY);
557 attr |= t;
558 }
559 break;
560 case 39: /* reset */
561 attr &= ~FOREGROUND_ALL;
562 attr |= (plain_attr & FOREGROUND_ALL);
563 break;
564
565 /* Background colours */
566 case 40: /* Black */
567 case 41: /* Red */
568 case 42: /* Green */
569 case 43: /* Yellow */
570 case 44: /* Blue */
571 case 45: /* Magenta */
572 case 46: /* Cyan */
573 case 47: /* White */
574 attr &= ~BACKGROUND_ALL;
575 attr |= colour_1bit[val - 40] << 4;
576 break;
577 case 48: /* 8/24 bit */
578 str = process_colour(str + 1, &t);
579 if (t != -1) {
580 attr &= ~(BACKGROUND_ALL|BACKGROUND_INTENSITY);
581 attr |= t << 4;
582 }
583 break;
584 case 49: /* reset */
585 attr &= ~BACKGROUND_ALL;
586 attr |= (plain_attr & BACKGROUND_ALL);
587 break;
588
589 default:
590 /* Unsupported code */
591 return pos;
592 }
593 str++;
594 } while (str < func);
595
596 current_attr = attr;
597 if (reverse)
598 attr = ((attr >> 4) & 0xf) | ((attr << 4) & 0xf0);
599 SetConsoleTextAttribute(get_console(), attr);
600 break;
601 case 'A': /* up */
602 move_cursor_row(-strtol(str, (char **)&str, 10));
603 break;
604 case 'B': /* down */
605 move_cursor_row(strtol(str, (char **)&str, 10));
606 break;
607 case 'C': /* forward */
608 move_cursor_column(strtol(str, (char **)&str, 10));
609 break;
610 case 'D': /* back */
611 move_cursor_column(-strtol(str, (char **)&str, 10));
612 break;
613 case 'H':
614 if (!len)
615 move_cursor(0, 0);
616 else {
617 int row, col = 1;
618
619 row = strtol(str, (char **)&str, 10);
620 if (*str == ';') {
621 col = strtol(str+1, (char **)&str, 10);
622 }
623 move_cursor(col > 0 ? col-1 : 0, row > 0 ? row-1 : 0);
624 }
625 break;
626 case 'J':
627 erase_till_end_of_screen();
628 break;
629 case 'K':
630 erase_in_line();
631 break;
632 case '?':
633 if (strncmp(str+1, "1049", 4) == 0 &&
634 (str[5] == 'h' || str[5] == 'l') ) {
635 use_alt_buffer(str[5] == 'h');
636 func = str + 5;
637 break;
638 }
639 /* fall through */
640 default:
641 /* Unsupported code */
642 return pos;
643 }
644
645 return (char *)func + 1;
646}
647
648#if ENABLE_FEATURE_EURO
649void init_codepage(void)
650{
651 if (GetConsoleCP() == 850 && GetConsoleOutputCP() == 850) {
652 SetConsoleCP(858);
653 SetConsoleOutputCP(858);
654 }
655}
656
657static BOOL winansi_CharToOemBuff(LPCSTR s, LPSTR d, DWORD len)
658{
659 WCHAR *buf;
660 int i;
661
662 if (!s || !d)
663 return FALSE;
664
665 buf = xmalloc(len*sizeof(WCHAR));
666 MultiByteToWideChar(CP_ACP, 0, s, len, buf, len);
667 WideCharToMultiByte(CP_OEMCP, 0, buf, len, d, len, NULL, NULL);
668 if (GetConsoleOutputCP() == 858) {
669 for (i=0; i<len; ++i) {
670 if (buf[i] == 0x20ac) {
671 d[i] = 0xd5;
672 }
673 }
674 }
675 free(buf);
676 return TRUE;
677}
678
679static BOOL winansi_CharToOem(LPCSTR s, LPSTR d)
680{
681 if (!s || !d)
682 return FALSE;
683 return winansi_CharToOemBuff(s, d, strlen(s)+1);
684}
685
686static BOOL winansi_OemToCharBuff(LPCSTR s, LPSTR d, DWORD len)
687{
688 WCHAR *buf;
689 int i;
690
691 if (!s || !d)
692 return FALSE;
693
694 buf = xmalloc(len*sizeof(WCHAR));
695 MultiByteToWideChar(CP_OEMCP, 0, s, len, buf, len);
696 WideCharToMultiByte(CP_ACP, 0, buf, len, d, len, NULL, NULL);
697 if (GetConsoleOutputCP() == 858) {
698 for (i=0; i<len; ++i) {
699 if (buf[i] == 0x0131) {
700 d[i] = 0x80;
701 }
702 }
703 }
704 free(buf);
705 return TRUE;
706}
707
708# undef CharToOemBuff
709# undef CharToOem
710# undef OemToCharBuff
711# define CharToOemBuff winansi_CharToOemBuff
712# define CharToOem winansi_CharToOem
713# define OemToCharBuff winansi_OemToCharBuff
714#endif
715
716static int ansi_emulate(const char *s, FILE *stream)
717{
718 int rv = 0;
719 const unsigned char *t;
720 char *pos, *str;
721 size_t cur_len;
722 static size_t max_len = 0;
723 static char *mem = NULL;
724
725 /* if no special treatment is required output the string as-is */
726 for ( t=(unsigned char *)s; *t; ++t ) {
727 if ( *t == '\033' || *t > 0x7f ) {
728 break;
729 }
730 }
731
732 if ( *t == '\0' ) {
733 return fputs(s, stream) == EOF ? EOF : strlen(s);
734 }
735
736 /*
737 * Make a writable copy of the string and retain array for reuse.
738 * The test above guarantees that the string length won't be zero
739 * so the array will always be allocated.
740 */
741 cur_len = strlen(s);
742 if ( cur_len > max_len ) {
743 free(mem);
744 mem = xstrdup(s);
745 max_len = cur_len;
746 }
747 else {
748 strcpy(mem, s);
749 }
750 pos = str = mem;
751
752 while (*pos) {
753 pos = strchr(str, '\033');
754 if (pos && !skip_ansi_emulation(FALSE)) {
755 size_t len = pos - str;
756
757 if (len) {
758 *pos = '\0'; /* NB, '\033' has been overwritten */
759 CharToOem(str, str);
760 if (fputs(str, stream) == EOF)
761 return EOF;
762 rv += len;
763 }
764
765 if (fflush(stream) == EOF)
766 return EOF;
767
768 str = process_escape(pos);
769 if (str == pos) {
770 if (fputc('\033', stream) == EOF)
771 return EOF;
772 ++str;
773 }
774 rv += str - pos;
775 pos = str;
776
777 if (fflush(stream) == EOF)
778 return EOF;
779
780 } else {
781 rv += strlen(str);
782 CharToOem(str, str);
783 return fputs(str, stream) == EOF ? EOF : rv;
784 }
785 }
786 return rv;
787}
788
789int winansi_putchar(int c)
790{
791 char t = c;
792 char *s = &t;
793
794 if (!is_console(STDOUT_FILENO))
795 return putchar(c);
796
797 CharToOemBuff(s, s, 1);
798 return putchar(t) == EOF ? EOF : (unsigned char)c;
799}
800
801int winansi_puts(const char *s)
802{
803 return (winansi_fputs(s, stdout) == EOF || putchar('\n') == EOF) ? EOF : 0;
804}
805
806static sighandler_t sigpipe_handler = SIG_DFL;
807
808#undef signal
809sighandler_t winansi_signal(int signum, sighandler_t handler)
810{
811 sighandler_t old;
812
813 if (signum == SIGPIPE) {
814 old = sigpipe_handler;
815 sigpipe_handler = handler;
816 return old;
817 }
818 return signal(signum, handler);
819}
820
821static void check_pipe_fd(int fd)
822{
823 int error = GetLastError();
824
825 if ((error == ERROR_NO_DATA &&
826 GetFileType((HANDLE)_get_osfhandle(fd)) == FILE_TYPE_PIPE) ||
827 error == ERROR_BROKEN_PIPE) {
828 if (sigpipe_handler == SIG_DFL)
829 exit(128+SIGPIPE);
830 else /* SIG_IGN */
831 errno = EPIPE;
832 }
833}
834
835static void check_pipe(FILE *stream)
836{
837 int fd = fileno(stream);
838
839 if (fd != -1 && ferror(stream)) {
840 check_pipe_fd(fd);
841 }
842}
843
844size_t winansi_fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream)
845{
846 size_t lsize, lmemb, ret;
847 char *str;
848 int rv;
849
850 lsize = MIN(size, nmemb);
851 lmemb = MAX(size, nmemb);
852 if (lsize != 1 || !is_console(fileno(stream))) {
853 SetLastError(0);
854 if ((ret=fwrite(ptr, size, nmemb, stream)) < nmemb)
855 check_pipe(stream);
856 return ret;
857 }
858
859 str = xmalloc(lmemb+1);
860 memcpy(str, ptr, lmemb);
861 str[lmemb] = '\0';
862
863 rv = ansi_emulate(str, stream);
864 free(str);
865
866 return rv == EOF ? 0 : nmemb;
867}
868
869int winansi_fputs(const char *str, FILE *stream)
870{
871 int ret;
872
873 if (!is_console(fileno(stream))) {
874 SetLastError(0);
875 if ((ret=fputs(str, stream)) == EOF)
876 check_pipe(stream);
877 return ret;
878 }
879
880 return ansi_emulate(str, stream) == EOF ? EOF : 0;
881}
882
883int winansi_fputc(int c, FILE *stream)
884{
885 int ret;
886 char t = c;
887 char *s = &t;
888
889 if (!is_console(fileno(stream))) {
890 SetLastError(0);
891 if ((ret=fputc(c, stream)) == EOF)
892 check_pipe(stream);
893 return ret;
894 }
895
896 CharToOemBuff(s, s, 1);
897 return fputc(t, stream) == EOF ? EOF : (unsigned char )c;
898}
899
900#if !defined(__USE_MINGW_ANSI_STDIO) || !__USE_MINGW_ANSI_STDIO
901/*
902 * Prior to Windows 10 vsnprintf was incompatible with the C99 standard.
903 * Implement a replacement using _vsnprintf.
904 */
905int winansi_vsnprintf(char *buf, size_t size, const char *format, va_list list)
906{
907 size_t len;
908 va_list list2;
909
910 va_copy(list2, list);
911 len = _vsnprintf(NULL, 0, format, list2);
912 va_end(list2);
913 if (len < 0)
914 return -1;
915
916 _vsnprintf(buf, size, format, list);
917 buf[size-1] = '\0';
918 return len;
919}
920#endif
921
922int winansi_vfprintf(FILE *stream, const char *format, va_list list)
923{
924 int len, rv;
925 char small_buf[256];
926 char *buf = small_buf;
927 va_list cp;
928
929 if (!is_console(fileno(stream)))
930 goto abort;
931
932 va_copy(cp, list);
933 len = vsnprintf(small_buf, sizeof(small_buf), format, cp);
934 va_end(cp);
935
936 if (len > sizeof(small_buf) - 1) {
937 buf = xmalloc(len + 1);
938 va_copy(cp, list);
939 len = vsnprintf(buf, len + 1, format, cp);
940 va_end(cp);
941 }
942
943 if (len == -1)
944 goto abort;
945
946 rv = ansi_emulate(buf, stream);
947
948 if (buf != small_buf)
949 free(buf);
950 return rv;
951
952abort:
953 SetLastError(0);
954 if ((rv=vfprintf(stream, format, list)) == EOF || ferror(stream) != 0)
955 check_pipe(stream);
956 return rv;
957}
958
959int winansi_fprintf(FILE *stream, const char *format, ...)
960{
961 va_list list;
962 int rv;
963
964 va_start(list, format);
965 rv = winansi_vfprintf(stream, format, list);
966 va_end(list);
967
968 return rv;
969}
970
971int winansi_printf(const char *format, ...)
972{
973 va_list list;
974 int rv;
975
976 va_start(list, format);
977 rv = winansi_vfprintf(stdout, format, list);
978 va_end(list);
979
980 return rv;
981}
982
983static int ansi_emulate_write(int fd, const void *buf, size_t count)
984{
985 int rv = 0, i;
986 int special = FALSE, has_null = FALSE;
987 const unsigned char *s = (const unsigned char *)buf;
988 char *pos, *str;
989 size_t len, out_len;
990 static size_t max_len = 0;
991 static char *mem = NULL;
992
993 for ( i=0; i<count; ++i ) {
994 if ( s[i] == '\033' || s[i] > 0x7f ) {
995 special = TRUE;
996 }
997 else if ( !s[i] ) {
998 has_null = TRUE;
999 }
1000 }
1001
1002 /*
1003 * If no special treatment is required or the data contains NUL
1004 * characters output the string as-is.
1005 */
1006 if ( !special || has_null ) {
1007 return write(fd, buf, count);
1008 }
1009
1010 /* make a writable copy of the data and retain array for reuse */
1011 if ( count > max_len ) {
1012 free(mem);
1013 mem = malloc(count+1);
1014 max_len = count;
1015 }
1016 memcpy(mem, buf, count);
1017 mem[count] = '\0';
1018 pos = str = mem;
1019
1020 /* we've checked the data doesn't contain any NULs */
1021 while (*pos) {
1022 pos = strchr(str, '\033');
1023 if (pos && !skip_ansi_emulation(FALSE)) {
1024 len = pos - str;
1025
1026 if (len) {
1027 CharToOemBuff(str, str, len);
1028 out_len = write(fd, str, len);
1029 if (out_len == -1)
1030 return -1;
1031 rv += out_len;
1032 }
1033
1034 str = process_escape(pos);
1035 if (str == pos) {
1036 if (write(fd, pos, 1) == -1)
1037 return -1;
1038 ++str;
1039 }
1040 rv += str - pos;
1041 pos = str;
1042 } else {
1043 len = strlen(str);
1044 CharToOem(str, str);
1045 out_len = write(fd, str, len);
1046 return (out_len == -1) ? -1 : rv+out_len;
1047 }
1048 }
1049 return rv;
1050}
1051
1052int winansi_write(int fd, const void *buf, size_t count)
1053{
1054 if (!is_console(fd)) {
1055 int ret;
1056
1057 SetLastError(0);
1058 if ((ret=write(fd, buf, count)) == -1) {
1059 check_pipe_fd(fd);
1060 }
1061 return ret;
1062 }
1063
1064 return ansi_emulate_write(fd, buf, count);
1065}
1066
1067int winansi_read(int fd, void *buf, size_t count)
1068{
1069 int rv;
1070
1071 rv = mingw_read(fd, buf, count);
1072 if (!is_console_in(fd))
1073 return rv;
1074
1075 if ( rv > 0 ) {
1076 OemToCharBuff(buf, buf, rv);
1077 }
1078
1079 return rv;
1080}
1081
1082int winansi_getc(FILE *stream)
1083{
1084 int rv;
1085
1086 rv = getc(stream);
1087 if (!is_console_in(fileno(stream)))
1088 return rv;
1089
1090 if ( rv != EOF ) {
1091 unsigned char c = (unsigned char)rv;
1092 char *s = (char *)&c;
1093 OemToCharBuff(s, s, 1);
1094 rv = (int)c;
1095 }
1096
1097 return rv;
1098}
1099
1100/* Ensure that isatty(fd) returns 0 for the NUL device */
1101int mingw_isatty(int fd)
1102{
1103 int result = _isatty(fd);
1104
1105 if (result) {
1106 HANDLE handle = (HANDLE) _get_osfhandle(fd);
1107 DWORD mode;
1108
1109 if (handle == INVALID_HANDLE_VALUE)
1110 return 0;
1111
1112 /* check if its a device (i.e. console, printer, serial port) */
1113 if (GetFileType(handle) != FILE_TYPE_CHAR)
1114 return 0;
1115
1116 if (!GetConsoleMode(handle, &mode))
1117 return 0;
1118 }
1119
1120 return result;
1121}