summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--winsup/cygwin/Makefile.in392
-rwxr-xr-xwinsup/cygwin/configure2415
-rw-r--r--winsup/cygwin/configure.in249
-rw-r--r--winsup/cygwin/dcrt0.cc1141
-rw-r--r--winsup/cygwin/dtable.cc883
-rw-r--r--winsup/cygwin/dtable.h93
-rw-r--r--winsup/cygwin/fhandler.cc1329
-rw-r--r--winsup/cygwin/fhandler.h1250
-rw-r--r--winsup/cygwin/fhandler_clipboard.cc277
-rw-r--r--winsup/cygwin/fhandler_console.cc1754
-rw-r--r--winsup/cygwin/fhandler_disk_file.cc850
-rw-r--r--winsup/cygwin/fhandler_dsp.cc653
-rw-r--r--winsup/cygwin/fhandler_floppy.cc316
-rw-r--r--winsup/cygwin/fhandler_mem.cc439
-rw-r--r--winsup/cygwin/fhandler_proc.cc500
-rw-r--r--winsup/cygwin/fhandler_process.cc751
-rw-r--r--winsup/cygwin/fhandler_random.cc171
-rw-r--r--winsup/cygwin/fhandler_raw.cc568
-rw-r--r--winsup/cygwin/fhandler_registry.cc668
-rw-r--r--winsup/cygwin/fhandler_serial.cc1026
-rw-r--r--winsup/cygwin/fhandler_socket.cc1264
-rw-r--r--winsup/cygwin/fhandler_tape.cc839
-rw-r--r--winsup/cygwin/fhandler_tty.cc1261
-rw-r--r--winsup/cygwin/fhandler_virtual.cc235
-rw-r--r--winsup/cygwin/fhandler_windows.cc147
-rw-r--r--winsup/cygwin/fhandler_zero.cc55
-rw-r--r--winsup/cygwin/mmap.cc1046
-rw-r--r--winsup/cygwin/net.cc2277
-rw-r--r--winsup/cygwin/path.cc3588
-rw-r--r--winsup/cygwin/path.h211
-rw-r--r--winsup/cygwin/pinfo.cc718
-rw-r--r--winsup/cygwin/pinfo.h244
-rw-r--r--winsup/cygwin/pipe.cc289
-rw-r--r--winsup/cygwin/select.cc1505
-rw-r--r--winsup/cygwin/shared_info.h191
-rw-r--r--winsup/cygwin/smallprint.c243
-rw-r--r--winsup/cygwin/syscalls.cc2662
-rw-r--r--winsup/cygwin/tty.cc464
-rw-r--r--winsup/cygwin/winsup.h306
39 files changed, 33270 insertions, 0 deletions
diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
new file mode 100644
index 00000000000..ac2b254713e
--- /dev/null
+++ b/winsup/cygwin/Makefile.in
@@ -0,0 +1,392 @@
+# Makefile.in for Cygwin.
+# Copyright 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+#
+# This file is part of Cygwin.
+#
+# This software is a copyrighted work licensed under the terms of the
+# Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+# details.
+
+# This makefile requires GNU make.
+
+SHELL:=@SHELL@
+srcdir:=@srcdir@
+objdir:=.
+
+CONFIG_DIR:=$(srcdir)/config/@CONFIG_DIR@
+VPATH:=$(srcdir):$(CONFIG_DIR):$(srcdir)/regex:$(srcdir)/regexp:$(srcdir)/lib
+
+target_alias:=@target_alias@
+build_alias:=@build_alias@
+host_alias:=@host_alias@
+prefix:=@prefix@
+
+program_transform_name:=@program_transform_name@
+exec_prefix:=@exec_prefix@
+bindir:=@bindir@
+libdir:=@libdir@
+ifeq ($(target_alias),$(host_alias))
+ifeq ($(build_alias),$(host_alias))
+tooldir:=$(exec_prefix)
+else
+tooldir:=$(exec_prefix)/$(target_alias)
+endif
+else
+tooldir:=$(exec_prefix)/$(target_alias)
+endif
+datadir:=@datadir@
+infodir:=@infodir@
+includedir:=@includedir@
+
+INSTALL:=@INSTALL@
+INSTALL_PROGRAM:=@INSTALL_PROGRAM@
+
+#
+# --enable options from configure
+#
+MT_SAFE:=@MT_SAFE@
+DEFS:=@DEFS@
+
+# cygheap_CFLAGS:=-fomit-frame-pointer
+malloc_CFLAGS:=-fomit-frame-pointer
+shared_CFLAGS:=-fomit-frame-pointer
+cygthread_CFLAGS:=-fomit-frame-pointer
+miscfuncs_CFLAGS:=-fomit-frame-pointer
+fhandler_CFLAGS:=-fomit-frame_pointer
+fhandler_clipboard_CFLAGS:=-fomit-frame-pointer
+fhandler_console_CFLAGS:=-fomit-frame-pointer
+fhandler_disk_file_CFLAGS:=-fomit-frame-pointer
+fhandler_dsp_CFLAGS:=-fomit-frame-pointer
+fhandler_floppy_CFLAGS:=-fomit-frame-pointer
+fhandler_mem_CFLAGS:=-fomit-frame-pointer
+fhandler_proc_CFLAGS:=-fomit-frame-pointer
+fhandler_process_CFLAGS:=-fomit-frame-pointer
+fhandler_random_CFLAGS:=-fomit-frame-pointer
+fhandler_raw_CFLAGS:=-fomit-frame-pointer
+fhandler_registry_CFLAGS:=-fomit-frame-pointer
+fhandler_serial_CFLAGS:=-fomit-frame-pointer
+fhandler_socket_CFLAGS:=-fomit-frame-pointer
+fhandler_tape_CFLAGS:=-fomit-frame-pointer
+fhandler_termios_CFLAGS:=-fomit-frame-pointer
+fhandler_tty_CFLAGS:=-fomit-frame-pointer
+fhandler_virtual_CFLAGS:=-fomit-frame-pointer
+fhandler_windows_CFLAGS:=-fomit-frame-pointer
+fhandler_zero_CFLAGS:=-fomit-frame-pointer
+
+CC:=@CC@
+# FIXME: Which is it, CC or CC_FOR_TARGET?
+CC_FOR_TARGET:=$(CC)
+CFLAGS=@CFLAGS@
+override CFLAGS+=-MMD ${$(*F)_CFLAGS}
+CXX:=@CXX@
+CXXFLAGS=@CXXFLAGS@
+
+# For linking mount, etc. crt0.o isn't accessable in a fresh build.
+EXE_LDFLAGS:=@EXE_LDFLAGS@
+
+AR:=@AR@
+AR_FLAGS:=qv
+RANLIB:=@RANLIB@
+LD:=@LD@
+DLLTOOL:=@DLLTOOL@
+WINDRES:=@WINDRES@
+AS:=@AS@
+NM:=@NM@
+LDSCRIPT:=cygwin.sc
+
+#
+# Include common definitions for winsup directory
+#
+include $(srcdir)/../Makefile.common
+
+INSTALL_DATA:=$(SHELL) $(updir1)/install-sh -c
+
+@SET_MAKE@
+
+# Setup the testing framework, if you have one
+EXPECT = `if [ -f $${rootme}/../../expect/expect$(EXEEXT) ] ; then \
+ echo $${rootme}/../../expect/expect$(EXEEXT) ; \
+ else echo expect ; fi`
+
+RUNTEST = `if [ -f $${srcdir}/../dejagnu/runtest ] ; then \
+ echo $${srcdir}/../dejagnu/runtest ; \
+ else echo runtest; fi`
+RUNTESTFLAGS =
+
+# Parameters used in building the cygwin.dll.
+# We build as new-cygwin.dll and rename at install time to overcome
+# native rebuilding issues (we don't want the build tools to see a partially
+# built cygwin.dll and attempt to use it instead of the old one).
+
+DLL_NAME:=cygwin1.dll
+LIB_NAME:=libcygwin.a
+DEF_FILE:=cygwin.def
+DLL_ENTRY:=@DLL_ENTRY@
+
+LIBGMON_A:=libgmon.a
+GMON_START:=gcrt0.o
+
+# Some things want these from libc, but they have their own static
+# data which apps can get to, which is a pain in the dll, so we
+# include them directly into the library.
+
+LIBCOS:=${sort ${addsuffix .o,${basename ${notdir ${wildcard $(srcdir)/lib/*.c}}}} \
+ ${addsuffix .o,${basename ${notdir ${wildcard $(srcdir)/lib/*.cc}}}}}
+
+# Build all source files in the config directory
+
+EXTRA_DLL_OFILES:=${addsuffix .o,${basename ${notdir ${wildcard $(CONFIG_DIR)/*.c}}}}
+
+EXTRA_OFILES=$(bupdir1)/libiberty/random.o $(bupdir1)/libiberty/strsignal.o
+
+MALLOC_OFILES=@MALLOC_OFILES@
+
+DLL_IMPORTS:=$(w32api_lib)/libkernel32.a
+
+MT_SAFE_OBJECTS:=
+# Please maintain this list in sorted order, with maximum files per 80 col line
+DLL_OFILES:=assert.o autoload.o cxx.o cygheap.o cygserver_client.o \
+ cygserver_transport.o cygserver_transport_pipes.o \
+ cygserver_transport_sockets.o cygthread.o dcrt0.o debug.o \
+ delqueue.o devices.o dir.o dlfcn.o dll_init.o dtable.o environ.o errno.o \
+ exceptions.o exec.o external.o fcntl.o fhandler.o \
+ fhandler_clipboard.o fhandler_console.o fhandler_disk_file.o \
+ fhandler_dsp.o fhandler_fifo.o fhandler_floppy.o fhandler_mem.o \
+ fhandler_proc.o fhandler_process.o fhandler_random.o \
+ fhandler_raw.o fhandler_registry.o fhandler_serial.o \
+ fhandler_socket.o fhandler_tape.o fhandler_termios.o \
+ fhandler_tty.o fhandler_virtual.o fhandler_windows.o \
+ fhandler_zero.o fnmatch.o fork.o glob.o grp.o heap.o init.o ioctl.o \
+ ipc.o localtime.o malloc.o malloc_wrapper.o miscfuncs.o mmap.o msg.o \
+ net.o netdb.o ntea.o passwd.o path.o pinfo.o pipe.o poll.o pthread.o \
+ regcomp.o regerror.o regexec.o regfree.o registry.o resource.o \
+ scandir.o sched.o sec_acl.o sec_helper.o security.o select.o sem.o \
+ shared.o shm.o signal.o sigproc.o smallprint.o spawn.o strace.o \
+ strsep.o sync.o syscalls.o sysconf.o syslog.o termios.o thread.o \
+ times.o tty.o uinfo.o uname.o v8_regexp.o v8_regerror.o v8_regsub.o \
+ wait.o wincap.o window.o \
+ $(EXTRA_DLL_OFILES) $(EXTRA_OFILES) $(MALLOC_OFILES) $(MT_SAFE_OBJECTS)
+
+GMON_OFILES:=gmon.o mcount.o profil.o
+
+OBSOLETE_FUNCTIONS:=regcomp regerror regexec regfree regsub
+NEW_FUNCTIONS:=regcomp posix_regcomp \
+ regerror posix_regerror \
+ regexec posix_regexec \
+ regfree posix_regfree
+
+API_VER:=$(srcdir)/include/cygwin/version.h
+
+PWD:=${shell pwd}
+SUBLIBS:=libpthread.a $(PWD)/libm.a libc.a
+EXTRALIBS:=libautomode.a libbinmode.a libtextmode.a
+INSTOBJS:=automode.o binmode.o textmode.o
+TARGET_LIBS:=$(LIB_NAME) $(SUBLIBS) $(GMON_START) $(LIBGMON_A) $(SUBLIBS) $(INSTOBJS) $(EXTRALIBS)
+.PHONY: all force dll_ofiles install all_target install_target all_host install_host \
+ install install-libs install-headers -lgcc
+
+.SUFFIXES:
+.SUFFIXES: .c .cc .def .a .o .d
+
+all_host=@all_host@
+install_host=@install_host@
+
+all: all_target $(all_host)
+
+all_target: $(TARGET_LIBS) cygserver.exe
+
+all_host: new-$(LIB_NAME) cygrun.exe
+
+force:
+
+install: install-libs install-headers install-man install_target \
+ $(install_host) $(install_target)
+
+uninstall: uninstall-libs uninstall-headers uninstall-man
+
+install-libs: $(TARGET_LIBS)
+ $(INSTALL_DATA) new-$(DLL_NAME) $(bindir)/$(DLL_NAME); \
+ for i in $^; do \
+ $(INSTALL_DATA) $$i $(tooldir)/lib/`basename $$i` ; \
+ done
+
+install-headers:
+ cd $(srcdir); \
+ for sub in `find include -name '[a-z]*' -type d -print | sort`; do \
+ for i in $$sub/*.h ; do \
+ $(INSTALL_DATA) $$i $(tooldir)/$$sub/`basename $$i` ; \
+ done ; \
+ done ; \
+ $(INSTALL_DATA) regex/regex.h $(tooldir)/include/regex.h
+
+install-man:
+ cd $(srcdir); \
+ for i in `find . -type f -name '*.2'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man2/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.3'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man3/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.5'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man5/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.7'`; do \
+ $(INSTALL_DATA) $$i $(tooldir)/man/man7/`basename $$i` ; \
+ done
+
+install_target: cygserver.exe
+ $(INSTALL_PROGRAM) cygserver.exe $(bindir)/cygserver.exe
+
+install_host:
+
+
+uninstall-libs: $(TARGET_LIBS)
+ rm -f $(bindir)/$(DLL_NAME); \
+ for i in $^; do \
+ rm -f $(tooldir)/lib/$$i ; \
+ done
+
+uninstall-headers:
+ cd $(srcdir); \
+ for sub in `find include -name '[a-z]*' -type d -print | sort`; do \
+ for i in $$sub/*.h ; do \
+ rm -f $(tooldir)/$$sub/`basename $$i` ; \
+ done ; \
+ done ; \
+ rm -f $(tooldir)/include/regex.h
+
+uninstall-man:
+ cd $(srcdir); \
+ for i in `find . -type f -name '*.2'`; do \
+ rm -f $(tooldir)/man/man2/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.3'`; do \
+ rm -f $(tooldir)/man/man3/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.5'`; do \
+ rm -f $(tooldir)/man/man5/`basename $$i` ; \
+ done; \
+ for i in `find . -type f -name '*.7'`; do \
+ rm -f $(tooldir)/man/man7/`basename $$i` ; \
+ done
+
+clean:
+ -rm -f *.o *.dll *.a *.exp junk *.base version.cc regexp/*.o winver_stamp *.exe *.d *stamp* *_magic.h
+
+maintainer-clean realclean: clean
+ @echo "This command is intended for maintainers to use;"
+ @echo "it deletes files that may require special tools to rebuild."
+ -rm -fr configure
+
+
+# Rule to build cygwin.dll
+new-$(DLL_NAME): $(LDSCRIPT) $(DLL_OFILES) $(DEF_FILE) $(DLL_IMPORTS) $(LIBC) $(LIBM) $(API_VER) Makefile winver_stamp
+ $(CXX) $(CXXFLAGS) -nostdlib -Wl,-T$(firstword $^) -Wl,--out-implib,cygdll.a -shared -o $@ \
+ -e $(DLL_ENTRY) $(DEF_FILE) $(DLL_OFILES) version.o winver.o \
+ $(MALLOC_OBJ) $(LIBM) $(LIBC) \
+ -lgcc $(DLL_IMPORTS)
+
+# Rule to build libcygwin.a
+$(LIB_NAME): rmsym newsym new-$(DLL_NAME) $(LIBCOS)
+ /bin/sh ${word 1,$^} ./cygdll.a "$(NM)" "$(AR)" "$(RANLIB)" $(OBSOLETE_FUNCTIONS) || exit 0
+ /bin/sh ${word 2,$^} ./cygdll.a "$(AS)" "$(AR)" "$(RANLIB)" $(NEW_FUNCTIONS) || exit 0
+ (echo create $(LIB_NAME); echo addmod $(LIBCOS); echo addlib cygdll.a; echo save) | $(AR) -M
+
+# Rule to make stub library used by testsuite
+# dependency set to $(LIB_NAME) to accommodate make -j2.
+# Otherwise dlltool gets confused. cgf (11-16-2000)
+new-$(LIB_NAME): $(LIB_NAME)
+ $(DLLTOOL) --as=$(AS) --dllname new-$(DLL_NAME) --def $(DEF_FILE) --output-lib new-templib.a
+ $(AR) rcv new-templib.a $(LIBCOS)
+ mv new-templib.a new-$(LIB_NAME)
+ $(RANLIB) $@
+
+dll_ofiles: $(DLL_OFILES)
+
+$(LIBGMON_A): $(GMON_OFILES) $(GMON_START)
+ $(AR) rcv $(LIBGMON_A) $(GMON_OFILES)
+
+$(API_VER): $(srcdir)/cygwin.din
+ @echo Error: Version info is older than DLL API!
+ @false
+
+version.cc winver.o: winver_stamp
+ @ :
+
+shared_info_magic.h: cygmagic shared_info.h
+ /bin/sh ${word 1,$^} $@ "$(CC) -x c" ${word 2,$^} MOUNT_MAGIC 'class mount_info' SHARED_MAGIC 'class shared_info'
+
+child_info_magic.h: cygmagic child_info.h
+ /bin/sh ${word 1,$^} $@ "$(CC) -x c" ${word 2,$^} CHILD_INFO_MAGIC 'class child_info'
+
+dcrt0.o sigproc.o: child_info_magic.h
+
+shared.o: shared_info_magic.h
+
+libpthread.a: speclib cygwin.def pthread.o thread.o
+ /bin/sh ${word 1, $^} $@ "${NM}" "${DLLTOOL}" "${AS}" ${wordlist 2, 99, $^}
+
+$(PWD)/libm.a: speclib cygwin.def $(LIBM)
+ /bin/sh ${word 1, $^} $@ "${NM}" "${DLLTOOL}" "${AS}" ${wordlist 2, 99, $^}
+
+$(PWD)/libc.a: speclib cygwin.def $(PWD)/libm.a libpthread.a
+ /bin/sh ${word 1, $^} -v $@ "${NM}" "${DLLTOOL}" "${AS}" ${wordlist 2, 99, $^}
+
+lib%.a: %.o
+ $(AR) cru $@ $?
+
+winver_stamp: mkvers.sh include/cygwin/version.h winver.rc $(DLL_OFILES)
+ @echo "Making version.o and winver.o";\
+ $(SHELL) ${word 1,$^} ${word 2,$^} ${word 3,$^} $(WINDRES) && \
+ $(COMPILE_CXX) -o version.o version.cc && \
+ touch $@
+
+cygrun.o: cygrun.c
+ $(CC) $(MINGW_CFLAGS) -o $@ -c $<
+
+cygrun.exe : cygrun.o -lgcc $(w32api_lib)/libuser32.a \
+ $(w32api_lib)/libshell32.a $(w32api_lib)/libkernel32.a
+ $(CC) ${MINGW_LDFLAGS} -mno-cygwin -o $@ $^
+
+cygserver_transport_outside.o: cygserver_transport.cc
+ $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
+
+cygserver_transport_pipes_outside.o: cygserver_transport_pipes.cc
+ $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
+
+cygserver_transport_sockets_outside.o: cygserver_transport_sockets.cc
+ $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
+
+cygserver_client_outside.o: cygserver_client.cc
+ $(COMPILE_CXX) -D__OUTSIDE_CYGWIN__ -o $@ $<
+
+# gperf -c --key-positions='1-126' -r -t -C -E -L 'ANSI-C' -Hdevhash -N'device::lookup' -Z devstring -7 $? |\
+
+$(srcdir)/devices.cc: devices.gperf devices.h
+ $(srcdir)/cygwin-gperf $^ > $@
+
+cygserver.exe: cygserver.o cygserver_shm.o cygserver_transport_outside.o cygserver_transport_pipes_outside.o cygserver_transport_sockets_outside.o cygserver_client_outside.o cygserver_process.o threaded_queue.o wincap.o version.o smallprint.o
+ $(CXX) -o $@ $^ -lstdc++
+#ifdef VERBOSE
+# $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS)
+#else
+# @echo $(CXX) -o $@ ${wordlist 1,3,$^} ${filter-out -B%, $(MINGW_CXXFLAGS) $(MINGW_LDFLAGS)};\
+# $(CXX) $(MINGW_CXXFLAGS) -o $@ ${wordlist 1,3,$^} -B$(mingw_build)/ $(MINGW_LDFLAGS)
+#endif
+
+-lgcc:
+ :
+
+#
+
+Makefile: cygwin.din
+
+$(DEF_FILE): cygwin.din config.status
+ $(SHELL) config.status
+
+winsup.h: config.h
+
+deps:=${wildcard *.d}
+ifneq (,$(deps))
+include $(deps)
+endif
diff --git a/winsup/cygwin/configure b/winsup/cygwin/configure
new file mode 100755
index 00000000000..05e791825b6
--- /dev/null
+++ b/winsup/cygwin/configure
@@ -0,0 +1,2415 @@
+#! /bin/sh
+
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf version 2.13
+# Copyright (C) 1992, 93, 94, 95, 96 Free Software Foundation, Inc.
+#
+# This configure script is free software; the Free Software Foundation
+# gives unlimited permission to copy, distribute and modify it.
+
+# Defaults:
+ac_help=
+ac_default_prefix=/usr/local
+# Any additions from configure.in:
+ac_help="$ac_help
+ --enable-threadsafe=[runtime] Build a cygwin DLL which is thread safe"
+ac_help="$ac_help
+ --enable-extra-threadsafe-checking Build a cygwin DLL which is thread safe with extra consistency checking"
+ac_help="$ac_help
+ --enable-debugging Build a cygwin DLL which has more consistency checking for debugging"
+ac_help="$ac_help
+ --enable-malloc-debugging Build a cygwin DLL with heap sanity checking (this is very slow, use only if you have heap corruption problems)"
+ac_help="$ac_help
+ --enable-vfork Build a cygwin DLL which uses experimental vfork code"
+
+# Initialize some variables set by options.
+# The variables have the same names as the options, with
+# dashes changed to underlines.
+build=NONE
+cache_file=./config.cache
+exec_prefix=NONE
+host=NONE
+no_create=
+nonopt=NONE
+no_recursion=
+prefix=NONE
+program_prefix=NONE
+program_suffix=NONE
+program_transform_name=s,x,x,
+silent=
+site=
+srcdir=
+target=NONE
+verbose=
+x_includes=NONE
+x_libraries=NONE
+bindir='${exec_prefix}/bin'
+sbindir='${exec_prefix}/sbin'
+libexecdir='${exec_prefix}/libexec'
+datadir='${prefix}/share'
+sysconfdir='${prefix}/etc'
+sharedstatedir='${prefix}/com'
+localstatedir='${prefix}/var'
+libdir='${exec_prefix}/lib'
+includedir='${prefix}/include'
+oldincludedir='/usr/include'
+infodir='${prefix}/info'
+mandir='${prefix}/man'
+
+# Initialize some other variables.
+subdirs=
+MFLAGS= MAKEFLAGS=
+SHELL=${CONFIG_SHELL-/bin/sh}
+# Maximum number of lines to put in a shell here document.
+ac_max_here_lines=12
+
+ac_prev=
+for ac_option
+do
+
+ # If the previous option needs an argument, assign it.
+ if test -n "$ac_prev"; then
+ eval "$ac_prev=\$ac_option"
+ ac_prev=
+ continue
+ fi
+
+ case "$ac_option" in
+ -*=*) ac_optarg=`echo "$ac_option" | sed 's/[-_a-zA-Z0-9]*=//'` ;;
+ *) ac_optarg= ;;
+ esac
+
+ # Accept the important Cygnus configure options, so we can diagnose typos.
+
+ case "$ac_option" in
+
+ -bindir | --bindir | --bindi | --bind | --bin | --bi)
+ ac_prev=bindir ;;
+ -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*)
+ bindir="$ac_optarg" ;;
+
+ -build | --build | --buil | --bui | --bu)
+ ac_prev=build ;;
+ -build=* | --build=* | --buil=* | --bui=* | --bu=*)
+ build="$ac_optarg" ;;
+
+ -cache-file | --cache-file | --cache-fil | --cache-fi \
+ | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c)
+ ac_prev=cache_file ;;
+ -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \
+ | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*)
+ cache_file="$ac_optarg" ;;
+
+ -datadir | --datadir | --datadi | --datad | --data | --dat | --da)
+ ac_prev=datadir ;;
+ -datadir=* | --datadir=* | --datadi=* | --datad=* | --data=* | --dat=* \
+ | --da=*)
+ datadir="$ac_optarg" ;;
+
+ -disable-* | --disable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*disable-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ eval "enable_${ac_feature}=no" ;;
+
+ -enable-* | --enable-*)
+ ac_feature=`echo $ac_option|sed -e 's/-*enable-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_feature| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_feature: invalid feature name" 1>&2; exit 1; }
+ fi
+ ac_feature=`echo $ac_feature| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "enable_${ac_feature}='$ac_optarg'" ;;
+
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \
+ | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \
+ | --exec | --exe | --ex)
+ ac_prev=exec_prefix ;;
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \
+ | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \
+ | --exec=* | --exe=* | --ex=*)
+ exec_prefix="$ac_optarg" ;;
+
+ -gas | --gas | --ga | --g)
+ # Obsolete; use --with-gas.
+ with_gas=yes ;;
+
+ -help | --help | --hel | --he)
+ # Omit some internal or obsolete options to make the list less imposing.
+ # This message is too long to be a string in the A/UX 3.1 sh.
+ cat << EOF
+Usage: configure [options] [host]
+Options: [defaults in brackets after descriptions]
+Configuration:
+ --cache-file=FILE cache test results in FILE
+ --help print this message
+ --no-create do not create output files
+ --quiet, --silent do not print \`checking...' messages
+ --version print the version of autoconf that created configure
+Directory and file names:
+ --prefix=PREFIX install architecture-independent files in PREFIX
+ [$ac_default_prefix]
+ --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX
+ [same as prefix]
+ --bindir=DIR user executables in DIR [EPREFIX/bin]
+ --sbindir=DIR system admin executables in DIR [EPREFIX/sbin]
+ --libexecdir=DIR program executables in DIR [EPREFIX/libexec]
+ --datadir=DIR read-only architecture-independent data in DIR
+ [PREFIX/share]
+ --sysconfdir=DIR read-only single-machine data in DIR [PREFIX/etc]
+ --sharedstatedir=DIR modifiable architecture-independent data in DIR
+ [PREFIX/com]
+ --localstatedir=DIR modifiable single-machine data in DIR [PREFIX/var]
+ --libdir=DIR object code libraries in DIR [EPREFIX/lib]
+ --includedir=DIR C header files in DIR [PREFIX/include]
+ --oldincludedir=DIR C header files for non-gcc in DIR [/usr/include]
+ --infodir=DIR info documentation in DIR [PREFIX/info]
+ --mandir=DIR man documentation in DIR [PREFIX/man]
+ --srcdir=DIR find the sources in DIR [configure dir or ..]
+ --program-prefix=PREFIX prepend PREFIX to installed program names
+ --program-suffix=SUFFIX append SUFFIX to installed program names
+ --program-transform-name=PROGRAM
+ run sed PROGRAM on installed program names
+EOF
+ cat << EOF
+Host type:
+ --build=BUILD configure for building on BUILD [BUILD=HOST]
+ --host=HOST configure for HOST [guessed]
+ --target=TARGET configure for TARGET [TARGET=HOST]
+Features and packages:
+ --disable-FEATURE do not include FEATURE (same as --enable-FEATURE=no)
+ --enable-FEATURE[=ARG] include FEATURE [ARG=yes]
+ --with-PACKAGE[=ARG] use PACKAGE [ARG=yes]
+ --without-PACKAGE do not use PACKAGE (same as --with-PACKAGE=no)
+ --x-includes=DIR X include files are in DIR
+ --x-libraries=DIR X library files are in DIR
+EOF
+ if test -n "$ac_help"; then
+ echo "--enable and --with options recognized:$ac_help"
+ fi
+ exit 0 ;;
+
+ -host | --host | --hos | --ho)
+ ac_prev=host ;;
+ -host=* | --host=* | --hos=* | --ho=*)
+ host="$ac_optarg" ;;
+
+ -includedir | --includedir | --includedi | --included | --include \
+ | --includ | --inclu | --incl | --inc)
+ ac_prev=includedir ;;
+ -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \
+ | --includ=* | --inclu=* | --incl=* | --inc=*)
+ includedir="$ac_optarg" ;;
+
+ -infodir | --infodir | --infodi | --infod | --info | --inf)
+ ac_prev=infodir ;;
+ -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*)
+ infodir="$ac_optarg" ;;
+
+ -libdir | --libdir | --libdi | --libd)
+ ac_prev=libdir ;;
+ -libdir=* | --libdir=* | --libdi=* | --libd=*)
+ libdir="$ac_optarg" ;;
+
+ -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \
+ | --libexe | --libex | --libe)
+ ac_prev=libexecdir ;;
+ -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \
+ | --libexe=* | --libex=* | --libe=*)
+ libexecdir="$ac_optarg" ;;
+
+ -localstatedir | --localstatedir | --localstatedi | --localstated \
+ | --localstate | --localstat | --localsta | --localst \
+ | --locals | --local | --loca | --loc | --lo)
+ ac_prev=localstatedir ;;
+ -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \
+ | --localstate=* | --localstat=* | --localsta=* | --localst=* \
+ | --locals=* | --local=* | --loca=* | --loc=* | --lo=*)
+ localstatedir="$ac_optarg" ;;
+
+ -mandir | --mandir | --mandi | --mand | --man | --ma | --m)
+ ac_prev=mandir ;;
+ -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*)
+ mandir="$ac_optarg" ;;
+
+ -nfp | --nfp | --nf)
+ # Obsolete; use --without-fp.
+ with_fp=no ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c)
+ no_create=yes ;;
+
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r)
+ no_recursion=yes ;;
+
+ -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \
+ | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \
+ | --oldin | --oldi | --old | --ol | --o)
+ ac_prev=oldincludedir ;;
+ -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \
+ | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \
+ | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*)
+ oldincludedir="$ac_optarg" ;;
+
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ ac_prev=prefix ;;
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix="$ac_optarg" ;;
+
+ -program-prefix | --program-prefix | --program-prefi | --program-pref \
+ | --program-pre | --program-pr | --program-p)
+ ac_prev=program_prefix ;;
+ -program-prefix=* | --program-prefix=* | --program-prefi=* \
+ | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*)
+ program_prefix="$ac_optarg" ;;
+
+ -program-suffix | --program-suffix | --program-suffi | --program-suff \
+ | --program-suf | --program-su | --program-s)
+ ac_prev=program_suffix ;;
+ -program-suffix=* | --program-suffix=* | --program-suffi=* \
+ | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*)
+ program_suffix="$ac_optarg" ;;
+
+ -program-transform-name | --program-transform-name \
+ | --program-transform-nam | --program-transform-na \
+ | --program-transform-n | --program-transform- \
+ | --program-transform | --program-transfor \
+ | --program-transfo | --program-transf \
+ | --program-trans | --program-tran \
+ | --progr-tra | --program-tr | --program-t)
+ ac_prev=program_transform_name ;;
+ -program-transform-name=* | --program-transform-name=* \
+ | --program-transform-nam=* | --program-transform-na=* \
+ | --program-transform-n=* | --program-transform-=* \
+ | --program-transform=* | --program-transfor=* \
+ | --program-transfo=* | --program-transf=* \
+ | --program-trans=* | --program-tran=* \
+ | --progr-tra=* | --program-tr=* | --program-t=*)
+ program_transform_name="$ac_optarg" ;;
+
+ -q | -quiet | --quiet | --quie | --qui | --qu | --q \
+ | -silent | --silent | --silen | --sile | --sil)
+ silent=yes ;;
+
+ -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
+ ac_prev=sbindir ;;
+ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
+ | --sbi=* | --sb=*)
+ sbindir="$ac_optarg" ;;
+
+ -sharedstatedir | --sharedstatedir | --sharedstatedi \
+ | --sharedstated | --sharedstate | --sharedstat | --sharedsta \
+ | --sharedst | --shareds | --shared | --share | --shar \
+ | --sha | --sh)
+ ac_prev=sharedstatedir ;;
+ -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \
+ | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \
+ | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \
+ | --sha=* | --sh=*)
+ sharedstatedir="$ac_optarg" ;;
+
+ -site | --site | --sit)
+ ac_prev=site ;;
+ -site=* | --site=* | --sit=*)
+ site="$ac_optarg" ;;
+
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr)
+ ac_prev=srcdir ;;
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*)
+ srcdir="$ac_optarg" ;;
+
+ -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \
+ | --syscon | --sysco | --sysc | --sys | --sy)
+ ac_prev=sysconfdir ;;
+ -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \
+ | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*)
+ sysconfdir="$ac_optarg" ;;
+
+ -target | --target | --targe | --targ | --tar | --ta | --t)
+ ac_prev=target ;;
+ -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*)
+ target="$ac_optarg" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb)
+ verbose=yes ;;
+
+ -version | --version | --versio | --versi | --vers)
+ echo "configure generated by autoconf version 2.13"
+ exit 0 ;;
+
+ -with-* | --with-*)
+ ac_package=`echo $ac_option|sed -e 's/-*with-//' -e 's/=.*//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-_a-zA-Z0-9]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ case "$ac_option" in
+ *=*) ;;
+ *) ac_optarg=yes ;;
+ esac
+ eval "with_${ac_package}='$ac_optarg'" ;;
+
+ -without-* | --without-*)
+ ac_package=`echo $ac_option|sed -e 's/-*without-//'`
+ # Reject names that are not valid shell variable names.
+ if test -n "`echo $ac_package| sed 's/[-a-zA-Z0-9_]//g'`"; then
+ { echo "configure: error: $ac_package: invalid package name" 1>&2; exit 1; }
+ fi
+ ac_package=`echo $ac_package| sed 's/-/_/g'`
+ eval "with_${ac_package}=no" ;;
+
+ --x)
+ # Obsolete; use --with-x.
+ with_x=yes ;;
+
+ -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \
+ | --x-incl | --x-inc | --x-in | --x-i)
+ ac_prev=x_includes ;;
+ -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \
+ | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*)
+ x_includes="$ac_optarg" ;;
+
+ -x-libraries | --x-libraries | --x-librarie | --x-librari \
+ | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l)
+ ac_prev=x_libraries ;;
+ -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \
+ | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*)
+ x_libraries="$ac_optarg" ;;
+
+ -*) { echo "configure: error: $ac_option: invalid option; use --help to show usage" 1>&2; exit 1; }
+ ;;
+
+ *)
+ if test -n "`echo $ac_option| sed 's/[-a-z0-9.]//g'`"; then
+ echo "configure: warning: $ac_option: invalid host type" 1>&2
+ fi
+ if test "x$nonopt" != xNONE; then
+ { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; }
+ fi
+ nonopt="$ac_option"
+ ;;
+
+ esac
+done
+
+if test -n "$ac_prev"; then
+ { echo "configure: error: missing argument to --`echo $ac_prev | sed 's/_/-/g'`" 1>&2; exit 1; }
+fi
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+# File descriptor usage:
+# 0 standard input
+# 1 file creation
+# 2 errors and warnings
+# 3 some systems may open it to /dev/tty
+# 4 used on the Kubota Titan
+# 6 checking for... messages and results
+# 5 compiler messages saved in config.log
+if test "$silent" = yes; then
+ exec 6>/dev/null
+else
+ exec 6>&1
+fi
+exec 5>./config.log
+
+echo "\
+This file contains any messages produced by compilers while
+running configure, to aid debugging if configure makes a mistake.
+" 1>&5
+
+# Strip out --no-create and --no-recursion so they do not pile up.
+# Also quote any args containing shell metacharacters.
+ac_configure_args=
+for ac_arg
+do
+ case "$ac_arg" in
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre \
+ | --no-cr | --no-c) ;;
+ -no-recursion | --no-recursion | --no-recursio | --no-recursi \
+ | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) ;;
+ *" "*|*" "*|*[\[\]\~\#\$\^\&\*\(\)\{\}\\\|\;\<\>\?]*)
+ ac_configure_args="$ac_configure_args '$ac_arg'" ;;
+ *) ac_configure_args="$ac_configure_args $ac_arg" ;;
+ esac
+done
+
+# NLS nuisances.
+# Only set these to C if already set. These must not be set unconditionally
+# because not all systems understand e.g. LANG=C (notably SCO).
+# Fixing LC_MESSAGES prevents Solaris sh from translating var values in `set'!
+# Non-C LC_CTYPE values break the ctype check.
+if test "${LANG+set}" = set; then LANG=C; export LANG; fi
+if test "${LC_ALL+set}" = set; then LC_ALL=C; export LC_ALL; fi
+if test "${LC_MESSAGES+set}" = set; then LC_MESSAGES=C; export LC_MESSAGES; fi
+if test "${LC_CTYPE+set}" = set; then LC_CTYPE=C; export LC_CTYPE; fi
+
+# confdefs.h avoids OS command line length limits that DEFS can exceed.
+rm -rf conftest* confdefs.h
+# AIX cpp loses on an empty file, so make sure it contains at least a newline.
+echo > confdefs.h
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+ac_unique_file=init.cc
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ ac_srcdir_defaulted=yes
+ # Try the directory containing this script, then its parent.
+ ac_prog=$0
+ ac_confdir=`echo $ac_prog|sed 's%/[^/][^/]*$%%'`
+ test "x$ac_confdir" = "x$ac_prog" && ac_confdir=.
+ srcdir=$ac_confdir
+ if test ! -r $srcdir/$ac_unique_file; then
+ srcdir=..
+ fi
+else
+ ac_srcdir_defaulted=no
+fi
+if test ! -r $srcdir/$ac_unique_file; then
+ if test "$ac_srcdir_defaulted" = yes; then
+ { echo "configure: error: can not find sources in $ac_confdir or .." 1>&2; exit 1; }
+ else
+ { echo "configure: error: can not find sources in $srcdir" 1>&2; exit 1; }
+ fi
+fi
+srcdir=`echo "${srcdir}" | sed 's%\([^/]\)/*$%\1%'`
+
+# Prefer explicitly selected file to automatically selected ones.
+if test -z "$CONFIG_SITE"; then
+ if test "x$prefix" != xNONE; then
+ CONFIG_SITE="$prefix/share/config.site $prefix/etc/config.site"
+ else
+ CONFIG_SITE="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site"
+ fi
+fi
+for ac_site_file in $CONFIG_SITE; do
+ if test -r "$ac_site_file"; then
+ echo "loading site script $ac_site_file"
+ . "$ac_site_file"
+ fi
+done
+
+if test -r "$cache_file"; then
+ echo "loading cache $cache_file"
+ . $cache_file
+else
+ echo "creating cache $cache_file"
+ > $cache_file
+fi
+
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+ac_exeext=
+ac_objext=o
+if (echo "testing\c"; echo 1,2,3) | grep c >/dev/null; then
+ # Stardent Vistra SVR4 grep lacks -e, says ghazi@caip.rutgers.edu.
+ if (echo -n testing; echo 1,2,3) | sed s/-n/xn/ | grep xn >/dev/null; then
+ ac_n= ac_c='
+' ac_t=' '
+ else
+ ac_n=-n ac_c= ac_t=
+ fi
+else
+ ac_n= ac_c='\c' ac_t=
+fi
+
+
+
+
+ac_aux_dir=
+for ac_dir in $srcdir $srcdir/.. $srcdir/../..; do
+ if test -f $ac_dir/install-sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install-sh -c"
+ break
+ elif test -f $ac_dir/install.sh; then
+ ac_aux_dir=$ac_dir
+ ac_install_sh="$ac_aux_dir/install.sh -c"
+ break
+ fi
+done
+if test -z "$ac_aux_dir"; then
+ { echo "configure: error: can not find install-sh or install.sh in $srcdir $srcdir/.. $srcdir/../.." 1>&2; exit 1; }
+fi
+ac_config_guess=$ac_aux_dir/config.guess
+ac_config_sub=$ac_aux_dir/config.sub
+ac_configure=$ac_aux_dir/configure # This should be Cygnus configure.
+
+# Find a good install program. We prefer a C program (faster),
+# so one script is as good as another. But avoid the broken or
+# incompatible versions:
+# SysV /etc/install, /usr/sbin/install
+# SunOS /usr/etc/install
+# IRIX /sbin/install
+# AIX /bin/install
+# AIX 4 /usr/bin/installbsd, which doesn't work without a -g flag
+# AFS /usr/afsws/bin/install, which mishandles nonexistent args
+# SVR4 /usr/ucb/install, which tries to use the nonexistent group "staff"
+# ./install, which can be erroneously created by make from ./install.sh.
+echo $ac_n "checking for a BSD compatible install""... $ac_c" 1>&6
+echo "configure:568: checking for a BSD compatible install" >&5
+if test -z "$INSTALL"; then
+if eval "test \"`echo '$''{'ac_cv_path_install'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ IFS="${IFS= }"; ac_save_IFS="$IFS"; IFS=":"
+ for ac_dir in $PATH; do
+ # Account for people who put trailing slashes in PATH elements.
+ case "$ac_dir/" in
+ /|./|.//|/etc/*|/usr/sbin/*|/usr/etc/*|/sbin/*|/usr/afsws/bin/*|/usr/ucb/*) ;;
+ *)
+ # OSF1 and SCO ODT 3.0 have their own names for install.
+ # Don't use installbsd from OSF since it installs stuff as root
+ # by default.
+ for ac_prog in ginstall scoinst install; do
+ if test -f $ac_dir/$ac_prog; then
+ if test $ac_prog = install &&
+ grep dspmsg $ac_dir/$ac_prog >/dev/null 2>&1; then
+ # AIX install. It has an incompatible calling convention.
+ :
+ else
+ ac_cv_path_install="$ac_dir/$ac_prog -c"
+ break 2
+ fi
+ fi
+ done
+ ;;
+ esac
+ done
+ IFS="$ac_save_IFS"
+
+fi
+ if test "${ac_cv_path_install+set}" = set; then
+ INSTALL="$ac_cv_path_install"
+ else
+ # As a last resort, use the slow shell script. We don't cache a
+ # path for INSTALL within a source directory, because that will
+ # break other packages using the cache if that directory is
+ # removed, or if the path is relative.
+ INSTALL="$ac_install_sh"
+ fi
+fi
+echo "$ac_t""$INSTALL" 1>&6
+
+# Use test -z because SunOS4 sh mishandles braces in ${var-val}.
+# It thinks the first close brace ends the variable substitution.
+test -z "$INSTALL_PROGRAM" && INSTALL_PROGRAM='${INSTALL}'
+
+test -z "$INSTALL_SCRIPT" && INSTALL_SCRIPT='${INSTALL_PROGRAM}'
+
+test -z "$INSTALL_DATA" && INSTALL_DATA='${INSTALL} -m 644'
+
+
+
+
+
+
+
+
+# Do some error checking and defaulting for the host and target type.
+# The inputs are:
+# configure --host=HOST --target=TARGET --build=BUILD NONOPT
+#
+# The rules are:
+# 1. You are not allowed to specify --host, --target, and nonopt at the
+# same time.
+# 2. Host defaults to nonopt.
+# 3. If nonopt is not specified, then host defaults to the current host,
+# as determined by config.guess.
+# 4. Target and build default to nonopt.
+# 5. If nonopt is not specified, then target and build default to host.
+
+# The aliases save the names the user supplied, while $host etc.
+# will get canonicalized.
+case $host---$target---$nonopt in
+NONE---*---* | *---NONE---* | *---*---NONE) ;;
+*) { echo "configure: error: can only configure for one host and one target at a time" 1>&2; exit 1; } ;;
+esac
+
+
+# Make sure we can run config.sub.
+if ${CONFIG_SHELL-/bin/sh} $ac_config_sub sun4 >/dev/null 2>&1; then :
+else { echo "configure: error: can not run $ac_config_sub" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking host system type""... $ac_c" 1>&6
+echo "configure:654: checking host system type" >&5
+
+host_alias=$host
+case "$host_alias" in
+NONE)
+ case $nonopt in
+ NONE)
+ if host_alias=`${CONFIG_SHELL-/bin/sh} $ac_config_guess`; then :
+ else { echo "configure: error: can not guess host type; you must specify one" 1>&2; exit 1; }
+ fi ;;
+ *) host_alias=$nonopt ;;
+ esac ;;
+esac
+
+host=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $host_alias`
+host_cpu=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo $host | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$host" 1>&6
+
+echo $ac_n "checking target system type""... $ac_c" 1>&6
+echo "configure:675: checking target system type" >&5
+
+target_alias=$target
+case "$target_alias" in
+NONE)
+ case $nonopt in
+ NONE) target_alias=$host_alias ;;
+ *) target_alias=$nonopt ;;
+ esac ;;
+esac
+
+target=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $target_alias`
+target_cpu=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+target_vendor=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+target_os=`echo $target | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$target" 1>&6
+
+echo $ac_n "checking build system type""... $ac_c" 1>&6
+echo "configure:693: checking build system type" >&5
+
+build_alias=$build
+case "$build_alias" in
+NONE)
+ case $nonopt in
+ NONE) build_alias=$host_alias ;;
+ *) build_alias=$nonopt ;;
+ esac ;;
+esac
+
+build=`${CONFIG_SHELL-/bin/sh} $ac_config_sub $build_alias`
+build_cpu=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+build_vendor=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+build_os=`echo $build | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+echo "$ac_t""$build" 1>&6
+
+test "$host_alias" != "$target_alias" &&
+ test "$program_prefix$program_suffix$program_transform_name" = \
+ NONENONEs,x,x, &&
+ program_prefix=${target_alias}-
+
+
+if test $host != $build; then
+ ac_tool_prefix=${host_alias}-
+else
+ ac_tool_prefix=
+fi
+
+# Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args.
+set dummy ${ac_tool_prefix}gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:725: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="${ac_tool_prefix}gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_CC"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "gcc", so it can be a program name with args.
+set dummy gcc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:757: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CC="gcc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_CC" && ac_cv_prog_CC="gcc"
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ CC="gcc"
+fi
+fi
+
+if test -z "$CC"; then
+ # Extract the first word of "cc", so it can be a program name with args.
+set dummy cc; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:793: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CC'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CC"; then
+ ac_cv_prog_CC="$CC" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_prog_rejected=no
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ if test "$ac_dir/$ac_word" = "/usr/ucb/cc"; then
+ ac_prog_rejected=yes
+ continue
+ fi
+ ac_cv_prog_CC="cc"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+if test $ac_prog_rejected = yes; then
+ # We found a bogon in the path, so make sure we never use it.
+ set dummy $ac_cv_prog_CC
+ shift
+ if test $# -gt 0; then
+ # We chose a different compiler from the bogus one.
+ # However, it has the same basename, so the bogon will be chosen
+ # first if we set CC to just the basename; use the full file name.
+ shift
+ set dummy "$ac_dir/$ac_word" "$@"
+ shift
+ ac_cv_prog_CC="$@"
+ fi
+fi
+fi
+fi
+CC="$ac_cv_prog_CC"
+if test -n "$CC"; then
+ echo "$ac_t""$CC" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+echo $ac_n "checking whether we are using GNU C""... $ac_c" 1>&6
+echo "configure:842: checking whether we are using GNU C" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_gcc'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes;
+#endif
+EOF
+if { ac_try='${CC-cc} -E conftest.c'; { (eval echo configure:851: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }; } | egrep yes >/dev/null 2>&1; then
+ ac_cv_prog_gcc=yes
+else
+ ac_cv_prog_gcc=no
+fi
+fi
+
+echo "$ac_t""$ac_cv_prog_gcc" 1>&6
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+ ac_test_CFLAGS="${CFLAGS+set}"
+ ac_save_CFLAGS="$CFLAGS"
+ CFLAGS=
+ echo $ac_n "checking whether ${CC-cc} accepts -g""... $ac_c" 1>&6
+echo "configure:866: checking whether ${CC-cc} accepts -g" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_cc_g'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ echo 'void f(){}' > conftest.c
+if test -z "`${CC-cc} -g -c conftest.c 2>&1`"; then
+ ac_cv_prog_cc_g=yes
+else
+ ac_cv_prog_cc_g=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_prog_cc_g" 1>&6
+ if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+ elif test $ac_cv_prog_cc_g = yes; then
+ CFLAGS="-gstabs+ -O2"
+ else
+ CFLAGS="-O2"
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+
+# Extract the first word of "${ac_tool_prefix}g++", so it can be a program name with args.
+set dummy ${ac_tool_prefix}g++; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:896: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CXX="${ac_tool_prefix}g++"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+ echo "$ac_t""$CXX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_CXX"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "g++", so it can be a program name with args.
+set dummy g++; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:928: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CXX="g++"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_CXX" && ac_cv_prog_CXX="g++"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+ echo "$ac_t""$CXX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ CXX="g++"
+fi
+fi
+
+if test -z "$CXX"; then
+ # Extract the first word of "c++", so it can be a program name with args.
+set dummy c++; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:964: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_CXX'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$CXX"; then
+ ac_cv_prog_CXX="$CXX" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_CXX="c++"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+CXX="$ac_cv_prog_CXX"
+if test -n "$CXX"; then
+ echo "$ac_t""$CXX" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+ test -z "$CC" && { echo "configure: error: no acceptable cc found in \$PATH" 1>&2; exit 1; }
+fi
+
+CXXFLAGS='$(CFLAGS)'
+
+
+case "$with_cross_host" in
+ ""|*cygwin*)
+ all_host="all_host"
+ install_host="install_host"
+ ;;
+ *)
+ all_host=
+ install_host=
+ ;;
+esac
+
+
+
+
+# Extract the first word of "${ac_tool_prefix}ar", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1013: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AR="${ac_tool_prefix}ar"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+AR="$ac_cv_prog_AR"
+if test -n "$AR"; then
+ echo "$ac_t""$AR" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_AR"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "ar", so it can be a program name with args.
+set dummy ar; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1045: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AR'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AR"; then
+ ac_cv_prog_AR="$AR" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AR="ar"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_AR" && ac_cv_prog_AR="ar"
+fi
+fi
+AR="$ac_cv_prog_AR"
+if test -n "$AR"; then
+ echo "$ac_t""$AR" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ AR="ar"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}as", so it can be a program name with args.
+set dummy ${ac_tool_prefix}as; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1080: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AS'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AS"; then
+ ac_cv_prog_AS="$AS" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AS="${ac_tool_prefix}as"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+AS="$ac_cv_prog_AS"
+if test -n "$AS"; then
+ echo "$ac_t""$AS" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_AS"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "as", so it can be a program name with args.
+set dummy as; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1112: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_AS'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$AS"; then
+ ac_cv_prog_AS="$AS" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_AS="as"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_AS" && ac_cv_prog_AS="as"
+fi
+fi
+AS="$ac_cv_prog_AS"
+if test -n "$AS"; then
+ echo "$ac_t""$AS" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ AS="as"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}ranlib", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1147: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="${ac_tool_prefix}ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_RANLIB"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "ranlib", so it can be a program name with args.
+set dummy ranlib; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1179: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_RANLIB'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$RANLIB"; then
+ ac_cv_prog_RANLIB="$RANLIB" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_RANLIB" && ac_cv_prog_RANLIB="ranlib"
+fi
+fi
+RANLIB="$ac_cv_prog_RANLIB"
+if test -n "$RANLIB"; then
+ echo "$ac_t""$RANLIB" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ RANLIB="ranlib"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}ld", so it can be a program name with args.
+set dummy ${ac_tool_prefix}ld; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1214: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LD'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$LD"; then
+ ac_cv_prog_LD="$LD" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_LD="${ac_tool_prefix}ld"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+LD="$ac_cv_prog_LD"
+if test -n "$LD"; then
+ echo "$ac_t""$LD" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_LD"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "ld", so it can be a program name with args.
+set dummy ld; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1246: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_LD'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$LD"; then
+ ac_cv_prog_LD="$LD" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_LD="ld"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_LD" && ac_cv_prog_LD="ld"
+fi
+fi
+LD="$ac_cv_prog_LD"
+if test -n "$LD"; then
+ echo "$ac_t""$LD" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ LD="ld"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}nm", so it can be a program name with args.
+set dummy ${ac_tool_prefix}nm; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1281: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_NM'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$NM"; then
+ ac_cv_prog_NM="$NM" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_NM="${ac_tool_prefix}nm"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+NM="$ac_cv_prog_NM"
+if test -n "$NM"; then
+ echo "$ac_t""$NM" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_NM"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "nm", so it can be a program name with args.
+set dummy nm; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1313: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_NM'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$NM"; then
+ ac_cv_prog_NM="$NM" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_NM="nm"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_NM" && ac_cv_prog_NM="nm"
+fi
+fi
+NM="$ac_cv_prog_NM"
+if test -n "$NM"; then
+ echo "$ac_t""$NM" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ NM="nm"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}dlltool", so it can be a program name with args.
+set dummy ${ac_tool_prefix}dlltool; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1348: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_DLLTOOL'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_DLLTOOL="${ac_tool_prefix}dlltool"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+DLLTOOL="$ac_cv_prog_DLLTOOL"
+if test -n "$DLLTOOL"; then
+ echo "$ac_t""$DLLTOOL" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_DLLTOOL"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "dlltool", so it can be a program name with args.
+set dummy dlltool; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1380: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_DLLTOOL'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$DLLTOOL"; then
+ ac_cv_prog_DLLTOOL="$DLLTOOL" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_DLLTOOL="dlltool"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_DLLTOOL" && ac_cv_prog_DLLTOOL="dlltool"
+fi
+fi
+DLLTOOL="$ac_cv_prog_DLLTOOL"
+if test -n "$DLLTOOL"; then
+ echo "$ac_t""$DLLTOOL" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ DLLTOOL="dlltool"
+fi
+fi
+
+# Extract the first word of "${ac_tool_prefix}windres", so it can be a program name with args.
+set dummy ${ac_tool_prefix}windres; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1415: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$WINDRES"; then
+ ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_WINDRES="${ac_tool_prefix}windres"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+fi
+fi
+WINDRES="$ac_cv_prog_WINDRES"
+if test -n "$WINDRES"; then
+ echo "$ac_t""$WINDRES" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test -z "$ac_cv_prog_WINDRES"; then
+if test -n "$ac_tool_prefix"; then
+ # Extract the first word of "windres", so it can be a program name with args.
+set dummy windres; ac_word=$2
+echo $ac_n "checking for $ac_word""... $ac_c" 1>&6
+echo "configure:1447: checking for $ac_word" >&5
+if eval "test \"`echo '$''{'ac_cv_prog_WINDRES'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test -n "$WINDRES"; then
+ ac_cv_prog_WINDRES="$WINDRES" # Let the user override the test.
+else
+ IFS="${IFS= }"; ac_save_ifs="$IFS"; IFS=":"
+ ac_dummy="$PATH"
+ for ac_dir in $ac_dummy; do
+ test -z "$ac_dir" && ac_dir=.
+ if test -f $ac_dir/$ac_word; then
+ ac_cv_prog_WINDRES="windres"
+ break
+ fi
+ done
+ IFS="$ac_save_ifs"
+ test -z "$ac_cv_prog_WINDRES" && ac_cv_prog_WINDRES="windres"
+fi
+fi
+WINDRES="$ac_cv_prog_WINDRES"
+if test -n "$WINDRES"; then
+ echo "$ac_t""$WINDRES" 1>&6
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+else
+ WINDRES="windres"
+fi
+fi
+
+
+echo $ac_n "checking how to run the C preprocessor""... $ac_c" 1>&6
+echo "configure:1481: checking how to run the C preprocessor" >&5
+# On Suns, sometimes $CPP names a directory.
+if test -n "$CPP" && test -d "$CPP"; then
+ CPP=
+fi
+if test -z "$CPP"; then
+if eval "test \"`echo '$''{'ac_cv_prog_CPP'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ # This must be in double quotes, not single quotes, because CPP may get
+ # substituted into the Makefile and "${CC-cc}" will confuse make.
+ CPP="${CC-cc} -E"
+ # On the NeXT, cc -E runs the code through the compiler's parser,
+ # not just through cpp.
+ cat > conftest.$ac_ext <<EOF
+#line 1496 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1502: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -E -traditional-cpp"
+ cat > conftest.$ac_ext <<EOF
+#line 1513 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1519: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP="${CC-cc} -nologo -E"
+ cat > conftest.$ac_ext <<EOF
+#line 1530 "configure"
+#include "confdefs.h"
+#include <assert.h>
+Syntax Error
+EOF
+ac_try="$ac_cpp conftest.$ac_ext >/dev/null 2>conftest.out"
+{ (eval echo configure:1536: \"$ac_try\") 1>&5; (eval $ac_try) 2>&5; }
+ac_err=`grep -v '^ *+' conftest.out | grep -v "^conftest.${ac_ext}\$"`
+if test -z "$ac_err"; then
+ :
+else
+ echo "$ac_err" >&5
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+ ac_cv_prog_CPP="$CPP"
+fi
+ CPP="$ac_cv_prog_CPP"
+else
+ ac_cv_prog_CPP="$CPP"
+fi
+echo "$ac_t""$CPP" 1>&6
+
+# The Ultrix 4.2 mips builtin alloca declared by alloca.h only works
+# for constant arguments. Useless!
+echo $ac_n "checking for working alloca.h""... $ac_c" 1>&6
+echo "configure:1563: checking for working alloca.h" >&5
+if eval "test \"`echo '$''{'ac_cv_header_alloca_h'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1568 "configure"
+#include "confdefs.h"
+#include <alloca.h>
+int main() {
+char *p = alloca(2 * sizeof(int));
+; return 0; }
+EOF
+if { (eval echo configure:1575: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_header_alloca_h=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_header_alloca_h=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_header_alloca_h" 1>&6
+if test $ac_cv_header_alloca_h = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ALLOCA_H 1
+EOF
+
+fi
+
+echo $ac_n "checking for alloca""... $ac_c" 1>&6
+echo "configure:1596: checking for alloca" >&5
+if eval "test \"`echo '$''{'ac_cv_func_alloca_works'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1601 "configure"
+#include "confdefs.h"
+
+#ifdef __GNUC__
+# define alloca __builtin_alloca
+#else
+# ifdef _MSC_VER
+# include <malloc.h>
+# define alloca _alloca
+# else
+# if HAVE_ALLOCA_H
+# include <alloca.h>
+# else
+# ifdef _AIX
+ #pragma alloca
+# else
+# ifndef alloca /* predefined by HP cc +Olibcalls */
+char *alloca ();
+# endif
+# endif
+# endif
+# endif
+#endif
+
+int main() {
+char *p = (char *) alloca(1);
+; return 0; }
+EOF
+if { (eval echo configure:1629: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ ac_cv_func_alloca_works=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ ac_cv_func_alloca_works=no
+fi
+rm -f conftest*
+fi
+
+echo "$ac_t""$ac_cv_func_alloca_works" 1>&6
+if test $ac_cv_func_alloca_works = yes; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_ALLOCA 1
+EOF
+
+fi
+
+if test $ac_cv_func_alloca_works = no; then
+ # The SVR3 libPW and SVR4 libucb both contain incompatible functions
+ # that cause trouble. Some versions do not even contain alloca or
+ # contain a buggy version. If you still want to use their alloca,
+ # use ar to extract alloca.o from them instead of compiling alloca.c.
+ ALLOCA=alloca.${ac_objext}
+ cat >> confdefs.h <<\EOF
+#define C_ALLOCA 1
+EOF
+
+
+echo $ac_n "checking whether alloca needs Cray hooks""... $ac_c" 1>&6
+echo "configure:1661: checking whether alloca needs Cray hooks" >&5
+if eval "test \"`echo '$''{'ac_cv_os_cray'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1666 "configure"
+#include "confdefs.h"
+#if defined(CRAY) && ! defined(CRAY2)
+webecray
+#else
+wenotbecray
+#endif
+
+EOF
+if (eval "$ac_cpp conftest.$ac_ext") 2>&5 |
+ egrep "webecray" >/dev/null 2>&1; then
+ rm -rf conftest*
+ ac_cv_os_cray=yes
+else
+ rm -rf conftest*
+ ac_cv_os_cray=no
+fi
+rm -f conftest*
+
+fi
+
+echo "$ac_t""$ac_cv_os_cray" 1>&6
+if test $ac_cv_os_cray = yes; then
+for ac_func in _getb67 GETB67 getb67; do
+ echo $ac_n "checking for $ac_func""... $ac_c" 1>&6
+echo "configure:1691: checking for $ac_func" >&5
+if eval "test \"`echo '$''{'ac_cv_func_$ac_func'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1696 "configure"
+#include "confdefs.h"
+/* System header to define __stub macros and hopefully few prototypes,
+ which can conflict with char $ac_func(); below. */
+#include <assert.h>
+/* Override any gcc2 internal prototype to avoid an error. */
+/* We use char because int might match the return type of a gcc2
+ builtin and then its argument prototype would still apply. */
+char $ac_func();
+
+int main() {
+
+/* The GNU C library defines this for functions which it implements
+ to always fail with ENOSYS. Some functions are actually named
+ something starting with __ and the normal name is an alias. */
+#if defined (__stub_$ac_func) || defined (__stub___$ac_func)
+choke me
+#else
+$ac_func();
+#endif
+
+; return 0; }
+EOF
+if { (eval echo configure:1719: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=yes"
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ eval "ac_cv_func_$ac_func=no"
+fi
+rm -f conftest*
+fi
+
+if eval "test \"`echo '$ac_cv_func_'$ac_func`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ cat >> confdefs.h <<EOF
+#define CRAY_STACKSEG_END $ac_func
+EOF
+
+ break
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+done
+fi
+
+echo $ac_n "checking stack direction for C alloca""... $ac_c" 1>&6
+echo "configure:1746: checking stack direction for C alloca" >&5
+if eval "test \"`echo '$''{'ac_cv_c_stack_direction'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ if test "$cross_compiling" = yes; then
+ ac_cv_c_stack_direction=0
+else
+ cat > conftest.$ac_ext <<EOF
+#line 1754 "configure"
+#include "confdefs.h"
+find_stack_direction ()
+{
+ static char *addr = 0;
+ auto char dummy;
+ if (addr == 0)
+ {
+ addr = &dummy;
+ return find_stack_direction ();
+ }
+ else
+ return (&dummy > addr) ? 1 : -1;
+}
+main ()
+{
+ exit (find_stack_direction() < 0);
+}
+EOF
+if { (eval echo configure:1773: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext} && (./conftest; exit) 2>/dev/null
+then
+ ac_cv_c_stack_direction=1
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -fr conftest*
+ ac_cv_c_stack_direction=-1
+fi
+rm -fr conftest*
+fi
+
+fi
+
+echo "$ac_t""$ac_cv_c_stack_direction" 1>&6
+cat >> confdefs.h <<EOF
+#define STACK_DIRECTION $ac_cv_c_stack_direction
+EOF
+
+fi
+
+echo $ac_n "checking whether ${MAKE-make} sets \${MAKE}""... $ac_c" 1>&6
+echo "configure:1795: checking whether ${MAKE-make} sets \${MAKE}" >&5
+set dummy ${MAKE-make}; ac_make=`echo "$2" | sed 'y%./+-%__p_%'`
+if eval "test \"`echo '$''{'ac_cv_prog_make_${ac_make}_set'+set}'`\" = set"; then
+ echo $ac_n "(cached) $ac_c" 1>&6
+else
+ cat > conftestmake <<\EOF
+all:
+ @echo 'ac_maketemp="${MAKE}"'
+EOF
+# GNU make sometimes prints "make[1]: Entering...", which would confuse us.
+eval `${MAKE-make} -f conftestmake 2>/dev/null | grep temp=`
+if test -n "$ac_maketemp"; then
+ eval ac_cv_prog_make_${ac_make}_set=yes
+else
+ eval ac_cv_prog_make_${ac_make}_set=no
+fi
+rm -f conftestmake
+fi
+if eval "test \"`echo '$ac_cv_prog_make_'${ac_make}_set`\" = yes"; then
+ echo "$ac_t""yes" 1>&6
+ SET_MAKE=
+else
+ echo "$ac_t""no" 1>&6
+ SET_MAKE="MAKE=${MAKE-make}"
+fi
+
+
+# Test for builtin mem* functions.
+
+ac_ext=C
+# CXXFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CXXCPP $CPPFLAGS'
+ac_compile='${CXX-g++} -c $CXXFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CXX-g++} -o conftest${ac_exeext} $CXXFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cxx_cross
+
+cat > conftest.$ac_ext <<EOF
+#line 1832 "configure"
+#include "confdefs.h"
+
+#include <string.h>
+void foo(char *s, int c, size_t n)
+{
+ __builtin_memset(s, c, n);
+}
+
+int main() {
+
+; return 0; }
+EOF
+if { (eval echo configure:1845: \"$ac_compile\") 1>&5; (eval $ac_compile) 2>&5; }; then
+ rm -rf conftest*
+ use_builtin_memset=yes
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ use_builtin_memset=no
+fi
+rm -f conftest*
+if test $use_builtin_memset = "yes"; then
+ cat >> confdefs.h <<\EOF
+#define HAVE_BUILTIN_MEMSET 1
+EOF
+
+fi
+ac_ext=c
+# CFLAGS is not in ac_cpp because -g, -O, etc. are not valid cpp options.
+ac_cpp='$CPP $CPPFLAGS'
+ac_compile='${CC-cc} -c $CFLAGS $CPPFLAGS conftest.$ac_ext 1>&5'
+ac_link='${CC-cc} -o conftest${ac_exeext} $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS 1>&5'
+cross_compiling=$ac_cv_prog_cc_cross
+
+
+mt_safe_val=1
+MT_SAFE=yes
+
+# Check whether --enable-threadsafe or --disable-threadsafe was given.
+if test "${enable_threadsafe+set}" = set; then
+ enableval="$enable_threadsafe"
+ case "${enableval}" in
+yes)
+ ;;
+runtime)
+ mt_safe_val=2
+ MT_SAFE=yes
+ ;;
+no)
+ mt_safe_val=0
+ MT_SAFE=no
+ ;;
+esac
+
+fi
+
+
+# Check whether --enable-extra-threadsafe-checking or --disable-extra-threadsafe-checking was given.
+if test "${enable_extra_threadsafe_checking+set}" = set; then
+ enableval="$enable_extra_threadsafe_checking"
+ case "${enableval}" in
+yes)
+ mt_safe_val=1
+ MT_SAFE=yes
+ cat >> confdefs.h <<\EOF
+#define _CYG_THREAD_FAILSAFE 1
+EOF
+
+ ;;
+no)
+ ;;
+esac
+
+fi
+
+
+if test "$MT_SAFE" = "yes"; then
+ cat >> confdefs.h <<EOF
+#define _MT_SAFE $mt_safe_val
+EOF
+
+fi
+
+
+
+# Check whether --enable-debugging or --disable-debugging was given.
+if test "${enable_debugging+set}" = set; then
+ enableval="$enable_debugging"
+ case "${enableval}" in
+yes) cat >> confdefs.h <<\EOF
+#define DEBUGGING 1
+EOF
+ ;;
+no) ;;
+esac
+
+fi
+
+
+MALLOC_OFILES=
+# Check whether --enable-malloc-debugging or --disable-malloc-debugging was given.
+if test "${enable_malloc_debugging+set}" = set; then
+ enableval="$enable_malloc_debugging"
+ case "${enableval}" in
+yes) cat >> confdefs.h <<\EOF
+#define MALLOC_DEBUG 1
+EOF
+
+ MALLOC_OFILES=dlmalloc.o
+ ;;
+no) ;;
+esac
+
+fi
+
+
+# Check whether --enable-vfork or --disable-vfork was given.
+if test "${enable_vfork+set}" = set; then
+ enableval="$enable_vfork"
+ vfork="${enableval}"
+]
+fi
+
+
+case "$vfork" in
+no) ;;
+yes|*) cat >> confdefs.h <<EOF
+#define NEWVFORK 1
+EOF
+ ;;
+esac
+
+
+
+
+echo $ac_n "checking if newlib is part of the build tree""... $ac_c" 1>&6
+echo "configure:1970: checking if newlib is part of the build tree" >&5
+
+EXE_LDFLAGS=
+if test -d ../newlib
+then
+ echo "$ac_t""yes" 1>&6
+ EXE_LDFLAGS="-B../../newlib/ -B../"
+else
+ echo "$ac_t""no" 1>&6
+fi
+
+
+if test x"$EXE_LDFLAGS" = x
+then
+ echo $ac_n "checking if installed newlib needed""... $ac_c" 1>&6
+echo "configure:1985: checking if installed newlib needed" >&5
+ cat > conftest.$ac_ext <<EOF
+#line 1987 "configure"
+#include "confdefs.h"
+
+int main() {
+/* main already defined */
+
+; return 0; }
+EOF
+if { (eval echo configure:1995: \"$ac_link\") 1>&5; (eval $ac_link) 2>&5; } && test -s conftest${ac_exeext}; then
+ rm -rf conftest*
+ echo "$ac_t""no" 1>&6
+
+else
+ echo "configure: failed program was:" >&5
+ cat conftest.$ac_ext >&5
+ rm -rf conftest*
+ echo "$ac_t""yes" 1>&6
+ echo "configure: warning: newlib not found - utility .exe's may not link" 1>&2
+fi
+rm -f conftest*
+fi
+
+
+case "$target_cpu" in
+ i386|i486|i586|i686) DLL_ENTRY="_dll_entry@12"
+ DEF_DLL_ENTRY="dll_entry@12"
+ ALLOCA="_alloca"
+ CONFIG_DIR="i386" ;;
+ powerpc*) DLL_ENTRY="dll_entry"
+ DEF_DLL_ENTRY="dll_entry"
+ ALLOCA=" __allocate_stack"
+ CONFIG_DIR="ppc" ;;
+ *) { echo "configure: error: Invalid target processor \"$target_cpu\"" 1>&2; exit 1; } ;;
+esac
+
+
+
+
+
+
+trap '' 1 2 15
+cat > confcache <<\EOF
+# This file is a shell script that caches the results of configure
+# tests run on this system so they can be shared between configure
+# scripts and configure runs. It is not useful on other systems.
+# If it contains results you don't want to keep, you may remove or edit it.
+#
+# By default, configure uses ./config.cache as the cache file,
+# creating it if it does not exist already. You can give configure
+# the --cache-file=FILE option to use a different cache file; that is
+# what configure does when it calls configure scripts in
+# subdirectories, so they share the cache.
+# Giving --cache-file=/dev/null disables caching, for debugging configure.
+# config.status only pays attention to the cache file if you give it the
+# --recheck option to rerun configure.
+#
+EOF
+# The following way of writing the cache mishandles newlines in values,
+# but we know of no workaround that is simple, portable, and efficient.
+# So, don't put newlines in cache variables' values.
+# Ultrix sh set writes to stderr and can't be redirected directly,
+# and sets the high bit in the cache file unless we assign to the vars.
+(set) 2>&1 |
+ case `(ac_space=' '; set | grep ac_space) 2>&1` in
+ *ac_space=\ *)
+ # `set' does not quote correctly, so add quotes (double-quote substitution
+ # turns \\\\ into \\, and sed turns \\ into \).
+ sed -n \
+ -e "s/'/'\\\\''/g" \
+ -e "s/^\\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\\)=\\(.*\\)/\\1=\${\\1='\\2'}/p"
+ ;;
+ *)
+ # `set' quotes correctly as required by POSIX, so do not add quotes.
+ sed -n -e 's/^\([a-zA-Z0-9_]*_cv_[a-zA-Z0-9_]*\)=\(.*\)/\1=${\1=\2}/p'
+ ;;
+ esac >> confcache
+if cmp -s $cache_file confcache; then
+ :
+else
+ if test -w $cache_file; then
+ echo "updating cache $cache_file"
+ cat confcache > $cache_file
+ else
+ echo "not updating unwritable cache $cache_file"
+ fi
+fi
+rm -f confcache
+
+trap 'rm -fr conftest* confdefs* core core.* *.core $ac_clean_files; exit 1' 1 2 15
+
+test "x$prefix" = xNONE && prefix=$ac_default_prefix
+# Let make expand exec_prefix.
+test "x$exec_prefix" = xNONE && exec_prefix='${prefix}'
+
+# Any assignment to VPATH causes Sun make to only execute
+# the first set of double-colon rules, so remove it if not needed.
+# If there is a colon in the path, we need to keep it.
+if test "x$srcdir" = x.; then
+ ac_vpsub='/^[ ]*VPATH[ ]*=[^:]*$/d'
+fi
+
+trap 'rm -f $CONFIG_STATUS conftest*; exit 1' 1 2 15
+
+DEFS=-DHAVE_CONFIG_H
+
+# Without the "./", some shells look in PATH for config.status.
+: ${CONFIG_STATUS=./config.status}
+
+echo creating $CONFIG_STATUS
+rm -f $CONFIG_STATUS
+cat > $CONFIG_STATUS <<EOF
+#! /bin/sh
+# Generated automatically by configure.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $ac_configure_args
+#
+# Compiler output produced by configure, useful for debugging
+# configure, is in ./config.log if it exists.
+
+ac_cs_usage="Usage: $CONFIG_STATUS [--recheck] [--version] [--help]"
+for ac_option
+do
+ case "\$ac_option" in
+ -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r)
+ echo "running \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion"
+ exec \${CONFIG_SHELL-/bin/sh} $0 $ac_configure_args --no-create --no-recursion ;;
+ -version | --version | --versio | --versi | --vers | --ver | --ve | --v)
+ echo "$CONFIG_STATUS generated by autoconf version 2.13"
+ exit 0 ;;
+ -help | --help | --hel | --he | --h)
+ echo "\$ac_cs_usage"; exit 0 ;;
+ *) echo "\$ac_cs_usage"; exit 1 ;;
+ esac
+done
+
+ac_given_srcdir=$srcdir
+ac_given_INSTALL="$INSTALL"
+
+trap 'rm -fr `echo "Makefile cygwin.def:cygwin.din config.h" | sed "s/:[^ ]*//g"` conftest*; exit 1' 1 2 15
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+# Protect against being on the right side of a sed subst in config.status.
+sed 's/%@/@@/; s/@%/@@/; s/%g\$/@g/; /@g\$/s/[\\\\&%]/\\\\&/g;
+ s/@@/%@/; s/@@/@%/; s/@g\$/%g/' > conftest.subs <<\\CEOF
+$ac_vpsub
+$extrasub
+s%@SHELL@%$SHELL%g
+s%@CFLAGS@%$CFLAGS%g
+s%@CPPFLAGS@%$CPPFLAGS%g
+s%@CXXFLAGS@%$CXXFLAGS%g
+s%@FFLAGS@%$FFLAGS%g
+s%@DEFS@%$DEFS%g
+s%@LDFLAGS@%$LDFLAGS%g
+s%@LIBS@%$LIBS%g
+s%@exec_prefix@%$exec_prefix%g
+s%@prefix@%$prefix%g
+s%@program_transform_name@%$program_transform_name%g
+s%@bindir@%$bindir%g
+s%@sbindir@%$sbindir%g
+s%@libexecdir@%$libexecdir%g
+s%@datadir@%$datadir%g
+s%@sysconfdir@%$sysconfdir%g
+s%@sharedstatedir@%$sharedstatedir%g
+s%@localstatedir@%$localstatedir%g
+s%@libdir@%$libdir%g
+s%@includedir@%$includedir%g
+s%@oldincludedir@%$oldincludedir%g
+s%@infodir@%$infodir%g
+s%@mandir@%$mandir%g
+s%@INSTALL_PROGRAM@%$INSTALL_PROGRAM%g
+s%@INSTALL_SCRIPT@%$INSTALL_SCRIPT%g
+s%@INSTALL_DATA@%$INSTALL_DATA%g
+s%@host@%$host%g
+s%@host_alias@%$host_alias%g
+s%@host_cpu@%$host_cpu%g
+s%@host_vendor@%$host_vendor%g
+s%@host_os@%$host_os%g
+s%@target@%$target%g
+s%@target_alias@%$target_alias%g
+s%@target_cpu@%$target_cpu%g
+s%@target_vendor@%$target_vendor%g
+s%@target_os@%$target_os%g
+s%@build@%$build%g
+s%@build_alias@%$build_alias%g
+s%@build_cpu@%$build_cpu%g
+s%@build_vendor@%$build_vendor%g
+s%@build_os@%$build_os%g
+s%@CC@%$CC%g
+s%@CXX@%$CXX%g
+s%@all_host@%$all_host%g
+s%@install_host@%$install_host%g
+s%@AR@%$AR%g
+s%@AS@%$AS%g
+s%@RANLIB@%$RANLIB%g
+s%@LD@%$LD%g
+s%@NM@%$NM%g
+s%@DLLTOOL@%$DLLTOOL%g
+s%@WINDRES@%$WINDRES%g
+s%@CPP@%$CPP%g
+s%@ALLOCA@%$ALLOCA%g
+s%@SET_MAKE@%$SET_MAKE%g
+s%@MT_SAFE@%$MT_SAFE%g
+s%@EXE_LDFLAGS@%$EXE_LDFLAGS%g
+s%@MALLOC_OFILES@%$MALLOC_OFILES%g
+s%@DLL_ENTRY@%$DLL_ENTRY%g
+s%@DEF_DLL_ENTRY@%$DEF_DLL_ENTRY%g
+s%@CONFIG_DIR@%$CONFIG_DIR%g
+
+CEOF
+EOF
+
+cat >> $CONFIG_STATUS <<\EOF
+
+# Split the substitutions into bite-sized pieces for seds with
+# small command number limits, like on Digital OSF/1 and HP-UX.
+ac_max_sed_cmds=90 # Maximum number of lines to put in a sed script.
+ac_file=1 # Number of current file.
+ac_beg=1 # First line for current file.
+ac_end=$ac_max_sed_cmds # Line after last line for current file.
+ac_more_lines=:
+ac_sed_cmds=""
+while $ac_more_lines; do
+ if test $ac_beg -gt 1; then
+ sed "1,${ac_beg}d; ${ac_end}q" conftest.subs > conftest.s$ac_file
+ else
+ sed "${ac_end}q" conftest.subs > conftest.s$ac_file
+ fi
+ if test ! -s conftest.s$ac_file; then
+ ac_more_lines=false
+ rm -f conftest.s$ac_file
+ else
+ if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds="sed -f conftest.s$ac_file"
+ else
+ ac_sed_cmds="$ac_sed_cmds | sed -f conftest.s$ac_file"
+ fi
+ ac_file=`expr $ac_file + 1`
+ ac_beg=$ac_end
+ ac_end=`expr $ac_end + $ac_max_sed_cmds`
+ fi
+done
+if test -z "$ac_sed_cmds"; then
+ ac_sed_cmds=cat
+fi
+EOF
+
+cat >> $CONFIG_STATUS <<EOF
+
+CONFIG_FILES=\${CONFIG_FILES-"Makefile cygwin.def:cygwin.din"}
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+for ac_file in .. $CONFIG_FILES; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ # Adjust a relative srcdir, top_srcdir, and INSTALL for subdirectories.
+
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ ac_dir_suffix="/`echo $ac_dir|sed 's%^\./%%'`"
+ # A "../" for each directory in $ac_dir_suffix.
+ ac_dots=`echo $ac_dir_suffix|sed 's%/[^/]*%../%g'`
+ else
+ ac_dir_suffix= ac_dots=
+ fi
+
+ case "$ac_given_srcdir" in
+ .) srcdir=.
+ if test -z "$ac_dots"; then top_srcdir=.
+ else top_srcdir=`echo $ac_dots|sed 's%/$%%'`; fi ;;
+ /*) srcdir="$ac_given_srcdir$ac_dir_suffix"; top_srcdir="$ac_given_srcdir" ;;
+ *) # Relative path.
+ srcdir="$ac_dots$ac_given_srcdir$ac_dir_suffix"
+ top_srcdir="$ac_dots$ac_given_srcdir" ;;
+ esac
+
+ case "$ac_given_INSTALL" in
+ [/$]*) INSTALL="$ac_given_INSTALL" ;;
+ *) INSTALL="$ac_dots$ac_given_INSTALL" ;;
+ esac
+
+ echo creating "$ac_file"
+ rm -f "$ac_file"
+ configure_input="Generated automatically from `echo $ac_file_in|sed 's%.*/%%'` by configure."
+ case "$ac_file" in
+ *Makefile*) ac_comsub="1i\\
+# $configure_input" ;;
+ *) ac_comsub= ;;
+ esac
+
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ sed -e "$ac_comsub
+s%@configure_input@%$configure_input%g
+s%@srcdir@%$srcdir%g
+s%@top_srcdir@%$top_srcdir%g
+s%@INSTALL@%$INSTALL%g
+" $ac_file_inputs | (eval "$ac_sed_cmds") > $ac_file
+fi; done
+rm -f conftest.s*
+
+# These sed commands are passed to sed as "A NAME B NAME C VALUE D", where
+# NAME is the cpp macro being defined and VALUE is the value it is being given.
+#
+# ac_d sets the value in "#define NAME VALUE" lines.
+ac_dA='s%^\([ ]*\)#\([ ]*define[ ][ ]*\)'
+ac_dB='\([ ][ ]*\)[^ ]*%\1#\2'
+ac_dC='\3'
+ac_dD='%g'
+# ac_u turns "#undef NAME" with trailing blanks into "#define NAME VALUE".
+ac_uA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_uB='\([ ]\)%\1#\2define\3'
+ac_uC=' '
+ac_uD='\4%g'
+# ac_e turns "#undef NAME" without trailing blanks into "#define NAME VALUE".
+ac_eA='s%^\([ ]*\)#\([ ]*\)undef\([ ][ ]*\)'
+ac_eB='$%\1#\2define\3'
+ac_eC=' '
+ac_eD='%g'
+
+if test "${CONFIG_HEADERS+set}" != set; then
+EOF
+cat >> $CONFIG_STATUS <<EOF
+ CONFIG_HEADERS="config.h"
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+fi
+for ac_file in .. $CONFIG_HEADERS; do if test "x$ac_file" != x..; then
+ # Support "outfile[:infile[:infile...]]", defaulting infile="outfile.in".
+ case "$ac_file" in
+ *:*) ac_file_in=`echo "$ac_file"|sed 's%[^:]*:%%'`
+ ac_file=`echo "$ac_file"|sed 's%:.*%%'` ;;
+ *) ac_file_in="${ac_file}.in" ;;
+ esac
+
+ echo creating $ac_file
+
+ rm -f conftest.frag conftest.in conftest.out
+ ac_file_inputs=`echo $ac_file_in|sed -e "s%^%$ac_given_srcdir/%" -e "s%:% $ac_given_srcdir/%g"`
+ cat $ac_file_inputs > conftest.in
+
+EOF
+
+# Transform confdefs.h into a sed script conftest.vals that substitutes
+# the proper values into config.h.in to produce config.h. And first:
+# Protect against being on the right side of a sed subst in config.status.
+# Protect against being in an unquoted here document in config.status.
+rm -f conftest.vals
+cat > conftest.hdr <<\EOF
+s/[\\&%]/\\&/g
+s%[\\$`]%\\&%g
+s%#define \([A-Za-z_][A-Za-z0-9_]*\) *\(.*\)%${ac_dA}\1${ac_dB}\1${ac_dC}\2${ac_dD}%gp
+s%ac_d%ac_u%gp
+s%ac_u%ac_e%gp
+EOF
+sed -n -f conftest.hdr confdefs.h > conftest.vals
+rm -f conftest.hdr
+
+# This sed command replaces #undef with comments. This is necessary, for
+# example, in the case of _POSIX_SOURCE, which is predefined and required
+# on some systems where configure will not decide to define it.
+cat >> conftest.vals <<\EOF
+s%^[ ]*#[ ]*undef[ ][ ]*[a-zA-Z_][a-zA-Z_0-9]*%/* & */%
+EOF
+
+# Break up conftest.vals because some shells have a limit on
+# the size of here documents, and old seds have small limits too.
+
+rm -f conftest.tail
+while :
+do
+ ac_lines=`grep -c . conftest.vals`
+ # grep -c gives empty output for an empty file on some AIX systems.
+ if test -z "$ac_lines" || test "$ac_lines" -eq 0; then break; fi
+ # Write a limited-size here document to conftest.frag.
+ echo ' cat > conftest.frag <<CEOF' >> $CONFIG_STATUS
+ sed ${ac_max_here_lines}q conftest.vals >> $CONFIG_STATUS
+ echo 'CEOF
+ sed -f conftest.frag conftest.in > conftest.out
+ rm -f conftest.in
+ mv conftest.out conftest.in
+' >> $CONFIG_STATUS
+ sed 1,${ac_max_here_lines}d conftest.vals > conftest.tail
+ rm -f conftest.vals
+ mv conftest.tail conftest.vals
+done
+rm -f conftest.vals
+
+cat >> $CONFIG_STATUS <<\EOF
+ rm -f conftest.frag conftest.h
+ echo "/* $ac_file. Generated automatically by configure. */" > conftest.h
+ cat conftest.in >> conftest.h
+ rm -f conftest.in
+ if cmp -s $ac_file conftest.h 2>/dev/null; then
+ echo "$ac_file is unchanged"
+ rm -f conftest.h
+ else
+ # Remove last slash and all that follows it. Not all systems have dirname.
+ ac_dir=`echo $ac_file|sed 's%/[^/][^/]*$%%'`
+ if test "$ac_dir" != "$ac_file" && test "$ac_dir" != .; then
+ # The file is in a subdirectory.
+ test ! -d "$ac_dir" && mkdir "$ac_dir"
+ fi
+ rm -f $ac_file
+ mv conftest.h $ac_file
+ fi
+fi; done
+
+EOF
+cat >> $CONFIG_STATUS <<EOF
+
+EOF
+cat >> $CONFIG_STATUS <<\EOF
+
+exit 0
+EOF
+chmod +x $CONFIG_STATUS
+rm -fr confdefs* $ac_clean_files
+test "$no_create" = yes || ${CONFIG_SHELL-/bin/sh} $CONFIG_STATUS || exit 1
+
diff --git a/winsup/cygwin/configure.in b/winsup/cygwin/configure.in
new file mode 100644
index 00000000000..864c04a273a
--- /dev/null
+++ b/winsup/cygwin/configure.in
@@ -0,0 +1,249 @@
+dnl Autoconf configure script for Cygwin.
+dnl Copyright 1996, 1997, 1998, 2000, 2001 Red Hat, Inc.
+dnl
+dnl This file is part of Cygwin.
+dnl
+dnl This software is a copyrighted work licensed under the terms of the
+dnl Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+dnl details.
+dnl
+dnl Process this file with autoconf to produce a configure script.
+
+AC_PREREQ(2.12)dnl
+AC_INIT(init.cc)
+AC_CONFIG_HEADER(config.h)
+
+AC_PROG_INSTALL
+
+dnl FIXME: We temporarily define our own version of AC_PROG_CC. This is
+dnl copied from autoconf 2.12, but does not call AC_PROG_CC_WORKS. We
+dnl are probably using a cross compiler, which will not be able to fully
+dnl link an executable. This should really be fixed in autoconf
+dnl itself.
+
+AC_DEFUN(LIB_AC_PROG_CC,
+[AC_BEFORE([$0], [AC_PROG_CPP])dnl
+AC_CHECK_TOOL(CC, gcc, gcc)
+if test -z "$CC"; then
+ AC_CHECK_PROG(CC, cc, cc, , , /usr/ucb/cc)
+ test -z "$CC" && AC_MSG_ERROR([no acceptable cc found in \$PATH])
+fi
+
+AC_PROG_CC_GNU
+
+if test $ac_cv_prog_gcc = yes; then
+ GCC=yes
+dnl Check whether -g works, even if CFLAGS is set, in case the package
+dnl plays around with CFLAGS (such as to build both debugging and
+dnl normal versions of a library), tasteless as that idea is.
+ ac_test_CFLAGS="${CFLAGS+set}"
+ ac_save_CFLAGS="$CFLAGS"
+ CFLAGS=
+ AC_PROG_CC_G
+ if test "$ac_test_CFLAGS" = set; then
+ CFLAGS="$ac_save_CFLAGS"
+ elif test $ac_cv_prog_cc_g = yes; then
+ CFLAGS="-gstabs+ -O2"
+ else
+ CFLAGS="-O2"
+ fi
+else
+ GCC=
+ test "${CFLAGS+set}" = set || CFLAGS="-g"
+fi
+])
+
+AC_DEFUN(LIB_AC_PROG_CXX,
+[AC_BEFORE([$0], [AC_PROG_CPP])dnl
+AC_CHECK_TOOL(CXX, g++, g++)
+if test -z "$CXX"; then
+ AC_CHECK_PROG(CXX, c++, c++, , , )
+ test -z "$CC" && AC_MSG_ERROR([no acceptable cc found in \$PATH])
+fi
+
+CXXFLAGS='$(CFLAGS)'
+])
+
+AC_CANONICAL_SYSTEM
+
+LIB_AC_PROG_CC
+LIB_AC_PROG_CXX
+
+case "$with_cross_host" in
+ ""|*cygwin*)
+ all_host="all_host"
+ install_host="install_host"
+ ;;
+ *)
+ all_host=
+ install_host=
+ ;;
+esac
+
+AC_SUBST(all_host)
+AC_SUBST(install_host)
+
+AC_CHECK_TOOL(AR, ar, ar)
+AC_CHECK_TOOL(AS, as, as)
+AC_CHECK_TOOL(RANLIB, ranlib, ranlib)
+AC_CHECK_TOOL(LD, ld, ld)
+AC_CHECK_TOOL(NM, nm, nm)
+AC_CHECK_TOOL(DLLTOOL, dlltool, dlltool)
+AC_CHECK_TOOL(WINDRES, windres, windres)
+
+AC_ALLOCA
+AC_PROG_MAKE_SET
+
+dnl check whether gcc supports __builtin_memset.
+# Test for builtin mem* functions.
+AC_LANG_SAVE
+AC_LANG_CPLUSPLUS
+AC_TRY_COMPILE([
+#include <string.h>
+void foo(char *s, int c, size_t n)
+{
+ __builtin_memset(s, c, n);
+}
+], [ ],
+use_builtin_memset=yes, use_builtin_memset=no)
+if test $use_builtin_memset = "yes"; then
+ AC_DEFINE(HAVE_BUILTIN_MEMSET)
+fi
+AC_LANG_RESTORE
+
+dnl set default mt safeness and then process the options.
+mt_safe_val=1
+MT_SAFE=yes
+
+AC_ARG_ENABLE(threadsafe,
+[ --enable-threadsafe=[runtime] Build a cygwin DLL which is thread safe],
+[case "${enableval}" in
+yes)
+ dnl default.
+ ;;
+runtime)
+ mt_safe_val=2
+ MT_SAFE=yes
+ ;;
+no)
+ mt_safe_val=0
+ MT_SAFE=no
+ ;;
+esac
+])
+
+AC_ARG_ENABLE(extra-threadsafe-checking,
+[ --enable-extra-threadsafe-checking Build a cygwin DLL which is thread safe with extra consistency checking],
+[case "${enableval}" in
+yes)
+ mt_safe_val=1
+ MT_SAFE=yes
+ AC_DEFINE(_CYG_THREAD_FAILSAFE)
+ ;;
+no)
+ dnl Don't do anything here to avoid overriding --enable-threadsafe.
+ ;;
+esac
+])
+
+if test "$MT_SAFE" = "yes"; then
+ AC_DEFINE_UNQUOTED(_MT_SAFE,$mt_safe_val)
+fi
+
+dnl Makefile uses MT_SAFE, so we subst as well as defining it.
+AC_SUBST(MT_SAFE)
+
+AC_ARG_ENABLE(debugging,
+[ --enable-debugging Build a cygwin DLL which has more consistency checking for debugging],
+[case "${enableval}" in
+yes) AC_DEFINE(DEBUGGING) ;;
+no) ;;
+esac
+])
+
+MALLOC_OFILES=
+AC_ARG_ENABLE(malloc-debugging,
+[ --enable-malloc-debugging Build a cygwin DLL with heap sanity checking (this is very slow, use only if you have heap corruption problems)],
+[case "${enableval}" in
+yes) AC_DEFINE(MALLOC_DEBUG)
+ MALLOC_OFILES=dlmalloc.o
+ ;;
+no) ;;
+esac
+])
+
+AC_ARG_ENABLE(vfork,
+[ --enable-vfork Build a cygwin DLL which uses experimental vfork code],
+vfork="${enableval}"
+])
+
+case "$vfork" in
+no) ;;
+yes|*) AC_DEFINE_UNQUOTED(NEWVFORK) ;;
+esac
+
+dnl The only time we might want to transform the install names
+dnl is for unix x cygwin. Otherwise we don't. For now we don't
+dnl transform names.
+
+dnl if test "x$cross_compiling" = "xno" -a ; then
+dnl if test "x$program_transform_name" = "xs,x,x,"; then
+dnl program_transform_name=""
+dnl fi
+dnl if test "x$program_transform_name" = "x"; then
+dnl program_transform_name="s,^,$target_alias-,"
+dnl else
+dnl program_transform_name="$program_transform_name -e s,^,$target_alias-,"
+dnl fi
+dnl fi
+
+dnl
+dnl If newlib is part of build tree, always set EXE_LDFLAGS to point to
+dnl it; this is important in cases where the installed newlib is perhaps
+dnl not compatible. Check and warn for installed newlib only if it's not
+dnl part of the build tree.
+dnl
+
+AC_MSG_CHECKING([if newlib is part of the build tree])
+
+EXE_LDFLAGS=
+if test -d ../newlib
+then
+ AC_MSG_RESULT(yes)
+ EXE_LDFLAGS="-B../../newlib/ -B../"
+else
+ AC_MSG_RESULT(no)
+fi
+ AC_SUBST(EXE_LDFLAGS)
+
+if test x"$EXE_LDFLAGS" = x
+then
+ AC_MSG_CHECKING([if installed newlib needed])
+ AC_TRY_LINK(,
+ [/* main already defined */]
+ ,
+ AC_MSG_RESULT(no)
+ ,
+ AC_MSG_RESULT(yes)
+ AC_MSG_WARN(newlib not found - utility .exe's may not link))
+fi
+AC_SUBST(EXE_LDFLAGS)
+
+case "$target_cpu" in
+ i386|i486|i586|i686) DLL_ENTRY="_dll_entry@12"
+ DEF_DLL_ENTRY="dll_entry@12"
+ ALLOCA="_alloca"
+ CONFIG_DIR="i386" ;;
+ powerpc*) DLL_ENTRY="dll_entry"
+ DEF_DLL_ENTRY="dll_entry"
+ ALLOCA=" __allocate_stack"
+ CONFIG_DIR="ppc" ;;
+ *) AC_MSG_ERROR(Invalid target processor \"$target_cpu\") ;;
+esac
+
+AC_SUBST(MALLOC_OFILES)
+AC_SUBST(DLL_ENTRY)
+AC_SUBST(DEF_DLL_ENTRY)
+AC_SUBST(ALLOCA)
+AC_SUBST(CONFIG_DIR)
+AC_OUTPUT(Makefile cygwin.def:cygwin.din)
diff --git a/winsup/cygwin/dcrt0.cc b/winsup/cygwin/dcrt0.cc
new file mode 100644
index 00000000000..4e4034bf663
--- /dev/null
+++ b/winsup/cygwin/dcrt0.cc
@@ -0,0 +1,1141 @@
+/* dcrt0.cc -- essentially the main() for the Cygwin dll
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include "glob.h"
+#include "exceptions.h"
+#include <ctype.h>
+#include <limits.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <errno.h>
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygerrno.h"
+#define NEED_VFORK
+#include "perprocess.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "child_info_magic.h"
+#include "perthread.h"
+#include "shared_info.h"
+#include "cygwin_version.h"
+#include "dll_init.h"
+#include "cygthread.h"
+
+#define MAX_AT_FILE_LEVEL 10
+
+#define PREMAIN_LEN (sizeof (user_data->premain) / sizeof (user_data->premain[0]))
+
+HANDLE NO_COPY hMainProc;
+HANDLE NO_COPY hMainThread;
+
+sigthread NO_COPY mainthread; // ID of the main thread
+
+per_thread_waitq NO_COPY waitq_storage;
+per_thread_vfork NO_COPY vfork_storage;
+per_thread_signal_dispatch NO_COPY signal_dispatch_storage;
+
+per_thread NO_COPY *threadstuff[] = {&waitq_storage,
+ &vfork_storage,
+ &signal_dispatch_storage,
+ NULL};
+
+BOOL display_title;
+BOOL strip_title_path;
+BOOL allow_glob = TRUE;
+codepage_type current_codepage = ansi_cp;
+
+int cygwin_finished_initializing;
+
+/* Used in SIGTOMASK for generating a bit for insertion into a sigset_t.
+ This is subtracted from the signal number prior to shifting the bit.
+ In older versions of cygwin, the signal was used as-is to shift the
+ bit for masking. So, we'll temporarily detect this and set it to zero
+ for programs that are linked using older cygwins. This is just a stopgap
+ measure to allow an orderly transfer to the new, correct sigmask method. */
+unsigned NO_COPY int signal_shift_subtract = 1;
+
+ResourceLocks _reslock NO_COPY;
+MTinterface _mtinterf;
+
+bool NO_COPY _cygwin_testing;
+unsigned NO_COPY _cygwin_testing_magic;
+
+char NO_COPY almost_null[1];
+
+extern "C"
+{
+ /* This is an exported copy of environ which can be used by DLLs
+ which use cygwin.dll. */
+ char **__cygwin_environ;
+ char ***main_environ;
+ /* __progname used in getopt error message */
+ char *__progname;
+ struct _reent reent_data = _REENT_INIT(reent_data);
+ struct per_process __cygwin_user_data =
+ {/* initial_sp */ 0, /* magic_biscuit */ 0,
+ /* dll_major */ CYGWIN_VERSION_DLL_MAJOR,
+ /* dll_major */ CYGWIN_VERSION_DLL_MINOR,
+ /* impure_ptr_ptr */ NULL, /* envptr */ NULL,
+ /* malloc */ malloc, /* free */ free,
+ /* realloc */ realloc,
+ /* fmode_ptr */ NULL, /* main */ NULL, /* ctors */ NULL,
+ /* dtors */ NULL, /* data_start */ NULL, /* data_end */ NULL,
+ /* bss_start */ NULL, /* bss_end */ NULL,
+ /* calloc */ calloc,
+ /* premain */ {NULL, NULL, NULL, NULL},
+ /* run_ctors_p */ 0,
+ /* unused */ {0, 0, 0, 0, 0, 0, 0},
+ /* forkee */ 0,
+ /* hmodule */ NULL,
+ /* api_major */ CYGWIN_VERSION_API_MAJOR,
+ /* api_minor */ CYGWIN_VERSION_API_MINOR,
+ /* unused2 */ {0, 0, 0, 0, 0},
+ /* resourcelocks */ &_reslock, /* threadinterface */ &_mtinterf,
+ /* impure_ptr */ &reent_data,
+ };
+ bool ignore_case_with_glob;
+ int __declspec (dllexport) _check_for_executable = TRUE;
+#ifdef DEBUGGING
+ int pinger;
+#endif
+};
+
+char *old_title;
+char title_buf[TITLESIZE + 1];
+
+static void
+do_global_dtors (void)
+{
+ if (user_data->dtors)
+ {
+ void (**pfunc)() = user_data->dtors;
+ while (*++pfunc)
+ (*pfunc) ();
+ }
+}
+
+static void __stdcall
+do_global_ctors (void (**in_pfunc)(), int force)
+{
+ if (!force)
+ {
+ if (user_data->forkee || user_data->run_ctors_p)
+ return; // inherit constructed stuff from parent pid
+ user_data->run_ctors_p = 1;
+ }
+
+ /* Run ctors backwards, so skip the first entry and find how many
+ there are, then run them. */
+
+ void (**pfunc)() = in_pfunc;
+
+ while (*++pfunc)
+ ;
+ while (--pfunc > in_pfunc)
+ (*pfunc) ();
+
+ if (user_data->magic_biscuit == SIZEOF_PER_PROCESS)
+ atexit (do_global_dtors);
+}
+
+/*
+ * Replaces @file in the command line with the contents of the file.
+ * There may be multiple @file's in a single command line
+ * A \@file is replaced with @file so that echo \@foo would print
+ * @foo and not the contents of foo.
+ */
+static int __stdcall
+insert_file (char *name, char *&cmd)
+{
+ HANDLE f;
+ DWORD size;
+
+ f = CreateFile (name + 1,
+ GENERIC_READ, /* open for reading */
+ FILE_SHARE_READ, /* share for reading */
+ &sec_none_nih, /* no security */
+ OPEN_EXISTING, /* existing file only */
+ FILE_ATTRIBUTE_NORMAL, /* normal file */
+ NULL); /* no attr. template */
+
+ if (f == INVALID_HANDLE_VALUE)
+ {
+ debug_printf ("couldn't open file '%s', %E", name);
+ return FALSE;
+ }
+
+ /* This only supports files up to about 4 billion bytes in
+ size. I am making the bold assumption that this is big
+ enough for this feature */
+ size = GetFileSize (f, NULL);
+ if (size == 0xFFFFFFFF)
+ {
+ debug_printf ("couldn't get file size for '%s', %E", name);
+ return FALSE;
+ }
+
+ int new_size = strlen (cmd) + size + 2;
+ char *tmp = (char *) malloc (new_size);
+ if (!tmp)
+ {
+ debug_printf ("malloc failed, %E");
+ return FALSE;
+ }
+
+ /* realloc passed as it should */
+ DWORD rf_read;
+ BOOL rf_result;
+ rf_result = ReadFile (f, tmp, size, &rf_read, NULL);
+ CloseHandle (f);
+ if (!rf_result || (rf_read != size))
+ {
+ debug_printf ("ReadFile failed, %E");
+ return FALSE;
+ }
+
+ tmp[size++] = ' ';
+ strcpy (tmp + size, cmd);
+ cmd = tmp;
+ return TRUE;
+}
+
+static inline int
+isquote (char c)
+{
+ char ch = c;
+ return ch == '"' || ch == '\'';
+}
+
+/* Step over a run of characters delimited by quotes */
+static /*__inline*/ char *
+quoted (char *cmd, int winshell)
+{
+ char *p;
+ char quote = *cmd;
+
+ if (!winshell)
+ {
+ char *p;
+ strcpy (cmd, cmd + 1);
+ if ((p = strchr (cmd, quote)) != NULL)
+ strcpy (p, p + 1);
+ else
+ p = strchr (cmd, '\0');
+ return p;
+ }
+
+ const char *s = quote == '\'' ? "'" : "\\\"";
+ /* This must have been run from a Windows shell, so preserve
+ quotes for globify to play with later. */
+ while (*cmd && *++cmd)
+ if ((p = strpbrk (cmd, s)) == NULL)
+ {
+ cmd = strchr (cmd, '\0'); // no closing quote
+ break;
+ }
+ else if (*p == '\\')
+ cmd = ++p;
+ else if (quote == '"' && p[1] == '"')
+ {
+ *p = '\\';
+ cmd = ++p; // a quoted quote
+ }
+ else
+ {
+ cmd = p + 1; // point to after end
+ break;
+ }
+ return cmd;
+}
+
+/* Perform a glob on word if it contains wildcard characters.
+ Also quote every character between quotes to force glob to
+ treat the characters literally. */
+static int __stdcall
+globify (char *word, char **&argv, int &argc, int &argvlen)
+{
+ if (*word != '~' && strpbrk (word, "?*[\"\'(){}") == NULL)
+ return 0;
+
+ int n = 0;
+ char *p, *s;
+ int dos_spec = isdrive (word);
+ if (!dos_spec && isquote (*word) && word[1] && word[2])
+ dos_spec = isdrive (word + 1);
+
+ /* We'll need more space if there are quoting characters in
+ word. If that is the case, doubling the size of the
+ string should provide more than enough space. */
+ if (strpbrk (word, "'\""))
+ n = strlen (word);
+ char pattern[strlen (word) + ((dos_spec + 1) * n) + 1];
+
+ /* Fill pattern with characters from word, quoting any
+ characters found within quotes. */
+ for (p = pattern, s = word; *s != '\000'; s++, p++)
+ if (!isquote (*s))
+ {
+ if (dos_spec && *s == '\\')
+ *p++ = '\\';
+ *p = *s;
+ }
+ else
+ {
+ char quote = *s;
+ while (*++s && *s != quote)
+ {
+ if (dos_spec || *s != '\\')
+ /* nothing */;
+ else if (s[1] == quote || s[1] == '\\')
+ s++;
+ *p++ = '\\';
+ *p++ = *s;
+ }
+ if (*s == quote)
+ p--;
+ if (*s == '\0')
+ break;
+ }
+
+ *p = '\0';
+
+ glob_t gl;
+ gl.gl_offs = 0;
+
+ /* Attempt to match the argument. Return just word (minus quoting) if no match. */
+ if (glob (pattern, GLOB_TILDE | GLOB_NOCHECK | GLOB_BRACE | GLOB_QUOTE, NULL, &gl) || !gl.gl_pathc)
+ return 0;
+
+ /* Allocate enough space in argv for the matched filenames. */
+ n = argc;
+ if ((argc += gl.gl_pathc) > argvlen)
+ {
+ argvlen = argc + 10;
+ argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0]));
+ }
+
+ /* Copy the matched filenames to argv. */
+ char **gv = gl.gl_pathv;
+ char **av = argv + n;
+ while (*gv)
+ {
+ debug_printf ("argv[%d] = '%s'", n++, *gv);
+ *av++ = *gv++;
+ }
+
+ /* Clean up after glob. */
+ free (gl.gl_pathv);
+ return 1;
+}
+
+/* Build argv, argc from string passed from Windows. */
+
+static void __stdcall
+build_argv (char *cmd, char **&argv, int &argc, int winshell)
+{
+ int argvlen = 0;
+ int nesting = 0; // monitor "nesting" from insert_file
+
+ argc = 0;
+ argvlen = 0;
+ argv = NULL;
+
+ /* Scan command line until there is nothing left. */
+ while (*cmd)
+ {
+ /* Ignore spaces */
+ if (issep (*cmd))
+ {
+ cmd++;
+ continue;
+ }
+
+ /* Found the beginning of an argument. */
+ char *word = cmd;
+ char *sawquote = NULL;
+ while (*cmd)
+ {
+ if (*cmd != '"' && (!winshell || *cmd != '\''))
+ cmd++; // Skip over this character
+ else
+ /* Skip over characters until the closing quote */
+ {
+ sawquote = cmd;
+ cmd = quoted (cmd, winshell && argc > 0);
+ }
+ if (issep (*cmd)) // End of argument if space
+ break;
+ }
+ if (*cmd)
+ *cmd++ = '\0'; // Terminate `word'
+
+ /* Possibly look for @file construction assuming that this isn't
+ the very first argument and the @ wasn't quoted */
+ if (argc && sawquote != word && *word == '@')
+ {
+ if (++nesting > MAX_AT_FILE_LEVEL)
+ api_fatal ("Too many levels of nesting for %s", word);
+ if (insert_file (word, cmd))
+ continue; // There's new stuff in cmd now
+ }
+
+ /* See if we need to allocate more space for argv */
+ if (argc >= argvlen)
+ {
+ argvlen = argc + 10;
+ argv = (char **) realloc (argv, (1 + argvlen) * sizeof (argv[0]));
+ }
+
+ /* Add word to argv file after (optional) wildcard expansion. */
+ if (!winshell || !argc || !globify (word, argv, argc, argvlen))
+ {
+ debug_printf ("argv[%d] = '%s'", argc, word);
+ argv[argc++] = word;
+ }
+ }
+
+ argv[argc] = NULL;
+
+ debug_printf ("argc %d", argc);
+}
+
+/* sanity and sync check */
+void __stdcall
+check_sanity_and_sync (per_process *p)
+{
+ /* Sanity check to make sure developers didn't change the per_process */
+ /* struct without updating SIZEOF_PER_PROCESS [it makes them think twice */
+ /* about changing it]. */
+ if (sizeof (per_process) != SIZEOF_PER_PROCESS)
+ {
+ api_fatal ("per_process sanity check failed");
+ }
+
+ /* Make sure that the app and the dll are in sync. */
+
+ /* Complain if older than last incompatible change */
+ if (p->dll_major < CYGWIN_VERSION_DLL_EPOCH)
+ api_fatal ("cygwin DLL and APP are out of sync -- DLL version mismatch %d < %d",
+ p->dll_major, CYGWIN_VERSION_DLL_EPOCH);
+
+ /* magic_biscuit != 0 if using the old style version numbering scheme. */
+ if (p->magic_biscuit != SIZEOF_PER_PROCESS)
+ api_fatal ("Incompatible cygwin .dll -- incompatible per_process info %d != %d",
+ p->magic_biscuit, SIZEOF_PER_PROCESS);
+
+ /* Complain if incompatible API changes made */
+ if (p->api_major != cygwin_version.api_major)
+ api_fatal ("cygwin DLL and APP are out of sync -- API version mismatch %d < %d",
+ p->api_major, cygwin_version.api_major);
+
+ if (CYGWIN_VERSION_DLL_MAKE_COMBINED (p->dll_major, p->dll_minor) <=
+ CYGWIN_VERSION_DLL_BAD_SIGNAL_MASK)
+ signal_shift_subtract = 0;
+}
+
+child_info NO_COPY *child_proc_info = NULL;
+static MEMORY_BASIC_INFORMATION NO_COPY sm;
+
+#define CYGWIN_GUARD ((wincap.has_page_guard ()) ? \
+ PAGE_EXECUTE_READWRITE|PAGE_GUARD : PAGE_NOACCESS)
+
+// __inline__ void
+extern void
+alloc_stack_hard_way (child_info_fork *ci, volatile char *b)
+{
+ void *new_stack_pointer;
+ MEMORY_BASIC_INFORMATION m;
+ void *newbase;
+ int newlen;
+ LPBYTE curbot = (LPBYTE) sm.BaseAddress + sm.RegionSize;
+ bool noguard;
+
+ if (ci->stacktop > (LPBYTE) sm.AllocationBase && ci->stacktop < curbot)
+ {
+ newbase = curbot;
+ newlen = (LPBYTE) ci->stackbottom - (LPBYTE) curbot;
+ noguard = 1;
+ }
+ else
+ {
+ newbase = ci->stacktop;
+ newlen = (DWORD) ci->stackbottom - (DWORD) ci->stacktop;
+ noguard = 0;
+ }
+ if (!VirtualAlloc (newbase, newlen, MEM_RESERVE, PAGE_NOACCESS))
+ api_fatal ("fork: can't reserve memory for stack %p - %p, %E",
+ ci->stacktop, ci->stackbottom);
+
+ new_stack_pointer = (void *) ((LPBYTE) ci->stackbottom - ci->stacksize);
+
+ if (!VirtualAlloc (new_stack_pointer, ci->stacksize, MEM_COMMIT,
+ PAGE_EXECUTE_READWRITE))
+ api_fatal ("fork: can't commit memory for stack %p(%d), %E",
+ new_stack_pointer, ci->stacksize);
+ if (!VirtualQuery ((LPCVOID) new_stack_pointer, &m, sizeof m))
+ api_fatal ("fork: couldn't get new stack info, %E");
+ if (!noguard)
+ {
+ m.BaseAddress = (LPVOID)((DWORD)m.BaseAddress - 1);
+ if (!VirtualAlloc ((LPVOID) m.BaseAddress, 1, MEM_COMMIT,
+ CYGWIN_GUARD))
+ api_fatal ("fork: couldn't allocate new stack guard page %p, %E",
+ m.BaseAddress);
+ }
+ if (!VirtualQuery ((LPCVOID) m.BaseAddress, &m, sizeof m))
+ api_fatal ("fork: couldn't get new stack info, %E");
+ ci->stacktop = m.BaseAddress;
+ *b = 0;
+}
+
+/* extend the stack prior to fork longjmp */
+
+static void
+alloc_stack (child_info_fork *ci)
+{
+ /* FIXME: adding 16384 seems to avoid a stack copy problem during
+ fork on Win95, but I don't know exactly why yet. DJ */
+ volatile char b[ci->stacksize + 16384];
+
+ if (!VirtualQuery ((LPCVOID) &b, &sm, sizeof sm))
+ api_fatal ("fork: couldn't get stack info, %E");
+
+ if (sm.AllocationBase != ci->stacktop)
+ alloc_stack_hard_way (ci, b + sizeof (b) - 1);
+ else
+ ci->stacksize = 0;
+
+ return;
+}
+
+static NO_COPY int mypid = 0;
+int _declspec(dllexport) __argc;
+char _declspec(dllexport) **__argv;
+vfork_save NO_COPY *main_vfork = NULL;
+
+void
+sigthread::init (const char *s)
+{
+ InitializeCriticalSection (&lock);
+ id = GetCurrentThreadId ();
+}
+
+/* Take over from libc's crt0.o and start the application. Note the
+ various special cases when Cygwin DLL is being runtime loaded (as
+ opposed to being link-time loaded by Cygwin apps) from a non
+ cygwin app via LoadLibrary. */
+static void
+dll_crt0_1 ()
+{
+ /* According to onno@stack.urc.tue.nl, the exception handler record must
+ be on the stack. */
+ /* FIXME: Verify forked children get their exception handler set up ok. */
+ exception_list cygwin_except_entry;
+
+ /* Initialize SIGSEGV handling, etc. */
+ init_exceptions (&cygwin_except_entry);
+
+ /* Set the os_being_run global. */
+ wincap.init ();
+ device::init ();
+ check_sanity_and_sync (user_data);
+
+ do_global_ctors (&__CTOR_LIST__, 1);
+
+ /* Nasty static stuff needed by newlib -- point to a local copy of
+ the reent stuff.
+ Note: this MUST be done here (before the forkee code) as the
+ fork copy code doesn't copy the data in libccrt0.cc (that's why we
+ pass in the per_process struct into the .dll from libccrt0). */
+
+ _impure_ptr = &reent_data;
+
+ user_data->resourcelocks->Init ();
+ user_data->threadinterface->Init (user_data->forkee);
+
+ mainthread.init ("mainthread"); // For use in determining if signals
+ // should be blocked.
+
+ winpids::init ();
+
+ int envc = 0;
+ char **envp = NULL;
+
+ if (!child_proc_info)
+ memory_init ();
+ else
+ {
+ bool close_ppid_handle = false;
+ bool close_hexec_proc = false;
+ switch (child_proc_info->type)
+ {
+ case _PROC_FORK:
+ alloc_stack (fork_info);
+ cygheap_fixup_in_child (0);
+ memory_init ();
+ set_myself (mypid);
+ close_ppid_handle = !!child_proc_info->pppid_handle;
+ break;
+ case _PROC_SPAWN:
+ /* Have to delay closes until after cygheap is setup */
+ close_hexec_proc = !!spawn_info->hexec_proc;
+ close_ppid_handle = !!child_proc_info->pppid_handle;
+ goto around;
+ case _PROC_EXEC:
+ hexec_proc = spawn_info->hexec_proc;
+ around:
+ HANDLE h;
+ cygheap_fixup_in_child (1);
+ memory_init ();
+ if (!spawn_info->moreinfo->myself_pinfo ||
+ !DuplicateHandle (hMainProc, spawn_info->moreinfo->myself_pinfo,
+ hMainProc, &h, 0, 0,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ h = NULL;
+ set_myself (mypid, h);
+ __argc = spawn_info->moreinfo->argc;
+ __argv = spawn_info->moreinfo->argv;
+ envp = spawn_info->moreinfo->envp;
+ envc = spawn_info->moreinfo->envc;
+ cygheap->fdtab.fixup_after_exec (spawn_info->parent);
+ signal_fixup_after_exec (child_proc_info->type == PROC_SPAWN);
+ CloseHandle (spawn_info->parent);
+ if (spawn_info->moreinfo->old_title)
+ {
+ old_title = strcpy (title_buf, spawn_info->moreinfo->old_title);
+ cfree (spawn_info->moreinfo->old_title);
+ }
+ break;
+ }
+ if (close_hexec_proc)
+ CloseHandle (spawn_info->hexec_proc);
+ if (close_ppid_handle)
+ CloseHandle (child_proc_info->pppid_handle);
+ }
+
+ ProtectHandle (hMainProc);
+ ProtectHandle (hMainThread);
+ cygthread::init ();
+
+ pthread::initMainThread (!user_data->forkee);
+
+ /* Initialize debug muto, if DLL is built with --enable-debugging.
+ Need to do this before any helper threads start. */
+ debug_init ();
+
+ cygheap->fdtab.vfork_child_fixup ();
+
+ (void) SetErrorMode (SEM_FAILCRITICALERRORS);
+
+ /* Initialize events. */
+ events_init ();
+
+ cygheap->cwd.init ();
+ main_vfork = vfork_storage.create ();
+
+ cygbench ("pre-forkee");
+ if (user_data->forkee)
+ {
+ /* If we've played with the stack, stacksize != 0. That means that
+ fork() was invoked from other than the main thread. Make sure that
+ frame pointer is referencing the new stack so that the OS knows what
+ to do when it needs to increase the size of the stack.
+
+ NOTE: Don't do anything that involves the stack until you've completed
+ this step. */
+ if (fork_info->stacksize)
+ {
+ asm ("movl %0,%%fs:4" : : "r" (fork_info->stackbottom));
+ asm ("movl %0,%%fs:8" : : "r" (fork_info->stacktop));
+ }
+
+ longjmp (fork_info->jmp, fork_info->cygpid);
+ }
+
+#ifdef DEBUGGING
+ {
+ extern void fork_init ();
+ fork_init ();
+ }
+#endif
+
+ /* Initialize our process table entry. */
+ pinfo_init (envp, envc);
+
+ if (!old_title && GetConsoleTitle (title_buf, TITLESIZE))
+ old_title = title_buf;
+
+ /* Allocate cygheap->fdtab */
+ dtable_init ();
+
+ /* Init global well known SID objects */
+ cygsid::init ();
+
+ /* Initialize uid, gid if necessary. */
+ if (child_proc_info == NULL || spawn_info->moreinfo->uid == ILLEGAL_UID)
+ uinfo_init ();
+
+ /* Initialize signal/subprocess handling. */
+ sigproc_init ();
+
+ /* Connect to tty. */
+ tty_init ();
+
+ if (!__argc)
+ {
+ char *line = GetCommandLineA ();
+ line = strcpy ((char *) alloca (strlen (line) + 1), line);
+
+ if (current_codepage == oem_cp)
+ CharToOemA (line, line);
+
+ /* Scan the command line and build argv. Expand wildcards if not
+ called from another cygwin process. */
+ build_argv (line, __argv, __argc,
+ NOTSTATE (myself, PID_CYGPARENT) && allow_glob);
+
+ /* Convert argv[0] to posix rules if it's currently blatantly
+ win32 style. */
+ if ((strchr (__argv[0], ':')) || (strchr (__argv[0], '\\')))
+ {
+ char *new_argv0 = (char *) alloca (MAX_PATH);
+ cygwin_conv_to_posix_path (__argv[0], new_argv0);
+ __argv[0] = new_argv0;
+ }
+ }
+
+ if (user_data->premain[0])
+ for (unsigned int i = 0; i < PREMAIN_LEN / 2; i++)
+ user_data->premain[i] (__argc, __argv, user_data);
+
+ /* Set up standard fds in file descriptor table. */
+ cygheap->fdtab.stdio_init ();
+
+ /* Set up __progname for getopt error call. */
+ if (__argv[0] && (__progname = strrchr (__argv[0], '/')))
+ ++__progname;
+ else
+ __progname = __argv[0];
+ if (__progname)
+ {
+ char *cp = strchr (__progname, '\0') - 4;
+ if (cp > __progname && strcasematch (cp, ".exe"))
+ *cp = '\0';
+ }
+
+ /* Set new console title if appropriate. */
+
+ if (display_title && !dynamically_loaded)
+ {
+ char *cp = __progname;
+ if (strip_title_path)
+ for (char *ptr = cp; *ptr && *ptr != ' '; ptr++)
+ if (isdirsep (*ptr))
+ cp = ptr + 1;
+ set_console_title (cp);
+ }
+
+ cygwin_finished_initializing = 1;
+ /* Call init of loaded dlls. */
+ dlls.init ();
+
+ /* Execute any specified "premain" functions */
+ if (user_data->premain[PREMAIN_LEN / 2])
+ for (unsigned int i = PREMAIN_LEN / 2; i < PREMAIN_LEN; i++)
+ user_data->premain[i] (__argc, __argv, user_data);
+
+ debug_printf ("user_data->main %p", user_data->main);
+
+ if (dynamically_loaded)
+ {
+ set_errno (0);
+ return;
+ }
+
+ /* Disable case-insensitive globbing */
+ ignore_case_with_glob = FALSE;
+
+ /* Flush signals and ensure that signal thread is up and running. Can't
+ do this for noncygwin case since the signal thread is blocked due to
+ LoadLibrary serialization. */
+ wait_for_sigthread ();
+
+ set_errno (0);
+
+ MALLOC_CHECK;
+ cygbench (__progname);
+ if (user_data->main)
+ exit (user_data->main (__argc, __argv, *user_data->envptr));
+}
+
+#ifdef DEBUGGING
+void
+break_here ()
+{
+ debug_printf ("break here");
+}
+#endif
+
+void
+initial_env ()
+{
+ DWORD len;
+ char buf[MAX_PATH + 1];
+#ifdef DEBUGGING
+ if (GetEnvironmentVariable ("CYGWIN_SLEEP", buf, sizeof (buf) - 1))
+ {
+ DWORD ms = atoi (buf);
+ buf[0] = '\0';
+ len = GetModuleFileName (NULL, buf, MAX_PATH);
+ console_printf ("Sleeping %d, pid %u %s\n", ms, GetCurrentProcessId (), buf);
+ Sleep (ms);
+ }
+ if (GetEnvironmentVariable ("CYGWIN_DEBUG", buf, sizeof (buf) - 1))
+ {
+ char buf1[MAX_PATH + 1];
+ len = GetModuleFileName (NULL, buf1, MAX_PATH);
+ strlwr (buf1);
+ strlwr (buf);
+ char *p = strchr (buf, '=');
+ if (!p)
+ p = (char *) "gdb.exe -nw";
+ else
+ *p++ = '\0';
+ if (strstr (buf1, buf))
+ {
+ error_start_init (p);
+ try_to_debug ();
+ break_here ();
+ }
+ }
+#endif
+
+ if (GetEnvironmentVariable ("CYGWIN_TESTING", buf, sizeof (buf) - 1))
+ {
+ _cygwin_testing = 1;
+ if ((len = GetModuleFileName (cygwin_hmodule, buf, MAX_PATH))
+ && len > sizeof ("new-cygwin1.dll")
+ && strcasematch (buf + len - sizeof ("new-cygwin1.dll"),
+ "\\new-cygwin1.dll"))
+ _cygwin_testing_magic = 0x10;
+ }
+}
+
+/* Wrap the real one, otherwise gdb gets confused about
+ two symbols with the same name, but different addresses.
+
+ UPTR is a pointer to global data that lives on the libc side of the
+ line [if one distinguishes the application from the dll]. */
+
+extern "C" void __stdcall
+_dll_crt0 ()
+{
+ DECLARE_TLS_STORAGE;
+ initial_env ();
+ char zeros[sizeof (fork_info->zero)] = {0};
+ static NO_COPY STARTUPINFO si;
+#ifdef DEBUGGING
+ strace.microseconds ();
+#endif
+
+ main_environ = user_data->envptr;
+ *main_environ = NULL;
+
+ early_stuff_init ();
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (),
+ GetCurrentProcess (), &hMainProc, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ hMainProc = GetCurrentProcess ();
+
+ DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc,
+ &hMainThread, 0, false, DUPLICATE_SAME_ACCESS);
+
+ GetStartupInfo (&si);
+ child_proc_info = (child_info *) si.lpReserved2;
+ if (si.cbReserved2 < EXEC_MAGIC_SIZE || !child_proc_info
+ || memcmp (child_proc_info->zero, zeros, sizeof (zeros)) != 0)
+ child_proc_info = NULL;
+ else
+ {
+ if ((child_proc_info->intro & OPROC_MAGIC_MASK) == OPROC_MAGIC_GENERIC)
+ multiple_cygwin_problem ("proc", child_proc_info->intro, 0);
+ else if (child_proc_info->intro == PROC_MAGIC_GENERIC
+ && child_proc_info->magic != CHILD_INFO_MAGIC)
+ multiple_cygwin_problem ("proc", child_proc_info->magic,
+ CHILD_INFO_MAGIC);
+ else if (child_proc_info->cygheap != (void *) &_cygheap_start)
+ multiple_cygwin_problem ("cygheap", (DWORD) child_proc_info->cygheap,
+ (DWORD) &_cygheap_start);
+ unsigned should_be_cb = 0;
+ switch (child_proc_info->type)
+ {
+ case _PROC_FORK:
+ user_data->forkee = child_proc_info->cygpid;
+ should_be_cb = sizeof (child_info_fork);
+ /* fall through */;
+ case _PROC_SPAWN:
+ case _PROC_EXEC:
+ if (!should_be_cb)
+ should_be_cb = sizeof (child_info);
+ if (should_be_cb != child_proc_info->cb)
+ multiple_cygwin_problem ("proc size", child_proc_info->cb, should_be_cb);
+ else if (sizeof (fhandler_union) != child_proc_info->fhandler_union_cb)
+ multiple_cygwin_problem ("fhandler size", child_proc_info->fhandler_union_cb, sizeof (fhandler_union));
+ else
+ {
+ cygwin_mount_h = child_proc_info->mount_h;
+ mypid = child_proc_info->cygpid;
+ break;
+ }
+ default:
+ system_printf ("unknown exec type %d", child_proc_info->type);
+ /* intentionally fall through */
+ case _PROC_WHOOPS:
+ child_proc_info = NULL;
+ break;
+ }
+ }
+ dll_crt0_1 ();
+}
+
+void
+dll_crt0 (per_process *uptr)
+{
+ DECLARE_TLS_STORAGE;
+ /* Set the local copy of the pointer into the user space. */
+ if (uptr && uptr != user_data)
+ {
+ memcpy (user_data, uptr, per_process_overwrite);
+ *(user_data->impure_ptr_ptr) = &reent_data;
+ }
+ _dll_crt0 ();
+}
+
+/* This must be called by anyone who uses LoadLibrary to load cygwin1.dll */
+extern "C" void
+cygwin_dll_init ()
+{
+ static char **envp;
+ static int _fmode;
+
+ if (!DuplicateHandle (GetCurrentProcess (), GetCurrentProcess (),
+ GetCurrentProcess (), &hMainProc, 0, FALSE,
+ DUPLICATE_SAME_ACCESS))
+ hMainProc = GetCurrentProcess ();
+
+ DuplicateHandle (hMainProc, GetCurrentThread (), hMainProc,
+ &hMainThread, 0, FALSE, DUPLICATE_SAME_ACCESS);
+ user_data->magic_biscuit = sizeof (per_process);
+
+ user_data->envptr = &envp;
+ user_data->fmode_ptr = &_fmode;
+
+ dll_crt0_1 ();
+}
+
+extern "C" void
+__main (void)
+{
+ do_global_ctors (user_data->ctors, FALSE);
+}
+
+enum exit_states
+ {
+ ES_NOT_EXITING = 0,
+ ES_THREADTERM,
+ ES_SIGNAL,
+ ES_CLOSEALL,
+ ES_SIGPROCTERMINATE,
+ ES_TITLE,
+ ES_HUP_PGRP,
+ ES_HUP_SID,
+ ES_TTY_TERMINATE,
+ ES_EVENTS_TERMINATE
+ };
+
+exit_states NO_COPY exit_state;
+extern CRITICAL_SECTION exit_lock;
+
+extern "C" void __stdcall
+do_exit (int status)
+{
+ EnterCriticalSection (&exit_lock);
+ UINT n = (UINT) status;
+
+ syscall_printf ("do_exit (%d)", n);
+
+ vfork_save *vf = vfork_storage.val ();
+ if (vf != NULL && vf->pid < 0)
+ vf->restore_exit (status);
+
+ if (exit_state < ES_THREADTERM)
+ {
+ exit_state = ES_THREADTERM;
+ cygthread::terminate ();
+ }
+
+ if (exit_state < ES_SIGNAL)
+ {
+ exit_state = ES_SIGNAL;
+ if (!(n & EXIT_REPARENTING))
+ {
+ signal (SIGCHLD, SIG_IGN);
+ signal (SIGHUP, SIG_IGN);
+ signal (SIGINT, SIG_IGN);
+ signal (SIGQUIT, SIG_IGN);
+ }
+ }
+
+ if (exit_state < ES_CLOSEALL)
+ {
+ exit_state = ES_CLOSEALL;
+ close_all_files ();
+ }
+
+ if (exit_state < ES_SIGPROCTERMINATE)
+ {
+ exit_state = ES_SIGPROCTERMINATE;
+ sigproc_terminate ();
+ }
+
+ myself->stopsig = 0;
+ if (exit_state < ES_TITLE)
+ {
+ exit_state = ES_TITLE;
+ /* restore console title */
+ if (old_title && display_title)
+ set_console_title (old_title);
+ }
+
+ if (exit_state < ES_HUP_PGRP)
+ {
+ exit_state = ES_HUP_PGRP;
+ /* Kill orphaned children on group leader exit */
+ if (myself->has_pgid_children && myself->pid == myself->pgid)
+ {
+ sigproc_printf ("%d == pgrp %d, send SIG{HUP,CONT} to stopped children",
+ myself->pid, myself->pgid);
+ kill_pgrp (myself->pgid, -SIGHUP);
+ }
+ }
+
+ if (exit_state < ES_HUP_SID)
+ {
+ exit_state = ES_HUP_SID;
+ /* Kill the foreground process group on session leader exit */
+ if (getpgrp () > 0 && myself->pid == myself->sid && real_tty_attached (myself))
+ {
+ tty *tp = cygwin_shared->tty[myself->ctty];
+ sigproc_printf ("%d == sid %d, send SIGHUP to children",
+ myself->pid, myself->sid);
+
+ /* CGF FIXME: This can't be right. */
+ if (tp->getsid () == myself->sid)
+ tp->kill_pgrp (SIGHUP);
+ }
+
+ }
+
+ if (exit_state < ES_TTY_TERMINATE)
+ {
+ exit_state = ES_TTY_TERMINATE;
+ tty_terminate ();
+ }
+
+ if (exit_state < ES_EVENTS_TERMINATE)
+ {
+ exit_state = ES_EVENTS_TERMINATE;
+ events_terminate ();
+ }
+
+ minimal_printf ("winpid %d, exit %d", GetCurrentProcessId (), n);
+ myself->exit (n);
+}
+
+extern "C" void
+_exit (int n)
+{
+ do_exit ((DWORD) n & 0xffff);
+}
+
+extern "C" void
+__api_fatal (const char *fmt, ...)
+{
+ char buf[4096];
+ va_list ap;
+
+ va_start (ap, fmt);
+ __small_vsprintf (buf, fmt, ap);
+ va_end (ap);
+ strcat (buf, "\n");
+ int len = strlen (buf);
+ DWORD done;
+ (void) WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, len, &done, 0);
+
+ /* Make sure that the message shows up on the screen, too, since this is
+ a serious error. */
+ if (GetFileType (GetStdHandle (STD_ERROR_HANDLE)) != FILE_TYPE_CHAR)
+ {
+ HANDLE h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_WRITE,
+ &sec_none, OPEN_EXISTING, 0, 0);
+ if (h != INVALID_HANDLE_VALUE)
+ (void) WriteFile (h, buf, len, &done, 0);
+ }
+
+ /* We are going down without mercy. Make sure we reset
+ our process_state. */
+ sigproc_terminate ();
+#ifdef DEBUGGING
+ (void) try_to_debug ();
+#endif
+ myself->exit (1);
+}
+
+void
+multiple_cygwin_problem (const char *what, unsigned magic_version, unsigned version)
+{
+ if (_cygwin_testing && (strstr (what, "proc") || strstr (what, "cygheap")))
+ {
+ child_proc_info->type = _PROC_WHOOPS;
+ return;
+ }
+
+ char buf[1024];
+ if (GetEnvironmentVariable ("CYGWIN_MISMATCH_OK", buf, sizeof (buf)))
+ return;
+
+ if (CYGWIN_VERSION_MAGIC_VERSION (magic_version) == version)
+ system_printf ("%s magic number mismatch detected - %p/%p", what, magic_version, version);
+ else
+ api_fatal ("%s version mismatch detected - %p/%p.\n\
+You have multiple copies of cygwin1.dll on your system.\n\
+Search for cygwin1.dll using the Windows Start->Find/Search facility\n\
+and delete all but the most recent version. The most recent version *should*\n\
+reside in x:\\cygwin\\bin, where 'x' is the drive on which you have\n\
+installed the cygwin distribution.", what, magic_version, version);
+}
+
+#ifdef DEBUGGING
+void __stdcall
+cygbench (const char *s)
+{
+ char buf[1024];
+ if (GetEnvironmentVariable ("CYGWIN_BENCH", buf, sizeof (buf)))
+ small_printf ("%05d ***** %s : %10d\n", GetCurrentProcessId (), s, strace.microseconds ());
+}
+#endif
diff --git a/winsup/cygwin/dtable.cc b/winsup/cygwin/dtable.cc
new file mode 100644
index 00000000000..6342e22231f
--- /dev/null
+++ b/winsup/cygwin/dtable.cc
@@ -0,0 +1,883 @@
+/* dtable.cc: file descriptor support.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <errno.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/cygwin.h>
+#include <assert.h>
+#include <ntdef.h>
+#include <winnls.h>
+
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "pinfo.h"
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+
+static const NO_COPY DWORD std_consts[] = {STD_INPUT_HANDLE, STD_OUTPUT_HANDLE,
+ STD_ERROR_HANDLE};
+
+static const char *handle_to_fn (HANDLE, char *);
+
+static const char NO_COPY unknown_file[] = "some disk file";
+
+/* Set aside space for the table of fds */
+void
+dtable_init (void)
+{
+ if (!cygheap->fdtab.size)
+ cygheap->fdtab.extend (NOFILE_INCR);
+}
+
+void __stdcall
+set_std_handle (int fd)
+{
+ if (fd == 0)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_handle ());
+ else if (fd <= 2)
+ SetStdHandle (std_consts[fd], cygheap->fdtab[fd]->get_output_handle ());
+}
+
+void
+dtable::dec_console_fds ()
+{
+ if (console_fds > 0 && !--console_fds &&
+ myself->ctty != TTY_CONSOLE && !check_pty_fds())
+ FreeConsole ();
+}
+
+int
+dtable::extend (int howmuch)
+{
+ int new_size = size + howmuch;
+ fhandler_base **newfds;
+
+ if (howmuch <= 0)
+ return 0;
+
+ /* Try to allocate more space for fd table. We can't call realloc ()
+ here to preserve old table if memory allocation fails */
+
+ if (!(newfds = (fhandler_base **) ccalloc (HEAP_ARGV, new_size, sizeof newfds[0])))
+ {
+ debug_printf ("calloc failed");
+ return 0;
+ }
+ if (fds)
+ {
+ memcpy (newfds, fds, size * sizeof (fds[0]));
+ cfree (fds);
+ }
+
+ size = new_size;
+ fds = newfds;
+ debug_printf ("size %d, fds %p", size, fds);
+ return 1;
+}
+
+void
+dtable::get_debugger_info ()
+{
+ if (being_debugged ())
+ {
+ char std[3][sizeof ("/dev/ttyNNNN")];
+ std[0][0] = std[1][0] = std [2][0] = '\0';
+ char buf[sizeof ("cYgstd %x") + 32];
+ sprintf (buf, "cYgstd %x %x %x", (unsigned) &std, sizeof (std[0]), 3);
+ OutputDebugString (buf);
+ for (int i = 0; i < 3; i++)
+ if (std[i][0])
+ {
+ path_conv pc;
+ HANDLE h = GetStdHandle (std_consts[i]);
+ fhandler_base *fh = build_fhandler_from_name (i, std[i], NULL, pc);
+ if (!fh)
+ continue;
+ if (!fh->open (&pc, (i ? O_WRONLY : O_RDONLY) | O_BINARY, 0777))
+ release (i);
+ else
+ CloseHandle (h);
+ }
+ }
+}
+
+/* Initialize the file descriptor/handle mapping table.
+ This function should only be called when a cygwin function is invoked
+ by a non-cygwin function, i.e., it should only happen very rarely. */
+
+void
+dtable::stdio_init ()
+{
+ extern void set_console_ctty ();
+ /* Set these before trying to output anything from strace.
+ Also, always set them even if we're to pick up our parent's fds
+ in case they're missed. */
+
+ if (myself->ppid_handle || ISSTATE (myself, PID_CYGPARENT))
+ return;
+
+ HANDLE in = GetStdHandle (STD_INPUT_HANDLE);
+ HANDLE out = GetStdHandle (STD_OUTPUT_HANDLE);
+ HANDLE err = GetStdHandle (STD_ERROR_HANDLE);
+
+ init_std_file_from_handle (0, in);
+
+ /* STD_ERROR_HANDLE has been observed to be the same as
+ STD_OUTPUT_HANDLE. We need separate handles (e.g. using pipes
+ to pass data from child to parent). */
+ if (out == err)
+ {
+ /* Since this code is not invoked for forked tasks, we don't have
+ to worry about the close-on-exec flag here. */
+ if (!DuplicateHandle (hMainProc, out, hMainProc, &err, 0,
+ 1, DUPLICATE_SAME_ACCESS))
+ {
+ /* If that fails, do this as a fall back. */
+ err = out;
+ system_printf ("couldn't make stderr distinct from stdout");
+ }
+ }
+
+ init_std_file_from_handle (1, out);
+ init_std_file_from_handle (2, err);
+ /* Assign the console as the controlling tty for this process if we actually
+ have a console and no other controlling tty has been assigned. */
+ if (myself->ctty < 0 && GetConsoleCP () > 0)
+ set_console_ctty ();
+}
+
+int
+dtable::find_unused_handle (int start)
+{
+ AssertResourceOwner (LOCK_FD_LIST, READ_LOCK);
+ do
+ {
+ for (size_t i = start; i < size; i++)
+ /* See if open -- no need for overhead of not_open */
+ if (fds[i] == NULL)
+ return i;
+ }
+ while (extend (NOFILE_INCR));
+ return -1;
+}
+
+void
+dtable::release (int fd)
+{
+ if (!not_open (fd))
+ {
+ switch (fds[fd]->get_device ())
+ {
+ case FH_SOCKET:
+ dec_need_fixup_before ();
+ break;
+ case FH_CONSOLE:
+ dec_console_fds ();
+ break;
+ }
+ delete fds[fd];
+ fds[fd] = NULL;
+ }
+}
+
+extern "C" int
+cygwin_attach_handle_to_fd (char *name, int fd, HANDLE handle, mode_t bin,
+ DWORD myaccess)
+{
+ if (fd == -1)
+ fd = cygheap->fdtab.find_unused_handle ();
+ path_conv pc;
+ fhandler_base *res = cygheap->fdtab.build_fhandler_from_name (fd, name, handle,
+ pc);
+ res->init (handle, myaccess, bin ?: pc.binmode ());
+ return fd;
+}
+
+void
+dtable::init_std_file_from_handle (int fd, HANDLE handle)
+{
+ const char *name = NULL;
+ CONSOLE_SCREEN_BUFFER_INFO buf;
+ struct sockaddr sa;
+ int sal = sizeof (sa);
+ DCB dcb;
+ unsigned bin = O_BINARY;
+ device dev;
+
+ dev.devn = 0; /* FIXME: device */
+ first_fd_for_open = 0;
+
+ if (!not_open (fd))
+ return;
+
+ SetLastError (0);
+ DWORD ft = GetFileType (handle);
+ if (ft != FILE_TYPE_UNKNOWN || GetLastError () != ERROR_INVALID_HANDLE)
+ {
+ /* See if we can consoleify it */
+ if (GetConsoleScreenBufferInfo (handle, &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (GetNumberOfConsoleInputEvents (handle, (DWORD *) &buf))
+ {
+ if (ISSTATE (myself, PID_USETTY))
+ dev.parse ("/dev/tty");
+ else
+ dev = *console_dev;
+ }
+ else if (ft == FILE_TYPE_PIPE)
+ {
+ if (fd == 0)
+ dev = *piper_dev;
+ else
+ dev = *pipew_dev;
+ }
+ else if (wsock_started && getpeername ((SOCKET) handle, &sa, &sal) == 0)
+ dev = *socket_dev;
+ else if (GetCommState (handle, &dcb))
+ dev.parse ("/dev/ttyS0");
+ else
+ {
+ name = handle_to_fn (handle, (char *) alloca (MAX_PATH + 100));
+ bin = 0;
+ }
+ }
+
+ if (!name && !dev)
+ fds[fd] = NULL;
+ else
+ {
+ path_conv pc;
+ fhandler_base *fh;
+
+ if (dev)
+ fh = build_fhandler (fd, dev);
+ else
+ fh = build_fhandler_from_name (fd, name, handle, pc);
+
+ if (!bin)
+ {
+ bin = fh->get_default_fmode (O_RDWR);
+ if (bin)
+ /* nothing */;
+ else if (dev)
+ bin = O_BINARY;
+ else if (name != unknown_file)
+ bin = pc.binmode ();
+ }
+
+ fh->init (handle, GENERIC_READ | GENERIC_WRITE, bin);
+ set_std_handle (fd);
+ paranoid_printf ("fd %d, handle %p", fd, handle);
+ }
+}
+
+fhandler_base *
+dtable::build_fhandler_from_name (int fd, const char *name, HANDLE handle,
+ path_conv& pc, unsigned opt, suffix_info *si)
+{
+ pc.check (name, opt | PC_NULLEMPTY | PC_FULL | PC_POSIX, si);
+ if (pc.error)
+ {
+ set_errno (pc.error);
+ return NULL;
+ }
+
+ if (!pc.exists () && handle)
+ pc.fillin (handle);
+
+ fhandler_base *fh = build_fhandler (fd, pc.dev,
+ pc.return_and_clear_normalized_path (),
+ pc);
+ return fh;
+}
+
+fhandler_base *
+dtable::build_fhandler (int fd, const device& dev, const char *unix_name,
+ const char *win32_name)
+{
+ return build_fhandler (fd, dev, cstrdup (unix_name), win32_name);
+}
+
+#define cnew(name) new ((void *) ccalloc (HEAP_FHANDLER, 1, sizeof (name))) name
+fhandler_base *
+dtable::build_fhandler (int fd, const device& dev, char *unix_name,
+ const char *win32_name)
+{
+ fhandler_base *fh = NULL;
+
+ if (dev.upper)
+ switch (dev.major)
+ {
+ case DEV_TTYS_MAJOR:
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ case DEV_TTYM_MAJOR:
+ fh = cnew (fhandler_tty_master) ();
+ break;
+ case DEV_CYGDRIVE_MAJOR:
+ fh = cnew (fhandler_cygdrive) ();
+ break;
+ case DEV_FLOPPY_MAJOR:
+ case DEV_CDROM_MAJOR:
+ case DEV_SD_MAJOR:
+ case DEV_RAWDRIVE_MAJOR:
+ fh = cnew (fhandler_dev_floppy) ();
+ break;
+ case DEV_TAPE_MAJOR:
+ fh = cnew (fhandler_dev_tape) ();
+ break;
+ }
+ else
+ switch (dev)
+ {
+ case FH_CONSOLE:
+ case FH_CONIN:
+ case FH_CONOUT:
+ if ((fh = cnew (fhandler_console) ()))
+ inc_console_fds ();
+ break;
+ case FH_PTYM:
+ fh = cnew (fhandler_pty_master) ();
+ break;
+ case FH_WINDOWS:
+ fh = cnew (fhandler_windows) ();
+ break;
+ case FH_SERIAL:
+ fh = cnew (fhandler_serial) ();
+ break;
+ case FH_FIFO:
+ fh = cnew (fhandler_fifo) ();
+ break;
+ case FH_PIPE:
+ case FH_PIPER:
+ case FH_PIPEW:
+ fh = cnew (fhandler_pipe) ();
+ break;
+ case FH_SOCKET:
+ if ((fh = cnew (fhandler_socket) ()))
+ inc_need_fixup_before ();
+ break;
+ case FH_FS:
+ fh = cnew (fhandler_disk_file) ();
+ break;
+ case FH_NULL:
+ fh = cnew (fhandler_dev_null) ();
+ break;
+ case FH_ZERO:
+ fh = cnew (fhandler_dev_zero) ();
+ break;
+ case FH_RANDOM:
+ case FH_URANDOM:
+ fh = cnew (fhandler_dev_random) ();
+ break;
+ case FH_MEM:
+ case FH_PORT:
+ fh = cnew (fhandler_dev_mem) ();
+ break;
+ case FH_CLIPBOARD:
+ fh = cnew (fhandler_dev_clipboard) ();
+ break;
+ case FH_OSS_DSP:
+ fh = cnew (fhandler_dev_dsp) ();
+ break;
+ case FH_PROC:
+ fh = cnew (fhandler_proc) ();
+ break;
+ case FH_REGISTRY:
+ fh = cnew (fhandler_registry) ();
+ break;
+ case FH_PROCESS:
+ fh = cnew (fhandler_process) ();
+ break;
+ case FH_TTY:
+ {
+ device newdev = dev;
+ newdev.tty_to_real_device ();
+ switch (newdev)
+ {
+ case FH_CONSOLE:
+ if ((fh = cnew (fhandler_console) ()))
+ inc_console_fds ();
+ break;
+ case FH_TTYS:
+ fh = cnew (fhandler_tty_slave) ();
+ break;
+ }
+ }
+ }
+
+ if (!fh)
+ {
+ set_errno (ENODEV);
+#ifdef DEBUGGING
+ system_printf ("unknown device - %p, '%s', upper %d",
+ (int) dev, dev.name, dev.upper);
+ return NULL;
+#endif
+ }
+
+ char w32buf[MAX_PATH + 1];
+ if (!unix_name || !*unix_name)
+ {
+ if (!win32_name && dev.fmt && *dev.fmt)
+ {
+ sprintf (w32buf, dev.fmt, dev.minor);
+ win32_name = w32buf;
+ }
+ if (win32_name)
+ {
+ unix_name = cstrdup (w32buf);
+ for (char *p = strchr (unix_name, '\\'); p; p = strchr (p + 1, '\\'))
+ *p = '/';
+ }
+ }
+
+ fh->dev = dev;
+ if (unix_name)
+ {
+ if (!win32_name)
+ {
+ /* FIXME: ? Should we call win32_device_name here?
+ It seems like overkill, but... */
+ win32_name = strcpy (w32buf, unix_name);
+ for (char *p = w32buf; (p = strchr (p, '/')); p++)
+ *p = '\\';
+ }
+ fh->set_name (unix_name, win32_name);
+ }
+
+ debug_printf ("fd %d, fh %p", fd, fh);
+ return fd >= 0 ? (fds[fd] = fh) : fh;
+}
+
+fhandler_base *
+dtable::dup_worker (fhandler_base *oldfh)
+{
+ fhandler_base *newfh = build_fhandler (-1, oldfh->dev);
+ *newfh = *oldfh;
+ newfh->set_io_handle (NULL);
+ if (oldfh->dup (newfh))
+ {
+ cfree (newfh);
+ newfh = NULL;
+ return NULL;
+ }
+
+ newfh->set_close_on_exec_flag (0);
+ MALLOC_CHECK;
+ debug_printf ("duped '%s' old %p, new %p", oldfh->get_name (), oldfh->get_io_handle (), newfh->get_io_handle ());
+ return newfh;
+}
+
+int
+dtable::dup2 (int oldfd, int newfd)
+{
+ int res = -1;
+ fhandler_base *newfh = NULL; // = NULL to avoid an incorrect warning
+
+ MALLOC_CHECK;
+ debug_printf ("dup2 (%d, %d)", oldfd, newfd);
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup");
+
+ if (not_open (oldfd))
+ {
+ syscall_printf ("fd %d not open", oldfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd < 0)
+ {
+ syscall_printf ("new fd out of bounds: %d", newfd);
+ set_errno (EBADF);
+ goto done;
+ }
+
+ if (newfd == oldfd)
+ {
+ res = 0;
+ goto done;
+ }
+
+ if ((newfh = dup_worker (fds[oldfd])) == NULL)
+ {
+ res = -1;
+ goto done;
+ }
+
+ debug_printf ("newfh->io_handle %p, oldfh->io_handle %p",
+ newfh->get_io_handle (), fds[oldfd]->get_io_handle ());
+
+ if (!not_open (newfd))
+ close (newfd);
+ else if ((size_t) newfd < size)
+ /* nothing to do */;
+ else if (find_unused_handle (newfd) < 0)
+ {
+ newfh->close ();
+ res = -1;
+ goto done;
+ }
+
+ fds[newfd] = newfh;
+
+ if ((res = newfd) <= 2)
+ set_std_handle (res);
+
+done:
+ MALLOC_CHECK;
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup");
+ syscall_printf ("%d = dup2 (%d, %d)", res, oldfd, newfd);
+
+ return res;
+}
+
+fhandler_fifo *
+dtable::find_fifo (ATOM hill)
+{
+ SetResourceLock (LOCK_FD_LIST, READ_LOCK, "dup");
+ for (unsigned i = 0; i < size; i++)
+ {
+ fhandler_base *fh = fds[i];
+ if (fh && fh->isfifo () && ((fhandler_fifo *) fh)->get_atom () == hill)
+ return (fhandler_fifo *) fh;
+ }
+ return NULL;
+}
+
+select_record *
+dtable::select_read (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_read (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_write (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_write (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+select_record *
+dtable::select_except (int fd, select_record *s)
+{
+ if (not_open (fd))
+ {
+ set_errno (EBADF);
+ return NULL;
+ }
+ fhandler_base *fh = fds[fd];
+ s = fh->select_except (s);
+ s->fd = fd;
+ s->fh = fh;
+ s->saw_error = 0;
+ debug_printf ("%s fd %d", fh->get_name (), fd);
+ return s;
+}
+
+/* Function to walk the fd table after an exec and perform
+ per-fhandler type fixups. */
+void
+dtable::fixup_before_fork (DWORD target_proc_id)
+{
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_fork");
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_fork");
+}
+
+void
+dtable::fixup_before_exec (DWORD target_proc_id)
+{
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_exec");
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL && !fh->get_close_on_exec ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_before_fork_exec (target_proc_id);
+ }
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_exec");
+}
+
+void
+dtable::set_file_pointers_for_exec ()
+{
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "set_file_pointers_for_exec");
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL && fh->get_flags () & O_APPEND)
+ SetFilePointer (fh->get_handle (), 0, 0, FILE_END);
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "fixup_before_exec");
+}
+
+void
+dtable::fixup_after_exec (HANDLE parent)
+{
+ first_fd_for_open = 0;
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (fh->get_close_on_exec ())
+ release (i);
+ else
+ {
+ fh->fixup_after_exec (parent);
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+ }
+}
+
+void
+dtable::fixup_after_fork (HANDLE parent)
+{
+ fhandler_base *fh;
+ for (size_t i = 0; i < size; i++)
+ if ((fh = fds[i]) != NULL)
+ {
+ if (fh->get_close_on_exec () || fh->get_need_fork_fixup ())
+ {
+ debug_printf ("fd %d (%s)", i, fh->get_name ());
+ fh->fixup_after_fork (parent);
+ }
+ if (i == 0)
+ SetStdHandle (std_consts[i], fh->get_io_handle ());
+ else if (i <= 2)
+ SetStdHandle (std_consts[i], fh->get_output_handle ());
+ }
+}
+
+int
+dtable::vfork_child_dup ()
+{
+ fhandler_base **newtable;
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup");
+ newtable = (fhandler_base **) ccalloc (HEAP_ARGV, size, sizeof (fds[0]));
+ int res = 1;
+
+ /* Remove impersonation */
+ if (cygheap->user.issetuid ())
+ RevertToSelf ();
+
+ for (size_t i = 0; i < size; i++)
+ if (not_open (i))
+ continue;
+ else if ((newtable[i] = dup_worker (fds[i])) != NULL)
+ newtable[i]->set_close_on_exec (fds[i]->get_close_on_exec ());
+ else
+ {
+ res = 0;
+ set_errno (EBADF);
+ goto out;
+ }
+
+ fds_on_hold = fds;
+ fds = newtable;
+
+out:
+ /* Restore impersonation */
+ if (cygheap->user.issetuid ())
+ ImpersonateLoggedOnUser (cygheap->user.token);
+
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "dup");
+ return 1;
+}
+
+void
+dtable::vfork_parent_restore ()
+{
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "restore");
+
+ close_all_files ();
+ fhandler_base **deleteme = fds;
+ fds = fds_on_hold;
+ fds_on_hold = NULL;
+ cfree (deleteme);
+
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "restore");
+ return;
+}
+
+void
+dtable::vfork_child_fixup ()
+{
+ if (!fds_on_hold)
+ return;
+ debug_printf ("here");
+ fhandler_base **saveme = fds;
+ fds = fds_on_hold;
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) cygheap->fdtab.size; i++)
+ if ((fh = cygheap->fdtab[i]) != NULL)
+ {
+ fh->clear_readahead ();
+ if (fh->get_close_on_exec ())
+ release (i);
+ else
+ {
+ fh->close ();
+ cygheap->fdtab.release (i);
+ }
+ }
+
+ fds = saveme;
+ cfree (fds_on_hold);
+ fds_on_hold = NULL;
+
+ return;
+}
+
+#define DEVICE_PREFIX "\\device\\"
+#define DEVICE_PREFIX_LEN sizeof (DEVICE_PREFIX) - 1
+#define REMOTE "\\Device\\LanmanRedirector\\"
+#define REMOTE_LEN sizeof (REMOTE) - 1
+
+static const char *
+handle_to_fn (HANDLE h, char *posix_fn)
+{
+ OBJECT_NAME_INFORMATION *ntfn;
+ char fnbuf[32768];
+
+ memset (fnbuf, 0, sizeof (fnbuf));
+ ntfn = (OBJECT_NAME_INFORMATION *) fnbuf;
+ ntfn->Name.MaximumLength = sizeof (fnbuf) - sizeof (*ntfn);
+ ntfn->Name.Buffer = (WCHAR *) (ntfn + 1);
+
+ DWORD res = NtQueryObject (h, ObjectNameInformation, ntfn, sizeof (fnbuf), NULL);
+
+ if (res)
+ {
+ strcpy (posix_fn, unknown_file);
+ debug_printf ("NtQueryObject failed");
+ return unknown_file;
+ }
+
+ // NT seems to do this on an unopened file
+ if (!ntfn->Name.Buffer)
+ {
+ debug_printf ("nt->Name.Buffer == NULL");
+ return NULL;
+ }
+
+ ntfn->Name.Buffer[ntfn->Name.Length / sizeof (WCHAR)] = 0;
+
+ char win32_fn[MAX_PATH + 100];
+ sys_wcstombs (win32_fn, ntfn->Name.Buffer, ntfn->Name.Length);
+ debug_printf ("nt name '%s'", win32_fn);
+ if (!strncasematch (win32_fn, DEVICE_PREFIX, DEVICE_PREFIX_LEN)
+ || !QueryDosDevice (NULL, fnbuf, sizeof (fnbuf)))
+ return strcpy (posix_fn, win32_fn);
+
+ char *p = strchr (win32_fn + DEVICE_PREFIX_LEN, '\\');
+ if (!p)
+ p = strchr (win32_fn + DEVICE_PREFIX_LEN, '\0');
+
+ int n = p - win32_fn;
+ int maxmatchlen = 0;
+ char *maxmatchdos = NULL;
+ for (char *s = fnbuf; *s; s = strchr (s, '\0') + 1)
+ {
+ char device[MAX_PATH + 10];
+ device[MAX_PATH + 9] = '\0';
+ if (strchr (s, ':') == NULL)
+ continue;
+ if (!QueryDosDevice (s, device, sizeof (device) - 1))
+ continue;
+ char *q = strrchr (device, ';');
+ if (q)
+ {
+ char *r = strchr (q, '\\');
+ if (r)
+ strcpy (q, r + 1);
+ }
+ int devlen = strlen (device);
+ if (device[devlen - 1] == '\\')
+ device[--devlen] = '\0';
+ if (devlen < maxmatchlen)
+ continue;
+ if (!strncasematch (device, win32_fn, devlen) ||
+ (win32_fn[devlen] != '\0' && win32_fn[devlen] != '\\'))
+ continue;
+ maxmatchlen = devlen;
+ maxmatchdos = s;
+ debug_printf ("current match '%s'", device);
+ }
+
+ char *w32 = win32_fn;
+ if (maxmatchlen)
+ {
+ n = strlen (maxmatchdos);
+ if (maxmatchdos[n - 1] == '\\')
+ n--;
+ w32 += maxmatchlen - n;
+ memcpy (w32, maxmatchdos, n);
+ w32[n] = '\\';
+ }
+ else if (strncasematch (w32, REMOTE, REMOTE_LEN))
+ {
+ w32 += REMOTE_LEN - 2;
+ *w32 = '\\';
+ debug_printf ("remote drive");
+ }
+
+
+ debug_printf ("derived path '%s'", w32);
+ cygwin_conv_to_full_posix_path (w32, posix_fn);
+ return posix_fn;
+}
diff --git a/winsup/cygwin/dtable.h b/winsup/cygwin/dtable.h
new file mode 100644
index 00000000000..07c976e86b3
--- /dev/null
+++ b/winsup/cygwin/dtable.h
@@ -0,0 +1,93 @@
+/* dtable.h: fd table definition.
+
+ Copyright 2000, 2001, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* Initial and increment values for cygwin's fd table */
+#define NOFILE_INCR 32
+
+#include "thread.h"
+
+class suffix_info;
+class fhandler_fifo;
+
+class dtable
+{
+ fhandler_base **fds;
+ fhandler_base **fds_on_hold;
+ int first_fd_for_open;
+ int cnt_need_fixup_before;
+ int console_fds;
+public:
+ size_t size;
+
+ dtable () : first_fd_for_open(3), cnt_need_fixup_before(0), console_fds(0) {}
+ void init () {first_fd_for_open = 3;}
+
+ void dec_need_fixup_before ()
+ { if (cnt_need_fixup_before > 0) --cnt_need_fixup_before; }
+ void inc_need_fixup_before ()
+ { cnt_need_fixup_before++; }
+ BOOL need_fixup_before ()
+ { return cnt_need_fixup_before > 0; }
+
+ void dec_console_fds ();
+ void inc_console_fds ()
+ { console_fds++; }
+ BOOL has_console_fds ()
+ { return console_fds > 0; }
+
+ int vfork_child_dup ();
+ void vfork_parent_restore ();
+ void vfork_child_fixup ();
+ fhandler_base *dup_worker (fhandler_base *oldfh);
+ int extend (int howmuch);
+ void fixup_before_exec (DWORD win_proc_id);
+ void fixup_before_fork (DWORD win_proc_id);
+ void fixup_after_fork (HANDLE);
+ fhandler_base *build_fhandler (int fd, const device& dev, const char *unix_name,
+ const char *win32_name = NULL);
+ fhandler_base *build_fhandler (int fd, const device& dev, char *unix_name = NULL,
+ const char *win32_name = NULL);
+ fhandler_base *build_fhandler_from_name (int fd, const char *name, HANDLE h,
+ path_conv& pc,
+ unsigned opts = PC_SYM_FOLLOW,
+ suffix_info *si = NULL);
+ inline int not_open (int fd)
+ {
+ SetResourceLock (LOCK_FD_LIST, READ_LOCK, "not_open");
+
+ int res = fd < 0 || fd >= (int) size || fds[fd] == NULL;
+
+ ReleaseResourceLock (LOCK_FD_LIST, READ_LOCK, "not open");
+ return res;
+ }
+ int find_unused_handle (int start);
+ int find_unused_handle () { return find_unused_handle (first_fd_for_open);}
+ void release (int fd);
+ void init_std_file_from_handle (int fd, HANDLE handle);
+ int dup2 (int oldfd, int newfd);
+ void fixup_after_exec (HANDLE);
+ inline fhandler_base *operator [](int fd) const { return fds[fd]; }
+ select_record *select_read (int fd, select_record *s);
+ select_record *select_write (int fd, select_record *s);
+ select_record *select_except (int fd, select_record *s);
+ operator fhandler_base **() {return fds;}
+ void stdio_init ();
+ void get_debugger_info ();
+ void set_file_pointers_for_exec ();
+ bool in_vfork_cleanup () {return fds_on_hold == fds;}
+ fhandler_fifo *find_fifo (ATOM);
+};
+
+void dtable_init (void);
+void stdio_init (void);
+extern dtable fdtab;
+
+extern "C" int getfdtabsize ();
+extern "C" void setfdtabsize (int);
diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
new file mode 100644
index 00000000000..664cac0b610
--- /dev/null
+++ b/winsup/cygwin/fhandler.cc
@@ -0,0 +1,1329 @@
+/* fhandler.cc. See console.cc for fhandler_console functions.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <sys/uio.h>
+#include <signal.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygwin/version.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "pinfo.h"
+#include <assert.h>
+#include <limits.h>
+
+static NO_COPY const int CHUNK_SIZE = 1024; /* Used for crlf conversions */
+
+struct __cygwin_perfile *perfile_table;
+
+DWORD binmode;
+
+inline fhandler_base&
+fhandler_base::operator =(fhandler_base &x)
+{
+ memcpy (this, &x, sizeof *this);
+ unix_path_name = x.unix_path_name ? cstrdup (x.unix_path_name) : NULL;
+ win32_path_name = x.win32_path_name ? cstrdup (x.win32_path_name) : NULL;
+ rabuf = NULL;
+ ralen = 0;
+ raixget = 0;
+ raixput = 0;
+ rabuflen = 0;
+ return *this;
+}
+
+int
+fhandler_base::puts_readahead (const char *s, size_t len)
+{
+ int success = 1;
+ while ((*s || (len != (size_t) -1 && len--))
+ && (success = put_readahead (*s++) > 0))
+ continue;
+ return success;
+}
+
+int
+fhandler_base::put_readahead (char value)
+{
+ char *newrabuf;
+ if (raixput < rabuflen)
+ /* Nothing to do */;
+ else if ((newrabuf = (char *) realloc (rabuf, rabuflen += 32)))
+ rabuf = newrabuf;
+ else
+ return 0;
+
+ rabuf[raixput++] = value;
+ ralen++;
+ return 1;
+}
+
+int
+fhandler_base::get_readahead ()
+{
+ int chret = -1;
+ if (raixget < ralen)
+ chret = ((unsigned char) rabuf[raixget++]) & 0xff;
+ /* FIXME - not thread safe */
+ if (raixget >= ralen)
+ raixget = raixput = ralen = 0;
+ return chret;
+}
+
+int
+fhandler_base::peek_readahead (int queryput)
+{
+ int chret = -1;
+ if (!queryput && raixget < ralen)
+ chret = ((unsigned char) rabuf[raixget]) & 0xff;
+ else if (queryput && raixput > 0)
+ chret = ((unsigned char) rabuf[raixput - 1]) & 0xff;
+ return chret;
+}
+
+void
+fhandler_base::set_readahead_valid (int val, int ch)
+{
+ if (!val)
+ ralen = raixget = raixput = 0;
+ if (ch != -1)
+ put_readahead (ch);
+}
+
+int
+fhandler_base::eat_readahead (int n)
+{
+ int oralen = ralen;
+ if (n < 0)
+ n = ralen;
+ if (n > 0 && ralen)
+ {
+ if ((int) (ralen -= n) < 0)
+ ralen = 0;
+
+ if (raixget >= ralen)
+ raixget = raixput = ralen = 0;
+ else if (raixput > ralen)
+ raixput = ralen;
+ }
+
+ return oralen;
+}
+
+int
+fhandler_base::get_readahead_into_buffer (char *buf, size_t buflen)
+{
+ int ch;
+ int copied_chars = 0;
+
+ while (buflen)
+ if ((ch = get_readahead ()) < 0)
+ break;
+ else
+ {
+ buf[copied_chars++] = (unsigned char)(ch & 0xff);
+ buflen--;
+ }
+
+ return copied_chars;
+}
+
+/* Record the file name.
+ Filenames are used mostly for debugging messages, and it's hoped that
+ in cases where the name is really required, the filename wouldn't ever
+ be too long (e.g. devices or some such).
+ The unix_path_name is also used by virtual fhandlers. */
+void
+fhandler_base::set_name (const char *unix_path, const char *win32_path)
+{
+ if (unix_path == NULL || !*unix_path)
+ return;
+
+ if (win32_path)
+ win32_path_name = cstrdup (win32_path);
+ else
+ {
+ const char *fmt = get_native_name ();
+ char *w = (char *) cmalloc (HEAP_STR, strlen (fmt) + 16);
+ __small_sprintf (w, fmt, get_unit ());
+ win32_path_name = w;
+ }
+
+ if (win32_path_name == NULL)
+ {
+ system_printf ("fatal error. strdup failed");
+ exit (ENOMEM);
+ }
+
+ assert (unix_path_name == NULL);
+ /* FIXME: This isn't really right. It ignores the first argument if we're
+ building names for a device and just converts the device name from the
+ win32 name since it has theoretically been previously detected by
+ path_conv. Ideally, we should pass in a format string and build the
+ unix_path, too. */
+ if (!is_auto_device () || *win32_path_name != '\\')
+ unix_path_name = unix_path;
+ else
+ {
+ char *p = cstrdup (win32_path_name);
+ unix_path_name = p;
+ while ((p = strchr (p, '\\')) != NULL)
+ *p++ = '/';
+ if (unix_path)
+ cfree ((void *) unix_path);
+ }
+
+ if (unix_path_name == NULL)
+ {
+ system_printf ("fatal error. strdup failed");
+ exit (ENOMEM);
+ }
+ namehash = hash_path_name (0, win32_path_name);
+}
+
+/* Detect if we are sitting at EOF for conditions where Windows
+ returns an error but UNIX doesn't. */
+static int __stdcall
+is_at_eof (HANDLE h, DWORD err)
+{
+ DWORD size, upper1, curr;
+
+ size = GetFileSize (h, &upper1);
+ if (upper1 != 0xffffffff || GetLastError () == NO_ERROR)
+ {
+ LONG upper2 = 0;
+ curr = SetFilePointer (h, 0, &upper2, FILE_CURRENT);
+ if (curr == size && upper1 == (DWORD) upper2)
+ return 1;
+ }
+
+ SetLastError (err);
+ return 0;
+}
+
+void
+fhandler_base::set_flags (int flags, int supplied_bin)
+{
+ int bin;
+ int fmode;
+ debug_printf ("flags %p, supplied_bin %p", flags, supplied_bin);
+ if ((bin = flags & (O_BINARY | O_TEXT)))
+ debug_printf ("O_TEXT/O_BINARY set in flags %p", bin);
+ else if (get_r_binset () && get_w_binset ())
+ bin = get_r_binary () ? O_BINARY : O_TEXT; // FIXME: Not quite right
+ else if ((fmode = get_default_fmode (flags)) & O_BINARY)
+ bin = O_BINARY;
+ else if (fmode & O_TEXT)
+ bin = O_TEXT;
+ else if (supplied_bin)
+ bin = supplied_bin;
+ else
+ bin = get_w_binary () || get_r_binary () || (binmode != O_TEXT)
+ ? O_BINARY : O_TEXT;
+
+ openflags = flags | bin;
+
+ bin &= O_BINARY;
+ set_r_binary (bin);
+ set_w_binary (bin);
+ syscall_printf ("filemode set to %s", bin ? "binary" : "text");
+}
+
+/* Normal file i/o handlers. */
+
+/* Cover function to ReadFile to achieve (as much as possible) Posix style
+ semantics and use of errno. */
+void
+fhandler_base::raw_read (void *ptr, size_t& ulen)
+{
+#define bytes_read ((ssize_t) ulen)
+
+ HANDLE h = NULL; /* grumble */
+ int prio = 0; /* ditto */
+ DWORD len = ulen;
+
+ (ssize_t) ulen = -1;
+ if (read_state)
+ {
+ h = GetCurrentThread ();
+ prio = GetThreadPriority (h);
+ (void) SetThreadPriority (h, THREAD_PRIORITY_TIME_CRITICAL);
+ SetEvent (read_state);
+ }
+ BOOL res = ReadFile (get_handle (), ptr, len, (DWORD *) &ulen, 0);
+ if (read_state)
+ {
+ SetEvent (read_state);
+ (void) SetThreadPriority (h, prio);
+ }
+ if (!res)
+ {
+ /* Some errors are not really errors. Detect such cases here. */
+
+ DWORD errcode = GetLastError ();
+ switch (errcode)
+ {
+ case ERROR_BROKEN_PIPE:
+ /* This is really EOF. */
+ bytes_read = 0;
+ break;
+ case ERROR_MORE_DATA:
+ /* `bytes_read' is supposedly valid. */
+ break;
+ case ERROR_NOACCESS:
+ if (is_at_eof (get_handle (), errcode))
+ {
+ bytes_read = 0;
+ break;
+ }
+ case ERROR_INVALID_FUNCTION:
+ case ERROR_INVALID_PARAMETER:
+ case ERROR_INVALID_HANDLE:
+ if (openflags & O_DIROPEN)
+ {
+ set_errno (EISDIR);
+ bytes_read = -1;
+ break;
+ }
+ default:
+ syscall_printf ("ReadFile %s failed, %E", unix_path_name);
+ __seterrno_from_win_error (errcode);
+ bytes_read = -1;
+ break;
+ }
+ }
+#undef bytes_read
+}
+
+/* Cover function to WriteFile to provide Posix interface and semantics
+ (as much as possible). */
+int
+fhandler_base::raw_write (const void *ptr, size_t len)
+{
+ DWORD bytes_written;
+
+ if (!WriteFile (get_output_handle (), ptr, len, &bytes_written, 0))
+ {
+ if (GetLastError () == ERROR_DISK_FULL && bytes_written > 0)
+ return bytes_written;
+ __seterrno ();
+ if (get_errno () == EPIPE)
+ raise (SIGPIPE);
+ return -1;
+ }
+ return bytes_written;
+}
+
+#define ACCFLAGS(x) (x & (O_RDONLY | O_WRONLY | O_RDWR))
+int
+fhandler_base::get_default_fmode (int flags)
+{
+ int fmode = __fmode;
+ if (perfile_table)
+ {
+ size_t nlen = strlen (get_name ());
+ unsigned accflags = ACCFLAGS (flags);
+ for (__cygwin_perfile *pf = perfile_table; pf->name; pf++)
+ if (!*pf->name && ACCFLAGS (pf->flags) == accflags)
+ {
+ fmode = pf->flags & ~(O_RDONLY | O_WRONLY | O_RDWR);
+ break;
+ }
+ else
+ {
+ size_t pflen = strlen (pf->name);
+ const char *stem = get_name () + nlen - pflen;
+ if (pflen > nlen || (stem != get_name () && !isdirsep (stem[-1])))
+ continue;
+ else if (ACCFLAGS (pf->flags) == accflags && strcasematch (stem, pf->name))
+ {
+ fmode = pf->flags & ~(O_RDONLY | O_WRONLY | O_RDWR);
+ break;
+ }
+ }
+ }
+ return fmode;
+}
+
+bool
+fhandler_base::device_access_denied (int flags)
+{
+ int mode = 0;
+ if (flags & O_RDWR)
+ mode |= R_OK | W_OK;
+ if (flags & (O_WRONLY | O_APPEND))
+ mode |= W_OK;
+ if (!mode)
+ mode |= R_OK;
+
+ return ::access (get_win32_name (), mode);
+}
+
+/* Open system call handler function. */
+int
+fhandler_base::open (path_conv *pc, int flags, mode_t mode)
+{
+ int res = 0;
+ HANDLE x;
+ int file_attributes;
+ int shared;
+ int creation_distribution;
+ SECURITY_ATTRIBUTES sa = sec_none;
+
+ syscall_printf ("(%s, %p) query_open %d", get_win32_name (), flags, get_query_open ());
+
+ if (get_win32_name () == NULL)
+ {
+ set_errno (ENOENT);
+ goto done;
+ }
+
+ if (get_query_open ())
+ access = 0;
+ else if (get_device () == FH_TAPE)
+ access = GENERIC_READ | GENERIC_WRITE;
+ else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
+ access = GENERIC_READ;
+ else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY)
+ access = GENERIC_WRITE;
+ else
+ access = GENERIC_READ | GENERIC_WRITE;
+
+ /* Allow reliable lseek on disk devices. */
+ if (get_device () == FH_FLOPPY)
+ access |= GENERIC_READ;
+
+ /* FIXME: O_EXCL handling? */
+
+ if ((flags & O_TRUNC) && ((flags & O_ACCMODE) != O_RDONLY))
+ {
+ if (flags & O_CREAT)
+ creation_distribution = CREATE_ALWAYS;
+ else
+ creation_distribution = TRUNCATE_EXISTING;
+ }
+ else if (flags & O_CREAT)
+ creation_distribution = OPEN_ALWAYS;
+ else
+ creation_distribution = OPEN_EXISTING;
+
+ if ((flags & O_EXCL) && (flags & O_CREAT))
+ creation_distribution = CREATE_NEW;
+
+ if (flags & O_APPEND)
+ set_append_p ();
+
+ /* These flags are host dependent. */
+ shared = wincap.shared ();
+
+ file_attributes = FILE_ATTRIBUTE_NORMAL;
+ if (flags & O_DIROPEN)
+ file_attributes |= FILE_FLAG_BACKUP_SEMANTICS;
+ if (get_device () == FH_SERIAL)
+ file_attributes |= FILE_FLAG_OVERLAPPED;
+
+#ifdef HIDDEN_DOT_FILES
+ if (flags & O_CREAT && get_device () == FH_FS)
+ {
+ char *c = strrchr (get_win32_name (), '\\');
+ if ((c && c[1] == '.') || *get_win32_name () == '.')
+ file_attributes |= FILE_ATTRIBUTE_HIDDEN;
+ }
+#endif
+
+ /* CreateFile() with dwDesiredAccess == 0 when called on remote
+ share returns some handle, even if file doesn't exist. This code
+ works around this bug. */
+ if (get_query_open () && isremote () &&
+ creation_distribution == OPEN_EXISTING && pc && !pc->exists ())
+ {
+ set_errno (ENOENT);
+ goto done;
+ }
+
+ /* If mode has no write bits set, we set the R/O attribute. */
+ if (!(mode & (S_IWUSR | S_IWGRP | S_IWOTH)))
+ file_attributes |= FILE_ATTRIBUTE_READONLY;
+
+ /* If the file should actually be created and ntsec is on,
+ set files attributes. */
+ if (flags & O_CREAT && get_device () == FH_FS && allow_ntsec && has_acls ())
+ set_security_attribute (mode, &sa, alloca (4096), 4096);
+
+ x = CreateFile (get_win32_name (), access, shared, &sa, creation_distribution,
+ file_attributes, 0);
+
+ syscall_printf ("%p = CreateFile (%s, %p, %p, %p, %p, %p, 0)",
+ x, get_win32_name (), access, shared, &sa,
+ creation_distribution, file_attributes);
+
+ if (x == INVALID_HANDLE_VALUE)
+ {
+ if (!wincap.can_open_directories () && pc && pc->isdir ())
+ {
+ if (flags & (O_CREAT | O_EXCL) == (O_CREAT | O_EXCL))
+ set_errno (EEXIST);
+ else if (flags & (O_WRONLY | O_RDWR))
+ set_errno (EISDIR);
+ else
+ set_nohandle (true);
+ }
+ else if (GetLastError () == ERROR_INVALID_HANDLE)
+ set_errno (ENOENT);
+ else
+ __seterrno ();
+ if (!get_nohandle ())
+ goto done;
+ }
+
+ /* Attributes may be set only if a file is _really_ created.
+ This code is now only used for ntea here since the files
+ security attributes are set in CreateFile () now. */
+ if (flags & O_CREAT && get_device () == FH_FS
+ && GetLastError () != ERROR_ALREADY_EXISTS
+ && !allow_ntsec && allow_ntea)
+ set_file_attribute (has_acls (), get_win32_name (), mode);
+
+ set_io_handle (x);
+ set_flags (flags, pc ? pc->binmode () : 0);
+
+ res = 1;
+ set_open_status ();
+done:
+ syscall_printf ("%d = fhandler_base::open (%s, %p)", res, get_win32_name (),
+ flags);
+ return res;
+}
+
+/* states:
+ open buffer in binary mode? Just do the read.
+
+ open buffer in text mode? Scan buffer for control zs and handle
+ the first one found. Then scan buffer, converting every \r\n into
+ an \n. If last char is an \r, look ahead one more char, if \n then
+ modify \r, if not, remember char.
+*/
+void
+fhandler_base::read (void *in_ptr, size_t& len)
+{
+ char *ptr = (char *) in_ptr;
+ ssize_t copied_chars = 0;
+ bool need_signal = !!read_state;
+ int c;
+
+ while (len)
+ if ((c = get_readahead ()) < 0)
+ break;
+ else
+ {
+ ptr[copied_chars++] = (unsigned char) (c & 0xff);
+ len--;
+ }
+
+ if (copied_chars && is_slow ())
+ {
+ len = (size_t) copied_chars;
+ goto out;
+ }
+
+ if (!len)
+ {
+ len = (size_t) copied_chars;
+ goto out;
+ }
+
+ raw_read (ptr + copied_chars, len);
+ need_signal = false;
+ if (!copied_chars)
+ /* nothing */;
+ else if ((ssize_t) len > 0)
+ len += copied_chars;
+ else
+ len = copied_chars;
+
+ if (get_r_binary () || len <= 0)
+ goto out;
+
+ /* Scan buffer and turn \r\n into \n */
+ char *src, *dst, *end;
+ src = (char *) ptr;
+ dst = (char *) ptr;
+ end = src + len - 1;
+
+ /* Read up to the last but one char - the last char needs special handling */
+ while (src < end)
+ {
+ if (*src == '\r' && src[1] == '\n')
+ src++;
+ *dst++ = *src++;
+ }
+
+ /* If not beyond end and last char is a '\r' then read one more
+ to see if we should translate this one too */
+ if (src > end)
+ /* nothing */;
+ else if (*src != '\r')
+ *dst++ = *src;
+ else
+ {
+ char c1;
+ size_t c1len = 1;
+ raw_read (&c1, c1len);
+ if (c1len <= 0)
+ /* nothing */;
+ else if (c1 == '\n')
+ *dst++ = '\n';
+ else
+ {
+ set_readahead_valid (1, c1);
+ *dst++ = *src;
+ }
+ }
+
+ len = dst - (char *) ptr;
+
+#ifndef NOSTRACE
+ if (strace.active)
+ {
+ char buf[16 * 6 + 1];
+ char *p = buf;
+
+ for (int i = 0; i < copied_chars && i < 16; ++i)
+ {
+ unsigned char c = ((unsigned char *) ptr)[i];
+ /* >= 33 so space prints in hex */
+ __small_sprintf (p, c >= 33 && c <= 127 ? " %c" : " %p", c);
+ p += strlen (p);
+ }
+ debug_printf ("read %d bytes (%s%s)", copied_chars, buf,
+ copied_chars > 16 ? " ..." : "");
+ }
+#endif
+
+out:
+ if (need_signal)
+ SetEvent (read_state);
+
+ debug_printf ("returning %d, %s mode", len,
+ get_r_binary () ? "binary" : "text");
+ return;
+}
+
+int
+fhandler_base::write (const void *ptr, size_t len)
+{
+ int res;
+
+ if (get_append_p ())
+ SetFilePointer (get_output_handle (), 0, 0, FILE_END);
+ else if (wincap.has_lseek_bug () && get_check_win95_lseek_bug ())
+ {
+ /* Note: this bug doesn't happen on NT4, even though the documentation
+ for WriteFile() says that it *may* happen on any OS. */
+ int actual_length, current_position;
+ set_check_win95_lseek_bug (0); /* don't do it again */
+ actual_length = GetFileSize (get_output_handle (), NULL);
+ current_position = SetFilePointer (get_output_handle (), 0, 0, FILE_CURRENT);
+ if (current_position > actual_length)
+ {
+ /* Oops, this is the bug case - Win95 uses whatever is on the disk
+ instead of some known (safe) value, so we must seek back and
+ fill in the gap with zeros. - DJ */
+ char zeros[512];
+ int number_of_zeros_to_write = current_position - actual_length;
+ memset (zeros, 0, 512);
+ SetFilePointer (get_output_handle (), 0, 0, FILE_END);
+ while (number_of_zeros_to_write > 0)
+ {
+ DWORD zeros_this_time = (number_of_zeros_to_write > 512
+ ? 512 : number_of_zeros_to_write);
+ DWORD written;
+ if (!WriteFile (get_output_handle (), zeros, zeros_this_time, &written,
+ NULL))
+ {
+ __seterrno ();
+ if (get_errno () == EPIPE)
+ raise (SIGPIPE);
+ /* This might fail, but it's the best we can hope for */
+ SetFilePointer (get_output_handle (), current_position, 0, FILE_BEGIN);
+ return -1;
+
+ }
+ if (written < zeros_this_time) /* just in case */
+ {
+ set_errno (ENOSPC);
+ /* This might fail, but it's the best we can hope for */
+ SetFilePointer (get_output_handle (), current_position, 0, FILE_BEGIN);
+ return -1;
+ }
+ number_of_zeros_to_write -= written;
+ }
+ }
+ }
+
+ if (get_w_binary ())
+ {
+ debug_printf ("binary write");
+ res = raw_write (ptr, len);
+ }
+ else
+ {
+ debug_printf ("text write");
+ /* This is the Microsoft/DJGPP way. Still not ideal, but it's
+ compatible.
+ Modified slightly by CGF 2000-10-07 */
+
+ int left_in_data = len;
+ char *data = (char *)ptr;
+ res = 0;
+
+ while (left_in_data > 0)
+ {
+ char buf[CHUNK_SIZE + 1], *buf_ptr = buf;
+ int left_in_buf = CHUNK_SIZE;
+
+ while (left_in_buf > 0 && left_in_data > 0)
+ {
+ char ch = *data++;
+ if (ch == '\n')
+ {
+ *buf_ptr++ = '\r';
+ left_in_buf--;
+ }
+ *buf_ptr++ = ch;
+ left_in_buf--;
+ left_in_data--;
+ if (left_in_data > 0 && ch == '\r' && *data == '\n')
+ {
+ *buf_ptr++ = *data++;
+ left_in_buf--;
+ left_in_data--;
+ }
+ }
+
+ /* We've got a buffer-full, or we're out of data. Write it out */
+ int nbytes;
+ int want = buf_ptr - buf;
+ if ((nbytes = raw_write (buf, want)) == want)
+ {
+ /* Keep track of how much written not counting additional \r's */
+ res = data - (char *)ptr;
+ continue;
+ }
+
+ if (nbytes == -1)
+ res = -1; /* Error */
+ else
+ res += nbytes; /* Partial write. Return total bytes written. */
+ break; /* All done */
+ }
+ }
+
+ debug_printf ("%d = write (%p, %d)", res, ptr, len);
+ return res;
+}
+
+ssize_t
+fhandler_base::readv (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ assert (iov);
+ assert (iovcnt >= 1);
+
+ if (iovcnt == 1)
+ {
+ size_t len = iov->iov_len;
+ read (iov->iov_base, len);
+ return len;
+ }
+
+ if (tot == -1) // i.e. if not pre-calculated by the caller.
+ {
+ tot = 0;
+ const struct iovec *iovptr = iov + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ tot += iovptr->iov_len;
+ }
+ while (iovptr != iov);
+ }
+
+ assert (tot >= 0);
+
+ if (tot == 0)
+ return 0;
+
+ char *buf = (char *) alloca (tot);
+
+ if (!buf)
+ {
+ set_errno (ENOMEM);
+ return -1;
+ }
+
+ read (buf, (size_t) tot);
+
+ const struct iovec *iovptr = iov;
+ int nbytes = tot;
+
+ while (nbytes > 0)
+ {
+ const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
+ memcpy (iovptr->iov_base, buf, frag);
+ buf += frag;
+ iovptr += 1;
+ nbytes -= frag;
+ }
+
+ return tot;
+}
+
+ssize_t
+fhandler_base::writev (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ assert (iov);
+ assert (iovcnt >= 1);
+
+ if (iovcnt == 1)
+ return write (iov->iov_base, iov->iov_len);
+
+ if (tot == -1) // i.e. if not pre-calculated by the caller.
+ {
+ tot = 0;
+ const struct iovec *iovptr = iov + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ tot += iovptr->iov_len;
+ }
+ while (iovptr != iov);
+ }
+
+ assert (tot >= 0);
+
+ if (tot == 0)
+ return 0;
+
+ char *const buf = (char *) alloca (tot);
+
+ if (!buf)
+ {
+ set_errno (ENOMEM);
+ return -1;
+ }
+
+ char *bufptr = buf;
+ const struct iovec *iovptr = iov;
+ int nbytes = tot;
+
+ while (nbytes != 0)
+ {
+ const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
+ memcpy (bufptr, iovptr->iov_base, frag);
+ bufptr += frag;
+ iovptr += 1;
+ nbytes -= frag;
+ }
+
+ return write (buf, tot);
+}
+
+__off64_t
+fhandler_base::lseek (__off64_t offset, int whence)
+{
+ __off64_t res;
+
+ /* 9x/Me doesn't support 64bit offsets. We trap that here and return
+ EINVAL. It doesn't make sense to simulate bigger offsets by a
+ SetFilePointer sequence since FAT and FAT32 don't support file
+ size >= 4GB anyway. */
+ if (!wincap.has_64bit_file_access ()
+ && (offset < LONG_MIN || offset > LONG_MAX))
+ {
+ debug_printf ("Win9x, offset not 32 bit.");
+ set_errno (EINVAL);
+ return (__off64_t)-1;
+ }
+
+ /* Seeks on text files is tough, we rewind and read till we get to the
+ right place. */
+
+ if (whence != SEEK_CUR || offset != 0)
+ {
+ if (whence == SEEK_CUR)
+ offset -= ralen - raixget;
+ set_readahead_valid (0);
+ }
+
+ debug_printf ("lseek (%s, %D, %d)", unix_path_name, offset, whence);
+
+ DWORD win32_whence = whence == SEEK_SET ? FILE_BEGIN
+ : (whence == SEEK_CUR ? FILE_CURRENT : FILE_END);
+
+ LONG off_low = offset & 0xffffffff;
+ LONG *poff_high, off_high;
+ if (!wincap.has_64bit_file_access ())
+ poff_high = NULL;
+ else
+ {
+ off_high = offset >> 32;
+ poff_high = &off_high;
+ }
+
+ debug_printf ("setting file pointer to %u (high), %u (low)", off_high, off_low);
+ res = SetFilePointer (get_handle (), off_low, poff_high, win32_whence);
+ if (res == INVALID_SET_FILE_POINTER && GetLastError ())
+ {
+ __seterrno ();
+ }
+ else
+ {
+ /* When next we write(), we will check to see if *this* seek went beyond
+ the end of the file, and back-seek and fill with zeros if so - DJ */
+ set_check_win95_lseek_bug ();
+
+ /* If this was a SEEK_CUR with offset 0, we still might have
+ readahead that we have to take into account when calculating
+ the actual position for the application. */
+ if (whence == SEEK_CUR)
+ res -= ralen - raixget;
+ }
+
+ return res;
+}
+
+int
+fhandler_base::close ()
+{
+ int res = -1;
+
+ syscall_printf ("closing '%s' handle %p", get_name (), get_handle ());
+ if (get_nohandle () || CloseHandle (get_handle ()))
+ res = 0;
+ else
+ {
+ paranoid_printf ("CloseHandle (%d <%s>) failed", get_handle (),
+ get_name ());
+
+ __seterrno ();
+ }
+ return res;
+}
+
+int
+fhandler_base::ioctl (unsigned int cmd, void *buf)
+{
+ if (cmd == FIONBIO)
+ syscall_printf ("ioctl (FIONBIO, %p)", buf);
+ else
+ syscall_printf ("ioctl (%x, %p)", cmd, buf);
+
+ set_errno (EINVAL);
+ return -1;
+}
+
+int
+fhandler_base::lock (int, struct flock *)
+{
+ set_errno (EINVAL);
+ return -1;
+}
+
+extern "C" char * __stdcall
+rootdir (char *full_path)
+{
+ /* Possible choices:
+ * d:... -> d:/
+ * \\server\share... -> \\server\share\
+ * else current drive.
+ */
+ char *root = full_path;
+
+ if (full_path[1] == ':')
+ strcpy (full_path + 2, "\\");
+ else if (full_path[0] == '\\' && full_path[1] == '\\')
+ {
+ char *cp = full_path + 2;
+ while (*cp && *cp != '\\')
+ cp++;
+ if (!*cp)
+ {
+ set_errno (ENOTDIR);
+ return NULL;
+ }
+ cp++;
+ while (*cp && *cp != '\\')
+ cp++;
+ strcpy (cp, "\\");
+ }
+ else
+ root = NULL;
+
+ return root;
+}
+
+int __stdcall
+fhandler_base::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ debug_printf ("here");
+
+ if (is_fs_special ())
+ return fstat_fs (buf, pc);
+
+ switch (get_device ())
+ {
+ case FH_PIPE:
+ buf->st_mode = S_IFIFO | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
+ break;
+ case FH_PIPEW:
+ buf->st_mode = S_IFIFO | STD_WBITS | S_IWGRP | S_IWOTH;
+ break;
+ case FH_PIPER:
+ buf->st_mode = S_IFIFO | STD_RBITS;
+ break;
+ case FH_FLOPPY:
+ buf->st_mode = S_IFBLK | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
+ break;
+ default:
+ buf->st_mode = S_IFCHR | STD_RBITS | STD_WBITS | S_IWGRP | S_IWOTH;
+ break;
+ }
+
+ buf->st_nlink = 1;
+ buf->st_blksize = S_BLKSIZE;
+ time_as_timestruc_t (&buf->st_ctim);
+ buf->st_atim = buf->st_mtim = buf->st_ctim;
+ return 0;
+}
+
+void
+fhandler_base::init (HANDLE f, DWORD a, mode_t bin)
+{
+ set_io_handle (f);
+ access = a;
+ a &= GENERIC_READ | GENERIC_WRITE;
+ int flags = 0;
+ if (a == GENERIC_READ)
+ flags = O_RDONLY;
+ else if (a == GENERIC_WRITE)
+ flags = O_WRONLY;
+ else if (a == (GENERIC_READ | GENERIC_WRITE))
+ flags = O_RDWR;
+ set_flags (flags | bin);
+ set_open_status ();
+ debug_printf ("created new fhandler_base for handle %p, bin %d", f, get_r_binary ());
+}
+
+void
+fhandler_base::dump (void)
+{
+ paranoid_printf ("here");
+}
+
+int
+fhandler_base::dup (fhandler_base *child)
+{
+ debug_printf ("in fhandler_base dup");
+
+ HANDLE nh;
+ if (!get_nohandle ())
+ {
+ if (!DuplicateHandle (hMainProc, get_handle (), hMainProc, &nh, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("dup(%s) failed, handle %x, %E",
+ get_name (), get_handle ());
+ __seterrno ();
+ return -1;
+ }
+
+ child->set_io_handle (nh);
+ }
+ return 0;
+}
+
+int fhandler_base::fcntl (int cmd, void *arg)
+{
+ int res;
+
+ switch (cmd)
+ {
+ case F_GETFD:
+ res = get_close_on_exec () ? FD_CLOEXEC : 0;
+ break;
+ case F_SETFD:
+ set_close_on_exec ((int) arg);
+ res = 0;
+ break;
+ case F_GETFL:
+ res = get_flags ();
+ debug_printf ("GETFL: %d", res);
+ break;
+ case F_SETFL:
+ {
+ /*
+ * Only O_APPEND, O_ASYNC and O_NONBLOCK/O_NDELAY are allowed.
+ * Each other flag will be ignored.
+ * Since O_ASYNC isn't defined in fcntl.h it's currently
+ * ignored as well.
+ */
+ const int allowed_flags = O_APPEND | O_NONBLOCK_MASK;
+ int new_flags = (int) arg & allowed_flags;
+ /* Carefully test for the O_NONBLOCK or deprecated OLD_O_NDELAY flag.
+ Set only the flag that has been passed in. If both are set, just
+ record O_NONBLOCK. */
+ if ((new_flags & OLD_O_NDELAY) && (new_flags & O_NONBLOCK))
+ new_flags = O_NONBLOCK;
+ set_flags ((get_flags () & ~allowed_flags) | new_flags);
+ }
+ res = 0;
+ break;
+ case F_GETLK:
+ case F_SETLK:
+ case F_SETLKW:
+ res = lock (cmd, (struct flock *) arg);
+ break;
+ default:
+ set_errno (EINVAL);
+ res = -1;
+ break;
+ }
+ return res;
+}
+
+/* Base terminal handlers. These just return errors. */
+
+int
+fhandler_base::tcflush (int)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+int
+fhandler_base::tcsendbreak (int)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+int
+fhandler_base::tcdrain (void)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+int
+fhandler_base::tcflow (int)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+int
+fhandler_base::tcsetattr (int, const struct termios *)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+int
+fhandler_base::tcgetattr (struct termios *)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+int
+fhandler_base::tcsetpgrp (const pid_t)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+int
+fhandler_base::tcgetpgrp (void)
+{
+ set_errno (ENOTTY);
+ return -1;
+}
+
+void
+fhandler_base::operator delete (void *p)
+{
+ cfree (p);
+ return;
+}
+
+/* Normal I/O constructor */
+fhandler_base::fhandler_base ():
+ status (0),
+ access (0),
+ io_handle (NULL),
+ namehash (0),
+ openflags (0),
+ rabuf (NULL),
+ ralen (0),
+ raixget (0),
+ raixput (0),
+ rabuflen (0),
+ unix_path_name (NULL),
+ win32_path_name (NULL),
+ open_status (0),
+ read_state (NULL)
+{
+}
+
+/* Normal I/O destructor */
+fhandler_base::~fhandler_base (void)
+{
+ if (unix_path_name != NULL)
+ cfree ((void *) unix_path_name);
+ if (win32_path_name != NULL)
+ cfree ((void *) win32_path_name);
+ if (rabuf)
+ free (rabuf);
+ unix_path_name = win32_path_name = NULL;
+}
+
+/**********************************************************************/
+/* /dev/null */
+
+fhandler_dev_null::fhandler_dev_null () :
+ fhandler_base ()
+{
+}
+
+void
+fhandler_dev_null::dump (void)
+{
+ paranoid_printf ("here");
+}
+
+void
+fhandler_base::set_inheritance (HANDLE &h, int not_inheriting)
+{
+#ifdef DEBUGGING_AND_FDS_PROTECTED
+ HANDLE oh = h;
+#endif
+ /* Note that we could use SetHandleInformation here but it is not available
+ on all platforms. Test cases seem to indicate that using DuplicateHandle
+ in this fashion does not actually close the original handle, which is
+ what we want. If this changes in the future, we may be forced to use
+ SetHandleInformation on newer OS's */
+ if (!DuplicateHandle (hMainProc, h, hMainProc, &h, 0, !not_inheriting,
+ DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ debug_printf ("DuplicateHandle failed, %E");
+#ifdef DEBUGGING_AND_FDS_PROTECTED
+ if (h)
+ setclexec (oh, h, not_inheriting);
+#endif
+}
+
+void
+fhandler_base::fork_fixup (HANDLE parent, HANDLE &h, const char *name)
+{
+ if (/* !is_socket () && */ !get_close_on_exec ())
+ debug_printf ("handle %p already opened", h);
+ else if (!DuplicateHandle (parent, h, hMainProc, &h, 0, !get_close_on_exec (),
+ DUPLICATE_SAME_ACCESS))
+ system_printf ("%s - %E, handle %s<%p>", get_name (), name, h);
+#ifdef DEBUGGING_AND_FDS_PROTECTED
+ else if (get_close_on_exec ())
+ ProtectHandle (h); /* would have to be fancier than this */
+ else
+ /* ProtectHandleINH (h) */; /* Should already be protected */
+#endif
+}
+
+void
+fhandler_base::set_close_on_exec (int val)
+{
+ if (!get_nohandle ())
+ set_inheritance (io_handle, val);
+ set_close_on_exec_flag (val);
+ debug_printf ("set close_on_exec for %s to %d", get_name (), val);
+}
+
+void
+fhandler_base::fixup_after_fork (HANDLE parent)
+{
+ debug_printf ("inheriting '%s' from parent", get_name ());
+ if (!get_nohandle ())
+ fork_fixup (parent, io_handle, "io_handle");
+}
+
+bool
+fhandler_base::is_nonblocking ()
+{
+ return (openflags & O_NONBLOCK_MASK) != 0;
+}
+
+void
+fhandler_base::set_nonblocking (int yes)
+{
+ int current = openflags & O_NONBLOCK_MASK;
+ int new_flags = yes ? (!current ? O_NONBLOCK : current) : 0;
+ openflags = (openflags & ~O_NONBLOCK_MASK) | new_flags;
+}
+
+DIR *
+fhandler_base::opendir (path_conv&)
+{
+ set_errno (ENOTDIR);
+ return NULL;
+}
+
+struct dirent *
+fhandler_base::readdir (DIR *)
+{
+ set_errno (ENOTDIR);
+ return NULL;
+}
+
+__off64_t
+fhandler_base::telldir (DIR *)
+{
+ set_errno (ENOTDIR);
+ return -1;
+}
+
+void
+fhandler_base::seekdir (DIR *, __off64_t)
+{
+ set_errno (ENOTDIR);
+ return;
+}
+
+void
+fhandler_base::rewinddir (DIR *)
+{
+ set_errno (ENOTDIR);
+ return;
+}
+
+int
+fhandler_base::closedir (DIR *)
+{
+ set_errno (ENOTDIR);
+ return -1;
+}
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
new file mode 100644
index 00000000000..337d40e5adf
--- /dev/null
+++ b/winsup/cygwin/fhandler.h
@@ -0,0 +1,1250 @@
+/* fhandler.h
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _FHANDLER_H_
+#define _FHANDLER_H_
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+
+enum
+{
+ FH_RBINARY = 0x00001000, /* binary read mode */
+ FH_WBINARY = 0x00002000, /* binary write mode */
+ FH_CLOEXEC = 0x00004000, /* close-on-exec */
+ FH_RBINSET = 0x00008000, /* binary read mode has been explicitly set */
+ FH_WBINSET = 0x00010000, /* binary write mode has been explicitly set */
+ FH_APPEND = 0x00020000, /* always append */
+ FH_ASYNC = 0x00040000, /* async I/O */
+ FH_SIGCLOSE = 0x00080000, /* signal handler should close fd on interrupt */
+
+ FH_SYMLINK = 0x00100000, /* is a symlink */
+ FH_EXECABL = 0x00200000, /* file looked like it would run:
+ * ends in .exe or .bat or begins with #! */
+ FH_W95LSBUG = 0x00400000, /* set when lseek is called as a flag that
+ * _write should check if we've moved beyond
+ * EOF, zero filling if so. */
+ FH_NOHANDLE = 0x00800000, /* No handle associated with fhandler. */
+ FH_NOEINTR = 0x01000000, /* Set if I/O should be uninterruptible. */
+ FH_FFIXUP = 0x02000000, /* Set if need to fixup after fork. */
+ FH_LOCAL = 0x04000000, /* File is unix domain socket */
+ FH_SHUTRD = 0x08000000, /* Socket saw a SHUT_RD */
+ FH_SHUTWR = 0x10000000, /* Socket saw a SHUT_WR */
+ FH_ISREMOTE = 0x10000000, /* File is on a remote drive */
+ FH_DCEXEC = 0x20000000, /* Don't care if this is executable */
+ FH_HASACLS = 0x40000000, /* True if fs of file has ACLS */
+ FH_QUERYOPEN = 0x80000000, /* open file without requesting either read
+ or write access */
+};
+
+#include "devices.h"
+
+#define FHDEVN(n) (n)
+#define FHISSETF(x) __ISSETF (this, x, FH)
+#define FHSETF(x) __SETF (this, x, FH)
+#define FHCLEARF(x) __CLEARF (this, x, FH)
+#define FHCONDSETF(n, x) __CONDSETF(n, this, x, FH)
+
+#define FHSTATOFF 0
+
+/* fcntl flags used only internaly. */
+#define O_NOSYMLINK 0x080000
+#define O_DIROPEN 0x100000
+
+/* newlib used to define O_NDELAY differently from O_NONBLOCK. Now it
+ properly defines both to be the same. Unfortunately, we have to
+ behave properly the old version, too, to accommodate older executables. */
+#define OLD_O_NDELAY (CYGWIN_VERSION_CHECK_FOR_OLD_O_NONBLOCK ? 4 : 0)
+
+/* Care for the old O_NDELAY flag. If one of the flags is set,
+ both flags are set. */
+#define O_NONBLOCK_MASK (O_NONBLOCK | OLD_O_NDELAY)
+
+#define UNCONNECTED 0
+#define CONNECT_PENDING 1
+#define CONNECTED 2
+
+extern const char *windows_device_names[];
+extern struct __cygwin_perfile *perfile_table;
+#define __fmode (*(user_data->fmode_ptr))
+extern const char proc[];
+extern const int proc_len;
+
+class select_record;
+class path_conv;
+class fhandler_disk_file;
+typedef struct __DIR DIR;
+struct dirent;
+struct iovec;
+
+enum line_edit_status
+{
+ line_edit_ok = 0,
+ line_edit_input_done = 1,
+ line_edit_signalled = 2,
+ line_edit_error = 3,
+ line_edit_pipe_full = 4
+};
+
+enum bg_check_types
+{
+ bg_error = -1,
+ bg_eof = 0,
+ bg_ok = 1,
+ bg_signalled = 2
+};
+
+enum executable_states
+{
+ is_executable,
+ dont_care_if_executable,
+ not_executable = dont_care_if_executable,
+ dont_know_if_executable
+};
+
+class fhandler_base
+{
+ protected:
+ DWORD status;
+ private:
+ int access;
+ HANDLE io_handle;
+
+ unsigned long namehash; /* hashed filename, used as inode num */
+
+ protected:
+ /* Full unix path name of this file */
+ /* File open flags from open () and fcntl () calls */
+ int openflags;
+
+ char *rabuf; /* used for crlf conversion in text files */
+ size_t ralen;
+ size_t raixget;
+ size_t raixput;
+ size_t rabuflen;
+
+ const char *unix_path_name;
+ const char *win32_path_name;
+ DWORD open_status;
+ HANDLE read_state;
+
+ public:
+ device dev;
+ void set_name (const char *unix_path, const char *win32_path = NULL);
+
+ virtual fhandler_base& operator =(fhandler_base &x);
+ fhandler_base ();
+ virtual ~fhandler_base ();
+
+ /* Non-virtual simple accessor functions. */
+ void set_io_handle (HANDLE x) { io_handle = x; }
+
+ DWORD get_device () const { return dev.devn; }
+ DWORD get_major () const { return dev.major; }
+ DWORD get_minor () const { return dev.minor; }
+ virtual int get_unit () const { return dev.minor; }
+
+ int get_access () const { return access; }
+ void set_access (int x) { access = x; }
+
+ bool get_async () { return FHISSETF (ASYNC); }
+ void set_async (int x) { FHCONDSETF (x, ASYNC); }
+
+ int get_flags () { return openflags; }
+ void set_flags (int x, int supplied_bin = 0);
+
+ bool is_nonblocking ();
+ void set_nonblocking (int yes);
+
+ bool get_w_binary () { return FHISSETF (WBINSET) ? FHISSETF (WBINARY) : 1; }
+ bool get_r_binary () { return FHISSETF (RBINSET) ? FHISSETF (RBINARY) : 1; }
+
+ bool get_w_binset () { return FHISSETF (WBINSET); }
+ bool get_r_binset () { return FHISSETF (RBINSET); }
+
+ void set_w_binary (int b) { FHCONDSETF (b, WBINARY); FHSETF (WBINSET); }
+ void set_r_binary (int b) { FHCONDSETF (b, RBINARY); FHSETF (RBINSET); }
+ void clear_w_binary () {FHCLEARF (WBINARY); FHCLEARF (WBINSET); }
+ void clear_r_binary () {FHCLEARF (RBINARY); FHCLEARF (RBINSET); }
+
+ bool get_nohandle () { return FHISSETF (NOHANDLE); }
+ void set_nohandle (int x) { FHCONDSETF (x, NOHANDLE); }
+
+ void set_open_status () {open_status = status;}
+ DWORD get_open_status () {return open_status;}
+ void reset_to_open_binmode ()
+ {
+ set_flags ((get_flags () & ~(O_TEXT | O_BINARY))
+ | ((open_status & (FH_WBINARY | FH_RBINARY)
+ ? O_BINARY : O_TEXT)));
+ }
+
+ int get_default_fmode (int flags);
+
+ bool get_r_no_interrupt () { return FHISSETF (NOEINTR); }
+ void set_r_no_interrupt (bool b) { FHCONDSETF (b, NOEINTR); }
+
+ bool get_close_on_exec () { return FHISSETF (CLOEXEC); }
+ int set_close_on_exec_flag (int b) { return FHCONDSETF (b, CLOEXEC); }
+
+ LPSECURITY_ATTRIBUTES get_inheritance (bool all = 0)
+ {
+ if (all)
+ return get_close_on_exec () ? &sec_all_nih : &sec_all;
+ else
+ return get_close_on_exec () ? &sec_none_nih : &sec_none;
+ }
+
+ void set_check_win95_lseek_bug (int b = 1) { FHCONDSETF (b, W95LSBUG); }
+ bool get_check_win95_lseek_bug () { return FHISSETF (W95LSBUG); }
+
+ bool get_need_fork_fixup () { return FHISSETF (FFIXUP); }
+ void set_need_fork_fixup () { FHSETF (FFIXUP); }
+
+ virtual void set_close_on_exec (int val);
+
+ virtual void fixup_before_fork_exec (DWORD) {}
+ virtual void fixup_after_fork (HANDLE);
+ virtual void fixup_after_exec (HANDLE) {}
+
+ bool get_symlink_p () { return FHISSETF (SYMLINK); }
+ void set_symlink_p (int val) { FHCONDSETF (val, SYMLINK); }
+ void set_symlink_p () { FHSETF (SYMLINK); }
+
+ bool get_socket_p () { return FHISSETF (LOCAL); }
+ void set_socket_p (int val) { FHCONDSETF (val, LOCAL); }
+ void set_socket_p () { FHSETF (LOCAL); }
+
+ bool get_execable_p () { return FHISSETF (EXECABL); }
+ void set_execable_p (executable_states val)
+ {
+ FHCONDSETF (val == is_executable, EXECABL);
+ FHCONDSETF (val == dont_care_if_executable, DCEXEC);
+ }
+ void set_execable_p () { FHSETF (EXECABL); }
+ bool dont_care_if_execable () { return FHISSETF (DCEXEC); }
+ bool exec_state_isknown () { return FHISSETF (DCEXEC) || FHISSETF (EXECABL); }
+
+ bool get_append_p () { return FHISSETF (APPEND); }
+ void set_append_p (int val) { FHCONDSETF (val, APPEND); }
+ void set_append_p () { FHSETF (APPEND); }
+
+ bool get_query_open () { return FHISSETF (QUERYOPEN); }
+ void set_query_open (bool val) { FHCONDSETF (val, QUERYOPEN); }
+
+ bool get_readahead_valid () { return raixget < ralen; }
+ int puts_readahead (const char *s, size_t len = (size_t) -1);
+ int put_readahead (char value);
+
+ int get_readahead ();
+ int peek_readahead (int queryput = 0);
+
+ int eat_readahead (int n);
+
+ void set_readahead_valid (int val, int ch = -1);
+
+ int get_readahead_into_buffer (char *buf, size_t buflen);
+
+ bool has_acls () { return FHISSETF (HASACLS); }
+ void set_has_acls (int val) { FHCONDSETF (val, HASACLS); }
+
+ bool isremote () { return FHISSETF (ISREMOTE); }
+ void set_isremote (int val) { FHCONDSETF (val, ISREMOTE); }
+
+ const char *get_name () { return unix_path_name; }
+ const char *get_win32_name () { return win32_path_name; }
+ unsigned long get_namehash () { return namehash; }
+
+ virtual void hclose (HANDLE h) {CloseHandle (h);}
+ virtual void set_inheritance (HANDLE &h, int not_inheriting);
+
+ /* fixup fd possibly non-inherited handles after fork */
+ void fork_fixup (HANDLE parent, HANDLE &h, const char *name);
+
+ virtual int open (path_conv *real_path, int flags, mode_t mode = 0);
+ int open_fs (path_conv *real_path, int flags, mode_t mode = 0);
+ virtual int close ();
+ int close_fs ();
+ virtual int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+ int __stdcall fstat_fs (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+ int __stdcall fstat_helper (struct __stat64 *buf, path_conv *pc,
+ FILETIME ftCreateionTime,
+ FILETIME ftLastAccessTime,
+ FILETIME ftLastWriteTime,
+ DWORD nFileSizeHigh,
+ DWORD nFileSizeLow,
+ DWORD nFileIndexHigh = 0,
+ DWORD nFileIndexLow = 0,
+ DWORD nNumberOfLinks = 1)
+ __attribute__ ((regparm (3)));
+ int __stdcall fstat_by_handle (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3)));
+ int __stdcall fstat_by_name (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3)));
+ virtual int ioctl (unsigned int cmd, void *);
+ virtual int fcntl (int cmd, void *);
+ virtual char const *ttyname () { return get_name(); }
+ virtual void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ virtual int write (const void *ptr, size_t len);
+ virtual ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1);
+ virtual ssize_t writev (const struct iovec *, int iovcnt, ssize_t tot = -1);
+ virtual __off64_t lseek (__off64_t offset, int whence);
+ virtual int lock (int, struct flock *);
+ virtual void dump ();
+ virtual int dup (fhandler_base *child);
+
+ virtual HANDLE mmap (caddr_t *addr, size_t len, DWORD access,
+ int flags, __off64_t off);
+ virtual int munmap (HANDLE h, caddr_t addr, size_t len);
+ virtual int msync (HANDLE h, caddr_t addr, size_t len, int flags);
+ virtual BOOL fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset,
+ DWORD size, void *address);
+
+ void *operator new (size_t, void *p) {return p;}
+
+ virtual void init (HANDLE, DWORD, mode_t);
+
+ virtual int tcflush (int);
+ virtual int tcsendbreak (int);
+ virtual int tcdrain ();
+ virtual int tcflow (int);
+ virtual int tcsetattr (int a, const struct termios *t);
+ virtual int tcgetattr (struct termios *t);
+ virtual int tcsetpgrp (const pid_t pid);
+ virtual int tcgetpgrp ();
+ virtual int is_tty () { return 0; }
+ virtual bool isdevice () { return true; }
+ virtual bool isfifo () { return false; }
+ virtual char *ptsname () { return NULL;}
+ virtual class fhandler_socket *is_socket () { return 0; }
+ virtual class fhandler_console *is_console () { return 0; }
+ virtual int is_windows () {return 0; }
+
+ virtual void raw_read (void *ptr, size_t& ulen);
+ virtual int raw_write (const void *ptr, size_t ulen);
+
+ /* Virtual accessor functions to hide the fact
+ that some fd's have two handles. */
+ virtual HANDLE& get_handle () { return io_handle; }
+ virtual HANDLE& get_io_handle () { return io_handle; }
+ virtual HANDLE& get_output_handle () { return io_handle; }
+ virtual bool hit_eof () {return FALSE;}
+ virtual select_record *select_read (select_record *s);
+ virtual select_record *select_write (select_record *s);
+ virtual select_record *select_except (select_record *s);
+ virtual int ready_for_read (int fd, DWORD howlong);
+ virtual const char *get_native_name ()
+ {
+ return dev.fmt;
+ }
+ virtual bg_check_types bg_check (int) {return bg_ok;}
+ void clear_readahead ()
+ {
+ raixput = raixget = ralen = rabuflen = 0;
+ rabuf = NULL;
+ }
+ void operator delete (void *);
+ virtual HANDLE get_guard () const {return NULL;}
+ virtual void set_eof () {}
+ virtual DIR *opendir (path_conv& pc);
+ virtual dirent *readdir (DIR *);
+ virtual __off64_t telldir (DIR *);
+ virtual void seekdir (DIR *, __off64_t);
+ virtual void rewinddir (DIR *);
+ virtual int closedir (DIR *);
+ virtual bool is_slow () {return 0;}
+ bool is_auto_device () {return isdevice () && !dev.isfs ();}
+ bool is_fs_special () {return dev.isfs ();}
+ bool device_access_denied (int) __attribute__ ((regparm (1)));
+};
+
+class fhandler_socket: public fhandler_base
+{
+ private:
+ int addr_family;
+ int type;
+ int connect_secret [4];
+ HANDLE secret_event;
+ struct _WSAPROTOCOL_INFOA *prot_info_ptr;
+ char *sun_path;
+ int had_connect_or_listen;
+
+ public:
+ fhandler_socket ();
+ ~fhandler_socket ();
+ int get_socket () { return (int) get_handle(); }
+ fhandler_socket *is_socket () { return this; }
+
+ bool saw_shutdown_read () const {return FHISSETF (SHUTRD);}
+ bool saw_shutdown_write () const {return FHISSETF (SHUTWR);}
+
+ void set_shutdown_read () {FHSETF (SHUTRD);}
+ void set_shutdown_write () {FHSETF (SHUTWR);}
+
+ bool is_unconnected () const {return had_connect_or_listen == UNCONNECTED;}
+ bool is_connect_pending () const {return had_connect_or_listen == CONNECT_PENDING;}
+ bool is_connected () const {return had_connect_or_listen == CONNECTED;}
+ void set_connect_state (int newstate) { had_connect_or_listen = newstate; }
+
+ int bind (const struct sockaddr *name, int namelen);
+ int connect (const struct sockaddr *name, int namelen);
+ int listen (int backlog);
+ int accept (struct sockaddr *peer, int *len);
+ int getsockname (struct sockaddr *name, int *namelen);
+ int getpeername (struct sockaddr *name, int *namelen);
+
+ ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1);
+ int recvfrom (void *ptr, size_t len, int flags,
+ struct sockaddr *from, int *fromlen);
+ int recvmsg (struct msghdr *msg, int flags, ssize_t tot = -1);
+
+ ssize_t writev (const struct iovec *, int iovcnt, ssize_t tot = -1);
+ int sendto (const void *ptr, size_t len, int flags,
+ const struct sockaddr *to, int tolen);
+ int sendmsg (const struct msghdr *msg, int flags, ssize_t tot = -1);
+
+ int ioctl (unsigned int cmd, void *);
+ int fcntl (int cmd, void *);
+ __off64_t lseek (__off64_t, int) { return 0; }
+ int shutdown (int how);
+ int close ();
+ void hclose (HANDLE) {close ();}
+ int dup (fhandler_base *child);
+
+ void set_close_on_exec (int val);
+ virtual void fixup_before_fork_exec (DWORD);
+ void fixup_after_fork (HANDLE);
+ void fixup_after_exec (HANDLE);
+
+ select_record *select_read (select_record *s);
+ select_record *select_write (select_record *s);
+ select_record *select_except (select_record *s);
+ void set_addr_family (int af) {addr_family = af;}
+ int get_addr_family () {return addr_family;}
+ void set_socket_type (int st) { type = st;}
+ int get_socket_type () {return type;}
+ void set_sun_path (const char *path);
+ char *get_sun_path () {return sun_path;}
+ void set_connect_secret ();
+ void get_connect_secret (char*);
+ HANDLE create_secret_event (int *secret = NULL);
+ int check_peer_secret_event (struct sockaddr_in *peer, int *secret = NULL);
+ void signal_secret_event ();
+ void close_secret_event ();
+ int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+ bool is_slow () {return 1;}
+};
+
+class fhandler_pipe: public fhandler_base
+{
+protected:
+ HANDLE guard;
+ bool broken_pipe;
+ HANDLE writepipe_exists;
+ DWORD orig_pid;
+ unsigned id;
+public:
+ fhandler_pipe ();
+ __off64_t lseek (__off64_t offset, int whence);
+ select_record *select_read (select_record *s);
+ select_record *select_write (select_record *s);
+ select_record *select_except (select_record *s);
+ void set_close_on_exec (int val);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ int close ();
+ void create_guard (SECURITY_ATTRIBUTES *sa) {guard = CreateMutex (sa, FALSE, NULL);}
+ int dup (fhandler_base *child);
+ int ioctl (unsigned int cmd, void *);
+ void fixup_after_fork (HANDLE);
+ void fixup_after_exec (HANDLE);
+ bool hit_eof ();
+ void set_eof () {broken_pipe = true;}
+ HANDLE get_guard () const {return guard;}
+ int ready_for_read (int fd, DWORD howlong);
+ static int create (fhandler_pipe *[2], unsigned, int, bool = false);
+ bool is_slow () {return 1;}
+ friend class fhandler_fifo;
+};
+
+class fhandler_fifo: public fhandler_pipe
+{
+ HANDLE output_handle;
+ HANDLE owner; // You can't have too many mutexes, now, can you?
+ ATOM upand;
+ long read_use;
+ long write_use;
+public:
+ fhandler_fifo ();
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int open_not_mine (int flags) __attribute__ ((regparm (2)));
+ int close ();
+ void set_use (int flags) __attribute__ ((regparm (2)));
+ bool isfifo () { return true; }
+ HANDLE& get_output_handle () { return output_handle; }
+ void set_output_handle (HANDLE h) { output_handle = h; }
+ void set_use ();
+ bool is_slow () {return 1;}
+ ATOM& get_atom () {return upand;}
+};
+
+class fhandler_dev_raw: public fhandler_base
+{
+ protected:
+ char *devbuf;
+ size_t devbufsiz;
+ size_t devbufstart;
+ size_t devbufend;
+ int eom_detected : 1;
+ int eof_detected : 1;
+ int lastblk_to_read : 1;
+ int is_writing : 1;
+ int has_written : 1;
+ int varblkop : 1;
+
+ virtual void clear (void);
+ virtual int writebuf (void);
+
+ /* returns not null, if `win_error' determines an end of media condition */
+ virtual int is_eom(int win_error) = 0;
+ /* returns not null, if `win_error' determines an end of file condition */
+ virtual int is_eof(int win_error) = 0;
+
+ fhandler_dev_raw ();
+
+ public:
+ ~fhandler_dev_raw (void);
+
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int close (void);
+
+ void raw_read (void *ptr, size_t& ulen);
+ int raw_write (const void *ptr, size_t ulen);
+
+ int dup (fhandler_base *child);
+
+ int ioctl (unsigned int cmd, void *buf);
+
+ void fixup_after_fork (HANDLE);
+ void fixup_after_exec (HANDLE);
+};
+
+class fhandler_dev_floppy: public fhandler_dev_raw
+{
+ protected:
+ virtual int is_eom (int win_error);
+ virtual int is_eof (int win_error);
+
+ public:
+ fhandler_dev_floppy ();
+
+ virtual int open (path_conv *, int flags, mode_t mode = 0);
+ virtual int close (void);
+
+ virtual __off64_t lseek (__off64_t offset, int whence);
+
+ virtual int ioctl (unsigned int cmd, void *buf);
+};
+
+class fhandler_dev_tape: public fhandler_dev_raw
+{
+ int lasterr;
+
+ bool is_rewind_device () { return get_unit () < 128; }
+
+ protected:
+ virtual void clear (void);
+
+ virtual int is_eom (int win_error);
+ virtual int is_eof (int win_error);
+
+ public:
+ fhandler_dev_tape ();
+
+ virtual int open (path_conv *, int flags, mode_t mode = 0);
+ virtual int close (void);
+
+ virtual __off64_t lseek (__off64_t offset, int whence);
+
+ virtual int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+
+ virtual int dup (fhandler_base *child);
+
+ virtual int ioctl (unsigned int cmd, void *buf);
+
+ private:
+ int tape_write_marks (int marktype, DWORD len);
+ int tape_get_pos (unsigned long *ret);
+ int tape_set_pos (int mode, long count, BOOLEAN sfm_func = FALSE);
+ int tape_erase (int mode);
+ int tape_prepare (int action);
+ BOOLEAN tape_get_feature (DWORD parm);
+ int tape_get_blocksize (long *min, long *def, long *max, long *cur);
+ int tape_set_blocksize (long count);
+ int tape_status (struct mtget *get);
+ int tape_compression (long count);
+};
+
+/* Standard disk file */
+
+class fhandler_disk_file: public fhandler_base
+{
+ public:
+ fhandler_disk_file ();
+
+ int open (path_conv *real_path, int flags, mode_t mode);
+ int close ();
+ int lock (int, struct flock *);
+ bool isdevice () { return false; }
+ int __stdcall fstat (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3)));
+
+ HANDLE mmap (caddr_t *addr, size_t len, DWORD access, int flags, __off64_t off);
+ int munmap (HANDLE h, caddr_t addr, size_t len);
+ int msync (HANDLE h, caddr_t addr, size_t len, int flags);
+ BOOL fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset,
+ DWORD size, void *address);
+ DIR *opendir (path_conv& pc);
+ struct dirent *readdir (DIR *);
+ __off64_t telldir (DIR *);
+ void seekdir (DIR *, __off64_t);
+ void rewinddir (DIR *);
+ int closedir (DIR *);
+};
+
+class fhandler_cygdrive: public fhandler_disk_file
+{
+ int ndrives;
+ const char *pdrive;
+ void set_drives ();
+ public:
+ bool iscygdrive_root () const { return !dev.minor; }
+ fhandler_cygdrive ();
+ DIR *opendir (path_conv& pc);
+ struct dirent *readdir (DIR *);
+ __off64_t telldir (DIR *);
+ void seekdir (DIR *, __off64_t);
+ void rewinddir (DIR *);
+ int closedir (DIR *);
+ int __stdcall fstat (struct __stat64 *buf, path_conv *pc) __attribute__ ((regparm (3)));
+};
+
+class fhandler_serial: public fhandler_base
+{
+ private:
+ size_t vmin_; /* from termios */
+ unsigned int vtime_; /* from termios */
+ pid_t pgrp_;
+ int rts; /* for Windows 9x purposes only */
+ int dtr; /* for Windows 9x purposes only */
+
+ public:
+ int overlapped_armed;
+ OVERLAPPED io_status;
+ DWORD ev;
+
+ /* Constructor */
+ fhandler_serial ();
+
+ int open (path_conv *, int flags, mode_t mode);
+ int close ();
+ void init (HANDLE h, DWORD a, mode_t flags);
+ void overlapped_setup ();
+ int dup (fhandler_base *child);
+ void raw_read (void *ptr, size_t& ulen);
+ int raw_write (const void *ptr, size_t ulen);
+ int tcsendbreak (int);
+ int tcdrain ();
+ int tcflow (int);
+ int ioctl (unsigned int cmd, void *);
+ int tcsetattr (int a, const struct termios *t);
+ int tcgetattr (struct termios *t);
+ __off64_t lseek (__off64_t, int) { return 0; }
+ int tcflush (int);
+ void dump ();
+ int is_tty () { return 1; }
+ void fixup_after_fork (HANDLE parent);
+ void fixup_after_exec (HANDLE);
+
+ /* We maintain a pgrp so that tcsetpgrp and tcgetpgrp work, but we
+ don't use it for permissions checking. fhandler_tty_slave does
+ permission checking on pgrps. */
+ virtual int tcgetpgrp () { return pgrp_; }
+ virtual int tcsetpgrp (const pid_t pid) { pgrp_ = pid; return 0; }
+ select_record *select_read (select_record *s);
+ select_record *select_write (select_record *s);
+ select_record *select_except (select_record *s);
+ bool is_slow () {return 1;}
+};
+
+#define acquire_output_mutex(ms) \
+ __acquire_output_mutex (__PRETTY_FUNCTION__, __LINE__, ms);
+
+#define release_output_mutex() \
+ __release_output_mutex (__PRETTY_FUNCTION__, __LINE__);
+
+class tty;
+class tty_min;
+class fhandler_termios: public fhandler_base
+{
+ protected:
+ HANDLE output_handle;
+ virtual void doecho (const void *, DWORD) {};
+ virtual int accept_input () {return 1;};
+ public:
+ tty_min *tc;
+ fhandler_termios () :
+ fhandler_base ()
+ {
+ set_need_fork_fixup ();
+ }
+ HANDLE& get_output_handle () { return output_handle; }
+ line_edit_status line_edit (const char *rptr, int nread, termios&);
+ void set_output_handle (HANDLE h) { output_handle = h; }
+ void tcinit (tty_min *this_tc, int force = FALSE);
+ virtual int is_tty () { return 1; }
+ int tcgetpgrp ();
+ int tcsetpgrp (int pid);
+ bg_check_types bg_check (int sig);
+ virtual DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms) {return 1;}
+ virtual void __release_output_mutex (const char *fn, int ln) {}
+ void fixup_after_fork (HANDLE);
+ void fixup_after_exec (HANDLE parent) { fixup_after_fork (parent); }
+ void echo_erase (int force = 0);
+ virtual __off64_t lseek (__off64_t, int);
+};
+
+enum ansi_intensity
+{
+ INTENSITY_INVISIBLE,
+ INTENSITY_DIM,
+ INTENSITY_NORMAL,
+ INTENSITY_BOLD
+};
+
+#define normal 0
+#define gotesc 1
+#define gotsquare 2
+#define gotarg1 3
+#define gotrsquare 4
+#define gotcommand 5
+#define gettitle 6
+#define eattitle 7
+#define MAXARGS 10
+
+class dev_console
+{
+ WORD default_color, underline_color, dim_color;
+
+ /* Used to determine if an input keystroke should be modified with META. */
+ int meta_mask;
+
+/* Output state */
+ int state_;
+ int args_[MAXARGS];
+ int nargs_;
+ unsigned rarg;
+ bool saw_question_mark;
+
+ char my_title_buf [TITLESIZE + 1];
+
+ WORD current_win32_attr;
+ ansi_intensity intensity;
+ bool underline, blink, reverse;
+ WORD fg, bg;
+
+ /* saved cursor coordinates */
+ int savex, savey;
+
+ /* saved screen */
+ COORD savebufsiz;
+ PCHAR_INFO savebuf;
+
+ struct
+ {
+ short Top, Bottom;
+ } scroll_region;
+ struct
+ {
+ SHORT winTop;
+ SHORT winBottom;
+ COORD dwWinSize;
+ COORD dwBufferSize;
+ COORD dwCursorPosition;
+ WORD wAttributes;
+ } info;
+
+ COORD dwLastCursorPosition;
+ DWORD dwLastButtonState;
+ int nModifiers;
+
+ bool insert_mode;
+ bool use_mouse;
+ bool raw_win32_keyboard_mode;
+ friend class fhandler_console;
+};
+
+/* This is a input and output console handle */
+class fhandler_console: public fhandler_termios
+{
+ private:
+ static dev_console *dev_state;
+
+/* Output calls */
+ void set_default_attr ();
+ WORD get_win32_attr ();
+
+ BOOL fillin_info ();
+ void clear_screen (int, int, int, int);
+ void scroll_screen (int, int, int, int, int, int);
+ void cursor_set (BOOL, int, int);
+ void cursor_get (int *, int *);
+ void cursor_rel (int, int);
+ const unsigned char *write_normal (unsigned const char*, unsigned const char *);
+ void char_command (char);
+ BOOL set_raw_win32_keyboard_mode (BOOL);
+ int output_tcsetattr (int a, const struct termios *t);
+
+/* Input calls */
+ int igncr_enabled ();
+ int input_tcsetattr (int a, const struct termios *t);
+ void set_cursor_maybe ();
+
+ public:
+
+ fhandler_console ();
+
+ fhandler_console* is_console () { return this; }
+
+ int open (path_conv *, int flags, mode_t mode = 0);
+
+ int write (const void *ptr, size_t len);
+ void doecho (const void *str, DWORD len) { (void) write (str, len); }
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ int close ();
+
+ int tcflush (int);
+ int tcsetattr (int a, const struct termios *t);
+ int tcgetattr (struct termios *t);
+
+ /* Special dup as we must dup two handles */
+ int dup (fhandler_base *child);
+
+ int ioctl (unsigned int cmd, void *);
+ void init (HANDLE, DWORD, mode_t);
+ bool mouse_aware () {return dev_state->use_mouse;}
+
+ select_record *select_read (select_record *s);
+ select_record *select_write (select_record *s);
+ select_record *select_except (select_record *s);
+ void fixup_after_exec (HANDLE);
+ void set_close_on_exec (int val);
+ void fixup_after_fork (HANDLE parent);
+ void set_input_state ();
+ void send_winch_maybe ();
+ static tty_min *get_tty_stuff (int);
+ bool is_slow () {return 1;}
+};
+
+class fhandler_tty_common: public fhandler_termios
+{
+ public:
+ fhandler_tty_common ()
+ : fhandler_termios (), output_done_event (NULL),
+ ioctl_request_event (NULL), ioctl_done_event (NULL), output_mutex (NULL),
+ input_mutex (NULL), input_available_event (NULL), inuse (NULL)
+ {
+ // nothing to do
+ }
+ HANDLE output_done_event; // Raised by master when tty's output buffer
+ // written. Write status in tty::write_retval.
+ HANDLE ioctl_request_event; // Raised by slave to perform ioctl() request.
+ // Ioctl() request in tty::cmd/arg.
+ HANDLE ioctl_done_event; // Raised by master on ioctl() completion.
+ // Ioctl() status in tty::ioctl_retval.
+ HANDLE output_mutex, input_mutex;
+ HANDLE input_available_event;
+ HANDLE inuse; // used to indicate that a tty is in use
+
+ DWORD __acquire_output_mutex (const char *fn, int ln, DWORD ms);
+ void __release_output_mutex (const char *fn, int ln);
+
+ virtual int dup (fhandler_base *child);
+
+ tty *get_ttyp () { return (tty *)tc; }
+
+ int close ();
+ void set_close_on_exec (int val);
+ void fixup_after_fork (HANDLE parent);
+ select_record *select_read (select_record *s);
+ select_record *select_write (select_record *s);
+ select_record *select_except (select_record *s);
+ bool is_slow () {return 1;}
+};
+
+class fhandler_tty_slave: public fhandler_tty_common
+{
+ public:
+ /* Constructor */
+ fhandler_tty_slave ();
+
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ void init (HANDLE, DWORD, mode_t);
+
+ int tcsetattr (int a, const struct termios *t);
+ int tcgetattr (struct termios *t);
+ int tcflush (int);
+ int ioctl (unsigned int cmd, void *);
+
+ __off64_t lseek (__off64_t, int) { return 0; }
+ select_record *select_read (select_record *s);
+ int cygserver_attach_tty (HANDLE*, HANDLE*);
+ int get_unit () __attribute__ ((regparm (1)));
+};
+
+class fhandler_pty_master: public fhandler_tty_common
+{
+ int pktmode; // non-zero if pty in a packet mode.
+protected:
+ device slave; // device type of slave
+public:
+ int need_nl; // Next read should start with \n
+
+ /* Constructor */
+ fhandler_pty_master ();
+
+ int process_slave_output (char *buf, size_t len, int pktmode_on);
+ void doecho (const void *str, DWORD len);
+ int accept_input ();
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ int close ();
+
+ int tcsetattr (int a, const struct termios *t);
+ int tcgetattr (struct termios *t);
+ int tcflush (int);
+ int ioctl (unsigned int cmd, void *);
+
+ __off64_t lseek (__off64_t, int) { return 0; }
+ char *ptsname ();
+
+ void set_close_on_exec (int val);
+ bool hit_eof ();
+ int get_unit () const { return slave.minor; }
+};
+
+class fhandler_tty_master: public fhandler_pty_master
+{
+ public:
+ /* Constructor */
+ fhandler_console *console; // device handler to perform real i/o.
+
+ fhandler_tty_master ();
+ int init ();
+ int init_console ();
+ void set_winsize (bool);
+ void fixup_after_fork (HANDLE parent);
+ void fixup_after_exec (HANDLE);
+ bool is_slow () {return 1;}
+};
+
+class fhandler_dev_null: public fhandler_base
+{
+ public:
+ fhandler_dev_null ();
+
+ void dump ();
+ select_record *select_read (select_record *s);
+ select_record *select_write (select_record *s);
+ select_record *select_except (select_record *s);
+};
+
+class fhandler_dev_zero: public fhandler_base
+{
+ public:
+ fhandler_dev_zero ();
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ __off64_t lseek (__off64_t offset, int whence);
+
+ void dump ();
+};
+
+class fhandler_dev_random: public fhandler_base
+{
+ protected:
+ HCRYPTPROV crypt_prov;
+ long pseudo;
+
+ BOOL crypt_gen_random (void *ptr, size_t len);
+ int pseudo_write (const void *ptr, size_t len);
+ int pseudo_read (void *ptr, size_t len);
+
+ public:
+ fhandler_dev_random ();
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ __off64_t lseek (__off64_t offset, int whence);
+ int close (void);
+ int dup (fhandler_base *child);
+
+ void dump ();
+};
+
+class fhandler_dev_mem: public fhandler_base
+{
+ protected:
+ DWORD mem_size;
+ __off64_t pos;
+
+ public:
+ fhandler_dev_mem ();
+ ~fhandler_dev_mem (void);
+
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t ulen);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ __off64_t lseek (__off64_t offset, int whence);
+ int close (void);
+ int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+ int dup (fhandler_base *child);
+
+ HANDLE mmap (caddr_t *addr, size_t len, DWORD access, int flags, __off64_t off);
+ int munmap (HANDLE h, caddr_t addr, size_t len);
+ int msync (HANDLE h, caddr_t addr, size_t len, int flags);
+ BOOL fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset,
+ DWORD size, void *address);
+
+ void dump ();
+} ;
+
+class fhandler_dev_clipboard: public fhandler_base
+{
+ public:
+ fhandler_dev_clipboard ();
+ int is_windows (void) { return 1; }
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ __off64_t lseek (__off64_t offset, int whence);
+ int close (void);
+
+ int dup (fhandler_base *child);
+
+ void dump ();
+
+ private:
+ __off64_t pos;
+ void *membuffer;
+ size_t msize;
+ bool eof;
+};
+
+class fhandler_windows: public fhandler_base
+{
+ private:
+ HWND hWnd_; // the window whose messages are to be retrieved by read() call
+ int method_; // write method (Post or Send)
+ public:
+ fhandler_windows ();
+ int is_windows (void) { return 1; }
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ int ioctl (unsigned int cmd, void *);
+ __off64_t lseek (__off64_t, int) { return 0; }
+ int close (void) { return 0; }
+
+ void set_close_on_exec (int val);
+ void fixup_after_fork (HANDLE parent);
+ select_record *select_read (select_record *s);
+ select_record *select_write (select_record *s);
+ select_record *select_except (select_record *s);
+ bool is_slow () {return 1;}
+};
+
+class fhandler_dev_dsp : public fhandler_base
+{
+ private:
+ int audioformat_;
+ int audiofreq_;
+ int audiobits_;
+ int audiochannels_;
+ bool setupwav(const char *pData, int nBytes);
+ public:
+ fhandler_dev_dsp ();
+ ~fhandler_dev_dsp();
+
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ int ioctl (unsigned int cmd, void *);
+ __off64_t lseek (__off64_t, int);
+ int close (void);
+ int dup (fhandler_base *child);
+ void dump (void);
+ void fixup_after_exec (HANDLE);
+};
+
+class fhandler_virtual : public fhandler_base
+{
+ protected:
+ char *filebuf;
+ size_t bufalloc, filesize;
+ __off64_t position;
+ int fileid; // unique within each class
+ public:
+
+ fhandler_virtual ();
+ virtual ~fhandler_virtual();
+
+ virtual int exists();
+ DIR *opendir (path_conv& pc);
+ __off64_t telldir (DIR *);
+ void seekdir (DIR *, __off64_t);
+ void rewinddir (DIR *);
+ int closedir (DIR *);
+ int write (const void *ptr, size_t len);
+ void __stdcall read (void *ptr, size_t& len) __attribute__ ((regparm (3)));
+ __off64_t lseek (__off64_t, int);
+ int dup (fhandler_base *child);
+ int open (path_conv *, int flags, mode_t mode = 0);
+ int close (void);
+ int __stdcall fstat (struct stat *buf, path_conv *pc) __attribute__ ((regparm (3)));
+ virtual bool fill_filebuf ();
+ void fixup_after_exec (HANDLE);
+};
+
+class fhandler_proc: public fhandler_virtual
+{
+ public:
+ fhandler_proc ();
+ int exists();
+ struct dirent *readdir (DIR *);
+ static DWORD get_proc_fhandler(const char *path);
+
+ int open (path_conv *real_path, int flags, mode_t mode = 0);
+ int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+ bool fill_filebuf ();
+};
+
+class fhandler_registry: public fhandler_proc
+{
+ private:
+ char *value_name;
+ public:
+ fhandler_registry ();
+ int exists();
+ struct dirent *readdir (DIR *);
+ __off64_t telldir (DIR *);
+ void seekdir (DIR *, __off64_t);
+ void rewinddir (DIR *);
+ int closedir (DIR *);
+
+ int open (path_conv *real_path, int flags, mode_t mode = 0);
+ int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+ bool fill_filebuf ();
+ int close (void);
+};
+
+class pinfo;
+class fhandler_process: public fhandler_proc
+{
+ pid_t pid;
+ public:
+ fhandler_process ();
+ int exists();
+ struct dirent *readdir (DIR *);
+ int open (path_conv *real_path, int flags, mode_t mode = 0);
+ int __stdcall fstat (struct __stat64 *buf, path_conv *) __attribute__ ((regparm (3)));
+ bool fill_filebuf ();
+};
+
+typedef union
+{
+ char __base[sizeof (fhandler_base)];
+ char __console[sizeof (fhandler_console)];
+ char __cygdrive[sizeof (fhandler_cygdrive)];
+ char __dev_clipboard[sizeof (fhandler_dev_clipboard)];
+ char __dev_dsp[sizeof (fhandler_dev_dsp)];
+ char __dev_floppy[sizeof (fhandler_dev_floppy)];
+ char __dev_mem[sizeof (fhandler_dev_mem)];
+ char __dev_null[sizeof (fhandler_dev_null)];
+ char __dev_random[sizeof (fhandler_dev_random)];
+ char __dev_raw[sizeof (fhandler_dev_raw)];
+ char __dev_tape[sizeof (fhandler_dev_tape)];
+ char __dev_zero[sizeof (fhandler_dev_zero)];
+ char __disk_file[sizeof (fhandler_disk_file)];
+ char __pipe[sizeof (fhandler_pipe)];
+ char __proc[sizeof (fhandler_proc)];
+ char __process[sizeof (fhandler_process)];
+ char __pty_master[sizeof (fhandler_pty_master)];
+ char __registry[sizeof (fhandler_registry)];
+ char __serial[sizeof (fhandler_serial)];
+ char __socket[sizeof (fhandler_socket)];
+ char __termios[sizeof (fhandler_termios)];
+ char __tty_common[sizeof (fhandler_tty_common)];
+ char __tty_master[sizeof (fhandler_tty_master)];
+ char __tty_slave[sizeof (fhandler_tty_slave)];
+ char __virtual[sizeof (fhandler_virtual)];
+ char __windows[sizeof (fhandler_windows)];
+} fhandler_union;
+
+struct select_record
+{
+ int fd;
+ HANDLE h;
+ fhandler_base *fh;
+ bool saw_error;
+ bool windows_handle;
+ bool read_ready, write_ready, except_ready;
+ bool read_selected, write_selected, except_selected;
+ bool except_on_write;
+ int (*startup) (select_record *me, class select_stuff *stuff);
+ int (*peek) (select_record *, bool);
+ int (*verify) (select_record *me, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds);
+ void (*cleanup) (select_record *me, class select_stuff *stuff);
+ struct select_record *next;
+
+ select_record (fhandler_base *in_fh = NULL) : fd (0), h (NULL),
+ fh (in_fh), saw_error (false), windows_handle (false),
+ read_ready (false), write_ready (false), except_ready (false),
+ read_selected (false), write_selected (false),
+ except_selected (false), except_on_write (false),
+ startup (NULL), peek (NULL), verify (NULL), cleanup (NULL),
+ next (NULL) {}
+};
+
+class select_stuff
+{
+ public:
+ ~select_stuff ();
+ bool always_ready, windows_used;
+ select_record start;
+ void *device_specific_pipe;
+ void *device_specific_socket;
+ void *device_specific_serial;
+
+ int test_and_set (int i, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds);
+ int poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds);
+ int wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds, DWORD ms);
+ void cleanup ();
+ select_stuff (): always_ready (0), windows_used (0), start (0),
+ device_specific_pipe (0),
+ device_specific_socket (0),
+ device_specific_serial (0) {}
+};
+
+int __stdcall set_console_state_for_spawn ();
+
+#endif /* _FHANDLER_H_ */
diff --git a/winsup/cygwin/fhandler_clipboard.cc b/winsup/cygwin/fhandler_clipboard.cc
new file mode 100644
index 00000000000..6f8559740be
--- /dev/null
+++ b/winsup/cygwin/fhandler_clipboard.cc
@@ -0,0 +1,277 @@
+/* fhandler_dev_clipboard: code to access /dev/clipboard
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc
+
+ Written by Charles Wilson (cwilson@ece.gatech.edu)
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <windows.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+
+/*
+ * Robert Collins:
+ * FIXME: should we use GetClipboardSequenceNumber to tell if the clipboard has
+ * changed? How does /dev/clipboard operate under (say) linux?
+ */
+
+static const NO_COPY char *CYGWIN_NATIVE = "CYGWIN_NATIVE_CLIPBOARD";
+/* this is MT safe because windows format id's are atomic */
+static UINT cygnativeformat;
+
+fhandler_dev_clipboard::fhandler_dev_clipboard ()
+ : fhandler_base (), pos (0), membuffer (NULL), msize (0),
+ eof (true)
+{
+ /* FIXME: check for errors and loop until we can open the clipboard */
+ OpenClipboard (NULL);
+ cygnativeformat = RegisterClipboardFormat (CYGWIN_NATIVE);
+ CloseClipboard ();
+}
+
+/*
+ * Special clipboard dup to duplicate input and output
+ * handles.
+ */
+
+int
+fhandler_dev_clipboard::dup (fhandler_base * child)
+{
+ fhandler_dev_clipboard *fhc = (fhandler_dev_clipboard *) child;
+
+ if (!fhc->open (NULL, get_flags (), 0))
+ system_printf ("error opening clipboard, %E");
+
+ fhc->membuffer = membuffer;
+ fhc->pos = pos;
+ fhc->msize = msize;
+
+ return 0;
+}
+
+int
+fhandler_dev_clipboard::open (path_conv *, int flags, mode_t)
+{
+ set_flags (flags | O_TEXT);
+ eof = false;
+ pos = 0;
+ if (membuffer)
+ free (membuffer);
+ membuffer = NULL;
+ if (!cygnativeformat)
+ cygnativeformat = RegisterClipboardFormat (CYGWIN_NATIVE);
+ set_nohandle (true);
+ set_open_status ();
+ return 1;
+}
+
+static int
+set_clipboard (const void *buf, size_t len)
+{
+ HGLOBAL hmem;
+ unsigned char *clipbuf;
+ /* Native CYGWIN format */
+ OpenClipboard (0);
+ hmem = GlobalAlloc (GMEM_MOVEABLE, len + sizeof (size_t));
+ if (!hmem)
+ {
+ system_printf ("Couldn't allocate global buffer for write");
+ return -1;
+ }
+ clipbuf = (unsigned char *) GlobalLock (hmem);
+ memcpy (clipbuf + sizeof (size_t), buf, len);
+ *(size_t *) (clipbuf) = len;
+ GlobalUnlock (hmem);
+ EmptyClipboard ();
+ if (!cygnativeformat)
+ cygnativeformat = RegisterClipboardFormat (CYGWIN_NATIVE);
+ if (!SetClipboardData (cygnativeformat, hmem))
+ {
+ system_printf
+ ("Couldn't write native format to the clipboard %04x %x",
+ cygnativeformat, hmem);
+/* FIXME: return an appriate error code &| set_errno(); */
+ return -1;
+ }
+ CloseClipboard ();
+ if (GlobalFree (hmem))
+ {
+ system_printf
+ ("Couldn't free global buffer after write to the clipboard");
+/* FIXME: return an appriate error code &| set_errno(); */
+ return 0;
+ }
+
+ /* CF_TEXT/CF_OEMTEXT for copying to wordpad and the like */
+
+ OpenClipboard (0);
+ hmem = GlobalAlloc (GMEM_MOVEABLE, len + 2);
+ if (!hmem)
+ {
+ system_printf ("Couldn't allocate global buffer for write");
+ return -1;
+ }
+ clipbuf = (unsigned char *) GlobalLock (hmem);
+ memcpy (clipbuf, buf, len);
+ *(clipbuf + len) = '\0';
+ *(clipbuf + len + 1) = '\0';
+ GlobalUnlock (hmem);
+ if (!SetClipboardData
+ ((current_codepage == ansi_cp ? CF_TEXT : CF_OEMTEXT), hmem))
+ {
+ system_printf ("Couldn't write to the clipboard");
+/* FIXME: return an appriate error code &| set_errno(); */
+ return -1;
+ }
+ CloseClipboard ();
+ if (GlobalFree (hmem))
+ {
+ system_printf
+ ("Couldn't free global buffer after write to the clipboard");
+/* FIXME: return an appriate error code &| set_errno(); */
+ }
+ return 0;
+}
+
+/* FIXME: arbitrary seeking is not handled */
+int
+fhandler_dev_clipboard::write (const void *buf, size_t len)
+{
+ if (!eof)
+ {
+ /* write to our membuffer */
+ size_t cursize = msize;
+ void *tempbuffer = realloc (membuffer, cursize + len);
+ if (!tempbuffer)
+ {
+ system_printf ("Couldn't realloc() clipboard buffer for write");
+ return -1;
+ }
+ membuffer = tempbuffer;
+ msize = cursize + len;
+ memcpy ((unsigned char *) membuffer + cursize, buf, len);
+
+ /* now pass to windows */
+ if (set_clipboard (membuffer, msize))
+ {
+ /* FIXME: membuffer is now out of sync with pos, but msize is used above */
+ set_errno (ENOSPC);
+ return -1;
+ }
+
+ pos = msize;
+
+ set_errno (0);
+ eof = false;
+ return len;
+ }
+ else
+ {
+ /* FIXME: return 0 bytes written, file not open */
+ return 0;
+ }
+}
+
+void __stdcall
+fhandler_dev_clipboard::read (void *ptr, size_t& len)
+{
+ HGLOBAL hglb;
+ size_t ret;
+ UINT formatlist[2];
+ UINT format;
+ if (eof)
+ len = 0;
+ else
+ {
+ formatlist[0] = cygnativeformat;
+ formatlist[1] = current_codepage == ansi_cp ? CF_TEXT : CF_OEMTEXT;
+ OpenClipboard (0);
+ if ((format = GetPriorityClipboardFormat (formatlist, 2)) <= 0)
+ {
+ CloseClipboard ();
+#if 0
+ system_printf ("a non-accepted format! %d", format);
+#endif
+ set_errno (0);
+ len = 0;
+ }
+ else
+ {
+ hglb = GetClipboardData (format);
+ if (format == cygnativeformat)
+ {
+ unsigned char *buf = (unsigned char *) GlobalLock (hglb);
+ size_t buflen = (*(size_t *) buf);
+ ret = ((len > (buflen - pos)) ? (buflen - pos) : len);
+ memcpy (ptr, buf + sizeof (size_t)+ pos , ret);
+ pos += ret;
+ if (pos + len - ret >= buflen)
+ eof = true;
+ GlobalUnlock (hglb);
+ }
+ else
+ {
+ LPSTR lpstr;
+ lpstr = (LPSTR) GlobalLock (hglb);
+
+ ret = ((len > (strlen (lpstr) - pos)) ? (strlen (lpstr) - pos)
+ : len);
+
+ memcpy (ptr, lpstr + pos, ret);
+ //ret = snprintf((char *) ptr, len, "%s", lpstr);//+pos);
+ pos += ret;
+ if (pos + len - ret >= strlen (lpstr))
+ eof = true;
+ GlobalUnlock (hglb);
+ }
+ CloseClipboard ();
+ set_errno (0);
+ len = ret;
+ }
+ }
+}
+
+__off64_t
+fhandler_dev_clipboard::lseek (__off64_t offset, int whence)
+{
+ /* On reads we check this at read time, not seek time.
+ * On writes we use this to decide how to write - empty and write, or open, copy, empty
+ * and write
+ */
+ pos = offset;
+ /* treat seek like rewind */
+ if (membuffer)
+ free (membuffer);
+ msize = 0;
+ return 0;
+}
+
+int
+fhandler_dev_clipboard::close (void)
+{
+ eof = true;
+ pos = 0;
+ if (membuffer)
+ free (membuffer);
+ msize = 0;
+ return 0;
+}
+
+void
+fhandler_dev_clipboard::dump ()
+{
+ paranoid_printf ("here, fhandler_dev_clipboard");
+}
diff --git a/winsup/cygwin/fhandler_console.cc b/winsup/cygwin/fhandler_console.cc
new file mode 100644
index 00000000000..5032e133eb5
--- /dev/null
+++ b/winsup/cygwin/fhandler_console.cc
@@ -0,0 +1,1754 @@
+/* fhandler_console.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <sys/termios.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <wincon.h>
+#include <winnls.h>
+#include <ctype.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "shared_info.h"
+#include "cygthread.h"
+
+#define CONVERT_LIMIT 4096
+
+static BOOL
+cp_convert (UINT destcp, char *dest, UINT srccp, const char *src, DWORD size)
+{
+ if (!size)
+ /* no action */;
+ else if (destcp == srccp)
+ {
+ if (dest != src)
+ memcpy (dest, src, size);
+ }
+ else
+ {
+ WCHAR wbuffer[CONVERT_LIMIT]; /* same size as the maximum input, s.b. */
+ if (!MultiByteToWideChar (srccp, 0, src, size, wbuffer, sizeof (wbuffer)))
+ return FALSE;
+ if (!WideCharToMultiByte (destcp, 0, wbuffer, size, dest, size,
+ NULL, NULL))
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* The results of GetConsoleCP() and GetConsoleOutputCP() cannot be
+ cached, because a program or the user can change these values at
+ any time. */
+inline BOOL
+con_to_str (char *d, const char *s, DWORD sz)
+{
+ return cp_convert (get_cp (), d, GetConsoleCP (), s, sz);
+}
+
+inline BOOL
+str_to_con (char *d, const char *s, DWORD sz)
+{
+ return cp_convert (GetConsoleOutputCP (), d, get_cp (), s, sz);
+}
+
+/*
+ * Scroll the screen context.
+ * x1, y1 - ul corner
+ * x2, y2 - dr corner
+ * xn, yn - new ul corner
+ * Negative values represents current screen dimensions
+ */
+
+#define srTop (dev_state->info.winTop + dev_state->scroll_region.Top)
+#define srBottom ((dev_state->scroll_region.Bottom < 0) ? dev_state->info.winBottom : dev_state->info.winTop + dev_state->scroll_region.Bottom)
+
+#define use_tty ISSTATE (myself, PID_USETTY)
+
+const char * get_nonascii_key (INPUT_RECORD&, char *);
+
+static console_state NO_COPY *shared_console_info;
+
+dev_console NO_COPY *fhandler_console::dev_state;
+
+/* Allocate and initialize the shared record for the current console.
+ Returns a pointer to shared_console_info. */
+tty_min *
+fhandler_console::get_tty_stuff (int flags = 0)
+{
+ if (dev_state)
+ return &shared_console_info->tty_min_state;
+
+ shared_console_info =
+ (console_state *) open_shared (NULL, 0, cygheap->console_h,
+ sizeof (*shared_console_info),
+ SH_SHARED_CONSOLE);
+ dev_state = &shared_console_info->dev_state;
+
+ ProtectHandleINH (cygheap->console_h);
+ if (!shared_console_info->tty_min_state.ntty)
+ {
+ shared_console_info->tty_min_state.setntty (TTY_CONSOLE);
+ shared_console_info->tty_min_state.setsid (myself->sid);
+ shared_console_info->tty_min_state.set_ctty (TTY_CONSOLE, flags);
+
+ dev_state->scroll_region.Bottom = -1;
+ dev_state->dwLastCursorPosition.X = -1;
+ dev_state->dwLastCursorPosition.Y = -1;
+ dev_state->default_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ dev_state->underline_color = FOREGROUND_GREEN | FOREGROUND_BLUE;
+ dev_state->dim_color = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
+ dev_state->meta_mask = LEFT_ALT_PRESSED;
+ /* Set the mask that determines if an input keystroke is modified by
+ META. We set this based on the keyboard layout language loaded
+ for the current thread. The left <ALT> key always generates
+ META, but the right <ALT> key only generates META if we are using
+ an English keyboard because many "international" keyboards
+ replace common shell symbols ('[', '{', etc.) with accented
+ language-specific characters (umlaut, accent grave, etc.). On
+ these keyboards right <ALT> (called AltGr) is used to produce the
+ shell symbols and should not be interpreted as META. */
+ if (PRIMARYLANGID (LOWORD (GetKeyboardLayout (0))) == LANG_ENGLISH)
+ dev_state->meta_mask |= RIGHT_ALT_PRESSED;
+ }
+
+ return &shared_console_info->tty_min_state;
+}
+
+void
+set_console_ctty ()
+{
+ (void) fhandler_console::get_tty_stuff ();
+}
+
+/* Return the tty structure associated with a given tty number. If the
+ tty number is < 0, just return a dummy record. */
+tty_min *
+tty_list::get_tty (int n)
+{
+ static tty_min nada;
+ if (n == TTY_CONSOLE)
+ return fhandler_console::get_tty_stuff ();
+ else if (n >= 0)
+ return &cygwin_shared->tty.ttys[n];
+ else
+ return &nada;
+}
+
+
+/* Determine if a console is associated with this process prior to a spawn.
+ If it is, then we'll return 1. If the console has been initialized, then
+ set it into a more friendly state for non-cygwin apps. */
+int __stdcall
+set_console_state_for_spawn ()
+{
+ HANDLE h = CreateFile ("CONIN$", GENERIC_READ, FILE_SHARE_WRITE,
+ &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL,
+ NULL);
+
+ if (h == INVALID_HANDLE_VALUE)
+ return 0;
+
+ if (shared_console_info != NULL)
+ {
+ /* ACK. Temporarily define for use in TTYSETF macro */
+# define tc &shared_console_info->tty_min_state
+ SetConsoleMode (h, ENABLE_LINE_INPUT | ENABLE_ECHO_INPUT | ENABLE_PROCESSED_INPUT);
+ TTYSETF (RSTCONS);
+# undef tc
+ }
+
+ CloseHandle (h);
+ return 1;
+}
+
+BOOL
+fhandler_console::set_raw_win32_keyboard_mode (BOOL new_mode)
+{
+ BOOL old_mode = dev_state->raw_win32_keyboard_mode;
+ dev_state->raw_win32_keyboard_mode = new_mode;
+ syscall_printf ("raw keyboard mode %sabled", dev_state->raw_win32_keyboard_mode ? "en" : "dis");
+ return old_mode;
+};
+
+void
+fhandler_console::set_cursor_maybe ()
+{
+ CONSOLE_SCREEN_BUFFER_INFO now;
+
+ if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
+ return;
+
+ if (dev_state->dwLastCursorPosition.X != now.dwCursorPosition.X ||
+ dev_state->dwLastCursorPosition.Y != now.dwCursorPosition.Y)
+ {
+ SetConsoleCursorPosition (get_output_handle (), now.dwCursorPosition);
+ dev_state->dwLastCursorPosition = now.dwCursorPosition;
+ }
+}
+
+void
+fhandler_console::send_winch_maybe ()
+{
+ SHORT y = dev_state->info.dwWinSize.Y;
+ SHORT x = dev_state->info.dwWinSize.X;
+ fillin_info ();
+
+ if (y != dev_state->info.dwWinSize.Y || x != dev_state->info.dwWinSize.X)
+ {
+ extern fhandler_tty_master *tty_master;
+ if (tty_master)
+ tty_master->set_winsize (true);
+ else
+ tc->kill_pgrp (SIGWINCH);
+ }
+}
+
+void __stdcall
+fhandler_console::read (void *pv, size_t& buflen)
+{
+ HANDLE h = get_io_handle ();
+
+#define buf ((char *) pv)
+
+ int ch;
+ set_input_state ();
+
+ int copied_chars = get_readahead_into_buffer (buf, buflen);
+
+ if (copied_chars)
+ {
+ buflen = copied_chars;
+ return;
+ }
+
+ HANDLE w4[2];
+ DWORD nwait;
+ char tmp[60];
+
+ w4[0] = h;
+ if (cygthread::is ())
+ nwait = 1;
+ else
+ {
+ w4[1] = signal_arrived;
+ nwait = 2;
+ }
+
+ termios ti = tc->ti;
+ for (;;)
+ {
+ int bgres;
+ if ((bgres = bg_check (SIGTTIN)) <= bg_eof)
+ {
+ buflen = bgres;
+ return;
+ }
+
+ set_cursor_maybe (); /* to make cursor appear on the screen immediately */
+ switch (WaitForMultipleObjects (nwait, w4, FALSE, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ break;
+ case WAIT_OBJECT_0 + 1:
+ goto sig_exit;
+ default:
+ goto err;
+ }
+
+ DWORD nread;
+ INPUT_RECORD input_rec;
+ const char *toadd = NULL;
+
+ if (!ReadConsoleInput (h, &input_rec, 1, &nread))
+ {
+ syscall_printf ("ReadConsoleInput failed, %E");
+ goto err; /* seems to be failure */
+ }
+
+ /* check the event that occurred */
+ switch (input_rec.EventType)
+ {
+ case KEY_EVENT:
+#define virtual_key_code (input_rec.Event.KeyEvent.wVirtualKeyCode)
+#define control_key_state (input_rec.Event.KeyEvent.dwControlKeyState)
+
+#ifdef DEBUGGING
+ /* allow manual switching to/from raw mode via ctrl-alt-scrolllock */
+ if (input_rec.Event.KeyEvent.bKeyDown &&
+ virtual_key_code == VK_SCROLL &&
+ control_key_state & (LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED) == LEFT_ALT_PRESSED | LEFT_CTRL_PRESSED
+ )
+ {
+ set_raw_win32_keyboard_mode (!dev_state->raw_win32_keyboard_mode);
+ continue;
+ }
+#endif
+
+ if (dev_state->raw_win32_keyboard_mode)
+ {
+ __small_sprintf (tmp, "\033{%u;%u;%u;%u;%u;%luK",
+ input_rec.Event.KeyEvent.bKeyDown,
+ input_rec.Event.KeyEvent.wRepeatCount,
+ input_rec.Event.KeyEvent.wVirtualKeyCode,
+ input_rec.Event.KeyEvent.wVirtualScanCode,
+ input_rec.Event.KeyEvent.uChar.UnicodeChar,
+ input_rec.Event.KeyEvent.dwControlKeyState);
+ toadd = tmp;
+ nread = strlen (toadd);
+ break;
+ }
+
+ if (!input_rec.Event.KeyEvent.bKeyDown)
+ continue;
+
+#define ich (input_rec.Event.KeyEvent.uChar.AsciiChar)
+#define wch (input_rec.Event.KeyEvent.uChar.UnicodeChar)
+#define ALT_PRESSED (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED)
+#define CTRL_PRESSED (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED)
+
+ if (wch == 0 ||
+ /* arrow/function keys */
+ (input_rec.Event.KeyEvent.dwControlKeyState & ENHANCED_KEY))
+ {
+ toadd = get_nonascii_key (input_rec, tmp);
+ if (!toadd)
+ continue;
+ nread = strlen (toadd);
+ }
+ else
+ {
+ tmp[1] = ich;
+ /* Need this check since US code page seems to have a bug when
+ converting a CTRL-U. */
+ if ((unsigned char) ich > 0x7f)
+ con_to_str (tmp + 1, tmp + 1, 1);
+ /* Determine if the keystroke is modified by META. The tricky
+ part is to distinguish whether the right Alt key should be
+ recognized as Alt, or as AltGr. */
+ bool meta;
+ if (wincap.altgr_is_ctrl_alt ())
+ /* WinNT: AltGr is reported as Ctrl+Alt, and Ctrl+Alt is
+ treated just like AltGr. However, if Ctrl+Alt+key generates
+ an ASCII control character, interpret is as META. */
+ meta = (control_key_state & ALT_PRESSED) != 0
+ && ((control_key_state & CTRL_PRESSED) == 0
+ || (ich >= 0 && ich <= 0x1f || ich == 0x7f));
+ else
+ /* Win9x: there's no way to distinguish Alt from AltGr, so rely
+ on dev_state->meta_mask heuristic (see fhandler_console constructor). */
+ meta = (control_key_state & dev_state->meta_mask) != 0;
+ if (!meta)
+ toadd = tmp + 1;
+ else
+ {
+ tmp[0] = '\033';
+ tmp[1] = cyg_tolower (tmp[1]);
+ toadd = tmp;
+ nread++;
+ }
+ }
+#undef ich
+#undef wch
+#undef ALT_PRESSED
+#undef CTRL_PRESSED
+ break;
+
+ case MOUSE_EVENT:
+ send_winch_maybe ();
+ if (dev_state->use_mouse)
+ {
+ MOUSE_EVENT_RECORD& mouse_event = input_rec.Event.MouseEvent;
+
+ /* Treat the double-click event like a regular button press */
+ if (mouse_event.dwEventFlags == DOUBLE_CLICK)
+ {
+ syscall_printf ("mouse: double-click -> click");
+ mouse_event.dwEventFlags = 0;
+ }
+
+ /* Did something other than a click occur? */
+ if (mouse_event.dwEventFlags)
+ continue;
+
+ /* If the mouse event occurred out of the area we can handle,
+ ignore it. */
+ int x = mouse_event.dwMousePosition.X;
+ int y = mouse_event.dwMousePosition.Y;
+ if ((x + ' ' + 1 > 0xFF) || (y + ' ' + 1 > 0xFF))
+ {
+ syscall_printf ("mouse: position out of range");
+ continue;
+ }
+
+ /* Ignore unimportant mouse buttons */
+ mouse_event.dwButtonState &= 0x7;
+
+ /* This code assumes Windows never reports multiple button
+ events at the same time. */
+ int b = 0;
+ char sz[32];
+ if (mouse_event.dwButtonState == dev_state->dwLastButtonState)
+ {
+ syscall_printf ("mouse: button state unchanged");
+ continue;
+ }
+ else if (mouse_event.dwButtonState < dev_state->dwLastButtonState)
+ {
+ b = 3;
+ strcpy (sz, "btn up");
+ }
+ else if ((mouse_event.dwButtonState & 1) != (dev_state->dwLastButtonState & 1))
+ {
+ b = 0;
+ strcpy (sz, "btn1 down");
+ }
+ else if ((mouse_event.dwButtonState & 2) != (dev_state->dwLastButtonState & 2))
+ {
+ b = 2;
+ strcpy (sz, "btn2 down");
+ }
+ else if ((mouse_event.dwButtonState & 4) != (dev_state->dwLastButtonState & 4))
+ {
+ b = 1;
+ strcpy (sz, "btn3 down");
+ }
+
+ /* Remember the current button state */
+ dev_state->dwLastButtonState = mouse_event.dwButtonState;
+
+ /* If a button was pressed, remember the modifiers */
+ if (b != 3)
+ {
+ dev_state->nModifiers = 0;
+ if (mouse_event.dwControlKeyState & SHIFT_PRESSED)
+ dev_state->nModifiers |= 0x4;
+ if (mouse_event.dwControlKeyState & (RIGHT_ALT_PRESSED|LEFT_ALT_PRESSED))
+ dev_state->nModifiers |= 0x8;
+ if (mouse_event.dwControlKeyState & (RIGHT_CTRL_PRESSED|LEFT_CTRL_PRESSED))
+ dev_state->nModifiers |= 0x10;
+ }
+
+ b |= dev_state->nModifiers;
+
+ /* We can now create the code. */
+ sprintf (tmp, "\033[M%c%c%c", b + ' ', x + ' ' + 1, y + ' ' + 1);
+ syscall_printf ("mouse: %s at (%d,%d)", sz, x, y);
+
+ toadd = tmp;
+ nread = 6;
+ }
+ break;
+
+ case FOCUS_EVENT:
+ case WINDOW_BUFFER_SIZE_EVENT:
+ send_winch_maybe ();
+ /* fall through */
+ default:
+ continue;
+ }
+
+ if (toadd)
+ {
+ line_edit_status res = line_edit (toadd, nread, ti);
+ if (res == line_edit_signalled)
+ goto sig_exit;
+ else if (res == line_edit_input_done)
+ break;
+ }
+#undef ich
+ }
+
+ while (buflen)
+ if ((ch = get_readahead ()) < 0)
+ break;
+ else
+ {
+ buf[copied_chars++] = (unsigned char)(ch & 0xff);
+ buflen--;
+ }
+#undef buf
+
+ buflen = copied_chars;
+ return;
+
+err:
+ __seterrno ();
+ (ssize_t) buflen = -1;
+ return;
+
+ sig_exit:
+ set_sig_errno (EINTR);
+ (ssize_t) buflen = -1;
+ return;
+}
+
+void
+fhandler_console::set_input_state ()
+{
+ if (TTYISSETF (RSTCONS))
+ input_tcsetattr (0, &tc->ti);
+}
+
+BOOL
+fhandler_console::fillin_info (void)
+{
+ BOOL ret;
+ CONSOLE_SCREEN_BUFFER_INFO linfo;
+
+ if ((ret = GetConsoleScreenBufferInfo (get_output_handle (), &linfo)))
+ {
+ dev_state->info.winTop = linfo.srWindow.Top;
+ dev_state->info.winBottom = linfo.srWindow.Bottom;
+ dev_state->info.dwWinSize.Y = 1 + linfo.srWindow.Bottom - linfo.srWindow.Top;
+ dev_state->info.dwWinSize.X = 1 + linfo.srWindow.Right - linfo.srWindow.Left;
+ dev_state->info.dwBufferSize = linfo.dwSize;
+ dev_state->info.dwCursorPosition = linfo.dwCursorPosition;
+ dev_state->info.wAttributes = linfo.wAttributes;
+ }
+ else
+ {
+ memset (&dev_state->info, 0, sizeof dev_state->info);
+ dev_state->info.dwWinSize.Y = 25;
+ dev_state->info.dwWinSize.X = 80;
+ dev_state->info.winBottom = 24;
+ }
+
+ return ret;
+}
+
+void
+fhandler_console::scroll_screen (int x1, int y1, int x2, int y2, int xn, int yn)
+{
+ SMALL_RECT sr1, sr2;
+ CHAR_INFO fill;
+ COORD dest;
+
+ (void) fillin_info ();
+ sr1.Left = x1 >= 0 ? x1 : dev_state->info.dwWinSize.X - 1;
+ if (y1 == 0)
+ sr1.Top = dev_state->info.winTop;
+ else
+ sr1.Top = y1 > 0 ? y1 : dev_state->info.winBottom;
+ sr1.Right = x2 >= 0 ? x2 : dev_state->info.dwWinSize.X - 1;
+ if (y2 == 0)
+ sr1.Bottom = dev_state->info.winTop;
+ else
+ sr1.Bottom = y2 > 0 ? y2 : dev_state->info.winBottom;
+ sr2.Top = srTop;
+ sr2.Left = 0;
+ sr2.Bottom = srBottom;
+ sr2.Right = dev_state->info.dwWinSize.X - 1;
+ if (sr1.Bottom > sr2.Bottom && sr1.Top <= sr2.Bottom)
+ sr1.Bottom = sr2.Bottom;
+ dest.X = xn >= 0 ? xn : dev_state->info.dwWinSize.X - 1;
+ if (yn == 0)
+ dest.Y = dev_state->info.winTop;
+ else
+ dest.Y = yn > 0 ? yn : dev_state->info.winBottom;
+ fill.Char.AsciiChar = ' ';
+ fill.Attributes = dev_state->current_win32_attr;
+ ScrollConsoleScreenBuffer (get_output_handle (), &sr1, &sr2, dest, &fill);
+
+ /* ScrollConsoleScreenBuffer on Windows 95 is buggy - when scroll distance
+ * is more than half of screen, filling doesn't work as expected */
+
+ if (sr1.Top != sr1.Bottom)
+ if (dest.Y <= sr1.Top) /* forward scroll */
+ clear_screen (0, 1 + dest.Y + sr1.Bottom - sr1.Top, sr2.Right, sr2.Bottom);
+ else /* reverse scroll */
+ clear_screen (0, sr1.Top, sr2.Right, dest.Y - 1);
+}
+
+int
+fhandler_console::open (path_conv *, int flags, mode_t)
+{
+ HANDLE h;
+
+ tcinit (get_tty_stuff (flags));
+
+ set_io_handle (NULL);
+ set_output_handle (NULL);
+
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+
+ /* Open the input handle as handle_ */
+ h = CreateFile ("CONIN$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
+ OPEN_EXISTING, 0, 0);
+
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return 0;
+ }
+ set_io_handle (h);
+ set_r_no_interrupt (1); // Handled explicitly in read code
+
+ h = CreateFile ("CONOUT$", GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE, &sec_none,
+ OPEN_EXISTING, 0, 0);
+
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ __seterrno ();
+ return 0;
+ }
+ set_output_handle (h);
+
+ if (fillin_info ())
+ dev_state->default_color = dev_state->info.wAttributes;
+
+ set_default_attr ();
+
+ DWORD cflags;
+ if (GetConsoleMode (get_io_handle (), &cflags))
+ {
+ cflags |= ENABLE_PROCESSED_INPUT;
+ SetConsoleMode (get_io_handle (), ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT | cflags);
+ }
+
+ TTYCLEARF (RSTCONS);
+ set_open_status ();
+ debug_printf ("opened conin$ %p, conout$ %p",
+ get_io_handle (), get_output_handle ());
+
+ return 1;
+}
+
+int
+fhandler_console::close (void)
+{
+ CloseHandle (get_io_handle ());
+ CloseHandle (get_output_handle ());
+ set_io_handle (NULL);
+ set_output_handle (NULL);
+ return 0;
+}
+
+/*
+ * Special console dup to duplicate input and output
+ * handles.
+ */
+
+int
+fhandler_console::dup (fhandler_base *child)
+{
+ fhandler_console *fhc = (fhandler_console *) child;
+
+ if (!fhc->open (NULL, get_flags () & ~O_NOCTTY, 0))
+ system_printf ("error opening console, %E");
+
+ return 0;
+}
+
+int
+fhandler_console::ioctl (unsigned int cmd, void *buf)
+{
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ int st;
+
+ st = fillin_info ();
+ if (st)
+ {
+ /* *not* the buffer size, the actual screen size... */
+ /* based on Left Top Right Bottom of srWindow */
+ ((struct winsize *) buf)->ws_row = dev_state->info.dwWinSize.Y;
+ ((struct winsize *) buf)->ws_col = dev_state->info.dwWinSize.X;
+ syscall_printf ("WINSZ: (row=%d,col=%d)",
+ ((struct winsize *) buf)->ws_row,
+ ((struct winsize *) buf)->ws_col);
+ return 0;
+ }
+ else
+ {
+ syscall_printf ("WINSZ failed");
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+ case TIOCSWINSZ:
+ (void) bg_check (SIGTTOU);
+ return 0;
+ }
+
+ return fhandler_base::ioctl (cmd, buf);
+}
+
+int
+fhandler_console::tcflush (int queue)
+{
+ int res = 0;
+ if (queue == TCIFLUSH
+ || queue == TCIOFLUSH)
+ {
+ if (!FlushConsoleInputBuffer (get_io_handle ()))
+ {
+ __seterrno ();
+ res = -1;
+ }
+ }
+ return res;
+}
+
+int
+fhandler_console::output_tcsetattr (int, struct termios const *t)
+{
+ /* All the output bits we can ignore */
+
+ DWORD flags = ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
+
+ int res = SetConsoleMode (get_output_handle (), flags) ? 0 : -1;
+ syscall_printf ("%d = tcsetattr (,%x) (ENABLE FLAGS %x) (lflag %x oflag %x)",
+ res, t, flags, t->c_lflag, t->c_oflag);
+ return res;
+}
+
+int
+fhandler_console::input_tcsetattr (int, struct termios const *t)
+{
+ /* Ignore the optional_actions stuff, since all output is emitted
+ instantly */
+
+ DWORD oflags;
+
+ if (!GetConsoleMode (get_io_handle (), &oflags))
+ oflags = 0;
+ DWORD flags = 0;
+
+#if 0
+ /* Enable/disable LF -> CRLF conversions */
+ set_r_binary ((t->c_iflag & INLCR) ? 0 : 1);
+#endif
+
+ /* There's some disparity between what we need and what's
+ available. We've got ECHO and ICANON, they've
+ got ENABLE_ECHO_INPUT and ENABLE_LINE_INPUT. */
+
+ tc->ti = *t;
+
+ if (t->c_lflag & ECHO)
+ {
+ flags |= ENABLE_ECHO_INPUT;
+ }
+ if (t->c_lflag & ICANON)
+ {
+ flags |= ENABLE_LINE_INPUT;
+ }
+
+ if (flags & ENABLE_ECHO_INPUT
+ && !(flags & ENABLE_LINE_INPUT))
+ {
+ /* This is illegal, so turn off the echo here, and fake it
+ when we read the characters */
+
+ flags &= ~ENABLE_ECHO_INPUT;
+ }
+
+ if (t->c_lflag & ISIG)
+ {
+ flags |= ENABLE_PROCESSED_INPUT;
+ }
+
+ if (use_tty)
+ {
+ flags = 0; // ENABLE_PROCESSED_OUTPUT | ENABLE_WRAP_AT_EOL_OUTPUT;
+ tc->ti.c_iflag = 0;
+ tc->ti.c_lflag = 0;
+ }
+
+ flags |= ENABLE_WINDOW_INPUT | ENABLE_MOUSE_INPUT;
+
+ int res;
+ if (flags == oflags)
+ res = 0;
+ else
+ {
+ res = SetConsoleMode (get_io_handle (), flags) ? 0 : -1;
+ if (res < 0)
+ __seterrno ();
+ syscall_printf ("%d = tcsetattr (,%x) enable flags %p, c_lflag %p iflag %p",
+ res, t, flags, t->c_lflag, t->c_iflag);
+ }
+
+ TTYCLEARF (RSTCONS);
+ return res;
+}
+
+int
+fhandler_console::tcsetattr (int a, struct termios const *t)
+{
+ int res = output_tcsetattr (a, t);
+ if (res != 0)
+ return res;
+ return input_tcsetattr (a, t);
+}
+
+int
+fhandler_console::tcgetattr (struct termios *t)
+{
+ int res;
+ *t = tc->ti;
+
+ t->c_cflag |= CS8;
+
+ DWORD flags;
+
+ if (!GetConsoleMode (get_io_handle (), &flags))
+ {
+ __seterrno ();
+ res = -1;
+ }
+ else
+ {
+ if (flags & ENABLE_ECHO_INPUT)
+ t->c_lflag |= ECHO;
+
+ if (flags & ENABLE_LINE_INPUT)
+ t->c_lflag |= ICANON;
+
+ if (flags & ENABLE_PROCESSED_INPUT)
+ t->c_lflag |= ISIG;
+
+ /* What about ENABLE_WINDOW_INPUT
+ and ENABLE_MOUSE_INPUT ? */
+
+ /* All the output bits we can ignore */
+ res = 0;
+ }
+ syscall_printf ("%d = tcgetattr (%p) enable flags %p, t->lflag %p, t->iflag %p",
+ res, t, flags, t->c_lflag, t->c_iflag);
+ return res;
+}
+
+fhandler_console::fhandler_console () :
+ fhandler_termios ()
+{
+}
+
+#define FOREGROUND_ATTR_MASK (FOREGROUND_RED | FOREGROUND_GREEN | \
+ FOREGROUND_BLUE | FOREGROUND_INTENSITY)
+#define BACKGROUND_ATTR_MASK (BACKGROUND_RED | BACKGROUND_GREEN | \
+ BACKGROUND_BLUE | BACKGROUND_INTENSITY)
+void
+fhandler_console::set_default_attr ()
+{
+ dev_state->blink = dev_state->underline = dev_state->reverse = FALSE;
+ dev_state->intensity = INTENSITY_NORMAL;
+ dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK;
+ dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK;
+ dev_state->current_win32_attr = get_win32_attr ();
+ SetConsoleTextAttribute (get_output_handle (), dev_state->current_win32_attr);
+}
+
+WORD
+fhandler_console::get_win32_attr ()
+{
+ WORD win_fg = dev_state->fg;
+ WORD win_bg = dev_state->bg;
+ if (dev_state->reverse)
+ {
+ WORD save_fg = win_fg;
+ win_fg = (win_bg & BACKGROUND_RED ? FOREGROUND_RED : 0) |
+ (win_bg & BACKGROUND_GREEN ? FOREGROUND_GREEN : 0) |
+ (win_bg & BACKGROUND_BLUE ? FOREGROUND_BLUE : 0) |
+ (win_fg & FOREGROUND_INTENSITY);
+ win_bg = (save_fg & FOREGROUND_RED ? BACKGROUND_RED : 0) |
+ (save_fg & FOREGROUND_GREEN ? BACKGROUND_GREEN : 0) |
+ (save_fg & FOREGROUND_BLUE ? BACKGROUND_BLUE : 0) |
+ (win_bg & BACKGROUND_INTENSITY);
+ }
+ if (dev_state->underline)
+ win_fg = dev_state->underline_color;
+ /* emulate blink with bright background */
+ if (dev_state->blink)
+ win_bg |= BACKGROUND_INTENSITY;
+ if (dev_state->intensity == INTENSITY_INVISIBLE)
+ win_fg = win_bg;
+ else if (dev_state->intensity == INTENSITY_BOLD)
+ win_fg |= FOREGROUND_INTENSITY;
+ return (win_fg | win_bg);
+}
+
+/*
+ * Clear the screen context from x1/y1 to x2/y2 cell.
+ * Negative values represents current screen dimensions
+ */
+void
+fhandler_console::clear_screen (int x1, int y1, int x2, int y2)
+{
+ COORD tlc;
+ DWORD done;
+ int num;
+
+ (void)fillin_info ();
+
+ if (x1 < 0)
+ x1 = dev_state->info.dwWinSize.X - 1;
+ if (y1 < 0)
+ y1 = dev_state->info.winBottom;
+ if (x2 < 0)
+ x2 = dev_state->info.dwWinSize.X - 1;
+ if (y2 < 0)
+ y2 = dev_state->info.winBottom;
+
+ num = abs (y1 - y2) * dev_state->info.dwBufferSize.X + abs (x1 - x2) + 1;
+
+ if ((y2 * dev_state->info.dwBufferSize.X + x2) > (y1 * dev_state->info.dwBufferSize.X + x1))
+ {
+ tlc.X = x1;
+ tlc.Y = y1;
+ }
+ else
+ {
+ tlc.X = x2;
+ tlc.Y = y2;
+ }
+ FillConsoleOutputCharacterA (get_output_handle (), ' ',
+ num,
+ tlc,
+ &done);
+ FillConsoleOutputAttribute (get_output_handle (),
+ dev_state->current_win32_attr,
+ num,
+ tlc,
+ &done);
+}
+
+void
+fhandler_console::cursor_set (BOOL rel_to_top, int x, int y)
+{
+ COORD pos;
+
+ (void) fillin_info ();
+ if (y > dev_state->info.winBottom)
+ y = dev_state->info.winBottom;
+ else if (y < 0)
+ y = 0;
+ else if (rel_to_top)
+ y += dev_state->info.winTop;
+
+ if (x > dev_state->info.dwWinSize.X)
+ x = dev_state->info.dwWinSize.X - 1;
+ else if (x < 0)
+ x = 0;
+
+ pos.X = x;
+ pos.Y = y;
+ SetConsoleCursorPosition (get_output_handle (), pos);
+}
+
+void
+fhandler_console::cursor_rel (int x, int y)
+{
+ fillin_info ();
+ x += dev_state->info.dwCursorPosition.X;
+ y += dev_state->info.dwCursorPosition.Y;
+ cursor_set (FALSE, x, y);
+}
+
+void
+fhandler_console::cursor_get (int *x, int *y)
+{
+ fillin_info ();
+ *y = dev_state->info.dwCursorPosition.Y;
+ *x = dev_state->info.dwCursorPosition.X;
+}
+
+#define BAK 1
+#define ESC 2
+#define NOR 0
+#define IGN 4
+#if 0
+#define ERR 5
+#else
+#define ERR NOR
+#endif
+#define DWN 6
+#define BEL 7
+#define TAB 8 /* We should't let the console deal with these */
+#define CR 13
+#define LF 10
+
+static const char base_chars[256] =
+{
+/*00 01 02 03 04 05 06 07 */ IGN, ERR, ERR, NOR, NOR, NOR, NOR, BEL,
+/*08 09 0A 0B 0C 0D 0E 0F */ BAK, TAB, DWN, ERR, ERR, CR, ERR, IGN,
+/*10 11 12 13 14 15 16 17 */ NOR, NOR, ERR, ERR, ERR, ERR, ERR, ERR,
+/*18 19 1A 1B 1C 1D 1E 1F */ NOR, NOR, ERR, ESC, ERR, ERR, ERR, ERR,
+/* ! " # $ % & ' */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*() * + , - . / */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*0 1 2 3 4 5 6 7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*8 9 : ; < = > ? */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*@ A B C D E F G */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*H I J K L M N O */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*P Q R S T U V W */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*X Y Z [ \ ] ^ _ */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*` a b c d e f g */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*h i j k l m n o */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*p q r s t u v w */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*x y z { | } ~ 7F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*80 81 82 83 84 85 86 87 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*88 89 8A 8B 8C 8D 8E 8F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*90 91 92 93 94 95 96 97 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*98 99 9A 9B 9C 9D 9E 9F */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*A0 A1 A2 A3 A4 A5 A6 A7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*A8 A9 AA AB AC AD AE AF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*B0 B1 B2 B3 B4 B5 B6 B7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*B8 B9 BA BB BC BD BE BF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*C0 C1 C2 C3 C4 C5 C6 C7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*C8 C9 CA CB CC CD CE CF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*D0 D1 D2 D3 D4 D5 D6 D7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*D8 D9 DA DB DC DD DE DF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*E0 E1 E2 E3 E4 E5 E6 E7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*E8 E9 EA EB EC ED EE EF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*F0 F1 F2 F3 F4 F5 F6 F7 */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR,
+/*F8 F9 FA FB FC FD FE FF */ NOR, NOR, NOR, NOR, NOR, NOR, NOR, NOR };
+
+void
+fhandler_console::char_command (char c)
+{
+ int x, y;
+ char buf[40];
+
+ switch (c)
+ {
+ case 'm': /* Set Graphics Rendition */
+ int i;
+
+ for (i = 0; i <= dev_state->nargs_; i++)
+ switch (dev_state->args_[i])
+ {
+ case 0: /* normal color */
+ set_default_attr ();
+ break;
+ case 1: /* bold */
+ dev_state->intensity = INTENSITY_BOLD;
+ break;
+ case 4:
+ dev_state->underline = 1;
+ break;
+ case 5: /* blink mode */
+ dev_state->blink = TRUE;
+ break;
+ case 7: /* reverse */
+ dev_state->reverse = TRUE;
+ break;
+ case 8: /* invisible */
+ dev_state->intensity = INTENSITY_INVISIBLE;
+ break;
+ case 9: /* dim */
+ dev_state->intensity = INTENSITY_DIM;
+ break;
+ case 24:
+ dev_state->underline = FALSE;
+ break;
+ case 27:
+ dev_state->reverse = FALSE;
+ break;
+ case 30: /* BLACK foreground */
+ dev_state->fg = 0;
+ break;
+ case 31: /* RED foreground */
+ dev_state->fg = FOREGROUND_RED;
+ break;
+ case 32: /* GREEN foreground */
+ dev_state->fg = FOREGROUND_GREEN;
+ break;
+ case 33: /* YELLOW foreground */
+ dev_state->fg = FOREGROUND_RED | FOREGROUND_GREEN;
+ break;
+ case 34: /* BLUE foreground */
+ dev_state->fg = FOREGROUND_BLUE;
+ break;
+ case 35: /* MAGENTA foreground */
+ dev_state->fg = FOREGROUND_RED | FOREGROUND_BLUE;
+ break;
+ case 36: /* CYAN foreground */
+ dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN;
+ break;
+ case 37: /* WHITE foreg */
+ dev_state->fg = FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED;
+ break;
+ case 39:
+ dev_state->fg = dev_state->default_color & FOREGROUND_ATTR_MASK;
+ break;
+ case 40: /* BLACK background */
+ dev_state->bg = 0;
+ break;
+ case 41: /* RED background */
+ dev_state->bg = BACKGROUND_RED;
+ break;
+ case 42: /* GREEN background */
+ dev_state->bg = BACKGROUND_GREEN;
+ break;
+ case 43: /* YELLOW background */
+ dev_state->bg = BACKGROUND_RED | BACKGROUND_GREEN;
+ break;
+ case 44: /* BLUE background */
+ dev_state->bg = BACKGROUND_BLUE;
+ break;
+ case 45: /* MAGENTA background */
+ dev_state->bg = BACKGROUND_RED | BACKGROUND_BLUE;
+ break;
+ case 46: /* CYAN background */
+ dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN;
+ break;
+ case 47: /* WHITE background */
+ dev_state->bg = BACKGROUND_BLUE | BACKGROUND_GREEN | BACKGROUND_RED;
+ break;
+ case 49:
+ dev_state->bg = dev_state->default_color & BACKGROUND_ATTR_MASK;
+ break;
+ }
+ dev_state->current_win32_attr = get_win32_attr ();
+ SetConsoleTextAttribute (get_output_handle (), dev_state->current_win32_attr);
+ break;
+ case 'h':
+ case 'l':
+ if (!dev_state->saw_question_mark)
+ {
+ switch (dev_state->args_[0])
+ {
+ case 4: /* Insert mode */
+ dev_state->insert_mode = (c == 'h') ? TRUE : FALSE;
+ syscall_printf ("insert mode %sabled", dev_state->insert_mode ? "en" : "dis");
+ break;
+ }
+ break;
+ }
+ switch (dev_state->args_[0])
+ {
+ case 47: /* Save/Restore screen */
+ if (c == 'h') /* save */
+ {
+ CONSOLE_SCREEN_BUFFER_INFO now;
+ COORD cob = { 0, 0 };
+
+ if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
+ break;
+
+ dev_state->savebufsiz.X = now.srWindow.Right - now.srWindow.Left + 1;
+ dev_state->savebufsiz.Y = now.srWindow.Bottom - now.srWindow.Top + 1;
+
+ if (dev_state->savebuf)
+ cfree (dev_state->savebuf);
+ dev_state->savebuf = (PCHAR_INFO) cmalloc (HEAP_1_BUF, sizeof (CHAR_INFO) *
+ dev_state->savebufsiz.X * dev_state->savebufsiz.Y);
+
+ ReadConsoleOutputA (get_output_handle (), dev_state->savebuf,
+ dev_state->savebufsiz, cob, &now.srWindow);
+ }
+ else /* restore */
+ {
+ CONSOLE_SCREEN_BUFFER_INFO now;
+ COORD cob = { 0, 0 };
+
+ if (!GetConsoleScreenBufferInfo (get_output_handle (), &now))
+ break;
+
+ if (!dev_state->savebuf)
+ break;
+
+ WriteConsoleOutputA (get_output_handle (), dev_state->savebuf,
+ dev_state->savebufsiz, cob, &now.srWindow);
+
+ cfree (dev_state->savebuf);
+ dev_state->savebuf = NULL;
+ dev_state->savebufsiz.X = dev_state->savebufsiz.Y = 0;
+ }
+ break;
+
+ case 1000: /* Mouse support */
+ dev_state->use_mouse = (c == 'h') ? TRUE : FALSE;
+ syscall_printf ("mouse support %sabled", dev_state->use_mouse ? "en" : "dis");
+ break;
+
+ case 2000: /* Raw keyboard mode */
+ set_raw_win32_keyboard_mode ((c == 'h') ? TRUE : FALSE);
+ break;
+
+ default: /* Ignore */
+ syscall_printf ("unknown h/l command: %d", dev_state->args_[0]);
+ break;
+ }
+ break;
+ case 'J':
+ switch (dev_state->args_[0])
+ {
+ case 0: /* Clear to end of screen */
+ cursor_get (&x, &y);
+ clear_screen (x, y, -1, -1);
+ break;
+ case 1: /* Clear from beginning of screen to cursor */
+ cursor_get (&x, &y);
+ clear_screen (0, 0, x, y);
+ break;
+ case 2: /* Clear screen */
+ clear_screen (0, 0, -1, -1);
+ cursor_set (TRUE, 0,0);
+ break;
+ default:
+ goto bad_escape;
+ }
+ break;
+
+ case 'A':
+ cursor_rel (0, -(dev_state->args_[0] ? dev_state->args_[0] : 1));
+ break;
+ case 'B':
+ cursor_rel (0, dev_state->args_[0] ? dev_state->args_[0] : 1);
+ break;
+ case 'C':
+ cursor_rel (dev_state->args_[0] ? dev_state->args_[0] : 1, 0);
+ break;
+ case 'D':
+ cursor_rel (-(dev_state->args_[0] ? dev_state->args_[0] : 1),0);
+ break;
+ case 'K':
+ switch (dev_state->args_[0])
+ {
+ case 0: /* Clear to end of line */
+ cursor_get (&x, &y);
+ clear_screen (x, y, -1, y);
+ break;
+ case 2: /* Clear line */
+ cursor_get (&x, &y);
+ clear_screen (0, y, -1, y);
+ break;
+ case 1: /* Clear from bol to cursor */
+ cursor_get (&x, &y);
+ clear_screen (0, y, x, y);
+ break;
+ default:
+ goto bad_escape;
+ }
+ break;
+ case 'H':
+ case 'f':
+ cursor_set (TRUE, (dev_state->args_[1] ? dev_state->args_[1] : 1) - 1,
+ (dev_state->args_[0] ? dev_state->args_[0] : 1) - 1);
+ break;
+ case 'G': /* hpa - position cursor at column n - 1 */
+ cursor_get (&x, &y);
+ cursor_set (FALSE, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0), y);
+ break;
+ case 'd': /* vpa - position cursor at line n */
+ cursor_get (&x, &y);
+ cursor_set (TRUE, x, (dev_state->args_[0] ? dev_state->args_[0] - 1 : 0));
+ break;
+ case 's': /* Save cursor position */
+ cursor_get (&dev_state->savex, &dev_state->savey);
+ dev_state->savey -= dev_state->info.winTop;
+ break;
+ case 'u': /* Restore cursor position */
+ cursor_set (TRUE, dev_state->savex, dev_state->savey);
+ break;
+ case 'I': /* TAB */
+ cursor_get (&x, &y);
+ cursor_set (FALSE, 8 * (x / 8 + 1), y);
+ break;
+ case 'L': /* AL - insert blank lines */
+ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
+ cursor_get (&x, &y);
+ scroll_screen (0, y, -1, -1, 0, y + dev_state->args_[0]);
+ break;
+ case 'M': /* DL - delete lines */
+ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
+ cursor_get (&x, &y);
+ scroll_screen (0, y + dev_state->args_[0], -1, -1, 0, y);
+ break;
+ case '@': /* IC - insert chars */
+ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
+ cursor_get (&x, &y);
+ scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
+ break;
+ case 'P': /* DC - delete chars */
+ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
+ cursor_get (&x, &y);
+ scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
+ break;
+ case 'S': /* SF - Scroll forward */
+ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
+ scroll_screen (0, dev_state->args_[0], -1, -1, 0, 0);
+ break;
+ case 'T': /* SR - Scroll down */
+ fillin_info ();
+ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
+ scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + dev_state->args_[0]);
+ break;
+ case 'X': /* ec - erase chars */
+ dev_state->args_[0] = dev_state->args_[0] ? dev_state->args_[0] : 1;
+ cursor_get (&x, &y);
+ scroll_screen (x + dev_state->args_[0], y, -1, y, x, y);
+ scroll_screen (x, y, -1, y, x + dev_state->args_[0], y);
+ break;
+ case 'Z': /* Back tab */
+ cursor_get (&x, &y);
+ cursor_set (FALSE, ((8 * (x / 8 + 1)) - 8), y);
+ break;
+ case 'b': /* Repeat char #1 #2 times */
+ if (dev_state->insert_mode)
+ {
+ cursor_get (&x, &y);
+ scroll_screen (x, y, -1, y, x + dev_state->args_[1], y);
+ }
+ while (dev_state->args_[1]--)
+ WriteFile (get_output_handle (), &dev_state->args_[0], 1, (DWORD *) &x, 0);
+ break;
+ case 'c': /* u9 - Terminal enquire string */
+ strcpy (buf, "\033[?6c");
+ puts_readahead (buf);
+ break;
+ case 'n':
+ switch (dev_state->args_[0])
+ {
+ case 6: /* u7 - Cursor position request */
+ cursor_get (&x, &y);
+ y -= dev_state->info.winTop;
+ /* x -= dev_state->info.winLeft; // not available yet */
+ __small_sprintf (buf, "\033[%d;%dR", y + 1, x + 1);
+ puts_readahead (buf);
+ break;
+ default:
+ goto bad_escape;
+ }
+ break;
+ case 'r': /* Set Scroll region */
+ dev_state->scroll_region.Top = dev_state->args_[0] ? dev_state->args_[0] - 1 : 0;
+ dev_state->scroll_region.Bottom = dev_state->args_[1] ? dev_state->args_[1] - 1 : -1;
+ cursor_set (TRUE, 0, 0);
+ break;
+ case 'g': /* TAB set/clear */
+ break;
+ default:
+bad_escape:
+ break;
+ }
+}
+
+const unsigned char *
+fhandler_console::write_normal (const unsigned char *src,
+ const unsigned char *end)
+{
+ /* Scan forward to see what a char which needs special treatment */
+ DWORD done;
+ const unsigned char *found = src;
+
+ while (found < end)
+ {
+ if (base_chars[*found] != NOR)
+ break;
+ found++;
+ }
+
+ /* Print all the base ones out */
+ if (found != src)
+ {
+ DWORD len = found - src;
+ do
+ {
+ DWORD buf_len;
+ char buf[CONVERT_LIMIT];
+ done = buf_len = min (sizeof (buf), len);
+ if (!str_to_con (buf, (const char *) src, buf_len))
+ {
+ debug_printf ("conversion error, handle %p",
+ get_output_handle ());
+ __seterrno ();
+ return 0;
+ }
+
+ if (dev_state->insert_mode)
+ {
+ int x, y;
+ cursor_get (&x, &y);
+ scroll_screen (x, y, -1, y, x + buf_len, y);
+ }
+
+ if (!WriteFile (get_output_handle (), buf, buf_len, &done, 0))
+ {
+ debug_printf ("write failed, handle %p", get_output_handle ());
+ __seterrno ();
+ return 0;
+ }
+ len -= done;
+ src += done;
+ }
+ while (len > 0);
+ }
+
+ if (src < end)
+ {
+ int x, y;
+ switch (base_chars[*src])
+ {
+ case BEL:
+ Beep (412, 100);
+ break;
+ case ESC:
+ dev_state->state_ = gotesc;
+ break;
+ case DWN:
+ cursor_get (&x, &y);
+ if (y >= srBottom)
+ {
+ if (y >= dev_state->info.winBottom && !dev_state->scroll_region.Top)
+ WriteFile (get_output_handle (), "\n", 1, &done, 0);
+ else
+ {
+ scroll_screen (0, srTop + 1, -1, srBottom, 0, srTop);
+ y--;
+ }
+ }
+ cursor_set (FALSE, ((tc->ti.c_oflag & ONLCR) ? 0 : x), y + 1);
+ break;
+ case BAK:
+ cursor_rel (-1, 0);
+ break;
+ case IGN:
+ cursor_rel (1, 0);
+ break;
+ case CR:
+ cursor_get (&x, &y);
+ cursor_set (FALSE, 0, y);
+ break;
+ case ERR:
+ WriteFile (get_output_handle (), src, 1, &done, 0);
+ break;
+ case TAB:
+ cursor_get (&x, &y);
+ cursor_set (FALSE, 8 * (x / 8 + 1), y);
+ break;
+ }
+ src ++;
+ }
+ return src;
+}
+
+int
+fhandler_console::write (const void *vsrc, size_t len)
+{
+ /* Run and check for ansi sequences */
+ unsigned const char *src = (unsigned char *) vsrc;
+ unsigned const char *end = src + len;
+
+ debug_printf ("%x, %d", vsrc, len);
+
+ while (src < end)
+ {
+ debug_printf ("at %d(%c) state is %d", *src, isprint (*src) ? *src : ' ',
+ dev_state->state_);
+ switch (dev_state->state_)
+ {
+ case normal:
+ src = write_normal (src, end);
+ if (!src) /* write_normal failed */
+ return -1;
+ break;
+ case gotesc:
+ if (*src == '[')
+ {
+ dev_state->state_ = gotsquare;
+ dev_state->saw_question_mark = FALSE;
+ for (dev_state->nargs_ = 0; dev_state->nargs_ < MAXARGS; dev_state->nargs_++)
+ dev_state->args_[dev_state->nargs_] = 0;
+ dev_state->nargs_ = 0;
+ }
+ else if (*src == ']')
+ {
+ dev_state->rarg = 0;
+ dev_state->my_title_buf[0] = '\0';
+ dev_state->state_ = gotrsquare;
+ }
+ else if (*src == 'M') /* Reverse Index */
+ {
+ fillin_info ();
+ scroll_screen (0, 0, -1, -1, 0, dev_state->info.winTop + 1);
+ dev_state->state_ = normal;
+ }
+ else if (*src == 'c') /* Reset Linux terminal */
+ {
+ set_default_attr ();
+ clear_screen (0, 0, -1, -1);
+ cursor_set (TRUE, 0, 0);
+ dev_state->state_ = normal;
+ }
+ else if (*src == '8') /* Restore cursor position */
+ {
+ cursor_set (TRUE, dev_state->savex, dev_state->savey);
+ dev_state->state_ = normal;
+ }
+ else if (*src == '7') /* Save cursor position */
+ {
+ cursor_get (&dev_state->savex, &dev_state->savey);
+ dev_state->savey -= dev_state->info.winTop;
+ dev_state->state_ = normal;
+ }
+ else if (*src == 'R')
+ dev_state->state_ = normal;
+ else
+ {
+ dev_state->state_ = normal;
+ }
+ src++;
+ break;
+ case gotarg1:
+ if (isdigit (*src))
+ {
+ dev_state->args_[dev_state->nargs_] = dev_state->args_[dev_state->nargs_] * 10 + *src - '0';
+ src++;
+ }
+ else if (*src == ';')
+ {
+ src++;
+ dev_state->nargs_++;
+ if (dev_state->nargs_ >= MAXARGS)
+ dev_state->nargs_--;
+ }
+ else
+ {
+ dev_state->state_ = gotcommand;
+ }
+ break;
+ case gotcommand:
+ char_command (*src++);
+ dev_state->state_ = normal;
+ break;
+ case gotrsquare:
+ if (isdigit (*src))
+ dev_state->rarg = dev_state->rarg * 10 + (*src - '0');
+ else if (*src == ';' && (dev_state->rarg == 2 || dev_state->rarg == 0))
+ dev_state->state_ = gettitle;
+ else
+ dev_state->state_ = eattitle;
+ src++;
+ break;
+ case eattitle:
+ case gettitle:
+ {
+ int n = strlen (dev_state->my_title_buf);
+ if (*src < ' ')
+ {
+ if (*src == '\007' && dev_state->state_ == gettitle)
+ {
+ if (old_title)
+ strcpy (old_title, dev_state->my_title_buf);
+ set_console_title (dev_state->my_title_buf);
+ }
+ dev_state->state_ = normal;
+ }
+ else if (n < TITLESIZE)
+ {
+ dev_state->my_title_buf[n++] = *src;
+ dev_state->my_title_buf[n] = '\0';
+ }
+ src++;
+ break;
+ }
+ case gotsquare:
+ if (*src == ';')
+ {
+ dev_state->state_ = gotarg1;
+ dev_state->nargs_++;
+ src++;
+ }
+ else if (isalpha (*src))
+ dev_state->state_ = gotcommand;
+ else if (*src != '@' && !isalpha (*src) && !isdigit (*src))
+ {
+ if (*src == '?')
+ dev_state->saw_question_mark = TRUE;
+ /* ignore any extra chars between [ and first arg or command */
+ src++;
+ }
+ else
+ dev_state->state_ = gotarg1;
+ break;
+ }
+ }
+
+ syscall_printf ("%d = write_console (,..%d)", len, len);
+
+ return len;
+}
+
+static struct {
+ int vk;
+ const char *val[4];
+} keytable[] NO_COPY = {
+ /* NORMAL */ /* SHIFT */ /* CTRL */ /* ALT */
+ {VK_LEFT, {"\033[D", "\033[D", "\033[D", "\033\033[D"}},
+ {VK_RIGHT, {"\033[C", "\033[C", "\033[C", "\033\033[C"}},
+ {VK_UP, {"\033[A", "\033[A", "\033[A", "\033\033[A"}},
+ {VK_DOWN, {"\033[B", "\033[B", "\033[B", "\033\033[B"}},
+ {VK_PRIOR, {"\033[5~", "\033[5~", "\033[5~", "\033\033[5~"}},
+ {VK_NEXT, {"\033[6~", "\033[6~", "\033[6~", "\033\033[6~"}},
+ {VK_HOME, {"\033[1~", "\033[1~", "\033[1~", "\033\033[1~"}},
+ {VK_END, {"\033[4~", "\033[4~", "\033[4~", "\033\033[4~"}},
+ {VK_INSERT, {"\033[2~", "\033[2~", "\033[2~", "\033\033[2~"}},
+ {VK_DELETE, {"\033[3~", "\033[3~", "\033[3~", "\033\033[3~"}},
+ {VK_F1, {"\033[[A", "\033[23~", NULL, NULL}},
+ {VK_F2, {"\033[[B", "\033[24~", NULL, NULL}},
+ {VK_F3, {"\033[[C", "\033[25~", NULL, NULL}},
+ {VK_F4, {"\033[[D", "\033[26~", NULL, NULL}},
+ {VK_F5, {"\033[[E", "\033[28~", NULL, NULL}},
+ {VK_F6, {"\033[17~", "\033[29~", "\036", NULL}},
+ {VK_F7, {"\033[18~", "\033[31~", NULL, NULL}},
+ {VK_F8, {"\033[19~", "\033[32~", NULL, NULL}},
+ {VK_F9, {"\033[20~", "\033[33~", NULL, NULL}},
+ {VK_F10, {"\033[21~", "\033[34~", NULL, NULL}},
+ {VK_F11, {"\033[23~", NULL, NULL, NULL}},
+ {VK_F12, {"\033[24~", NULL, NULL, NULL}},
+ {VK_NUMPAD5, {"\033[G", NULL, NULL, NULL}},
+ {VK_CLEAR, {"\033[G", NULL, NULL, NULL}},
+ {'6', {NULL, NULL, "\036", NULL}},
+ {0, {"", NULL, NULL, NULL}}
+};
+
+const char *
+get_nonascii_key (INPUT_RECORD& input_rec, char *tmp)
+{
+#define NORMAL 0
+#define SHIFT 1
+#define CONTROL 2
+#define ALT 3
+ int modifier_index = NORMAL;
+
+ if (input_rec.Event.KeyEvent.dwControlKeyState & SHIFT_PRESSED)
+ modifier_index = SHIFT;
+ else if (input_rec.Event.KeyEvent.dwControlKeyState &
+ (LEFT_CTRL_PRESSED | RIGHT_CTRL_PRESSED))
+ modifier_index = CONTROL;
+ else if (input_rec.Event.KeyEvent.dwControlKeyState &
+ (LEFT_ALT_PRESSED | RIGHT_ALT_PRESSED))
+ modifier_index = ALT;
+
+ for (int i = 0; keytable[i].vk; i++)
+ if (input_rec.Event.KeyEvent.wVirtualKeyCode == keytable[i].vk)
+ return keytable[i].val[modifier_index];
+
+ if (input_rec.Event.KeyEvent.uChar.AsciiChar)
+ {
+ tmp[0] = input_rec.Event.KeyEvent.uChar.AsciiChar;
+ tmp[1] = '\0';
+ return tmp;
+ }
+ return NULL;
+}
+
+void
+fhandler_console::init (HANDLE f, DWORD a, mode_t bin)
+{
+ // this->fhandler_termios::init (f, mode, bin);
+ /* Ensure both input and output console handles are open */
+ int flags = 0;
+
+ a &= GENERIC_READ | GENERIC_WRITE;
+ if (a == GENERIC_READ)
+ flags = O_RDONLY;
+ if (a == GENERIC_WRITE)
+ flags = O_WRONLY;
+ if (a == (GENERIC_READ | GENERIC_WRITE))
+ flags = O_RDWR;
+ open ((path_conv *) NULL, flags | O_BINARY);
+ if (f != INVALID_HANDLE_VALUE)
+ CloseHandle (f); /* Reopened by open */
+
+ this->tcsetattr (0, &tc->ti);
+}
+
+int
+fhandler_console::igncr_enabled (void)
+{
+ return tc->ti.c_iflag & IGNCR;
+}
+
+void
+fhandler_console::set_close_on_exec (int val)
+{
+ this->fhandler_base::set_close_on_exec (val);
+ set_inheritance (output_handle, val);
+}
+
+void
+fhandler_console::fixup_after_fork (HANDLE)
+{
+ HANDLE h = get_handle ();
+ HANDLE oh = get_output_handle ();
+
+ /* Windows does not allow duplication of console handles between processes
+ so open the console explicitly. */
+
+ if (!open (NULL, O_NOCTTY | get_flags (), 0))
+ system_printf ("error opening console after fork, %E");
+
+ if (!get_close_on_exec ())
+ {
+ CloseHandle (h);
+ CloseHandle (oh);
+ }
+}
+
+void __stdcall
+set_console_title (char *title)
+{
+ int rc;
+ char buf[257];
+ strncpy (buf, title, sizeof (buf) - 1);
+ buf[sizeof (buf) - 1] = '\0';
+ if ((rc = WaitForSingleObject (title_mutex, 15000)) != WAIT_OBJECT_0)
+ sigproc_printf ("wait for title mutex failed rc %d, %E", rc);
+ SetConsoleTitle (buf);
+ ReleaseMutex (title_mutex);
+ debug_printf ("title '%s'", buf);
+}
+
+void
+fhandler_console::fixup_after_exec (HANDLE)
+{
+ HANDLE h = get_handle ();
+ HANDLE oh = get_output_handle ();
+
+ if (!open (NULL, O_NOCTTY | get_flags (), 0))
+ {
+ int sawerr = 0;
+ if (!get_io_handle ())
+ {
+ system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ());
+ sawerr = 1;
+ }
+ if (!get_output_handle ())
+ {
+ system_printf ("error opening input console handle after exec, errno %d, %E", get_errno ());
+ sawerr = 1;
+ }
+
+ if (!sawerr)
+ system_printf ("error opening console after exec, errno %d, %E", get_errno ());
+ }
+
+ CloseHandle (h);
+ CloseHandle (oh);
+ return;
+}
diff --git a/winsup/cygwin/fhandler_disk_file.cc b/winsup/cygwin/fhandler_disk_file.cc
new file mode 100644
index 00000000000..17ad55cf78e
--- /dev/null
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -0,0 +1,850 @@
+/* fhandler_disk_file.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <signal.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "cygwin/version.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "pinfo.h"
+#include <assert.h>
+#include <ctype.h>
+
+#define _COMPILING_NEWLIB
+#include <dirent.h>
+
+/* The fhandler_base stat calls and helpers are actually only
+ applicable to files on disk. However, they are part of the
+ base class so that on-disk device files can also access them
+ as appropriate. */
+
+static int __stdcall
+num_entries (const char *win32_name)
+{
+ WIN32_FIND_DATA buf;
+ HANDLE handle;
+ char buf1[MAX_PATH];
+ int count = 0;
+
+ strcpy (buf1, win32_name);
+ int len = strlen (buf1);
+ if (len == 0 || isdirsep (buf1[len - 1]))
+ strcat (buf1, "*");
+ else
+ strcat (buf1, "/*"); /* */
+
+ handle = FindFirstFileA (buf1, &buf);
+
+ if (handle == INVALID_HANDLE_VALUE)
+ return 2; /* 2 is the minimum number of links to a dir, so... */
+ count ++;
+ while (FindNextFileA (handle, &buf))
+ {
+ if ((buf.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ count ++;
+ }
+ FindClose (handle);
+ return count;
+}
+
+int __stdcall
+fhandler_base::fstat_by_handle (struct __stat64 *buf, path_conv *pc)
+{
+ int res = 0;
+ BY_HANDLE_FILE_INFORMATION local;
+
+ /* NT 3.51 seems to have a bug when attempting to get vol serial
+ numbers. This loop gets around this. */
+ for (int i = 0; i < 2; i++)
+ {
+ if (!(res = GetFileInformationByHandle (get_handle (), &local)))
+ break;
+ if (local.dwVolumeSerialNumber && (long) local.dwVolumeSerialNumber != -1)
+ break;
+ }
+
+ debug_printf ("%d = GetFileInformationByHandle (%s, %d)",
+ res, get_win32_name (), get_handle ());
+ if (res == 0)
+ /* GetFileInformationByHandle will fail if it's given stdin/out/err
+ or a pipe*/
+ {
+ memset (&local, 0, sizeof (local));
+ local.nFileSizeLow = GetFileSize (get_handle (), &local.nFileSizeHigh);
+ }
+
+ return fstat_helper (buf, pc,
+ local.ftCreationTime,
+ local.ftLastAccessTime,
+ local.ftLastWriteTime,
+ local.nFileSizeHigh,
+ local.nFileSizeLow,
+ local.nFileIndexHigh,
+ local.nFileIndexLow,
+ local.nNumberOfLinks);
+}
+
+int __stdcall
+fhandler_base::fstat_by_name (struct __stat64 *buf, path_conv *pc)
+{
+ int res;
+
+ if (!pc->exists ())
+ {
+ debug_printf ("already determined that pc does not exist");
+ set_errno (ENOENT);
+ res = -1;
+ }
+ else
+ {
+ char drivebuf[5];
+ char *name;
+ if ((*pc)[3] != '\0' || !isalpha ((*pc)[0]) || (*pc)[1] != ':' || (*pc)[2] != '\\')
+ name = *pc;
+ else
+ {
+ /* FIXME: Does this work on empty disks? */
+ drivebuf[0] = (*pc)[0];
+ drivebuf[1] = (*pc)[1];
+ drivebuf[2] = (*pc)[2];
+ drivebuf[3] = '*';
+ drivebuf[4] = '\0';
+ name = drivebuf;
+ }
+
+ HANDLE h;
+ WIN32_FIND_DATA local;
+ if ((h = FindFirstFile (name, &local)) == INVALID_HANDLE_VALUE)
+ {
+ debug_printf ("FindFirstFile failed for '%s', %E", name);
+ __seterrno ();
+ res = -1;
+ }
+ else
+ {
+ FindClose (h);
+ res = fstat_helper (buf, pc,
+ local.ftCreationTime,
+ local.ftLastAccessTime,
+ local.ftLastWriteTime,
+ local.nFileSizeHigh,
+ local.nFileSizeLow);
+ }
+ }
+ return res;
+}
+
+int __stdcall
+fhandler_base::fstat_fs (struct __stat64 *buf, path_conv *pc)
+{
+ int res = -1;
+ int oret;
+ __uid32_t uid;
+ __gid32_t gid;
+ int open_flags = O_RDONLY | O_BINARY | O_DIROPEN;
+ bool query_open_already;
+
+ if (get_io_handle ())
+ {
+ if (get_nohandle ())
+ return fstat_by_name (buf, pc);
+ else
+ return fstat_by_handle (buf, pc);
+ }
+ /* If we don't care if the file is executable or we already know if it is,
+ then just do a "query open" as it is apparently much faster. */
+ if (pc->exec_state () != dont_know_if_executable)
+ set_query_open (query_open_already = true);
+ else
+ query_open_already = false;
+
+ if (query_open_already && strncasematch (pc->volname (), "FAT", 3)
+ && !strpbrk (get_win32_name (), "?*|<>"))
+ oret = 0;
+ else if (!(oret = open_fs (pc, open_flags, 0)))
+ {
+ int ntsec_atts = 0;
+ /* If we couldn't open the file, try a "query open" with no permissions.
+ This will allow us to determine *some* things about the file, at least. */
+ set_query_open (true);
+ if (!query_open_already && (oret = open (pc, open_flags, 0)))
+ /* ok */;
+ else if (allow_ntsec && pc->has_acls () && get_errno () == EACCES
+ && !get_file_attribute (TRUE, get_win32_name (), &ntsec_atts, &uid, &gid)
+ && !ntsec_atts && uid == myself->uid && gid == myself->gid)
+ {
+ /* Check a special case here. If ntsec is ON it happens
+ that a process creates a file using mode 000 to disallow
+ other processes access. In contrast to UNIX, this results
+ in a failing open call in the same process. Check that
+ case. */
+ set_file_attribute (TRUE, get_win32_name (), 0400);
+ oret = open (pc, open_flags, 0);
+ set_file_attribute (TRUE, get_win32_name (), ntsec_atts);
+ }
+ }
+
+ if (!oret || get_nohandle ())
+ res = fstat_by_name (buf, pc);
+ else
+ {
+ res = fstat_by_handle (buf, pc);
+ close_fs ();
+ }
+
+ return res;
+}
+
+int __stdcall
+fhandler_base::fstat_helper (struct __stat64 *buf, path_conv *pc,
+ FILETIME ftCreationTime,
+ FILETIME ftLastAccessTime,
+ FILETIME ftLastWriteTime,
+ DWORD nFileSizeHigh,
+ DWORD nFileSizeLow,
+ DWORD nFileIndexHigh,
+ DWORD nFileIndexLow,
+ DWORD nNumberOfLinks)
+{
+ /* This is for FAT filesystems, which don't support atime/ctime */
+ if (ftLastAccessTime.dwLowDateTime == 0
+ && ftLastAccessTime.dwHighDateTime == 0)
+ ftLastAccessTime = ftLastWriteTime;
+ if (ftCreationTime.dwLowDateTime == 0
+ && ftCreationTime.dwHighDateTime == 0)
+ ftCreationTime = ftLastWriteTime;
+
+ to_timestruc_t (&ftLastAccessTime, &buf->st_atim);
+ to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
+ to_timestruc_t (&ftCreationTime, &buf->st_ctim);
+ buf->st_dev = pc->volser ();
+ buf->st_size = ((__off64_t)nFileSizeHigh << 32) + nFileSizeLow;
+ /* Unfortunately the count of 2 confuses `find (1)' command. So
+ let's try it with `1' as link count. */
+ if (pc->isdir () && !pc->isremote () && nNumberOfLinks == 1)
+ buf->st_nlink = num_entries (pc->get_win32 ());
+ else
+ buf->st_nlink = nNumberOfLinks;
+
+ /* Assume that if a drive has ACL support it MAY have valid "inodes".
+ It definitely does not have valid inodes if it does not have ACL
+ support. */
+ switch (pc->has_acls () && (nFileIndexHigh || nFileIndexLow)
+ ? pc->drive_type () : DRIVE_UNKNOWN)
+ {
+ case DRIVE_FIXED:
+ case DRIVE_REMOVABLE:
+ case DRIVE_CDROM:
+ case DRIVE_RAMDISK:
+ /* Although the documentation indicates otherwise, it seems like
+ "inodes" on these devices are persistent, at least across reboots. */
+ buf->st_ino = nFileIndexHigh | nFileIndexLow;
+ break;
+ default:
+ /* Either the nFileIndex* fields are unreliable or unavailable. Use the
+ next best alternative. */
+ buf->st_ino = get_namehash ();
+ break;
+ }
+
+ buf->st_blksize = S_BLKSIZE;
+ buf->st_blocks = (buf->st_size + S_BLKSIZE - 1) / S_BLKSIZE;
+
+ buf->st_mode = 0;
+ /* Using a side effect: get_file_attibutes checks for
+ directory. This is used, to set S_ISVTX, if needed. */
+ if (pc->isdir ())
+ buf->st_mode = S_IFDIR;
+ else if (pc->issymlink ())
+ buf->st_mode = S_IFLNK;
+ else if (pc->issocket ())
+ buf->st_mode = S_IFSOCK;
+
+ __uid32_t uid;
+ __gid32_t gid;
+ if (get_file_attribute (pc->has_acls (), get_win32_name (), &buf->st_mode,
+ &uid, &gid) == 0)
+ {
+ /* If read-only attribute is set, modify ntsec return value */
+ if (pc->has_attribute (FILE_ATTRIBUTE_READONLY) && !get_symlink_p ())
+ buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+
+ if (!(buf->st_mode & S_IFMT))
+ buf->st_mode |= S_IFREG;
+ }
+ else
+ {
+ buf->st_mode |= STD_RBITS;
+
+ if (!pc->has_attribute (FILE_ATTRIBUTE_READONLY))
+ buf->st_mode |= STD_WBITS;
+ /* | S_IWGRP | S_IWOTH; we don't give write to group etc */
+
+ if (buf->st_mode & S_IFDIR)
+ buf->st_mode |= S_IFDIR | STD_XBITS;
+ else if (buf->st_mode & S_IFMT)
+ /* nothing */;
+ else if (pc->issocket ())
+ buf->st_mode |= S_IFSOCK;
+ else if (is_fs_special ())
+ {
+ buf->st_dev = dev;
+ buf->st_mode = dev.mode;
+ }
+ else
+ {
+ buf->st_mode |= S_IFREG;
+ if (pc->exec_state () == dont_know_if_executable)
+ {
+ DWORD cur, done;
+ char magic[3];
+
+ /* First retrieve current position, set to beginning
+ of file if not already there. */
+ cur = SetFilePointer (get_handle (), 0, NULL, FILE_CURRENT);
+ if (cur != INVALID_SET_FILE_POINTER
+ && (!cur || SetFilePointer (get_handle (), 0, NULL, FILE_BEGIN)
+ != INVALID_SET_FILE_POINTER))
+ {
+ /* FIXME should we use /etc/magic ? */
+ magic[0] = magic[1] = magic[2] = '\0';
+ if (ReadFile (get_handle (), magic, 3, &done, NULL)
+ && has_exec_chars (magic, done))
+ {
+ set_execable_p ();
+ pc->set_exec ();
+ buf->st_mode |= STD_XBITS;
+ }
+ (void) SetFilePointer (get_handle (), cur, NULL, FILE_BEGIN);
+ }
+ }
+ }
+
+ if (pc->exec_state () == is_executable)
+ buf->st_mode |= STD_XBITS;
+ }
+
+ buf->st_uid = uid;
+ buf->st_gid = gid;
+
+ /* The number of links to a directory includes the
+ number of subdirectories in the directory, since all
+ those subdirectories point to it.
+ This is too slow on remote drives, so we do without it and
+ set the number of links to 2. */
+
+ syscall_printf ("0 = fstat (, %p) st_atime=%x st_size=%D, st_mode=%p, st_ino=%d, sizeof=%d",
+ buf, buf->st_atime, buf->st_size, buf->st_mode,
+ (int) buf->st_ino, sizeof (*buf));
+ return 0;
+}
+
+int __stdcall
+fhandler_disk_file::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ return fstat_fs (buf, pc);
+}
+
+fhandler_disk_file::fhandler_disk_file () :
+ fhandler_base ()
+{
+}
+
+int
+fhandler_disk_file::open (path_conv *real_path, int flags, mode_t mode)
+{
+ return open_fs (real_path, flags, mode);
+}
+
+int
+fhandler_base::open_fs (path_conv *real_path, int flags, mode_t mode)
+{
+ if (real_path->case_clash && flags & O_CREAT)
+ {
+ debug_printf ("case clash detected");
+ set_errno (ECASECLASH);
+ return 0;
+ }
+
+ set_has_acls (real_path->has_acls ());
+ set_isremote (real_path->isremote ());
+
+ int res = this->fhandler_base::open (real_path, flags | O_DIROPEN, mode);
+ if (!res)
+ goto out;
+
+ /* This is for file systems known for having a buggy CreateFile call
+ which might return a valid HANDLE without having actually opened
+ the file.
+ The only known file system to date is the SUN NFS Solstice Client 3.1
+ which returns a valid handle when trying to open a file in a nonexistent
+ directory. */
+ if (real_path->has_buggy_open () && !real_path->exists ())
+ {
+ debug_printf ("Buggy open detected.");
+ close_fs ();
+ set_errno (ENOENT);
+ return 0;
+ }
+
+ set_symlink_p (real_path->issymlink ());
+ set_execable_p (real_path->exec_state ());
+ set_socket_p (real_path->issocket ());
+
+out:
+ syscall_printf ("%d = fhandler_disk_file::open (%s, %p)", res,
+ get_win32_name (), flags);
+ return res;
+}
+
+int
+fhandler_disk_file::close ()
+{
+ return close_fs ();
+}
+
+int
+fhandler_base::close_fs ()
+{
+ int res = this->fhandler_base::close ();
+ if (!res)
+ cygwin_shared->delqueue.process_queue ();
+ return res;
+}
+
+/* FIXME: The correct way to do this to get POSIX locking semantics is to
+ keep a linked list of posix lock requests and map them into Win32 locks.
+ he problem is that Win32 does not deal correctly with overlapping lock
+ requests. Also another pain is that Win95 doesn't do non-blocking or
+ non-exclusive locks at all. For '95 just convert all lock requests into
+ blocking,exclusive locks. This shouldn't break many apps but denying all
+ locking would. For now just convert to Win32 locks and hope for
+ the best. */
+
+int
+fhandler_disk_file::lock (int cmd, struct flock *fl)
+{
+ __off64_t win32_start;
+ int win32_len;
+ DWORD win32_upper;
+ __off64_t startpos;
+
+ /*
+ * We don't do getlck calls yet.
+ */
+
+ if (cmd == F_GETLK)
+ {
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ /*
+ * Calculate where in the file to start from,
+ * then adjust this by fl->l_start.
+ */
+
+ switch (fl->l_whence)
+ {
+ case SEEK_SET:
+ startpos = 0;
+ break;
+ case SEEK_CUR:
+ if ((startpos = lseek (0, SEEK_CUR)) == ILLEGAL_SEEK)
+ return -1;
+ break;
+ case SEEK_END:
+ {
+ BY_HANDLE_FILE_INFORMATION finfo;
+ if (GetFileInformationByHandle (get_handle (), &finfo) == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ startpos = ((__off64_t)finfo.nFileSizeHigh << 32)
+ + finfo.nFileSizeLow;
+ break;
+ }
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /*
+ * Now the fun starts. Adjust the start and length
+ * fields until they make sense.
+ */
+
+ win32_start = startpos + fl->l_start;
+ if (fl->l_len < 0)
+ {
+ win32_start -= fl->l_len;
+ win32_len = -fl->l_len;
+ }
+ else
+ win32_len = fl->l_len;
+
+ if (win32_start < 0)
+ {
+ /* watch the signs! */
+ win32_len -= -win32_start;
+ if (win32_len <= 0)
+ {
+ /* Failure ! */
+ set_errno (EINVAL);
+ return -1;
+ }
+ win32_start = 0;
+ }
+
+ /*
+ * Special case if len == 0 for POSIX means lock
+ * to the end of the entire file (and all future extensions).
+ */
+ if (win32_len == 0)
+ {
+ win32_len = 0xffffffff;
+ win32_upper = wincap.lock_file_highword ();
+ }
+ else
+ win32_upper = 0;
+
+ BOOL res;
+
+ if (wincap.has_lock_file_ex ())
+ {
+ DWORD lock_flags = (cmd == F_SETLK) ? LOCKFILE_FAIL_IMMEDIATELY : 0;
+ lock_flags |= (fl->l_type == F_WRLCK) ? LOCKFILE_EXCLUSIVE_LOCK : 0;
+
+ OVERLAPPED ov;
+
+ ov.Internal = 0;
+ ov.InternalHigh = 0;
+ ov.Offset = (DWORD)win32_start;
+ ov.OffsetHigh = 0;
+ ov.hEvent = (HANDLE) 0;
+
+ if (fl->l_type == F_UNLCK)
+ {
+ res = UnlockFileEx (get_handle (), 0, (DWORD)win32_len, win32_upper, &ov);
+ }
+ else
+ {
+ res = LockFileEx (get_handle (), lock_flags, 0, (DWORD)win32_len,
+ win32_upper, &ov);
+ /* Deal with the fail immediately case. */
+ /*
+ * FIXME !! I think this is the right error to check for
+ * but I must admit I haven't checked....
+ */
+ if ((res == 0) && (lock_flags & LOCKFILE_FAIL_IMMEDIATELY) &&
+ (GetLastError () == ERROR_LOCK_FAILED))
+ {
+ set_errno (EAGAIN);
+ return -1;
+ }
+ }
+ }
+ else
+ {
+ /* Windows 95 -- use primitive lock call */
+ if (fl->l_type == F_UNLCK)
+ res = UnlockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len,
+ win32_upper);
+ else
+ res = LockFile (get_handle (), (DWORD)win32_start, 0, (DWORD)win32_len, win32_upper);
+ }
+
+ if (res == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+DIR *
+fhandler_disk_file::opendir (path_conv& real_name)
+{
+ DIR *dir;
+ DIR *res = NULL;
+ size_t len;
+
+ if (!real_name.isdir ())
+ set_errno (ENOTDIR);
+ else if ((len = strlen (real_name))> MAX_PATH - 3)
+ set_errno (ENAMETOOLONG);
+ else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
+ set_errno (ENOMEM);
+ else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL)
+ {
+ free (dir);
+ set_errno (ENOMEM);
+ }
+ else if ((dir->__d_dirent =
+ (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
+ {
+ free (dir->__d_dirname);
+ free (dir);
+ set_errno (ENOMEM);
+ }
+ else
+ {
+ strcpy (dir->__d_dirname, real_name.get_win32 ());
+ dir->__d_dirent->d_version = __DIRENT_VERSION;
+ cygheap_fdnew fd;
+ fd = this;
+ fd->set_nohandle (true);
+ dir->__d_dirent->d_fd = fd;
+ dir->__d_u.__d_data.__fh = this;
+ /* FindFirstFile doesn't seem to like duplicate /'s. */
+ len = strlen (dir->__d_dirname);
+ if (len == 0 || isdirsep (dir->__d_dirname[len - 1]))
+ strcat (dir->__d_dirname, "*");
+ else
+ strcat (dir->__d_dirname, "\\*"); /**/
+ dir->__d_cookie = __DIRENT_COOKIE;
+ dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
+ dir->__d_position = 0;
+ dir->__d_dirhash = get_namehash ();
+
+ res = dir;
+ }
+
+ syscall_printf ("%p = opendir (%s)", res, get_name ());
+ return res;
+}
+
+struct dirent *
+fhandler_disk_file::readdir (DIR *dir)
+{
+ WIN32_FIND_DATA buf;
+ HANDLE handle;
+ struct dirent *res = NULL;
+
+ if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE
+ && dir->__d_position == 0)
+ {
+ handle = FindFirstFileA (dir->__d_dirname, &buf);
+ DWORD lasterr = GetLastError ();
+ dir->__d_u.__d_data.__handle = handle;
+ if (handle == INVALID_HANDLE_VALUE && (lasterr != ERROR_NO_MORE_FILES))
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, lasterr);
+ return res;
+ }
+ }
+ else if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE)
+ {
+ return res;
+ }
+ else if (!FindNextFileA (dir->__d_u.__d_data.__handle, &buf))
+ {
+ DWORD lasterr = GetLastError ();
+ (void) FindClose (dir->__d_u.__d_data.__handle);
+ dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
+ /* POSIX says you shouldn't set errno when readdir can't
+ find any more files; so, if another error we leave it set. */
+ if (lasterr != ERROR_NO_MORE_FILES)
+ seterrno_from_win_error (__FILE__, __LINE__, lasterr);
+ syscall_printf ("%p = readdir (%p)", res, dir);
+ return res;
+ }
+
+ /* We get here if `buf' contains valid data. */
+ strcpy (dir->__d_dirent->d_name, buf.cFileName);
+
+ /* Check for Windows shortcut. If it's a Cygwin or U/WIN
+ symlink, drop the .lnk suffix. */
+ if (buf.dwFileAttributes & FILE_ATTRIBUTE_READONLY)
+ {
+ char *c = dir->__d_dirent->d_name;
+ int len = strlen (c);
+ if (strcasematch (c + len - 4, ".lnk"))
+ {
+ char fbuf[MAX_PATH + 1];
+ strcpy (fbuf, dir->__d_dirname);
+ strcpy (fbuf + strlen (fbuf) - 1, dir->__d_dirent->d_name);
+ path_conv fpath (fbuf, PC_SYM_NOFOLLOW);
+ if (fpath.issymlink () || fpath.isspecial ())
+ c[len - 4] = '\0';
+ }
+ }
+
+ dir->__d_position++;
+ res = dir->__d_dirent;
+ syscall_printf ("%p = readdir (%p) (%s)",
+ &dir->__d_dirent, dir, buf.cFileName);
+ return res;
+}
+
+__off64_t
+fhandler_disk_file::telldir (DIR *dir)
+{
+ return dir->__d_position;
+}
+
+void
+fhandler_disk_file::seekdir (DIR *dir, __off64_t loc)
+{
+ rewinddir (dir);
+ while (loc > dir->__d_position)
+ if (!readdir (dir))
+ break;
+}
+
+void
+fhandler_disk_file::rewinddir (DIR *dir)
+{
+ if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE)
+ {
+ (void) FindClose (dir->__d_u.__d_data.__handle);
+ dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
+ }
+ dir->__d_position = 0;
+}
+
+int
+fhandler_disk_file::closedir (DIR *dir)
+{
+ int res = 0;
+ if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE &&
+ FindClose (dir->__d_u.__d_data.__handle) == 0)
+ {
+ __seterrno ();
+ res = -1;
+ }
+ syscall_printf ("%d = closedir (%p)", res, dir);
+ return 0;
+}
+
+fhandler_cygdrive::fhandler_cygdrive () :
+ fhandler_disk_file (), ndrives (0), pdrive (NULL)
+{
+}
+
+#define DRVSZ sizeof ("x:\\")
+void
+fhandler_cygdrive::set_drives ()
+{
+ const int len = 1 + 26 * DRVSZ;
+ char *p = (char *) crealloc ((void *) win32_path_name,
+ sizeof (".") + sizeof ("..") + len);
+
+ win32_path_name = pdrive = p;
+ strcpy (p, ".");
+ strcpy (p + sizeof ("."), "..");
+ p += sizeof (".") + sizeof ("..");
+ ndrives = GetLogicalDriveStrings (len, p) / DRVSZ;
+}
+
+int
+fhandler_cygdrive::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ if (!iscygdrive_root ())
+ return fhandler_disk_file::fstat (buf, pc);
+ buf->st_mode = S_IFDIR | 0555;
+ if (!ndrives)
+ set_drives ();
+ buf->st_nlink = ndrives;
+ return 0;
+}
+
+DIR *
+fhandler_cygdrive::opendir (path_conv& real_name)
+{
+ DIR *dir;
+
+ dir = fhandler_disk_file::opendir (real_name);
+ if (dir && iscygdrive_root () && !ndrives)
+ set_drives ();
+
+ return dir;
+}
+
+struct dirent *
+fhandler_cygdrive::readdir (DIR *dir)
+{
+ if (!iscygdrive_root ())
+ return fhandler_disk_file::readdir (dir);
+ if (!pdrive || !*pdrive)
+ {
+ set_errno (ENMFILE);
+ return NULL;
+ }
+ else if (dir->__d_position > 1
+ && GetFileAttributes (pdrive) == INVALID_FILE_ATTRIBUTES)
+ {
+ pdrive = strchr (pdrive, '\0') + 1;
+ return readdir (dir);
+ }
+ else if (*pdrive == '.')
+ strcpy (dir->__d_dirent->d_name, pdrive);
+ else
+ {
+ *dir->__d_dirent->d_name = cyg_tolower (*pdrive);
+ dir->__d_dirent->d_name[1] = '\0';
+ }
+ dir->__d_position++;
+ pdrive = strchr (pdrive, '\0') + 1;
+ syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir,
+ dir->__d_dirent->d_name);
+ return dir->__d_dirent;
+}
+
+__off64_t
+fhandler_cygdrive::telldir (DIR *dir)
+{
+ return fhandler_disk_file::telldir (dir);
+}
+
+void
+fhandler_cygdrive::seekdir (DIR *dir, __off64_t loc)
+{
+ if (!iscygdrive_root ())
+ return fhandler_disk_file::seekdir (dir, loc);
+
+ for (pdrive = win32_path_name, dir->__d_position = -1; *pdrive;
+ pdrive = strchr (pdrive, '\0') + 1)
+ if (++dir->__d_position >= loc)
+ break;
+
+ return;
+}
+
+void
+fhandler_cygdrive::rewinddir (DIR *dir)
+{
+ if (!iscygdrive_root ())
+ return fhandler_disk_file::rewinddir (dir);
+ pdrive = win32_path_name;
+ dir->__d_position = 0;
+ return;
+}
+
+int
+fhandler_cygdrive::closedir (DIR *dir)
+{
+ if (!iscygdrive_root ())
+ return fhandler_disk_file::closedir (dir);
+ pdrive = win32_path_name;
+ return -1;
+}
diff --git a/winsup/cygwin/fhandler_dsp.cc b/winsup/cygwin/fhandler_dsp.cc
new file mode 100644
index 00000000000..daa9a629807
--- /dev/null
+++ b/winsup/cygwin/fhandler_dsp.cc
@@ -0,0 +1,653 @@
+/* fhandler_dev_dsp: code to emulate OSS sound model /dev/dsp
+
+ Copyright 2001, 2002, 2003 Red Hat, Inc
+
+ Written by Andy Younger (andy@snoogie.demon.co.uk)
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdio.h>
+#include <errno.h>
+#include <windows.h>
+#include <sys/soundcard.h>
+#include <mmsystem.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+
+//------------------------------------------------------------------------
+// Simple encapsulation of the win32 audio device.
+//
+static void CALLBACK wave_callback (HWAVE hWave, UINT msg, DWORD instance,
+ DWORD param1, DWORD param2);
+class Audio
+{
+public:
+ enum
+ {
+ MAX_BLOCKS = 12,
+ BLOCK_SIZE = 16384,
+ TOT_BLOCK_SIZE = BLOCK_SIZE + sizeof (WAVEHDR)
+ };
+
+ Audio ();
+ ~Audio ();
+
+ bool open (int rate, int bits, int channels, bool bCallback = false);
+ void close ();
+ int getvolume ();
+ void setvolume (int newVolume);
+ bool write (const void *pSampleData, int nBytes);
+ int blocks ();
+ void callback_sampledone (void *pData);
+ void setformat (int format) {formattype_ = format;}
+ int numbytesoutput ();
+
+ void *operator new (size_t, void *p) {return p;}
+
+private:
+ char *initialisebuffer ();
+ void waitforcallback ();
+ bool flush ();
+
+ HWAVEOUT dev_;
+ volatile int nBlocksInQue_;
+ int nBytesWritten_;
+ char *buffer_;
+ int bufferIndex_;
+ CRITICAL_SECTION lock_;
+ char *freeblocks_[MAX_BLOCKS];
+ int formattype_;
+
+ char bigwavebuffer_[MAX_BLOCKS * TOT_BLOCK_SIZE];
+};
+
+static char audio_buf[sizeof (class Audio)];
+
+Audio::Audio ()
+{
+ InitializeCriticalSection (&lock_);
+ memset (bigwavebuffer_, 0, sizeof (bigwavebuffer_));
+ for (int i = 0; i < MAX_BLOCKS; i++)
+ freeblocks_[i] = &bigwavebuffer_[i * TOT_BLOCK_SIZE];
+}
+
+Audio::~Audio ()
+{
+ if (dev_)
+ close ();
+ DeleteCriticalSection (&lock_);
+}
+
+bool
+Audio::open (int rate, int bits, int channels, bool bCallback)
+{
+ WAVEFORMATEX format;
+ int nDevices = waveOutGetNumDevs ();
+
+ nBytesWritten_ = 0L;
+ bufferIndex_ = 0;
+ buffer_ = 0L;
+ debug_printf ("number devices %d", nDevices);
+ if (nDevices <= 0)
+ return false;
+
+ debug_printf ("trying to map device freq %d, bits %d, "
+ "channels %d, callback %d", rate, bits, channels,
+ bCallback);
+
+ int bytesperSample = bits / 8;
+
+ memset (&format, 0, sizeof (format));
+ format.wFormatTag = WAVE_FORMAT_PCM;
+ format.wBitsPerSample = bits;
+ format.nChannels = channels;
+ format.nSamplesPerSec = rate;
+ format.nAvgBytesPerSec = format.nSamplesPerSec * format.nChannels *
+ bytesperSample;
+ format.nBlockAlign = format.nChannels * bytesperSample;
+
+ nBlocksInQue_ = 0;
+ HRESULT res = waveOutOpen (&dev_, WAVE_MAPPER, &format, (DWORD) wave_callback,
+ (DWORD) this, bCallback ? CALLBACK_FUNCTION : 0);
+ if (res == S_OK)
+ {
+ debug_printf ("Sucessfully opened!");
+ return true;
+ }
+ else
+ {
+ debug_printf ("failed to open");
+ return false;
+ }
+}
+
+void
+Audio::close ()
+{
+ if (dev_)
+ {
+ flush (); // force out last block whatever size..
+
+ while (blocks ()) // block till finished..
+ waitforcallback ();
+
+ waveOutReset (dev_);
+ waveOutClose (dev_);
+ dev_ = 0L;
+ }
+ nBytesWritten_ = 0L;
+}
+
+int
+Audio::numbytesoutput ()
+{
+ return nBytesWritten_;
+}
+
+int
+Audio::getvolume ()
+{
+ DWORD volume;
+
+ waveOutGetVolume (dev_, &volume);
+ return ((volume >> 16) + (volume & 0xffff)) >> 1;
+}
+
+void
+Audio::setvolume (int newVolume)
+{
+ waveOutSetVolume (dev_, (newVolume << 16) | newVolume);
+}
+
+char *
+Audio::initialisebuffer ()
+{
+ EnterCriticalSection (&lock_);
+ WAVEHDR *pHeader = 0L;
+ for (int i = 0; i < MAX_BLOCKS; i++)
+ {
+ char *pData = freeblocks_[i];
+ if (pData)
+ {
+ pHeader = (WAVEHDR *) pData;
+ if (pHeader->dwFlags & WHDR_DONE)
+ {
+ waveOutUnprepareHeader (dev_, pHeader, sizeof (WAVEHDR));
+ }
+ freeblocks_[i] = 0L;
+ break;
+ }
+ }
+ LeaveCriticalSection (&lock_);
+
+ if (pHeader)
+ {
+ memset (pHeader, 0, sizeof (WAVEHDR));
+ pHeader->dwBufferLength = BLOCK_SIZE;
+ pHeader->lpData = (LPSTR) (&pHeader[1]);
+ return (char *) pHeader->lpData;
+ }
+ return 0L;
+}
+
+bool
+Audio::write (const void *pSampleData, int nBytes)
+{
+ // split up big blocks into smaller BLOCK_SIZE chunks
+ while (nBytes > BLOCK_SIZE)
+ {
+ write (pSampleData, BLOCK_SIZE);
+ nBytes -= BLOCK_SIZE;
+ pSampleData = (void *) ((char *) pSampleData + BLOCK_SIZE);
+ }
+
+ // Block till next sound is flushed
+ if (blocks () == MAX_BLOCKS)
+ waitforcallback ();
+
+ // Allocate new wave buffer if necessary
+ if (buffer_ == 0L)
+ {
+ buffer_ = initialisebuffer ();
+ if (buffer_ == 0L)
+ return false;
+ }
+
+
+ // Handle gathering blocks into larger buffer
+ int sizeleft = BLOCK_SIZE - bufferIndex_;
+ if (nBytes < sizeleft)
+ {
+ memcpy (&buffer_[bufferIndex_], pSampleData, nBytes);
+ bufferIndex_ += nBytes;
+ nBytesWritten_ += nBytes;
+ return true;
+ }
+
+ // flushing when we reach our limit of BLOCK_SIZE
+ memcpy (&buffer_[bufferIndex_], pSampleData, sizeleft);
+ bufferIndex_ += sizeleft;
+ nBytesWritten_ += sizeleft;
+ flush ();
+
+ // change pointer to rest of sample, and size accordingly
+ pSampleData = (void *) ((char *) pSampleData + sizeleft);
+ nBytes -= sizeleft;
+
+ // if we still have some sample left over write it out
+ if (nBytes)
+ return write (pSampleData, nBytes);
+
+ return true;
+}
+
+// return number of blocks back.
+int
+Audio::blocks ()
+{
+ EnterCriticalSection (&lock_);
+ int ret = nBlocksInQue_;
+ LeaveCriticalSection (&lock_);
+ return ret;
+}
+
+// This is called on an interupt so use locking.. Note nBlocksInQue_ is
+// modified by it so we should wrap all references to it in locks.
+void
+Audio::callback_sampledone (void *pData)
+{
+ EnterCriticalSection (&lock_);
+
+ nBlocksInQue_--;
+ for (int i = 0; i < MAX_BLOCKS; i++)
+ if (!freeblocks_[i])
+ {
+ freeblocks_[i] = (char *) pData;
+ break;
+ }
+
+ LeaveCriticalSection (&lock_);
+}
+
+void
+Audio::waitforcallback ()
+{
+ int n = blocks ();
+ if (!n)
+ return;
+ do
+ {
+ Sleep (250);
+ }
+ while (n == blocks ());
+}
+
+bool
+Audio::flush ()
+{
+ if (!buffer_)
+ return false;
+
+ // Send internal buffer out to the soundcard
+ WAVEHDR *pHeader = ((WAVEHDR *) buffer_) - 1;
+ pHeader->dwBufferLength = bufferIndex_;
+
+ // Quick bit of sample buffer conversion
+ if (formattype_ == AFMT_S8)
+ {
+ unsigned char *p = ((unsigned char *) buffer_);
+ for (int i = 0; i < bufferIndex_; i++)
+ {
+ p[i] -= 0x7f;
+ }
+ }
+
+ if (waveOutPrepareHeader (dev_, pHeader, sizeof (WAVEHDR)) == S_OK &&
+ waveOutWrite (dev_, pHeader, sizeof (WAVEHDR)) == S_OK)
+ {
+ EnterCriticalSection (&lock_);
+ nBlocksInQue_++;
+ LeaveCriticalSection (&lock_);
+ bufferIndex_ = 0;
+ buffer_ = 0L;
+ return true;
+ }
+ else
+ {
+ EnterCriticalSection (&lock_);
+ for (int i = 0; i < MAX_BLOCKS; i++)
+ if (!freeblocks_[i])
+ {
+ freeblocks_[i] = (char *) pHeader;
+ break;
+ }
+ LeaveCriticalSection (&lock_);
+ }
+ return false;
+}
+
+//------------------------------------------------------------------------
+// Call back routine
+static void CALLBACK
+wave_callback (HWAVE hWave, UINT msg, DWORD instance, DWORD param1,
+ DWORD param2)
+{
+ if (msg == WOM_DONE)
+ {
+ Audio *ptr = (Audio *) instance;
+ ptr->callback_sampledone ((void *) param1);
+ }
+}
+
+//------------------------------------------------------------------------
+// /dev/dsp handler
+static Audio *s_audio; // static instance of the Audio handler
+
+//------------------------------------------------------------------------
+// wav file detection..
+#pragma pack(1)
+struct wavchunk
+{
+ char id[4];
+ unsigned int len;
+};
+struct wavformat
+{
+ unsigned short wFormatTag;
+ unsigned short wChannels;
+ unsigned int dwSamplesPerSec;
+ unsigned int dwAvgBytesPerSec;
+ unsigned short wBlockAlign;
+ unsigned short wBitsPerSample;
+};
+#pragma pack()
+
+bool
+fhandler_dev_dsp::setupwav (const char *pData, int nBytes)
+{
+ int len;
+ const char *end = pData + nBytes;
+
+ if (!(pData[0] == 'R' && pData[1] == 'I' &&
+ pData[2] == 'F' && pData[3] == 'F'))
+ return false;
+ if (!(pData[8] == 'W' && pData[9] == 'A' &&
+ pData[10] == 'V' && pData[11] == 'E'))
+ return false;
+
+ len = *(int *) &pData[4];
+ pData += 12;
+ while (len && pData < end)
+ {
+ wavchunk * pChunk = (wavchunk *) pData;
+ int blklen = pChunk-> len;
+ if (pChunk->id[0] == 'f' && pChunk->id[1] == 'm' &&
+ pChunk->id[2] == 't' && pChunk->id[3] == ' ')
+ {
+ wavformat *format = (wavformat *) (pChunk + 1);
+ if ((char *) (format + 1) > end)
+ return false;
+
+ // Open up audio device with correct frequency for wav file
+ //
+ // FIXME: should through away all the header & not output
+ // it to the soundcard.
+ s_audio->close ();
+ if (s_audio->open (format->dwSamplesPerSec, format->wBitsPerSample,
+ format->wChannels) == false)
+ {
+ s_audio->open (audiofreq_, audiobits_, audiochannels_);
+ }
+ else
+ {
+ audiofreq_ = format->dwSamplesPerSec;
+ audiobits_ = format->wBitsPerSample;
+ audiochannels_ = format->wChannels;
+ }
+ return true;
+ }
+
+ pData += blklen + sizeof (wavchunk);
+ }
+ return false;
+}
+
+//------------------------------------------------------------------------
+fhandler_dev_dsp::fhandler_dev_dsp ():
+ fhandler_base ()
+{
+}
+
+fhandler_dev_dsp::~fhandler_dev_dsp ()
+{
+}
+
+int
+fhandler_dev_dsp::open (path_conv *, int flags, mode_t mode)
+{
+ // currently we only support writing
+ if ((flags & (O_WRONLY | O_RDONLY | O_RDWR)) != O_WRONLY)
+ {
+ set_errno (EACCES);
+ return 0;
+ }
+
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+
+ if (!s_audio)
+ s_audio = new (audio_buf) Audio;
+
+ // Work out initial sample format & frequency
+ // dev/dsp defaults
+ audioformat_ = AFMT_S8;
+ audiofreq_ = 8000;
+ audiobits_ = 8;
+ audiochannels_ = 1;
+
+ int res;
+ if (!s_audio->open (audiofreq_, audiobits_, audiochannels_))
+ res = 0;
+ else
+ {
+ set_open_status ();
+ res = 1;
+ }
+
+ debug_printf ("returns %d", res);
+ return res;
+}
+
+int
+fhandler_dev_dsp::write (const void *ptr, size_t len)
+{
+ if (s_audio->numbytesoutput () == 0)
+ {
+ // check for wave file & setup frequencys properly if possible.
+ setupwav ((const char *) ptr, len);
+
+ // Open audio device properly with callbacks.
+ s_audio->close ();
+ if (!s_audio->open (audiofreq_, audiobits_, audiochannels_, true))
+ return 0;
+ }
+
+ s_audio->write (ptr, len);
+ return len;
+}
+
+void __stdcall
+fhandler_dev_dsp::read (void *ptr, size_t& len)
+{
+ return;
+}
+
+__off64_t
+fhandler_dev_dsp::lseek (__off64_t offset, int whence)
+{
+ return 0;
+}
+
+int
+fhandler_dev_dsp::close (void)
+{
+ s_audio->close ();
+ return 0;
+}
+
+int
+fhandler_dev_dsp::dup (fhandler_base * child)
+{
+ fhandler_dev_dsp *fhc = (fhandler_dev_dsp *) child;
+
+ fhc->set_flags (get_flags ());
+ fhc->audiochannels_ = audiochannels_;
+ fhc->audiobits_ = audiobits_;
+ fhc->audiofreq_ = audiofreq_;
+ fhc->audioformat_ = audioformat_;
+ return 0;
+}
+
+int
+fhandler_dev_dsp::ioctl (unsigned int cmd, void *ptr)
+{
+ int *intptr = (int *) ptr;
+ switch (cmd)
+ {
+#define CASE(a) case a : debug_printf("/dev/dsp: ioctl %s", #a);
+
+ CASE (SNDCTL_DSP_RESET)
+ audioformat_ = AFMT_S8;
+ audiofreq_ = 8000;
+ audiobits_ = 8;
+ audiochannels_ = 1;
+ return 0;
+
+ CASE (SNDCTL_DSP_GETBLKSIZE)
+ *intptr = Audio::BLOCK_SIZE;
+ return 0;
+
+ CASE (SNDCTL_DSP_SETFMT)
+ {
+ int nBits = 0;
+ if (*intptr == AFMT_S16_LE)
+ nBits = 16;
+ else if (*intptr == AFMT_U8)
+ nBits = 8;
+ else if (*intptr == AFMT_S8)
+ nBits = 8;
+ if (nBits)
+ {
+ s_audio->setformat (*intptr);
+ s_audio->close ();
+ if (s_audio->open (audiofreq_, nBits, audiochannels_) == true)
+ {
+ audiobits_ = nBits;
+ return 0;
+ }
+ else
+ {
+ s_audio->open (audiofreq_, audiobits_, audiochannels_);
+ return -1;
+ }
+ }
+ }
+ break;
+
+ CASE (SNDCTL_DSP_SPEED)
+ s_audio->close ();
+ if (s_audio->open (*intptr, audiobits_, audiochannels_) == true)
+ {
+ audiofreq_ = *intptr;
+ return 0;
+ }
+ else
+ {
+ s_audio->open (audiofreq_, audiobits_, audiochannels_);
+ return -1;
+ }
+ break;
+
+ CASE (SNDCTL_DSP_STEREO)
+ {
+ int nChannels = *intptr + 1;
+
+ s_audio->close ();
+ if (s_audio->open (audiofreq_, audiobits_, nChannels) == true)
+ {
+ audiochannels_ = nChannels;
+ return 0;
+ }
+ else
+ {
+ s_audio->open (audiofreq_, audiobits_, audiochannels_);
+ return -1;
+ }
+ }
+ break;
+
+ CASE (SNDCTL_DSP_GETOSPACE)
+ {
+ audio_buf_info *p = (audio_buf_info *) ptr;
+
+ int nBlocks = s_audio->blocks ();
+ int leftblocks = ((Audio::MAX_BLOCKS - nBlocks) - 1);
+ if (leftblocks < 0)
+ leftblocks = 0;
+ if (leftblocks > 1)
+ leftblocks = 1;
+ int left = leftblocks * Audio::BLOCK_SIZE;
+
+ p->fragments = leftblocks;
+ p->fragstotal = Audio::MAX_BLOCKS;
+ p->fragsize = Audio::BLOCK_SIZE;
+ p->bytes = left;
+
+ debug_printf ("ptr %p nblocks %d leftblocks %d left bytes %d ",
+ ptr, nBlocks, leftblocks, left);
+
+ return 0;
+ }
+ break;
+
+ CASE (SNDCTL_DSP_SETFRAGMENT)
+ {
+ // Fake!! esound & mikmod require this on non PowerPC platforms.
+ //
+ return 0;
+ }
+ break;
+
+ CASE (SNDCTL_DSP_GETFMTS)
+ {
+ *intptr = AFMT_S16_LE | AFMT_U8 | AFMT_S8; // more?
+ return 0;
+ }
+ break;
+
+ default:
+ debug_printf ("/dev/dsp: ioctl not handled yet! FIXME:");
+ break;
+
+#undef CASE
+ };
+ return -1;
+}
+
+void
+fhandler_dev_dsp::dump ()
+{
+ paranoid_printf ("here, fhandler_dev_dsp");
+}
+
+void
+fhandler_dev_dsp::fixup_after_exec (HANDLE)
+{
+ /* FIXME: Is there a better way to do this? */
+ s_audio = new (audio_buf) Audio;
+}
diff --git a/winsup/cygwin/fhandler_floppy.cc b/winsup/cygwin/fhandler_floppy.cc
new file mode 100644
index 00000000000..b8d5173e90c
--- /dev/null
+++ b/winsup/cygwin/fhandler_floppy.cc
@@ -0,0 +1,316 @@
+/* fhandler_floppy.cc. See fhandler.h for a description of the
+ fhandler classes.
+
+ Copyright 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <sys/termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <winioctl.h>
+#include <asm/socket.h>
+#include <cygwin/hdreg.h>
+#include <cygwin/fs.h>
+#include "security.h"
+#include "fhandler.h"
+#include "cygerrno.h"
+
+/**********************************************************************/
+/* fhandler_dev_floppy */
+
+int
+fhandler_dev_floppy::is_eom (int win_error)
+{
+ int ret = (win_error == ERROR_INVALID_PARAMETER);
+ if (ret)
+ debug_printf ("end of medium");
+ return ret;
+}
+
+int
+fhandler_dev_floppy::is_eof (int)
+{
+ int ret = 0;
+ if (ret)
+ debug_printf ("end of file");
+ return ret;
+}
+
+fhandler_dev_floppy::fhandler_dev_floppy ()
+ : fhandler_dev_raw ()
+{
+}
+
+int
+fhandler_dev_floppy::open (path_conv *real_path, int flags, mode_t)
+{
+ /* The correct size of the buffer would be 512 bytes,
+ * which is the atomic size, supported by WinNT.
+ * Unfortunately, the performance is worse than
+ * access to file system on same device!
+ * Setting buffer size to a relatively big value
+ * increases performance by means.
+ * The new ioctl call with 'rdevio.h' header file
+ * supports changing this value.
+ *
+ * Let's be smart: Let's take a multiplier of typical tar
+ * and cpio buffer sizes by default!
+ */
+ devbufsiz = 61440L; /* 512L; */
+ return fhandler_dev_raw::open (real_path, flags);
+}
+
+int
+fhandler_dev_floppy::close (void)
+{
+ int ret;
+
+ ret = writebuf ();
+ if (ret)
+ {
+ fhandler_dev_raw::close ();
+ return ret;
+ }
+ return fhandler_dev_raw::close ();
+}
+
+__off64_t
+fhandler_dev_floppy::lseek (__off64_t offset, int whence)
+{
+ int ret;
+ char buf[512];
+ __off64_t drive_size = 0;
+ __off64_t lloffset = offset;
+ __off64_t current_position;
+ __off64_t sector_aligned_offset;
+ __off64_t bytes_left;
+ DWORD low;
+ LONG high = 0;
+
+ DISK_GEOMETRY di;
+ PARTITION_INFORMATION pi;
+ DWORD bytes_read;
+
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ NULL, 0,
+ &di, sizeof (di),
+ &bytes_read, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)",
+ di.Cylinders.LowPart,
+ di.TracksPerCylinder,
+ di.SectorsPerTrack,
+ di.BytesPerSector);
+ if (DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_PARTITION_INFO,
+ NULL, 0,
+ &pi, sizeof (pi),
+ &bytes_read, NULL))
+ {
+ debug_printf ("partition info: %ld (%ld)",
+ pi.StartingOffset.LowPart,
+ pi.PartitionLength.LowPart);
+ drive_size = pi.PartitionLength.QuadPart;
+ }
+ else
+ {
+ drive_size = di.Cylinders.QuadPart * di.TracksPerCylinder *
+ di.SectorsPerTrack * di.BytesPerSector;
+ }
+ debug_printf ("drive size: %ld", drive_size);
+
+ if (whence == SEEK_END && drive_size > 0)
+ {
+ lloffset = offset + drive_size;
+ whence = SEEK_SET;
+ }
+
+ if (whence == SEEK_CUR)
+ {
+ low = SetFilePointer (get_handle (), 0, &high, FILE_CURRENT);
+ if (low == INVALID_SET_FILE_POINTER && GetLastError ())
+ {
+ __seterrno ();
+ return -1;
+ }
+ current_position = low + ((__off64_t) high << 32);
+ if (is_writing)
+ current_position += devbufend - devbufstart;
+ else
+ current_position -= devbufend - devbufstart;
+
+ lloffset += current_position;
+ whence = SEEK_SET;
+ }
+
+ if (lloffset < 0 ||
+ drive_size > 0 && lloffset > drive_size)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* FIXME: sector can possibly be not 512 bytes long */
+ sector_aligned_offset = (lloffset / 512) * 512;
+ bytes_left = lloffset - sector_aligned_offset;
+
+ if (whence == SEEK_SET)
+ {
+ /* Invalidate buffer. */
+ ret = writebuf ();
+ if (ret)
+ return ret;
+ devbufstart = devbufend = 0;
+
+ low = sector_aligned_offset & 0xffffffff;
+ high = sector_aligned_offset >> 32;
+ if (SetFilePointer (get_handle (), low, &high, FILE_BEGIN)
+ == INVALID_SET_FILE_POINTER && GetLastError ())
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ size_t len = bytes_left;
+ raw_read (buf, len);
+ return sector_aligned_offset + bytes_left;
+ }
+
+ set_errno (EINVAL);
+ return -1;
+}
+
+int
+fhandler_dev_floppy::ioctl (unsigned int cmd, void *buf)
+{
+ DISK_GEOMETRY di;
+ PARTITION_INFORMATION pi;
+ DWORD bytes_read;
+ __off64_t drive_size = 0;
+ __off64_t start = 0;
+ switch (cmd)
+ {
+ case HDIO_GETGEO:
+ {
+ debug_printf ("HDIO_GETGEO");
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ NULL, 0,
+ &di, sizeof (di),
+ &bytes_read, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)",
+ di.Cylinders.LowPart,
+ di.TracksPerCylinder,
+ di.SectorsPerTrack,
+ di.BytesPerSector);
+ if (DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_PARTITION_INFO,
+ NULL, 0,
+ &pi, sizeof (pi),
+ &bytes_read, NULL))
+ {
+ debug_printf ("partition info: %ld (%ld)",
+ pi.StartingOffset.LowPart,
+ pi.PartitionLength.LowPart);
+ start = pi.StartingOffset.QuadPart >> 9ULL;
+ }
+ struct hd_geometry *geo = (struct hd_geometry *) buf;
+ geo->heads = di.TracksPerCylinder;
+ geo->sectors = di.SectorsPerTrack;
+ geo->cylinders = di.Cylinders.LowPart;
+ geo->start = start;
+ return 0;
+ }
+ case BLKGETSIZE:
+ case BLKGETSIZE64:
+ {
+ debug_printf ("BLKGETSIZE");
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ NULL, 0,
+ &di, sizeof (di),
+ &bytes_read, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)",
+ di.Cylinders.LowPart,
+ di.TracksPerCylinder,
+ di.SectorsPerTrack,
+ di.BytesPerSector);
+ if (DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_PARTITION_INFO,
+ NULL, 0,
+ &pi, sizeof (pi),
+ &bytes_read, NULL))
+ {
+ debug_printf ("partition info: %ld (%ld)",
+ pi.StartingOffset.LowPart,
+ pi.PartitionLength.LowPart);
+ drive_size = pi.PartitionLength.QuadPart;
+ }
+ else
+ {
+ drive_size = di.Cylinders.QuadPart * di.TracksPerCylinder *
+ di.SectorsPerTrack * di.BytesPerSector;
+ }
+ if (cmd == BLKGETSIZE)
+ *(long *)buf = drive_size >> 9UL;
+ else
+ *(__off64_t *)buf = drive_size;
+ return 0;
+ }
+ case BLKRRPART:
+ {
+ debug_printf ("BLKRRPART");
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_UPDATE_DRIVE_SIZE,
+ NULL, 0,
+ &di, sizeof (di),
+ &bytes_read, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+ }
+ case BLKSSZGET:
+ {
+ debug_printf ("BLKSSZGET");
+ if (!DeviceIoControl (get_handle (),
+ IOCTL_DISK_GET_DRIVE_GEOMETRY,
+ NULL, 0,
+ &di, sizeof (di),
+ &bytes_read, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ debug_printf ("disk geometry: (%ld cyl)*(%ld trk)*(%ld sec)*(%ld bps)",
+ di.Cylinders.LowPart,
+ di.TracksPerCylinder,
+ di.SectorsPerTrack,
+ di.BytesPerSector);
+ *(int *)buf = di.BytesPerSector;
+ return 0;
+ }
+ default:
+ return fhandler_dev_raw::ioctl (cmd, buf);
+ }
+}
+
diff --git a/winsup/cygwin/fhandler_mem.cc b/winsup/cygwin/fhandler_mem.cc
new file mode 100644
index 00000000000..710267ded37
--- /dev/null
+++ b/winsup/cygwin/fhandler_mem.cc
@@ -0,0 +1,439 @@
+/* fhandler_mem.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc.
+
+ This file is part of Cygwin.
+
+ This software is a copyrighted work licensed under the terms of the
+ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+ details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mman.h>
+#include <ntdef.h>
+
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "ntdll.h"
+
+/**********************************************************************/
+/* fhandler_dev_mem */
+
+fhandler_dev_mem::fhandler_dev_mem ()
+ : fhandler_base ()
+{
+ /* Reading physical memory only supported on NT/W2K. */
+ if (!wincap.has_physical_mem_access ())
+ {
+ mem_size = 0;
+ return;
+ }
+
+ if (dev == FH_MEM) /* /dev/mem */
+ {
+ NTSTATUS ret;
+ SYSTEM_BASIC_INFORMATION sbi;
+ if ((ret = NtQuerySystemInformation (SystemBasicInformation, (PVOID) &sbi,
+ sizeof sbi, NULL)) != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ debug_printf("NtQuerySystemInformation: ret = %d, Dos(ret) = %d",
+ ret, RtlNtStatusToDosError (ret));
+ mem_size = 0;
+ }
+ else
+ mem_size = sbi.PhysicalPageSize * sbi.NumberOfPhysicalPages;
+ debug_printf ("MemSize: %d MB", mem_size >> 20);
+ }
+ else if (dev == FH_KMEM) /* /dev/kmem - Not yet supported */
+ {
+ mem_size = 0;
+ debug_printf ("KMemSize: %d MB", mem_size >> 20);
+ }
+ else if (dev == FH_ZERO) /* /dev/port == First 64K of /dev/mem */
+ {
+ mem_size = 65536;
+ debug_printf ("PortSize: 64 KB");
+ }
+ else
+ {
+ mem_size = 0;
+ debug_printf ("Illegal minor number!!!");
+ }
+}
+
+fhandler_dev_mem::~fhandler_dev_mem (void)
+{
+}
+
+int
+fhandler_dev_mem::open (path_conv *, int flags, mode_t)
+{
+ if (!wincap.has_physical_mem_access ())
+ {
+ set_errno (ENOENT);
+ debug_printf ("%s is accessible under NT/W2K only", dev.name);
+ return 0;
+ }
+
+ /* Check for illegal flags. */
+ if (flags & (O_APPEND | O_TRUNC | O_EXCL))
+ {
+ set_errno (EINVAL);
+ return 0;
+ }
+
+ UNICODE_STRING memstr;
+ RtlInitUnicodeString (&memstr, L"\\device\\physicalmemory");
+
+ OBJECT_ATTRIBUTES attr;
+ InitializeObjectAttributes (&attr, &memstr,
+ OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+ NULL, NULL);
+
+ ACCESS_MASK section_access;
+ if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDONLY)
+ {
+ set_access (GENERIC_READ);
+ section_access = SECTION_MAP_READ;
+ }
+ else if ((flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY)
+ {
+ set_access (GENERIC_WRITE);
+ section_access = SECTION_MAP_READ | SECTION_MAP_WRITE;
+ }
+ else
+ {
+ set_access (GENERIC_READ | GENERIC_WRITE);
+ section_access = SECTION_MAP_READ | SECTION_MAP_WRITE;
+ }
+
+ HANDLE mem;
+ NTSTATUS ret = NtOpenSection (&mem, section_access, &attr);
+ if (!NT_SUCCESS (ret))
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ set_io_handle (NULL);
+ return 0;
+ }
+
+ set_io_handle (mem);
+ set_open_status ();
+ return 1;
+}
+
+int
+fhandler_dev_mem::write (const void *ptr, size_t ulen)
+{
+ if (!ulen || pos >= mem_size)
+ return 0;
+
+ if (!(get_access () & GENERIC_WRITE))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ if (pos + ulen > mem_size)
+ ulen = mem_size - pos;
+
+ PHYSICAL_ADDRESS phys;
+ NTSTATUS ret;
+ void *viewmem = NULL;
+ DWORD len = ulen + getpagesize () - 1;
+
+ phys.QuadPart = (ULONGLONG) pos;
+ if ((ret = NtMapViewOfSection (get_handle (),
+ INVALID_HANDLE_VALUE,
+ &viewmem,
+ 0L,
+ len,
+ &phys,
+ &len,
+ ViewShare,
+ 0,
+ PAGE_READONLY)) != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ return -1;
+ }
+
+ memcpy ((char *) viewmem + (pos - phys.QuadPart), ptr, ulen);
+
+ if (!NT_SUCCESS (ret = NtUnmapViewOfSection (INVALID_HANDLE_VALUE, viewmem)))
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ return -1;
+ }
+
+ pos += ulen;
+ return ulen;
+}
+
+void __stdcall
+fhandler_dev_mem::read (void *ptr, size_t& ulen)
+{
+ if (!ulen || pos >= mem_size)
+ {
+ ulen = 0;
+ return;
+ }
+
+ if (!(get_access () & GENERIC_READ))
+ {
+ set_errno (EINVAL);
+ (ssize_t) ulen = -1;
+ return;
+ }
+
+ if (pos + ulen > mem_size)
+ ulen = mem_size - pos;
+
+ PHYSICAL_ADDRESS phys;
+ NTSTATUS ret;
+ void *viewmem = NULL;
+ DWORD len = ulen + getpagesize () - 1;
+
+ phys.QuadPart = (ULONGLONG) pos;
+ if ((ret = NtMapViewOfSection (get_handle (),
+ INVALID_HANDLE_VALUE,
+ &viewmem,
+ 0L,
+ len,
+ &phys,
+ &len,
+ ViewShare,
+ 0,
+ PAGE_READONLY)) != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ (ssize_t) ulen = -1;
+ return;
+ }
+
+ memcpy (ptr, (char *) viewmem + (pos - phys.QuadPart), ulen);
+
+ if (!NT_SUCCESS (ret = NtUnmapViewOfSection (INVALID_HANDLE_VALUE, viewmem)))
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ (ssize_t) ulen = -1;
+ return;
+ }
+
+ pos += ulen;
+ return;
+}
+
+int
+fhandler_dev_mem::close (void)
+{
+ return fhandler_base::close ();
+}
+
+__off64_t
+fhandler_dev_mem::lseek (__off64_t offset, int whence)
+{
+ switch (whence)
+ {
+ case SEEK_SET:
+ pos = offset;
+ break;
+
+ case SEEK_CUR:
+ pos += offset;
+ break;
+
+ case SEEK_END:
+ pos = mem_size;
+ pos += offset;
+ break;
+
+ default:
+ set_errno (EINVAL);
+ return ILLEGAL_SEEK;
+ }
+
+ if (pos > mem_size)
+ {
+ set_errno (EINVAL);
+ return ILLEGAL_SEEK;
+ }
+
+ return pos;
+}
+
+HANDLE
+fhandler_dev_mem::mmap (caddr_t *addr, size_t len, DWORD access,
+ int flags, __off64_t off)
+{
+ if (off >= mem_size
+ || (DWORD) len >= mem_size
+ || off + len >= mem_size)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("-1 = mmap(): illegal parameter, set EINVAL");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ UNICODE_STRING memstr;
+ RtlInitUnicodeString (&memstr, L"\\device\\physicalmemory");
+
+ OBJECT_ATTRIBUTES attr;
+ InitializeObjectAttributes (&attr, &memstr,
+ OBJ_CASE_INSENSITIVE | OBJ_INHERIT,
+ NULL, NULL);
+
+ ACCESS_MASK section_access;
+ ULONG protect;
+
+ if (access & FILE_MAP_COPY)
+ {
+ section_access = SECTION_MAP_READ | SECTION_MAP_WRITE;
+ protect = PAGE_WRITECOPY;
+ }
+ else if (access & FILE_MAP_WRITE)
+ {
+ section_access = SECTION_MAP_READ | SECTION_MAP_WRITE;
+ protect = PAGE_READWRITE;
+ }
+ else
+ {
+ section_access = SECTION_MAP_READ;
+ protect = PAGE_READONLY;
+ }
+
+ HANDLE h;
+ NTSTATUS ret = NtOpenSection (&h, section_access, &attr);
+ if (!NT_SUCCESS (ret))
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ syscall_printf ("-1 = mmap(): NtOpenSection failed with %E");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ PHYSICAL_ADDRESS phys;
+ void *base = *addr;
+ DWORD dlen = len;
+
+ phys.QuadPart = (ULONGLONG) off;
+
+ if ((ret = NtMapViewOfSection (h,
+ INVALID_HANDLE_VALUE,
+ &base,
+ 0L,
+ dlen,
+ &phys,
+ &dlen,
+ ViewShare /*??*/,
+ 0,
+ protect)) != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ syscall_printf ("-1 = mmap(): NtMapViewOfSection failed with %E");
+ return INVALID_HANDLE_VALUE;
+ }
+ if ((flags & MAP_FIXED) && base != *addr)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("-1 = mmap(): address shift with MAP_FIXED given");
+ NtUnmapViewOfSection (INVALID_HANDLE_VALUE, base);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ *addr = (caddr_t) base;
+ return h;
+}
+
+int
+fhandler_dev_mem::munmap (HANDLE h, caddr_t addr, size_t len)
+{
+ NTSTATUS ret;
+ if (!NT_SUCCESS (ret = NtUnmapViewOfSection (INVALID_HANDLE_VALUE, addr)))
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ return -1;
+ }
+ CloseHandle (h);
+ return 0;
+}
+
+int
+fhandler_dev_mem::msync (HANDLE h, caddr_t addr, size_t len, int flags)
+{
+ return 0;
+}
+
+BOOL
+fhandler_dev_mem::fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset,
+ DWORD size, void *address)
+{
+ DWORD ret;
+ PHYSICAL_ADDRESS phys;
+ void *base = address;
+ DWORD dlen = size;
+ ULONG protect;
+
+ if (access & FILE_MAP_COPY)
+ protect = PAGE_WRITECOPY;
+ else if (access & FILE_MAP_WRITE)
+ protect = PAGE_READWRITE;
+ else
+ protect = PAGE_READONLY;
+
+ phys.QuadPart = (ULONGLONG) offset;
+
+ if ((ret = NtMapViewOfSection (h,
+ INVALID_HANDLE_VALUE,
+ &base,
+ 0L,
+ dlen,
+ &phys,
+ &dlen,
+ ViewShare /*??*/,
+ 0,
+ protect)) != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ syscall_printf ("-1 = fixup_mmap_after_fork(): NtMapViewOfSection failed with %E");
+ return FALSE;
+ }
+ return base == address;
+}
+
+int
+fhandler_dev_mem::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ this->fhandler_base::fstat (buf, pc);
+ buf->st_mode = S_IFCHR;
+ if (wincap.has_physical_mem_access ())
+ buf->st_mode |= S_IRUSR | S_IWUSR |
+ S_IRGRP | S_IWGRP |
+ S_IROTH | S_IWOTH;
+ buf->st_blksize = getpagesize ();
+
+ return 0;
+}
+
+int
+fhandler_dev_mem::dup (fhandler_base *child)
+{
+ int ret = fhandler_base::dup (child);
+
+ if (! ret)
+ {
+ fhandler_dev_mem *fhc = (fhandler_dev_mem *) child;
+
+ fhc->mem_size = mem_size;
+ fhc->pos = pos;
+ }
+ return ret;
+}
+
+void
+fhandler_dev_mem::dump ()
+{
+ paranoid_printf ("here, fhandler_dev_mem");
+}
diff --git a/winsup/cygwin/fhandler_proc.cc b/winsup/cygwin/fhandler_proc.cc
new file mode 100644
index 00000000000..ce7c107a5e6
--- /dev/null
+++ b/winsup/cygwin/fhandler_proc.cc
@@ -0,0 +1,500 @@
+/* fhandler_proc.cc: fhandler for /proc virtual filesystem
+
+ Copyright 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <ntdef.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "pinfo.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include <assert.h>
+#include <sys/utsname.h>
+#include <sys/param.h>
+#include "ntdll.h"
+
+#define _COMPILING_NEWLIB
+#include <dirent.h>
+
+/* offsets in proc_listing */
+static const int PROC_LOADAVG = 2; // /proc/loadavg
+static const int PROC_MEMINFO = 3; // /proc/meminfo
+static const int PROC_REGISTRY = 4; // /proc/registry
+static const int PROC_STAT = 5; // /proc/stat
+static const int PROC_VERSION = 6; // /proc/version
+static const int PROC_UPTIME = 7; // /proc/uptime
+
+/* names of objects in /proc */
+static const char *proc_listing[] = {
+ ".",
+ "..",
+ "loadavg",
+ "meminfo",
+ "registry",
+ "stat",
+ "version",
+ "uptime",
+ NULL
+};
+
+static const int PROC_LINK_COUNT = (sizeof (proc_listing) / sizeof (const char *)) - 1;
+
+/* FH_PROC in the table below means the file/directory is handles by
+ * fhandler_proc.
+ */
+static const DWORD proc_fhandlers[PROC_LINK_COUNT] = {
+ FH_PROC,
+ FH_PROC,
+ FH_PROC,
+ FH_PROC,
+ FH_REGISTRY,
+ FH_PROC,
+ FH_PROC,
+ FH_PROC
+};
+
+/* name of the /proc filesystem */
+const char proc[] = "/proc";
+const int proc_len = sizeof (proc) - 1;
+
+static off_t format_proc_meminfo (char *destbuf, size_t maxsize);
+static off_t format_proc_stat (char *destbuf, size_t maxsize);
+static off_t format_proc_uptime (char *destbuf, size_t maxsize);
+
+/* auxillary function that returns the fhandler associated with the given path
+ * this is where it would be nice to have pattern matching in C - polymorphism
+ * just doesn't cut it
+ */
+DWORD
+fhandler_proc::get_proc_fhandler (const char *path)
+{
+ debug_printf ("get_proc_fhandler(%s)", path);
+ path += proc_len;
+ /* Since this method is called from path_conv::check we can't rely on
+ * it being normalised and therefore the path may have runs of slashes
+ * in it.
+ */
+ while (isdirsep (*path))
+ path++;
+
+ /* Check if this is the root of the virtual filesystem (i.e. /proc). */
+ if (*path == 0)
+ return FH_PROC;
+
+ for (int i = 0; proc_listing[i]; i++)
+ {
+ if (path_prefix_p (proc_listing[i], path, strlen (proc_listing[i])))
+ return proc_fhandlers[i];
+ }
+
+ if (pinfo (atoi (path)))
+ return FH_PROCESS;
+
+ bool has_subdir = false;
+ while (*path)
+ if (isdirsep (*path++))
+ {
+ has_subdir = true;
+ break;
+ }
+
+ if (has_subdir)
+ /* The user is trying to access a non-existent subdirectory of /proc. */
+ return FH_BAD;
+ else
+ /* Return FH_PROC so that we can return EROFS if the user is trying to create
+ a file. */
+ return FH_PROC;
+}
+
+/* Returns 0 if path doesn't exist, >0 if path is a directory,
+ * <0 if path is a file.
+ */
+int
+fhandler_proc::exists ()
+{
+ const char *path = get_name ();
+ debug_printf ("exists (%s)", path);
+ path += proc_len;
+ if (*path == 0)
+ return 2;
+ for (int i = 0; proc_listing[i]; i++)
+ if (pathmatch (path + 1, proc_listing[i]))
+ return (proc_fhandlers[i] == FH_PROC) ? -1 : 1;
+ return 0;
+}
+
+fhandler_proc::fhandler_proc ():
+ fhandler_virtual ()
+{
+}
+
+int
+fhandler_proc::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ const char *path = get_name ();
+ debug_printf ("fstat (%s)", path);
+
+ path += proc_len;
+ (void) fhandler_base::fstat (buf, pc);
+
+ buf->st_mode &= ~_IFMT & NO_W;
+
+ if (!*path)
+ {
+ buf->st_nlink = PROC_LINK_COUNT;
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ return 0;
+ }
+ else
+ {
+ path++;
+ for (int i = 0; proc_listing[i]; i++)
+ if (pathmatch (path, proc_listing[i]))
+ {
+ if (proc_fhandlers[i] != FH_PROC)
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ else
+ {
+ buf->st_mode &= NO_X;
+ buf->st_mode |= S_IFREG;
+ }
+ return 0;
+ }
+ }
+ set_errno (ENOENT);
+ return -1;
+}
+
+struct dirent *
+fhandler_proc::readdir (DIR * dir)
+{
+ if (dir->__d_position >= PROC_LINK_COUNT)
+ {
+ winpids pids;
+ int found = 0;
+ for (unsigned i = 0; i < pids.npids; i++)
+ if (found++ == dir->__d_position - PROC_LINK_COUNT)
+ {
+ __small_sprintf (dir->__d_dirent->d_name, "%d", pids[i]->pid);
+ dir->__d_position++;
+ return dir->__d_dirent;
+ }
+ set_errno (ENMFILE);
+ return NULL;
+ }
+
+ strcpy (dir->__d_dirent->d_name, proc_listing[dir->__d_position++]);
+ syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir,
+ dir->__d_dirent->d_name);
+ return dir->__d_dirent;
+}
+
+int
+fhandler_proc::open (path_conv *pc, int flags, mode_t mode)
+{
+ int proc_file_no = -1;
+
+ int res = fhandler_virtual::open (pc, flags, mode);
+ if (!res)
+ goto out;
+
+ set_nohandle (true);
+
+ const char *path;
+
+ path = get_name () + proc_len;
+
+ if (!*path)
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ flags |= O_DIROPEN;
+ goto success;
+ }
+ }
+
+ proc_file_no = -1;
+ for (int i = 0; proc_listing[i]; i++)
+ if (path_prefix_p (proc_listing[i], path + 1, strlen (proc_listing[i])))
+ {
+ proc_file_no = i;
+ if (proc_fhandlers[i] != FH_PROC)
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ flags |= O_DIROPEN;
+ goto success;
+ }
+ }
+ }
+
+ if (proc_file_no == -1)
+ {
+ if (flags & O_CREAT)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ set_errno (ENOENT);
+ res = 0;
+ goto out;
+ }
+ }
+ if (flags & O_WRONLY)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+
+ fileid = proc_file_no;
+ if (!fill_filebuf ())
+ {
+ res = 0;
+ goto out;
+ }
+
+ if (flags & O_APPEND)
+ position = filesize;
+ else
+ position = 0;
+
+success:
+ res = 1;
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+out:
+ syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode);
+ return res;
+}
+
+bool
+fhandler_proc::fill_filebuf ()
+{
+ switch (fileid)
+ {
+ case PROC_VERSION:
+ {
+ if (!filebuf)
+ {
+ struct utsname uts_name;
+ uname (&uts_name);
+ bufalloc = strlen (uts_name.sysname) + 1 + strlen (uts_name.release) +
+ 1 + strlen (uts_name.version) + 2;
+ filebuf = (char *) realloc (filebuf, bufalloc);
+ filesize = __small_sprintf (filebuf, "%s %s %s\n", uts_name.sysname,
+ uts_name.release, uts_name.version);
+ }
+ break;
+ }
+ case PROC_UPTIME:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 80);
+ filesize = format_proc_uptime (filebuf, bufalloc);
+ break;
+ }
+ case PROC_STAT:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 2048);
+ filesize = format_proc_stat (filebuf, bufalloc);
+ break;
+ }
+ case PROC_LOADAVG:
+ {
+ /*
+ * not really supported - Windows doesn't keep track of these values
+ * Windows 95/98/me does have the KERNEL/CPUUsage performance counter
+ * which is similar.
+ */
+ filebuf = (char *) realloc (filebuf, bufalloc = 16);
+ filesize = __small_sprintf (filebuf, "%u.%02u %u.%02u %u.%02u\n",
+ 0, 0, 0, 0, 0, 0);
+ break;
+ }
+ case PROC_MEMINFO:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 2048);
+ filesize = format_proc_meminfo (filebuf, bufalloc);
+ break;
+ }
+ }
+ return true;
+}
+
+static
+off_t
+format_proc_meminfo (char *destbuf, size_t maxsize)
+{
+ unsigned long mem_total = 0UL, mem_free = 0UL, swap_total = 0UL,
+ swap_free = 0UL;
+ MEMORYSTATUS memory_status;
+ GlobalMemoryStatus (&memory_status);
+ mem_total = memory_status.dwTotalPhys;
+ mem_free = memory_status.dwAvailPhys;
+ swap_total = memory_status.dwTotalPageFile;
+ swap_free = memory_status.dwAvailPageFile;
+ return __small_sprintf (destbuf, " total: used: free:\n"
+ "Mem: %10lu %10lu %10lu\n"
+ "Swap: %10lu %10lu %10lu\n"
+ "MemTotal: %10lu kB\n"
+ "MemFree: %10lu kB\n"
+ "MemShared: 0 kB\n"
+ "HighTotal: 0 kB\n"
+ "HighFree: 0 kB\n"
+ "LowTotal: %10lu kB\n"
+ "LowFree: %10lu kB\n"
+ "SwapTotal: %10lu kB\n"
+ "SwapFree: %10lu kB\n",
+ mem_total, mem_total - mem_free, mem_free,
+ swap_total, swap_total - swap_free, swap_free,
+ mem_total >> 10, mem_free >> 10,
+ mem_total >> 10, mem_free >> 10,
+ swap_total >> 10, swap_free >> 10);
+}
+
+static
+off_t
+format_proc_uptime (char *destbuf, size_t maxsize)
+{
+ unsigned long long uptime = 0ULL, idle_time = 0ULL;
+ SYSTEM_PROCESSOR_TIMES spt;
+
+ NTSTATUS ret = NtQuerySystemInformation (SystemProcessorTimes, (PVOID) &spt,
+ sizeof spt, NULL);
+ if (!ret && GetLastError () == ERROR_PROC_NOT_FOUND)
+ uptime = GetTickCount () / 10;
+ else if (ret != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ debug_printf("NtQuerySystemInformation: ret = %d, "
+ "Dos(ret) = %d",
+ ret, RtlNtStatusToDosError (ret));
+ return 0;
+ }
+ else
+ {
+ idle_time = spt.IdleTime.QuadPart / 100000ULL;
+ uptime = (spt.KernelTime.QuadPart +
+ spt.UserTime.QuadPart) / 100000ULL;
+ }
+
+ return __small_sprintf (destbuf, "%U.%02u %U.%02u\n",
+ uptime / 100, long (uptime % 100),
+ idle_time / 100, long (idle_time % 100));
+}
+
+static
+off_t
+format_proc_stat (char *destbuf, size_t maxsize)
+{
+ unsigned long long user_time = 0ULL, kernel_time = 0ULL, idle_time = 0ULL;
+ unsigned long pages_in = 0UL, pages_out = 0UL, interrupt_count = 0UL,
+ context_switches = 0UL, swap_in = 0UL, swap_out = 0UL;
+ time_t boot_time = 0;
+
+ if (wincap.is_winnt ())
+ {
+ NTSTATUS ret;
+ SYSTEM_PROCESSOR_TIMES spt;
+ SYSTEM_PERFORMANCE_INFORMATION spi;
+ SYSTEM_TIME_OF_DAY_INFORMATION stodi;
+ ret = NtQuerySystemInformation (SystemProcessorTimes,
+ (PVOID) &spt,
+ sizeof spt, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQuerySystemInformation (SystemPerformanceInformation,
+ (PVOID) &spi,
+ sizeof spi, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQuerySystemInformation (SystemTimeOfDayInformation,
+ (PVOID) &stodi,
+ sizeof stodi, NULL);
+ if (ret != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ debug_printf("NtQuerySystemInformation: ret = %d, "
+ "Dos(ret) = %d",
+ ret, RtlNtStatusToDosError (ret));
+ return 0;
+ }
+ kernel_time = (spt.KernelTime.QuadPart - spt.IdleTime.QuadPart) * HZ / 10000000ULL;
+ user_time = spt.UserTime.QuadPart * HZ / 10000000ULL;
+ idle_time = spt.IdleTime.QuadPart * HZ / 10000000ULL;
+ interrupt_count = spt.InterruptCount;
+ pages_in = spi.PagesRead;
+ pages_out = spi.PagefilePagesWritten + spi.MappedFilePagesWritten;
+ /*
+ * Note: there is no distinction made in this structure between pages
+ * read from the page file and pages read from mapped files, but there
+ * is such a distinction made when it comes to writing. Goodness knows
+ * why. The value of swap_in, then, will obviously be wrong but its our
+ * best guess.
+ */
+ swap_in = spi.PagesRead;
+ swap_out = spi.PagefilePagesWritten;
+ context_switches = spi.ContextSwitches;
+ boot_time = to_time_t ((FILETIME *) &stodi.BootTime.QuadPart);
+ }
+ /*
+ * else
+ * {
+ * There are only two relevant performance counters on Windows 95/98/me,
+ * VMM/cPageIns and VMM/cPageOuts. The extra effort needed to read these
+ * counters is by no means worth it.
+ * }
+ */
+ return __small_sprintf (destbuf, "cpu %U %U %U %U\n"
+ "page %u %u\n"
+ "swap %u %u\n"
+ "intr %u\n"
+ "ctxt %u\n"
+ "btime %u\n",
+ user_time, 0ULL,
+ kernel_time, idle_time,
+ pages_in, pages_out,
+ swap_in, swap_out,
+ interrupt_count,
+ context_switches,
+ boot_time);
+}
diff --git a/winsup/cygwin/fhandler_process.cc b/winsup/cygwin/fhandler_process.cc
new file mode 100644
index 00000000000..054c9e373cd
--- /dev/null
+++ b/winsup/cygwin/fhandler_process.cc
@@ -0,0 +1,751 @@
+/* fhandler_process.cc: fhandler for /proc/<pid> virtual filesystem
+
+ Copyright 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include <ntdef.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "pinfo.h"
+#include "path.h"
+#include "shared_info.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+#include <sys/param.h>
+#include <assert.h>
+#include <sys/sysmacros.h>
+
+#define _COMPILING_NEWLIB
+#include <dirent.h>
+
+static const int PROCESS_PPID = 2;
+static const int PROCESS_EXENAME = 3;
+static const int PROCESS_WINPID = 4;
+static const int PROCESS_WINEXENAME = 5;
+static const int PROCESS_STATUS = 6;
+static const int PROCESS_UID = 7;
+static const int PROCESS_GID = 8;
+static const int PROCESS_PGID = 9;
+static const int PROCESS_SID = 10;
+static const int PROCESS_CTTY = 11;
+static const int PROCESS_STAT = 12;
+static const int PROCESS_STATM = 13;
+static const int PROCESS_CMDLINE = 14;
+
+static const char * const process_listing[] =
+{
+ ".",
+ "..",
+ "ppid",
+ "exename",
+ "winpid",
+ "winexename",
+ "status",
+ "uid",
+ "gid",
+ "pgid",
+ "sid",
+ "ctty",
+ "stat",
+ "statm",
+ "cmdline",
+ NULL
+};
+
+static const int PROCESS_LINK_COUNT =
+ (sizeof (process_listing) / sizeof (const char *)) - 1;
+
+static off_t format_process_stat (_pinfo *p, char *destbuf, size_t maxsize);
+static off_t format_process_status (_pinfo *p, char *destbuf, size_t maxsize);
+static off_t format_process_statm (_pinfo *p, char *destbuf, size_t maxsize);
+static int get_process_state (DWORD dwProcessId);
+static bool get_mem_values (DWORD dwProcessId, unsigned long *vmsize,
+ unsigned long *vmrss, unsigned long *vmtext,
+ unsigned long *vmdata, unsigned long *vmlib,
+ unsigned long *vmshare);
+
+/* Returns 0 if path doesn't exist, >0 if path is a directory,
+ * <0 if path is a file.
+ */
+int
+fhandler_process::exists ()
+{
+ const char *path = get_name ();
+ debug_printf ("exists (%s)", path);
+ path += proc_len + 1;
+ while (*path != 0 && !isdirsep (*path))
+ path++;
+ if (*path == 0)
+ return 2;
+
+ for (int i = 0; process_listing[i]; i++)
+ if (pathmatch (path + 1, process_listing[i]))
+ return -1;
+ return 0;
+}
+
+fhandler_process::fhandler_process ():
+ fhandler_proc ()
+{
+}
+
+int
+fhandler_process::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ const char *path = get_name ();
+ int file_type = exists ();
+ (void) fhandler_base::fstat (buf, pc);
+ path += proc_len + 1;
+ pid = atoi (path);
+ pinfo p (pid);
+ if (!p)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ buf->st_mode &= ~_IFMT & NO_W;
+
+ switch (file_type)
+ {
+ case 0:
+ set_errno (ENOENT);
+ return -1;
+ case 1:
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ return 0;
+ case 2:
+ buf->st_ctime = buf->st_mtime = p->start_time;
+ buf->st_ctim.tv_nsec = buf->st_mtim.tv_nsec = 0;
+ time_as_timestruc_t (&buf->st_atim);
+ buf->st_uid = p->uid;
+ buf->st_gid = p->gid;
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ buf->st_nlink = PROCESS_LINK_COUNT;
+ return 0;
+ default:
+ case -1:
+ buf->st_uid = p->uid;
+ buf->st_gid = p->gid;
+ buf->st_mode |= S_IFREG | S_IRUSR | S_IRGRP | S_IROTH;
+ return 0;
+ }
+}
+
+struct dirent *
+fhandler_process::readdir (DIR * dir)
+{
+ if (dir->__d_position >= PROCESS_LINK_COUNT)
+ {
+ set_errno (ENMFILE);
+ return NULL;
+ }
+ strcpy (dir->__d_dirent->d_name, process_listing[dir->__d_position++]);
+ syscall_printf ("%p = readdir (%p) (%s)", &dir->__d_dirent, dir,
+ dir->__d_dirent->d_name);
+ return dir->__d_dirent;
+}
+
+int
+fhandler_process::open (path_conv *pc, int flags, mode_t mode)
+{
+ int process_file_no = -1;
+
+ int res = fhandler_virtual::open (pc, flags, mode);
+ if (!res)
+ goto out;
+
+ set_nohandle (true);
+
+ const char *path;
+ path = get_name () + proc_len + 1;
+ pid = atoi (path);
+ while (*path != 0 && !isdirsep (*path))
+ path++;
+
+ if (*path == 0)
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ flags |= O_DIROPEN;
+ goto success;
+ }
+ }
+
+ process_file_no = -1;
+ for (int i = 0; process_listing[i]; i++)
+ {
+ if (path_prefix_p
+ (process_listing[i], path + 1, strlen (process_listing[i])))
+ process_file_no = i;
+ }
+ if (process_file_no == -1)
+ {
+ if (flags & O_CREAT)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ set_errno (ENOENT);
+ res = 0;
+ goto out;
+ }
+ }
+ if (flags & O_WRONLY)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+
+ fileid = process_file_no;
+ if (!fill_filebuf ())
+ {
+ res = 0;
+ goto out;
+ }
+
+ if (flags & O_APPEND)
+ position = filesize;
+ else
+ position = 0;
+
+success:
+ res = 1;
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+out:
+ syscall_printf ("%d = fhandler_proc::open (%p, %d)", res, flags, mode);
+ return res;
+}
+
+bool
+fhandler_process::fill_filebuf ()
+{
+ pinfo p (pid);
+
+ if (!p)
+ {
+ set_errno (ENOENT);
+ return false;
+ }
+
+ switch (fileid)
+ {
+ case PROCESS_UID:
+ case PROCESS_GID:
+ case PROCESS_PGID:
+ case PROCESS_SID:
+ case PROCESS_CTTY:
+ case PROCESS_PPID:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 40);
+ int num;
+ switch (fileid)
+ {
+ case PROCESS_PPID:
+ num = p->ppid;
+ break;
+ case PROCESS_UID:
+ num = p->uid;
+ break;
+ case PROCESS_PGID:
+ num = p->pgid;
+ break;
+ case PROCESS_SID:
+ num = p->sid;
+ break;
+ case PROCESS_GID:
+ num = p->gid;
+ break;
+ case PROCESS_CTTY:
+ num = p->ctty;
+ break;
+ default: // what's this here for?
+ num = 0;
+ break;
+ }
+ __small_sprintf (filebuf, "%d\n", num);
+ filesize = strlen (filebuf);
+ break;
+ }
+ case PROCESS_CMDLINE:
+ {
+ if (filebuf)
+ free (filebuf);
+ filebuf = p->cmdline (filesize);
+ if (!filebuf || !*filebuf)
+ filebuf = strdup ("<defunct>");
+ break;
+ }
+ case PROCESS_EXENAME:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = MAX_PATH);
+ if (p->process_state & (PID_ZOMBIE | PID_EXITED))
+ strcpy (filebuf, "<defunct>");
+ else
+ {
+ mount_table->conv_to_posix_path (p->progname, filebuf, 1);
+ int len = strlen (filebuf);
+ if (len > 4)
+ {
+ char *s = filebuf + len - 4;
+ if (strcasecmp (s, ".exe") == 0)
+ *s = 0;
+ }
+ }
+ filesize = strlen (filebuf);
+ break;
+ }
+ case PROCESS_WINPID:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 40);
+ __small_sprintf (filebuf, "%d\n", p->dwProcessId);
+ filesize = strlen (filebuf);
+ break;
+ }
+ case PROCESS_WINEXENAME:
+ {
+ int len = strlen (p->progname);
+ filebuf = (char *) realloc (filebuf, bufalloc = (len + 2));
+ strcpy (filebuf, p->progname);
+ filebuf[len] = '\n';
+ filesize = len + 1;
+ break;
+ }
+ case PROCESS_STATUS:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 2048);
+ filesize = format_process_status (*p, filebuf, bufalloc);
+ break;
+ }
+ case PROCESS_STAT:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 2048);
+ filesize = format_process_stat (*p, filebuf, bufalloc);
+ break;
+ }
+ case PROCESS_STATM:
+ {
+ filebuf = (char *) realloc (filebuf, bufalloc = 2048);
+ filesize = format_process_statm (*p, filebuf, bufalloc);
+ break;
+ }
+ }
+
+ return true;
+}
+
+static
+off_t
+format_process_stat (_pinfo *p, char *destbuf, size_t maxsize)
+{
+ char cmd[MAX_PATH];
+ int state = 'R';
+ unsigned long fault_count = 0UL,
+ utime = 0UL, stime = 0UL,
+ start_time = 0UL,
+ vmsize = 0UL, vmrss = 0UL, vmmaxrss = 0UL;
+ int priority = 0;
+ if (p->process_state & (PID_ZOMBIE | PID_EXITED))
+ strcpy (cmd, "<defunct>");
+ else
+ {
+ strcpy (cmd, p->progname);
+ char *last_slash = strrchr (cmd, '\\');
+ if (last_slash != NULL)
+ strcpy (cmd, last_slash + 1);
+ int len = strlen (cmd);
+ if (len > 4)
+ {
+ char *s = cmd + len - 4;
+ if (strcasecmp (s, ".exe") == 0)
+ *s = 0;
+ }
+ }
+ /*
+ * Note: under Windows, a _process_ is always running - it's only _threads_
+ * that get suspended. Therefore the default state is R (runnable).
+ */
+ if (p->process_state & PID_ZOMBIE)
+ state = 'Z';
+ else if (p->process_state & PID_STOPPED)
+ state = 'T';
+ else if (wincap.is_winnt ())
+ state = get_process_state (p->dwProcessId);
+ if (wincap.is_winnt ())
+ {
+ NTSTATUS ret;
+ HANDLE hProcess;
+ VM_COUNTERS vmc;
+ KERNEL_USER_TIMES put;
+ PROCESS_BASIC_INFORMATION pbi;
+ QUOTA_LIMITS ql;
+ SYSTEM_TIME_OF_DAY_INFORMATION stodi;
+ SYSTEM_PROCESSOR_TIMES spt;
+ hProcess = OpenProcess (PROCESS_VM_READ | PROCESS_QUERY_INFORMATION,
+ FALSE, p->dwProcessId);
+ if (hProcess != NULL)
+ {
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessVmCounters,
+ (PVOID) &vmc,
+ sizeof vmc, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessTimes,
+ (PVOID) &put,
+ sizeof put, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessBasicInformation,
+ (PVOID) &pbi,
+ sizeof pbi, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessQuotaLimits,
+ (PVOID) &ql,
+ sizeof ql, NULL);
+ CloseHandle (hProcess);
+ }
+ else
+ {
+ DWORD error = GetLastError ();
+ __seterrno_from_win_error (error);
+ debug_printf ("OpenProcess: ret = %d",
+ error);
+ return 0;
+ }
+ if (ret == STATUS_SUCCESS)
+ ret = NtQuerySystemInformation (SystemTimeOfDayInformation,
+ (PVOID) &stodi,
+ sizeof stodi, NULL);
+ if (ret == STATUS_SUCCESS)
+ ret = NtQuerySystemInformation (SystemProcessorTimes,
+ (PVOID) &spt,
+ sizeof spt, NULL);
+ if (ret != STATUS_SUCCESS)
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (ret));
+ debug_printf ("NtQueryInformationProcess: ret = %d, "
+ "Dos(ret) = %d",
+ ret, RtlNtStatusToDosError (ret));
+ return 0;
+ }
+ fault_count = vmc.PageFaultCount;
+ utime = put.UserTime.QuadPart * HZ / 10000000ULL;
+ stime = put.KernelTime.QuadPart * HZ / 10000000ULL;
+ if (stodi.CurrentTime.QuadPart > put.CreateTime.QuadPart)
+ start_time = (spt.KernelTime.QuadPart + spt.UserTime.QuadPart -
+ stodi.CurrentTime.QuadPart + put.CreateTime.QuadPart) * HZ / 10000000ULL;
+ else
+ /*
+ * sometimes stodi.CurrentTime is a bit behind
+ * Note: some older versions of procps are broken and can't cope
+ * with process start times > time(NULL).
+ */
+ start_time = (spt.KernelTime.QuadPart + spt.UserTime.QuadPart) * HZ / 10000000ULL;
+ priority = pbi.BasePriority;
+ unsigned page_size = getpagesize ();
+ vmsize = vmc.VirtualSize;
+ vmrss = vmc.WorkingSetSize / page_size;
+ vmmaxrss = ql.MaximumWorkingSetSize / page_size;
+ }
+ else
+ {
+ start_time = (GetTickCount () / 1000 - time (NULL) + p->start_time) * HZ;
+ }
+ return __small_sprintf (destbuf, "%d (%s) %c "
+ "%d %d %d %d %d "
+ "%lu %lu %lu %lu %lu %lu %lu "
+ "%ld %ld %ld %ld %ld %ld "
+ "%lu %lu "
+ "%ld "
+ "%lu",
+ p->pid, cmd,
+ state,
+ p->ppid, p->pgid, p->sid, makedev (FH_TTYS, p->ctty),
+ -1, 0, fault_count, fault_count, 0, 0, utime, stime,
+ utime, stime, priority, 0, 0, 0,
+ start_time, vmsize,
+ vmrss, vmmaxrss
+ );
+}
+
+static
+off_t
+format_process_status (_pinfo *p, char *destbuf, size_t maxsize)
+{
+ char cmd[MAX_PATH];
+ int state = 'R';
+ const char *state_str = "unknown";
+ unsigned long vmsize = 0UL, vmrss = 0UL, vmdata = 0UL, vmlib = 0UL, vmtext = 0UL,
+ vmshare = 0UL;
+ if (p->process_state & (PID_ZOMBIE | PID_EXITED))
+ strcpy (cmd, "<defunct>");
+ else
+ {
+ strcpy (cmd, p->progname);
+ char *last_slash = strrchr (cmd, '\\');
+ if (last_slash != NULL)
+ strcpy (cmd, last_slash + 1);
+ int len = strlen (cmd);
+ if (len > 4)
+ {
+ char *s = cmd + len - 4;
+ if (strcasecmp (s, ".exe") == 0)
+ *s = 0;
+ }
+ }
+ /*
+ * Note: under Windows, a _process_ is always running - it's only _threads_
+ * that get suspended. Therefore the default state is R (runnable).
+ */
+ if (p->process_state & PID_ZOMBIE)
+ state = 'Z';
+ else if (p->process_state & PID_STOPPED)
+ state = 'T';
+ else if (wincap.is_winnt ())
+ state = get_process_state (p->dwProcessId);
+ switch (state)
+ {
+ case 'O':
+ state_str = "running";
+ break;
+ case 'D':
+ case 'S':
+ state_str = "sleeping";
+ break;
+ case 'R':
+ state_str = "runnable";
+ break;
+ case 'Z':
+ state_str = "zombie";
+ break;
+ case 'T':
+ state_str = "stopped";
+ break;
+ }
+ if (wincap.is_winnt ())
+ {
+ if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata, &vmlib, &vmshare))
+ return 0;
+ unsigned page_size = getpagesize ();
+ vmsize *= page_size; vmrss *= page_size; vmdata *= page_size;
+ vmtext *= page_size; vmlib *= page_size;
+ }
+ // The real uid value for *this* process is stored at cygheap->user.real_uid
+ // but we can't get at the real uid value for any other process, so
+ // just fake it as p->uid. Similar for p->gid.
+ return __small_sprintf (destbuf, "Name: %s\n"
+ "State: %c (%s)\n"
+ "Tgid: %d\n"
+ "Pid: %d\n"
+ "PPid: %d\n"
+ "Uid: %d %d %d %d\n"
+ "Gid: %d %d %d %d\n"
+ "VmSize: %8d kB\n"
+ "VmLck: %8d kB\n"
+ "VmRSS: %8d kB\n"
+ "VmData: %8d kB\n"
+ "VmStk: %8d kB\n"
+ "VmExe: %8d kB\n"
+ "VmLib: %8d kB\n"
+ "SigPnd: %016x\n"
+ "SigBlk: %016x\n"
+ "SigIgn: %016x\n",
+ cmd,
+ state, state_str,
+ p->pgid,
+ p->pid,
+ p->ppid,
+ p->uid, p->uid, p->uid, p->uid,
+ p->gid, p->gid, p->gid, p->gid,
+ vmsize >> 10, 0, vmrss >> 10, vmdata >> 10, 0, vmtext >> 10, vmlib >> 10,
+ 0, 0, p->getsigmask ()
+ );
+}
+
+static
+off_t
+format_process_statm (_pinfo *p, char *destbuf, size_t maxsize)
+{
+ unsigned long vmsize = 0UL, vmrss = 0UL, vmtext = 0UL, vmdata = 0UL,
+ vmlib = 0UL, vmshare = 0UL;
+ if (wincap.is_winnt ())
+ {
+ if (!get_mem_values (p->dwProcessId, &vmsize, &vmrss, &vmtext, &vmdata,
+ &vmlib, &vmshare))
+ return 0;
+ }
+ return __small_sprintf (destbuf, "%ld %ld %ld %ld %ld %ld %ld",
+ vmsize, vmrss, vmshare, vmtext, vmlib, vmdata, 0
+ );
+}
+
+static
+int
+get_process_state (DWORD dwProcessId)
+{
+ /*
+ * This isn't really heavy magic - just go through the processes'
+ * threads one by one and return a value accordingly
+ * Errors are silently ignored.
+ */
+ NTSTATUS ret;
+ SYSTEM_PROCESSES *sp;
+ ULONG n = 0x1000;
+ PULONG p = new ULONG[n];
+ int state =' ';
+ while (STATUS_INFO_LENGTH_MISMATCH ==
+ (ret = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
+ (PVOID) p,
+ n * sizeof *p, NULL)))
+ delete [] p, p = new ULONG[n *= 2];
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("NtQuerySystemInformation: ret = %d, "
+ "Dos(ret) = %d",
+ ret, RtlNtStatusToDosError (ret));
+ goto out;
+ }
+ state = 'Z';
+ sp = (SYSTEM_PROCESSES *) p;
+ for (;;)
+ {
+ if (sp->ProcessId == dwProcessId)
+ {
+ SYSTEM_THREADS *st;
+ if (wincap.has_process_io_counters ())
+ /*
+ * Windows 2000 and XP have an extra member in SYSTEM_PROCESSES
+ * which means the offset of the first SYSTEM_THREADS entry is
+ * different on these operating systems compared to NT 4.
+ */
+ st = &sp->Threads[0];
+ else
+ /*
+ * 136 is the offset of the first SYSTEM_THREADS entry on
+ * Windows NT 4.
+ */
+ st = (SYSTEM_THREADS *) ((char *) sp + 136);
+ state = 'S';
+ for (unsigned i = 0; i < sp->ThreadCount; i++)
+ {
+ if (st->State == StateRunning ||
+ st->State == StateReady)
+ {
+ state = 'R';
+ goto out;
+ }
+ st++;
+ }
+ break;
+ }
+ if (!sp->NextEntryDelta)
+ break;
+ sp = (SYSTEM_PROCESSES *) ((char *) sp + sp->NextEntryDelta);
+ }
+out:
+ delete [] p;
+ return state;
+}
+
+static
+bool
+get_mem_values (DWORD dwProcessId, unsigned long *vmsize, unsigned long *vmrss,
+ unsigned long *vmtext, unsigned long *vmdata,
+ unsigned long *vmlib, unsigned long *vmshare)
+{
+ bool res = true;
+ NTSTATUS ret;
+ HANDLE hProcess;
+ VM_COUNTERS vmc;
+ MEMORY_WORKING_SET_LIST *mwsl;
+ ULONG n = 0x1000, length;
+ PULONG p = new ULONG[n];
+ unsigned page_size = getpagesize ();
+ hProcess = OpenProcess (PROCESS_QUERY_INFORMATION,
+ FALSE, dwProcessId);
+ if (hProcess == NULL)
+ {
+ DWORD error = GetLastError ();
+ __seterrno_from_win_error (error);
+ debug_printf ("OpenProcess: ret = %d",
+ error);
+ return false;
+ }
+ while ((ret = NtQueryVirtualMemory (hProcess, 0,
+ MemoryWorkingSetList,
+ (PVOID) p,
+ n * sizeof *p, &length)),
+ (ret == STATUS_SUCCESS || ret == STATUS_INFO_LENGTH_MISMATCH) &&
+ length >= n * sizeof *p)
+ delete [] p, p = new ULONG[n *= 2];
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("NtQueryVirtualMemory: ret = %d, "
+ "Dos(ret) = %d",
+ ret, RtlNtStatusToDosError (ret));
+ res = false;
+ goto out;
+ }
+ mwsl = (MEMORY_WORKING_SET_LIST *) p;
+ for (unsigned long i = 0; i < mwsl->NumberOfPages; i++)
+ {
+ ++*vmrss;
+ unsigned flags = mwsl->WorkingSetList[i] & 0x0FFF;
+ if (flags & (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE) == (WSLE_PAGE_EXECUTE | WSLE_PAGE_SHAREABLE))
+ ++*vmlib;
+ else if (flags & WSLE_PAGE_SHAREABLE)
+ ++*vmshare;
+ else if (flags & WSLE_PAGE_EXECUTE)
+ ++*vmtext;
+ else
+ ++*vmdata;
+ }
+ ret = NtQueryInformationProcess (hProcess,
+ ProcessVmCounters,
+ (PVOID) &vmc,
+ sizeof vmc, NULL);
+ if (ret != STATUS_SUCCESS)
+ {
+ debug_printf ("NtQueryInformationProcess: ret = %d, "
+ "Dos(ret) = %d",
+ ret, RtlNtStatusToDosError (ret));
+ res = false;
+ goto out;
+ }
+ *vmsize = vmc.VirtualSize / page_size;
+out:
+ delete [] p;
+ CloseHandle (hProcess);
+ return res;
+}
diff --git a/winsup/cygwin/fhandler_random.cc b/winsup/cygwin/fhandler_random.cc
new file mode 100644
index 00000000000..e1faed4bb62
--- /dev/null
+++ b/winsup/cygwin/fhandler_random.cc
@@ -0,0 +1,171 @@
+/* fhandler_random.cc: code to access /dev/random and /dev/urandom
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by Corinna Vinschen (vinschen@cygnus.com)
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <limits.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+
+#define RANDOM 8
+#define URANDOM 9
+
+#define PSEUDO_MULTIPLIER (6364136223846793005LL)
+#define PSEUDO_SHIFTVAL (21)
+
+fhandler_dev_random::fhandler_dev_random ()
+ : fhandler_base (), crypt_prov ((HCRYPTPROV) NULL)
+{
+}
+
+int
+fhandler_dev_random::open (path_conv *, int flags, mode_t)
+{
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_nohandle (true);
+ set_open_status ();
+ return 1;
+}
+
+BOOL
+fhandler_dev_random::crypt_gen_random (void *ptr, size_t len)
+{
+ if (!crypt_prov
+ && !CryptAcquireContext (&crypt_prov, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET)
+ && !CryptAcquireContext (&crypt_prov, NULL, MS_DEF_PROV, PROV_RSA_FULL,
+ CRYPT_VERIFYCONTEXT | CRYPT_MACHINE_KEYSET
+ | CRYPT_NEWKEYSET))
+ {
+ debug_printf ("%E = CryptAquireContext()");
+ return FALSE;
+ }
+ if (!CryptGenRandom (crypt_prov, len, (BYTE *)ptr))
+ {
+ debug_printf ("%E = CryptGenRandom()");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+fhandler_dev_random::pseudo_write (const void *ptr, size_t len)
+{
+ /* Use buffer to mess up the pseudo random number generator. */
+ for (size_t i = 0; i < len; ++i)
+ pseudo = (pseudo + ((unsigned char *)ptr)[i]) * PSEUDO_MULTIPLIER + 1;
+ return len;
+}
+
+int
+fhandler_dev_random::write (const void *ptr, size_t len)
+{
+ if (!len)
+ return 0;
+ if (!ptr)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Limit len to a value <= 512 since we don't want to overact.
+ Copy to local buffer because CryptGenRandom violates const. */
+ unsigned char buf[512];
+ size_t limited_len = len <= 512 ? len : 512;
+ memcpy (buf, ptr, limited_len);
+
+ /* Mess up system entropy source. Return error if device is /dev/random. */
+ if (!crypt_gen_random (buf, limited_len) && dev == FH_RANDOM)
+ {
+ __seterrno ();
+ return -1;
+ }
+ /* Mess up the pseudo random number generator. */
+ pseudo_write (buf, limited_len);
+ return len;
+}
+
+int
+fhandler_dev_random::pseudo_read (void *ptr, size_t len)
+{
+ /* Use pseudo random number generator as fallback entropy source.
+ This multiplier was obtained from Knuth, D.E., "The Art of
+ Computer Programming," Vol 2, Seminumerical Algorithms, Third
+ Edition, Addison-Wesley, 1998, p. 106 (line 26) & p. 108 */
+ for (size_t i = 0; i < len; ++i)
+ {
+ pseudo = pseudo * PSEUDO_MULTIPLIER + 1;
+ ((unsigned char *)ptr)[i] = (pseudo >> PSEUDO_SHIFTVAL) & UCHAR_MAX;
+ }
+ return len;
+}
+
+void __stdcall
+fhandler_dev_random::read (void *ptr, size_t& len)
+{
+ if (!len)
+ return;
+
+ if (!ptr)
+ {
+ set_errno (EINVAL);
+ (ssize_t) len = -1;
+ return;
+ }
+
+ if (crypt_gen_random (ptr, len))
+ return;
+
+ /* If device is /dev/urandom, use pseudo number generator as fallback.
+ Don't do this for /dev/random since it's intended for uses that need
+ very high quality randomness. */
+ if (dev == FH_URANDOM)
+ {
+ len = pseudo_read (ptr, len);
+ return;
+ }
+
+ __seterrno ();
+ (ssize_t) len = -1;
+}
+
+__off64_t
+fhandler_dev_random::lseek (__off64_t, int)
+{
+ return 0;
+}
+
+int
+fhandler_dev_random::close (void)
+{
+ if (crypt_prov)
+ while (!CryptReleaseContext (crypt_prov, 0)
+ && GetLastError () == ERROR_BUSY)
+ Sleep (10);
+ return 0;
+}
+
+int
+fhandler_dev_random::dup (fhandler_base *child)
+{
+ fhandler_dev_random *fhr = (fhandler_dev_random *) child;
+ fhr->crypt_prov = (HCRYPTPROV)NULL;
+ return 0;
+}
+
+void
+fhandler_dev_random::dump ()
+{
+ paranoid_printf ("here, fhandler_dev_random");
+}
+
diff --git a/winsup/cygwin/fhandler_raw.cc b/winsup/cygwin/fhandler_raw.cc
new file mode 100644
index 00000000000..5e566a1aa35
--- /dev/null
+++ b/winsup/cygwin/fhandler_raw.cc
@@ -0,0 +1,568 @@
+/* fhandler_raw.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+ This file is part of Cygwin.
+
+ This software is a copyrighted work licensed under the terms of the
+ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+ details. */
+
+#include "winsup.h"
+#include <sys/termios.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include <cygwin/rdevio.h>
+#include <sys/mtio.h>
+#include <ntdef.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "ntdll.h"
+
+/* static wrapper functions to hide the effect of media changes and
+ bus resets which occurs after a new media is inserted. This is
+ also related to the used tape device. */
+
+static BOOL write_file (HANDLE fh, const void *buf, DWORD to_write,
+ DWORD *written, int *err)
+{
+ BOOL ret;
+
+ *err = 0;
+ if (!(ret = WriteFile (fh, buf, to_write, written, 0)))
+ {
+ if ((*err = GetLastError ()) == ERROR_MEDIA_CHANGED
+ || *err == ERROR_BUS_RESET)
+ {
+ *err = 0;
+ if (!(ret = WriteFile (fh, buf, to_write, written, 0)))
+ *err = GetLastError ();
+ }
+ }
+ syscall_printf ("%d (err %d) = WriteFile (%d, %d, write %d, written %d, 0)",
+ ret, *err, fh, buf, to_write, *written);
+ return ret;
+}
+
+static BOOL read_file (HANDLE fh, void *buf, DWORD to_read,
+ DWORD *read, int *err)
+{
+ BOOL ret;
+
+ *err = 0;
+ if (!(ret = ReadFile (fh, buf, to_read, read, 0)))
+ {
+ if ((*err = GetLastError ()) == ERROR_MEDIA_CHANGED
+ || *err == ERROR_BUS_RESET)
+ {
+ *err = 0;
+ if (!(ret = ReadFile (fh, buf, to_read, read, 0)))
+ *err = GetLastError ();
+ }
+ }
+ syscall_printf ("%d (err %d) = ReadFile (%d, %d, to_read %d, read %d, 0)",
+ ret, *err, fh, buf, to_read, *read);
+ return ret;
+}
+
+/**********************************************************************/
+/* fhandler_dev_raw */
+
+void
+fhandler_dev_raw::clear (void)
+{
+ devbuf = NULL;
+ devbufsiz = 0;
+ devbufstart = 0;
+ devbufend = 0;
+ eom_detected = 0;
+ eof_detected = 0;
+ lastblk_to_read = 0;
+ varblkop = 0;
+}
+
+int
+fhandler_dev_raw::writebuf (void)
+{
+ DWORD written;
+ int ret = 0;
+
+ if (is_writing && devbuf && devbufend)
+ {
+ DWORD to_write;
+ int ret = 0;
+
+ memset (devbuf + devbufend, 0, devbufsiz - devbufend);
+ if (get_device () != FH_TAPE)
+ to_write = ((devbufend - 1) / 512 + 1) * 512;
+ else if (varblkop)
+ to_write = devbufend;
+ else
+ to_write = devbufsiz;
+ if (!write_file (get_handle (), devbuf, to_write, &written, &ret)
+ && is_eom (ret))
+ eom_detected = 1;
+ if (written)
+ has_written = 1;
+ devbufstart = devbufend = 0;
+ }
+ is_writing = 0;
+ return ret;
+}
+
+fhandler_dev_raw::fhandler_dev_raw ()
+ : fhandler_base ()
+{
+ clear ();
+ set_need_fork_fixup ();
+}
+
+fhandler_dev_raw::~fhandler_dev_raw (void)
+{
+ if (devbufsiz > 1L)
+ delete [] devbuf;
+ clear ();
+}
+
+int
+fhandler_dev_raw::open (path_conv *real_path, int flags, mode_t)
+{
+ if (!wincap.has_raw_devices ())
+ {
+ set_errno (ENOENT);
+ debug_printf ("%s is accessible under NT/W2K only",real_path->get_win32());
+ return 0;
+ }
+
+ /* Check for illegal flags. */
+ if (flags & (O_APPEND | O_EXCL))
+ {
+ set_errno (EINVAL);
+ return 0;
+ }
+
+ /* Always open a raw device existing and binary. */
+ flags &= ~(O_CREAT | O_TRUNC);
+ flags |= O_BINARY;
+
+ DWORD access = GENERIC_READ | SYNCHRONIZE;
+ if (get_device () == FH_TAPE
+ || (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_WRONLY
+ || (flags & (O_RDONLY | O_WRONLY | O_RDWR)) == O_RDWR)
+ access |= GENERIC_WRITE;
+
+ extern void str2buf2uni (UNICODE_STRING &, WCHAR *, const char *);
+ UNICODE_STRING dev;
+ WCHAR devname[MAX_PATH + 1];
+ str2buf2uni (dev, devname, real_path->get_win32 ());
+ OBJECT_ATTRIBUTES attr;
+ InitializeObjectAttributes (&attr, &dev, OBJ_CASE_INSENSITIVE, NULL, NULL);
+
+ HANDLE h;
+ IO_STATUS_BLOCK io;
+ NTSTATUS status = NtOpenFile (&h, access, &attr, &io, wincap.shared (),
+ FILE_SYNCHRONOUS_IO_NONALERT);
+ if (!NT_SUCCESS (status))
+ {
+ __seterrno_from_win_error (RtlNtStatusToDosError (status));
+ return 0;
+ }
+
+ set_io_handle (h);
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+
+ if (devbufsiz > 1L)
+ devbuf = new char [devbufsiz];
+
+ return 1;
+}
+
+int
+fhandler_dev_raw::close (void)
+{
+ return fhandler_base::close ();
+}
+
+void
+fhandler_dev_raw::raw_read (void *ptr, size_t& ulen)
+{
+ DWORD bytes_read = 0;
+ DWORD read2;
+ DWORD bytes_to_read;
+ int ret;
+ size_t len = ulen;
+ char *tgt;
+
+ /* In mode O_RDWR the buffer has to be written to device first */
+ ret = writebuf ();
+ if (ret)
+ {
+ set_errno (is_eom (ret) ? ENOSPC : EACCES);
+ goto err;
+ }
+
+ /* Checking a previous end of file */
+ if (eof_detected && !lastblk_to_read)
+ {
+ eof_detected = 0;
+ ulen = 0;
+ return;
+ }
+
+ /* Checking a previous end of media */
+ if (eom_detected && !lastblk_to_read)
+ {
+ set_errno (ENOSPC);
+ goto err;
+ }
+
+ if (devbuf)
+ {
+ while (len > 0)
+ {
+ if (devbufstart < devbufend)
+ {
+ bytes_to_read = min (len, devbufend - devbufstart);
+ debug_printf ("read %d bytes from buffer (rest %d)",
+ bytes_to_read, devbufstart - devbufend);
+ memcpy (ptr, devbuf + devbufstart, bytes_to_read);
+ len -= bytes_to_read;
+ ptr = (void *) ((char *) ptr + bytes_to_read);
+ bytes_read += bytes_to_read;
+ devbufstart += bytes_to_read;
+
+ if (lastblk_to_read)
+ {
+ lastblk_to_read = 0;
+ break;
+ }
+ }
+ if (len > 0)
+ {
+ if (!varblkop && len >= devbufsiz)
+ {
+ if (get_device () == FH_TAPE)
+ bytes_to_read = (len / devbufsiz) * devbufsiz;
+ else
+ bytes_to_read = (len / 512) * 512;
+ tgt = (char *) ptr;
+ debug_printf ("read %d bytes direct from file",bytes_to_read);
+ }
+ else
+ {
+ bytes_to_read = devbufsiz;
+ tgt = devbuf;
+ if (varblkop)
+ debug_printf ("read variable bytes from file into buffer");
+ else
+ debug_printf ("read %d bytes from file into buffer",
+ bytes_to_read);
+ }
+ if (!read_file (get_handle (), tgt, bytes_to_read, &read2, &ret))
+ {
+ if (!is_eof (ret) && !is_eom (ret))
+ {
+ debug_printf ("return -1, set errno to EACCES");
+ set_errno (EACCES);
+ goto err;
+ }
+
+ if (is_eof (ret))
+ eof_detected = 1;
+ else
+ eom_detected = 1;
+
+ if (!read2)
+ {
+ if (!bytes_read && is_eom (ret))
+ {
+ debug_printf ("return -1, set errno to ENOSPC");
+ set_errno (ENOSPC);
+ goto err;
+ }
+ break;
+ }
+ lastblk_to_read = 1;
+ }
+ if (! read2)
+ break;
+ if (tgt == devbuf)
+ {
+ devbufstart = 0;
+ devbufend = read2;
+ }
+ else
+ {
+ len -= bytes_to_read;
+ ptr = (void *) ((char *) ptr + bytes_to_read);
+ bytes_read += bytes_to_read;
+ }
+ }
+ }
+ }
+ else if (!read_file (get_handle (), ptr, len, &bytes_read, &ret))
+ {
+ if (!is_eof (ret) && !is_eom (ret))
+ {
+ debug_printf ("return -1, set errno to EACCES");
+ set_errno (EACCES);
+ goto err;
+ }
+ if (bytes_read)
+ {
+ if (is_eof (ret))
+ eof_detected = 1;
+ else
+ eom_detected = 1;
+ }
+ else if (is_eom (ret))
+ {
+ debug_printf ("return -1, set errno to ENOSPC");
+ set_errno (ENOSPC);
+ goto err;
+ }
+ }
+
+ (ssize_t) ulen = bytes_read;
+ return;
+
+err:
+ (ssize_t) ulen = -1;
+ return;
+}
+
+int
+fhandler_dev_raw::raw_write (const void *ptr, size_t len)
+{
+ DWORD bytes_written = 0;
+ DWORD bytes_to_write;
+ DWORD written;
+ char *p = (char *) ptr;
+ char *tgt;
+ int ret;
+
+ /* Checking a previous end of media on tape */
+ if (eom_detected)
+ {
+ set_errno (ENOSPC);
+ return -1;
+ }
+
+ if (!is_writing)
+ {
+ devbufend = devbufstart;
+ devbufstart = 0;
+ }
+ is_writing = 1;
+
+ if (devbuf)
+ {
+ while (len > 0)
+ {
+ if (!varblkop &&
+ (len < devbufsiz || devbufend > 0) && devbufend < devbufsiz)
+ {
+ bytes_to_write = min (len, devbufsiz - devbufend);
+ memcpy (devbuf + devbufend, p, bytes_to_write);
+ bytes_written += bytes_to_write;
+ devbufend += bytes_to_write;
+ p += bytes_to_write;
+ len -= bytes_to_write;
+ }
+ else
+ {
+ if (varblkop)
+ {
+ bytes_to_write = len;
+ tgt = p;
+ }
+ else if (devbufend == devbufsiz)
+ {
+ bytes_to_write = devbufsiz;
+ tgt = devbuf;
+ }
+ else
+ {
+ bytes_to_write = (len / devbufsiz) * devbufsiz;
+ tgt = p;
+ }
+
+ ret = 0;
+ write_file (get_handle (), tgt, bytes_to_write, &written, &ret);
+ if (written)
+ has_written = 1;
+
+ if (ret)
+ {
+ if (!is_eom (ret))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ eom_detected = 1;
+
+ if (!written && !bytes_written)
+ {
+ set_errno (ENOSPC);
+ return -1;
+ }
+
+ if (tgt == p)
+ bytes_written += written;
+
+ break; // from while (len > 0)
+ }
+
+ if (tgt == devbuf)
+ {
+ if (written != devbufsiz)
+ memmove (devbuf, devbuf + written, devbufsiz - written);
+ devbufend = devbufsiz - written;
+ }
+ else
+ {
+ len -= written;
+ p += written;
+ bytes_written += written;
+ }
+ }
+ }
+ }
+ else if (len > 0)
+ {
+ if (!write_file (get_handle (), ptr, len, &bytes_written, &ret))
+ {
+ if (bytes_written)
+ has_written = 1;
+ if (!is_eom (ret))
+ {
+ set_errno (EACCES);
+ return -1;
+ }
+ eom_detected = 1;
+ if (!bytes_written)
+ {
+ set_errno (ENOSPC);
+ return -1;
+ }
+ }
+ has_written = 1;
+ }
+ return bytes_written;
+}
+
+int
+fhandler_dev_raw::dup (fhandler_base *child)
+{
+ int ret = fhandler_base::dup (child);
+
+ if (! ret)
+ {
+ fhandler_dev_raw *fhc = (fhandler_dev_raw *) child;
+
+ fhc->devbufsiz = devbufsiz;
+ if (devbufsiz > 1L)
+ fhc->devbuf = new char [devbufsiz];
+ fhc->devbufstart = 0;
+ fhc->devbufend = 0;
+ fhc->eom_detected = eom_detected;
+ fhc->eof_detected = eof_detected;
+ fhc->lastblk_to_read = 0;
+ fhc->varblkop = varblkop;
+ }
+ return ret;
+}
+
+void
+fhandler_dev_raw::fixup_after_fork (HANDLE)
+{
+ devbufstart = 0;
+ devbufend = 0;
+ lastblk_to_read = 0;
+}
+
+void
+fhandler_dev_raw::fixup_after_exec (HANDLE)
+{
+ if (devbufsiz > 1L)
+ devbuf = new char [devbufsiz];
+ devbufstart = 0;
+ devbufend = 0;
+ lastblk_to_read = 0;
+}
+
+int
+fhandler_dev_raw::ioctl (unsigned int cmd, void *buf)
+{
+ int ret = NO_ERROR;
+
+ if (cmd == RDIOCDOP)
+ {
+ struct rdop *op = (struct rdop *) buf;
+
+ if (!op)
+ ret = ERROR_INVALID_PARAMETER;
+ else
+ switch (op->rd_op)
+ {
+ case RDSETBLK:
+ if (get_device () == FH_TAPE)
+ {
+ struct mtop mop;
+
+ mop.mt_op = MTSETBLK;
+ mop.mt_count = op->rd_parm;
+ ret = ioctl (MTIOCTOP, &mop);
+ }
+ else if (op->rd_parm % 512)
+ ret = ERROR_INVALID_PARAMETER;
+ else if (devbuf && op->rd_parm < devbufend - devbufstart)
+ ret = ERROR_INVALID_PARAMETER;
+ else if (!devbuf || op->rd_parm != devbufsiz)
+ {
+ char *buf = new char [op->rd_parm];
+ if (devbufsiz > 1L)
+ {
+ memcpy (buf, devbuf + devbufstart, devbufend - devbufstart);
+ devbufend -= devbufstart;
+ delete [] devbuf;
+ }
+ else
+ devbufend = 0;
+
+ devbufstart = 0;
+ devbuf = buf;
+ devbufsiz = op->rd_parm;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ else if (cmd == RDIOCGET)
+ {
+ struct rdget *get = (struct rdget *) buf;
+
+ if (!get)
+ ret = ERROR_INVALID_PARAMETER;
+ else
+ get->bufsiz = devbufsiz ? devbufsiz : 1L;
+ }
+ else
+ return fhandler_base::ioctl (cmd, buf);
+
+ if (ret != NO_ERROR)
+ {
+ SetLastError (ret);
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
diff --git a/winsup/cygwin/fhandler_registry.cc b/winsup/cygwin/fhandler_registry.cc
new file mode 100644
index 00000000000..2024416d8bf
--- /dev/null
+++ b/winsup/cygwin/fhandler_registry.cc
@@ -0,0 +1,668 @@
+/* fhandler_registry.cc: fhandler for /proc/registry virtual filesystem
+
+ Copyright 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* FIXME: Access permissions are ignored at the moment. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include <assert.h>
+
+#define _COMPILING_NEWLIB
+#include <dirent.h>
+
+static const int registry_len = sizeof ("registry") - 1;
+/* If this bit is set in __d_position then we are enumerating values,
+ * else sub-keys. keeping track of where we are is horribly messy
+ * the bottom 16 bits are the absolute position and the top 15 bits
+ * make up the value index if we are enuerating values.
+ */
+static const __off32_t REG_ENUM_VALUES_MASK = 0x8000000;
+static const __off32_t REG_POSITION_MASK = 0xffff;
+
+/* List of root keys in /proc/registry.
+ * Possibly we should filter out those not relevant to the flavour of Windows
+ * Cygwin is running on.
+ */
+static const char *registry_listing[] =
+{
+ ".",
+ "..",
+ "HKEY_CLASSES_ROOT",
+ "HKEY_CURRENT_CONFIG",
+ "HKEY_CURRENT_USER",
+ "HKEY_LOCAL_MACHINE",
+ "HKEY_USERS",
+ "HKEY_DYN_DATA", // 95/98/Me
+ "HKEY_PERFOMANCE_DATA", // NT/2000/XP
+ NULL
+};
+
+static const HKEY registry_keys[] =
+{
+ (HKEY) INVALID_HANDLE_VALUE,
+ (HKEY) INVALID_HANDLE_VALUE,
+ HKEY_CLASSES_ROOT,
+ HKEY_CURRENT_CONFIG,
+ HKEY_CURRENT_USER,
+ HKEY_LOCAL_MACHINE,
+ HKEY_USERS,
+ HKEY_DYN_DATA,
+ HKEY_PERFORMANCE_DATA
+};
+
+static const int ROOT_KEY_COUNT = sizeof (registry_keys) / sizeof (HKEY);
+
+/* These get added to each subdirectory in /proc/registry.
+ * If we wanted to implement writing, we could maybe add a '.writable' entry or
+ * suchlike.
+ */
+static const char *special_dot_files[] =
+{
+ ".",
+ "..",
+ NULL
+};
+
+static const int SPECIAL_DOT_FILE_COUNT =
+ (sizeof (special_dot_files) / sizeof (const char *)) - 1;
+
+/* Name given to default values */
+static const char *DEFAULT_VALUE_NAME = "@";
+
+static HKEY open_key (const char *name, REGSAM access, bool isValue);
+
+/* Returns 0 if path doesn't exist, >0 if path is a directory,
+ * <0 if path is a file.
+ *
+ * We open the last key but one and then enum it's sub-keys and values to see if the
+ * final component is there. This gets round the problem of not having security access
+ * to the final key in the path.
+ */
+int
+fhandler_registry::exists ()
+{
+ int file_type = 0, index = 0, pathlen;
+ DWORD buf_size = MAX_PATH;
+ LONG error;
+ char buf[buf_size];
+ const char *file;
+ HKEY hKey = (HKEY) INVALID_HANDLE_VALUE;
+
+ const char *path = get_name ();
+ debug_printf ("exists (%s)", path);
+ path += proc_len + registry_len + 2;
+ if (*path == 0)
+ {
+ file_type = 2;
+ goto out;
+ }
+ pathlen = strlen (path);
+ file = path + pathlen - 1;
+ if (isdirsep (*file) && pathlen > 1)
+ file--;
+ while (!isdirsep (*file))
+ file--;
+ file++;
+
+ if (file == path)
+ {
+ for (int i = 0; registry_listing[i]; i++)
+ if (path_prefix_p
+ (registry_listing[i], path, strlen (registry_listing[i])))
+ {
+ file_type = 1;
+ goto out;
+ }
+ goto out;
+ }
+
+ hKey = open_key (path, KEY_READ, false);
+ if (hKey != (HKEY) INVALID_HANDLE_VALUE)
+ file_type = 1;
+ else
+ {
+ hKey = open_key (path, KEY_READ, true);
+ if (hKey == (HKEY) INVALID_HANDLE_VALUE)
+ return 0;
+
+ while (ERROR_SUCCESS ==
+ (error = RegEnumKeyEx (hKey, index++, buf, &buf_size, NULL, NULL,
+ NULL, NULL))
+ || (error == ERROR_MORE_DATA))
+ {
+ if (pathmatch (buf, file))
+ {
+ file_type = 1;
+ goto out;
+ }
+ buf_size = MAX_PATH;
+ }
+ if (error != ERROR_NO_MORE_ITEMS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ goto out;
+ }
+ index = 0;
+ buf_size = MAX_PATH;
+ while (ERROR_SUCCESS ==
+ (error = RegEnumValue (hKey, index++, buf, &buf_size, NULL, NULL,
+ NULL, NULL))
+ || (error == ERROR_MORE_DATA))
+ {
+ if (pathmatch (buf, file) || (buf[0] == '\0' &&
+ pathmatch (file, DEFAULT_VALUE_NAME)))
+ {
+ file_type = -1;
+ goto out;
+ }
+ buf_size = MAX_PATH;
+ }
+ if (error != ERROR_NO_MORE_ITEMS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ goto out;
+ }
+ }
+out:
+ if (hKey != (HKEY) INVALID_HANDLE_VALUE)
+ RegCloseKey (hKey);
+ return file_type;
+}
+
+fhandler_registry::fhandler_registry ():
+fhandler_proc ()
+{
+}
+
+int
+fhandler_registry::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ this->fhandler_base::fstat (buf, pc);
+ buf->st_mode &= ~_IFMT & NO_W;
+ int file_type = exists ();
+ switch (file_type)
+ {
+ case 0:
+ set_errno (ENOENT);
+ return -1;
+ case 1:
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ break;
+ case 2:
+ buf->st_mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH;
+ buf->st_nlink = ROOT_KEY_COUNT;
+ break;
+ default:
+ case -1:
+ buf->st_mode |= S_IFREG;
+ buf->st_mode &= NO_X;
+ break;
+ }
+ if (file_type != 0 && file_type != 2)
+ {
+ HKEY hKey;
+ const char *path = get_name () + proc_len + registry_len + 2;
+ hKey =
+ open_key (path, STANDARD_RIGHTS_READ | KEY_QUERY_VALUE,
+ (file_type < 0) ? true : false);
+
+ if (hKey != (HKEY) INVALID_HANDLE_VALUE)
+ {
+ FILETIME ftLastWriteTime;
+ DWORD subkey_count;
+ if (ERROR_SUCCESS ==
+ RegQueryInfoKey (hKey, NULL, NULL, NULL, &subkey_count, NULL,
+ NULL, NULL, NULL, NULL, NULL,
+ &ftLastWriteTime))
+ {
+ to_timestruc_t (&ftLastWriteTime, &buf->st_mtim);
+ buf->st_ctim = buf->st_mtim;
+ time_as_timestruc_t (&buf->st_atim);
+ if (file_type > 0)
+ buf->st_nlink = subkey_count;
+ else
+ {
+ int pathlen = strlen (path);
+ const char *value_name = path + pathlen - 1;
+ if (isdirsep (*value_name) && pathlen > 1)
+ value_name--;
+ while (!isdirsep (*value_name))
+ value_name--;
+ value_name++;
+ DWORD dwSize;
+ if (ERROR_SUCCESS ==
+ RegQueryValueEx (hKey, value_name, NULL, NULL, NULL,
+ &dwSize))
+ buf->st_size = dwSize;
+ }
+ __uid32_t uid;
+ __gid32_t gid;
+ if (get_object_attribute
+ ((HANDLE) hKey, SE_REGISTRY_KEY, &buf->st_mode, &uid,
+ &gid) == 0)
+ {
+ buf->st_uid = uid;
+ buf->st_gid = gid;
+ buf->st_mode &= ~(S_IWUSR | S_IWGRP | S_IWOTH);
+ if (file_type > 0)
+ buf->st_mode |= S_IFDIR;
+ else
+ buf->st_mode &= NO_X;
+ }
+ }
+ RegCloseKey (hKey);
+ }
+ }
+ return 0;
+}
+
+struct dirent *
+fhandler_registry::readdir (DIR * dir)
+{
+ DWORD buf_size = MAX_PATH;
+ char buf[buf_size];
+ HANDLE handle;
+ struct dirent *res = NULL;
+ const char *path = dir->__d_dirname + proc_len + 1 + registry_len;
+ LONG error;
+
+ if (*path == 0)
+ {
+ if (dir->__d_position >= ROOT_KEY_COUNT)
+ goto out;
+ strcpy (dir->__d_dirent->d_name, registry_listing[dir->__d_position++]);
+ res = dir->__d_dirent;
+ goto out;
+ }
+ if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE
+ && dir->__d_position == 0)
+ {
+ handle = open_key (path + 1, KEY_READ, false);
+ dir->__d_u.__d_data.__handle = handle;
+ }
+ if (dir->__d_u.__d_data.__handle == INVALID_HANDLE_VALUE)
+ goto out;
+ if (dir->__d_position < SPECIAL_DOT_FILE_COUNT)
+ {
+ strcpy (dir->__d_dirent->d_name,
+ special_dot_files[dir->__d_position++]);
+ res = dir->__d_dirent;
+ goto out;
+ }
+retry:
+ if (dir->__d_position & REG_ENUM_VALUES_MASK)
+ /* For the moment, the type of key is ignored here. when write access is added,
+ * maybe add an extension for the type of each value?
+ */
+ error = RegEnumValue ((HKEY) dir->__d_u.__d_data.__handle,
+ (dir->__d_position & ~REG_ENUM_VALUES_MASK) >> 16,
+ buf, &buf_size, NULL, NULL, NULL, NULL);
+ else
+ error =
+ RegEnumKeyEx ((HKEY) dir->__d_u.__d_data.__handle, dir->__d_position -
+ SPECIAL_DOT_FILE_COUNT, buf, &buf_size, NULL, NULL, NULL,
+ NULL);
+ if (error == ERROR_NO_MORE_ITEMS
+ && (dir->__d_position & REG_ENUM_VALUES_MASK) == 0)
+ {
+ /* If we're finished with sub-keys, start on values under this key. */
+ dir->__d_position |= REG_ENUM_VALUES_MASK;
+ buf_size = MAX_PATH;
+ goto retry;
+ }
+ if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA)
+ {
+ RegCloseKey ((HKEY) dir->__d_u.__d_data.__handle);
+ dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ goto out;
+ }
+
+ /* We get here if `buf' contains valid data. */
+ if (*buf == 0)
+ strcpy (dir->__d_dirent->d_name, DEFAULT_VALUE_NAME);
+ else
+ strcpy (dir->__d_dirent->d_name, buf);
+
+ dir->__d_position++;
+ if (dir->__d_position & REG_ENUM_VALUES_MASK)
+ dir->__d_position += 0x10000;
+ res = dir->__d_dirent;
+out:
+ syscall_printf ("%p = readdir (%p)", &dir->__d_dirent, dir);
+ return res;
+}
+
+__off64_t
+fhandler_registry::telldir (DIR * dir)
+{
+ return dir->__d_position & REG_POSITION_MASK;
+}
+
+void
+fhandler_registry::seekdir (DIR * dir, __off64_t loc)
+{
+ /* Unfortunately cannot simply set __d_position due to transition from sub-keys to
+ * values.
+ */
+ rewinddir (dir);
+ while (loc > (dir->__d_position & REG_POSITION_MASK))
+ if (!readdir (dir))
+ break;
+}
+
+void
+fhandler_registry::rewinddir (DIR * dir)
+{
+ if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE)
+ {
+ (void) RegCloseKey ((HKEY) dir->__d_u.__d_data.__handle);
+ dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
+ }
+ dir->__d_position = 0;
+ return;
+}
+
+int
+fhandler_registry::closedir (DIR * dir)
+{
+ int res = 0;
+ if (dir->__d_u.__d_data.__handle != INVALID_HANDLE_VALUE &&
+ RegCloseKey ((HKEY) dir->__d_u.__d_data.__handle) != ERROR_SUCCESS)
+ {
+ __seterrno ();
+ res = -1;
+ }
+ syscall_printf ("%d = closedir (%p)", res, dir);
+ return 0;
+}
+
+int
+fhandler_registry::open (path_conv * pc, int flags, mode_t mode)
+{
+ int pathlen;
+ const char *file;
+ HKEY handle;
+
+ int res = fhandler_virtual::open (pc, flags, mode);
+ if (!res)
+ goto out;
+
+ const char *path;
+ path = get_name () + proc_len + 1 + registry_len;
+ if (!*path)
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ flags |= O_DIROPEN;
+ goto success;
+ }
+ }
+ path++;
+ pathlen = strlen (path);
+ file = path + pathlen - 1;
+ if (isdirsep (*file) && pathlen > 1)
+ file--;
+ while (!isdirsep (*file))
+ file--;
+ file++;
+
+ if (file == path)
+ {
+ for (int i = 0; registry_listing[i]; i++)
+ if (path_prefix_p
+ (registry_listing[i], path, strlen (registry_listing[i])))
+ {
+ if ((flags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL))
+ {
+ set_errno (EEXIST);
+ res = 0;
+ goto out;
+ }
+ else if (flags & O_WRONLY)
+ {
+ set_errno (EISDIR);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ flags |= O_DIROPEN;
+ goto success;
+ }
+ }
+
+ if (flags & O_CREAT)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+ else
+ {
+ set_errno (ENOENT);
+ res = 0;
+ goto out;
+ }
+ }
+
+ if (flags & O_WRONLY)
+ {
+ set_errno (EROFS);
+ res = 0;
+ goto out;
+ }
+
+ handle = open_key (path, KEY_READ, true);
+ if (handle == (HKEY) INVALID_HANDLE_VALUE)
+ {
+ res = 0;
+ goto out;
+ }
+
+ set_io_handle (handle);
+
+ if (pathmatch (file, DEFAULT_VALUE_NAME))
+ value_name = cstrdup ("");
+ else
+ value_name = cstrdup (file);
+
+ if (!fill_filebuf ())
+ {
+ RegCloseKey (handle);
+ res = 0;
+ goto out;
+ }
+
+ if (flags & O_APPEND)
+ position = filesize;
+ else
+ position = 0;
+
+success:
+ res = 1;
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+out:
+ syscall_printf ("%d = fhandler_registry::open (%p, %d)", res, flags, mode);
+ return res;
+}
+
+int
+fhandler_registry::close ()
+{
+ int res = fhandler_virtual::close ();
+ if (res != 0)
+ return res;
+ HKEY handle = (HKEY) get_handle ();
+ if (handle != (HKEY) INVALID_HANDLE_VALUE)
+ {
+ if (RegCloseKey (handle) != ERROR_SUCCESS)
+ {
+ __seterrno ();
+ res = -1;
+ }
+ }
+ if (value_name)
+ cfree (value_name);
+ return res;
+}
+
+bool
+fhandler_registry::fill_filebuf ()
+{
+ DWORD type, size;
+ LONG error;
+ HKEY handle = (HKEY) get_handle ();
+ if (handle != HKEY_PERFORMANCE_DATA)
+ {
+ error = RegQueryValueEx (handle, value_name, NULL, &type, NULL, &size);
+ if (error != ERROR_SUCCESS)
+ {
+ if (error != ERROR_FILE_NOT_FOUND)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return false;
+ }
+ goto value_not_found;
+ }
+ bufalloc = size;
+ filebuf = (char *) malloc (bufalloc);
+ error =
+ RegQueryValueEx (handle, value_name, NULL, NULL, (BYTE *) filebuf,
+ &size);
+ if (error != ERROR_SUCCESS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return true;
+ }
+ filesize = size;
+ }
+ else
+ {
+ bufalloc = 0;
+ do
+ {
+ bufalloc += 1000;
+ filebuf = (char *) realloc (filebuf, bufalloc);
+ error = RegQueryValueEx (handle, value_name, NULL, &type,
+ (BYTE *) filebuf, &size);
+ if (error != ERROR_SUCCESS && error != ERROR_MORE_DATA)
+ {
+ if (error != ERROR_FILE_NOT_FOUND)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return true;
+ }
+ goto value_not_found;
+ }
+ }
+ while (error == ERROR_MORE_DATA);
+ filesize = size;
+ }
+ return true;
+value_not_found:
+ DWORD buf_size = MAX_PATH;
+ char buf[buf_size];
+ int index = 0;
+ while (ERROR_SUCCESS ==
+ (error = RegEnumKeyEx (handle, index++, buf, &buf_size, NULL, NULL,
+ NULL, NULL)) || (error == ERROR_MORE_DATA))
+ {
+ if (pathmatch (buf, value_name))
+ {
+ set_errno (EISDIR);
+ return false;
+ }
+ buf_size = MAX_PATH;
+ }
+ if (error != ERROR_NO_MORE_ITEMS)
+ {
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return false;
+ }
+ set_errno (ENOENT);
+ return false;
+}
+
+/* Auxillary member function to open registry keys. */
+static HKEY
+open_key (const char *name, REGSAM access, bool isValue)
+{
+ HKEY hKey = (HKEY) INVALID_HANDLE_VALUE;
+ HKEY hParentKey = (HKEY) INVALID_HANDLE_VALUE;
+ bool parentOpened = false;
+ char component[MAX_PATH];
+
+ while (*name)
+ {
+ const char *anchor = name;
+ while (*name && !isdirsep (*name))
+ name++;
+ strncpy (component, anchor, name - anchor);
+ component[name - anchor] = '\0';
+ if (*name)
+ name++;
+ if (*name == 0 && isValue == true)
+ goto out;
+
+ if (hParentKey != (HKEY) INVALID_HANDLE_VALUE)
+ {
+ REGSAM effective_access = KEY_READ;
+ if ((strchr (name, '/') == NULL && isValue == true) || *name == 0)
+ effective_access = access;
+ LONG
+ error =
+ RegOpenKeyEx (hParentKey, component, 0, effective_access, &hKey);
+ if (error != ERROR_SUCCESS)
+ {
+ hKey = (HKEY) INVALID_HANDLE_VALUE;
+ seterrno_from_win_error (__FILE__, __LINE__, error);
+ return hKey;
+ }
+ if (parentOpened)
+ RegCloseKey (hParentKey);
+ hParentKey = hKey;
+ parentOpened = true;
+ }
+ else
+ {
+ for (int i = 0; registry_listing[i]; i++)
+ if (pathmatch (component, registry_listing[i]))
+ hKey = registry_keys[i];
+ if (hKey == (HKEY) INVALID_HANDLE_VALUE)
+ return hKey;
+ hParentKey = hKey;
+ }
+ }
+out:
+ return hKey;
+}
diff --git a/winsup/cygwin/fhandler_serial.cc b/winsup/cygwin/fhandler_serial.cc
new file mode 100644
index 00000000000..b813c9121a7
--- /dev/null
+++ b/winsup/cygwin/fhandler_serial.cc
@@ -0,0 +1,1026 @@
+/* fhandler_serial.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include <sys/termios.h>
+#include <ddk/ntddser.h>
+
+/**********************************************************************/
+/* fhandler_serial */
+
+fhandler_serial::fhandler_serial ()
+ : fhandler_base (), vmin_ (0), vtime_ (0), pgrp_ (myself->pgid)
+{
+ set_need_fork_fixup ();
+}
+
+void
+fhandler_serial::overlapped_setup ()
+{
+ memset (&io_status, 0, sizeof (io_status));
+ io_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ ProtectHandle (io_status.hEvent);
+ overlapped_armed = 0;
+}
+
+void
+fhandler_serial::raw_read (void *ptr, size_t& ulen)
+{
+ int tot;
+ DWORD n;
+ HANDLE w4[2];
+ size_t minchars = vmin_ ?: ulen;
+
+ w4[0] = io_status.hEvent;
+ w4[1] = signal_arrived;
+
+ debug_printf ("ulen %d, vmin_ %d, vtime_ %d, hEvent %p", ulen, vmin_, vtime_,
+ io_status.hEvent);
+ if (!overlapped_armed)
+ {
+ (void) SetCommMask (get_handle (), EV_RXCHAR);
+ ResetEvent (io_status.hEvent);
+ }
+
+ for (n = 0, tot = 0; ulen; ulen -= n, ptr = (char *) ptr + n)
+ {
+ COMSTAT st;
+ DWORD inq = 1;
+
+ n = 0;
+
+ if (!vtime_ && !vmin_)
+ inq = ulen;
+ else if (vtime_)
+ {
+ inq = ulen; // non-interruptible -- have to use kernel timeouts
+ // also note that this is not strictly correct.
+ // if vmin > ulen then things won't work right.
+ overlapped_armed = -1;
+ }
+
+ if (!ClearCommError (get_handle (), &ev, &st))
+ goto err;
+ else if (ev)
+ termios_printf ("error detected %x", ev);
+ else if (st.cbInQue)
+ inq = st.cbInQue;
+ else if (!overlapped_armed)
+ {
+ if ((size_t) tot >= minchars)
+ break;
+ else if (WaitCommEvent (get_handle (), &ev, &io_status))
+ {
+ debug_printf ("WaitCommEvent succeeded: ev %x", ev);
+ if (!ev)
+ continue;
+ }
+ else if (GetLastError () != ERROR_IO_PENDING)
+ goto err;
+ else
+ {
+ overlapped_armed = 1;
+ switch (WaitForMultipleObjects (2, w4, FALSE, INFINITE))
+ {
+ case WAIT_OBJECT_0:
+ if (!GetOverlappedResult (get_handle (), &io_status, &n, FALSE))
+ goto err;
+ debug_printf ("n %d, ev %x", n, ev);
+ break;
+ case WAIT_OBJECT_0 + 1:
+ tot = -1;
+ PurgeComm (get_handle (), PURGE_RXABORT);
+ overlapped_armed = 0;
+ set_sig_errno (EINTR);
+ goto out;
+ default:
+ goto err;
+ }
+ }
+ }
+
+ overlapped_armed = 0;
+ ResetEvent (io_status.hEvent);
+ if (inq > ulen)
+ inq = ulen;
+ debug_printf ("inq %d", inq);
+ if (ReadFile (get_handle (), ptr, min (inq, ulen), &n, &io_status))
+ /* Got something */;
+ else if (GetLastError () != ERROR_IO_PENDING)
+ goto err;
+ else if (!GetOverlappedResult (get_handle (), &io_status, &n, TRUE))
+ goto err;
+
+ tot += n;
+ debug_printf ("vtime_ %d, vmin_ %d, n %d, tot %d", vtime_, vmin_, n, tot);
+ if (vtime_ || !vmin_ || !n)
+ break;
+ continue;
+
+ err:
+ PurgeComm (get_handle (), PURGE_RXABORT);
+ debug_printf ("err %E");
+ if (GetLastError () == ERROR_OPERATION_ABORTED)
+ n = 0;
+ else
+ {
+ tot = -1;
+ __seterrno ();
+ break;
+ }
+ }
+
+out:
+ ulen = tot;
+}
+
+/* Cover function to WriteFile to provide Posix interface and semantics
+ (as much as possible). */
+int
+fhandler_serial::raw_write (const void *ptr, size_t len)
+{
+ DWORD bytes_written;
+ OVERLAPPED write_status;
+
+ memset (&write_status, 0, sizeof (write_status));
+ write_status.hEvent = CreateEvent (&sec_none_nih, TRUE, FALSE, NULL);
+ ProtectHandle (write_status.hEvent);
+
+ for (;;)
+ {
+ if (WriteFile (get_handle (), ptr, len, &bytes_written, &write_status))
+ break;
+
+ switch (GetLastError ())
+ {
+ case ERROR_OPERATION_ABORTED:
+ continue;
+ case ERROR_IO_PENDING:
+ break;
+ default:
+ goto err;
+ }
+
+ if (!GetOverlappedResult (get_handle (), &write_status, &bytes_written, TRUE))
+ goto err;
+
+ break;
+ }
+
+ ForceCloseHandle (write_status.hEvent);
+
+ return bytes_written;
+
+err:
+ __seterrno ();
+ ForceCloseHandle (write_status.hEvent);
+ return -1;
+}
+
+void
+fhandler_serial::dump (void)
+{
+ paranoid_printf ("here");
+}
+
+void
+fhandler_serial::init (HANDLE f, DWORD flags, mode_t bin)
+{
+ (void) open (NULL, flags, bin & (O_BINARY | O_TEXT));
+}
+
+int
+fhandler_serial::open (path_conv *, int flags, mode_t mode)
+{
+ int res;
+ COMMTIMEOUTS to;
+ extern BOOL reset_com;
+
+ syscall_printf ("fhandler_serial::open (%s, %p, %p)",
+ get_name (), flags, mode);
+
+ if (!(res = this->fhandler_base::open (NULL, flags, mode)))
+ return 0;
+
+ res = 1;
+
+ (void) SetCommMask (get_handle (), EV_RXCHAR);
+
+ set_r_no_interrupt (1); // Handled explicitly in read code
+
+ overlapped_setup ();
+
+ memset (&to, 0, sizeof (to));
+ (void) SetCommTimeouts (get_handle (), &to);
+
+ /* Reset serial port to known state of 9600-8-1-no flow control
+ on open for better behavior under Win 95.
+
+ FIXME: This should only be done when explicitly opening the com
+ port. It should not be reset if an fd is inherited.
+ Using __progname in this way, to determine how far along in the
+ initialization we are, is really a terrible kludge and should
+ be fixed ASAP.
+ */
+ extern char *__progname;
+ if (reset_com && __progname)
+ {
+ DCB state;
+ GetCommState (get_handle (), &state);
+ syscall_printf ("setting initial state on %s (reset_com %d)",
+ get_name (), reset_com);
+ state.BaudRate = CBR_9600;
+ state.ByteSize = 8;
+ state.StopBits = ONESTOPBIT;
+ state.Parity = NOPARITY; /* FIXME: correct default? */
+ state.fBinary = TRUE; /* binary xfer */
+ state.EofChar = 0; /* no end-of-data in binary mode */
+ state.fNull = FALSE; /* don't discard nulls in binary mode */
+ state.fParity = FALSE; /* ignore parity errors */
+ state.fErrorChar = FALSE;
+ state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */
+ state.fOutX = FALSE; /* disable transmission flow control */
+ state.fInX = FALSE; /* disable reception flow control */
+ state.XonChar = 0x11;
+ state.XoffChar = 0x13;
+ state.fOutxDsrFlow = FALSE; /* disable DSR flow control */
+ state.fRtsControl = RTS_CONTROL_ENABLE; /* ignore lead control except
+ DTR */
+ state.fOutxCtsFlow = FALSE; /* disable output flow control */
+ state.fDtrControl = DTR_CONTROL_ENABLE; /* assert DTR */
+ state.fDsrSensitivity = FALSE; /* don't assert DSR */
+ state.fAbortOnError = TRUE;
+ if (!SetCommState (get_handle (), &state))
+ system_printf ("couldn't set initial state for %s, %E", get_name ());
+ }
+
+ /* setting rts and dtr to known state so that ioctl() function with
+ request TIOCMGET could return correct value of RTS and DTR lines.
+ Important only for Win 9x systems */
+
+ if (!wincap.supports_reading_modem_output_lines ())
+ {
+ if (EscapeCommFunction (get_handle (), SETDTR) == 0)
+ system_printf ("couldn't set initial state of DTR for %s, %E", get_name ());
+ if (EscapeCommFunction (get_handle (), SETRTS) == 0)
+ system_printf ("couldn't set initial state of RTS for %s, %E", get_name ());
+
+ /* even though one of above functions fail I have to set rts and dtr
+ variables to initial value. */
+ rts = TIOCM_RTS;
+ dtr = TIOCM_DTR;
+ }
+
+ SetCommMask (get_handle (), EV_RXCHAR);
+ set_open_status ();
+ syscall_printf ("%p = fhandler_serial::open (%s, %p, %p)",
+ res, get_name (), flags, mode);
+ return res;
+}
+
+int
+fhandler_serial::close ()
+{
+ (void) ForceCloseHandle (io_status.hEvent);
+ return fhandler_base::close ();
+}
+
+/* tcsendbreak: POSIX 7.2.2.1 */
+/* Break for 250-500 milliseconds if duration == 0 */
+/* Otherwise, units for duration are undefined */
+int
+fhandler_serial::tcsendbreak (int duration)
+{
+ unsigned int sleeptime = 300000;
+
+ if (duration > 0)
+ sleeptime *= duration;
+
+ if (SetCommBreak (get_handle ()) == 0)
+ return -1;
+
+ /* FIXME: need to send zero bits during duration */
+ usleep (sleeptime);
+
+ if (ClearCommBreak (get_handle ()) == 0)
+ return -1;
+
+ syscall_printf ("0 = fhandler_serial:tcsendbreak (%d)", duration);
+
+ return 0;
+}
+
+/* tcdrain: POSIX 7.2.2.1 */
+int
+fhandler_serial::tcdrain (void)
+{
+ if (FlushFileBuffers (get_handle ()) == 0)
+ return -1;
+
+ return 0;
+}
+
+/* tcflow: POSIX 7.2.2.1 */
+int
+fhandler_serial::tcflow (int action)
+{
+ DWORD win32action = 0;
+ DCB dcb;
+ char xchar;
+
+ termios_printf ("action %d", action);
+
+ switch (action)
+ {
+ case TCOOFF:
+ win32action = SETXOFF;
+ break;
+ case TCOON:
+ win32action = SETXON;
+ break;
+ case TCION:
+ case TCIOFF:
+ if (GetCommState (get_handle (), &dcb) == 0)
+ return -1;
+ if (action == TCION)
+ xchar = (dcb.XonChar ? dcb.XonChar : 0x11);
+ else
+ xchar = (dcb.XoffChar ? dcb.XoffChar : 0x13);
+ if (TransmitCommChar (get_handle (), xchar) == 0)
+ return -1;
+ return 0;
+ break;
+ default:
+ return -1;
+ break;
+ }
+
+ if (EscapeCommFunction (get_handle (), win32action) == 0)
+ return -1;
+
+ return 0;
+}
+
+
+/* ioctl: */
+int
+fhandler_serial::ioctl (unsigned int cmd, void *buffer)
+{
+ int res = 0;
+
+# define ibuffer ((int) buffer)
+# define ipbuffer (*(int *) buffer)
+
+ DWORD ev;
+ COMSTAT st;
+ if (!ClearCommError (get_handle (), &ev, &st))
+ {
+ __seterrno ();
+ res = -1;
+ }
+ else
+ switch (cmd)
+ {
+ case TCFLSH:
+ res = tcflush (ibuffer);
+ break;
+ case TIOCMGET:
+ DWORD modem_lines;
+ if (!GetCommModemStatus (get_handle (), &modem_lines))
+ {
+ __seterrno ();
+ res = -1;
+ }
+ else
+ {
+ ipbuffer = 0;
+ if (modem_lines & MS_CTS_ON)
+ ipbuffer |= TIOCM_CTS;
+ if (modem_lines & MS_DSR_ON)
+ ipbuffer |= TIOCM_DSR;
+ if (modem_lines & MS_RING_ON)
+ ipbuffer |= TIOCM_RI;
+ if (modem_lines & MS_RLSD_ON)
+ ipbuffer |= TIOCM_CD;
+
+ DWORD cb;
+ DWORD mcr;
+ if (!DeviceIoControl (get_handle (), IOCTL_SERIAL_GET_DTRRTS,
+ NULL, 0, &mcr, 4, &cb, 0) || cb != 4)
+ ipbuffer |= rts | dtr;
+ else
+ {
+ if (mcr & 2)
+ ipbuffer |= TIOCM_RTS;
+ if (mcr & 1)
+ ipbuffer |= TIOCM_DTR;
+ }
+ }
+ break;
+ case TIOCMSET:
+ if (ipbuffer & TIOCM_RTS)
+ {
+ if (EscapeCommFunction (get_handle (), SETRTS))
+ rts = TIOCM_RTS;
+ else
+ {
+ __seterrno ();
+ res = -1;
+ }
+ }
+ else
+ {
+ if (EscapeCommFunction (get_handle (), CLRRTS))
+ rts = 0;
+ else
+ {
+ __seterrno ();
+ res = -1;
+ }
+ }
+ if (ipbuffer & TIOCM_DTR)
+ {
+ if (EscapeCommFunction (get_handle (), SETDTR))
+ dtr = TIOCM_DTR;
+ else
+ {
+ __seterrno ();
+ res = -1;
+ }
+ }
+ else if (EscapeCommFunction (get_handle (), CLRDTR))
+ dtr = 0;
+ else
+ {
+ __seterrno ();
+ res = -1;
+ }
+ break;
+ case TIOCINQ:
+ if (ev & CE_FRAME || ev & CE_IOE || ev & CE_OVERRUN || ev & CE_RXOVER
+ || ev & CE_RXPARITY)
+ {
+ set_errno (EINVAL); /* FIXME: Use correct errno */
+ res = -1;
+ }
+ else
+ ipbuffer = st.cbInQue;
+ break;
+ default:
+ set_errno (ENOSYS);
+ res = -1;
+ break;
+ }
+
+ termios_printf ("%d = ioctl (%p, %p)", res, cmd, buffer);
+# undef ibuffer
+# undef ipbuffer
+ return res;
+}
+
+/* tcflush: POSIX 7.2.2.1 */
+int
+fhandler_serial::tcflush (int queue)
+{
+ if (queue == TCOFLUSH || queue == TCIOFLUSH)
+ PurgeComm (get_handle (), PURGE_TXABORT | PURGE_TXCLEAR);
+
+ if (queue == TCIFLUSH || queue == TCIOFLUSH)
+ /* Input flushing by polling until nothing turns up
+ (we stop after 1000 chars anyway) */
+ for (int max = 1000; max > 0; max--)
+ {
+ COMSTAT st;
+ if (!PurgeComm (get_handle (), PURGE_RXABORT | PURGE_RXCLEAR))
+ break;
+ low_priority_sleep (100);
+ if (!ClearCommError (get_handle (), &ev, &st) || !st.cbInQue)
+ break;
+ }
+
+ return 0;
+}
+
+/* tcsetattr: POSIX 7.2.1.1 */
+int
+fhandler_serial::tcsetattr (int action, const struct termios *t)
+{
+ /* Possible actions:
+ TCSANOW: immediately change attributes.
+ TCSADRAIN: flush output, then change attributes.
+ TCSAFLUSH: flush output and discard input, then change attributes.
+ */
+
+ BOOL dropDTR = FALSE;
+ COMMTIMEOUTS to;
+ DCB ostate, state;
+ unsigned int ovtime = vtime_, ovmin = vmin_;
+ int tmpDtr, tmpRts;
+ tmpDtr = tmpRts = 0;
+
+ termios_printf ("action %d", action);
+ if ((action == TCSADRAIN) || (action == TCSAFLUSH))
+ {
+ FlushFileBuffers (get_handle ());
+ termios_printf ("flushed file buffers");
+ }
+ if (action == TCSAFLUSH)
+ PurgeComm (get_handle (), (PURGE_RXABORT | PURGE_RXCLEAR));
+
+ /* get default/last comm state */
+ if (!GetCommState (get_handle (), &ostate))
+ return -1;
+
+ state = ostate;
+
+ /* -------------- Set baud rate ------------------ */
+ /* FIXME: WIN32 also has 14400, 56000, 128000, and 256000.
+ Unix also has 230400. */
+
+ switch (t->c_ospeed)
+ {
+ case B0: /* drop DTR */
+ dropDTR = TRUE;
+ state.BaudRate = 0;
+ break;
+ case B110:
+ state.BaudRate = CBR_110;
+ break;
+ case B300:
+ state.BaudRate = CBR_300;
+ break;
+ case B600:
+ state.BaudRate = CBR_600;
+ break;
+ case B1200:
+ state.BaudRate = CBR_1200;
+ break;
+ case B2400:
+ state.BaudRate = CBR_2400;
+ break;
+ case B4800:
+ state.BaudRate = CBR_4800;
+ break;
+ case B9600:
+ state.BaudRate = CBR_9600;
+ break;
+ case B19200:
+ state.BaudRate = CBR_19200;
+ break;
+ case B38400:
+ state.BaudRate = CBR_38400;
+ break;
+ case B57600:
+ state.BaudRate = CBR_57600;
+ break;
+ case B115200:
+ state.BaudRate = CBR_115200;
+ break;
+ case B230400:
+ state.BaudRate = 230400 /* CBR_230400 - not defined */;
+ break;
+ default:
+ /* Unsupported baud rate! */
+ termios_printf ("Invalid t->c_ospeed %d", t->c_ospeed);
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* -------------- Set byte size ------------------ */
+
+ switch (t->c_cflag & CSIZE)
+ {
+ case CS5:
+ state.ByteSize = 5;
+ break;
+ case CS6:
+ state.ByteSize = 6;
+ break;
+ case CS7:
+ state.ByteSize = 7;
+ break;
+ case CS8:
+ state.ByteSize = 8;
+ break;
+ default:
+ /* Unsupported byte size! */
+ termios_printf ("Invalid t->c_cflag byte size %d",
+ t->c_cflag & CSIZE);
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* -------------- Set stop bits ------------------ */
+
+ if (t->c_cflag & CSTOPB)
+ state.StopBits = TWOSTOPBITS;
+ else
+ state.StopBits = ONESTOPBIT;
+
+ /* -------------- Set parity ------------------ */
+
+ if (t->c_cflag & PARENB)
+ state.Parity = (t->c_cflag & PARODD) ? ODDPARITY : EVENPARITY;
+ else
+ state.Parity = NOPARITY;
+
+ state.fBinary = TRUE; /* Binary transfer */
+ state.EofChar = 0; /* No end-of-data in binary mode */
+ state.fNull = FALSE; /* Don't discard nulls in binary mode */
+
+ /* -------------- Parity errors ------------------ */
+ /* fParity combines the function of INPCK and NOT IGNPAR */
+
+ if ((t->c_iflag & INPCK) && !(t->c_iflag & IGNPAR))
+ state.fParity = TRUE; /* detect parity errors */
+ else
+ state.fParity = FALSE; /* ignore parity errors */
+
+ /* Only present in Win32, Unix has no equivalent */
+ state.fErrorChar = FALSE;
+ state.ErrorChar = 0;
+
+ /* -------------- Set software flow control ------------------ */
+ /* Set fTXContinueOnXoff to FALSE. This prevents the triggering of a
+ premature XON when the remote device interprets a received character
+ as XON (same as IXANY on the remote side). Otherwise, a TRUE
+ value separates the TX and RX functions. */
+
+ state.fTXContinueOnXoff = TRUE; /* separate TX and RX flow control */
+
+ /* Transmission flow control */
+ if (t->c_iflag & IXON)
+ state.fOutX = TRUE; /* enable */
+ else
+ state.fOutX = FALSE; /* disable */
+
+ /* Reception flow control */
+ if (t->c_iflag & IXOFF)
+ state.fInX = TRUE; /* enable */
+ else
+ state.fInX = FALSE; /* disable */
+
+ /* XoffLim and XonLim are left at default values */
+
+ state.XonChar = (t->c_cc[VSTART] ? t->c_cc[VSTART] : 0x11);
+ state.XoffChar = (t->c_cc[VSTOP] ? t->c_cc[VSTOP] : 0x13);
+
+ /* -------------- Set hardware flow control ------------------ */
+
+ /* Disable DSR flow control */
+ state.fOutxDsrFlow = FALSE;
+
+ /* Some old flavors of Unix automatically enabled hardware flow
+ control when software flow control was not enabled. Since newer
+ Unices tend to require explicit setting of hardware flow-control,
+ this is what we do. */
+
+ /* RTS/CTS flow control */
+ if (t->c_cflag & CRTSCTS)
+ { /* enable */
+ state.fOutxCtsFlow = TRUE;
+ state.fRtsControl = RTS_CONTROL_HANDSHAKE;
+ }
+ else
+ { /* disable */
+ state.fRtsControl = RTS_CONTROL_ENABLE;
+ state.fOutxCtsFlow = FALSE;
+ tmpRts = TIOCM_RTS;
+ }
+
+ if (t->c_cflag & CRTSXOFF)
+ state.fRtsControl = RTS_CONTROL_HANDSHAKE;
+
+ /* -------------- DTR ------------------ */
+ /* Assert DTR on device open */
+
+ state.fDtrControl = DTR_CONTROL_ENABLE;
+
+ /* -------------- DSR ------------------ */
+ /* Assert DSR at the device? */
+
+ if (t->c_cflag & CLOCAL)
+ state.fDsrSensitivity = FALSE; /* no */
+ else
+ state.fDsrSensitivity = TRUE; /* yes */
+
+ /* -------------- Error handling ------------------ */
+ /* Since read/write operations terminate upon error, we
+ will use ClearCommError() to resume. */
+
+ state.fAbortOnError = TRUE;
+
+ if (memcmp (&ostate, &state, sizeof (state)) != 0)
+ SetCommState (get_handle (), &state);
+
+ set_r_binary ((t->c_iflag & IGNCR) ? 0 : 1);
+ set_w_binary ((t->c_oflag & ONLCR) ? 0 : 1);
+
+ if (dropDTR == TRUE)
+ {
+ EscapeCommFunction (get_handle (), CLRDTR);
+ tmpDtr = 0;
+ }
+ else
+ {
+ /* FIXME: Sometimes when CLRDTR is set, setting
+ state.fDtrControl = DTR_CONTROL_ENABLE will fail. This
+ is a problem since a program might want to change some
+ parameters while DTR is still down. */
+
+ EscapeCommFunction (get_handle (), SETDTR);
+ tmpDtr = TIOCM_DTR;
+ }
+
+ rts = tmpRts;
+ dtr = tmpDtr;
+
+ /*
+ The following documentation on was taken from "Linux Serial Programming
+ HOWTO". It explains how MIN (t->c_cc[VMIN] || vmin_) and TIME
+ (t->c_cc[VTIME] || vtime_) is to be used.
+
+ In non-canonical input processing mode, input is not assembled into
+ lines and input processing (erase, kill, delete, etc.) does not
+ occur. Two parameters control the behavior of this mode: c_cc[VTIME]
+ sets the character timer, and c_cc[VMIN] sets the minimum number of
+ characters to receive before satisfying the read.
+
+ If MIN > 0 and TIME = 0, MIN sets the number of characters to receive
+ before the read is satisfied. As TIME is zero, the timer is not used.
+
+ If MIN = 0 and TIME > 0, TIME serves as a timeout value. The read will
+ be satisfied if a single character is read, or TIME is exceeded (t =
+ TIME *0.1 s). If TIME is exceeded, no character will be returned.
+
+ If MIN > 0 and TIME > 0, TIME serves as an inter-character timer. The
+ read will be satisfied if MIN characters are received, or the time
+ between two characters exceeds TIME. The timer is restarted every time
+ a character is received and only becomes active after the first
+ character has been received.
+
+ If MIN = 0 and TIME = 0, read will be satisfied immediately. The
+ number of characters currently available, or the number of characters
+ requested will be returned. According to Antonino (see contributions),
+ you could issue a fcntl(fd, F_SETFL, FNDELAY); before reading to get
+ the same result.
+ */
+
+ if (t->c_lflag & ICANON)
+ {
+ vmin_ = 0;
+ vtime_ = 0;
+ }
+ else
+ {
+ vtime_ = t->c_cc[VTIME] * 100;
+ vmin_ = t->c_cc[VMIN];
+ }
+
+ debug_printf ("vtime %d, vmin %d", vtime_, vmin_);
+
+ if (ovmin == vmin_ && ovtime == vtime_)
+ return 0;
+
+ memset (&to, 0, sizeof (to));
+
+ if ((vmin_ > 0) && (vtime_ == 0))
+ {
+ /* Returns immediately with whatever is in buffer on a ReadFile();
+ or blocks if nothing found. We will keep calling ReadFile(); until
+ vmin_ characters are read */
+ to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD;
+ to.ReadTotalTimeoutConstant = MAXDWORD - 1;
+ }
+ else if ((vmin_ == 0) && (vtime_ > 0))
+ {
+ /* set timeoout constant appropriately and we will only try to
+ read one character in ReadFile() */
+ to.ReadTotalTimeoutConstant = vtime_;
+ to.ReadIntervalTimeout = to.ReadTotalTimeoutMultiplier = MAXDWORD;
+ }
+ else if ((vmin_ > 0) && (vtime_ > 0))
+ {
+ /* time applies to the interval time for this case */
+ to.ReadIntervalTimeout = vtime_;
+ }
+ else if ((vmin_ == 0) && (vtime_ == 0))
+ {
+ /* returns immediately with whatever is in buffer as per
+ Time-Outs docs in Win32 SDK API docs */
+ to.ReadIntervalTimeout = MAXDWORD;
+ }
+
+ debug_printf ("ReadTotalTimeoutConstant %d, ReadIntervalTimeout %d, ReadTotalTimeoutMultiplier %d",
+ to.ReadTotalTimeoutConstant, to.ReadIntervalTimeout, to.ReadTotalTimeoutMultiplier);
+ int res = SetCommTimeouts (get_handle (), &to);
+ if (!res)
+ {
+ system_printf ("SetCommTimeout failed, %E");
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+/* tcgetattr: POSIX 7.2.1.1 */
+int
+fhandler_serial::tcgetattr (struct termios *t)
+{
+ DCB state;
+
+ /* Get current Win32 comm state */
+ if (GetCommState (get_handle (), &state) == 0)
+ return -1;
+
+ /* for safety */
+ memset (t, 0, sizeof (*t));
+
+ /* -------------- Baud rate ------------------ */
+
+ switch (state.BaudRate)
+ {
+ case 0:
+ /* FIXME: need to drop DTR */
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B0;
+ break;
+ case CBR_110:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B110;
+ break;
+ case CBR_300:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B300;
+ break;
+ case CBR_600:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B600;
+ break;
+ case CBR_1200:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B1200;
+ break;
+ case CBR_2400:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B2400;
+ break;
+ case CBR_4800:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B4800;
+ break;
+ case CBR_9600:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B9600;
+ break;
+ case CBR_19200:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B19200;
+ break;
+ case CBR_38400:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B38400;
+ break;
+ case CBR_57600:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B57600;
+ break;
+ case CBR_115200:
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B115200;
+ break;
+ case 230400: /* CBR_230400 - not defined */
+ t->c_cflag = t->c_ospeed = t->c_ispeed = B230400;
+ break;
+ default:
+ /* Unsupported baud rate! */
+ termios_printf ("Invalid baud rate %d", state.BaudRate);
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* -------------- Byte size ------------------ */
+
+ switch (state.ByteSize)
+ {
+ case 5:
+ t->c_cflag |= CS5;
+ break;
+ case 6:
+ t->c_cflag |= CS6;
+ break;
+ case 7:
+ t->c_cflag |= CS7;
+ break;
+ case 8:
+ t->c_cflag |= CS8;
+ break;
+ default:
+ /* Unsupported byte size! */
+ termios_printf ("Invalid byte size %d", state.ByteSize);
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* -------------- Stop bits ------------------ */
+
+ if (state.StopBits == TWOSTOPBITS)
+ t->c_cflag |= CSTOPB;
+
+ /* -------------- Parity ------------------ */
+
+ if (state.Parity == ODDPARITY)
+ t->c_cflag |= (PARENB | PARODD);
+ if (state.Parity == EVENPARITY)
+ t->c_cflag |= PARENB;
+
+ /* -------------- Parity errors ------------------ */
+
+ /* fParity combines the function of INPCK and NOT IGNPAR */
+ if (state.fParity == TRUE)
+ t->c_iflag |= INPCK;
+ else
+ t->c_iflag |= IGNPAR; /* not necessarily! */
+
+ /* -------------- Software flow control ------------------ */
+
+ /* transmission flow control */
+ if (state.fOutX)
+ t->c_iflag |= IXON;
+
+ /* reception flow control */
+ if (state.fInX)
+ t->c_iflag |= IXOFF;
+
+ t->c_cc[VSTART] = (state.XonChar ? state.XonChar : 0x11);
+ t->c_cc[VSTOP] = (state.XoffChar ? state.XoffChar : 0x13);
+
+ /* -------------- Hardware flow control ------------------ */
+ /* Some old flavors of Unix automatically enabled hardware flow
+ control when software flow control was not enabled. Since newer
+ Unices tend to require explicit setting of hardware flow-control,
+ this is what we do. */
+
+ /* Input flow-control */
+ if ((state.fRtsControl == RTS_CONTROL_HANDSHAKE) &&
+ (state.fOutxCtsFlow == TRUE))
+ t->c_cflag |= CRTSCTS;
+ if (state.fRtsControl == RTS_CONTROL_HANDSHAKE)
+ t->c_cflag |= CRTSXOFF;
+
+ /* -------------- CLOCAL --------------- */
+ /* DSR is only lead toggled only by CLOCAL. Check it to see if
+ CLOCAL was called. */
+ /* FIXME: If tcsetattr() hasn't been called previously, this may
+ give a false CLOCAL. */
+
+ if (state.fDsrSensitivity == FALSE)
+ t->c_cflag |= CLOCAL;
+
+ /* FIXME: need to handle IGNCR */
+#if 0
+ if (!get_r_binary ())
+ t->c_iflag |= IGNCR;
+#endif
+
+ if (!get_w_binary ())
+ t->c_oflag |= ONLCR;
+
+ t->c_cc[VTIME] = vtime_ / 100;
+ t->c_cc[VMIN] = vmin_;
+
+ debug_printf ("vmin_ %d, vtime_ %d", vmin_, vtime_);
+
+ return 0;
+}
+
+void
+fhandler_serial::fixup_after_fork (HANDLE parent)
+{
+ if (get_close_on_exec ())
+ this->fhandler_base::fixup_after_fork (parent);
+ overlapped_setup ();
+ debug_printf ("io_status.hEvent %p", io_status.hEvent);
+}
+
+void
+fhandler_serial::fixup_after_exec (HANDLE)
+{
+ overlapped_setup ();
+ debug_printf ("io_status.hEvent %p", io_status.hEvent);
+ return;
+}
+
+int
+fhandler_serial::dup (fhandler_base *child)
+{
+ fhandler_serial *fhc = (fhandler_serial *) child;
+ overlapped_setup ();
+ fhc->vmin_ = vmin_;
+ fhc->vtime_ = vtime_;
+ return fhandler_base::dup (child);
+}
diff --git a/winsup/cygwin/fhandler_socket.cc b/winsup/cygwin/fhandler_socket.cc
new file mode 100644
index 00000000000..27fa7101278
--- /dev/null
+++ b/winsup/cygwin/fhandler_socket.cc
@@ -0,0 +1,1264 @@
+/* fhandler_socket.cc. See fhandler.h for a description of the fhandler classes.
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc.
+
+ This file is part of Cygwin.
+
+ This software is a copyrighted work licensed under the terms of the
+ Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+ details. */
+
+/* #define DEBUG_NEST_ON 1 */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/uio.h>
+#include <asm/byteorder.h>
+
+#include <stdlib.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock2.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "cygwin/version.h"
+#include "perprocess.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "wsock_event.h"
+#include <unistd.h>
+
+#define SECRET_EVENT_NAME "cygwin.local_socket.secret.%d.%08x-%08x-%08x-%08x"
+#define ENTROPY_SOURCE_NAME "/dev/urandom"
+
+extern fhandler_socket *fdsock (int& fd, const char *name, SOCKET soc);
+extern "C" {
+int sscanf (const char *, const char *, ...);
+} /* End of "C" section */
+
+fhandler_dev_random* entropy_source;
+
+/* cygwin internal: map sockaddr into internet domain address */
+static int
+get_inet_addr (const struct sockaddr *in, int inlen,
+ struct sockaddr_in *out, int *outlen, int* secret = 0)
+{
+ int secret_buf [4];
+ int* secret_ptr = (secret ? : secret_buf);
+
+ if (in->sa_family == AF_INET)
+ {
+ *out = * (sockaddr_in *)in;
+ *outlen = inlen;
+ return 1;
+ }
+ else if (in->sa_family == AF_LOCAL)
+ {
+ int fd = open (in->sa_data, O_RDONLY);
+ if (fd == -1)
+ return 0;
+
+ int ret = 0;
+ char buf[128];
+ memset (buf, 0, sizeof buf);
+ if (read (fd, buf, sizeof buf) != -1)
+ {
+ sockaddr_in sin;
+ sin.sin_family = AF_INET;
+ sscanf (buf + strlen (SOCKET_COOKIE), "%hu %08x-%08x-%08x-%08x",
+ &sin.sin_port,
+ secret_ptr, secret_ptr + 1, secret_ptr + 2, secret_ptr + 3);
+ sin.sin_port = htons (sin.sin_port);
+ sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ *out = sin;
+ *outlen = sizeof sin;
+ ret = 1;
+ }
+ close (fd);
+ return ret;
+ }
+ else
+ {
+ set_errno (EAFNOSUPPORT);
+ return 0;
+ }
+}
+
+/**********************************************************************/
+/* fhandler_socket */
+
+fhandler_socket::fhandler_socket ()
+ : fhandler_base (), sun_path (NULL)
+{
+ set_need_fork_fixup ();
+ prot_info_ptr = (LPWSAPROTOCOL_INFOA) cmalloc (HEAP_BUF,
+ sizeof (WSAPROTOCOL_INFOA));
+}
+
+fhandler_socket::~fhandler_socket ()
+{
+ if (prot_info_ptr)
+ cfree (prot_info_ptr);
+ if (sun_path)
+ cfree (sun_path);
+}
+
+void
+fhandler_socket::set_connect_secret ()
+{
+ if (!entropy_source)
+ {
+ void *buf = malloc (sizeof (fhandler_dev_random));
+ entropy_source = new (buf) fhandler_dev_random ();
+ entropy_source->dev = *urandom_dev;
+ }
+ if (entropy_source &&
+ !entropy_source->open (NULL, O_RDONLY))
+ {
+ delete entropy_source;
+ entropy_source = NULL;
+ }
+ if (!entropy_source)
+ {
+ size_t len = sizeof (connect_secret);
+ entropy_source->read (connect_secret, len);
+ if (len != sizeof (connect_secret))
+ bzero ((char*) connect_secret, sizeof (connect_secret));
+ }
+}
+
+void
+fhandler_socket::get_connect_secret (char* buf)
+{
+ __small_sprintf (buf, "%08x-%08x-%08x-%08x",
+ connect_secret [0], connect_secret [1],
+ connect_secret [2], connect_secret [3]);
+}
+
+HANDLE
+fhandler_socket::create_secret_event (int* secret)
+{
+ char buf [128];
+ int* secret_ptr = (secret ? : connect_secret);
+ struct sockaddr_in sin;
+ int sin_len = sizeof (sin);
+
+ if (::getsockname (get_socket (), (struct sockaddr*) &sin, &sin_len))
+ {
+ debug_printf ("error getting local socket name (%d)", WSAGetLastError ());
+ return NULL;
+ }
+
+ __small_sprintf (buf, SECRET_EVENT_NAME, sin.sin_port,
+ secret_ptr [0], secret_ptr [1],
+ secret_ptr [2], secret_ptr [3]);
+ LPSECURITY_ATTRIBUTES sec = get_inheritance (true);
+ secret_event = CreateEvent (sec, FALSE, FALSE, buf);
+ if (!secret_event && GetLastError () == ERROR_ALREADY_EXISTS)
+ secret_event = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf);
+
+ if (!secret_event)
+ /* nothing to do */;
+ else if (sec == &sec_all_nih || sec == &sec_none_nih)
+ ProtectHandle (secret_event);
+ else
+ ProtectHandleINH (secret_event);
+
+ return secret_event;
+}
+
+void
+fhandler_socket::signal_secret_event ()
+{
+ if (!secret_event)
+ debug_printf ("no secret event?");
+ else
+ {
+ SetEvent (secret_event);
+ debug_printf ("signaled secret_event");
+ }
+}
+
+void
+fhandler_socket::close_secret_event ()
+{
+ if (secret_event)
+ ForceCloseHandle (secret_event);
+ secret_event = NULL;
+}
+
+int
+fhandler_socket::check_peer_secret_event (struct sockaddr_in* peer, int* secret)
+{
+ char buf [128];
+ HANDLE ev;
+ int* secret_ptr = (secret ? : connect_secret);
+
+ __small_sprintf (buf, SECRET_EVENT_NAME, peer->sin_port,
+ secret_ptr [0], secret_ptr [1],
+ secret_ptr [2], secret_ptr [3]);
+ ev = CreateEvent (&sec_all_nih, FALSE, FALSE, buf);
+ if (!ev && GetLastError () == ERROR_ALREADY_EXISTS)
+ {
+ debug_printf ("event \"%s\" already exists", buf);
+ ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf);
+ }
+
+ signal_secret_event ();
+
+ if (ev)
+ {
+ DWORD rc = WaitForSingleObject (ev, 10000);
+ debug_printf ("WFSO rc=%d", rc);
+ CloseHandle (ev);
+ return (rc == WAIT_OBJECT_0 ? 1 : 0 );
+ }
+ else
+ return 0;
+}
+
+void
+fhandler_socket::fixup_before_fork_exec (DWORD win_proc_id)
+{
+ if (!winsock2_active)
+ {
+ fhandler_base::fixup_before_fork_exec (win_proc_id);
+ debug_printf ("Without Winsock 2.0");
+ }
+ else if (!WSADuplicateSocketA (get_socket (), win_proc_id, prot_info_ptr))
+ debug_printf ("WSADuplicateSocket went fine, sock %p, win_proc_id %d, prot_info_ptr %p",
+ get_socket (), win_proc_id, prot_info_ptr);
+ else
+ {
+ debug_printf ("WSADuplicateSocket error, sock %p, win_proc_id %d, prot_info_ptr %p",
+ get_socket (), win_proc_id, prot_info_ptr);
+ set_winsock_errno ();
+ }
+}
+
+extern "C" void __stdcall load_wsock32 ();
+void
+fhandler_socket::fixup_after_fork (HANDLE parent)
+{
+ SOCKET new_sock;
+
+ debug_printf ("WSASocket begin, dwServiceFlags1=%d",
+ prot_info_ptr->dwServiceFlags1);
+
+ if ((new_sock = WSASocketA (FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ FROM_PROTOCOL_INFO,
+ prot_info_ptr, 0, 0)) == INVALID_SOCKET)
+ {
+ debug_printf ("WSASocket error");
+ set_winsock_errno ();
+ }
+ else if (!new_sock && !winsock2_active)
+ {
+ load_wsock32 ();
+ fhandler_base::fixup_after_fork (parent);
+ debug_printf ("Without Winsock 2.0");
+ }
+ else
+ {
+ debug_printf ("WSASocket went fine new_sock %p, old_sock %p", new_sock, get_io_handle ());
+ set_io_handle ((HANDLE) new_sock);
+ }
+
+ if (secret_event)
+ fork_fixup (parent, secret_event, "secret_event");
+}
+
+void
+fhandler_socket::fixup_after_exec (HANDLE parent)
+{
+ debug_printf ("here");
+ if (!get_close_on_exec ())
+ fixup_after_fork (parent);
+#if 0
+ else if (!winsock2_active)
+ closesocket (get_socket ());
+#endif
+}
+
+int
+fhandler_socket::dup (fhandler_base *child)
+{
+ debug_printf ("here");
+ fhandler_socket *fhs = (fhandler_socket *) child;
+ fhs->addr_family = addr_family;
+ fhs->set_io_handle (get_io_handle ());
+ if (get_addr_family () == AF_LOCAL)
+ fhs->set_sun_path (get_sun_path ());
+
+ fhs->fixup_before_fork_exec (GetCurrentProcessId ());
+ if (winsock2_active)
+ {
+ fhs->fixup_after_fork (hMainProc);
+ return 0;
+ }
+ return fhandler_base::dup (child);
+}
+
+int __stdcall
+fhandler_socket::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ int res = fhandler_base::fstat (buf, pc);
+ if (!res)
+ {
+ buf->st_mode &= ~_IFMT;
+ buf->st_mode |= _IFSOCK;
+ buf->st_ino = (ino_t) get_handle ();
+ }
+ return res;
+}
+
+int
+fhandler_socket::bind (const struct sockaddr *name, int namelen)
+{
+ int res = -1;
+
+ if (name->sa_family == AF_LOCAL)
+ {
+#define un_addr ((struct sockaddr_un *) name)
+ struct sockaddr_in sin;
+ int len = sizeof sin;
+ int fd;
+
+ if (strlen (un_addr->sun_path) >= UNIX_PATH_LEN)
+ {
+ set_errno (ENAMETOOLONG);
+ goto out;
+ }
+ sin.sin_family = AF_INET;
+ sin.sin_port = 0;
+ sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ if (::bind (get_socket (), (sockaddr *) &sin, len))
+ {
+ syscall_printf ("AF_LOCAL: bind failed %d", get_errno ());
+ set_winsock_errno ();
+ goto out;
+ }
+ if (::getsockname (get_socket (), (sockaddr *) &sin, &len))
+ {
+ syscall_printf ("AF_LOCAL: getsockname failed %d", get_errno ());
+ set_winsock_errno ();
+ goto out;
+ }
+
+ sin.sin_port = ntohs (sin.sin_port);
+ debug_printf ("AF_LOCAL: socket bound to port %u", sin.sin_port);
+
+ /* bind must fail if file system socket object already exists
+ so _open () is called with O_EXCL flag. */
+ fd = ::open (un_addr->sun_path, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, 0);
+ if (fd < 0)
+ {
+ if (get_errno () == EEXIST)
+ set_errno (EADDRINUSE);
+ goto out;
+ }
+
+ set_connect_secret ();
+
+ char buf[sizeof (SOCKET_COOKIE) + 80];
+ __small_sprintf (buf, "%s%u ", SOCKET_COOKIE, sin.sin_port);
+ get_connect_secret (strchr (buf, '\0'));
+ len = strlen (buf) + 1;
+
+ /* Note that the terminating nul is written. */
+ if (::write (fd, buf, len) != len)
+ {
+ save_errno here;
+ ::close (fd);
+ unlink (un_addr->sun_path);
+ }
+ else
+ {
+ ::close (fd);
+ chmod (un_addr->sun_path,
+ (S_IFSOCK | S_IRWXU | S_IRWXG | S_IRWXO) & ~cygheap->umask);
+ set_sun_path (un_addr->sun_path);
+ res = 0;
+ }
+#undef un_addr
+ }
+ else if (::bind (get_socket (), name, namelen))
+ set_winsock_errno ();
+ else
+ res = 0;
+
+out:
+ return res;
+}
+
+int
+fhandler_socket::connect (const struct sockaddr *name, int namelen)
+{
+ int res = -1;
+ BOOL secret_check_failed = FALSE;
+ BOOL in_progress = FALSE;
+ sockaddr_in sin;
+ int secret [4];
+
+ if (!get_inet_addr (name, namelen, &sin, &namelen, secret))
+ return -1;
+
+ res = ::connect (get_socket (), (sockaddr *) &sin, namelen);
+ if (res)
+ {
+ /* Special handling for connect to return the correct error code
+ when called on a non-blocking socket. */
+ if (is_nonblocking ())
+ {
+ DWORD err = WSAGetLastError ();
+ if (err == WSAEWOULDBLOCK || err == WSAEALREADY)
+ {
+ WSASetLastError (WSAEINPROGRESS);
+ in_progress = TRUE;
+ }
+ else if (err == WSAEINVAL)
+ WSASetLastError (WSAEISCONN);
+ }
+ set_winsock_errno ();
+ }
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ {
+ if (!res || in_progress)
+ {
+ if (!create_secret_event (secret))
+ {
+ secret_check_failed = TRUE;
+ }
+ else if (in_progress)
+ signal_secret_event ();
+ }
+
+ if (!secret_check_failed && !res)
+ {
+ if (!check_peer_secret_event (&sin, secret))
+ {
+ debug_printf ( "accept from unauthorized server" );
+ secret_check_failed = TRUE;
+ }
+ }
+
+ if (secret_check_failed)
+ {
+ close_secret_event ();
+ if (res)
+ closesocket (res);
+ set_errno (ECONNREFUSED);
+ res = -1;
+ }
+ }
+
+ if (WSAGetLastError () == WSAEINPROGRESS)
+ set_connect_state (CONNECT_PENDING);
+ else
+ set_connect_state (CONNECTED);
+ return res;
+}
+
+int
+fhandler_socket::listen (int backlog)
+{
+ int res = ::listen (get_socket (), backlog);
+ if (res)
+ set_winsock_errno ();
+ else
+ set_connect_state (CONNECTED);
+ return res;
+}
+
+int
+fhandler_socket::accept (struct sockaddr *peer, int *len)
+{
+ int res = -1;
+ WSAEVENT ev[2] = { WSA_INVALID_EVENT, signal_arrived };
+ BOOL secret_check_failed = FALSE;
+ BOOL in_progress = FALSE;
+
+ /* Allows NULL peer and len parameters. */
+ struct sockaddr_in peer_dummy;
+ int len_dummy;
+ if (!peer)
+ peer = (struct sockaddr *) &peer_dummy;
+ if (!len)
+ {
+ len_dummy = sizeof (struct sockaddr_in);
+ len = &len_dummy;
+ }
+
+ /* accept on NT fails if len < sizeof (sockaddr_in)
+ * some programs set len to
+ * sizeof (name.sun_family) + strlen (name.sun_path) for UNIX domain
+ */
+ if (len && ((unsigned) *len < sizeof (struct sockaddr_in)))
+ *len = sizeof (struct sockaddr_in);
+
+ if (!is_nonblocking ())
+ {
+ ev[0] = WSACreateEvent ();
+
+ if (ev[0] != WSA_INVALID_EVENT &&
+ !WSAEventSelect (get_socket (), ev[0], FD_ACCEPT))
+ {
+ WSANETWORKEVENTS sock_event;
+ int wait_result;
+
+ wait_result = WSAWaitForMultipleEvents (2, ev, FALSE, WSA_INFINITE,
+ FALSE);
+ if (wait_result == WSA_WAIT_EVENT_0)
+ WSAEnumNetworkEvents (get_socket (), ev[0], &sock_event);
+
+ /* Unset events for listening socket and
+ switch back to blocking mode */
+ WSAEventSelect (get_socket (), ev[0], 0);
+ unsigned long nonblocking = 0;
+ ioctlsocket (get_socket (), FIONBIO, &nonblocking);
+
+ switch (wait_result)
+ {
+ case WSA_WAIT_EVENT_0:
+ if (sock_event.lNetworkEvents & FD_ACCEPT)
+ {
+ if (sock_event.iErrorCode[FD_ACCEPT_BIT])
+ {
+ WSASetLastError (sock_event.iErrorCode[FD_ACCEPT_BIT]);
+ set_winsock_errno ();
+ res = -1;
+ goto done;
+ }
+ }
+ /* else; : Should never happen since FD_ACCEPT is the only event
+ that has been selected */
+ break;
+ case WSA_WAIT_EVENT_0 + 1:
+ debug_printf ("signal received during accept");
+ set_errno (EINTR);
+ res = -1;
+ goto done;
+ case WSA_WAIT_FAILED:
+ default: /* Should never happen */
+ WSASetLastError (WSAEFAULT);
+ set_winsock_errno ();
+ res = -1;
+ goto done;
+ }
+ }
+ }
+
+ res = ::accept (get_socket (), peer, len);
+
+ if ((SOCKET) res == (SOCKET) INVALID_SOCKET &&
+ WSAGetLastError () == WSAEWOULDBLOCK)
+ in_progress = TRUE;
+
+ if (get_addr_family () == AF_LOCAL && get_socket_type () == SOCK_STREAM)
+ {
+ if ((SOCKET) res != (SOCKET) INVALID_SOCKET || in_progress)
+ {
+ if (!create_secret_event ())
+ secret_check_failed = TRUE;
+ else if (in_progress)
+ signal_secret_event ();
+ }
+
+ if (!secret_check_failed &&
+ (SOCKET) res != (SOCKET) INVALID_SOCKET)
+ {
+ if (!check_peer_secret_event ((struct sockaddr_in*) peer))
+ {
+ debug_printf ("connect from unauthorized client");
+ secret_check_failed = TRUE;
+ }
+ }
+
+ if (secret_check_failed)
+ {
+ close_secret_event ();
+ if ((SOCKET) res != (SOCKET) INVALID_SOCKET)
+ closesocket (res);
+ set_errno (ECONNABORTED);
+ res = -1;
+ goto done;
+ }
+ }
+
+ {
+ cygheap_fdnew res_fd;
+ if (res_fd < 0)
+ /* FIXME: what is correct errno? */;
+ else if ((SOCKET) res == (SOCKET) INVALID_SOCKET)
+ set_winsock_errno ();
+ else
+ {
+ fhandler_socket* res_fh = fdsock (res_fd, get_name (), res);
+ if (get_addr_family () == AF_LOCAL)
+ res_fh->set_sun_path (get_sun_path ());
+ res_fh->set_addr_family (get_addr_family ());
+ res_fh->set_socket_type (get_socket_type ());
+ res = res_fd;
+ }
+ }
+
+done:
+ if (ev[0] != WSA_INVALID_EVENT)
+ WSACloseEvent (ev[0]);
+
+ return res;
+}
+
+int
+fhandler_socket::getsockname (struct sockaddr *name, int *namelen)
+{
+ int res = -1;
+
+ if (get_addr_family () == AF_LOCAL)
+ {
+ struct sockaddr_un *sun = (struct sockaddr_un *) name;
+ memset (sun, 0, *namelen);
+ sun->sun_family = AF_LOCAL;
+
+ if (!get_sun_path ())
+ sun->sun_path[0] = '\0';
+ else
+ /* According to SUSv2 "If the actual length of the address is
+ greater than the length of the supplied sockaddr structure, the
+ stored address will be truncated." We play it save here so
+ that the path always has a trailing 0 even if it's truncated. */
+ strncpy (sun->sun_path, get_sun_path (),
+ *namelen - sizeof *sun + sizeof sun->sun_path - 1);
+
+ *namelen = sizeof *sun - sizeof sun->sun_path
+ + strlen (sun->sun_path) + 1;
+ res = 0;
+ }
+ else
+ {
+ res = ::getsockname (get_socket (), name, namelen);
+ if (res)
+ set_winsock_errno ();
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::getpeername (struct sockaddr *name, int *namelen)
+{
+ int res = ::getpeername (get_socket (), name, namelen);
+ if (res)
+ set_winsock_errno ();
+
+ return res;
+}
+
+int
+fhandler_socket::readv (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ struct msghdr msg =
+ {
+ msg_name: NULL,
+ msg_namelen: 0,
+ msg_iov: (struct iovec *) iov, // const_cast
+ msg_iovlen: iovcnt,
+ msg_accrights: NULL,
+ msg_accrightslen: 0
+ };
+
+ return recvmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::recvfrom (void *ptr, size_t len, int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int res;
+ DWORD ret;
+
+ flags &= MSG_WINMASK;
+ if (!winsock2_active)
+ ret = res = ::recvfrom (get_socket (),
+ (char *) ptr, len, flags,
+ from, fromlen);
+ else
+ {
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking ())
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, (DWORD *) &flags,
+ from, fromlen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSARecvFrom (get_socket (), &wsabuf, 1, &ret, (DWORD *) &flags,
+ from, fromlen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ res = -1;
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+
+ return res;
+}
+
+int
+fhandler_socket::recvmsg (struct msghdr *msg, int flags, ssize_t tot)
+{
+ if (get_addr_family () == AF_LOCAL)
+ {
+ /* On AF_LOCAL sockets the (fixed-size) name of the shared memory
+ area used for descriptor passing is transmitted first.
+ If this string is empty, no descriptors are passed and we can
+ go ahead recv'ing the normal data blocks. Otherwise start
+ special handling for descriptor passing. */
+ /*TODO*/
+ }
+
+ struct iovec *const iov = msg->msg_iov;
+ const int iovcnt = msg->msg_iovlen;
+
+ struct sockaddr *from = (struct sockaddr *) msg->msg_name;
+ int *fromlen = from ? &msg->msg_namelen : NULL;
+
+ int res;
+
+ if (!winsock2_active)
+ {
+ if (iovcnt == 1)
+ res = recvfrom (iov->iov_base, iov->iov_len, flags,
+ from, fromlen);
+ else
+ {
+ if (tot == -1) // i.e. if not pre-calculated by the caller.
+ {
+ tot = 0;
+ const struct iovec *iovptr = iov + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ tot += iovptr->iov_len;
+ }
+ while (iovptr != iov);
+ }
+
+ char *buf = (char *) alloca (tot);
+
+ if (!buf)
+ {
+ set_errno (ENOMEM);
+ res = -1;
+ }
+ else
+ {
+ res = recvfrom (buf, tot, flags,
+ from, fromlen);
+
+ const struct iovec *iovptr = iov;
+ int nbytes = res;
+
+ while (nbytes > 0)
+ {
+ const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
+ memcpy (iovptr->iov_base, buf, frag);
+ buf += frag;
+ iovptr += 1;
+ nbytes -= frag;
+ }
+ }
+ }
+ }
+ else
+ {
+ WSABUF wsabuf[iovcnt];
+
+ {
+ const struct iovec *iovptr = iov + iovcnt;
+ WSABUF *wsaptr = wsabuf + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ wsaptr -= 1;
+ wsaptr->len = iovptr->iov_len;
+ wsaptr->buf = (char *) iovptr->iov_base;
+ }
+ while (wsaptr != wsabuf);
+ }
+
+ DWORD ret;
+
+ if (is_nonblocking ())
+ res = WSARecvFrom (get_socket (),
+ wsabuf, iovcnt, &ret, (DWORD *) &flags,
+ from, fromlen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSARecvFrom (get_socket (),
+ wsabuf, iovcnt, &ret, (DWORD *) &flags,
+ from, fromlen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ res = -1;
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::writev (const struct iovec *const iov, const int iovcnt,
+ ssize_t tot)
+{
+ struct msghdr msg =
+ {
+ msg_name: NULL,
+ msg_namelen: 0,
+ msg_iov: (struct iovec *) iov, // const_cast
+ msg_iovlen: iovcnt,
+ msg_accrights: NULL,
+ msg_accrightslen: 0
+ };
+
+ return sendmsg (&msg, 0, tot);
+}
+
+int
+fhandler_socket::sendto (const void *ptr, size_t len, int flags,
+ const struct sockaddr *to, int tolen)
+{
+ sockaddr_in sin;
+
+ if (to && !get_inet_addr (to, tolen, &sin, &tolen))
+ return -1;
+
+ int res;
+ DWORD ret;
+
+ if (!winsock2_active)
+ ret = res = ::sendto (get_socket (), (const char *) ptr, len,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL), tolen);
+ else
+ {
+ WSABUF wsabuf = { len, (char *) ptr };
+
+ if (is_nonblocking ())
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL), tolen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSASendTo (get_socket (), &wsabuf, 1, &ret,
+ flags & MSG_WINMASK,
+ (to ? (const struct sockaddr *) &sin : NULL), tolen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ res = -1;
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+
+ /* Special handling for SIGPIPE */
+ if (res == -1 && get_errno () == ESHUTDOWN)
+ {
+ set_errno (EPIPE);
+ if (! (flags & MSG_NOSIGNAL))
+ raise (SIGPIPE);
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::sendmsg (const struct msghdr *msg, int flags, ssize_t tot)
+{
+ if (get_addr_family () == AF_LOCAL)
+ {
+ /* For AF_LOCAL/AF_UNIX sockets, if descriptors are given, start
+ the special handling for descriptor passing. Otherwise just
+ transmit an empty string to tell the receiver that no
+ descriptor passing is done. */
+ /*TODO*/
+ }
+
+ struct iovec *const iov = msg->msg_iov;
+ const int iovcnt = msg->msg_iovlen;
+
+ int res;
+
+ if (!winsock2_active)
+ {
+ if (iovcnt == 1)
+ res = sendto (iov->iov_base, iov->iov_len, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen);
+ else
+ {
+ if (tot == -1) // i.e. if not pre-calculated by the caller.
+ {
+ tot = 0;
+ const struct iovec *iovptr = iov + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ tot += iovptr->iov_len;
+ }
+ while (iovptr != iov);
+ }
+
+ char *const buf = (char *) alloca (tot);
+
+ if (!buf)
+ {
+ set_errno (ENOMEM);
+ res = -1;
+ }
+ else
+ {
+ char *bufptr = buf;
+ const struct iovec *iovptr = iov;
+ int nbytes = tot;
+
+ while (nbytes != 0)
+ {
+ const int frag = min (nbytes, (ssize_t) iovptr->iov_len);
+ memcpy (bufptr, iovptr->iov_base, frag);
+ bufptr += frag;
+ iovptr += 1;
+ nbytes -= frag;
+ }
+
+ res = sendto (buf, tot, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen);
+ }
+ }
+ }
+ else
+ {
+ WSABUF wsabuf[iovcnt];
+
+ {
+ const struct iovec *iovptr = iov + iovcnt;
+ WSABUF *wsaptr = wsabuf + iovcnt;
+ do
+ {
+ iovptr -= 1;
+ wsaptr -= 1;
+ wsaptr->len = iovptr->iov_len;
+ wsaptr->buf = (char *) iovptr->iov_base;
+ }
+ while (wsaptr != wsabuf);
+ }
+
+ DWORD ret;
+
+ if (is_nonblocking ())
+ res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen,
+ NULL, NULL);
+ else
+ {
+ wsock_event wsock_evt;
+ res = WSASendTo (get_socket (), wsabuf, iovcnt, &ret, flags,
+ (struct sockaddr *) msg->msg_name,
+ msg->msg_namelen,
+ wsock_evt.prepare (), NULL);
+
+ if (res == SOCKET_ERROR && WSAGetLastError () == WSA_IO_PENDING)
+ ret = res = wsock_evt.wait (get_socket (), (DWORD *) &flags);
+ }
+
+ if (res == SOCKET_ERROR)
+ {
+ res = -1;
+ set_winsock_errno ();
+ }
+ else
+ res = ret;
+ }
+
+ return res;
+}
+
+int
+fhandler_socket::shutdown (int how)
+{
+ int res = ::shutdown (get_socket (), how);
+
+ if (res)
+ set_winsock_errno ();
+ else
+ switch (how)
+ {
+ case SHUT_RD:
+ set_shutdown_read ();
+ break;
+ case SHUT_WR:
+ set_shutdown_write ();
+ break;
+ case SHUT_RDWR:
+ set_shutdown_read ();
+ set_shutdown_write ();
+ break;
+ }
+ return res;
+}
+
+int
+fhandler_socket::close ()
+{
+ int res = 0;
+
+ /* HACK to allow a graceful shutdown even if shutdown() hasn't been
+ called by the application. Note that this isn't the ultimate
+ solution but it helps in many cases. */
+ struct linger linger;
+ linger.l_onoff = 1;
+ linger.l_linger = 240; /* seconds. default 2MSL value according to MSDN. */
+ setsockopt (get_socket (), SOL_SOCKET, SO_LINGER,
+ (const char *)&linger, sizeof linger);
+
+ while ((res = closesocket (get_socket ())) != 0)
+ {
+ if (WSAGetLastError () != WSAEWOULDBLOCK)
+ {
+ set_winsock_errno ();
+ res = -1;
+ break;
+ }
+ if (WaitForSingleObject (signal_arrived, 10) == WAIT_OBJECT_0)
+ {
+ set_errno (EINTR);
+ res = -1;
+ break;
+ }
+ WSASetLastError (0);
+ }
+
+ close_secret_event ();
+
+ debug_printf ("%d = fhandler_socket::close()", res);
+ return res;
+}
+
+#define ASYNC_MASK (FD_READ|FD_WRITE|FD_OOB|FD_ACCEPT|FD_CONNECT)
+
+int
+fhandler_socket::ioctl (unsigned int cmd, void *p)
+{
+ extern int get_ifconf (struct ifconf *ifc, int what); /* net.cc */
+ int res;
+ struct ifconf ifc, *ifcp;
+ struct ifreq *ifr, *ifrp;
+
+ switch (cmd)
+ {
+ case SIOCGIFCONF:
+ ifcp = (struct ifconf *) p;
+ if (!ifcp)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ res = get_ifconf (ifcp, cmd);
+ if (res)
+ debug_printf ("error in get_ifconf");
+ break;
+ case SIOCGIFFLAGS:
+ ifr = (struct ifreq *) p;
+ if (ifr == 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ ifr->ifr_flags = IFF_NOTRAILERS | IFF_UP | IFF_RUNNING;
+ if (!strncmp(ifr->ifr_name, "lo", 2)
+ || ntohl (((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr)
+ == INADDR_LOOPBACK)
+ ifr->ifr_flags |= IFF_LOOPBACK;
+ else
+ ifr->ifr_flags |= IFF_BROADCAST;
+ res = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFADDR:
+ case SIOCGIFHWADDR:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ {
+ ifc.ifc_len = 2048;
+ ifc.ifc_buf = (char *) alloca (2048);
+
+ ifr = (struct ifreq *) p;
+ if (ifr == 0)
+ {
+ debug_printf ("ifr == NULL");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ res = get_ifconf (&ifc, cmd);
+ if (res)
+ {
+ debug_printf ("error in get_ifconf");
+ break;
+ }
+
+ debug_printf (" name: %s", ifr->ifr_name);
+ for (ifrp = ifc.ifc_req;
+ (caddr_t) ifrp < ifc.ifc_buf + ifc.ifc_len;
+ ++ifrp)
+ {
+ debug_printf ("testname: %s", ifrp->ifr_name);
+ if (! strcmp (ifrp->ifr_name, ifr->ifr_name))
+ {
+ switch (cmd)
+ {
+ case SIOCGIFADDR:
+ ifr->ifr_addr = ifrp->ifr_addr;
+ break;
+ case SIOCGIFBRDADDR:
+ ifr->ifr_broadaddr = ifrp->ifr_broadaddr;
+ break;
+ case SIOCGIFNETMASK:
+ ifr->ifr_netmask = ifrp->ifr_netmask;
+ break;
+ case SIOCGIFHWADDR:
+ ifr->ifr_hwaddr = ifrp->ifr_hwaddr;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = ifrp->ifr_metric;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = ifrp->ifr_mtu;
+ break;
+ }
+ break;
+ }
+ }
+ if ((caddr_t) ifrp >= ifc.ifc_buf + ifc.ifc_len)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ break;
+ }
+ case FIOASYNC:
+ res = WSAAsyncSelect (get_socket (), gethwnd (), WM_ASYNCIO,
+ *(int *) p ? ASYNC_MASK : 0);
+ syscall_printf ("Async I/O on socket %s",
+ *(int *) p ? "started" : "cancelled");
+ set_async (*(int *) p);
+ break;
+ default:
+ /* We must cancel WSAAsyncSelect (if any) before setting socket to
+ * blocking mode
+ */
+ if (cmd == FIONBIO && *(int *) p == 0)
+ WSAAsyncSelect (get_socket (), gethwnd (), 0, 0);
+ res = ioctlsocket (get_socket (), cmd, (unsigned long *) p);
+ if (res == SOCKET_ERROR)
+ set_winsock_errno ();
+ if (cmd == FIONBIO)
+ {
+ syscall_printf ("socket is now %sblocking",
+ *(int *) p ? "non" : "");
+ /* Start AsyncSelect if async socket unblocked */
+ if (*(int *) p && get_async ())
+ WSAAsyncSelect (get_socket (), gethwnd (), WM_ASYNCIO, ASYNC_MASK);
+
+ set_nonblocking (*(int *) p);
+ }
+ break;
+ }
+ syscall_printf ("%d = ioctl_socket (%x, %x)", res, cmd, p);
+ return res;
+}
+
+int
+fhandler_socket::fcntl (int cmd, void *arg)
+{
+ int res = 0;
+ int request, current;
+
+ switch (cmd)
+ {
+ case F_SETFL:
+ {
+ /* Carefully test for the O_NONBLOCK or deprecated OLD_O_NDELAY flag.
+ Set only the flag that has been passed in. If both are set, just
+ record O_NONBLOCK. */
+ int new_flags = (int) arg & O_NONBLOCK_MASK;
+ if ((new_flags & OLD_O_NDELAY) && (new_flags & O_NONBLOCK))
+ new_flags = O_NONBLOCK;
+ current = get_flags () & O_NONBLOCK_MASK;
+ request = new_flags ? 1 : 0;
+ if (!!current != !!new_flags && (res = ioctl (FIONBIO, &request)))
+ break;
+ set_flags ((get_flags () & ~O_NONBLOCK_MASK) | new_flags);
+ break;
+ }
+ default:
+ res = fhandler_base::fcntl (cmd, arg);
+ break;
+ }
+ return res;
+}
+
+void
+fhandler_socket::set_close_on_exec (int val)
+{
+ if (!winsock2_active) /* < Winsock 2.0 */
+ set_inheritance (get_handle (), val);
+ set_close_on_exec_flag (val);
+ debug_printf ("set close_on_exec for %s to %d", get_name (), val);
+}
+
+void
+fhandler_socket::set_sun_path (const char *path)
+{
+ sun_path = path ? cstrdup (path) : NULL;
+}
diff --git a/winsup/cygwin/fhandler_tape.cc b/winsup/cygwin/fhandler_tape.cc
new file mode 100644
index 00000000000..7affc5524b9
--- /dev/null
+++ b/winsup/cygwin/fhandler_tape.cc
@@ -0,0 +1,839 @@
+/* fhandler_tape.cc. See fhandler.h for a description of the fhandler
+ classes.
+
+ Copyright 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <sys/termios.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/mtio.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+
+/**********************************************************************/
+/* fhandler_dev_tape */
+
+void
+fhandler_dev_tape::clear (void)
+{
+ lasterr = 0;
+ fhandler_dev_raw::clear ();
+}
+
+int
+fhandler_dev_tape::is_eom (int win_error)
+{
+ int ret = ((win_error == ERROR_END_OF_MEDIA)
+ || (win_error == ERROR_EOM_OVERFLOW)
+ || (win_error == ERROR_NO_DATA_DETECTED));
+ if (ret)
+ debug_printf ("end of medium");
+ return ret;
+}
+
+int
+fhandler_dev_tape::is_eof (int win_error)
+{
+ int ret = ((win_error == ERROR_FILEMARK_DETECTED)
+ || (win_error == ERROR_SETMARK_DETECTED));
+ if (ret)
+ debug_printf ("end of file");
+ return ret;
+}
+
+fhandler_dev_tape::fhandler_dev_tape ()
+ : fhandler_dev_raw ()
+{
+ debug_printf ("unit: %d", dev.minor);
+}
+
+int
+fhandler_dev_tape::open (path_conv *real_path, int flags, mode_t)
+{
+ int ret;
+
+ devbufsiz = 1L;
+
+ ret = fhandler_dev_raw::open (real_path, flags);
+ if (ret)
+ {
+ struct mtget get;
+ struct mtop op;
+ struct mtpos pos;
+
+ if (!ioctl (MTIOCGET, &get))
+ /* Tape drive supports and is set to variable block size. */
+ if (get.mt_dsreg == 0)
+ devbufsiz = get.mt_maxblksize;
+ else
+ devbufsiz = get.mt_dsreg;
+ varblkop = get.mt_dsreg == 0;
+
+ if (devbufsiz > 1L)
+ devbuf = new char [devbufsiz];
+
+ /* The following rewind in position 0 solves a problem which appears
+ * in case of multi volume archives: The last ReadFile on first medium
+ * returns ERROR_NO_DATA_DETECTED. After media change, all subsequent
+ * ReadFile calls return ERROR_NO_DATA_DETECTED, too.
+ * The call to tape_set_pos seems to reset some internal flags. */
+ if ((!ioctl (MTIOCPOS, &pos)) && (!pos.mt_blkno))
+ {
+ op.mt_op = MTREW;
+ ioctl (MTIOCTOP, &op);
+ }
+
+ if (flags & O_APPEND)
+ {
+ /* In append mode, seek to beginning of next filemark */
+ op.mt_op = MTFSFM;
+ op.mt_count = 1;
+ ioctl (MTIOCTOP, &op);
+ }
+ }
+
+ return ret;
+}
+
+int
+fhandler_dev_tape::close (void)
+{
+ struct mtop op;
+ int ret = 0;
+
+ if (is_writing)
+ {
+ ret = writebuf ();
+ if ((has_written) && (! eom_detected))
+ {
+ /* if last operation was writing, write a filemark */
+ debug_printf ("writing filemark");
+ op.mt_op = MTWEOF;
+ op.mt_count = 1;
+ ioctl (MTIOCTOP, &op);
+ }
+ }
+
+ // To protected reads on signaling (e.g. Ctrl-C)
+ eof_detected = 1;
+
+ if (is_rewind_device ())
+ {
+ debug_printf ("rewinding");
+ op.mt_op = MTREW;
+ ioctl (MTIOCTOP, &op);
+ }
+
+ if (ret)
+ {
+ fhandler_dev_raw::close ();
+ return ret;
+ }
+
+ return fhandler_dev_raw::close ();
+}
+
+int
+fhandler_dev_tape::fstat (struct __stat64 *buf, path_conv *pc)
+{
+ int ret;
+
+ if (!(ret = fhandler_base::fstat (buf, pc)))
+ {
+ struct mtget get;
+
+ if (!ioctl (MTIOCGET, &get))
+ buf->st_blocks = get.mt_capacity / buf->st_blksize;
+ }
+
+ return ret;
+}
+
+__off64_t
+fhandler_dev_tape::lseek (__off64_t offset, int whence)
+{
+ struct mtop op;
+ struct mtpos pos;
+
+ debug_printf ("lseek (%s, %d, %d)", get_name (), offset, whence);
+
+ writebuf ();
+ eom_detected = eof_detected = 0;
+ lastblk_to_read = 0;
+ devbufstart = devbufend = 0;
+
+ if (ioctl (MTIOCPOS, &pos))
+ {
+ return ILLEGAL_SEEK;
+ }
+
+ switch (whence)
+ {
+ case SEEK_END:
+ op.mt_op = MTFSF;
+ op.mt_count = 1;
+ if (ioctl (MTIOCTOP, &op))
+ return -1;
+ break;
+ case SEEK_SET:
+ if (whence == SEEK_SET && offset < 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ break;
+ case SEEK_CUR:
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ op.mt_op = MTFSR;
+ op.mt_count = offset / devbufsiz
+ - (whence == SEEK_SET ? pos.mt_blkno : 0);
+
+ if (op.mt_count < 0)
+ {
+ op.mt_op = MTBSR;
+ op.mt_count = -op.mt_count;
+ }
+
+ if (ioctl (MTIOCTOP, &op) || ioctl (MTIOCPOS, &pos))
+ return -1;
+
+ return (pos.mt_blkno * devbufsiz);
+}
+
+int
+fhandler_dev_tape::dup (fhandler_base *child)
+{
+ fhandler_dev_tape *fhc = (fhandler_dev_tape *) child;
+
+ fhc->lasterr = lasterr;
+ return fhandler_dev_raw::dup (child);
+}
+
+int
+fhandler_dev_tape::ioctl (unsigned int cmd, void *buf)
+{
+ int ret = NO_ERROR;
+ unsigned long block;
+
+ if (cmd == MTIOCTOP)
+ {
+ struct mtop *op = (struct mtop *) buf;
+
+ if (! op)
+ ret = ERROR_INVALID_PARAMETER;
+ else
+ switch (op->mt_op)
+ {
+ case MTRESET:
+ break;
+ case MTFSF:
+ ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count);
+ break;
+ case MTBSF:
+ ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count);
+ break;
+ case MTFSR:
+ ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, op->mt_count);
+ break;
+ case MTBSR:
+ ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS, -op->mt_count);
+ break;
+ case MTWEOF:
+ if (tape_get_feature (TAPE_DRIVE_WRITE_FILEMARKS))
+ ret = tape_write_marks (TAPE_FILEMARKS, op->mt_count);
+ else if (tape_get_feature (TAPE_DRIVE_WRITE_LONG_FMKS))
+ ret = tape_write_marks (TAPE_LONG_FILEMARKS, op->mt_count);
+ else
+ ret = tape_write_marks (TAPE_SHORT_FILEMARKS, op->mt_count);
+ break;
+ case MTREW:
+ ret = tape_set_pos (TAPE_REWIND, 0);
+ break;
+ case MTOFFL:
+ ret = tape_prepare (TAPE_UNLOAD);
+ break;
+ case MTNOP:
+ break;
+ case MTRETEN:
+ if (! tape_get_feature (TAPE_DRIVE_END_OF_DATA))
+ ret = ERROR_INVALID_PARAMETER;
+ else if (! (ret = tape_set_pos (TAPE_REWIND, 0, FALSE)))
+ ret = tape_prepare (TAPE_TENSION);
+ break;
+ case MTBSFM:
+ ret = tape_set_pos (TAPE_SPACE_FILEMARKS, -op->mt_count, TRUE);
+ break;
+ case MTFSFM:
+ ret = tape_set_pos (TAPE_SPACE_FILEMARKS, op->mt_count, TRUE);
+ break;
+ case MTEOM:
+ if (tape_get_feature (TAPE_DRIVE_END_OF_DATA))
+ ret = tape_set_pos (TAPE_SPACE_END_OF_DATA, 0);
+ else
+ ret = tape_set_pos (TAPE_SPACE_FILEMARKS, 32767);
+ break;
+ case MTERASE:
+ ret = tape_erase (TAPE_ERASE_SHORT);
+ break;
+ case MTRAS1:
+ case MTRAS2:
+ case MTRAS3:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ case MTSETBLK:
+ {
+ long min, max;
+
+ if (! tape_get_feature (TAPE_DRIVE_SET_BLOCK_SIZE))
+ {
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ ret = tape_get_blocksize (&min, NULL, &max, NULL);
+ if (ret)
+ break;
+ if (devbuf && (size_t) op->mt_count == devbufsiz && !varblkop)
+ {
+ ret = 0;
+ break;
+ }
+ if ((op->mt_count == 0
+ && !tape_get_feature (TAPE_DRIVE_VARIABLE_BLOCK))
+ || (op->mt_count > 0
+ && (op->mt_count < min || op->mt_count > max)))
+ {
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ if (devbuf && op->mt_count > 0
+ && (size_t) op->mt_count < devbufend - devbufstart)
+ {
+ ret = ERROR_MORE_DATA;
+ break;
+ }
+ if (! (ret = tape_set_blocksize (op->mt_count)))
+ {
+ size_t size = 0;
+ if (op->mt_count == 0)
+ {
+ struct mtget get;
+ if ((ret = tape_status (&get)) != NO_ERROR)
+ break;
+ size = get.mt_maxblksize;
+ ret = NO_ERROR;
+ }
+ char *buf = NULL;
+ if (size > 1L && !(buf = new char [size]))
+ {
+ ret = ERROR_OUTOFMEMORY;
+ break;
+ }
+ if (devbufsiz > 1L && size > 1L)
+ {
+ memcpy (buf, devbuf + devbufstart,
+ devbufend - devbufstart);
+ devbufend -= devbufstart;
+ }
+ else
+ devbufend = 0;
+ if (devbufsiz > 1L)
+ delete [] devbuf;
+ devbufstart = 0;
+ devbuf = buf;
+ devbufsiz = size;
+ varblkop = op->mt_count == 0;
+ }
+ }
+ break;
+ case MTSETDENSITY:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ case MTSEEK:
+ if (tape_get_feature (TAPE_DRIVE_ABSOLUTE_BLK))
+ {
+ ret = tape_set_pos (TAPE_ABSOLUTE_BLOCK, op->mt_count);
+ break;
+ }
+ if (! (ret = tape_get_pos (&block)))
+ {
+ ret = tape_set_pos (TAPE_SPACE_RELATIVE_BLOCKS,
+ op->mt_count - block);
+ }
+ break;
+ case MTTELL:
+ if (! (ret = tape_get_pos (&block)))
+ op->mt_count = block;
+ break;
+ case MTSETDRVBUFFER:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ case MTFSS:
+ ret = tape_set_pos (TAPE_SPACE_SETMARKS, op->mt_count);
+ break;
+ case MTBSS:
+ ret = tape_set_pos (TAPE_SPACE_SETMARKS, -op->mt_count);
+ break;
+ case MTWSM:
+ ret = tape_write_marks (TAPE_SETMARKS, op->mt_count);
+ break;
+ case MTLOCK:
+ ret = tape_prepare (TAPE_LOCK);
+ break;
+ case MTUNLOCK:
+ ret = tape_prepare (TAPE_UNLOCK);
+ break;
+ case MTLOAD:
+ ret = tape_prepare (TAPE_LOAD);
+ break;
+ case MTUNLOAD:
+ ret = tape_prepare (TAPE_UNLOAD);
+ break;
+ case MTCOMPRESSION:
+ ret = tape_compression (op->mt_count);
+ break;
+ case MTSETPART:
+ case MTMKPART:
+ default:
+ ret = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ }
+ else if (cmd == MTIOCGET)
+ ret = tape_status ((struct mtget *) buf);
+ else if (cmd == MTIOCPOS)
+ {
+ ret = ERROR_INVALID_PARAMETER;
+ if (buf && (ret = tape_get_pos (&block)))
+ ((struct mtpos *) buf)->mt_blkno = block;
+ }
+ else
+ return fhandler_dev_raw::ioctl (cmd, buf);
+
+ if (ret != NO_ERROR)
+ {
+ SetLastError (ret);
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+/* ------------------------------------------------------------------ */
+/* Private functions used by `ioctl' */
+/* ------------------------------------------------------------------ */
+
+static int
+tape_error (DWORD lasterr, const char *txt)
+{
+ if (lasterr)
+ debug_printf ("%s: error: %d", txt, lasterr);
+
+ return lasterr;
+}
+
+int
+fhandler_dev_tape::tape_write_marks (int marktype, DWORD len)
+{
+ syscall_printf ("write_tapemark");
+ while (((lasterr = WriteTapemark (get_handle (),
+ marktype,
+ len,
+ FALSE)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+
+ return tape_error (lasterr, "tape_write_marks");
+}
+
+int
+fhandler_dev_tape::tape_get_pos (unsigned long *ret)
+{
+ DWORD part, low, high;
+
+ while (((lasterr = GetTapePosition (get_handle (),
+ TAPE_ABSOLUTE_POSITION,
+ &part,
+ &low,
+ &high)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+ if (! tape_error (lasterr, "tape_get_pos") && ret)
+ *ret = low;
+
+ return lasterr;
+}
+
+static int _tape_set_pos (HANDLE hTape, int mode, long count)
+{
+ int err;
+
+ while (((err = SetTapePosition (hTape,
+ mode,
+ 1,
+ count,
+ count < 0 ? -1 : 0,
+ FALSE)) == ERROR_MEDIA_CHANGED)
+ || (err == ERROR_BUS_RESET))
+ ;
+
+ return err;
+}
+
+int
+fhandler_dev_tape::tape_set_pos (int mode, long count, BOOLEAN sfm_func)
+{
+ unsigned long pos, tgtpos;
+
+ switch (mode)
+ {
+ case TAPE_SPACE_RELATIVE_BLOCKS:
+ lasterr = tape_get_pos (&pos);
+
+ if (lasterr)
+ return lasterr;
+
+ tgtpos = pos + count;
+
+ while (((lasterr = _tape_set_pos (get_handle (),
+ mode,
+ count)) == ERROR_FILEMARK_DETECTED)
+ || (lasterr == ERROR_SETMARK_DETECTED))
+ {
+ lasterr = tape_get_pos (&pos);
+ if (lasterr)
+ return lasterr;
+ count = tgtpos - pos;
+ }
+
+ if (lasterr == ERROR_BEGINNING_OF_MEDIA && ! tgtpos)
+ lasterr = NO_ERROR;
+
+ break;
+ case TAPE_SPACE_FILEMARKS:
+ if (count < 0)
+ {
+ if (pos > 0)
+ {
+ if ((! _tape_set_pos (get_handle (),
+ TAPE_SPACE_RELATIVE_BLOCKS,
+ -1))
+ || (sfm_func))
+ ++count;
+ _tape_set_pos (get_handle (), TAPE_SPACE_RELATIVE_BLOCKS, 1);
+ }
+
+ while (! (lasterr = _tape_set_pos (get_handle (), mode, -1))
+ && count++ < 0)
+ ;
+
+ if (lasterr == ERROR_BEGINNING_OF_MEDIA)
+ {
+ if (! count)
+ lasterr = NO_ERROR;
+ }
+ else if (! sfm_func)
+ lasterr = _tape_set_pos (get_handle (), mode, 1);
+ }
+ else
+ {
+ if (sfm_func)
+ {
+ if (_tape_set_pos (get_handle (),
+ TAPE_SPACE_RELATIVE_BLOCKS,
+ 1) == ERROR_FILEMARK_DETECTED)
+ ++count;
+ _tape_set_pos (get_handle (), TAPE_SPACE_RELATIVE_BLOCKS, -1);
+ }
+
+ if (! (lasterr = _tape_set_pos (get_handle (), mode, count))
+ && sfm_func)
+ lasterr = _tape_set_pos (get_handle (), mode, -1);
+ }
+ break;
+ case TAPE_SPACE_SETMARKS:
+ case TAPE_ABSOLUTE_BLOCK:
+ case TAPE_SPACE_END_OF_DATA:
+ case TAPE_REWIND:
+ lasterr = _tape_set_pos (get_handle (), mode, count);
+ break;
+ }
+
+ return tape_error (lasterr, "tape_set_pos");
+}
+
+int
+fhandler_dev_tape::tape_erase (int mode)
+{
+ DWORD varlen;
+ TAPE_GET_DRIVE_PARAMETERS dp;
+
+ while (((lasterr = GetTapeParameters (get_handle (),
+ GET_TAPE_DRIVE_INFORMATION,
+ (varlen = sizeof dp, &varlen),
+ &dp)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+
+ switch (mode)
+ {
+ case TAPE_ERASE_SHORT:
+ if (! lasterr && ! (dp.FeaturesLow & TAPE_DRIVE_ERASE_SHORT))
+ mode = TAPE_ERASE_LONG;
+ break;
+ case TAPE_ERASE_LONG:
+ if (! lasterr && ! (dp.FeaturesLow & TAPE_DRIVE_ERASE_LONG))
+ mode = TAPE_ERASE_SHORT;
+ break;
+ }
+
+ return tape_error (EraseTape (get_handle (), mode, FALSE), "tape_erase");
+}
+
+int
+fhandler_dev_tape::tape_prepare (int action)
+{
+ while (((lasterr = PrepareTape (get_handle (),
+ action,
+ FALSE)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+ return tape_error (lasterr, "tape_prepare");
+}
+
+BOOLEAN
+fhandler_dev_tape::tape_get_feature (DWORD parm)
+{
+ DWORD varlen;
+ TAPE_GET_DRIVE_PARAMETERS dp;
+
+ while (((lasterr = GetTapeParameters (get_handle (),
+ GET_TAPE_DRIVE_INFORMATION,
+ (varlen = sizeof dp, &varlen),
+ &dp)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+
+ if (lasterr)
+ return FALSE;
+
+ return ((parm & TAPE_DRIVE_HIGH_FEATURES)
+ ? ((dp.FeaturesHigh & parm) != 0)
+ : ((dp.FeaturesLow & parm) != 0));
+}
+
+int
+fhandler_dev_tape::tape_get_blocksize (long *min, long *def, long *max, long *cur)
+{
+ DWORD varlen;
+ TAPE_GET_DRIVE_PARAMETERS dp;
+ TAPE_GET_MEDIA_PARAMETERS mp;
+
+ while (((lasterr = GetTapeParameters (get_handle (),
+ GET_TAPE_DRIVE_INFORMATION,
+ (varlen = sizeof dp, &varlen),
+ &dp)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+
+ if (lasterr)
+ return tape_error (lasterr, "tape_get_blocksize");
+
+ while (((lasterr = GetTapeParameters (get_handle (),
+ GET_TAPE_MEDIA_INFORMATION,
+ (varlen = sizeof dp, &varlen),
+ &mp)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+
+ if (lasterr)
+ return tape_error (lasterr, "tape_get_blocksize");
+
+ if (min)
+ *min = (long) dp.MinimumBlockSize;
+ if (def)
+ *def = (long) dp.DefaultBlockSize;
+ if (max)
+ *max = (long) dp.MaximumBlockSize;
+ if (cur)
+ *cur = (long) mp.BlockSize;
+
+ return tape_error (lasterr, "tape_get_blocksize");
+}
+
+int
+fhandler_dev_tape::tape_set_blocksize (long count)
+{
+ long min, max;
+ TAPE_SET_MEDIA_PARAMETERS mp;
+
+ lasterr = tape_get_blocksize (&min, NULL, &max, NULL);
+
+ if (lasterr)
+ return lasterr;
+
+ if (count != 0 && (count < min || count > max))
+ return tape_error (ERROR_INVALID_PARAMETER, "tape_set_blocksize");
+
+ mp.BlockSize = count;
+
+ return tape_error (SetTapeParameters (get_handle (),
+ SET_TAPE_MEDIA_INFORMATION,
+ &mp),
+ "tape_set_blocksize");
+}
+
+static long long
+get_ll (PLARGE_INTEGER i)
+{
+ long long l = 0;
+
+ l = i->HighPart;
+ l <<= 32;
+ l |= i->LowPart;
+ return l;
+}
+
+int
+fhandler_dev_tape::tape_status (struct mtget *get)
+{
+ DWORD varlen;
+ TAPE_GET_DRIVE_PARAMETERS dp;
+ TAPE_GET_MEDIA_PARAMETERS mp;
+ int notape = 0;
+
+ if (! get)
+ return ERROR_INVALID_PARAMETER;
+
+ while (((lasterr = GetTapeParameters (get_handle (),
+ GET_TAPE_DRIVE_INFORMATION,
+ (varlen = sizeof dp, &varlen),
+ &dp)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+
+ /* Setting varlen to sizeof DP is by intention, actually! Never set
+ it to sizeof MP which seems to be more correct but results in a
+ ERROR_MORE_DATA error at least on W2K. */
+ if ((lasterr) || (lasterr = GetTapeParameters (get_handle (),
+ GET_TAPE_MEDIA_INFORMATION,
+ (varlen = sizeof dp, &varlen),
+ &mp)))
+ notape = 1;
+
+ memset (get, 0, sizeof *get);
+
+ get->mt_type = MT_ISUNKNOWN;
+
+ if (! notape && (dp.FeaturesLow & TAPE_DRIVE_TAPE_REMAINING))
+ {
+ get->mt_remaining = get_ll (&mp.Remaining);
+ get->mt_resid = get->mt_remaining >> 10;
+ }
+
+ if ((dp.FeaturesHigh & TAPE_DRIVE_SET_BLOCK_SIZE) && ! notape)
+ get->mt_dsreg = mp.BlockSize;
+ else
+ get->mt_dsreg = dp.DefaultBlockSize;
+
+ if (notape)
+ get->mt_gstat |= GMT_DR_OPEN (-1);
+
+ if (! notape)
+ {
+ if (dp.FeaturesLow & TAPE_DRIVE_GET_ABSOLUTE_BLK)
+ tape_get_pos ((unsigned long *) &get->mt_blkno);
+
+ if (! get->mt_blkno)
+ get->mt_gstat |= GMT_BOT (-1);
+
+ get->mt_gstat |= GMT_ONLINE (-1);
+
+ if ((dp.FeaturesLow & TAPE_DRIVE_WRITE_PROTECT) && mp.WriteProtected)
+ get->mt_gstat |= GMT_WR_PROT (-1);
+
+ if (dp.FeaturesLow & TAPE_DRIVE_TAPE_CAPACITY)
+ get->mt_capacity = get_ll (&mp.Capacity);
+ }
+
+ if ((dp.FeaturesLow & TAPE_DRIVE_COMPRESSION) && dp.Compression)
+ get->mt_gstat |= GMT_HW_COMP (-1);
+
+ if ((dp.FeaturesLow & TAPE_DRIVE_ECC) && dp.ECC)
+ get->mt_gstat |= GMT_HW_ECC (-1);
+
+ if ((dp.FeaturesLow & TAPE_DRIVE_PADDING) && dp.DataPadding)
+ get->mt_gstat |= GMT_PADDING (-1);
+
+ if ((dp.FeaturesLow & TAPE_DRIVE_REPORT_SMKS) && dp.ReportSetmarks)
+ get->mt_gstat |= GMT_IM_REP_EN (-1);
+
+ get->mt_erreg = lasterr;
+
+ get->mt_minblksize = dp.MinimumBlockSize;
+ get->mt_maxblksize = dp.MaximumBlockSize;
+ get->mt_defblksize = dp.DefaultBlockSize;
+ get->mt_featureslow = dp.FeaturesLow;
+ get->mt_featureshigh = dp.FeaturesHigh;
+ get->mt_eotwarningzonesize = dp.EOTWarningZoneSize;
+
+ return 0;
+}
+
+int
+fhandler_dev_tape::tape_compression (long count)
+{
+ DWORD varlen;
+ TAPE_GET_DRIVE_PARAMETERS dpg;
+ TAPE_SET_DRIVE_PARAMETERS dps;
+
+ while (((lasterr = GetTapeParameters (get_handle (),
+ GET_TAPE_DRIVE_INFORMATION,
+ (varlen = sizeof dpg, &varlen),
+ &dpg)) == ERROR_MEDIA_CHANGED)
+ || (lasterr == ERROR_BUS_RESET))
+ ;
+
+ if (lasterr)
+ return tape_error (lasterr, "tape_compression");
+
+ if (! (dpg.FeaturesLow & TAPE_DRIVE_COMPRESSION))
+ return ERROR_INVALID_PARAMETER;
+
+ if (count)
+ {
+ dps.ECC = dpg.ECC;
+ dps.Compression = count ? TRUE : FALSE;
+ dps.DataPadding = dpg.DataPadding;
+ dps.ReportSetmarks = dpg.ReportSetmarks;
+ dps.EOTWarningZoneSize = dpg.EOTWarningZoneSize;
+ lasterr = SetTapeParameters (get_handle (),
+ SET_TAPE_DRIVE_INFORMATION,
+ &dps);
+
+ if (lasterr)
+ return tape_error (lasterr, "tape_compression");
+
+ dpg.Compression = dps.Compression;
+ }
+
+ return 0;
+}
+
diff --git a/winsup/cygwin/fhandler_tty.cc b/winsup/cygwin/fhandler_tty.cc
new file mode 100644
index 00000000000..dd55ee22cc6
--- /dev/null
+++ b/winsup/cygwin/fhandler_tty.cc
@@ -0,0 +1,1261 @@
+/* fhandler_tty.cc
+
+ Copyright 1997, 1998, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <ctype.h>
+#include <limits.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "cygwin/cygserver.h"
+#include "cygthread.h"
+
+/* Tty master stuff */
+
+fhandler_tty_master NO_COPY *tty_master;
+
+static DWORD WINAPI process_input (void *); // Input queue thread
+static DWORD WINAPI process_output (void *); // Output queue thread
+static DWORD WINAPI process_ioctl (void *); // Ioctl requests thread
+
+fhandler_tty_master::fhandler_tty_master ()
+ : fhandler_pty_master (), console (NULL)
+{
+}
+
+int
+fhandler_tty_slave::get_unit ()
+{
+ return dev == FH_TTY ? myself->ctty : dev.minor;
+}
+
+void
+fhandler_tty_master::set_winsize (bool sendSIGWINCH)
+{
+ winsize w;
+ console->ioctl (TIOCGWINSZ, &w);
+ get_ttyp ()->winsize = w;
+ if (sendSIGWINCH)
+ tc->kill_pgrp (SIGWINCH);
+}
+
+int
+fhandler_tty_master::init ()
+{
+ termios_printf ("Creating master for tty%d", get_unit ());
+
+ slave = dev;
+
+ if (init_console ())
+ {
+ termios_printf ("can't create fhandler");
+ return -1;
+ }
+
+ termios ti;
+ memset (&ti, 0, sizeof (ti));
+ console->tcsetattr (0, &ti);
+
+ cygwin_shared->tty[get_unit ()]->common_init (this);
+
+ set_winsize (false);
+
+ inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
+
+ cygthread *h;
+ h = new cygthread (process_input, cygself, "ttyin");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_ioctl, cygself, "ttyioctl");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ h = new cygthread (process_output, cygself, "ttyout");
+ h->SetThreadPriority (THREAD_PRIORITY_HIGHEST);
+ h->zap_h ();
+
+ return 0;
+}
+
+#ifdef DEBUGGING
+static class mutex_stack
+{
+public:
+ const char *fn;
+ int ln;
+ const char *tname;
+} ostack[100];
+
+static int osi;
+#endif /*DEBUGGING*/
+
+DWORD
+fhandler_tty_common::__acquire_output_mutex (const char *fn, int ln,
+ DWORD ms)
+{
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: waiting %d ms", ln, ms);
+ DWORD res = WaitForSingleObject (output_mutex, ms);
+ if (res == WAIT_OBJECT_0)
+ {
+#ifndef DEBUGGING
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex: acquired", ln, res);
+#else
+ ostack[osi].fn = fn;
+ ostack[osi].ln = ln;
+ ostack[osi].tname = cygthread::name ();
+ termios_printf ("acquired for %s:%d, osi %d", fn, ln, osi);
+ osi++;
+#endif
+ }
+ return res;
+}
+
+void
+fhandler_tty_common::__release_output_mutex (const char *fn, int ln)
+{
+ if (ReleaseMutex (output_mutex))
+ {
+#ifndef DEBUGGING
+ if (strace.active)
+ strace.prntf (_STRACE_TERMIOS, fn, "(%d): tty output_mutex released", ln);
+#else
+ if (osi > 0)
+ osi--;
+ termios_printf ("released at %s:%d, osi %d", fn, ln, osi);
+ termios_printf (" for %s:%d (%s)", ostack[osi].fn, ostack[osi].ln, ostack[osi].tname);
+ ostack[osi].ln = -ln;
+#endif
+ }
+}
+
+/* Process tty input. */
+
+void
+fhandler_pty_master::doecho (const void *str, DWORD len)
+{
+ acquire_output_mutex (INFINITE);
+ if (!WriteFile (get_ttyp ()->to_master, str, len, &len, NULL))
+ termios_printf ("Write to %p failed, %E", get_ttyp ()->to_master);
+// WaitForSingleObject (output_done_event, INFINITE);
+ release_output_mutex ();
+}
+
+int
+fhandler_pty_master::accept_input ()
+{
+ DWORD bytes_left;
+ int ret = 1;
+
+ (void) WaitForSingleObject (input_mutex, INFINITE);
+
+ bytes_left = eat_readahead (-1);
+
+ if (!bytes_left)
+ {
+ termios_printf ("sending EOF to slave");
+ get_ttyp ()->read_retval = 0;
+ }
+ else
+ {
+ char *p = rabuf;
+ DWORD rc;
+ DWORD written = 0;
+
+ termios_printf ("about to write %d chars to slave", bytes_left);
+ rc = WriteFile (get_output_handle (), p, bytes_left, &written, NULL);
+ if (!rc)
+ {
+ debug_printf ("error writing to pipe %E");
+ get_ttyp ()->read_retval = -1;
+ ret = -1;
+ }
+ else
+ {
+ get_ttyp ()->read_retval = 1;
+ p += written;
+ bytes_left -= written;
+ if (bytes_left > 0)
+ {
+ debug_printf ("to_slave pipe is full");
+ puts_readahead (p, bytes_left);
+ ret = 0;
+ }
+ }
+ }
+
+ SetEvent (input_available_event);
+ ReleaseMutex (input_mutex);
+ return ret;
+}
+
+static DWORD WINAPI
+process_input (void *)
+{
+ char rawbuf[INP_BUFFER_SIZE];
+
+ while (1)
+ {
+ size_t nraw = INP_BUFFER_SIZE;
+ tty_master->console->read ((void *) rawbuf, nraw);
+ (void) tty_master->line_edit (rawbuf, nraw, tty_master->get_ttyp ()->ti);
+ }
+}
+
+bool
+fhandler_pty_master::hit_eof ()
+{
+ if (get_ttyp ()->was_opened && !get_ttyp ()->slave_alive ())
+ {
+ /* We have the only remaining open handle to this pty, and
+ the slave pty has been opened at least once. We treat
+ this as EOF. */
+ termios_printf ("all other handles closed");
+ return 1;
+ }
+ return 0;
+}
+
+/* Process tty output requests */
+
+int
+fhandler_pty_master::process_slave_output (char *buf, size_t len, int pktmode_on)
+{
+ size_t rlen;
+ char outbuf[OUT_BUFFER_SIZE + 1];
+ DWORD n;
+ int column = 0;
+ int rc = 0;
+
+ if (len == 0)
+ goto out;
+
+ if (need_nl)
+ {
+ /* We need to return a left over \n character, resulting from
+ \r\n conversion. Note that we already checked for FLUSHO and
+ output_stopped at the time that we read the character, so we
+ don't check again here. */
+ buf[0] = '\n';
+ need_nl = 0;
+ rc = 1;
+ goto out;
+ }
+
+
+ for (;;)
+ {
+ /* Set RLEN to the number of bytes to read from the pipe. */
+ rlen = len;
+ if (get_ttyp ()->ti.c_oflag & OPOST && get_ttyp ()->ti.c_oflag & ONLCR)
+ {
+ /* We are going to expand \n to \r\n, so don't read more than
+ half of the number of bytes requested. */
+ rlen /= 2;
+ if (rlen == 0)
+ rlen = 1;
+ }
+ if (rlen > sizeof outbuf)
+ rlen = sizeof outbuf;
+
+ HANDLE handle = get_io_handle ();
+
+ n = 0; // get_readahead_into_buffer (outbuf, len);
+ if (!n)
+ {
+ /* Doing a busy wait like this is quite inefficient, but nothing
+ else seems to work completely. Windows should provide some sort
+ of overlapped I/O for pipes, or something, but it doesn't. */
+ while (1)
+ {
+ if (!PeekNamedPipe (handle, NULL, 0, NULL, &n, NULL))
+ goto err;
+ if (n > 0)
+ break;
+ if (hit_eof ())
+ goto out;
+ if (n == 0 && is_nonblocking ())
+ {
+ set_errno (EAGAIN);
+ rc = -1;
+ break;
+ }
+
+ Sleep (10);
+ }
+
+ if (ReadFile (handle, outbuf, rlen, &n, NULL) == FALSE)
+ goto err;
+ }
+
+ termios_printf ("bytes read %u", n);
+ get_ttyp ()->write_error = 0;
+ if (output_done_event != NULL)
+ SetEvent (output_done_event);
+
+ if (get_ttyp ()->ti.c_lflag & FLUSHO)
+ continue;
+
+ char *optr;
+ optr = buf;
+ if (pktmode_on)
+ *optr++ = TIOCPKT_DATA;
+
+ if (!(get_ttyp ()->ti.c_oflag & OPOST)) // post-process output
+ {
+ memcpy (optr, outbuf, n);
+ optr += n;
+ }
+ else // raw output mode
+ {
+ char *iptr = outbuf;
+
+ while (n--)
+ {
+ switch (*iptr)
+ {
+ case '\r':
+ if ((get_ttyp ()->ti.c_oflag & ONOCR) && column == 0)
+ {
+ iptr++;
+ continue;
+ }
+ if (get_ttyp ()->ti.c_oflag & OCRNL)
+ *iptr = '\n';
+ else
+ column = 0;
+ break;
+ case '\n':
+ if (get_ttyp ()->ti.c_oflag & ONLCR)
+ {
+ *optr++ = '\r';
+ column = 0;
+ }
+ if (get_ttyp ()->ti.c_oflag & ONLRET)
+ column = 0;
+ break;
+ default:
+ column++;
+ break;
+ }
+
+ /* Don't store data past the end of the user's buffer. This
+ can happen if the user requests a read of 1 byte when
+ doing \r\n expansion. */
+ if (optr - buf >= (int) len)
+ {
+ if (*iptr != '\n' || n != 0)
+ system_printf ("internal error: %d unexpected characters", n);
+ need_nl = 1;
+ break;
+ }
+
+ *optr++ = *iptr++;
+ }
+ }
+ rc = optr - buf;
+ break;
+
+ err:
+ if (GetLastError () == ERROR_BROKEN_PIPE)
+ rc = 0;
+ else
+ {
+ __seterrno ();
+ rc = -1;
+ }
+ break;
+ }
+
+out:
+ termios_printf ("returning %d", rc);
+ return rc;
+}
+
+static DWORD WINAPI
+process_output (void *)
+{
+ char buf[OUT_BUFFER_SIZE * 2];
+
+ for (;;)
+ {
+ int n = tty_master->process_slave_output (buf, OUT_BUFFER_SIZE, 0);
+ if (n <= 0)
+ {
+ if (n < 0)
+ termios_printf ("ReadFile %E");
+ ExitThread (0);
+ }
+ n = tty_master->console->write ((void *) buf, (size_t) n);
+ tty_master->get_ttyp ()->write_error = n == -1 ? get_errno () : 0;
+ }
+}
+
+
+/* Process tty ioctl requests */
+
+static DWORD WINAPI
+process_ioctl (void *)
+{
+ while (1)
+ {
+ WaitForSingleObject (tty_master->ioctl_request_event, INFINITE);
+ termios_printf ("ioctl() request");
+ tty_master->get_ttyp ()->ioctl_retval =
+ tty_master->console->ioctl (tty_master->get_ttyp ()->cmd,
+ (void *) &tty_master->get_ttyp ()->arg);
+ SetEvent (tty_master->ioctl_done_event);
+ }
+}
+
+/**********************************************************************/
+/* Tty slave stuff */
+
+fhandler_tty_slave::fhandler_tty_slave ()
+ : fhandler_tty_common ()
+{
+ set_r_no_interrupt (1);
+}
+
+/* FIXME: This function needs to close handles when it has
+ a failing condition. */
+int
+fhandler_tty_slave::open (path_conv *, int flags, mode_t)
+{
+ tcinit (cygwin_shared->tty[get_unit ()]);
+
+ attach_tty (get_unit ());
+ tc->set_ctty (get_unit (), flags);
+
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ /* Create synchronisation events */
+ char buf[40];
+
+ /* output_done_event may or may not exist. It will exist if the tty
+ was opened by fhandler_tty_master::init, normally called at
+ startup if use_tty is non-zero. It will not exist if this is a
+ pty opened by fhandler_pty_master::open. In the former case, tty
+ output is handled by a separate thread which controls output. */
+ __small_sprintf (buf, OUTPUT_DONE_EVENT, get_unit ());
+ output_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+
+ if (!(output_mutex = get_ttyp ()->open_output_mutex ()))
+ {
+ termios_printf ("open output mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ if (!(input_mutex = get_ttyp ()->open_input_mutex ()))
+ {
+ termios_printf ("open input mutex failed, %E");
+ __seterrno ();
+ return 0;
+ }
+ __small_sprintf (buf, INPUT_AVAILABLE_EVENT, get_unit ());
+ if (!(input_available_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf)))
+ {
+ termios_printf ("open input event failed, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ /* The ioctl events may or may not exist. See output_done_event,
+ above. */
+ __small_sprintf (buf, IOCTL_REQUEST_EVENT, get_unit ());
+ ioctl_request_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+ __small_sprintf (buf, IOCTL_DONE_EVENT, get_unit ());
+ ioctl_done_event = OpenEvent (EVENT_ALL_ACCESS, TRUE, buf);
+
+ /* FIXME: Needs a method to eliminate tty races */
+ {
+ acquire_output_mutex (500);
+ inuse = get_ttyp ()->create_inuse (TTY_SLAVE_ALIVE);
+ get_ttyp ()->was_opened = TRUE;
+ release_output_mutex ();
+ }
+
+ /* Duplicate tty handles. */
+
+ if (!get_ttyp ()->from_slave || !get_ttyp ()->to_slave)
+ {
+ termios_printf ("tty handles have been closed");
+ set_errno (EACCES);
+ return 0;
+ }
+
+ HANDLE from_master_local, to_master_local;
+
+ if (!wincap.has_security () ||
+ cygserver_running == CYGSERVER_UNAVAIL ||
+ !cygserver_attach_tty (&from_master_local, &to_master_local))
+ {
+ termios_printf ("cannot dup handles via server. using old method.");
+
+ HANDLE tty_owner = OpenProcess (PROCESS_DUP_HANDLE, FALSE,
+ get_ttyp ()->master_pid);
+ termios_printf ("tty own handle %p",tty_owner);
+ if (tty_owner == NULL)
+ {
+ termios_printf ("can't open tty (%d) handle process %d",
+ get_unit (), get_ttyp ()->master_pid);
+ __seterrno ();
+ return 0;
+ }
+
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->from_master,
+ hMainProc, &from_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate input, %E");
+ __seterrno ();
+ return 0;
+ }
+
+ if (!DuplicateHandle (tty_owner, get_ttyp ()->to_master,
+ hMainProc, &to_master_local, 0, TRUE,
+ DUPLICATE_SAME_ACCESS))
+ {
+ termios_printf ("can't duplicate output, %E");
+ __seterrno ();
+ return 0;
+ }
+ CloseHandle (tty_owner);
+ }
+
+ termios_printf ("duplicated from_master %p->%p from tty_owner",
+ get_ttyp ()->from_master, from_master_local);
+ termios_printf ("duplicated to_master %p->%p from tty_owner",
+ get_ttyp ()->to_master, to_master_local);
+
+ set_io_handle (from_master_local);
+ set_output_handle (to_master_local);
+
+ set_open_status ();
+ termios_printf ("tty%d opened", get_unit ());
+
+ return 1;
+}
+
+int
+fhandler_tty_slave::cygserver_attach_tty (LPHANDLE from_master_ptr,
+ LPHANDLE to_master_ptr)
+{
+ if (!from_master_ptr || !to_master_ptr)
+ return 0;
+
+ client_request_attach_tty req ((DWORD) get_ttyp ()->master_pid,
+ (HANDLE) get_ttyp ()->from_master,
+ (HANDLE) get_ttyp ()->to_master);
+
+ if (req.make_request () == -1 || req.error_code ())
+ return 0;
+
+ *from_master_ptr = req.from_master ();
+ *to_master_ptr = req.to_master ();
+ return 1;
+}
+
+void
+fhandler_tty_slave::init (HANDLE, DWORD a, mode_t)
+{
+ int mode = 0;
+
+ a &= GENERIC_READ | GENERIC_WRITE;
+ if (a == GENERIC_READ)
+ mode = O_RDONLY;
+ if (a == GENERIC_WRITE)
+ mode = O_WRONLY;
+ if (a == (GENERIC_READ | GENERIC_WRITE))
+ mode = O_RDWR;
+
+ open (0, mode);
+}
+
+int
+fhandler_tty_slave::write (const void *ptr, size_t len)
+{
+ DWORD n, towrite = len;
+
+ termios_printf ("tty%d, write(%x, %d)", get_unit (), ptr, len);
+
+ acquire_output_mutex (INFINITE);
+
+ while (len)
+ {
+ n = min (OUT_BUFFER_SIZE, len);
+ char *buf = (char *)ptr;
+ ptr = (char *) ptr + n;
+ len -= n;
+
+ /* Previous write may have set write_error to != 0. Check it here.
+ This is less than optimal, but the alternative slows down tty
+ writes enormously. */
+ if (get_ttyp ()->write_error)
+ {
+ set_errno (get_ttyp ()->write_error);
+ towrite = (DWORD) -1;
+ break;
+ }
+
+ if (WriteFile (get_output_handle (), buf, n, &n, NULL) == FALSE)
+ {
+ DWORD err = GetLastError ();
+ termios_printf ("WriteFile failed, %E");
+ switch (err)
+ {
+ case ERROR_NO_DATA:
+ err = ERROR_IO_DEVICE;
+ default:
+ __seterrno_from_win_error (err);
+ }
+ raise (SIGHUP); /* FIXME: Should this be SIGTTOU? */
+ towrite = (DWORD) -1;
+ break;
+ }
+
+ if (output_done_event != NULL)
+ {
+ DWORD rc;
+ DWORD x = n * 1000;
+ rc = WaitForSingleObject (output_done_event, x);
+ termios_printf ("waited %d ms for output_done_event, WFSO %d", x, rc);
+ }
+ }
+ release_output_mutex ();
+ return towrite;
+}
+
+void __stdcall
+fhandler_tty_slave::read (void *ptr, size_t& len)
+{
+ int totalread = 0;
+ int vmin = 0;
+ int vtime = 0; /* Initialized to prevent -Wuninitialized warning */
+ size_t readlen;
+ DWORD bytes_in_pipe;
+ char buf[INP_BUFFER_SIZE];
+ char peek_buf[INP_BUFFER_SIZE];
+ DWORD time_to_wait;
+ DWORD rc;
+ HANDLE w4[2];
+
+ termios_printf ("read(%x, %d) handle %p", ptr, len, get_handle ());
+
+ if ((get_ttyp ()->ti.c_lflag & ICANON))
+ time_to_wait = INFINITE;
+ else
+ {
+ vmin = get_ttyp ()->ti.c_cc[VMIN];
+ if (vmin > INP_BUFFER_SIZE)
+ vmin = INP_BUFFER_SIZE;
+ vtime = get_ttyp ()->ti.c_cc[VTIME];
+ if (vmin < 0)
+ vmin = 0;
+ if (vtime < 0)
+ vtime = 0;
+ if (!vmin && !vtime)
+ time_to_wait = 0;
+ else
+ time_to_wait = !vtime ? INFINITE : 100 * vtime;
+ }
+
+ w4[0] = signal_arrived;
+ w4[1] = input_available_event;
+
+ DWORD waiter = INFINITE;
+ while (len)
+ {
+ rc = WaitForMultipleObjects (2, w4, FALSE, waiter);
+
+ if (rc == WAIT_TIMEOUT)
+ break;
+
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input event failed, %E");
+ break;
+ }
+
+ if (rc == WAIT_OBJECT_0)
+ {
+ /* if we've received signal after successfully reading some data,
+ just return all data successfully read */
+ if (totalread > 0)
+ break;
+ set_sig_errno (EINTR);
+ (ssize_t) len = -1;
+ return;
+ }
+
+ rc = WaitForSingleObject (input_mutex, 1000);
+ if (rc == WAIT_FAILED)
+ {
+ termios_printf ("wait for input mutex failed, %E");
+ break;
+ }
+ else if (rc == WAIT_TIMEOUT)
+ {
+ termios_printf ("failed to acquire input mutex after input event arrived");
+ break;
+ }
+ if (!PeekNamedPipe (get_handle (), peek_buf, sizeof (peek_buf), &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+
+ if (!vmin && !time_to_wait)
+ {
+ ReleaseMutex (input_mutex);
+ (ssize_t) len = bytes_in_pipe;
+ return;
+ }
+
+ readlen = min (bytes_in_pipe, min (len, sizeof (buf)));
+
+ if (vmin && readlen > (unsigned) vmin)
+ readlen = vmin;
+
+ DWORD n = 0;
+ if (readlen)
+ {
+ termios_printf ("reading %d bytes (vtime %d)", readlen, vtime);
+ if (ReadFile (get_handle (), buf, readlen, &n, NULL) == FALSE)
+ {
+ termios_printf ("read failed, %E");
+ raise (SIGHUP);
+ }
+ /* MSDN states that 5th prameter can be used to determine total
+ number of bytes in pipe, but for some reason this number doesn't
+ change after successful read. So we have to peek into the pipe
+ again to see if input is still available */
+ if (!PeekNamedPipe (get_handle (), peek_buf, 1, &bytes_in_pipe, NULL, NULL))
+ {
+ termios_printf ("PeekNamedPipe failed, %E");
+ raise (SIGHUP);
+ bytes_in_pipe = 0;
+ }
+ if (n)
+ {
+ len -= n;
+ totalread += n;
+ memcpy (ptr, buf, n);
+ ptr = (char *) ptr + n;
+ }
+ }
+
+ if (!bytes_in_pipe)
+ ResetEvent (input_available_event);
+
+ ReleaseMutex (input_mutex);
+
+ if (get_ttyp ()->read_retval < 0) // read error
+ {
+ set_errno (-get_ttyp ()->read_retval);
+ totalread = -1;
+ break;
+ }
+ if (get_ttyp ()->read_retval == 0) //EOF
+ {
+ termios_printf ("saw EOF");
+ break;
+ }
+ if (get_ttyp ()->ti.c_lflag & ICANON || is_nonblocking ())
+ break;
+ if (vmin && totalread >= vmin)
+ break;
+
+ /* vmin == 0 && vtime == 0:
+ * we've already read all input, if any, so return immediately
+ * vmin == 0 && vtime > 0:
+ * we've waited for input 10*vtime ms in WFSO(input_available_event),
+ * no matter whether any input arrived, we shouldn't wait any longer,
+ * so return immediately
+ * vmin > 0 && vtime == 0:
+ * here, totalread < vmin, so continue waiting until more data
+ * arrive
+ * vmin > 0 && vtime > 0:
+ * similar to the previous here, totalread < vmin, and timer
+ * hadn't expired -- WFSO(input_available_event) != WAIT_TIMEOUT,
+ * so "restart timer" and wait until more data arrive
+ */
+
+ if (vmin == 0)
+ break;
+
+ if (n)
+ waiter = time_to_wait;
+ }
+ termios_printf ("%d=read(%x, %d)", totalread, ptr, len);
+ (ssize_t) len = totalread;
+ return;
+}
+
+int
+fhandler_tty_common::dup (fhandler_base *child)
+{
+ fhandler_tty_slave *fts = (fhandler_tty_slave *) child;
+ int errind;
+
+ fts->tcinit (get_ttyp ());
+
+ attach_tty (get_unit ());
+ tc->set_ctty (get_unit (), openflags);
+
+ HANDLE nh;
+
+ if (output_done_event == NULL)
+ fts->output_done_event = NULL;
+ else if (!DuplicateHandle (hMainProc, output_done_event, hMainProc,
+ &fts->output_done_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 1;
+ goto err;
+ }
+ if (ioctl_request_event == NULL)
+ fts->ioctl_request_event = NULL;
+ else if (!DuplicateHandle (hMainProc, ioctl_request_event, hMainProc,
+ &fts->ioctl_request_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 2;
+ goto err;
+ }
+ if (ioctl_done_event == NULL)
+ fts->ioctl_done_event = NULL;
+ else if (!DuplicateHandle (hMainProc, ioctl_done_event, hMainProc,
+ &fts->ioctl_done_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 3;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, input_available_event, hMainProc,
+ &fts->input_available_event, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 4;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, output_mutex, hMainProc,
+ &fts->output_mutex, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 5;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, input_mutex, hMainProc,
+ &fts->input_mutex, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 6;
+ goto err;
+ }
+ if (!DuplicateHandle (hMainProc, get_handle (), hMainProc,
+ &nh, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 7;
+ goto err;
+ }
+ fts->set_io_handle (nh);
+
+ if (!DuplicateHandle (hMainProc, get_output_handle (), hMainProc,
+ &nh, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 8;
+ goto err;
+ }
+ fts->set_output_handle (nh);
+
+ if (inuse == NULL)
+ fts->inuse = NULL;
+ else if (!DuplicateHandle (hMainProc, inuse, hMainProc,
+ &fts->inuse, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ errind = 9;
+ goto err;
+ }
+ return 0;
+
+err:
+ __seterrno ();
+ termios_printf ("dup %d failed in DuplicateHandle, %E", errind);
+ return -1;
+}
+
+int
+fhandler_tty_slave::tcgetattr (struct termios *t)
+{
+ *t = get_ttyp ()->ti;
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcsetattr (int, const struct termios *t)
+{
+ acquire_output_mutex (INFINITE);
+ get_ttyp ()->ti = *t;
+ release_output_mutex ();
+ return 0;
+}
+
+int
+fhandler_tty_slave::tcflush (int)
+{
+ return 0;
+}
+
+int
+fhandler_tty_slave::ioctl (unsigned int cmd, void *arg)
+{
+ termios_printf ("ioctl (%x)", cmd);
+
+ if (myself->pgid && get_ttyp ()->getpgid () != myself->pgid
+ && myself->ctty == get_unit () && (get_ttyp ()->ti.c_lflag & TOSTOP))
+ {
+ /* background process */
+ termios_printf ("bg ioctl pgid %d, tpgid %d, ctty %d",
+ myself->pgid, get_ttyp ()->getpgid (), myself->ctty);
+ raise (SIGTTOU);
+ }
+
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ case TIOCSWINSZ:
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ goto out;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ acquire_output_mutex (INFINITE);
+
+ get_ttyp ()->cmd = cmd;
+ get_ttyp ()->ioctl_retval = 0;
+ switch (cmd)
+ {
+ case TIOCGWINSZ:
+ get_ttyp ()->arg.winsize = get_ttyp ()->winsize;
+ if (ioctl_request_event)
+ SetEvent (ioctl_request_event);
+ *(struct winsize *) arg = get_ttyp ()->arg.winsize;
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ get_ttyp ()->winsize = get_ttyp ()->arg.winsize;
+ break;
+ case TIOCSWINSZ:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->arg.winsize = *(struct winsize *) arg;
+ if (ioctl_request_event)
+ {
+ get_ttyp ()->ioctl_retval = -1;
+ SetEvent (ioctl_request_event);
+ }
+ else
+ {
+ get_ttyp ()->winsize = *(struct winsize *) arg;
+ kill (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ if (ioctl_done_event)
+ WaitForSingleObject (ioctl_done_event, INFINITE);
+ }
+ break;
+ }
+
+ release_output_mutex ();
+
+out:
+ termios_printf ("%d = ioctl (%x)", get_ttyp ()->ioctl_retval, cmd);
+ return get_ttyp ()->ioctl_retval;
+}
+
+/*******************************************************
+ fhandler_pty_master
+*/
+fhandler_pty_master::fhandler_pty_master ()
+ : fhandler_tty_common ()
+{
+}
+
+int
+fhandler_pty_master::open (path_conv *, int flags, mode_t)
+{
+ int ntty = cygwin_shared->tty.allocate_tty (0);
+ if (ntty < 0)
+ return 0;
+
+ slave = *ttys_dev;
+ slave.setunit (ntty);
+ cygwin_shared->tty[ntty]->common_init (this);
+ inuse = get_ttyp ()->create_inuse (TTY_MASTER_ALIVE);
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_open_status ();
+
+ termios_printf ("opened pty master tty%d", get_unit ());
+ return 1;
+}
+
+int
+fhandler_tty_common::close ()
+{
+ if (output_done_event && !CloseHandle (output_done_event))
+ termios_printf ("CloseHandle (output_done_event), %E");
+ if (ioctl_done_event && !CloseHandle (ioctl_done_event))
+ termios_printf ("CloseHandle (ioctl_done_event), %E");
+ if (ioctl_request_event && !CloseHandle (ioctl_request_event))
+ termios_printf ("CloseHandle (ioctl_request_event), %E");
+ if (inuse && !CloseHandle (inuse))
+ termios_printf ("CloseHandle (inuse), %E");
+ if (!ForceCloseHandle (input_mutex))
+ termios_printf ("CloseHandle (input_mutex<%p>), %E", input_mutex);
+ if (!ForceCloseHandle (output_mutex))
+ termios_printf ("CloseHandle (output_mutex<%p>), %E", output_mutex);
+
+ /* Send EOF to slaves if master side is closed */
+ if (!get_ttyp ()->master_alive ())
+ {
+ termios_printf ("no more masters left. sending EOF" );
+ SetEvent (input_available_event);
+ }
+
+ if (!ForceCloseHandle (input_available_event))
+ termios_printf ("CloseHandle (input_available_event<%p>), %E", input_available_event);
+ if (!ForceCloseHandle1 (get_handle (), from_pty))
+ termios_printf ("CloseHandle (get_handle ()<%p>), %E", get_handle ());
+ if (!ForceCloseHandle1 (get_output_handle (), to_pty))
+ termios_printf ("CloseHandle (get_output_handle ()<%p>), %E", get_output_handle ());
+
+ inuse = NULL;
+ termios_printf ("tty%d <%p,%p> closed", get_unit (), get_handle (), get_output_handle ());
+ return 0;
+}
+
+int
+fhandler_pty_master::close ()
+{
+#if 0
+ while (accept_input () > 0)
+ continue;
+#endif
+ this->fhandler_tty_common::close ();
+
+ if (!get_ttyp ()->master_alive ())
+ {
+ termios_printf ("freeing tty%d (%d)", get_unit (), get_ttyp ()->ntty);
+#if 0
+ if (get_ttyp ()->to_slave)
+ ForceCloseHandle1 (get_ttyp ()->to_slave, to_slave);
+ if (get_ttyp ()->from_slave)
+ ForceCloseHandle1 (get_ttyp ()->from_slave, from_slave);
+#endif
+ if (get_ttyp ()->from_master)
+ CloseHandle (get_ttyp ()->from_master);
+ if (get_ttyp ()->to_master)
+ CloseHandle (get_ttyp ()->to_master);
+ get_ttyp ()->init ();
+ }
+
+ return 0;
+}
+
+int
+fhandler_pty_master::write (const void *ptr, size_t len)
+{
+ int i;
+ char *p = (char *) ptr;
+ termios ti = tc->ti;
+
+ for (i = 0; i < (int) len; i++)
+ {
+ line_edit_status status = line_edit (p++, 1, ti);
+ if (status > line_edit_signalled)
+ {
+ if (status != line_edit_pipe_full)
+ i = -1;
+ break;
+ }
+ }
+ return i;
+}
+
+void __stdcall
+fhandler_pty_master::read (void *ptr, size_t& len)
+{
+ (ssize_t) len = process_slave_output ((char *) ptr, len, pktmode);
+ return;
+}
+
+int
+fhandler_pty_master::tcgetattr (struct termios *t)
+{
+ *t = cygwin_shared->tty[get_unit ()]->ti;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcsetattr (int, const struct termios *t)
+{
+ cygwin_shared->tty[get_unit ()]->ti = *t;
+ return 0;
+}
+
+int
+fhandler_pty_master::tcflush (int)
+{
+ return 0;
+}
+
+int
+fhandler_pty_master::ioctl (unsigned int cmd, void *arg)
+{
+ switch (cmd)
+ {
+ case TIOCPKT:
+ pktmode = * (int *) arg;
+ break;
+ case TIOCGWINSZ:
+ *(struct winsize *) arg = get_ttyp ()->winsize;
+ break;
+ case TIOCSWINSZ:
+ if (get_ttyp ()->winsize.ws_row != ((struct winsize *) arg)->ws_row
+ || get_ttyp ()->winsize.ws_col != ((struct winsize *) arg)->ws_col)
+ {
+ get_ttyp ()->winsize = * (struct winsize *) arg;
+ kill (-get_ttyp ()->getpgid (), SIGWINCH);
+ }
+ break;
+ case FIONBIO:
+ set_nonblocking (*(int *) arg);
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+ return 0;
+}
+
+char *
+fhandler_pty_master::ptsname ()
+{
+ static char buf[32];
+
+ __small_sprintf (buf, "/dev/tty%d", get_unit ());
+ return buf;
+}
+
+void
+fhandler_tty_common::set_close_on_exec (int val)
+{
+#ifndef DEBUGGING
+ this->fhandler_base::set_close_on_exec (val);
+#else
+ /* FIXME: This is a duplication from fhandler_base::set_close_on_exec.
+ It is here because we need to specify the "from_pty" stuff here or
+ we'll get warnings from ForceCloseHandle when debugging. */
+ set_inheritance (get_io_handle (), val);
+ set_close_on_exec_flag (val);
+#endif
+ if (output_done_event)
+ set_inheritance (output_done_event, val);
+ if (ioctl_request_event)
+ set_inheritance (ioctl_request_event, val);
+ if (ioctl_done_event)
+ set_inheritance (ioctl_done_event, val);
+ if (inuse)
+ set_inheritance (inuse, val);
+ set_inheritance (output_mutex, val);
+ set_inheritance (input_mutex, val);
+ set_inheritance (input_available_event, val);
+ set_inheritance (output_handle, val);
+}
+
+void
+fhandler_tty_common::fixup_after_fork (HANDLE parent)
+{
+ this->fhandler_termios::fixup_after_fork (parent);
+ if (output_done_event)
+ fork_fixup (parent, output_done_event, "output_done_event");
+ if (ioctl_request_event)
+ fork_fixup (parent, ioctl_request_event, "ioctl_request_event");
+ if (ioctl_done_event)
+ fork_fixup (parent, ioctl_done_event, "ioctl_done_event");
+ if (output_mutex)
+ fork_fixup (parent, output_mutex, "output_mutex");
+ if (input_mutex)
+ fork_fixup (parent, input_mutex, "input_mutex");
+ if (input_available_event)
+ fork_fixup (parent, input_available_event, "input_available_event");
+ fork_fixup (parent, inuse, "inuse");
+}
+
+void
+fhandler_pty_master::set_close_on_exec (int val)
+{
+ this->fhandler_tty_common::set_close_on_exec (val);
+
+ /* FIXME: There is a console handle leak here. */
+ if (get_ttyp ()->master_pid == GetCurrentProcessId ())
+ {
+ get_ttyp ()->from_slave = get_handle ();
+ get_ttyp ()->to_slave = get_output_handle ();
+ termios_printf ("from_slave %p, to_slave %p", get_handle (),
+ get_output_handle ());
+ }
+}
+
+void
+fhandler_tty_master::fixup_after_fork (HANDLE child)
+{
+ this->fhandler_pty_master::fixup_after_fork (child);
+ console->fixup_after_fork (child);
+}
+
+void
+fhandler_tty_master::fixup_after_exec (HANDLE)
+{
+ console->close ();
+ init_console ();
+}
+
+int
+fhandler_tty_master::init_console ()
+{
+ console = (fhandler_console *)
+ cygheap->fdtab.build_fhandler (-1, *console_dev, "/dev/ttym");
+ if (console == NULL)
+ return -1;
+
+ console->init (INVALID_HANDLE_VALUE, GENERIC_READ | GENERIC_WRITE, O_BINARY);
+ console->set_r_no_interrupt (1);
+ return 0;
+}
diff --git a/winsup/cygwin/fhandler_virtual.cc b/winsup/cygwin/fhandler_virtual.cc
new file mode 100644
index 00000000000..4487275bf31
--- /dev/null
+++ b/winsup/cygwin/fhandler_virtual.cc
@@ -0,0 +1,235 @@
+/* fhandler_virtual.cc: base fhandler class for virtual filesystems
+
+ Copyright 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#include <assert.h>
+
+#define _COMPILING_NEWLIB
+#include <dirent.h>
+
+fhandler_virtual::fhandler_virtual ():
+ fhandler_base (), filebuf (NULL), bufalloc ((size_t) -1),
+ fileid (-1)
+{
+}
+
+fhandler_virtual::~fhandler_virtual ()
+{
+ if (filebuf)
+ free (filebuf);
+ filebuf = NULL;
+}
+
+void
+fhandler_virtual::fixup_after_exec (HANDLE)
+{
+ if (filebuf)
+ filebuf = NULL;
+}
+
+DIR *
+fhandler_virtual::opendir (path_conv& pc)
+{
+ DIR *dir;
+ DIR *res = NULL;
+ size_t len;
+
+ if (exists () <= 0)
+ set_errno (ENOTDIR);
+ else if ((len = strlen (get_name ())) > MAX_PATH - 3)
+ set_errno (ENAMETOOLONG);
+ else if ((dir = (DIR *) malloc (sizeof (DIR))) == NULL)
+ set_errno (ENOMEM);
+ else if ((dir->__d_dirname = (char *) malloc (len + 3)) == NULL)
+ {
+ free (dir);
+ set_errno (ENOMEM);
+ }
+ else if ((dir->__d_dirent =
+ (struct dirent *) malloc (sizeof (struct dirent))) == NULL)
+ {
+ free (dir);
+ set_errno (ENOMEM);
+ }
+ else
+ {
+ strcpy (dir->__d_dirname, get_name ());
+ dir->__d_dirent->d_version = __DIRENT_VERSION;
+ cygheap_fdnew fd;
+ fd = this;
+ fd->set_nohandle (true);
+ dir->__d_dirent->d_fd = fd;
+ dir->__d_u.__d_data.__fh = this;
+ dir->__d_cookie = __DIRENT_COOKIE;
+ dir->__d_u.__d_data.__handle = INVALID_HANDLE_VALUE;
+ dir->__d_position = 0;
+ dir->__d_dirhash = get_namehash ();
+
+ res = dir;
+ }
+
+ syscall_printf ("%p = opendir (%s)", res, get_name ());
+ return res;
+}
+
+__off64_t fhandler_virtual::telldir (DIR * dir)
+{
+ return dir->__d_position;
+}
+
+void
+fhandler_virtual::seekdir (DIR * dir, __off64_t loc)
+{
+ dir->__d_position = loc;
+ return;
+}
+
+void
+fhandler_virtual::rewinddir (DIR * dir)
+{
+ dir->__d_position = 0;
+ return;
+}
+
+int
+fhandler_virtual::closedir (DIR * dir)
+{
+ return 0;
+}
+
+__off64_t
+fhandler_virtual::lseek (__off64_t offset, int whence)
+{
+ /*
+ * On Linux, when you lseek within a /proc file,
+ * the contents of the file are updated.
+ */
+ if (!fill_filebuf ())
+ return (__off64_t) -1;
+ switch (whence)
+ {
+ case SEEK_SET:
+ position = offset;
+ break;
+ case SEEK_CUR:
+ position += offset;
+ break;
+ case SEEK_END:
+ position = filesize + offset;
+ break;
+ default:
+ set_errno (EINVAL);
+ return (__off64_t) -1;
+ }
+ return position;
+}
+
+int
+fhandler_virtual::dup (fhandler_base * child)
+{
+ int ret = fhandler_base::dup (child);
+
+ if (!ret)
+ {
+ fhandler_virtual *fhproc_child = (fhandler_virtual *) child;
+ fhproc_child->filebuf = (char *) malloc (filesize);
+ fhproc_child->bufalloc = fhproc_child->filesize = filesize;
+ fhproc_child->position = position;
+ memcpy (fhproc_child->filebuf, filebuf, filesize);
+ fhproc_child->set_flags (get_flags ());
+ }
+ return ret;
+}
+
+int
+fhandler_virtual::close ()
+{
+ if (filebuf)
+ free (filebuf);
+ filebuf = NULL;
+ bufalloc = (size_t) -1;
+ cygwin_shared->delqueue.process_queue ();
+ return 0;
+}
+
+void
+fhandler_virtual::read (void *ptr, size_t& len)
+{
+ if (len == 0)
+ return;
+ if (openflags & O_DIROPEN)
+ {
+ set_errno (EISDIR);
+ (ssize_t) len = -1;
+ return;
+ }
+ if (!filebuf)
+ {
+ (ssize_t) len = 0;
+ return;
+ }
+ if ((ssize_t) len > filesize - position)
+ (ssize_t) len = filesize - position;
+ if ((ssize_t) len < 0)
+ (ssize_t) len = 0;
+ else
+ memcpy (ptr, filebuf + position, len);
+ position += len;
+}
+
+int
+fhandler_virtual::write (const void *ptr, size_t len)
+{
+ set_errno (EACCES);
+ return -1;
+}
+
+/* low-level open for all proc files */
+int
+fhandler_virtual::open (path_conv *, int flags, mode_t mode)
+{
+ set_r_binary (1);
+ set_w_binary (1);
+
+ set_has_acls (false);
+ set_isremote (false);
+
+ /* what to do about symlinks? */
+ set_symlink_p (false);
+ set_execable_p (not_executable);
+ set_socket_p (false);
+
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+
+ return 1;
+}
+
+int
+fhandler_virtual::exists ()
+{
+ return 0;
+}
+
+bool
+fhandler_virtual::fill_filebuf ()
+{
+ return true;
+}
diff --git a/winsup/cygwin/fhandler_windows.cc b/winsup/cygwin/fhandler_windows.cc
new file mode 100644
index 00000000000..929aae5cec6
--- /dev/null
+++ b/winsup/cygwin/fhandler_windows.cc
@@ -0,0 +1,147 @@
+/* fhandler_windows.cc: code to access windows message queues.
+
+ Copyright 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by Sergey S. Okhapkin (sos@prospect.com.ru).
+ Feedback and testing by Andy Piper (andyp@parallax.co.uk).
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+
+/*
+The following unix-style calls are supported:
+
+ open ("/dev/windows", flags, mode=0)
+ - create a unix fd for message queue.
+ O_NONBLOCK flag controls the read() call behavior.
+
+ read (fd, buf, len)
+ - return next message from queue. buf must point to MSG
+ structure, len must be >= sizeof (MSG). If read is set to
+ non-blocking and the queue is empty, read call returns -1
+ immediately with errno set to EAGAIN, otherwise it blocks
+ untill the message will be received.
+
+ write (fd, buf, len)
+ - send a message pointed by buf. len argument ignored.
+
+ ioctl (fd, command, *param)
+ - control read()/write() behavior.
+ ioctl (fd, WINDOWS_POST, NULL): write() will PostMessage();
+ ioctl (fd, WINDOWS_SEND, NULL): write() will SendMessage();
+ ioctl (fd, WINDOWS_HWND, &hWnd): read() messages for
+ hWnd window.
+
+ select () call marks read fd when any message posted to queue.
+*/
+
+fhandler_windows::fhandler_windows ()
+ : fhandler_base (), hWnd_ (NULL), method_ (WINDOWS_POST)
+{
+}
+
+int
+fhandler_windows::open (path_conv *, int flags, mode_t)
+{
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_close_on_exec_flag (1);
+ set_open_status ();
+ return 1;
+}
+
+int
+fhandler_windows::write (const void *buf, size_t)
+{
+ MSG *ptr = (MSG *) buf;
+
+ if (method_ == WINDOWS_POST)
+ {
+ if (!PostMessage (ptr->hwnd, ptr->message, ptr->wParam, ptr->lParam))
+ {
+ __seterrno ();
+ return -1;
+ }
+ else
+ return sizeof (MSG);
+ }
+ else
+ return SendMessage (ptr->hwnd, ptr->message, ptr->wParam, ptr->lParam);
+}
+
+void __stdcall
+fhandler_windows::read (void *buf, size_t& len)
+{
+ MSG *ptr = (MSG *) buf;
+
+ if (len < sizeof (MSG))
+ {
+ set_errno (EINVAL);
+ (ssize_t) len = -1;
+ return;
+ }
+
+ (ssize_t) len = GetMessage (ptr, hWnd_, 0, 0);
+
+ if ((ssize_t) len == -1)
+ __seterrno ();
+ else
+ set_errno (0);
+ return;
+}
+
+int
+fhandler_windows::ioctl (unsigned int cmd, void *val)
+{
+ switch (cmd)
+ {
+ case WINDOWS_POST:
+ case WINDOWS_SEND:
+ method_ = cmd;
+ break;
+ case WINDOWS_HWND:
+ if (val == NULL)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ hWnd_ = * ((HWND *) val);
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+ return 0;
+}
+
+void
+fhandler_windows::set_close_on_exec (int val)
+{
+ if (get_handle ())
+ this->fhandler_base::set_close_on_exec (val);
+ else
+ this->fhandler_base::set_close_on_exec_flag (val);
+ void *h = hWnd_;
+ if (h)
+ set_inheritance (h, val);
+}
+
+void
+fhandler_windows::fixup_after_fork (HANDLE parent)
+{
+ if (get_handle ())
+ this->fhandler_base::fixup_after_fork (parent);
+ void *h = hWnd_;
+ if (h)
+ fork_fixup (parent, h, "hWnd_");
+}
diff --git a/winsup/cygwin/fhandler_zero.cc b/winsup/cygwin/fhandler_zero.cc
new file mode 100644
index 00000000000..64ad21fdc8f
--- /dev/null
+++ b/winsup/cygwin/fhandler_zero.cc
@@ -0,0 +1,55 @@
+/* fhandler_dev_zero.cc: code to access /dev/zero
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by DJ Delorie (dj@cygnus.com)
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include "security.h"
+#include "fhandler.h"
+
+fhandler_dev_zero::fhandler_dev_zero ()
+ : fhandler_base ()
+{
+}
+
+int
+fhandler_dev_zero::open (path_conv *, int flags, mode_t)
+{
+ set_flags ((flags & ~O_TEXT) | O_BINARY);
+ set_nohandle (true);
+ set_open_status ();
+ return 1;
+}
+
+int
+fhandler_dev_zero::write (const void *, size_t len)
+{
+ return len;
+}
+
+void __stdcall
+fhandler_dev_zero::read (void *ptr, size_t& len)
+{
+ memset (ptr, 0, len);
+ return;
+}
+
+__off64_t
+fhandler_dev_zero::lseek (__off64_t, int)
+{
+ return 0;
+}
+
+void
+fhandler_dev_zero::dump ()
+{
+ paranoid_printf ("here, fhandler_dev_zero");
+}
diff --git a/winsup/cygwin/mmap.cc b/winsup/cygwin/mmap.cc
new file mode 100644
index 00000000000..67ff8238074
--- /dev/null
+++ b/winsup/cygwin/mmap.cc
@@ -0,0 +1,1046 @@
+/* mmap.cc
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <unistd.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <sys/mman.h>
+#include <errno.h>
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "sys/cygwin.h"
+
+#define PAGE_CNT(bytes) howmany((bytes),getpagesize())
+
+#define PGBITS (sizeof (DWORD)*8)
+#define MAPSIZE(pages) howmany ((pages), PGBITS)
+
+#define MAP_SET(n) (map_map_[(n)/PGBITS] |= (1L << ((n) % PGBITS)))
+#define MAP_CLR(n) (map_map_[(n)/PGBITS] &= ~(1L << ((n) % PGBITS)))
+#define MAP_ISSET(n) (map_map_[(n)/PGBITS] & (1L << ((n) % PGBITS)))
+
+/*
+ * Simple class used to keep a record of all current
+ * mmap areas in a process. Needed so that
+ * they can be duplicated after a fork().
+ */
+
+class mmap_record
+{
+ private:
+ int fdesc_;
+ HANDLE mapping_handle_;
+ DWORD access_mode_;
+ __off64_t offset_;
+ DWORD size_to_map_;
+ caddr_t base_address_;
+ DWORD *map_map_;
+ device dev;
+
+ public:
+ mmap_record (int fd, HANDLE h, DWORD ac, __off64_t o, DWORD s, caddr_t b) :
+ fdesc_ (fd),
+ mapping_handle_ (h),
+ access_mode_ (ac),
+ offset_ (o),
+ size_to_map_ (s),
+ base_address_ (b),
+ map_map_ (NULL)
+ {
+ dev.devn = 0;
+ if (fd >= 0 && !cygheap->fdtab.not_open (fd))
+ dev = cygheap->fdtab[fd]->dev;
+ }
+
+ /* Default Copy constructor/operator=/destructor are ok */
+
+ /* Simple accessors */
+ int get_fd () const { return fdesc_; }
+ HANDLE get_handle () const { return mapping_handle_; }
+ device& get_device () { return dev; }
+ DWORD get_access () const { return access_mode_; }
+ DWORD get_offset () const { return offset_; }
+ DWORD get_size () const { return size_to_map_; }
+ caddr_t get_address () const { return base_address_; }
+ DWORD *get_map () const { return map_map_; }
+
+ void alloc_map ()
+ {
+ /* Allocate one bit per page */
+ map_map_ = (DWORD *) calloc (MAPSIZE (PAGE_CNT (size_to_map_)),
+ sizeof (DWORD));
+ if (wincap.virtual_protect_works_on_shared_pages ())
+ {
+ DWORD old_prot;
+ if (!VirtualProtect (base_address_, size_to_map_,
+ PAGE_NOACCESS, &old_prot))
+ syscall_printf ("-1 = alloc_map (): %E");
+ }
+ }
+ void free_map () { if (map_map_) free (map_map_); }
+
+ DWORD find_empty (DWORD pages);
+ __off64_t map_map (__off64_t off, DWORD len);
+ BOOL unmap_map (caddr_t addr, DWORD len);
+ void fixup_map (void);
+ int access (caddr_t address);
+
+ fhandler_base *alloc_fh ();
+ void free_fh (fhandler_base *fh);
+};
+
+DWORD
+mmap_record::find_empty (DWORD pages)
+{
+ DWORD mapped_pages = PAGE_CNT (size_to_map_);
+ DWORD start;
+
+ if (pages > mapped_pages)
+ return (DWORD)-1;
+ for (start = 0; start <= mapped_pages - pages; ++start)
+ if (!MAP_ISSET (start))
+ {
+ DWORD cnt;
+ for (cnt = 0; cnt < pages; ++cnt)
+ if (MAP_ISSET (start + cnt))
+ break;
+ if (cnt >= pages)
+ return start;
+ }
+ return (DWORD)-1;
+}
+
+__off64_t
+mmap_record::map_map (__off64_t off, DWORD len)
+{
+ DWORD prot, old_prot;
+ switch (access_mode_)
+ {
+ case FILE_MAP_WRITE:
+ prot = PAGE_READWRITE;
+ break;
+ case FILE_MAP_READ:
+ prot = PAGE_READONLY;
+ break;
+ default:
+ prot = PAGE_WRITECOPY;
+ break;
+ }
+
+ debug_printf ("map_map (fd=%d, off=%D, len=%d)", fdesc_, off, len);
+ len = PAGE_CNT (len);
+ if (fdesc_ == -1 && !off)
+ {
+ off = find_empty (len);
+ if (off != (DWORD)-1)
+ {
+ if (wincap.virtual_protect_works_on_shared_pages ()
+ && !VirtualProtect (base_address_ + off * getpagesize (),
+ len * getpagesize (), prot, &old_prot))
+ {
+ __seterrno ();
+ return (__off64_t)-1;
+ }
+
+ while (len-- > 0)
+ MAP_SET (off + len);
+ return off * getpagesize ();
+ }
+ return 0L;
+ }
+ off -= offset_;
+ DWORD start = off / getpagesize ();
+ if (wincap.virtual_protect_works_on_shared_pages ()
+ && !VirtualProtect (base_address_ + start * getpagesize (),
+ len * getpagesize (), prot, &old_prot))
+ {
+ __seterrno ();
+ return (__off64_t)-1;
+ }
+
+ for (; len-- > 0; ++start)
+ MAP_SET (start);
+ return off;
+}
+
+BOOL
+mmap_record::unmap_map (caddr_t addr, DWORD len)
+{
+ DWORD old_prot;
+ DWORD off = addr - base_address_;
+ off /= getpagesize ();
+ len = PAGE_CNT (len);
+ if (wincap.virtual_protect_works_on_shared_pages ()
+ && !VirtualProtect (base_address_ + off * getpagesize (),
+ len * getpagesize (), PAGE_NOACCESS, &old_prot))
+ syscall_printf ("-1 = unmap_map (): %E");
+
+ for (; len-- > 0; ++off)
+ MAP_CLR (off);
+ /* Return TRUE if all pages are free'd which may result in unmapping
+ the whole chunk. */
+ for (len = MAPSIZE (PAGE_CNT (size_to_map_)); len > 0; )
+ if (map_map_[--len])
+ return FALSE;
+ return TRUE;
+}
+
+void
+mmap_record::fixup_map ()
+{
+ if (!wincap.virtual_protect_works_on_shared_pages ())
+ return;
+
+ DWORD prot, old_prot;
+ switch (access_mode_)
+ {
+ case FILE_MAP_WRITE:
+ prot = PAGE_READWRITE;
+ break;
+ case FILE_MAP_READ:
+ prot = PAGE_READONLY;
+ break;
+ default:
+ prot = PAGE_WRITECOPY;
+ break;
+ }
+
+ for (DWORD off = PAGE_CNT (size_to_map_); off > 0; --off)
+ VirtualProtect (base_address_ + off * getpagesize (),
+ getpagesize (),
+ MAP_ISSET (off - 1) ? prot : PAGE_NOACCESS,
+ &old_prot);
+}
+
+int
+mmap_record::access (caddr_t address)
+{
+ if (address < base_address_ || address >= base_address_ + size_to_map_)
+ return 0;
+ DWORD off = (address - base_address_) / getpagesize ();
+ return MAP_ISSET (off);
+}
+
+static fhandler_disk_file fh_paging_file;
+
+fhandler_base *
+mmap_record::alloc_fh ()
+{
+ if (get_fd () == -1)
+ {
+ fh_paging_file.set_io_handle (INVALID_HANDLE_VALUE);
+ return &fh_paging_file;
+ }
+
+ /* The file descriptor could have been closed or, even
+ worse, could have been reused for another file before
+ the call to fork(). This requires creating a fhandler
+ of the correct type to be sure to call the method of the
+ correct class. */
+ return cygheap->fdtab.build_fhandler (-1, get_device ());
+}
+
+void
+mmap_record::free_fh (fhandler_base *fh)
+{
+ if (get_fd () != -1)
+ cfree (fh);
+}
+
+class list {
+public:
+ mmap_record *recs;
+ int nrecs, maxrecs;
+ int fd;
+ DWORD hash;
+ list ();
+ ~list ();
+ mmap_record *add_record (mmap_record r);
+ void erase (int i);
+ void erase ();
+ mmap_record *match (__off64_t off, DWORD len);
+ long match (caddr_t addr, DWORD len, long start);
+};
+
+list::list ()
+: nrecs (0), maxrecs (10), fd (0), hash (0)
+{
+ recs = (mmap_record *) malloc (10 * sizeof (mmap_record));
+}
+
+list::~list ()
+{
+ for (mmap_record *rec = recs; nrecs-- > 0; ++rec)
+ rec->free_map ();
+ free (recs);
+}
+
+mmap_record *
+list::add_record (mmap_record r)
+{
+ if (nrecs == maxrecs)
+ {
+ maxrecs += 5;
+ recs = (mmap_record *) realloc (recs, maxrecs * sizeof (mmap_record));
+ }
+ recs[nrecs] = r;
+ recs[nrecs].alloc_map ();
+ return recs + nrecs++;
+}
+
+/* Used in mmap() */
+mmap_record *
+list::match (__off64_t off, DWORD len)
+{
+ if (fd == -1 && !off)
+ {
+ len = PAGE_CNT (len);
+ for (int i = 0; i < nrecs; ++i)
+ if (recs[i].find_empty (len) != (DWORD)-1)
+ return recs + i;
+ }
+ else
+ {
+ for (int i = 0; i < nrecs; ++i)
+ if (off >= recs[i].get_offset ()
+ && off + len <= recs[i].get_offset ()
+ + (PAGE_CNT (recs[i].get_size ()) * getpagesize ()))
+ return recs + i;
+ }
+ return NULL;
+}
+
+/* Used in munmap() */
+long
+list::match (caddr_t addr, DWORD len, __off32_t start)
+{
+ for (int i = start + 1; i < nrecs; ++i)
+ if (addr >= recs[i].get_address ()
+ && addr + len <= recs[i].get_address ()
+ + (PAGE_CNT (recs[i].get_size ()) * getpagesize ()))
+ return i;
+ return -1;
+}
+
+void
+list::erase (int i)
+{
+ recs[i].free_map ();
+ for (; i < nrecs-1; i++)
+ recs[i] = recs[i+1];
+ nrecs--;
+}
+
+void
+list::erase ()
+{
+ erase (nrecs-1);
+}
+
+class map {
+public:
+ list **lists;
+ int nlists, maxlists;
+ map ();
+ ~map ();
+ list *get_list_by_fd (int fd);
+ list *add_list (list *l, int fd);
+ void erase (int i);
+};
+
+map::map ()
+{
+ lists = (list **) malloc (10 * sizeof (list *));
+ nlists = 0;
+ maxlists = 10;
+}
+
+map::~map ()
+{
+ free (lists);
+}
+
+list *
+map::get_list_by_fd (int fd)
+{
+ int i;
+ for (i=0; i<nlists; i++)
+#if 0 /* The fd isn't sufficient since it could already be another file. */
+ if (lists[i]->fd == fd
+#else /* so we use the name hash value to identify the file unless
+ it's not an anonymous mapping. */
+ if ((fd == -1 && lists[i]->fd == -1)
+ || (fd != -1 && lists[i]->hash == cygheap->fdtab[fd]->get_namehash ()))
+#endif
+ return lists[i];
+ return 0;
+}
+
+list *
+map::add_list (list *l, int fd)
+{
+ l->fd = fd;
+ if (fd != -1)
+ l->hash = cygheap->fdtab[fd]->get_namehash ();
+ if (nlists == maxlists)
+ {
+ maxlists += 5;
+ lists = (list **) realloc (lists, maxlists * sizeof (list *));
+ }
+ lists[nlists++] = l;
+ return lists[nlists-1];
+}
+
+void
+map::erase (int i)
+{
+ for (; i < nlists-1; i++)
+ lists[i] = lists[i+1];
+ nlists--;
+}
+
+/*
+ * Code to keep a record of all mmap'ed areas in a process.
+ * Needed to duplicate tham in a child of fork().
+ * mmap_record classes are kept in an STL list in an STL map, keyed
+ * by file descriptor. This is *NOT* duplicated accross a fork(), it
+ * needs to be specially handled by the fork code.
+ */
+
+static map *mmapped_areas;
+
+extern "C"
+caddr_t
+mmap64 (caddr_t addr, size_t len, int prot, int flags, int fd, __off64_t off)
+{
+ syscall_printf ("addr %x, len %d, prot %x, flags %x, fd %d, off %D",
+ addr, len, prot, flags, fd, off);
+
+ static DWORD granularity;
+ if (!granularity)
+ {
+ SYSTEM_INFO si;
+ GetSystemInfo (&si);
+ granularity = si.dwAllocationGranularity;
+ }
+
+ /* Error conditions according to SUSv2 */
+ if (off % getpagesize ()
+ || (!(flags & MAP_SHARED) && !(flags & MAP_PRIVATE))
+ || ((flags & MAP_SHARED) && (flags & MAP_PRIVATE))
+ || ((flags & MAP_FIXED) && ((DWORD)addr % granularity))
+ || !len)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("-1 = mmap(): EINVAL");
+ return MAP_FAILED;
+ }
+
+ SetResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+
+ if (mmapped_areas == NULL)
+ {
+ /* First mmap call, create STL map */
+ mmapped_areas = new map;
+ if (mmapped_areas == NULL)
+ {
+ set_errno (ENOMEM);
+ syscall_printf ("-1 = mmap(): ENOMEM");
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+ return MAP_FAILED;
+ }
+ }
+
+ if (flags & MAP_ANONYMOUS)
+ fd = -1;
+
+ /* Map always in multipliers of `granularity'-sized chunks. */
+ __off64_t gran_off = off & ~(granularity - 1);
+ DWORD gran_len = howmany (off + len, granularity) * granularity - gran_off;
+
+ fhandler_base *fh;
+ caddr_t base = addr;
+ HANDLE h;
+
+ if (fd != -1)
+ {
+ /* Ensure that fd is open */
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = mmap(): EBADF");
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+ return MAP_FAILED;
+ }
+ fh = cfd;
+ if (fh->get_device () == FH_FS)
+ {
+ DWORD high;
+ DWORD low = GetFileSize (fh->get_handle (), &high);
+ __off64_t fsiz = ((__off64_t)high << 32) + low;
+ fsiz -= gran_off;
+ if (gran_len > fsiz)
+ gran_len = fsiz;
+ }
+ else if (fh->get_device () == FH_ZERO)
+ /* mmap /dev/zero is like MAP_ANONYMOUS. */
+ fd = -1;
+ }
+ if (fd == -1)
+ {
+ fh_paging_file.set_io_handle (INVALID_HANDLE_VALUE);
+ fh = &fh_paging_file;
+ }
+
+ list *l = mmapped_areas->get_list_by_fd (fd);
+
+ /* First check if this mapping matches into the chunk of another
+ already performed mapping. Only valid for MAP_ANON in a special
+ case of MAP_PRIVATE. */
+ if (l && fd == -1 && off == 0 && !(flags & MAP_FIXED))
+ {
+ mmap_record *rec;
+ if ((rec = l->match (off, len)) != NULL)
+ {
+ if ((off = rec->map_map (off, len)) == (__off64_t)-1)
+ {
+ syscall_printf ("-1 = mmap()");
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK|WRITE_LOCK, "mmap");
+ return MAP_FAILED;
+ }
+ caddr_t ret = rec->get_address () + off;
+ syscall_printf ("%x = mmap() succeeded", ret);
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+ return ret;
+ }
+ }
+
+ DWORD access = (prot & PROT_WRITE) ? FILE_MAP_WRITE : FILE_MAP_READ;
+ /* copy-on-write doesn't work at all on 9x using anonymous maps.
+ Workaround: Anonymous mappings always use normal READ or WRITE
+ access and don't use named file mapping.
+ copy-on-write doesn't also work properly on 9x with real files.
+ While the changes are not propagated to the file, they are
+ visible to other processes sharing the same file mapping object.
+ Workaround: Don't use named file mapping. That should work since
+ sharing file mappings only works reliable using named
+ file mapping on 9x.
+ */
+ if ((flags & MAP_PRIVATE)
+ && (wincap.has_working_copy_on_write () || fd != -1))
+ access = FILE_MAP_COPY;
+
+ h = fh->mmap (&base, gran_len, access, flags, gran_off);
+
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+ return MAP_FAILED;
+ }
+
+ /* Now we should have a successfully mmapped area.
+ Need to save it so forked children can reproduce it.
+ */
+ if (fd == -1)
+ gran_len = PAGE_CNT (gran_len) * getpagesize ();
+ mmap_record mmap_rec (fd, h, access, gran_off, gran_len, base);
+
+ /* Get list of mmapped areas for this fd, create a new one if
+ one does not exist yet.
+ */
+ if (l == 0)
+ {
+ /* Create a new one */
+ l = new list;
+ if (l == 0)
+ {
+ fh->munmap (h, base, gran_len);
+ set_errno (ENOMEM);
+ syscall_printf ("-1 = mmap(): ENOMEM");
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+ return MAP_FAILED;
+ }
+ l = mmapped_areas->add_list (l, fd);
+ }
+
+ /* Insert into the list */
+ mmap_record *rec = l->add_record (mmap_rec);
+ if ((off = rec->map_map (off, len)) == (__off64_t)-1)
+ {
+ fh->munmap (h, base, gran_len);
+ l->erase ();
+ syscall_printf ("-1 = mmap()");
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+ return MAP_FAILED;
+ }
+ caddr_t ret = rec->get_address () + off;
+ syscall_printf ("%x = mmap() succeeded", ret);
+ ReleaseResourceLock (LOCK_MMAP_LIST, READ_LOCK | WRITE_LOCK, "mmap");
+ return ret;
+}
+
+extern "C"
+caddr_t
+mmap (caddr_t addr, size_t len, int prot, int flags, int fd, __off32_t off)
+{
+ return mmap64 (addr, len, prot, flags, fd, (__off64_t)off);
+}
+
+/* munmap () removes an mmapped area. It insists that base area
+ requested is the same as that mmapped, error if not. */
+
+extern "C"
+int
+munmap (caddr_t addr, size_t len)
+{
+ syscall_printf ("munmap (addr %x, len %d)", addr, len);
+
+ /* Error conditions according to SUSv2 */
+ if (((DWORD)addr % getpagesize ()) || !len)
+ {
+ set_errno (EINVAL);
+ syscall_printf ("-1 = munmap(): Invalid parameters");
+ return -1;
+ }
+
+ SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap");
+ /* Check if a mmap'ed area was ever created */
+ if (mmapped_areas == NULL)
+ {
+ syscall_printf ("-1 = munmap(): mmapped_areas == NULL");
+ set_errno (EINVAL);
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap");
+ return -1;
+ }
+
+ /* Iterate through the map, looking for the mmapped area.
+ Error if not found. */
+
+ for (int it = 0; it < mmapped_areas->nlists; ++it)
+ {
+ list *l = mmapped_areas->lists[it];
+ if (l)
+ {
+ long li = -1;
+ if ((li = l->match(addr, len, li)) >= 0)
+ {
+ mmap_record *rec = l->recs + li;
+ if (rec->unmap_map (addr, len))
+ {
+ fhandler_base *fh = rec->alloc_fh ();
+ fh->munmap (rec->get_handle (), addr, len);
+ rec->free_fh (fh);
+
+ /* Delete the entry. */
+ l->erase (li);
+ }
+ syscall_printf ("0 = munmap(): %x", addr);
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap");
+ return 0;
+ }
+ }
+ }
+
+ set_errno (EINVAL);
+ syscall_printf ("-1 = munmap(): EINVAL");
+
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "munmap");
+ return -1;
+}
+
+/* Sync file with memory. Ignore flags for now. */
+
+extern "C"
+int
+msync (caddr_t addr, size_t len, int flags)
+{
+ syscall_printf ("addr = %x, len = %d, flags = %x",
+ addr, len, flags);
+
+ /* However, check flags for validity. */
+ if ((flags & ~(MS_ASYNC | MS_SYNC | MS_INVALIDATE))
+ || ((flags & MS_ASYNC) && (flags & MS_SYNC)))
+ {
+ syscall_printf ("-1 = msync(): Invalid flags");
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ SetResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync");
+ /* Check if a mmap'ed area was ever created */
+ if (mmapped_areas == NULL)
+ {
+ syscall_printf ("-1 = msync(): mmapped_areas == NULL");
+ set_errno (EINVAL);
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync");
+ return -1;
+ }
+
+ /* Iterate through the map, looking for the mmapped area.
+ Error if not found. */
+
+ for (int it = 0; it < mmapped_areas->nlists; ++it)
+ {
+ list *l = mmapped_areas->lists[it];
+ if (l != 0)
+ {
+ for (int li = 0; li < l->nrecs; ++li)
+ {
+ mmap_record *rec = l->recs + li;
+ if (rec->access (addr))
+ {
+ /* Check whole area given by len. */
+ for (DWORD i = getpagesize (); i < len; ++i)
+ if (!rec->access (addr + i))
+ goto invalid_address_range;
+ fhandler_base *fh = rec->alloc_fh ();
+ int ret = fh->msync (rec->get_handle (), addr, len, flags);
+ rec->free_fh (fh);
+
+ if (ret)
+ syscall_printf ("%d = msync(): %E", ret);
+ else
+ syscall_printf ("0 = msync()");
+
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync");
+ return 0;
+ }
+ }
+ }
+ }
+
+invalid_address_range:
+ /* SUSv2: Return code if indicated memory was not mapped is ENOMEM. */
+ set_errno (ENOMEM);
+ syscall_printf ("-1 = msync(): ENOMEM");
+
+ ReleaseResourceLock (LOCK_MMAP_LIST, WRITE_LOCK | READ_LOCK, "msync");
+ return -1;
+}
+
+/*
+ * Base implementation:
+ *
+ * `mmap' returns ENODEV as documented in SUSv2.
+ * In contrast to the global function implementation, the member function
+ * `mmap' has to return the mapped base address in `addr' and the handle to
+ * the mapping object as return value. In case of failure, the fhandler
+ * mmap has to close that handle by itself and return INVALID_HANDLE_VALUE.
+ *
+ * `munmap' and `msync' get the handle to the mapping object as first parameter
+ * additionally.
+*/
+HANDLE
+fhandler_base::mmap (caddr_t *addr, size_t len, DWORD access,
+ int flags, __off64_t off)
+{
+ set_errno (ENODEV);
+ return INVALID_HANDLE_VALUE;
+}
+
+int
+fhandler_base::munmap (HANDLE h, caddr_t addr, size_t len)
+{
+ set_errno (ENODEV);
+ return -1;
+}
+
+int
+fhandler_base::msync (HANDLE h, caddr_t addr, size_t len, int flags)
+{
+ set_errno (ENODEV);
+ return -1;
+}
+
+BOOL
+fhandler_base::fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset,
+ DWORD size, void *address)
+{
+ set_errno (ENODEV);
+ return -1;
+}
+
+/* Implementation for disk files. */
+HANDLE
+fhandler_disk_file::mmap (caddr_t *addr, size_t len, DWORD access,
+ int flags, __off64_t off)
+{
+ DWORD protect;
+
+ if (access & FILE_MAP_COPY)
+ protect = PAGE_WRITECOPY;
+ else if (access & FILE_MAP_WRITE)
+ protect = PAGE_READWRITE;
+ else
+ protect = PAGE_READONLY;
+
+ HANDLE h;
+
+ /* On 9x/ME try first to open the mapping by name when opening a
+ shared file object. This is needed since 9x/ME only shares
+ objects between processes by name. What a mess... */
+ if (wincap.share_mmaps_only_by_name ()
+ && get_handle () != INVALID_HANDLE_VALUE
+ && !(access & FILE_MAP_COPY))
+ {
+ /* Grrr, the whole stuff is just needed to try to get a reliable
+ mapping of the same file. Even that uprising isn't bullet
+ proof but it does it's best... */
+ char namebuf[MAX_PATH];
+ cygwin_conv_to_full_posix_path (get_name (), namebuf);
+ for (int i = strlen (namebuf) - 1; i >= 0; --i)
+ namebuf[i] = cyg_tolower (namebuf [i]);
+
+ debug_printf ("named sharing");
+ if (!(h = OpenFileMapping (access, TRUE, namebuf)))
+ h = CreateFileMapping (get_handle (), &sec_none, protect, 0, 0, namebuf);
+ }
+ else
+ h = CreateFileMapping (get_handle (), &sec_none, protect, 0,
+ get_handle () == INVALID_HANDLE_VALUE ? len : 0,
+ NULL);
+ if (!h)
+ {
+ __seterrno ();
+ syscall_printf ("-1 = mmap(): CreateFileMapping failed with %E");
+ return INVALID_HANDLE_VALUE;
+ }
+
+ DWORD high = off >> 32, low = off & 0xffffffff;
+ void *base = MapViewOfFileEx (h, access, high, low, len,
+ (flags & MAP_FIXED) ? *addr : NULL);
+ debug_printf ("%x = MapViewOfFileEx (h:%x, access:%x, 0, off:%D, len:%d, addr:%x)", base, h, access, off, len, (flags & MAP_FIXED) ? *addr : NULL);
+ if (!base || ((flags & MAP_FIXED) && base != *addr))
+ {
+ if (!base)
+ {
+ __seterrno ();
+ syscall_printf ("-1 = mmap(): MapViewOfFileEx failed with %E");
+ }
+ else
+ {
+ set_errno (EINVAL);
+ syscall_printf ("-1 = mmap(): address shift with MAP_FIXED given");
+ }
+ CloseHandle (h);
+ return INVALID_HANDLE_VALUE;
+ }
+
+ *addr = (caddr_t) base;
+ return h;
+}
+
+int
+fhandler_disk_file::munmap (HANDLE h, caddr_t addr, size_t len)
+{
+ UnmapViewOfFile (addr);
+ CloseHandle (h);
+ return 0;
+}
+
+int
+fhandler_disk_file::msync (HANDLE h, caddr_t addr, size_t len, int flags)
+{
+ if (FlushViewOfFile (addr, len) == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+BOOL
+fhandler_disk_file::fixup_mmap_after_fork (HANDLE h, DWORD access, DWORD offset,
+ DWORD size, void *address)
+{
+ /* Re-create the MapViewOfFileEx call */
+ void *base = MapViewOfFileEx (h, access, 0, offset, size, address);
+ if (base != address)
+ {
+ MEMORY_BASIC_INFORMATION m;
+ (void) VirtualQuery (address, &m, sizeof (m));
+ system_printf ("requested %p != %p mem alloc base %p, state %p, size %d, %E",
+ address, base, m.AllocationBase, m.State, m.RegionSize);
+ }
+ return base == address;
+}
+
+/* Set memory protection */
+
+extern "C"
+int
+mprotect (caddr_t addr, size_t len, int prot)
+{
+ DWORD old_prot;
+ DWORD new_prot = 0;
+
+ syscall_printf ("mprotect (addr %x, len %d, prot %x)", addr, len, prot);
+
+ if (!wincap.virtual_protect_works_on_shared_pages ()
+ && addr >= (caddr_t)0x80000000 && addr <= (caddr_t)0xBFFFFFFF)
+ {
+ syscall_printf ("0 = mprotect (9x: No VirtualProtect on shared memory)");
+ return 0;
+ }
+
+ if (prot == PROT_NONE)
+ new_prot = PAGE_NOACCESS;
+ else
+ {
+ switch (prot)
+ {
+ case PROT_READ | PROT_WRITE | PROT_EXEC:
+ new_prot = PAGE_EXECUTE_READWRITE;
+ break;
+ case PROT_READ | PROT_WRITE:
+ new_prot = PAGE_READWRITE;
+ break;
+ case PROT_READ | PROT_EXEC:
+ new_prot = PAGE_EXECUTE_READ;
+ break;
+ case PROT_READ:
+ new_prot = PAGE_READONLY;
+ break;
+ default:
+ syscall_printf ("-1 = mprotect (): invalid prot value");
+ set_errno (EINVAL);
+ return -1;
+ }
+ }
+
+ if (VirtualProtect (addr, len, new_prot, &old_prot) == 0)
+ {
+ __seterrno ();
+ syscall_printf ("-1 = mprotect (): %E");
+ return -1;
+ }
+
+ syscall_printf ("0 = mprotect ()");
+ return 0;
+}
+
+/*
+ * Call to re-create all the file mappings in a forked
+ * child. Called from the child in initialization. At this
+ * point we are passed a valid mmapped_areas map, and all the
+ * HANDLE's are valid for the child, but none of the
+ * mapped areas are in our address space. We need to iterate
+ * through the map, doing the MapViewOfFile calls.
+ */
+
+int __stdcall
+fixup_mmaps_after_fork (HANDLE parent)
+{
+
+ debug_printf ("recreate_mmaps_after_fork, mmapped_areas %p", mmapped_areas);
+
+ /* Check if a mmapped area was ever created */
+ if (mmapped_areas == NULL)
+ return 0;
+
+ /* Iterate through the map */
+ for (int it = 0; it < mmapped_areas->nlists; ++it)
+ {
+ list *l = mmapped_areas->lists[it];
+ if (l)
+ {
+ int li;
+ for (li = 0; li < l->nrecs; ++li)
+ {
+ mmap_record *rec = l->recs + li;
+
+ debug_printf ("fd %d, h %x, access %x, offset %d, size %d, address %p",
+ rec->get_fd (), rec->get_handle (), rec->get_access (),
+ rec->get_offset (), rec->get_size (), rec->get_address ());
+
+ fhandler_base *fh = rec->alloc_fh ();
+ BOOL ret = fh->fixup_mmap_after_fork (rec->get_handle (),
+ rec->get_access (),
+ rec->get_offset (),
+ rec->get_size (),
+ rec->get_address ());
+ rec->free_fh (fh);
+
+ if (!ret)
+ return -1;
+ if (rec->get_access () == FILE_MAP_COPY)
+ {
+ for (char *address = rec->get_address ();
+ address < rec->get_address () + rec->get_size ();
+ address += getpagesize ())
+ if (rec->access (address)
+ && !ReadProcessMemory (parent, address, address,
+ getpagesize (), NULL))
+ {
+ DWORD old_prot;
+
+ if (GetLastError () != ERROR_PARTIAL_COPY ||
+ !wincap.virtual_protect_works_on_shared_pages ())
+ {
+ system_printf ("ReadProcessMemory failed for "
+ "MAP_PRIVATE address %p, %E",
+ rec->get_address ());
+ return -1;
+ }
+ if (!VirtualProtectEx (parent,
+ address, getpagesize (),
+ PAGE_READONLY, &old_prot))
+ {
+ system_printf ("VirtualProtectEx failed for "
+ "MAP_PRIVATE address %p, %E",
+ rec->get_address ());
+ return -1;
+ }
+ else
+ {
+ BOOL ret;
+ DWORD dummy_prot;
+
+ ret = ReadProcessMemory (parent, address, address,
+ getpagesize (), NULL);
+ if (!VirtualProtectEx(parent,
+ address, getpagesize (),
+ old_prot, &dummy_prot))
+ system_printf ("WARNING: VirtualProtectEx to "
+ "return to previous state "
+ "in parent failed for "
+ "MAP_PRIVATE address %p, %E",
+ rec->get_address ());
+ if (!VirtualProtect (address, getpagesize (),
+ old_prot, &dummy_prot))
+ system_printf ("WARNING: VirtualProtect to copy "
+ "protection to child failed for"
+ "MAP_PRIVATE address %p, %E",
+ rec->get_address ());
+ if (!ret)
+ {
+ system_printf ("ReadProcessMemory (2nd try) "
+ "failed for "
+ "MAP_PRIVATE address %p, %E",
+ rec->get_address ());
+ return -1;
+ }
+ }
+ }
+ }
+ rec->fixup_map ();
+ }
+ }
+ }
+
+ debug_printf ("succeeded");
+ return 0;
+}
diff --git a/winsup/cygwin/net.cc b/winsup/cygwin/net.cc
new file mode 100644
index 00000000000..8bad529748c
--- /dev/null
+++ b/winsup/cygwin/net.cc
@@ -0,0 +1,2277 @@
+/* net.cc: network-related routines.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* #define DEBUG_NEST_ON 1 */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <iphlpapi.h>
+
+#include <stdlib.h>
+#define gethostname cygwin_gethostname
+#include <unistd.h>
+#undef gethostname
+#include <netdb.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock2.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "registry.h"
+#include "wsock_event.h"
+
+extern "C"
+{
+ int h_errno;
+
+ int __stdcall rcmd (char **ahost, unsigned short inport, char *locuser,
+ char *remuser, char *cmd, SOCKET * fd2p);
+ int __stdcall rexec (char **ahost, unsigned short inport, char *locuser,
+ char *password, char *cmd, SOCKET * fd2p);
+ int __stdcall rresvport (int *);
+ int sscanf (const char *, const char *, ...);
+} /* End of "C" section */
+
+LPWSAOVERLAPPED
+wsock_event::prepare ()
+{
+ LPWSAOVERLAPPED ret = NULL;
+
+ SetLastError (0);
+ if ((event = WSACreateEvent ()) != WSA_INVALID_EVENT)
+ {
+ memset (&ovr, 0, sizeof ovr);
+ ovr.hEvent = event;
+ ret = &ovr;
+ }
+ else if (GetLastError () == ERROR_PROC_NOT_FOUND) /* winsock2 not available */
+ WSASetLastError (0);
+
+ debug_printf ("%d = wsock_event::prepare ()", ret);
+ return ret;
+}
+
+int
+wsock_event::wait (int socket, LPDWORD flags)
+{
+ int ret = -1;
+ WSAEVENT ev[2] = { event, signal_arrived };
+
+ switch (WSAWaitForMultipleEvents (2, ev, FALSE, WSA_INFINITE, FALSE))
+ {
+ case WSA_WAIT_EVENT_0:
+ DWORD len;
+ if (WSAGetOverlappedResult (socket, &ovr, &len, FALSE, flags))
+ ret = (int) len;
+ break;
+ case WSA_WAIT_EVENT_0 + 1:
+ if (!CancelIo ((HANDLE) socket))
+ {
+ debug_printf ("CancelIo() %E, fallback to blocking io");
+ WSAGetOverlappedResult (socket, &ovr, &len, TRUE, flags);
+ }
+ else
+ WSASetLastError (WSAEINTR);
+ break;
+ case WSA_WAIT_FAILED:
+ break;
+ default: /* Should be impossible. *LOL* */
+ WSASetLastError (WSAEFAULT);
+ break;
+ }
+ WSACloseEvent (event);
+ event = NULL;
+ return ret;
+}
+
+WSADATA wsadata;
+
+static fhandler_socket *
+get (const int fd)
+{
+ cygheap_fdget cfd (fd);
+
+ if (cfd < 0)
+ return 0;
+
+ fhandler_socket *const fh = cfd->is_socket ();
+
+ if (!fh)
+ set_errno (ENOTSOCK);
+
+ return fh;
+}
+
+static SOCKET __stdcall
+set_socket_inheritance (SOCKET sock)
+{
+ SOCKET osock = sock;
+
+ if (!DuplicateHandle (hMainProc, (HANDLE) sock, hMainProc, (HANDLE *) &sock,
+ 0, TRUE, DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE))
+ system_printf ("DuplicateHandle failed %E");
+ else
+ debug_printf ("DuplicateHandle succeeded osock %p, sock %p", osock, sock);
+ return sock;
+}
+
+/* htonl: standards? */
+extern "C" unsigned long int
+htonl (unsigned long int x)
+{
+ return ((((x & 0x000000ffU) << 24) |
+ ((x & 0x0000ff00U) << 8) |
+ ((x & 0x00ff0000U) >> 8) |
+ ((x & 0xff000000U) >> 24)));
+}
+
+/* ntohl: standards? */
+extern "C" unsigned long int
+ntohl (unsigned long int x)
+{
+ return htonl (x);
+}
+
+/* htons: standards? */
+extern "C" unsigned short
+htons (unsigned short x)
+{
+ return ((((x & 0x000000ffU) << 8) |
+ ((x & 0x0000ff00U) >> 8)));
+}
+
+/* ntohs: standards? */
+extern "C" unsigned short
+ntohs (unsigned short x)
+{
+ return htons (x);
+}
+
+static void
+dump_protoent (struct protoent *p)
+{
+ if (p)
+ debug_printf ("protoent %s %x %x", p->p_name, p->p_aliases, p->p_proto);
+}
+
+/* exported as inet_ntoa: BSD 4.3 */
+extern "C" char *
+cygwin_inet_ntoa (struct in_addr in)
+{
+#ifdef _MT_SAFE
+#define ntoa_buf _reent_winsup ()->_ntoa_buf
+#else
+ static char *ntoa_buf = NULL;
+#endif
+
+ char *res = inet_ntoa (in);
+
+ if (ntoa_buf)
+ {
+ free (ntoa_buf);
+ ntoa_buf = NULL;
+ }
+ if (res)
+ ntoa_buf = strdup (res);
+ return ntoa_buf;
+}
+
+/* exported as inet_addr: BSD 4.3 */
+extern "C" unsigned long
+cygwin_inet_addr (const char *cp)
+{
+ if (check_null_str_errno (cp))
+ return INADDR_NONE;
+ unsigned long res = inet_addr (cp);
+
+ return res;
+}
+
+/* exported as inet_aton: BSD 4.3
+ inet_aton is not exported by wsock32 and ws2_32,
+ so it has to be implemented here. */
+extern "C" int
+cygwin_inet_aton (const char *cp, struct in_addr *inp)
+{
+ if (check_null_str_errno (cp) || check_null_invalid_struct_errno (inp))
+ return 0;
+
+ unsigned long res = inet_addr (cp);
+
+ if (res == INADDR_NONE && strcmp (cp, "255.255.255.255"))
+ return 0;
+ if (inp)
+ inp->s_addr = res;
+ return 1;
+}
+
+/* undocumented in wsock32.dll */
+extern "C" unsigned int WINAPI inet_network (const char *);
+
+extern "C" unsigned int
+cygwin_inet_network (const char *cp)
+{
+ if (check_null_str_errno (cp))
+ return INADDR_NONE;
+ unsigned int res = inet_network (cp);
+
+ return res;
+}
+
+/* inet_netof is in the standard BSD sockets library. It is useless
+ for modern networks, since it assumes network values which are no
+ longer meaningful, but some existing code calls it. */
+
+extern "C" unsigned long
+inet_netof (struct in_addr in)
+{
+ unsigned long i, res;
+
+ i = ntohl (in.s_addr);
+ if (IN_CLASSA (i))
+ res = (i & IN_CLASSA_NET) >> IN_CLASSA_NSHIFT;
+ else if (IN_CLASSB (i))
+ res = (i & IN_CLASSB_NET) >> IN_CLASSB_NSHIFT;
+ else
+ res = (i & IN_CLASSC_NET) >> IN_CLASSC_NSHIFT;
+
+
+ return res;
+}
+
+/* inet_makeaddr is in the standard BSD sockets library. It is
+ useless for modern networks, since it assumes network values which
+ are no longer meaningful, but some existing code calls it. */
+
+extern "C" struct in_addr
+inet_makeaddr (int net, int lna)
+{
+ unsigned long i;
+ struct in_addr in;
+
+ if (net < IN_CLASSA_MAX)
+ i = (net << IN_CLASSA_NSHIFT) | (lna & IN_CLASSA_HOST);
+ else if (net < IN_CLASSB_MAX)
+ i = (net << IN_CLASSB_NSHIFT) | (lna & IN_CLASSB_HOST);
+ else if (net < 0x1000000)
+ i = (net << IN_CLASSC_NSHIFT) | (lna & IN_CLASSC_HOST);
+ else
+ i = net | lna;
+
+ in.s_addr = htonl (i);
+
+
+ return in;
+}
+
+struct tl
+{
+ int w;
+ const char *s;
+ int e;
+};
+
+static NO_COPY struct tl errmap[] = {
+ {WSAEINTR, "WSAEINTR", EINTR},
+ {WSAEWOULDBLOCK, "WSAEWOULDBLOCK", EWOULDBLOCK},
+ {WSAEINPROGRESS, "WSAEINPROGRESS", EINPROGRESS},
+ {WSAEALREADY, "WSAEALREADY", EALREADY},
+ {WSAENOTSOCK, "WSAENOTSOCK", ENOTSOCK},
+ {WSAEDESTADDRREQ, "WSAEDESTADDRREQ", EDESTADDRREQ},
+ {WSAEMSGSIZE, "WSAEMSGSIZE", EMSGSIZE},
+ {WSAEPROTOTYPE, "WSAEPROTOTYPE", EPROTOTYPE},
+ {WSAENOPROTOOPT, "WSAENOPROTOOPT", ENOPROTOOPT},
+ {WSAEPROTONOSUPPORT, "WSAEPROTONOSUPPORT", EPROTONOSUPPORT},
+ {WSAESOCKTNOSUPPORT, "WSAESOCKTNOSUPPORT", ESOCKTNOSUPPORT},
+ {WSAEOPNOTSUPP, "WSAEOPNOTSUPP", EOPNOTSUPP},
+ {WSAEPFNOSUPPORT, "WSAEPFNOSUPPORT", EPFNOSUPPORT},
+ {WSAEAFNOSUPPORT, "WSAEAFNOSUPPORT", EAFNOSUPPORT},
+ {WSAEADDRINUSE, "WSAEADDRINUSE", EADDRINUSE},
+ {WSAEADDRNOTAVAIL, "WSAEADDRNOTAVAIL", EADDRNOTAVAIL},
+ {WSAENETDOWN, "WSAENETDOWN", ENETDOWN},
+ {WSAENETUNREACH, "WSAENETUNREACH", ENETUNREACH},
+ {WSAENETRESET, "WSAENETRESET", ENETRESET},
+ {WSAECONNABORTED, "WSAECONNABORTED", ECONNABORTED},
+ {WSAECONNRESET, "WSAECONNRESET", ECONNRESET},
+ {WSAENOBUFS, "WSAENOBUFS", ENOBUFS},
+ {WSAEISCONN, "WSAEISCONN", EISCONN},
+ {WSAENOTCONN, "WSAENOTCONN", ENOTCONN},
+ {WSAESHUTDOWN, "WSAESHUTDOWN", ESHUTDOWN},
+ {WSAETOOMANYREFS, "WSAETOOMANYREFS", ETOOMANYREFS},
+ {WSAETIMEDOUT, "WSAETIMEDOUT", ETIMEDOUT},
+ {WSAECONNREFUSED, "WSAECONNREFUSED", ECONNREFUSED},
+ {WSAELOOP, "WSAELOOP", ELOOP},
+ {WSAENAMETOOLONG, "WSAENAMETOOLONG", ENAMETOOLONG},
+ {WSAEHOSTDOWN, "WSAEHOSTDOWN", EHOSTDOWN},
+ {WSAEHOSTUNREACH, "WSAEHOSTUNREACH", EHOSTUNREACH},
+ {WSAENOTEMPTY, "WSAENOTEMPTY", ENOTEMPTY},
+ {WSAEPROCLIM, "WSAEPROCLIM", EPROCLIM},
+ {WSAEUSERS, "WSAEUSERS", EUSERS},
+ {WSAEDQUOT, "WSAEDQUOT", EDQUOT},
+ {WSAESTALE, "WSAESTALE", ESTALE},
+ {WSAEREMOTE, "WSAEREMOTE", EREMOTE},
+ {WSAEINVAL, "WSAEINVAL", EINVAL},
+ {WSAEFAULT, "WSAEFAULT", EFAULT},
+ {0, "NOERROR", 0},
+ {0, NULL, 0}
+};
+
+static int
+find_winsock_errno (int why)
+{
+ for (int i = 0; errmap[i].s != NULL; ++i)
+ if (why == errmap[i].w)
+ return errmap[i].e;
+
+ return EPERM;
+}
+
+void
+__set_winsock_errno (const char *fn, int ln)
+{
+ DWORD werr = WSAGetLastError ();
+ int err = find_winsock_errno (werr);
+
+ set_errno (err);
+ syscall_printf ("%s:%d - winsock error %d -> errno %d", fn, ln, werr, err);
+}
+
+/*
+ * Since the member `s' isn't used for debug output we can use it
+ * for the error text returned by herror and hstrerror.
+ */
+static NO_COPY struct tl host_errmap[] = {
+ {WSAHOST_NOT_FOUND, "Unknown host", HOST_NOT_FOUND},
+ {WSATRY_AGAIN, "Host name lookup failure", TRY_AGAIN},
+ {WSANO_RECOVERY, "Unknown server error", NO_RECOVERY},
+ {WSANO_DATA, "No address associated with name", NO_DATA},
+ {0, NULL, 0}
+};
+
+static void
+set_host_errno ()
+{
+ int i;
+
+ int why = WSAGetLastError ();
+
+ for (i = 0; host_errmap[i].w != 0; ++i)
+ if (why == host_errmap[i].w)
+ break;
+
+ if (host_errmap[i].w != 0)
+ h_errno = host_errmap[i].e;
+ else
+ h_errno = NETDB_INTERNAL;
+}
+
+static void
+free_char_list (char **clist)
+{
+ if (clist)
+ {
+ for (char **cl = clist; *cl; ++cl)
+ free (*cl);
+ free (clist);
+ }
+}
+
+static char **
+dup_char_list (char **src)
+{
+ char **dst;
+ int cnt = 0;
+
+ for (char **cl = src; *cl; ++cl)
+ ++cnt;
+ if (!(dst = (char **) calloc (cnt + 1, sizeof *dst)))
+ return NULL;
+ while (cnt-- > 0)
+ if (!(dst[cnt] = strdup (src[cnt])))
+ return NULL;
+ return dst;
+}
+
+#define free_addr_list(addr_list) free_char_list (addr_list)
+
+static char **
+dup_addr_list (char **src, unsigned int size)
+{
+ char **dst;
+ int cnt = 0;
+
+ for (char **cl = src; *cl; ++cl)
+ ++cnt;
+ if (!(dst = (char **) calloc (cnt + 1, sizeof *dst)))
+ return NULL;
+ while (cnt-- > 0)
+ {
+ if (!(dst[cnt] = (char *) malloc (size)))
+ return NULL;
+ memcpy (dst[cnt], src[cnt], size);
+ }
+ return dst;
+}
+
+static void
+free_protoent_ptr (struct protoent *&p)
+{
+ if (p)
+ {
+ debug_printf ("protoent: %s", p->p_name);
+
+ if (p->p_name)
+ free (p->p_name);
+ free_char_list (p->p_aliases);
+ free ((void *) p);
+ p = NULL;
+ }
+}
+
+static struct protoent *
+dup_protoent_ptr (struct protoent *src)
+{
+ if (!src)
+ return NULL;
+
+ struct protoent *dst = (struct protoent *) calloc (1, sizeof *dst);
+
+ if (!dst)
+ return NULL;
+
+ debug_printf ("protoent: %s", src->p_name);
+
+ dst->p_proto = src->p_proto;
+ if (src->p_name && !(dst->p_name = strdup (src->p_name)))
+ goto out;
+ if (src->p_aliases && !(dst->p_aliases = dup_char_list (src->p_aliases)))
+ goto out;
+
+ debug_printf ("protoent: copied %s", dst->p_name);
+
+ return dst;
+
+out:
+ free_protoent_ptr (dst);
+ return NULL;
+}
+
+#ifdef _MT_SAFE
+#define protoent_buf _reent_winsup ()->_protoent_buf
+#else
+static struct protoent *protoent_buf = NULL;
+#endif
+
+/* exported as getprotobyname: standards? */
+extern "C" struct protoent *
+cygwin_getprotobyname (const char *p)
+{
+ if (check_null_str_errno (p))
+ return NULL;
+ free_protoent_ptr (protoent_buf);
+ protoent_buf = dup_protoent_ptr (getprotobyname (p));
+ if (!protoent_buf)
+ set_winsock_errno ();
+
+ dump_protoent (protoent_buf);
+ return protoent_buf;
+}
+
+/* exported as getprotobynumber: standards? */
+extern "C" struct protoent *
+cygwin_getprotobynumber (int number)
+{
+ free_protoent_ptr (protoent_buf);
+ protoent_buf = dup_protoent_ptr (getprotobynumber (number));
+ if (!protoent_buf)
+ set_winsock_errno ();
+
+ dump_protoent (protoent_buf);
+ return protoent_buf;
+}
+
+fhandler_socket *
+fdsock (int &fd, const char *name, SOCKET soc)
+{
+ if (!winsock2_active)
+ soc = set_socket_inheritance (soc);
+ else if (wincap.has_set_handle_information ())
+ {
+ /* NT systems apparently set sockets to inheritable by default */
+ SetHandleInformation ((HANDLE) soc, HANDLE_FLAG_INHERIT, 0);
+ debug_printf ("reset socket inheritance since winsock2_active %d",
+ winsock2_active);
+ }
+ else
+ debug_printf ("not setting socket inheritance since winsock2_active %d",
+ winsock2_active);
+ fhandler_socket *fh =
+ (fhandler_socket *) cygheap->fdtab.build_fhandler (fd, *socket_dev, name);
+ if (!fh)
+ return NULL;
+ fh->set_io_handle ((HANDLE) soc);
+ fh->set_flags (O_RDWR | O_BINARY);
+ fh->set_r_no_interrupt (winsock2_active);
+ debug_printf ("fd %d, name '%s', soc %p", fd, name, soc);
+ return fh;
+}
+
+/* exported as socket: standards? */
+extern "C" int
+cygwin_socket (int af, int type, int protocol)
+{
+ int res = -1;
+ SOCKET soc = 0;
+ fhandler_socket *fh = NULL;
+
+ cygheap_fdnew fd;
+
+ if (fd >= 0)
+ {
+ debug_printf ("socket (%d, %d, %d)", af, type, protocol);
+
+ soc = socket (AF_INET, type, af == AF_LOCAL ? 0 : protocol);
+
+ if (soc == INVALID_SOCKET)
+ {
+ set_winsock_errno ();
+ goto done;
+ }
+
+ const char *name;
+
+ if (af == AF_INET)
+ name = (type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp");
+ else
+ name = (type == SOCK_STREAM ? "/dev/streamsocket" : "/dev/dgsocket");
+
+ fh = fdsock (fd, name, soc);
+ if (!fh)
+ {
+ closesocket (soc);
+ res = -1;
+ }
+ else
+ {
+ fh->set_addr_family (af);
+ fh->set_socket_type (type);
+ res = fd;
+ }
+ }
+
+done:
+ syscall_printf ("%d = socket (%d, %d, %d)", res, af, type, protocol);
+ return res;
+}
+
+/* exported as sendto: standards? */
+extern "C" int
+cygwin_sendto (int fd, const void *buf, int len, int flags,
+ const struct sockaddr *to, int tolen)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if ((len && __check_invalid_read_ptr_errno (buf, (unsigned) len))
+ || (to && __check_invalid_read_ptr_errno (to, tolen))
+ || !fh)
+ res = -1;
+ else if ((res = len) != 0)
+ res = fh->sendto (buf, len, flags, to, tolen);
+
+ syscall_printf ("%d = sendto (%d, %p, %d, %x, %p, %d)",
+ res, fd, buf, len, flags, to, tolen);
+
+ return res;
+}
+
+/* exported as recvfrom: standards? */
+extern "C" int
+cygwin_recvfrom (int fd, void *buf, int len, int flags,
+ struct sockaddr *from, int *fromlen)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if ((len && __check_null_invalid_struct_errno (buf, (unsigned) len))
+ || (from
+ && (check_null_invalid_struct_errno (fromlen)
+ || __check_null_invalid_struct_errno (from, (unsigned) *fromlen)))
+ || !fh)
+ res = -1;
+ else if ((res = len) != 0)
+ res = fh->recvfrom (buf, len, flags, from, fromlen);
+
+ syscall_printf ("%d = recvfrom (%d, %p, %d, %x, %p, %p)",
+ res, fd, buf, len, flags, from, fromlen);
+
+ return res;
+}
+
+/* exported as setsockopt: standards? */
+extern "C" int
+cygwin_setsockopt (int fd, int level, int optname, const void *optval,
+ int optlen)
+{
+ int res;
+ fhandler_socket *fh = get (fd);
+ const char *name = "error";
+
+ /* For the following debug_printf */
+ switch (optname)
+ {
+ case SO_DEBUG:
+ name = "SO_DEBUG";
+ break;
+ case SO_ACCEPTCONN:
+ name = "SO_ACCEPTCONN";
+ break;
+ case SO_REUSEADDR:
+ name = "SO_REUSEADDR";
+ break;
+ case SO_KEEPALIVE:
+ name = "SO_KEEPALIVE";
+ break;
+ case SO_DONTROUTE:
+ name = "SO_DONTROUTE";
+ break;
+ case SO_BROADCAST:
+ name = "SO_BROADCAST";
+ break;
+ case SO_USELOOPBACK:
+ name = "SO_USELOOPBACK";
+ break;
+ case SO_LINGER:
+ name = "SO_LINGER";
+ break;
+ case SO_OOBINLINE:
+ name = "SO_OOBINLINE";
+ break;
+ case SO_ERROR:
+ name = "SO_ERROR";
+ break;
+ }
+
+ if ((optval && __check_invalid_read_ptr_errno (optval, optlen)) || !fh)
+ res = -1;
+ else
+ {
+ res = setsockopt (fh->get_socket (), level, optname,
+ (const char *) optval, optlen);
+
+ if (optlen == 4)
+ syscall_printf ("setsockopt optval=%x", *(long *) optval);
+
+ if (res)
+ set_winsock_errno ();
+ }
+
+ syscall_printf ("%d = setsockopt (%d, %d, %x (%s), %p, %d)",
+ res, fd, level, optname, name, optval, optlen);
+ return res;
+}
+
+/* exported as getsockopt: standards? */
+extern "C" int
+cygwin_getsockopt (int fd, int level, int optname, void *optval, int *optlen)
+{
+ int res;
+ fhandler_socket *fh = get (fd);
+ const char *name = "error";
+
+ /* For the following debug_printf */
+ switch (optname)
+ {
+ case SO_DEBUG:
+ name = "SO_DEBUG";
+ break;
+ case SO_ACCEPTCONN:
+ name = "SO_ACCEPTCONN";
+ break;
+ case SO_REUSEADDR:
+ name = "SO_REUSEADDR";
+ break;
+ case SO_KEEPALIVE:
+ name = "SO_KEEPALIVE";
+ break;
+ case SO_DONTROUTE:
+ name = "SO_DONTROUTE";
+ break;
+ case SO_BROADCAST:
+ name = "SO_BROADCAST";
+ break;
+ case SO_USELOOPBACK:
+ name = "SO_USELOOPBACK";
+ break;
+ case SO_LINGER:
+ name = "SO_LINGER";
+ break;
+ case SO_OOBINLINE:
+ name = "SO_OOBINLINE";
+ break;
+ case SO_ERROR:
+ name = "SO_ERROR";
+ break;
+ }
+
+ if ((optval
+ && (check_null_invalid_struct_errno (optlen)
+ || __check_null_invalid_struct_errno (optval, (unsigned) *optlen)))
+ || !fh)
+ res = -1;
+ else
+ {
+ res = getsockopt (fh->get_socket (), level, optname, (char *) optval,
+ (int *) optlen);
+
+ if (optname == SO_ERROR)
+ {
+ int *e = (int *) optval;
+
+ *e = find_winsock_errno (*e);
+ }
+
+ if (res)
+ set_winsock_errno ();
+ }
+
+ syscall_printf ("%d = getsockopt (%d, %d, %x (%s), %p, %p)",
+ res, fd, level, optname, name, optval, optlen);
+ return res;
+}
+
+/* exported as connect: standards? */
+extern "C" int
+cygwin_connect (int fd, const struct sockaddr *name, int namelen)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if (__check_invalid_read_ptr_errno (name, namelen) || !fh)
+ res = -1;
+ else
+ res = fh->connect (name, namelen);
+
+ syscall_printf ("%d = connect (%d, %p, %d)", res, fd, name, namelen);
+
+ return res;
+}
+
+static void
+free_servent_ptr (struct servent *&p)
+{
+ if (p)
+ {
+ debug_printf ("servent: %s", p->s_name);
+
+ if (p->s_name)
+ free (p->s_name);
+ if (p->s_proto)
+ free (p->s_proto);
+ free_char_list (p->s_aliases);
+ free ((void *) p);
+ p = NULL;
+ }
+}
+
+#pragma pack(push,2)
+struct pservent
+{
+ char *s_name;
+ char **s_aliases;
+ short s_port;
+ char *s_proto;
+};
+
+#pragma pack(pop)
+static struct servent *
+dup_servent_ptr (struct servent *src)
+{
+ if (!src)
+ return NULL;
+
+ struct servent *dst = (struct servent *) calloc (1, sizeof *dst);
+
+ if (!dst)
+ return NULL;
+
+ debug_printf ("servent: %s", src->s_name);
+
+ dst->s_port = src->s_port;
+ if (src->s_name && !(dst->s_name = strdup (src->s_name)))
+ goto out;
+ if (src->s_aliases && !(dst->s_aliases = dup_char_list (src->s_aliases)))
+ goto out;
+ char *s_proto;
+
+ if (IsBadReadPtr (src->s_proto, sizeof (src->s_proto))
+ && !IsBadReadPtr (((pservent *) src)->s_proto, sizeof (src->s_proto)))
+ s_proto = ((pservent *) src)->s_proto;
+ else
+ s_proto = src->s_proto;
+
+ if (s_proto && !(dst->s_proto = strdup (s_proto)))
+ goto out;
+
+ debug_printf ("servent: copied %s", dst->s_name);
+
+ return dst;
+
+out:
+ free_servent_ptr (dst);
+ return NULL;
+}
+
+#ifdef _MT_SAFE
+#define servent_buf _reent_winsup ()->_servent_buf
+#else
+static struct servent *servent_buf = NULL;
+#endif
+
+/* exported as getservbyname: standards? */
+extern "C" struct servent *
+cygwin_getservbyname (const char *name, const char *proto)
+{
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (check_null_str_errno (name)
+ || (proto != NULL && check_null_str_errno (proto)))
+ return NULL;
+
+ free_servent_ptr (servent_buf);
+ servent_buf = dup_servent_ptr (getservbyname (name, proto));
+ if (!servent_buf)
+ set_winsock_errno ();
+
+ syscall_printf ("%x = getservbyname (%s, %s)", servent_buf, name, proto);
+ return servent_buf;
+}
+
+/* exported as getservbyport: standards? */
+extern "C" struct servent *
+cygwin_getservbyport (int port, const char *proto)
+{
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (proto != NULL && check_null_str_errno (proto))
+ return NULL;
+
+ free_servent_ptr (servent_buf);
+ servent_buf = dup_servent_ptr (getservbyport (port, proto));
+ if (!servent_buf)
+ set_winsock_errno ();
+
+ syscall_printf ("%x = getservbyport (%d, %s)", servent_buf, port, proto);
+ return servent_buf;
+}
+
+extern "C" int
+cygwin_gethostname (char *name, size_t len)
+{
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (__check_null_invalid_struct_errno (name, len))
+ return -1;
+
+ if (gethostname (name, len))
+ {
+ DWORD local_len = len;
+
+ if (!GetComputerNameA (name, &local_len))
+ {
+ set_winsock_errno ();
+ return -1;
+ }
+ }
+ debug_printf ("name %s", name);
+ h_errno = 0;
+ return 0;
+}
+
+static void
+free_hostent_ptr (struct hostent *&p)
+{
+ if (p)
+ {
+ debug_printf ("hostent: %s", p->h_name);
+
+ if (p->h_name)
+ free ((void *) p->h_name);
+ free_char_list (p->h_aliases);
+ free_addr_list (p->h_addr_list);
+ free ((void *) p);
+ p = NULL;
+ }
+}
+
+static struct hostent *
+dup_hostent_ptr (struct hostent *src)
+{
+ if (!src)
+ return NULL;
+
+ struct hostent *dst = (struct hostent *) calloc (1, sizeof *dst);
+
+ if (!dst)
+ return NULL;
+
+ debug_printf ("hostent: %s", src->h_name);
+
+ dst->h_addrtype = src->h_addrtype;
+ dst->h_length = src->h_length;
+ if (src->h_name && !(dst->h_name = strdup (src->h_name)))
+ goto out;
+ if (src->h_aliases && !(dst->h_aliases = dup_char_list (src->h_aliases)))
+ goto out;
+ if (src->h_addr_list
+ && !(dst->h_addr_list = dup_addr_list (src->h_addr_list, src->h_length)))
+ goto out;
+
+ debug_printf ("hostent: copied %s", dst->h_name);
+
+ return dst;
+
+out:
+ free_hostent_ptr (dst);
+ return NULL;
+}
+
+#ifdef _MT_SAFE
+#define hostent_buf _reent_winsup ()->_hostent_buf
+#else
+static struct hostent *hostent_buf = NULL;
+#endif
+
+/* exported as gethostbyname: standards? */
+extern "C" struct hostent *
+cygwin_gethostbyname (const char *name)
+{
+ static unsigned char tmp_addr[4];
+ static struct hostent tmp;
+ static char *tmp_aliases[1];
+ static char *tmp_addr_list[2];
+ static int a, b, c, d;
+
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (check_null_str_errno (name))
+ return NULL;
+
+ if (sscanf (name, "%d.%d.%d.%d", &a, &b, &c, &d) == 4)
+ {
+ /* In case you don't have DNS, at least x.x.x.x still works */
+ memset (&tmp, 0, sizeof (tmp));
+ tmp_addr[0] = a;
+ tmp_addr[1] = b;
+ tmp_addr[2] = c;
+ tmp_addr[3] = d;
+ tmp_addr_list[0] = (char *) tmp_addr;
+ tmp.h_name = name;
+ tmp.h_aliases = tmp_aliases;
+ tmp.h_addrtype = 2;
+ tmp.h_length = 4;
+ tmp.h_addr_list = tmp_addr_list;
+ return &tmp;
+ }
+
+ free_hostent_ptr (hostent_buf);
+ hostent_buf = dup_hostent_ptr (gethostbyname (name));
+ if (!hostent_buf)
+ {
+ set_winsock_errno ();
+ set_host_errno ();
+ }
+ else
+ {
+ debug_printf ("h_name %s", hostent_buf->h_name);
+ h_errno = 0;
+ }
+ return hostent_buf;
+}
+
+/* exported as gethostbyaddr: standards? */
+extern "C" struct hostent *
+cygwin_gethostbyaddr (const char *addr, int len, int type)
+{
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (__check_invalid_read_ptr_errno (addr, len))
+ return NULL;
+
+ free_hostent_ptr (hostent_buf);
+ hostent_buf = dup_hostent_ptr (gethostbyaddr (addr, len, type));
+ if (!hostent_buf)
+ {
+ set_winsock_errno ();
+ set_host_errno ();
+ }
+ else
+ {
+ debug_printf ("h_name %s", hostent_buf->h_name);
+ h_errno = 0;
+ }
+ return hostent_buf;
+}
+
+/* exported as accept: standards? */
+extern "C" int
+cygwin_accept (int fd, struct sockaddr *peer, int *len)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if ((peer && (check_null_invalid_struct_errno (len)
+ || __check_null_invalid_struct_errno (peer, (unsigned) *len)))
+ || !fh)
+ res = -1;
+ else
+ res = fh->accept (peer, len);
+
+ syscall_printf ("%d = accept (%d, %p, %p)", res, fd, peer, len);
+ return res;
+}
+
+/* exported as bind: standards? */
+extern "C" int
+cygwin_bind (int fd, const struct sockaddr *my_addr, int addrlen)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ fhandler_socket *fh = get (fd);
+
+ if (__check_invalid_read_ptr_errno (my_addr, addrlen) || !fh)
+ res = -1;
+ else
+ res = fh->bind (my_addr, addrlen);
+
+ syscall_printf ("%d = bind (%d, %p, %d)", res, fd, my_addr, addrlen);
+ return res;
+}
+
+/* exported as getsockname: standards? */
+extern "C" int
+cygwin_getsockname (int fd, struct sockaddr *addr, int *namelen)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if (check_null_invalid_struct_errno (namelen)
+ || __check_null_invalid_struct_errno (addr, (unsigned) *namelen)
+ || !fh)
+ res = -1;
+ else
+ res = fh->getsockname (addr, namelen);
+
+ syscall_printf ("%d = getsockname (%d, %p, %p)", res, fd, addr, namelen);
+ return res;
+}
+
+/* exported as listen: standards? */
+extern "C" int
+cygwin_listen (int fd, int backlog)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ fhandler_socket *fh = get (fd);
+
+ if (!fh)
+ res = -1;
+ else
+ res = fh->listen (backlog);
+
+ syscall_printf ("%d = listen (%d, %d)", res, fd, backlog);
+ return res;
+}
+
+/* exported as shutdown: standards? */
+extern "C" int
+cygwin_shutdown (int fd, int how)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if (!fh)
+ res = -1;
+ else
+ res = fh->shutdown (how);
+
+ syscall_printf ("%d = shutdown (%d, %d)", res, fd, how);
+ return res;
+}
+
+/* exported as hstrerror: BSD 4.3 */
+extern "C" const char *
+cygwin_hstrerror (int err)
+{
+ int i;
+
+ for (i = 0; host_errmap[i].e != 0; ++i)
+ if (err == host_errmap[i].e)
+ break;
+
+ return host_errmap[i].s;
+}
+
+/* exported as herror: BSD 4.3 */
+extern "C" void
+cygwin_herror (const char *s)
+{
+ if (s && check_null_str (s))
+ return;
+ if (cygheap->fdtab.not_open (2))
+ return;
+
+ if (s)
+ {
+ write (2, s, strlen (s));
+ write (2, ": ", 2);
+ }
+
+ const char *h_errstr = cygwin_hstrerror (h_errno);
+
+ if (!h_errstr)
+ switch (h_errno)
+ {
+ case NETDB_INTERNAL:
+ h_errstr = "Resolver internal error";
+ break;
+ case NETDB_SUCCESS:
+ h_errstr = "Resolver error 0 (no error)";
+ break;
+ default:
+ h_errstr = "Unknown resolver error";
+ break;
+ }
+ write (2, h_errstr, strlen (h_errstr));
+ write (2, "\n", 1);
+}
+
+/* exported as getpeername: standards? */
+extern "C" int
+cygwin_getpeername (int fd, struct sockaddr *name, int *len)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if (check_null_invalid_struct_errno (len)
+ || __check_null_invalid_struct_errno (name, (unsigned) *len)
+ || !fh)
+ res = -1;
+ else
+ res = fh->getpeername (name, len);
+
+ syscall_printf ("%d = getpeername %d", res, (fh ? fh->get_socket () : -1));
+ return res;
+}
+
+/* exported as recv: standards? */
+extern "C" int
+cygwin_recv (int fd, void *buf, int len, int flags)
+{
+ return cygwin_recvfrom (fd, buf, len, flags, NULL, NULL);
+}
+
+/* exported as send: standards? */
+extern "C" int
+cygwin_send (int fd, const void *buf, int len, int flags)
+{
+ return cygwin_sendto (fd, buf, len, flags, NULL, 0);
+}
+
+/* getdomainname: standards? */
+extern "C" int
+getdomainname (char *domain, size_t len)
+{
+ /*
+ * This works for Win95 only if the machine is configured to use MS-TCP.
+ * If a third-party TCP is being used this will fail.
+ * FIXME: On Win95, is there a way to portably check the TCP stack
+ * in use and include paths for the Domain name in each ?
+ * Punt for now and assume MS-TCP on Win95.
+ */
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (__check_null_invalid_struct_errno (domain, len))
+ return -1;
+
+ reg_key r (HKEY_LOCAL_MACHINE, KEY_READ,
+ (!wincap.is_winnt ()) ? "System" : "SYSTEM",
+ "CurrentControlSet", "Services",
+ (!wincap.is_winnt ()) ? "VxD" : "Tcpip",
+ (!wincap.is_winnt ()) ? "MSTCP" : "Parameters", NULL);
+
+ /* FIXME: Are registry keys case sensitive? */
+ if (r.error () || r.get_string ("Domain", domain, len, "") != ERROR_SUCCESS)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Fill out an ifconf struct. */
+
+/*
+ * IFCONF 98/ME, NTSP4, W2K:
+ * Use IP Helper Library
+ */
+static void
+get_2k_ifconf (struct ifconf *ifc, int what)
+{
+ int cnt = 0;
+ int ethId = 0, pppId = 0, slpId = 0, tokId = 0;
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ DWORD ip_cnt, lip, lnp;
+ DWORD siz_ip_table = 0;
+ PMIB_IPADDRTABLE ipt;
+ PMIB_IFROW ifrow;
+ struct sockaddr_in *sa = NULL;
+ struct sockaddr *so = NULL;
+
+ typedef struct ifcount_t
+ {
+ DWORD ifIndex;
+ size_t count;
+ unsigned int enumerated; // for eth0:1
+ unsigned int classId; // for eth0, tok0 ...
+
+ };
+ ifcount_t *iflist, *ifEntry;
+
+ if (GetIpAddrTable (NULL, &siz_ip_table, TRUE) == ERROR_INSUFFICIENT_BUFFER
+ && (ifrow = (PMIB_IFROW) alloca (sizeof (MIB_IFROW)))
+ && (ipt = (PMIB_IPADDRTABLE) alloca (siz_ip_table))
+ && !GetIpAddrTable (ipt, &siz_ip_table, TRUE))
+ {
+ iflist =
+ (ifcount_t *) alloca (sizeof (ifcount_t) * (ipt->dwNumEntries + 1));
+ memset (iflist, 0, sizeof (ifcount_t) * (ipt->dwNumEntries + 1));
+ for (ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ++ip_cnt)
+ {
+ ifEntry = iflist;
+ /* search for matching entry (and stop at first free entry) */
+ while (ifEntry->count != 0)
+ {
+ if (ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex)
+ break;
+ ifEntry++;
+ }
+ if (ifEntry->count == 0)
+ {
+ ifEntry->count = 1;
+ ifEntry->ifIndex = ipt->table[ip_cnt].dwIndex;
+ }
+ else
+ {
+ ifEntry->count++;
+ }
+ }
+ // reset the last element. This is just the stopper for the loop.
+ iflist[ipt->dwNumEntries].count = 0;
+
+ /* Iterate over all configured IP-addresses */
+ for (ip_cnt = 0; ip_cnt < ipt->dwNumEntries; ++ip_cnt)
+ {
+ memset (ifrow, 0, sizeof (MIB_IFROW));
+ ifrow->dwIndex = ipt->table[ip_cnt].dwIndex;
+ if (GetIfEntry (ifrow) != NO_ERROR)
+ continue;
+
+ ifcount_t *ifEntry = iflist;
+
+ /* search for matching entry (and stop at first free entry) */
+ while (ifEntry->count != 0)
+ {
+ if (ifEntry->ifIndex == ipt->table[ip_cnt].dwIndex)
+ break;
+ ifEntry++;
+ }
+
+ /* Setup the interface name */
+ switch (ifrow->dwType)
+ {
+ case MIB_IF_TYPE_TOKENRING:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = tokId++;
+ __small_sprintf (ifr->ifr_name, "tok%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "tok%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_ETHERNET:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = ethId++;
+ __small_sprintf (ifr->ifr_name, "eth%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "eth%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_PPP:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = pppId++;
+ __small_sprintf (ifr->ifr_name, "ppp%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "ppp%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_SLIP:
+ if (ifEntry->enumerated == 0)
+ {
+ ifEntry->classId = slpId++;
+ __small_sprintf (ifr->ifr_name, "slp%u",
+ ifEntry->classId);
+ }
+ else
+ {
+ __small_sprintf (ifr->ifr_name, "slp%u:%u",
+ ifEntry->classId,
+ ifEntry->enumerated - 1);
+ }
+ ifEntry->enumerated++;
+ break;
+ case MIB_IF_TYPE_LOOPBACK:
+ strcpy (ifr->ifr_name, "lo");
+ break;
+ default:
+ continue;
+ }
+ /* setup sockaddr struct */
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = ipt->table[ip_cnt].dwAddr;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+#if 0
+ /* Unfortunately, the field returns only crap. */
+ sa->sin_addr.s_addr = ipt->table[ip_cnt].dwBCastAddr;
+#else
+ lip = ipt->table[ip_cnt].dwAddr;
+ lnp = ipt->table[ip_cnt].dwMask;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+#endif
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = ipt->table[ip_cnt].dwMask;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ for (UINT i = 0; i < IFHWADDRLEN; ++i)
+ if (i >= ifrow->dwPhysAddrLen)
+ so->sa_data[i] = '\0';
+ else
+ so->sa_data[i] = ifrow->bPhysAddr[i];
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = ifrow->dwMtu;
+ break;
+ }
+ ++cnt;
+ if ((caddr_t)++ ifr >
+ ifc->ifc_buf + ifc->ifc_len - sizeof (struct ifreq))
+ goto done;
+ }
+ }
+
+done:
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+}
+
+/*
+ * IFCONF Windows NT < SP4:
+ * Look at the Bind value in
+ * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Linkage\
+ * This is a REG_MULTI_SZ with strings of the form:
+ * \Device\<Netcard>, where netcard is the name of the net device.
+ * Then look under:
+ * HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\<NetCard>\
+ * Parameters\Tcpip
+ * at the IPAddress, Subnetmask and DefaultGateway values for the
+ * required values.
+ */
+static void
+get_nt_ifconf (struct ifconf *ifc, int what)
+{
+ HKEY key;
+ unsigned long lip, lnp;
+ struct sockaddr_in *sa = NULL;
+ struct sockaddr *so = NULL;
+ DWORD size;
+ int cnt = 1;
+ char *binding = (char *) 0;
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE,
+ "SYSTEM\\"
+ "CurrentControlSet\\"
+ "Services\\"
+ "Tcpip\\" "Linkage",
+ 0, KEY_READ, &key) == ERROR_SUCCESS)
+ {
+ if (RegQueryValueEx (key, "Bind",
+ NULL, NULL,
+ NULL, &size) == ERROR_SUCCESS)
+ {
+ binding = (char *) alloca (size);
+ if (RegQueryValueEx (key, "Bind",
+ NULL, NULL,
+ (unsigned char *) binding,
+ &size) != ERROR_SUCCESS)
+ {
+ binding = NULL;
+ }
+ }
+ RegCloseKey (key);
+ }
+
+ if (binding)
+ {
+ char *bp, eth[2] = "/";
+ char cardkey[256], ipaddress[256], netmask[256];
+
+ for (bp = binding; *bp; bp += strlen (bp) + 1)
+ {
+ bp += strlen ("\\Device\\");
+ strcpy (cardkey, "SYSTEM\\CurrentControlSet\\Services\\");
+ strcat (cardkey, bp);
+ strcat (cardkey, "\\Parameters\\Tcpip");
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, cardkey,
+ 0, KEY_READ, &key) != ERROR_SUCCESS)
+ continue;
+
+ if (RegQueryValueEx (key, "IPAddress",
+ NULL, NULL,
+ (unsigned char *) ipaddress,
+ (size = 256, &size)) == ERROR_SUCCESS
+ && RegQueryValueEx (key, "SubnetMask",
+ NULL, NULL,
+ (unsigned char *) netmask,
+ (size = 256, &size)) == ERROR_SUCCESS)
+ {
+ char *ip, *np;
+ char dhcpaddress[256], dhcpnetmask[256];
+
+ for (ip = ipaddress, np = netmask;
+ *ip && *np;
+ ip += strlen (ip) + 1, np += strlen (np) + 1)
+ {
+ if ((caddr_t)++ ifr > ifc->ifc_buf
+ + ifc->ifc_len - sizeof (struct ifreq))
+ break;
+
+ if (!strncmp (bp, "NdisWan", 7))
+ {
+ strcpy (ifr->ifr_name, "ppp");
+ strcat (ifr->ifr_name, bp + 7);
+ }
+ else
+ {
+ ++*eth;
+ strcpy (ifr->ifr_name, "eth");
+ strcat (ifr->ifr_name, eth);
+ }
+ memset (&ifr->ifr_addr, '\0', sizeof ifr->ifr_addr);
+ if (cygwin_inet_addr (ip) == 0L
+ && RegQueryValueEx (key, "DhcpIPAddress",
+ NULL, NULL,
+ (unsigned char *) dhcpaddress,
+ (size = 256, &size))
+ == ERROR_SUCCESS
+ && RegQueryValueEx (key, "DhcpSubnetMask",
+ NULL, NULL,
+ (unsigned char *) dhcpnetmask,
+ (size = 256, &size))
+ == ERROR_SUCCESS)
+ {
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr =
+ cygwin_inet_addr (dhcpaddress);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = cygwin_inet_addr (dhcpaddress);
+ lnp = cygwin_inet_addr (dhcpnetmask);
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr =
+ cygwin_inet_addr (dhcpnetmask);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ memset (so->sa_data, 0, IFHWADDRLEN);
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = 1500;
+ break;
+ }
+ }
+ else
+ {
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = cygwin_inet_addr (ip);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = cygwin_inet_addr (ip);
+ lnp = cygwin_inet_addr (np);
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = cygwin_inet_addr (np);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ memset (so->sa_data, 0, IFHWADDRLEN);
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = 1500;
+ break;
+ }
+ }
+ ++cnt;
+ }
+ }
+ RegCloseKey (key);
+ }
+ }
+
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+}
+
+/*
+ * IFCONF Windows 95:
+ * HKLM/Enum/Network/MSTCP/"*"
+ * -> Value "Driver" enthält Subkey relativ zu
+ * HKLM/System/CurrentControlSet/Class/
+ * -> In Subkey "Bindings" die Values aufzählen
+ * -> Enthält Subkeys der Form "VREDIR\*"
+ * Das * ist ein Subkey relativ zu
+ * HKLM/System/CurrentControlSet/Class/Net/
+ * HKLM/System/CurrentControlSet/Class/"Driver"
+ * -> Value "IPAddress"
+ * -> Value "IPMask"
+ * HKLM/System/CurrentControlSet/Class/Net/"*"(aus "VREDIR\*")
+ * -> Wenn Value "AdapterName" == "MS$PPP" -> ppp interface
+ * -> Value "DriverDesc" enthält den Namen
+ *
+ */
+static void
+get_95_ifconf (struct ifconf *ifc, int what)
+{
+ HKEY key;
+ unsigned long lip, lnp;
+ struct sockaddr_in *sa = NULL;
+ struct sockaddr *so = NULL;
+ FILETIME update;
+ LONG res;
+ DWORD size;
+ int cnt = 1;
+ char ifname[256];
+ char eth[2] = "/";
+ char ppp[2] = "/";
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, "Enum\\Network\\MSTCP",
+ 0, KEY_READ, &key) != ERROR_SUCCESS)
+ {
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+ return;
+ }
+
+ for (int i = 0;
+ (res = RegEnumKeyEx (key, i, ifname,
+ (size = sizeof ifname, &size),
+ 0, 0, 0, &update)) != ERROR_NO_MORE_ITEMS;
+ ++i)
+ {
+ HKEY ifkey, subkey;
+ char driver[256], classname[256], netname[256];
+ char adapter[256], ip[256], np[256];
+
+ if (res != ERROR_SUCCESS
+ || RegOpenKeyEx (key, ifname, 0, KEY_READ, &ifkey) != ERROR_SUCCESS)
+ continue;
+
+ if (RegQueryValueEx (ifkey, "Driver", 0,
+ NULL, (unsigned char *) driver,
+ (size = sizeof driver, &size)) != ERROR_SUCCESS)
+ {
+ RegCloseKey (ifkey);
+ continue;
+ }
+
+ strcpy (classname, "System\\CurrentControlSet\\Services\\Class\\");
+ strcat (classname, driver);
+ if ((res = RegOpenKeyEx (HKEY_LOCAL_MACHINE, classname,
+ 0, KEY_READ, &subkey)) != ERROR_SUCCESS)
+ {
+ RegCloseKey (ifkey);
+ continue;
+ }
+
+ if (RegQueryValueEx (subkey, "IPAddress", 0,
+ NULL, (unsigned char *) ip,
+ (size = sizeof ip, &size)) == ERROR_SUCCESS
+ && RegQueryValueEx (subkey, "IPMask", 0,
+ NULL, (unsigned char *) np,
+ (size = sizeof np, &size)) == ERROR_SUCCESS)
+ {
+ if ((caddr_t)++ ifr > ifc->ifc_buf
+ + ifc->ifc_len - sizeof (struct ifreq))
+ goto out;
+
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = cygwin_inet_addr (ip);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = cygwin_inet_addr (ip);
+ lnp = cygwin_inet_addr (np);
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = cygwin_inet_addr (np);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ so = &ifr->ifr_hwaddr;
+ memset (so->sa_data, 0, IFHWADDRLEN);
+ so->sa_family = AF_INET;
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ ifr->ifr_mtu = 1500;
+ break;
+ }
+ }
+
+ RegCloseKey (subkey);
+
+ strcpy (netname, "System\\CurrentControlSet\\Services\\Class\\Net\\");
+ strcat (netname, ifname);
+
+ if (RegOpenKeyEx (HKEY_LOCAL_MACHINE, netname,
+ 0, KEY_READ, &subkey) != ERROR_SUCCESS)
+ {
+ RegCloseKey (ifkey);
+ --ifr;
+ continue;
+ }
+
+ if (RegQueryValueEx (subkey, "AdapterName", 0,
+ NULL, (unsigned char *) adapter,
+ (size = sizeof adapter, &size)) == ERROR_SUCCESS
+ && strcasematch (adapter, "MS$PPP"))
+ {
+ ++*ppp;
+ strcpy (ifr->ifr_name, "ppp");
+ strcat (ifr->ifr_name, ppp);
+ }
+ else
+ {
+ ++*eth;
+ strcpy (ifr->ifr_name, "eth");
+ strcat (ifr->ifr_name, eth);
+ }
+
+ RegCloseKey (subkey);
+ RegCloseKey (ifkey);
+
+ ++cnt;
+ }
+
+out:
+
+ RegCloseKey (key);
+
+ /* Set the correct length */
+ ifc->ifc_len = cnt * sizeof (struct ifreq);
+}
+
+int
+get_ifconf (struct ifconf *ifc, int what)
+{
+ unsigned long lip, lnp;
+ struct sockaddr_in *sa;
+
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (check_null_invalid_struct_errno (ifc))
+ return -1;
+
+ /* Union maps buffer to correct struct */
+ struct ifreq *ifr = ifc->ifc_req;
+
+ /* Ensure we have space for two struct ifreqs, fail if not. */
+ if (ifc->ifc_len < (int) (2 * sizeof (struct ifreq)))
+ {
+ set_errno (EFAULT);
+ return -1;
+ }
+
+ /* Set up interface lo0 first */
+ strcpy (ifr->ifr_name, "lo");
+ memset (&ifr->ifr_addr, '\0', sizeof (ifr->ifr_addr));
+ switch (what)
+ {
+ case SIOCGIFCONF:
+ case SIOCGIFADDR:
+ sa = (struct sockaddr_in *) &ifr->ifr_addr;
+ sa->sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFBRDADDR:
+ lip = htonl (INADDR_LOOPBACK);
+ lnp = cygwin_inet_addr ("255.0.0.0");
+ sa = (struct sockaddr_in *) &ifr->ifr_broadaddr;
+ sa->sin_addr.s_addr = lip & lnp | ~lnp;
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFNETMASK:
+ sa = (struct sockaddr_in *) &ifr->ifr_netmask;
+ sa->sin_addr.s_addr = cygwin_inet_addr ("255.0.0.0");
+ sa->sin_family = AF_INET;
+ sa->sin_port = 0;
+ break;
+ case SIOCGIFHWADDR:
+ ifr->ifr_hwaddr.sa_family = AF_INET;
+ memset (ifr->ifr_hwaddr.sa_data, 0, IFHWADDRLEN);
+ break;
+ case SIOCGIFMETRIC:
+ ifr->ifr_metric = 1;
+ break;
+ case SIOCGIFMTU:
+ /* This funny value is returned by `ifconfig lo' on Linux 2.2 kernel. */
+ ifr->ifr_mtu = 3924;
+ break;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ OSVERSIONINFO os_version_info;
+
+ memset (&os_version_info, 0, sizeof os_version_info);
+ os_version_info.dwOSVersionInfoSize = sizeof (OSVERSIONINFO);
+ GetVersionEx (&os_version_info);
+ if (wincap.has_ip_helper_lib ())
+ get_2k_ifconf (ifc, what);
+ else if (wincap.is_winnt ())
+ get_nt_ifconf (ifc, what);
+ else
+ get_95_ifconf (ifc, what);
+ return 0;
+}
+
+/* exported as rcmd: standards? */
+extern "C" int
+cygwin_rcmd (char **ahost, unsigned short inport, char *locuser,
+ char *remuser, char *cmd, int *fd2p)
+{
+ int res = -1;
+ SOCKET fd2s;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ if (check_null_invalid_struct_errno (ahost) ||
+ check_null_empty_str_errno (*ahost) ||
+ (locuser && check_null_empty_str_errno (locuser)) ||
+ (remuser && check_null_str_errno (remuser)))
+ return (int) INVALID_SOCKET;
+
+ cygheap_fdnew res_fd;
+
+ if (res_fd < 0)
+ goto done;
+
+ if (fd2p)
+ {
+ cygheap_fdnew newfd (res_fd, false);
+
+ if (*fd2p < 0)
+ goto done;
+ *fd2p = newfd;
+ }
+
+ res = rcmd (ahost, inport, locuser, remuser, cmd, fd2p ? &fd2s : NULL);
+ if (res == (int) INVALID_SOCKET)
+ goto done;
+ else
+ {
+ fdsock (res_fd, "/dev/tcp", res);
+ res = res_fd;
+ }
+
+ if (fd2p)
+ fdsock (*fd2p, "/dev/tcp", fd2s);
+
+done:
+ syscall_printf ("%d = rcmd (...)", res);
+ return res;
+}
+
+/* exported as rresvport: standards? */
+extern "C" int
+cygwin_rresvport (int *port)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ if (check_null_invalid_struct_errno (port))
+ return -1;
+
+ cygheap_fdnew res_fd;
+
+ if (res_fd < 0)
+ res = -1;
+ else
+ {
+ res = rresvport (port);
+
+ if (res != (int) INVALID_SOCKET)
+ {
+ fdsock (res_fd, "/dev/tcp", res);
+ res = res_fd;
+ }
+ }
+
+ syscall_printf ("%d = rresvport (%d)", res, port ? *port : 0);
+ return res;
+}
+
+/* exported as rexec: standards? */
+extern "C" int
+cygwin_rexec (char **ahost, unsigned short inport, char *locuser,
+ char *password, char *cmd, int *fd2p)
+{
+ int res = -1;
+ SOCKET fd2s;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ if (check_null_invalid_struct_errno (ahost) ||
+ check_null_empty_str_errno (*ahost) ||
+ (locuser && check_null_empty_str_errno (locuser)) ||
+ (password && check_null_str_errno (password)))
+ return (int) INVALID_SOCKET;
+
+ cygheap_fdnew res_fd;
+
+ if (res_fd < 0)
+ goto done;
+ if (fd2p)
+ {
+ cygheap_fdnew newfd (res_fd);
+
+ if (newfd < 0)
+ goto done;
+ *fd2p = newfd;
+ }
+ res = rexec (ahost, inport, locuser, password, cmd, fd2p ? &fd2s : NULL);
+ if (res == (int) INVALID_SOCKET)
+ goto done;
+ else
+ {
+ fdsock (res_fd, "/dev/tcp", res);
+ res = res_fd;
+ }
+ if (fd2p)
+ fdsock (*fd2p, "/dev/tcp", fd2s);
+
+done:
+ syscall_printf ("%d = rexec (...)", res);
+ return res;
+}
+
+/* socketpair: standards? */
+/* Win32 supports AF_INET only, so ignore domain and protocol arguments */
+extern "C" int
+socketpair (int family, int type, int protocol, int *sb)
+{
+ int res = -1;
+ SOCKET insock, outsock, newsock;
+ struct sockaddr_in sock_in, sock_out;
+ int len;
+ cygheap_fdnew sb0;
+ fhandler_socket *fh;
+
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+ if (__check_null_invalid_struct_errno (sb, 2 * sizeof (int)))
+ return -1;
+
+ if (family != AF_LOCAL && family != AF_INET)
+ {
+ set_errno (EAFNOSUPPORT);
+ goto done;
+ }
+ if (type != SOCK_STREAM && type != SOCK_DGRAM)
+ {
+ set_errno (EPROTOTYPE);
+ goto done;
+ }
+ if ((family == AF_LOCAL && protocol != PF_UNSPEC && protocol != PF_LOCAL)
+ || (family == AF_INET && protocol != PF_UNSPEC && protocol != PF_INET))
+ {
+ set_errno (EPROTONOSUPPORT);
+ goto done;
+ }
+
+ if (sb0 < 0)
+ goto done;
+ else
+ {
+ sb[0] = sb0;
+ cygheap_fdnew sb1 (sb0, false);
+
+ if (sb1 < 0)
+ goto done;
+
+ sb[1] = sb1;
+ }
+
+ /* create the first socket */
+ newsock = socket (AF_INET, type, 0);
+ if (newsock == INVALID_SOCKET)
+ {
+ debug_printf ("first socket call failed");
+ set_winsock_errno ();
+ goto done;
+ }
+
+ /* bind the socket to any unused port */
+ sock_in.sin_family = AF_INET;
+ sock_in.sin_port = 0;
+ sock_in.sin_addr.s_addr = INADDR_ANY;
+ if (bind (newsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
+ {
+ debug_printf ("bind failed");
+ set_winsock_errno ();
+ closesocket (newsock);
+ goto done;
+ }
+ len = sizeof (sock_in);
+ if (getsockname (newsock, (struct sockaddr *) &sock_in, &len) < 0)
+ {
+ debug_printf ("getsockname error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ goto done;
+ }
+
+ /* For stream sockets, create a listener */
+ if (type == SOCK_STREAM)
+ listen (newsock, 2);
+
+ /* create a connecting socket */
+ outsock = socket (AF_INET, type, 0);
+ if (outsock == INVALID_SOCKET)
+ {
+ debug_printf ("second socket call failed");
+ set_winsock_errno ();
+ closesocket (newsock);
+ goto done;
+ }
+
+ /* For datagram sockets, bind the 2nd socket to an unused address, too */
+ if (type == SOCK_DGRAM)
+ {
+ sock_out.sin_family = AF_INET;
+ sock_out.sin_port = 0;
+ sock_out.sin_addr.s_addr = INADDR_ANY;
+ if (bind (outsock, (struct sockaddr *) &sock_out, sizeof (sock_out)) < 0)
+ {
+ debug_printf ("bind failed");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ len = sizeof (sock_out);
+ if (getsockname (outsock, (struct sockaddr *) &sock_out, &len) < 0)
+ {
+ debug_printf ("getsockname error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ }
+
+ /* Force IP address to loopback */
+ sock_in.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ if (type == SOCK_DGRAM)
+ sock_out.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+
+ /* Do a connect */
+ if (connect (outsock, (struct sockaddr *) &sock_in, sizeof (sock_in)) < 0)
+ {
+ debug_printf ("connect error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+
+ if (type == SOCK_STREAM)
+ {
+ /* For stream sockets, accept the connection and close the listener */
+ len = sizeof (sock_in);
+ insock = accept (newsock, (struct sockaddr *) &sock_in, &len);
+ if (insock == INVALID_SOCKET)
+ {
+ debug_printf ("accept error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ closesocket (newsock);
+ }
+ else
+ {
+ /* For datagram sockets, connect the 2nd socket */
+ if (connect (newsock, (struct sockaddr *) &sock_out,
+ sizeof (sock_out)) < 0)
+ {
+ debug_printf ("connect error");
+ set_winsock_errno ();
+ closesocket (newsock);
+ closesocket (outsock);
+ goto done;
+ }
+ insock = newsock;
+ }
+
+ res = 0;
+
+ if (family == AF_LOCAL)
+ {
+
+ fh = fdsock (sb[0],
+ type == SOCK_STREAM ? "/dev/streamsocket" : "/dev/dgsocket",
+ insock);
+ fh->set_sun_path ("");
+ fh->set_addr_family (AF_LOCAL);
+ fh->set_socket_type (type);
+ fh = fdsock (sb[1],
+ type == SOCK_STREAM ? "/dev/streamsocket" : "/dev/dgsocket",
+ outsock);
+ fh->set_sun_path ("");
+ fh->set_addr_family (AF_LOCAL);
+ fh->set_socket_type (type);
+ }
+ else
+ {
+ fh = fdsock (sb[0], type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp",
+ insock);
+ fh->set_addr_family (AF_INET);
+ fh->set_socket_type (type);
+ fh = fdsock (sb[1], type == SOCK_STREAM ? "/dev/tcp" : "/dev/udp",
+ outsock);
+ fh->set_addr_family (AF_INET);
+ fh->set_socket_type (type);
+ }
+
+done:
+ syscall_printf ("%d = socketpair (...)", res);
+ return res;
+}
+
+/* sethostent: standards? */
+extern "C" void
+sethostent (int)
+{
+}
+
+/* endhostent: standards? */
+extern "C" void
+endhostent (void)
+{
+}
+
+/* exported as recvmsg: standards? */
+extern "C" int
+cygwin_recvmsg (int fd, struct msghdr *msg, int flags)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if (check_null_invalid_struct_errno (msg)
+ || (msg->msg_name
+ && __check_null_invalid_struct_errno (msg->msg_name,
+ (unsigned) msg->msg_namelen))
+ || !fh)
+ res = -1;
+ else
+ {
+ res = check_iovec_for_read (msg->msg_iov, msg->msg_iovlen);
+ if (res > 0)
+ res = fh->recvmsg (msg, flags, res); // res == iovec tot
+ }
+
+ syscall_printf ("%d = recvmsg (%d, %p, %x)", res, fd, msg, flags);
+ return res;
+}
+
+/* exported as sendmsg: standards? */
+extern "C" int
+cygwin_sendmsg (int fd, const struct msghdr *msg, int flags)
+{
+ int res;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ fhandler_socket *fh = get (fd);
+
+ if (__check_invalid_read_ptr_errno (msg, sizeof msg)
+ || (msg->msg_name
+ && __check_invalid_read_ptr_errno (msg->msg_name,
+ (unsigned) msg->msg_namelen))
+ || !fh)
+ res = -1;
+ else
+ {
+ res = check_iovec_for_write (msg->msg_iov, msg->msg_iovlen);
+ if (res > 0)
+ res = fh->sendmsg (msg, flags, res); // res == iovec tot
+ }
+
+ syscall_printf ("%d = sendmsg (%d, %p, %x)", res, fd, msg, flags);
+ return res;
+}
diff --git a/winsup/cygwin/path.cc b/winsup/cygwin/path.cc
new file mode 100644
index 00000000000..bb8870a9333
--- /dev/null
+++ b/winsup/cygwin/path.cc
@@ -0,0 +1,3588 @@
+/* path.cc: path support.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* This module's job is to
+ - convert between POSIX and Win32 style filenames,
+ - support the `mount' functionality,
+ - support symlinks for files and directories
+
+ Pathnames are handled as follows:
+
+ - A \ or : in a path denotes a pure windows spec.
+ - Paths beginning with // (or \\) are not translated (i.e. looked
+ up in the mount table) and are assumed to be UNC path names.
+
+ The goal in the above set of rules is to allow both POSIX and Win32
+ flavors of pathnames without either interfering. The rules are
+ intended to be as close to a superset of both as possible.
+
+ Note that you can have more than one path to a file. The mount
+ table is always prefered when translating Win32 paths to POSIX
+ paths. Win32 paths in mount table entries may be UNC paths or
+ standard Win32 paths starting with <drive-letter>:
+
+ Text vs Binary issues are not considered here in path style
+ decisions, although the appropriate flags are retrieved and
+ stored in various structures.
+
+ Removing mounted filesystem support would simplify things greatly,
+ but having it gives us a mechanism of treating disk that lives on a
+ UNIX machine as having UNIX semantics [it allows one to edit a text
+ file on that disk and not have cr's magically appear and perhaps
+ break apps running on UNIX boxes]. It also useful to be able to
+ layout a hierarchy without changing the underlying directories.
+
+ The semantics of mounting file systems is not intended to precisely
+ follow normal UNIX systems.
+
+ Each DOS drive is defined to have a current directory. Supporting
+ this would complicate things so for now things are defined so that
+ c: means c:\. FIXME: Is this still true?
+*/
+
+#include "winsup.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/mount.h>
+#include <mntent.h>
+#include <unistd.h>
+#include <errno.h>
+#include <ctype.h>
+#include <winioctl.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <winnls.h>
+#include <winnetwk.h>
+#include <sys/cygwin.h>
+#include <cygwin/version.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "sync.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "shared_info.h"
+#include "registry.h"
+#include <assert.h>
+
+#ifdef _MT_SAFE
+#define iteration _reent_winsup ()->_iteration
+#define available_drives _reent_winsup ()->available_drives
+#else
+static int iteration;
+static DWORD available_drives;
+#endif
+
+static int normalize_win32_path (const char *src, char *dst);
+static void slashify (const char *src, char *dst, int trailing_slash_p);
+static void backslashify (const char *src, char *dst, int trailing_slash_p);
+
+struct symlink_info
+{
+ char contents[MAX_PATH + 4];
+ char *ext_here;
+ int extn;
+ unsigned pflags;
+ DWORD fileattr;
+ int is_symlink;
+ bool ext_tacked_on;
+ int error;
+ bool case_clash;
+ _major_t major;
+ _minor_t minor;
+ _mode_t mode;
+ int check (char *path, const suffix_info *suffixes, unsigned opt);
+ bool parse_device (const char *);
+ BOOL case_check (char *path);
+};
+
+int pcheck_case = PCHECK_RELAXED; /* Determines the case check behaviour. */
+
+static char shortcut_header[SHORTCUT_HDR_SIZE];
+static BOOL shortcut_initalized;
+
+static void
+create_shortcut_header (void)
+{
+ if (!shortcut_initalized)
+ {
+ shortcut_header[0] = 'L';
+ shortcut_header[4] = '\001';
+ shortcut_header[5] = '\024';
+ shortcut_header[6] = '\002';
+ shortcut_header[12] = '\300';
+ shortcut_header[19] = 'F';
+ shortcut_header[20] = '\f';
+ shortcut_header[60] = '\001';
+ shortcut_initalized = TRUE;
+ }
+}
+
+#define CYGWIN_REGNAME (cygheap->cygwin_regname ?: CYGWIN_INFO_CYGWIN_REGISTRY_NAME)
+
+/* Determine if path prefix matches current cygdrive */
+#define iscygdrive(path) \
+ (path_prefix_p (mount_table->cygdrive, (path), mount_table->cygdrive_len))
+
+#define iscygdrive_device(path) \
+ (isalpha (path[mount_table->cygdrive_len]) && \
+ (path[mount_table->cygdrive_len + 1] == '/' || \
+ !path[mount_table->cygdrive_len + 1]))
+
+#define isproc(path) \
+ (path_prefix_p (proc, (path), proc_len))
+
+#define isvirtual_dev(devn) \
+ (devn == FH_CYGDRIVE || devn == FH_PROC || devn == FH_REGISTRY || devn == FH_PROCESS)
+
+/* Return non-zero if PATH1 is a prefix of PATH2.
+ Both are assumed to be of the same path style and / vs \ usage.
+ Neither may be "".
+ LEN1 = strlen (PATH1). It's passed because often it's already known.
+
+ Examples:
+ /foo/ is a prefix of /foo <-- may seem odd, but desired
+ /foo is a prefix of /foo/
+ / is a prefix of /foo/bar
+ / is not a prefix of foo/bar
+ foo/ is a prefix foo/bar
+ /foo is not a prefix of /foobar
+*/
+
+int
+path_prefix_p (const char *path1, const char *path2, int len1)
+{
+ /* Handle case where PATH1 has trailing '/' and when it doesn't. */
+ if (len1 > 0 && isdirsep (path1[len1 - 1]))
+ len1--;
+
+ if (len1 == 0)
+ return isdirsep (path2[0]) && !isdirsep (path2[1]);
+
+ if (!pathnmatch (path1, path2, len1))
+ return 0;
+
+ return isdirsep (path2[len1]) || path2[len1] == 0 || path1[len1 - 1] == ':';
+}
+
+/* Return non-zero if paths match in first len chars.
+ Check is dependent of the case sensitivity setting. */
+int
+pathnmatch (const char *path1, const char *path2, int len)
+{
+ return pcheck_case == PCHECK_STRICT ? !strncmp (path1, path2, len)
+ : strncasematch (path1, path2, len);
+}
+
+/* Return non-zero if paths match. Check is dependent of the case
+ sensitivity setting. */
+int
+pathmatch (const char *path1, const char *path2)
+{
+ return pcheck_case == PCHECK_STRICT ? !strcmp (path1, path2)
+ : strcasematch (path1, path2);
+}
+
+/* Normalize a POSIX path.
+ \'s are converted to /'s in the process.
+ All duplicate /'s, except for 2 leading /'s, are deleted.
+ The result is 0 for success, or an errno error value. */
+
+#define isslash(c) ((c) == '/')
+
+static int
+normalize_posix_path (const char *src, char *dst)
+{
+ const char *src_start = src;
+ char *dst_start = dst;
+
+ syscall_printf ("src %s", src);
+
+ if (isdrive (src) || strpbrk (src, "\\:"))
+ {
+ int err = normalize_win32_path (src, dst);
+ if (!err)
+ for (char *p = dst; (p = strchr (p, '\\')); p++)
+ *p = '/';
+ return err;
+ }
+
+ if (!isslash (src[0]))
+ {
+ if (!cygheap->cwd.get (dst))
+ return get_errno ();
+ dst = strchr (dst, '\0');
+ if (*src == '.')
+ {
+ if (dst == dst_start + 1 && *dst_start == '/')
+ --dst;
+ goto sawdot;
+ }
+ if (dst > dst_start && !isslash (dst[-1]))
+ *dst++ = '/';
+ }
+ /* Two leading /'s? If so, preserve them. */
+ else if (isslash (src[1]))
+ {
+ *dst++ = '/';
+ *dst++ = '/';
+ src += 2;
+ if (isslash (*src))
+ { /* Starts with three or more slashes - reset. */
+ dst = dst_start;
+ *dst++ = '/';
+ src = src_start + 1;
+ }
+ else if (src[0] == '.' && isslash (src[1]))
+ {
+ *dst++ = '.';
+ *dst++ = '/';
+ src += 2;
+ }
+ }
+ else
+ *dst = '\0';
+
+ while (*src)
+ {
+ /* Strip runs of /'s. */
+ if (!isslash (*src))
+ *dst++ = *src++;
+ else
+ {
+ while (*++src)
+ {
+ if (isslash (*src))
+ continue;
+
+ if (*src != '.')
+ break;
+
+ sawdot:
+ if (src[1] != '.')
+ {
+ if (!src[1])
+ {
+ if (dst == dst_start)
+ *dst++ = '/';
+ goto done;
+ }
+ if (!isslash (src[1]))
+ break;
+ }
+ else if (src[2] && !isslash (src[2]))
+ {
+ if (src[2] == '.')
+ return ENOENT;
+ break;
+ }
+ else
+ {
+ while (dst > dst_start && !isslash (*--dst))
+ continue;
+ src++;
+ }
+ }
+
+ *dst++ = '/';
+ }
+ if ((dst - dst_start) >= MAX_PATH)
+ {
+ debug_printf ("ENAMETOOLONG = normalize_posix_path (%s)", src);
+ return ENAMETOOLONG;
+ }
+ }
+
+done:
+ *dst = '\0';
+ if (--dst > dst_start && isslash (*dst))
+ *dst = '\0';
+
+ debug_printf ("%s = normalize_posix_path (%s)", dst_start, src_start);
+ return 0;
+}
+
+inline void
+path_conv::add_ext_from_sym (symlink_info &sym)
+{
+ if (sym.ext_here && *sym.ext_here)
+ {
+ known_suffix = path + sym.extn;
+ if (sym.ext_tacked_on)
+ strcpy (known_suffix, sym.ext_here);
+ }
+}
+
+static void __stdcall mkrelpath (char *dst) __attribute__ ((regparm (2)));
+static void __stdcall
+mkrelpath (char *path)
+{
+ char cwd_win32[MAX_PATH];
+ if (!cygheap->cwd.get (cwd_win32, 0))
+ return;
+
+ unsigned cwdlen = strlen (cwd_win32);
+ if (!path_prefix_p (cwd_win32, path, cwdlen))
+ return;
+
+ size_t n = strlen (path);
+ if (n < cwdlen)
+ return;
+
+ char *tail = path;
+ if (n == cwdlen)
+ tail += cwdlen;
+ else
+ tail += isdirsep (cwd_win32[cwdlen - 1]) ? cwdlen : cwdlen + 1;
+
+ memmove (path, tail, strlen (tail) + 1);
+ if (!*path)
+ strcpy (path, ".");
+}
+
+bool
+fs_info::update (const char *win32_path)
+{
+ char tmp_buf [MAX_PATH];
+ strncpy (tmp_buf, win32_path, MAX_PATH);
+
+ if (!rootdir (tmp_buf))
+ {
+ debug_printf ("Cannot get root component of path %s", win32_path);
+ name [0] = '\0';
+ sym_opt = flags = serial = 0;
+ return false;
+ }
+
+ if (strcmp (tmp_buf, root_dir) == 0)
+ return 1;
+
+ strncpy (root_dir, tmp_buf, MAX_PATH);
+ drive_type = GetDriveType (root_dir);
+ if (drive_type == DRIVE_REMOTE || (drive_type == DRIVE_UNKNOWN && (root_dir[0] == '\\' && root_dir[1] == '\\')))
+ is_remote_drive = 1;
+ else
+ is_remote_drive = 0;
+
+ if (!GetVolumeInformation (root_dir, NULL, 0, &serial, NULL, &flags,
+ name, sizeof (name)))
+ {
+ debug_printf ("Cannot get volume information (%s), %E", root_dir);
+ name [0] = '\0';
+ sym_opt = flags = serial = 0;
+ return false;
+ }
+ /* FIXME: Samba by default returns "NTFS" in file system name, but
+ * doesn't support Extended Attributes. If there's some fast way to
+ * distinguish between samba and real ntfs, it should be implemented
+ * here.
+ */
+ sym_opt = (!is_remote_drive && strcmp (name, "NTFS") == 0) ? PC_CHECK_EA : 0;
+
+ return true;
+}
+
+char *
+path_conv::return_and_clear_normalized_path ()
+{
+ char *s = normalized_path;
+ normalized_path = NULL;
+ return s;
+}
+
+void
+path_conv::fillin (HANDLE h)
+{
+ BY_HANDLE_FILE_INFORMATION local;
+ if (!GetFileInformationByHandle (h, &local))
+ {
+ fileattr = INVALID_FILE_ATTRIBUTES;
+ fs.serial = 0;
+ }
+ else
+ {
+ fileattr = local.dwFileAttributes;
+ fs.serial = local.dwVolumeSerialNumber;
+ }
+ fs.drive_type = DRIVE_UNKNOWN;
+}
+
+/* Convert an arbitrary path SRC to a pure Win32 path, suitable for
+ passing to Win32 API routines.
+
+ If an error occurs, `error' is set to the errno value.
+ Otherwise it is set to 0.
+
+ follow_mode values:
+ SYMLINK_FOLLOW - convert to PATH symlink points to
+ SYMLINK_NOFOLLOW - convert to PATH of symlink itself
+ SYMLINK_IGNORE - do not check PATH for symlinks
+ SYMLINK_CONTENTS - just return symlink contents
+*/
+
+void
+path_conv::check (const char *src, unsigned opt,
+ const suffix_info *suffixes)
+{
+ /* This array is used when expanding symlinks. It is MAX_PATH * 2
+ in length so that we can hold the expanded symlink plus a
+ trailer. */
+ char path_copy[MAX_PATH + 3];
+ char tmp_buf[2 * MAX_PATH + 3];
+ symlink_info sym;
+ bool need_directory = 0;
+ bool saw_symlinks = 0;
+ int is_relpath;
+ char *tail;
+ sigframe thisframe (mainthread);
+
+#if 0
+ static path_conv last_path_conv;
+ static char last_src[MAX_PATH + 1];
+
+ if (*last_src && strcmp (last_src, src) == 0)
+ {
+ *this = last_path_conv;
+ return;
+ }
+#endif
+
+ int loop = 0;
+ path_flags = 0;
+ known_suffix = NULL;
+ fileattr = INVALID_FILE_ATTRIBUTES;
+ case_clash = false;
+ memset (&dev, 0, sizeof (dev));
+ fs.root_dir[0] = '\0';
+ fs.name[0] = '\0';
+ fs.flags = fs.serial = 0;
+ fs.sym_opt = 0;
+ fs.drive_type = 0;
+ fs.is_remote_drive = 0;
+ normalized_path = NULL;
+
+ if (!(opt & PC_NULLEMPTY))
+ error = 0;
+ else if ((error = check_null_empty_str (src)))
+ return;
+ /* This loop handles symlink expansion. */
+ for (;;)
+ {
+ MALLOC_CHECK;
+ assert (src);
+
+ char *p = strrchr (src, '\0');
+ /* Detect if the user was looking for a directory. We have to strip the
+ trailing slash initially and add it back on at the end due to Windows
+ brain damage. */
+ if (--p > src)
+ {
+ if (isdirsep (*p))
+ need_directory = 1;
+ else if (--p > src && p[1] == '.' && isdirsep (*p))
+ need_directory = 1;
+ }
+
+ is_relpath = !isabspath (src);
+ error = normalize_posix_path (src, path_copy);
+ if (error)
+ return;
+
+ tail = strchr (path_copy, '\0'); // Point to end of copy
+ char *path_end = tail;
+ tail[1] = '\0';
+
+ /* Scan path_copy from right to left looking either for a symlink
+ or an actual existing file. If an existing file is found, just
+ return. If a symlink is found exit the for loop.
+ Also: be careful to preserve the errno returned from
+ symlink.check as the caller may need it. */
+ /* FIXME: Do we have to worry about multiple \'s here? */
+ int component = 0; // Number of translated components
+ sym.contents[0] = '\0';
+
+ for (;;)
+ {
+ const suffix_info *suff;
+ char pathbuf[MAX_PATH];
+ char *full_path;
+
+ /* Don't allow symlink.check to set anything in the path_conv
+ class if we're working on an inner component of the path */
+ if (component)
+ {
+ suff = NULL;
+ sym.pflags = 0;
+ full_path = pathbuf;
+ }
+ else
+ {
+ suff = suffixes;
+ sym.pflags = path_flags;
+ full_path = this->path;
+ }
+
+ /* Convert to native path spec sans symbolic link info. */
+ error = mount_table->conv_to_win32_path (path_copy, full_path, dev,
+ &sym.pflags, 1);
+
+ if (error)
+ return;
+
+ if (dev.major == DEV_CYGDRIVE_MAJOR)
+ {
+ if (!component)
+ fileattr = FILE_ATTRIBUTE_DIRECTORY;
+ else
+ {
+ dev.devn = FH_BAD;
+ fileattr = GetFileAttributes (this->path);
+ }
+ goto out;
+ }
+ else if (isvirtual_dev (dev.devn))
+ {
+ /* FIXME: Calling build_fhandler here is not the right way to handle this. */
+ fhandler_virtual *fh =
+ (fhandler_virtual *) cygheap->fdtab.build_fhandler (-1, dev, (const char *) path_copy, NULL);
+ int file_type = fh->exists ();
+ switch (file_type)
+ {
+ case 1:
+ case 2:
+ fileattr = FILE_ATTRIBUTE_DIRECTORY;
+ break;
+ case -1:
+ fileattr = 0;
+ break;
+ default:
+ fileattr = INVALID_FILE_ATTRIBUTES;
+ break;
+ }
+ delete fh;
+ goto out;
+ }
+ /* devn should not be a device. If it is, then stop parsing now. */
+ else if (dev.devn != FH_BAD)
+ {
+ fileattr = 0;
+ path_flags = sym.pflags;
+ if (component)
+ {
+ error = ENOTDIR;
+ return;
+ }
+ goto out; /* Found a device. Stop parsing. */
+ }
+
+ if (!fs.update (full_path))
+ fs.root_dir[0] = '\0';
+
+ /* Eat trailing slashes */
+ char *dostail = strchr (full_path, '\0');
+
+ /* If path is only a drivename, Windows interprets it as the
+ current working directory on this drive instead of the root
+ dir which is what we want. So we need the trailing backslash
+ in this case. */
+ while (dostail > full_path + 3 && (*--dostail == '\\'))
+ *tail = '\0';
+
+ if (full_path[0] && full_path[1] == ':' && full_path[2] == '\0')
+ {
+ full_path[2] = '\\';
+ full_path[3] = '\0';
+ }
+
+ if ((opt & PC_SYM_IGNORE) && pcheck_case == PCHECK_RELAXED)
+ {
+ fileattr = GetFileAttributes (this->path);
+ goto out;
+ }
+
+ int len = sym.check (full_path, suff, opt | fs.sym_opt);
+
+ if (sym.minor || sym.major)
+ {
+ dev.parse (sym.major, sym.minor);
+ if (!dev)
+ error = ENODEV;
+ else
+ {
+ dev.setfs (1);
+ dev.mode = sym.mode;
+ fileattr = sym.fileattr;
+ }
+ goto out;
+ }
+
+ if (sym.case_clash)
+ {
+ if (pcheck_case == PCHECK_STRICT)
+ {
+ case_clash = TRUE;
+ error = ENOENT;
+ goto out;
+ }
+ /* If pcheck_case==PCHECK_ADJUST the case_clash is remembered
+ if the last component is concerned. This allows functions
+ which shall create files to avoid overriding already existing
+ files with another case. */
+ if (!component)
+ case_clash = TRUE;
+ }
+ if (!(opt & PC_SYM_IGNORE))
+ {
+ if (!component)
+ {
+ fileattr = sym.fileattr;
+ path_flags = sym.pflags;
+ }
+
+ /* If symlink.check found an existing non-symlink file, then
+ it sets the appropriate flag. It also sets any suffix found
+ into `ext_here'. */
+ if (!sym.is_symlink && sym.fileattr != INVALID_FILE_ATTRIBUTES)
+ {
+ error = sym.error;
+ if (component == 0)
+ add_ext_from_sym (sym);
+ if (pcheck_case == PCHECK_RELAXED)
+ goto out; // file found
+ /* Avoid further symlink evaluation. Only case checks are
+ done now. */
+ opt |= PC_SYM_IGNORE;
+ }
+ /* Found a symlink if len > 0. If component == 0, then the
+ src path itself was a symlink. If !follow_mode then
+ we're done. Otherwise we have to insert the path found
+ into the full path that we are building and perform all of
+ these operations again on the newly derived path. */
+ else if (len > 0)
+ {
+ saw_symlinks = 1;
+ if (component == 0 && !need_directory && !(opt & PC_SYM_FOLLOW))
+ {
+ set_symlink (); // last component of path is a symlink.
+ if (opt & PC_SYM_CONTENTS)
+ {
+ strcpy (path, sym.contents);
+ goto out;
+ }
+ add_ext_from_sym (sym);
+ if (pcheck_case == PCHECK_RELAXED)
+ goto out;
+ /* Avoid further symlink evaluation. Only case checks are
+ done now. */
+ opt |= PC_SYM_IGNORE;
+ }
+ else
+ break;
+ }
+ /* No existing file found. */
+ }
+
+ /* Find the "tail" of the path, e.g. in '/for/bar/baz',
+ /baz is the tail. */
+ char *newtail = strrchr (path_copy, '/');
+ if (tail != path_end)
+ *tail = '/';
+
+ /* Exit loop if there is no tail or we are at the
+ beginning of a UNC path */
+ if (!newtail || newtail == path_copy || (newtail == path_copy + 1 && newtail[-1] == '/'))
+ goto out; // all done
+
+ tail = newtail;
+
+ /* Haven't found an existing pathname component yet.
+ Pinch off the tail and try again. */
+ *tail = '\0';
+ component++;
+ }
+
+ /* Arrive here if above loop detected a symlink. */
+ if (++loop > MAX_LINK_DEPTH)
+ {
+ error = ELOOP; // Eep.
+ return;
+ }
+
+ MALLOC_CHECK;
+
+ /* The tail is pointing at a null pointer. Increment it and get the length.
+ If the tail was empty then this increment will end up pointing to the extra
+ \0 added to path_copy above. */
+ int taillen = strlen (++tail);
+ int buflen = strlen (sym.contents);
+ if (buflen + taillen > MAX_PATH)
+ {
+ error = ENAMETOOLONG;
+ strcpy (path, "::ENAMETOOLONG::");
+ return;
+ }
+
+ /* Strip off current directory component since this is the part that refers
+ to the symbolic link. */
+ if ((p = strrchr (path_copy, '/')) == NULL)
+ p = path_copy;
+ else if (p == path_copy)
+ p++;
+ *p = '\0';
+
+ char *headptr;
+ if (isabspath (sym.contents))
+ headptr = tmp_buf; /* absolute path */
+ else
+ {
+ /* Copy the first part of the path and point to the end. */
+ strcpy (tmp_buf, path_copy);
+ headptr = strchr (tmp_buf, '\0');
+ }
+
+ /* See if we need to separate first part + symlink contents with a / */
+ if (headptr > tmp_buf && headptr[-1] != '/')
+ *headptr++ = '/';
+
+ /* Copy the symlink contents to the end of tmp_buf.
+ Convert slashes. FIXME? */
+ for (p = sym.contents; *p; p++)
+ *headptr++ = *p == '\\' ? '/' : *p;
+
+ /* Copy any tail component */
+ if (tail >= path_end)
+ *headptr = '\0';
+ else
+ {
+ *headptr++ = '/';
+ strcpy (headptr, tail);
+ }
+
+ /* Now evaluate everything all over again. */
+ src = tmp_buf;
+ }
+
+ if (!(opt & PC_SYM_CONTENTS))
+ add_ext_from_sym (sym);
+
+out:
+ if (opt & PC_POSIX)
+ {
+ if (tail[1] != '\0')
+ *tail = '/';
+ normalized_path = cstrdup (path_copy);
+ }
+ /* Deal with Windows stupidity which considers filename\. to be valid
+ even when "filename" is not a directory. */
+ if (!need_directory || error)
+ /* nothing to do */;
+ else if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
+ path_flags &= ~PATH_SYMLINK;
+ else
+ {
+ debug_printf ("%s is a non-directory", path);
+ error = ENOTDIR;
+ return;
+ }
+
+ if (dev.devn == FH_BAD)
+ {
+ if (!fs.update (path))
+ {
+ fs.root_dir[0] = '\0';
+ set_has_acls (false);
+ set_has_buggy_open (false);
+ }
+ else
+ {
+ set_isdisk ();
+ debug_printf ("root_dir(%s), this->path(%s), set_has_acls(%d)",
+ fs.root_dir, this->path, fs.flags & FS_PERSISTENT_ACLS);
+ if (!allow_smbntsec && fs.is_remote_drive)
+ set_has_acls (false);
+ else
+ set_has_acls (fs.flags & FS_PERSISTENT_ACLS);
+ /* Known file systems with buggy open calls. Further explanation
+ in fhandler.cc (fhandler_disk_file::open). */
+ set_has_buggy_open (strcmp (fs.name, "SUNWNFS") == 0);
+ }
+ }
+#if 0
+ if (issocket ())
+ devn = FH_SOCKET;
+#endif
+
+ if (!(opt & PC_FULL))
+ {
+ if (is_relpath)
+ mkrelpath (this->path);
+ if (need_directory)
+ {
+ size_t n = strlen (this->path);
+ /* Do not add trailing \ to UNC device names like \\.\a: */
+ if (this->path[n - 1] != '\\' &&
+ (strncmp (this->path, "\\\\.\\", 4) != 0 ||
+ !strncasematch (this->path + 4, "unc\\", 4)))
+ {
+ this->path[n] = '\\';
+ this->path[n + 1] = '\0';
+ }
+ }
+ }
+
+ if (saw_symlinks)
+ set_has_symlinks ();
+
+ if (!error && !isdir () && !(path_flags & PATH_ALL_EXEC))
+ {
+ const char *p = strchr (path, '\0') - 4;
+ if (p >= path &&
+ (strcasematch (".exe", p) ||
+ strcasematch (".bat", p) ||
+ strcasematch (".com", p)))
+ path_flags |= PATH_EXEC;
+ }
+
+#if 0
+ if (!error)
+ {
+ last_path_conv = *this;
+ strcpy (last_src, src);
+ }
+#endif
+}
+
+static __inline int
+digits (const char *name)
+{
+ char *p;
+ int n = strtol (name, &p, 10);
+
+ return p > name && !*p ? n : -1;
+}
+
+
+
+/* Return TRUE if src_path is a Win32 device name, filling out the device
+ name in win32_path */
+
+static BOOL
+win32_device_name (const char *src_path, char *win32_path, device& dev)
+{
+ dev.parse (src_path);
+
+ if (dev.devn == FH_BAD)
+ return false;
+
+ switch (dev.devn)
+ {
+ case FH_TAPE:
+ __small_sprintf (win32_path, dev.fmt, dev.minor % 128);
+ break;
+ case FH_RAWDRIVE:
+ __small_sprintf (win32_path, dev.fmt, dev.minor - 224 + 'A');
+ break;
+ default:
+ __small_sprintf (win32_path, dev.fmt, dev.minor);
+ break;
+ }
+ return true;
+}
+
+/* Normalize a Win32 path.
+ /'s are converted to \'s in the process.
+ All duplicate \'s, except for 2 leading \'s, are deleted.
+
+ The result is 0 for success, or an errno error value.
+ FIXME: A lot of this should be mergeable with the POSIX critter. */
+static int
+normalize_win32_path (const char *src, char *dst)
+{
+ const char *src_start = src;
+ char *dst_start = dst;
+ char *dst_root_start = dst;
+ bool beg_src_slash = isdirsep (src[0]);
+
+ if (beg_src_slash && isdirsep (src[1]))
+ {
+ *dst++ = '\\';
+ src++;
+ if (src[1] == '.' && isdirsep (src[2]))
+ {
+ *dst++ = '\\';
+ *dst++ = '.';
+ src += 2;
+ }
+ }
+ else if (strchr (src, ':') == NULL && *src != '/')
+ {
+ if (!cygheap->cwd.get (dst, 0))
+ return get_errno ();
+ if (beg_src_slash)
+ {
+ if (dst[1] == ':')
+ dst[2] = '\0';
+ else if (slash_unc_prefix_p (dst))
+ {
+ char *p = strpbrk (dst + 2, "\\/");
+ if (p && (p = strpbrk (p + 1, "\\/")))
+ *p = '\0';
+ }
+ }
+ if (strlen (dst) + 1 + strlen (src) >= MAX_PATH)
+ {
+ debug_printf ("ENAMETOOLONG = normalize_win32_path (%s)", src);
+ return ENAMETOOLONG;
+ }
+ dst += strlen (dst);
+ if (!beg_src_slash)
+ *dst++ = '\\';
+ }
+
+ while (*src)
+ {
+ /* Strip duplicate /'s. */
+ if (isdirsep (src[0]) && isdirsep (src[1]))
+ src++;
+ /* Ignore "./". */
+ else if (src[0] == '.' && isdirsep (src[1])
+ && (src == src_start || isdirsep (src[-1])))
+ src += 2;
+
+ /* Backup if "..". */
+ else if (src[0] == '.' && src[1] == '.'
+ /* dst must be greater than dst_start */
+ && dst[-1] == '\\'
+ && (isdirsep (src[2]) || src[2] == 0))
+ {
+ /* Back up over /, but not if it's the first one. */
+ if (dst > dst_root_start + 1)
+ dst--;
+ /* Now back up to the next /. */
+ while (dst > dst_root_start + 1 && dst[-1] != '\\' && dst[-2] != ':')
+ dst--;
+ src += 2;
+ if (isdirsep (*src))
+ src++;
+ }
+ /* Otherwise, add char to result. */
+ else
+ {
+ if (*src == '/')
+ *dst++ = '\\';
+ else
+ *dst++ = *src;
+ ++src;
+ }
+ if ((dst - dst_start) >= MAX_PATH)
+ return ENAMETOOLONG;
+ }
+ *dst = 0;
+ debug_printf ("%s = normalize_win32_path (%s)", dst_start, src_start);
+ return 0;
+}
+
+/* Various utilities. */
+
+/* slashify: Convert all back slashes in src path to forward slashes
+ in dst path. Add a trailing slash to dst when trailing_slash_p arg
+ is set to 1. */
+
+static void
+slashify (const char *src, char *dst, int trailing_slash_p)
+{
+ const char *start = src;
+
+ while (*src)
+ {
+ if (*src == '\\')
+ *dst++ = '/';
+ else
+ *dst++ = *src;
+ ++src;
+ }
+ if (trailing_slash_p
+ && src > start
+ && !isdirsep (src[-1]))
+ *dst++ = '/';
+ *dst++ = 0;
+}
+
+/* backslashify: Convert all forward slashes in src path to back slashes
+ in dst path. Add a trailing slash to dst when trailing_slash_p arg
+ is set to 1. */
+
+static void
+backslashify (const char *src, char *dst, int trailing_slash_p)
+{
+ const char *start = src;
+
+ while (*src)
+ {
+ if (*src == '/')
+ *dst++ = '\\';
+ else
+ *dst++ = *src;
+ ++src;
+ }
+ if (trailing_slash_p
+ && src > start
+ && !isdirsep (src[-1]))
+ *dst++ = '\\';
+ *dst++ = 0;
+}
+
+/* nofinalslash: Remove trailing / and \ from SRC (except for the
+ first one). It is ok for src == dst. */
+
+void __stdcall
+nofinalslash (const char *src, char *dst)
+{
+ int len = strlen (src);
+ if (src != dst)
+ memcpy (dst, src, len + 1);
+ while (len > 1 && isdirsep (dst[--len]))
+ dst[len] = '\0';
+}
+
+/* slash_unc_prefix_p: Return non-zero if PATH begins with //UNC/SHARE */
+
+int __stdcall
+slash_unc_prefix_p (const char *path)
+{
+ char *p = NULL;
+ int ret = (isdirsep (path[0])
+ && isdirsep (path[1])
+ && isalpha (path[2])
+ && path[3] != 0
+ && !isdirsep (path[3])
+ && ((p = strpbrk (path + 3, "\\/")) != NULL));
+ if (!ret || p == NULL)
+ return ret;
+ return ret && isalnum (p[1]);
+}
+
+/* conv_path_list: Convert a list of path names to/from Win32/POSIX. */
+
+static void
+conv_path_list (const char *src, char *dst, int to_posix_p)
+{
+ char *s;
+ char *d = dst;
+ char src_delim = to_posix_p ? ';' : ':';
+ char dst_delim = to_posix_p ? ':' : ';';
+ int (*conv_fn) (const char *, char *) = (to_posix_p
+ ? cygwin_conv_to_posix_path
+ : cygwin_conv_to_win32_path);
+
+ char *srcbuf = (char *) alloca (strlen (src) + 1);
+
+ for (;;)
+ {
+ s = strccpy (srcbuf, &src, src_delim);
+ int len = s - srcbuf;
+ if (len >= MAX_PATH)
+ srcbuf[MAX_PATH - 1] = '\0';
+ (*conv_fn) (len ? srcbuf : ".", d);
+ if (!*src++)
+ break;
+ d = strchr (d, '\0');
+ *d++ = dst_delim;
+ }
+}
+
+/* init: Initialize the mount table. */
+
+void
+mount_info::init ()
+{
+ nmounts = 0;
+
+ /* Fetch the mount table and cygdrive-related information from
+ the registry. */
+ from_registry ();
+}
+
+static void
+set_flags (unsigned *flags, unsigned val)
+{
+ *flags = val;
+ if (!(*flags & PATH_BINARY))
+ {
+ *flags |= PATH_TEXT;
+ debug_printf ("flags: text (%p)", *flags & (PATH_TEXT | PATH_BINARY));
+ }
+ else
+ {
+ *flags |= PATH_BINARY;
+ debug_printf ("flags: binary (%p)", *flags & (PATH_TEXT | PATH_BINARY));
+ }
+}
+
+/* CGF FIXME device */
+static const device dev_proc =
+{"/proc", FH_PROC, "/proc", 0, 0, 0, 0};
+
+static const device dev_cygdrive =
+{"/cygdrive", FH_CYGDRIVE, "/cygdrive", 0, 0, 0, 0};
+
+static const device dev_fs =
+{"", FH_FS, "", 0, 0, 0, 0};
+
+/* conv_to_win32_path: Ensure src_path is a pure Win32 path and store
+ the result in win32_path.
+
+ If win32_path != NULL, the relative path, if possible to keep, is
+ stored in win32_path. If the relative path isn't possible to keep,
+ the full path is stored.
+
+ If full_win32_path != NULL, the full path is stored there.
+
+ The result is zero for success, or an errno value.
+
+ {,full_}win32_path must have sufficient space (i.e. MAX_PATH bytes). */
+
+int
+mount_info::conv_to_win32_path (const char *src_path, char *dst, device& dev,
+ unsigned *flags, bool no_normalize)
+{
+ while (sys_mount_table_counter < cygwin_shared->sys_mount_table_counter)
+ {
+ init ();
+ sys_mount_table_counter++;
+ }
+ int src_path_len = strlen (src_path);
+ MALLOC_CHECK;
+ unsigned dummy_flags;
+ int chroot_ok = !cygheap->root.exists ();
+
+ dev.devn = FH_BAD;
+
+ if (!flags)
+ flags = &dummy_flags;
+
+ *flags = 0;
+ debug_printf ("conv_to_win32_path (%s)", src_path);
+
+ if (src_path_len >= MAX_PATH)
+ {
+ debug_printf ("ENAMETOOLONG = conv_to_win32_path (%s)", src_path);
+ return ENAMETOOLONG;
+ }
+
+ int i, rc;
+ mount_item *mi = NULL; /* initialized to avoid compiler warning */
+ char pathbuf[MAX_PATH];
+
+ if (dst == NULL)
+ goto out; /* Sanity check. */
+
+ /* An MS-DOS spec has either a : or a \. If this is found, short
+ circuit most of the rest of this function. */
+ if (strpbrk (src_path, ":\\") != NULL || slash_unc_prefix_p (src_path))
+ {
+ debug_printf ("%s already win32", src_path);
+ rc = normalize_win32_path (src_path, dst);
+ if (rc)
+ {
+ debug_printf ("normalize_win32_path failed, rc %d", rc);
+ return rc;
+ }
+
+ set_flags (flags, (unsigned) set_flags_from_win32_path (dst));
+ goto out;
+ }
+
+ /* Normalize the path, taking out ../../ stuff, we need to do this
+ so that we can move from one mounted directory to another with relative
+ stuff.
+
+ eg mounting c:/foo /foo
+ d:/bar /bar
+
+ cd /bar
+ ls ../foo
+
+ should look in c:/foo, not d:/foo.
+
+ We do this by first getting an absolute UNIX-style path and then
+ converting it to a DOS-style path, looking up the appropriate drive
+ in the mount table. */
+
+ if (no_normalize)
+ strcpy (pathbuf, src_path);
+ else
+ {
+ rc = normalize_posix_path (src_path, pathbuf);
+
+ if (rc)
+ {
+ debug_printf ("%d = conv_to_win32_path (%s)", rc, src_path);
+ return rc;
+ }
+ }
+
+ /* See if this is a cygwin "device" */
+ if (win32_device_name (pathbuf, dst, dev))
+ {
+ *flags = MOUNT_BINARY; /* FIXME: Is this a sensible default for devices? */
+ rc = 0;
+ goto out_no_chroot_check;
+ }
+
+ /* Check if the cygdrive prefix was specified. If so, just strip
+ off the prefix and transform it into an MS-DOS path. */
+ MALLOC_CHECK;
+ if (isproc (pathbuf))
+ {
+ dev = dev_proc;
+ dev.devn = fhandler_proc::get_proc_fhandler (pathbuf);
+ if (dev.devn == FH_BAD)
+ return ENOENT;
+ }
+ else if (iscygdrive (pathbuf))
+ {
+ int n = mount_table->cygdrive_len - 1;
+ int unit;
+
+ if (!pathbuf[n] ||
+ (pathbuf[n] == '/' && pathbuf[n + 1] == '.' && !pathbuf[n + 2]))
+ {
+ unit = 0;
+ dst[0] = '\0';
+ if (mount_table->cygdrive_len > 1)
+ dev = dev_cygdrive;
+ }
+ else if (cygdrive_win32_path (pathbuf, dst, unit))
+ {
+ set_flags (flags, (unsigned) cygdrive_flags);
+ goto out;
+ }
+ else if (mount_table->cygdrive_len > 1)
+ return ENOENT;
+ }
+
+ int chrooted_path_len;
+ chrooted_path_len = 0;
+ /* Check the mount table for prefix matches. */
+ for (i = 0; i < nmounts; i++)
+ {
+ const char *path;
+ int len;
+
+ mi = mount + posix_sorted[i];
+ if (!cygheap->root.exists ()
+ || (mi->posix_pathlen == 1 && mi->posix_path[0] == '/'))
+ {
+ path = mi->posix_path;
+ len = mi->posix_pathlen;
+ }
+ else if (cygheap->root.posix_ok (mi->posix_path))
+ {
+ path = cygheap->root.unchroot (mi->posix_path);
+ chrooted_path_len = len = strlen (path);
+ }
+ else
+ {
+ chrooted_path_len = 0;
+ continue;
+ }
+
+ if (path_prefix_p (path, pathbuf, len))
+ break;
+ }
+
+ if (i >= nmounts)
+ {
+ backslashify (pathbuf, dst, 0); /* just convert */
+ set_flags (flags, PATH_BINARY);
+ }
+ else
+ {
+ int n;
+ const char *native_path;
+ int posix_pathlen;
+ if (chroot_ok || chrooted_path_len || mi->posix_pathlen != 1
+ || mi->posix_path[0] != '/')
+ {
+ n = mi->native_pathlen;
+ native_path = mi->native_path;
+ posix_pathlen = chrooted_path_len ?: mi->posix_pathlen;
+ chroot_ok = 1;
+ }
+ else
+ {
+ n = cygheap->root.native_length ();
+ native_path = cygheap->root.native_path ();
+ posix_pathlen = mi->posix_pathlen;
+ chroot_ok = 1;
+ }
+ memcpy (dst, native_path, n + 1);
+ const char *p = pathbuf + posix_pathlen;
+ if (*p == '/')
+ /* nothing */;
+ else if ((isdrive (dst) && !dst[2]) || *p)
+ dst[n++] = '\\';
+ strcpy (dst + n, p);
+ backslashify (dst, dst, 0);
+ set_flags (flags, (unsigned) mi->flags);
+ }
+
+ if (!isvirtual_dev (dev.devn))
+ win32_device_name (src_path, dst, dev);
+
+ out:
+ MALLOC_CHECK;
+ if (chroot_ok || cygheap->root.ischroot_native (dst))
+ rc = 0;
+ else
+ {
+ debug_printf ("attempt to access outside of chroot '%s = %s'",
+ cygheap->root.posix_path (), cygheap->root.native_path ());
+ rc = ENOENT;
+ }
+
+ out_no_chroot_check:
+ debug_printf ("src_path %s, dst %s, flags %p, rc %d", src_path, dst, *flags, rc);
+ return rc;
+}
+
+/* cygdrive_posix_path: Build POSIX path used as the
+ mount point for cygdrives created when there is no other way to
+ obtain a POSIX path from a Win32 one. */
+
+void
+mount_info::cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p)
+{
+ int len = cygdrive_len;
+
+ memcpy (dst, cygdrive, len + 1);
+
+ /* Now finish the path off with the drive letter to be used.
+ The cygdrive prefix always ends with a trailing slash so
+ the drive letter is added after the path. */
+ dst[len++] = cyg_tolower (src[0]);
+ if (!src[2] || (isdirsep (src[2]) && !src[3]))
+ dst[len++] = '\000';
+ else
+ {
+ int n;
+ dst[len++] = '/';
+ if (isdirsep (src[2]))
+ n = 3;
+ else
+ n = 2;
+ strcpy (dst + len, src + n);
+ }
+ slashify (dst, dst, trailing_slash_p);
+}
+
+int
+mount_info::cygdrive_win32_path (const char *src, char *dst, int& unit)
+{
+ int res;
+ const char *p = src + cygdrive_len;
+ if (!isalpha (*p) || (!isdirsep (p[1]) && p[1]))
+ {
+ unit = -1; /* FIXME: should be zero, maybe? */
+ dst[0] = '\0';
+ res = 0;
+ }
+ else
+ {
+ dst[0] = cyg_tolower (*p);
+ dst[1] = ':';
+ strcpy (dst + 2, p + 1);
+ backslashify (dst, dst, !dst[2]);
+ unit = dst[0];
+ res = 1;
+ }
+ debug_printf ("src '%s', dst '%s'", src, dst);
+ return res;
+}
+
+/* conv_to_posix_path: Ensure src_path is a POSIX path.
+
+ The result is zero for success, or an errno value.
+ posix_path must have sufficient space (i.e. MAX_PATH bytes).
+ If keep_rel_p is non-zero, relative paths stay that way. */
+
+int
+mount_info::conv_to_posix_path (const char *src_path, char *posix_path,
+ int keep_rel_p)
+{
+ int src_path_len = strlen (src_path);
+ int relative_path_p = !isabspath (src_path);
+ int trailing_slash_p;
+
+ if (src_path_len <= 1)
+ trailing_slash_p = 0;
+ else
+ {
+ const char *lastchar = src_path + src_path_len - 1;
+ trailing_slash_p = isdirsep (*lastchar) && lastchar[-1] != ':';
+ }
+
+ debug_printf ("conv_to_posix_path (%s, %s, %s)", src_path,
+ keep_rel_p ? "keep-rel" : "no-keep-rel",
+ trailing_slash_p ? "add-slash" : "no-add-slash");
+ MALLOC_CHECK;
+
+ if (src_path_len >= MAX_PATH)
+ {
+ debug_printf ("ENAMETOOLONG");
+ return ENAMETOOLONG;
+ }
+
+ /* FIXME: For now, if the path is relative and it's supposed to stay
+ that way, skip mount table processing. */
+
+ if (keep_rel_p && relative_path_p)
+ {
+ slashify (src_path, posix_path, 0);
+ debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
+ return 0;
+ }
+
+ char pathbuf[MAX_PATH];
+ int rc = normalize_win32_path (src_path, pathbuf);
+ if (rc != 0)
+ {
+ debug_printf ("%d = conv_to_posix_path (%s)", rc, src_path);
+ return rc;
+ }
+
+ int pathbuflen = strlen (pathbuf);
+ for (int i = 0; i < nmounts; ++i)
+ {
+ mount_item &mi = mount[native_sorted[i]];
+ if (!path_prefix_p (mi.native_path, pathbuf, mi.native_pathlen))
+ continue;
+
+ if (cygheap->root.exists () && !cygheap->root.posix_ok (mi.posix_path))
+ continue;
+
+ /* SRC_PATH is in the mount table. */
+ int nextchar;
+ const char *p = pathbuf + mi.native_pathlen;
+
+ if (!*p || !p[1])
+ nextchar = 0;
+ else if (*p == '/')
+ nextchar = -1;
+ else
+ nextchar = 1;
+
+ int addslash = nextchar > 0 ? 1 : 0;
+ if ((mi.posix_pathlen + (pathbuflen - mi.native_pathlen) + addslash) >= MAX_PATH)
+ return ENAMETOOLONG;
+ strcpy (posix_path, mi.posix_path);
+ if (addslash)
+ strcat (posix_path, "/");
+ if (nextchar)
+ slashify (p,
+ posix_path + addslash + (mi.posix_pathlen == 1 ? 0 : mi.posix_pathlen),
+ trailing_slash_p);
+
+ if (cygheap->root.exists ())
+ {
+ const char *p = cygheap->root.unchroot (posix_path);
+ memmove (posix_path, p, strlen (p) + 1);
+ }
+ goto out;
+ }
+
+ if (!cygheap->root.exists ())
+ /* nothing */;
+ else if (cygheap->root.ischroot_native (pathbuf))
+ {
+ const char *p = pathbuf + cygheap->root.native_length ();
+ if (*p)
+ slashify (p, posix_path, trailing_slash_p);
+ else
+ {
+ posix_path[0] = '/';
+ posix_path[1] = '\0';
+ }
+ }
+ else
+ return ENOENT;
+
+ /* Not in the database. This should [theoretically] only happen if either
+ the path begins with //, or / isn't mounted, or the path has a drive
+ letter not covered by the mount table. If it's a relative path then the
+ caller must want an absolute path (otherwise we would have returned
+ above). So we always return an absolute path at this point. */
+ if (isdrive (pathbuf))
+ cygdrive_posix_path (pathbuf, posix_path, trailing_slash_p);
+ else
+ {
+ /* The use of src_path and not pathbuf here is intentional.
+ We couldn't translate the path, so just ensure no \'s are present. */
+ slashify (src_path, posix_path, trailing_slash_p);
+ }
+
+out:
+ debug_printf ("%s = conv_to_posix_path (%s)", posix_path, src_path);
+ MALLOC_CHECK;
+ return 0;
+}
+
+/* Return flags associated with a mount point given the win32 path. */
+
+unsigned
+mount_info::set_flags_from_win32_path (const char *p)
+{
+ for (int i = 0; i < nmounts; i++)
+ {
+ mount_item &mi = mount[native_sorted[i]];
+ if (path_prefix_p (mi.native_path, p, mi.native_pathlen))
+ return mi.flags;
+ }
+ return PATH_BINARY;
+}
+
+/* read_mounts: Given a specific regkey, read mounts from under its
+ key. */
+
+void
+mount_info::read_mounts (reg_key& r)
+{
+ char posix_path[MAX_PATH];
+ HKEY key = r.get_key ();
+ DWORD i, posix_path_size;
+ int res;
+
+ /* Loop through subkeys */
+ /* FIXME: we would like to not check MAX_MOUNTS but the heap in the
+ shared area is currently statically allocated so we can't have an
+ arbitrarily large number of mounts. */
+ for (i = 0; ; i++)
+ {
+ char native_path[MAX_PATH];
+ int mount_flags;
+
+ posix_path_size = MAX_PATH;
+ /* FIXME: if maximum posix_path_size is 256, we're going to
+ run into problems if we ever try to store a mount point that's
+ over 256 but is under MAX_PATH. */
+ res = RegEnumKeyEx (key, i, posix_path, &posix_path_size, NULL,
+ NULL, NULL, NULL);
+
+ if (res == ERROR_NO_MORE_ITEMS)
+ break;
+ else if (res != ERROR_SUCCESS)
+ {
+ debug_printf ("RegEnumKeyEx failed, error %d!", res);
+ break;
+ }
+
+ /* Get a reg_key based on i. */
+ reg_key subkey = reg_key (key, KEY_READ, posix_path, NULL);
+
+ /* Fetch info from the subkey. */
+ subkey.get_string ("native", native_path, sizeof (native_path), "");
+ mount_flags = subkey.get_int ("flags", 0);
+
+ /* Add mount_item corresponding to registry mount point. */
+ res = mount_table->add_item (native_path, posix_path, mount_flags, false);
+ if (res && get_errno () == EMFILE)
+ break; /* The number of entries exceeds MAX_MOUNTS */
+ }
+}
+
+/* from_registry: Build the entire mount table from the registry. Also,
+ read in cygdrive-related information from its registry location. */
+
+void
+mount_info::from_registry ()
+{
+ /* Use current mount areas if either user or system mount areas
+ already exist. Otherwise, import old mounts. */
+
+ reg_key r;
+
+ /* Retrieve cygdrive-related information. */
+ read_cygdrive_info_from_registry ();
+
+ nmounts = 0;
+
+ /* First read mounts from user's table. */
+ read_mounts (r);
+
+ /* Then read mounts from system-wide mount table. */
+ reg_key r1 (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+ read_mounts (r1);
+}
+
+/* add_reg_mount: Add mount item to registry. Return zero on success,
+ non-zero on failure. */
+/* FIXME: Need a mutex to avoid collisions with other tasks. */
+
+int
+mount_info::add_reg_mount (const char * native_path, const char * posix_path, unsigned mountflags)
+{
+ int res = 0;
+
+ /* Add the mount to the right registry location, depending on
+ whether MOUNT_SYSTEM is set in the mount flags. */
+ if (!(mountflags & MOUNT_SYSTEM)) /* current_user mount */
+ {
+ /* reg_key for user mounts in HKEY_CURRENT_USER. */
+ reg_key reg_user;
+
+ /* Start by deleting existing mount if one exists. */
+ res = reg_user.kill (posix_path);
+ if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND)
+ goto err;
+
+ /* Create the new mount. */
+ reg_key subkey = reg_key (reg_user.get_key (),
+ KEY_ALL_ACCESS,
+ posix_path, NULL);
+ res = subkey.set_string ("native", native_path);
+ if (res != ERROR_SUCCESS)
+ goto err;
+ res = subkey.set_int ("flags", mountflags);
+ }
+ else /* local_machine mount */
+ {
+ /* reg_key for system mounts in HKEY_LOCAL_MACHINE. */
+ reg_key reg_sys (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+
+ /* Start by deleting existing mount if one exists. */
+ res = reg_sys.kill (posix_path);
+ if (res != ERROR_SUCCESS && res != ERROR_FILE_NOT_FOUND)
+ goto err;
+
+ /* Create the new mount. */
+ reg_key subkey = reg_key (reg_sys.get_key (),
+ KEY_ALL_ACCESS,
+ posix_path, NULL);
+ res = subkey.set_string ("native", native_path);
+ if (res != ERROR_SUCCESS)
+ goto err;
+ res = subkey.set_int ("flags", mountflags);
+
+ sys_mount_table_counter++;
+ cygwin_shared->sys_mount_table_counter++;
+ }
+
+ return 0; /* Success */
+ err:
+ __seterrno_from_win_error (res);
+ return -1;
+}
+
+/* del_reg_mount: delete mount item from registry indicated in flags.
+ Return zero on success, non-zero on failure.*/
+/* FIXME: Need a mutex to avoid collisions with other tasks. */
+
+int
+mount_info::del_reg_mount (const char * posix_path, unsigned flags)
+{
+ int res;
+
+ if (!(flags & MOUNT_SYSTEM)) /* Delete from user registry */
+ {
+ reg_key reg_user (KEY_ALL_ACCESS,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME, NULL);
+ res = reg_user.kill (posix_path);
+ }
+ else /* Delete from system registry */
+ {
+ sys_mount_table_counter++;
+ cygwin_shared->sys_mount_table_counter++;
+ reg_key reg_sys (HKEY_LOCAL_MACHINE, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+ res = reg_sys.kill (posix_path);
+ }
+
+ if (res != ERROR_SUCCESS)
+ {
+ __seterrno_from_win_error (res);
+ return -1;
+ }
+
+ return 0; /* Success */
+}
+
+/* read_cygdrive_info_from_registry: Read the default prefix and flags
+ to use when creating cygdrives from the special user registry
+ location used to store cygdrive information. */
+
+void
+mount_info::read_cygdrive_info_from_registry ()
+{
+ /* reg_key for user path prefix in HKEY_CURRENT_USER. */
+ reg_key r;
+
+ if (r.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, cygdrive, sizeof (cygdrive), "") != 0)
+ {
+ /* Didn't find the user path prefix so check the system path prefix. */
+
+ /* reg_key for system path prefix in HKEY_LOCAL_MACHINE. */
+ reg_key r2 (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+
+ if (r2.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, cygdrive,
+ sizeof (cygdrive), ""))
+ strcpy (cygdrive, CYGWIN_INFO_CYGDRIVE_DEFAULT_PREFIX);
+ cygdrive_flags = r2.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE);
+ slashify (cygdrive, cygdrive, 1);
+ cygdrive_len = strlen (cygdrive);
+ }
+ else
+ {
+ /* Fetch user cygdrive_flags from registry; returns MOUNT_CYGDRIVE on
+ error. */
+ cygdrive_flags = r.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE);
+ slashify (cygdrive, cygdrive, 1);
+ cygdrive_len = strlen (cygdrive);
+ }
+}
+
+/* write_cygdrive_info_to_registry: Write the default prefix and flags
+ to use when creating cygdrives to the special user registry
+ location used to store cygdrive information. */
+
+int
+mount_info::write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags)
+{
+ /* Determine whether to modify user or system cygdrive path prefix. */
+ HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+
+ if (flags & MOUNT_SYSTEM)
+ {
+ sys_mount_table_counter++;
+ cygwin_shared->sys_mount_table_counter++;
+ }
+
+ /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in
+ HKEY_LOCAL_MACHINE. */
+ reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+
+ /* Verify cygdrive prefix starts with a forward slash and if there's
+ another character, it's not a slash. */
+ if ((cygdrive_prefix == NULL) || (*cygdrive_prefix == 0) ||
+ (!isslash (cygdrive_prefix[0])) ||
+ ((cygdrive_prefix[1] != '\0') && (isslash (cygdrive_prefix[1]))))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ char hold_cygdrive_prefix[strlen (cygdrive_prefix) + 1];
+ /* Ensure that there is never a final slash */
+ nofinalslash (cygdrive_prefix, hold_cygdrive_prefix);
+
+ int res;
+ res = r.set_string (CYGWIN_INFO_CYGDRIVE_PREFIX, hold_cygdrive_prefix);
+ if (res != ERROR_SUCCESS)
+ {
+ __seterrno_from_win_error (res);
+ return -1;
+ }
+ r.set_int (CYGWIN_INFO_CYGDRIVE_FLAGS, flags);
+
+ /* This also needs to go in the in-memory copy of "cygdrive", but only if
+ appropriate:
+ 1. setting user path prefix, or
+ 2. overwriting (a previous) system path prefix */
+ if (!(flags & MOUNT_SYSTEM) || (mount_table->cygdrive_flags & MOUNT_SYSTEM))
+ {
+ slashify (cygdrive_prefix, mount_table->cygdrive, 1);
+ mount_table->cygdrive_flags = flags;
+ mount_table->cygdrive_len = strlen (mount_table->cygdrive);
+ }
+
+ return 0;
+}
+
+int
+mount_info::remove_cygdrive_info_from_registry (const char *cygdrive_prefix, unsigned flags)
+{
+ /* Determine whether to modify user or system cygdrive path prefix. */
+ HKEY top = (flags & MOUNT_SYSTEM) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
+
+ if (flags & MOUNT_SYSTEM)
+ {
+ sys_mount_table_counter++;
+ cygwin_shared->sys_mount_table_counter++;
+ }
+
+ /* reg_key for user path prefix in HKEY_CURRENT_USER or system path prefix in
+ HKEY_LOCAL_MACHINE. */
+ reg_key r (top, KEY_ALL_ACCESS, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+
+ /* Delete cygdrive prefix and flags. */
+ int res = r.killvalue (CYGWIN_INFO_CYGDRIVE_PREFIX);
+ int res2 = r.killvalue (CYGWIN_INFO_CYGDRIVE_FLAGS);
+
+ /* Reinitialize the cygdrive path prefix to reflect to removal from the
+ registry. */
+ read_cygdrive_info_from_registry ();
+
+ return (res != ERROR_SUCCESS) ? res : res2;
+}
+
+int
+mount_info::get_cygdrive_info (char *user, char *system, char* user_flags,
+ char* system_flags)
+{
+ /* Get the user path prefix from HKEY_CURRENT_USER. */
+ reg_key r;
+ int res = r.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, user, MAX_PATH, "");
+
+ /* Get the user flags, if appropriate */
+ if (res == ERROR_SUCCESS)
+ {
+ int flags = r.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE);
+ strcpy (user_flags, (flags & MOUNT_BINARY) ? "binmode" : "textmode");
+ }
+
+ /* Get the system path prefix from HKEY_LOCAL_MACHINE. */
+ reg_key r2 (HKEY_LOCAL_MACHINE, KEY_READ, "SOFTWARE",
+ CYGWIN_INFO_CYGNUS_REGISTRY_NAME, CYGWIN_REGNAME,
+ CYGWIN_INFO_CYGWIN_MOUNT_REGISTRY_NAME,
+ NULL);
+ int res2 = r2.get_string (CYGWIN_INFO_CYGDRIVE_PREFIX, system, MAX_PATH, "");
+
+ /* Get the system flags, if appropriate */
+ if (res2 == ERROR_SUCCESS)
+ {
+ int flags = r2.get_int (CYGWIN_INFO_CYGDRIVE_FLAGS, MOUNT_CYGDRIVE);
+ strcpy (system_flags, (flags & MOUNT_BINARY) ? "binmode" : "textmode");
+ }
+
+ return (res != ERROR_SUCCESS) ? res : res2;
+}
+
+static mount_item *mounts_for_sort;
+
+/* sort_by_posix_name: qsort callback to sort the mount entries. Sort
+ user mounts ahead of system mounts to the same POSIX path. */
+/* FIXME: should the user should be able to choose whether to
+ prefer user or system mounts??? */
+static int
+sort_by_posix_name (const void *a, const void *b)
+{
+ mount_item *ap = mounts_for_sort + (*((int*) a));
+ mount_item *bp = mounts_for_sort + (*((int*) b));
+
+ /* Base weighting on longest posix path first so that the most
+ obvious path will be chosen. */
+ size_t alen = strlen (ap->posix_path);
+ size_t blen = strlen (bp->posix_path);
+
+ int res = blen - alen;
+
+ if (res)
+ return res; /* Path lengths differed */
+
+ /* The two paths were the same length, so just determine normal
+ lexical sorted order. */
+ res = strcmp (ap->posix_path, bp->posix_path);
+
+ if (res == 0)
+ {
+ /* need to select between user and system mount to same POSIX path */
+ if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
+ return 1;
+ else
+ return -1;
+ }
+
+ return res;
+}
+
+/* sort_by_native_name: qsort callback to sort the mount entries. Sort
+ user mounts ahead of system mounts to the same POSIX path. */
+/* FIXME: should the user should be able to choose whether to
+ prefer user or system mounts??? */
+static int
+sort_by_native_name (const void *a, const void *b)
+{
+ mount_item *ap = mounts_for_sort + (*((int*) a));
+ mount_item *bp = mounts_for_sort + (*((int*) b));
+
+ /* Base weighting on longest win32 path first so that the most
+ obvious path will be chosen. */
+ size_t alen = strlen (ap->native_path);
+ size_t blen = strlen (bp->native_path);
+
+ int res = blen - alen;
+
+ if (res)
+ return res; /* Path lengths differed */
+
+ /* The two paths were the same length, so just determine normal
+ lexical sorted order. */
+ res = strcmp (ap->native_path, bp->native_path);
+
+ if (res == 0)
+ {
+ /* need to select between user and system mount to same POSIX path */
+ if (!(bp->flags & MOUNT_SYSTEM)) /* user mount */
+ return 1;
+ else
+ return -1;
+ }
+
+ return res;
+}
+
+void
+mount_info::sort ()
+{
+ for (int i = 0; i < nmounts; i++)
+ native_sorted[i] = posix_sorted[i] = i;
+ /* Sort them into reverse length order, otherwise we won't
+ be able to look for /foo in /. */
+ mounts_for_sort = mount; /* ouch. */
+ qsort (posix_sorted, nmounts, sizeof (posix_sorted[0]), sort_by_posix_name);
+ qsort (native_sorted, nmounts, sizeof (native_sorted[0]), sort_by_native_name);
+}
+
+/* Add an entry to the mount table.
+ Returns 0 on success, -1 on failure and errno is set.
+
+ This is where all argument validation is done. It may not make sense to
+ do this when called internally, but it's cleaner to keep it all here. */
+
+int
+mount_info::add_item (const char *native, const char *posix, unsigned mountflags, int reg_p)
+{
+ /* Something's wrong if either path is NULL or empty, or if it's
+ not a UNC or absolute path. */
+
+ if ((native == NULL) || (*native == 0) ||
+ (posix == NULL) || (*posix == 0) ||
+ !isabspath (native) || !isabspath (posix) ||
+ slash_unc_prefix_p (posix) || isdrive (posix))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Make sure both paths do not end in /. */
+ char nativetmp[MAX_PATH];
+ char posixtmp[MAX_PATH];
+
+ backslashify (native, nativetmp, 0);
+ nofinalslash (nativetmp, nativetmp);
+
+ slashify (posix, posixtmp, 0);
+ nofinalslash (posixtmp, posixtmp);
+
+ debug_printf ("%s[%s], %s[%s], %p",
+ native, nativetmp, posix, posixtmp, mountflags);
+
+ /* Duplicate /'s in path are an error. */
+ for (char *p = posixtmp + 1; *p; ++p)
+ {
+ if (p[-1] == '/' && p[0] == '/')
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ }
+
+ /* Write over an existing mount item with the same POSIX path if
+ it exists and is from the same registry area. */
+ int i;
+ for (i = 0; i < nmounts; i++)
+ {
+ if (strcasematch (mount[i].posix_path, posixtmp) &&
+ (mount[i].flags & MOUNT_SYSTEM) == (mountflags & MOUNT_SYSTEM))
+ break;
+ }
+
+ if (i == nmounts && nmounts == MAX_MOUNTS)
+ {
+ set_errno (EMFILE);
+ return -1;
+ }
+
+ if (reg_p && add_reg_mount (nativetmp, posixtmp, mountflags))
+ return -1;
+
+ if (i == nmounts)
+ nmounts++;
+ mount[i].init (nativetmp, posixtmp, mountflags);
+ sort ();
+
+ return 0;
+}
+
+/* Delete a mount table entry where path is either a Win32 or POSIX
+ path. Since the mount table is really just a table of aliases,
+ deleting / is ok (although running without a slash mount is
+ strongly discouraged because some programs may run erratically
+ without one). If MOUNT_SYSTEM is set in flags, remove from system
+ registry, otherwise remove the user registry mount.
+*/
+
+int
+mount_info::del_item (const char *path, unsigned flags, int reg_p)
+{
+ char pathtmp[MAX_PATH];
+ int posix_path_p = false;
+
+ /* Something's wrong if path is NULL or empty. */
+ if (path == NULL || *path == 0 || !isabspath (path))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ if (slash_unc_prefix_p (path) || strpbrk (path, ":\\"))
+ backslashify (path, pathtmp, 0);
+ else
+ {
+ slashify (path, pathtmp, 0);
+ posix_path_p = TRUE;
+ }
+ nofinalslash (pathtmp, pathtmp);
+
+ if (reg_p && posix_path_p &&
+ del_reg_mount (pathtmp, flags) &&
+ del_reg_mount (path, flags)) /* for old irregular entries */
+ return -1;
+
+ for (int i = 0; i < nmounts; i++)
+ {
+ int ent = native_sorted[i]; /* in the same order as getmntent() */
+ if (((posix_path_p)
+ ? strcasematch (mount[ent].posix_path, pathtmp)
+ : strcasematch (mount[ent].native_path, pathtmp)) &&
+ (mount[ent].flags & MOUNT_SYSTEM) == (flags & MOUNT_SYSTEM))
+ {
+ if (!posix_path_p &&
+ reg_p && del_reg_mount (mount[ent].posix_path, flags))
+ return -1;
+
+ nmounts--; /* One less mount table entry */
+ /* Fill in the hole if not at the end of the table */
+ if (ent < nmounts)
+ memmove (mount + ent, mount + ent + 1,
+ sizeof (mount[ent]) * (nmounts - ent));
+ sort (); /* Resort the table */
+ return 0;
+ }
+ }
+ set_errno (EINVAL);
+ return -1;
+}
+
+/************************* mount_item class ****************************/
+
+static mntent *
+fillout_mntent (const char *native_path, const char *posix_path, unsigned flags)
+{
+#ifdef _MT_SAFE
+ struct mntent &ret=_reent_winsup ()->mntbuf;
+#else
+ static NO_COPY struct mntent ret;
+#endif
+
+ /* Remove drivenum from list if we see a x: style path */
+ if (strlen (native_path) == 2 && native_path[1] == ':')
+ {
+ int drivenum = cyg_tolower (native_path[0]) - 'a';
+ if (drivenum >= 0 && drivenum <= 31)
+ available_drives &= ~(1 << drivenum);
+ }
+
+ /* Pass back pointers to mount_table strings reserved for use by
+ getmntent rather than pointers to strings in the internal mount
+ table because the mount table might change, causing weird effects
+ from the getmntent user's point of view. */
+
+ strcpy (_reent_winsup ()->mnt_fsname, native_path);
+ ret.mnt_fsname = _reent_winsup ()->mnt_fsname;
+ strcpy (_reent_winsup ()->mnt_dir, posix_path);
+ ret.mnt_dir = _reent_winsup ()->mnt_dir;
+
+ if (!(flags & MOUNT_SYSTEM)) /* user mount */
+ strcpy (_reent_winsup ()->mnt_type, (char *) "user");
+ else /* system mount */
+ strcpy (_reent_winsup ()->mnt_type, (char *) "system");
+
+ ret.mnt_type = _reent_winsup ()->mnt_type;
+
+ /* mnt_opts is a string that details mount params such as
+ binary or textmode, or exec. We don't print
+ `silent' here; it's a magic internal thing. */
+
+ if (!(flags & MOUNT_BINARY))
+ strcpy (_reent_winsup ()->mnt_opts, (char *) "textmode");
+ else
+ strcpy (_reent_winsup ()->mnt_opts, (char *) "binmode");
+
+ if (flags & MOUNT_CYGWIN_EXEC)
+ strcat (_reent_winsup ()->mnt_opts, (char *) ",cygexec");
+ else if (flags & MOUNT_EXEC)
+ strcat (_reent_winsup ()->mnt_opts, (char *) ",exec");
+ else if (flags & MOUNT_NOTEXEC)
+ strcat (_reent_winsup ()->mnt_opts, (char *) ",noexec");
+
+ if ((flags & MOUNT_CYGDRIVE)) /* cygdrive */
+ strcat (_reent_winsup ()->mnt_opts, (char *) ",noumount");
+
+ ret.mnt_opts = _reent_winsup ()->mnt_opts;
+
+ ret.mnt_freq = 1;
+ ret.mnt_passno = 1;
+ return &ret;
+}
+
+struct mntent *
+mount_item::getmntent ()
+{
+ return fillout_mntent (native_path, posix_path, flags);
+}
+
+static struct mntent *
+cygdrive_getmntent ()
+{
+ char native_path[4];
+ char posix_path[MAX_PATH];
+ DWORD mask = 1, drive = 'a';
+ struct mntent *ret = NULL;
+
+ while (available_drives)
+ {
+ for (/* nothing */; drive <= 'z'; mask <<= 1, drive++)
+ if (available_drives & mask)
+ break;
+
+ __small_sprintf (native_path, "%c:\\", drive);
+ if (GetDriveType (native_path) == DRIVE_REMOVABLE ||
+ GetFileAttributes (native_path) == INVALID_FILE_ATTRIBUTES)
+ {
+ available_drives &= ~mask;
+ continue;
+ }
+ native_path[2] = '\0';
+ __small_sprintf (posix_path, "%s%c", mount_table->cygdrive, drive);
+ ret = fillout_mntent (native_path, posix_path, mount_table->cygdrive_flags);
+ break;
+ }
+
+ return ret;
+}
+
+struct mntent *
+mount_info::getmntent (int x)
+{
+ if (x < 0 || x >= nmounts)
+ return cygdrive_getmntent ();
+
+ return mount[native_sorted[x]].getmntent ();
+}
+
+/* Fill in the fields of a mount table entry. */
+
+void
+mount_item::init (const char *native, const char *posix, unsigned mountflags)
+{
+ strcpy ((char *) native_path, native);
+ strcpy ((char *) posix_path, posix);
+
+ native_pathlen = strlen (native_path);
+ posix_pathlen = strlen (posix_path);
+
+ flags = mountflags;
+}
+
+/********************** Mount System Calls **************************/
+
+/* Mount table system calls.
+ Note that these are exported to the application. */
+
+/* mount: Add a mount to the mount table in memory and to the registry
+ that will cause paths under win32_path to be translated to paths
+ under posix_path. */
+
+extern "C" int
+mount (const char *win32_path, const char *posix_path, unsigned flags)
+{
+ int res = -1;
+
+ if (flags & MOUNT_CYGDRIVE) /* normal mount */
+ {
+ /* When flags include MOUNT_CYGDRIVE, take this to mean that
+ we actually want to change the cygdrive prefix and flags
+ without actually mounting anything. */
+ res = mount_table->write_cygdrive_info_to_registry (posix_path, flags);
+ win32_path = NULL;
+ }
+ else
+ res = mount_table->add_item (win32_path, posix_path, flags, TRUE);
+
+ syscall_printf ("%d = mount (%s, %s, %p)", res, win32_path, posix_path, flags);
+ return res;
+}
+
+/* umount: The standard umount call only has a path parameter. Since
+ it is not possible for this call to specify whether to remove the
+ mount from the user or global mount registry table, assume the user
+ table. */
+
+extern "C" int
+umount (const char *path)
+{
+ return cygwin_umount (path, 0);
+}
+
+/* cygwin_umount: This is like umount but takes an additional flags
+ parameter that specifies whether to umount from the user or system-wide
+ registry area. */
+
+extern "C" int
+cygwin_umount (const char *path, unsigned flags)
+{
+ int res = -1;
+
+ if (flags & MOUNT_CYGDRIVE)
+ {
+ /* When flags include MOUNT_CYGDRIVE, take this to mean that we actually want
+ to remove the cygdrive prefix and flags without actually unmounting
+ anything. */
+ res = mount_table->remove_cygdrive_info_from_registry (path, flags);
+ }
+ else
+ {
+ res = mount_table->del_item (path, flags, TRUE);
+ }
+
+ syscall_printf ("%d = cygwin_umount (%s, %d)", res, path, flags);
+ return res;
+}
+
+extern "C" FILE *
+setmntent (const char *filep, const char *)
+{
+ iteration = 0;
+ available_drives = GetLogicalDrives ();
+ return (FILE *) filep;
+}
+
+extern "C" struct mntent *
+getmntent (FILE *)
+{
+ return mount_table->getmntent (iteration++);
+}
+
+extern "C" int
+endmntent (FILE *)
+{
+ return 1;
+}
+
+/********************** Symbolic Link Support **************************/
+
+/* Read symlink from Extended Attribute */
+int
+get_symlink_ea (const char* frompath, char* buf, int buf_size)
+{
+ int res = NTReadEA (frompath, SYMLINK_EA_NAME, buf, buf_size);
+ if (res == 0)
+ debug_printf ("Cannot read symlink from EA");
+ return (res - 1);
+}
+
+/* Save symlink to Extended Attribute */
+BOOL
+set_symlink_ea (const char* frompath, const char* topath)
+{
+ if (!NTWriteEA (frompath, SYMLINK_EA_NAME, topath, strlen (topath) + 1))
+ {
+ debug_printf ("Cannot save symlink in EA");
+ return false;
+ }
+ return TRUE;
+}
+
+/* Create a symlink from FROMPATH to TOPATH. */
+
+/* If TRUE create symlinks as Windows shortcuts, if false create symlinks
+ as normal files with magic number and system bit set. */
+int allow_winsymlinks = TRUE;
+
+extern "C" int
+symlink (const char *topath, const char *frompath)
+{
+ return symlink_worker (topath, frompath, allow_winsymlinks, false);
+}
+
+int
+symlink_worker (const char *topath, const char *frompath, bool use_winsym,
+ bool isdevice)
+{
+ HANDLE h;
+ int res = -1;
+ path_conv win32_path, win32_topath;
+ char from[MAX_PATH + 5];
+ char cwd[MAX_PATH + 1], *cp = NULL, c = 0;
+ char w32topath[MAX_PATH + 1];
+ DWORD written;
+ SECURITY_ATTRIBUTES sa = sec_none_nih;
+
+ /* POSIX says that empty 'frompath' is invalid input whlie empty
+ 'topath' is valid -- it's symlink resolver job to verify if
+ symlink contents point to existing filesystem object */
+ if (check_null_empty_str_errno (topath) == EFAULT ||
+ check_null_empty_str_errno (frompath))
+ goto done;
+
+ if (strlen (topath) >= MAX_PATH)
+ {
+ set_errno (ENAMETOOLONG);
+ goto done;
+ }
+
+ win32_path.check (frompath, PC_SYM_NOFOLLOW);
+ if (use_winsym && !win32_path.exists ())
+ {
+ strcpy (from, frompath);
+ strcat (from, ".lnk");
+ win32_path.check (from, PC_SYM_NOFOLLOW);
+ }
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.case_clash ? ECASECLASH : win32_path.error);
+ goto done;
+ }
+
+ syscall_printf ("symlink (%s, %s)", topath, win32_path.get_win32 ());
+
+ if (win32_path.is_auto_device ())
+ {
+ set_errno (EEXIST);
+ goto done;
+ }
+
+ DWORD create_how;
+ if (!use_winsym)
+ create_how = CREATE_NEW;
+ else if (isdevice)
+ {
+ strcpy (w32topath, topath);
+ create_how = CREATE_ALWAYS;
+ (void) SetFileAttributes (win32_path, FILE_ATTRIBUTE_NORMAL);
+ }
+ else
+ {
+ if (!isabspath (topath))
+ {
+ getcwd (cwd, MAX_PATH + 1);
+ if ((cp = strrchr (from, '/')) || (cp = strrchr (from, '\\')))
+ {
+ c = *cp;
+ *cp = '\0';
+ chdir (from);
+ }
+ backslashify (topath, w32topath, 0);
+ }
+ if (!cp || GetFileAttributes (w32topath) == INVALID_FILE_ATTRIBUTES)
+ {
+ win32_topath.check (topath, PC_SYM_NOFOLLOW);
+ if (!cp || win32_topath.error != ENOENT)
+ strcpy (w32topath, win32_topath);
+ }
+ if (cp)
+ {
+ *cp = c;
+ chdir (cwd);
+ }
+ create_how = CREATE_NEW;
+ }
+
+ if (allow_ntsec && win32_path.has_acls ())
+ set_security_attribute (S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO,
+ &sa, alloca (4096), 4096);
+
+ h = CreateFile (win32_path, GENERIC_WRITE, 0, &sa, create_how,
+ FILE_ATTRIBUTE_NORMAL, 0);
+ if (h == INVALID_HANDLE_VALUE)
+ __seterrno ();
+ else
+ {
+ BOOL success;
+
+ if (use_winsym)
+ {
+ create_shortcut_header ();
+ /* Don't change the datatypes of `len' and `win_len' since
+ their sizeof is used when writing. */
+ unsigned short len = strlen (topath);
+ unsigned short win_len = strlen (w32topath);
+ success = WriteFile (h, shortcut_header, SHORTCUT_HDR_SIZE,
+ &written, NULL)
+ && written == SHORTCUT_HDR_SIZE
+ && WriteFile (h, &len, sizeof len, &written, NULL)
+ && written == sizeof len
+ && WriteFile (h, topath, len, &written, NULL)
+ && written == len
+ && WriteFile (h, &win_len, sizeof win_len, &written, NULL)
+ && written == sizeof win_len
+ && WriteFile (h, w32topath, win_len, &written, NULL)
+ && written == win_len;
+ }
+ else
+ {
+ /* This is the old technique creating a symlink. */
+ char buf[sizeof (SYMLINK_COOKIE) + MAX_PATH + 10];
+
+ __small_sprintf (buf, "%s%s", SYMLINK_COOKIE, topath);
+ DWORD len = strlen (buf) + 1;
+
+ /* Note that the terminating nul is written. */
+ success = WriteFile (h, buf, len, &written, NULL)
+ || written != len;
+
+ }
+ if (success)
+ {
+ CloseHandle (h);
+ if (!allow_ntsec && allow_ntea)
+ set_file_attribute (win32_path.has_acls (),
+ win32_path.get_win32 (),
+ S_IFLNK | S_IRWXU | S_IRWXG | S_IRWXO);
+
+ DWORD attr = use_winsym ? FILE_ATTRIBUTE_READONLY
+ : FILE_ATTRIBUTE_SYSTEM;
+#ifdef HIDDEN_DOT_FILES
+ cp = strrchr (win32_path, '\\');
+ if ((cp && cp[1] == '.') || *win32_path == '.')
+ attr |= FILE_ATTRIBUTE_HIDDEN;
+#endif
+ SetFileAttributes (win32_path, attr);
+
+ if (!isdevice && win32_path.fs_fast_ea ())
+ set_symlink_ea (win32_path, topath);
+ res = 0;
+ }
+ else
+ {
+ __seterrno ();
+ CloseHandle (h);
+ DeleteFileA (win32_path.get_win32 ());
+ }
+ }
+
+done:
+ syscall_printf ("%d = symlink_worker (%s, %s, %d, %d)", res, topath,
+ frompath, use_winsym, isdevice);
+ return res;
+}
+
+static BOOL
+cmp_shortcut_header (const char *file_header)
+{
+ create_shortcut_header ();
+ return memcmp (shortcut_header, file_header, SHORTCUT_HDR_SIZE);
+}
+
+static int
+check_shortcut (const char *path, DWORD fileattr, HANDLE h,
+ char *contents, int *error, unsigned *pflags)
+{
+ char file_header[SHORTCUT_HDR_SIZE];
+ unsigned short len;
+ int res = 0;
+ DWORD got = 0;
+
+ /* Valid Cygwin & U/WIN shortcuts are R/O. */
+ if (!(fileattr & FILE_ATTRIBUTE_READONLY))
+ goto file_not_symlink;
+ /* Read the files header information. This is used to check for a
+ Cygwin or U/WIN shortcut or later to check for executable files. */
+ if (!ReadFile (h, file_header, SHORTCUT_HDR_SIZE, &got, 0))
+ {
+ *error = EIO;
+ goto close_it;
+ }
+ /* Check header if the shortcut is really created by Cygwin or U/WIN. */
+ if (got != SHORTCUT_HDR_SIZE || cmp_shortcut_header (file_header))
+ goto file_not_symlink;
+ /* Next 2 byte are USHORT, containing length of description entry. */
+ if (!ReadFile (h, &len, sizeof len, &got, 0))
+ {
+ *error = EIO;
+ goto close_it;
+ }
+ if (got != sizeof len || len == 0 || len > MAX_PATH)
+ goto file_not_symlink;
+ /* Now read description entry. */
+ if (!ReadFile (h, contents, len, &got, 0))
+ {
+ *error = EIO;
+ goto close_it;
+ }
+ if (got != len)
+ goto file_not_symlink;
+ contents[len] = '\0';
+ res = len;
+ if (res) /* It's a symlink. */
+ *pflags = PATH_SYMLINK;
+ goto close_it;
+
+file_not_symlink:
+ /* Not a symlink, see if executable. */
+ if (!(*pflags & PATH_ALL_EXEC) && has_exec_chars (file_header, got))
+ *pflags |= PATH_EXEC;
+
+close_it:
+ CloseHandle (h);
+ return res;
+}
+
+
+static int
+check_sysfile (const char *path, DWORD fileattr, HANDLE h,
+ char *contents, int *error, unsigned *pflags)
+{
+ char cookie_buf[sizeof (SYMLINK_COOKIE) - 1];
+ DWORD got;
+ int res = 0;
+
+ if (!ReadFile (h, cookie_buf, sizeof (cookie_buf), &got, 0))
+ {
+ debug_printf ("ReadFile1 failed");
+ *error = EIO;
+ }
+ else if (got == sizeof (cookie_buf)
+ && memcmp (cookie_buf, SYMLINK_COOKIE, sizeof (cookie_buf)) == 0)
+ {
+ /* It's a symlink. */
+ *pflags = PATH_SYMLINK;
+
+ res = ReadFile (h, contents, MAX_PATH + 1, &got, 0);
+ if (!res)
+ {
+ debug_printf ("ReadFile2 failed");
+ *error = EIO;
+ }
+ else
+ {
+ /* Versions prior to b16 stored several trailing
+ NULs with the path (to fill the path out to 1024
+ chars). Current versions only store one trailing
+ NUL. The length returned is the path without
+ *any* trailing NULs. We also have to handle (or
+ at least not die from) corrupted paths. */
+ if (memchr (contents, 0, got) != NULL)
+ res = strlen (contents);
+ else
+ res = got;
+ }
+ }
+ else if (got == sizeof (cookie_buf)
+ && memcmp (cookie_buf, SOCKET_COOKIE, sizeof (cookie_buf)) == 0)
+ *pflags |= PATH_SOCKET;
+ else
+ {
+ /* Not a symlink, see if executable. */
+ if (*pflags & PATH_ALL_EXEC)
+ /* Nothing to do */;
+ else if (has_exec_chars (cookie_buf, got))
+ *pflags |= PATH_EXEC;
+ else
+ *pflags |= PATH_NOTEXEC;
+ }
+ syscall_printf ("%d = symlink.check_sysfile (%s, %s) (%p)",
+ res, path, contents, *pflags);
+
+ CloseHandle (h);
+ return res;
+}
+
+enum
+{
+ SCAN_BEG,
+ SCAN_LNK,
+ SCAN_HASLNK,
+ SCAN_JUSTCHECK,
+ SCAN_APPENDLNK,
+ SCAN_EXTRALNK,
+ SCAN_DONE,
+};
+
+class suffix_scan
+{
+ const suffix_info *suffixes, *suffixes_start;
+ int nextstate;
+ char *eopath;
+public:
+ const char *path;
+ char *has (const char *, const suffix_info *);
+ int next ();
+ int lnk_match () {return nextstate >= SCAN_EXTRALNK;}
+};
+
+char *
+suffix_scan::has (const char *in_path, const suffix_info *in_suffixes)
+{
+ nextstate = SCAN_BEG;
+ suffixes = suffixes_start = in_suffixes;
+
+ char *ext_here = strrchr (in_path, '.');
+ path = in_path;
+ eopath = strchr (path, '\0');
+
+ if (!ext_here)
+ goto noext;
+
+ if (suffixes)
+ {
+ /* Check if the extension matches a known extension */
+ for (const suffix_info *ex = in_suffixes; ex->name != NULL; ex++)
+ if (strcasematch (ext_here, ex->name))
+ {
+ nextstate = SCAN_JUSTCHECK;
+ suffixes = NULL; /* Has an extension so don't scan for one. */
+ goto done;
+ }
+ }
+
+ /* Didn't match. Use last resort -- .lnk. */
+ if (strcasematch (ext_here, ".lnk"))
+ {
+ nextstate = SCAN_HASLNK;
+ suffixes = NULL;
+ }
+
+ noext:
+ ext_here = eopath;
+
+ done:
+ return ext_here;
+}
+
+int
+suffix_scan::next ()
+{
+ for (;;)
+ {
+ if (!suffixes)
+ switch (nextstate)
+ {
+ case SCAN_BEG:
+ suffixes = suffixes_start;
+ if (!suffixes)
+ {
+ nextstate = SCAN_LNK;
+ return 1;
+ }
+ if (!*suffixes->name)
+ suffixes++;
+ nextstate = SCAN_EXTRALNK;
+ /* fall through to suffix checking below */
+ break;
+ case SCAN_HASLNK:
+ nextstate = SCAN_EXTRALNK; /* Skip SCAN_BEG */
+ return 1;
+ case SCAN_LNK:
+ case SCAN_EXTRALNK:
+ strcpy (eopath, ".lnk");
+ nextstate = SCAN_DONE;
+ return 1;
+ case SCAN_JUSTCHECK:
+ nextstate = SCAN_APPENDLNK;
+ return 1;
+ case SCAN_APPENDLNK:
+ strcat (eopath, ".lnk");
+ nextstate = SCAN_DONE;
+ return 1;
+ default:
+ *eopath = '\0';
+ return 0;
+ }
+
+ while (suffixes && suffixes->name)
+ if (!suffixes->addon)
+ suffixes++;
+ else
+ {
+ strcpy (eopath, suffixes->name);
+ if (nextstate == SCAN_EXTRALNK)
+ strcat (eopath, ".lnk");
+ suffixes++;
+ return 1;
+ }
+ suffixes = NULL;
+ }
+}
+
+bool
+symlink_info::parse_device (const char *contents)
+{
+ char *endptr;
+ _major_t mymajor;
+ _major_t myminor;
+ _mode_t mymode;
+
+ mymajor = strtol (++contents, &endptr, 16);
+ if (endptr == contents)
+ return false;
+
+ contents = endptr;
+ myminor = strtol (++contents, &endptr, 16);
+ if (endptr == contents)
+ return false;
+
+ contents = endptr;
+ mymode = strtol (++contents, &endptr, 16);
+ if (endptr == contents)
+ return false;
+
+ switch (mymode & S_IFMT)
+ {
+ case S_IFIFO:
+ mymajor = _major (FH_FIFO);
+ myminor = _minor (FH_FIFO);
+ break;
+ case S_IFBLK:
+ case S_IFCHR:
+ if (mymajor || myminor)
+ break;
+ default:
+ return false;
+ }
+
+ major = mymajor;
+ minor = myminor;
+ mode = mymode;
+ return true;
+}
+
+/* Check if PATH is a symlink. PATH must be a valid Win32 path name.
+
+ If PATH is a symlink, put the value of the symlink--the file to
+ which it points--into BUF. The value stored in BUF is not
+ necessarily null terminated. BUFLEN is the length of BUF; only up
+ to BUFLEN characters will be stored in BUF. BUF may be NULL, in
+ which case nothing will be stored.
+
+ Set *SYML if PATH is a symlink.
+
+ Set *EXEC if PATH appears to be executable. This is an efficiency
+ hack because we sometimes have to open the file anyhow. *EXEC will
+ not be set for every executable file.
+
+ Return -1 on error, 0 if PATH is not a symlink, or the length
+ stored into BUF if PATH is a symlink. */
+
+int
+symlink_info::check (char *path, const suffix_info *suffixes, unsigned opt)
+{
+ HANDLE h;
+ int res = 0;
+ suffix_scan suffix;
+ contents[0] = '\0';
+
+ is_symlink = TRUE;
+ ext_here = suffix.has (path, suffixes);
+ extn = ext_here - path;
+ major = 0;
+ minor = 0;
+
+ pflags &= ~PATH_SYMLINK;
+
+ case_clash = false;
+
+ while (suffix.next ())
+ {
+ error = 0;
+ fileattr = GetFileAttributes (suffix.path);
+ if (fileattr == INVALID_FILE_ATTRIBUTES)
+ {
+ /* The GetFileAttributes call can fail for reasons that don't
+ matter, so we just return 0. For example, getting the
+ attributes of \\HOST will typically fail. */
+ debug_printf ("GetFileAttributes (%s) failed", suffix.path);
+ error = geterrno_from_win_error (GetLastError (), EACCES);
+ continue;
+ }
+
+
+ ext_tacked_on = !!*ext_here;
+
+ if (pcheck_case != PCHECK_RELAXED && !case_check (path)
+ || (opt & PC_SYM_IGNORE))
+ goto file_not_symlink;
+
+ int sym_check;
+
+ sym_check = 0;
+
+ if (fileattr & FILE_ATTRIBUTE_DIRECTORY)
+ goto file_not_symlink;
+
+ /* Windows shortcuts are treated as symlinks. */
+ if (suffix.lnk_match ())
+ sym_check = 1;
+
+ /* This is the old Cygwin method creating symlinks: */
+ /* A symlink will have the `system' file attribute. */
+ /* Only files can be symlinks (which can be symlinks to directories). */
+ if (fileattr & FILE_ATTRIBUTE_SYSTEM)
+ sym_check = 2;
+
+ if (!sym_check)
+ goto file_not_symlink;
+
+ if (sym_check > 0 && opt & PC_CHECK_EA &&
+ (res = get_symlink_ea (suffix.path, contents, sizeof (contents))) > 0)
+ {
+ pflags = PATH_SYMLINK;
+ debug_printf ("Got symlink from EA: %s", contents);
+ break;
+ }
+
+ /* Open the file. */
+
+ h = CreateFile (suffix.path, GENERIC_READ, FILE_SHARE_READ,
+ &sec_none_nih, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
+ res = -1;
+ if (h == INVALID_HANDLE_VALUE)
+ goto file_not_symlink;
+
+ /* FIXME: if symlink isn't present in EA, but EAs are supported,
+ should we write it there? */
+ switch (sym_check)
+ {
+ case 1:
+ res = check_shortcut (suffix.path, fileattr, h, contents, &error, &pflags);
+ if (!res)
+ /* check more below */;
+ else if (*contents == ':' && parse_device (contents))
+ goto file_not_symlink;
+ else
+ break;
+ /* If searching for `foo' and then finding a `foo.lnk' which is
+ no shortcut, return the same as if file not found. */
+ if (!suffix.lnk_match () || !ext_tacked_on)
+ goto file_not_symlink;
+
+ fileattr = INVALID_FILE_ATTRIBUTES;
+ continue; /* in case we're going to tack *another* .lnk on this filename. */
+ case 2:
+ res = check_sysfile (suffix.path, fileattr, h, contents, &error, &pflags);
+ if (!res)
+ goto file_not_symlink;
+ break;
+ }
+ break;
+
+ file_not_symlink:
+ is_symlink = false;
+ syscall_printf ("%s", (major || minor) ? "is a device" : "not a symlink");
+ res = 0;
+ break;
+ }
+
+ syscall_printf ("%d = symlink.check (%s, %p) (%p)",
+ res, suffix.path, contents, pflags);
+ return res;
+}
+
+/* Check the correct case of the last path component (given in DOS style).
+ Adjust the case in this->path if pcheck_case == PCHECK_ADJUST or return
+ false if pcheck_case == PCHECK_STRICT.
+ Dont't call if pcheck_case == PCHECK_RELAXED.
+*/
+
+BOOL
+symlink_info::case_check (char *path)
+{
+ WIN32_FIND_DATA data;
+ HANDLE h;
+ char *c;
+
+ /* Set a pointer to the beginning of the last component. */
+ if (!(c = strrchr (path, '\\')))
+ c = path;
+ else
+ ++c;
+
+ if ((h = FindFirstFile (path, &data))
+ != INVALID_HANDLE_VALUE)
+ {
+ FindClose (h);
+
+ /* If that part of the component exists, check the case. */
+ if (strcmp (c, data.cFileName))
+ {
+ case_clash = TRUE;
+
+ /* If check is set to STRICT, a wrong case results
+ in returning a ENOENT. */
+ if (pcheck_case == PCHECK_STRICT)
+ return false;
+
+ /* PCHECK_ADJUST adjusts the case in the incoming
+ path which points to the path in *this. */
+ strcpy (c, data.cFileName);
+ }
+ }
+ return TRUE;
+}
+
+/* readlink system call */
+
+extern "C" int
+readlink (const char *path, char *buf, int buflen)
+{
+ extern suffix_info stat_suffixes[];
+
+ if (buflen < 0)
+ {
+ set_errno (ENAMETOOLONG);
+ return -1;
+ }
+
+ path_conv pathbuf (path, PC_SYM_CONTENTS, stat_suffixes);
+
+ if (pathbuf.error)
+ {
+ set_errno (pathbuf.error);
+ syscall_printf ("-1 = readlink (%s, %p, %d)", path, buf, buflen);
+ return -1;
+ }
+
+ if (!pathbuf.exists ())
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ if (!pathbuf.issymlink ())
+ {
+ if (pathbuf.exists ())
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ int len = min (buflen, (int) strlen (pathbuf.get_win32 ()));
+ memcpy (buf, pathbuf.get_win32 (), len);
+
+ /* errno set by symlink.check if error */
+ return len;
+}
+
+/* Some programs rely on st_dev/st_ino being unique for each file.
+ Hash the path name and hope for the best. The hash arg is not
+ always initialized to zero since readdir needs to compute the
+ dirent ino_t based on a combination of the hash of the directory
+ done during the opendir call and the hash or the filename within
+ the directory. FIXME: Not bullet-proof. */
+/* Cygwin internal */
+
+unsigned long __stdcall
+hash_path_name (unsigned long hash, const char *name)
+{
+ if (!*name)
+ return hash;
+
+ /* Perform some initial permutations on the pathname if this is
+ not "seeded" */
+ if (!hash)
+ {
+ /* Simplistic handling of drives. If there is a drive specified,
+ make sure that the initial letter is upper case. If there is
+ no \ after the ':' assume access through the root directory
+ of that drive.
+ FIXME: Should really honor MS-Windows convention of using
+ the environment to track current directory on various drives. */
+ if (name[1] == ':')
+ {
+ char *nn, *newname = (char *) alloca (strlen (name) + 2);
+ nn = newname;
+ *nn = isupper (*name) ? cyg_tolower (*name) : *name;
+ *++nn = ':';
+ name += 2;
+ if (*name != '\\')
+ *++nn = '\\';
+ strcpy (++nn, name);
+ name = newname;
+ goto hashit;
+ }
+
+ /* Fill out the hashed path name with the current working directory if
+ this is not an absolute path and there is no pre-specified hash value.
+ Otherwise the inodes same will differ depending on whether a file is
+ referenced with an absolute value or relatively. */
+
+ if (!hash && !isabspath (name))
+ {
+ hash = cygheap->cwd.get_hash ();
+ if (name[0] == '.' && name[1] == '\0')
+ return hash;
+ hash = (hash << 5) - hash + '\\';
+ }
+ }
+
+hashit:
+ /* Build up hash. Ignore single trailing slash or \a\b\ != \a\b or
+ \a\b\. but allow a single \ if that's all there is. */
+ do
+ {
+ int ch = cyg_tolower (*name);
+ hash = (hash << 5) - hash + ch;
+ }
+ while (*++name != '\0' &&
+ !(*name == '\\' && (!name[1] || (name[1] == '.' && !name[2]))));
+ return hash;
+}
+
+char *
+getcwd (char *buf, size_t ulen)
+{
+ char* res = NULL;
+ if (ulen == 0 && buf)
+ set_errno (EINVAL);
+ else if (buf == NULL || !__check_null_invalid_struct_errno (buf, ulen))
+ res = cygheap->cwd.get (buf, 1, 1, ulen);
+ return res;
+}
+
+/* getwd: standards? */
+extern "C" char *
+getwd (char *buf)
+{
+ return getcwd (buf, MAX_PATH);
+}
+
+/* chdir: POSIX 5.2.1.1 */
+extern "C" int
+chdir (const char *in_dir)
+{
+ if (check_null_empty_str_errno (in_dir))
+ return -1;
+
+ syscall_printf ("dir '%s'", in_dir);
+
+ char *s;
+ char dir[strlen (in_dir) + 1];
+ strcpy (dir, in_dir);
+ /* Incredibly. Windows allows you to specify a path with trailing
+ whitespace to SetCurrentDirectory. This doesn't work too well
+ with other parts of the API, though, apparently. So nuke trailing
+ white space. */
+ for (s = strchr (dir, '\0'); --s >= dir && isspace ((unsigned int) (*s & 0xff)); )
+ *s = '\0';
+
+ if (!*s)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ /* Convert path. First argument ensures that we don't check for NULL/empty/invalid
+ again. */
+ path_conv path (PC_NONULLEMPTY, dir, PC_FULL | PC_SYM_FOLLOW);
+ if (path.error)
+ {
+ set_errno (path.error);
+ syscall_printf ("-1 = chdir (%s)", dir);
+ return -1;
+ }
+
+
+ /* Look for trailing path component consisting entirely of dots. This
+ is needed only in case of chdir since Windows simply ignores count
+ of dots > 2 here instead of returning an error code. Counts of dots
+ <= 2 are already eliminated by normalize_posix_path. */
+ const char *p = strrchr (dir, '/');
+ if (!p)
+ p = dir;
+ else
+ p++;
+
+ size_t len = strlen (p);
+ if (len > 2 && strspn (p, ".") == len)
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+
+ const char *native_dir = path.get_win32 ();
+
+ /* Check to see if path translates to something like C:.
+ If it does, append a \ to the native directory specification to
+ defeat the Windows 95 (i.e. MS-DOS) tendency of returning to
+ the last directory visited on the given drive. */
+ if (isdrive (native_dir) && !native_dir[2])
+ {
+ path.get_win32 ()[2] = '\\';
+ path.get_win32 ()[3] = '\0';
+ }
+ int res;
+ int devn = path.get_devn ();
+ if (!isvirtual_dev (devn))
+ res = SetCurrentDirectory (native_dir) ? 0 : -1;
+ else if (!path.exists ())
+ {
+ set_errno (ENOENT);
+ return -1;
+ }
+ else if (!path.isdir ())
+ {
+ set_errno (ENOTDIR);
+ return -1;
+ }
+ else
+ {
+ native_dir = "c:\\";
+ res = 0;
+ }
+
+ /* If res != 0, we didn't change to a new directory.
+ Otherwise, set the current windows and posix directory cache from input.
+ If the specified directory is a MS-DOS style directory or if the directory
+ was symlinked, convert the MS-DOS path back to posix style. Otherwise just
+ store the given directory. This allows things like "find", which traverse
+ directory trees, to work correctly with Cygwin mounted directories.
+ FIXME: Is just storing the posixized windows directory the correct thing to
+ do when we detect a symlink? Should we instead rebuild the posix path from
+ the input by traversing links? This would be an expensive operation but
+ we'll see if Cygwin mailing list users whine about the current behavior. */
+ if (res)
+ __seterrno ();
+ else if ((!path.has_symlinks () && strpbrk (dir, ":\\") == NULL
+ && pcheck_case == PCHECK_RELAXED) || isvirtual_dev (devn))
+ cygheap->cwd.set (native_dir, dir);
+ else
+ cygheap->cwd.set (native_dir, NULL);
+
+ /* Note that we're accessing cwd.posix without a lock here. I didn't think
+ it was worth locking just for strace. */
+ syscall_printf ("%d = chdir() cygheap->cwd.posix '%s' native '%s'", res,
+ cygheap->cwd.posix, native_dir);
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" int
+fchdir (int fd)
+{
+ int res;
+ sigframe thisframe (mainthread);
+
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = chdir (cfd->get_win32_name ());
+ else
+ res = -1;
+
+ syscall_printf ("%d = fchdir (%d)", res, fd);
+ return res;
+}
+
+/******************** Exported Path Routines *********************/
+
+/* Cover functions to the path conversion routines.
+ These are exported to the world as cygwin_foo by cygwin.din. */
+
+extern "C" int
+cygwin_conv_to_win32_path (const char *path, char *win32_path)
+{
+ path_conv p (path, PC_SYM_FOLLOW);
+ if (p.error)
+ {
+ win32_path[0] = '\0';
+ set_errno (p.error);
+ return -1;
+ }
+
+ strcpy (win32_path, p);
+ return 0;
+}
+
+extern "C" int
+cygwin_conv_to_full_win32_path (const char *path, char *win32_path)
+{
+ path_conv p (path, PC_SYM_FOLLOW | PC_FULL);
+ if (p.error)
+ {
+ win32_path[0] = '\0';
+ set_errno (p.error);
+ return -1;
+ }
+
+ strcpy (win32_path, p);
+ return 0;
+}
+
+/* This is exported to the world as cygwin_foo by cygwin.din. */
+
+extern "C" int
+cygwin_conv_to_posix_path (const char *path, char *posix_path)
+{
+ if (check_null_empty_str_errno (path))
+ return -1;
+ mount_table->conv_to_posix_path (path, posix_path, 1);
+ return 0;
+}
+
+extern "C" int
+cygwin_conv_to_full_posix_path (const char *path, char *posix_path)
+{
+ if (check_null_empty_str_errno (path))
+ return -1;
+ mount_table->conv_to_posix_path (path, posix_path, 0);
+ return 0;
+}
+
+/* The realpath function is supported on some UNIX systems. */
+
+extern "C" char *
+realpath (const char *path, char *resolved)
+{
+ int err;
+
+ path_conv real_path (path, PC_SYM_FOLLOW | PC_FULL);
+
+ if (real_path.error)
+ err = real_path.error;
+ else
+ {
+ err = mount_table->conv_to_posix_path (real_path.get_win32 (), resolved, 0);
+ if (err == 0)
+ return resolved;
+ }
+
+ /* FIXME: on error, we are supposed to put the name of the path
+ component which could not be resolved into RESOLVED. */
+ resolved[0] = '\0';
+
+ set_errno (err);
+ return NULL;
+}
+
+/* Return non-zero if path is a POSIX path list.
+ This is exported to the world as cygwin_foo by cygwin.din.
+
+DOCTOOL-START
+<sect1 id="add-func-cygwin-posix-path-list-p">
+ <para>Rather than use a mode to say what the "proper" path list
+ format is, we allow any, and give apps the tools they need to
+ convert between the two. If a ';' is present in the path list it's
+ a Win32 path list. Otherwise, if the first path begins with
+ [letter]: (in which case it can be the only element since if it
+ wasn't a ';' would be present) it's a Win32 path list. Otherwise,
+ it's a POSIX path list.</para>
+</sect1>
+DOCTOOL-END
+ */
+
+extern "C" int
+cygwin_posix_path_list_p (const char *path)
+{
+ int posix_p = !(strchr (path, ';') || isdrive (path));
+ return posix_p;
+}
+
+/* These are used for apps that need to convert env vars like PATH back and
+ forth. The conversion is a two step process. First, an upper bound on the
+ size of the buffer needed is computed. Then the conversion is done. This
+ allows the caller to use alloca if it wants. */
+
+static int
+conv_path_list_buf_size (const char *path_list, int to_posix_p)
+{
+ int i, num_elms, max_mount_path_len, size;
+ const char *p;
+
+ /* The theory is that an upper bound is
+ current_size + (num_elms * max_mount_path_len) */
+
+ char delim = to_posix_p ? ';' : ':';
+ p = path_list;
+ for (num_elms = 1; (p = strchr (p, delim)) != NULL; ++num_elms)
+ ++p;
+
+ /* 7: strlen ("//c") + slop, a conservative initial value */
+ for (max_mount_path_len = 7, i = 0; i < mount_table->nmounts; ++i)
+ {
+ int mount_len = (to_posix_p
+ ? mount_table->mount[i].posix_pathlen
+ : mount_table->mount[i].native_pathlen);
+ if (max_mount_path_len < mount_len)
+ max_mount_path_len = mount_len;
+ }
+
+ /* 100: slop */
+ size = strlen (path_list) + (num_elms * max_mount_path_len) + 100;
+ return size;
+}
+
+extern "C" int
+cygwin_win32_to_posix_path_list_buf_size (const char *path_list)
+{
+ return conv_path_list_buf_size (path_list, 1);
+}
+
+extern "C" int
+cygwin_posix_to_win32_path_list_buf_size (const char *path_list)
+{
+ return conv_path_list_buf_size (path_list, 0);
+}
+
+extern "C" int
+cygwin_win32_to_posix_path_list (const char *win32, char *posix)
+{
+ conv_path_list (win32, posix, 1);
+ return 0;
+}
+
+extern "C" int
+cygwin_posix_to_win32_path_list (const char *posix, char *win32)
+{
+ conv_path_list (posix, win32, 0);
+ return 0;
+}
+
+/* cygwin_split_path: Split a path into directory and file name parts.
+ Buffers DIR and FILE are assumed to be big enough.
+
+ Examples (path -> `dir' / `file'):
+ / -> `/' / `'
+ "" -> `.' / `'
+ . -> `.' / `.' (FIXME: should this be `.' / `'?)
+ .. -> `.' / `..' (FIXME: should this be `..' / `'?)
+ foo -> `.' / `foo'
+ foo/bar -> `foo' / `bar'
+ foo/bar/ -> `foo' / `bar'
+ /foo -> `/' / `foo'
+ /foo/bar -> `/foo' / `bar'
+ c: -> `c:/' / `'
+ c:/ -> `c:/' / `'
+ c:foo -> `c:/' / `foo'
+ c:/foo -> `c:/' / `foo'
+ */
+
+extern "C" void
+cygwin_split_path (const char *path, char *dir, char *file)
+{
+ int dir_started_p = 0;
+
+ /* Deal with drives.
+ Remember that c:foo <==> c:/foo. */
+ if (isdrive (path))
+ {
+ *dir++ = *path++;
+ *dir++ = *path++;
+ *dir++ = '/';
+ if (!*path)
+ {
+ *dir = 0;
+ *file = 0;
+ return;
+ }
+ if (isdirsep (*path))
+ ++path;
+ dir_started_p = 1;
+ }
+
+ /* Determine if there are trailing slashes and "delete" them if present.
+ We pretend as if they don't exist. */
+ const char *end = path + strlen (path);
+ /* path + 1: keep leading slash. */
+ while (end > path + 1 && isdirsep (end[-1]))
+ --end;
+
+ /* At this point, END points to one beyond the last character
+ (with trailing slashes "deleted"). */
+
+ /* Point LAST_SLASH at the last slash (duh...). */
+ const char *last_slash;
+ for (last_slash = end - 1; last_slash >= path; --last_slash)
+ if (isdirsep (*last_slash))
+ break;
+
+ if (last_slash == path)
+ {
+ *dir++ = '/';
+ *dir = 0;
+ }
+ else if (last_slash > path)
+ {
+ memcpy (dir, path, last_slash - path);
+ dir[last_slash - path] = 0;
+ }
+ else
+ {
+ if (dir_started_p)
+ ; /* nothing to do */
+ else
+ *dir++ = '.';
+ *dir = 0;
+ }
+
+ memcpy (file, last_slash + 1, end - last_slash - 1);
+ file[end - last_slash - 1] = 0;
+}
+
+/*****************************************************************************/
+
+/* Return the hash value for the current win32 value.
+ This is used when constructing inodes. */
+DWORD
+cwdstuff::get_hash ()
+{
+ DWORD hashnow;
+ cwd_lock->acquire ();
+ hashnow = hash;
+ cwd_lock->release ();
+ return hashnow;
+}
+
+/* Initialize cygcwd 'muto' for serializing access to cwd info. */
+void
+cwdstuff::init ()
+{
+ new_muto (cwd_lock);
+}
+
+/* Get initial cwd. Should only be called once in a
+ process tree. */
+bool
+cwdstuff::get_initial ()
+{
+ cwd_lock->acquire ();
+
+ if (win32)
+ return 1;
+
+ int i;
+ DWORD len, dlen;
+ for (i = 0, dlen = MAX_PATH, len = 0; i < 3; dlen *= 2, i++)
+ {
+ win32 = (char *) crealloc (win32, dlen + 2);
+ if ((len = GetCurrentDirectoryA (dlen, win32)) < dlen)
+ break;
+ }
+
+ if (len == 0)
+ {
+ __seterrno ();
+ cwd_lock->release ();
+ debug_printf ("get_initial_cwd failed, %E");
+ cwd_lock->release ();
+ return 0;
+ }
+ set (NULL);
+ return 1; /* Leaves cwd lock unreleased */
+}
+
+/* Fill out the elements of a cwdstuff struct.
+ It is assumed that the lock for the cwd is acquired if
+ win32_cwd == NULL. */
+void
+cwdstuff::set (const char *win32_cwd, const char *posix_cwd)
+{
+ char pathbuf[MAX_PATH];
+
+ if (win32_cwd)
+ {
+ cwd_lock->acquire ();
+ win32 = (char *) crealloc (win32, strlen (win32_cwd) + 1);
+ strcpy (win32, win32_cwd);
+ }
+
+ if (!posix_cwd)
+ mount_table->conv_to_posix_path (win32, pathbuf, 0);
+ else
+ (void) normalize_posix_path (posix_cwd, pathbuf);
+
+ posix = (char *) crealloc (posix, strlen (pathbuf) + 1);
+ strcpy (posix, pathbuf);
+
+ hash = hash_path_name (0, win32);
+
+ if (win32_cwd)
+ cwd_lock->release ();
+
+ return;
+}
+
+/* Copy the value for either the posix or the win32 cwd into a buffer. */
+char *
+cwdstuff::get (char *buf, int need_posix, int with_chroot, unsigned ulen)
+{
+ MALLOC_CHECK;
+
+ if (ulen)
+ /* nothing */;
+ else if (buf == NULL)
+ ulen = (unsigned) -1;
+ else
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+
+ if (!get_initial ()) /* Get initial cwd and set cwd lock */
+ return NULL;
+
+ char *tocopy;
+ if (!need_posix)
+ tocopy = win32;
+ else
+ tocopy = posix;
+
+ debug_printf ("posix %s", posix);
+ if (strlen (tocopy) >= ulen)
+ {
+ set_errno (ERANGE);
+ buf = NULL;
+ }
+ else
+ {
+ if (!buf)
+ buf = (char *) malloc (strlen (tocopy) + 1);
+ strcpy (buf, tocopy);
+ if (!buf[0]) /* Should only happen when chroot */
+ strcpy (buf, "/");
+ }
+
+ cwd_lock->release ();
+
+out:
+ syscall_printf ("(%s) = cwdstuff::get (%p, %d, %d, %d), errno %d",
+ buf, buf, ulen, need_posix, with_chroot, errno);
+ MALLOC_CHECK;
+ return buf;
+}
diff --git a/winsup/cygwin/path.h b/winsup/cygwin/path.h
new file mode 100644
index 00000000000..67a9ed79d1b
--- /dev/null
+++ b/winsup/cygwin/path.h
@@ -0,0 +1,211 @@
+/* path.h: path data structures
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+struct suffix_info
+{
+ const char *name;
+ int addon;
+ suffix_info (const char *s, int addit = 0): name (s), addon (addit) {}
+};
+
+enum pathconv_arg
+{
+ PC_SYM_FOLLOW = 0x0001,
+ PC_SYM_NOFOLLOW = 0x0002,
+ PC_SYM_IGNORE = 0x0004,
+ PC_SYM_CONTENTS = 0x0008,
+ PC_FULL = 0x0010,
+ PC_NULLEMPTY = 0x0020,
+ PC_CHECK_EA = 0x0040,
+ PC_POSIX = 0x0080
+};
+
+enum case_checking
+{
+ PCHECK_RELAXED = 0,
+ PCHECK_ADJUST = 1,
+ PCHECK_STRICT = 2
+};
+
+#define PC_NONULLEMPTY -1
+
+#include <sys/mount.h>
+
+enum path_types
+{
+ PATH_NOTHING = 0,
+ PATH_SYMLINK = MOUNT_SYMLINK,
+ PATH_BINARY = MOUNT_BINARY,
+ PATH_EXEC = MOUNT_EXEC,
+ PATH_NOTEXEC = MOUNT_NOTEXEC,
+ PATH_CYGWIN_EXEC = MOUNT_CYGWIN_EXEC,
+ PATH_ALL_EXEC = (PATH_CYGWIN_EXEC | PATH_EXEC),
+ PATH_TEXT = 0x02000000,
+ PATH_ISDISK = 0x04000000,
+ PATH_HAS_SYMLINKS = 0x10000000,
+ PATH_HASBUGGYOPEN = 0x20000000,
+ PATH_SOCKET = 0x40000000,
+ PATH_HASACLS = 0x80000000
+};
+
+class symlink_info;
+struct fs_info
+{
+ char name[MAX_PATH];
+ char root_dir[MAX_PATH];
+ DWORD flags;
+ DWORD serial;
+ DWORD sym_opt; /* additional options to pass to symlink_info resolver */
+ DWORD is_remote_drive;
+ DWORD drive_type;
+ bool update (const char *);
+};
+class path_conv
+{
+ char path[MAX_PATH];
+ DWORD fileattr;
+ fs_info fs;
+ void add_ext_from_sym (symlink_info&);
+ public:
+
+ unsigned path_flags;
+ char *known_suffix;
+ int error;
+ device dev;
+ BOOL case_clash;
+ char *normalized_path;
+
+ int isdisk () const { return path_flags & PATH_ISDISK;}
+ int isremote () const {return fs.is_remote_drive;}
+ int has_acls () const {return path_flags & PATH_HASACLS;}
+ int has_symlinks () const {return path_flags & PATH_HAS_SYMLINKS;}
+ int hasgood_inode () const {return path_flags & PATH_HASACLS;} // Not strictly correct
+ int has_buggy_open () const {return path_flags & PATH_HASBUGGYOPEN;}
+ int binmode () const
+ {
+ if (path_flags & PATH_BINARY)
+ return O_BINARY;
+ if (path_flags & PATH_TEXT)
+ return O_TEXT;
+ return 0;
+ }
+ int issymlink () const {return path_flags & PATH_SYMLINK;}
+ int isdevice () const {return dev.devn && dev.devn != FH_FS && dev.devn != FH_FIFO;}
+ int isfifo () const {return dev == FH_FIFO;}
+ int isspecial () const {return dev.devn && dev.devn != FH_FS;}
+ int is_auto_device () const {return isdevice () && !is_fs_special ();}
+ int is_fs_special () const {return isspecial () && dev.isfs ();}
+ int issocket () const {return path_flags & PATH_SOCKET;}
+ int iscygexec () const {return path_flags & PATH_CYGWIN_EXEC;}
+ bool exists () const {return fileattr != INVALID_FILE_ATTRIBUTES;}
+ bool has_attribute (DWORD x) const {return exists () && (fileattr & x);}
+ int isdir () const {return has_attribute (FILE_ATTRIBUTE_DIRECTORY);}
+ executable_states exec_state ()
+ {
+ extern int _check_for_executable;
+ if (path_flags & PATH_ALL_EXEC)
+ return is_executable;
+ if (path_flags & PATH_NOTEXEC)
+ return not_executable;
+ if (!_check_for_executable)
+ return dont_care_if_executable;
+ return dont_know_if_executable;
+ }
+
+ void set_binary () {path_flags |= PATH_BINARY;}
+ void set_symlink () {path_flags |= PATH_SYMLINK;}
+ void set_has_symlinks () {path_flags |= PATH_HAS_SYMLINKS;}
+ void set_isdisk () {path_flags |= PATH_ISDISK; dev.devn = FH_FS;}
+ void set_exec (int x = 1) {path_flags |= x ? PATH_EXEC : PATH_NOTEXEC;}
+ void set_has_acls (int x = 1) {path_flags |= x ? PATH_HASACLS : PATH_NOTHING;}
+ void set_has_buggy_open (int x = 1) {path_flags |= x ? PATH_HASBUGGYOPEN : PATH_NOTHING;}
+
+ void check (const char *src, unsigned opt = PC_SYM_FOLLOW,
+ const suffix_info *suffixes = NULL) __attribute__ ((regparm(3)));
+
+ path_conv (int, const char *src, unsigned opt = PC_SYM_FOLLOW,
+ const suffix_info *suffixes = NULL)
+ {
+ check (src, opt, suffixes);
+ }
+
+ path_conv (const char *src, unsigned opt = PC_SYM_FOLLOW,
+ const suffix_info *suffixes = NULL)
+ {
+ check (src, opt | PC_NULLEMPTY, suffixes);
+ }
+
+ path_conv (): fileattr (INVALID_FILE_ATTRIBUTES), path_flags (0),
+ known_suffix (NULL), error (0), normalized_path (NULL)
+ {path[0] = '\0';}
+
+ inline char *get_win32 () { return path; }
+ operator char *() {return path;}
+ operator const char *() {return path;}
+ operator DWORD &() {return fileattr;}
+ operator int () {return fileattr; }
+ char operator [](int i) const {return path[i];}
+ DWORD get_devn () {return dev.devn == FH_BAD ? (DWORD) FH_FS : dev.devn;}
+ short get_unitn () {return dev.minor;}
+ DWORD file_attributes () {return fileattr;}
+ DWORD drive_type () {return fs.drive_type;}
+ BOOL fs_fast_ea () {return fs.sym_opt & PC_CHECK_EA;}
+ void set_path (const char *p) {strcpy (path, p);}
+ char *return_and_clear_normalized_path ();
+ const char * root_dir () { return fs.root_dir; }
+ DWORD volser () { return fs.serial; }
+ const char *volname () {return fs.name; }
+ void fillin (HANDLE h);
+};
+
+/* Symlink marker */
+#define SYMLINK_COOKIE "!<symlink>"
+
+#define SYMLINK_EA_NAME ".CYGSYMLINK"
+
+/* Socket marker */
+#define SOCKET_COOKIE "!<socket >"
+
+/* The sizeof header written to a shortcut by Cygwin or U/WIN. */
+#define SHORTCUT_HDR_SIZE 76
+
+/* Maximum depth of symlinks (after which ELOOP is issued). */
+#define MAX_LINK_DEPTH 10
+int __stdcall slash_unc_prefix_p (const char *path) __attribute__ ((regparm(1)));
+
+enum fe_types
+{
+ FE_NADA = 0, /* Nothing special */
+ FE_NNF = 1, /* Return NULL if not found */
+ FE_NATIVE = 2, /* Return native path in path_conv struct */
+ FE_CWD = 4 /* Search CWD for program */
+};
+const char * __stdcall find_exec (const char *name, path_conv& buf,
+ const char *winenv = "PATH=",
+ unsigned opt = FE_NADA,
+ const char **known_suffix = NULL)
+ __attribute__ ((regparm(3)));
+
+/* Common macros for checking for invalid path names */
+#define isdrive(s) (isalpha (*(s)) && (s)[1] == ':')
+
+static inline bool
+has_exec_chars (const char *buf, int len)
+{
+ return len >= 2 &&
+ ((buf[0] == '#' && buf[1] == '!') ||
+ (buf[0] == ':' && buf[1] == '\n') ||
+ (buf[0] == 'M' && buf[1] == 'Z'));
+}
+
+int pathmatch (const char *path1, const char *path2) __attribute__ ((regparm (2)));
+int pathnmatch (const char *path1, const char *path2, int len) __attribute__ ((regparm (2)));
+
+int path_prefix_p (const char *path1, const char *path2, int len1) __attribute__ ((regparm (3)));
diff --git a/winsup/cygwin/pinfo.cc b/winsup/cygwin/pinfo.cc
new file mode 100644
index 00000000000..c55d44b2a5d
--- /dev/null
+++ b/winsup/cygwin/pinfo.cc
@@ -0,0 +1,718 @@
+/* pinfo.cc: process table support
+
+ Copyright 1996, 1997, 1998, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdlib.h>
+#include <time.h>
+#include <errno.h>
+#include <limits.h>
+#include <stdarg.h>
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygerrno.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include "cygwin_version.h"
+#include "perprocess.h"
+#include "environ.h"
+#include <assert.h>
+#include <ntdef.h>
+#include "ntdll.h"
+#include "cygthread.h"
+#include "shared_info.h"
+#include "cygheap.h"
+#include "fhandler.h"
+
+static char NO_COPY pinfo_dummy[sizeof (_pinfo)] = {0};
+
+pinfo NO_COPY myself ((_pinfo *)&pinfo_dummy); // Avoid myself != NULL checks
+
+HANDLE hexec_proc;
+
+void __stdcall
+pinfo_fixup_after_fork ()
+{
+ if (hexec_proc)
+ CloseHandle (hexec_proc);
+
+ if (!DuplicateHandle (hMainProc, hMainProc, hMainProc, &hexec_proc, 0,
+ TRUE, DUPLICATE_SAME_ACCESS))
+ {
+ system_printf ("couldn't save current process handle %p, %E", hMainProc);
+ hexec_proc = NULL;
+ }
+}
+
+/* Initialize the process table.
+ This is done once when the dll is first loaded. */
+
+void __stdcall
+set_myself (pid_t pid, HANDLE h)
+{
+ DWORD winpid = GetCurrentProcessId ();
+ if (pid == 1)
+ pid = cygwin_pid (winpid);
+ myself.init (pid, PID_IN_USE | PID_MYSELF, h);
+ myself->dwProcessId = winpid;
+ myself->process_state |= PID_IN_USE;
+ myself->start_time = time (NULL); /* Register our starting time. */
+
+ (void) GetModuleFileName (NULL, myself->progname, sizeof (myself->progname));
+ if (!strace.active)
+ strace.hello ();
+ InitializeCriticalSection (&myself->lock);
+ return;
+}
+
+/* Initialize the process table entry for the current task.
+ This is not called for forked tasks, only execed ones. */
+void __stdcall
+pinfo_init (char **envp, int envc)
+{
+ if (envp)
+ {
+ environ_init (envp, envc);
+ /* spawn has already set up a pid structure for us so we'll use that */
+ myself->process_state |= PID_CYGPARENT;
+ }
+ else
+ {
+ /* Invent our own pid. */
+
+ set_myself (1);
+ myself->ppid = 1;
+ myself->pgid = myself->sid = myself->pid;
+ myself->ctty = -1;
+ myself->uid = ILLEGAL_UID;
+
+ environ_init (NULL, 0); /* call after myself has been set up */
+ }
+
+ debug_printf ("pid %d, pgid %d", myself->pid, myself->pgid);
+}
+
+void
+_pinfo::exit (UINT n, bool norecord)
+{
+ if (this)
+ {
+ if (!norecord)
+ process_state = PID_EXITED;
+
+ /* FIXME: There is a potential race between an execed process and its
+ parent here. I hated to add a mutex just for this, though. */
+ struct rusage r;
+ fill_rusage (&r, hMainProc);
+ add_rusage (&rusage_self, &r);
+ }
+
+ cygthread::terminate ();
+ sigproc_printf ("Calling ExitProcess %d", n);
+ ExitProcess (n);
+}
+
+void
+pinfo::init (pid_t n, DWORD flag, HANDLE in_h)
+{
+ if (myself && n == myself->pid)
+ {
+ procinfo = myself;
+ destroy = 0;
+ h = NULL;
+ return;
+ }
+
+ void *mapaddr;
+ if (!(flag & PID_MYSELF))
+ mapaddr = NULL;
+ else
+ {
+ flag &= ~PID_MYSELF;
+ HANDLE hdummy;
+ mapaddr = open_shared (NULL, 0, hdummy, 0, SH_MYSELF);
+ }
+
+ int createit = flag & (PID_IN_USE | PID_EXECED);
+ for (int i = 0; i < 10; i++)
+ {
+ int created;
+ char mapname[MAX_PATH];
+ __small_sprintf (mapname, "cygpid.%x", n);
+
+ int mapsize;
+ if (flag & PID_EXECED)
+ mapsize = PINFO_REDIR_SIZE;
+ else
+ mapsize = sizeof (_pinfo);
+
+ if (in_h)
+ {
+ h = in_h;
+ created = 0;
+ }
+ else if (!createit)
+ {
+ h = OpenFileMappingA (FILE_MAP_READ | FILE_MAP_WRITE, FALSE, mapname);
+ created = 0;
+ }
+ else
+ {
+ h = CreateFileMapping (INVALID_HANDLE_VALUE, &sec_all_nih,
+ PAGE_READWRITE, 0, mapsize, mapname);
+ created = h && GetLastError () != ERROR_ALREADY_EXISTS;
+ }
+
+ if (!h)
+ {
+ if (createit)
+ __seterrno ();
+ procinfo = NULL;
+ return;
+ }
+
+ procinfo = (_pinfo *) MapViewOfFileEx (h, FILE_MAP_READ | FILE_MAP_WRITE,
+ 0, 0, 0, mapaddr);
+ ProtectHandle1 (h, pinfo_shared_handle);
+
+ if ((procinfo->process_state & PID_INITIALIZING) && (flag & PID_NOREDIR)
+ && cygwin_pid (procinfo->dwProcessId) != procinfo->pid)
+ {
+ release ();
+ set_errno (ENOENT);
+ return;
+ }
+
+ if (procinfo->process_state & PID_EXECED)
+ {
+ assert (!i);
+ pid_t realpid = procinfo->pid;
+ debug_printf ("execed process windows pid %d, cygwin pid %d", n, realpid);
+ if (realpid == n)
+ api_fatal ("retrieval of execed process info for pid %d failed due to recursion.", n);
+ n = realpid;
+ release ();
+ if (flag & PID_ALLPIDS)
+ {
+ set_errno (ENOENT);
+ break;
+ }
+ continue;
+ }
+
+ /* In certain rare, pathological cases, it is possible for the shared
+ memory region to exist for a while after a process has exited. This
+ should only be a brief occurrence, so rather than introduce some kind
+ of locking mechanism, just loop. FIXME: I'm sure I'll regret doing it
+ this way at some point. */
+ if (i < 9 && !created && createit && (procinfo->process_state & PID_EXITED))
+ {
+ low_priority_sleep (5);
+ release ();
+ continue;
+ }
+
+ if (!created)
+ /* nothing */;
+ else if (!(flag & PID_EXECED))
+ procinfo->pid = n;
+ else
+ {
+ procinfo->process_state |= PID_IN_USE | PID_EXECED;
+ procinfo->pid = myself->pid;
+ }
+ break;
+ }
+ destroy = 1;
+}
+
+bool
+_pinfo::alive ()
+{
+ HANDLE h = OpenProcess (PROCESS_QUERY_INFORMATION, false, dwProcessId);
+ if (h)
+ CloseHandle (h);
+ return !!h;
+}
+
+extern char **__argv;
+
+void
+_pinfo::commune_recv ()
+{
+ DWORD nr;
+ DWORD code;
+ HANDLE hp;
+ HANDLE __fromthem = NULL;
+ HANDLE __tothem = NULL;
+
+ hp = OpenProcess (PROCESS_DUP_HANDLE, false, dwProcessId);
+ if (!hp)
+ {
+ sigproc_printf ("couldn't open handle for pid %d(%u)", pid, dwProcessId);
+ hello_pid = -1;
+ return;
+ }
+ if (!DuplicateHandle (hp, fromthem, hMainProc, &__fromthem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate fromthem, %E");
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ if (!DuplicateHandle (hp, tothem, hMainProc, &__tothem, 0, false, DUPLICATE_SAME_ACCESS))
+ {
+ sigproc_printf ("couldn't duplicate tothem, %E");
+ CloseHandle (__fromthem);
+ CloseHandle (hp);
+ hello_pid = -1;
+ return;
+ }
+
+ CloseHandle (hp);
+ hello_pid = 0;
+
+ if (!ReadFile (__fromthem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ {
+ unsigned n = 1;
+ CloseHandle (__fromthem); __fromthem = NULL;
+ for (char **a = __argv; *a; a++)
+ n += strlen (*a) + 1;
+ if (!WriteFile (__tothem, &n, sizeof n, &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile sizeof argv failed, %E");
+ }
+ else
+ for (char **a = __argv; *a; a++)
+ if (!WriteFile (__tothem, *a, strlen (*a) + 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile arg %d failed, %E", a - __argv);
+ break;
+ }
+ if (!WriteFile (__tothem, "", 1, &nr, NULL))
+ {
+ sigproc_printf ("WriteFile null failed, %E");
+ break;
+ }
+ break;
+ }
+ case PICOM_FIFO:
+ {
+ int formic;
+ if (!ReadFile (__fromthem, &formic, sizeof formic, &nr, NULL)
+ || nr != sizeof formic)
+ {
+ /* __seterrno ();*/ // this is run from the signal thread, so don't set errno
+ goto out;
+ }
+
+ fhandler_fifo *fh = cygheap->fdtab.find_fifo ((ATOM) formic);
+ HANDLE it[] = {(fh->get_handle ()), (fh->get_output_handle ())};
+
+ if (!WriteFile (__tothem, it, sizeof (it), &nr, NULL))
+ {
+ /*__seterrno ();*/ // this is run from the signal thread, so don't set errno
+ sigproc_printf ("WriteFile read handle failed, %E");
+ }
+
+ (void) ReadFile (__fromthem, &nr, sizeof (nr), &nr, NULL);
+ break;
+ }
+ }
+
+out:
+ if (__fromthem)
+ CloseHandle (__fromthem);
+ if (__tothem)
+ CloseHandle (__tothem);
+}
+
+#define PIPEBUFSIZE (16 * sizeof (DWORD))
+
+commune_result
+_pinfo::commune_send (DWORD code, ...)
+{
+ HANDLE fromthem = NULL, tome = NULL;
+ HANDLE fromme = NULL, tothem = NULL;
+ DWORD nr;
+ commune_result res;
+ va_list args;
+
+ va_start (args, code);
+
+ res.s = NULL;
+ res.n = 0;
+
+ if (!pid || !this)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+ if (!CreatePipe (&fromthem, &tome, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("first CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ if (!CreatePipe (&fromme, &tothem, &sec_all_nih, PIPEBUFSIZE))
+ {
+ sigproc_printf ("first CreatePipe failed, %E");
+ __seterrno ();
+ goto err;
+ }
+ EnterCriticalSection (&myself->lock);
+ myself->tothem = tome;
+ myself->fromthem = fromme;
+ myself->hello_pid = pid;
+ if (!WriteFile (tothem, &code, sizeof code, &nr, NULL) || nr != sizeof code)
+ {
+ __seterrno ();
+ goto err;
+ }
+
+ switch (code)
+ {
+ case PICOM_FIFO:
+ {
+ int formic = va_arg (args, int);
+ if (!WriteFile (tothem, &formic, sizeof formic, &nr, NULL) || nr != sizeof formic)
+ {
+ __seterrno ();
+ goto err;
+ }
+ break;
+ }
+ }
+
+ if (sig_send (this, __SIGCOMMUNE))
+ goto err;
+
+ /* FIXME: Need something better than an busy loop here */
+ bool isalive;
+ while ((isalive = alive ()))
+ if (myself->hello_pid <= 0)
+ break;
+ else
+ low_priority_sleep (0);
+
+ CloseHandle (tome);
+ tome = NULL;
+ CloseHandle (fromme);
+ fromme = NULL;
+
+ if (!isalive)
+ {
+ set_errno (ESRCH);
+ goto err;
+ }
+
+ if (myself->hello_pid < 0)
+ {
+ set_errno (ENOSYS);
+ goto err;
+ }
+
+ size_t n;
+ switch (code)
+ {
+ case PICOM_CMDLINE:
+ if (!ReadFile (fromthem, &n, sizeof n, &nr, NULL) || nr != sizeof n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.s = (char *) malloc (n);
+ char *p;
+ for (p = res.s; ReadFile (fromthem, p, n, &nr, NULL); p += nr)
+ continue;
+ if ((unsigned) (p - res.s) != n)
+ {
+ __seterrno ();
+ goto err;
+ }
+ res.n = n;
+ break;
+ case PICOM_FIFO:
+ {
+ DWORD x = ReadFile (fromthem, res.handles, sizeof (res.handles), &nr, NULL);
+ WriteFile (tothem, &x, sizeof (x), &x, NULL);
+ if (!x)
+ goto err;
+ if (nr != sizeof (res.handles))
+ {
+ set_errno (EPIPE);
+ goto err;
+ }
+ break;
+ }
+ }
+ CloseHandle (tothem);
+ CloseHandle (fromthem);
+ goto out;
+
+err:
+ if (tome)
+ CloseHandle (tome);
+ if (fromthem)
+ CloseHandle (fromthem);
+ if (tothem)
+ CloseHandle (tothem);
+ if (fromme)
+ CloseHandle (fromme);
+ memset (&res, 0, sizeof (res));
+
+out:
+ myself->hello_pid = 0;
+ LeaveCriticalSection (&myself->lock);
+ return res;
+}
+
+char *
+_pinfo::cmdline (size_t& n)
+{
+ char *s;
+ if (!this || !pid)
+ return NULL;
+ if (pid != myself->pid)
+ {
+ commune_result cr = commune_send (PICOM_CMDLINE);
+ s = cr.s;
+ n = cr.n;
+ }
+ else
+ {
+ n = 1;
+ for (char **a = __argv; *a; a++)
+ n += strlen (*a) + 1;
+ char *p;
+ p = s = (char *) malloc (n);
+ for (char **a = __argv; *a; a++)
+ {
+ strcpy (p, *a);
+ p = strchr (p, '\0') + 1;
+ }
+ *p = '\0';
+ }
+ return s;
+}
+
+void
+pinfo::release ()
+{
+ if (h)
+ {
+#ifdef DEBUGGING
+ if (((DWORD) procinfo & 0x77000000) == 0x61000000) try_to_debug ();
+#endif
+ UnmapViewOfFile (procinfo);
+ procinfo = NULL;
+ ForceCloseHandle1 (h, pinfo_shared_handle);
+ h = NULL;
+ }
+}
+
+/* DOCTOOL-START
+
+<sect1 id="func-cygwin-winpid-to-pid">
+ <title>cygwin_winpid_to_pid</title>
+
+ <funcsynopsis>
+ <funcdef>extern "C" pid_t
+ <function>cygwin_winpid_to_pid</function>
+ </funcdef>
+ <paramdef>int <parameter>winpid</parameter></paramdef>
+ </funcsynopsis>
+
+ <para>Given a windows pid, converts to the corresponding Cygwin
+pid, if any. Returns -1 if windows pid does not correspond to
+a cygwin pid.</para>
+ <example>
+ <title>Example use of cygwin_winpid_to_pid</title>
+ <programlisting>
+ extern "C" cygwin_winpid_to_pid (int winpid);
+ pid_t mypid;
+ mypid = cygwin_winpid_to_pid (windows_pid);
+ </programlisting>
+ </example>
+</sect1>
+
+ DOCTOOL-END */
+
+extern "C" pid_t
+cygwin_winpid_to_pid (int winpid)
+{
+ pinfo p (cygwin_pid (winpid));
+ if (p)
+ return p->pid;
+
+ set_errno (ESRCH);
+ return (pid_t) -1;
+}
+
+#include <tlhelp32.h>
+
+#define slop_pidlist 200
+#define size_pidlist(i) (sizeof (pidlist[0]) * ((i) + 1))
+#define size_pinfolist(i) (sizeof (pinfolist[0]) * ((i) + 1))
+
+inline void
+winpids::add (DWORD& nelem, bool winpid, DWORD pid)
+{
+ pid_t cygpid = cygwin_pid (pid);
+ if (nelem >= npidlist)
+ {
+ npidlist += slop_pidlist;
+ pidlist = (DWORD *) realloc (pidlist, size_pidlist (npidlist + 1));
+ pinfolist = (pinfo *) realloc (pinfolist, size_pinfolist (npidlist + 1));
+ }
+
+ pinfolist[nelem].init (cygpid, PID_NOREDIR | (winpid ? PID_ALLPIDS : 0));
+ if (winpid)
+ /* nothing to do */;
+ else if (!pinfolist[nelem])
+ return;
+ else
+ /* Scan list of previously recorded pids to make sure that this pid hasn't
+ shown up before. This can happen when a process execs. */
+ for (unsigned i = 0; i < nelem; i++)
+ if (pinfolist[i]->pid == pinfolist[nelem]->pid)
+ {
+ if ((_pinfo *) pinfolist[nelem] != (_pinfo *) myself)
+ pinfolist[nelem].release ();
+ return;
+ }
+
+ pidlist[nelem++] = pid;
+}
+
+DWORD
+winpids::enumNT (bool winpid)
+{
+ static DWORD szprocs;
+ static SYSTEM_PROCESSES *procs;
+
+ DWORD nelem = 0;
+ if (!szprocs)
+ procs = (SYSTEM_PROCESSES *) malloc (sizeof (*procs) + (szprocs = 200 * sizeof (*procs)));
+
+ NTSTATUS res;
+ for (;;)
+ {
+ res = NtQuerySystemInformation (SystemProcessesAndThreadsInformation,
+ procs, szprocs, NULL);
+ if (res == 0)
+ break;
+
+ if (res == STATUS_INFO_LENGTH_MISMATCH)
+ procs = (SYSTEM_PROCESSES *) realloc (procs, szprocs += 200 * sizeof (*procs));
+ else
+ {
+ system_printf ("error %p reading system process information", res);
+ return 0;
+ }
+ }
+
+ SYSTEM_PROCESSES *px = procs;
+ for (;;)
+ {
+ if (px->ProcessId)
+ add (nelem, winpid, px->ProcessId);
+ if (!px->NextEntryDelta)
+ break;
+ px = (SYSTEM_PROCESSES *) ((char *) px + px->NextEntryDelta);
+ }
+
+ return nelem;
+}
+
+DWORD
+winpids::enum9x (bool winpid)
+{
+ DWORD nelem = 0;
+
+ HANDLE h = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS, 0);
+ if (!h)
+ {
+ system_printf ("Couldn't create process snapshot, %E");
+ return 0;
+ }
+
+ PROCESSENTRY32 proc;
+ proc.dwSize = sizeof (proc);
+
+ if (Process32First (h, &proc))
+ do
+ {
+ if (proc.th32ProcessID)
+ add (nelem, winpid, proc.th32ProcessID);
+ }
+ while (Process32Next (h, &proc));
+
+ CloseHandle (h);
+ return nelem;
+}
+
+NO_COPY CRITICAL_SECTION winpids::cs;
+
+void
+winpids::set (bool winpid)
+{
+ EnterCriticalSection (&cs);
+ npids = (this->*enum_processes) (winpid);
+ if (pidlist)
+ pidlist[npids] = 0;
+ LeaveCriticalSection (&cs);
+}
+
+void
+winpids::init ()
+{
+ InitializeCriticalSection (&cs);
+}
+
+DWORD
+winpids::enum_init (bool winpid)
+{
+ if (wincap.is_winnt ())
+ enum_processes = &winpids::enumNT;
+ else
+ enum_processes = &winpids::enum9x;
+
+ return (this->*enum_processes) (winpid);
+}
+
+void
+winpids::release ()
+{
+ for (unsigned i = 0; i < npids; i++)
+ if (pinfolist[i] && (_pinfo *) pinfolist[i] != (_pinfo *) myself)
+ pinfolist[i].release ();
+}
+
+winpids::~winpids ()
+{
+ if (npidlist)
+ {
+ release ();
+ free (pidlist);
+ free (pinfolist);
+ }
+}
diff --git a/winsup/cygwin/pinfo.h b/winsup/cygwin/pinfo.h
new file mode 100644
index 00000000000..3599015cc60
--- /dev/null
+++ b/winsup/cygwin/pinfo.h
@@ -0,0 +1,244 @@
+/* pinfo.h: process table info
+
+ Copyright 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifndef _PINFO_H
+#define _PINFO_H
+/* Signal constants (have to define them here, unfortunately) */
+
+enum
+{
+ __SIGFLUSH = -2,
+ __SIGSTRACE = -1,
+ __SIGCOMMUNE = 0,
+ __SIGOFFSET = 2
+};
+
+#define PSIZE 63
+
+#include <sys/resource.h>
+#include "thread.h"
+
+struct commune_result
+{
+ char *s;
+ int n;
+ HANDLE handles[2];
+};
+
+enum picom
+{
+ PICOM_CMDLINE = 1,
+ PICOM_FIFO = 2
+};
+
+class _pinfo
+{
+public:
+ /* Cygwin pid */
+ pid_t pid;
+
+ /* Various flags indicating the state of the process. See PID_
+ constants below. */
+ DWORD process_state;
+
+ /* If hProcess is set, it's because it came from a
+ CreateProcess call. This means it's process relative
+ to the thing which created the process. That's ok because
+ we only use this handle from the parent. */
+ HANDLE hProcess;
+
+#define PINFO_REDIR_SIZE ((char *) &myself.procinfo->hProcess - (char *) myself.procinfo)
+
+ /* Handle associated with initial Windows pid which started it all. */
+ HANDLE pid_handle;
+
+ /* Handle to the logical parent of this pid. */
+ HANDLE ppid_handle;
+
+ /* Parent process id. */
+ pid_t ppid;
+
+ /* dwProcessId contains the processid used for sending signals. It
+ * will be reset in a child process when it is capable of receiving
+ * signals.
+ */
+ DWORD dwProcessId;
+
+ /* Used to spawn a child for fork(), among other things. */
+ char progname[MAX_PATH];
+
+ /* User information.
+ The information is derived from the GetUserName system call,
+ with the name looked up in /etc/passwd and assigned a default value
+ if not found. This data resides in the shared data area (allowing
+ tasks to store whatever they want here) so it's for informational
+ purposes only. */
+ __uid32_t uid; /* User ID */
+ __gid32_t gid; /* Group ID */
+ pid_t pgid; /* Process group ID */
+ pid_t sid; /* Session ID */
+ int ctty; /* Control tty */
+ bool has_pgid_children;/* True if we've forked or spawned children with our GID. */
+
+ /* Resources used by process. */
+ long start_time;
+ struct rusage rusage_self;
+ struct rusage rusage_children;
+
+ /* Non-zero if process was stopped by a signal. */
+ char stopsig;
+
+ /* commune */
+ pid_t hello_pid;
+ HANDLE tothem;
+ HANDLE fromthem;
+
+ void exit (UINT n, bool norecord = 0) __attribute__ ((noreturn, regparm(2)));
+
+ inline void set_has_pgid_children ()
+ {
+ if (pgid == pid)
+ has_pgid_children = 1;
+ }
+
+ inline void set_has_pgid_children (bool val) {has_pgid_children = val;}
+
+ inline struct sigaction& getsig (int sig)
+ {
+ return thread2signal ? thread2signal->sigs[sig] : sigs[sig];
+ }
+
+ inline void copysigs (_pinfo *p) {memcpy (sigs, p->sigs, sizeof (sigs));}
+
+ inline sigset_t& getsigmask ()
+ {
+ return thread2signal ? *thread2signal->sigmask : sig_mask;
+ }
+
+ inline void setsigmask (sigset_t mask)
+ {
+ if (thread2signal)
+ *(thread2signal->sigmask) = mask;
+ sig_mask = mask;
+ }
+
+ inline LONG* getsigtodo (int sig) {return _sigtodo + __SIGOFFSET + sig;}
+
+ inline HANDLE getthread2signal ()
+ {
+ return thread2signal ? thread2signal->win32_obj_id : hMainThread;
+ }
+
+ inline void setthread2signal (void *thr) {thread2signal = (pthread *) thr;}
+ void commune_recv ();
+ commune_result commune_send (DWORD, ...);
+ bool alive ();
+ char *cmdline (size_t &);
+
+ friend void __stdcall set_myself (pid_t, HANDLE);
+
+private:
+ struct sigaction sigs[NSIG];
+ sigset_t sig_mask; /* one set for everything to ignore. */
+ LONG _sigtodo[NSIG + __SIGOFFSET];
+ pthread *thread2signal; // NULL means thread any other means a pthread
+ CRITICAL_SECTION lock;
+};
+
+class pinfo
+{
+ HANDLE h;
+ _pinfo *procinfo;
+ bool destroy;
+public:
+ void init (pid_t n, DWORD create = 0, HANDLE h = NULL) __attribute__ ((regparm(3)));
+ pinfo () {}
+ pinfo (_pinfo *x): procinfo (x) {}
+ pinfo (pid_t n) {init (n);}
+ pinfo (pid_t n, int create) {init (n, create);}
+ void release ();
+ ~pinfo ()
+ {
+ if (destroy && procinfo)
+ release ();
+ }
+
+ _pinfo *operator -> () const {return procinfo;}
+ int operator == (pinfo *x) const {return x->procinfo == procinfo;}
+ int operator == (pinfo &x) const {return x.procinfo == procinfo;}
+ int operator == (void *x) const {return procinfo == x;}
+ int operator == (int x) const {return (int) procinfo == (int) x;}
+ int operator == (char *x) const {return (char *) procinfo == x;}
+ _pinfo *operator * () const {return procinfo;}
+ operator _pinfo * () const {return procinfo;}
+ // operator bool () const {return (int) h;}
+#ifndef _SIGPROC_H
+ int remember () {system_printf ("remember is not here"); return 0;}
+#else
+ int remember ()
+ {
+ int res = proc_subproc (PROC_ADDCHILD, (DWORD) this);
+ destroy = res ? false : true;
+ return res;
+ }
+#endif
+ HANDLE shared_handle () {return h;}
+};
+
+#define ISSTATE(p, f) (!!((p)->process_state & f))
+#define NOTSTATE(p, f) (!((p)->process_state & f))
+
+class winpids
+{
+ DWORD *pidlist;
+ DWORD npidlist;
+ pinfo *pinfolist;
+ DWORD (winpids::* enum_processes) (bool winpid);
+ DWORD enum_init (bool winpid);
+ DWORD enumNT (bool winpid);
+ DWORD enum9x (bool winpid);
+ void add (DWORD& nelem, bool, DWORD pid);
+ static CRITICAL_SECTION cs;
+public:
+ DWORD npids;
+ inline void reset () { npids = 0; release (); }
+ void set (bool winpid);
+ winpids (int): enum_processes (&winpids::enum_init) { reset (); }
+ winpids (): pidlist (NULL), npidlist (0), pinfolist (NULL),
+ enum_processes (&winpids::enum_init), npids (0) { set (0); }
+ inline DWORD& winpid (int i) const {return pidlist[i];}
+ inline _pinfo *operator [] (int i) const {return (_pinfo *) pinfolist[i];}
+ ~winpids ();
+ void release ();
+ static void init ();
+};
+
+extern __inline pid_t
+cygwin_pid (pid_t pid)
+{
+ return (pid_t) (wincap.has_negative_pids ()) ? -(int) pid : pid;
+}
+
+void __stdcall pinfo_init (char **, int);
+void __stdcall set_myself (pid_t pid, HANDLE h = NULL);
+extern pinfo myself;
+
+#define _P_VFORK 0
+extern void __stdcall pinfo_fixup_after_fork ();
+extern HANDLE hexec_proc;
+
+/* For mmaps across fork(). */
+int __stdcall fixup_mmaps_after_fork (HANDLE parent);
+/* for shm areas across fork (). */
+int __stdcall fixup_shms_after_fork ();
+
+void __stdcall fill_rusage (struct rusage *, HANDLE);
+void __stdcall add_rusage (struct rusage *, struct rusage *);
+#endif /*_PINFO_H*/
diff --git a/winsup/cygwin/pipe.cc b/winsup/cygwin/pipe.cc
new file mode 100644
index 00000000000..76a3d43a942
--- /dev/null
+++ b/winsup/cygwin/pipe.cc
@@ -0,0 +1,289 @@
+/* pipe.cc: pipe for Cygwin.
+
+ Copyright 1996, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/* FIXME: Should this really be fhandler_pipe.cc? */
+
+#include "winsup.h"
+#include <unistd.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "thread.h"
+#include "pinfo.h"
+#include "cygthread.h"
+
+static unsigned pipecount;
+static const NO_COPY char pipeid_fmt[] = "stupid_pipe.%u.%u";
+
+fhandler_pipe::fhandler_pipe ()
+ : fhandler_base (), guard (NULL), broken_pipe (false), writepipe_exists(0),
+ orig_pid (0), id (0)
+{
+}
+
+__off64_t
+fhandler_pipe::lseek (__off64_t offset, int whence)
+{
+ debug_printf ("(%d, %d)", offset, whence);
+ set_errno (ESPIPE);
+ return -1;
+}
+
+void
+fhandler_pipe::set_close_on_exec (int val)
+{
+ this->fhandler_base::set_close_on_exec (val);
+ if (guard)
+ set_inheritance (guard, val);
+ if (writepipe_exists)
+ set_inheritance (writepipe_exists, val);
+}
+
+struct pipeargs
+{
+ fhandler_base *fh;
+ void *ptr;
+ size_t *len;
+};
+
+static DWORD WINAPI
+read_pipe (void *arg)
+{
+ pipeargs *pi = (pipeargs *) arg;
+ pi->fh->fhandler_base::read (pi->ptr, *pi->len);
+ return 0;
+}
+
+void __stdcall
+fhandler_pipe::read (void *in_ptr, size_t& in_len)
+{
+ if (broken_pipe)
+ in_len = 0;
+ else
+ {
+ pipeargs pi = {this, in_ptr, &in_len};
+ ResetEvent (read_state);
+ cygthread *th = new cygthread (read_pipe, &pi, "read_pipe");
+ if (th->detach (read_state) && !in_len)
+ (ssize_t) in_len = -1; /* received a signal */
+ }
+ (void) ReleaseMutex (guard);
+ return;
+}
+
+int
+fhandler_pipe::close ()
+{
+ if (guard)
+ CloseHandle (guard);
+ if (writepipe_exists)
+ CloseHandle (writepipe_exists);
+ if (read_state && !cygheap->fdtab.in_vfork_cleanup ())
+ CloseHandle (read_state);
+ if (get_handle ())
+ {
+ CloseHandle (get_handle ());
+ set_io_handle (NULL);
+ }
+ return 0;
+}
+
+bool
+fhandler_pipe::hit_eof ()
+{
+ char buf[80];
+ HANDLE ev;
+ if (broken_pipe)
+ return 1;
+ if (!orig_pid)
+ return false;
+ __small_sprintf (buf, pipeid_fmt, orig_pid, id);
+ if ((ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf)))
+ CloseHandle (ev);
+ debug_printf ("%s %p", buf, ev);
+ return ev == NULL;
+}
+
+void
+fhandler_pipe::fixup_after_exec (HANDLE parent)
+{
+ if (read_state)
+ read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+}
+
+void
+fhandler_pipe::fixup_after_fork (HANDLE parent)
+{
+ this->fhandler_base::fixup_after_fork (parent);
+ if (guard)
+ fork_fixup (parent, guard, "guard");
+ if (writepipe_exists)
+ fork_fixup (parent, writepipe_exists, "guard");
+ fixup_after_exec (parent);
+}
+
+int
+fhandler_pipe::dup (fhandler_base *child)
+{
+ int res = this->fhandler_base::dup (child);
+ if (res)
+ return res;
+
+ fhandler_pipe *ftp = (fhandler_pipe *) child;
+
+ /* FIXME: This leaks handles in the failing condition */
+ if (guard == NULL)
+ ftp->guard = NULL;
+ else if (!DuplicateHandle (hMainProc, guard, hMainProc, &ftp->guard, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ debug_printf ("couldn't duplicate guard %p, %E", guard);
+ return -1;
+ }
+
+ if (writepipe_exists == NULL)
+ ftp->writepipe_exists = NULL;
+ else if (!DuplicateHandle (hMainProc, writepipe_exists, hMainProc,
+ &ftp->writepipe_exists, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ debug_printf ("couldn't duplicate writepipe_exists %p, %E", writepipe_exists);
+ return -1;
+ }
+
+ if (read_state == NULL)
+ ftp->read_state = NULL;
+ else if (!DuplicateHandle (hMainProc, read_state, hMainProc,
+ &ftp->read_state, 0, 1,
+ DUPLICATE_SAME_ACCESS))
+ {
+ debug_printf ("couldn't duplicate read_state %p, %E", writepipe_exists);
+ return -1;
+ }
+
+ ftp->id = id;
+ ftp->orig_pid = orig_pid;
+ return 0;
+}
+
+int
+fhandler_pipe::create (fhandler_pipe *fhs[2], unsigned psize, int mode, bool fifo)
+{
+ HANDLE r, w;
+ SECURITY_ATTRIBUTES *sa = (mode & O_NOINHERIT) ? &sec_none_nih : &sec_none;
+ int res = -1;
+
+ if (!CreatePipe (&r, &w, sa, psize))
+ __seterrno ();
+ else
+ {
+ fhs[0] = (fhandler_pipe *) cygheap->fdtab.build_fhandler (-1, *piper_dev, "/dev/piper");
+ fhs[1] = (fhandler_pipe *) cygheap->fdtab.build_fhandler (-1, *pipew_dev, "/dev/pipew");
+
+ int binmode = mode & O_TEXT ?: O_BINARY;
+ fhs[0]->init (r, GENERIC_READ, binmode);
+ fhs[1]->init (w, GENERIC_WRITE, binmode);
+ if (mode & O_NOINHERIT)
+ {
+ fhs[0]->set_close_on_exec_flag (1);
+ fhs[1]->set_close_on_exec_flag (1);
+ }
+
+ fhs[0]->read_state = CreateEvent (&sec_none_nih, FALSE, FALSE, NULL);
+ fhs[0]->set_need_fork_fixup ();
+
+ res = 0;
+ fhs[0]->create_guard (sa);
+ if (wincap.has_unreliable_pipes ())
+ {
+ char buf[80];
+ int count = pipecount++; /* FIXME: Should this be InterlockedIncrement? */
+ __small_sprintf (buf, pipeid_fmt, myself->pid, count);
+ fhs[1]->writepipe_exists = CreateEvent (sa, TRUE, FALSE, buf);
+ fhs[0]->orig_pid = myself->pid;
+ fhs[0]->id = count;
+ }
+ }
+
+ syscall_printf ("%d = ([%p, %p], %d, %p)", res, fhs[0], fhs[1], psize, mode);
+ return res;
+}
+
+int
+fhandler_pipe::ioctl (unsigned int cmd, void *p)
+{
+ int n;
+
+ switch (cmd)
+ {
+ case FIONREAD:
+ if (get_device () == FH_PIPEW)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ if (!PeekNamedPipe (get_handle (), NULL, 0, NULL, (DWORD *) &n, NULL))
+ {
+ __seterrno ();
+ return -1;
+ }
+ break;
+ default:
+ return fhandler_base::ioctl (cmd, p);
+ break;
+ }
+ *(int *) p = n;
+ return 0;
+}
+
+extern "C" int
+pipe (int filedes[2])
+{
+ extern DWORD binmode;
+ fhandler_pipe *fhs[2];
+ int res = fhandler_pipe::create (fhs, 16384, (!binmode || binmode == O_BINARY)
+ ? O_BINARY : O_TEXT);
+ if (res == 0)
+ {
+ cygheap_fdnew fdin;
+ cygheap_fdnew fdout (fdin, false);
+ fdin = fhs[0];
+ fdout = fhs[1];
+ filedes[0] = fdin;
+ filedes[1] = fdout;
+ }
+
+ return res;
+}
+
+extern "C" int
+_pipe (int filedes[2], unsigned int psize, int mode)
+{
+ fhandler_pipe *fhs[2];
+ int res = fhandler_pipe::create (fhs, psize, mode);
+ /* This type of pipe is not interruptible so set the appropriate flag. */
+ if (!res)
+ {
+ cygheap_fdnew fdin;
+ cygheap_fdnew fdout (fdin, false);
+ fhs[0]->set_r_no_interrupt (1);
+ fdin = fhs[0];
+ fdout = fhs[1];
+ filedes[0] = fdin;
+ filedes[1] = fdout;
+ }
+
+ return res;
+}
diff --git a/winsup/cygwin/select.cc b/winsup/cygwin/select.cc
new file mode 100644
index 00000000000..442b8b89883
--- /dev/null
+++ b/winsup/cygwin/select.cc
@@ -0,0 +1,1505 @@
+/* select.cc
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002 Red Hat, Inc.
+
+ Written by Christopher Faylor of Cygnus Solutions
+ cgf@cygnus.com
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+/*
+ * The following line means that the BSD socket
+ * definitions for fd_set, FD_ISSET etc. are used in this
+ * file.
+ */
+
+#define __INSIDE_CYGWIN_NET__
+
+#include "winsup.h"
+#include <errno.h>
+#include <sys/socket.h>
+#include <stdlib.h>
+#include <sys/time.h>
+
+#include <wingdi.h>
+#include <winuser.h>
+#include <netdb.h>
+#include <unistd.h>
+#include <stdio.h>
+#define USE_SYS_TYPES_FD_SET
+#include <winsock.h>
+#include "select.h"
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include "perthread.h"
+#include "tty.h"
+#include "cygthread.h"
+
+/*
+ * All these defines below should be in sys/types.h
+ * but because of the includes above, they may not have
+ * been included. We create special UNIX_xxxx versions here.
+ */
+
+#ifndef NBBY
+#define NBBY 8 /* number of bits in a byte */
+#endif /* NBBY */
+
+/*
+ * Select uses bit masks of file descriptors in longs.
+ * These macros manipulate such bit fields (the filesystem macros use chars).
+ * FD_SETSIZE may be defined by the user, but the default here
+ * should be >= NOFILE (param.h).
+ */
+
+typedef long fd_mask;
+#define UNIX_NFDBITS (sizeof (fd_mask) * NBBY) /* bits per mask */
+#ifndef unix_howmany
+#define unix_howmany(x,y) (((x)+((y)-1))/(y))
+#endif
+
+#define unix_fd_set fd_set
+
+#define NULL_fd_set ((fd_set *) NULL)
+#define sizeof_fd_set(n) \
+ ((unsigned) (NULL_fd_set->fds_bits + unix_howmany ((n), UNIX_NFDBITS)))
+#define UNIX_FD_SET(n, p) \
+ ((p)->fds_bits[(n)/UNIX_NFDBITS] |= (1L << ((n) % UNIX_NFDBITS)))
+#define UNIX_FD_CLR(n, p) \
+ ((p)->fds_bits[(n)/UNIX_NFDBITS] &= ~(1L << ((n) % UNIX_NFDBITS)))
+#define UNIX_FD_ISSET(n, p) \
+ ((p)->fds_bits[(n)/UNIX_NFDBITS] & (1L << ((n) % UNIX_NFDBITS)))
+#define UNIX_FD_ZERO(p, n) \
+ bzero ((caddr_t)(p), sizeof_fd_set ((n)))
+
+#define allocfd_set(n) ((fd_set *) memset (alloca (sizeof_fd_set (n)), 0, sizeof_fd_set (n)))
+#define copyfd_set(to, from, n) memcpy (to, from, sizeof_fd_set (n));
+
+#define set_handle_or_return_if_not_open(h, s) \
+ h = (s)->fh->get_handle (); \
+ if (cygheap->fdtab.not_open ((s)->fd)) \
+ { \
+ (s)->saw_error = true; \
+ set_sig_errno (EBADF); \
+ return -1; \
+ } \
+
+/* The main select code.
+ */
+extern "C"
+int
+cygwin_select (int maxfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ struct timeval *to)
+{
+ select_stuff sel;
+ fd_set *dummy_readfds = allocfd_set (maxfds);
+ fd_set *dummy_writefds = allocfd_set (maxfds);
+ fd_set *dummy_exceptfds = allocfd_set (maxfds);
+ sigframe thisframe (mainthread);
+
+ select_printf ("%d, %p, %p, %p, %p", maxfds, readfds, writefds, exceptfds, to);
+
+ if (!readfds)
+ readfds = dummy_readfds;
+ if (!writefds)
+ writefds = dummy_writefds;
+ if (!exceptfds)
+ exceptfds = dummy_exceptfds;
+
+ for (int i = 0; i < maxfds; i++)
+ if (!sel.test_and_set (i, readfds, writefds, exceptfds))
+ {
+ select_printf ("aborting due to test_and_set error");
+ return -1; /* Invalid fd, maybe? */
+ }
+
+ /* Convert to milliseconds or INFINITE if to == NULL */
+ DWORD ms = to ? (to->tv_sec * 1000) + (to->tv_usec / 1000) : INFINITE;
+ if (ms == 0 && to->tv_usec)
+ ms = 1; /* At least 1 ms granularity */
+
+ if (to)
+ select_printf ("to->tv_sec %d, to->tv_usec %d, ms %d", to->tv_sec, to->tv_usec, ms);
+ else
+ select_printf ("to NULL, ms %x", ms);
+
+ select_printf ("sel.always_ready %d", sel.always_ready);
+
+ int timeout = 0;
+ /* Allocate some fd_set structures using the number of fds as a guide. */
+ fd_set *r = allocfd_set (maxfds);
+ fd_set *w = allocfd_set (maxfds);
+ fd_set *e = allocfd_set (maxfds);
+
+ /* Degenerate case. No fds to wait for. Just wait. */
+ if (sel.start.next == NULL)
+ {
+ if (WaitForSingleObject (signal_arrived, ms) == WAIT_OBJECT_0)
+ {
+ select_printf ("signal received");
+ set_sig_errno (EINTR);
+ return -1;
+ }
+ timeout = 1;
+ }
+ else if (sel.always_ready || ms == 0)
+ /* Don't bother waiting. */;
+ else if ((timeout = sel.wait (r, w, e, ms) < 0))
+ return -1; /* some kind of error */
+
+ sel.cleanup ();
+ copyfd_set (readfds, r, maxfds);
+ copyfd_set (writefds, w, maxfds);
+ copyfd_set (exceptfds, e, maxfds);
+ return timeout ? 0 : sel.poll (readfds, writefds, exceptfds);
+}
+
+/* Call cleanup functions for all inspected fds. Gets rid of any
+ executing threads. */
+void
+select_stuff::cleanup ()
+{
+ select_record *s = &start;
+
+ select_printf ("calling cleanup routines");
+ while ((s = s->next))
+ if (s->cleanup)
+ {
+ s->cleanup (s, this);
+ s->cleanup = NULL;
+ }
+}
+
+/* Destroy all storage associated with select stuff. */
+select_stuff::~select_stuff ()
+{
+ cleanup ();
+ select_record *s = &start;
+ select_record *snext = start.next;
+
+ select_printf ("deleting select records");
+ while ((s = snext))
+ {
+ snext = s->next;
+ delete s;
+ }
+}
+
+/* Add a record to the select chain */
+int
+select_stuff::test_and_set (int i, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds)
+{
+ select_record *s = NULL;
+ if (UNIX_FD_ISSET (i, readfds) && (s = cygheap->fdtab.select_read (i, s)) == NULL)
+ return 0; /* error */
+ if (UNIX_FD_ISSET (i, writefds) && (s = cygheap->fdtab.select_write (i, s)) == NULL)
+ return 0; /* error */
+ if (UNIX_FD_ISSET (i, exceptfds) && (s = cygheap->fdtab.select_except (i, s)) == NULL)
+ return 0; /* error */
+ if (s == NULL)
+ return 1; /* nothing to do */
+
+ if (s->read_ready || s->write_ready || s->except_ready)
+ always_ready = true;
+
+ if (s->windows_handle || s->windows_handle || s->windows_handle)
+ windows_used = true;
+
+ s->next = start.next;
+ start.next = s;
+ return 1;
+}
+
+/* The heart of select. Waits for an fd to do something interesting. */
+int
+select_stuff::wait (fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
+ DWORD ms)
+{
+ int wait_ret;
+ HANDLE w4[MAXIMUM_WAIT_OBJECTS];
+ select_record *s = &start;
+ int m = 0;
+ int res = 0;
+
+ w4[m++] = signal_arrived; /* Always wait for the arrival of a signal. */
+ /* Loop through the select chain, starting up anything appropriate and
+ counting the number of active fds. */
+ while ((s = s->next))
+ {
+ if (m > MAXIMUM_WAIT_OBJECTS)
+ {
+ set_sig_errno (EINVAL);
+ return -1;
+ }
+ if (!s->startup (s, this))
+ {
+ __seterrno ();
+ return -1;
+ }
+ if (s->h == NULL)
+ continue;
+ for (int i = 1; i < m; i++)
+ if (w4[i] == s->h)
+ goto next_while;
+ w4[m++] = s->h;
+ next_while:
+ continue;
+ }
+
+ DWORD start_time = GetTickCount (); /* Record the current time for later use. */
+
+ debug_printf ("m %d, ms %u", m, ms);
+ for (;;)
+ {
+ if (!windows_used)
+ wait_ret = WaitForMultipleObjects (m, w4, FALSE, ms);
+ else
+ wait_ret = MsgWaitForMultipleObjects (m, w4, FALSE, ms, QS_ALLINPUT);
+
+ switch (wait_ret)
+ {
+ case WAIT_OBJECT_0:
+ select_printf ("signal received");
+ set_sig_errno (EINTR);
+ return -1;
+ case WAIT_FAILED:
+ select_printf ("WaitForMultipleObjects failed");
+ __seterrno ();
+ return -1;
+ case WAIT_TIMEOUT:
+ select_printf ("timed out");
+ res = 1;
+ goto out;
+ }
+
+ select_printf ("woke up. wait_ret %d. verifying", wait_ret);
+ s = &start;
+ int gotone = FALSE;
+ /* Some types of object (e.g., consoles) wake up on "inappropriate" events
+ like mouse movements. The verify function will detect these situations.
+ If it returns false, then this wakeup was a false alarm and we should go
+ back to waiting. */
+ while ((s = s->next))
+ if (s->saw_error)
+ return -1; /* Somebody detected an error */
+ else if ((((wait_ret >= m && s->windows_handle) || s->h == w4[wait_ret])) &&
+ s->verify (s, readfds, writefds, exceptfds))
+ gotone = true;
+
+ select_printf ("gotone %d", gotone);
+ if (gotone)
+ goto out;
+
+ if (ms == INFINITE)
+ {
+ select_printf ("looping");
+ continue;
+ }
+ select_printf ("recalculating ms");
+
+ DWORD now = GetTickCount ();
+ if (now > (start_time + ms))
+ {
+ select_printf ("timed out after verification");
+ goto out;
+ }
+ ms -= (now - start_time);
+ start_time = now;
+ select_printf ("ms now %u", ms);
+ }
+
+out:
+ select_printf ("returning %d", res);
+ return res;
+}
+
+static int
+set_bits (select_record *me, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds)
+{
+ int ready = 0;
+ select_printf ("me %p, testing fd %d (%s)", me, me->fd, me->fh->get_name ());
+ if (me->read_selected && me->read_ready)
+ {
+ UNIX_FD_SET (me->fd, readfds);
+ ready++;
+ }
+ if (me->write_selected && me->write_ready)
+ {
+ UNIX_FD_SET (me->fd, writefds);
+ if (me->except_on_write && me->fh->get_device () == FH_SOCKET)
+ ((fhandler_socket *) me->fh)->set_connect_state (CONNECTED);
+ ready++;
+ }
+ if ((me->except_selected || me->except_on_write) && me->except_ready)
+ {
+ if (me->except_on_write) /* Only on sockets */
+ {
+ UNIX_FD_SET (me->fd, writefds);
+ if (me->fh->get_device () == FH_SOCKET)
+ ((fhandler_socket *) me->fh)->set_connect_state (CONNECTED);
+ }
+ if (me->except_selected)
+ UNIX_FD_SET (me->fd, exceptfds);
+ ready++;
+ }
+ select_printf ("ready %d", ready);
+ return ready;
+}
+
+/* Poll every fd in the select chain. Set appropriate fd in mask. */
+int
+select_stuff::poll (fd_set *readfds, fd_set *writefds, fd_set *exceptfds)
+{
+ int n = 0;
+ select_record *s = &start;
+ while ((s = s->next))
+ n += (!s->peek || s->peek (s, true)) ?
+ set_bits (s, readfds, writefds, exceptfds) : 0;
+ select_printf ("returning %d", n);
+ return n;
+}
+
+static int
+verify_true (select_record *, fd_set *, fd_set *, fd_set *)
+{
+ return 1;
+}
+
+static int
+verify_ok (select_record *me, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds)
+{
+ return set_bits (me, readfds, writefds, exceptfds);
+}
+
+static int
+no_startup (select_record *, select_stuff *)
+{
+ return 1;
+}
+
+static int
+no_verify (select_record *, fd_set *, fd_set *, fd_set *)
+{
+ return 0;
+}
+
+static int
+peek_pipe (select_record *s, bool from_select)
+{
+ int n = 0;
+ int gotone = 0;
+ fhandler_base *fh = s->fh;
+
+ HANDLE h;
+ set_handle_or_return_if_not_open (h, s);
+
+ /* pipes require a guard mutex to guard against the situation where multiple
+ readers are attempting to read from the same pipe. In this scenario, it
+ is possible for PeekNamedPipe to report available data to two readers but
+ only one will actually get the data. This will result in the other reader
+ entering fhandler_base::raw_read and blocking indefinitely in an interruptible
+ state. This causes things like "make -j2" to hang. So, for the non-select case
+ we use the pipe mutex, if it is available. */
+ HANDLE guard_mutex = from_select ? NULL : fh->get_guard ();
+
+ /* Don't perform complicated tests if we don't need to. */
+ if (!s->read_selected && !s->except_selected)
+ goto out;
+
+ if (s->read_selected)
+ {
+ if (s->read_ready)
+ {
+ select_printf ("already ready");
+ gotone = 1;
+ goto out;
+ }
+
+ switch (fh->get_device ())
+ {
+ case FH_PTYM:
+ case FH_TTYM:
+ if (((fhandler_pty_master *) fh)->need_nl)
+ {
+ gotone = s->read_ready = true;
+ goto out;
+ }
+ break;
+ default:
+ if (fh->get_readahead_valid ())
+ {
+ select_printf ("readahead");
+ gotone = s->read_ready = true;
+ goto out;
+ }
+ }
+
+ if (fh->bg_check (SIGTTIN) <= bg_eof)
+ {
+ gotone = s->read_ready = true;
+ goto out;
+ }
+ }
+
+ if (fh->get_device () == FH_PIPEW)
+ /* nothing */;
+ else if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL))
+ {
+ select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ());
+ n = -1;
+ }
+ else if (!n || !guard_mutex)
+ /* no guard mutex or nothing to read from the pipe. */;
+ else if (WaitForSingleObject (guard_mutex, 0) != WAIT_OBJECT_0)
+ {
+ select_printf ("%s, couldn't get mutex %p, %E", fh->get_name (),
+ guard_mutex);
+ n = 0;
+ }
+ else
+ {
+ /* Now that we have the mutex, make sure that no one else has snuck
+ in and grabbed the data that we originally saw. */
+ if (!PeekNamedPipe (h, NULL, 0, NULL, (DWORD *) &n, NULL))
+ {
+ select_printf ("%s, PeekNamedPipe failed, %E", fh->get_name ());
+ n = -1;
+ }
+ if (n <= 0)
+ ReleaseMutex (guard_mutex); /* Oops. We lost the race. */
+ }
+
+ if (n < 0)
+ {
+ fh->set_eof (); /* Flag that other end of pipe is gone */
+ select_printf ("%s, n %d", fh->get_name (), n);
+ if (s->except_selected)
+ gotone += s->except_ready = true;
+ if (s->read_selected)
+ gotone += s->read_ready = true;
+ }
+ if (n > 0 && s->read_selected)
+ {
+ select_printf ("%s, ready for read", fh->get_name ());
+ gotone += s->read_ready = true;
+ }
+ if (!gotone && s->fh->hit_eof ())
+ {
+ select_printf ("%s, saw EOF", fh->get_name ());
+ if (s->except_selected)
+ gotone = s->except_ready = true;
+ if (s->read_selected)
+ gotone += s->read_ready = true;
+ select_printf ("saw eof on '%s'", fh->get_name ());
+ }
+
+out:
+ return gotone || s->write_ready;
+}
+
+static int start_thread_pipe (select_record *me, select_stuff *stuff);
+
+struct pipeinf
+ {
+ cygthread *thread;
+ BOOL stop_thread_pipe;
+ select_record *start;
+ };
+
+static DWORD WINAPI
+thread_pipe (void *arg)
+{
+ pipeinf *pi = (pipeinf *) arg;
+ BOOL gotone = FALSE;
+
+ for (;;)
+ {
+ select_record *s = pi->start;
+ while ((s = s->next))
+ if (s->startup == start_thread_pipe)
+ {
+ if (peek_pipe (s, true))
+ gotone = true;
+ if (pi->stop_thread_pipe)
+ {
+ select_printf ("stopping");
+ goto out;
+ }
+ }
+ /* Paranoid check */
+ if (pi->stop_thread_pipe)
+ {
+ select_printf ("stopping from outer loop");
+ break;
+ }
+ if (gotone)
+ break;
+ Sleep (10);
+ }
+out:
+ return 0;
+}
+
+static int
+start_thread_pipe (select_record *me, select_stuff *stuff)
+{
+ if (stuff->device_specific_pipe)
+ {
+ me->h = *((pipeinf *) stuff->device_specific_pipe)->thread;
+ return 1;
+ }
+ pipeinf *pi = new pipeinf;
+ pi->start = &stuff->start;
+ pi->stop_thread_pipe = FALSE;
+ pi->thread = new cygthread (thread_pipe, (LPVOID) pi, "select_pipe");
+ me->h = *pi->thread;
+ if (!me->h)
+ return 0;
+ stuff->device_specific_pipe = (void *) pi;
+ return 1;
+}
+
+static void
+pipe_cleanup (select_record *, select_stuff *stuff)
+{
+ pipeinf *pi = (pipeinf *) stuff->device_specific_pipe;
+ if (pi && pi->thread)
+ {
+ pi->stop_thread_pipe = true;
+ pi->thread->detach ();
+ delete pi;
+ stuff->device_specific_pipe = NULL;
+ }
+}
+
+int
+fhandler_pipe::ready_for_read (int fd, DWORD howlong)
+{
+ if (!howlong)
+ return this->fhandler_base::ready_for_read (fd, howlong);
+
+ get_guard ();
+ return true;
+}
+
+select_record *
+fhandler_pipe::select_read (select_record *s)
+{
+ if (!s)
+ s = new select_record;
+ s->startup = start_thread_pipe;
+ s->peek = peek_pipe;
+ s->verify = verify_ok;
+ s->read_selected = true;
+ s->read_ready = false;
+ s->cleanup = pipe_cleanup;
+ return s;
+}
+
+select_record *
+fhandler_pipe::select_write (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = no_verify;
+ }
+ s->peek = peek_pipe;
+ s->write_selected = true;
+ s->write_ready = true;
+ return s;
+}
+
+select_record *
+fhandler_pipe::select_except (select_record *s)
+{
+ if (!s)
+ s = new select_record;
+ s->startup = start_thread_pipe;
+ s->peek = peek_pipe;
+ s->verify = verify_ok;
+ s->cleanup = pipe_cleanup;
+ s->except_selected = true;
+ s->except_ready = false;
+ return s;
+}
+
+static int
+peek_console (select_record *me, bool)
+{
+ extern const char * get_nonascii_key (INPUT_RECORD& input_rec, char *);
+ fhandler_console *fh = (fhandler_console *) me->fh;
+
+ if (!me->read_selected)
+ return me->write_ready;
+
+ if (fh->get_readahead_valid ())
+ {
+ select_printf ("readahead");
+ return me->read_ready = true;
+ }
+
+ if (me->read_ready)
+ {
+ select_printf ("already ready");
+ return 1;
+ }
+
+ INPUT_RECORD irec;
+ DWORD events_read;
+ HANDLE h;
+ char tmpbuf[17];
+ set_handle_or_return_if_not_open (h, me);
+
+ for (;;)
+ if (fh->bg_check (SIGTTIN) <= bg_eof)
+ return me->read_ready = true;
+ else if (!PeekConsoleInput (h, &irec, 1, &events_read) || !events_read)
+ break;
+ else
+ {
+ if (irec.EventType == KEY_EVENT)
+ {
+ if (irec.Event.KeyEvent.bKeyDown
+ && (irec.Event.KeyEvent.uChar.AsciiChar
+ || get_nonascii_key (irec, tmpbuf)))
+ return me->read_ready = true;
+ }
+ else
+ {
+ fh->send_winch_maybe ();
+ if (irec.EventType == MOUSE_EVENT
+ && fh->mouse_aware ()
+ && (irec.Event.MouseEvent.dwEventFlags == 0
+ || irec.Event.MouseEvent.dwEventFlags == DOUBLE_CLICK))
+ return me->read_ready = true;
+ }
+
+ /* Read and discard the event */
+ ReadConsoleInput (h, &irec, 1, &events_read);
+ }
+
+ return me->write_ready;
+}
+
+static int
+verify_console (select_record *me, fd_set *rfds, fd_set *wfds,
+ fd_set *efds)
+{
+ return peek_console (me, true);
+}
+
+
+select_record *
+fhandler_console::select_read (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_console;
+ set_cursor_maybe ();
+ }
+
+ s->peek = peek_console;
+ s->h = get_handle ();
+ s->read_selected = true;
+ s->read_ready = false;
+ return s;
+}
+
+select_record *
+fhandler_console::select_write (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = no_verify;
+ set_cursor_maybe ();
+ }
+
+ s->peek = peek_console;
+ s->write_selected = true;
+ s->write_ready = true;
+ return s;
+}
+
+select_record *
+fhandler_console::select_except (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = no_verify;
+ set_cursor_maybe ();
+ }
+
+ s->peek = peek_console;
+ s->except_selected = true;
+ s->except_ready = false;
+ return s;
+}
+
+select_record *
+fhandler_tty_common::select_read (select_record *s)
+{
+ return ((fhandler_pipe *) this)->fhandler_pipe::select_read (s);
+}
+
+select_record *
+fhandler_tty_common::select_write (select_record *s)
+{
+ return ((fhandler_pipe *) this)->fhandler_pipe::select_write (s);
+}
+
+select_record *
+fhandler_tty_common::select_except (select_record *s)
+{
+ return ((fhandler_pipe *) this)->fhandler_pipe::select_except (s);
+}
+
+static int
+verify_tty_slave (select_record *me, fd_set *readfds, fd_set *writefds,
+ fd_set *exceptfds)
+{
+ if (WaitForSingleObject (me->h, 0) == WAIT_OBJECT_0)
+ me->read_ready = true;
+ return set_bits (me, readfds, writefds, exceptfds);
+}
+
+select_record *
+fhandler_tty_slave::select_read (select_record *s)
+{
+ if (!s)
+ s = new select_record;
+ s->h = input_available_event;
+ s->startup = no_startup;
+ s->peek = peek_pipe;
+ s->verify = verify_tty_slave;
+ s->read_selected = true;
+ s->read_ready = false;
+ s->cleanup = NULL;
+ return s;
+}
+
+select_record *
+fhandler_dev_null::select_read (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = no_verify;
+ }
+ s->h = get_handle ();
+ s->read_selected = true;
+ s->read_ready = true;
+ return s;
+}
+
+select_record *
+fhandler_dev_null::select_write (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = no_verify;
+ }
+ s->h = get_handle ();
+ s->write_selected = true;
+ s->write_ready = true;
+ return s;
+}
+
+select_record *
+fhandler_dev_null::select_except (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = no_verify;
+ }
+ s->h = get_handle ();
+ s->except_selected = true;
+ s->except_ready = true;
+ return s;
+}
+
+static int start_thread_serial (select_record *me, select_stuff *stuff);
+
+struct serialinf
+ {
+ cygthread *thread;
+ BOOL stop_thread_serial;
+ select_record *start;
+ };
+
+static int
+peek_serial (select_record *s, bool)
+{
+ COMSTAT st;
+
+ fhandler_serial *fh = (fhandler_serial *) s->fh;
+
+ if (fh->get_readahead_valid () || fh->overlapped_armed < 0)
+ return s->read_ready = true;
+
+ select_printf ("fh->overlapped_armed %d", fh->overlapped_armed);
+
+ HANDLE h;
+ set_handle_or_return_if_not_open (h, s);
+ int ready = 0;
+
+ if (s->read_selected && s->read_ready || (s->write_selected && s->write_ready))
+ {
+ select_printf ("already ready");
+ ready = 1;
+ goto out;
+ }
+
+ (void) SetCommMask (h, EV_RXCHAR);
+
+ if (!fh->overlapped_armed)
+ {
+ COMSTAT st;
+
+ ResetEvent (fh->io_status.hEvent);
+
+ if (!ClearCommError (h, &fh->ev, &st))
+ {
+ debug_printf ("ClearCommError");
+ goto err;
+ }
+ else if (st.cbInQue)
+ return s->read_ready = true;
+ else if (WaitCommEvent (h, &fh->ev, &fh->io_status))
+ return s->read_ready = true;
+ else if (GetLastError () == ERROR_IO_PENDING)
+ fh->overlapped_armed = 1;
+ else
+ {
+ debug_printf ("WaitCommEvent");
+ goto err;
+ }
+ }
+
+ HANDLE w4[2];
+ DWORD to;
+
+ w4[0] = fh->io_status.hEvent;
+ w4[1] = signal_arrived;
+ to = 10;
+
+ switch (WaitForMultipleObjects (2, w4, FALSE, to))
+ {
+ case WAIT_OBJECT_0:
+ if (!ClearCommError (h, &fh->ev, &st))
+ {
+ debug_printf ("ClearCommError");
+ goto err;
+ }
+ else if (!st.cbInQue)
+ Sleep (to);
+ else
+ {
+ return s->read_ready = true;
+ select_printf ("got something");
+ }
+ break;
+ case WAIT_OBJECT_0 + 1:
+ select_printf ("interrupt");
+ set_sig_errno (EINTR);
+ ready = -1;
+ break;
+ case WAIT_TIMEOUT:
+ break;
+ default:
+ debug_printf ("WaitForMultipleObjects");
+ goto err;
+ }
+
+out:
+ return ready;
+
+err:
+ if (GetLastError () == ERROR_OPERATION_ABORTED)
+ {
+ select_printf ("operation aborted");
+ return ready;
+ }
+
+ __seterrno ();
+ s->saw_error = true;
+ select_printf ("error %E");
+ return -1;
+}
+
+static DWORD WINAPI
+thread_serial (void *arg)
+{
+ serialinf *si = (serialinf *) arg;
+ BOOL gotone= FALSE;
+
+ for (;;)
+ {
+ select_record *s = si->start;
+ while ((s = s->next))
+ if (s->startup == start_thread_serial)
+ {
+ if (peek_serial (s, true))
+ gotone = true;
+ }
+ if (si->stop_thread_serial)
+ {
+ select_printf ("stopping");
+ break;
+ }
+ if (gotone)
+ break;
+ }
+
+ select_printf ("exiting");
+ return 0;
+}
+
+static int
+start_thread_serial (select_record *me, select_stuff *stuff)
+{
+ if (stuff->device_specific_serial)
+ {
+ me->h = *((serialinf *) stuff->device_specific_serial)->thread;
+ return 1;
+ }
+ serialinf *si = new serialinf;
+ si->start = &stuff->start;
+ si->stop_thread_serial = FALSE;
+ si->thread = new cygthread (thread_serial, (LPVOID) si, "select_serial");
+ me->h = *si->thread;
+ stuff->device_specific_serial = (void *) si;
+ return 1;
+}
+
+static void
+serial_cleanup (select_record *, select_stuff *stuff)
+{
+ serialinf *si = (serialinf *) stuff->device_specific_serial;
+ if (si && si->thread)
+ {
+ si->stop_thread_serial = true;
+ si->thread->detach ();
+ delete si;
+ stuff->device_specific_serial = NULL;
+ }
+}
+
+select_record *
+fhandler_serial::select_read (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = start_thread_serial;
+ s->verify = verify_ok;
+ s->cleanup = serial_cleanup;
+ }
+ s->peek = peek_serial;
+ s->read_selected = true;
+ s->read_ready = false;
+ return s;
+}
+
+select_record *
+fhandler_serial::select_write (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_ok;
+ }
+ s->peek = peek_serial;
+ s->h = get_handle ();
+ s->write_selected = true;
+ s->write_ready = true;
+ return s;
+}
+
+select_record *
+fhandler_serial::select_except (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_ok;
+ }
+ s->h = NULL;
+ s->peek = peek_serial;
+ s->except_selected = false; // Can't do this
+ s->except_ready = false;
+ return s;
+}
+
+int
+fhandler_base::ready_for_read (int fd, DWORD howlong)
+{
+ int avail = 0;
+ select_record me (this);
+ me.fd = fd;
+ while (!avail)
+ {
+ (void) select_read (&me);
+ avail = me.read_ready ?: me.peek (&me, false);
+
+ if (fd >= 0 && cygheap->fdtab.not_open (fd))
+ {
+ set_sig_errno (EBADF);
+ avail = 0;
+ break;
+ }
+
+ if (howlong != INFINITE)
+ {
+ if (!avail)
+ set_sig_errno (EAGAIN);
+ break;
+ }
+
+ if (WaitForSingleObject (signal_arrived, avail ? 0 : 10) == WAIT_OBJECT_0)
+ {
+ set_sig_errno (EINTR);
+ avail = 0;
+ break;
+ }
+ }
+
+ if (get_guard () && !avail && me.read_ready)
+ ReleaseMutex (get_guard ());
+
+ select_printf ("read_ready %d, avail %d", me.read_ready, avail);
+ return avail;
+}
+
+select_record *
+fhandler_base::select_read (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_ok;
+ }
+ s->h = get_handle ();
+ s->read_selected = true;
+ s->read_ready = true;
+ return s;
+}
+
+select_record *
+fhandler_base::select_write (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_ok;
+ }
+ s->h = get_handle ();
+ s->write_selected = true;
+ s->write_ready = true;
+ return s;
+}
+
+select_record *
+fhandler_base::select_except (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_ok;
+ }
+ s->h = NULL;
+ s->except_selected = true;
+ s->except_ready = false;
+ return s;
+}
+
+struct socketinf
+ {
+ cygthread *thread;
+ winsock_fd_set readfds, writefds, exceptfds;
+ SOCKET exitsock;
+ struct sockaddr_in sin;
+ select_record *start;
+ };
+
+static int
+peek_socket (select_record *me, bool)
+{
+ winsock_fd_set ws_readfds, ws_writefds, ws_exceptfds;
+ struct timeval tv = {0, 0};
+ WINSOCK_FD_ZERO (&ws_readfds);
+ WINSOCK_FD_ZERO (&ws_writefds);
+ WINSOCK_FD_ZERO (&ws_exceptfds);
+
+ HANDLE h;
+ set_handle_or_return_if_not_open (h, me);
+ select_printf ("considering handle %p", h);
+
+ if (me->read_selected && !me->read_ready)
+ {
+ select_printf ("adding read fd_set %s, fd %d", me->fh->get_name (),
+ me->fd);
+ WINSOCK_FD_SET (h, &ws_readfds);
+ }
+ if (me->write_selected && !me->write_ready)
+ {
+ select_printf ("adding write fd_set %s, fd %d", me->fh->get_name (),
+ me->fd);
+ WINSOCK_FD_SET (h, &ws_writefds);
+ }
+ if ((me->except_selected || me->except_on_write) && !me->except_ready)
+ {
+ select_printf ("adding except fd_set %s, fd %d", me->fh->get_name (),
+ me->fd);
+ WINSOCK_FD_SET (h, &ws_exceptfds);
+ }
+ int r;
+ if ((me->read_selected && !me->read_ready)
+ || (me->write_selected && !me->write_ready)
+ || ((me->except_selected || me->except_on_write) && !me->except_ready))
+ {
+ r = WINSOCK_SELECT (0, &ws_readfds, &ws_writefds, &ws_exceptfds, &tv);
+ select_printf ("WINSOCK_SELECT returned %d", r);
+ if (r == -1)
+ {
+ select_printf ("error %d", WSAGetLastError ());
+ set_winsock_errno ();
+ return 0;
+ }
+ if (WINSOCK_FD_ISSET (h, &ws_readfds) || (me->read_selected && me->read_ready))
+ me->read_ready = true;
+ if (WINSOCK_FD_ISSET (h, &ws_writefds) || (me->write_selected && me->write_ready))
+ me->write_ready = true;
+ if (WINSOCK_FD_ISSET (h, &ws_exceptfds) || ((me->except_selected || me->except_on_write) && me->except_ready))
+ me->except_ready = true;
+ }
+ return me->read_ready || me->write_ready || me->except_ready;
+}
+
+static int start_thread_socket (select_record *, select_stuff *);
+
+static DWORD WINAPI
+thread_socket (void *arg)
+{
+ socketinf *si = (socketinf *) arg;
+
+ select_printf ("stuff_start %p", &si->start);
+ int r = WINSOCK_SELECT (0, &si->readfds, &si->writefds, &si->exceptfds, NULL);
+ select_printf ("Win32 select returned %d", r);
+ if (r == -1)
+ select_printf ("error %d", WSAGetLastError ());
+ select_record *s = si->start;
+ while ((s = s->next))
+ if (s->startup == start_thread_socket)
+ {
+ HANDLE h = s->fh->get_handle ();
+ select_printf ("s %p, testing fd %d (%s)", s, s->fd, s->fh->get_name ());
+ if (WINSOCK_FD_ISSET (h, &si->readfds))
+ {
+ select_printf ("read_ready");
+ s->read_ready = true;
+ }
+ if (WINSOCK_FD_ISSET (h, &si->writefds))
+ {
+ select_printf ("write_ready");
+ s->write_ready = true;
+ }
+ if (WINSOCK_FD_ISSET (h, &si->exceptfds))
+ {
+ select_printf ("except_ready");
+ s->except_ready = true;
+ }
+ }
+
+ if (WINSOCK_FD_ISSET (si->exitsock, &si->readfds))
+ select_printf ("saw exitsock read");
+
+ return 0;
+}
+
+extern "C" unsigned long htonl (unsigned long);
+
+static int
+start_thread_socket (select_record *me, select_stuff *stuff)
+{
+ socketinf *si;
+
+ if ((si = (socketinf *) stuff->device_specific_socket))
+ {
+ me->h = *si->thread;
+ return 1;
+ }
+
+ si = new socketinf;
+ WINSOCK_FD_ZERO (&si->readfds);
+ WINSOCK_FD_ZERO (&si->writefds);
+ WINSOCK_FD_ZERO (&si->exceptfds);
+ select_record *s = &stuff->start;
+ while ((s = s->next))
+ if (s->startup == start_thread_socket)
+ {
+ HANDLE h = s->fh->get_handle ();
+ select_printf ("Handle %p", h);
+ if (s->read_selected && !s->read_ready)
+ {
+ WINSOCK_FD_SET (h, &si->readfds);
+ select_printf ("Added to readfds");
+ }
+ if (s->write_selected && !s->write_ready)
+ {
+ WINSOCK_FD_SET (h, &si->writefds);
+ select_printf ("Added to writefds");
+ }
+ if ((s->except_selected || s->except_on_write) && !s->except_ready)
+ {
+ WINSOCK_FD_SET (h, &si->exceptfds);
+ select_printf ("Added to exceptfds");
+ }
+ }
+
+ if ((si->exitsock = socket (PF_INET, SOCK_STREAM, 0)) == INVALID_SOCKET)
+ {
+ set_winsock_errno ();
+ select_printf ("cannot create socket, %E");
+ return -1;
+ }
+ /* Allow rapid reuse of the port. */
+ int tmp = 1;
+ (void) setsockopt (si->exitsock, SOL_SOCKET, SO_REUSEADDR, (char *) &tmp, sizeof (tmp));
+
+ int sin_len = sizeof (si->sin);
+ memset (&si->sin, 0, sizeof (si->sin));
+ si->sin.sin_family = AF_INET;
+ si->sin.sin_addr.s_addr = htonl (INADDR_LOOPBACK);
+ if (bind (si->exitsock, (struct sockaddr *) &si->sin, sizeof (si->sin)) < 0)
+ {
+ select_printf ("cannot bind socket, %E");
+ goto err;
+ }
+
+ if (getsockname (si->exitsock, (struct sockaddr *) &si->sin, &sin_len) < 0)
+ {
+ select_printf ("getsockname error");
+ goto err;
+ }
+
+ if (listen (si->exitsock, 1))
+ {
+ select_printf ("listen failed, %E");
+ goto err;
+ }
+
+ select_printf ("exitsock %p", si->exitsock);
+ WINSOCK_FD_SET ((HANDLE) si->exitsock, &si->readfds);
+ WINSOCK_FD_SET ((HANDLE) si->exitsock, &si->exceptfds);
+ stuff->device_specific_socket = (void *) si;
+ si->start = &stuff->start;
+ select_printf ("stuff_start %p", &stuff->start);
+ si->thread = new cygthread (thread_socket, (LPVOID) si, "select_socket");
+ me->h = *si->thread;
+ return 1;
+
+err:
+ set_winsock_errno ();
+ closesocket (si->exitsock);
+ return -1;
+}
+
+void
+socket_cleanup (select_record *, select_stuff *stuff)
+{
+ socketinf *si = (socketinf *) stuff->device_specific_socket;
+ select_printf ("si %p si->thread %p", si, si ? si->thread : NULL);
+ if (si && si->thread)
+ {
+ select_printf ("connection to si->exitsock %p", si->exitsock);
+ SOCKET s = socket (AF_INET, SOCK_STREAM, 0);
+
+ /* Set LINGER with 0 timeout for hard close */
+ struct linger tmp = {1, 0}; /* On, 0 delay */
+ (void) setsockopt (s, SOL_SOCKET, SO_LINGER, (char *)&tmp, sizeof (tmp));
+ (void) setsockopt (si->exitsock, SOL_SOCKET, SO_LINGER, (char *)&tmp, sizeof (tmp));
+
+ /* Connecting to si->exitsock will cause any executing select to wake
+ up. When this happens then the exitsock condition will cause the
+ thread to terminate. */
+ if (connect (s, (struct sockaddr *) &si->sin, sizeof (si->sin)) < 0)
+ {
+ set_winsock_errno ();
+ select_printf ("connect failed");
+ /* FIXME: now what? */
+ }
+ shutdown (s, SD_BOTH);
+ closesocket (s);
+
+ /* Wait for thread to go away */
+ si->thread->detach ();
+ shutdown (si->exitsock, SD_BOTH);
+ closesocket (si->exitsock);
+ stuff->device_specific_socket = NULL;
+ delete si;
+ }
+ select_printf ("returning");
+}
+
+select_record *
+fhandler_socket::select_read (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = start_thread_socket;
+ s->verify = verify_true;
+ s->cleanup = socket_cleanup;
+ }
+ s->peek = peek_socket;
+ s->read_ready = saw_shutdown_read ();
+ s->read_selected = true;
+ return s;
+}
+
+select_record *
+fhandler_socket::select_write (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = start_thread_socket;
+ s->verify = verify_true;
+ s->cleanup = socket_cleanup;
+ }
+ s->peek = peek_socket;
+ s->write_ready = saw_shutdown_write () || is_unconnected ();
+ s->write_selected = true;
+ if (is_connect_pending ())
+ {
+ s->except_ready = saw_shutdown_write () || saw_shutdown_read ();
+ s->except_on_write = true;
+ }
+ return s;
+}
+
+select_record *
+fhandler_socket::select_except (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = start_thread_socket;
+ s->verify = verify_true;
+ s->cleanup = socket_cleanup;
+ }
+ s->peek = peek_socket;
+ /* FIXME: Is this right? Should these be used as criteria for except? */
+ s->except_ready = saw_shutdown_write () || saw_shutdown_read ();
+ s->except_selected = true;
+ return s;
+}
+
+static int
+peek_windows (select_record *me, bool)
+{
+ MSG m;
+ HANDLE h;
+ set_handle_or_return_if_not_open (h, me);
+
+ if (me->read_selected && me->read_ready)
+ return 1;
+
+ if (PeekMessage (&m, (HWND) h, 0, 0, PM_NOREMOVE))
+ {
+ me->read_ready = true;
+ select_printf ("window %d(%p) ready", me->fd, me->fh->get_handle ());
+ return 1;
+ }
+
+ select_printf ("window %d(%p) not ready", me->fd, me->fh->get_handle ());
+ return me->write_ready;
+}
+
+static int
+verify_windows (select_record *me, fd_set *rfds, fd_set *wfds,
+ fd_set *efds)
+{
+ return peek_windows (me, true);
+}
+
+select_record *
+fhandler_windows::select_read (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ }
+ s->verify = verify_windows;
+ s->peek = peek_windows;
+ s->read_selected = true;
+ s->read_ready = false;
+ s->h = get_handle ();
+ s->windows_handle = true;
+ return s;
+}
+
+select_record *
+fhandler_windows::select_write (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_ok;
+ }
+ s->peek = peek_windows;
+ s->h = get_handle ();
+ s->write_selected = true;
+ s->write_ready = true;
+ s->windows_handle = true;
+ return s;
+}
+
+select_record *
+fhandler_windows::select_except (select_record *s)
+{
+ if (!s)
+ {
+ s = new select_record;
+ s->startup = no_startup;
+ s->verify = verify_ok;
+ }
+ s->peek = peek_windows;
+ s->h = get_handle ();
+ s->except_selected = true;
+ s->except_ready = true;
+ s->windows_handle = true;
+ return s;
+}
diff --git a/winsup/cygwin/shared_info.h b/winsup/cygwin/shared_info.h
new file mode 100644
index 00000000000..77e57a279fe
--- /dev/null
+++ b/winsup/cygwin/shared_info.h
@@ -0,0 +1,191 @@
+/* shared_info.h: shared info for cygwin
+
+ Copyright 2000, 2001 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "tty.h"
+
+/* Mount table entry */
+
+class mount_item
+{
+ public:
+ /* FIXME: Nasty static allocation. Need to have a heap in the shared
+ area [with the user being able to configure at runtime the max size]. */
+ /* Win32-style mounted partition source ("C:\foo\bar").
+ native_path[0] == 0 for unused entries. */
+ char native_path[MAX_PATH];
+ int native_pathlen;
+
+ /* POSIX-style mount point ("/foo/bar") */
+ char posix_path[MAX_PATH];
+ int posix_pathlen;
+
+ unsigned flags;
+
+ void init (const char *dev, const char *path, unsigned flags);
+
+ struct mntent *getmntent ();
+};
+
+/* Warning: Decreasing this value will cause cygwin.dll to ignore existing
+ higher numbered registry entries. Don't change this number willy-nilly.
+ What we need is to have a more dynamic allocation scheme, but the current
+ scheme should be satisfactory for a long while yet. */
+#define MAX_MOUNTS 30
+
+#define MOUNT_VERSION 27 // increment when mount table changes and
+#define MOUNT_VERSION_MAGIC CYGWIN_VERSION_MAGIC (MOUNT_MAGIC, MOUNT_VERSION)
+#define CURR_MOUNT_MAGIC 0x6dd73a3fU
+#define MOUNT_INFO_CB 16488
+
+class reg_key;
+struct device;
+
+/* NOTE: Do not make gratuitous changes to the names or organization of the
+ below class. The layout is checksummed to determine compatibility between
+ different cygwin versions. */
+class mount_info
+{
+ public:
+ DWORD version;
+ unsigned cb;
+ DWORD sys_mount_table_counter;
+ int nmounts;
+ mount_item mount[MAX_MOUNTS];
+
+ /* cygdrive_prefix is used as the root of the path automatically
+ prepended to a path when the path has no associated mount.
+ cygdrive_flags are the default flags for the cygdrives. */
+ char cygdrive[MAX_PATH];
+ size_t cygdrive_len;
+ unsigned cygdrive_flags;
+ private:
+ int posix_sorted[MAX_MOUNTS];
+ int native_sorted[MAX_MOUNTS];
+
+ public:
+ /* Increment when setting up a reg_key if mounts area had to be
+ created so we know when we need to import old mount tables. */
+ int had_to_create_mount_areas;
+
+ void init ();
+ int add_item (const char *dev, const char *path, unsigned flags, int reg_p);
+ int del_item (const char *path, unsigned flags, int reg_p);
+
+ void from_registry ();
+ int add_reg_mount (const char * native_path, const char * posix_path,
+ unsigned mountflags);
+ int del_reg_mount (const char * posix_path, unsigned mountflags);
+
+ unsigned set_flags_from_win32_path (const char *path);
+ int conv_to_win32_path (const char *src_path, char *dst, device&,
+ unsigned *flags = NULL, bool no_normalize = 0);
+ int conv_to_posix_path (const char *src_path, char *posix_path,
+ int keep_rel_p);
+ struct mntent *getmntent (int x);
+
+ int write_cygdrive_info_to_registry (const char *cygdrive_prefix, unsigned flags);
+ int remove_cygdrive_info_from_registry (const char *cygdrive_prefix, unsigned flags);
+ int get_cygdrive_info (char *user, char *system, char* user_flags,
+ char* system_flags);
+
+ private:
+
+ void sort ();
+ void read_mounts (reg_key& r);
+ void mount_slash ();
+ void to_registry ();
+
+ int cygdrive_win32_path (const char *src, char *dst, int& unit);
+ void cygdrive_posix_path (const char *src, char *dst, int trailing_slash_p);
+ void read_cygdrive_info_from_registry ();
+};
+
+/******** Close-on-delete queue ********/
+
+/* First pass at a file deletion queue structure.
+
+ We can't keep this list in the per-process info, since
+ one process may open a file, and outlive a process which
+ wanted to unlink the file - and the data would go away.
+*/
+
+#define MAX_DELQUEUES_PENDING 100
+
+class delqueue_list
+{
+ char name[MAX_DELQUEUES_PENDING][MAX_PATH];
+ char inuse[MAX_DELQUEUES_PENDING];
+ int empty;
+
+public:
+ void init ();
+ void queue_file (const char *dosname);
+ void process_queue ();
+};
+
+/******** Shared Info ********/
+/* Data accessible to all tasks */
+
+#define SHARED_VERSION (unsigned)(cygwin_version.api_major << 8 | \
+ cygwin_version.api_minor)
+#define SHARED_VERSION_MAGIC CYGWIN_VERSION_MAGIC (SHARED_MAGIC, SHARED_VERSION)
+
+#define SHARED_INFO_CB 47112
+
+#define CURR_SHARED_MAGIC 0x359218a2U
+
+/* NOTE: Do not make gratuitous changes to the names or organization of the
+ below class. The layout is checksummed to determine compatibility between
+ different cygwin versions. */
+class shared_info
+{
+ DWORD version;
+ DWORD cb;
+ public:
+ unsigned heap_chunk;
+ DWORD sys_mount_table_counter;
+
+ tty_list tty;
+ delqueue_list delqueue;
+ void initialize ();
+ unsigned heap_chunk_size ();
+};
+
+extern shared_info *cygwin_shared;
+extern mount_info *mount_table;
+extern HANDLE cygwin_mount_h;
+
+enum shared_locations
+{
+ SH_CYGWIN_SHARED,
+ SH_MOUNT_TABLE,
+ SH_SHARED_CONSOLE,
+ SH_MYSELF,
+ SH_TOTAL_SIZE
+};
+void __stdcall memory_init ();
+
+#define shared_align_past(p) \
+ ((char *) (system_info.dwAllocationGranularity * \
+ (((DWORD) ((p) + 1) + system_info.dwAllocationGranularity - 1) / \
+ system_info.dwAllocationGranularity)))
+
+#define cygwin_shared_address ((void *) 0xa000000)
+
+#ifdef FHDEVN
+struct console_state
+{
+ tty_min tty_min_state;
+ dev_console dev_state;
+};
+#endif
+
+char *__stdcall shared_name (const char *, int);
+void *__stdcall open_shared (const char *name, int n, HANDLE &shared_h, DWORD size, shared_locations);
diff --git a/winsup/cygwin/smallprint.c b/winsup/cygwin/smallprint.c
new file mode 100644
index 00000000000..ec31afa9cbf
--- /dev/null
+++ b/winsup/cygwin/smallprint.c
@@ -0,0 +1,243 @@
+/* smallprint.c: small print routines for WIN32
+
+ Copyright 1996, 1998, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+int __small_sprintf (char *dst, const char *fmt, ...);
+int __small_vsprintf (char *dst, const char *fmt, va_list ap);
+
+static char *
+rn (char *dst, int base, int dosign, long long val, int len, int pad)
+{
+ /* longest number is ULLONG_MAX, 18446744073709551615, 20 digits */
+ unsigned uval;
+ char res[20];
+ static const char str[16] = "0123456789ABCDEF";
+ int l = 0;
+
+ if (dosign && val < 0)
+ {
+ *dst++ = '-';
+ uval = -val;
+ }
+ else if (dosign > 0 && val > 0)
+ {
+ *dst++ = '+';
+ uval = val;
+ }
+ else
+ {
+ uval = val;
+ }
+
+ do
+ {
+ res[l++] = str[uval % base];
+ uval /= base;
+ }
+ while (uval);
+
+ while (len -- > l)
+ *dst++ = pad;
+
+ while (l > 0)
+ {
+ *dst++ = res[--l];
+ }
+
+ return dst;
+}
+
+int
+__small_vsprintf (char *dst, const char *fmt, va_list ap)
+{
+ char tmp[MAX_PATH + 1];
+ char *orig = dst;
+ const char *s;
+
+ while (*fmt)
+ {
+ int i, n = 0x7fff;
+ if (*fmt != '%')
+ *dst++ = *fmt++;
+ else
+ {
+ int len = 0;
+ char pad = ' ';
+ int addsign = -1;
+
+ switch (*++fmt)
+ {
+ case '+':
+ addsign = 1;
+ fmt++;
+ break;
+ case '%':
+ *dst++ = *fmt++;
+ continue;
+ }
+
+ for (;;)
+ {
+ char c = *fmt++;
+ switch (c)
+ {
+ case '0':
+ if (len == 0)
+ {
+ pad = '0';
+ continue;
+ }
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ len = len * 10 + (c - '0');
+ continue;
+ case 'l':
+ continue;
+ case 'c':
+ {
+ int c = va_arg (ap, int);
+ if (c > ' ' && c <= 127)
+ *dst++ = c;
+ else
+ {
+ *dst++ = '0';
+ *dst++ = 'x';
+ dst = rn (dst, 16, 0, c, len, pad);
+ }
+ }
+ break;
+ case 'E':
+ strcpy (dst, "Win32 error ");
+ dst = rn (dst + sizeof ("Win32 error"), 10, 0, GetLastError (), len, pad);
+ break;
+ case 'd':
+ dst = rn (dst, 10, addsign, va_arg (ap, int), len, pad);
+ break;
+ case 'D':
+ dst = rn (dst, 10, addsign, va_arg (ap, long long), len, pad);
+ break;
+ case 'u':
+ dst = rn (dst, 10, 0, va_arg (ap, int), len, pad);
+ break;
+ case 'U':
+ dst = rn (dst, 10, 0, va_arg (ap, long long), len, pad);
+ break;
+ case 'o':
+ dst = rn (dst, 8, 0, va_arg (ap, unsigned), len, pad);
+ break;
+ case 'p':
+ *dst++ = '0';
+ *dst++ = 'x';
+ /* fall through */
+ case 'x':
+ dst = rn (dst, 16, 0, va_arg (ap, int), len, pad);
+ break;
+ case 'X':
+ dst = rn (dst, 16, 0, va_arg (ap, long long), len, pad);
+ break;
+ case 'P':
+ if (!GetModuleFileName (NULL, tmp, MAX_PATH))
+ s = "cygwin program";
+ else
+ s = tmp;
+ goto fillin;
+ case '.':
+ n = strtol (fmt, (char **)&fmt, 10);
+ if (*fmt++ != 's')
+ goto endfor;
+ case 's':
+ s = va_arg (ap, char *);
+ if (s == NULL)
+ s = "(null)";
+ fillin:
+ for (i = 0; *s && i < n; i++)
+ *dst++ = *s++;
+ break;
+ default:
+ *dst++ = '?';
+ *dst++ = fmt[-1];
+ }
+ endfor:
+ break;
+ }
+ }
+ }
+ *dst = 0;
+ return dst - orig;
+}
+
+int
+__small_sprintf (char *dst, const char *fmt, ...)
+{
+ int r;
+ va_list ap;
+ va_start (ap, fmt);
+ r = __small_vsprintf (dst, fmt, ap);
+ va_end (ap);
+ return r;
+}
+
+void
+small_printf (const char *fmt, ...)
+{
+ char buf[16384];
+ va_list ap;
+ DWORD done;
+ int count;
+
+#if 0 /* Turn on to force console errors */
+ extern SECURITY_ATTRIBUTES sec_none;
+ HANDLE h = CreateFileA ("CONOUT$", GENERIC_READ|GENERIC_WRITE,
+ FILE_SHARE_WRITE | FILE_SHARE_WRITE, &sec_none,
+ OPEN_EXISTING, 0, 0);
+ if (h)
+ SetStdHandle (STD_ERROR_HANDLE, h);
+#endif
+
+ va_start (ap, fmt);
+ count = __small_vsprintf (buf, fmt, ap);
+ va_end (ap);
+
+ WriteFile (GetStdHandle (STD_ERROR_HANDLE), buf, count, &done, NULL);
+ FlushFileBuffers (GetStdHandle (STD_ERROR_HANDLE));
+}
+
+#ifdef DEBUGGING
+static HANDLE NO_COPY console_handle = NULL;
+void
+console_printf (const char *fmt, ...)
+{
+ char buf[16384];
+ va_list ap;
+ DWORD done;
+ int count;
+
+ if (!console_handle)
+ console_handle = CreateFileA ("CON", GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, 0, 0);
+
+ if (console_handle == INVALID_HANDLE_VALUE)
+ console_handle = GetStdHandle (STD_ERROR_HANDLE);
+
+ va_start (ap, fmt);
+ count = __small_vsprintf (buf, fmt, ap);
+ va_end (ap);
+
+ WriteFile (console_handle, buf, count, &done, NULL);
+ FlushFileBuffers (console_handle);
+}
+#endif
diff --git a/winsup/cygwin/syscalls.cc b/winsup/cygwin/syscalls.cc
new file mode 100644
index 00000000000..1c196384fbb
--- /dev/null
+++ b/winsup/cygwin/syscalls.cc
@@ -0,0 +1,2662 @@
+/* syscalls.cc: syscalls
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#define _close __FOO_close__
+#define _lseek __FOO_lseek__
+#define _open __FOO_open__
+#define _read __FOO_read__
+#define _write __FOO_write__
+
+#include "winsup.h"
+#include <sys/stat.h>
+#include <sys/vfs.h> /* needed for statfs */
+#include <pwd.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <process.h>
+#include <utmp.h>
+#include <sys/uio.h>
+#include <errno.h>
+#include <limits.h>
+#include <winnls.h>
+#include <wininet.h>
+#include <lmcons.h> /* for UNLEN */
+#include <cygwin/version.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "perprocess.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "sigproc.h"
+#include "pinfo.h"
+#include <unistd.h>
+#include "shared_info.h"
+#include "cygheap.h"
+#define NEED_VFORK
+#include <setjmp.h>
+#include "perthread.h"
+#include "pwdgrp.h"
+
+#undef _close
+#undef _lseek
+#undef _open
+#undef _read
+#undef _write
+
+SYSTEM_INFO system_info;
+
+static int __stdcall mknod_worker (const char *, mode_t, mode_t, _major_t,
+ _minor_t);
+
+/* Close all files and process any queued deletions.
+ Lots of unix style applications will open a tmp file, unlink it,
+ but never call close. This function is called by _exit to
+ ensure we don't leave any such files lying around. */
+
+void __stdcall
+close_all_files (void)
+{
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files");
+
+ fhandler_base *fh;
+ for (int i = 0; i < (int) cygheap->fdtab.size; i++)
+ if ((fh = cygheap->fdtab[i]) != NULL)
+ {
+ fh->close ();
+ cygheap->fdtab.release (i);
+ }
+
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK | READ_LOCK, "close_all_files");
+ cygwin_shared->delqueue.process_queue ();
+}
+
+BOOL __stdcall
+check_pty_fds (void)
+{
+ int res = FALSE;
+ SetResourceLock (LOCK_FD_LIST, WRITE_LOCK, "check_pty_fds");
+ fhandler_base *fh;
+ for (int i = 0; i < (int) cygheap->fdtab.size; i++)
+ if ((fh = cygheap->fdtab[i]) != NULL &&
+ (fh->get_device () == FH_TTYS || fh->get_device () == FH_PTYM))
+ {
+ res = TRUE;
+ break;
+ }
+ ReleaseResourceLock (LOCK_FD_LIST, WRITE_LOCK, "check_pty_fds");
+ return res;
+}
+
+int
+dup (int fd)
+{
+ return cygheap->fdtab.dup2 (fd, cygheap_fdnew ());
+}
+
+int
+dup2 (int oldfd, int newfd)
+{
+ return cygheap->fdtab.dup2 (oldfd, newfd);
+}
+
+extern "C" int
+unlink (const char *ourname)
+{
+ int res = -1;
+ DWORD devn;
+ sigframe thisframe (mainthread);
+
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ goto done;
+ }
+
+ if ((devn = win32_name.get_devn ()) == FH_PROC || devn == FH_REGISTRY
+ || devn == FH_PROCESS)
+ {
+ set_errno (EROFS);
+ goto done;
+ }
+
+ syscall_printf ("_unlink (%s)", win32_name.get_win32 ());
+
+ if (!win32_name.exists ())
+ {
+ syscall_printf ("unlinking a nonexistent file");
+ set_errno (ENOENT);
+ goto done;
+ }
+ else if (win32_name.isdir ())
+ {
+ syscall_printf ("unlinking a directory");
+ set_errno (EPERM);
+ goto done;
+ }
+
+ /* Windows won't check the directory mode, so we do that ourselves. */
+ if (!writable_directory (win32_name))
+ {
+ syscall_printf ("non-writable directory");
+ goto done;
+ }
+
+ /* Check for shortcut as symlink condition. */
+ if (win32_name.has_attribute (FILE_ATTRIBUTE_READONLY))
+ {
+ int len = strlen (win32_name);
+ if (len > 4 && strcasematch ((char *) win32_name + len - 4, ".lnk"))
+ SetFileAttributes (win32_name, (DWORD) win32_name & ~FILE_ATTRIBUTE_READONLY);
+ }
+
+ DWORD lasterr;
+ lasterr = 0;
+ for (int i = 0; i < 2; i++)
+ {
+ if (DeleteFile (win32_name))
+ {
+ syscall_printf ("DeleteFile succeeded");
+ goto ok;
+ }
+
+ lasterr = GetLastError ();
+ if (i || lasterr != ERROR_ACCESS_DENIED || win32_name.issymlink ())
+ break; /* Couldn't delete it. */
+
+ /* if access denied, chmod to be writable, in case it is not,
+ and try again */
+ (void) chmod (win32_name, 0777);
+ }
+
+ /* Windows 9x seems to report ERROR_ACCESS_DENIED rather than sharing
+ violation. So, set lasterr to ERROR_SHARING_VIOLATION in this case
+ to simplify tests. */
+ if (wincap.access_denied_on_delete () && lasterr == ERROR_ACCESS_DENIED
+ && !win32_name.isremote ())
+ lasterr = ERROR_SHARING_VIOLATION;
+
+ /* Tried to delete file by normal DeleteFile and by resetting protection
+ and then deleting. That didn't work.
+
+ There are two possible reasons for this: 1) The file may be opened and
+ Windows is not allowing it to be deleted, or 2) We may not have permissions
+ to delete the file.
+
+ So, first assume that it may be 1) and try to remove the file using the
+ Windows FILE_FLAG_DELETE_ON_CLOSE semantics. This seems to work only
+ spottily on Windows 9x/Me but it does seem to work reliably on NT as
+ long as the file doesn't exist on a remote drive. */
+
+ bool delete_on_close_ok;
+
+ delete_on_close_ok = !win32_name.isremote ()
+ && wincap.has_delete_on_close ();
+
+ /* Attempt to use "delete on close" semantics to handle removing
+ a file which may be open. */
+ HANDLE h;
+ h = CreateFile (win32_name, GENERIC_READ, FILE_SHARE_READ, &sec_none_nih,
+ OPEN_EXISTING, FILE_FLAG_DELETE_ON_CLOSE, 0);
+ if (h == INVALID_HANDLE_VALUE)
+ {
+ if (GetLastError () == ERROR_FILE_NOT_FOUND)
+ goto ok;
+ }
+ else
+ {
+ CloseHandle (h);
+ syscall_printf ("CreateFile/CloseHandle succeeded");
+ /* Everything is fine if the file has disappeared or if we know that the
+ FILE_FLAG_DELETE_ON_CLOSE will eventually work. */
+ if (GetFileAttributes (win32_name) == INVALID_FILE_ATTRIBUTES
+ || delete_on_close_ok)
+ goto ok; /* The file is either gone already or will eventually be
+ deleted by the OS. */
+ }
+
+ /* FILE_FLAGS_DELETE_ON_CLOSE was a bust. If this is a sharing
+ violation, then queue the file for deletion when the process
+ exits. Otherwise, punt. */
+ if (lasterr != ERROR_SHARING_VIOLATION)
+ goto err;
+
+ syscall_printf ("couldn't delete file, err %d", lasterr);
+
+ /* Add file to the "to be deleted" queue. */
+ cygwin_shared->delqueue.queue_file (win32_name);
+
+ /* Success condition. */
+ ok:
+ res = 0;
+ goto done;
+
+ /* Error condition. */
+ err:
+ __seterrno ();
+ res = -1;
+
+ done:
+ syscall_printf ("%d = unlink (%s)", res, ourname);
+ return res;
+}
+
+extern "C" int
+remove (const char *ourname)
+{
+ path_conv win32_name (ourname, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (win32_name.error)
+ {
+ set_errno (win32_name.error);
+ syscall_printf ("-1 = remove (%s)", ourname);
+ return -1;
+ }
+
+ return win32_name.isdir () ? rmdir (ourname) : unlink (ourname);
+}
+
+extern "C" pid_t
+getpid ()
+{
+ return myself->pid;
+}
+
+extern "C" pid_t
+_getpid_r (struct _reent *)
+{
+ return getpid ();
+}
+
+/* getppid: POSIX 4.1.1.1 */
+extern "C" pid_t
+getppid ()
+{
+ return myself->ppid;
+}
+
+/* setsid: POSIX 4.3.2.1 */
+extern "C" pid_t
+setsid (void)
+{
+ vfork_save *vf = vfork_storage.val ();
+ /* This is a horrible, horrible kludge */
+ if (vf && vf->pid < 0)
+ {
+ pid_t pid = fork ();
+ if (pid > 0)
+ {
+ syscall_printf ("longjmping due to vfork");
+ vf->restore_pid (pid);
+ }
+ /* assuming that fork was successful */
+ }
+
+ if (myself->pgid != myself->pid)
+ {
+ if (myself->ctty == TTY_CONSOLE
+ && !cygheap->fdtab.has_console_fds ()
+ && !check_pty_fds ())
+ FreeConsole ();
+ myself->ctty = -1;
+ myself->sid = getpid ();
+ myself->pgid = getpid ();
+ syscall_printf ("sid %d, pgid %d, ctty %d", myself->sid, myself->pgid, myself->ctty);
+ return myself->sid;
+ }
+
+ set_errno (EPERM);
+ return -1;
+}
+
+extern "C" pid_t
+getsid (pid_t pid)
+{
+ pid_t res;
+ if (!pid)
+ res = myself->sid;
+ else
+ {
+ pinfo p (pid);
+ if (p)
+ res = p->sid;
+ else
+ {
+ set_errno (ESRCH);
+ res = -1;
+ }
+ }
+ return res;
+}
+
+extern "C" ssize_t
+read (int fd, void *ptr, size_t len)
+{
+ const iovec iov =
+ {
+ iov_base: ptr,
+ iov_len: len
+ };
+
+ return readv (fd, &iov, 1);
+}
+
+extern "C" ssize_t _read (int, void *, size_t)
+ __attribute__ ((alias ("read")));
+
+extern "C" ssize_t
+write (int fd, const void *ptr, size_t len)
+{
+ const struct iovec iov =
+ {
+ iov_base: (void *) ptr, // const_cast
+ iov_len: len
+ };
+
+ return writev (fd, &iov, 1);
+}
+
+extern "C" ssize_t _write (int fd, const void *ptr, size_t len)
+ __attribute__ ((alias ("write")));
+
+extern "C" ssize_t
+readv (int fd, const struct iovec *const iov, const int iovcnt)
+{
+ extern int sigcatchers;
+ const int e = get_errno ();
+
+ int res = -1;
+
+ const ssize_t tot = check_iovec_for_read (iov, iovcnt);
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ while (1)
+ {
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ break;
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_WRONLY)
+ {
+ set_errno (EBADF);
+ break;
+ }
+
+ DWORD wait = cfd->is_nonblocking () ? 0 : INFINITE;
+
+ /* Could block, so let user know we at least got here. */
+ syscall_printf ("readv (%d, %p, %d) %sblocking, sigcatchers %d",
+ fd, iov, iovcnt, wait ? "" : "non", sigcatchers);
+
+ if (wait && (!cfd->is_slow () || cfd->get_r_no_interrupt ()))
+ debug_printf ("no need to call ready_for_read");
+ else if (!cfd->ready_for_read (fd, wait))
+ {
+ res = -1;
+ goto out;
+ }
+
+ /* FIXME: This is not thread safe. We need some method to
+ ensure that an fd, closed in another thread, aborts I/O
+ operations. */
+ if (!cfd.isopen ())
+ break;
+
+ /* Check to see if this is a background read from a "tty",
+ sending a SIGTTIN, if appropriate */
+ res = cfd->bg_check (SIGTTIN);
+
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+
+ if (res > bg_eof)
+ {
+ myself->process_state |= PID_TTYIN;
+ if (!cfd.isopen ())
+ {
+ res = -1;
+ break;
+ }
+ res = cfd->readv (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYIN;
+ }
+
+ out:
+ if (res >= 0 || get_errno () != EINTR || !thisframe.call_signal_handler ())
+ break;
+ set_errno (e);
+ }
+
+done:
+ syscall_printf ("%d = readv (%d, %p, %d), errno %d", res, fd, iov, iovcnt,
+ get_errno ());
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" ssize_t
+writev (const int fd, const struct iovec *const iov, const int iovcnt)
+{
+ int res = -1;
+ sig_dispatch_pending (0);
+ const ssize_t tot = check_iovec_for_write (iov, iovcnt);
+
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ goto done;
+
+ if (tot <= 0)
+ {
+ res = tot;
+ goto done;
+ }
+
+ if ((cfd->get_flags () & O_ACCMODE) == O_RDONLY)
+ {
+ set_errno (EBADF);
+ goto done;
+ }
+
+ /* Could block, so let user know we at least got here. */
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+ else
+ syscall_printf ("writev (%d, %p, %d)", fd, iov, iovcnt);
+
+ res = cfd->bg_check (SIGTTOU);
+
+ if (res > bg_eof)
+ {
+ myself->process_state |= PID_TTYOU;
+ res = cfd->writev (iov, iovcnt, tot);
+ myself->process_state &= ~PID_TTYOU;
+ }
+
+done:
+ if (fd == 1 || fd == 2)
+ paranoid_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+ else
+ syscall_printf ("%d = write (%d, %p, %d), errno %d",
+ res, fd, iov, iovcnt, get_errno ());
+
+ MALLOC_CHECK;
+ return res;
+}
+
+/* _open */
+/* newlib's fcntl.h defines _open as taking variable args so we must
+ correspond. The third arg if it exists is: mode_t mode. */
+extern "C" int
+open (const char *unix_path, int flags, ...)
+{
+ int res = -1;
+ va_list ap;
+ mode_t mode = 0;
+ sig_dispatch_pending (0);
+ sigframe thisframe (mainthread);
+
+ syscall_printf ("open (%s, %p)", unix_path, flags);
+ if (!check_null_empty_str_errno (unix_path))
+ {
+ /* check for optional mode argument */
+ va_start (ap, flags);
+ mode = va_arg (ap, mode_t);
+ va_end (ap);
+
+ fhandler_base *fh;
+ cygheap_fdnew fd;
+
+ if (fd >= 0)
+ {
+ path_conv pc;
+ if (!(fh = cygheap->fdtab.build_fhandler_from_name (fd, unix_path,
+ NULL, pc)))
+ res = -1; // errno already set
+ else if (fh->is_fs_special () && fh->device_access_denied (flags))
+ {
+ fd.release ();
+ res = -1;
+ }
+ else if (!fh->open (&pc, flags, (mode & 07777) & ~cygheap->umask))
+ {
+ fd.release ();
+ res = -1;
+ }
+ else if ((res = fd) <= 2)
+ set_std_handle (res);
+ }
+ }
+
+ syscall_printf ("%d = open (%s, %p)", res, unix_path, flags);
+ return res;
+}
+
+extern "C" int _open (const char *, int flags, ...)
+ __attribute__ ((alias ("open")));
+
+extern "C" __off64_t
+lseek64 (int fd, __off64_t pos, int dir)
+{
+ __off64_t res;
+ sigframe thisframe (mainthread);
+
+ if (dir != SEEK_SET && dir != SEEK_CUR && dir != SEEK_END)
+ {
+ set_errno (EINVAL);
+ res = -1;
+ }
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = cfd->lseek (pos, dir);
+ else
+ res = -1;
+ }
+ syscall_printf ("%d = lseek (%d, %D, %d)", res, fd, pos, dir);
+
+ return res;
+}
+
+extern "C" __off32_t
+lseek (int fd, __off32_t pos, int dir)
+{
+ return lseek64 (fd, (__off64_t) pos, dir);
+}
+
+extern "C" __off32_t _lseek (int, __off32_t, int)
+ __attribute__ ((alias ("lseek")));
+
+extern "C" int
+close (int fd)
+{
+ int res;
+ sigframe thisframe (mainthread);
+
+ syscall_printf ("close (%d)", fd);
+
+ MALLOC_CHECK;
+ cygheap_fdget cfd (fd, true);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ res = cfd->close ();
+ cfd.release ();
+ }
+
+ syscall_printf ("%d = close (%d)", res, fd);
+ MALLOC_CHECK;
+ return res;
+}
+
+extern "C" int _close (int) __attribute__ ((alias ("close")));
+
+extern "C" int
+isatty (int fd)
+{
+ int res;
+ sigframe thisframe (mainthread);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = 0;
+ else
+ res = cfd->is_tty ();
+ syscall_printf ("%d = isatty (%d)", res, fd);
+ return res;
+}
+
+/* Under NT, try to make a hard link using backup API. If that
+ fails or we are Win 95, just copy the file.
+ FIXME: We should actually be checking partition type, not OS.
+ Under NTFS, we should support hard links. On FAT partitions,
+ we should just copy the file.
+*/
+
+extern "C" int
+link (const char *a, const char *b)
+{
+ int res = -1;
+ sigframe thisframe (mainthread);
+ path_conv real_a (a, PC_SYM_FOLLOW | PC_FULL);
+ path_conv real_b (b, PC_SYM_NOFOLLOW | PC_FULL);
+
+ if (real_a.error)
+ {
+ set_errno (real_a.error);
+ goto done;
+ }
+
+ if (real_b.error)
+ {
+ set_errno (real_b.case_clash ? ECASECLASH : real_b.error);
+ goto done;
+ }
+
+ if (real_b.exists ())
+ {
+ syscall_printf ("file '%s' exists?", (char *) real_b);
+ set_errno (EEXIST);
+ goto done;
+ }
+
+ if (real_b[strlen (real_b) - 1] == '.')
+ {
+ syscall_printf ("trailing dot, bailing out");
+ set_errno (EINVAL);
+ goto done;
+ }
+
+ /* Try to make hard link first on Windows NT */
+ if (wincap.has_hard_links ())
+ {
+ if (CreateHardLinkA (real_b, real_a, NULL))
+ {
+ res = 0;
+ goto done;
+ }
+
+ HANDLE hFileSource;
+
+ WIN32_STREAM_ID StreamId;
+ DWORD dwBytesWritten;
+ LPVOID lpContext;
+ DWORD cbPathLen;
+ DWORD StreamSize;
+ WCHAR wbuf[MAX_PATH];
+
+ BOOL bSuccess;
+
+ hFileSource = CreateFile (real_a, FILE_WRITE_ATTRIBUTES,
+ FILE_SHARE_READ | FILE_SHARE_WRITE /*| FILE_SHARE_DELETE*/,
+ &sec_none_nih, // sa
+ OPEN_EXISTING, 0, NULL);
+
+ if (hFileSource == INVALID_HANDLE_VALUE)
+ {
+ syscall_printf ("cannot open source, %E");
+ goto docopy;
+ }
+
+ cbPathLen = sys_mbstowcs (wbuf, real_b, MAX_PATH) * sizeof (WCHAR);
+
+ StreamId.dwStreamId = BACKUP_LINK;
+ StreamId.dwStreamAttributes = 0;
+ StreamId.dwStreamNameSize = 0;
+ StreamId.Size.HighPart = 0;
+ StreamId.Size.LowPart = cbPathLen;
+
+ StreamSize = sizeof (WIN32_STREAM_ID) - sizeof (WCHAR**) +
+ StreamId.dwStreamNameSize;
+
+ lpContext = NULL;
+ /* Write the WIN32_STREAM_ID */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) &StreamId, // buffer to write
+ StreamSize, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext);
+
+ if (bSuccess)
+ {
+ /* write the buffer containing the path */
+ /* FIXME: BackupWrite sometimes traps if linkname is invalid.
+ Need to handle. */
+ bSuccess = BackupWrite (
+ hFileSource,
+ (LPBYTE) wbuf, // buffer to write
+ cbPathLen, // number of bytes to write
+ &dwBytesWritten,
+ FALSE, // don't abort yet
+ FALSE, // don't process security
+ &lpContext
+ );
+
+ if (!bSuccess)
+ syscall_printf ("cannot write linkname, %E");
+
+ /* Free context */
+ BackupWrite (
+ hFileSource,
+ NULL, // buffer to write
+ 0, // number of bytes to write
+ &dwBytesWritten,
+ TRUE, // abort
+ FALSE, // don't process security
+ &lpContext);
+ }
+ else
+ syscall_printf ("cannot write streamId, %E");
+
+ CloseHandle (hFileSource);
+
+ if (!bSuccess)
+ goto docopy;
+
+ res = 0;
+ goto done;
+ }
+docopy:
+ /* do this with a copy */
+ if (CopyFileA (real_a, real_b, 1))
+ res = 0;
+ else
+ __seterrno ();
+
+done:
+ syscall_printf ("%d = link (%s, %s)", res, a, b);
+ return res;
+}
+
+/* chown: POSIX 5.6.5.1 */
+/*
+ * chown () is only implemented for Windows NT. Under other operating
+ * systems, it is only a stub that always returns zero.
+ */
+static int
+chown_worker (const char *name, unsigned fmode, __uid32_t uid, __gid32_t gid)
+{
+ int res;
+
+ if (check_null_empty_str_errno (name))
+ return -1;
+
+ if (!wincap.has_security ()) // real chown only works on NT
+ res = 0; // return zero (and do nothing) under Windows 9x
+ else
+ {
+ /* we need Win32 path names because of usage of Win32 API functions */
+ path_conv win32_path (PC_NONULLEMPTY, name, fmode);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ res = -1;
+ goto done;
+ }
+
+ /* FIXME: This makes chown on a device succeed always. Someday we'll want
+ to actually allow chown to work properly on devices. */
+ if (win32_path.is_auto_device ())
+ {
+ res = 0;
+ goto done;
+ }
+
+ DWORD attrib = 0;
+ if (win32_path.isdir ())
+ attrib |= S_IFDIR;
+ res = get_file_attribute (win32_path.has_acls (),
+ win32_path.get_win32 (),
+ (int *) &attrib);
+ if (!res)
+ res = set_file_attribute (win32_path.has_acls (), win32_path, uid,
+ gid, attrib);
+ if (res != 0 && (!win32_path.has_acls () || !allow_ntsec))
+ {
+ /* fake - if not supported, pretend we're like win95
+ where it just works */
+ res = 0;
+ }
+ }
+
+done:
+ syscall_printf ("%d = %schown (%s,...)",
+ res, (fmode & PC_SYM_NOFOLLOW) ? "l" : "", name);
+ return res;
+}
+
+extern "C" int
+chown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+chown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_FOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+lchown32 (const char * name, __uid32_t uid, __gid32_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_NOFOLLOW, uid, gid);
+}
+
+extern "C" int
+lchown (const char * name, __uid16_t uid, __gid16_t gid)
+{
+ sigframe thisframe (mainthread);
+ return chown_worker (name, PC_SYM_NOFOLLOW,
+ uid16touid32 (uid), gid16togid32 (gid));
+}
+
+extern "C" int
+fchown32 (int fd, __uid32_t uid, __gid32_t gid)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchown (%d,...)", fd);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchown (%d,...) (no name)", fd);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchown (%d,...): calling chown_worker (%s,FOLLOW,...)",
+ fd, path);
+ return chown_worker (path, PC_SYM_FOLLOW, uid, gid);
+}
+
+extern "C" int
+fchown (int fd, __uid16_t uid, __gid16_t gid)
+{
+ return fchown32 (fd, uid16touid32 (uid), gid16togid32 (gid));
+}
+
+/* umask: POSIX 5.3.3.1 */
+extern "C" mode_t
+umask (mode_t mask)
+{
+ mode_t oldmask;
+
+ oldmask = cygheap->umask;
+ cygheap->umask = mask & 0777;
+ return oldmask;
+}
+
+int
+chmod_device (path_conv& pc, mode_t mode)
+{
+ return mknod_worker (pc, pc.dev.mode & S_IFMT, mode, pc.dev.major, pc.dev.minor);
+}
+
+/* chmod: POSIX 5.6.4.1 */
+extern "C" int
+chmod (const char *path, mode_t mode)
+{
+ int res = -1;
+ sigframe thisframe (mainthread);
+
+ path_conv win32_path (path);
+
+ if (win32_path.error)
+ {
+ set_errno (win32_path.error);
+ goto done;
+ }
+
+ /* FIXME: This makes chmod on a device succeed always. Someday we'll want
+ to actually allow chmod to work properly on devices. */
+ if (win32_path.is_auto_device ())
+ {
+ res = 0;
+ goto done;
+ }
+ if (win32_path.is_fs_special ())
+ {
+ res = chmod_device (win32_path, mode);
+ goto done;
+ }
+
+ if (!win32_path.exists ())
+ __seterrno ();
+ else
+ {
+ /* temporary erase read only bit, to be able to set file security */
+ SetFileAttributes (win32_path, (DWORD) win32_path & ~FILE_ATTRIBUTE_READONLY);
+
+ if (win32_path.isdir ())
+ mode |= S_IFDIR;
+ if (!set_file_attribute (win32_path.has_acls (), win32_path,
+ ILLEGAL_UID, ILLEGAL_GID, mode)
+ && allow_ntsec)
+ res = 0;
+
+ /* if the mode we want has any write bits set, we can't
+ be read only. */
+ if (mode & (S_IWUSR | S_IWGRP | S_IWOTH))
+ (DWORD) win32_path &= ~FILE_ATTRIBUTE_READONLY;
+ else
+ (DWORD) win32_path |= FILE_ATTRIBUTE_READONLY;
+
+ if (S_ISLNK (mode) || S_ISSOCK (mode))
+ (DWORD) win32_path |= FILE_ATTRIBUTE_SYSTEM;
+
+ if (!SetFileAttributes (win32_path, win32_path))
+ __seterrno ();
+ else if (!allow_ntsec)
+ /* Correct NTFS security attributes have higher priority */
+ res = 0;
+ }
+
+done:
+ syscall_printf ("%d = chmod (%s, %p)", res, path, mode);
+ return res;
+}
+
+/* fchmod: P96 5.6.4.1 */
+
+extern "C" int
+fchmod (int fd, mode_t mode)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o)", fd, mode);
+ return -1;
+ }
+
+ const char *path = cfd->get_name ();
+
+ if (path == NULL)
+ {
+ syscall_printf ("-1 = fchmod (%d, 0%o) (no name)", fd, mode);
+ set_errno (ENOSYS);
+ return -1;
+ }
+
+ syscall_printf ("fchmod (%d, 0%o): calling chmod (%s, 0%o)",
+ fd, mode, path, mode);
+ return chmod (path, mode);
+}
+
+static void
+stat64_to_stat32 (struct __stat64 *src, struct __stat32 *dst)
+{
+ dst->st_dev = ((src->st_dev >> 8) & 0xff00) | (src->st_dev & 0xff);
+ dst->st_ino = src->st_ino;
+ dst->st_mode = src->st_mode;
+ dst->st_nlink = src->st_nlink;
+ dst->st_uid = src->st_uid;
+ dst->st_gid = src->st_gid;
+ dst->st_rdev = ((src->st_rdev >> 8) & 0xff00) | (src->st_rdev & 0xff);
+ dst->st_size = src->st_size;
+ dst->st_atim = src->st_atim;
+ dst->st_mtim = src->st_mtim;
+ dst->st_ctim = src->st_ctim;
+ dst->st_blksize = src->st_blksize;
+ dst->st_blocks = src->st_blocks;
+}
+
+extern "C" int
+fstat64 (int fd, struct __stat64 *buf)
+{
+ int res;
+ sigframe thisframe (mainthread);
+
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ res = -1;
+ else
+ {
+ path_conv pc (cfd->get_win32_name (), PC_SYM_NOFOLLOW);
+ memset (buf, 0, sizeof (struct __stat64));
+ res = cfd->fstat (buf, &pc);
+ if (!res)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, cfd->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = cfd->get_device ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ syscall_printf ("%d = fstat (%d, %p)", res, fd, buf);
+ return res;
+}
+
+extern "C" int
+_fstat (int fd, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = fstat64 (fd, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+/* fsync: P96 6.6.1.1 */
+extern "C" int
+fsync (int fd)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ {
+ syscall_printf ("-1 = fsync (%d)", fd);
+ return -1;
+ }
+
+ if (FlushFileBuffers (cfd->get_handle ()) == 0)
+ {
+ __seterrno ();
+ return -1;
+ }
+ return 0;
+}
+
+/* sync: standards? */
+extern "C" int
+sync ()
+{
+ return 0;
+}
+
+suffix_info stat_suffixes[] =
+{
+ suffix_info ("", 1),
+ suffix_info (".exe", 1),
+ suffix_info (NULL)
+};
+
+/* Cygwin internal */
+int __stdcall
+stat_worker (const char *name, struct __stat64 *buf, int nofollow,
+ path_conv *pc)
+{
+ int res = -1;
+ path_conv real_path;
+ fhandler_base *fh = NULL;
+
+ if (check_null_invalid_struct_errno (buf))
+ goto done;
+
+ if (!pc)
+ pc = &real_path;
+
+ fh = cygheap->fdtab.build_fhandler_from_name (-1, name, NULL, *pc,
+ (nofollow ? PC_SYM_NOFOLLOW
+ : PC_SYM_FOLLOW)
+ | PC_FULL, stat_suffixes);
+
+ if (pc->error)
+ {
+ debug_printf ("got %d error from build_fhandler_from_name", pc->error);
+ set_errno (pc->error);
+ }
+ else
+ {
+ debug_printf ("(%s, %p, %d, %p), file_attributes %d", name, buf, nofollow,
+ pc, (DWORD) real_path);
+ memset (buf, 0, sizeof (*buf));
+ res = fh->fstat (buf, pc);
+ if (!res)
+ {
+ if (!buf->st_ino)
+ buf->st_ino = hash_path_name (0, fh->get_win32_name ());
+ if (!buf->st_dev)
+ buf->st_dev = fh->get_device ();
+ if (!buf->st_rdev)
+ buf->st_rdev = buf->st_dev;
+ }
+ }
+
+ done:
+ if (fh)
+ delete fh;
+ MALLOC_CHECK;
+ syscall_printf ("%d = (%s, %p)", res, name, buf);
+ return res;
+}
+
+extern "C" int
+stat64 (const char *name, struct __stat64 *buf)
+{
+ sigframe thisframe (mainthread);
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 0);
+}
+
+extern "C" int
+_stat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = stat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+lstat64 (const char *name, struct __stat64 *buf)
+{
+ sigframe thisframe (mainthread);
+ syscall_printf ("entering");
+ return stat_worker (name, buf, 1);
+}
+
+/* lstat: Provided by SVR4 and 4.3+BSD, POSIX? */
+extern "C" int
+cygwin_lstat (const char *name, struct __stat32 *buf)
+{
+ struct __stat64 buf64;
+ int ret = lstat64 (name, &buf64);
+ if (!ret)
+ stat64_to_stat32 (&buf64, buf);
+ return ret;
+}
+
+extern int acl_access (const char *, int);
+
+extern "C" int
+access (const char *fn, int flags)
+{
+ sigframe thisframe (mainthread);
+ // flags were incorrectly specified
+ if (flags & ~(F_OK|R_OK|W_OK|X_OK))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ if (allow_ntsec)
+ return acl_access (fn, flags);
+
+ struct __stat64 st;
+ int r = stat_worker (fn, &st, 0);
+ if (r)
+ return -1;
+ r = -1;
+ if (flags & R_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IRUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IRGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IROTH))
+ goto done;
+ }
+ if (flags & W_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IWUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IWGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IWOTH))
+ goto done;
+ }
+ if (flags & X_OK)
+ {
+ if (st.st_uid == myself->uid)
+ {
+ if (!(st.st_mode & S_IXUSR))
+ goto done;
+ }
+ else if (st.st_gid == myself->gid)
+ {
+ if (!(st.st_mode & S_IXGRP))
+ goto done;
+ }
+ else if (!(st.st_mode & S_IXOTH))
+ goto done;
+ }
+ r = 0;
+done:
+ if (r)
+ set_errno (EACCES);
+ return r;
+}
+
+extern "C" int
+rename (const char *oldpath, const char *newpath)
+{
+ sigframe thisframe (mainthread);
+ int res = 0;
+ char *lnk_suffix = NULL;
+
+ path_conv real_old (oldpath, PC_SYM_NOFOLLOW);
+
+ if (real_old.error)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_old.error);
+ return -1;
+ }
+
+ path_conv real_new (newpath, PC_SYM_NOFOLLOW);
+
+ /* Shortcut hack. */
+ char new_lnk_buf[MAX_PATH + 5];
+ if (real_old.issymlink () && !real_new.error && !real_new.case_clash)
+ {
+ int len_old = strlen (real_old.get_win32 ());
+ if (strcasematch (real_old.get_win32 () + len_old - 4, ".lnk"))
+ {
+ strcpy (new_lnk_buf, newpath);
+ strcat (new_lnk_buf, ".lnk");
+ newpath = new_lnk_buf;
+ real_new.check (newpath, PC_SYM_NOFOLLOW);
+ }
+ }
+
+ if (real_new.error || real_new.case_clash)
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (real_new.case_clash ? ECASECLASH : real_new.error);
+ return -1;
+ }
+
+ if (!writable_directory (real_old) || !writable_directory (real_new))
+ {
+ syscall_printf ("-1 = rename (%s, %s)", oldpath, newpath);
+ set_errno (EACCES);
+ return -1;
+ }
+
+ if (!real_old.exists ()) /* file to move doesn't exist */
+ {
+ syscall_printf ("file to move doesn't exist");
+ set_errno (ENOENT);
+ return (-1);
+ }
+
+ /* Destination file exists and is read only, change that or else
+ the rename won't work. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, (DWORD) real_new & ~FILE_ATTRIBUTE_READONLY);
+
+ /* Shortcut hack No. 2, part 1 */
+ if (!real_old.issymlink () && !real_new.error && real_new.issymlink () &&
+ real_new.known_suffix && strcasematch (real_new.known_suffix, ".lnk") &&
+ (lnk_suffix = strrchr (real_new.get_win32 (), '.')))
+ *lnk_suffix = '\0';
+
+ if (!MoveFile (real_old, real_new))
+ res = -1;
+
+ if (res == 0 || (GetLastError () != ERROR_ALREADY_EXISTS
+ && GetLastError () != ERROR_FILE_EXISTS))
+ goto done;
+
+ if (wincap.has_move_file_ex ())
+ {
+ if (MoveFileEx (real_old.get_win32 (), real_new.get_win32 (),
+ MOVEFILE_REPLACE_EXISTING))
+ res = 0;
+ }
+ else
+ {
+ syscall_printf ("try win95 hack");
+ for (int i = 0; i < 2; i++)
+ {
+ if (!DeleteFileA (real_new.get_win32 ()) &&
+ GetLastError () != ERROR_FILE_NOT_FOUND)
+ {
+ syscall_printf ("deleting %s to be paranoid",
+ real_new.get_win32 ());
+ break;
+ }
+ else if (MoveFile (real_old.get_win32 (), real_new.get_win32 ()))
+ {
+ res = 0;
+ break;
+ }
+ }
+ }
+
+done:
+ if (res)
+ {
+ __seterrno ();
+ /* Reset R/O attributes if neccessary. */
+ if (real_new.has_attribute (FILE_ATTRIBUTE_READONLY))
+ SetFileAttributes (real_new, real_new);
+ }
+ else
+ {
+ /* make the new file have the permissions of the old one */
+ DWORD attr = real_old;
+#ifdef HIDDEN_DOT_FILES
+ char *c = strrchr (real_old.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_old.get_win32 () == '.')
+ attr &= ~FILE_ATTRIBUTE_HIDDEN;
+ c = strrchr (real_new.get_win32 (), '\\');
+ if ((c && c[1] == '.') || *real_new.get_win32 () == '.')
+ attr |= FILE_ATTRIBUTE_HIDDEN;
+#endif
+ SetFileAttributes (real_new, attr);
+
+ /* Shortcut hack, No. 2, part 2 */
+ /* if the new filename was an existing shortcut, remove it now if the
+ new filename is equal to the shortcut name without .lnk suffix. */
+ if (lnk_suffix)
+ {
+ *lnk_suffix = '.';
+ DeleteFile (real_new);
+ }
+ }
+
+ syscall_printf ("%d = rename (%s, %s)", res, (char *) real_old,
+ (char *) real_new);
+
+ return res;
+}
+
+extern "C" int
+system (const char *cmdstring)
+{
+ pthread_testcancel ();
+
+ if (check_null_empty_str_errno (cmdstring))
+ return -1;
+
+ sigframe thisframe (mainthread);
+ int res;
+ const char* command[4];
+ _sig_func_ptr oldint, oldquit;
+ sigset_t child_block, old_mask;
+
+ if (cmdstring == (const char *) NULL)
+ return 1;
+
+ oldint = signal (SIGINT, SIG_IGN);
+ oldquit = signal (SIGQUIT, SIG_IGN);
+ sigemptyset (&child_block);
+ sigaddset (&child_block, SIGCHLD);
+ (void) sigprocmask (SIG_BLOCK, &child_block, &old_mask);
+
+ command[0] = "sh";
+ command[1] = "-c";
+ command[2] = cmdstring;
+ command[3] = (const char *) NULL;
+
+ if ((res = spawnvp (_P_WAIT, "sh", command)) == -1)
+ {
+ // when exec fails, return value should be as if shell
+ // executed exit (127)
+ res = 127;
+ }
+
+ signal (SIGINT, oldint);
+ signal (SIGQUIT, oldquit);
+ (void) sigprocmask (SIG_SETMASK, &old_mask, 0);
+ return res;
+}
+
+extern "C" int
+setdtablesize (int size)
+{
+ if (size <= (int)cygheap->fdtab.size || cygheap->fdtab.extend (size - cygheap->fdtab.size))
+ return 0;
+
+ return -1;
+}
+
+extern "C" int
+getdtablesize ()
+{
+ return cygheap->fdtab.size > OPEN_MAX ? cygheap->fdtab.size : OPEN_MAX;
+}
+
+extern "C" size_t
+getpagesize ()
+{
+ if (!system_info.dwPageSize)
+ GetSystemInfo (&system_info);
+ return (int) system_info.dwPageSize;
+}
+
+static int
+check_posix_perm (const char *fname, int v)
+{
+ extern int allow_ntea, allow_ntsec, allow_smbntsec;
+
+ /* Windows 95/98/ME don't support file system security at all. */
+ if (!wincap.has_security ())
+ return 0;
+
+ /* ntea is ok for supporting permission bits but it doesn't support
+ full POSIX security settings. */
+ if (v == _PC_POSIX_PERMISSIONS && allow_ntea)
+ return 1;
+
+ if (!allow_ntsec)
+ return 0;
+
+ char *root = rootdir (strcpy ((char *)alloca (strlen (fname)), fname));
+
+ if (!allow_smbntsec
+ && ((root[0] == '\\' && root[1] == '\\')
+ || GetDriveType (root) == DRIVE_REMOTE))
+ return 0;
+
+ DWORD vsn, len, flags;
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &len, &flags, NULL, 16))
+ {
+ __seterrno ();
+ return 0;
+ }
+
+ return (flags & FS_PERSISTENT_ACLS) ? 1 : 0;
+}
+
+/* FIXME: not all values are correct... */
+extern "C" long int
+fpathconf (int fd, int v)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ switch (v)
+ {
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ if (isatty (fd))
+ return _POSIX_MAX_CANON;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_NAME_MAX:
+ case _PC_PATH_MAX:
+ return PATH_MAX;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ if (cfd->is_tty ())
+ return -1;
+ else
+ {
+ set_errno (EBADF);
+ return -1;
+ }
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ if (cfd->get_device () == FH_FS)
+ return check_posix_perm (cfd->get_win32_name (), v);
+ set_errno (EINVAL);
+ return -1;
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" long int
+pathconf (const char *file, int v)
+{
+ switch (v)
+ {
+ case _PC_PATH_MAX:
+ if (check_null_empty_str_errno (file))
+ return -1;
+ return PATH_MAX - strlen (file);
+ case _PC_NAME_MAX:
+ return PATH_MAX;
+ case _PC_LINK_MAX:
+ return _POSIX_LINK_MAX;
+ case _PC_MAX_CANON:
+ case _PC_MAX_INPUT:
+ return _POSIX_MAX_CANON;
+ case _PC_PIPE_BUF:
+ return PIPE_BUF;
+ case _PC_CHOWN_RESTRICTED:
+ case _PC_NO_TRUNC:
+ return -1;
+ case _PC_VDISABLE:
+ return -1;
+ case _PC_POSIX_PERMISSIONS:
+ case _PC_POSIX_SECURITY:
+ {
+ path_conv full_path (file, PC_SYM_FOLLOW | PC_FULL);
+ if (full_path.error)
+ {
+ set_errno (full_path.error);
+ return -1;
+ }
+ if (full_path.is_auto_device ())
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ return check_posix_perm (full_path, v);
+ }
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+}
+
+extern "C" char *
+ttyname (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0 || !cfd->is_tty ())
+ return 0;
+ return (char *) (cfd->ttyname ());
+}
+
+extern "C" char *
+ctermid (char *str)
+{
+ static NO_COPY char buf[16];
+ if (str == NULL)
+ str = buf;
+ if (!real_tty_attached (myself))
+ strcpy (str, "/dev/conin");
+ else
+ __small_sprintf (str, "/dev/tty%d", myself->ctty);
+ return str;
+}
+
+/* Tells stdio if it should do the cr/lf conversion for this file */
+extern "C" int
+_cygwin_istext_for_stdio (int fd)
+{
+ syscall_printf ("_cygwin_istext_for_stdio (%d)", fd);
+ if (CYGWIN_VERSION_OLD_STDIO_CRLF_HANDLING)
+ {
+ syscall_printf (" _cifs: old API");
+ return 0; /* we do it for old apps, due to getc/putc macros */
+ }
+
+ cygheap_fdget cfd (fd, false, false);
+ if (cfd < 0)
+ {
+ syscall_printf (" _cifs: fd not open");
+ return 0;
+ }
+
+ if (cfd->get_device () != FH_FS)
+ {
+ syscall_printf (" _cifs: fd not disk file");
+ return 0;
+ }
+
+ if (cfd->get_w_binary () || cfd->get_r_binary ())
+ {
+ syscall_printf (" _cifs: get_*_binary");
+ return 0;
+ }
+
+ syscall_printf ("_cygwin_istext_for_stdio says yes");
+ return 1;
+}
+
+/* internal newlib function */
+extern "C" int _fwalk (struct _reent *ptr, int (*function) (FILE *));
+
+static int setmode_mode;
+static int setmode_file;
+
+static int
+setmode_helper (FILE *f)
+{
+ if (fileno (f) != setmode_file)
+ return 0;
+ syscall_printf ("setmode: file was %s now %s",
+ f->_flags & __SCLE ? "text" : "raw",
+ setmode_mode & O_TEXT ? "text" : "raw");
+ if (setmode_mode & O_TEXT)
+ f->_flags |= __SCLE;
+ else
+ f->_flags &= ~__SCLE;
+ return 0;
+}
+
+extern "C" int
+getmode (int fd)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+
+ return cfd->get_flags () & (O_BINARY | O_TEXT);
+}
+
+/* Set a file descriptor into text or binary mode, returning the
+ previous mode. */
+
+extern "C" int
+setmode (int fd, int mode)
+{
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ if (mode != O_BINARY && mode != O_TEXT && mode != 0)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ /* Note that we have no way to indicate the case that writes are
+ binary but not reads, or vice-versa. These cases can arise when
+ using the tty or console interface. People using those
+ interfaces should not use setmode. */
+
+ int res;
+ if (cfd->get_w_binary () && cfd->get_r_binary ())
+ res = O_BINARY;
+ else if (cfd->get_w_binset () && cfd->get_r_binset ())
+ res = O_TEXT; /* Specifically set O_TEXT */
+ else
+ res = 0;
+
+ if (!mode)
+ cfd->reset_to_open_binmode ();
+ else
+ cfd->set_flags ((cfd->get_flags () & ~(O_TEXT | O_BINARY)) | mode);
+
+ if (_cygwin_istext_for_stdio (fd))
+ setmode_mode = O_TEXT;
+ else
+ setmode_mode = O_BINARY;
+ setmode_file = fd;
+ _fwalk (_REENT, setmode_helper);
+
+ syscall_printf ("setmode (%d<%s>, %p) returns %s", fd, cfd->get_name (),
+ mode, res & O_TEXT ? "text" : "binary");
+ return res;
+}
+
+extern "C" int
+ftruncate64 (int fd, __off64_t length)
+{
+ sigframe thisframe (mainthread);
+ int res = -1;
+
+ if (length < 0)
+ set_errno (EINVAL);
+ else
+ {
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ {
+ HANDLE h = cygheap->fdtab[fd]->get_handle ();
+
+ if (cfd->get_handle ())
+ {
+ /* remember curr file pointer location */
+ __off64_t prev_loc = cfd->lseek (0, SEEK_CUR);
+
+ cfd->lseek (length, SEEK_SET);
+ if (!SetEndOfFile (h))
+ __seterrno ();
+ else
+ res = 0;
+
+ /* restore original file pointer location */
+ cfd->lseek (prev_loc, SEEK_SET);
+ }
+ }
+ }
+
+ syscall_printf ("%d = ftruncate (%d, %d)", res, fd, length);
+ return res;
+}
+
+/* ftruncate: P96 5.6.7.1 */
+extern "C" int
+ftruncate (int fd, __off32_t length)
+{
+ return ftruncate64 (fd, (__off64_t)length);
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate64 (const char *pathname, __off64_t length)
+{
+ sigframe thisframe (mainthread);
+ int fd;
+ int res = -1;
+
+ fd = open (pathname, O_RDWR);
+
+ if (fd == -1)
+ set_errno (EBADF);
+ else
+ {
+ res = ftruncate64 (fd, length);
+ close (fd);
+ }
+ syscall_printf ("%d = truncate (%s, %d)", res, pathname, length);
+
+ return res;
+}
+
+/* truncate: Provided by SVR4 and 4.3+BSD. Not part of POSIX.1 or XPG3 */
+extern "C" int
+truncate (const char *pathname, __off32_t length)
+{
+ return truncate64 (pathname, (__off64_t)length);
+}
+
+extern "C" long
+get_osfhandle (int fd)
+{
+ long res;
+
+ cygheap_fdget cfd (fd);
+ if (cfd >= 0)
+ res = (long) cfd->get_handle ();
+ else
+ res = -1;
+
+ syscall_printf ("%d = get_osfhandle (%d)", res, fd);
+ return res;
+}
+
+extern "C" int
+statfs (const char *fname, struct statfs *sfs)
+{
+ sigframe thisframe (mainthread);
+ if (!sfs)
+ {
+ set_errno (EFAULT);
+ return -1;
+ }
+
+ path_conv full_path (fname, PC_SYM_FOLLOW | PC_FULL);
+ char *root = rootdir (full_path);
+
+ syscall_printf ("statfs %s", root);
+
+ DWORD spc, bps, freec, totalc;
+
+ if (!GetDiskFreeSpace (root, &spc, &bps, &freec, &totalc))
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ DWORD vsn, maxlen, flags;
+
+ if (!GetVolumeInformation (root, NULL, 0, &vsn, &maxlen, &flags, NULL, 0))
+ {
+ __seterrno ();
+ return -1;
+ }
+ sfs->f_type = flags;
+ sfs->f_bsize = spc*bps;
+ sfs->f_blocks = totalc;
+ sfs->f_bfree = sfs->f_bavail = freec;
+ sfs->f_files = -1;
+ sfs->f_ffree = -1;
+ sfs->f_fsid = vsn;
+ sfs->f_namelen = maxlen;
+ return 0;
+}
+
+extern "C" int
+fstatfs (int fd, struct statfs *sfs)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return -1;
+ return statfs (cfd->get_name (), sfs);
+}
+
+/* setpgid: POSIX 4.3.3.1 */
+extern "C" int
+setpgid (pid_t pid, pid_t pgid)
+{
+ sigframe thisframe (mainthread);
+ int res = -1;
+ if (pid == 0)
+ pid = getpid ();
+ if (pgid == 0)
+ pgid = pid;
+
+ if (pgid < 0)
+ {
+ set_errno (EINVAL);
+ goto out;
+ }
+ else
+ {
+ pinfo p (pid);
+ if (!p)
+ {
+ set_errno (ESRCH);
+ goto out;
+ }
+ /* A process may only change the process group of itself and its children */
+ if (p == myself || p->ppid == myself->pid)
+ {
+ p->pgid = pgid;
+ if (p->pid != p->pgid)
+ p->set_has_pgid_children (0);
+ res = 0;
+ }
+ else
+ {
+ set_errno (EPERM);
+ goto out;
+ }
+ }
+out:
+ syscall_printf ("pid %d, pgid %d, res %d", pid, pgid, res);
+ return res;
+}
+
+extern "C" pid_t
+getpgid (pid_t pid)
+{
+ sigframe thisframe (mainthread);
+ if (pid == 0)
+ pid = getpid ();
+
+ pinfo p (pid);
+ if (p == 0)
+ {
+ set_errno (ESRCH);
+ return -1;
+ }
+ return p->pgid;
+}
+
+extern "C" int
+setpgrp (void)
+{
+ sigframe thisframe (mainthread);
+ return setpgid (0, 0);
+}
+
+extern "C" pid_t
+getpgrp (void)
+{
+ sigframe thisframe (mainthread);
+ return getpgid (0);
+}
+
+extern "C" char *
+ptsname (int fd)
+{
+ sigframe thisframe (mainthread);
+ cygheap_fdget cfd (fd);
+ if (cfd < 0)
+ return 0;
+ return (char *) (cfd->ptsname ());
+}
+
+/* FIXME: what is this? */
+extern "C" int __declspec(dllexport)
+regfree ()
+{
+ return 0;
+}
+
+static int __stdcall
+mknod_worker (const char *path, mode_t type, mode_t mode, _major_t major,
+ _minor_t minor)
+{
+ char buf[sizeof (":00000000:00000000:00000000") + MAX_PATH];
+ sprintf (buf, ":%x:%x:%x", major, minor,
+ type | (mode & (S_IRWXU | S_IRWXG | S_IRWXO)));
+ return symlink_worker (buf, path, true, true);
+}
+
+/* mknod was the call to create directories before the introduction
+ of mkdir in 4.2BSD and SVR3. Use of mknod required superuser privs
+ so the mkdir command had to be setuid root.
+ Although mknod hasn't been implemented yet, some GNU tools (e.g. the
+ fileutils) assume its existence so we must provide a stub that always
+ fails. */
+extern "C" int
+mknod (const char *path, mode_t mode, dev_t dev)
+{
+ if (check_null_empty_str_errno (path))
+ return -1;
+
+ if (strlen (path) >= MAX_PATH)
+ return -1;
+
+ path_conv w32path (path, PC_SYM_NOFOLLOW | PC_FULL);
+ if (w32path.exists ())
+ {
+ set_errno (EEXIST);
+ return -1;
+ }
+
+ mode_t type = mode & S_IFMT;
+ _major_t major = dev >> 8 /* SIGH. _major (dev) */;
+ _minor_t minor = dev & 0xff /* SIGH _minor (dev) */;
+ switch (type)
+ {
+ case S_IFCHR:
+ case S_IFBLK:
+ break;
+
+ case S_IFIFO:
+ major = _major (FH_FIFO);
+ minor = _minor (FH_FIFO) & 0xff; /* SIGH again */
+ break;
+
+ case 0:
+ case S_IFREG:
+ {
+ int fd = open (path, O_CREAT, mode);
+ if (fd < 0)
+ return -1;
+ close (fd);
+ return 0;
+ }
+
+ default:
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ return mknod_worker (w32path, type, mode, major, minor);
+}
+
+extern "C" int
+mkfifo (const char *_path, mode_t mode)
+{
+ return -1;
+}
+
+/* seteuid: standards? */
+extern "C" int
+seteuid32 (__uid32_t uid)
+{
+ debug_printf ("uid: %d myself->gid: %d", uid, myself->gid);
+
+ if (!wincap.has_security ()
+ || (uid == myself->uid && !cygheap->user.groups.ischanged))
+ {
+ debug_printf ("Nothing happens");
+ return 0;
+ }
+
+ if (uid == ILLEGAL_UID)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ sigframe thisframe (mainthread);
+ cygsid usersid;
+ user_groups &groups = cygheap->user.groups;
+ HANDLE ptok, sav_token;
+ BOOL sav_impersonated, sav_token_is_internal_token;
+ BOOL process_ok, explicitly_created_token = FALSE;
+ struct passwd * pw_new;
+ PSID origpsid, psid2 = NO_SID;
+
+ pw_new = internal_getpwuid (uid);
+ if (!usersid.getfrompw (pw_new))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ /* Save current information */
+ sav_token = cygheap->user.token;
+ sav_impersonated = cygheap->user.impersonated;
+
+ RevertToSelf ();
+ if (!OpenProcessToken (hMainProc, TOKEN_QUERY | TOKEN_ADJUST_DEFAULT, &ptok))
+ {
+ __seterrno ();
+ goto failed;
+ }
+ /* Verify if the process token is suitable.
+ Currently we do not try to differentiate between
+ internal tokens and others */
+ process_ok = verify_token (ptok, usersid, groups);
+ debug_printf ("Process token %sverified", process_ok ? "" : "not ");
+ if (process_ok)
+ {
+ if (cygheap->user.issetuid ())
+ cygheap->user.impersonated = FALSE;
+ else
+ {
+ CloseHandle (ptok);
+ goto success; /* No change */
+ }
+ }
+
+ if (!process_ok && cygheap->user.token != INVALID_HANDLE_VALUE)
+ {
+ /* Verify if the current tokem is suitable */
+ BOOL token_ok = verify_token (cygheap->user.token, usersid, groups,
+ &sav_token_is_internal_token);
+ debug_printf ("Thread token %d %sverified",
+ cygheap->user.token, token_ok?"":"not ");
+ if (!token_ok)
+ cygheap->user.token = INVALID_HANDLE_VALUE;
+ else
+ {
+ /* Return if current token is valid */
+ if (cygheap->user.impersonated)
+ {
+ CloseHandle (ptok);
+ if (!ImpersonateLoggedOnUser (cygheap->user.token))
+ system_printf ("Impersonating in seteuid failed: %E");
+ goto success; /* No change */
+ }
+ }
+ }
+
+ /* Set process def dacl to allow access to impersonated token */
+ char dacl_buf[MAX_DACL_LEN (5)];
+ if (usersid != (origpsid = cygheap->user.orig_sid ())) psid2 = usersid;
+ if (sec_acl ((PACL) dacl_buf, FALSE, origpsid, psid2))
+ {
+ TOKEN_DEFAULT_DACL tdacl;
+ tdacl.DefaultDacl = (PACL) dacl_buf;
+ if (!SetTokenInformation (ptok, TokenDefaultDacl,
+ &tdacl, sizeof dacl_buf))
+ debug_printf ("SetTokenInformation"
+ "(TokenDefaultDacl): %E");
+ }
+ CloseHandle (ptok);
+
+ if (!process_ok && cygheap->user.token == INVALID_HANDLE_VALUE)
+ {
+ /* If no impersonation token is available, try to
+ authenticate using NtCreateToken () or subauthentication. */
+ cygheap->user.token = create_token (usersid, groups, pw_new);
+ if (cygheap->user.token != INVALID_HANDLE_VALUE)
+ explicitly_created_token = TRUE;
+ else
+ {
+ /* create_token failed. Try subauthentication. */
+ debug_printf ("create token failed, try subauthentication.");
+ cygheap->user.token = subauth (pw_new);
+ if (cygheap->user.token == INVALID_HANDLE_VALUE)
+ goto failed;
+ }
+ }
+
+ /* If using the token, set info and impersonate */
+ if (!process_ok)
+ {
+ /* If the token was explicitly created, all information has
+ already been set correctly. */
+ if (!explicitly_created_token)
+ {
+ /* Try setting owner to same value as user. */
+ if (!SetTokenInformation (cygheap->user.token, TokenOwner,
+ &usersid, sizeof usersid))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenOwner): %E");
+ /* Try setting primary group in token to current group */
+ if (!SetTokenInformation (cygheap->user.token,
+ TokenPrimaryGroup,
+ &groups.pgsid, sizeof (cygsid)))
+ debug_printf ("SetTokenInformation(user.token, "
+ "TokenPrimaryGroup): %E");
+ }
+ /* Now try to impersonate. */
+ if (!ImpersonateLoggedOnUser (cygheap->user.token))
+ {
+ debug_printf ("ImpersonateLoggedOnUser %E");
+ __seterrno ();
+ goto failed;
+ }
+ cygheap->user.impersonated = TRUE;
+ }
+
+ /* If sav_token was internally created and is replaced, destroy it. */
+ if (sav_token != INVALID_HANDLE_VALUE &&
+ sav_token != cygheap->user.token &&
+ sav_token_is_internal_token)
+ CloseHandle (sav_token);
+ cygheap->user.set_name (pw_new->pw_name);
+ cygheap->user.set_sid (usersid);
+success:
+ myself->uid = uid;
+ groups.ischanged = FALSE;
+ return 0;
+
+ failed:
+ cygheap->user.token = sav_token;
+ cygheap->user.impersonated = sav_impersonated;
+ if (cygheap->user.issetuid ()
+ && !ImpersonateLoggedOnUser (cygheap->user.token))
+ system_printf ("Impersonating in seteuid failed: %E");
+ return -1;
+}
+
+extern "C" int
+seteuid (__uid16_t uid)
+{
+ return seteuid32 (uid16touid32 (uid));
+}
+
+/* setuid: POSIX 4.2.2.1 */
+extern "C" int
+setuid32 (__uid32_t uid)
+{
+ int ret = seteuid32 (uid);
+ if (!ret)
+ cygheap->user.real_uid = myself->uid;
+ debug_printf ("real: %d, effective: %d", cygheap->user.real_uid, myself->uid);
+ return ret;
+}
+
+extern "C" int
+setuid (__uid16_t uid)
+{
+ return setuid32 (uid16touid32 (uid));
+}
+
+/* setegid: from System V. */
+extern "C" int
+setegid32 (__gid32_t gid)
+{
+ if (!wincap.has_security () || gid == myself->gid)
+ return 0;
+
+ if (gid == ILLEGAL_GID)
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+
+ sigframe thisframe (mainthread);
+ user_groups * groups = &cygheap->user.groups;
+ cygsid gsid;
+ HANDLE ptok;
+
+ struct __group32 * gr = internal_getgrgid (gid);
+ if (!gr || gr->gr_gid != gid || !gsid.getfromgr (gr))
+ {
+ set_errno (EINVAL);
+ return -1;
+ }
+ myself->gid = gid;
+
+ groups->update_pgrp (gsid);
+ /* If impersonated, update primary group and revert */
+ if (cygheap->user.issetuid ())
+ {
+ if (!SetTokenInformation (cygheap->user.token,
+ TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(thread, "
+ "TokenPrimaryGroup): %E");
+ RevertToSelf ();
+ }
+ if (!OpenProcessToken (hMainProc, TOKEN_ADJUST_DEFAULT, &ptok))
+ debug_printf ("OpenProcessToken(): %E");
+ else
+ {
+ if (!SetTokenInformation (ptok, TokenPrimaryGroup,
+ &gsid, sizeof gsid))
+ debug_printf ("SetTokenInformation(process, "
+ "TokenPrimaryGroup): %E");
+ CloseHandle (ptok);
+ }
+ if (cygheap->user.issetuid ()
+ && !ImpersonateLoggedOnUser (cygheap->user.token))
+ system_printf ("Impersonating in setegid failed: %E");
+ return 0;
+}
+
+extern "C" int
+setegid (__gid16_t gid)
+{
+ return setegid32 (gid16togid32 (gid));
+}
+
+/* setgid: POSIX 4.2.2.1 */
+extern "C" int
+setgid32 (__gid32_t gid)
+{
+ int ret = setegid32 (gid);
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+extern "C" int
+setgid (__gid16_t gid)
+{
+ int ret = setegid32 (gid16togid32 (gid));
+ if (!ret)
+ cygheap->user.real_gid = myself->gid;
+ return ret;
+}
+
+/* chroot: privileged Unix system call. */
+/* FIXME: Not privileged here. How should this be done? */
+extern "C" int
+chroot (const char *newroot)
+{
+ sigframe thisframe (mainthread);
+ path_conv path (newroot, PC_SYM_FOLLOW | PC_FULL | PC_POSIX);
+
+ int ret;
+ if (path.error)
+ ret = -1;
+ else if (!path.exists ())
+ {
+ set_errno (ENOENT);
+ ret = -1;
+ }
+ else if (!path.isdir ())
+ {
+ set_errno (ENOTDIR);
+ ret = -1;
+ }
+ else
+ {
+ cygheap->root.set (path.normalized_path, path);
+ ret = 0;
+ }
+
+ syscall_printf ("%d = chroot (%s)", ret ? get_errno () : 0,
+ newroot ? newroot : "NULL");
+ return ret;
+}
+
+extern "C" int
+creat (const char *path, mode_t mode)
+{
+ sigframe thisframe (mainthread);
+ return open (path, O_WRONLY | O_CREAT | O_TRUNC, mode);
+}
+
+extern "C" void
+__assertfail ()
+{
+ exit (99);
+}
+
+extern "C" int
+getw (FILE *fp)
+{
+ sigframe thisframe (mainthread);
+ int w, ret;
+ ret = fread (&w, sizeof (int), 1, fp);
+ return ret != 1 ? EOF : w;
+}
+
+extern "C" int
+putw (int w, FILE *fp)
+{
+ sigframe thisframe (mainthread);
+ int ret;
+ ret = fwrite (&w, sizeof (int), 1, fp);
+ if (feof (fp) || ferror (fp))
+ return -1;
+ return 0;
+}
+
+extern "C" int
+wcscmp (const wchar_t *s1, const wchar_t *s2)
+{
+ while (*s1 && *s1 == *s2)
+ {
+ s1++;
+ s2++;
+ }
+
+ return (* (unsigned short *) s1) - (* (unsigned short *) s2);
+}
+
+extern "C" size_t
+wcslen (const wchar_t *s1)
+{
+ int l = 0;
+ while (s1[l])
+ l++;
+ return l;
+}
+
+/* FIXME: to do this right, maybe work out the usoft va_list machine
+ and use wsvprintfW instead?
+*/
+extern "C" int
+wprintf (const char *fmt, ...)
+{
+ va_list ap;
+ int ret;
+
+ va_start (ap, fmt);
+ ret = vprintf (fmt, ap);
+ va_end (ap);
+ return ret;
+}
+
+extern "C" int
+vhangup ()
+{
+ set_errno (ENOSYS);
+ return -1;
+}
+
+extern "C" _PTR
+memccpy (_PTR out, const _PTR in, int c, size_t len)
+{
+ const char *inc = (char *) in;
+ char *outc = (char *) out;
+
+ while (len)
+ {
+ char x = *inc++;
+ *outc++ = x;
+ if (x == c)
+ return outc;
+ len --;
+ }
+ return 0;
+}
+
+extern "C" int
+nice (int incr)
+{
+ sigframe thisframe (mainthread);
+ DWORD priority[] =
+ {
+ IDLE_PRIORITY_CLASS,
+ IDLE_PRIORITY_CLASS,
+ NORMAL_PRIORITY_CLASS,
+ HIGH_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS,
+ REALTIME_PRIORITY_CLASS
+ };
+ int curr = 2;
+
+ switch (GetPriorityClass (hMainProc))
+ {
+ case IDLE_PRIORITY_CLASS:
+ curr = 1;
+ break;
+ case NORMAL_PRIORITY_CLASS:
+ curr = 2;
+ break;
+ case HIGH_PRIORITY_CLASS:
+ curr = 3;
+ break;
+ case REALTIME_PRIORITY_CLASS:
+ curr = 4;
+ break;
+ }
+ if (incr > 0)
+ incr = -1;
+ else if (incr < 0)
+ incr = 1;
+
+ if (SetPriorityClass (hMainProc, priority[curr + incr]) == FALSE)
+ {
+ __seterrno ();
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * Find the first bit set in I.
+ */
+
+extern "C" int
+ffs (int i)
+{
+ static const unsigned char table[] =
+ {
+ 0,1,2,2,3,3,3,3,4,4,4,4,4,4,4,4,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,5,
+ 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,
+ 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8,8
+ };
+ unsigned long int a;
+ unsigned long int x = i & -i;
+
+ a = x <= 0xffff ? (x <= 0xff ? 0 : 8) : (x <= 0xffffff ? 16 : 24);
+
+ return table[x >> a] + a;
+}
+
+extern "C" void
+login (struct utmp *ut)
+{
+ sigframe thisframe (mainthread);
+ register int fd;
+
+ pututline (ut);
+ if ((fd = open (_PATH_WTMP, O_WRONLY | O_APPEND | O_BINARY, 0)) >= 0)
+ {
+ (void) write (fd, (char *) ut, sizeof (struct utmp));
+ (void) close (fd);
+ }
+}
+
+/* It isn't possible to use unix-style I/O function in logout code because
+cygwin's I/O subsystem may be inaccessible at logout () call time.
+FIXME (cgf): huh?
+*/
+extern "C" int
+logout (char *line)
+{
+ sigframe thisframe (mainthread);
+ int res = 0;
+ HANDLE ut_fd;
+ static const char path_utmp[] = _PATH_UTMP;
+
+ path_conv win32_path (path_utmp);
+ if (win32_path.error)
+ return 0;
+
+ ut_fd = CreateFile (win32_path.get_win32 (),
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &sec_none_nih, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (ut_fd != INVALID_HANDLE_VALUE)
+ {
+ struct utmp *ut;
+ struct utmp ut_buf[100];
+ /* FIXME: utmp file access is not 64 bit clean for now. */
+ __off32_t pos = 0; /* Position in file */
+ DWORD rd;
+
+ while (!res && ReadFile (ut_fd, ut_buf, sizeof ut_buf, &rd, NULL)
+ && rd != 0)
+ {
+ struct utmp *ut_end = (struct utmp *) ((char *) ut_buf + rd);
+
+ for (ut = ut_buf; ut < ut_end; ut++, pos += sizeof (*ut))
+ if (ut->ut_name[0]
+ && strncmp (ut->ut_line, line, sizeof (ut->ut_line)) == 0)
+ /* Found the entry for LINE; mark it as logged out. */
+ {
+ /* Zero out entries describing who's logged in. */
+ bzero (ut->ut_name, sizeof (ut->ut_name));
+ bzero (ut->ut_host, sizeof (ut->ut_host));
+ time (&ut->ut_time);
+
+ /* Now seek back to the position in utmp at which UT occured,
+ and write the new version of UT there. */
+ if ((SetFilePointer (ut_fd, pos, 0, FILE_BEGIN) != 0xFFFFFFFF)
+ && (WriteFile (ut_fd, (char *) ut, sizeof (*ut),
+ &rd, NULL)))
+ {
+ res = 1;
+ break;
+ }
+ }
+ }
+
+ CloseHandle (ut_fd);
+ }
+
+ return res;
+}
+
+static int utmp_fd = -2;
+static char *utmp_file = (char *) _PATH_UTMP;
+
+static struct utmp utmp_data;
+
+extern "C" void
+setutent ()
+{
+ sigframe thisframe (mainthread);
+ if (utmp_fd == -2)
+ {
+ utmp_fd = open (utmp_file, O_RDWR);
+ }
+ lseek (utmp_fd, 0, SEEK_SET);
+}
+
+extern "C" void
+endutent ()
+{
+ sigframe thisframe (mainthread);
+ if (utmp_fd != -2)
+ {
+ close (utmp_fd);
+ utmp_fd = -2;
+ }
+}
+
+extern "C" void
+utmpname (_CONST char *file)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_empty_str (file))
+ {
+ debug_printf ("Invalid file");
+ return;
+ }
+ endutent ();
+ utmp_file = strdup (file);
+ debug_printf ("New UTMP file: %s", utmp_file);
+}
+
+extern "C" struct utmp *
+getutent ()
+{
+ sigframe thisframe (mainthread);
+ if (utmp_fd == -2)
+ setutent ();
+ if (read (utmp_fd, &utmp_data, sizeof (utmp_data)) != sizeof (utmp_data))
+ return NULL;
+ return &utmp_data;
+}
+
+extern "C" struct utmp *
+getutid (struct utmp *id)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_invalid_struct_errno (id))
+ return NULL;
+ while (read (utmp_fd, &utmp_data, sizeof (utmp_data)) == sizeof (utmp_data))
+ {
+ switch (id->ut_type)
+ {
+ case RUN_LVL:
+ case BOOT_TIME:
+ case OLD_TIME:
+ case NEW_TIME:
+ if (id->ut_type == utmp_data.ut_type)
+ return &utmp_data;
+ break;
+ case INIT_PROCESS:
+ case LOGIN_PROCESS:
+ case USER_PROCESS:
+ case DEAD_PROCESS:
+ if (strncmp (id->ut_id, utmp_data.ut_id, UT_IDLEN) == 0)
+ return &utmp_data;
+ break;
+ default:
+ return NULL;
+ }
+ }
+ return NULL;
+}
+
+extern "C" struct utmp *
+getutline (struct utmp *line)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_invalid_struct_errno (line))
+ return NULL;
+ while (read (utmp_fd, &utmp_data, sizeof (utmp_data)) == sizeof (utmp_data))
+ {
+ if ((utmp_data.ut_type == LOGIN_PROCESS ||
+ utmp_data.ut_type == USER_PROCESS) &&
+ !strncmp (utmp_data.ut_line, line->ut_line,
+ sizeof (utmp_data.ut_line)))
+ return &utmp_data;
+ }
+ return NULL;
+}
+
+extern "C" void
+pututline (struct utmp *ut)
+{
+ sigframe thisframe (mainthread);
+ if (check_null_invalid_struct (ut))
+ return;
+ setutent ();
+ struct utmp *u;
+ if ((u = getutid (ut)))
+ lseek (utmp_fd, -sizeof(struct utmp), SEEK_CUR);
+ else
+ lseek (utmp_fd, 0, SEEK_END);
+ (void) write (utmp_fd, (char *) ut, sizeof (struct utmp));
+}
diff --git a/winsup/cygwin/tty.cc b/winsup/cygwin/tty.cc
new file mode 100644
index 00000000000..aaabdd509c0
--- /dev/null
+++ b/winsup/cygwin/tty.cc
@@ -0,0 +1,464 @@
+/* tty.cc
+
+ Copyright 1997, 1998, 2000, 2001, 2002 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#include "winsup.h"
+#include <errno.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <wingdi.h>
+#include <winuser.h>
+#include <sys/cygwin.h>
+#include "cygerrno.h"
+#include "security.h"
+#include "fhandler.h"
+#include "path.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "pinfo.h"
+#include "cygwin/cygserver.h"
+#include "shared_info.h"
+#include "cygthread.h"
+
+extern fhandler_tty_master *tty_master;
+
+extern "C"
+int
+grantpt (int fd)
+{
+ return 0;
+}
+
+extern "C"
+int
+unlockpt (int fd)
+{
+ return 0;
+}
+
+extern "C"
+int
+ttyslot (void)
+{
+ if (NOTSTATE (myself, PID_USETTY))
+ return -1;
+ return myself->ctty;
+}
+
+void __stdcall
+tty_init (void)
+{
+ if (!myself->ppid_handle && NOTSTATE (myself, PID_CYGPARENT))
+ cygheap->fdtab.get_debugger_info ();
+
+ if (NOTSTATE (myself, PID_USETTY))
+ return;
+ if (myself->ctty == -1)
+ if (NOTSTATE (myself, PID_CYGPARENT))
+ myself->ctty = attach_tty (myself->ctty);
+ else
+ return;
+ if (myself->ctty == -1)
+ termios_printf ("Can't attach to tty");
+}
+
+/* Create session's master tty */
+
+void __stdcall
+create_tty_master (int ttynum)
+{
+ device ttym = *ttym_dev;
+ ttym.setunit (ttynum); /* CGF FIXME device */
+ tty_master = (fhandler_tty_master *)
+ cygheap->fdtab.build_fhandler (-1, ttym, "/dev/ttym", NULL);
+ if (tty_master->init ())
+ api_fatal ("Can't create master tty");
+ else
+ {
+ /* Log utmp entry */
+ struct utmp our_utmp;
+
+ bzero ((char *) &our_utmp, sizeof (utmp));
+ (void) time (&our_utmp.ut_time);
+ strncpy (our_utmp.ut_name, getlogin (), sizeof (our_utmp.ut_name));
+ cygwin_gethostname (our_utmp.ut_host, sizeof (our_utmp.ut_host));
+ __small_sprintf (our_utmp.ut_line, "tty%d", ttynum);
+ our_utmp.ut_type = USER_PROCESS;
+ our_utmp.ut_pid = myself->pid;
+ myself->ctty = ttynum;
+ login (&our_utmp);
+ }
+}
+
+void __stdcall
+tty_terminate (void)
+{
+ if (NOTSTATE (myself, PID_USETTY))
+ return;
+ cygwin_shared->tty.terminate ();
+}
+
+int __stdcall
+attach_tty (int num)
+{
+ if (num != -1)
+ {
+ return cygwin_shared->tty.connect_tty (num);
+ }
+ if (NOTSTATE (myself, PID_USETTY))
+ return -1;
+ return cygwin_shared->tty.allocate_tty (1);
+}
+
+void
+tty_list::terminate (void)
+{
+ int ttynum = myself->ctty;
+
+ /* Keep master running till there are connected clients */
+ if (ttynum != -1 && ttys[ttynum].master_pid == GetCurrentProcessId ())
+ {
+ tty *t = ttys + ttynum;
+ CloseHandle (t->from_master);
+ CloseHandle (t->to_master);
+ /* Wait for children which rely on tty handling in this process to
+ go away */
+ for (int i = 0; ; i++)
+ {
+ if (!t->slave_alive ())
+ break;
+ if (i >= 100)
+ {
+ small_printf ("waiting for children using tty%d to terminate\n",
+ ttynum);
+ i = 0;
+ }
+
+ low_priority_sleep (200);
+ }
+
+ termios_printf ("tty %d master about to finish", ttynum);
+ ForceCloseHandle1 (t->to_slave, to_pty);
+ ForceCloseHandle1 (t->from_slave, from_pty);
+ CloseHandle (tty_master->inuse);
+ t->init ();
+
+ char buf[20];
+ __small_sprintf (buf, "tty%d", ttynum);
+ logout (buf);
+ }
+}
+
+int
+tty_list::connect_tty (int ttynum)
+{
+ if (ttynum < 0 || ttynum >= NTTYS)
+ {
+ termios_printf ("ttynum (%d) out of range", ttynum);
+ return -1;
+ }
+ if (!ttys[ttynum].exists ())
+ {
+ termios_printf ("tty %d was not allocated", ttynum);
+ return -1;
+ }
+
+ return ttynum;
+}
+
+void
+tty_list::init (void)
+{
+ for (int i = 0; i < NTTYS; i++)
+ {
+ ttys[i].init ();
+ ttys[i].setntty (i);
+ }
+}
+
+/* Search for tty class for our console. Allocate new tty if our process is
+ the only cygwin process in the current console.
+ Return tty number or -1 if error.
+ If flag == 0, just find a free tty.
+ */
+int
+tty_list::allocate_tty (int with_console)
+{
+ HWND console;
+
+ /* FIXME: This whole function needs a protective mutex. */
+
+ if (!with_console)
+ console = NULL;
+ else if (!(console = GetConsoleWindow ()))
+ {
+ char *oldtitle = new char [TITLESIZE];
+
+ if (!oldtitle)
+ {
+ termios_printf ("Can't *allocate console title buffer");
+ return -1;
+ }
+ if (!GetConsoleTitle (oldtitle, TITLESIZE))
+ {
+ termios_printf ("Can't read console title");
+ return -1;
+ }
+
+ if (WaitForSingleObject (title_mutex, INFINITE) == WAIT_FAILED)
+ termios_printf ("WFSO for title_mutext %p failed, %E", title_mutex);
+
+ char buf[40];
+
+ __small_sprintf (buf, "cygwin.find.console.%d", myself->pid);
+ SetConsoleTitle (buf);
+ for (int times = 0; times < 25; times++)
+ {
+ Sleep (10);
+ if ((console = FindWindow (NULL, buf)))
+ break;
+ }
+ SetConsoleTitle (oldtitle);
+ Sleep (40);
+ ReleaseMutex (title_mutex);
+ if (console == NULL)
+ {
+ termios_printf ("Can't find console window");
+ return -1;
+ }
+ }
+ /* Is a tty allocated for console? */
+
+ int freetty = -1;
+ for (int i = 0; i < NTTYS; i++)
+ {
+ if (!ttys[i].exists ())
+ {
+ if (freetty < 0) /* Scanning? */
+ freetty = i; /* Yes. */
+ if (!with_console) /* Do we want to attach this to a console? */
+ break; /* No. We've got one. */
+ }
+
+ if (with_console && ttys[i].gethwnd () == console)
+ {
+ termios_printf ("console %x already associated with tty%d",
+ console, i);
+ /* Is the master alive? */
+ HANDLE hMaster;
+ hMaster = OpenProcess (PROCESS_DUP_HANDLE, FALSE, ttys[i].master_pid);
+ if (hMaster)
+ {
+ CloseHandle (hMaster);
+ return i;
+ }
+ /* Master is dead */
+ freetty = i;
+ break;
+ }
+ }
+
+ /* There is no tty allocated to console, allocate the first free found */
+ if (freetty == -1)
+ {
+ system_printf ("No free ttys available");
+ return -1;
+ }
+ tty *t = ttys + freetty;
+ t->init ();
+ t->setsid (-1);
+ t->setpgid (myself->pgid);
+ t->sethwnd (console);
+
+ if (with_console)
+ {
+ termios_printf ("console %x associated with tty%d", console, freetty);
+ create_tty_master (freetty);
+ }
+ else
+ termios_printf ("tty%d allocated", freetty);
+ return freetty;
+}
+
+BOOL
+tty::slave_alive ()
+{
+ return alive (TTY_SLAVE_ALIVE);
+}
+
+BOOL
+tty::master_alive ()
+{
+ return alive (TTY_MASTER_ALIVE);
+}
+
+BOOL
+tty::alive (const char *fmt)
+{
+ HANDLE ev;
+ char buf[sizeof (TTY_MASTER_ALIVE) + 16];
+
+ __small_sprintf (buf, fmt, ntty);
+ if ((ev = OpenEvent (EVENT_ALL_ACCESS, FALSE, buf)))
+ CloseHandle (ev);
+ return ev != NULL;
+}
+
+HANDLE
+tty::create_inuse (const char *fmt)
+{
+ HANDLE h;
+ char buf[sizeof (TTY_MASTER_ALIVE) + 16];
+
+ __small_sprintf (buf, fmt, ntty);
+ h = CreateEvent (&sec_all, TRUE, FALSE, buf);
+ termios_printf ("%s = %p", buf, h);
+ if (!h)
+ termios_printf ("couldn't open inuse event, %E", buf);
+ return h;
+}
+
+void
+tty::init (void)
+{
+ output_stopped = 0;
+ setsid (0);
+ pgid = 0;
+ hwnd = NULL;
+ to_slave = NULL;
+ from_slave = NULL;
+ was_opened = 0;
+}
+
+HANDLE
+tty::get_event (const char *fmt, BOOL manual_reset)
+{
+ HANDLE hev;
+ char buf[40];
+
+ __small_sprintf (buf, fmt, ntty);
+ if (!(hev = CreateEvent (&sec_all, manual_reset, FALSE, buf)))
+ {
+ termios_printf ("couldn't create %s", buf);
+ set_errno (ENOENT); /* FIXME this can't be the right errno */
+ return NULL;
+ }
+
+ termios_printf ("created event %s", buf);
+ return hev;
+}
+
+int
+tty::make_pipes (fhandler_pty_master *ptym)
+{
+ /* Create communication pipes */
+
+ /* FIXME: should this be sec_none_nih? */
+ if (CreatePipe (&from_master, &to_slave, &sec_all, 128 * 1024) == FALSE)
+ {
+ termios_printf ("can't create input pipe");
+ set_errno (ENOENT);
+ return FALSE;
+ }
+
+ // ProtectHandle1INH (to_slave, to_pty);
+ if (CreatePipe (&from_slave, &to_master, &sec_all, 128 * 1024) == FALSE)
+ {
+ termios_printf ("can't create output pipe");
+ set_errno (ENOENT);
+ return FALSE;
+ }
+ // ProtectHandle1INH (from_slave, from_pty);
+ termios_printf ("tty%d from_slave %p, to_slave %p", ntty, from_slave,
+ to_slave);
+
+ DWORD pipe_mode = PIPE_NOWAIT;
+ if (!SetNamedPipeHandleState (to_slave, &pipe_mode, NULL, NULL))
+ termios_printf ("can't set to_slave to non-blocking mode");
+ ptym->set_io_handle (from_slave);
+ ptym->set_output_handle (to_slave);
+ return TRUE;
+}
+
+BOOL
+tty::common_init (fhandler_pty_master *ptym)
+{
+ /* Set termios information. Force initialization. */
+ ptym->tcinit (this, TRUE);
+
+ if (!make_pipes (ptym))
+ return FALSE;
+ ptym->need_nl = 0;
+
+ /* Save our pid */
+
+ master_pid = GetCurrentProcessId ();
+
+ /* Allow the others to open us (for handle duplication) */
+
+ /* FIXME: we shold NOT set the security wide open when the
+ daemon is running
+ */
+ if (wincap.has_security ())
+ {
+ if (cygserver_running == CYGSERVER_UNKNOWN)
+ cygserver_init ();
+
+ if (cygserver_running != CYGSERVER_OK
+ && !SetKernelObjectSecurity (hMainProc,
+ DACL_SECURITY_INFORMATION,
+ get_null_sd ()))
+ system_printf ("Can't set process security, %E");
+ }
+
+ /* Create synchronisation events */
+
+ if (ptym->get_major () != DEV_TTYM_MAJOR)
+ {
+ ptym->output_done_event = ptym->ioctl_done_event =
+ ptym->ioctl_request_event = NULL;
+ }
+ else
+ {
+ if (!(ptym->output_done_event = get_event (OUTPUT_DONE_EVENT)))
+ return FALSE;
+ if (!(ptym->ioctl_done_event = get_event (IOCTL_DONE_EVENT)))
+ return FALSE;
+ if (!(ptym->ioctl_request_event = get_event (IOCTL_REQUEST_EVENT)))
+ return FALSE;
+ }
+
+ if (!(ptym->input_available_event = get_event (INPUT_AVAILABLE_EVENT, TRUE)))
+ return FALSE;
+
+ char buf[40];
+ __small_sprintf (buf, OUTPUT_MUTEX, ntty);
+ if (!(ptym->output_mutex = CreateMutex (&sec_all, FALSE, buf)))
+ {
+ termios_printf ("can't create %s", buf);
+ set_errno (ENOENT);
+ return FALSE;
+ }
+
+ __small_sprintf (buf, INPUT_MUTEX, ntty);
+ if (!(ptym->input_mutex = CreateMutex (&sec_all, FALSE, buf)))
+ {
+ termios_printf ("can't create %s", buf);
+ set_errno (ENOENT);
+ return FALSE;
+ }
+
+ ProtectHandle1INH (ptym->output_mutex, output_mutex);
+ ProtectHandle1INH (ptym->input_mutex, input_mutex);
+ winsize.ws_col = 80;
+ winsize.ws_row = 25;
+
+ termios_printf ("tty%d opened", ntty);
+ return TRUE;
+}
diff --git a/winsup/cygwin/winsup.h b/winsup/cygwin/winsup.h
new file mode 100644
index 00000000000..a2b54199fd7
--- /dev/null
+++ b/winsup/cygwin/winsup.h
@@ -0,0 +1,306 @@
+/* winsup.h: main Cygwin header file.
+
+ Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003 Red Hat, Inc.
+
+This file is part of Cygwin.
+
+This software is a copyrighted work licensed under the terms of the
+Cygwin license. Please consult the file "CYGWIN_LICENSE" for
+details. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#define __INSIDE_CYGWIN__
+
+#define strlen __builtin_strlen
+#define strcmp __builtin_strcmp
+#define strcpy __builtin_strcpy
+#define memcpy __builtin_memcpy
+#define memcmp __builtin_memcmp
+#ifdef HAVE_BUILTIN_MEMSET
+# define memset __builtin_memset
+#endif
+
+#define NO_COPY __attribute__((nocommon)) __attribute__((section(".data_cygwin_nocopy")))
+
+#ifdef __cplusplus
+
+#if !defined(__STDC_VERSION__) || __STDC_VERSION__ >= 199900L
+#define NEW_MACRO_VARARGS
+#endif
+
+#ifndef _WIN32_WINNT
+#define _WIN32_WINNT 0x0500
+#endif
+
+#include <sys/types.h>
+#include <sys/strace.h>
+
+extern const char case_folded_lower[];
+#define cyg_tolower(c) (case_folded_lower[(unsigned char)(c)])
+extern const char case_folded_upper[];
+#define cyg_toupper(c) (case_folded_upper[(unsigned char)(c)])
+
+#ifndef MALLOC_DEBUG
+#define cfree newlib_cfree_dont_use
+#endif
+
+#define WIN32_LEAN_AND_MEAN 1
+#define _WINGDI_H
+#define _WINUSER_H
+#define _WINNLS_H
+#define _WINVER_H
+#define _WINNETWK_H
+#define _WINSVC_H
+#include <windows.h>
+#include <wincrypt.h>
+#include <lmcons.h>
+#undef _WINGDI_H
+#undef _WINUSER_H
+#undef _WINNLS_H
+#undef _WINVER_H
+#undef _WINNETWK_H
+#undef _WINSVC_H
+
+#include "wincap.h"
+
+/* The one function we use from winuser.h most of the time */
+extern "C" DWORD WINAPI GetLastError (void);
+
+enum codepage_type {ansi_cp, oem_cp};
+extern codepage_type current_codepage;
+
+UINT get_cp ();
+
+int __stdcall sys_wcstombs(char *, const WCHAR *, int)
+ __attribute__ ((regparm(3)));
+
+int __stdcall sys_mbstowcs(WCHAR *, const char *, int)
+ __attribute__ ((regparm(3)));
+
+/* Used to check if Cygwin DLL is dynamically loaded. */
+extern int dynamically_loaded;
+
+extern int cygserver_running;
+
+#define TITLESIZE 1024
+
+/* status bit manipulation */
+#define __ISSETF(what, x, prefix) \
+ ((what)->status & prefix##_##x)
+#define __SETF(what, x, prefix) \
+ ((what)->status |= prefix##_##x)
+#define __CLEARF(what, x, prefix) \
+ ((what)->status &= ~prefix##_##x)
+#define __CONDSETF(n, what, x, prefix) \
+ ((n) ? __SETF (what, x, prefix) : __CLEARF (what, x, prefix))
+
+#include "debug.h"
+
+/* Events/mutexes */
+extern HANDLE title_mutex;
+
+/**************************** Convenience ******************************/
+
+/* Used when treating / and \ as equivalent. */
+#define isdirsep(ch) \
+ ({ \
+ char __c = (ch); \
+ ((__c) == '/' || (__c) == '\\'); \
+ })
+
+/* Convert a signal to a signal mask */
+#define SIGTOMASK(sig) (1<<((sig) - signal_shift_subtract))
+extern unsigned int signal_shift_subtract;
+
+#ifdef NEW_MACRO_VARARGS
+# define api_fatal(...) __api_fatal ("%P: *** " __VA_ARGS__)
+#else
+# define api_fatal(fmt, args...) __api_fatal ("%P: *** " fmt,## args)
+#endif
+
+#undef issep
+#define issep(ch) (strchr (" \t\n\r", (ch)) != NULL)
+
+#define isabspath(p) \
+ (isdirsep (*(p)) || (isalpha (*(p)) && (p)[1] == ':' && (!(p)[2] || isdirsep ((p)[2]))))
+
+/******************** Initialization/Termination **********************/
+
+class per_process;
+/* cygwin .dll initialization */
+void dll_crt0 (per_process *) __asm__ ("_dll_crt0__FP11per_process");
+extern "C" void __stdcall _dll_crt0 ();
+
+/* dynamically loaded dll initialization */
+extern "C" int dll_dllcrt0 (HMODULE, per_process *);
+
+/* dynamically loaded dll initialization for non-cygwin apps */
+extern "C" int dll_noncygwin_dllcrt0 (HMODULE, per_process *);
+
+/* exit the program */
+extern "C" void __stdcall do_exit (int) __attribute__ ((noreturn));
+
+/* UID/GID */
+void uinfo_init (void);
+
+#define ILLEGAL_UID16 ((__uid16_t)-1)
+#define ILLEGAL_UID ((__uid32_t)-1)
+#define ILLEGAL_GID16 ((__gid16_t)-1)
+#define ILLEGAL_GID ((__gid32_t)-1)
+#define ILLEGAL_SEEK ((__off64_t)-1)
+
+#define uid16touid32(u16) ((u16)==ILLEGAL_UID16?ILLEGAL_UID:(__uid32_t)(u16))
+#define gid16togid32(g16) ((g16)==ILLEGAL_GID16?ILLEGAL_GID:(__gid32_t)(g16))
+
+extern "C" __uid32_t getuid32 (void);
+extern "C" __uid32_t geteuid32 (void);
+extern "C" struct passwd *getpwuid32 (__uid32_t);
+
+/* various events */
+void events_init (void);
+void events_terminate (void);
+
+void __stdcall close_all_files (void);
+BOOL __stdcall check_pty_fds (void);
+
+/* Invisible window initialization/termination. */
+HWND __stdcall gethwnd (void);
+
+/* Globals that handle initialization of winsock in a child process. */
+extern HANDLE wsock32_handle;
+extern HANDLE ws2_32_handle;
+
+/* Globals that handle initialization of netapi in a child process. */
+extern HANDLE netapi32_handle;
+
+/* debug_on_trap support. see exceptions.cc:try_to_debug() */
+extern "C" void error_start_init (const char*);
+extern "C" int try_to_debug (bool waitloop = 1);
+
+void set_file_api_mode (codepage_type);
+
+extern int cygwin_finished_initializing;
+
+/**************************** Miscellaneous ******************************/
+
+void __stdcall set_std_handle (int);
+int __stdcall writable_directory (const char *file);
+int __stdcall stat_dev (DWORD, int, unsigned long, struct __stat64 *);
+extern BOOL allow_ntsec;
+
+unsigned long __stdcall hash_path_name (unsigned long hash, const char *name) __attribute__ ((regparm(2)));
+void __stdcall nofinalslash (const char *src, char *dst) __attribute__ ((regparm(2)));
+extern "C" char *__stdcall rootdir (char *full_path) __attribute__ ((regparm(1)));
+
+/* String manipulation */
+extern "C" char *__stdcall strccpy (char *s1, const char **s2, char c);
+extern "C" int __stdcall strcasematch (const char *s1, const char *s2) __attribute__ ((regparm(2)));
+extern "C" int __stdcall strncasematch (const char *s1, const char *s2, size_t n) __attribute__ ((regparm(3)));
+extern "C" char *__stdcall strcasestr (const char *searchee, const char *lookfor) __attribute__ ((regparm(2)));
+
+/* Time related */
+void __stdcall totimeval (struct timeval *dst, FILETIME * src, int sub, int flag);
+long __stdcall to_time_t (FILETIME * ptr);
+void __stdcall to_timestruc_t (FILETIME * ptr, timestruc_t * out);
+void __stdcall time_as_timestruc_t (timestruc_t * out);
+
+void __stdcall set_console_title (char *);
+void early_stuff_init ();
+
+int __stdcall check_null_str (const char *name) __attribute__ ((regparm(1)));
+int __stdcall check_null_empty_str (const char *name) __attribute__ ((regparm(1)));
+int __stdcall check_null_empty_str_errno (const char *name) __attribute__ ((regparm(1)));
+int __stdcall check_null_str_errno (const char *name) __attribute__ ((regparm(1)));
+int __stdcall __check_null_invalid_struct (void *s, unsigned sz) __attribute__ ((regparm(2)));
+int __stdcall __check_null_invalid_struct_errno (void *s, unsigned sz) __attribute__ ((regparm(2)));
+int __stdcall __check_invalid_read_ptr_errno (const void *s, unsigned sz) __attribute__ ((regparm(2)));
+
+#define check_null_invalid_struct(s) \
+ __check_null_invalid_struct ((s), sizeof (*(s)))
+#define check_null_invalid_struct_errno(s) \
+ __check_null_invalid_struct_errno ((s), sizeof (*(s)))
+
+struct iovec;
+ssize_t check_iovec_for_read (const struct iovec *, int) __attribute__ ((regparm(2)));
+ssize_t check_iovec_for_write (const struct iovec *, int) __attribute__ ((regparm(2)));
+
+#define set_winsock_errno() __set_winsock_errno (__FUNCTION__, __LINE__)
+void __set_winsock_errno (const char *fn, int ln) __attribute__ ((regparm(2)));
+
+extern bool wsock_started;
+
+/* Printf type functions */
+extern "C" void __api_fatal (const char *, ...) __attribute__ ((noreturn));
+extern "C" int __small_sprintf (char *dst, const char *fmt, ...) /*__attribute__ ((regparm (2)))*/;
+extern "C" int __small_vsprintf (char *dst, const char *fmt, va_list ap) /*__attribute__ ((regparm (3)))*/;
+extern void multiple_cygwin_problem (const char *, unsigned, unsigned);
+
+class path_conv;
+int __stdcall stat_worker (const char *name, struct __stat64 *buf, int nofollow,
+ path_conv *pc = NULL) __attribute__ ((regparm (3)));
+
+int symlink_worker (const char *, const char *, bool, bool)
+ __attribute__ ((regparm (3)));
+
+int __stdcall low_priority_sleep (DWORD) __attribute__ ((regparm (1)));
+#define SLEEP_0_STAY_LOW INFINITE
+
+/**************************** Exports ******************************/
+
+extern "C" {
+int cygwin_select (int , fd_set *, fd_set *, fd_set *,
+ struct timeval *to);
+int cygwin_gethostname (char *__name, size_t __len);
+
+int kill_pgrp (pid_t, int);
+int _kill (int, int);
+int _raise (int sig);
+
+extern DWORD binmode;
+extern char _data_start__, _data_end__, _bss_start__, _bss_end__;
+extern void (*__CTOR_LIST__) (void);
+extern void (*__DTOR_LIST__) (void);
+extern SYSTEM_INFO system_info;
+};
+
+/*************************** Unsorted ******************************/
+
+#define WM_ASYNCIO 0x8000 // WM_APP
+
+/* Note that MAX_PATH is defined in the windows headers */
+/* There is also PATH_MAX and MAXPATHLEN.
+ PATH_MAX is from Posix and does *not* include the trailing NUL.
+ MAXPATHLEN is from Unix.
+
+ Thou shalt use MAX_PATH throughout. It avoids the NUL vs no-NUL
+ issue and is neither of the Unixy ones [so we can punt on which
+ one is the right one to use]. */
+
+#define STD_RBITS (S_IRUSR | S_IRGRP | S_IROTH)
+#define STD_WBITS (S_IWUSR)
+#define STD_XBITS (S_IXUSR | S_IXGRP | S_IXOTH)
+#define NO_W ~(S_IWUSR | S_IWGRP | S_IWOTH)
+#define NO_R ~(S_IRUSR | S_IRGRP | S_IROTH)
+#define NO_X ~(S_IXUSR | S_IXGRP | S_IXOTH)
+
+/* The title on program start. */
+extern char *old_title;
+extern BOOL display_title;
+
+extern HANDLE hMainThread;
+extern HANDLE hMainProc;
+
+extern bool cygwin_testing;
+extern unsigned _cygwin_testing_magic;
+extern HMODULE cygwin_hmodule;
+
+extern char almost_null[];
+
+#define winsock2_active (wsadata.wVersion >= 512)
+#define winsock_active (wsadata.wVersion < 512)
+extern struct WSAData wsadata;
+
+#endif /* defined __cplusplus */