summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ChangeLog90
-rw-r--r--NMakefile.template135
-rw-r--r--README.W3249
-rw-r--r--README.WIN3249
-rw-r--r--build_w32.bat136
-rw-r--r--config.h.W32.template289
-rw-r--r--config.h.WIN32289
-rw-r--r--dir.c102
-rw-r--r--function.c67
-rw-r--r--job.c198
-rw-r--r--main.c183
-rw-r--r--make.h16
-rw-r--r--misc.c31
-rw-r--r--read.c41
-rw-r--r--remake.c10
-rw-r--r--subproc.bat3
-rw-r--r--variable.c48
-rw-r--r--vpath.c18
-rw-r--r--w32/compat/dirent.c188
-rw-r--r--w32/include/dirent.h37
-rw-r--r--w32/include/pathstuff.h10
-rw-r--r--w32/include/sub_proc.h54
-rw-r--r--w32/include/w32err.h10
-rw-r--r--w32/pathstuff.c219
-rw-r--r--w32/subproc/NMakefile59
-rw-r--r--w32/subproc/build.bat10
-rw-r--r--w32/subproc/misc.c63
-rw-r--r--w32/subproc/proc.h13
-rw-r--r--w32/subproc/sub_proc.c1100
-rw-r--r--w32/subproc/w32err.c51
30 files changed, 3551 insertions, 17 deletions
diff --git a/ChangeLog b/ChangeLog
index 78d9bc1..5ed4f0b 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,93 @@
+Wed May 15 10:14:14 CDT 1996 Rob Tulloh <tulloh@tivoli.com>
+
+ * dir.c: WIN32 does not support inode. For now, fully qualified
+ pathname along with st_mtime will be keys for files.
+ Fixed problem where vpath can be confused when files
+ are added to a directory after the directory has already been
+ read in. The code now attempts to reread the directory if it
+ discovers that the datestamp on the directory has changed since
+ it was cached by make. This problem only seems to occur on WIN32
+ right now so it is lumped under port #ifdef WIN32.
+
+ * function.c: WIN32: call subproc library (CreateProcess()) instead of
+ fork/exec.
+
+ * job.c: WIN32: Added the code to do fork/exec/waitpid style processing
+ on WIN32 systems via calls to subproc library.
+
+ * main.c: WIN32: Several things added here. First, there is code
+ for dealing with PATH and SHELL defaults. Make tries to figure
+ out if the user has %PATH% set in the environment and sets it to
+ %Path% if it is not set already. Make also looks to see if sh.exe
+ is anywhere to be found. Code path through job.c will change
+ based on existence of a working Bourne shell. The checking for
+ default shell is done twice: once before makefiles are read in
+ and again after. Fall back to MSDOS style execution mode if no sh.exe
+ is found. Also added some debug support that allows user to pause make
+ with -D switch and attach a debugger. This is especially useful for
+ debugging recursive calls to make where problems appear only in the
+ sub-make.
+
+ * make.h: WIN32: A few macros and header files for WIN32 support.
+
+ * misc.c: WIN32: Added a function end_of_token_w32() to assist
+ in parsing code in read.c.
+
+ * read.c: WIN32: Fixes similar to MSDOS which allow colon to
+ appear in filenames. Use of colon in filenames would otherwise
+ confuse make.
+
+ * remake.c: WIN32: Added include of io.h to eliminate compiler
+ warnings. Added some code to default LIBDIR if it is not set
+ on WIN32.
+
+ * variable.c: WIN32: Added support for detecting Path/PATH
+ and converting them to semicolon separated lists for make's
+ internal use. New function sync_Path_environment()
+ which is called in job.c and function.c before creating a new
+ process. Caller must set Path in environment since we don't
+ have fork() to do this for us.
+
+ * vpath.c: WIN32: Added detection for filenames containing
+ forward or backward slashes.
+
+ * NMakefile: WIN32: Visual C compatible makefile for use with nmake.
+ Use this to build GNU make the first time on Windows NT or Windows 95.
+
+ * README.WIN32: WIN32: Contains some helpful notes.
+
+ * build_w32.bat: WIN32: If you don't like nmake, use this the first
+ time you build GNU make on Windows NT or Windows 95.
+
+ * config.h.WIN32: WIN32 version of config.h
+
+ * subproc.bat: WIN32: A bat file used to build the
+ subproc library from the top-level NMakefile. Needed because
+ WIndows 95 (nmake) doesn't allow you to cd in a make rule.
+
+ * w32/include/dirent.h
+ * w32/compat/dirent.c: WIN32: opendir, readdir, closedir, etc.
+
+ * w32/include/pathstuff.h: WIN32: used by files needed functions
+ defined in pathstuff.c (prototypes).
+
+ * w32/include/sub_proc.h: WIN32: prototypes for subproc.lib functions.
+
+ * w32/include/w32err.h: WIN32: prototypes for w32err.c.
+
+ * w32/pathstuff.c: WIN32: File and Path/Path conversion functions.
+
+ * w32/subproc/build.bat: WIN32: build script for subproc library
+ if you don't wish to use nmake.
+
+ * w32/subproc/NMakefile: WIN32: Visual C compatible makefile for use
+ with nmake. Used to build subproc library.
+
+ * w32/subproc/misc.c: WIN32: subproc library support code
+ * w32/subproc/proc.h: WIN32: subproc library support code
+ * w32/subproc/sub_proc.c: WIN32: subproc library source code
+ * w32/subproc/w32err.c: WIN32: subproc library support code
+
Wed May 22 17:24:51 1996 Roland McGrath <roland@delasyd.gnu.ai.mit.edu>
* makefile.vms: Set LOADLIBES.
diff --git a/NMakefile.template b/NMakefile.template
new file mode 100644
index 0000000..f546074
--- /dev/null
+++ b/NMakefile.template
@@ -0,0 +1,135 @@
+# NOTE: If you have no `make' program at all to process this makefile, run
+# `build_w32.bat' instead.
+#
+# Copyright (C) 1988, 89, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc.
+# This file is part of GNU Make.
+#
+# GNU Make is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Make is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Make; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#
+# NMakefile for GNU Make
+#
+
+LINK = link
+CC = cl
+
+OUTDIR=.
+MAKEFILE=NMakefile
+SUBPROC_MAKEFILE=NMakefile
+
+CFLAGS_any = /nologo /MT /W3 /GX /Zi /YX /I . /I glob /I w32/include /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES
+CFLAGS_debug = $(CFLAGS_any) /Od /D _DEBUG /FR.\WinDebug/ /Fp.\WinDebug/make.pch /Fo.\WinDebug/ /Fd.\WinDebug/make.pdb
+CFLAGS_release = $(CFLAGS_any) /O2 /D NDEBUG /FR.\WinRel/ /Fp.\WinRel/make.pch /Fo.\WinRel/
+
+LDFLAGS_debug = w32\subproc\WinDebug\subproc.lib /NOLOGO /SUBSYSTEM:console\
+ /INCREMENTAL:no /PDB:WinRel/make.pdb /MACHINE:I386 /OUT:WinDebug/make.exe
+LDFLAGS_release = w32\subproc\WinRel\subproc.lib /NOLOGO /SUBSYSTEM:console\
+ /INCREMENTAL:no /MACHINE:I386 /OUT:WinRel/make.exe
+
+all: subproc Release Debug
+
+#
+# Make sure we build the subproc library first. It has it's own
+# makefile. To be portable to Windows 95, we put the instructions
+# on how to build the library into a batch file. On NT, we could
+# simply have done foo && bar && dog, but this doesn't port.
+#
+subproc: w32/subproc/WinDebug/subproc.lib w32/subproc/WinRel/subproc.lib
+
+w32/subproc/WinDebug/subproc.lib w32/subproc/WinRel/subproc.lib:
+ subproc.bat $(SUBPROC_MAKEFILE)
+
+Release:
+ nmake /f $(MAKEFILE) LDFLAGS="$(LDFLAGS_release)" CFLAGS="$(CFLAGS_release)" OUTDIR=WinRel WinRel/make.exe
+Debug:
+ nmake /f $(MAKEFILE) LDFLAGS="$(LDFLAGS_debug)" CFLAGS="$(CFLAGS_debug)" OUTDIR=WinDebug WinDebug/make.exe
+
+clean:
+ rmdir /s /q WinDebug WinRel
+
+$(OUTDIR):
+ if not exist .\$@\nul mkdir .\$@
+
+LIBS = kernel32.lib user32.lib advapi32.lib
+
+OBJS = \
+ $(OUTDIR)/ar.obj \
+ $(OUTDIR)/arscan.obj \
+ $(OUTDIR)/commands.obj \
+ $(OUTDIR)/default.obj \
+ $(OUTDIR)/dir.obj \
+ $(OUTDIR)/expand.obj \
+ $(OUTDIR)/file.obj \
+ $(OUTDIR)/function.obj \
+ $(OUTDIR)/getloadavg.obj \
+ $(OUTDIR)/getopt.obj \
+ $(OUTDIR)/getopt1.obj \
+ $(OUTDIR)/implicit.obj \
+ $(OUTDIR)/job.obj \
+ $(OUTDIR)/main.obj \
+ $(OUTDIR)/misc.obj \
+ $(OUTDIR)/read.obj \
+ $(OUTDIR)/remake.obj \
+ $(OUTDIR)/remote-stub.obj \
+ $(OUTDIR)/rule.obj \
+ $(OUTDIR)/signame.obj \
+ $(OUTDIR)/variable.obj \
+ $(OUTDIR)/version.obj \
+ $(OUTDIR)/vpath.obj \
+ $(OUTDIR)/glob.obj \
+ $(OUTDIR)/fnmatch.obj \
+ $(OUTDIR)/dirent.obj \
+ $(OUTDIR)/pathstuff.obj
+
+$(OUTDIR)/make.exe: $(OUTDIR) $(OBJS)
+ $(LINK) @<<
+ $(LDFLAGS) $(LIBS) $(OBJS)
+<<
+
+.c{$(OUTDIR)}.obj:
+ $(CC) $(CFLAGS) /c $<
+
+$(OUTDIR)/ar.obj : ar.c make.h filedef.h dep.h
+$(OUTDIR)/arscan.obj : arscan.c make.h
+$(OUTDIR)/commands.obj : commands.c
+$(OUTDIR)/default.obj : default.c make.h rule.h dep.h filedef.h job.h commands.h variable.h
+$(OUTDIR)/dir.obj : dir.c make.h
+$(OUTDIR)/expand.obj : expand.c make.h filedef.h job.h commands.h variable.h
+$(OUTDIR)/file.obj : file.c make.h dep.h filedef.h job.h commands.h variable.h
+$(OUTDIR)/function.obj : function.c make.h filedef.h variable.h dep.h job.h commands.h
+$(OUTDIR)/getloadavg.obj : getloadavg.c
+$(OUTDIR)/getopt.obj : getopt.c
+$(OUTDIR)/getopt1.obj : getopt1.c getopt.h
+$(OUTDIR)/implicit.obj : implicit.c make.h rule.h dep.h filedef.h
+$(OUTDIR)/job.obj : job.c make.h job.h filedef.h commands.h variable.h
+$(OUTDIR)/main.obj : main.c make.h dep.h filedef.h variable.h job.h commands.h getopt.h
+$(OUTDIR)/misc.obj : misc.c make.h dep.h
+$(OUTDIR)/read.obj : read.c make.h dep.h filedef.h job.h commands.h variable.h glob/glob.h
+$(OUTDIR)/remake.obj : remake.c make.h filedef.h job.h commands.h dep.h
+$(OUTDIR)/remote-stub.obj : remote-stub.c make.h filedef.h job.h commands.h
+$(OUTDIR)/rule.obj : rule.c make.h dep.h filedef.h job.h commands.h variable.h rule.h
+$(OUTDIR)/signame.obj : signame.c signame.h
+$(OUTDIR)/variable.obj : variable.c make.h dep.h filedef.h job.h commands.h variable.h
+$(OUTDIR)/version.obj : version.c
+$(OUTDIR)/vpath.obj : vpath.c make.h filedef.h variable.h
+$(OUTDIR)/glob.obj : glob/glob.c
+ $(CC) $(CFLAGS) /c $?
+$(OUTDIR)/fnmatch.obj : glob/fnmatch.c
+ $(CC) $(CFLAGS) /c $?
+$(OUTDIR)/dirent.obj : w32/compat/dirent.c
+ $(CC) $(CFLAGS) /c $?
+$(OUTDIR)/pathstuff.obj : w32/pathstuff.c
+ $(CC) $(CFLAGS) /c $?
+
diff --git a/README.W32 b/README.W32
new file mode 100644
index 0000000..9e4c56e
--- /dev/null
+++ b/README.W32
@@ -0,0 +1,49 @@
+Port of GNU make to Windows NT and Windows 95
+Builds natively with MSVC 2.x or MSVC 4.x compilers.
+
+To build with nmake on Windows NT or Windows 95:
+
+ 1. Make sure cl.exe is in your %Path%. Example:
+
+ set Path=%Path%;c:/msdev/bin
+
+ 2. Make sure %include% is set to msvc include directory. Example:
+
+ set include=c:/msdev/include
+
+ 3. Make sure %lib% is set to msvc lib directory. Example:
+
+ set lib=c:/msdev/lib
+
+ 4. nmake /f NMakefile
+
+
+There is a bat file (build_w32.bat) for folks who have fear of nmake.
+
+Outputs:
+
+ WinDebug/make.exe
+ WinRel/make.exe
+
+Notes:
+
+ This port prefers you have a working sh.exe somewhere on your
+ system. If you don't have sh.exe, port falls back to
+ MSDOS mode for launching programs (via a batch file).
+ The MSDOS mode style execution has not been tested too
+ carefully though (I use GNU bash as sh.exe).
+
+ I verified all functionality with a slightly modified version
+ of make-test-0.4.5 (modifications to get test suite to run
+ on Windows NT). All tests pass in an environment that includes
+ sh.exe.
+
+ I did not provide a Visual C project file with this port as
+ the project file would not be considered freely distributable
+ (or so I think). It is easy enough to create one though if
+ you know how to use Visual C.
+
+ I build the program statically to avoid problems locating DLL's
+ on machines that may not have MSVC runtime installed. If you
+ prefer, you can change make to build with shared libraries by
+ changing /MT to /MD in the NMakefile (or build_w32.bat).
diff --git a/README.WIN32 b/README.WIN32
new file mode 100644
index 0000000..9e4c56e
--- /dev/null
+++ b/README.WIN32
@@ -0,0 +1,49 @@
+Port of GNU make to Windows NT and Windows 95
+Builds natively with MSVC 2.x or MSVC 4.x compilers.
+
+To build with nmake on Windows NT or Windows 95:
+
+ 1. Make sure cl.exe is in your %Path%. Example:
+
+ set Path=%Path%;c:/msdev/bin
+
+ 2. Make sure %include% is set to msvc include directory. Example:
+
+ set include=c:/msdev/include
+
+ 3. Make sure %lib% is set to msvc lib directory. Example:
+
+ set lib=c:/msdev/lib
+
+ 4. nmake /f NMakefile
+
+
+There is a bat file (build_w32.bat) for folks who have fear of nmake.
+
+Outputs:
+
+ WinDebug/make.exe
+ WinRel/make.exe
+
+Notes:
+
+ This port prefers you have a working sh.exe somewhere on your
+ system. If you don't have sh.exe, port falls back to
+ MSDOS mode for launching programs (via a batch file).
+ The MSDOS mode style execution has not been tested too
+ carefully though (I use GNU bash as sh.exe).
+
+ I verified all functionality with a slightly modified version
+ of make-test-0.4.5 (modifications to get test suite to run
+ on Windows NT). All tests pass in an environment that includes
+ sh.exe.
+
+ I did not provide a Visual C project file with this port as
+ the project file would not be considered freely distributable
+ (or so I think). It is easy enough to create one though if
+ you know how to use Visual C.
+
+ I build the program statically to avoid problems locating DLL's
+ on machines that may not have MSVC runtime installed. If you
+ prefer, you can change make to build with shared libraries by
+ changing /MT to /MD in the NMakefile (or build_w32.bat).
diff --git a/build_w32.bat b/build_w32.bat
new file mode 100644
index 0000000..5a0c9c1
--- /dev/null
+++ b/build_w32.bat
@@ -0,0 +1,136 @@
+cd w32\subproc
+echo "Creating the subproc library"
+%ComSpec% /c build.bat
+cd ..\..
+del link.dbg link.rel
+del config.h
+copy config.h.WIN32 config.h
+echo off
+echo "Creating GNU make 3.74 for Windows NT"
+echo on
+if not exist .\WinDebug\nul mkdir .\WinDebug
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D TIVOLI /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c variable.c
+echo WinDebug\variable.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c rule.c
+echo WinDebug\rule.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c remote-stub.c
+echo WinDebug\remote-stub.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c commands.c
+echo WinDebug\commands.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c file.c
+echo WinDebug\file.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c getloadavg.c
+echo WinDebug\getloadavg.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c default.c
+echo WinDebug\default.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c signame.c
+echo WinDebug\signame.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c expand.c
+echo WinDebug\expand.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c dir.c
+echo WinDebug\dir.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c main.c
+echo WinDebug\main.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c getopt1.c
+echo WinDebug\getopt1.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c job.c
+echo WinDebug\job.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c read.c
+echo WinDebug\read.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c version.c
+echo WinDebug\version.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c getopt.c
+echo WinDebug\getopt.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c arscan.c
+echo WinDebug\arscan.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c remake.c
+echo WinDebug\remake.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c misc.c
+echo WinDebug\misc.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c ar.c
+echo WinDebug\ar.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c function.c
+echo WinDebug\function.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c vpath.c
+echo WinDebug\vpath.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c implicit.c
+echo WinDebug\implicit.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c .\w32\compat\dirent.c
+echo WinDebug\dirent.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c .\glob\glob.c
+echo WinDebug\glob.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c .\glob\fnmatch.c
+echo WinDebug\fnmatch.obj >>link.dbg
+cl.exe /nologo /MT /W3 /GX /Zi /YX /Od /I . /I glob /I w32/include /D _DEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinDebug/ /Fp.\WinDebug/make374.pch /Fo.\WinDebug/ /Fd.\WinDebug/make374.pdb /c .\w32\pathstuff.c
+echo WinDebug\pathstuff.obj >>link.dbg
+echo off
+echo "Linking WinDebug/make374.exe"
+rem link.exe kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\windebug\subproc.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes /PDB:.\WinDebug/make374.pdb /DEBUG /MACHINE:I386 /OUT:.\WinDebug/make374.exe .\WinDebug/variable.obj .\WinDebug/rule.obj .\WinDebug/remote-stub.obj .\WinDebug/commands.obj .\WinDebug/file.obj .\WinDebug/getloadavg.obj .\WinDebug/default.obj .\WinDebug/signame.obj .\WinDebug/expand.obj .\WinDebug/dir.obj .\WinDebug/main.obj .\WinDebug/getopt1.obj .\WinDebug/job.obj .\WinDebug/read.obj .\WinDebug/version.obj .\WinDebug/getopt.obj .\WinDebug/arscan.obj .\WinDebug/remake.obj .\WinDebug/misc.obj .\WinDebug/ar.obj .\WinDebug/function.obj .\WinDebug/vpath.obj .\WinDebug/implicit.obj .\WinDebug/dirent.obj .\WinDebug/glob.obj .\WinDebug/fnmatch.obj .\WinDebug/pathstuff.obj
+echo kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\windebug\subproc.lib >>link.dbg
+link.exe /NOLOGO /SUBSYSTEM:console /INCREMENTAL:yes /PDB:.\WinDebug/make374.pdb /DEBUG /MACHINE:I386 /OUT:.\WinDebug/make374.exe @link.dbg
+if not exist .\WinDebug/make374.exe echo "WinDebug build failed"
+if exist .\WinDebug/make374.exe echo "WinDebug build succeeded!"
+if not exist .\WinRel\nul mkdir .\WinRel
+echo on
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /D TIVOLI /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c variable.c
+echo WinRel\variable.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c rule.c
+echo WinRel\rule.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c remote-stub.c
+echo WinRel\remote-stub.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c commands.c
+echo WinRel\commands.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c file.c
+echo WinRel\file.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c getloadavg.c
+echo WinRel\getloadavg.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c default.c
+echo WinRel\default.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c signame.c
+echo WinRel\signame.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c expand.c
+echo WinRel\expand.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c dir.c
+echo WinRel\dir.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c main.c
+echo WinRel\main.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c getopt1.c
+echo WinRel\getopt1.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c job.c
+echo WinRel\job.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c read.c
+echo WinRel\read.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c version.c
+echo WinRel\version.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c getopt.c
+echo WinRel\getopt.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c arscan.c
+echo WinRel\arscan.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c remake.c
+echo WinRel\remake.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c misc.c
+echo WinRel\misc.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c ar.c
+echo WinRel\ar.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c function.c
+echo WinRel\function.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c vpath.c
+echo WinRel\vpath.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c implicit.c
+echo WinRel\implicit.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c .\w32\compat\dirent.c
+echo WinRel\dirent.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c .\glob\glob.c
+echo WinRel\glob.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c .\glob\fnmatch.c
+echo WinRel\fnmatch.obj >>link.rel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I . /I glob /I w32/include /D NDEBUG /D WIN32 /D _CONSOLE /D HAVE_CONFIG_H /D NO_ARCHIVES /FR.\WinRel/ /Fp.\WinRel/make374.pch /Fo.\WinRel/ /c .\w32\pathstuff.c
+echo WinRel\pathstuff.obj >>link.rel
+echo off
+echo "Linking WinRel/make374.exe"
+rem link.exe kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\winrel\subproc.lib /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no /PDB:.\WinRel/make374.pdb /MACHINE:I386 /OUT:.\WinRel/make374.exe .\WinRel/variable.obj .\WinRel/rule.obj .\WinRel/remote-stub.obj .\WinRel/commands.obj .\WinRel/file.obj .\WinRel/getloadavg.obj .\WinRel/default.obj .\WinRel/signame.obj .\WinRel/expand.obj .\WinRel/dir.obj .\WinRel/main.obj .\WinRel/getopt1.obj .\WinRel/job.obj .\WinRel/read.obj .\WinRel/version.obj .\WinRel/getopt.obj .\WinRel/arscan.obj .\WinRel/remake.obj .\WinRel/misc.obj .\WinRel/ar.obj .\WinRel/function.obj .\WinRel/vpath.obj .\WinRel/implicit.obj .\WinRel/dirent.obj .\WinRel/glob.obj .\WinRel/fnmatch.obj .\WinRel/pathstuff.obj
+echo kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib w32\subproc\winrel\subproc.lib >>link.rel
+link.exe /NOLOGO /SUBSYSTEM:console /INCREMENTAL:no /PDB:.\WinRel/make374.pdb /MACHINE:I386 /OUT:.\WinRel/make374.exe @link.rel
+if not exist .\WinRel/make374.exe echo "WinRel build failed"
+if exist .\WinRel/make374.exe echo "WinRel build succeeded!"
+echo on
diff --git a/config.h.W32.template b/config.h.W32.template
new file mode 100644
index 0000000..5188b1f
--- /dev/null
+++ b/config.h.W32.template
@@ -0,0 +1,289 @@
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+#undef _ALL_SOURCE
+#endif
+
+/* Define if using alloca.c. */
+#undef C_ALLOCA
+
+/* Define to empty if the keyword does not work. */
+#undef const
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+#undef CRAY_STACKSEG_END
+
+/* Define for DGUX with <sys/dg_sys_info.h>. */
+#undef DGUX
+
+/* Define if the `getloadavg' function needs to be run setuid or setgid. */
+#undef GETLOADAVG_PRIVILEGED
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+#define gid_t int
+
+/* Define if you have alloca, as a function or macro. */
+#undef HAVE_ALLOCA
+#define HAVE_ALLOCA
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+#undef HAVE_ALLOCA_H
+
+/* Define if you don't have vprintf but do have _doprnt. */
+#undef HAVE_DOPRNT
+
+/* Define if your system has its own `getloadavg' function. */
+#undef HAVE_GETLOADAVG
+
+/* Define if you have the getmntent function. */
+#undef HAVE_GETMNTENT
+
+/* Define if the `long double' type works. */
+#undef HAVE_LONG_DOUBLE
+
+/* Define if you support file names longer than 14 characters. */
+#undef HAVE_LONG_FILE_NAMES
+#define HAVE_LONG_FILE_NAMES
+
+/* Define if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define if system calls automatically restart after interruption
+ by a signal. */
+#undef HAVE_RESTARTABLE_SYSCALLS
+
+/* Define if your struct stat has st_blksize. */
+#undef HAVE_ST_BLKSIZE
+
+/* Define if your struct stat has st_blocks. */
+#undef HAVE_ST_BLOCKS
+
+/* Define if you have the strcoll function and it is properly defined. */
+#undef HAVE_STRCOLL
+#define HAVE_STRCOLL
+
+/* Define if your struct stat has st_rdev. */
+#undef HAVE_ST_RDEV
+#define HAVE_ST_RDEV
+
+/* Define if you have the strftime function. */
+#undef HAVE_STRFTIME
+#define HAVE_STRFTIME
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if your struct tm has tm_zone. */
+#undef HAVE_TM_ZONE
+
+/* Define if you don't have tm_zone but do have the external array
+ tzname. */
+#undef HAVE_TZNAME
+#define HAVE_TZNAME
+
+/* Define if you have <unistd.h>. */
+#undef HAVE_UNISTD_H
+
+/* Define if utime(file, NULL) sets file's timestamp to the present. */
+#undef HAVE_UTIME_NULL
+#define HAVE_UTIME_NULL
+
+/* Define if you have <vfork.h>. */
+#undef HAVE_VFORK_H
+
+/* Define if you have the vprintf function. */
+#undef HAVE_VPRINTF
+#define HAVE_VPRINTF
+
+/* Define if you have the wait3 system call. */
+#undef HAVE_WAIT3
+
+/* Define if on MINIX. */
+#undef _MINIX
+
+/* Define if your struct nlist has an n_un member. */
+#undef NLIST_NAME_UNION
+
+/* Define if you have <nlist.h>. */
+#undef NLIST_STRUCT
+
+/* Define if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef pid_t
+#define pid_t int
+
+/* Define if the system does not provide POSIX.1 features except
+ with this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define if you need to in order for stat and other things to work. */
+#undef _POSIX_SOURCE
+#define _POSIX_SOURCE
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+#define RETSIGTYPE void
+
+/* Define if the setvbuf function takes the buffering type as its second
+ argument and the buffer pointer as the third, as on System V
+ before release 3. */
+#undef SETVBUF_REVERSED
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#undef STACK_DIRECTION
+
+/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+#undef STAT_MACROS_BROKEN
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+#define STDC_HEADERS
+
+/* Define on System V Release 4. */
+#undef SVR4
+
+/* Define if `sys_siglist' is declared by <signal.h>. */
+#undef SYS_SIGLIST_DECLARED
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+#define uid_t int
+
+/* Define for Encore UMAX. */
+#undef UMAX
+
+/* Define for Encore UMAX 4.3 that has <inq_status/cpustats.h>
+ instead of <sys/cpustats.h>. */
+#undef UMAX4_3
+
+/* Define vfork as fork if vfork does not work. */
+#undef vfork
+
+/* Define to the name of the SCCS `get' command. */
+#undef SCCS_GET
+#define SCCS_GET "echo no sccs get"
+
+/* Define this if the SCCS `get' command understands the `-G<file>' option. */
+#undef SCCS_GET_MINUS_G
+
+/* Define this if the C library defines the variable `sys_siglist'. */
+#undef HAVE_SYS_SIGLIST
+
+/* Define this if the C library defines the variable `_sys_siglist'. */
+#undef HAVE__SYS_SIGLIST
+
+/* Define this if you have the `union wait' type in <sys/wait.h>. */
+#undef HAVE_UNION_WAIT
+
+/* Define if you have the dup2 function. */
+#undef HAVE_DUP2
+#define HAVE_DUP2
+
+/* Define if you have the getcwd function. */
+#undef HAVE_GETCWD
+#define HAVE_GETCWD
+
+/* Define if you have the getgroups function. */
+#undef HAVE_GETGROUPS
+
+/* Define if you have the mktemp function. */
+#undef HAVE_MKTEMP
+#define HAVE_MKTEMP
+
+/* Define if you have the psignal function. */
+#undef HAVE_PSIGNAL
+
+/* Define if you have the setegid function. */
+#undef HAVE_SETEGID
+
+/* Define if you have the seteuid function. */
+#undef HAVE_SETEUID
+
+/* Define if you have the setlinebuf function. */
+#undef HAVE_SETLINEBUF
+
+/* Define if you have the setregid function. */
+#undef HAVE_SETREGID
+
+/* Define if you have the setreuid function. */
+#undef HAVE_SETREUID
+
+/* Define if you have the sigsetmask function. */
+#undef HAVE_SIGSETMASK
+
+/* Define if you have the strerror function. */
+#undef HAVE_STRERROR
+#define HAVE_STRERROR
+
+/* Define if you have the strsignal function. */
+#undef HAVE_STRSIGNAL
+
+/* Define if you have the wait3 function. */
+#undef HAVE_WAIT3
+
+/* Define if you have the waitpid function. */
+#undef HAVE_WAITPID
+
+/* Define if you have the <dirent.h> header file. */
+#undef HAVE_DIRENT_H
+#define HAVE_DIRENT_H
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+#define HAVE_FCNTL_H
+
+/* Define if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+#define HAVE_LIMITS_H
+
+/* Define if you have the <mach/mach.h> header file. */
+#undef HAVE_MACH_MACH_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+#define HAVE_MEMORY_H
+
+/* Define if you have the <ndir.h> header file. */
+#undef HAVE_NDIR_H
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+#define HAVE_STRING_H
+
+/* Define if you have the <sys/dir.h> header file. */
+#undef HAVE_SYS_DIR_H
+
+/* Define if you have the <sys/ndir.h> header file. */
+#undef HAVE_SYS_NDIR_H
+
+/* Define if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/timeb.h> header file. */
+#undef HAVE_SYS_TIMEB_H
+#define HAVE_SYS_TIMEB_H
+
+/* Define if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the dgc library (-ldgc). */
+#undef HAVE_LIBDGC
+
+/* Define if you have the sun library (-lsun). */
+#undef HAVE_LIBSUN
diff --git a/config.h.WIN32 b/config.h.WIN32
new file mode 100644
index 0000000..5188b1f
--- /dev/null
+++ b/config.h.WIN32
@@ -0,0 +1,289 @@
+/* config.h.in. Generated automatically from configure.in by autoheader. */
+
+/* Define if on AIX 3.
+ System headers sometimes define this.
+ We just want to avoid a redefinition error message. */
+#ifndef _ALL_SOURCE
+#undef _ALL_SOURCE
+#endif
+
+/* Define if using alloca.c. */
+#undef C_ALLOCA
+
+/* Define to empty if the keyword does not work. */
+#undef const
+
+/* Define to one of _getb67, GETB67, getb67 for Cray-2 and Cray-YMP systems.
+ This function is required for alloca.c support on those systems. */
+#undef CRAY_STACKSEG_END
+
+/* Define for DGUX with <sys/dg_sys_info.h>. */
+#undef DGUX
+
+/* Define if the `getloadavg' function needs to be run setuid or setgid. */
+#undef GETLOADAVG_PRIVILEGED
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef gid_t
+#define gid_t int
+
+/* Define if you have alloca, as a function or macro. */
+#undef HAVE_ALLOCA
+#define HAVE_ALLOCA
+
+/* Define if you have <alloca.h> and it should be used (not on Ultrix). */
+#undef HAVE_ALLOCA_H
+
+/* Define if you don't have vprintf but do have _doprnt. */
+#undef HAVE_DOPRNT
+
+/* Define if your system has its own `getloadavg' function. */
+#undef HAVE_GETLOADAVG
+
+/* Define if you have the getmntent function. */
+#undef HAVE_GETMNTENT
+
+/* Define if the `long double' type works. */
+#undef HAVE_LONG_DOUBLE
+
+/* Define if you support file names longer than 14 characters. */
+#undef HAVE_LONG_FILE_NAMES
+#define HAVE_LONG_FILE_NAMES
+
+/* Define if you have a working `mmap' system call. */
+#undef HAVE_MMAP
+
+/* Define if system calls automatically restart after interruption
+ by a signal. */
+#undef HAVE_RESTARTABLE_SYSCALLS
+
+/* Define if your struct stat has st_blksize. */
+#undef HAVE_ST_BLKSIZE
+
+/* Define if your struct stat has st_blocks. */
+#undef HAVE_ST_BLOCKS
+
+/* Define if you have the strcoll function and it is properly defined. */
+#undef HAVE_STRCOLL
+#define HAVE_STRCOLL
+
+/* Define if your struct stat has st_rdev. */
+#undef HAVE_ST_RDEV
+#define HAVE_ST_RDEV
+
+/* Define if you have the strftime function. */
+#undef HAVE_STRFTIME
+#define HAVE_STRFTIME
+
+/* Define if you have <sys/wait.h> that is POSIX.1 compatible. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if your struct tm has tm_zone. */
+#undef HAVE_TM_ZONE
+
+/* Define if you don't have tm_zone but do have the external array
+ tzname. */
+#undef HAVE_TZNAME
+#define HAVE_TZNAME
+
+/* Define if you have <unistd.h>. */
+#undef HAVE_UNISTD_H
+
+/* Define if utime(file, NULL) sets file's timestamp to the present. */
+#undef HAVE_UTIME_NULL
+#define HAVE_UTIME_NULL
+
+/* Define if you have <vfork.h>. */
+#undef HAVE_VFORK_H
+
+/* Define if you have the vprintf function. */
+#undef HAVE_VPRINTF
+#define HAVE_VPRINTF
+
+/* Define if you have the wait3 system call. */
+#undef HAVE_WAIT3
+
+/* Define if on MINIX. */
+#undef _MINIX
+
+/* Define if your struct nlist has an n_un member. */
+#undef NLIST_NAME_UNION
+
+/* Define if you have <nlist.h>. */
+#undef NLIST_STRUCT
+
+/* Define if your C compiler doesn't accept -c and -o together. */
+#undef NO_MINUS_C_MINUS_O
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef pid_t
+#define pid_t int
+
+/* Define if the system does not provide POSIX.1 features except
+ with this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define if you need to in order for stat and other things to work. */
+#undef _POSIX_SOURCE
+#define _POSIX_SOURCE
+
+/* Define as the return type of signal handlers (int or void). */
+#undef RETSIGTYPE
+#define RETSIGTYPE void
+
+/* Define if the setvbuf function takes the buffering type as its second
+ argument and the buffer pointer as the third, as on System V
+ before release 3. */
+#undef SETVBUF_REVERSED
+
+/* If using the C implementation of alloca, define if you know the
+ direction of stack growth for your system; otherwise it will be
+ automatically deduced at run-time.
+ STACK_DIRECTION > 0 => grows toward higher addresses
+ STACK_DIRECTION < 0 => grows toward lower addresses
+ STACK_DIRECTION = 0 => direction of growth unknown
+ */
+#undef STACK_DIRECTION
+
+/* Define if the `S_IS*' macros in <sys/stat.h> do not work properly. */
+#undef STAT_MACROS_BROKEN
+
+/* Define if you have the ANSI C header files. */
+#undef STDC_HEADERS
+#define STDC_HEADERS
+
+/* Define on System V Release 4. */
+#undef SVR4
+
+/* Define if `sys_siglist' is declared by <signal.h>. */
+#undef SYS_SIGLIST_DECLARED
+
+/* Define to `int' if <sys/types.h> doesn't define. */
+#undef uid_t
+#define uid_t int
+
+/* Define for Encore UMAX. */
+#undef UMAX
+
+/* Define for Encore UMAX 4.3 that has <inq_status/cpustats.h>
+ instead of <sys/cpustats.h>. */
+#undef UMAX4_3
+
+/* Define vfork as fork if vfork does not work. */
+#undef vfork
+
+/* Define to the name of the SCCS `get' command. */
+#undef SCCS_GET
+#define SCCS_GET "echo no sccs get"
+
+/* Define this if the SCCS `get' command understands the `-G<file>' option. */
+#undef SCCS_GET_MINUS_G
+
+/* Define this if the C library defines the variable `sys_siglist'. */
+#undef HAVE_SYS_SIGLIST
+
+/* Define this if the C library defines the variable `_sys_siglist'. */
+#undef HAVE__SYS_SIGLIST
+
+/* Define this if you have the `union wait' type in <sys/wait.h>. */
+#undef HAVE_UNION_WAIT
+
+/* Define if you have the dup2 function. */
+#undef HAVE_DUP2
+#define HAVE_DUP2
+
+/* Define if you have the getcwd function. */
+#undef HAVE_GETCWD
+#define HAVE_GETCWD
+
+/* Define if you have the getgroups function. */
+#undef HAVE_GETGROUPS
+
+/* Define if you have the mktemp function. */
+#undef HAVE_MKTEMP
+#define HAVE_MKTEMP
+
+/* Define if you have the psignal function. */
+#undef HAVE_PSIGNAL
+
+/* Define if you have the setegid function. */
+#undef HAVE_SETEGID
+
+/* Define if you have the seteuid function. */
+#undef HAVE_SETEUID
+
+/* Define if you have the setlinebuf function. */
+#undef HAVE_SETLINEBUF
+
+/* Define if you have the setregid function. */
+#undef HAVE_SETREGID
+
+/* Define if you have the setreuid function. */
+#undef HAVE_SETREUID
+
+/* Define if you have the sigsetmask function. */
+#undef HAVE_SIGSETMASK
+
+/* Define if you have the strerror function. */
+#undef HAVE_STRERROR
+#define HAVE_STRERROR
+
+/* Define if you have the strsignal function. */
+#undef HAVE_STRSIGNAL
+
+/* Define if you have the wait3 function. */
+#undef HAVE_WAIT3
+
+/* Define if you have the waitpid function. */
+#undef HAVE_WAITPID
+
+/* Define if you have the <dirent.h> header file. */
+#undef HAVE_DIRENT_H
+#define HAVE_DIRENT_H
+
+/* Define if you have the <fcntl.h> header file. */
+#undef HAVE_FCNTL_H
+#define HAVE_FCNTL_H
+
+/* Define if you have the <limits.h> header file. */
+#undef HAVE_LIMITS_H
+#define HAVE_LIMITS_H
+
+/* Define if you have the <mach/mach.h> header file. */
+#undef HAVE_MACH_MACH_H
+
+/* Define if you have the <memory.h> header file. */
+#undef HAVE_MEMORY_H
+#define HAVE_MEMORY_H
+
+/* Define if you have the <ndir.h> header file. */
+#undef HAVE_NDIR_H
+
+/* Define if you have the <string.h> header file. */
+#undef HAVE_STRING_H
+#define HAVE_STRING_H
+
+/* Define if you have the <sys/dir.h> header file. */
+#undef HAVE_SYS_DIR_H
+
+/* Define if you have the <sys/ndir.h> header file. */
+#undef HAVE_SYS_NDIR_H
+
+/* Define if you have the <sys/param.h> header file. */
+#undef HAVE_SYS_PARAM_H
+
+/* Define if you have the <sys/timeb.h> header file. */
+#undef HAVE_SYS_TIMEB_H
+#define HAVE_SYS_TIMEB_H
+
+/* Define if you have the <sys/wait.h> header file. */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have the <unistd.h> header file. */
+#undef HAVE_UNISTD_H
+
+/* Define if you have the dgc library (-ldgc). */
+#undef HAVE_LIBDGC
+
+/* Define if you have the sun library (-lsun). */
+#undef HAVE_LIBSUN
diff --git a/dir.c b/dir.c
index 84a9a42..d997762 100644
--- a/dir.c
+++ b/dir.c
@@ -44,7 +44,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#define NAMLEN(d) _D_NAMLEN(d)
#endif
-#if defined (POSIX) && !defined (__GNU_LIBRARY__)
+#if (defined (POSIX) || defined (WIN32)) && !defined (__GNU_LIBRARY__)
/* Posix does not require that the d_ino field be present, and some
systems do not provide it. */
#define REAL_DIR_ENTRY(dp) 1
@@ -97,6 +97,10 @@ dosify (filename)
}
#endif /* __MSDOS__ */
+#ifdef WIN32
+#include "pathstuff.h"
+#endif
+
#ifdef _AMIGA
#include <ctype.h>
@@ -193,11 +197,24 @@ struct directory_contents
struct directory_contents *next;
dev_t dev; /* Device and inode numbers of this dir. */
+#ifdef WIN32
+ /*
+ * Inode means nothing on WIN32. Even file key information is
+ * unreliable because it is random per file open and undefined
+ * for remote filesystems. The most unique attribute I can
+ * come up with is the fully qualified name of the directory. Beware
+ * though, this is also unreliable. I'm open to suggestion on a better
+ * way to emulate inode.
+ */
+ char *path_key;
+ int mtime; /* controls check for stale directory cache */
+#else
#ifdef VMS
ino_t ino[3];
#else
ino_t ino;
#endif
+#endif /* WIN32 */
struct dirfile **files; /* Files in this directory. */
DIR *dirstream; /* Stream reading this directory. */
};
@@ -253,6 +270,9 @@ find_directory (name)
register unsigned int hash = 0;
register char *p;
register struct directory *dir;
+#ifdef WIN32
+ char* w32_path;
+#endif
#ifdef VMS
if ((*name == '.') && (*(name+1) == 0))
name = "[]";
@@ -298,6 +318,10 @@ find_directory (name)
struct directory_contents *dc;
+#ifdef WIN32
+ w32_path = w32ify(name, 1);
+ hash = ((unsigned int) st.st_dev << 16) | (unsigned int) st.st_ctime;
+#else
#ifdef VMS
hash = ((unsigned int) st.st_dev << 16)
| ((unsigned int) st.st_ino[0]
@@ -306,9 +330,13 @@ find_directory (name)
#else
hash = ((unsigned int) st.st_dev << 16) | (unsigned int) st.st_ino;
#endif
+#endif
hash %= DIRECTORY_BUCKETS;
for (dc = directories_contents[hash]; dc != 0; dc = dc->next)
+#ifdef WIN32
+ if (!strcmp(dc->path_key, w32_path))
+#else
if (dc->dev == st.st_dev
#ifdef VMS
&& dc->ino[0] == st.st_ino[0]
@@ -317,6 +345,7 @@ find_directory (name)
#else
&& dc->ino == st.st_ino)
#endif
+#endif /* WIN32 */
break;
if (dc == 0)
@@ -328,6 +357,10 @@ find_directory (name)
/* Enter it in the contents hash table. */
dc->dev = st.st_dev;
+#ifdef WIN32
+ dc->path_key = strdup(w32_path);
+ dc->mtime = st.st_mtime;
+#else
#ifdef VMS
dc->ino[0] = st.st_ino[0];
dc->ino[1] = st.st_ino[1];
@@ -335,6 +368,7 @@ find_directory (name)
#else
dc->ino = st.st_ino;
#endif
+#endif /* WIN32 */
dc->next = directories_contents[hash];
directories_contents[hash] = dc;
@@ -382,6 +416,10 @@ dir_contents_file_exists_p (dir, filename)
register char *p;
register struct dirfile *df;
register struct dirent *d;
+#ifdef WIN32
+ struct stat st;
+ int rehash = 0;
+#endif
if (dir == 0 || dir->files == 0)
{
@@ -429,6 +467,24 @@ dir_contents_file_exists_p (dir, filename)
if (dir->dirstream == 0)
{
+#ifdef WIN32
+ /* Check to see if directory has changed since last read */
+ if (dir->path_key &&
+ stat(dir->path_key, &st) == 0 &&
+ st.st_mtime > dir->mtime) {
+
+ /* reset date stamp to show most recent re-process */
+ dir->mtime = st.st_mtime;
+
+ /* make sure directory can still be opened */
+ dir->dirstream = opendir(dir->path_key);
+
+ if (dir->dirstream)
+ rehash = 1;
+ else
+ return 0; /* couldn't re-read - fail */
+ } else
+#endif
/* The directory has been all read in. */
return 0;
}
@@ -447,12 +503,33 @@ dir_contents_file_exists_p (dir, filename)
for (i = 0; i < len; ++i)
HASHI (newhash, d->d_name[i]);
newhash %= DIRFILE_BUCKETS;
+#ifdef WIN32
+ /*
+ * If re-reading a directory, check that this file isn't already
+ * in the cache.
+ */
+ if (rehash) {
+ for (df = dir->files[newhash]; df != 0; df = df->next)
+ if (streq(df->name, d->d_name))
+ break;
+ } else
+ df = 0;
+
+ /*
+ * If re-reading a directory, don't cache files that have
+ * already been discovered.
+ */
+ if (!df) {
+#endif
df = (struct dirfile *) xmalloc (sizeof (struct dirfile));
df->next = dir->files[newhash];
dir->files[newhash] = df;
df->name = savestring (d->d_name, len);
df->impossible = 0;
+#ifdef WIN32
+ }
+#endif
/* Check if the name matches the one we're searching for. */
if (filename != 0
&& newhash == hash && strieq (d->d_name, filename))
@@ -506,6 +583,10 @@ file_exists_p (name)
return dir_file_exists_p ("[]", name);
#else /* !VMS */
dirend = rindex (name, '/');
+#ifdef WIN32
+ if (!dirend)
+ dirend = rindex(name, '\\');
+#endif /* WIN32 */
if (dirend == 0)
return dir_file_exists_p (".", name);
if (dirend == 0)
@@ -569,6 +650,10 @@ file_impossible (filename)
structure for it, but leave it out of the contents hash table. */
dir->contents = (struct directory_contents *)
xmalloc (sizeof (struct directory_contents));
+#ifdef WIN32
+ dir->contents->path_key = NULL;
+ dir->contents->mtime = 0;
+#else /* WIN32 */
#ifdef VMS
dir->contents->dev = 0;
dir->contents->ino[0] = dir->contents->ino[1] =
@@ -576,6 +661,7 @@ file_impossible (filename)
#else
dir->contents->dev = dir->contents->ino = 0;
#endif
+#endif /* WIN32 */
dir->contents->files = 0;
dir->contents->dirstream = 0;
}
@@ -616,6 +702,10 @@ file_impossible_p (filename)
dir = find_directory ("[]")->contents;
#else
dirend = rindex (filename, '/');
+#ifdef WIN32
+ if (!dirend)
+ dirend = rindex (filename, '\\');
+#endif /* WIN32 */
if (dirend == 0)
#ifdef _AMIGA
dir = find_directory ("")->contents;
@@ -685,6 +775,10 @@ print_dir_data_base ()
if (dir->contents == 0)
printf ("# %s: could not be stat'd.\n", dir->name);
else if (dir->contents->files == 0)
+#ifdef WIN32
+ printf ("# %s (key %s, mtime %d): could not be opened.\n",
+ dir->name, dir->contents->path_key,dir->contents->mtime);
+#else /* WIN32 */
#ifdef VMS
printf ("# %s (device %d, inode [%d,%d,%d]): could not be opened.\n",
dir->name, dir->contents->dev,
@@ -694,6 +788,7 @@ print_dir_data_base ()
printf ("# %s (device %d, inode %d): could not be opened.\n",
dir->name, dir->contents->dev, dir->contents->ino);
#endif
+#endif /* WIN32 */
else
{
register unsigned int f = 0, im = 0;
@@ -705,6 +800,10 @@ print_dir_data_base ()
++im;
else
++f;
+#ifdef WIN32
+ printf ("# %s (key %s, mtime %d): ",
+ dir->name, dir->contents->path_key, dir->contents->mtime);
+#else /* WIN32 */
#ifdef VMS
printf ("# %s (device %d, inode [%d,%d,%d]): ",
dir->name, dir->contents->dev,
@@ -714,6 +813,7 @@ print_dir_data_base ()
printf ("# %s (device %d, inode %d): ",
dir->name, dir->contents->dev, dir->contents->ino);
#endif
+#endif /* WIN32 */
if (f == 0)
fputs ("No", stdout);
else
diff --git a/function.c b/function.c
index 73e34a9..6771fa8 100644
--- a/function.c
+++ b/function.c
@@ -30,6 +30,11 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef _AMIGA
#include "amiga.h"
#endif
+#ifdef WIN32
+#include <windows.h>
+#include <io.h>
+#include "sub_proc.h"
+#endif
static char *string_glob PARAMS ((char *line));
@@ -345,6 +350,14 @@ expand_function (o, function, text, end)
#ifndef VMS /* not supported for vms yet */
case function_shell:
{
+#ifdef WIN32
+ SECURITY_ATTRIBUTES saAttr;
+ HANDLE hIn;
+ HANDLE hErr;
+ HANDLE hChildOutRd;
+ HANDLE hChildOutWr;
+ HANDLE hProcess;
+#endif
char **argv;
char *error_prefix;
#ifndef _AMIGA
@@ -387,7 +400,58 @@ expand_function (o, function, text, end)
else
error_prefix = "";
-#if !defined(__MSDOS__) && !defined(_AMIGA)
+#if !defined(__MSDOS__) && !defined(_AMIGA)
+# ifdef WIN32
+ saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
+ saAttr.bInheritHandle = TRUE;
+ saAttr.lpSecurityDescriptor = NULL;
+
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_INPUT_HANDLE),
+ GetCurrentProcess(),
+ &hIn,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fatal("create_child_process: DuplicateHandle(In) failed (e=%d)\n",
+ GetLastError());
+ }
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_ERROR_HANDLE),
+ GetCurrentProcess(),
+ &hErr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fatal("create_child_process: DuplicateHandle(Err) failed (e=%d)\n",
+ GetLastError());
+ }
+
+ if (!CreatePipe(&hChildOutRd, &hChildOutWr, &saAttr, 0))
+ fatal("CreatePipe() failed (e=%d)\n", GetLastError());
+
+ hProcess = process_init_fd(hIn, hChildOutWr, hErr);
+
+ if (!hProcess)
+ fatal("expand_function: process_init_fd() failed\n");
+ else
+ process_register(hProcess);
+
+ /* make sure that CreateProcess() has Path it needs */
+ sync_Path_environment();
+
+ if (!process_begin(hProcess, argv, envp, argv[0], NULL))
+ pid = (int) hProcess;
+ else
+ fatal("expand_function: unable to launch process (e=%d)\n",
+ process_last_err(hProcess));
+
+ /* set up to read data from child */
+ pipedes[0] = _open_osfhandle((long) hChildOutRd, O_RDONLY);
+
+ /* this will be closed almost right away */
+ pipedes[1] = _open_osfhandle((long) hChildOutWr, O_APPEND);
+# else /* WIN32 */
if (pipe (pipedes) < 0)
{
perror_with_name (error_prefix, "pipe");
@@ -400,6 +464,7 @@ expand_function (o, function, text, end)
else if (pid == 0)
child_execute_job (0, pipedes[1], argv, envp);
else
+# endif /* WIN32 */
{
/* We are the parent. */
diff --git a/job.c b/job.c
index b2cc86f..cc09810 100644
--- a/job.c
+++ b/job.c
@@ -25,12 +25,17 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include <assert.h>
/* Default shell to use. */
+#ifdef WIN32
+char *default_shell = "sh.exe";
+int no_default_sh_exe = 1;
+#else /* WIN32 */
#ifndef _AMIGA
char default_shell[] = "/bin/sh";
#else
char default_shell[] = "";
extern int MyExecute (char **);
#endif
+#endif /* WIN32 */
#ifdef __MSDOS__
#include <process.h>
@@ -40,6 +45,7 @@ static char *dos_bname;
static char *dos_bename;
static int dos_batch_file;
#endif /* MSDOS. */
+
#ifdef _AMIGA
#include <proto/dos.h>
static int amiga_pid = 123;
@@ -55,6 +61,20 @@ static int amiga_batch_file;
#include <lib$routines.h>
#endif
+#ifdef WIN32
+#include <windows.h>
+#include <io.h>
+#include <process.h>
+#include "sub_proc.h"
+#include "w32err.h"
+#include "pathstuff.h"
+
+/* this stuff used if no sh.exe is around */
+static char *dos_bname;
+static char *dos_bename;
+static int dos_batch_file;
+#endif /* WIN32 */
+
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#else
@@ -172,6 +192,16 @@ static int good_stdin_used = 0;
static struct child *waiting_jobs = 0;
+#ifdef WIN32
+/*
+ * The macro which references this function is defined in make.h.
+ */
+int w32_kill(int pid, int sig)
+{
+ return ((process_kill(pid, sig) == TRUE) ? 0 : -1);
+}
+#endif /* WIN32 */
+
/* Write an error message describing the exit status given in
EXIT_CODE, EXIT_SIG, and COREDUMP, for the target TARGET_NAME.
Append "(ignored)" if IGNORED is nonzero. */
@@ -312,7 +342,7 @@ reap_children (block, err)
}
else if (pid == 0)
{
-#if !defined(__MSDOS__) && !defined(_AMIGA)
+#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WIN32)
/* No remote children. Check for local children. */
if (any_local)
@@ -366,7 +396,7 @@ reap_children (block, err)
exit_sig = WIFSIGNALED (status) ? WTERMSIG (status) : 0;
coredump = WCOREDUMP (status);
}
-#else /* MSDOS. */
+#else /* MSDOS, Amiga, WIN32. */
#ifdef __MSDOS__
/* Life is very different on MSDOS. */
pid = dos_pid - 1;
@@ -374,14 +404,49 @@ reap_children (block, err)
exit_code = dos_status;
exit_sig = 0;
coredump = 0;
-#else
+#endif /* __MSDOS__ */
+#ifdef _AMIGA
/* Same on Amiga */
pid = amiga_pid - 1;
status = amiga_status;
exit_code = amiga_status;
exit_sig = 0;
coredump = 0;
-#endif
+#endif /* _AMIGA */
+#ifdef WIN32
+ {
+ HANDLE hPID;
+ int err;
+
+ /* wait for anything to finish */
+ if (hPID = process_wait_for_any()) {
+
+ /* was an error found on this process? */
+ err = process_last_err(hPID);
+
+ /* get exit data */
+ exit_code = process_exit_code(hPID);
+
+ if (err)
+ fprintf(stderr, "make (e=%d): %s",
+ exit_code, map_win32_error_to_string(exit_code));
+
+ exit_sig = process_signal(hPID);
+
+ /* cleanup process */
+ process_cleanup(hPID);
+
+ if (dos_batch_file) {
+ remove (dos_bname);
+ remove (dos_bename);
+ dos_batch_file = 0;
+ }
+
+ coredump = 0;
+ }
+ pid = (int) hPID;
+ }
+#endif /* WIN32 */
#endif /* Not MSDOS. */
}
else
@@ -715,6 +780,7 @@ start_job_command (child)
fflush (stdout);
fflush (stderr);
+#ifndef WIN32
#ifndef _AMIGA
#ifndef VMS
@@ -745,6 +811,7 @@ start_job_command (child)
}
#endif /* !AMIGA */
+#endif /* !WIN32 */
/* Decide whether to give this child the `good' standard input
(one that points to the terminal or whatever), or the `bad' one
@@ -764,7 +831,7 @@ start_job_command (child)
child->environment = target_environment (child->file);
#endif
-#if !defined(__MSDOS__) && !defined(_AMIGA)
+#if !defined(__MSDOS__) && !defined(_AMIGA) && !defined(WIN32)
#ifndef VMS
/* start_waiting_job has set CHILD->remote if we can start a remote job. */
@@ -848,7 +915,8 @@ start_job_command (child)
dos_status = 0;
remove (dos_bename);
}
-#else
+#endif /* __MSDOS__ */
+#ifdef _AMIGA
amiga_status = MyExecute (argv);
++dead_children;
@@ -859,6 +927,37 @@ start_job_command (child)
DeleteFile (amiga_bname); /* Ignore errors. */
}
#endif /* Not Amiga */
+#ifdef WIN32
+ {
+ HANDLE hPID;
+ char* arg0;
+
+ /* make UNC paths safe for CreateProcess -- backslash format */
+ arg0 = argv[0];
+ if (arg0 && arg0[0] == '/' && arg0[1] == '/')
+ for ( ; arg0 && *arg0; arg0++)
+ if (*arg0 == '/')
+ *arg0 = '\\';
+
+ /* make sure CreateProcess() has Path it needs */
+ sync_Path_environment();
+
+ hPID = process_easy(argv, child->environment);
+
+ if (hPID != INVALID_HANDLE_VALUE)
+ child->pid = (int) hPID;
+ else {
+ int i;
+ unblock_sigs();
+ fprintf(stderr,
+ "process_easy() failed failed to launch process (e=%d)\n",
+ process_last_err(hPID));
+ for (i = 0; argv[i]; i++)
+ fprintf(stderr, "%s ", argv[i]);
+ fprintf(stderr, "\nCounted %d args in failed launch\n", i);
+ }
+ }
+#endif /* WIN32 */
#endif /* Not MSDOS. */
/* We are the parent side. Set the state to
@@ -1177,6 +1276,7 @@ start_waiting_jobs ()
return;
}
+#ifndef WIN32
#ifdef VMS
#include <descrip.h>
#include <clidef.h>
@@ -1414,6 +1514,7 @@ child_execute_job (stdin_fd, stdout_fd, argv, envp)
}
#endif /* !AMIGA */
#endif /* !VMS */
+#endif /* !WIN32 */
#ifndef _AMIGA
/* Replace the current process with one running the command in ARGV,
@@ -1532,12 +1633,30 @@ construct_command_argv_internal (line, restp, shell, ifs)
"unset", "unsetenv", "version",
0 };
#else
+#ifdef WIN32
+ static char sh_chars_dos[] = "\"|<>";
+ static char *sh_cmds_dos[] = { "break", "call", "cd", "chcp", "chdir", "cls",
+ "copy", "ctty", "date", "del", "dir", "echo",
+ "erase", "exit", "for", "goto", "if", "if", "md",
+ "mkdir", "path", "pause", "prompt", "rem", "ren",
+ "rename", "set", "shift", "time", "type",
+ "ver", "verify", "vol", ":", 0 };
+ static char sh_chars_sh[] = "#;\"*?[]&|<>(){}$`^";
+ static char *sh_cmds_sh[] = { "cd", "eval", "exec", "exit", "login",
+ "logout", "set", "umask", "wait", "while", "for",
+ "case", "if", ":", ".", "break", "continue",
+ "export", "read", "readonly", "shift", "times",
+ "trap", "switch", "test", 0 };
+ char* sh_chars;
+ char** sh_cmds;
+#else /* WIN32 */
static char sh_chars[] = "#;\"*?[]&|<>(){}$`^";
static char *sh_cmds[] = { "cd", "eval", "exec", "exit", "login",
"logout", "set", "umask", "wait", "while", "for",
"case", "if", ":", ".", "break", "continue",
"export", "read", "readonly", "shift", "times",
"trap", "switch", 0 };
+#endif /* WIN32 */
#endif
register int i;
register char *p;
@@ -1545,6 +1664,17 @@ construct_command_argv_internal (line, restp, shell, ifs)
char *end;
int instring, word_has_equals, seen_nonequals;
char **new_argv = 0;
+#ifdef WIN32
+ int slow_flag = 0;
+
+ if (no_default_sh_exe) {
+ sh_cmds = sh_cmds_dos;
+ sh_chars = sh_chars_dos;
+ } else {
+ sh_cmds = sh_cmds_sh;
+ sh_chars = sh_chars_sh;
+ }
+#endif
if (restp != NULL)
*restp = NULL;
@@ -1559,6 +1689,20 @@ construct_command_argv_internal (line, restp, shell, ifs)
if (shell == 0)
shell = default_shell;
else if (strcmp (shell, default_shell))
+#ifdef WIN32
+ {
+ char *s1 = _fullpath(NULL, shell, 0);
+ char *s2 = _fullpath(NULL, default_shell, 0);
+
+ slow_flag = strcmp((s1 ? s1 : ""), (s2 ? s2 : ""));
+
+ if (s1);
+ free(s1);
+ if (s2);
+ free(s2);
+ }
+ if (slow_flag)
+#endif /* WIN32 */
goto slow;
if (ifs != 0)
@@ -1750,8 +1894,31 @@ construct_command_argv_internal (line, restp, shell, ifs)
free (new_argv[0]);
free ((void *)new_argv);
}
+#ifdef WIN32
+ /*
+ * Not eating this whitespace caused things like
+ *
+ * sh -c "\n"
+ *
+ * which gave the shell fits. I think we have to eat
+ * whitespace here, but this code should be considered
+ * suspicious if things start failing....
+ */
-#ifdef __MSDOS__
+ /* Make sure not to bother processing an empty line. */
+ while (isspace (*line))
+ ++line;
+ if (*line == '\0')
+ return 0;
+#endif
+
+#if defined(__MSDOS__) || defined(WIN32)
+#ifdef WIN32
+ /*
+ * only come here if no sh.exe command
+ */
+ if (no_default_sh_exe)
+#endif
{
FILE *batch;
dos_batch_file = 1;
@@ -1807,6 +1974,13 @@ construct_command_argv_internal (line, restp, shell, ifs)
new_argv[1] = 0;
}
#else /* Not MSDOS or Amiga */
+#ifdef WIN32
+ /*
+ * This is technically an else to the above 'if (no_default_sh_exe)',
+ * but (IMHO) coding if-else across ifdef is dangerous.
+ */
+ if (!no_default_sh_exe)
+#endif
{
/* SHELL may be a multi-word command. Construct a command line
"SHELL -c LINE", with all special chars in LINE escaped.
@@ -1896,6 +2070,16 @@ construct_command_argv (line, restp, file)
warn_undefined_variables_flag = 0;
shell = allocated_variable_expand_for_file ("$(SHELL)", file);
+#ifdef WIN32
+ /*
+ * Convert to forward slashes so that construct_command_argv_internal()
+ * is not confused.
+ */
+ if (shell) {
+ char *p = w32ify(shell, 0);
+ strcpy(shell, p);
+ }
+#endif
ifs = allocated_variable_expand_for_file ("$(IFS)", file);
warn_undefined_variables_flag = save;
diff --git a/main.c b/main.c
index c3f8089..9cb5a83 100644
--- a/main.c
+++ b/main.c
@@ -28,6 +28,10 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
# include <dos/dos.h>
# include <proto/dos.h>
#endif
+#ifdef WIN32
+#include <windows.h>
+#include "pathstuff.h"
+#endif
#ifdef _AMIGA
int __stack = 20000; /* Make sure we have 20K of stack space */
@@ -123,6 +127,12 @@ int just_print_flag;
int debug_flag = 0;
+#ifdef WIN32
+/* Suspend make in main for a short time to allow debugger to attach */
+
+int suspend_flag = 0;
+#endif
+
/* Environment variables override makefile definitions. */
int env_overrides = 0;
@@ -228,6 +238,11 @@ static const struct command_switch switches[] =
{ 'd', flag, (char *) &debug_flag, 1, 1, 0, 0, 0,
"debug", 0,
"Print lots of debugging information" },
+#ifdef WIN32
+ { 'D', flag, (char *) &suspend_flag, 1, 1, 0, 0, 0,
+ "suspend-for-debug", 0,
+ "Suspend process to allow a debugger to attach" },
+#endif
{ 'e', flag, (char *) &env_overrides, 1, 1, 0, 0, 0,
"environment-overrides", 0,
"Environment variables override makefiles" },
@@ -448,6 +463,11 @@ int main (int argc, char ** argv)
char **p;
struct dep *read_makefiles;
PATH_VAR (current_directory);
+#ifdef WIN32
+ extern int no_default_sh_exe;
+ char *unix_path = NULL;
+ char *win32_path = NULL;
+#endif
default_goal_file = 0;
reading_filename = 0;
@@ -539,7 +559,11 @@ int main (int argc, char ** argv)
/* Figure out where we are. */
+#ifdef WIN32
+ if (getcwd_fs (current_directory, GET_PATH_MAX) == 0)
+#else
if (getcwd (current_directory, GET_PATH_MAX) == 0)
+#endif
{
#ifdef HAVE_GETCWD
perror_with_name ("getcwd: ", "");
@@ -563,6 +587,12 @@ int main (int argc, char ** argv)
register char *ep = envp[i];
while (*ep != '=')
++ep;
+#ifdef WIN32
+ if (!strncmp(ep, "PATH", 4))
+ unix_path = &ep[5];
+ if (!strncmp(ep, "Path", 4))
+ win32_path = &ep[5];
+#endif
/* The result of pointer arithmetic is cast to unsigned int for
machines where ptrdiff_t is a different size that doesn't widen
the same. */
@@ -575,6 +605,13 @@ int main (int argc, char ** argv)
be exported, because it was originally in the environment. */
->export = v_export;
}
+#ifdef WIN32
+ /*
+ * PATH defaults to Path iff PATH not found and Path is found.
+ */
+ if (!unix_path && win32_path)
+ define_variable("PATH", 4, win32_path, o_env, 1)->export = v_export;
+#endif
#else /* For Amiga, read the ENV: device, ignoring all dirs */
{
BPTR env, file, old;
@@ -616,6 +653,14 @@ int main (int argc, char ** argv)
decode_env_switches ("MFLAGS", 6);
#endif
decode_switches (argc, argv, 0);
+#ifdef WIN32
+ if (suspend_flag) {
+ fprintf(stderr, "%s (pid = %d)\n", argv[0], GetCurrentProcessId());
+ fprintf(stderr, "%s is suspending for 30 seconds...", argv[0]);
+ Sleep(30 * 1000);
+ fprintf(stderr, "done sleep(30). Continuing.\n");
+ }
+#endif
/* Print version information. */
@@ -632,10 +677,22 @@ int main (int argc, char ** argv)
so the result will run the same program regardless of the current dir.
If it is a name with no slash, we can only hope that PATH did not
find it in the current directory.) */
-
+#ifdef WIN32
+ /*
+ * Convert from backslashes to forward slashes for
+ * programs like sh which don't like them. Shouldn't
+ * matter if the path is one way or the other for
+ * CreateProcess().
+ */
+ if (strpbrk(argv[0], "/:\\") ||
+ strstr(argv[0], "..") ||
+ !strncmp(argv[0], "//", 2))
+ argv[0] = strdup(w32ify(argv[0],1));
+#else /* WIN32 */
if (current_directory[0] != '\0'
&& argv[0] != 0 && argv[0][0] != '/' && index (argv[0], '/') != 0)
argv[0] = concat (current_directory, "/", argv[0]);
+#endif /* WIN32 */
#endif
/* The extra indirection through $(MAKE_COMMAND) is done
@@ -708,6 +765,79 @@ int main (int argc, char ** argv)
free (dir);
}
+#ifdef WIN32
+ /*
+ * THIS BLOCK OF CODE MUST COME AFTER chdir() CALL ABOVE IN ORDER
+ * TO NOT CONFUSE THE DEPENDENCY CHECKING CODE IN implicit.c.
+ *
+ * The functions in dir.c can incorrectly cache information for "."
+ * before we have changed directory and this can cause file
+ * lookups to fail because the current directory (.) was pointing
+ * at the wrong place when it was first evaluated.
+ */
+
+ /*
+ * On Windows/NT, we don't have the luxury of a /bin directory that
+ * is mapped globally to every drive mounted to the system. Since make could
+ * be invoked from any drive, and we don't want to propogate /bin/sh
+ * to every single drive. Allow ourselves a chance to search for
+ * a value for default shell here (if the default path does not exist).
+ *
+ * The value of default_shell is set here, but it could get reset after
+ * the Makefiles are read in. See logic below where SHELL is checked
+ * after the call to read_all_makefiles() completes.
+ *
+ * The reason SHELL is set here is so that macros can be safely evaluated
+ * as makefiles are read in (some macros require $SHELL).
+ */
+
+ {
+ extern char *default_shell;
+
+ if (!file_exists_p(default_shell)) {
+ char *p;
+ struct variable *v = lookup_variable ("Path", 4);
+
+ /*
+ * Try and make sure we have a full path to default_shell before
+ * we parse makefiles.
+ */
+ if (v && v->value) {
+ PATH_VAR(sh_path);
+ char *ep;
+
+ p = v->value;
+ ep = strchr(p, PATH_SEPARATOR_CHAR);
+
+ while (ep && *ep) {
+ *ep = '\0';
+
+ if (dir_file_exists_p(p, default_shell)) {
+ sprintf(sh_path, "%s/%s", p, default_shell);
+ default_shell = strdup(w32ify(sh_path,0));
+ no_default_sh_exe = 0;
+ *ep = PATH_SEPARATOR_CHAR;
+
+ /* terminate loop */
+ p += strlen(p);
+ } else {
+ *ep = PATH_SEPARATOR_CHAR;
+ p = ++ep;
+ }
+
+ ep = strchr(p, PATH_SEPARATOR_CHAR);
+ }
+
+ /* be sure to check last element of Path */
+ if (p && *p && dir_file_exists_p(p, default_shell)) {
+ sprintf(sh_path, "%s/%s", p, default_shell);
+ default_shell = strdup(w32ify(sh_path,0));
+ no_default_sh_exe = 0;
+ }
+ }
+ }
+ }
+#endif /* WIN32 */
/* Figure out the level of recursion. */
{
struct variable *v = lookup_variable ("MAKELEVEL", 9);
@@ -736,7 +866,11 @@ int main (int argc, char ** argv)
starting_directory = current_directory;
else
{
+#ifdef WIN32
+ if (getcwd_fs (current_directory, GET_PATH_MAX) == 0)
+#else
if (getcwd (current_directory, GET_PATH_MAX) == 0)
+#endif
{
#ifdef HAVE_GETCWD
perror_with_name ("getcwd: ", "");
@@ -849,6 +983,53 @@ int main (int argc, char ** argv)
define_makeflags (0, 0);
+#ifdef WIN32
+ /*
+ * Now that makefiles are parsed, see if a Makefile gave a
+ * value for SHELL and use that for default_shell instead if
+ * that filename exists. This should speed up the
+ * construct_argv_internal() function by avoiding unnecessary
+ * recursion.
+ */
+ {
+ struct variable *v = lookup_variable("SHELL", 5);
+ extern char* default_shell;
+
+ /*
+ * to change value:
+ *
+ * SHELL must be found, SHELL must be set, value of SHELL
+ * must be different from current value, and the
+ * specified file must exist. Whew!
+ */
+ if (v != 0 && *v->value != '\0') {
+ char *fn = recursively_expand(v);
+
+ if (fn && strcmp(fn, default_shell) && file_exists_p(fn)) {
+ char *p;
+
+ default_shell = fn;
+
+ /* if Makefile says SHELL is sh.exe, believe it */
+ if (strstr(default_shell, "sh.exe"))
+ no_default_sh_exe = 0;
+
+ /*
+ * Convert from backslashes to forward slashes so
+ * create_command_line_argv_internal() is not confused.
+ */
+ for (p = strchr(default_shell, '\\'); p; p = strchr(default_shell, '\\'))
+ *p = '/';
+ }
+ }
+ }
+ if (no_default_sh_exe && job_slots != 1) {
+ error("Do not specify -j or --jobs if sh.exe is not available.");
+ error("Resetting make for single job mode.");
+ job_slots = 1;
+ }
+#endif /* WIN32 */
+
/* Define the default variables. */
define_default_variables ();
diff --git a/make.h b/make.h
index dc88f2a..4369653 100644
--- a/make.h
+++ b/make.h
@@ -286,11 +286,23 @@ extern char *alloca ();
#define ENUM_BITFIELD(bits)
#endif
-#ifdef __MSDOS__
+#if defined(__MSDOS__) || defined(WIN32)
#define PATH_SEPARATOR_CHAR ';'
#else
#define PATH_SEPARATOR_CHAR ':'
#endif
+
+#ifdef WIN32
+#include <fcntl.h>
+#include <malloc.h>
+#define pipe(p) _pipe(p, 512, O_BINARY)
+#define kill(pid,sig) w32_kill(pid,sig)
+
+extern void sync_Path_environment(void);
+extern int kill(int pid, int sig);
+extern int safe_stat(char *file, struct stat *sb);
+extern char *end_of_token_w32();
+#endif
extern void die ();
extern void message ();
@@ -361,7 +373,7 @@ extern void child_access ();
/* We omit these declarations on non-POSIX systems which define _POSIX_VERSION,
because such systems often declare the in header files anyway. */
-#if !defined (__GNU_LIBRARY__) && !defined (POSIX) && !defined (_POSIX_VERSION)
+#if !defined (__GNU_LIBRARY__) && !defined (POSIX) && !defined (_POSIX_VERSION) && !defined(WIN32)
extern long int atol ();
#ifndef VMS
diff --git a/misc.c b/misc.c
index 10fce3a..b8ce6e9 100644
--- a/misc.c
+++ b/misc.c
@@ -397,6 +397,37 @@ end_of_token (s)
return s;
}
+#ifdef WIN32
+/*
+ * Same as end_of_token, but take into account a stop character
+ */
+char *
+end_of_token_w32 (s, stopchar)
+ char *s;
+ char stopchar;
+{
+ register char *p = s;
+ register int backslash = 0;
+
+ while (*p != '\0' && *p != stopchar && (backslash || !isblank (*p)))
+ {
+ if (*p++ == '\\')
+ {
+ backslash = !backslash;
+ while (*p == '\\')
+ {
+ backslash = !backslash;
+ ++p;
+ }
+ }
+ else
+ backslash = 0;
+ }
+
+ return p;
+}
+#endif
+
/* Return the address of the first nonwhitespace or null in the string S. */
char *
diff --git a/read.c b/read.c
index 97af2ca..7668162 100644
--- a/read.c
+++ b/read.c
@@ -30,6 +30,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "glob/glob.h"
#endif
+#ifndef WIN32
#ifndef _AMIGA
#ifndef VMS
#include <pwd.h>
@@ -37,6 +38,7 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
struct passwd *getpwnam PARAMS ((char *name));
#endif
#endif
+#endif /* !WIN32 */
/* A `struct linebuffer' is a structure which holds a line of text.
`readline' reads a line from a stream into a linebuffer
@@ -79,6 +81,13 @@ static struct conditionals *conditionals = &toplevel_conditionals;
static char *default_include_directories[] =
{
+#if defined(WIN32) && !defined(INCLUDEDIR)
+/*
+ * This completly up to the user when they install MSVC or other packages.
+ * This is defined as a placeholder.
+ */
+#define INCLUDEDIR "."
+#endif
INCLUDEDIR,
#ifndef _AMIGA
"/usr/gnu/include",
@@ -274,6 +283,9 @@ read_makefile (filename, flags)
char *pattern = 0, *pattern_percent;
int makefile_errno;
+#ifdef WIN32
+ int check_again;
+#endif
#define record_waiting_files() \
do \
@@ -773,6 +785,16 @@ read_makefile (filename, flags)
if (p && !(isspace(p[1]) || !p[1] || isspace(p[-1])))
p = 0;
#endif
+#ifdef WIN32
+ do {
+ check_again = 0;
+ /* For WIN32, skip a "C:\..." or a "C:/..." */
+ if (p != 0 && (p[1] == '\\' || p[1] == '/') && isalpha (p[-1])) {
+ p = index(p + 1, ':');
+ check_again = 1;
+ }
+ } while (check_again);
+#endif
if (p != 0)
{
struct nameseq *target;
@@ -1599,6 +1621,17 @@ parse_file_seq (stringp, stopchar, size, strip)
p = find_char_unquote (p+1, stopchars, 1);
}
#endif
+#ifdef WIN32
+ /* For WIN32, skip a "C:\..." or "C:/...". */
+ if (stopchar == ':' &&
+ p != 0 &&
+ (p[1] == '\\' || p[1] == '/') &&
+ isalpha (p[-1])) {
+ p = end_of_token_w32(++p, ':');
+ if (*p == '\0' && p[-1] == ':')
+ p--;
+ }
+#endif
if (p == 0)
p = q + strlen (q);
@@ -1984,7 +2017,7 @@ tilde_expand (name)
free (home_dir);
home_dir = getenv ("HOME");
}
-#ifndef _AMIGA
+#if !defined(_AMIGA) && !defined(WIN32)
if (home_dir == 0 || home_dir[0] == '\0')
{
extern char *getlogin ();
@@ -1997,7 +2030,7 @@ tilde_expand (name)
home_dir = p->pw_dir;
}
}
-#endif /* !AMIGA */
+#endif /* !AMIGA && !WIN32 */
if (home_dir != 0)
{
char *new = concat (home_dir, "", name + 1);
@@ -2006,7 +2039,7 @@ tilde_expand (name)
return new;
}
}
-#ifndef _AMIGA
+#if !defined(_AMIGA) && !defined(WIN32)
else
{
struct passwd *pwent;
@@ -2024,7 +2057,7 @@ tilde_expand (name)
else if (userend != 0)
*userend = '/';
}
-#endif /* !AMIGA */
+#endif /* !AMIGA && !WIN32 */
#endif /* !VMS */
return 0;
}
diff --git a/remake.c b/remake.c
index 8eb6a5d..33824af 100644
--- a/remake.c
+++ b/remake.c
@@ -32,6 +32,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#ifdef VMS
#include <starlet.h>
#endif
+#ifdef WIN32
+#include <io.h>
+#endif
extern int try_implicit_rule PARAMS ((struct file *file, unsigned int depth));
@@ -1104,6 +1107,13 @@ library_search (lib, mtime_ptr)
"/lib",
"/usr/lib",
#endif
+#if defined(WIN32) && !defined(LIBDIR)
+/*
+ * This is completely up to the user at product install time. Just define
+ * a placeholder.
+ */
+#define LIBDIR "."
+#endif
LIBDIR, /* Defined by configuration. */
0
};
diff --git a/subproc.bat b/subproc.bat
new file mode 100644
index 0000000..95d9d0d
--- /dev/null
+++ b/subproc.bat
@@ -0,0 +1,3 @@
+cd w32\subproc
+nmake /f %1
+cd ..\..
diff --git a/variable.c b/variable.c
index 65b9ccc..3bdf65b 100644
--- a/variable.c
+++ b/variable.c
@@ -22,6 +22,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "job.h"
#include "commands.h"
#include "variable.h"
+#ifdef WIN32
+#include "pathstuff.h"
+#endif
/* Hash table of all global variable definitions. */
@@ -351,7 +354,11 @@ merge_variable_set_lists (setlist0, setlist1)
void
define_automatic_variables ()
{
+#ifdef WIN32
+ extern char* default_shell;
+#else
extern char default_shell[];
+#endif
register struct variable *v;
char buf[200];
@@ -539,11 +546,25 @@ target_environment (file)
&& v->origin != o_env && v->origin != o_env_override)
{
char *value = recursively_expand (v);
+#ifdef WIN32
+ if (strcmp(v->name, "Path") == 0 ||
+ strcmp(v->name, "PATH") == 0)
+ convert_Path_to_win32(value, ';');
+#endif
result[nvariables++] = concat (v->name, "=", value);
free (value);
}
else
+#ifdef WIN32
+ {
+ if (strcmp(v->name, "Path") == 0 ||
+ strcmp(v->name, "PATH") == 0)
+ convert_Path_to_win32(v->value, ';');
+ result[nvariables++] = concat (v->name, "=", v->value);
+ }
+#else
result[nvariables++] = concat (v->name, "=", v->value);
+#endif
}
}
result[nvariables] = (char *) xmalloc (100);
@@ -862,3 +883,30 @@ print_file_variables (file)
if (file->variables != 0)
print_variable_set (file->variables->set, "# ");
}
+
+#ifdef WIN32
+void
+sync_Path_environment(void)
+{
+ char* path = allocated_variable_expand("$(Path)");
+ static char* environ_path = NULL;
+
+ if (!path)
+ return;
+
+ /*
+ * If done this before, don't leak memory unnecessarily.
+ * Free the previous entry before allocating new one.
+ */
+ if (environ_path)
+ free(environ_path);
+
+ /*
+ * Create something WIN32 world can grok
+ */
+ convert_Path_to_win32(path, ';');
+ environ_path = concat("Path", "=", path);
+ putenv(environ_path);
+ free(path);
+}
+#endif
diff --git a/vpath.c b/vpath.c
index 57f2b7c..bf13bc3 100644
--- a/vpath.c
+++ b/vpath.c
@@ -19,6 +19,9 @@ the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
#include "make.h"
#include "filedef.h"
#include "variable.h"
+#ifdef WIN32
+#include "pathstuff.h"
+#endif
/* Structure used to represent a selective VPATH searchpath. */
@@ -170,6 +173,10 @@ construct_vpath_list (pattern, dirpath)
return;
}
+#ifdef WIN32
+ convert_vpath_to_win32(dirpath, ';');
+#endif
+
/* Figure out the maximum number of VPATH entries and
put it in MAXELEM. We start with 2, one before the
first colon and one nil, the list terminator and
@@ -279,7 +286,12 @@ vpath_search (file, mtime_ptr)
/* If there are no VPATH entries or FILENAME starts at the root,
there is nothing we can do. */
- if (**file == '/' || (vpaths == 0 && general_vpath == 0))
+ if (**file == '/'
+#ifdef WIN32
+ || **file == '\\'
+ || (*file)[1] == ':'
+#endif
+ || (vpaths == 0 && general_vpath == 0))
return 0;
for (v = vpaths; v != 0; v = v->next)
@@ -331,6 +343,10 @@ selective_vpath_search (path, file, mtime_ptr)
pointer to the name-within-directory and FLEN is its length. */
n = rindex (*file, '/');
+#ifdef WIN32
+ if (!n)
+ n = rindex(*file,, '\\');
+#endif
name_dplen = n != 0 ? n - *file : 0;
filename = name_dplen > 0 ? n + 1 : *file;
if (name_dplen > 0)
diff --git a/w32/compat/dirent.c b/w32/compat/dirent.c
new file mode 100644
index 0000000..f516eb9
--- /dev/null
+++ b/w32/compat/dirent.c
@@ -0,0 +1,188 @@
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include "dirent.h"
+
+
+DIR*
+opendir(const char* pDirName)
+{
+ struct stat sb;
+ DIR* pDir;
+ char* pEndDirName;
+ int nBufferLen;
+
+ /* sanity checks */
+ if (!pDirName) {
+ errno = EINVAL;
+ return NULL;
+ }
+ if (stat(pDirName, &sb) != 0) {
+ errno = ENOENT;
+ return NULL;
+ }
+ if ((sb.st_mode & S_IFMT) != S_IFDIR) {
+ errno = ENOTDIR;
+ return NULL;
+ }
+
+ /* allocate a DIR structure to return */
+ pDir = (DIR *) malloc(sizeof (DIR));
+
+ if (!pDir)
+ return NULL;
+
+ /* input directory name length */
+ nBufferLen = strlen(pDirName);
+
+ /* copy input directory name to DIR buffer */
+ strcpy(pDir->dir_pDirectoryName, pDirName);
+
+ /* point to end of the copied directory name */
+ pEndDirName = &pDir->dir_pDirectoryName[nBufferLen - 1];
+
+ /* if directory name did not end in '/' or '\', add '/' */
+ if ((*pEndDirName != '/') && (*pEndDirName != '\\')) {
+ pEndDirName++;
+ *pEndDirName = '/';
+ }
+
+ /* now append the wildcard character to the buffer */
+ pEndDirName++;
+ *pEndDirName = '*';
+ pEndDirName++;
+ *pEndDirName = '\0';
+
+ /* other values defaulted */
+ pDir->dir_nNumFiles = 0;
+ pDir->dir_hDirHandle = INVALID_HANDLE_VALUE;
+ pDir->dir_ulCookie = __DIRENT_COOKIE;
+
+ return pDir;
+}
+
+void
+closedir(DIR *pDir)
+{
+ /* got a valid pointer? */
+ if (!pDir) {
+ errno = EINVAL;
+ return;
+ }
+
+ /* sanity check that this is a DIR pointer */
+ if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
+ errno = EINVAL;
+ return;
+ }
+
+ /* close the WIN32 directory handle */
+ if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE)
+ FindClose(pDir->dir_hDirHandle);
+
+ free(pDir);
+
+ return;
+}
+
+struct dirent *
+readdir(DIR* pDir)
+{
+ WIN32_FIND_DATA wfdFindData;
+
+ if (!pDir) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ /* sanity check that this is a DIR pointer */
+ if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
+ errno = EINVAL;
+ return NULL;
+ }
+
+ if (pDir->dir_nNumFiles == 0) {
+ pDir->dir_hDirHandle = FindFirstFile(pDir->dir_pDirectoryName, &wfdFindData);
+ if (pDir->dir_hDirHandle == INVALID_HANDLE_VALUE)
+ return NULL;
+ } else if (!FindNextFile(pDir->dir_hDirHandle, &wfdFindData))
+ return NULL;
+
+ /* bump count for next call to readdir() or telldir() */
+ pDir->dir_nNumFiles++;
+
+ /* fill in struct dirent values */
+ pDir->dir_sdReturn.d_ino = -1;
+ strcpy(pDir->dir_sdReturn.d_name, wfdFindData.cFileName);
+
+ return &pDir->dir_sdReturn;
+}
+
+void
+rewinddir(DIR* pDir)
+{
+ if (!pDir) {
+ errno = EINVAL;
+ return;
+ }
+
+ /* sanity check that this is a DIR pointer */
+ if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
+ errno = EINVAL;
+ return;
+ }
+
+ /* close the WIN32 directory handle */
+ if (pDir->dir_hDirHandle != INVALID_HANDLE_VALUE)
+ if (!FindClose(pDir->dir_hDirHandle))
+ errno = EBADF;
+
+ /* reset members which control readdir() */
+ pDir->dir_hDirHandle = INVALID_HANDLE_VALUE;
+ pDir->dir_nNumFiles = 0;
+
+ return;
+}
+
+int
+telldir(DIR* pDir)
+{
+ if (!pDir) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* sanity check that this is a DIR pointer */
+ if (pDir->dir_ulCookie != __DIRENT_COOKIE) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ /* return number of times readdir() called */
+ return pDir->dir_nNumFiles;
+}
+
+void
+seekdir(DIR* pDir, long nPosition)
+{
+ if (!pDir)
+ return;
+
+ /* sanity check that this is a DIR pointer */
+ if (pDir->dir_ulCookie != __DIRENT_COOKIE)
+ return;
+
+ /* go back to beginning of directory */
+ rewinddir(pDir);
+
+ /* loop until we have found position we care about */
+ for (--nPosition; nPosition && readdir(pDir); nPosition--);
+
+ /* flag invalid nPosition value */
+ if (nPosition)
+ errno = EINVAL;
+
+ return;
+}
diff --git a/w32/include/dirent.h b/w32/include/dirent.h
new file mode 100644
index 0000000..6bb7fbd
--- /dev/null
+++ b/w32/include/dirent.h
@@ -0,0 +1,37 @@
+#ifndef _DIRENT_H
+#define _DIRENT_H
+
+#include <stdlib.h>
+#include <windows.h>
+#include <limits.h>
+#include <sys/types.h>
+
+#ifndef NAME_MAX
+#define NAME_MAX 255
+#endif
+
+#define __DIRENT_COOKIE 0xfefeabab
+
+
+struct dirent
+{
+ ino_t d_ino; /* unused - no equivalent on WIN32 */
+ char d_name[NAME_MAX+1];
+};
+
+typedef struct dir_struct {
+ ULONG dir_ulCookie;
+ HANDLE dir_hDirHandle;
+ DWORD dir_nNumFiles;
+ char dir_pDirectoryName[NAME_MAX+1];
+ struct dirent dir_sdReturn;
+} DIR;
+
+DIR *opendir(const char *);
+struct dirent *readdir(DIR *);
+void rewinddir(DIR *);
+void closedir(DIR *);
+int telldir(DIR *);
+void seekdir(DIR *, long);
+
+#endif
diff --git a/w32/include/pathstuff.h b/w32/include/pathstuff.h
new file mode 100644
index 0000000..285ed04
--- /dev/null
+++ b/w32/include/pathstuff.h
@@ -0,0 +1,10 @@
+#ifndef _PATHSTUFF_H
+#define _PATHSTUFF_H
+
+extern char * convert_Path_to_win32(char *Path, char to_delim);
+extern char * w32ify(char *file, int resolve);
+extern char * getcwd_fs(char *buf, int len);
+
+#define convert_vpath_to_win32(vpath, delim) convert_Path_to_win32(vpath, delim)
+
+#endif
diff --git a/w32/include/sub_proc.h b/w32/include/sub_proc.h
new file mode 100644
index 0000000..12e9cf3
--- /dev/null
+++ b/w32/include/sub_proc.h
@@ -0,0 +1,54 @@
+#ifndef SUB_PROC_H
+#define SUB_PROC_H
+
+/*
+ * Component Name:
+ *
+ * $Date$
+ *
+ * $Source$
+ *
+ * $Revision$
+ *
+ * Description:
+ *
+ * (C) COPYRIGHT TIVOLI Systems, Inc. 1991-1994
+ * Unpublished Work
+ * All Rights Reserved
+ * Licensed Material - Property of TIVOLI Systems, Inc.
+ */
+
+/* $Id$ */
+
+#ifdef WIN32
+
+#define EXTERN_DECL(entry, args) extern entry args
+#define VOID_DECL void
+
+EXTERN_DECL(HANDLE process_init, (VOID_DECL));
+EXTERN_DECL(HANDLE process_init_fd, (HANDLE stdinh, HANDLE stdouth,
+ HANDLE stderrh));
+EXTERN_DECL(long process_begin, (HANDLE proc, char **argv, char **envp,
+ char *exec_path, char *as_user));
+EXTERN_DECL(long process_pipe_io, (HANDLE proc, char *stdin_data,
+ int stdin_data_len));
+EXTERN_DECL(long process_file_io, (HANDLE proc));
+EXTERN_DECL(void process_cleanup, (HANDLE proc));
+EXTERN_DECL(HANDLE process_wait_for_any, (VOID_DECL));
+EXTERN_DECL(void process_register, (HANDLE proc));
+EXTERN_DECL(HANDLE process_easy, (char** argv, char** env));
+EXTERN_DECL(BOOL process_kill, (HANDLE proc, int signal));
+
+/* support routines */
+EXTERN_DECL(long process_errno, (HANDLE proc));
+EXTERN_DECL(long process_last_err, (HANDLE proc));
+EXTERN_DECL(long process_exit_code, (HANDLE proc));
+EXTERN_DECL(long process_signal, (HANDLE proc));
+EXTERN_DECL(char * process_outbuf, (HANDLE proc));
+EXTERN_DECL(char * process_errbuf, (HANDLE proc));
+EXTERN_DECL(int process_outcnt, (HANDLE proc));
+EXTERN_DECL(int process_errcnt, (HANDLE proc));
+EXTERN_DECL(void process_pipes, (HANDLE proc, int pipes[3]));
+
+#endif
+#endif
diff --git a/w32/include/w32err.h b/w32/include/w32err.h
new file mode 100644
index 0000000..7e9df78
--- /dev/null
+++ b/w32/include/w32err.h
@@ -0,0 +1,10 @@
+#ifndef _W32ERR_H_
+#define _W32ERR_H_
+
+#ifndef EXTERN_DECL
+#define EXTERN_DECL(entry, args) entry args
+#endif
+
+EXTERN_DECL(char * map_win32_error_to_string, (DWORD error));
+
+#endif /* !_W32ERR_H */
diff --git a/w32/pathstuff.c b/w32/pathstuff.c
new file mode 100644
index 0000000..e5011f8
--- /dev/null
+++ b/w32/pathstuff.c
@@ -0,0 +1,219 @@
+#include <string.h>
+#include <stdlib.h>
+#include "make.h"
+
+/*
+ * Convert delimiter separated path to Canonical format.
+ */
+char *
+convert_Path_to_win32(char *Path, char to_delim)
+{
+ char *etok; /* token separator for old Path */
+ char *p; /* points to element of old Path */
+
+ /* is this a multi-element Path ? */
+ for (p = Path, etok = strpbrk(p, ":;");
+ etok;
+ etok = strpbrk(p, ":;"))
+ if ((etok - p) == 1) {
+ if (*(etok - 1) == ';' ||
+ *(etok - 1) == ':') {
+ etok[-1] = to_delim;
+ etok[0] = to_delim;
+ p = ++etok;
+ continue; /* ignore empty bucket */
+ } else if (etok = strpbrk(etok+1, ":;")) {
+ /* found one to count, handle drive letter */
+ *etok = to_delim;
+ p = ++etok;
+ } else
+ /* all finished, force abort */
+ p += strlen(p);
+ } else {
+ /* found another one, no drive letter */
+ *etok = to_delim;
+ p = ++etok;
+ }
+
+#if 0
+ /* convert to backward slashes */
+ for (p = Path, p = strchr(p, '/'); p; p = strchr(p, '/'))
+ *p = '\\';
+#endif
+ return Path;
+}
+
+/*
+ * Convert to forward slashes. Resolve to full pathname optionally
+ */
+char *
+w32ify(char *filename, int resolve)
+{
+ static char w32_path[FILENAME_MAX];
+ char *p;
+
+ if (resolve)
+ _fullpath(w32_path, filename, sizeof (w32_path));
+ else
+ strncpy(w32_path, filename, sizeof (w32_path));
+
+ for (p = w32_path; p && *p; p++)
+ if (*p == '\\')
+ *p = '/';
+
+ return w32_path;
+}
+
+char *
+getcwd_fs(char* buf, int len)
+{
+ char *p;
+
+ if (p = getcwd(buf, len)) {
+ char *q = w32ify(buf, 0);
+ strncpy(buf, q, len);
+ }
+
+ return p;
+}
+
+#ifdef unused
+/*
+ * Convert delimiter separated pathnames (e.g. PATH) or single file pathname
+ * (e.g. c:/foo, c:\bar) to NutC format. If we are handed a string that
+ * _NutPathToNutc() fails to convert, just return the path we were handed
+ * and assume the caller will know what to do with it (It was probably
+ * a mistake to try and convert it anyway due to some of the bizarre things
+ * that might look like pathnames in makefiles).
+ */
+char *
+convert_path_to_nutc(char *path)
+{
+ int count; /* count of path elements */
+ char *nutc_path; /* new NutC path */
+ int nutc_path_len; /* length of buffer to allocate for new path */
+ char *pathp; /* pointer to nutc_path used to build it */
+ char *etok; /* token separator for old path */
+ char *p; /* points to element of old path */
+ char sep; /* what flavor of separator used in old path */
+ char *rval;
+
+ /* is this a multi-element path ? */
+ for (p = path, etok = strpbrk(p, ":;"), count = 0;
+ etok;
+ etok = strpbrk(p, ":;"))
+ if ((etok - p) == 1) {
+ if (*(etok - 1) == ';' ||
+ *(etok - 1) == ':') {
+ p = ++etok;
+ continue; /* ignore empty bucket */
+ } else if (etok = strpbrk(etok+1, ":;"))
+ /* found one to count, handle drive letter */
+ p = ++etok, count++;
+ else
+ /* all finished, force abort */
+ p += strlen(p);
+ } else
+ /* found another one, no drive letter */
+ p = ++etok, count++;
+
+ if (count) {
+ count++; /* x1;x2;x3 <- need to count x3 */
+
+ /*
+ * Hazard a guess on how big the buffer needs to be.
+ * We have to convert things like c:/foo to /c=/foo.
+ */
+ nutc_path_len = strlen(path) + (count*2) + 1;
+ nutc_path = xmalloc(nutc_path_len);
+ pathp = nutc_path;
+ *pathp = '\0';
+
+ /*
+ * Loop through PATH and convert one elemnt of the path at at
+ * a time. Single file pathnames will fail this and fall
+ * to the logic below loop.
+ */
+ for (p = path, etok = strpbrk(p, ":;");
+ etok;
+ etok = strpbrk(p, ":;")) {
+
+ /* don't trip up on device specifiers or empty path slots */
+ if ((etok - p) == 1)
+ if (*(etok - 1) == ';' ||
+ *(etok - 1) == ':') {
+ p = ++etok;
+ continue;
+ } else if ((etok = strpbrk(etok+1, ":;")) == NULL)
+ break; /* thing found was a WIN32 pathname */
+
+ /* save separator */
+ sep = *etok;
+
+ /* terminate the current path element -- temporarily */
+ *etok = '\0';
+
+#ifdef __NUTC__
+ /* convert to NutC format */
+ if (_NutPathToNutc(p, pathp, 0) == FALSE) {
+ free(nutc_path);
+ rval = savestring(path, strlen(path));
+ return rval;
+ }
+#else
+ *pathp++ = '/';
+ *pathp++ = p[0];
+ *pathp++ = '=';
+ *pathp++ = '/';
+ strcpy(pathp, &p[2]);
+#endif
+
+ pathp += strlen(pathp);
+ *pathp++ = ':'; /* use Unix style path separtor for new path */
+ *pathp = '\0'; /* make sure we are null terminaed */
+
+ /* restore path separator */
+ *etok = sep;
+
+ /* point p to first char of next path element */
+ p = ++etok;
+
+ }
+ } else {
+ nutc_path_len = strlen(path) + 3;
+ nutc_path = xmalloc(nutc_path_len);
+ pathp = nutc_path;
+ *pathp = '\0';
+ p = path;
+ }
+
+ /*
+ * OK, here we handle the last element in PATH (e.g. c of a;b;c)
+ * or the path was a single filename and will be converted
+ * here. Note, testing p here assures that we don't trip up
+ * on paths like a;b; which have trailing delimiter followed by
+ * nothing.
+ */
+ if (*p != '\0') {
+#ifdef __NUTC__
+ if (_NutPathToNutc(p, pathp, 0) == FALSE) {
+ free(nutc_path);
+ rval = savestring(path, strlen(path));
+ return rval;
+ }
+#else
+ *pathp++ = '/';
+ *pathp++ = p[0];
+ *pathp++ = '=';
+ *pathp++ = '/';
+ strcpy(pathp, &p[2]);
+#endif
+ } else
+ *(pathp-1) = '\0'; /* we're already done, don't leave trailing : */
+
+ rval = savestring(nutc_path, strlen(nutc_path));
+ free(nutc_path);
+ return rval;
+}
+
+#endif
diff --git a/w32/subproc/NMakefile b/w32/subproc/NMakefile
new file mode 100644
index 0000000..3d44b82
--- /dev/null
+++ b/w32/subproc/NMakefile
@@ -0,0 +1,59 @@
+# NOTE: If you have no `make' program at all to process this makefile, run
+# `build.bat' instead.
+#
+# Copyright (C) 1988, 89, 91, 92, 93, 94, 95, 1996 Free Software Foundation, Inc
+# This file is part of GNU Make.
+#
+# GNU Make is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+#
+# GNU Make is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with GNU Make; see the file COPYING. If not, write to
+# the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+#
+# NMakefile for GNU Make (subproc library)
+#
+LIB = lib
+CC = cl
+
+OUTDIR=.
+MAKEFILE=NMakefile
+
+CFLAGS_any = /nologo /MT /W3 /GX /Z7 /YX /D WIN32 /D _WINDOWS -I. -I../include
+CFLAGS_debug = $(CFLAGS_any) /Od /D _DEBUG /FR.\WinDebug\ /Fp.\WinDebug\subproc.pch /Fo.\WinDebug/
+CFLAGS_release = $(CFLAGS_any) /O2 /FR.\WinRel\ /Fp.\WinRel\subproc.pch /Fo.\WinRel/
+
+all: Release Debug
+
+Release:
+ $(MAKE) /f $(MAKEFILE) OUTDIR=WinRel CFLAGS="$(CFLAGS_release)" WinRel/subproc.lib
+Debug:
+ $(MAKE) /f $(MAKEFILE) OUTDIR=WinDebug CFLAGS="$(CFLAGS_debug)" WinDebug/subproc.lib
+
+clean:
+ rmdir /s /q WinRel WinDebug
+
+$(OUTDIR):
+ if not exist .\$@\nul mkdir .\$@
+
+OBJS = $(OUTDIR)/misc.obj $(OUTDIR)/w32err.obj $(OUTDIR)/sub_proc.obj
+
+$(OUTDIR)/subproc.lib: $(OUTDIR) $(OBJS)
+ $(LIB) -out:$@ @<<
+ $(OBJS)
+<<
+
+.c{$(OUTDIR)}.obj:
+ $(CC) $(CFLAGS) /c $<
+
+$(OUTDIR)/misc.obj: misc.c proc.h
+$(OUTDIR)/sub_proc.obj: sub_proc.c ../include/sub_proc.h ../include/w32err.h proc.h
+$(OUTDIR)/w32err.obj: w32err.c ../include/w32err.h
diff --git a/w32/subproc/build.bat b/w32/subproc/build.bat
new file mode 100644
index 0000000..45231bf
--- /dev/null
+++ b/w32/subproc/build.bat
@@ -0,0 +1,10 @@
+if not exist .\WinDebug\nul mkdir .\WinDebug
+cl.exe /nologo /MT /W3 /GX /Z7 /YX /Od /I .. /I . /I ../include /D WIN32 /D _DEBUG /D _WINDOWS /FR.\WinDebug/ /Fp.\WinDebug/subproc.pch /Fo.\WinDebug/ /c misc.c
+cl.exe /nologo /MT /W3 /GX /Z7 /YX /Od /I .. /I . /I ../include /D WIN32 /D _DEBUG /D _WINDOWS /FR.\WinDebug/ /Fp.\WinDebug/subproc.pch /Fo.\WinDebug/ /c sub_proc.c
+cl.exe /nologo /MT /W3 /GX /Z7 /YX /Od /I .. /I . /I ../include /D WIN32 /D _DEBUG /D _WINDOWS /FR.\WinDebug/ /Fp.\WinDebug/subproc.pch /Fo.\WinDebug/ /c w32err.c
+lib.exe /NOLOGO /OUT:.\WinDebug\subproc.lib .\WinDebug/misc.obj .\WinDebug/sub_proc.obj .\WinDebug/w32err.obj
+if not exist .\WinRel\nul mkdir .\WinRel
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I ../include /D WIN32 /D NDEBUG /D _WINDOWS /FR.\WinRel/ /Fp.\WinRel/subproc.pch /Fo.\WinRel/ /c misc.c
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I ../include /D WIN32 /D NDEBUG /D _WINDOWS /FR.\WinRel/ /Fp.\WinRel/subproc.pch /Fo.\WinRel/ /c sub_proc.c
+cl.exe /nologo /MT /W3 /GX /YX /O2 /I ../include /D WIN32 /D NDEBUG /D _WINDOWS /FR.\WinRel/ /Fp.\WinRel/subproc.pch /Fo.\WinRel/ /c w32err.c
+lib.exe /NOLOGO /OUT:.\WinRel\subproc.lib .\WinRel/misc.obj .\WinRel/sub_proc.obj .\WinRel/w32err.obj
diff --git a/w32/subproc/misc.c b/w32/subproc/misc.c
new file mode 100644
index 0000000..23b6124
--- /dev/null
+++ b/w32/subproc/misc.c
@@ -0,0 +1,63 @@
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include "proc.h"
+
+
+/*
+ * Description: Convert a NULL string terminated UNIX environment block to
+ * an environment block suitable for a win32 system call
+ *
+ * Returns: TRUE= success, FALSE=fail
+ *
+ * Notes/Dependencies: the environment block is sorted in case-insensitive
+ * order, is double-null terminated, and is a char *, not a char **
+ */
+int _cdecl compare(const void *a1, const void *a2)
+{
+ return _stricoll(*((char**)a1),*((char**)a2));
+}
+bool_t
+arr2envblk(char **arr, char **envblk_out)
+{
+ char **tmp;
+ int size_needed;
+ int arrcnt;
+ char *ptr;
+
+ arrcnt = 0;
+ while (arr[arrcnt]) {
+ arrcnt++;
+ }
+
+ tmp = (char**) calloc(arrcnt + 1, sizeof(char *));
+ if (!tmp) {
+ return FALSE;
+ }
+
+ arrcnt = 0;
+ size_needed = 0;
+ while (arr[arrcnt]) {
+ tmp[arrcnt] = arr[arrcnt];
+ size_needed += strlen(arr[arrcnt]) + 1;
+ arrcnt++;
+ }
+ size_needed++;
+
+ qsort((void *) tmp, (size_t) arrcnt, sizeof (char*), compare);
+
+ ptr = *envblk_out = calloc(size_needed, 1);
+ if (!ptr) {
+ free(tmp);
+ return FALSE;
+ }
+
+ arrcnt = 0;
+ while (tmp[arrcnt]) {
+ strcpy(ptr, tmp[arrcnt]);
+ ptr += strlen(tmp[arrcnt]) + 1;
+ arrcnt++;
+ }
+ return TRUE;
+}
diff --git a/w32/subproc/proc.h b/w32/subproc/proc.h
new file mode 100644
index 0000000..ce7a14f
--- /dev/null
+++ b/w32/subproc/proc.h
@@ -0,0 +1,13 @@
+#ifndef _PROC_H
+#define _PROC_H
+
+typedef int bool_t;
+
+#define E_SCALL 101
+#define E_IO 102
+#define E_NO_MEM 103
+#define E_FORK 104
+
+extern bool_t arr2envblk(char **arr, char **envblk_out);
+
+#endif
diff --git a/w32/subproc/sub_proc.c b/w32/subproc/sub_proc.c
new file mode 100644
index 0000000..52cd9d7
--- /dev/null
+++ b/w32/subproc/sub_proc.c
@@ -0,0 +1,1100 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <process.h> /* for msvc _beginthreadex, _endthreadex */
+#include <windows.h>
+
+#include "sub_proc.h"
+#include "proc.h"
+#include "w32err.h"
+
+static char *make_command_line( char *shell_name, char *exec_path, char **argv);
+
+typedef struct sub_process_t {
+ int sv_stdin[2];
+ int sv_stdout[2];
+ int sv_stderr[2];
+ int using_pipes;
+ char *inp;
+ DWORD incnt;
+ char * volatile outp;
+ volatile DWORD outcnt;
+ char * volatile errp;
+ volatile DWORD errcnt;
+ int pid;
+ int exit_code;
+ int signal;
+ long last_err;
+ long lerrno;
+} sub_process;
+
+/* keep track of children so we can implement a waitpid-like routine */
+static sub_process *proc_array[256];
+static int proc_index = 0;
+static int fake_exits_pending = 0;
+
+/*
+ * When a process has been waited for, adjust the wait state
+ * array so that we don't wait for it again
+ */
+static void
+process_adjust_wait_state(sub_process* pproc)
+{
+ int i;
+
+ if (!proc_index)
+ return;
+
+ for (i = 0; i < proc_index; i++)
+ if (proc_array[i]->pid == pproc->pid)
+ break;
+
+ if (i < proc_index) {
+ proc_index--;
+ if (i != proc_index)
+ memmove(&proc_array[i], &proc_array[i+1],
+ (proc_index-i) * sizeof(sub_process*));
+ proc_array[proc_index] = NULL;
+ }
+}
+
+/*
+ * Waits for any of the registered child processes to finish.
+ */
+static sub_process *
+process_wait_for_any_private(void)
+{
+ HANDLE handles[256];
+ DWORD retval, which;
+ int i;
+
+ if (!proc_index)
+ return NULL;
+
+ /* build array of handles to wait for */
+ for (i = 0; i < proc_index; i++) {
+ handles[i] = (HANDLE) proc_array[i]->pid;
+
+ if (fake_exits_pending && proc_array[i]->exit_code)
+ break;
+ }
+
+ /* wait for someone to exit */
+ if (!fake_exits_pending) {
+ retval = WaitForMultipleObjects(proc_index, handles, FALSE, INFINITE);
+ which = retval - WAIT_OBJECT_0;
+ } else {
+ fake_exits_pending--;
+ retval = !WAIT_FAILED;
+ which = i;
+ }
+
+ /* return pointer to process */
+ if (retval != WAIT_FAILED) {
+ sub_process* pproc = proc_array[which];
+ process_adjust_wait_state(pproc);
+ return pproc;
+ } else
+ return NULL;
+}
+
+/*
+ * Terminate a process.
+ */
+BOOL
+process_kill(HANDLE proc, int signal)
+{
+ sub_process* pproc = (sub_process*) proc;
+ pproc->signal = signal;
+ return (TerminateProcess((HANDLE) pproc->pid, signal));
+}
+
+/*
+ * Use this function to register processes you wish to wait for by
+ * calling process_file_io(NULL) or process_wait_any(). This must be done
+ * because it is possible for callers of this library to reuse the same
+ * handle for multiple processes launches :-(
+ */
+void
+process_register(HANDLE proc)
+{
+ proc_array[proc_index++] = (sub_process *) proc;
+}
+
+/*
+ * Public function which works kind of like waitpid(). Wait for any
+ * of the children to die and return results. To call this function,
+ * you must do 1 of things:
+ *
+ * x = process_easy(...);
+ *
+ * or
+ *
+ * x = process_init_fd();
+ * process_register(x);
+ *
+ * or
+ *
+ * x = process_init();
+ * process_register(x);
+ *
+ * You must NOT then call process_pipe_io() because this function is
+ * not capable of handling automatic notification of any child
+ * death.
+ */
+
+HANDLE
+process_wait_for_any(void)
+{
+ sub_process* pproc = process_wait_for_any_private();
+
+ if (!pproc)
+ return NULL;
+ else {
+ /*
+ * Ouch! can't tell caller if this fails directly. Caller
+ * will have to use process_last_err()
+ */
+ (void) process_file_io(pproc);
+ return ((HANDLE) pproc);
+ }
+}
+
+long
+process_errno(HANDLE proc)
+{
+ return (((sub_process *)proc)->lerrno);
+}
+
+long
+process_signal(HANDLE proc)
+{
+ return (((sub_process *)proc)->signal);
+}
+
+ long
+process_last_err(HANDLE proc)
+{
+ return (((sub_process *)proc)->last_err);
+}
+
+ long
+process_exit_code(HANDLE proc)
+{
+ return (((sub_process *)proc)->exit_code);
+}
+
+ char *
+process_outbuf(HANDLE proc)
+{
+ return (((sub_process *)proc)->outp);
+}
+
+ char *
+process_errbuf(HANDLE proc)
+{
+ return (((sub_process *)proc)->errp);
+}
+
+ int
+process_outcnt(HANDLE proc)
+{
+ return (((sub_process *)proc)->outcnt);
+}
+
+ int
+process_errcnt(HANDLE proc)
+{
+ return (((sub_process *)proc)->errcnt);
+}
+
+ void
+process_pipes(HANDLE proc, int pipes[3])
+{
+ pipes[0] = ((sub_process *)proc)->sv_stdin[0];
+ pipes[1] = ((sub_process *)proc)->sv_stdout[0];
+ pipes[2] = ((sub_process *)proc)->sv_stderr[0];
+ return;
+}
+
+
+ HANDLE
+process_init()
+{
+ sub_process *pproc;
+ /*
+ * open file descriptors for attaching stdin/stdout/sterr
+ */
+ HANDLE stdin_pipes[2];
+ HANDLE stdout_pipes[2];
+ HANDLE stderr_pipes[2];
+ SECURITY_ATTRIBUTES inherit;
+ BYTE sd[SECURITY_DESCRIPTOR_MIN_LENGTH];
+
+ pproc = malloc(sizeof(*pproc));
+ memset(pproc, 0, sizeof(*pproc));
+
+ /* We can't use NULL for lpSecurityDescriptor because that
+ uses the default security descriptor of the calling process.
+ Instead we use a security descriptor with no DACL. This
+ allows nonrestricted access to the associated objects. */
+
+ if (!InitializeSecurityDescriptor((PSECURITY_DESCRIPTOR)(&sd),
+ SECURITY_DESCRIPTOR_REVISION)) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ return((HANDLE)pproc);
+ }
+
+ inherit.nLength = sizeof(inherit);
+ inherit.lpSecurityDescriptor = (PSECURITY_DESCRIPTOR)(&sd);
+ inherit.bInheritHandle = TRUE;
+
+ // By convention, parent gets pipe[0], and child gets pipe[1]
+ // This means the READ side of stdin pipe goes into pipe[1]
+ // and the WRITE side of the stdout and stderr pipes go into pipe[1]
+ if (CreatePipe( &stdin_pipes[1], &stdin_pipes[0], &inherit, 0) == FALSE ||
+ CreatePipe( &stdout_pipes[0], &stdout_pipes[1], &inherit, 0) == FALSE ||
+ CreatePipe( &stderr_pipes[0], &stderr_pipes[1], &inherit, 0) == FALSE) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ return((HANDLE)pproc);
+ }
+
+ //
+ // Mark the parent sides of the pipes as non-inheritable
+ //
+ if (SetHandleInformation(stdin_pipes[0],
+ HANDLE_FLAG_INHERIT, 0) == FALSE ||
+ SetHandleInformation(stdout_pipes[0],
+ HANDLE_FLAG_INHERIT, 0) == FALSE ||
+ SetHandleInformation(stderr_pipes[0],
+ HANDLE_FLAG_INHERIT, 0) == FALSE) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ return((HANDLE)pproc);
+ }
+ pproc->sv_stdin[0] = (int) stdin_pipes[0];
+ pproc->sv_stdin[1] = (int) stdin_pipes[1];
+ pproc->sv_stdout[0] = (int) stdout_pipes[0];
+ pproc->sv_stdout[1] = (int) stdout_pipes[1];
+ pproc->sv_stderr[0] = (int) stderr_pipes[0];
+ pproc->sv_stderr[1] = (int) stderr_pipes[1];
+
+ pproc->using_pipes = 1;
+
+ pproc->lerrno = 0;
+
+ return((HANDLE)pproc);
+}
+
+
+ HANDLE
+process_init_fd(HANDLE stdinh, HANDLE stdouth, HANDLE stderrh)
+{
+ sub_process *pproc;
+
+ pproc = malloc(sizeof(*pproc));
+ memset(pproc, 0, sizeof(*pproc));
+
+ /*
+ * Just pass the provided file handles to the 'child side' of the
+ * pipe, bypassing pipes altogether.
+ */
+ pproc->sv_stdin[1] = (int) stdinh;
+ pproc->sv_stdout[1] = (int) stdouth;
+ pproc->sv_stderr[1] = (int) stderrh;
+
+ pproc->last_err = pproc->lerrno = 0;
+
+ return((HANDLE)pproc);
+}
+
+
+static HANDLE
+find_file(char *exec_path, LPOFSTRUCT file_info)
+{
+ HANDLE exec_handle;
+ char *fname;
+ char *ext;
+
+ if ((exec_handle = (HANDLE)OpenFile(exec_path, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ return(exec_handle);
+ }
+
+ fname = malloc(strlen(exec_path) + 5);
+ strcpy(fname, exec_path);
+ ext = fname + strlen(fname);
+ strcpy(ext, ".exe");
+ if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ free(fname);
+ return(exec_handle);
+ }
+
+ strcpy(ext, ".bat");
+ if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ free(fname);
+ return(exec_handle);
+ }
+
+ strcpy(ext, ".com");
+ if ((exec_handle = (HANDLE)OpenFile(fname, file_info,
+ OF_READ | OF_SHARE_COMPAT)) != (HANDLE)HFILE_ERROR) {
+ free(fname);
+ return(exec_handle);
+ }
+
+ free(fname);
+ return(exec_handle);
+}
+
+
+/*
+ * Description: Create the child process to be helped
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+long
+process_begin(
+ HANDLE proc,
+ char **argv,
+ char **envp,
+ char *exec_path,
+ char *as_user)
+{
+ sub_process *pproc = (sub_process *)proc;
+ char *shell_name = 0;
+ int file_not_found=0;
+ HANDLE exec_handle;
+ char buf[256];
+ DWORD bytes_returned;
+ DWORD flags;
+ char *command_line;
+ STARTUPINFO startInfo;
+ PROCESS_INFORMATION procInfo;
+ char *envblk=NULL;
+ OFSTRUCT file_info;
+
+
+ /*
+ * Shell script detection... if the exec_path starts with #! then
+ * we want to exec shell-script-name exec-path, not just exec-path
+ * NT doesn't recognize #!/bin/sh or #!/etc/Tivoli/bin/perl. We do not
+ * hard-code the path to the shell or perl or whatever: Instead, we
+ * assume it's in the path somewhere (generally, the NT tools
+ * bin directory)
+ * We use OpenFile here because it is capable of searching the Path.
+ */
+
+ exec_handle = find_file(exec_path, &file_info);
+
+ /*
+ * If we couldn't open the file, just assume that Win32 will be able
+ * to find and execute it.
+ */
+ if (exec_handle == (HANDLE)HFILE_ERROR) {
+ file_not_found++;
+ }
+ else {
+ /* Attempt to read the first line of the file */
+ if (ReadFile( exec_handle,
+ buf, sizeof(buf) - 1, /* leave room for trailing NULL */
+ &bytes_returned, 0) == FALSE || bytes_returned < 2) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_IO;
+ CloseHandle(exec_handle);
+ return(-1);
+ }
+ if (buf[0] == '#' && buf[1] == '!') {
+ /*
+ * This is a shell script... Change the command line from
+ * exec_path args to shell_name exec_path args
+ */
+ char *p;
+
+ /* Make sure buf is NULL terminated */
+ buf[bytes_returned] = 0;
+ /*
+ * Depending on the file system type, etc. the first line
+ * of the shell script may end with newline or newline-carriage-return
+ * Whatever it ends with, cut it off.
+ */
+ p= strchr(buf, '\n');
+ if (p)
+ *p = 0;
+ p = strchr(buf, '\r');
+ if (p)
+ *p = 0;
+
+ /*
+ * Find base name of shell
+ */
+ shell_name = strrchr( buf, '/');
+ if (shell_name) {
+ shell_name++;
+ } else {
+ shell_name = &buf[2];/* skipping "#!" */
+ }
+
+ }
+ CloseHandle(exec_handle);
+ }
+
+ flags = 0;
+
+ if (file_not_found)
+ command_line = make_command_line( shell_name, exec_path, argv);
+ else
+ command_line = make_command_line( shell_name, file_info.szPathName,
+ argv);
+
+ if ( command_line == NULL ) {
+ pproc->last_err = 0;
+ pproc->lerrno = E_NO_MEM;
+ return(-1);
+ }
+
+ if (envp) {
+ if (arr2envblk(envp, &envblk) ==FALSE) {
+ pproc->last_err = 0;
+ pproc->lerrno = E_NO_MEM;
+ free( command_line );
+ return(-1);
+ }
+ }
+
+ if ((shell_name) || (file_not_found)) {
+ exec_path = 0; /* Search for the program in %Path% */
+ } else {
+ exec_path = file_info.szPathName;
+ }
+
+ /*
+ * Set up inherited stdin, stdout, stderr for child
+ */
+ GetStartupInfo(&startInfo);
+ startInfo.dwFlags = STARTF_USESTDHANDLES;
+ startInfo.lpReserved = 0;
+ startInfo.cbReserved2 = 0;
+ startInfo.lpReserved2 = 0;
+ startInfo.lpTitle = shell_name ? shell_name : exec_path;
+ startInfo.hStdInput = (HANDLE)pproc->sv_stdin[1];
+ startInfo.hStdOutput = (HANDLE)pproc->sv_stdout[1];
+ startInfo.hStdError = (HANDLE)pproc->sv_stderr[1];
+
+ /*
+ * See if we need to setuid to a different user.
+ */
+ if (as_user) {
+ return -1;
+ }
+
+ if (as_user) {
+ return -1;
+ } else {
+ if (CreateProcess(
+ exec_path,
+ command_line,
+ NULL,
+ 0, /* default security attributes for thread */
+ TRUE, /* inherit handles (e.g. helper pipes, oserv socket) */
+ flags,
+ envblk,
+ 0, /* default starting directory */
+ &startInfo,
+ &procInfo) == FALSE) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_FORK;
+ fprintf(stderr, "process_begin: CreateProcess(%s, %s, ...) failed.\n", exec_path, command_line);
+ free( command_line );
+ return(-1);
+ }
+ }
+
+ pproc->pid = (int)procInfo.hProcess;
+ /* Close the thread handle -- we'll just watch the process */
+ CloseHandle(procInfo.hThread);
+
+ /* Close the halves of the pipes we don't need */
+ if (pproc->sv_stdin) {
+ CloseHandle((HANDLE)pproc->sv_stdin[1]);
+ (HANDLE)pproc->sv_stdin[1] = 0;
+ }
+ if (pproc->sv_stdout) {
+ CloseHandle((HANDLE)pproc->sv_stdout[1]);
+ (HANDLE)pproc->sv_stdout[1] = 0;
+ }
+ if (pproc->sv_stderr) {
+ CloseHandle((HANDLE)pproc->sv_stderr[1]);
+ (HANDLE)pproc->sv_stderr[1] = 0;
+ }
+
+ free( command_line );
+ pproc->lerrno=0;
+ return 0;
+}
+
+
+
+static DWORD
+proc_stdin_thread(sub_process *pproc)
+{
+ DWORD in_done;
+ for (;;) {
+ if (WriteFile( (HANDLE) pproc->sv_stdin[0], pproc->inp, pproc->incnt,
+ &in_done, NULL) == FALSE)
+ _endthreadex(0);
+ // This if should never be true for anonymous pipes, but gives
+ // us a chance to change I/O mechanisms later
+ if (in_done < pproc->incnt) {
+ pproc->incnt -= in_done;
+ pproc->inp += in_done;
+ } else {
+ _endthreadex(0);
+ }
+ }
+ return 0; // for compiler warnings only.. not reached
+}
+
+static DWORD
+proc_stdout_thread(sub_process *pproc)
+{
+ DWORD bufsize = 1024;
+ char c;
+ DWORD nread;
+ pproc->outp = malloc(bufsize);
+ if (pproc->outp == NULL)
+ _endthreadex(0);
+ pproc->outcnt = 0;
+
+ for (;;) {
+ if (ReadFile( (HANDLE)pproc->sv_stdout[0], &c, 1, &nread, NULL)
+ == FALSE) {
+/* map_win32_error_to_string(GetLastError());*/
+ _endthreadex(0);
+ }
+ if (nread == 0)
+ _endthreadex(0);
+ if (pproc->outcnt + nread > bufsize) {
+ bufsize += nread + 512;
+ pproc->outp = realloc(pproc->outp, bufsize);
+ if (pproc->outp == NULL) {
+ pproc->outcnt = 0;
+ _endthreadex(0);
+ }
+ }
+ pproc->outp[pproc->outcnt++] = c;
+ }
+ return 0;
+}
+
+static DWORD
+proc_stderr_thread(sub_process *pproc)
+{
+ DWORD bufsize = 1024;
+ char c;
+ DWORD nread;
+ pproc->errp = malloc(bufsize);
+ if (pproc->errp == NULL)
+ _endthreadex(0);
+ pproc->errcnt = 0;
+
+ for (;;) {
+ if (ReadFile( (HANDLE)pproc->sv_stderr[0], &c, 1, &nread, NULL) == FALSE) {
+ map_win32_error_to_string(GetLastError());
+ _endthreadex(0);
+ }
+ if (nread == 0)
+ _endthreadex(0);
+ if (pproc->errcnt + nread > bufsize) {
+ bufsize += nread + 512;
+ pproc->errp = realloc(pproc->errp, bufsize);
+ if (pproc->errp == NULL) {
+ pproc->errcnt = 0;
+ _endthreadex(0);
+ }
+ }
+ pproc->errp[pproc->errcnt++] = c;
+ }
+ return 0;
+}
+
+
+/*
+ * Purpose: collects output from child process and returns results
+ *
+ * Description:
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+ long
+process_pipe_io(
+ HANDLE proc,
+ char *stdin_data,
+ int stdin_data_len)
+{
+ sub_process *pproc = (sub_process *)proc;
+ bool_t stdin_eof = FALSE, stdout_eof = FALSE, stderr_eof = FALSE;
+ HANDLE childhand = (HANDLE) pproc->pid;
+ HANDLE tStdin, tStdout, tStderr;
+ DWORD dwStdin, dwStdout, dwStderr;
+ HANDLE wait_list[4];
+ DWORD wait_count;
+ DWORD wait_return;
+ HANDLE ready_hand;
+ bool_t child_dead = FALSE;
+
+
+ /*
+ * Create stdin thread, if needed
+ */
+ pproc->inp = stdin_data;
+ pproc->incnt = stdin_data_len;
+ if (!pproc->inp) {
+ stdin_eof = TRUE;
+ CloseHandle((HANDLE)pproc->sv_stdin[0]);
+ (HANDLE)pproc->sv_stdin[0] = 0;
+ } else {
+ tStdin = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stdin_thread, pproc, 0,
+ (unsigned int *) &dwStdin);
+ if (tStdin == 0) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+ }
+
+ /*
+ * Assume child will produce stdout and stderr
+ */
+ tStdout = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stdout_thread, pproc, 0,
+ (unsigned int *) &dwStdout);
+ tStderr = (HANDLE) _beginthreadex( 0, 1024,
+ (unsigned (__stdcall *) (void *))proc_stderr_thread, pproc, 0,
+ (unsigned int *) &dwStderr);
+
+ if (tStdout == 0 || tStderr == 0) {
+
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+
+
+ /*
+ * Wait for all I/O to finish and for the child process to exit
+ */
+
+ while (!stdin_eof || !stdout_eof || !stderr_eof || !child_dead) {
+ wait_count = 0;
+ if (!stdin_eof) {
+ wait_list[wait_count++] = tStdin;
+ }
+ if (!stdout_eof) {
+ wait_list[wait_count++] = tStdout;
+ }
+ if (!stderr_eof) {
+ wait_list[wait_count++] = tStderr;
+ }
+ if (!child_dead) {
+ wait_list[wait_count++] = childhand;
+ }
+
+ wait_return = WaitForMultipleObjects(wait_count, wait_list,
+ FALSE, /* don't wait for all: one ready will do */
+ child_dead? 1000 :INFINITE); /* after the child dies, subthreads have
+ one second to collect all remaining output */
+
+ if (wait_return == WAIT_FAILED) {
+/* map_win32_error_to_string(GetLastError());*/
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+
+ ready_hand = wait_list[wait_return - WAIT_OBJECT_0];
+
+ if (ready_hand == tStdin) {
+ CloseHandle((HANDLE)pproc->sv_stdin[0]);
+ (HANDLE)pproc->sv_stdin[0] = 0;
+ CloseHandle(tStdin);
+ tStdin = 0;
+ stdin_eof = TRUE;
+
+ } else if (ready_hand == tStdout) {
+
+ CloseHandle((HANDLE)pproc->sv_stdout[0]);
+ (HANDLE)pproc->sv_stdout[0] = 0;
+ CloseHandle(tStdout);
+ tStdout = 0;
+ stdout_eof = TRUE;
+
+ } else if (ready_hand == tStderr) {
+
+ CloseHandle((HANDLE)pproc->sv_stderr[0]);
+ (HANDLE)pproc->sv_stderr[0] = 0;
+ CloseHandle(tStderr);
+ tStderr = 0;
+ stderr_eof = TRUE;
+
+ } else if (ready_hand == childhand) {
+
+ if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done;
+ }
+ child_dead = TRUE;
+
+ } else {
+
+ /* ?? Got back a handle we didn't query ?? */
+ pproc->last_err = 0;
+ pproc->lerrno = E_FAIL;
+ goto done;
+ }
+ }
+
+ done:
+ if (tStdin != 0)
+ CloseHandle(tStdin);
+ if (tStdout != 0)
+ CloseHandle(tStdout);
+ if (tStderr != 0)
+ CloseHandle(tStderr);
+
+ if (pproc->lerrno)
+ return(-1);
+ else
+ return(0);
+
+}
+
+/*
+ * Purpose: collects output from child process and returns results
+ *
+ * Description:
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+ long
+process_file_io(
+ HANDLE proc)
+{
+ sub_process *pproc;
+ HANDLE childhand;
+ DWORD wait_return;
+
+ if (proc == NULL)
+ pproc = process_wait_for_any_private();
+ else
+ pproc = (sub_process *)proc;
+
+ /* some sort of internal error */
+ if (!pproc)
+ return -1;
+
+ childhand = (HANDLE) pproc->pid;
+
+ /*
+ * This function is poorly named, and could also be used just to wait
+ * for child death if you're doing your own pipe I/O. If that is
+ * the case, close the pipe handles here.
+ */
+ if (pproc->sv_stdin[0]) {
+ CloseHandle((HANDLE)pproc->sv_stdin[0]);
+ pproc->sv_stdin[0] = 0;
+ }
+ if (pproc->sv_stdout[0]) {
+ CloseHandle((HANDLE)pproc->sv_stdout[0]);
+ pproc->sv_stdout[0] = 0;
+ }
+ if (pproc->sv_stderr[0]) {
+ CloseHandle((HANDLE)pproc->sv_stderr[0]);
+ pproc->sv_stderr[0] = 0;
+ }
+
+ /*
+ * Wait for the child process to exit
+ */
+
+ wait_return = WaitForSingleObject(childhand, INFINITE);
+
+ if (wait_return != WAIT_OBJECT_0) {
+/* map_win32_error_to_string(GetLastError());*/
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ goto done2;
+ }
+
+ if (GetExitCodeProcess(childhand, &pproc->exit_code) == FALSE) {
+ pproc->last_err = GetLastError();
+ pproc->lerrno = E_SCALL;
+ }
+
+done2:
+ if (pproc->lerrno)
+ return(-1);
+ else
+ return(0);
+
+}
+
+/*
+ * Description: Clean up any leftover handles, etc. It is up to the
+ * caller to manage and free the input, ouput, and stderr buffers.
+ */
+ void
+process_cleanup(
+ HANDLE proc)
+{
+ sub_process *pproc = (sub_process *)proc;
+ int i;
+
+ if (pproc->using_pipes) {
+ for (i= 0; i <= 1; i++) {
+ if ((HANDLE)pproc->sv_stdin[i])
+ CloseHandle((HANDLE)pproc->sv_stdin[i]);
+ if ((HANDLE)pproc->sv_stdout[i])
+ CloseHandle((HANDLE)pproc->sv_stdout[i]);
+ if ((HANDLE)pproc->sv_stderr[i])
+ CloseHandle((HANDLE)pproc->sv_stderr[i]);
+ }
+ }
+ if ((HANDLE)pproc->pid)
+ CloseHandle((HANDLE)pproc->pid);
+
+ free(pproc);
+}
+
+
+/*
+ * Try to protect against WIN32 argument munging. This function takes
+ * an argv vector and outputs a 'protected' string as a return
+ * value. The return code can be safely passed to CreateProcess().
+ *
+ * The caller should free the return value.
+ */
+
+#define TRACE(x)
+static char *fix_command_line(char *args[])
+{
+ int i;
+ char *narg;
+ char *nargp;
+ char *p;
+ char *q;
+ int alloc_len = 0;
+
+ for (i = 0; args[i]; i++)
+ alloc_len += ((strlen(args[i]) * 2) + 1);
+ /* account for possible enclosing quotes and null termination */
+ alloc_len += 3;
+
+ nargp = narg = malloc(alloc_len);
+
+ for (i = 0; args[i]; i++) {
+ p = args[i];
+ TRACE(("original arg: %s\n", p));
+
+ if (*p == '\0') {
+ *nargp++ = '"';
+ *nargp++ = '"';
+ *nargp = '\0';
+ TRACE(("empty string arg: %s\n", nargp-2));
+ } else if (strpbrk(p, "\" \t")) {
+ /* point to end of copy buffer */
+ q = narg;
+ q += (alloc_len-1);
+ *q-- = '\0'; /* ensure null terminated string */
+ *q-- = '"'; /* terminating quote of argument */
+
+ /* point to end of the input string */
+ p = args[i];
+ p += strlen(args[i]);
+ p--;
+
+ /*
+ * Because arg is quoted, escape any backslashes
+ * that might occur at the end of the string which
+ * proceed the closing quote.
+ * Example:
+ * foo c:\
+ * Becomes:
+ * "foo c:\\"
+ */
+ while (*p == '\\')
+ *q-- = *p--, *q-- = '\\';
+
+ /* copy the string in reverse */
+ while (p >= args[i]) {
+ /* copy the character */
+ *q-- = *p--;
+
+ /*
+ * Escape any double quote found. Also escape
+ * each backslash preceding the double quote.
+ */
+ if (*(p+1) == '"') {
+ *q-- = '\\';
+ if (p >= args[i] && *p == '\\')
+ while (p >= args[i] && *p == '\\')
+ *q-- = *p--, *q-- = '\\';
+ }
+ }
+
+ /* finish quoting arg, q now points to complete arg */
+ *q = '"';
+
+ /* rejustify */
+ memmove(nargp, q, strlen(q) + 1);
+ TRACE(("arg with white space or doublequotes: %s\n", nargp));
+ nargp += strlen(nargp);
+ } else {
+ /* just copy the argument, no protection needed */
+ strcpy(nargp, args[i]);
+ TRACE(("plain arg: %s\n", nargp));
+ nargp += strlen(nargp);
+ }
+
+ /* separate arguments with spaces (if more args to gather) */
+ if (args[i+1])
+ *nargp++ = ' ';
+ *nargp = '\0';
+ } /* end for */
+
+ /* NULL terminate the arg list */
+ *nargp = '\0';
+
+ return (narg);
+}
+#undef TRACE
+
+/*
+ * Description:
+ * Create a command line buffer to pass to CreateProcess
+ *
+ * Returns: the buffer or NULL for failure
+ * Shell case: sh_name a:/full/path/to/script argv[1] argv[2] ...
+ * Otherwise: argv[0] argv[1] argv[2] ...
+ *
+ * Notes/Dependencies:
+ * CreateProcess does not take an argv, so this command creates a
+ * command line for the executable.
+ */
+
+static char *
+make_command_line( char *shell_name, char *exec_path, char **argv)
+{
+ char** nargv;
+ char* buf;
+ int i;
+
+ if (shell_name) {
+ for (i = 0; argv[i]; i++);
+ i += 2;
+ nargv = (char **) malloc(i * sizeof (char *));
+ nargv[0] = shell_name;
+ for (i = 1; argv[i-1]; i++)
+ nargv[i] = argv[i-1];
+ nargv[i] = NULL;
+ } else
+ nargv = argv;
+
+ /* create string suitable for CreateProcess() */
+ buf = fix_command_line(nargv);
+
+ if (shell_name)
+ free(nargv);
+
+ return buf;
+}
+
+/*
+ * Description: Given an argv and optional envp, launch the process
+ * using the default stdin, stdout, and stderr handles.
+ * Also, register process so that process_wait_for_any_private()
+ * can be used via process_file_io(NULL) or
+ * process_wait_for_any().
+ *
+ * Returns:
+ *
+ * Notes/Dependencies:
+ */
+HANDLE
+process_easy(
+ char **argv,
+ char **envp)
+{
+ HANDLE hIn;
+ HANDLE hOut;
+ HANDLE hErr;
+ HANDLE hProcess;
+
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_INPUT_HANDLE),
+ GetCurrentProcess(),
+ &hIn,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fprintf(stderr,
+ "process_easy: DuplicateHandle(In) failed (e=%d)\n",
+ GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_OUTPUT_HANDLE),
+ GetCurrentProcess(),
+ &hOut,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fprintf(stderr,
+ "process_easy: DuplicateHandle(Out) failed (e=%d)\n",
+ GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+ if (DuplicateHandle(GetCurrentProcess(),
+ GetStdHandle(STD_ERROR_HANDLE),
+ GetCurrentProcess(),
+ &hErr,
+ 0,
+ TRUE,
+ DUPLICATE_SAME_ACCESS) == FALSE) {
+ fprintf(stderr,
+ "process_easy: DuplicateHandle(Err) failed (e=%d)\n",
+ GetLastError());
+ return INVALID_HANDLE_VALUE;
+ }
+
+ hProcess = process_init_fd(hIn, hOut, hErr);
+
+ if (process_begin(hProcess, argv, envp, argv[0], NULL)) {
+ fake_exits_pending++;
+ ((sub_process*) hProcess)->exit_code = process_last_err(hProcess);
+
+ /* close up unused handles */
+ CloseHandle(hIn);
+ CloseHandle(hOut);
+ CloseHandle(hErr);
+ }
+
+ process_register(hProcess);
+
+ return hProcess;
+}
diff --git a/w32/subproc/w32err.c b/w32/subproc/w32err.c
new file mode 100644
index 0000000..e4e8835
--- /dev/null
+++ b/w32/subproc/w32err.c
@@ -0,0 +1,51 @@
+#include <windows.h>
+#include "w32err.h"
+
+/*
+ * Description: the win32 version of perror()
+ *
+ * Returns: a pointer to a static error
+ *
+ * Notes/Dependencies: I got this from
+ * comp.os.ms-windows.programmer.win32
+ */
+char *
+map_win32_error_to_string (DWORD ercode) {
+/* __declspec (thread) necessary if you will use multiple threads */
+__declspec (thread) static char szMessageBuffer[128];
+
+ /* Fill message buffer with a default message in
+ * case FormatMessage fails
+ */
+ wsprintf (szMessageBuffer, "Error %ld", ercode);
+
+ /*
+ * Special code for winsock error handling.
+ */
+ if (ercode > WSABASEERR) {
+ HMODULE hModule = GetModuleHandle("wsock32");
+ if (hModule != NULL) {
+ FormatMessage(FORMAT_MESSAGE_FROM_HMODULE,
+ hModule,
+ ercode,
+ LANG_NEUTRAL,
+ szMessageBuffer,
+ sizeof(szMessageBuffer),
+ NULL);
+ FreeLibrary(hModule);
+ }
+ } else {
+ /*
+ * Default system message handling
+ */
+ FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ ercode,
+ LANG_NEUTRAL,
+ szMessageBuffer,
+ sizeof(szMessageBuffer),
+ NULL);
+ }
+ return szMessageBuffer;
+}
+