summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoland McGrath <roland@redhat.com>1996-05-22 21:51:45 +0000
committerRoland McGrath <roland@redhat.com>1996-05-22 21:51:45 +0000
commite7a525c5d53029de18871890196b665c803e98d3 (patch)
tree1edeb907cbe04f4136523d71c10536764dfbe01b
parentd369e0531ab0715a05ac72c86f3d2fddcb8c817c (diff)
downloadgunmake-e7a525c5d53029de18871890196b665c803e98d3.tar.gz
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
-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;
+}
+