aboutsummaryrefslogtreecommitdiffstats
path: root/erts/etc
diff options
context:
space:
mode:
Diffstat (limited to 'erts/etc')
-rw-r--r--erts/etc/Makefile27
-rw-r--r--erts/etc/common/Makefile23
-rw-r--r--erts/etc/common/Makefile.in564
-rw-r--r--erts/etc/common/dialyzer.c466
-rw-r--r--erts/etc/common/erlc.c701
-rw-r--r--erts/etc/common/erlexec.c2038
-rw-r--r--erts/etc/common/escript.c697
-rw-r--r--erts/etc/common/heart.c1142
-rw-r--r--erts/etc/common/inet_gethost.c2757
-rw-r--r--erts/etc/common/typer.c416
-rw-r--r--erts/etc/unix/Install.src175
-rw-r--r--erts/etc/unix/README111
-rw-r--r--erts/etc/unix/RELNOTES327
-rw-r--r--erts/etc/unix/cerl.src285
-rw-r--r--erts/etc/unix/dyn_erl.c400
-rw-r--r--erts/etc/unix/erl.src.src28
-rw-r--r--erts/etc/unix/etp-commands2054
-rw-r--r--erts/etc/unix/etp_commands.erl173
-rw-r--r--erts/etc/unix/etp_commands.mk27
-rw-r--r--erts/etc/unix/format_man_pages149
-rw-r--r--erts/etc/unix/makewhatis327
-rw-r--r--erts/etc/unix/run_erl.c1298
-rw-r--r--erts/etc/unix/run_erl.h30
-rw-r--r--erts/etc/unix/safe_string.c123
-rw-r--r--erts/etc/unix/safe_string.h65
-rw-r--r--erts/etc/unix/setuid_socket_wrap.c259
-rw-r--r--erts/etc/unix/start.src36
-rw-r--r--erts/etc/unix/start_erl.src47
-rw-r--r--erts/etc/unix/to_erl.c610
-rw-r--r--erts/etc/vxworks/README.VxWorks350
-rw-r--r--erts/etc/vxworks/erl.exec.c129
-rw-r--r--erts/etc/vxworks/erl_io.c108
-rw-r--r--erts/etc/vxworks/erl_script.sam.in100
-rw-r--r--erts/etc/vxworks/heart_config.c60
-rw-r--r--erts/etc/vxworks/heart_config.h35
-rw-r--r--erts/etc/vxworks/rdate.c87
-rw-r--r--erts/etc/vxworks/reclaim.c551
-rw-r--r--erts/etc/vxworks/reclaim.h150
-rw-r--r--erts/etc/vxworks/reclaim_private.h44
-rw-r--r--erts/etc/vxworks/resolv.conf6
-rw-r--r--erts/etc/vxworks/vxcall.c145
-rw-r--r--erts/etc/vxworks/wd_example.c141
-rw-r--r--erts/etc/win32/Install.c229
-rw-r--r--erts/etc/win32/Install.src4
-rw-r--r--erts/etc/win32/Makefile72
-rw-r--r--erts/etc/win32/Nmakefile.start_erl33
-rw-r--r--erts/etc/win32/beam.rc102
-rw-r--r--erts/etc/win32/beam_icon.icobin0 -> 766 bytes
-rwxr-xr-xerts/etc/win32/cygwin_tools/erl48
-rwxr-xr-xerts/etc/win32/cygwin_tools/erlc61
-rwxr-xr-xerts/etc/win32/cygwin_tools/javac.sh53
-rwxr-xr-xerts/etc/win32/cygwin_tools/make_bootstrap_ini.sh44
-rwxr-xr-xerts/etc/win32/cygwin_tools/make_local_ini.sh41
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/ar.sh55
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/cc.sh293
-rw-r--r--erts/etc/win32/cygwin_tools/mingw/coffix.c161
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/emu_cc.sh90
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/ld.sh147
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/mc.sh89
-rwxr-xr-xerts/etc/win32/cygwin_tools/mingw/rc.sh94
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/ar.sh47
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/cc.sh321
-rw-r--r--erts/etc/win32/cygwin_tools/vc/cc_wrap.c864
-rw-r--r--erts/etc/win32/cygwin_tools/vc/coffix.c161
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/emu_cc.sh90
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/ld.sh192
-rw-r--r--erts/etc/win32/cygwin_tools/vc/ld_wrap.c796
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/mc.sh87
-rwxr-xr-xerts/etc/win32/cygwin_tools/vc/rc.sh86
-rw-r--r--erts/etc/win32/erl.c283
-rw-r--r--erts/etc/win32/erl.rc31
-rw-r--r--erts/etc/win32/erl_icon.icobin0 -> 766 bytes
-rw-r--r--erts/etc/win32/erl_log.c73
-rw-r--r--erts/etc/win32/erlang.icobin0 -> 1398 bytes
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_global.h37
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_interactive.c1163
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_interactive.h24
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_logmess.mc33
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_main.c44
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_registry.c404
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_registry.h76
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.c966
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_service.h32
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_util.c154
-rw-r--r--erts/etc/win32/erlsrv/erlsrv_util.h50
-rw-r--r--erts/etc/win32/hrl_icon.icobin0 -> 766 bytes
-rw-r--r--erts/etc/win32/init_file.c565
-rw-r--r--erts/etc/win32/init_file.h93
-rw-r--r--erts/etc/win32/nsis/Makefile88
-rwxr-xr-xerts/etc/win32/nsis/custom_modern.exebin0 -> 6144 bytes
-rwxr-xr-xerts/etc/win32/nsis/dll_version_helper.sh49
-rw-r--r--erts/etc/win32/nsis/erlang.nsi386
-rw-r--r--erts/etc/win32/nsis/erlang20.nsi440
-rw-r--r--erts/etc/win32/nsis/erlang_inst.icobin0 -> 766 bytes
-rwxr-xr-xerts/etc/win32/nsis/erlang_uninst.icobin0 -> 766 bytes
-rwxr-xr-xerts/etc/win32/nsis/find_redist.sh122
-rw-r--r--erts/etc/win32/port_entry.c75
-rw-r--r--erts/etc/win32/resource.h33
-rw-r--r--erts/etc/win32/start_erl.c677
-rw-r--r--erts/etc/win32/toolbar.bmpbin0 -> 598 bytes
-rw-r--r--erts/etc/win32/win_erlexec.c405
101 files changed, 27824 insertions, 0 deletions
diff --git a/erts/etc/Makefile b/erts/etc/Makefile
new file mode 100644
index 0000000000..2b32b8ae50
--- /dev/null
+++ b/erts/etc/Makefile
@@ -0,0 +1,27 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1999-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+include $(ERL_TOP)/make/target.mk
+
+
+SUB_DIRECTORIES = common
+ifeq ($(TARGET),win32)
+SUB_DIRECTORIES += win32
+endif
+
+include $(ERL_TOP)/make/otp_subdir.mk
diff --git a/erts/etc/common/Makefile b/erts/etc/common/Makefile
new file mode 100644
index 0000000000..73ab79d145
--- /dev/null
+++ b/erts/etc/common/Makefile
@@ -0,0 +1,23 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# Invoke with GNU make or clearmake -C gnu.
+#
+
+include $(ERL_TOP)/make/run_make.mk
diff --git a/erts/etc/common/Makefile.in b/erts/etc/common/Makefile.in
new file mode 100644
index 0000000000..a9acab640e
--- /dev/null
+++ b/erts/etc/common/Makefile.in
@@ -0,0 +1,564 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include $(ERL_TOP)/make/target.mk
+
+ERTS_LIB_TYPEMARKER=.$(TYPE)
+
+USING_MINGW=@MIXED_CYGWIN_MINGW@
+USING_VC=@MIXED_CYGWIN_VC@
+
+ifeq ($(TYPE),debug)
+PURIFY =
+TYPEMARKER = .debug
+TYPE_FLAGS = -DDEBUG @DEBUG_FLAGS@
+else
+ifeq ($(TYPE),purify)
+PURIFY = purify
+TYPEMARKER =
+ifeq ($(findstring ose,$(TARGET)),ose)
+TYPE_FLAGS = -g -XO -DPURIFY
+else
+TYPE_FLAGS = -g -O2 -DPURIFY
+endif
+else
+PURIFY =
+TYPEMARKER =
+ERTS_LIB_TYPEMARKER=
+TYPE_FLAGS =
+endif
+endif
+
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+include ../../vsn.mk
+
+ERTS_INCL = -I$(ERL_TOP)/erts/include \
+ -I$(ERL_TOP)/erts/include/$(TARGET) \
+ -I$(ERL_TOP)/erts/include/internal \
+ -I$(ERL_TOP)/erts/include/internal/$(TARGET)
+
+CC = @CC@
+WFLAGS = @WFLAGS@
+CFLAGS = @CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) -I$(EMUDIR) \
+ $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\"
+LD = @LD@
+LIBS = @LIBS@
+LDFLAGS = @LDFLAGS@
+
+ifeq ($(TARGET),win32)
+ifeq ($(TYPE),debug)
+CFLAGS = $(subst -O2,-g,@CFLAGS@ @DEFS@ $(TYPE_FLAGS) @WFLAGS@ -I$(SYSDIR) \
+ -I$(EMUDIR) $(ERTS_INCL) -DOTP_SYSTEM_VERSION=\"$(SYSTEM_VSN)\")
+LDFLAGS += -g
+endif
+endif
+BINDIR = $(ERL_TOP)/bin/$(TARGET)
+OBJDIR = $(ERL_TOP)/erts/obj$(TYPEMARKER)/$(TARGET)
+EMUDIR = $(ERL_TOP)/erts/emulator/beam
+EMUOSDIR = $(ERL_TOP)/erts/emulator/@ERLANG_OSTYPE@
+SYSDIR = $(ERL_TOP)/erts/emulator/sys/@ERLANG_OSTYPE@
+DRVDIR = $(ERL_TOP)/erts/emulator/drivers/@ERLANG_OSTYPE@
+VXETC = ../vxworks
+UXETC = ../unix
+OSEETC = ../ose
+WINETC = ../win32
+
+ifeq ($(findstring vxworks,$(TARGET)), vxworks)
+ERLEXEC = erl.exec
+else
+ifeq ($(findstring ose,$(TARGET)), ose)
+ERLEXEC =
+TAR = @TAR@
+else
+ifeq ($(TARGET), win32)
+ERLEXEC = erlexec.dll
+else
+ERLEXEC = erlexec
+endif
+endif
+endif
+
+# On windows we always need reentrant libraries.
+ifeq ($(TARGET),win32)
+ERLEXEC_XLIBS=-L../../lib/internal/$(TARGET) -lerts_internal_r$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
+else
+ERLEXEC_XLIBS=-L../../lib/internal/$(TARGET) -lerts_internal$(ERTS_LIB_TYPEMARKER) @ERTS_INTERNAL_X_LIBS@
+endif
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+
+ifeq ($(findstring vxworks,$(TARGET)), vxworks)
+INSTALL_EMBEDDED_PROGS = $(BINDIR)/erl_io $(BINDIR)/rdate $(BINDIR)/vxcall
+INSTALL_EMBEDDED_DATA = $(BINDIR)/erl_script.sam $(VXETC)/resolv.conf
+INSTALL_INCLUDES = $(VXETC)/reclaim.h
+INSTALL_TOP = $(VXETC)/README.VxWorks
+INSTALL_MISC =
+INSTALL_SRC = heart.c $(VXETC)/heart_config.h $(VXETC)/heart_config.c \
+ $(VXETC)/erl.exec.c $(VXETC)/rdate.c $(VXETC)/vxcall.c \
+ $(VXETC)/erl_io.c
+ERLEXECDIR = $(VXETC)
+INSTALL_LIBS = $(OBJDIR)/reclaim.o
+INSTALL_OBJS = $(OBJDIR)/heart.o
+TEXTFILES = $(BINDIR)/erl_script.sam
+ERLSRV_OBJECTS=
+MC_OUTPUTS=
+ENTRY_LDFLAGS=
+ENTRY_OBJ=
+INSTALL_PROGS = \
+ $(INET_GETHOST) \
+ $(BINDIR)/heart \
+ $(BINDIR)/$(ERLEXEC) \
+ $(INSTALL_EMBEDDED_PROGS)
+else
+ifeq ($(findstring ose,$(TARGET)), ose)
+INSTALL_TOP = $(OSEETC)/README.OSE
+INSTALL_ERL_OSE = monolith lm erl_utils drivers port_progs host
+INSTALL_SRC =
+INSTALL_LIBS =
+INSTALL_OBJS =
+INSTALL_INCLUDES =
+INSTALL_PROGS =
+ERLSRV_OBJECTS=
+MC_OUTPUTS=
+ENTRY_LDFLAGS=
+ENTRY_OBJ=
+else
+ifeq ($(TARGET),win32)
+CFLAGS += -I$(EMUOSDIR) -I$(WINETC)
+RC=rc.sh
+MC=mc.sh
+ERLSRV_HEADERS= \
+ $(WINETC)/erlsrv/erlsrv_global.h \
+ $(WINETC)/erlsrv/erlsrv_registry.h \
+ $(WINETC)/erlsrv/erlsrv_util.h \
+ $(WINETC)/erlsrv/erlsrv_interactive.h \
+ $(WINETC)/erlsrv/erlsrv_service.h
+
+ifeq ($(USING_VC), yes)
+ERLRES_OBJ=erl.res
+ERLSRV_OBJECTS= \
+ $(OBJDIR)/erlsrv_registry.o \
+ $(OBJDIR)/erlsrv_service.o \
+ $(OBJDIR)/erlsrv_interactive.o \
+ $(OBJDIR)/erlsrv_main.o \
+ $(OBJDIR)/erlsrv_util.o \
+ $(OBJDIR)/erlsrv_logmess.res
+MC_OUTPUTS= \
+ $(OBJDIR)/erlsrv_logmess.h $(OBJDIR)/erlsrv_logmess.rc
+ MT_FLAG="-MT"
+else
+ERLRES_OBJ=erl_res.o
+ERLSRV_OBJECTS= \
+ $(OBJDIR)/erlsrv_registry.o \
+ $(OBJDIR)/erlsrv_service.o \
+ $(OBJDIR)/erlsrv_interactive.o \
+ $(OBJDIR)/erlsrv_main.o \
+ $(OBJDIR)/erlsrv_util.o \
+ $(OBJDIR)/erlsrv_logmess.o
+MC_OUTPUTS= \
+ $(OBJDIR)/erlsrv_logmess.h $(OBJDIR)/erlsrv_logmess.res
+ MT_FLAG="-MD"
+endif
+INET_GETHOST = $(BINDIR)/inet_gethost.exe
+INSTALL_EMBEDDED_PROGS += $(BINDIR)/typer.exe $(BINDIR)/dialyzer.exe $(BINDIR)/erlc.exe $(BINDIR)/start_erl.exe $(BINDIR)/escript.exe
+INSTALL_SRC = $(WINETC)/start_erl.c $(WINETC)/Nmakefile.start_erl
+ERLEXECDIR=.
+INSTALL_LIBS =
+INSTALL_OBJS =
+INSTALL_INCLUDES =
+TEXTFILES = Install.ini
+INSTALL_TOP = Install.ini
+INSTALL_TOP_BIN = $(BINDIR)/Install.exe
+INSTALL_PROGS = \
+ $(INET_GETHOST) \
+ $(BINDIR)/heart.exe $(BINDIR)/erlsrv.exe \
+ $(BINDIR)/erl.exe $(BINDIR)/werl.exe \
+ $(BINDIR)/$(ERLEXEC) \
+ $(INSTALL_EMBEDDED_PROGS)
+
+ENTRY_SRC=$(WINETC)/port_entry.c
+ENTRY_OBJ=$(OBJDIR)/port_entry.o
+ifeq ($(USING_VC), yes)
+WINDSOCK = ws2_32.lib mswsock.lib
+else
+WINDSOCK = -lws2_32
+endif
+PORT_ENTRY_POINT=erl_port_entry
+ENTRY_LDFLAGS=-entry:$(PORT_ENTRY_POINT)
+
+else
+ENTRY_LDFLAGS=
+ENTRY_OBJ=
+ERLSRV_OBJECTS=
+MC_OUTPUTS=
+INET_GETHOST = $(BINDIR)/inet_gethost@EXEEXT@
+INSTALL_EMBEDDED_PROGS += $(BINDIR)/typer@EXEEXT@ $(BINDIR)/dialyzer@EXEEXT@ \
+ $(BINDIR)/erlc@EXEEXT@ $(BINDIR)/escript@EXEEXT@ \
+ $(BINDIR)/run_erl $(BINDIR)/to_erl $(BINDIR)/dyn_erl
+INSTALL_EMBEDDED_DATA = ../unix/start.src ../unix/start_erl.src
+INSTALL_TOP = Install
+INSTALL_TOP_BIN =
+INSTALL_MISC = ../unix/format_man_pages ../unix/makewhatis
+INSTALL_SRC = ../unix/setuid_socket_wrap.c #delivered as an example
+ERLEXECDIR = .
+INSTALL_LIBS =
+INSTALL_OBJS =
+INSTALL_INCLUDES =
+TEXTFILES = Install erl.src
+INSTALL_PROGS = \
+ $(INET_GETHOST) \
+ $(BINDIR)/heart@EXEEXT@ \
+ $(BINDIR)/$(ERLEXEC) \
+ $(INSTALL_EMBEDDED_PROGS)
+endif
+endif
+endif
+
+etc: erts_lib $(ENTRY_OBJ) $(INSTALL_PROGS) $(INSTALL_LIBS) $(TEXTFILES) $(INSTALL_TOP_BIN)
+
+# erlexec needs the erts_internal library...
+erts_lib:
+ cd $(ERL_TOP)/erts/lib_src && $(MAKE) $(TYPE)
+
+docs:
+
+clean:
+ifneq ($(INSTALL_PROGS),)
+ rm -f $(INSTALL_PROGS)
+endif
+ifneq ($(ENTRY_OBJ),)
+ rm -f $(ENTRY_OBJ)
+endif
+ifneq ($(ERLSRV_OBJECTS),)
+ rm -f $(ERLSRV_OBJECTS)
+endif
+ifneq ($(MC_OUTPUTS),)
+ rm -f $(MC_OUTPUTS)
+endif
+ifneq ($(ERLRES_OBJ),)
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/$(ERLRES_OBJ)
+endif
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/win_erlexec.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/init_file.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/start_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dialyzer.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erlexec.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl_io.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erlc.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/escript.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/heart.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/heart_config.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/inet_gethost.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/rdate.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/reclaim.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/run_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/to_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/dyn_erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/safe_string.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/typer.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/vxcall.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/erl.o
+ rm -f $(ERL_TOP)/erts/obj*/$(TARGET)/werl.o
+ rm -f *~ core
+
+#
+# Objects & executables
+#
+#$(OBJDIR)/%.o: %.c
+# $(CC) $(CFLAGS) -o $@ -c $<
+#
+#$(OBJDIR)/%.o: ../unix/%.c
+# $(CC) $(CFLAGS) -o $@ -c $<
+#
+#$(BINDIR)/%: $(OBJDIR)/%.o
+# $(PURIFY) $(LD) $(LDFLAGS) -o $@ $< $(LIBS)
+
+$(OBJDIR)/inet_gethost.o: inet_gethost.c
+ $(CC) $(CFLAGS) -o $@ -c inet_gethost.c
+
+$(BINDIR)/inet_gethost@EXEEXT@: $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ)
+ $(PURIFY) $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/inet_gethost.o $(ENTRY_OBJ) $(LIBS)
+
+$(BINDIR)/run_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/run_erl.o $(LIBS)
+
+$(OBJDIR)/run_erl.o: ../unix/run_erl.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/run_erl.c
+
+$(BINDIR)/to_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/to_erl.o
+
+$(OBJDIR)/to_erl.o: ../unix/to_erl.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/to_erl.c
+
+$(BINDIR)/dyn_erl: $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/safe_string.o $(OBJDIR)/dyn_erl.o
+
+$(OBJDIR)/dyn_erl.o: ../unix/dyn_erl.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/dyn_erl.c
+
+$(OBJDIR)/safe_string.o: ../unix/safe_string.c
+ $(CC) $(CFLAGS) -o $@ -c ../unix/safe_string.c
+
+ifneq ($(TARGET),win32)
+$(BINDIR)/$(ERLEXEC): $(OBJDIR)/$(ERLEXEC).o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/$(ERLEXEC).o $(ERLEXEC_XLIBS)
+
+$(OBJDIR)/$(ERLEXEC).o: $(ERLEXECDIR)/$(ERLEXEC).c
+ $(CC) -I$(EMUDIR) $(CFLAGS) -o $@ -c $(ERLEXECDIR)/$(ERLEXEC).c
+endif
+$(BINDIR)/erlc@EXEEXT@: $(OBJDIR)/erlc.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erlc.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/erlc.o: erlc.c
+ $(CC) $(CFLAGS) -o $@ -c erlc.c
+
+$(BINDIR)/dialyzer@EXEEXT@: $(OBJDIR)/dialyzer.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/dialyzer.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/dialyzer.o: dialyzer.c
+ $(CC) $(CFLAGS) -o $@ -c dialyzer.c
+
+$(BINDIR)/typer@EXEEXT@: $(OBJDIR)/typer.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/typer.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/typer.o: typer.c
+ $(CC) $(CFLAGS) -o $@ -c typer.c
+
+$(BINDIR)/escript@EXEEXT@: $(OBJDIR)/escript.o
+ $(PURIFY) $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/escript.o -L$(OBJDIR) $(LIBS)
+
+$(OBJDIR)/escript.o: escript.c
+ $(CC) $(CFLAGS) -o $@ -c escript.c
+
+#------------------------------------------------------------------------
+# Windows specific targets
+# The windows platform is quite different from the others. erl/werl are small C programs
+# loading a DLL. INI files are used instead of environment variables and the Install
+# script is actually a program, also Install has an INI file which tells of emulator
+# versions etc.
+#----------------------------------------------------------------------
+
+ifeq ($(TARGET),win32)
+
+$(BINDIR)/$(ERLEXEC): $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) dbg
+ $(LD) -dll $(LDFLAGS) -o $@ $(OBJDIR)/erlexec.o $(OBJDIR)/win_erlexec.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ) $(ERLEXEC_XLIBS)
+
+dbg:
+ echo DBG
+
+$(BINDIR)/erl@EXEEXT@: $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+
+$(BINDIR)/werl@EXEEXT@: $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/werl.o $(OBJDIR)/init_file.o $(OBJDIR)/$(ERLRES_OBJ)
+
+$(BINDIR)/start_erl@EXEEXT@: $(OBJDIR)/start_erl.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/start_erl.o
+
+$(BINDIR)/Install@EXEEXT@: $(OBJDIR)/Install.o $(OBJDIR)/init_file.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/Install.o $(OBJDIR)/init_file.o
+
+$(BINDIR)/erlsrv@EXEEXT@: $(ERLSRV_OBJECTS)
+ $(LD) $(LDFLAGS) $(MT_FLAG) -o $@ $(ERLSRV_OBJECTS)
+
+# The service expects to be compiled with $(MT_FLAG) flag.
+
+$(OBJDIR)/%.o: $(WINETC)/erlsrv/%.c $(ERLSRV_HEADERS)
+ $(CC) $(CFLAGS) $(MT_FLAG) -o $@ -c $<
+
+$(OBJDIR)/erlsrv_util.o: $(WINETC)/erlsrv/erlsrv_util.c $(ERLSRV_HEADERS) \
+$(OBJDIR)/erlsrv_logmess.h
+ $(CC) $(CFLAGS) -I$(OBJDIR) $(MT_FLAG) -o $@ -c $<
+
+ifeq ($(USING_VC), yes)
+$(OBJDIR)/erlsrv_logmess.res: $(OBJDIR)/erlsrv_logmess.rc
+ $(RC) -o $(OBJDIR)/erlsrv_logmess.res -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.rc
+else
+$(OBJDIR)/erlsrv_logmess.o: $(OBJDIR)/erlsrv_logmess.res
+ $(RC) -o $(OBJDIR)/erlsrv_logmess.o -I$(OBJDIR) $(OBJDIR)/erlsrv_logmess.res
+endif
+
+$(MC_OUTPUTS): $(WINETC)/erlsrv/erlsrv_logmess.mc
+ $(MC) -o $(OBJDIR) $(WINETC)/erlsrv/erlsrv_logmess.mc
+
+$(OBJDIR)/werl.o: $(WINETC)/erl.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -DWIN32_WERL -o $@ -c $(WINETC)/erl.c
+
+$(OBJDIR)/erl.o: $(WINETC)/erl.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -o $@ -c $(WINETC)/erl.c
+
+$(OBJDIR)/erlexec.o: $(ERLEXECDIR)/erlexec.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -o $@ -c $(ERLEXECDIR)/erlexec.c
+
+$(OBJDIR)/win_erlexec.o: $(WINETC)/win_erlexec.c
+ $(CC) $(CFLAGS) -DBUILD_TYPE=\"-$(TYPE)\" -DERL_RUN_SHARED_LIB=1 \
+ -o $@ -c $(WINETC)/win_erlexec.c
+
+$(OBJDIR)/init_file.o: $(WINETC)/init_file.c $(WINETC)/init_file.h
+ $(CC) $(CFLAGS) -o $@ -c $(WINETC)/init_file.c
+
+$(OBJDIR)/Install.o: $(WINETC)/Install.c $(WINETC)/init_file.h
+ $(CC) $(CFLAGS) -o $@ -c $(WINETC)/Install.c
+
+$(OBJDIR)/$(ERLRES_OBJ): $(WINETC)/erl.rc $(WINETC)/erlang.ico $(WINETC)/erl_icon.ico $(WINETC)/hrl_icon.ico $(WINETC)/beam_icon.ico
+ $(RC) -o $@ -I$(WINETC) $(WINETC)/erl.rc
+
+$(OBJDIR)/start_erl.o: $(WINETC)/start_erl.c
+ $(CC) $(CFLAGS) -o $@ -c $(WINETC)/start_erl.c
+
+$(ENTRY_OBJ): $(ENTRY_SRC)
+ $(CC) $(CFLAGS) -o $@ -c $(ENTRY_SRC)
+
+Install.ini: ../$(TARGET)/Install.src ../../vsn.mk $(TARGET)/Makefile
+ sed -e 's;%I_VSN%;$(VSN);' \
+ -e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \
+ ../$(TARGET)/Install.src > Install.ini
+
+
+endif
+#---------------------------------------------------------
+# End of windows specific targets.
+#---------------------------------------------------------
+
+ifeq ($(findstring vxworks,$(TARGET)), vxworks)
+$(BINDIR)/heart: $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/heart.o $(OBJDIR)/heart_config.o
+
+$(OBJDIR)/heart_config.o: $(VXETC)/heart_config.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/heart_config.c
+
+$(OBJDIR)/reclaim.o: $(VXETC)/reclaim.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/reclaim.c
+
+$(OBJDIR)/heart.o: heart.c
+ $(CC) $(CFLAGS) -I$(VXETC) -o $@ -c heart.c
+
+$(BINDIR)/erl_script.sam: $(VXETC)/erl_script.sam.in ../../vsn.mk
+ sed -e 's;%VSN%;$(VSN);' \
+ $(VXETC)/erl_script.sam.in > $(BINDIR)/erl_script.sam
+else
+
+$(BINDIR)/heart@EXEEXT@: $(OBJDIR)/heart.o $(ENTRY_OBJ)
+ $(LD) $(LDFLAGS) $(ENTRY_LDFLAGS) -o $@ $(OBJDIR)/heart.o \
+ $(ENTRY_OBJ) $(WINDSOCK)
+
+$(OBJDIR)/heart.o: heart.c
+ $(CC) $(CFLAGS) -o $@ -c heart.c
+
+endif
+
+
+# VxWorks specific executables and objects ...
+
+$(BINDIR)/erl_io: $(OBJDIR)/erl_io.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/erl_io.o
+
+$(OBJDIR)/erl_io.o: $(VXETC)/erl_io.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/erl_io.c
+
+$(BINDIR)/rdate: $(OBJDIR)/rdate.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/rdate.o
+
+$(OBJDIR)/rdate.o: $(VXETC)/rdate.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/rdate.c
+
+$(BINDIR)/vxcall: $(OBJDIR)/vxcall.o
+ $(LD) $(LDFLAGS) -o $@ $(OBJDIR)/vxcall.o
+
+$(OBJDIR)/vxcall.o: $(VXETC)/vxcall.c
+ $(CC) $(CFLAGS) -o $@ -c $(VXETC)/vxcall.c
+
+
+
+Install: ../unix/Install.src ../../vsn.mk $(TARGET)/Makefile
+ sed -e 's;%I_VSN%;$(VSN);' \
+ -e 's;%EMULATOR%;$(EMULATOR);' \
+ -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \
+ -e 's;%I_SYSTEM_VSN%;$(SYSTEM_VSN);' \
+ ../unix/Install.src > Install
+
+erl.src: ../unix/erl.src.src ../../vsn.mk $(TARGET)/Makefile
+ sed -e 's;%EMULATOR%;$(EMULATOR);' \
+ -e 's;%EMULATOR_NUMBER%;$(EMULATOR_NUMBER);' \
+ -e 's;%VSN%;$(VSN);' \
+ ../unix/erl.src.src > erl.src
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec: etc
+ifneq ($(INSTALL_OBJS),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/obj
+ $(INSTALL_DATA) $(INSTALL_OBJS) $(RELEASE_PATH)/erts-$(VSN)/obj
+endif
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/bin
+ifneq ($(TARGET), win32)
+ifneq ($(findstring vxworks,$(TARGET)), vxworks)
+ifneq ($(findstring ose,$(TARGET)), ose)
+ $(INSTALL_SCRIPT) erl.src $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+endif
+endif
+ifneq ($(INSTALL_PROGS),)
+ $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+ifneq ($(INSTALL_TOP),)
+ $(INSTALL_SCRIPT) $(INSTALL_TOP) $(RELEASE_PATH)
+endif
+ifneq ($(INSTALL_TOP_BIN),)
+ $(INSTALL_PROGRAM) $(INSTALL_TOP_BIN) $(RELEASE_PATH)
+endif
+ifneq ($(INSTALL_MISC),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/misc
+ $(INSTALL_SCRIPT) $(INSTALL_MISC) $(RELEASE_PATH)/misc
+endif
+ifneq ($(INSTALL_ERL_OSE),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/build_erl_ose
+ cd $(OSEETC) && $(TAR) erl_ose_$(SYSTEM_VSN).tar $(INSTALL_ERL_OSE)
+ cd $(OSEETC) && $(INSTALL_SCRIPT) erl_ose_$(SYSTEM_VSN).tar $(RELEASE_PATH)/build_erl_ose
+endif
+ifneq ($(INSTALL_SRC),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/src
+ $(INSTALL_DATA) $(INSTALL_SRC) $(RELEASE_PATH)/erts-$(VSN)/src
+endif
+ifneq ($(INSTALL_EMBEDDED_DATA),)
+ $(INSTALL_DATA) $(INSTALL_EMBEDDED_DATA) $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+ifneq ($(INSTALL_LIBS),)
+ $(INSTALL_DATA) $(INSTALL_LIBS) $(RELEASE_PATH)/erts-$(VSN)/bin
+endif
+ifneq ($(INSTALL_INCLUDES),)
+ $(INSTALL_DIR) $(RELEASE_PATH)/erts-$(VSN)/include
+ $(INSTALL_DATA) $(INSTALL_INCLUDES) $(RELEASE_PATH)/erts-$(VSN)/include
+endif
+
+release_docs_spec:
+
+
+
+
+
diff --git a/erts/etc/common/dialyzer.c b/erts/etc/common/dialyzer.c
new file mode 100644
index 0000000000..9c66be7f0f
--- /dev/null
+++ b/erts/etc/common/dialyzer.c
@@ -0,0 +1,466 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: Dialyzer front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+#endif
+
+#include <ctype.h>
+
+#define NO 0
+#define YES 1
+
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+
+/*
+ * Local functions.
+ */
+
+static void error(char* format, ...);
+static char* emalloc(size_t size);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ free(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+int
+main(int argc, char** argv)
+{
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ char *env;
+ int need_shell = 0;
+
+ env = get_env("DIALYZER_EMULATOR");
+ emulator = env ? env : get_default_emulator(argv[0]);
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy addition of commands in the beginning.
+ */
+
+ eargv_size = argc*4+100;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ free_env_val(env);
+
+ /*
+ * Push initial arguments.
+ */
+
+ if (argc > 1 && strcmp(argv[1], "--wx") == 0) {
+ PUSH2("-smp", "--wx"); /* wx currently requires SMP enabled */
+ argc--, argv++;
+ }
+
+ if (argc > 1 && strcmp(argv[1], "-smp") == 0) {
+ PUSH("-smpauto");
+ argc--, argv++;
+ }
+
+ if (argc > 2 && strcmp(argv[1], "+S") == 0) {
+ PUSH3("-smp", "+S", argv[2]);
+ argc--, argv++;
+ argc--, argv++;
+ }
+
+ PUSH("+B");
+ PUSH2("-boot", "start_clean");
+ PUSH3("-run", "dialyzer", "plain_cl");
+ PUSH("-extra");
+
+ /*
+ * Push everything except --shell.
+ */
+
+ while (argc > 1) {
+ if (strcmp(argv[1], "--shell") == 0) {
+ need_shell = 1;
+ } else {
+ PUSH(argv[1]);
+ }
+ argc--, argv++;
+ }
+
+ if (!need_shell) {
+ UNSHIFT("-noinput");
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = malloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we was executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "dialyzer: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "dialyzer: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = YES;
+ continue;
+ case '"':
+ mustQuote = YES;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */
diff --git a/erts/etc/common/erlc.c b/erts/etc/common/erlc.c
new file mode 100644
index 0000000000..c958fed741
--- /dev/null
+++ b/erts/etc/common/erlc.c
@@ -0,0 +1,701 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: Common compiler front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+/* FIXE ME config_win32.h? */
+#define HAVE_STRERROR 1
+#endif
+
+#include <ctype.h>
+
+#define NO 0
+#define YES 1
+
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+
+static char* output_type = NULL; /* Type of output file. */
+#ifdef __WIN32__
+static int pause_after_execution = 0;
+#endif
+
+/*
+ * Local functions.
+ */
+
+static char* process_opt(int* pArgc, char*** pArgv, int offset);
+static void error(char* format, ...);
+static void usage(void);
+static char* emalloc(size_t size);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ free(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+
+int
+main(int argc, char** argv)
+{
+ char cwd[MAXPATHLEN]; /* Current working directory. */
+ char** rpc_eargv; /* Pointer to the beginning of arguments
+ * if calling a running Erlang system
+ * via erl_rpc().
+ */
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ char *env;
+
+ env = get_env("ERLC_EMULATOR");
+ emulator = env ? env : get_default_emulator(argv[0]);
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy adding of emulator options (like -pa)
+ * before '-s erlcompile compile_cmdline...'.
+ *
+ * Oh, by the way, we will push the compiler command in the
+ * base of the eargv vector, and move it up later.
+ */
+
+ eargv_size = argc*4+100;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ free_env_val(env);
+
+ /*
+ * Push initial arguments.
+ */
+
+ PUSH("-noinput");
+ PUSH2("-mode", "minimal");
+ PUSH2("-boot", "start_clean");
+ PUSH3("-s", "erl_compile", "compile_cmdline");
+ rpc_eargv = eargv+eargc;
+
+ /*
+ * Push standard arguments to Erlang.
+ *
+ * The @cwd argument was once needed, but from on R13B02 is optional.
+ * For maximum compatibility between erlc and erl of different versions,
+ * still provide the @cwd argument, unless it is too long to be
+ * represented as an atom.
+ */
+ if (getcwd(cwd, sizeof(cwd)) == NULL)
+ error("Failed to get current working directory: %s", strerror(errno));
+#ifdef __WIN32__
+ (void) GetShortPathName(cwd, cwd, sizeof(cwd));
+#endif
+ if (strlen(cwd) < 256) {
+ PUSH2("@cwd", cwd);
+ }
+
+ /*
+ * Parse all command line switches.
+ */
+
+ while (argc > 1 && (argv[1][0] == '-' || argv[1][0] == '+')) {
+
+ /*
+ * Options starting with '+' are passed on to Erlang.
+ */
+
+ if (argv[1][0] == '+') {
+ PUSH2("@option", argv[1]+1);
+ } else {
+ /*
+ * Interpret options starting with '-'.
+ */
+
+ switch (argv[1][1]) {
+ case 'b':
+ output_type = process_opt(&argc, &argv, 0);
+ PUSH2("@output_type", output_type);
+ break;
+ case 'c': /* Allowed for compatibility with 'erl'. */
+ if (strcmp(argv[1], "-compile") != 0)
+ goto error;
+ break;
+ case 'd':
+ debug = 1;
+ break;
+ case 'D':
+ {
+ char* def = process_opt(&argc, &argv, 0);
+ char* equals;
+
+ def = strsave(def); /* Do not clobber original. */
+ if ((equals = strchr(def, '=')) == NULL) {
+ PUSH2("@d", def);
+ } else {
+ *equals = '\0';
+ equals++;
+ PUSH3("@dv", def, equals);
+ }
+ }
+ break;
+ case 'h':
+ if (strcmp(argv[1], "-hybrid") == 0) {
+ UNSHIFT(argv[1]);
+ } else {
+ usage();
+ }
+ break;
+ case 'I':
+ PUSH2("@i", process_opt(&argc, &argv, 0));
+ break;
+ case 'o':
+ PUSH2("@outdir", process_opt(&argc, &argv, 0));
+ break;
+ case 'O':
+ PUSH("@optimize");
+ if (argv[1][2] == '\0')
+ PUSH("1");
+ else
+ PUSH(argv[1]+2);
+ break;
+ case 'p':
+ {
+ int c = argv[1][2];
+
+ if (c != 'a' && c != 'z') {
+ goto error;
+#ifdef __WIN32__
+ } else if (strcmp(argv[1], "-pause") == 0) {
+ pause_after_execution = 1;
+#endif
+ } else {
+ char option[4];
+
+ UNSHIFT(process_opt(&argc, &argv, 1));
+ option[0] = '-';
+ option[1] = 'p';
+ option[2] = c;
+ option[3] = '\0';
+ UNSHIFT(strsave(option));
+ }
+ }
+ break;
+ case 's':
+ if (strcmp(argv[1], "-smp") == 0) {
+ UNSHIFT(argv[1]);
+ } else {
+ goto error;
+ }
+ break;
+ case 'v': /* Verbose. */
+ PUSH2("@verbose", "true");
+ break;
+ case 'V':
+ /** XXX Version perhaps, but of what? **/
+ break;
+ case 'W': /* Enable warnings. */
+ if (strcmp(argv[1]+2, "all") == 0) {
+ PUSH2("@warn", "999");
+ } else if (isdigit((int)argv[1][2])) {
+ PUSH2("@warn", argv[1]+2);
+ } else {
+ PUSH2("@warn", "1");
+ }
+ break;
+ case 'E':
+ case 'S':
+ case 'P':
+ {
+ char* buf;
+
+ /*
+ * From the given upper-case letter, construct
+ * a quoted atom. This is a convenience for the
+ * Erlang compiler, to avoid fighting with the shell's
+ * quoting.
+ */
+
+ buf = emalloc(4);
+ buf[0] = '\'';
+ buf[1] = argv[1][1];
+ buf[2] = '\'';
+ buf[3] = '\0';
+
+ PUSH2("@option", buf);
+ }
+ break;
+
+ case '-':
+ goto no_more_options;
+
+ default:
+ error:
+ usage();
+ break;
+ }
+ }
+ argc--, argv++;
+ }
+
+ no_more_options:
+
+ if (argc <= 1) {
+ /*
+ * To avoid starting an Erlang system unless absolutely needed
+ * exit if no files were specified on the command line.
+ */
+ exit(0);
+ }
+
+ /*
+ * The rest of the command line must be filenames. Simply push them.
+ */
+
+ PUSH("@files");
+ while (argc > 1) {
+ PUSH(argv[1]);
+ argc--, argv++;
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static char*
+process_opt(int* pArgc, char*** pArgv, int offset)
+{
+ int argc = *pArgc;
+ char** argv = *pArgv;
+ int c = argv[1][1];
+
+ if (argv[1][2+offset] != '\0') {
+ /*
+ * The option was given as -x<value>.
+ */
+ return argv[1]+2+offset;
+ }
+
+ /*
+ * Look at the next argument.
+ */
+
+ argc--, argv++;
+ if (argc < 2 || argv[1][0] == '-')
+ error("No value given to -%c option", c);
+ *pArgc = argc;
+ *pArgv = argv;
+ return argv[1];
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = malloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we was executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "erlc: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ if (pause_after_execution) {
+ fprintf(stderr, "Press ENTER to continue . . .\n");
+ while (getchar() != '\n')
+ ;
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+usage(void)
+{
+ static struct {
+ char* name;
+ char* desc;
+ } options[] = {
+ {"-b type", "type of output file (e.g. jam or beam)"},
+ {"-d", "turn on debugging of erlc itself"},
+ {"-Dname", "define name"},
+ {"-Dname=value", "define name to have value"},
+ {"-hybrid", "compile using hybrid-heap emulator"},
+ {"-help", "shows this help text"},
+ {"-I path", "where to search for include files"},
+ {"-o name", "name output directory or file"},
+ {"-pa path", "add path to the front of Erlang's code path"},
+ {"-pz path", "add path to the end of Erlang's code path"},
+ {"-smp", "compile using SMP emulator"},
+ {"-v", "verbose compiler output"},
+ {"-W0", "disable warnings"},
+ {"-Wnumber", "set warning level to number"},
+ {"-Wall", "enable all warnings"},
+ {"-W", "enable warnings (default; same as -W1)"},
+ {"-E", "generate listing of expanded code (Erlang compiler)"},
+ {"-S", "generate assembly listing (Erlang compiler)"},
+ {"-P", "generate listing of preprocessed code (Erlang compiler)"},
+ {"+term", "pass the Erlang term unchanged to the compiler"},
+ };
+ int i;
+
+ fprintf(stderr, "Usage:\terlc [options] file.ext ...\n");
+ fprintf(stderr, "Options:\n");
+ for (i = 0; i < sizeof(options)/sizeof(options[0]); i++) {
+ fprintf(stderr, "%-14s %s\n", options[i].name, options[i].desc);
+ }
+ exit(1);
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "erlc: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = YES;
+ continue;
+ case '"':
+ mustQuote = YES;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */
diff --git a/erts/etc/common/erlexec.c b/erts/etc/common/erlexec.c
new file mode 100644
index 0000000000..4325418e7c
--- /dev/null
+++ b/erts/etc/common/erlexec.c
@@ -0,0 +1,2038 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * This is a C version of the erl.exec Bourne shell script, including
+ * additions required for Windows NT.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include "erl_driver.h"
+#include <stdlib.h>
+#include <stdarg.h>
+#include "erl_misc_utils.h"
+
+#ifdef __WIN32__
+# include "erl_version.h"
+# include "init_file.h"
+#endif
+
+#define NO 0
+#define YES 1
+#define DEFAULT_PROGNAME "erl"
+
+#ifdef __WIN32__
+#define INI_FILENAME "erl.ini"
+#define INI_SECTION "erlang"
+#define DIRSEP "\\"
+#define PATHSEP ";"
+#define NULL_DEVICE "nul"
+#define BINARY_EXT ""
+#define DLL_EXT ".dll"
+#define EMULATOR_EXECUTABLE "beam.dll"
+#else
+#define PATHSEP ":"
+#define DIRSEP "/"
+#define NULL_DEVICE "/dev/null"
+#define BINARY_EXT ""
+#define EMULATOR_EXECUTABLE "beam"
+
+#endif
+#define QUOTE(s) s
+
+/* +M alloc_util allocators */
+static const char plusM_au_allocs[]= {
+ 'u', /* all alloc_util allocators */
+ 'B', /* binary_alloc */
+ 'D', /* std_alloc */
+ 'E', /* ets_alloc */
+ 'H', /* eheap_alloc */
+ 'L', /* ll_alloc */
+ 'R', /* driver_alloc */
+ 'S', /* sl_alloc */
+ 'T', /* temp_alloc */
+ '\0'
+};
+
+/* +M alloc_util allocator specific arguments */
+static char *plusM_au_alloc_switches[] = {
+ "as",
+ "asbcst",
+ "e",
+ "t",
+ "lmbcs",
+ "mbcgs",
+ "mbsd",
+ "mmbcs",
+ "mmmbc",
+ "mmsbc",
+ "msbclt",
+ "ramv",
+ "rmbcmt",
+ "rsbcmt",
+ "rsbcst",
+ "sbct",
+ "smbcs",
+ NULL
+};
+
+/* +M other arguments */
+static char *plusM_other_switches[] = {
+ "ea",
+ "ummc",
+ "uycs",
+ "im",
+ "is",
+ "it",
+ "Mamcbf",
+ "Mrmcbf",
+ "Mmcs",
+ "Mcci",
+ "Fe",
+ "Ye",
+ "Ym",
+ "Ytp",
+ "Ytt",
+ NULL
+};
+
+/* +s arguments with values */
+static char *pluss_val_switches[] = {
+ "bt",
+ "ct",
+ "ss",
+ NULL
+};
+
+/*
+ * Define sleep(seconds) in terms of Sleep() on Windows.
+ */
+
+#ifdef __WIN32__
+#define sleep(seconds) Sleep(seconds*1000)
+#endif
+
+#define SMP_SUFFIX ".smp"
+#define HYBRID_SUFFIX ".hybrid"
+
+#ifdef __WIN32__
+#define DEBUG_SUFFIX ".debug"
+#define EMU_TYPE_SUFFIX_LENGTH (strlen(HYBRID_SUFFIX)+(strlen(DEBUG_SUFFIX)))
+#else
+/* The length of the longest memory architecture suffix. */
+#define EMU_TYPE_SUFFIX_LENGTH strlen(HYBRID_SUFFIX)
+#endif
+/*
+ * Define flags for different memory architectures.
+ */
+#define EMU_TYPE_SMP 0x0001
+#define EMU_TYPE_HYBRID 0x0002
+
+#ifdef __WIN32__
+#define EMU_TYPE_DEBUG 0x0004
+#endif
+
+void usage(const char *switchname);
+void start_epmd(char *epmd);
+void error(char* format, ...);
+
+/*
+ * Local functions.
+ */
+
+#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU)
+static void usage_notsup(const char *switchname);
+#endif
+static void usage_msg(const char *msg);
+static char **build_args_from_env(char *env_var);
+static char **build_args_from_string(char *env_var);
+static void initial_argv_massage(int *argc, char ***argv);
+static void get_parameters(int argc, char** argv);
+static void add_arg(char *new_arg);
+static void add_args(char *first_arg, ...);
+static void ensure_EargsSz(int sz);
+static void add_Eargs(char *new_arg);
+static void *emalloc(size_t size);
+static void *erealloc(void *p, size_t size);
+static void efree(void *p);
+static char* strsave(char* string);
+static int is_one_of_strings(char *str, char *strs[]);
+static char *write_str(char *to, char *from);
+static void get_home(void);
+static void add_epmd_port(void);
+#ifdef __WIN32__
+static void get_start_erl_data(char *);
+static char* get_value(HKEY key, char* value_name, BOOL mustExit);
+static char* possibly_quote(char* arg);
+
+/*
+ * Functions from win_erlexec.c
+ */
+int start_win_emulator(char* emu, char *startprog,char** argv, int start_detached);
+int start_emulator(char* emu, char*start_prog, char** argv, int start_detached);
+#endif
+
+
+
+/*
+ * Variables.
+ */
+int nohup = 0;
+int keep_window = 0;
+
+static char **Eargsp = NULL; /* Emulator arguments (to appear first). */
+static int EargsSz = 0; /* Size of Eargsp */
+static int EargsCnt = 0; /* Number of emulator arguments. */
+static char **argsp = NULL; /* Common arguments. */
+static int argsCnt = 0; /* Number of common arguments */
+static int argsSz = 0; /* Size of argsp */
+static char tmpStr[10240]; /* Temporary string buffer. */
+static int verbose = 0; /* If non-zero, print some extra information. */
+static int start_detached = 0; /* If non-zero, the emulator should be
+ * started detached (in the background).
+ */
+static int emu_type = 0; /* If non-zero, start beam.ARCH or beam.ARCH.exe
+ * instead of beam or beam.exe, where ARCH is defined by flags. */
+static int emu_type_passed = 0; /* Types explicitly set */
+
+#ifdef __WIN32__
+static char *start_emulator_program = NULL; /* For detachec mode -
+ erl.exe/werl.exe */
+static char* key_val_name = ERLANG_VERSION; /* Used by the registry
+ * access functions.
+ */
+static char* boot_script = NULL; /* used by option -start_erl and -boot */
+static char* config_script = NULL; /* used by option -start_erl and -config */
+
+static HANDLE this_module_handle;
+static int run_werl;
+
+#endif
+
+/*
+ * Needed parameters to be fetched from the environment (Unix)
+ * or the ini file (Win32).
+ */
+
+static char* bindir; /* Location of executables. */
+static char* rootdir; /* Root location of Erlang installation. */
+static char* emu; /* Emulator to run. */
+static char* progname; /* Name of this program. */
+static char* home; /* Path of user's home directory. */
+
+static void
+set_env(char *key, char *value)
+{
+#ifdef __WIN32__
+ if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value))
+ error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
+#else
+ size_t size = strlen(key) + 1 + strlen(value) + 1;
+ char *str = emalloc(size);
+ sprintf(str, "%s=%s", key, value);
+ if (putenv(str) != 0)
+ error("putenv(\"%s\") failed!", str);
+#ifdef HAVE_COPYING_PUTENV
+ efree(str);
+#endif
+#endif
+}
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ efree(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ efree(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+/*
+ * Add the arcitecture suffix to the program name if needed,
+ * except on Windows, where we insert it just before ".DLL".
+ */
+static char*
+add_extra_suffixes(char *prog, int type)
+{
+ char *res;
+ char *p;
+ int len;
+#ifdef __WIN32__
+ char *dll_p;
+ int dll = 0;
+#endif
+
+ if (!type) {
+ return prog;
+ }
+
+ len = strlen(prog);
+
+ /* Worst-case allocation */
+ p = emalloc(len +
+ EMU_TYPE_SUFFIX_LENGTH +
+ + 1);
+ res = p;
+ p = write_str(p, prog);
+
+#ifdef __WIN32__
+ dll_p = res + len - 4;
+ if (dll_p >= res) {
+ if (dll_p[0] == '.' &&
+ (dll_p[1] == 'd' || dll_p[1] == 'D') &&
+ (dll_p[2] == 'l' || dll_p[2] == 'L') &&
+ (dll_p[3] == 'l' || dll_p[3] == 'L')) {
+ p = dll_p;
+ dll = 1;
+ }
+ }
+#endif
+
+#ifdef __WIN32__
+ if (type & EMU_TYPE_DEBUG) {
+ p = write_str(p, DEBUG_SUFFIX);
+ type &= ~(EMU_TYPE_DEBUG);
+ }
+#endif
+ if (type == EMU_TYPE_SMP) {
+ p = write_str(p, SMP_SUFFIX);
+ }
+ else if (type == EMU_TYPE_HYBRID) {
+ p = write_str(p, HYBRID_SUFFIX);
+ }
+#ifdef __WIN32__
+ if (dll) {
+ p = write_str(p, DLL_EXT);
+ }
+#endif
+
+ return res;
+}
+
+#ifdef __WIN32__
+__declspec(dllexport) int win_erlexec(int argc, char **argv, HANDLE module, int windowed)
+#else
+int main(int argc, char **argv)
+#endif
+{
+ int haltAfterwards = 0; /* If true, put 's erlang halt' at the end
+ * of the arguments. */
+ int isdistributed = 0;
+ int no_epmd = 0;
+ int i;
+ char* s;
+ char *epmd_prog = NULL;
+ char *malloc_lib;
+ int process_args = 1;
+ int print_args_exit = 0;
+ int print_qouted_cmd_exit = 0;
+ erts_cpu_info_t *cpuinfo = NULL;
+
+#ifdef __WIN32__
+ this_module_handle = module;
+ run_werl = windowed;
+ /* if we started this erl just to get a detached emulator,
+ * the arguments are already prepared for beam, so we skip
+ * directly to start_emulator */
+ s = get_env("ERL_CONSOLE_MODE");
+ if (s != NULL && strcmp(s, "detached")==0) {
+ free_env_val(s);
+ s = get_env("ERL_EMULATOR_DLL");
+ if (s != NULL) {
+ argv[0] = strsave(s);
+ } else {
+ argv[0] = strsave(EMULATOR_EXECUTABLE);
+ }
+ ensure_EargsSz(argc + 1);
+ memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *));
+ Eargsp[argc] = NULL;
+ emu = argv[0];
+ start_emulator_program = strsave(argv[0]);
+ goto skip_arg_massage;
+ }
+ free_env_val(s);
+#else
+ int reset_cerl_detached = 0;
+
+ s = get_env("CERL_DETACHED_PROG");
+ if (s && strcmp(s, "") != 0) {
+ emu = s;
+ start_detached = 1;
+ reset_cerl_detached = 1;
+ ensure_EargsSz(argc + 1);
+ memcpy((void *) Eargsp, (void *) argv, argc * sizeof(char *));
+ Eargsp[argc] = emu;
+ Eargsp[argc] = NULL;
+ goto skip_arg_massage;
+ }
+ free_env_val(s);
+#endif
+
+ initial_argv_massage(&argc, &argv); /* Merge with env; expand -args_file */
+
+ i = 1;
+#ifdef __WIN32__
+ /* Not used? /rickard */
+ if ((argc > 2) && (strcmp(argv[i], "-regkey") == 0)) {
+ key_val_name = strsave(argv[i+1]);
+ i = 3;
+ }
+#endif
+
+ get_parameters(argc, argv);
+
+ /*
+ * Construct the path of the executable.
+ */
+ cpuinfo = erts_cpu_info_create();
+ /* '-smp auto' is default */
+#ifdef ERTS_HAVE_SMP_EMU
+ if (erts_get_cpu_configured(cpuinfo) > 1)
+ emu_type |= EMU_TYPE_SMP;
+#endif
+
+#if defined(__WIN32__) && defined(WIN32_ALWAYS_DEBUG)
+ emu_type_passed |= EMU_TYPE_DEBUG;
+ emu_type |= EMU_TYPE_DEBUG;
+#endif
+
+ /* We need to do this before the ordinary processing. */
+ malloc_lib = get_env("ERL_MALLOC_LIB");
+ while (i < argc) {
+ if (argv[i][0] == '+') {
+ if (argv[i][1] == 'M' && argv[i][2] == 'Y' && argv[i][3] == 'm') {
+ if (argv[i][4] == '\0') {
+ if (++i < argc)
+ malloc_lib = argv[i];
+ else
+ usage("+MYm");
+ }
+ else
+ malloc_lib = &argv[i][4];
+ }
+ }
+ else if (argv[i][0] == '-') {
+ if (strcmp(argv[i], "-smp") == 0) {
+ if (i + 1 >= argc)
+ goto smp;
+
+ if (strcmp(argv[i+1], "auto") == 0) {
+ i++;
+ smp_auto:
+ emu_type_passed |= EMU_TYPE_SMP;
+#ifdef ERTS_HAVE_SMP_EMU
+ if (erts_get_cpu_configured(cpuinfo) > 1)
+ emu_type |= EMU_TYPE_SMP;
+ else
+#endif
+ emu_type &= ~EMU_TYPE_SMP;
+ }
+ else if (strcmp(argv[i+1], "enable") == 0) {
+ i++;
+ smp_enable:
+ emu_type_passed |= EMU_TYPE_SMP;
+#ifdef ERTS_HAVE_SMP_EMU
+ emu_type |= EMU_TYPE_SMP;
+#else
+ usage_notsup("-smp enable");
+#endif
+ }
+ else if (strcmp(argv[i+1], "disable") == 0) {
+ i++;
+ smp_disable:
+ emu_type_passed |= EMU_TYPE_SMP;
+ emu_type &= ~EMU_TYPE_SMP;
+ }
+ else {
+ smp:
+
+ emu_type_passed |= EMU_TYPE_SMP;
+#ifdef ERTS_HAVE_SMP_EMU
+ emu_type |= EMU_TYPE_SMP;
+#else
+ usage_notsup("-smp");
+#endif
+ }
+ } else if (strcmp(argv[i], "-smpenable") == 0) {
+ goto smp_enable;
+ } else if (strcmp(argv[i], "-smpauto") == 0) {
+ goto smp_auto;
+ } else if (strcmp(argv[i], "-smpdisable") == 0) {
+ goto smp_disable;
+#ifdef __WIN32__
+ } else if (strcmp(argv[i], "-debug") == 0) {
+ emu_type_passed |= EMU_TYPE_DEBUG;
+ emu_type |= EMU_TYPE_DEBUG;
+#endif
+ } else if (strcmp(argv[i], "-hybrid") == 0) {
+ emu_type_passed |= EMU_TYPE_HYBRID;
+#ifdef ERTS_HAVE_HYBRID_EMU
+ emu_type |= EMU_TYPE_HYBRID;
+#else
+ usage_notsup("-hybrid");
+#endif
+ } else if (strcmp(argv[i], "-extra") == 0) {
+ break;
+ }
+ }
+ i++;
+ }
+
+ erts_cpu_info_destroy(cpuinfo);
+ cpuinfo = NULL;
+
+ if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) {
+ /*
+ * We have a conflict. Only using explicitly passed arguments
+ * may solve it...
+ */
+ emu_type &= emu_type_passed;
+ if ((emu_type & EMU_TYPE_HYBRID) && (emu_type & EMU_TYPE_SMP)) {
+ usage_msg("Hybrid heap emulator with SMP support selected. The "
+ "combination hybrid heap and SMP support is currently "
+ "not supported.");
+ }
+ }
+
+ if (malloc_lib) {
+ if (strcmp(malloc_lib, "libc") != 0)
+ usage("+MYm");
+ }
+ emu = add_extra_suffixes(emu, emu_type);
+ sprintf(tmpStr, "%s" DIRSEP "%s" BINARY_EXT, bindir, emu);
+ emu = strsave(tmpStr);
+
+ add_Eargs(emu); /* Will be argv[0] -- necessary! */
+
+ /*
+ * Add the bindir to the path (unless it is there already).
+ */
+
+ s = get_env("PATH");
+ if (!s) {
+ sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin", bindir, rootdir);
+ } else if (strstr(s, bindir) == NULL) {
+ sprintf(tmpStr, "%s" PATHSEP "%s" DIRSEP "bin" PATHSEP "%s", bindir,
+ rootdir, s);
+ } else {
+ sprintf(tmpStr, "%s", s);
+ }
+ free_env_val(s);
+ set_env("PATH", tmpStr);
+
+ i = 1;
+
+#ifdef __WIN32__
+#define ADD_BOOT_CONFIG \
+ if (boot_script) \
+ add_args("-boot", boot_script, NULL); \
+ if (config_script) \
+ add_args("-config", config_script, NULL);
+#else
+#define ADD_BOOT_CONFIG
+#endif
+
+ get_home();
+ add_args("-home", home, NULL);
+
+ add_epmd_port();
+
+ add_arg("--");
+
+ while (i < argc) {
+ if (!process_args) { /* Copy arguments after '-extra' */
+ add_arg(argv[i]);
+ i++;
+ } else {
+ switch (argv[i][0]) {
+ case '-':
+ switch (argv[i][1]) {
+#ifdef __WIN32__
+ case 'b':
+ if (strcmp(argv[i], "-boot") == 0) {
+ if (boot_script)
+ error("Conflicting -start_erl and -boot options");
+ if (i+1 >= argc)
+ usage("-boot");
+ boot_script = strsave(argv[i+1]);
+ i++;
+ }
+ else {
+ add_arg(argv[i]);
+ }
+ break;
+#endif
+ case 'c':
+ if (strcmp(argv[i], "-compile") == 0) {
+ /*
+ * Note that the shell script erl.exec does an recursive call
+ * on itself here. We'll avoid doing that.
+ */
+ add_args("-noshell", "-noinput", "-s", "c", "lc_batch",
+ NULL);
+ add_Eargs("-B");
+ haltAfterwards = 0;
+ }
+#ifdef __WIN32__
+ else if (strcmp(argv[i], "-config") == 0){
+ if (config_script)
+ error("Conflicting -start_erl and -config options");
+ if (i+1 >= argc)
+ usage("-config");
+ config_script = strsave(argv[i+1]);
+ i++;
+ }
+#endif
+ else {
+ add_arg(argv[i]);
+ }
+ break;
+
+ case 'd':
+ if (strcmp(argv[i], "-detached") != 0) {
+ add_arg(argv[i]);
+ } else {
+ start_detached = 1;
+ add_args("-noshell", "-noinput", NULL);
+ }
+ break;
+
+ case 'i':
+ if (strcmp(argv[i], "-instr") == 0) {
+ add_Eargs("-Mim");
+ add_Eargs("true");
+ }
+ else
+ add_arg(argv[i]);
+ break;
+
+ case 'e':
+ if (strcmp(argv[i], "-extra") == 0) {
+ process_args = 0;
+ ADD_BOOT_CONFIG;
+ add_arg(argv[i]);
+ } else if (strcmp(argv[i], "-emu_args") == 0) { /* -emu_args */
+ verbose = 1;
+ } else if (strcmp(argv[i], "-emu_args_exit") == 0) {
+ print_args_exit = 1;
+ } else if (strcmp(argv[i], "-emu_qouted_cmd_exit") == 0) {
+ print_qouted_cmd_exit = 1;
+ } else if (strcmp(argv[i], "-env") == 0) { /* -env VARNAME VARVALUE */
+ if (i+2 >= argc)
+ usage("-env");
+ set_env(argv[i+1], argv[i+2]);
+ i += 2;
+ } else if (strcmp(argv[i], "-epmd") == 0) {
+ if (i+1 >= argc)
+ usage("-epmd");
+ epmd_prog = argv[i+1];
+ ++i;
+ } else {
+ add_arg(argv[i]);
+ }
+ break;
+ case 'k':
+ if (strcmp(argv[i], "-keep_window") == 0) {
+ keep_window = 1;
+ } else
+ add_arg(argv[i]);
+ break;
+
+ case 'm':
+ /*
+ * Note that the shell script erl.exec does an recursive call
+ * on itself here. We'll avoid doing that.
+ */
+ if (strcmp(argv[i], "-make") == 0) {
+ add_args("-noshell", "-noinput", "-s", "make", "all", NULL);
+ add_Eargs("-B");
+ haltAfterwards = 1;
+ i = argc; /* Skip rest of command line */
+ } else if (strcmp(argv[i], "-man") == 0) {
+#if defined(__WIN32__)
+ error("-man not supported on Windows");
+#else
+ argv[i] = "man";
+ sprintf(tmpStr, "%s/man", rootdir);
+ set_env("MANPATH", tmpStr);
+ execvp("man", argv+i);
+ error("Could not execute the 'man' command.");
+#endif
+ } else
+ add_arg(argv[i]);
+ break;
+
+ case 'n':
+ if (strcmp(argv[i], "-name") == 0) { /* -name NAME */
+ if (i+1 >= argc)
+ usage("-name");
+
+ /*
+ * Note: Cannot use add_args() here, due to non-defined
+ * evaluation order.
+ */
+
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ isdistributed = 1;
+ i++;
+ } else if (strcmp(argv[i], "-noinput") == 0) {
+ add_args("-noshell", "-noinput", NULL);
+ } else if (strcmp(argv[i], "-nohup") == 0) {
+ add_arg("-nohup");
+ nohup = 1;
+ } else if (strcmp(argv[i], "-no_epmd") == 0) {
+ add_arg("-no_epmd");
+ no_epmd = 1;
+ } else {
+ add_arg(argv[i]);
+ }
+ break;
+
+ case 's': /* -sname NAME */
+ if (strcmp(argv[i], "-sname") == 0) {
+ if (i+1 >= argc)
+ usage("-sname");
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ isdistributed = 1;
+ i++;
+ }
+#ifdef __WIN32__
+ else if (strcmp(argv[i], "-service_event") == 0) {
+ add_arg(argv[i]);
+ add_arg(argv[i+1]);
+ i++;
+ }
+ else if (strcmp(argv[i], "-start_erl") == 0) {
+ if (i+1 < argc && argv[i+1][0] != '-') {
+ get_start_erl_data(argv[i+1]);
+ i++;
+ } else
+ get_start_erl_data((char *) NULL);
+ }
+#endif
+ else
+ add_arg(argv[i]);
+
+ break;
+
+ case 'v': /* -version */
+ if (strcmp(argv[i], "-version") == 0) {
+ add_Eargs("-V");
+ } else {
+ add_arg(argv[i]);
+ }
+ break;
+
+ default:
+ add_arg(argv[i]);
+ break;
+ } /* switch(argv[i][1] */
+ break;
+
+ case '+':
+ switch (argv[i][1]) {
+ case '#':
+ case 'a':
+ case 'A':
+ case 'b':
+ case 'h':
+ case 'i':
+ case 'P':
+ case 'S':
+ case 'T':
+ case 'R':
+ case 'W':
+ case 'K':
+ if (argv[i][2] != '\0')
+ goto the_default;
+ if (i+1 >= argc)
+ usage(argv[i]);
+ argv[i][0] = '-';
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ break;
+ case 'B':
+ argv[i][0] = '-';
+ if (argv[i][2] != '\0') {
+ if ((argv[i][2] != 'i') &&
+ (argv[i][2] != 'c') &&
+ (argv[i][2] != 'd')) {
+ usage(argv[i]);
+ } else {
+ add_Eargs(argv[i]);
+ break;
+ }
+ }
+ if (i+1 < argc) {
+ if ((argv[i+1][0] != '-') &&
+ (argv[i+1][0] != '+')) {
+ if (argv[i+1][0] == 'i') {
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ break;
+ } else {
+ usage(argv[i]);
+ }
+ }
+ }
+ add_Eargs(argv[i]);
+ break;
+ case 'M': {
+ int x;
+ for (x = 0; plusM_au_allocs[x]; x++)
+ if (plusM_au_allocs[x] == argv[i][2])
+ break;
+ if ((plusM_au_allocs[x]
+ && is_one_of_strings(&argv[i][3],
+ plusM_au_alloc_switches))
+ || is_one_of_strings(&argv[i][2],
+ plusM_other_switches)) {
+ if (i+1 >= argc
+ || argv[i+1][0] == '-'
+ || argv[i+1][0] == '+')
+ usage(argv[i]);
+ argv[i][0] = '-';
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ }
+ else
+ goto the_default;
+ break;
+ }
+ case 's':
+ if (!is_one_of_strings(&argv[i][2],
+ pluss_val_switches))
+ goto the_default;
+ else {
+ if (i+1 >= argc
+ || argv[i+1][0] == '-'
+ || argv[i+1][0] == '+')
+ usage(argv[i]);
+ argv[i][0] = '-';
+ add_Eargs(argv[i]);
+ add_Eargs(argv[i+1]);
+ i++;
+ }
+ break;
+ default:
+ the_default:
+ argv[i][0] = '-'; /* Change +option to -option. */
+ add_Eargs(argv[i]);
+ }
+ break;
+
+ default:
+ add_arg(argv[i]);
+ } /* switch(argv[i][0] */
+ i++;
+ }
+ }
+
+ if (process_args) {
+ ADD_BOOT_CONFIG;
+ }
+#undef ADD_BOOT_CONFIG
+
+ /* Doesn't conflict with -extra, since -make skips all the rest of
+ the arguments. */
+ if (haltAfterwards) {
+ add_args("-s", "erlang", "halt", NULL);
+ }
+
+ if (isdistributed && !no_epmd)
+ start_epmd(epmd_prog);
+
+#if (! defined(__WIN32__)) && defined(DEBUG)
+ if (start_detached) {
+ /* Start the emulator within an xterm.
+ * Move up all arguments and insert
+ * "xterm -e " first.
+ * The path must be searched for this
+ * to work, i.e execvp() must be used.
+ */
+ ensure_EargsSz(EargsCnt+2);
+ for (i = EargsCnt; i > 0; i--)
+ Eargsp[i+1] = Eargsp[i-1]; /* Two args to insert */
+ EargsCnt += 2; /* Two args to insert */
+ Eargsp[0] = emu = "xterm";
+ Eargsp[1] = "-e";
+ }
+#endif
+
+ add_Eargs("--");
+ add_Eargs("-root");
+ add_Eargs(rootdir);
+ add_Eargs("-progname");
+ add_Eargs(progname);
+ add_Eargs("--");
+ ensure_EargsSz(EargsCnt + argsCnt + 1);
+ for (i = 0; i < argsCnt; i++)
+ Eargsp[EargsCnt++] = argsp[i];
+ Eargsp[EargsCnt] = NULL;
+
+ if (print_qouted_cmd_exit) {
+ printf("\"%s\" ", emu);
+ for (i = 1; i < EargsCnt; i++)
+ printf("\"%s\" ", Eargsp[i]);
+ printf("\n");
+ exit(0);
+ }
+
+ if (print_args_exit) {
+ for (i = 1; i < EargsCnt; i++)
+ printf("%s ", Eargsp[i]);
+ printf("\n");
+ exit(0);
+ }
+
+ if (verbose) {
+ printf("Executing: %s", emu);
+ for (i = 0; i < EargsCnt; i++)
+ printf(" %s", Eargsp[i]);
+ printf("\n\n");
+ }
+
+#ifdef __WIN32__
+
+ if (EargsSz != EargsCnt + 1)
+ Eargsp = (char **) erealloc((void *) Eargsp, (EargsCnt + 1) *
+ sizeof(char *));
+ efree((void *) argsp);
+
+ skip_arg_massage:
+ /*DebugBreak();*/
+
+ if (run_werl) {
+ if (start_detached) {
+ char *p;
+ /* transform werl to erl */
+ p = start_emulator_program+strlen(start_emulator_program);
+ while (--p >= start_emulator_program && *p != '/' && *p != '\\' &&
+ *p != 'W' && *p != 'w')
+ ;
+ if (p >= start_emulator_program && (*p == 'W' || *p == 'w') &&
+ (p[1] == 'E' || p[1] == 'e') && (p[2] == 'R' || p[2] == 'r') &&
+ (p[3] == 'L' || p[3] == 'l')) {
+ memmove(p,p+1,strlen(p));
+ }
+ }
+ return start_win_emulator(emu, start_emulator_program, Eargsp, start_detached);
+ } else {
+ return start_emulator(emu, start_emulator_program, Eargsp, start_detached);
+ }
+
+#else
+
+ skip_arg_massage:
+ if (start_detached) {
+ int status = fork();
+ if (status != 0) /* Parent */
+ return 0;
+
+ if (reset_cerl_detached)
+ putenv("CERL_DETACHED_PROG=");
+
+ /* Detach from controlling terminal */
+#ifdef HAVE_SETSID
+ setsid();
+#elif defined(TIOCNOTTY)
+ {
+ int fd = open("/dev/tty", O_RDWR);
+ if (fd >= 0) {
+ ioctl(fd, TIOCNOTTY, NULL);
+ close(fd);
+ }
+ }
+#endif
+
+ status = fork();
+ if (status != 0) /* Parent */
+ return 0;
+
+ /*
+ * Grandchild.
+ */
+ close(0);
+ open("/dev/null", O_RDONLY);
+ close(1);
+ open("/dev/null", O_WRONLY);
+ close(2);
+ open("/dev/null", O_WRONLY);
+#ifdef DEBUG
+ execvp(emu, Eargsp); /* "xterm ..." needs to search the path */
+#endif
+ }
+#ifdef DEBUG
+ else
+#endif
+ {
+ execv(emu, Eargsp);
+ }
+ error("Error %d executing \'%s\'.", errno, emu);
+ return 1;
+#endif
+}
+
+
+static void
+usage_aux(void)
+{
+ fprintf(stderr,
+ "Usage: erl [-version] [-sname NAME | -name NAME] "
+ "[-noshell] [-noinput] [-env VAR VALUE] [-compile file ...] "
+#ifdef __WIN32__
+ "[-start_erl [datafile]] "
+#endif
+ "[-smp "
+#ifdef ERTS_HAVE_SMP_EMU
+ "[enable|"
+#endif
+ "auto|disable"
+#ifdef ERTS_HAVE_SMP_EMU
+ "]"
+#endif
+ "] "
+#ifdef ERTS_HAVE_HYBRID_EMU
+ "[-hybrid] "
+#endif
+ "[-make] [-man [manopts] MANPAGE] [-x] [-emu_args] "
+ "[-args_file FILENAME] "
+ "[+A THREADS] [+a SIZE] [+B[c|d|i]] [+c] [+h HEAP_SIZE] [+K BOOLEAN] "
+ "[+l] [+M<SUBSWITCH> <ARGUMENT>] [+P MAX_PROCS] [+R COMPAT_REL] "
+ "[+r] [+s SCHEDULER_OPTION] [+S NO_SCHEDULERS:NO_SCHEDULERS_ONLINE] [+T LEVEL] [+V] [+v] [+W<i|w>] "
+ "[args ...]\n");
+ exit(1);
+}
+
+void
+usage(const char *switchname)
+{
+ fprintf(stderr, "Missing argument(s) for \'%s\'.\n", switchname);
+ usage_aux();
+}
+
+#if !defined(ERTS_HAVE_SMP_EMU) || !defined(ERTS_HAVE_HYBRID_EMU)
+static void
+usage_notsup(const char *switchname)
+{
+ fprintf(stderr, "Argument \'%s\' not supported.\n", switchname);
+ usage_aux();
+}
+#endif
+
+static void
+usage_msg(const char *msg)
+{
+ fprintf(stderr, "%s\n", msg);
+ usage_aux();
+}
+
+static void
+usage_format(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ usage_aux();
+}
+
+void
+start_epmd(char *epmd)
+{
+ char epmd_cmd[MAXPATHLEN+100];
+#ifdef __WIN32__
+ char* arg1 = NULL;
+#endif
+ int result;
+
+ if (!epmd) {
+ epmd = epmd_cmd;
+#ifdef __WIN32__
+ sprintf(epmd_cmd, "%s" DIRSEP "epmd", bindir);
+ arg1 = "-daemon";
+#else
+ sprintf(epmd_cmd, "%s" DIRSEP "epmd -daemon", bindir);
+#endif
+ }
+#ifdef __WIN32__
+ if (arg1 != NULL) {
+ strcat(epmd, " ");
+ strcat(epmd, arg1);
+ }
+ {
+ STARTUPINFO start;
+ PROCESS_INFORMATION pi;
+ memset(&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+ if (!CreateProcess(NULL, epmd, NULL, NULL, FALSE,
+ CREATE_DEFAULT_ERROR_MODE | DETACHED_PROCESS,
+ NULL, NULL, &start, &pi))
+ result = -1;
+ else
+ result = 0;
+ }
+#else
+ result = system(epmd);
+#endif
+ if (result == -1) {
+ fprintf(stderr, "Error spawning %s (error %d)\n", epmd_cmd,errno);
+ exit(1);
+ }
+}
+
+static void
+add_arg(char *new_arg)
+{
+ if (argsCnt >= argsSz)
+ argsp = (char **) erealloc((void *) argsp,
+ sizeof(char *) * (argsSz += 20));
+ argsp[argsCnt++] = QUOTE(new_arg);
+}
+
+static void
+add_args(char *first_arg, ...)
+{
+ va_list ap;
+ char* arg;
+
+ add_arg(first_arg);
+ va_start(ap, first_arg);
+ while ((arg = va_arg(ap, char *)) != NULL) {
+ add_arg(arg);
+ }
+ va_end(ap);
+}
+
+static void
+ensure_EargsSz(int sz)
+{
+ if (EargsSz < sz)
+ Eargsp = (char **) erealloc((void *) Eargsp,
+ sizeof(char *) * (EargsSz = sz));
+}
+
+static void
+add_Eargs(char *new_arg)
+{
+ if (EargsCnt >= EargsSz)
+ Eargsp = (char **) erealloc((void *) Eargsp,
+ sizeof(char *) * (EargsSz += 20));
+ Eargsp[EargsCnt++] = QUOTE(new_arg);
+}
+
+#if !defined(__WIN32__)
+void error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "erlexec: %s\n", sbuf);
+ exit(1);
+}
+#endif
+
+static void *
+emalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+
+static void
+efree(void *p)
+{
+ free(p);
+}
+
+static int
+is_one_of_strings(char *str, char *strs[])
+{
+ int i, j;
+ for (i = 0; strs[i]; i++) {
+ for (j = 0; str[j] && strs[i][j] && str[j] == strs[i][j]; j++);
+ if (!str[j] && !strs[i][j])
+ return 1;
+ }
+ return 0;
+}
+
+static char *write_str(char *to, char *from)
+{
+ while (*from)
+ *(to++) = *(from++);
+ *to = '\0';
+ return to;
+}
+
+char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+
+#if defined(__WIN32__)
+
+static void get_start_erl_data(char *file)
+{
+ int fp;
+ char tmpbuffer[512];
+ char start_erl_data[512];
+ int bytesread;
+ char* env;
+ char* reldir;
+ char* otpstring;
+ char* tprogname;
+ if (boot_script)
+ error("Conflicting -start_erl and -boot options");
+ if (config_script)
+ error("Conflicting -start_erl and -config options");
+ env = get_env("RELDIR");
+ if (env)
+ reldir = strsave(env);
+ else {
+ sprintf(tmpbuffer, "%s/releases", rootdir);
+ reldir = strsave(tmpbuffer);
+ }
+ free_env_val(env);
+ if (file == NULL)
+ sprintf(start_erl_data, "%s/start_erl.data", reldir);
+ else
+ sprintf(start_erl_data, "%s", file);
+ fp = _open(start_erl_data, _O_RDONLY );
+ if( fp == -1 )
+ error( "open failed on %s",start_erl_data );
+ else {
+ if( ( bytesread = _read( fp, tmpbuffer, 512 ) ) <= 0 )
+ error( "Problem reading file %s", start_erl_data );
+ else {
+ tmpbuffer[bytesread]='\0';
+ if ((otpstring = strchr(tmpbuffer,' ')) != NULL) {
+ *otpstring = '\0';
+ otpstring++;
+
+/*
+ * otpstring is the otpversion
+ * tmpbuffer is the emuversion
+*/
+ }
+ }
+ }
+ tprogname = otpstring;
+ while (*tprogname) {
+ if (*tprogname <= ' ') {
+ *tprogname='\0';
+ break;
+ }
+ tprogname++;
+ }
+
+ bindir = emalloc(512);
+ sprintf(bindir,"%s/erts-%s/bin",rootdir,tmpbuffer);
+ /* BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin */
+ tprogname = progname;
+ progname = emalloc(strlen(tprogname) + 20);
+ sprintf(progname,"%s -start_erl",tprogname);
+
+ boot_script = emalloc(512);
+ config_script = emalloc(512);
+ sprintf(boot_script, "%s/%s/start", reldir, otpstring);
+ sprintf(config_script, "%s/%s/sys", reldir, otpstring);
+
+}
+
+
+static char *replace_filename(char *path, char *new_base)
+{
+ int plen = strlen(path);
+ char *res = malloc((plen+strlen(new_base)+1)*sizeof(char));
+ char *p;
+
+ strcpy(res,path);
+ for (p = res+plen-1 ;p >= res && *p != '\\'; --p)
+ ;
+ *(p+1) ='\0';
+ strcat(res,new_base);
+ return res;
+}
+
+static char *path_massage(char *long_path)
+{
+ char *p;
+
+ p = malloc(MAX_PATH+1);
+ strcpy(p, long_path);
+ GetShortPathName(p, p, MAX_PATH);
+ return p;
+}
+
+static char *do_lookup_in_section(InitSection *inis, char *name,
+ char *section, char *filename, int is_path)
+{
+ char *p = lookup_init_entry(inis, name);
+
+ if (p == NULL) {
+ error("Could not find key %s in section %s of file %s",
+ name,section,filename);
+ }
+
+ if (is_path) {
+ return path_massage(p);
+ } else {
+ return strsave(p);
+ }
+}
+
+
+static void get_parameters(int argc, char** argv)
+{
+ char *p;
+ char buffer[MAX_PATH];
+ char *ini_filename;
+ HANDLE module = GetModuleHandle(NULL); /* This might look strange, but we want the erl.ini
+ that resides in the same dir as erl.exe, not
+ an erl.ini in our directory */
+ InitFile *inif;
+ InitSection *inis;
+
+ if (module == NULL) {
+ error("Cannot GetModuleHandle()");
+ }
+
+ if (GetModuleFileName(module,buffer,MAX_PATH) == 0) {
+ error("Could not GetModuleFileName");
+ }
+
+ ini_filename = replace_filename(buffer,INI_FILENAME);
+
+ if ((inif = load_init_file(ini_filename)) == NULL) {
+ /* Assume that the path is absolute and that
+ it does not contain any symbolic link */
+
+ char buffer[MAX_PATH];
+
+ /* Determine bindir */
+ if (GetEnvironmentVariable("ERLEXEC_DIR", buffer, MAX_PATH) == 0) {
+ strcpy(buffer, ini_filename);
+ for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p)
+ ;
+ *p ='\0';
+ }
+ bindir = path_massage(buffer);
+
+ /* Determine rootdir */
+ for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '\\'; --p)
+ ;
+ p--;
+ for (;p >= buffer && *p != '\\'; --p)
+ ;
+ *p ='\0';
+ rootdir = path_massage(buffer);
+
+ /* Hardcoded progname */
+ progname = strsave(DEFAULT_PROGNAME);
+ } else {
+ if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) {
+ error("Could not find section %s in init file %s",
+ INI_SECTION, ini_filename);
+ }
+
+ bindir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename,1);
+ rootdir = do_lookup_in_section(inis, "Rootdir", INI_SECTION,
+ ini_filename,1);
+ progname = do_lookup_in_section(inis, "Progname", INI_SECTION,
+ ini_filename,0);
+ free_init_file(inif);
+ }
+
+ emu = EMULATOR_EXECUTABLE;
+ start_emulator_program = strsave(argv[0]);
+
+ free(ini_filename);
+}
+
+static void
+get_home(void)
+{
+ int len;
+ char tmpstr[MAX_PATH+1];
+ char* homedrive;
+ char* homepath;
+
+ homedrive = get_env("HOMEDRIVE");
+ homepath = get_env("HOMEPATH");
+ if (!homedrive || !homepath) {
+ if (len = GetWindowsDirectory(tmpstr,MAX_PATH)) {
+ home = emalloc(len+1);
+ strcpy(home,tmpstr);
+ } else
+ error("HOMEDRIVE or HOMEPATH is not set and GetWindowsDir failed");
+ } else {
+ home = emalloc(strlen(homedrive)+strlen(homepath)+1);
+ strcpy(home, homedrive);
+ strcat(home, homepath);
+ }
+ free_env_val(homedrive);
+ free_env_val(homepath);
+}
+
+#else
+
+static void
+get_parameters(int argc, char** argv)
+{
+ progname = get_env("PROGNAME");
+ if (!progname) {
+ progname = strsave(DEFAULT_PROGNAME);
+ }
+
+ emu = get_env("EMU");
+ if (!emu) {
+ emu = strsave(EMULATOR_EXECUTABLE);
+ }
+
+ bindir = get_env("BINDIR");
+ if (!bindir) {
+ /* Determine bindir from absolute path to executable */
+ char *p;
+ char buffer[PATH_MAX];
+ strcpy(buffer, argv[0]);
+
+ for (p = buffer+strlen(buffer)-1 ; p >= buffer && *p != '/'; --p)
+ ;
+ *p ='\0';
+ bindir = strsave(buffer);
+ }
+
+ rootdir = get_env("ROOTDIR");
+ if (!rootdir) {
+ /* Determine rootdir from absolute path to bindir */
+ char *p;
+ char buffer[PATH_MAX];
+ strcpy(buffer, bindir);
+
+ for (p = buffer+strlen(buffer)-1; p >= buffer && *p != '/'; --p)
+ ;
+ p--;
+ for (; p >= buffer && *p != '/'; --p)
+ ;
+ *p ='\0';
+ rootdir = strsave(buffer);
+ }
+
+ if (!progname || !emu || !rootdir || !bindir) {
+ error("PROGNAME, EMU, ROOTDIR and BINDIR must be set");
+ }
+}
+
+static void
+get_home(void)
+{
+ home = get_env("HOME");
+ if (home == NULL)
+ error("HOME must be set");
+}
+
+#endif
+
+static void add_epmd_port(void)
+{
+ char* port = get_env("ERL_EPMD_PORT");
+ if (port != NULL) {
+ add_args("-epmd_port", port, NULL);
+ }
+}
+
+static char **build_args_from_env(char *env_var)
+{
+ char *value = get_env(env_var);
+ char **res = build_args_from_string(value);
+ free_env_val(value);
+ return res;
+}
+
+static char **build_args_from_string(char *string)
+{
+ int argc = 0;
+ char **argv = NULL;
+ int alloced = 0;
+ char **cur_s = NULL; /* Initialized to avoid warning. */
+ int s_alloced = 0;
+ int s_pos = 0;
+ char *p = string;
+ enum {Start, Build, Build0, BuildSQuoted, BuildDQuoted, AcceptNext} state;
+
+#define ENSURE() \
+ if (s_pos >= s_alloced) { \
+ if (!*cur_s) { \
+ *cur_s = emalloc(s_alloced = 20); \
+ } else { \
+ *cur_s = erealloc(*cur_s, s_alloced += 20); \
+ } \
+ }
+
+
+ if (!p)
+ return NULL;
+ argv = emalloc(sizeof(char *) * (alloced = 10));
+ state = Start;
+ for(;;) {
+ switch (state) {
+ case Start:
+ if (!*p)
+ goto done;
+ if (argc >= alloced - 1) { /* Make room for extra NULL */
+ argv = erealloc(argv, (alloced += 10) * sizeof(char *));
+ }
+ cur_s = argc + argv;
+ *cur_s = NULL;
+ s_pos = 0;
+ s_alloced = 0;
+ state = Build0;
+ break;
+ case Build0:
+ switch (*p) {
+ case ' ':
+ ++p;
+ break;
+ case '\0':
+ state = Start;
+ break;
+ default:
+ state = Build;
+ break;
+ }
+ break;
+ case Build:
+ switch (*p) {
+ case ' ':
+ case '\0':
+ ENSURE();
+ (*cur_s)[s_pos] = '\0';
+ ++argc;
+ state = Start;
+ break;
+ case '"':
+ ++p;
+ state = BuildDQuoted;
+ break;
+ case '\'':
+ ++p;
+ state = BuildSQuoted;
+ break;
+ case '\\':
+ ++p;
+ state = AcceptNext;
+ break;
+ default:
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ break;
+ }
+ break;
+ case BuildDQuoted:
+ switch (*p) {
+ case '"':
+ ++p;
+ /* fall through */
+ case '\0':
+ state = Build;
+ break;
+ default:
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ break;
+ }
+ break;
+ case BuildSQuoted:
+ switch (*p) {
+ case '\'':
+ ++p;
+ /* fall through */
+ case '\0':
+ state = Build;
+ break;
+ default:
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ break;
+ }
+ break;
+ case AcceptNext:
+ if (!*p) {
+ state = Build;
+ } else {
+ ENSURE();
+ (*cur_s)[s_pos++] = *p++;
+ }
+ state = Build;
+ break;
+ }
+ }
+done:
+ argv[argc] = NULL; /* Sure to be large enough */
+ if (!argc) {
+ efree(argv);
+ return NULL;
+ }
+ return argv;
+#undef ENSURE
+}
+
+static char *
+errno_string(void)
+{
+ char *str = strerror(errno);
+ if (!str)
+ return "unknown error";
+ return str;
+}
+
+static char **
+read_args_file(char *filename)
+{
+ int c, aix = 0, quote = 0, cmnt = 0, asize = 0;
+ char **res, *astr = NULL;
+ FILE *file;
+
+#undef EAF_CMNT
+#undef EAF_QUOTE
+#undef SAVE_CHAR
+
+#define EAF_CMNT (1 << 8)
+#define EAF_QUOTE (1 << 9)
+#define SAVE_CHAR(C) \
+ do { \
+ if (!astr) \
+ astr = emalloc(sizeof(char)*(asize = 20)); \
+ if (aix == asize) \
+ astr = erealloc(astr, sizeof(char)*(asize += 20)); \
+ if (' ' != (char) (C)) \
+ astr[aix++] = (char) (C); \
+ else if (aix > 0 && astr[aix-1] != ' ') \
+ astr[aix++] = ' '; \
+ } while (0)
+
+ do {
+ errno = 0;
+ file = fopen(filename, "r");
+ } while (!file && errno == EINTR);
+ if (!file) {
+ usage_format("Failed to open arguments file \"%s\": %s\n",
+ filename,
+ errno_string());
+ }
+
+ while (1) {
+ c = getc(file);
+ if (c == EOF) {
+ if (ferror(file)) {
+ if (errno == EINTR) {
+ clearerr(file);
+ continue;
+ }
+ usage_format("Failed to read arguments file \"%s\": %s\n",
+ filename,
+ errno_string());
+ }
+ break;
+ }
+
+ switch (quote | cmnt | c) {
+ case '\\':
+ quote = EAF_QUOTE;
+ break;
+ case '#':
+ cmnt = EAF_CMNT;
+ break;
+ case EAF_CMNT|'\n':
+ cmnt = 0;
+ /* Fall through... */
+ case '\n':
+ case '\f':
+ case '\r':
+ case '\t':
+ case '\v':
+ if (!quote)
+ c = ' ';
+ /* Fall through... */
+ default:
+ if (!cmnt)
+ SAVE_CHAR(c);
+ quote = 0;
+ break;
+ }
+ }
+
+ SAVE_CHAR('\0');
+
+ fclose(file);
+
+ if (astr[0] == '\0')
+ res = NULL;
+ else
+ res = build_args_from_string(astr);
+
+ efree(astr);
+
+ return res;
+
+#undef EAF_CMNT
+#undef EAF_QUOTE
+#undef SAVE_CHAR
+}
+
+typedef struct {
+ char **argv;
+ int argc;
+ int size;
+} argv_buf;
+
+static void
+trim_argv_buf(argv_buf *abp)
+{
+ abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size = abp->argc));
+}
+
+static void
+save_arg(argv_buf *abp, char *arg)
+{
+ if (abp->size <= abp->argc) {
+ if (!abp->argv)
+ abp->argv = emalloc(sizeof(char *)*(abp->size = 100));
+ else
+ abp->argv = erealloc(abp->argv, sizeof(char *)*(abp->size += 100));
+ }
+ abp->argv[abp->argc++] = arg;
+}
+
+#define DEF_ARGV_STACK_SIZE 10
+#define ARGV_STACK_SIZE_INCR 50
+
+typedef struct {
+ char **argv;
+ int ix;
+} argv_stack_element;
+
+typedef struct {
+ int top_ix;
+ int size;
+ argv_stack_element *base;
+ argv_stack_element def_buf[DEF_ARGV_STACK_SIZE];
+} argv_stack;
+
+#define ARGV_STACK_INIT(S) \
+do { \
+ (S)->top_ix = 0; \
+ (S)->size = DEF_ARGV_STACK_SIZE; \
+ (S)->base = &(S)->def_buf[0]; \
+} while (0)
+
+static void
+push_argv(argv_stack *stck, char **argv, int ix)
+{
+ if (stck->top_ix == stck->size) {
+ if (stck->base != &stck->def_buf[0]) {
+ stck->size += ARGV_STACK_SIZE_INCR;
+ stck->base = erealloc(stck->base,
+ sizeof(argv_stack_element)*stck->size);
+ }
+ else {
+ argv_stack_element *base;
+ base = emalloc(sizeof(argv_stack_element)
+ *(stck->size + ARGV_STACK_SIZE_INCR));
+ memcpy((void *) base,
+ (void *) stck->base,
+ sizeof(argv_stack_element)*stck->size);
+ stck->base = base;
+ stck->size += ARGV_STACK_SIZE_INCR;
+ }
+ }
+ stck->base[stck->top_ix].argv = argv;
+ stck->base[stck->top_ix++].ix = ix;
+}
+
+static void
+pop_argv(argv_stack *stck, char ***argvp, int *ixp)
+{
+ if (stck->top_ix == 0) {
+ *argvp = NULL;
+ *ixp = 0;
+ }
+ else {
+ *argvp = stck->base[--stck->top_ix].argv;
+ *ixp = stck->base[stck->top_ix].ix;
+ if (stck->top_ix == 0 && stck->base != &stck->def_buf[0]) {
+ efree(stck->base);
+ stck->base = &stck->def_buf[0];
+ stck->size = DEF_ARGV_STACK_SIZE;
+ }
+ }
+}
+
+static void
+get_file_args(char *filename, argv_buf *abp, argv_buf *xabp)
+{
+ argv_stack stck;
+ int i;
+ char **argv;
+
+ ARGV_STACK_INIT(&stck);
+
+ i = 0;
+ argv = read_args_file(filename);
+
+ while (argv) {
+
+ while (argv[i]) {
+ if (strcmp(argv[i], "-args_file") == 0) {
+ char **new_argv;
+ char *fname;
+ if (!argv[++i])
+ usage("-args_file");
+ fname = argv[i++];
+ new_argv = read_args_file(fname);
+ if (new_argv) {
+ if (argv[i])
+ push_argv(&stck, argv, i);
+ else
+ efree(argv);
+ i = 0;
+ argv = new_argv;
+ }
+ }
+ else {
+ if (strcmp(argv[i], "-extra") == 0) {
+ i++;
+ while (argv[i])
+ save_arg(xabp, argv[i++]);
+ break;
+ }
+ save_arg(abp, argv[i++]);
+ }
+ }
+
+ efree(argv);
+
+ pop_argv(&stck, &argv, &i);
+ }
+}
+
+static void
+initial_argv_massage(int *argc, char ***argv)
+{
+ argv_buf ab = {0}, xab = {0};
+ int ix, vix, ac;
+ char **av;
+ struct {
+ int argc;
+ char **argv;
+ } avv[] = {{INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL},
+ {INT_MAX, NULL}, {INT_MAX, NULL}, {INT_MAX, NULL}};
+ /*
+ * The environment flag containing OTP release is intentionally
+ * undocumented and intended for OTP internal use only.
+ */
+
+ vix = 0;
+ av = build_args_from_env("ERL_AFLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ /* command line */
+ if (*argc > 1) {
+ avv[vix].argc = *argc - 1;
+ avv[vix++].argv = &(*argv)[1];
+ }
+
+ av = build_args_from_env("ERL_FLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ av = build_args_from_env("ERL_" OTP_SYSTEM_VERSION "_FLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ av = build_args_from_env("ERL_ZFLAGS");
+ if (av)
+ avv[vix++].argv = av;
+
+ if (vix == (*argc > 1 ? 1 : 0)) {
+ /* Only command line argv; check if we can use argv as it is... */
+ ac = *argc;
+ av = *argv;
+ for (ix = 1; ix < ac; ix++) {
+ if (strcmp(av[ix], "-args_file") == 0) {
+ /* ... no; we need to expand arguments from
+ file into argument list */
+ goto build_new_argv;
+ }
+ if (strcmp(av[ix], "-extra") == 0) {
+ break;
+ }
+ }
+
+ /* ... yes; we can use argv as it is. */
+ return;
+ }
+
+ build_new_argv:
+
+ save_arg(&ab, (*argv)[0]);
+
+ vix = 0;
+ while (avv[vix].argv) {
+ ac = avv[vix].argc;
+ av = avv[vix].argv;
+
+ ix = 0;
+ while (ix < ac && av[ix]) {
+ if (strcmp(av[ix], "-args_file") == 0) {
+ if (++ix == ac)
+ usage("-args_file");
+ get_file_args(av[ix++], &ab, &xab);
+ }
+ else {
+ if (strcmp(av[ix], "-extra") == 0) {
+ ix++;
+ while (ix < ac && av[ix])
+ save_arg(&xab, av[ix++]);
+ break;
+ }
+ save_arg(&ab, av[ix++]);
+ }
+ }
+
+ vix++;
+ }
+
+ vix = 0;
+ while (avv[vix].argv) {
+ if (avv[vix].argc == INT_MAX) /* not command line */
+ efree(avv[vix].argv);
+ vix++;
+ }
+
+ if (xab.argc) {
+ save_arg(&ab, "-extra");
+ for (ix = 0; ix < xab.argc; ix++)
+ save_arg(&ab, xab.argv[ix]);
+ efree(xab.argv);
+ }
+
+ save_arg(&ab, NULL);
+ trim_argv_buf(&ab);
+ *argv = ab.argv;
+ *argc = ab.argc - 1;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ if (*s == ' ' || *s == '"') {
+ mustQuote = YES;
+ n++;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*s == '"') {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+
+#endif
diff --git a/erts/etc/common/escript.c b/erts/etc/common/escript.c
new file mode 100644
index 0000000000..ab37d4af46
--- /dev/null
+++ b/erts/etc/common/escript.c
@@ -0,0 +1,697 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2007-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: escript front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+#endif
+
+#include <ctype.h>
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#define BOOL int
+#define FALSE 0
+#define TRUE 1
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define DIRSEPSTR "\\"
+# define PATHSEPSTR ";"
+# define PMAX MAX_PATH
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define DIRSEPSTR "/"
+# define PATHSEPSTR ":"
+# define PMAX PATH_MAX
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define UNSHIFT3(s, t, u) UNSHIFT(u); UNSHIFT(t); UNSHIFT(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+#define LINEBUFSZ 1024
+
+/*
+ * Local functions.
+ */
+
+static void error(char* format, ...);
+static char* emalloc(size_t size);
+static void efree(void *p);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ efree(value);
+ value = emalloc(size);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ efree(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ efree(value);
+#endif
+}
+/*
+ * Find absolute path to this program
+ */
+
+static char *
+find_prog(char *origpath)
+{
+ char relpath[PMAX];
+ char abspath[PMAX];
+
+ strcpy(relpath, origpath);
+
+ if (strstr(relpath, DIRSEPSTR) == NULL) {
+ /* Just a base name */
+ char *envpath;
+
+ envpath = get_env("PATH");
+ if (envpath) {
+ /* Try to find the executable in the path */
+ char dir[PMAX];
+ char *beg = envpath;
+ char *end;
+ int sz;
+
+#ifdef __WIN32__
+ HANDLE dir_handle; /* Handle to directory. */
+ char wildcard[PMAX]; /* Wildcard to search for. */
+ WIN32_FIND_DATA find_data; /* Data found by FindFirstFile() or FindNext(). */
+#else
+ DIR *dp; /* Pointer to directory structure. */
+ struct dirent* dirp; /* Pointer to directory entry. */
+#endif /* __WIN32__ */
+
+ BOOL look_for_sep = TRUE;
+
+ while (look_for_sep) {
+ end = strstr(beg, PATHSEPSTR);
+ if (end != NULL) {
+ sz = end - beg;
+ strncpy(dir, beg, sz);
+ dir[sz] = '\0';
+ } else {
+ sz = strlen(beg);
+ strcpy(dir, beg);
+ look_for_sep = FALSE;
+ }
+ beg = end + 1;
+
+#ifdef __WIN32__
+ strcpy(wildcard, dir);
+ strcat(wildcard, DIRSEPSTR);
+ strcat(wildcard, relpath); /* basename */
+ dir_handle = FindFirstFile(wildcard, &find_data);
+ if (dir_handle == INVALID_HANDLE_VALUE) {
+ /* Try next directory in path */
+ continue;
+ } else {
+ /* Wow we found the executable. */
+ strcpy(abspath, wildcard);
+ FindClose(dir_handle);
+ return strsave(abspath);
+ }
+#else
+ dp = opendir(dir);
+ if (dp != NULL) {
+ while (TRUE) {
+ dirp = readdir(dp);
+ if (dirp == NULL) {
+ closedir(dp);
+ /* Try next directory in path */
+ break;
+ }
+
+ if (strcmp(origpath, dirp->d_name) == 0) {
+ /* Wow we found the executable. */
+ strcpy(abspath, dir);
+ strcat(abspath, DIRSEPSTR);
+ strcat(abspath, dirp->d_name);
+ closedir(dp);
+ return strsave(abspath);
+ }
+ }
+ }
+#endif /* __WIN32__ */
+ }
+ }
+ }
+
+ {
+#ifdef __WIN32__
+ DWORD size;
+ char *absrest;
+ size = GetFullPathName(relpath, PMAX, abspath, &absrest);
+ if ((size == 0) || (size > PMAX)) {
+
+#else
+ if (!realpath(relpath, abspath)) {
+#endif /* __WIN32__ */
+ /* Cannot determine absolute path to escript. Try the relative. */
+ return strsave(relpath);
+ } else {
+ return strsave(abspath);
+ }
+ }
+}
+
+static void
+append_shebang_args(char* scriptname)
+{
+ /* Open script file */
+ FILE* fd = fopen (scriptname,"r");
+
+ if (fd != NULL) {
+ /* Read first line in script file */
+ static char linebuf[LINEBUFSZ];
+ char* ptr = fgets(linebuf, LINEBUFSZ, fd);
+
+ if (ptr != NULL && linebuf[0] == '#' && linebuf[1] == '!') {
+ /* Try to find args on second or third line */
+ ptr = fgets(linebuf, LINEBUFSZ, fd);
+ if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') {
+ /* Use second line */
+ } else {
+ /* Try third line */
+ ptr = fgets(linebuf, LINEBUFSZ, fd);
+ if (ptr != NULL && linebuf[0] == '%' && linebuf[1] == '%' && linebuf[2] == '!') {
+ /* Use third line */
+ } else {
+ /* Do not use any line */
+ ptr = NULL;
+ }
+ }
+
+ if (ptr != NULL) {
+ /* Use entire line but the leading chars */
+ char* beg = linebuf + 3;
+ char* end;
+ BOOL newline = FALSE;
+
+ /* Push all args */
+ while(beg && !newline) {
+ /* Skip leading spaces */
+ while (beg && beg[0] == ' ') {
+ beg++;
+ }
+
+ /* Find end of arg */
+ end = beg;
+ while (end && end[0] != ' ') {
+ if (end[0] == '\n') {
+ newline = TRUE;
+ end[0]= '\0';
+ break;
+ } else {
+ end++;
+ }
+ }
+
+ /* Empty arg */
+ if (beg == end) {
+ break;
+ }
+ end[0]= '\0';
+ PUSH(beg);
+ beg = end + 1;
+ }
+ }
+ }
+ fclose(fd);
+ } else {
+ error("Failed to open file: %s", scriptname);
+ }
+}
+
+int
+main(int argc, char** argv)
+{
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ char* env;
+ char* basename;
+ char* absname;
+ char scriptname[PMAX];
+ char** last_opt;
+ char** first_opt;
+
+ emulator = env = get_env("ESCRIPT_EMULATOR");
+ if (emulator == NULL) {
+ emulator = get_default_emulator(argv[0]);
+ }
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy addition of commands in the beginning.
+ */
+
+ eargv_size = argc*4+1000;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ free_env_val(env);
+
+ /*
+ * Push initial arguments.
+ */
+
+ PUSH("+B");
+ PUSH2("-boot", "start_clean");
+ PUSH("-noshell");
+
+ /* Determine basename of the executable */
+ for (basename = argv[0]+strlen(argv[0]);
+ basename > argv[0] && !(IS_DIRSEP(basename[-1]));
+ --basename)
+ ;
+
+ first_opt = argv;
+ last_opt = argv;
+
+#ifdef __WIN32__
+ if (_stricmp(basename, "escript.exe") == 0) {
+#else
+ if (strcmp(basename, "escript") == 0) {
+#endif
+ /*
+ * Push all options (without the hyphen) before the script name.
+ */
+
+ while (argc > 1 && argv[1][0] == '-') {
+ PUSH(argv[1]+1);
+ argc--;
+ argv++;
+ last_opt = argv;
+ }
+
+ if (argc <= 1) {
+ error("Missing filename\n");
+ }
+ strcpy(scriptname, argv[1]);
+ argc--;
+ argv++;
+ } else {
+#ifdef __WIN32__
+ int len;
+#endif
+ absname = find_prog(argv[0]);
+ strcpy(scriptname, absname);
+ efree(absname);
+#ifdef __WIN32__
+ len = strlen(scriptname);
+ if (len >= 4 && _stricmp(scriptname+len-4, ".exe") == 0) {
+ scriptname[len-4] = '\0';
+ }
+#endif
+ strcat(scriptname, ".escript");
+ }
+
+ /*
+ * Read options from the %%! row in the script and add them as args
+ */
+
+ append_shebang_args(scriptname);
+
+ /*
+ * Push the script name and everything following it as extra arguments.
+ */
+
+ PUSH3("-run", "escript", "start");
+
+ /*
+ * Push all options (without the hyphen) before the script name.
+ */
+
+ while (first_opt != last_opt) {
+ PUSH(first_opt[1]+1);
+ first_opt++;
+ }
+
+ PUSH("-extra");
+ PUSH(scriptname);
+ while (argc > 1) {
+ PUSH(argv[1]);
+ argc--, argv++;
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = emalloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we was executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "escript: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "escript: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static void
+efree(void *p)
+{
+ free(p);
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = FALSE;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = TRUE;
+ continue;
+ case '"':
+ mustQuote = TRUE;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */
diff --git a/erts/etc/common/heart.c b/erts/etc/common/heart.c
new file mode 100644
index 0000000000..4f738947b7
--- /dev/null
+++ b/erts/etc/common/heart.c
@@ -0,0 +1,1142 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/**
+ *
+ * File: heart.c
+ * Purpose: Portprogram for supervision of the Erlang emulator.
+ *
+ * Synopsis: heart
+ *
+ * SPAWNING FROM ERLANG
+ *
+ * This program is started from Erlang as follows,
+ *
+ * Port = open_port({spawn, 'heart'}, [{packet, 2}]),
+ *
+ * ROLE OF THIS PORT PROGRAM
+ *
+ * This program is started by the Erlang emulator. It communicates
+ * with the emulator through file descriptor 0 (standard input).
+ *
+ * MESSAGE FORMAT
+ *
+ * All messages have the following format (a value in parentheses
+ * indicate field length in bytes),
+ *
+ * {Length(2), Operation(1)}
+ *
+ * START ACK
+ *
+ * When this program has started it sends an START ACK message to Erlang.
+ *
+ * HEART_BEATING
+ *
+ * This program expects a heart beat messages. If it does not receive a
+ * heart beat message from Erlang within heart_beat_timeout seconds, it
+ * reboots the system. The variable heart_beat_timeout is exported (so
+ * that it can be set from the shell in VxWorks, as is the variable
+ * heart_beat_report_delay). When using Solaris, the system is rebooted
+ * by executing the command stored in the environment variable
+ * HEART_COMMAND.
+ *
+ * BLOCKING DESCRIPTORS
+ *
+ * All file descriptors in this program are blocking. This can lead
+ * to deadlocks. The emulator reads and writes are blocking.
+ *
+ * STANDARD INPUT, OUTPUT AND ERROR
+ *
+ * This program communicates with Erlang through the standard
+ * input and output file descriptors (0 and 1). These descriptors
+ * (and the standard error descriptor 2) must NOT be closed
+ * explicitely by this program at termination (in UNIX it is
+ * taken care of by the operating system itself; in VxWorks
+ * it is taken care of by the spawn driver part of the Emulator).
+ *
+ * END OF FILE
+ *
+ * If a read from a file descriptor returns zero (0), it means
+ * that there is no process at the other end of the connection
+ * having the connection open for writing (end-of-file).
+ *
+ * HARDWARE WATCHDOG
+ *
+ * When used with VxWorks(with CPU40), the hardware
+ * watchdog is enabled, making sure that the system reboots
+ * even if the heart port program malfunctions or the system
+ * is completely overloaded.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef __WIN32__
+#include <windows.h>
+#include <io.h>
+#include <fcntl.h>
+#include <process.h>
+#endif
+#ifdef VXWORKS
+#include "sys.h"
+#endif
+
+/*
+ * Implement time correction using times() call even on Linuxes
+ * that can simulate gethrtime with clock_gettime, no use implementing
+ * a phony gethrtime in this file as the time questions are so infrequent.
+ */
+#if defined(CORRET_USING_TIMES) || defined(GETHRTIME_WITH_CLOCK_GETTIME)
+# define HEART_CORRECT_USING_TIMES 1
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+
+#include <stdarg.h>
+
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#ifdef VXWORKS
+# include <vxWorks.h>
+# include <ioLib.h>
+# include <selectLib.h>
+# include <netinet/in.h>
+# include <rebootLib.h>
+# include <sysLib.h>
+# include <taskLib.h>
+# include <wdLib.h>
+# include <taskHookLib.h>
+# include <selectLib.h>
+#endif
+#if !defined(__WIN32__) && !defined(VXWORKS)
+# include <sys/types.h>
+# include <netinet/in.h>
+# include <sys/time.h>
+# include <unistd.h>
+# include <signal.h>
+# if defined(HEART_CORRECT_USING_TIMES)
+# include <sys/times.h>
+# include <limits.h>
+# endif
+#endif
+
+#define HEART_COMMAND_ENV "HEART_COMMAND"
+
+#define MSG_HDR_SIZE 2
+#define MSG_HDR_PLUS_OP_SIZE 3
+#define MSG_BODY_SIZE 2048
+#define MSG_TOTAL_SIZE 2050
+
+unsigned char cmd[MSG_BODY_SIZE];
+
+struct msg {
+ unsigned short len;
+ unsigned char op;
+ unsigned char fill[MSG_BODY_SIZE]; /* one too many */
+};
+
+/* operations */
+#define HEART_ACK 1
+#define HEART_BEAT 2
+#define SHUT_DOWN 3
+#define SET_CMD 4
+#define CLEAR_CMD 5
+#define GET_CMD 6
+#define HEART_CMD 7
+
+
+/* Maybe interesting to change */
+
+/* Times in seconds */
+#define HEART_BEAT_BOOT_DELAY 60 /* 1 minute */
+#define SELECT_TIMEOUT 5 /* Every 5 seconds we reset the
+ watchdog timer */
+
+/* heart_beat_timeout is the maximum gap in seconds between two
+ consecutive heart beat messages from Erlang, and HEART_BEAT_BOOT_DELAY
+ is the the extra delay that wd_keeper allows for, to give heart a
+ chance to reboot in the "normal" way before the hardware watchdog
+ enters the scene. heart_beat_report_delay is the time allowed for reporting
+ before rebooting under VxWorks. */
+
+int heart_beat_timeout = 60;
+int heart_beat_report_delay = 30;
+int heart_beat_boot_delay = HEART_BEAT_BOOT_DELAY;
+/* All current platforms have a process identifier that
+ fits in an unsigned long and where 0 is an impossible or invalid value */
+unsigned long heart_beat_kill_pid = 0;
+
+#define VW_WD_TIMEOUT (heart_beat_timeout+heart_beat_report_delay+heart_beat_boot_delay)
+#define SOL_WD_TIMEOUT (heart_beat_timeout+heart_beat_boot_delay)
+
+/* reasons for reboot */
+#define R_TIMEOUT 1
+#define R_CLOSED 2
+#define R_ERROR 3
+#define R_SHUT_DOWN 4
+
+
+/* macros */
+
+#define NULLFDS ((fd_set *) NULL)
+#define NULLTV ((struct timeval *) NULL)
+
+/* prototypes */
+
+static int message_loop(int,int);
+static void do_terminate(int);
+static int notify_ack(int);
+static int heart_cmd_reply(int, char *);
+static int write_message(int, struct msg *);
+static int read_message(int, struct msg *);
+static int read_skip(int, char *, int, int);
+static int read_fill(int, char *, int);
+static void print_error(const char *,...);
+static void debugf(const char *,...);
+static void init_timestamp(void);
+static time_t timestamp(time_t *);
+
+#ifdef __WIN32__
+static BOOL enable_privilege(void);
+static BOOL do_shutdown(int);
+static void print_last_error(void);
+static HANDLE start_reader_thread(void);
+static DWORD WINAPI reader(LPVOID);
+static int test_win95(void);
+#define read _read
+#define write _write
+#endif
+
+/* static variables */
+
+static char program_name[256];
+static int erlin_fd = 0, erlout_fd = 1; /* std in and out */
+static int debug_on = 0;
+#ifdef __WIN32__
+static HANDLE hreader_thread;
+static HANDLE hevent_dataready;
+static struct msg m, *mp = &m;
+static int tlen; /* total message length */
+static FILE* conh;
+
+#endif
+
+static int
+is_env_set(char *key)
+{
+#ifdef __WIN32__
+ char buf[1];
+ DWORD sz = (DWORD) sizeof(buf);
+ SetLastError(0);
+ sz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) buf, sz);
+ return sz || GetLastError() != ERROR_ENVVAR_NOT_FOUND;
+#else
+ return getenv(key) != NULL;
+#endif
+}
+
+static char *
+get_env(char *key)
+{
+#ifdef __WIN32__
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ free(value);
+ value = malloc(size);
+ if (!value) {
+ print_error("Failed to allocate memory. Terminating...");
+ exit(1);
+ }
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+#else
+ return getenv(key);
+#endif
+}
+
+static void
+free_env_val(char *value)
+{
+#ifdef __WIN32__
+ if (value)
+ free(value);
+#endif
+}
+
+/*
+ * main
+ */
+static void get_arguments(int argc, char** argv) {
+ int i = 1;
+ int h;
+ int w;
+ unsigned long p;
+
+ while (i < argc) {
+ switch (argv[i][0]) {
+ case '-':
+ switch (argv[i][1]) {
+ case 'h':
+ if (strcmp(argv[i], "-ht") == 0)
+ if (sscanf(argv[i+1],"%i",&h) ==1)
+ if ((h > 10) && (h <= 65535)) {
+ heart_beat_timeout = h;
+ fprintf(stderr,"heart_beat_timeout = %d\n",h);
+ i++;
+ }
+ break;
+ case 'w':
+ if (strcmp(argv[i], "-wt") == 0)
+ if (sscanf(argv[i+1],"%i",&w) ==1)
+ if ((w > 10) && (w <= 65535)) {
+ heart_beat_boot_delay = w;
+ fprintf(stderr,"heart_beat_boot_delay = %d\n",w);
+ i++;
+ }
+ break;
+ case 'p':
+ if (strcmp(argv[i], "-pid") == 0)
+ if (sscanf(argv[i+1],"%lu",&p) ==1){
+ heart_beat_kill_pid = p;
+ fprintf(stderr,"heart_beat_kill_pid = %lu\n",p);
+ i++;
+ }
+ break;
+#ifdef __WIN32__
+ case 's':
+ if (strcmp(argv[i], "-shutdown") == 0){
+ do_shutdown(1);
+ exit(0);
+ }
+ break;
+#endif
+ default:
+ ;
+ }
+ break;
+ default:
+ ;
+ }
+ i++;
+ }
+ debugf("arguments -ht %d -wt %d -pid %lu\n",h,w,p);
+}
+
+int
+main(int argc, char **argv)
+{
+ get_arguments(argc,argv);
+ if (is_env_set("HEART_DEBUG"))
+ debug_on=1;
+#ifdef __WIN32__
+ if (debug_on) {
+ if(!is_env_set("ERLSRV_SERVICE_NAME")) {
+ /* this redirects stderr to a separate console (for debugging purposes)*/
+ erlin_fd = _dup(0);
+ erlout_fd = _dup(1);
+ AllocConsole();
+ conh = freopen("CONOUT$","w",stderr);
+ if (conh != NULL)
+ fprintf(conh,"console alloced\n");
+ }
+ debugf("stderr\n");
+ }
+ _setmode(erlin_fd,_O_BINARY);
+ _setmode(erlout_fd,_O_BINARY);
+#endif
+ strcpy(program_name, argv[0]);
+ notify_ack(erlout_fd);
+ cmd[0] = '\0';
+ do_terminate(message_loop(erlin_fd,erlout_fd));
+ return 0;
+}
+
+/*
+ * message loop
+ */
+static int
+message_loop(erlin_fd, erlout_fd)
+ int erlin_fd, erlout_fd;
+{
+ int i;
+ time_t now, last_received;
+#ifdef __WIN32__
+ DWORD wresult;
+#else
+ fd_set read_fds;
+ int max_fd;
+ struct timeval timeout;
+ int tlen; /* total message length */
+ struct msg m, *mp = &m;
+#endif
+
+ init_timestamp();
+ timestamp(&now);
+ last_received = now;
+#ifdef __WIN32__
+ hevent_dataready = CreateEvent(NULL,FALSE,FALSE,NULL);
+ hreader_thread = start_reader_thread();
+#else
+ max_fd = erlin_fd;
+#endif
+
+ while (1) {
+#ifdef __WIN32__
+ wresult = WaitForSingleObject(hevent_dataready,SELECT_TIMEOUT*1000+ 2);
+ if (wresult == WAIT_FAILED) {
+ print_last_error();
+ return R_ERROR;
+ }
+
+ if (wresult == WAIT_TIMEOUT) {
+ debugf("wait timed out\n");
+ i = 0;
+ } else {
+ debugf("wait ok\n");
+ i = 1;
+ }
+#else
+ FD_ZERO(&read_fds); /* ZERO on each turn */
+ FD_SET(erlin_fd, &read_fds);
+ timeout.tv_sec = SELECT_TIMEOUT; /* On Linux timeout is modified
+ by select */
+ timeout.tv_usec = 0;
+ if ((i = select(max_fd + 1, &read_fds, NULLFDS, NULLFDS, &timeout)) < 0) {
+ print_error("error in select.");
+ return R_ERROR;
+ }
+#endif
+ /*
+ * Maybe heart beat time-out
+ * If we havn't got anything in 60 seconds we reboot, even if we may
+ * have got something in the last 5 seconds. We may end up here if
+ * the system clock is adjusted with more than 55 seconds, but we
+ * regard this as en error and reboot anyway.
+ */
+ timestamp(&now);
+ if (now > last_received + heart_beat_timeout) {
+ print_error("heart-beat time-out.");
+ return R_TIMEOUT;
+ }
+ /*
+ * Do not check fd-bits if select timeout
+ */
+ if (i == 0) {
+ continue;
+ }
+ /*
+ * Message from ERLANG
+ */
+#ifdef __WIN32__
+ if (wresult == WAIT_OBJECT_0) {
+ if (tlen < 0) {
+#else
+ if (FD_ISSET(erlin_fd, &read_fds)) {
+ if ((tlen = read_message(erlin_fd, mp)) < 0) {
+#endif
+ print_error("error in read_message.");
+ return R_ERROR;
+ }
+ if ((tlen > MSG_HDR_SIZE) && (tlen <= MSG_TOTAL_SIZE)) {
+ switch (mp->op) {
+ case HEART_BEAT:
+ timestamp(&last_received);
+#ifdef USE_WATCHDOG
+ /* reset the hardware watchdog timer */
+ wd_reset();
+#endif
+ break;
+ case SHUT_DOWN:
+ return R_SHUT_DOWN;
+ case SET_CMD:
+ /* override the HEART_COMMAND_ENV command */
+ memcpy(&cmd, &(mp->fill[0]),
+ tlen-MSG_HDR_PLUS_OP_SIZE);
+ cmd[tlen-MSG_HDR_PLUS_OP_SIZE] = '\0';
+ notify_ack(erlout_fd);
+ break;
+ case CLEAR_CMD:
+ /* use the HEART_COMMAND_ENV command */
+ cmd[0] = '\0';
+ notify_ack(erlout_fd);
+ break;
+ case GET_CMD:
+ /* send back command string */
+ {
+ char *env = NULL;
+ char *command
+ = (cmd[0]
+ ? (char *)cmd
+ : (env = get_env(HEART_COMMAND_ENV)));
+ /* Not set and not in env return "" */
+ if (!command) command = "";
+ heart_cmd_reply(erlout_fd, command);
+ free_env_val(env);
+ }
+ break;
+ default:
+ /* ignore all other messages */
+ break;
+ }
+ } else if (tlen == 0) {
+ /* Erlang has closed its end */
+ print_error("Erlang has closed.");
+ return R_CLOSED;
+ }
+ /* Junk erroneous messages */
+ }
+ }
+}
+
+#if defined(__WIN32__)
+static void
+kill_old_erlang(void){
+ HANDLE erlh;
+ DWORD exit_code;
+ if(heart_beat_kill_pid != 0){
+ if((erlh = OpenProcess(PROCESS_TERMINATE |
+ SYNCHRONIZE |
+ PROCESS_QUERY_INFORMATION ,
+ FALSE,
+ (DWORD) heart_beat_kill_pid)) == NULL){
+ return;
+ }
+ if(!TerminateProcess(erlh, 1)){
+ CloseHandle(erlh);
+ return;
+ }
+ if(WaitForSingleObject(erlh,5000) != WAIT_OBJECT_0){
+ print_error("Old process did not die, "
+ "WaitForSingleObject timed out.");
+ CloseHandle(erlh);
+ return;
+ }
+ if(!GetExitCodeProcess(erlh, &exit_code)){
+ print_error("Old process did not die, "
+ "GetExitCodeProcess failed.");
+ }
+ CloseHandle(erlh);
+ }
+}
+#elif !defined(VXWORKS)
+/* Unix eh? */
+static void
+kill_old_erlang(void){
+ pid_t pid;
+ int i;
+ int res;
+ if(heart_beat_kill_pid != 0){
+ pid = (pid_t) heart_beat_kill_pid;
+ res = kill(pid,SIGKILL);
+ for(i=0; i < 5 && res == 0; ++i){
+ sleep(1);
+ res = kill(pid,SIGKILL);
+ }
+ if(errno != ESRCH){
+ print_error("Unable to kill old process, "
+ "kill failed (tried multiple times).");
+ }
+ }
+}
+#endif /* Not on VxWorks */
+
+#ifdef __WIN32__
+void win_system(char *command)
+{
+ char *comspec;
+ char * cmdbuff;
+ char * extra = " /C ";
+ char *env;
+ STARTUPINFO start;
+ SECURITY_ATTRIBUTES attr;
+ PROCESS_INFORMATION info;
+
+ if (!debug_on || test_win95()) {
+ system(command);
+ return;
+ }
+ comspec = env = get_env("COMSPEC");
+ if (!comspec)
+ comspec = "CMD.EXE";
+ cmdbuff = malloc(strlen(command) + strlen(comspec) + strlen(extra) + 1);
+ if (!cmdbuff) {
+ print_error("Failed to allocate memory. Terminating...");
+ exit(1);
+ }
+ strcpy(cmdbuff, comspec);
+ strcat(cmdbuff, extra);
+ strcat(cmdbuff, command);
+ free_env_val(env);
+
+ debugf("running \"%s\"\r\n", cmdbuff);
+
+ memset (&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+ start.dwFlags = STARTF_USESHOWWINDOW | STARTF_USESTDHANDLES;
+ start.wShowWindow = SW_HIDE;
+ start.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start.hStdOutput = GetStdHandle(STD_ERROR_HANDLE);
+ start.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ attr.nLength = sizeof(attr);
+ attr.lpSecurityDescriptor = NULL;
+ attr.bInheritHandle = TRUE;
+
+ fflush(stderr);
+
+ if (!CreateProcess(NULL,
+ cmdbuff,
+ &attr,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &start,
+ &info)) {
+ debugf("Could not create process for the command %s.\r\n", cmdbuff);
+ }
+ WaitForSingleObject(info.hProcess,INFINITE);
+ free(cmdbuff);
+}
+#endif /* defined(__WIN32__) */
+
+/*
+ * do_terminate
+ */
+static void
+do_terminate(reason)
+ int reason;
+{
+ /*
+ When we get here, we have HEART_BEAT_BOOT_DELAY secs to finish
+ (plus heart_beat_report_delay if under VxWorks), so we don't need
+ to call wd_reset().
+ */
+
+ switch (reason) {
+ case R_SHUT_DOWN:
+ break;
+ case R_TIMEOUT:
+ case R_ERROR:
+ case R_CLOSED:
+ default:
+#if defined(__WIN32__) /* Not VxWorks */
+ {
+ if(!cmd[0]) {
+ char *command = get_env(HEART_COMMAND_ENV);
+ if(!command)
+ print_error("Would reboot. Terminating.");
+ else {
+ kill_old_erlang();
+ /* High prio combined with system() works badly indeed... */
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+ win_system(command);
+ print_error("Executed \"%s\". Terminating.",command);
+ }
+ free_env_val(command);
+ }
+ else {
+ kill_old_erlang();
+ /* High prio combined with system() works badly indeed... */
+ SetPriorityClass(GetCurrentProcess(), NORMAL_PRIORITY_CLASS);
+ win_system(&cmd[0]);
+ print_error("Executed \"%s\". Terminating.",cmd);
+ }
+ }
+
+#else
+ {
+ if(!cmd[0]) {
+ char *command = get_env(HEART_COMMAND_ENV);
+ if(!command)
+ print_error("Would reboot. Terminating.");
+ else {
+ kill_old_erlang();
+ system(command);
+ print_error("Executed \"%s\". Terminating.",command);
+ }
+ free_env_val(command);
+ }
+ else {
+ kill_old_erlang();
+ system((char*)&cmd[0]);
+ print_error("Executed \"%s\". Terminating.",cmd);
+ }
+ }
+ break;
+#endif
+ } /* switch(reason) */
+}
+
+/*
+ * notify_ack
+ *
+ * Sends an HEART_ACK.
+ */
+static int
+notify_ack(fd)
+ int fd;
+{
+ struct msg m;
+
+ m.op = HEART_ACK;
+ m.len = htons(1);
+ return write_message(fd, &m);
+}
+
+
+/*
+ * send back current command
+ *
+ * Sends an HEART_CMD.
+ */
+static int
+heart_cmd_reply(int fd, char *s)
+{
+ struct msg m;
+ int len = strlen(s) + 1; /* Include \0 */
+
+ /* FIXME if s >= MSG_BODY_SIZE error */
+
+ m.op = HEART_CMD;
+ m.len = htons(len + 2); /* Include Op */
+ strcpy((char*)m.fill, s);
+
+ return write_message(fd, &m);
+}
+
+
+/*
+ * write_message
+ *
+ * Writes a message to a blocking file descriptor. Returns the total
+ * size of the message written (always > 0), or -1 if error.
+ *
+ * A message which is too short or too long, is not written. The return
+ * value is then MSG_HDR_SIZE (2), as if the message had been written.
+ * Is this really necessary? Can't we assume that the length is ok?
+ * FIXME.
+ */
+static int
+write_message(fd, mp)
+ int fd;
+ struct msg *mp;
+{
+ int len;
+ char* tmp;
+
+ tmp = (char*) &(mp->len);
+ len = (*tmp * 256) + *(tmp+1);
+ if ((len == 0) || (len > MSG_BODY_SIZE)) {
+ return MSG_HDR_SIZE;
+ } /* cc68k wants (char *) */
+ if (write(fd, (char *) mp, len + MSG_HDR_SIZE) != len + MSG_HDR_SIZE) {
+ return -1;
+ }
+ return len + MSG_HDR_SIZE;
+}
+
+/*
+ * read_message
+ *
+ * Reads a message from a blocking file descriptor. Returns the total
+ * size of the message read (> 0), 0 if eof, and < 0 if error.
+ *
+ * Note: The return value MSG_HDR_SIZE means a message of total size
+ * MSG_HDR_SIZE, i.e. without even an operation field.
+ *
+ * If the size of the message is larger than MSG_TOTAL_SIZE, the total
+ * number of bytes read is returned, but the buffer contains a truncated
+ * message.
+ */
+static int
+read_message(fd, mp)
+ int fd;
+ struct msg *mp;
+{
+ int rlen, i;
+ unsigned char* tmp;
+
+ if ((i = read_fill(fd, (char *) mp, MSG_HDR_SIZE)) != MSG_HDR_SIZE) {
+ /* < 0 is an error; = 0 is eof */
+ return i;
+ }
+
+ tmp = (unsigned char*) &(mp->len);
+ rlen = (*tmp * 256) + *(tmp+1);
+ if (rlen == 0) {
+ return MSG_HDR_SIZE;
+ }
+ if (rlen > MSG_BODY_SIZE) {
+ if ((i = read_skip(fd, (((char *) mp) + MSG_HDR_SIZE),
+ MSG_BODY_SIZE, rlen)) != rlen) {
+ return i;
+ } else {
+ return rlen + MSG_HDR_SIZE;
+ }
+ }
+ if ((i = read_fill(fd, ((char *) mp + MSG_HDR_SIZE), rlen)) != rlen) {
+ return i;
+ }
+ return rlen + MSG_HDR_SIZE;
+}
+
+/*
+ * read_fill
+ *
+ * Reads len bytes into buf from a blocking fd. Returns total number of
+ * bytes read (i.e. len) , 0 if eof, or < 0 if error. len must be > 0.
+ */
+static int
+read_fill(fd, buf, len)
+ int fd, len;
+ char *buf;
+{
+ int i, got = 0;
+
+ do {
+ if ((i = read(fd, buf + got, len - got)) <= 0) {
+ return i;
+ }
+ got += i;
+ } while (got < len);
+ return len;
+}
+
+/*
+ * read_skip
+ *
+ * Reads len bytes into buf from a blocking fd, but puts not more than
+ * maxlen bytes in buf. Returns total number of bytes read ( > 0),
+ * 0 if eof, or < 0 if error. len > maxlen > 0 must hold.
+ */
+static int
+read_skip(fd, buf, maxlen, len)
+ int fd, maxlen, len;
+ char *buf;
+{
+ int i, got = 0;
+ char c;
+
+ if ((i = read_fill(fd, buf, maxlen)) <= 0) {
+ return i;
+ }
+ do {
+ if ((i = read(fd, &c, 1)) <= 0) {
+ return i;
+ }
+ got += i;
+ } while (got < len - maxlen);
+ return len;
+}
+
+/*
+ * print_error
+ */
+static void
+print_error(const char *format,...)
+{
+ va_list args;
+ time_t now;
+ char *timestr;
+
+ va_start(args, format);
+ time(&now);
+ timestr = ctime(&now);
+ fprintf(stderr, "%s: %.*s: ", program_name, (int) strlen(timestr)-1, timestr);
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\r\n");
+}
+
+static void
+debugf(const char *format,...)
+{
+ va_list args;
+
+ if (!debug_on) return;
+ va_start(args, format);
+ fprintf(stderr, "Heart: ");
+ vfprintf(stderr, format, args);
+ va_end(args);
+ fprintf(stderr, "\r\n");
+}
+
+#ifdef __WIN32__
+void print_last_error() {
+ LPVOID lpMsgBuf;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* Default language */
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+
+ /* Display the string.*/
+ fprintf(stderr,"GetLastError:%s\n",lpMsgBuf);
+
+ /* Free the buffer. */
+ LocalFree( lpMsgBuf );
+}
+
+static int test_win95(void)
+{
+ OSVERSIONINFO osinfo;
+ osinfo.dwOSVersionInfoSize=sizeof(OSVERSIONINFO);
+ GetVersionEx(&osinfo);
+ if (osinfo.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS)
+ return 1;
+ else
+ return 0;
+}
+
+static BOOL enable_privilege() {
+ HANDLE ProcessHandle;
+ DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
+ HANDLE TokenHandle;
+ TOKEN_PRIVILEGES Tpriv;
+ LUID luid;
+ ProcessHandle = GetCurrentProcess();
+ OpenProcessToken(ProcessHandle, DesiredAccess, &TokenHandle);
+ LookupPrivilegeValue(0,SE_SHUTDOWN_NAME,&luid);
+ Tpriv.PrivilegeCount = 1;
+ Tpriv.Privileges[0].Luid = luid;
+ Tpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ return AdjustTokenPrivileges(TokenHandle,FALSE,&Tpriv,0,0,0);
+}
+
+static BOOL do_shutdown(int really_shutdown) {
+ if (test_win95()) {
+ if (ExitWindowsEx(EWX_REBOOT,0)) {
+ return TRUE;
+ } else {
+ print_last_error();
+ return FALSE;
+ }
+ } else {
+ enable_privilege();
+ if (really_shutdown) {
+ if (InitiateSystemShutdown(NULL,"shutdown by HEART",10,TRUE,TRUE))
+ return TRUE;
+ } else if (InitiateSystemShutdown(NULL,
+ "shutdown by HEART\n"
+ "will be interrupted",
+ 30,TRUE,TRUE)) {
+ AbortSystemShutdown(NULL);
+ return TRUE;
+ }
+ return FALSE;
+ }
+}
+
+DWORD WINAPI reader(LPVOID lpvParam) {
+
+ while (1) {
+ debugf("reader is reading\n");
+ tlen = read_message(erlin_fd, mp);
+ debugf("reader setting event\n");
+ SetEvent(hevent_dataready);
+ if(tlen == 0)
+ break;
+ }
+ return 0;
+}
+
+HANDLE start_reader_thread(void) {
+ DWORD tid;
+ HANDLE thandle;
+ if ((thandle = (HANDLE)
+ _beginthreadex(NULL,0,reader,NULL,0,&tid)) == NULL) {
+ print_last_error();
+ exit(1);
+ }
+ return thandle;
+}
+#endif
+
+#if defined(__WIN32__)
+
+# define TICK_MASK 0x7FFFFFFFUL
+
+void init_timestamp(void)
+{
+}
+
+time_t timestamp(time_t *res)
+{
+ static time_t extra = 0;
+ static unsigned last_ticks = 0;
+ unsigned this_ticks;
+ time_t r;
+
+ this_ticks = GetTickCount() & TICK_MASK;
+
+ if (this_ticks < last_ticks) {
+ extra += (time_t) ((TICK_MASK + 1) / 1000);
+ }
+
+ last_ticks = this_ticks;
+
+ r = ((time_t) (this_ticks / 1000)) + extra;
+
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#elif defined(VXWORKS)
+
+static WDOG_ID watchdog_id;
+static volatile unsigned elapsed;
+static WIND_TCB *this_task;
+/* A simple variable is enough to lock the time update, as the
+ watchdog is run at interrupt level and never preempted. */
+static volatile int lock_time;
+
+static void my_delete_hook(WIND_TCB *tcb)
+{
+ if (tcb == this_task) {
+ wdDelete(watchdog_id);
+ watchdog_id = NULL;
+ taskDeleteHookDelete((FUNCPTR) &my_delete_hook);
+ }
+}
+
+static void my_wd_routine(int count)
+{
+ if (watchdog_id != NULL) {
+ ++count;
+ if (!lock_time) {
+ elapsed += count;
+ count = 0;
+ }
+ wdStart(watchdog_id, sysClkRateGet(),
+ (FUNCPTR) &my_wd_routine, count);
+ }
+}
+
+void init_timestamp(void)
+{
+ lock_time = 0;
+ elapsed = 0;
+ watchdog_id = wdCreate();
+ this_task = (WIND_TCB *) taskIdSelf();
+ taskDeleteHookAdd((FUNCPTR) &my_delete_hook);
+ wdStart(watchdog_id, sysClkRateGet(),
+ (FUNCPTR) &my_wd_routine, 0);
+}
+
+time_t timestamp(time_t *res)
+{
+ time_t r;
+ ++lock_time;
+ r = (time_t) elapsed;
+ --lock_time;
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#elif defined(HAVE_GETHRTIME)
+
+void init_timestamp(void)
+{
+}
+
+time_t timestamp(time_t *res)
+{
+ hrtime_t ht = gethrtime();
+ time_t r = (time_t) (ht / 1000000000);
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#elif defined(HEART_CORRECT_USING_TIMES)
+
+# ifdef NO_SYSCONF
+# include <sys/param.h>
+# define TICKS_PER_SEC() HZ
+# else
+# define TICKS_PER_SEC() sysconf(_SC_CLK_TCK)
+# endif
+
+# define TICK_MASK 0x7FFFFFFFUL
+
+static unsigned tps;
+
+void init_timestamp(void)
+{
+ tps = TICKS_PER_SEC();
+}
+
+time_t timestamp(time_t *res)
+{
+ static time_t extra = 0;
+ static clock_t last_ticks = 0;
+ clock_t this_ticks;
+ struct tms dummy;
+ time_t r;
+
+ this_ticks = (times(&dummy) & TICK_MASK);
+
+ if (this_ticks < last_ticks) {
+ extra += (time_t) ((TICK_MASK + 1) / tps);
+ }
+
+ last_ticks = this_ticks;
+
+ r = ((time_t) (this_ticks / tps)) + extra;
+
+ if (res != NULL)
+ *res = r;
+ return r;
+}
+
+#else
+
+void init_timestamp(void)
+{
+}
+
+time_t timestamp(time_t *res)
+{
+ return time(res);
+}
+
+#endif
diff --git a/erts/etc/common/inet_gethost.c b/erts/etc/common/inet_gethost.c
new file mode 100644
index 0000000000..ff16ee02c4
--- /dev/null
+++ b/erts/etc/common/inet_gethost.c
@@ -0,0 +1,2757 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Erlang port program to do the name service lookup for the erlang
+ * distribution and inet part of the kernel.
+ * A pool of subprocess is kept, to which a pair of pipes is connected.
+ * The main process schedules requests among the different subprocesses
+ * (created with fork()), to be able to handle as many requests as possible
+ * simultaneously. The controlling erlang machine may request a "cancel",
+ * in which case the process may be killed and restarted when the need arises.
+ * The single numeric parameter to this program is the maximum port pool size,
+ * which is the size of the bookkeeping array.
+ *
+ * Windows:
+ * There is instead of a pool of processes a pool of threads.
+ * Communication is not done through pipes but via message queues between
+ * the threads. The only "pipes" involved are the ones used for communicating
+ * with Erlang.
+ * Important note:
+ * For unknown reasons, the combination of a thread doing blocking I/O on
+ * a named pipe at the same time as another thread tries to resolve a hostname
+ * may (with certain software configurations) block the gethostbyname call (!)
+ * For that reason, standard input (and standard output) should be opened
+ * in asynchronous mode (FILE_FLAG_OVERLAPPED), which has to be done by Erlang.
+ * A special flag to open_port is used to work around this behaviour in winsock
+ * and the threads doing read and write handle asynchronous I/O.
+ * The ReadFile and WriteFile calls try to cope with both types of I/O, why
+ * the code is not really as Microsoft describes "the right way to do it" in
+ * their documentation. Important to note is that **there is no supported way
+ * to retrieve the information if the HANDLE was opened with
+ * FILE_FLAG_OVERLAPPED from the HANDLE itself**.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifdef WIN32
+
+#define WIN32_LEAN_AND_MEAN
+#include <winsock2.h>
+#include <windows.h>
+#include <process.h>
+#include <stdio.h>
+#include <stdlib.h>
+
+/* These are not used even if they would exist which they should not */
+#undef HAVE_GETADDRINFO
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETHOSTBYNAME2
+#undef HAVE_GETNAMEINFO
+#undef HAVE_GETIPNODEBYADDR
+
+#else /* Unix */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#include <errno.h>
+#include <signal.h>
+
+#ifdef HAVE_SYS_TIME_H
+#include <sys/time.h>
+#else
+#include <time.h>
+#endif
+#include <sys/times.h>
+
+#ifndef RETSIGTYPE
+#define RETSIGTYPE void
+#endif
+
+/* To simplify #ifdef code further down - select only one to be defined...
+** Use them in pairs - if one is broken do not trust its mate.
+**/
+#if defined(HAVE_GETADDRINFO) && defined(HAVE_GETNAMEINFO)
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETIPNODEBYADDR
+#undef HAVE_GETHOSTBYNAME2
+#elif defined(HAVE_GETIPNODEBYNAME) && defined(HAVE_GETIPNODEBYADDR)
+#undef HAVE_GETADDRINFO
+#undef HAVE_GETNAMEINFO
+#undef HAVE_GETHOSTBYNAME2
+#else
+#undef HAVE_GETIPNODEBYNAME
+#undef HAVE_GETIPNODEBYADDR
+#undef HAVE_GETADDRINFO
+#undef HAVE_GETNAMEINFO
+#endif
+
+#endif /* !WIN32 */
+
+#define PACKET_BYTES 4
+#ifdef WIN32
+#define READ_PACKET_BYTES(X,Y,Z) read_int32((X),(Y),(Z))
+#else
+#define READ_PACKET_BYTES(X,Y) read_int32((X),(Y))
+#endif
+#define PUT_PACKET_BYTES(X,Y) put_int32((X),(Y))
+/* The serial numbers of the requests */
+typedef int SerialType;
+
+#define INVALID_SERIAL -1
+
+/* The operations performed by this program */
+typedef unsigned char OpType;
+
+#define OP_GETHOSTBYNAME 1
+#define OP_GETHOSTBYADDR 2
+#define OP_CANCEL_REQUEST 3
+#define OP_CONTROL 4
+
+/* The protocol (IPV4/IPV6) */
+typedef unsigned char ProtoType;
+
+#define PROTO_IPV4 1
+#define PROTO_IPV6 2
+
+/* OP_CONTROL */
+typedef unsigned char CtlType;
+#define SETOPT_DEBUG_LEVEL 0
+
+/* The unit of an IP address (0 == error, 4 == IPV4, 16 == IPV6) */
+typedef unsigned char UnitType;
+
+#define UNIT_ERROR 0
+#define UNIT_IPV4 4
+#define UNIT_IPV6 16
+
+/* And the byte type */
+typedef unsigned char AddrByte; /* Must be compatible with character
+ datatype */
+
+/*
+ * Marshalled format of request:
+ *{
+ * Serial: 32 bit big endian
+ * Op:8 bit [1,2,3]
+ * If op == 1 {
+ * Proto:8 bit [1,2]
+ * Str: Null terminated array of characters
+ * } Else if op == 2 {
+ * Proto:8 bit [1,2]
+ * If proto == 1 {
+ * B0..B3: 4 bytes, most significant first
+ * } Else (proto == 2) {
+ * B0..B15: 16 bytes, most significant first
+ * }
+ * }
+ * (No more if op == 3)
+ *}
+ * The request arrives as a packet, with 4 packet size bytes.
+ */
+
+/* The main process unpackes the marshalled message and sends the data
+ * to a suitable port process or, in the case of a close request, kills the
+ * suitable port process. There is also a que of requests linked together,
+ * for when all subrocesses are busy.
+ */
+
+typedef struct QueItem {
+ struct QueItem *next;
+ int req_size;
+ AddrByte request[1];
+} QueItem; /* Variable size due to request's variable size */
+
+QueItem *que_first;
+QueItem *que_last;
+
+#ifdef WIN32
+typedef struct mesq {
+ HANDLE data_present;
+ CRITICAL_SECTION crit;
+ int shutdown;
+ QueItem *first;
+ QueItem *last;
+} MesQ;
+
+MesQ *to_erlang;
+MesQ *from_erlang;
+#endif
+
+/*
+ * Marshalled format of reply:
+ *{
+ * Serial: 32 bit big endian
+ * Unit: 8 bit, same as h_length or 0 for error
+ * if unit == 0 {
+ * Str: Null terminated character string explaining the error
+ * } else {
+ * Naddr: 32 bit big endian
+ * if unit = 4 {
+ * (B0..B3)0..(B0..B3)Naddr-1: Naddr*4 bytes most significant first
+ * } else if unit == 16 {
+ * (B0..B15)0..(B0..B15)Naddr-1: Naddr*16 bytes most significant first
+ * }
+ * Nnames: 32 bit big endian >= 1
+ * Name0: Null terminated string of characters
+ * Alias[0]..Alias[Nnames - 2]: Nnames - 1 Null terminated strings of chars
+ * }
+ *}
+ * Four packet size bytes prepended (big endian)
+ */
+/* Internal error codes */
+#define ERRCODE_NOTSUP 1
+#define ERRCODE_HOST_NOT_FOUND 2
+#define ERRCODE_TRY_AGAIN 3
+#define ERRCODE_NO_RECOVERY 4
+#define ERRCODE_NO_DATA 5
+#define ERRCODE_NETDB_INTERNAL 7
+
+/*
+ * Each worker process is represented in the parent by the following struct
+ */
+
+typedef unsigned WorkerState;
+
+#define WORKER_EMPTY 0 /* No process created */
+#define WORKER_FREE 1 /* Living waiting process */
+#define WORKER_BUSY 2 /* Living busy process */
+#define WORKER_STALLED 3 /* Living cancelled process */
+
+/* The timeout when killing a child process in seconds*/
+#define CHILDWAIT_TMO 1
+/* The domainname size_limit */
+#define DOMAINNAME_MAX 258 /* 255 + Opcode + Protocol + Null termination */
+
+typedef struct {
+ WorkerState state;
+#ifdef WIN32
+ DWORD pid; /* 0 if unused */
+ MesQ *writeto; /* Message queues */
+ MesQ *readfrom;
+#else
+ pid_t pid; /* -1 if unused */
+ int writeto, readfrom; /* Pipes */
+#endif
+ SerialType serial;
+ AddrByte domain[DOMAINNAME_MAX];
+ QueItem *que_first;
+ QueItem *que_last;
+ int que_size;
+} Worker;
+
+int num_busy_workers;
+int num_free_workers;
+int num_stalled_workers;
+int max_workers;
+int greedy_threshold;
+Worker *busy_workers; /* Workers doing any job that someone really is
+ interested in */
+Worker *free_workers; /* Really free workers */
+Worker *stalled_workers; /* May still deliver answers which we will
+ discard */
+#define BEE_GREEDY() (num_busy_workers >= greedy_threshold)
+
+static char *program_name;
+
+static int debug_level;
+#ifdef WIN32
+static HANDLE debug_console_allocated = INVALID_HANDLE_VALUE;
+#endif
+
+#ifdef NODEBUG
+#define DEBUGF(L,P) /* Nothing */
+#else
+#define DEBUGF(Level,Printf) do { if (debug_level >= (Level)) \
+ debugf Printf;} while(0)
+#endif
+#define ALLOC(Size) my_malloc(Size)
+#define REALLOC(Old, Size) my_realloc((Old), (Size))
+#define FREE(Ptr) free(Ptr)
+
+#ifdef WIN32
+#define WAKEUP_WINSOCK() do { \
+ char dummy_buff[100]; \
+ gethostname(dummy_buff,99); \
+} while (0)
+#endif
+
+/* The internal prototypes */
+static char *format_address(int siz, AddrByte *addr);
+static void debugf(char *format, ...);
+static void warning(char *format, ...);
+static void fatal(char *format, ...);
+static void *my_malloc(size_t size);
+static void *my_realloc(void *old, size_t size);
+static int get_int32(AddrByte *buff);
+static void put_int32(AddrByte *buff, int value);
+static int create_worker(Worker *pworker, int save_que);
+static int map_netdb_error(int netdb_code);
+#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
+static int map_netdb_error_ai(int netdb_code);
+#endif
+static char *errcode_to_string(int errcode);
+static size_t build_error_reply(SerialType serial, int errnum,
+ AddrByte **preply,
+ size_t *preply_size);
+#ifdef HAVE_GETADDRINFO
+static size_t build_reply_ai(SerialType serial, int, struct addrinfo *,
+ AddrByte **preply, size_t *preply_size);
+#endif
+static size_t build_reply(SerialType serial, struct hostent *he,
+ AddrByte **preply, size_t *preply_size);
+static int read_request(AddrByte **buff, size_t *buff_size);
+static OpType get_op(AddrByte *buff);
+static AddrByte *get_op_addr(AddrByte *buff);
+static SerialType get_serial(AddrByte *buff);
+static ProtoType get_proto(AddrByte *buff);
+static CtlType get_ctl(AddrByte *buff);
+static AddrByte *get_data(AddrByte *buff);
+static int get_debug_level(AddrByte *buff);
+static int relay_reply(Worker *pw);
+static int ignore_reply(Worker *pw);
+static void init_workers(int max);
+static void kill_worker(Worker *pw);
+static Worker *pick_worker(void);
+static void kill_last_picked_worker(void);
+static void stall_worker(SerialType serial);
+static int handle_io_busy(int ndx);
+static int handle_io_free(int ndx);
+static int handle_io_stalled(int ndx);
+static void check_que(void);
+static void main_loop(void);
+static void usage(char *unknown);
+static void domaincopy(AddrByte *out,AddrByte *in);
+static int domaineq(AddrByte *d1, AddrByte *d2);
+static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff);
+static Worker *pick_worker_greedy(AddrByte *domainbuff);
+static void restart_worker(Worker *w);
+static void start_que_request(Worker *w) ;
+#ifdef WIN32
+static int read_int32(HANDLE fd, int *res, HANDLE ev);
+static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev);
+static int write_exact(HANDLE fd, AddrByte *buff, DWORD len,HANDLE ev);
+DWORD WINAPI worker_loop(void *v);
+DWORD WINAPI reader(void *data);
+DWORD WINAPI writer(void *data);
+static int send_mes_to_worker(QueItem *m, Worker *pw);
+BOOL create_mesq(MesQ **q);
+BOOL enque_mesq(MesQ *q, QueItem *m);
+BOOL deque_mesq(MesQ *q, QueItem **m);
+BOOL close_mesq(MesQ *q);
+HANDLE event_mesq(MesQ *q);
+#else
+static size_t read_int32(int fd, int *res);
+static ssize_t read_exact(int fd, void *vbuff, size_t nbytes);
+static int write_exact(int fd, AddrByte *buff, int len);
+void reap_children(int ignored);
+static void init_signals(void);
+static void kill_all_workers(void);
+static void close_all_worker_fds(void);
+static int worker_loop(void);
+static int fillin_reply(Worker *pw);
+static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw);
+#endif
+
+#define ERL_DBG_LVL_ENV_VAR "ERL_INET_GETHOST_DEBUG"
+
+static int
+get_env_debug_level(void)
+{
+#ifdef __WIN32__
+ char value[21]; /* Enough for any 64-bit values */
+ DWORD sz = GetEnvironmentVariable((LPCTSTR) ERL_DBG_LVL_ENV_VAR,
+ (LPTSTR) value,
+ (DWORD) sizeof(value));
+ if (sz == 0 || sz > sizeof(value))
+ return 0;
+#else
+ char *value = getenv(ERL_DBG_LVL_ENV_VAR);
+ if (!value)
+ return 0;
+#endif
+ return atoi(value);
+}
+
+#ifdef WIN32
+static void do_allocate_console(void)
+{
+ AllocConsole();
+ debug_console_allocated = CreateFile ("CONOUT$", GENERIC_WRITE,
+ FILE_SHARE_WRITE, NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+}
+#ifdef HARDDEBUG
+DWORD WINAPI pseudo_worker_loop(void *v);
+static void poll_gethost(int row);
+#endif
+#endif
+
+/*
+ * Main
+ */
+int main(int argc, char **argv)
+{
+ int num_workers = 1;
+ char **ap = argv + 1;
+ int x;
+ int disable_greedy = 0;
+
+ program_name = *argv;
+ que_first = que_last = NULL;
+ debug_level = get_env_debug_level();
+ greedy_threshold = 0;
+
+ while (*ap) {
+ if (!strcmp(*ap, "-d")) {
+ ++debug_level;
+ } else if(!strcmp(*ap, "-g") && *(ap + 1)) {
+ ++ap;
+ x = atoi(*ap);
+ if (!x) {
+ usage(*ap);
+ } else {
+ greedy_threshold = x;
+ }
+ } else if(!strcmp(*ap, "-ng")) {
+ disable_greedy = 1;
+ } else {
+ x = atoi(*ap);
+ if (!x) {
+ usage(*ap);
+ } else {
+ num_workers = x;
+ }
+ }
+ ++ap;
+ }
+
+#ifdef WIN32
+ if (num_workers > 60 || greedy_threshold > 60) {
+ usage("More than 60 workers on windows impossible!");
+ num_workers = 60;
+ greedy_threshold = 0;
+ }
+#endif
+
+ if(!greedy_threshold) {
+ greedy_threshold = (3*num_workers)/4; /* 75% */
+ if (!greedy_threshold) {
+ greedy_threshold = num_workers;
+ }
+ }
+
+ if (disable_greedy) {
+ greedy_threshold = num_workers + 1;
+ }
+
+#ifdef WIN32
+ {
+ WORD wr;
+ WSADATA wsa_data;
+ int wsa_error;
+ wr = MAKEWORD(2,0);
+
+ wsa_error = WSAStartup(wr,&wsa_data);
+ if (wsa_error) {
+ fatal("Could not open usable winsock library.");
+ }
+ if (LOBYTE(wsa_data.wVersion) != 2 || HIBYTE(wsa_data.wVersion) != 0) {
+ fatal("Could not open recent enough winsock library.");
+ }
+
+ if (debug_level >= 1) {
+ do_allocate_console();
+
+ DEBUGF(1,("num_workers = %d, greedy_threshold = %d, "
+ "debug_level = %d.",
+ num_workers, greedy_threshold, debug_level));
+ }
+ }
+ WAKEUP_WINSOCK(); /* Why on earth is this needed? */
+
+#endif
+
+ init_workers(num_workers);
+
+ main_loop();
+#ifndef WIN32
+ kill_all_workers();
+#endif
+ return 0;
+}
+
+static void usage(char *unknown)
+{
+ fprintf(stderr,"%s: Unknown option \"%s\"\n"
+ "Usage: %s [-d [-d ...]] [-g <greedy threshold>] "
+ "[<number of workers>]\n",
+ program_name, unknown, program_name);
+}
+
+/*
+ * Main process main loop
+ */
+
+static int handle_io_busy(int ndx)
+{
+ /* Probably an answer */
+ int res;
+ res = relay_reply(&busy_workers[ndx]);
+ if (res < 0) {
+ /* Bad worker */
+ if (busy_workers[ndx].que_size) {
+ restart_worker(&busy_workers[ndx]);
+ start_que_request(&busy_workers[ndx]);
+ return 0;
+ } else {
+ kill_worker(&busy_workers[ndx]);
+ --num_busy_workers;
+ busy_workers[ndx] = busy_workers[num_busy_workers];
+ }
+ return 1;
+ } else if (res == 0) {
+ /* Erlang has closed */
+ return -1;
+ } else {
+ if (busy_workers[ndx].que_size) {
+ start_que_request(&busy_workers[ndx]);
+ return 0;
+ }
+ /* The worker is no longer busy, it should be in the free list */
+ free_workers[num_free_workers] = busy_workers[ndx];
+ free_workers[num_free_workers].state = WORKER_FREE;
+ ++num_free_workers;
+ --num_busy_workers;
+ busy_workers[ndx] = busy_workers[num_busy_workers];
+ return 1;
+ }
+}
+
+static int handle_io_free(int ndx)
+{
+ /* IO from a free worker means "kill me" */
+ DEBUGF(1,("Free worker[%ld] spontaneously died.",
+ (long) free_workers[ndx].pid));
+ kill_worker(&free_workers[ndx]);
+ --num_free_workers;
+ free_workers[ndx] = free_workers[num_free_workers];
+ return 1;
+}
+
+static int handle_io_stalled(int ndx)
+{
+ int res;
+ res = ignore_reply(&stalled_workers[ndx]);
+ if (res <= 0) {
+ /* Bad worker */
+ kill_worker(&stalled_workers[ndx]);
+ --num_stalled_workers;
+ stalled_workers[ndx] = stalled_workers[num_stalled_workers];
+ return 1;
+ } else {
+ DEBUGF(3,("Ignoring reply from stalled worker[%ld].",
+ (long) stalled_workers[ndx].pid));
+ free_workers[num_free_workers] = stalled_workers[ndx];
+ free_workers[num_free_workers].state = WORKER_FREE;
+ ++num_free_workers;
+ --num_stalled_workers;
+ stalled_workers[ndx] = stalled_workers[num_stalled_workers];
+ return 1;
+ }
+}
+
+static void check_que(void)
+{
+ /* Check if anything in the que can be handled */
+ Worker *cw;
+
+ while (que_first) {
+ QueItem *qi,*nxt;
+ qi = que_first;
+ nxt = qi->next; /* Need to save before it's getting put in another que
+ in threaded solution */
+ if ((cw = pick_worker()) == NULL) {
+ break;
+ }
+#ifdef WIN32
+ {
+ SerialType save_serial = get_serial(que_first->request);
+ if (send_mes_to_worker(que_first, cw) != 0) {
+ kill_last_picked_worker();
+ continue;
+ }
+ cw->serial = save_serial;
+ }
+#else
+ if (send_request_to_worker(que_first->request,
+ que_first->req_size, cw) != 0) {
+ /* Couldn't send request, kill the worker and retry */
+ kill_last_picked_worker();
+ continue;
+ }
+ cw->serial = get_serial(que_first->request);
+#endif
+ /* Went well, lets deque */
+ que_first = nxt;
+ if (que_first == NULL) {
+ que_last = NULL;
+ }
+ DEBUGF(3,("Did deque serial %d, Que is %sempty",
+ get_serial(qi->request), (que_first) ? "not " : ""));
+#ifndef WIN32
+ FREE(qi);
+#endif
+ }
+}
+
+static int clean_que_of(SerialType s)
+{
+ QueItem **qi;
+ int i;
+
+ for(qi=&que_first;*qi != NULL &&
+ s != get_serial((*qi)->request); qi = &((*qi)->next))
+ ;
+ if(*qi != NULL) {
+ QueItem *r = *qi;
+ *qi = (*qi)->next;
+ FREE(r);
+ if(que_last == r) {
+ /* Lost the "last" pointer, should be very uncommon
+ if the que is not empty, so we simply do a traversal
+ to reclaim it. */
+ if (que_first == NULL) {
+ que_last = NULL;
+ } else {
+ for (que_last=que_first;que_last->next != NULL;
+ que_last = que_last->next)
+ ;
+ }
+ }
+ DEBUGF(3,("Removing serial %d from global que on request, "
+ "que %sempty",s, (que_first) ? "not " : ""));
+ return 1;
+ }
+ for (i = 0; i < num_busy_workers; ++i) {
+ for(qi=&(busy_workers[i].que_first);*qi != NULL &&
+ s != get_serial((*qi)->request); qi = &((*qi)->next))
+ ;
+ if(*qi != NULL) {
+ QueItem *r = *qi;
+ *qi = (*qi)->next;
+ FREE(r);
+ if(busy_workers[i].que_last == r) {
+ /* Lost the "last" pointer, should be very uncommon
+ if the que is not empty, so we simply do a traversal
+ to reclaim it. */
+ if (busy_workers[i].que_first == NULL) {
+ busy_workers[i].que_last = NULL;
+ if (busy_workers[i].que_size != 1) {
+ fatal("Worker que size counter incorrect, internal datastructure error.");
+ }
+ } else {
+ for (busy_workers[i].que_last = busy_workers[i].que_first;
+ busy_workers[i].que_last->next != NULL;
+ busy_workers[i].que_last = busy_workers[i].que_last->next)
+ ;
+ }
+ }
+ --(busy_workers[i].que_size);
+ DEBUGF(3,("Removing serial %d from worker[%ld] specific que "
+ "on request, que %sempty",
+ s, (long) busy_workers[i].pid,
+ (busy_workers[i].que_first) ? "not " : ""));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static void main_loop(void)
+{
+ AddrByte *inbuff = NULL;
+ int insize;
+ int i,w;
+#ifdef WIN32
+ HANDLE handles[64];
+ DWORD num_handles;
+ DWORD index;
+ QueItem *qi;
+#else
+ size_t inbuff_size = 0;
+ fd_set fds;
+ int max_fd;
+#endif
+ int new_data;
+ int save_serial;
+ /* It's important that the free workers list is handled first */
+ Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
+ int *wsizes[3] = {&num_free_workers, &num_busy_workers,
+ &num_stalled_workers};
+ int (*handlers[3])(int) = {&handle_io_free, &handle_io_busy,
+ &handle_io_stalled};
+ Worker *cw;
+ AddrByte domainbuff[DOMAINNAME_MAX];
+
+#ifdef WIN32
+
+ {
+ DWORD dummy;
+ /* Create the reader and writer */
+ if ((!create_mesq(&to_erlang)) || (!create_mesq(&from_erlang))) {
+ fatal("Could not create message que! errno = %d.",GetLastError());
+ }
+ if (((HANDLE) _beginthreadex(NULL,0,writer,to_erlang,0,&dummy))
+ == NULL) {
+ fatal("Could not create writer thread! errno = %d.",GetLastError());
+ }
+ if (((HANDLE) _beginthreadex(NULL,0,reader,from_erlang,0,&dummy))
+ == NULL) {
+ fatal("Could not create reader thread! errno = %d.",GetLastError());
+ }
+ DEBUGF(4,("Created reader and writer threads."));
+#ifdef HARDDEBUG
+ poll_gethost(__LINE__);
+#endif
+ }
+#endif
+
+ for(;;) {
+#ifdef WIN32
+ num_handles = 0;
+ handles[num_handles++] = event_mesq(from_erlang);
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; ++i) {
+ handles[num_handles++] = event_mesq(workers[w][i].readfrom);
+ }
+ }
+
+ if ((index = WaitForMultipleObjects(num_handles, handles, FALSE, INFINITE))
+ == WAIT_FAILED) {
+ fatal("Could not WaitForMultpleObjects! errno = %d.",GetLastError());
+ }
+ w = 0;
+ index -= WAIT_OBJECT_0;
+
+ DEBUGF(4,("Got data on index %d.",index));
+ if (index > 0) {
+ if (((int)index - 1) < *wsizes[0]) {
+ (*handlers[0])(index - 1);
+ } else if (((int)index - 1) < ((*wsizes[0]) + (*wsizes[1]))) {
+ (*handlers[1])(index - 1 - (*wsizes[0]));
+ } else {
+ (*handlers[2])(index - 1 - (*wsizes[0]) - (*wsizes[1]));
+ }
+ }
+ new_data = (index == 0);
+#else
+ max_fd = 0;
+ FD_ZERO(&fds);
+ FD_SET(0,&fds);
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; ++i) {
+ FD_SET(workers[w][i].readfrom,&fds);
+ if (workers[w][i].readfrom > max_fd) {
+ max_fd = workers[w][i].readfrom;
+ }
+ }
+ }
+ for (;;) {
+ if (select(max_fd + 1,&fds,NULL,NULL,NULL) < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ fatal("Select failed (invalid internal structures?), "
+ "errno = %d.",errno);
+ }
+ }
+ break;
+ }
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; ++i) {
+ if (FD_ISSET(workers[w][i].readfrom, &fds)) {
+ int hres = (*handlers[w])(i);
+ if (hres < 0) {
+ return;
+ } else {
+ i -= hres; /* We'll retry this position, if hres == 1.
+ The position is usually
+ replaced with another worker,
+ a worker with
+ I/O usually changes state as we
+ use blocking file I/O */
+ }
+ }
+ }
+ }
+ new_data = FD_ISSET(0,&fds);
+
+#endif
+
+ check_que();
+
+ /* Now check for new requests... */
+ if (new_data) { /* Erlang... */
+ OpType op;
+#ifdef WIN32
+ if (!deque_mesq(from_erlang,&qi)) {
+ DEBUGF(1,("Erlang has closed."));
+ return;
+ }
+ insize = qi->req_size;
+ inbuff = qi->request;
+ DEBUGF(4,("Got data from erlang."));
+ DEBUGF(4,("OPeration == %d.",get_op(inbuff)));
+#else
+ insize = read_request(&inbuff, &inbuff_size);
+ if (insize == 0) { /* Other errors taken care of in
+ read_request */
+ DEBUGF(1,("Erlang has closed."));
+ return;
+ }
+#endif
+ op = get_op(inbuff);
+ if (op == OP_CANCEL_REQUEST) {
+ SerialType serial = get_serial(inbuff);
+ if (!clean_que_of(serial)) {
+ for (i = 0; i < num_busy_workers; ++i) {
+ if (busy_workers[i].serial == serial) {
+ if (busy_workers[i].que_size) {
+ restart_worker(&busy_workers[i]);
+ start_que_request(&busy_workers[i]);
+ } else {
+ stall_worker(i);
+ check_que();
+ }
+ break;
+ }
+ }
+ }
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue; /* New select */
+ } else if (op == OP_CONTROL) {
+ CtlType ctl;
+ SerialType serial = get_serial(inbuff);
+ if (serial != INVALID_SERIAL) {
+ fatal("Invalid serial: %d.", serial);
+ }
+ switch (ctl = get_ctl(inbuff)) {
+ case SETOPT_DEBUG_LEVEL:
+ {
+ int tmp_debug_level = get_debug_level(inbuff);
+#ifdef WIN32
+ if (debug_console_allocated == INVALID_HANDLE_VALUE &&
+ tmp_debug_level > 0) {
+ DWORD res;
+ do_allocate_console();
+ WriteFile(debug_console_allocated,
+ "Hej\n",4,&res,NULL);
+ }
+#endif
+ debug_level = tmp_debug_level;
+ DEBUGF(debug_level, ("debug_level = %d", debug_level));
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < *wsizes[w]; i++) {
+ int res;
+#ifdef WIN32
+ QueItem *m;
+#endif
+ cw = &(workers[w][i]);
+#ifdef WIN32
+ m = ALLOC(sizeof(QueItem) - 1 + qi->req_size);
+ memcpy(m->request, qi->request,
+ (m->req_size = qi->req_size));
+ m->next = NULL;
+ if ((res = send_mes_to_worker(m, cw)) != 0) {
+ FREE(m);
+ }
+#else
+ res = send_request_to_worker(inbuff, insize, cw);
+#endif
+ if (res != 0) {
+ kill_worker(cw);
+ (*wsizes[w])--;
+ *cw = workers[w][*wsizes[w]];
+ }
+ }
+ }
+ }
+ break;
+ default:
+ warning("Unknown control requested from erlang (%d), "
+ "message discarded.", (int) ctl);
+ break;
+ }
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue; /* New select */
+ } else {
+ ProtoType proto;
+ if (op != OP_GETHOSTBYNAME && op != OP_GETHOSTBYADDR) {
+ warning("Unknown operation requested from erlang (%d), "
+ "message discarded.", op);
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue;
+ }
+ if ((proto = get_proto(inbuff)) != PROTO_IPV4 &&
+ proto != PROTO_IPV6) {
+ warning("Unknown protocol requested from erlang (%d), "
+ "message discarded.", proto);
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue;
+ }
+ if (get_domainname(inbuff,insize,domainbuff) < 0) {
+ warning("Malformed message sent from erlang, no domain, "
+ "message discarded.", op);
+#ifdef WIN32
+ FREE(qi);
+#endif
+ continue;
+ }
+ }
+
+ if (BEE_GREEDY()) {
+ DEBUGF(4,("Beeing greedy!"));
+ if ((cw = pick_worker_greedy(domainbuff)) != NULL) {
+ /* Put it in the worker specific que if the
+ domainname matches... */
+#ifndef WIN32
+ QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
+ insize);
+ qi->req_size = insize;
+ memcpy(&(qi->request), inbuff, insize);
+ qi->next = NULL;
+#endif
+ if (!cw->que_first) {
+ cw->que_first = cw->que_last = qi;
+ } else {
+ cw->que_last->next = qi;
+ cw->que_last = qi;
+ }
+ ++(cw->que_size);
+ continue;
+ }
+ /* Otherwise busyness as usual */
+ }
+
+ save_serial = get_serial(inbuff);
+
+ while ((cw = pick_worker()) != NULL) {
+ int res;
+#ifdef WIN32
+ res = send_mes_to_worker(qi,cw);
+#else
+ res = send_request_to_worker(inbuff, insize, cw);
+#endif
+ if (res == 0) {
+ break;
+ } else {
+ kill_last_picked_worker();
+ }
+ }
+
+ if (cw == NULL) {
+ /* Insert into que */
+#ifndef WIN32
+ QueItem *qi = ALLOC(sizeof(QueItem) - 1 +
+ insize);
+ qi->req_size = insize;
+ memcpy(&(qi->request), inbuff, insize);
+ qi->next = NULL;
+#endif
+ if (!que_first) {
+ que_first = que_last = qi;
+ } else {
+ que_last->next = qi;
+ que_last = qi;
+ }
+ } else {
+ cw->serial = save_serial;
+ domaincopy(cw->domain, domainbuff);
+ }
+ }
+ }
+}
+
+/*
+ * Main process worker administration
+ */
+
+static void init_workers(int max)
+{
+ max_workers = max;
+ num_busy_workers = 0;
+ num_free_workers = 0;
+ num_stalled_workers = 0;
+
+ busy_workers = ALLOC(sizeof(Worker) * max_workers);
+ free_workers = ALLOC(sizeof(Worker) * max_workers);
+ stalled_workers = ALLOC(sizeof(Worker) * max_workers);
+#ifndef WIN32
+ init_signals();
+#endif
+}
+
+#ifdef WIN32
+static void kill_worker(Worker *pw)
+{
+ /* Cannot really kill a thread in win32, have to just leave it to die */
+ close_mesq(pw->writeto);
+ close_mesq(pw->readfrom);
+ pw->state = WORKER_EMPTY;
+}
+#else
+static void kill_worker(Worker *pw)
+{
+ fd_set fds;
+ struct timeval tmo;
+ int selret;
+ static char buff[1024];
+
+ DEBUGF(3,("Killing worker[%ld] with fd %d, serial %d",
+ (long) pw->pid,
+ (int) pw->readfrom,
+ (int) pw->serial));
+ kill(pw->pid, SIGUSR1);
+ /* This is all just to check that the child died, not
+ really necessary */
+ for(;;) {
+ FD_ZERO(&fds);
+ FD_SET(pw->readfrom, &fds);
+ tmo.tv_usec=0;
+ tmo.tv_sec = CHILDWAIT_TMO;
+ selret = select(pw->readfrom+1, &fds, NULL, NULL, &tmo);
+ if (selret < 0) {
+ if (errno != EINTR) {
+ warning("Unable to select on dying child file descriptor, "
+ "errno = %d.",errno);
+ break;
+ }
+ } else if (selret == 0) {
+ warning("Timeout waiting for child process to die, "
+ "ignoring child (pid = %d).", pw->pid);
+ break;
+ } else {
+ int ret;
+ if ((ret = read(pw->readfrom, buff, 1024)) < 0) {
+ if (errno != EINTR) {
+ warning("Child file descriptor not closed properly, "
+ "errno = %d", errno);
+ break;
+ }
+ } else if (ret == 0) {
+ break;
+ }
+ /* continue */
+ }
+ }
+ /* Waiting is done by signal handler... */
+ close(pw->readfrom);
+ close(pw->writeto);
+ pw->state = WORKER_EMPTY;
+ /* Leave rest as is... */
+}
+
+static void kill_all_workers(void)
+/* Emergency function, will not check that the children died... */
+{
+ int i;
+ for (i = 0; i < num_busy_workers; ++i) {
+ kill(busy_workers[i].pid, SIGUSR1);
+ }
+ for (i = 0; i < num_free_workers; ++i) {
+ kill(free_workers[i].pid, SIGUSR1);
+ }
+ for (i = 0; i < num_stalled_workers; ++i) {
+ kill(stalled_workers[i].pid, SIGUSR1);
+ }
+}
+#endif /* !WIN32 */
+
+static Worker *pick_worker(void)
+{
+ Worker tmp;
+ if (num_free_workers > 0) {
+ --num_free_workers;
+ tmp = free_workers[num_free_workers];
+ } else if (num_stalled_workers > 0) {
+ /* "restart" the worker... */
+ --num_stalled_workers;
+ kill_worker(&(stalled_workers[num_stalled_workers]));
+ if (create_worker(&tmp,0) < 0) {
+ warning("Unable to create worker process, insufficient "
+ "resources");
+ return NULL;
+ }
+ } else {
+ if (num_busy_workers == max_workers) {
+ return NULL;
+ }
+ if (create_worker(&tmp,0) < 0) {
+ warning("Unable to create worker process, insufficient "
+ "resources");
+ return NULL;
+ }
+ }
+ /* tmp contains a worker now, make it busy and put it in the right
+ array */
+ tmp.state = WORKER_BUSY;
+ busy_workers[num_busy_workers] = tmp;
+ ++num_busy_workers;
+ return &(busy_workers[num_busy_workers-1]);
+}
+
+static Worker *pick_worker_greedy(AddrByte *domainbuff)
+{
+ int i;
+ int ql = 0;
+ int found = -1;
+ for (i=0; i < num_busy_workers; ++i) {
+ if (domaineq(busy_workers[i].domain, domainbuff)) {
+ if ((found < 0) || (busy_workers[i].que_size <
+ busy_workers[found].que_size)) {
+ found = i;
+ ql = busy_workers[i].que_size;
+ }
+ }
+ }
+ if (found >= 0) {
+ return &busy_workers[found];
+ }
+ return NULL;
+}
+
+static void restart_worker(Worker *w)
+{
+ kill_worker(w);
+ if (create_worker(w,1) < 0) {
+ fatal("Unable to create worker process, insufficient resources");
+ }
+}
+
+static void kill_last_picked_worker(void)
+{
+ kill_worker( &(busy_workers[num_busy_workers-1]));
+ --num_busy_workers;
+}
+
+/*
+ * Starts a request qued to a specific worker, check_que starts normally queued requests.
+ * We expect a que here...
+ */
+static void start_que_request(Worker *w)
+{
+ QueItem *qi;
+ SerialType save_serial;
+ if (!w->que_first || !w->que_size) {
+ fatal("Expected que'd requests but found none, "
+ "internal datastructure corrupted!");
+ }
+ qi = w->que_first;
+ w->que_first = w->que_first->next;
+ if (!w->que_first) {
+ w->que_last = NULL;
+ }
+ --(w->que_size);
+ save_serial = get_serial(qi->request);
+#ifdef WIN32
+ while (send_mes_to_worker(qi, w) != 0) {
+ restart_worker(w);
+ }
+#else
+ while (send_request_to_worker(qi->request,
+ qi->req_size, w) != 0) {
+ restart_worker(w);
+ }
+#endif
+ w->serial = save_serial;
+ DEBUGF(3,("Did deque serial %d from worker[%ld] specific que, "
+ "Que is %sempty",
+ get_serial(qi->request), (long) w->pid,
+ (w->que_first) ? "not " : ""));
+#ifndef WIN32
+ FREE(qi);
+#endif
+}
+
+#ifndef WIN32
+/* Signal utilities */
+static RETSIGTYPE (*sys_sigset(int sig, RETSIGTYPE (*func)(int)))(int)
+{
+ struct sigaction act, oact;
+
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ act.sa_handler = func;
+ sigaction(sig, &act, &oact);
+ return(oact.sa_handler);
+}
+
+
+static void sys_sigblock(int sig)
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(SIG_BLOCK, &mask, (sigset_t *)NULL);
+}
+
+static void sys_sigrelease(int sig)
+{
+ sigset_t mask;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, sig);
+ sigprocmask(SIG_UNBLOCK, &mask, (sigset_t *)NULL);
+}
+
+/* Child signal handler */
+void reap_children(int ignored)
+{
+ int res;
+ sys_sigblock(SIGCHLD);
+ for (;;) {
+ while ((res = waitpid((pid_t)-1, NULL, WNOHANG)) > 0)
+ ;
+ if (!(res < 0 && errno == EAGAIN)) {
+ DEBUGF(4,("reap_children: res = %d, errno = %d.",res,errno));
+ break;
+ }
+ }
+ sys_sigrelease(SIGCHLD);
+}
+
+static void init_signals(void)
+{
+ sys_sigset(SIGCHLD,&reap_children); /* SIG_IGN would give same result
+ on most (?) platforms. */
+ sys_sigset(SIGPIPE, SIG_IGN);
+}
+#endif
+
+static void stall_worker(int ndx)
+{
+ --num_busy_workers;
+ stalled_workers[num_stalled_workers] = busy_workers[ndx];
+ stalled_workers[num_stalled_workers].state = WORKER_STALLED;
+ busy_workers[ndx] = busy_workers[num_busy_workers];
+ DEBUGF(3, ("Stalled worker[%ld]",
+ (long) stalled_workers[num_stalled_workers].pid));
+ ++num_stalled_workers;
+}
+
+
+/*
+ * Main loop message passing
+ */
+#ifndef WIN32
+static int read_request(AddrByte **buff, size_t *buff_size)
+{
+ int siz;
+ int r;
+
+ if ((r = READ_PACKET_BYTES(0,&siz)) != PACKET_BYTES) {
+ if (r == 0) {
+ return 0;
+ } else {
+ fatal("Unexpected end of file on main input, errno = %d",errno);
+ }
+ }
+
+ if (siz > *buff_size) {
+ if (buff_size == 0) {
+ *buff = ALLOC((*buff_size = siz));
+ } else {
+ *buff = REALLOC(*buff, (*buff_size = siz));
+ }
+ }
+ if (read_exact(0,*buff, siz) != siz) {
+ fatal("Unexpected end of file on main input, errno = %d",errno);
+ }
+ if (siz < 5) {
+ fatal("Unexpected message on main input, message size %d less "
+ "than minimum.");
+ }
+ return siz;
+}
+
+#endif /* !WIN32 */
+
+static OpType get_op(AddrByte *buff)
+{
+ return (OpType) buff[4];
+}
+
+static AddrByte *get_op_addr(AddrByte *buff)
+{
+ return buff + 4;
+}
+
+static SerialType get_serial(AddrByte *buff)
+{
+ return get_int32(buff);
+}
+
+static ProtoType get_proto(AddrByte *buff)
+{
+ return (ProtoType) buff[5];
+}
+
+static CtlType get_ctl(AddrByte *buff)
+{
+ return (CtlType) buff[5];
+}
+
+static AddrByte *get_data(AddrByte *buff)
+{
+ return buff + 6;
+}
+
+static int get_debug_level(AddrByte *buff)
+{
+ return get_int32(buff + 6);
+}
+
+#ifdef WIN32
+static int send_mes_to_worker(QueItem *m, Worker *pw)
+{
+ if (!enque_mesq(pw->writeto, m)) {
+ warning("Unable to send to child process.");
+ return -1;
+ }
+ return 0;
+}
+#else
+static int send_request_to_worker(AddrByte *pr, int rsize, Worker *pw)
+{
+ AddrByte hdr[PACKET_BYTES];
+
+ PUT_PACKET_BYTES(hdr, rsize);
+ if (write_exact(pw->writeto, hdr, PACKET_BYTES) < 0) {
+ warning("Unable to write to child process.");
+ return -1;
+ }
+ if (write_exact(pw->writeto, (AddrByte *) pr, rsize) < 0) {
+ warning("Unable to write to child process.");
+ return -1;
+ }
+ return 0;
+}
+#endif /* !WIN32 */
+
+#ifdef WIN32
+static int relay_reply(Worker *pw)
+{
+ QueItem *m;
+ if (!deque_mesq(pw->readfrom,&m)) {
+ return 0;
+ }
+ if (!enque_mesq(to_erlang,m)) {
+ FREE(m);
+ return 0;
+ }
+ return 1;
+}
+
+static int ignore_reply(Worker *pw) {
+ QueItem *m;
+ if (!deque_mesq(pw->readfrom,&m)) {
+ return 0;
+ }
+ FREE(m);
+ return 1;
+}
+
+#else
+
+/* Static buffers used by the next three functions */
+static AddrByte *relay_buff = NULL;
+static int relay_buff_size = 0;
+
+static int fillin_reply(Worker *pw)
+{
+ int length;
+
+ if (READ_PACKET_BYTES(pw->readfrom, &length) != PACKET_BYTES) {
+ warning("Malformed reply (header) from worker process %d.",
+ pw->pid);
+ return -1;
+ }
+
+ if (relay_buff_size < (length + PACKET_BYTES)) {
+ if (!relay_buff_size) {
+ relay_buff =
+ ALLOC((relay_buff_size = (length + PACKET_BYTES)));
+ } else {
+ relay_buff =
+ REALLOC(relay_buff,
+ (relay_buff_size = (length + PACKET_BYTES)));
+ }
+ }
+ PUT_PACKET_BYTES(relay_buff, length);
+ if (read_exact(pw->readfrom, relay_buff + PACKET_BYTES, length) !=
+ length) {
+ warning("Malformed reply (data) from worker process %d.", pw->pid);
+ return -1;
+ }
+ return length;
+}
+
+static int relay_reply(Worker *pw)
+{
+ int length = fillin_reply(pw); /* Filled into the "global" buffer */
+ int res;
+
+ if (length < 0) {
+ return -1;
+ }
+ if ((res = write_exact(1, relay_buff, length + PACKET_BYTES)) < 0) {
+ fatal("Cannot write reply to erlang process, errno = %d.", errno);
+ } else if (res == 0) {
+ DEBUGF(1,("Erlang has closed write pipe."));
+ return 0;
+ }
+ return length;
+}
+
+static int ignore_reply(Worker *pw)
+{
+ return fillin_reply(pw);
+}
+
+#endif /* !WIN32 */
+
+/*
+ * Domain name "parsing" and worker specific queing
+ */
+static void domaincopy(AddrByte *out, AddrByte *in)
+{
+ AddrByte *ptr = out;
+ *ptr++ = *in++;
+ *ptr++ = *in++;
+ switch(*out) {
+ case OP_GETHOSTBYNAME:
+ while(*in != '\0' && *in != '.')
+ ++in;
+ strncpy((char*)ptr, (char*)in, DOMAINNAME_MAX-2);
+ ptr[DOMAINNAME_MAX-3] = '\0';
+ DEBUGF(4,("Saved domainname %s.", ptr));
+ return;
+ case OP_GETHOSTBYADDR:
+ memcpy(ptr,in, ((out[1] == PROTO_IPV4) ? UNIT_IPV4 : UNIT_IPV6) - 1);
+ DEBUGF(4, ("Saved domain address: %s.",
+ format_address(((out[1] == PROTO_IPV4) ?
+ UNIT_IPV4 : UNIT_IPV6) - 1,ptr)));
+ return;
+ default:
+ fatal("Trying to copy buffer not containing valid domain, [%d,%d].",
+ (int) out[0], (int) out[1]);
+ }
+}
+
+static int domaineq(AddrByte *d1, AddrByte *d2)
+{
+ if (d1[0] != d2[0] || d1[1] != d2[1]) {
+ return 0;
+ }
+ switch (d1[0]) {
+ case OP_GETHOSTBYNAME:
+ return !strcmp((char*)d1+2,(char*)d2+2);
+ case OP_GETHOSTBYADDR:
+ return !memcmp(d1+2,d2+2, ((d1[1] == PROTO_IPV4)
+ ? UNIT_IPV4 : UNIT_IPV6) - 1);
+ default:
+ fatal("Trying to compare buffers not containing valid domain, "
+ "[%d,%d].",
+ (int) d1[0], (int) d1[1]);
+ return -1; /* Lint... */
+ }
+}
+
+static int get_domainname(AddrByte *inbuff, int insize, AddrByte *domainbuff)
+{
+ OpType op = get_op(inbuff);
+ ProtoType proto;
+ int i;
+ AddrByte *data;
+
+ data = get_data(inbuff);
+ switch (op) {
+ case OP_GETHOSTBYNAME:
+ data = get_data(inbuff);
+ for (i = (data - inbuff); i < insize && inbuff[i] != '\0'; ++i)
+ ;
+ if (i < insize) {
+ domaincopy(domainbuff, get_op_addr(inbuff));
+ return 0;
+ }
+ DEBUGF(3, ("Could not pick valid domainname in "
+ "gethostbyname operation"));
+ return -1;
+ case OP_GETHOSTBYADDR:
+ proto = get_proto(inbuff);
+ i = insize - (data - inbuff);
+ if ((proto == PROTO_IPV4 && i == UNIT_IPV4) ||
+ (proto == PROTO_IPV6 && i == UNIT_IPV6)) {
+ /* An address buffer */
+ domaincopy(domainbuff, get_op_addr(inbuff));
+ return 0;
+ }
+ DEBUGF(3, ("Could not pick valid domainname in gethostbyaddr "
+ "operation"));
+ return -1;
+ default:
+ DEBUGF(2, ("Could not pick valid domainname because of "
+ "invalid opcode %d.", (int) op));
+ return -1;
+ }
+}
+
+/*
+ * Worker subprocesses with utilities
+ */
+#ifdef WIN32
+static int create_worker(Worker *pworker, int save_que)
+{
+ MesQ **thread_data = ALLOC(2*sizeof(MesQ *));
+ DWORD tid;
+
+
+ if (!create_mesq(thread_data)) {
+ fatal("Could not create, pipes for subprocess, errno = %d",
+ GetLastError());
+ }
+ if (!create_mesq(thread_data + 1)) {
+ fatal("Could not create, pipes for subprocess, errno = %d",
+ GetLastError());
+ }
+ /* Save those before the thread starts */
+ pworker->writeto = thread_data[0];
+ pworker->readfrom = thread_data[1];
+
+ if (((HANDLE) _beginthreadex(NULL, 0, worker_loop, thread_data, 0, &tid))
+ == NULL) {
+ fatal("Could not create thread errno = %d",
+ GetLastError());
+ }
+ pworker->pid = tid;
+ pworker->state = WORKER_FREE;
+ pworker->serial = INVALID_SERIAL;
+ if (!save_que) {
+ pworker->que_first = pworker->que_last = NULL;
+ pworker->que_size = 0;
+ }
+ DEBUGF(3,("Created worker[%ld] with fd %d",
+ (long) pworker->pid, (int) pworker->readfrom));
+ return 0;
+}
+
+#else
+
+static int create_worker(Worker *pworker, int save_que)
+{
+ int p0[2], p1[2];
+ pid_t child;
+
+ if (pipe(p0)) {
+ warning("Could not create, pipes for subprocess, errno = %d",
+ errno);
+ return -1;
+ }
+
+ if (pipe(p1)) {
+ warning("Could not create, pipes for subprocess, errno = %d",
+ errno);
+ close(p0[0]);
+ close(p0[1]);
+ return -1;
+ }
+ if ((child = fork()) < 0) { /* failure */
+ warning("Could not fork(), errno = %d",
+ errno);
+ close(p0[0]);
+ close(p0[1]);
+ close(p1[0]);
+ close(p1[1]);
+ return -1;
+ } else if (child > 0) { /* parent */
+ close(p0[1]);
+ close(p1[0]);
+ pworker->writeto = p1[1];
+ pworker->readfrom = p0[0];
+ pworker->pid = child;
+ pworker->state = WORKER_FREE;
+ pworker->serial = INVALID_SERIAL;
+ if (!save_que) {
+ pworker->que_first = pworker->que_last = NULL;
+ pworker->que_size = 0;
+ }
+ DEBUGF(3,("Created worker[%ld] with fd %d",
+ (long) pworker->pid, (int) pworker->readfrom));
+ return 0;
+ } else { /* child */
+ close(p1[1]);
+ close(p0[0]);
+ close_all_worker_fds();
+ /* Make "fatal" not find any children */
+ num_busy_workers = num_free_workers = num_stalled_workers = 0;
+ if((dup2(p1[0],0) < 0) || (dup2(p0[1],1) < 0)) {
+ fatal("Worker could not dup2(), errno = %d",
+ errno);
+ return -1; /* lint... */
+ }
+ close(p1[0]);
+ close(p0[1]);
+ signal(SIGCHLD, SIG_IGN);
+ return worker_loop();
+ }
+}
+
+static void close_all_worker_fds(void)
+{
+ int w,i;
+ Worker *workers[3] = {free_workers, busy_workers, stalled_workers};
+ int wsizes[3] = {num_free_workers, num_busy_workers,
+ num_stalled_workers};
+ for (w = 0; w < 3; ++w) {
+ for (i = 0; i < wsizes[w]; ++i) {
+ if (workers[w][i].state != WORKER_EMPTY) {
+ close(workers[w][i].readfrom);
+ close(workers[w][i].writeto);
+ }
+ }
+ }
+}
+
+#endif /* !WIN32 */
+
+#ifdef WIN32
+DWORD WINAPI worker_loop(void *v)
+#else
+static int worker_loop(void)
+#endif
+{
+ AddrByte *req = NULL;
+ size_t req_size = 0;
+ int this_size;
+ AddrByte *reply = NULL;
+ size_t reply_size = 0;
+ size_t data_size;
+
+#ifdef WIN32
+ QueItem *m = NULL;
+ MesQ *readfrom = ((MesQ **) v)[0];
+ MesQ *writeto = ((MesQ **) v)[1];
+ /* XXX:PaN */
+ FREE(v);
+#endif
+
+ for(;;) {
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo *ai = NULL;
+#endif
+ struct hostent *he = NULL;
+#ifdef HAVE_GETNAMEINFO
+ struct sockaddr *sa = NULL;
+ char name[NI_MAXHOST];
+#endif
+#if defined(HAVE_GETIPNODEBYNAME) || defined(HAVE_GETIPNODEBYADDR)
+ int free_he = 0;
+#endif
+ int error_num = 0;
+ SerialType serial;
+ OpType op;
+ ProtoType proto;
+ AddrByte *data;
+
+#ifdef WIN32
+ WaitForSingleObject(event_mesq(readfrom),INFINITE);
+ DEBUGF(4,("Worker got data on message que."));
+
+ if(!deque_mesq(readfrom,&m)) {
+ goto fail;
+ }
+ this_size = m->req_size;
+ req = m->request;
+#else
+ if (READ_PACKET_BYTES(0,&this_size) != PACKET_BYTES) {
+ DEBUGF(2,("Worker got error/EOF while reading size, exiting."));
+ exit(0);
+ }
+ if (this_size > req_size) {
+ if (req == NULL) {
+ req = ALLOC((req_size = this_size));
+ } else {
+ req = REALLOC(req, (req_size = this_size));
+ }
+ }
+ if (read_exact(0, req, (size_t) this_size) != this_size) {
+ DEBUGF(1,("Worker got EOF while reading data, exiting."));
+ exit(0);
+ }
+#endif
+ /* Decode the request... */
+ serial = get_serial(req);
+ if (OP_CONTROL == (op = get_op(req))) {
+ CtlType ctl;
+ if (serial != INVALID_SERIAL) {
+ DEBUGF(1, ("Worker got invalid serial: %d.", serial));
+ exit(0);
+ }
+ switch (ctl = get_ctl(req)) {
+ case SETOPT_DEBUG_LEVEL:
+ debug_level = get_debug_level(req);
+ DEBUGF(debug_level,
+ ("Worker debug_level = %d.", debug_level));
+ break;
+ }
+ continue;
+ }
+ proto = get_proto(req);
+ data = get_data(req);
+ DEBUGF(4,("Worker got request, op = %d, proto = %d, data = %s.",
+ op,proto,data));
+ /* Got a request, lets go... */
+ switch (op) {
+ case OP_GETHOSTBYNAME:
+ switch (proto) {
+
+#ifdef HAVE_IN6
+ case PROTO_IPV6: { /* switch (proto) { */
+#ifdef HAVE_GETADDRINFO
+ struct addrinfo hints;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_flags = (AI_CANONNAME|AI_V4MAPPED|AI_ADDRCONFIG);
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_family = AF_INET6;
+ DEBUGF(5, ("Starting getaddrinfo(%s, ...)", data));
+ error_num = getaddrinfo((char *)data, NULL, &hints, &ai);
+ DEBUGF(5,("getaddrinfo returned %d", error_num));
+ if (error_num) {
+ error_num = map_netdb_error_ai(error_num);
+ }
+#elif defined(HAVE_GETIPNODEBYNAME) /*#ifdef HAVE_GETADDRINFO */
+ DEBUGF(5,("Starting getipnodebyname(%s)",data));
+ he = getipnodebyname(data, AF_INET6, AI_DEFAULT, &error_num);
+ if (he) {
+ free_he = 1;
+ error_num = 0;
+ DEBUGF(5,("getipnodebyname(,AF_INET6,,) OK"));
+ } else {
+ DEBUGF(5,("getipnodebyname(,AF_INET6,,) error %d", error_num));
+ error_num = map_netdb_error(error_num);
+ }
+#elif defined(HAVE_GETHOSTBYNAME2) /*#ifdef HAVE_GETADDRINFO */
+ DEBUGF(5,("Starting gethostbyname2(%s, AF_INET6)",data));
+ he = gethostbyname2((char*)data, AF_INET6);
+ if (he) {
+ error_num = 0;
+ DEBUGF(5,("gethostbyname2(, AF_INET6) OK"));
+ } else {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyname2(, AF_INET6) error %d", h_errno));
+ }
+#else
+ error_num = ERRCODE_NOTSUP;
+#endif /*#ifdef HAVE_GETADDRINFO */
+ } break;
+#endif /*ifdef HAVE_IN6 */
+
+ case PROTO_IPV4: { /* switch (proto) { */
+ DEBUGF(5,("Starting gethostbyname(%s)",data));
+ he = gethostbyname((char*)data);
+ if (he) {
+ error_num = 0;
+ DEBUGF(5,("gethostbyname OK"));
+ } else {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyname error %d", h_errno));
+ }
+ } break;
+
+ default: /* switch (proto) { */
+ /* Not supported... */
+ error_num = ERRCODE_NOTSUP;
+ break;
+ } /* switch (proto) { */
+
+ if (he) {
+ data_size = build_reply(serial, he, &reply, &reply_size);
+#ifdef HAVE_GETIPNODEBYNAME
+ if (free_he) {
+ freehostent(he);
+ }
+#endif
+#ifdef HAVE_GETADDRINFO
+ } else if (ai) {
+ data_size = build_reply_ai(serial, 16, ai,
+ &reply, &reply_size);
+ freeaddrinfo(ai);
+#endif
+ } else {
+ data_size = build_error_reply(serial, error_num,
+ &reply, &reply_size);
+ }
+ break; /* case OP_GETHOSTBYNAME: */
+
+ case OP_GETHOSTBYADDR: /* switch (op) { */
+ switch (proto) {
+#ifdef HAVE_IN6
+ case PROTO_IPV6: {
+#ifdef HAVE_GETNAMEINFO
+ struct sockaddr_in6 *sin;
+ socklen_t salen = sizeof(*sin);
+
+ sin = ALLOC(salen);
+#ifndef NO_SA_LEN
+ sin->sin6_len = salen;
+#endif
+ sin->sin6_family = AF_INET6;
+ sin->sin6_port = 0;
+ memcpy(&sin->sin6_addr, data, 16);
+ sa = (struct sockaddr *)sin;
+ DEBUGF(5,("Starting getnameinfo(,,%s,16,,,)",
+ format_address(16, data)));
+ error_num = getnameinfo(sa, salen, name, sizeof(name),
+ NULL, 0, NI_NAMEREQD);
+ DEBUGF(5,("getnameinfo returned %d", error_num));
+ if (error_num) {
+ error_num = map_netdb_error_ai(error_num);
+ sa = NULL;
+ }
+#elif defined(HAVE_GETIPNODEBYADDR) /*#ifdef HAVE_GETNAMEINFO*/
+ struct in6_addr ia;
+ memcpy(ia.s6_addr, data, 16);
+ DEBUGF(5,("Starting getipnodebyaddr(%s,16,AF_INET6,)",
+ format_address(16, data)));
+ he = getipnodebyaddr(&ia, 16, AF_INET6, &error_num);
+ free_he = 1;
+ if (! he) {
+ DEBUGF(5,("getipnodebyaddr error %d", error_num));
+ error_num = map_netdb_error(error_num);
+ } else {
+ DEBUGF(5,("getipnodebyaddr OK"));
+ }
+#else /*#ifdef HAVE_GETNAMEINFO*/
+ struct in6_addr ia;
+ memcpy(ia.s6_addr, data, 16);
+ DEBUGF(5,("Starting gethostbyaddr(%s,16,AF_INET6)",
+ format_address(16, data)));
+ he = gethostbyaddr((const char *) &ia, 16, AF_INET6);
+ if (! he) {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyaddr error %d", h_errno));
+ } else {
+ DEBUGF(5,("gethostbyaddr OK"));
+ }
+#endif /* #ifdef HAVE_GETNAMEINFO */
+ } break; /* case PROTO_IPV6: { */
+#endif /* #ifdef HAVE_IN6 */
+
+ case PROTO_IPV4: { /* switch(proto) { */
+ struct in_addr ia;
+ memcpy(&ia.s_addr, data, 4); /* Alignment required... */
+ DEBUGF(5,("Starting gethostbyaddr(%s,4,AF_INET)",
+ format_address(4, data)));
+ he = gethostbyaddr((const char *) &ia, 4, AF_INET);
+ if (! he) {
+ error_num = map_netdb_error(h_errno);
+ DEBUGF(5,("gethostbyaddr error %d", h_errno));
+ } else {
+ DEBUGF(5,("gethostbyaddr OK"));
+ }
+ } break;
+
+ default:
+ error_num = ERRCODE_NOTSUP;
+ } /* switch(proto) { */
+
+ if (he) {
+ data_size = build_reply(serial, he, &reply, &reply_size);
+#ifdef HAVE_GETIPNODEBYADDR
+ if (free_he) {
+ freehostent(he);
+ }
+#endif
+#ifdef HAVE_GETNAMEINFO
+ } else if (sa) {
+ struct addrinfo res;
+ memset(&res, 0, sizeof(res));
+ res.ai_canonname = name;
+ res.ai_addr = sa;
+ res.ai_next = NULL;
+ data_size = build_reply_ai(serial, 16, &res,
+ &reply, &reply_size);
+ free(sa);
+#endif
+ } else {
+ data_size = build_error_reply(serial, error_num,
+ &reply, &reply_size);
+ }
+ break; /* case OP_GETHOSTBYADR: */
+
+ default:
+ data_size = build_error_reply(serial, ERRCODE_NOTSUP,
+ &reply, &reply_size);
+ break;
+ } /* switch (op) { */
+
+#ifdef WIN32
+ m = REALLOC(m, sizeof(QueItem) - 1 + data_size - PACKET_BYTES);
+ m->next = NULL;
+ m->req_size = data_size - PACKET_BYTES;
+ memcpy(m->request,reply + PACKET_BYTES,data_size - PACKET_BYTES);
+ if (!enque_mesq(writeto,m)) {
+ goto fail;
+ }
+ m = NULL;
+#else
+ write(1, reply, data_size); /* No signals expected */
+#endif
+ } /* for (;;) */
+
+#ifdef WIN32
+ fail:
+ if (m != NULL) {
+ FREE(m);
+ }
+ close_mesq(readfrom);
+ close_mesq(writeto);
+ if (reply) {
+ FREE(reply);
+ }
+ return 1;
+#endif
+}
+
+static int map_netdb_error(int netdb_code)
+{
+ switch (netdb_code) {
+#ifdef HOST_NOT_FOUND
+ case HOST_NOT_FOUND:
+ return ERRCODE_HOST_NOT_FOUND;
+#endif
+#ifdef TRY_AGAIN
+ case TRY_AGAIN:
+ return ERRCODE_TRY_AGAIN;
+#endif
+#ifdef NO_RECOVERY
+ case NO_RECOVERY:
+ return ERRCODE_NO_RECOVERY;
+#endif
+#if defined(NO_DATA) || defined(NO_ADDRESS)
+#ifdef NO_DATA
+ case NO_DATA:
+#endif
+#ifdef NO_ADDRESS
+#if !defined(NO_DATA) || (NO_DATA != NO_ADDRESS)
+ case NO_ADDRESS:
+#endif
+#endif
+ return ERRCODE_NO_DATA;
+#endif
+ default:
+ return ERRCODE_NETDB_INTERNAL;
+ }
+}
+
+#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
+static int map_netdb_error_ai(int netdb_code)
+{
+ switch(netdb_code) {
+#ifdef EAI_ADDRFAMILY
+ case EAI_ADDRFAMILY:
+ return ERRCODE_NETDB_INTERNAL;
+#endif
+ case EAI_AGAIN:
+ return ERRCODE_TRY_AGAIN;
+ case EAI_BADFLAGS:
+ return ERRCODE_NETDB_INTERNAL;
+ case EAI_FAIL:
+ return ERRCODE_HOST_NOT_FOUND;
+ case EAI_FAMILY:
+ return ERRCODE_NETDB_INTERNAL;
+ case EAI_MEMORY:
+ return ERRCODE_NETDB_INTERNAL;
+#if defined(EAI_NODATA) && EAI_NODATA != EAI_NONAME
+ case EAI_NODATA:
+ return ERRCODE_HOST_NOT_FOUND;
+#endif
+ case EAI_NONAME:
+ return ERRCODE_HOST_NOT_FOUND;
+ case EAI_SERVICE:
+ return ERRCODE_NETDB_INTERNAL;
+ case EAI_SOCKTYPE:
+ return ERRCODE_NETDB_INTERNAL;
+ default:
+ return ERRCODE_NETDB_INTERNAL;
+ }
+}
+#endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
+
+
+static char *errcode_to_string(int errcode)
+{
+ switch (errcode) {
+ case ERRCODE_NOTSUP:
+ return "enotsup";
+ case ERRCODE_HOST_NOT_FOUND:
+ /*
+ * I would preffer
+ * return "host_not_found";
+ * but have to keep compatibility with the old
+ * inet_gethost's error codes...
+ */
+ return "notfound";
+ case ERRCODE_TRY_AGAIN:
+ return "try_again";
+ case ERRCODE_NO_RECOVERY:
+ return "no_recovery";
+ case ERRCODE_NO_DATA:
+ return "no_data";
+ default:
+ /*case ERRCODE_NETDB_INTERNAL:*/
+ return "netdb_internal";
+ }
+}
+
+static size_t build_error_reply(SerialType serial, int errnum,
+ AddrByte **preply,
+ size_t *preply_size)
+{
+ char *errstring = errcode_to_string(errnum);
+ int string_need = strlen(errstring) + 1; /* a '\0' too */
+ unsigned need;
+ AddrByte *ptr;
+
+ need = PACKET_BYTES + 4 /* Serial */ + 1 /* Unit */ + string_need;
+ if (*preply_size < need) {
+ if (*preply_size == 0) {
+ *preply = ALLOC((*preply_size = need));
+ } else {
+ *preply = REALLOC(*preply,
+ (*preply_size = need));
+ }
+ }
+ ptr = *preply;
+ PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
+ ptr += PACKET_BYTES;
+ put_int32(ptr,serial);
+ ptr +=4;
+ *ptr++ = (AddrByte) 0; /* 4 or 16 */
+ strcpy((char*)ptr, errstring);
+ return need;
+}
+
+
+
+static size_t build_reply(SerialType serial, struct hostent *he,
+ AddrByte **preply, size_t *preply_size)
+{
+ unsigned need;
+ int strings_need;
+ int num_strings;
+ int num_addresses;
+ int i;
+ AddrByte *ptr;
+ int unit = he->h_length;
+
+ for (num_addresses = 0; he->h_addr_list[num_addresses] != NULL;
+ ++num_addresses)
+ ;
+ strings_need = strlen(he->h_name) + 1; /* 1 for null byte */
+ num_strings = 1;
+ if (he->h_aliases) {
+ for(i=0; he->h_aliases[i] != NULL; ++i) {
+ strings_need += strlen(he->h_aliases[i]) + 1;
+ ++num_strings;
+ }
+ }
+
+ need = PACKET_BYTES +
+ 4 /* Serial */ + 1 /* Unit */ + 4 /* Naddr */ +
+ (unit * num_addresses) /* Address bytes */ +
+ 4 /* Nnames */ + strings_need /* The name and alias strings */;
+
+ if (*preply_size < need) {
+ if (*preply_size == 0) {
+ *preply = ALLOC((*preply_size = need));
+ } else {
+ *preply = REALLOC(*preply,
+ (*preply_size = need));
+ }
+ }
+ ptr = *preply;
+ PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
+ ptr += PACKET_BYTES;
+ put_int32(ptr,serial);
+ ptr +=4;
+ *ptr++ = (AddrByte) unit; /* 4 or 16 */
+ put_int32(ptr, num_addresses);
+ ptr += 4;
+ for (i = 0; i < num_addresses; ++i) {
+ memcpy(ptr, he->h_addr_list[i], unit);
+ ptr += unit;
+ }
+ put_int32(ptr, num_strings);
+ ptr += 4;
+ strcpy((char*)ptr, he->h_name);
+ ptr += 1 + strlen(he->h_name);
+ for (i = 0; i < (num_strings - 1); ++i) {
+ strcpy((char*)ptr, he->h_aliases[i]);
+ ptr += 1 + strlen(he->h_aliases[i]);
+ }
+ return need;
+}
+
+#if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO)
+static size_t build_reply_ai(SerialType serial, int addrlen,
+ struct addrinfo *res0,
+ AddrByte **preply, size_t *preply_size)
+{
+ struct addrinfo *res;
+ int num_strings;
+ int num_addresses;
+ AddrByte *ptr;
+ int need;
+
+ num_addresses = 0;
+ num_strings = 0;
+ need = PACKET_BYTES +
+ 4 /* Serial */ + 1 /* addrlen */ +
+ 4 /* Naddr */ + 4 /* Nnames */;
+
+ for (res = res0; res != NULL; res = res->ai_next) {
+ if (res->ai_addr) {
+ num_addresses++;
+ need += addrlen;
+ }
+ if (res->ai_canonname) {
+ num_strings++;
+ need += strlen(res->ai_canonname) + 1;
+ }
+ }
+
+ if (*preply_size < need) {
+ if (*preply_size == 0) {
+ *preply = ALLOC((*preply_size = need));
+ } else {
+ *preply = REALLOC(*preply,
+ (*preply_size = need));
+ }
+ }
+
+ ptr = *preply;
+ PUT_PACKET_BYTES(ptr,need - PACKET_BYTES);
+ ptr += PACKET_BYTES;
+ put_int32(ptr,serial);
+ ptr +=4;
+ *ptr++ = (AddrByte) addrlen; /* 4 or 16 */
+ put_int32(ptr, num_addresses);
+ ptr += 4;
+ for (res = res0; res != NULL && num_addresses; res = res->ai_next) {
+ if (res->ai_addr == NULL)
+ continue;
+ if (addrlen == 4)
+ memcpy(ptr, &((struct sockaddr_in *)res->ai_addr)->sin_addr, addrlen);
+#ifdef AF_INET6
+ else if (addrlen == 16)
+ memcpy(ptr, &((struct sockaddr_in6 *)res->ai_addr)->sin6_addr, addrlen);
+#endif
+ else
+ memcpy(ptr, res->ai_addr->sa_data, addrlen);
+ ptr += addrlen;
+ num_addresses--;
+ }
+ put_int32(ptr, num_strings);
+ ptr += 4;
+ for (res = res0; res != NULL && num_strings; res = res->ai_next) {
+ if (res->ai_canonname == NULL)
+ continue;
+ strcpy((char *)ptr, res->ai_canonname);
+ ptr += strlen(res->ai_canonname) + 1;
+ num_strings--;
+ }
+ return need;
+}
+
+#endif /* #if defined(HAVE_GETADDRINFO) || defined(HAVE_GETNAMEINFO) */
+
+
+
+/*
+ * Encode/decode/read/write
+ */
+
+static int get_int32(AddrByte *b)
+{
+ int res;
+ res = (unsigned) b[3];
+ res |= ((unsigned) b[2]) << 8;
+ res |= ((unsigned) b[1]) << 16;
+ res |= ((unsigned) b[0]) << 24;
+ return res;
+}
+
+static void put_int32(AddrByte *buff, int value)
+{
+ buff[0] = (((unsigned) value) >> 24) & 0xFF;
+ buff[1] = (((unsigned) value) >> 16) & 0xFF;
+ buff[2] = (((unsigned) value) >> 8) & 0xFF;
+ buff[3] = ((unsigned) value) & 0xFF;
+}
+#ifdef WIN32
+
+static int read_int32(HANDLE fd, int *res, HANDLE ev)
+{
+ AddrByte b[4];
+ int r;
+ if ((r = read_exact(fd,b,4,ev)) < 0) {
+ return -1;
+ } else if (r == 0) {
+ return 0;
+ } else {
+ *res = (unsigned) b[3];
+ *res |= ((unsigned) b[2]) << 8;
+ *res |= ((unsigned) b[1]) << 16;
+ *res |= ((unsigned) b[0]) << 24;
+ }
+ return 4;
+}
+/*
+ * The standard input is expected to be opened with FILE_FLAG_OVERLAPPED
+ * but this code should handle both cases (although winsock might not).
+ */
+static int read_exact(HANDLE fd, void *vbuff, DWORD nbytes, HANDLE ev)
+{
+ DWORD ret,got;
+ BOOL stat;
+ char *buff = vbuff;
+ OVERLAPPED ov;
+ DWORD err;
+
+
+ got = 0;
+ for(;;) {
+ memset(&ov,0,sizeof(ov));
+ ov.hEvent = ev;
+ ResetEvent(ov.hEvent);
+ stat = ReadFile(fd, buff, nbytes - got, &ret, &ov);
+ if (!stat) {
+ if ((err = GetLastError()) == ERROR_IO_PENDING) {
+ DEBUGF(4,("Overlapped read, waiting for completion..."));
+ WaitForSingleObject(ov.hEvent,INFINITE);
+ stat = GetOverlappedResult(fd,&ov,&ret,TRUE);
+ DEBUGF(4,("Overlapped read, completed with status %d,"
+ " result %d",stat,ret));
+ }
+ if (!stat) {
+ if (GetLastError() == ERROR_BROKEN_PIPE) {
+ DEBUGF(1, ("End of file while reading from pipe."));
+ return 0;
+ } else {
+ DEBUGF(1, ("Error while reading from pipe,"
+ " errno = %d",
+ GetLastError()));
+ return -1;
+ }
+ }
+ } else {
+ DEBUGF(4,("Read completed syncronously, result %d",ret));
+ }
+ if (ret == 0) {
+ DEBUGF(1, ("End of file detected as zero read from pipe."));
+ return 0;
+ }
+ if (ret < nbytes - got) {
+ DEBUGF(4,("Not all data read from pipe, still %d bytes to read.",
+ nbytes - (got + ret)));
+ got += ret;
+ buff += ret;
+ } else {
+ return nbytes;
+ }
+ }
+}
+/*
+ * Now, we actually expect a HANDLE opened with FILE_FLAG_OVERLAPPED,
+ * but this code should handle both cases (although winsock
+ * does not always..)
+ */
+static int write_exact(HANDLE fd, AddrByte *buff, DWORD len, HANDLE ev)
+{
+ DWORD res,stat;
+ DWORD x = len;
+ OVERLAPPED ov;
+ DWORD err;
+
+
+ for(;;) {
+ memset(&ov,0,sizeof(ov));
+ ov.hEvent = ev;
+ ResetEvent(ov.hEvent);
+ stat = WriteFile(fd,buff,x,&res,&ov);
+ if (!stat) {
+ if ((err = GetLastError()) == ERROR_IO_PENDING) {
+ DEBUGF(4,("Overlapped write, waiting for competion..."));
+ WaitForSingleObject(ov.hEvent,INFINITE);
+ stat = GetOverlappedResult(fd,&ov,&res,TRUE);
+ DEBUGF(4,("Overlapped write, completed with status %d,"
+ " result %d",stat,res));
+ }
+ if (!stat) {
+ if (GetLastError() == ERROR_BROKEN_PIPE) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ } else {
+ DEBUGF(4,("Write completed syncronously, result %d",res));
+ }
+
+ if (res < x) {
+ /* Microsoft states this can happen as HANDLE is a pipe... */
+ DEBUGF(4,("Not all data written to pipe, still %d bytes to write.",
+ x - res));
+ x -= res;
+ buff += res;
+ } else {
+ return len;
+ }
+ }
+}
+
+DWORD WINAPI reader(void *data) {
+ MesQ *mq = (MesQ *) data;
+ QueItem *m;
+ int siz;
+ int r;
+ HANDLE inp;
+ int x = 0;
+ HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+ inp = GetStdHandle(STD_INPUT_HANDLE);
+ for (;;) {
+ if ((r = READ_PACKET_BYTES(inp,&siz,ev)) != 4) {
+ DEBUGF(1,("Erlang has closed (reading)"));
+ exit(0);
+ }
+ DEBUGF(4,("Read packet of size %d from erlang",siz));
+ m = ALLOC(sizeof(QueItem) - 1 + siz);
+ if (read_exact(inp, m->request, siz,ev) != siz) {
+ fatal("Unexpected end of file on main input, errno = %d",errno);
+ }
+ if (siz < 5) {
+ fatal("Unexpected message on main input, message size %d less "
+ "than minimum.");
+ }
+ m->req_size = siz;
+ m->next = NULL;
+ if (!enque_mesq(mq, m)) {
+ fatal("Reader could not talk to main thread!");
+ }
+ }
+}
+
+DWORD WINAPI writer(void *data)
+{
+ MesQ *mq = (MesQ *) data;
+ QueItem *m;
+ HANDLE outp = GetStdHandle(STD_OUTPUT_HANDLE);
+ AddrByte hdr[PACKET_BYTES];
+ HANDLE ev = CreateEvent(NULL, TRUE, FALSE, NULL);
+
+
+ for (;;) {
+ WaitForSingleObject(event_mesq(mq),INFINITE);
+ if (!deque_mesq(mq, &m)) {
+ fatal("Writer could not talk to main thread!");
+ }
+ PUT_PACKET_BYTES(hdr, m->req_size);
+ if (write_exact(outp, hdr, 4, ev) != 4) {
+ DEBUGF(1,("Erlang has closed (writing)"));
+ exit(0);
+ }
+ if (write_exact(outp, m->request, m->req_size, ev) != m->req_size) {
+ DEBUGF(1,("Erlang has closed (writing)"));
+ exit(0);
+ }
+ FREE(m);
+ }
+}
+
+
+#else
+
+static size_t read_int32(int fd, int *res)
+{
+ AddrByte b[4];
+ int r;
+ if ((r = read_exact(fd,b,4)) < 0) {
+ return -1;
+ } else if (r == 0) {
+ return 0;
+ } else {
+ *res = (unsigned) b[3];
+ *res |= ((unsigned) b[2]) << 8;
+ *res |= ((unsigned) b[1]) << 16;
+ *res |= ((unsigned) b[0]) << 24;
+ }
+ return 4;
+}
+
+static ssize_t read_exact(int fd, void *vbuff, size_t nbytes)
+{
+ ssize_t ret, got;
+ char *buff = vbuff;
+
+ got = 0;
+ for(;;) {
+ ret = read(fd, buff, nbytes - got);
+ if (ret < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else {
+ DEBUGF(1, ("Error while reading from pipe,"
+ " errno = %d",
+ errno));
+ return -1;
+ }
+ } else if (ret == 0) {
+ DEBUGF(1, ("End of file while reading from pipe."));
+ if (got == 0) {
+ return 0; /* "Normal" EOF */
+ } else {
+ return -1;
+ }
+ } else if (ret < nbytes - got) {
+ got += ret;
+ buff += ret;
+ } else {
+ return nbytes;
+ }
+ }
+}
+
+static int write_exact(int fd, AddrByte *buff, int len)
+{
+ int res;
+ int x = len;
+ for(;;) {
+ if((res = write(fd, buff, x)) == x) {
+ break;
+ }
+ if (res < 0) {
+ if (errno == EINTR) {
+ continue;
+ } else if (errno == EPIPE) {
+ return 0;
+ }
+#ifdef ENXIO
+ else if (errno == ENXIO) {
+ return 0;
+ }
+#endif
+ else {
+ return -1;
+ }
+ } else {
+ /* Hmmm, blocking write but not all written, could this happen
+ if the other end was closed during the operation? Well,
+ it costs very little to handle anyway... */
+ x -= res;
+ buff += res;
+ }
+ }
+ return len;
+}
+
+#endif /* !WIN32 */
+
+/*
+ * Debug and memory allocation
+ */
+
+static char *format_address(int siz, AddrByte *addr)
+{
+ static char buff[50];
+ char tmp[10];
+ if (siz > 16) {
+ return "(unknown)";
+ }
+ *buff='\0';
+ if (siz <= 4) {
+ while(siz--) {
+ sprintf(tmp,"%d",(int) *addr++);
+ strcat(buff,tmp);
+ if(siz) {
+ strcat(buff,".");
+ }
+ }
+ return buff;
+ }
+ while(siz--) {
+ sprintf(tmp,"%02x",(int) *addr++);
+ strcat(buff,tmp);
+ if(siz) {
+ strcat(buff,":");
+ }
+ }
+ return buff;
+}
+
+static void debugf(char *format, ...)
+{
+ char buff[2048];
+ char *ptr;
+ va_list ap;
+
+ va_start(ap,format);
+#ifdef WIN32
+ sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) GetCurrentThreadId());
+#else
+ sprintf(buff,"%s[%d] (DEBUG):",program_name,(int) getpid());
+#endif
+ ptr = buff + strlen(buff);
+ vsprintf(ptr,format,ap);
+ strcat(ptr,"\r\n");
+#ifdef WIN32
+ if (debug_console_allocated != INVALID_HANDLE_VALUE) {
+ DWORD res;
+ WriteFile(debug_console_allocated,buff,strlen(buff),&res,NULL);
+ }
+#else
+ write(2,buff,strlen(buff));
+#endif
+ va_end(ap);
+}
+
+static void warning(char *format, ...)
+{
+ char buff[2048];
+ char *ptr;
+ va_list ap;
+
+ va_start(ap,format);
+ sprintf(buff,"%s[%d]: WARNING:",program_name, (int) getpid());
+ ptr = buff + strlen(buff);
+ vsprintf(ptr,format,ap);
+ strcat(ptr,"\r\n");
+#ifdef WIN32
+ {
+ DWORD res;
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
+ }
+#else
+ write(2,buff,strlen(buff));
+#endif
+ va_end(ap);
+}
+
+static void fatal(char *format, ...)
+{
+ char buff[2048];
+ char *ptr;
+ va_list ap;
+
+ va_start(ap,format);
+ sprintf(buff,"%s[%d]: FATAL ERROR:",program_name, (int) getpid());
+ ptr = buff + strlen(buff);
+ vsprintf(ptr,format,ap);
+ strcat(ptr,"\r\n");
+#ifdef WIN32
+ {
+ DWORD res;
+ WriteFile(GetStdHandle(STD_ERROR_HANDLE),buff,strlen(buff),&res,NULL);
+ }
+#else
+ write(2,buff,strlen(buff));
+#endif
+ va_end(ap);
+#ifndef WIN32
+ kill_all_workers();
+#endif
+ exit(1);
+}
+
+static void *my_malloc(size_t size)
+{
+ void *ptr = malloc(size);
+ if (!ptr) {
+ fatal("Cannot allocate %u bytes of memory.", (unsigned) size);
+ return NULL; /* lint... */
+ }
+ return ptr;
+}
+
+static void *my_realloc(void *old, size_t size)
+{
+ void *ptr = realloc(old, size);
+ if (!ptr) {
+ fatal("Cannot reallocate %u bytes of memory from %p.",
+ (unsigned) size, old);
+ return NULL; /* lint... */
+ }
+ return ptr;
+}
+
+#ifdef WIN32
+
+BOOL create_mesq(MesQ **q)
+{
+ MesQ *tmp = malloc(sizeof(MesQ));
+ tmp->data_present = CreateEvent(NULL, TRUE, FALSE,NULL);
+ if (tmp->data_present == NULL) {
+ free(tmp);
+ return FALSE;
+ }
+ InitializeCriticalSection(&(tmp->crit)); /* Cannot fail */
+ tmp->shutdown = 0;
+ tmp->first = NULL;
+ tmp->last = NULL;
+ *q = tmp;
+ return TRUE;
+}
+
+BOOL enque_mesq(MesQ *q, QueItem *m)
+{
+ EnterCriticalSection(&(q->crit));
+ if (q->shutdown) {
+ LeaveCriticalSection(&(q->crit));
+ return FALSE;
+ }
+ if (q->last == NULL) {
+ q->first = q->last = m;
+ } else {
+ q->last->next = m;
+ q->last = m;
+ }
+ m->next = NULL;
+ if (!SetEvent(q->data_present)) {
+ fprintf(stderr,"Fatal: Unable to signal event in %s:%d, last error: %d\n",
+ __FILE__,__LINE__,GetLastError());
+ exit(1); /* Unable to continue at all */
+ }
+ LeaveCriticalSection(&(q->crit));
+ return TRUE;
+}
+
+BOOL deque_mesq(MesQ *q, QueItem **m)
+{
+ EnterCriticalSection(&(q->crit));
+ if (q->first == NULL) { /* Usually shutdown from other end */
+ ResetEvent(q->data_present);
+ LeaveCriticalSection(&(q->crit));
+ return FALSE;
+ }
+ *m = q->first;
+ q->first = q->first->next;
+ if (q->first == NULL) {
+ q->last = NULL;
+ ResetEvent(q->data_present);
+ }
+ (*m)->next = NULL;
+ LeaveCriticalSection(&(q->crit));
+ return TRUE;
+}
+
+BOOL close_mesq(MesQ *q)
+{
+ QueItem *tmp;
+ EnterCriticalSection(&(q->crit));
+ if (!q->shutdown) {
+ q->shutdown = TRUE;
+ if (!SetEvent(q->data_present)) {
+ fprintf(stderr,
+ "Fatal: Unable to signal event in %s:%d, last error: %d\n",
+ __FILE__,__LINE__,GetLastError());
+ exit(1); /* Unable to continue at all */
+ }
+ LeaveCriticalSection(&(q->crit));
+ return FALSE;
+ }
+ /* Noone else is supposed to use this object any more */
+ LeaveCriticalSection(&(q->crit));
+ DeleteCriticalSection(&(q->crit));
+ CloseHandle(q->data_present);
+ tmp = q->first;
+ while(tmp) {
+ q->first = q->first->next;
+ free(tmp);
+ tmp = q->first;
+ }
+ free(q);
+ return TRUE;
+}
+
+HANDLE event_mesq(MesQ *q)
+{
+ return q->data_present;
+}
+
+#ifdef HARDDEBUG
+DWORD WINAPI pseudo_worker_loop(void *v)
+{
+ HOSTENT *hep;
+
+ DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") starting"));
+ hep = gethostbyname("ftp.funet.fi");
+
+ DEBUGF(1,("gethostbyname(\"ftp.funet.fi\") -> %d OK",(int) hep));
+ return 0;
+}
+
+static void poll_gethost(int row) {
+ HANDLE h;
+ DWORD tid;
+ h = (HANDLE) _beginthreadex(NULL, 0, pseudo_worker_loop, NULL, 0, &tid);
+ if (h == NULL) {
+ DEBUGF(1,("Failed to spawn pseudo worker (%d)...",row));
+ } else {
+ DEBUGF(1,("Waiting for pseudo worker (%d)", row));
+ WaitForSingleObject(h,INFINITE);
+ DEBUGF(1,("Done Waiting for pseudo worker (%d)", row));
+ }
+}
+#endif
+
+#endif /* WIN32 */
diff --git a/erts/etc/common/typer.c b/erts/etc/common/typer.c
new file mode 100644
index 0000000000..c2567cb8b4
--- /dev/null
+++ b/erts/etc/common/typer.c
@@ -0,0 +1,416 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Purpose: Typer front-end.
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#ifdef __WIN32__
+#include <winbase.h>
+#endif
+
+#include <ctype.h>
+
+#define NO 0
+#define YES 1
+
+#define ASIZE(a) (sizeof(a)/sizeof(a[0]))
+
+static int debug = 0; /* Bit flags for debug printouts. */
+
+static char** eargv_base; /* Base of vector. */
+static char** eargv; /* First argument for erl. */
+
+static int eargc; /* Number of arguments in eargv. */
+
+#ifdef __WIN32__
+# define QUOTE(s) possibly_quote(s)
+# define IS_DIRSEP(c) ((c) == '/' || (c) == '\\')
+# define ERL_NAME "erl.exe"
+#else
+# define QUOTE(s) s
+# define IS_DIRSEP(c) ((c) == '/')
+# define ERL_NAME "erl"
+#endif
+
+#define UNSHIFT(s) eargc++, eargv--; eargv[0] = QUOTE(s)
+#define PUSH(s) eargv[eargc++] = QUOTE(s)
+#define PUSH2(s, t) PUSH(s); PUSH(t)
+#define PUSH3(s, t, u) PUSH2(s, t); PUSH(u)
+
+/*
+ * Local functions.
+ */
+
+static void error(char* format, ...);
+static char* emalloc(size_t size);
+static char* strsave(char* string);
+static void push_words(char* src);
+static int run_erlang(char* name, char** argv);
+static char* get_default_emulator(char* progname);
+#ifdef __WIN32__
+static char* possibly_quote(char* arg);
+#endif
+
+/*
+ * Supply a strerror() function if libc doesn't.
+ */
+#ifndef HAVE_STRERROR
+
+extern int sys_nerr;
+
+#ifndef SYS_ERRLIST_DECLARED
+extern const char * const sys_errlist[];
+#endif /* !SYS_ERRLIST_DECLARED */
+
+char *strerror(int errnum)
+{
+ static char *emsg[1024];
+
+ if (errnum != 0) {
+ if (errnum > 0 && errnum < sys_nerr)
+ sprintf((char *) &emsg[0], "(%s)", sys_errlist[errnum]);
+ else
+ sprintf((char *) &emsg[0], "errnum = %d ", errnum);
+ }
+ else {
+ emsg[0] = '\0';
+ }
+ return (char *) &emsg[0];
+}
+#endif /* !HAVE_STRERROR */
+
+int
+main(int argc, char** argv)
+{
+ int eargv_size;
+ int eargc_base; /* How many arguments in the base of eargv. */
+ char* emulator;
+ int need_shell = 0;
+
+ emulator = get_default_emulator(argv[0]);
+
+ /*
+ * Allocate the argv vector to be used for arguments to Erlang.
+ * Arrange for starting to pushing information in the middle of
+ * the array, to allow easy addition of commands in the beginning.
+ */
+
+ eargv_size = argc*4+100;
+ eargv_base = (char **) emalloc(eargv_size*sizeof(char*));
+ eargv = eargv_base;
+ eargc = 0;
+ push_words(emulator);
+ eargc_base = eargc;
+ eargv = eargv + eargv_size/2;
+ eargc = 0;
+
+ /*
+ * Push initial arguments.
+ */
+
+ if (argc > 1 && strcmp(argv[1], "-smp") == 0) {
+ PUSH("-smpauto");
+ argc--, argv++;
+ }
+
+ PUSH("+B");
+ PUSH2("-boot", "start_clean");
+ PUSH3("-run", "typer", "start");
+ PUSH("-extra");
+
+ /*
+ * Push everything except --shell.
+ */
+
+ while (argc > 1) {
+ if (strcmp(argv[1], "--shell") == 0) {
+ need_shell = 1;
+ } else {
+ PUSH(argv[1]);
+ }
+ argc--, argv++;
+ }
+
+ if (!need_shell) {
+ UNSHIFT("-noinput");
+ }
+
+ /*
+ * Move up the commands for invoking the emulator and adjust eargv
+ * accordingly.
+ */
+
+ while (--eargc_base >= 0) {
+ UNSHIFT(eargv_base[eargc_base]);
+ }
+
+ /*
+ * Invoke Erlang with the collected options.
+ */
+
+ PUSH(NULL);
+ return run_erlang(eargv[0], eargv);
+}
+
+static void
+push_words(char* src)
+{
+ char sbuf[1024];
+ char* dst;
+
+ dst = sbuf;
+ while ((*dst++ = *src++) != '\0') {
+ if (isspace((int)*src)) {
+ *dst = '\0';
+ PUSH(strsave(sbuf));
+ dst = sbuf;
+ do {
+ src++;
+ } while (isspace((int)*src));
+ }
+ }
+ if (sbuf[0])
+ PUSH(strsave(sbuf));
+}
+#ifdef __WIN32__
+char *make_commandline(char **argv)
+{
+ static char *buff = NULL;
+ static int siz = 0;
+ int num = 0;
+ char **arg, *p;
+
+ if (*argv == NULL) {
+ return "";
+ }
+ for (arg = argv; *arg != NULL; ++arg) {
+ num += strlen(*arg)+1;
+ }
+ if (!siz) {
+ siz = num;
+ buff = malloc(siz*sizeof(char));
+ } else if (siz < num) {
+ siz = num;
+ buff = realloc(buff,siz*sizeof(char));
+ }
+ p = buff;
+ for (arg = argv; *arg != NULL; ++arg) {
+ strcpy(p,*arg);
+ p+=strlen(*arg);
+ *p++=' ';
+ }
+ *(--p) = '\0';
+
+ if (debug) {
+ printf("Processed commandline:%s\n",buff);
+ }
+ return buff;
+}
+
+int my_spawnvp(char **argv)
+{
+ STARTUPINFO siStartInfo;
+ PROCESS_INFORMATION piProcInfo;
+ DWORD ec;
+
+ memset(&siStartInfo,0,sizeof(STARTUPINFO));
+ siStartInfo.cb = sizeof(STARTUPINFO);
+ siStartInfo.dwFlags = STARTF_USESTDHANDLES;
+ siStartInfo.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ siStartInfo.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ siStartInfo.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ siStartInfo.wShowWindow = SW_HIDE;
+ siStartInfo.dwFlags |= STARTF_USESHOWWINDOW;
+
+
+ if (!CreateProcess(NULL,
+ make_commandline(argv),
+ NULL,
+ NULL,
+ TRUE,
+ 0,
+ NULL,
+ NULL,
+ &siStartInfo,
+ &piProcInfo)) {
+ return -1;
+ }
+ CloseHandle(piProcInfo.hThread);
+
+ WaitForSingleObject(piProcInfo.hProcess,INFINITE);
+ if (!GetExitCodeProcess(piProcInfo.hProcess,&ec)) {
+ return 0;
+ }
+ return (int) ec;
+}
+#endif /* __WIN32__ */
+
+
+static int
+run_erlang(char* progname, char** argv)
+{
+#ifdef __WIN32__
+ int status;
+#endif
+
+ if (debug) {
+ int i = 0;
+ while (argv[i] != NULL)
+ printf(" %s", argv[i++]);
+ printf("\n");
+ }
+
+#ifdef __WIN32__
+ /*
+ * Alas, we must wait here for the program to finish.
+ * Otherwise, the shell from which we were executed will think
+ * we are finished and print a prompt and read keyboard input.
+ */
+
+ status = my_spawnvp(argv)/*_spawnvp(_P_WAIT,progname,argv)*/;
+ if (status == -1) {
+ fprintf(stderr, "typer: Error executing '%s': %d", progname,
+ GetLastError());
+ }
+ return status;
+#else
+ execvp(progname, argv);
+ error("Error %d executing \'%s\'.", errno, progname);
+ return 2;
+#endif
+}
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "typer: %s\n", sbuf);
+ exit(1);
+}
+
+static char*
+emalloc(size_t size)
+{
+ char *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+static char*
+get_default_emulator(char* progname)
+{
+ char sbuf[MAXPATHLEN];
+ char* s;
+
+ strcpy(sbuf, progname);
+ for (s = sbuf+strlen(sbuf); s >= sbuf; s--) {
+ if (IS_DIRSEP(*s)) {
+ strcpy(s+1, ERL_NAME);
+#ifdef __WIN32__
+ if (_access(sbuf, 0) != -1) {
+ return strsave(sbuf);
+ }
+#else
+ if (access(sbuf, 1) != -1) {
+ return strsave(sbuf);
+ }
+#endif
+ break;
+ }
+ }
+ return ERL_NAME;
+}
+
+#ifdef __WIN32__
+static char*
+possibly_quote(char* arg)
+{
+ int mustQuote = NO;
+ int n = 0;
+ char* s;
+ char* narg;
+
+ if (arg == NULL) {
+ return arg;
+ }
+
+ /*
+ * Scan the string to find out if it needs quoting and return
+ * the original argument if not.
+ */
+
+ for (s = arg; *s; s++, n++) {
+ switch(*s) {
+ case ' ':
+ mustQuote = YES;
+ continue;
+ case '"':
+ mustQuote = YES;
+ n++;
+ continue;
+ case '\\':
+ if(s[1] == '"')
+ n++;
+ continue;
+ default:
+ continue;
+ }
+ }
+ if (!mustQuote) {
+ return arg;
+ }
+
+ /*
+ * Insert the quotes and put a backslash in front of every quote
+ * inside the string.
+ */
+
+ s = narg = emalloc(n+2+1);
+ for (*s++ = '"'; *arg; arg++, s++) {
+ if (*arg == '"' || (*arg == '\\' && arg[1] == '"')) {
+ *s++ = '\\';
+ }
+ *s = *arg;
+ }
+ if (s[-1] == '\\') {
+ *s++ ='\\';
+ }
+ *s++ = '"';
+ *s = '\0';
+ return narg;
+}
+#endif /* __WIN32__ */
diff --git a/erts/etc/unix/Install.src b/erts/etc/unix/Install.src
new file mode 100644
index 0000000000..410a77d91c
--- /dev/null
+++ b/erts/etc/unix/Install.src
@@ -0,0 +1,175 @@
+#!/bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Patch $ERL_ROOT/emulator/obj/Makefile.dist & make
+#
+#
+start_option=query
+unset cross
+while [ $# -ne 0 ]; do
+ case $1 in
+ -minimal) start_option=minimal ;;
+ -sasl) start_option=sasl ;;
+ -cross) cross=yes ;;
+ *) ERL_ROOT=$1 ;;
+ esac
+ shift
+done
+
+if [ -z "$cross" ]
+then
+ TARGET_ERL_ROOT="$ERL_ROOT"
+else
+ TARGET_ERL_ROOT="$ERL_ROOT"
+ ERL_ROOT=`pwd`
+fi
+
+if [ -z "$ERL_ROOT" -o ! -d "$ERL_ROOT" ]
+then
+ echo "Install: need ERL_ROOT directory as argument"
+ exit 1
+fi
+
+case ":$ERL_ROOT" in
+ :/*)
+ ;;
+ *)
+ echo "Install: need an absolute path to ERL_ROOT"
+ exit 1
+ ;;
+esac
+
+if [ ! -d "$ERL_ROOT/erts-%I_VSN%/bin" ]
+then
+ echo "Install: The directory $ERL_ROOT/erts-%I_VSN%/bin does not exist"
+ echo " Bad location or erts module not un-tared"
+ exit 1
+fi
+
+if [ ! -d $ERL_ROOT/bin ]
+then
+ mkdir $ERL_ROOT/bin
+fi
+
+#
+# Fetch target system.
+#
+SYS=`(uname -s) 2>/dev/null` || SYS=unknown
+REL=`(uname -r) 2>/dev/null` || REL=unknown
+case $SYS:$REL in
+ SunOS:5.*)
+ TARGET=sunos5 ;;
+ Linux:*)
+ TARGET=linux ;;
+ *)
+ TARGET="" ;;
+esac
+
+cd $ERL_ROOT/erts-%I_VSN%/bin
+
+sed -e "s;%FINAL_ROOTDIR%;$TARGET_ERL_ROOT;" erl.src > erl
+chmod 755 erl
+
+#
+# Create start file for embedded system use,
+#
+(cd $ERL_ROOT/erts-%I_VSN%/bin;
+ sed -e "s;%FINAL_ROOTDIR%;$TARGET_ERL_ROOT;" start.src > start;
+ chmod 755 start)
+
+cd $ERL_ROOT/bin
+
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/erl .
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/erlc .
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/dialyzer .
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/typer .
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/escript .
+
+#
+# Set a soft link to epmd
+# This should not be done for an embedded system!
+#
+
+# Remove old links first.
+if [ -h epmd ]; then
+ /bin/rm -f epmd
+fi
+
+ln -s $TARGET_ERL_ROOT/erts-%I_VSN%/bin/epmd epmd
+
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/run_erl .
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/to_erl .
+cp -p $ERL_ROOT/erts-%I_VSN%/bin/start .
+sed -e "s;%EMU%;%EMULATOR%%EMULATOR_NUMBER%;" $ERL_ROOT/erts-%I_VSN%/bin/start_erl.src > start_erl
+chmod 755 start_erl
+echo ""
+
+echo %I_VSN% %I_SYSTEM_VSN% > $ERL_ROOT/releases/start_erl.data
+sed -e "s;%ERL_ROOT%;$TARGET_ERL_ROOT;" $ERL_ROOT/releases/RELEASES.src > $ERL_ROOT/releases/RELEASES
+
+if [ "$start_option" = "query" ]
+then
+ echo "Do you want to use a minimal system startup"
+ echo "instead of the SASL startup? (y/n) [n]: " | tr -d '\012'
+ read reply
+ case $reply in
+ [Yy]*)
+ start_option=minimal ;;
+ *)
+ start_option=sasl ;;
+ esac
+fi
+
+case $start_option in
+ minimal)
+ Name=start_clean ;;
+ sasl)
+ Name=start_sasl ;;
+ *)
+ Name=start_sasl ;;
+esac
+
+cp -p ../releases/%I_SYSTEM_VSN%/start_*.boot .
+cp -p $Name.boot start.boot
+cp -p ../releases/%I_SYSTEM_VSN%/$Name.script start.script
+
+#
+# We always run ranlib unless Solaris/SunOS 5
+# but ignore failures.
+#
+if [ "X$TARGET" != "Xsunos5" -a -d $ERL_ROOT/usr/lib ]; then
+ cd $ERL_ROOT/usr/lib
+ for library in lib*.a
+ do
+ (ranlib $library) > /dev/null 2>&1
+ done
+fi
+
+
+#
+# Fixing the man pages
+#
+
+if [ -d $ERL_ROOT/man ]
+then
+ cd $ERL_ROOT
+ ./misc/format_man_pages $ERL_ROOT
+fi
+
+
diff --git a/erts/etc/unix/README b/erts/etc/unix/README
new file mode 100644
index 0000000000..45b4aec2da
--- /dev/null
+++ b/erts/etc/unix/README
@@ -0,0 +1,111 @@
+
+ %CopyrightBegin%
+
+ Copyright Ericsson AB 1996-2009. All Rights Reserved.
+
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ %CopyrightEnd%
+
+--------------------------------------------------------------------------------
+%sunos4 This is Erlang version %VERSION% for SunOS 4.
+%solaris2 This is Erlang version %VERSION% for Solaris 2 (SunOS 5).
+%isc32 This is Erlang version %VERSION% for Interactive UNIX.
+%aix This is Erlang version %VERSION% for AIX.
+%hpux This is Erlang version %VERSION% for HP-UX.
+%osf This is Erlang version %VERSION% for OSF/1 (currently unsupported).
+%linux This is Erlang version %VERSION% for Linux.
+%qnx This is Erlang version %VERSION% for QNX.
+%freebsd This is Erlang version %VERSION% for FreeBSD.
+
+
+Installation
+------------
+
+Please refer to the "System Administrator's Guide" for a description
+of how to install the Erlang system. Ultra-short summary for the
+impatient: Run the 'Install' script in this directory and answer the
+questions; defaults (if any) are given in square brackets [] at the
+end of each question.
+
+Note that the Install script will terminate if it detects problems -
+you will have to correct them and re-run the script. If everything
+goes well, the last printout should be:
+
+Erlang installation sucessfully completed
+
+If it isn't, something went wrong - check the printouts to find out
+what it was.
+
+%hpux Note: On HP-UX, it isn't possible to have per-manpage-tree 'whatis'
+%hpux files. Thus, 'erl -man -k <subject>' will not work, and it isn't
+%hpux recommended to integrate the Erlang man pages into /usr/lib/whatis
+%hpux since (as mentioned in the "System Administrator's Guide") there are
+%hpux some potential conflicts in naming with standard Unix man pages.
+%hpux
+%isc32 Note: The release currently includes several files with names longer
+%isc32 than 14 characters - this means that you will have problems unpacking
+%isc32 it in a standard Interactive S51K (or S52K) filesystem (which you've
+%isc32 probably already noticed...). Furthermore, the Erlang filer makes no
+%isc32 attempts to deal "intelligently" with such restrictions. The bottom
+%isc32 line is that you have to install the Erlang system in an S5L (or
+%isc32 possibly NFS) filesystem, unless you have found a way to make the
+%isc32 Interactive system silently truncate filenames longer than 14
+%isc32 characters when using S5?K (if so, please tell us about it!).
+%isc32
+
+Overview of the files/directories in the system
+-----------------------------------------------
+
+README - this file.
+
+RELNOTES - release notes.
+
+Install - the main installation script.
+
+bin - the directory where all code that is to be executed
+ directly by UNIX is placed during the installation.
+
+lib - a number of "bundles" included in the release - each
+ bundle lives in a subdirectory. Most of them are written
+ entirely in Erlang, but in some cases C programs are also
+ used (these are copied to the bin directory during
+ installation). The code server will automatically add the
+ appropriate directory for each bundle to the search path.
+ Some of the more noteworthy bundles:
+ std - this is the standard library, with modules such as
+ file, io, lists, etc.
+ compiler - the Erlang compiler (of course)
+ debugger - the Erlang debugger (ditto)
+ pxw - the "Primitive X Window interface", which perhaps
+ isn't so primitive anymore...
+ For further information on these and the other bundles,
+ please refer to the man pages.
+
+doc - The printed documentation in compressed PostScript format,
+ and some code examples.
+
+man - Manual pages, best accessed with 'erl -man' - there are
+ some conflicts with standard Unix manpages if you put
+ this directory in $MANPATH.
+
+emulator - The object code for the emulator itself is in the 'obj'
+ subdirectory, along with a simple Makefile and a couple
+ of source files that advanced users *may* be interested in
+ changing - care should be taken, of course, since any
+ changes may make the system non-functional. Refer to the
+ "System Adminstrator's Guide" and "Additional Features"
+ documents for some more information on this.
+
+misc - Some pieces that don't belong to any particular part of the
+ system - e.g. the new erl_interface package, and an Erlang
+ mode for emacs.
diff --git a/erts/etc/unix/RELNOTES b/erts/etc/unix/RELNOTES
new file mode 100644
index 0000000000..d1a110fce3
--- /dev/null
+++ b/erts/etc/unix/RELNOTES
@@ -0,0 +1,327 @@
+
+ %CopyrightBegin%
+
+ Copyright Ericsson AB 1996-2009. All Rights Reserved.
+
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ %CopyrightEnd%
+
+=============================================================================
+
+Release Notes Erlang 4.3.1, Stockholm, October 1995
+
+This is a maintenance release correcting some minor problems in the 4.3
+release. The changes are mostly related to problems when using Erlang in
+an distributed environment. For features and incompatibilities look in the
+following description of the 4.3 release.
+
+-- If you already have the 4.3 release and run in an distributed environment
+ you should change all nodes to 4.3.1 since some changes could (at least
+ potentially) create problems/incompatibilities. (You ought to change
+ anyway due to bugs fixed...).
+
+=============================================================================
+
+Release Notes Erlang 4.3, Stockholm, June 1995
+
+This is a list of the changes, fixes, and enhancements which have occurred
+between the Erlang 4.2 release of March 1994, and the Erlang 4.3 release of
+June 1995. There is also information on problems reported for 4.2 that still
+remain in release 4.3. For a more detailed description of new or changed
+functions, please refer to the respective manual pages, which are referred
+to as man-page(3) or man-page(1).
+
+Erlang release 4.3 is basically the same system as release 4.2.
+A large number of improvements and enhancements have, however,
+occurred. The 4.3 system is largely source code compatible with
+the 4.2 system, but there is a number of incompatibilities.
+
+*** Major Improvements
+------------------------------------------------------------------
+
+-- The system is considerably faster and smaller.
+ A fully networked system now requires less than a Megabyte
+ of memory to start.
+
+-- The system has built-in hashlists which makes it possible
+ to store,search and manipulate very large quantities of data,
+ see ets(3).
+
+-- Bignums, integers can now be arbitrarily large (almost)
+
+-- A fully integrated debugger/interpreter that can be used
+ to debug distributed applications, see int(3), and
+ the user manual.
+
+-- Distributed Erlang can now be run in environments where DNS
+ (The Domain Name system) is not configured, see dist_erl(1).
+
+-- A new trace/3 BIF which can be used for a variety of
+ purposes, see erlang(3).
+
+
+*** Minor improvements and new modules.
+---------------------------------------------------------------------
+
+-- A new BIF to monitor other nodes, monitor_node/2,
+ see erlang(3).
+
+-- Floating point exceptions and bad parameters to math functions
+ are now handled correctly, (possibly not implemented on all
+ architectures)
+
+-- epmd can be used to access DNS, see epmd(3).
+
+-- Erlang now contains MACROS, include files, structures, etc.
+ These are currently not documented, and are used at the
+ user's own risk, as the syntax might change.
+
+-- The configuration of the Erlang system has been simplified.
+ Not many users are aware of this possibility at all, however.
+ The only parameter left for configuration is now the size of
+ TMP_BUF, so no upper limits remain for the number of functions,
+ modules, etc.
+
+-- Parallel make, see make(3).
+
+-- generic.erl, is recommended for writing servers,
+ see generic(3).
+
+-- timer.erl a new module to be used for various types of timing
+ related activities.
+
+-- The new formatter ~p has been introduced into the formatting
+ routines. io:format("String ~p", [Term]). will print the
+ term Term with any lists of integers that seem to be strings
+ as strings. This means that the majority of printouts will
+ now have a more attractive appearance. However, it also means
+ that some lists of integers that are real "lists of integers"
+ will be displayed as strings. Example:
+
+ 1> [65,66].
+ "AB"
+
+-- Deep lists can now be sent to ports. The lists must be well formed
+ but can contain integers and binaries.
+
+-- There is a new interface to udp/ip, see udp(3).
+
+-- slave.erl is a new and nicer way to start up slave nodes in a
+ UNIX environment.
+
+-- ddd.erl is a distributed fully replicated data dictionary.
+
+-- queue.erl FIFO queues as an abstract datatype.
+
+-- There are enhancements in the socket interface, see socket(3).
+
+-- rpc.erl is a new module that now contains some of the functions
+ that used to be in net.erl, which has now been removed,
+ see rpc(3).
+
+-- lists.erl contains some new functionality, see lists(3).
+
+-- BIF erlang:now() returns the current time.
+ This BIF is guaranteed to produce continuously increasing values.
+
+-- The new module auth.erl is for handling authentication, see auth(3).
+
+-- The file $HOME/.erlang.cookie is now automatically and
+ silently created if it does not exist. This means that new and/or
+ naive users can ignore the issues of cookies entirely.
+
+-- user.erl has been slightly rewritten so that Erlang
+ programs can now be part of a UNIX pipe, see erl(3), io(3).
+
+-- The new library directory tools now contain various
+ "tools"
+
+
+*** Command line flags changes.
+-------------------------------------------------------------------
+
+-- The -s and -h flags take values measured in H_GRAIN and S_GRAIN
+ H_GRAIN == S_GRAIN == 64. (Default is 1, which means that the default
+ heap and stack size is 64 words.)
+
+-- The maximum size of the atom_table is now configurable from
+ the command line.
+
+-- erl -sname Name starts a new node with a short name. (s == short),
+ see erl(1).
+
+-- The breakhandler can now be turned off with the aid of the flag +B.
+
+-- init.erl has been rewritten. A -load flag is now accepted,
+ see init(3).
+
+-- The -cookie flag is no longer necessary if the system is to
+ read the $HOME/.erlang.cookie file. This is the default.
+
+-- The flag -env Variable Value extends the UNIX environment
+ for the Erlang system, see erl(3).
+
+
+*** Reported Fixed Bugs and Malfunctions
+-------------------------------------------------------------------
+
+-- Do not assume that the first two directory entries "." and ".."
+ make file:list_dir() work better in some environments.
+
+-- Faster/better garbage collection.
+
+-- Stack sizes are automatically shrunk.
+
+-- Distributed Erlang now handles the case when for example the
+ Ethernet plug is unplugged. Erlang continuously polls all
+ remote nodes to ensure that the remote nodes are really alive.
+
+-- A bug has been corrected in the terminal driver. The system
+ could fail after a large series of printouts without any
+ newlines "\n" at all.
+
+-- Formating of floats: a '9' would sometimes become a ':'.
+
+-- Formating with the use of '*' and negativ size fields now work
+ as expected.
+
+-- The format of the 'EXIT' values is now ALWAYS the same
+ regardless of where the 'EXIT' was produced.
+
+-- Bugs in exit/2 when the first argument is a port
+ and second argument is a tuple, have been fixed.
+
+-- A bug in the random generator has been fixed, see random(3))
+
+-- Object code can now be 'trace' compiled by passing the
+ flag 'trace' to the compiler. This is necessary for
+ the trace/3 BIF to work on function calls.
+
+-- error_logger has been improved and is more flexible, see error_logger(3).
+
+-- The compiler is not so verbose any more.
+
+-- A bug in the loading of code has been fixed. It is now possible to load
+ code into the "hole" created by erlang:delete_module/1.
+
+-- The file system now accepts very large messages. In 4.2 there
+ was a limit of 64K, which meant that some VERY large modules
+ could not be compiled.
+
+-- Support for real interrupts/signals in linked-in drivers have been added.
+
+-- open_port does not make new atoms all the time.
+
+-- statistics(io) does now return two counters,
+ one for all input and one for all output.
+
+-- There have been minor bug fixes in the erl_interface c-library.
+
+
+*** New TOOLS for Software Development/Tuning/Debugging
+---------------------------------------------------------------
+
+-- int, is a fully integrated debugger/interpreter, see int(3).
+
+-- eprof, is a (tty-based) tool for real-time profiling, see eprof(3).
+
+-- dbg, is a (tty-based) interface to the the trace/3 BIF, see dbg(3).
+
+-- pman, is a (pxw-based) interface to the trace/3 BIF.
+
+-- emseq, is a (tty-based) message sequence chart tool. (Not documented)
+
+-- perfmon, is a (pxw-based) performance monitor. (Not documented)
+
+-- exref, is a (tty-based) cross-reference tool.
+
+
+*** New Targets Not Generally Available for 4.2
+------------------------------------------------------------------
+
+FreeBSD running on PCs
+LINUX running on PCs
+QNX
+
+
+*** Incompatibilities with 4.2.
+--------------------------------------------------------------------
+
+-- The BIF node_link/1 has been replace by monitor_node/2
+ See erlang(3).
+
+-- The 4.3 system is not object code compatible with 4.2.
+ This means that all source code has to be recompiled. It
+ is not possible to load 4.2 object code. It is also not
+ possible to run distribution between 4.3 and erlier versions
+ due to the new alive check.
+
+-- The external term format has been changed. This will only affect
+ programs where the BIF term_to_binary/1 has been used for writing
+ output on files. The directory misc/external contains a program
+ ext42_to_ext43.erl that can be used for converting files and
+ binaries from 4.2 format to 4.3 format. This will affect very
+ few programs.
+
+-- The names of the Erlang specific i/o modules are now prefixed by
+ "erl_", for example erl_scan and erl_parse.
+
+-- The calls to tokenize/parse have been changed, partially to make their
+ naming more systematic and also to handle the new line number
+ information. Their return values have also been made more regular with
+ all functions returning 'ok', {ok, Value} or {ok, Value, EndLine} where
+ appropriate when successful, and {error, ErrorInfo} or
+ {error, ErrorInfo, EndLine} if there is an error.
+
+-- There is a standardised error information format, ErrorInfo above, which
+ is returned for all input functions. It has the format:
+ {ErrorLine, Module, ErrorDescriptor}
+ where a string describing the error can be obtained by calling
+ apply(Module, format_error, [ErrorDescriptor]).
+ The handling of line number is application specific.
+
+-- The function io:read/1/2 now returns {ok, Term} or {error, ErrorInfo}
+ for consistency.
+
+-- The Erlang tokeniser/parser has been converted to return line number
+ information in the token and parse formats. These formats can now
+ be handled by the pretty printer (erl_pp) but it does not make use of them.
+
+-- The function file:open/2 now returns {ok, Pid} or {error, What}. This is
+ consistent with the return values from the rest of the i/o system.
+
+-- RTFMP! (Read The Friendly Man Pages)
+
+-- Module net.erl has been removed. The functionality of net.erl
+ now resides in the rpc, auth and net_kernel modules.
+
+-- The old debug BIFs (including the module debugger.erl) have
+ been removed. The BIF trace/3 replaces it.
+
+-- The BIF not_alive/0 has been removed.
+
+
+*** Documentation:
+--------------
+
+All manual pages have been updated, some of them substantially.
+
+
+*** Known problems:
+---------------
+
+The $HOME/.erlang file should be run before the shell is started.
+
+The Postscript documentation in the doc directory assumes A4 paper.
+
+list_to_pid/1 on remote pids may behave in an unexpected manner.
diff --git a/erts/etc/unix/cerl.src b/erts/etc/unix/cerl.src
new file mode 100644
index 0000000000..f81ef6b0fe
--- /dev/null
+++ b/erts/etc/unix/cerl.src
@@ -0,0 +1,285 @@
+#!/bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# This is a script to start Erlang/OTP for debugging. PATH is set to
+# include this script so if slave nodes are started they will use this
+# script as well.
+#
+# usage: cerl [ OPTIONS ] [ ARGS ]
+#
+# The OPTIONS are
+#
+# -rootdir $MYROOTDIR
+# Run an installed emulator built from this source
+# -debug Run debug compiled emulator
+# -gdb Run the debug compiled emulator in emacs and gdb.
+# You have to start beam in gdb using "run".
+# -break F Run the debug compiled emulator in emacs and gdb and set break.
+# The session is started, i.e. "run" is already don for you.
+# -xxgdb FIXME currently disabled
+# -purify Run emulator compiled for purify
+# -quantify Run emulator compiled for quantify
+# -purecov Run emulator compiled for purecov
+# -gcov Run emulator compiled for gcov
+# -valgrind Run emulator compiled for valgrind
+# -lcnt Run emulator compiled for lock counting
+# -nox Unset the DISPLAY variable to disable us of X Windows
+#
+# FIXME For GDB you can also set the break point using "-break FUNCTION".
+# FIXME For GDB you can also point out your own .gdbini......
+
+# These are marked for export
+export ROOTDIR
+export PROGNAME
+export EMU
+export BINDIR
+export PATH
+
+cargs=
+xargs=
+cxargs_add() {
+ while [ $# -gt 0 ]; do
+ cargs="$cargs $1"
+ xargs="$xargs $1"
+ shift
+ done
+}
+
+core=
+
+GDB=
+GDBBP=
+TYPE=
+EMU_TYPE=
+debug=
+run_valgrind=no
+
+# Default rootdir
+ROOTDIR=%SRC_ROOTDIR%
+BINDIR="$ROOTDIR/bin/`$ROOTDIR/erts/autoconf/config.guess`"
+#BINDIR="$ROOTDIR/bin/%TARGET%"
+PROGNAME=$ROOTDIR/bin/cerl
+EMU=beam
+
+PRELOADED=$ROOTDIR/erts/preloaded/ebin
+
+
+while [ $# -gt 0 ]; do
+ case "$1" in
+ +*)
+ # A system parameter!
+ cxargs_add $1
+ shift
+ # If next argument does not begin with a hyphen or a plus,
+ # it is used as the value of the system parameter.
+ if [ $# -gt 0 ]; then
+ case $1 in
+ -*|+*)
+ ;;
+ *)
+ cxargs_add $1
+ shift;;
+ esac
+ fi;;
+ "-instr")
+ cxargs_add $1
+ shift
+ ;;
+ "-target")
+ shift
+ BINDIR="$ROOTDIR/bin/$1"
+ shift
+ ;;
+ "-rootdir")
+ shift
+ cargs="$cargs -rootdir $1"
+ ROOTDIR="$1"
+ BINDIR=$ROOTDIR/erts-%VSN%/bin
+ shift
+ ;;
+ "-display")
+ shift
+ DISPLAY="$1"
+ export DISPLAY
+ shift
+ ;;
+ "-nox")
+ shift
+ unset DISPLAY
+ ;;
+ "-smp")
+ shift
+ cargs="$cargs -smp"
+ EMU_TYPE=.smp
+ ;;
+ "-lcnt")
+ shift
+ cargs="$cargs -lcnt"
+ TYPE=.lcnt
+ ;;
+ "-frag")
+ shift
+ cargs="$cargs -frag"
+ EMU_TYPE=.frag
+ ;;
+ "-smp_frag")
+ shift
+ cargs="$cargs -smp_frag"
+ EMU_TYPE=.smp_frag
+ ;;
+ "-gprof")
+ shift
+ cargs="$cargs -gprof"
+ TYPE=.gprof
+ ;;
+ "-hybrid")
+ shift
+ cargs="$cargs -hybrid"
+ EMU_TYPE=.hybrid
+ ;;
+ "-debug")
+ shift
+ cargs="$cargs -debug"
+ TYPE=.debug
+ ;;
+ "-gdb")
+ shift
+ GDB=gdb
+ ;;
+ "-break")
+ shift
+ GDB=gdb
+ GDBBP="$GDBBP (insert-string \"break $1\") (comint-send-input)"
+ shift
+ ;;
+ "-core")
+ shift
+ GDB=gdb
+ core="$1"
+ shift
+ ;;
+# "-xxgdb")
+# shift
+# GDB=xxgdb
+# ;;
+ "-shared")
+ shift
+ cargs="$cargs -shared"
+ TYPE=.shared
+ ;;
+ "-purify")
+ shift
+ cargs="$cargs -purify"
+ TYPE=.purify
+ ;;
+ "-quantify")
+ shift
+ cargs="$cargs -quantify"
+ TYPE=.quantify
+ ;;
+ "-purecov")
+ shift
+ cargs="$cargs -purecov"
+ TYPE=.purecov
+ ;;
+ "-gcov")
+ shift
+ cargs="$cargs -gcov"
+ TYPE=.gcov
+ ;;
+ "-valgrind")
+ shift
+ cargs="$cargs -valgrind"
+ TYPE=.valgrind
+ run_valgrind=yes
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+
+PATH=$BINDIR:$ROOTDIR/bin:$PATH
+EXEC=$BINDIR/erlexec
+
+PROGNAME="$PROGNAME $cargs"
+EMU=$EMU$TYPE$EMU_TYPE
+if [ $run_valgrind != yes ]; then
+ xargs="$xargs -pz $PRELOADED --"
+fi
+if [ "x$GDB" = "x" ]; then
+ if [ $run_valgrind = yes ]; then
+ emu_xargs=`echo $xargs | sed "s|+|-|g"`
+ if [ "x$VALGRIND_LOG_DIR" = "x" ]; then
+ valgrind_log=
+ else
+ valgrind_log="--log-file=$VALGRIND_LOG_DIR/$VALGRIND_LOGFILE_PREFIX$VALGRIND_LOGFILE_INFIX$EMU.log"
+ fi
+ if [ "x$VALGRIND_LOG_XML" = "x" ]; then
+ valgrind_xml=
+ else
+ export VALGRIND_LOG_XML
+ valgrind_xml="--xml=yes"
+ fi
+ if [ "x$VALGRIND_MISC_FLAGS" = "x" ]; then
+ valgrind_misc_flags=
+ else
+ valgrind_misc_flags="$VALGRIND_MISC_FLAGS"
+ fi
+ beam_args=`$EXEC -emu_args_exit ${1+"$@"}`
+ # Ahhhh... Need to quote $PROGNAME...
+ early_beam_args=`echo $beam_args | sed "s|^\(.*-progname\).*$|\1|g"`
+ late_beam_args=`echo $beam_args | sed "s|^$pre_beam_args.*\(-- -home.*\)$|\1|g"`
+
+ exec valgrind $valgrind_xml $valgrind_log $valgrind_misc_flags $BINDIR/$EMU $emu_xargs $early_beam_args "$PROGNAME" $late_beam_args -pz $PRELOADED
+ else
+ exec $EXEC $xargs ${1+"$@"}
+ fi
+else
+ if [ "x$EMACS" = "x" ]; then
+ EMACS=emacs
+ fi
+
+ case "x$core" in
+ x)
+ # Get emu args to use from erlexec...
+ beam_args=`$EXEC -emu_args_exit ${1+"$@"}`
+ gdbcmd="(insert-string \"set args $beam_args\") \
+ (comint-send-input)"
+ ;;
+ x/*)
+ gdbcmd="(insert-string \"core ${core}\") (comint-send-input)"
+ GDBBP=
+ ;;
+ *)
+ dir=`pwd`
+ gdbcmd="(insert-string \"core ${dir}/${core}\") \
+ (comint-send-input)"
+ GDBBP=
+ ;;
+ esac
+
+ gdbcmd="$gdbcmd $GDBBP \
+ (insert-string \"source $ROOTDIR/erts/etc/unix/etp-commands\") \
+ (comint-send-input)"
+ # Fire up gdb in emacs...
+ exec $EMACS --eval "(progn (gdb \"gdb $EMU\") $gdbcmd)"
+fi
diff --git a/erts/etc/unix/dyn_erl.c b/erts/etc/unix/dyn_erl.c
new file mode 100644
index 0000000000..984935417e
--- /dev/null
+++ b/erts/etc/unix/dyn_erl.c
@@ -0,0 +1,400 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * This is a C version of the erl Bourne shell script
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "sys.h"
+#include <stdlib.h>
+#include <stdarg.h>
+
+#define BOOL int
+#define TRUE 1
+#define FALSE 0
+#define PATHSEP ":"
+#define DIRSEP "/"
+#define DIRSEPCHAR '/'
+
+static void
+error(char* format, ...)
+{
+ char sbuf[1024];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+ fprintf(stderr, "erl: %s\n", sbuf);
+ exit(1);
+}
+
+/*
+ * Variables.
+ */
+
+/*
+ * Manage memory
+ */
+
+static void *
+emalloc(size_t size)
+{
+ void *p = malloc(size);
+ if (p == NULL)
+ error("Insufficient memory");
+ return p;
+}
+
+/*
+static void *
+erealloc(void *p, size_t size)
+{
+ void *res = realloc(p, size);
+ if (res == NULL)
+ error("Insufficient memory");
+ return res;
+}
+*/
+
+static void
+efree(void *p)
+{
+ free(p);
+}
+
+static char*
+strsave(char* string)
+{
+ char* p = emalloc(strlen(string)+1);
+ strcpy(p, string);
+ return p;
+}
+
+/*
+ * Manage environment variables
+ */
+
+static char *
+get_env(char *key)
+{
+ return getenv(key);
+}
+
+static void
+set_env(char *key, char *value)
+{
+ size_t size = strlen(key) + 1 + strlen(value) + 1;
+ char *str = emalloc(size);
+ sprintf(str, "%s=%s", key, value);
+ if (putenv(str) != 0)
+ error("putenv(\"%s\") failed!", str);
+#ifdef HAVE_COPYING_PUTENV
+ efree(str);
+#endif
+}
+
+// /* A realpath look alike */
+// static char *
+// follow_symlinks(const char *path, char *resolved_path)
+// {
+// char tmp[PATH_MAX];
+// int len;
+//
+// strcpy(resolved_path, path);
+//
+// for (;;) {
+// len = readlink(resolved_path, tmp, PATH_MAX);
+//
+// if (len == -1) {
+// if (errno == EINVAL) {
+// /* Not a symbolic link. use the original name */
+// break;
+// } else {
+// return NULL;
+// }
+// } else {
+// tmp[len] = '\0';
+// strcpy(resolved_path, tmp);
+// }
+// }
+//
+// return resolved_path;
+// }
+
+/*
+ * Find absolute path to this program
+ */
+
+static char *
+find_prog(char *origpath)
+{
+ char relpath[PATH_MAX];
+ char abspath[PATH_MAX];
+
+ strcpy(relpath, origpath);
+
+ if (strstr(relpath, DIRSEP) == NULL) {
+ /* Just a base name */
+ char *envpath;
+
+ envpath = get_env("PATH");
+ if (envpath) {
+ /* Try to find the executable in the path */
+ char dir[PATH_MAX];
+ char *beg = envpath;
+ char *end;
+ int sz;
+ DIR *dp; /* Pointer to directory structure. */
+ struct dirent* dirp; /* Pointer to directory entry. */
+ BOOL look_for_sep = TRUE;
+
+ while (look_for_sep) {
+ end = strstr(beg, PATHSEP);
+ if (end != NULL) {
+ sz = end - beg;
+ strncpy(dir, beg, sz);
+ dir[sz] = '\0';
+ } else {
+ sz = strlen(beg);
+ strcpy(dir, beg);
+ look_for_sep = FALSE;
+ }
+ beg = end + 1;
+
+ dp = opendir(dir);
+ if (dp != NULL) {
+ while (TRUE) {
+ dirp = readdir(dp);
+ if (dirp == NULL) {
+ closedir(dp);
+ /* Try next directory in path */
+ break;
+ }
+
+ if (strcmp(origpath, dirp->d_name) == 0) {
+ /* Wow. We found the executable. */
+ strcpy(relpath, dir);
+ strcat(relpath, DIRSEP);
+ strcat(relpath, dirp->d_name);
+ closedir(dp);
+ look_for_sep = FALSE;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (!realpath(relpath, abspath)) {
+ error("Cannot determine real path to erl");
+ }
+
+ return strdup(abspath);
+}
+
+/*
+ * Find bindir
+ */
+
+static void
+copy_latest_vsn(char *latest_vsn, char *next_vsn)
+{
+ char *lp;
+ char *np;
+ BOOL greater;
+
+ /* Find vsn */
+ for (lp = latest_vsn+strlen(latest_vsn)-1 ;lp > latest_vsn && *lp != DIRSEPCHAR; --lp)
+ ;
+
+ /* lp =+ length("erts-"); */
+ for (np = next_vsn+strlen(next_vsn)-1 ;np > next_vsn && *np != DIRSEPCHAR; --np)
+ ;
+
+ /* np =+ length("erts-"); */
+ while (TRUE) {
+ if (*lp != *np) {
+ if (*np > *lp) {
+ greater = TRUE;
+ } else {
+ greater = FALSE;
+ }
+
+ /* Find next dot or eos */
+ while (*lp != '\0' && *np != '\0') {
+ lp++;
+ np++;
+ if (*np == '.' && *lp == '.') {
+ break;
+ }
+ if (*np == '\0' && *lp == '\0') {
+ break;
+ }
+ if (*lp == '.' || *lp == '\0') {
+ greater = TRUE;
+ }
+ if (*np == '.' || *np == '\0') {
+ greater = FALSE;
+ }
+ }
+ if (greater) {
+ strcpy(latest_vsn, next_vsn);
+ }
+ return;
+ }
+ ++lp;
+ ++np;
+ }
+}
+
+static char *
+find_erts_vsn(char *erl_top)
+{
+ /* List install dir and look for latest erts-vsn */
+ DIR *dp; /* Pointer to directory structure. */
+ struct dirent* dirp; /* Pointer to directory entry. */
+ char latest_vsn[PATH_MAX]; /* Latest erts-vsn directory name. */
+
+ dp = opendir(erl_top);
+ if (dp == NULL) {
+ return NULL;
+ }
+
+ latest_vsn[0] = '\0';
+ for (;;) {
+ dirp = readdir(dp);
+ if (dirp == NULL) {
+ closedir(dp);
+ break;
+ }
+ if (strncmp("erts-", dirp->d_name, 5) == 0) {
+ copy_latest_vsn(latest_vsn, dirp->d_name);
+ }
+ }
+
+ if (latest_vsn[0] == '\0') {
+ return NULL;
+ } else {
+ char *p = malloc((strlen(erl_top)+1+strlen(latest_vsn)+4+1)*sizeof(char));
+ strcpy(p,erl_top);
+ strcat(p,DIRSEP);
+ strcat(p,latest_vsn);
+ strcat(p,DIRSEP);
+ strcat(p,"bin");
+ return p;
+ }
+}
+
+static char *
+find_bindir(char *erlpath)
+{
+ /* Assume that the path to erl is absolute and
+ * that it is not a symbolic link*/
+
+ char *p;
+ char *p2;
+ char buffer[PATH_MAX];
+
+ strcpy(buffer, erlpath);
+
+ /* Chop of base name*/
+ for (p = buffer+strlen(buffer)-1 ;p >= buffer && *p != DIRSEPCHAR; --p)
+ ;
+ *p = '\0';
+ p--;
+
+ /* Check if dir path is like ...\buffer\erts-vsn\bin */
+ for (;p >= buffer && *p != DIRSEPCHAR; --p)
+ ;
+ p--;
+ for (p2 = p;p2 >= buffer && *p2 != DIRSEPCHAR; --p2)
+ ;
+ p2++;
+ if (strncmp(p2, "erts-", 5) == 0) {
+ p = strsave(buffer);
+ return p;
+ }
+
+ /* Assume that dir path is like ...\buffer\bin */
+ *++p ='\0'; /* chop off bin dir */
+
+ p = find_erts_vsn(buffer);
+ if (p == NULL) {
+ return strsave(buffer);
+ } else {
+ return p;
+ }
+}
+
+/*
+ * main
+ */
+
+int
+main(int argc, char **argv)
+{
+ char *p;
+ char *abspath;
+ char *bindir; /* Location of executables. */
+ char rootdir[PATH_MAX]; /* Root location of Erlang installation. */
+ char progname[PATH_MAX]; /* Name of this program. */
+ char erlexec[PATH_MAX]; /* Path to erlexec */
+
+ /* Determine progname */
+ abspath = find_prog(argv[0]);
+ strcpy(progname, abspath);
+ for (p = progname+strlen(progname)-1;p >= progname && *p != '/'; --p)
+ ;
+
+ /* Determine bindir */
+ bindir = find_bindir(abspath);
+
+ /* Determine rootdir */
+ strcpy(rootdir, bindir);
+ for (p = rootdir+strlen(rootdir)-1;p >= rootdir && *p != '/'; --p)
+ ;
+ p--;
+ for (;p >= rootdir && *p != '/'; --p)
+ ;
+ *p ='\0';
+
+ /* Update environment */
+ set_env("EMU", "beam");
+ set_env("PROGNAME", progname);
+ set_env("BINDIR", bindir);
+ set_env("ROOTDIR", rootdir);
+
+ /* Invoke erlexec */
+ strcpy(erlexec, bindir);
+ strcat(erlexec, DIRSEP);
+ strcat(erlexec, "erlexec");
+
+ efree(abspath);
+ efree(bindir);
+
+ execvp(erlexec, argv);
+ error("Error %d executing \'%s\'.", errno, erlexec);
+ return 2;
+}
diff --git a/erts/etc/unix/erl.src.src b/erts/etc/unix/erl.src.src
new file mode 100644
index 0000000000..50603f12f4
--- /dev/null
+++ b/erts/etc/unix/erl.src.src
@@ -0,0 +1,28 @@
+#!/bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+ROOTDIR=%FINAL_ROOTDIR%
+BINDIR=$ROOTDIR/erts-%VSN%/bin
+EMU=%EMULATOR%%EMULATOR_NUMBER%
+PROGNAME=`echo $0 | sed 's/.*\///'`
+export EMU
+export ROOTDIR
+export BINDIR
+export PROGNAME
+exec $BINDIR/erlexec ${1+"$@"}
diff --git a/erts/etc/unix/etp-commands b/erts/etc/unix/etp-commands
new file mode 100644
index 0000000000..6a01e0b7e0
--- /dev/null
+++ b/erts/etc/unix/etp-commands
@@ -0,0 +1,2054 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2005-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+############################################################################
+# Help commands
+#
+
+define etp-help
+ help etp-help
+end
+
+document etp-help
+%---------------------------------------------------------------------------
+% etp-help
+%
+% Same as "help etp-help"
+%
+% Emulator Toolbox for Pathologists
+% - GDB command toolbox for analyzing core dumps from the
+% Erlang emulator (BEAM).
+%
+% Should work for 32-bit erts-5.2/R9B, ...
+%
+% The commands are prefixed with:
+% etp: Acronym for erts-term-print
+% etpf: Acronym for erts-term-print-flat
+%
+% User commands (these have help themselves):
+%
+% Most useful:
+% etp, etpf
+%
+% Useful for doing step-by-step traversal of lists and tuples after
+% calling the toplevel command etpf:
+% etpf-cons, etpf-boxed,
+%
+% Special commands for not really terms:
+% etp-mfa, etp-cp,
+% etp-msgq, etpf-msgq,
+% etp-stacktrace, etp-stackdump, etpf-stackdump, etp-dictdump
+% etp-offheapdump, etpf-offheapdump,
+% etp-print-procs, etp-search-heaps, etp-search-alloc,
+% etp-ets-tables, etp-ets-tabledump
+%
+% Complex commands that use the Erlang support module.
+% etp-overlapped-heaps, etp-chart, etp-chart-start, etp-chart-end
+%
+% Erlang support module handling commands:
+% etp-run
+%
+% Parameter handling commands:
+% etp-show, etp-set-max-depth, etp-set-max-string-length
+%
+% Other commands you may find in this toolbox are suffixed -1, -2, ...
+% and are internal; not for the console user.
+%
+% The Erlang support module requires `erl' and `erlc' in the path.
+% The compiled "erl_commands.beam" file is stored in the current
+% working directory, so it is thereby in the search path of `erl'.
+%
+% These are just helpful commands when analyzing core dumps, but
+% you will not get away without knowing the gory details of the
+% tag bits. Do not forget about the e.g p, p/x, x and x/4x commands.
+%
+% Execution speed of user defined gdb commands is not lightning fast.
+% It may well take half a minute to dump a complex term with the default
+% max depth values on our old Sparc Ultra-10's.
+%
+% To use the Erlang support module, the environment variable ROOTDIR
+% must be set to the toplevel installation directory of Erlang/OTP,
+% so the etp-commands file becomes:
+% $ROOTDIR/erts/etc/unix/etp-commands
+% Also, erl and erlc must be in the path.
+%---------------------------------------------------------------------------
+end
+
+############################################################################
+# Toplevel commands
+#
+
+define etp
+# Args: Eterm
+#
+# Reentrant
+#
+ etp-1 ((Eterm)($arg0)) 0
+ printf ".\n"
+end
+
+document etp
+%---------------------------------------------------------------------------
+% etp Eterm
+%
+% Takes a toplevel Erlang term and prints the whole deep term
+% very much as in Erlang itself. Up to a max depth. See etp-show.
+%---------------------------------------------------------------------------
+end
+
+define etp-1
+# Args: Eterm, int depth
+#
+# Reentrant
+#
+ if (($arg0) & 0x3) == 1
+ # Cons pointer
+ if $etp_flat
+ printf "<etpf-cons %#x>", ($arg0)
+ else
+ etp-list-1 ($arg0) ($arg1)
+ end
+ else
+ if (($arg0) & 0x3) == 2
+ if $etp_flat
+ printf "<etpf-boxed %#x>", ($arg0)
+ else
+ etp-boxed-1 ($arg0) ($arg1)
+ end
+ else
+ if (($arg0) & 0x3) == 3
+ etp-immediate-1 ($arg0)
+ else
+ # (($arg0) & 0x3) == 0
+ if (($arg0) == 0x0)
+ printf "<the non-value>"
+ else
+ if (($arg0) == 0x4)
+ printf "<the non-value debug>"
+ else
+ etp-cp-1 ($arg0)
+ end
+ end
+ end
+ end
+ end
+end
+
+define etpf
+# Args: Eterm
+#
+# Non-reentrant
+ set $etp_flat = 1
+ etp-1 ((Eterm)($arg0))
+ set $etp_flat = 0
+ printf ".\n"
+end
+
+document etpf
+%---------------------------------------------------------------------------
+% etpf Eterm
+%
+% Takes a toplevel Erlang term and prints it is. If it is a deep term
+% print which command to use to traverse down one level.
+%---------------------------------------------------------------------------
+end
+
+############################################################################
+# Commands for nested terms. Some are recursive.
+#
+
+define etp-list-1
+# Args: Eterm cons_cell, int depth
+#
+# Reentrant
+#
+ if (($arg0) & 0x3) != 0x1
+ printf "#NotCons<%#x>", ($arg0)
+ else
+ # Cons pointer
+ if $etp_chart
+ etp-chart-entry-1 ($arg0) ($arg1) 2
+ end
+ etp-list-printable-1 ($arg0) ($arg1)
+ if !$etp_list_printable
+ # Print normal list
+ printf "["
+ etp-list-2 ($arg0) (($arg1)+1)
+ end
+ end
+end
+
+define etp-list-printable-1
+# Args: Eterm list, int depth
+#
+# Non-reentrant
+#
+# Returns: $etp_list_printable
+#
+ if (($arg0) & 0x3) != 0x1
+ printf "#NotCons<%#x>", ($arg0)
+ else
+ # Loop to check if it is a printable string
+ set $etp_list_p = ($arg0)
+ set $etp_list_printable = ($etp_list_p != $etp_nil)
+ set $etp_list_i = 0
+ while ($etp_list_p != $etp_nil) && \
+ ($etp_list_i < $etp_max_string_length) && \
+ $etp_list_printable
+ if ($etp_list_p & 0x3) == 0x1
+ # Cons pointer
+ set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0]
+ if ($etp_list_n & 0xF) == 0xF
+ etp-ct-printable-1 ($etp_list_n>>4)
+ if $etp_ct_printable
+ # Printable
+ set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1]
+ set $etp_list_i++
+ else
+ set $etp_list_printable = 0
+ end
+ else
+ set $etp_list_printable = 0
+ end
+ else
+ set $etp_list_printable = 0
+ end
+ end
+ #
+ if $etp_list_printable
+ # Print printable string
+ printf "\""
+ set $etp_list_p = ($arg0)
+ set $etp_list_i = 0
+ while $etp_list_p != $etp_nil
+ set $etp_list_n = ((Eterm*)($etp_list_p & ~0x3))[0]
+ etp-char-1 ($etp_list_n>>4) '"'
+ set $etp_list_p = ((Eterm*)($etp_list_p & ~0x3))[1]
+ set $etp_list_i++
+ if $etp_list_p == $etp_nil
+ printf "\""
+ else
+ if $etp_list_i >= $etp_max_string_length
+ set $etp_list_p = $etp_nil
+ printf "\"++[...]"
+ else
+ if $etp_chart
+ etp-chart-entry-1 ($arg0) (($arg1)+$etp_list_i) 2
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+define etp-list-2
+# Args: Eterm cons_cell, int depth
+#
+# Reentrant
+#
+ if (($arg0) & 0x3) != 0x1
+ printf "#NotCons<%#x>", ($arg0)
+ else
+ # Cons pointer
+ if ($arg1) >= $etp_max_depth
+ printf "...]"
+ else
+ etp-1 (((Eterm*)(($arg0)&~0x3))[0]) (($arg1)+1)
+ if ((Eterm*)(($arg0) & ~0x3))[1] == $etp_nil
+ # Tail is []
+ printf "]"
+ else
+ if $etp_chart
+ etp-chart-entry-1 ($arg0) ($arg1) 2
+ end
+ if (((Eterm*)(($arg0)&~0x3))[1]&0x3) == 0x1
+ # Tail is cons cell
+ printf ","
+ etp-list-2 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1)
+ else
+ # Tail is other term
+ printf "|"
+ etp-1 (((Eterm*)(($arg0)&~0x3))[1]) (($arg1)+1)
+ printf "]"
+ end
+ end
+ end
+ end
+end
+
+define etpf-cons
+# Args: Eterm
+#
+# Reentrant capable
+#
+ if ((Eterm)($arg0) & 0x3) != 0x1
+ printf "#NotCons<%#x>", ($arg0)
+ else
+ # Cons pointer
+ set $etp_flat = 1
+ printf "["
+ etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[0])
+ printf "|"
+ etp-1 (((Eterm*)((Eterm)($arg0)&~0x3))[1])
+ printf "]\n"
+ set $etp_flat = 0
+ end
+end
+
+document etpf-cons
+%---------------------------------------------------------------------------
+% etpf-cons Eterm
+%
+% Takes a Cons ptr and prints the Car and Cdr cells with etpf (flat).
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-boxed-1
+# Args: Eterm, int depth
+#
+# Reentrant
+#
+ if (($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", ($arg0)
+ else
+ if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0
+ if $etp_chart
+ etp-chart-entry-1 (($arg0)&~0x3) ($arg1) 1
+ end
+ printf "#BoxedError<%#x>", ($arg0)
+ else
+ if $etp_chart
+ etp-chart-entry-1 (($arg0)&~0x3) ($arg1) \
+ ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1)
+ end
+ if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3f) == 0x0
+ printf "{"
+ etp-array-1 ((Eterm*)(($arg0)&~0x3)) ($arg1) ($arg1) \
+ 1 ((((Eterm*)(($arg0)&~0x3))[0]>>6)+1) '}'
+ else
+ etp-boxed-immediate-1 ($arg0)
+ end
+ end
+ end
+end
+
+define etp-boxed-immediate-1
+# Args: Eterm, int depth
+#
+# Non-reentrant
+#
+ if (($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", ($arg0)
+ else
+ if (((Eterm*)(($arg0) & ~0x3))[0] & 0x3) != 0x0
+ printf "#BoxedError<%#x>", ($arg0)
+ else
+ set $etp_boxed_immediate_p = (Eterm*)(($arg0) & ~0x3)
+ set $etp_boxed_immediate_h = ($etp_boxed_immediate_p[0] >> 2) & 0xF
+ if $etp_boxed_immediate_h == 0xC
+ etp-extpid-1 ($arg0)
+ else
+ if $etp_boxed_immediate_h == 0xD
+ etp-extport-1 ($arg0)
+ else
+ if ($etp_boxed_immediate_h == 0x2) || \
+ ($etp_boxed_immediate_h == 0x3)
+ etp-bignum-1 ($arg0)
+ else
+ if ($etp_boxed_immediate_h == 0x6)
+ etp-float-1 ($arg0)
+ else
+ if ($etp_boxed_immediate_h == 0x4)
+ etp-ref-1 ($arg0)
+ else
+ if ($etp_boxed_immediate_h == 0xE)
+ etp-extref-1 ($arg0)
+ else
+ # Hexdump the rest
+ if ($etp_boxed_immediate_h == 0x5)
+ printf "#Fun<"
+ else
+ if ($etp_boxed_immediate_h == 0x8)
+ printf "#RefcBinary<"
+ else
+ if ($etp_boxed_immediate_h == 0x9)
+ printf "#HeapBinary<"
+ else
+ if ($etp_boxed_immediate_h == 0xA)
+ printf "#SubBinary<"
+ else
+ printf "#Header%X<", $etp_boxed_immediate_h
+ end
+ end
+ end
+ end
+ set $etp_boxed_immediate_arity = $etp_boxed_immediate_p[0]>>6
+ while $etp_boxed_immediate_arity > 0
+ set $etp_boxed_immediate_p++
+ if $etp_boxed_immediate_arity > 1
+ printf "%#x,", *$etp_boxed_immediate_p
+ else
+ printf "%#x", *$etp_boxed_immediate_p
+ if ($etp_boxed_immediate_h == 0xA)
+ set $etp_boxed_immediate_p++
+ printf ":%#x", *$etp_boxed_immediate_p
+ end
+ printf ">"
+ end
+ set $etp_boxed_immediate_arity--
+ end
+ # End of hexdump
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+define etpf-boxed
+# Args: Eterm
+#
+# Non-reentrant
+#
+ set $etp_flat = 1
+ etp-boxed-1 ((Eterm)($arg0)) 0
+ set $etp_flat = 0
+ printf ".\n"
+end
+
+document etpf-boxed
+%---------------------------------------------------------------------------
+% etpf-boxed Eterm
+%
+% Take a Boxed ptr and print the contents in one level using etpf (flat).
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-array-1
+# Args: Eterm* p, int depth, int width, int pos, int size, int end_char
+#
+# Reentrant
+#
+ if ($arg3) < ($arg4)
+ if (($arg1) < $etp_max_depth) && (($arg2) < $etp_max_depth)
+ etp-1 (($arg0)[($arg3)]) (($arg1)+1)
+ if (($arg3) + 1) != ($arg4)
+ printf ","
+ end
+ etp-array-1 ($arg0) ($arg1) (($arg2)+1) (($arg3)+1) ($arg4) ($arg5)
+ else
+ printf "...%c", ($arg5)
+ end
+ else
+ printf "%c", ($arg5)
+ end
+end
+
+
+
+#define etpa-1
+## Args: Eterm, int depth, int index, int arity
+##
+## Reentrant
+##
+# if ($arg1) >= $etp_max_depth+$etp_max_string_length
+# printf "%% Max depth for term %d\n", $etp_chart_id
+# else
+# if ($arg2) < ($arg3)
+# etp-1 (((Eterm*)(($arg0)&~0x3))[$arg2]) (($arg1)+1)
+# etpa-1 ($arg0) (($arg1)+1) (($arg2)+1) ($arg3)
+# end
+# end
+#end
+
+############################################################################
+# Commands for non-nested terms. Recursion leaves. Some call other leaves.
+#
+
+define etp-immediate-1
+# Args: Eterm
+#
+# Reentrant capable
+#
+ if (($arg0) & 0x3) != 0x3
+ printf "#NotImmediate<%#x>", ($arg0)
+ else
+ if (($arg0) & 0xF) == 0x3
+ etp-pid-1 ($arg0)
+ else
+ if (($arg0) & 0xF) == 0x7
+ etp-port-1 ($arg0)
+ else
+ if (($arg0) & 0xF) == 0xf
+ # Fixnum
+ printf "%ld", (long)((Sint)($arg0)>>4)
+ else
+ # Immediate2 - 0xB
+ if (($arg0) & 0x3f) == 0x0b
+ etp-atom-1 ($arg0)
+ else
+ if (($arg0) & 0x3f) == 0x1b
+ printf "#Catch<%d>", ($arg0)>>6
+ else
+ if (($arg0) == $etp_nil)
+ printf "[]"
+ else
+ printf "#UnknownImmediate<%#x>", ($arg0)
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+
+
+define etp-atom-1
+# Args: Eterm atom
+#
+# Non-reentrant
+#
+ if ((Eterm)($arg0) & 0x3f) != 0xb
+ printf "#NotAtom<%#x>", ($arg0)
+ else
+ set $etp_atom_1_ap = (Atom*)erts_atom_table.seg_table[(Eterm)($arg0)>>16][((Eterm)($arg0)>>6)&0x3FF]
+ set $etp_atom_1_i = ($etp_atom_1_ap)->len
+ set $etp_atom_1_p = ($etp_atom_1_ap)->name
+ set $etp_atom_1_quote = 1
+ # Check if atom has to be quoted
+ if ($etp_atom_1_i > 0)
+ etp-ct-atom-1 (*$etp_atom_1_p)
+ if $etp_ct_atom
+ # Atom start character
+ set $etp_atom_1_p++
+ set $etp_atom_1_i--
+ set $etp_atom_1_quote = 0
+ else
+ set $etp_atom_1_i = 0
+ end
+ end
+ while $etp_atom_1_i > 0
+ etp-ct-name-1 (*$etp_atom_1_p)
+ if $etp_ct_name
+ # Name character
+ set $etp_atom_1_p++
+ set $etp_atom_1_i--
+ else
+ set $etp_atom_1_quote = 1
+ set $etp_atom_1_i = 0
+ end
+ end
+ # Print the atom
+ if $etp_atom_1_quote
+ printf "'"
+ end
+ set $etp_atom_1_i = ($etp_atom_1_ap)->len
+ set $etp_atom_1_p = ($etp_atom_1_ap)->name
+ while $etp_atom_1_i > 0
+ etp-char-1 (*$etp_atom_1_p) '\''
+ set $etp_atom_1_p++
+ set $etp_atom_1_i--
+ end
+ if $etp_atom_1_quote
+ printf "'"
+ end
+ end
+end
+
+
+
+define etp-char-1
+# Args: int char, int quote_char
+#
+# Non-reentrant
+#
+ if (($arg0) < 0) || (0377 < ($arg0))
+ printf "#NotChar<%#x>", ($arg0)
+ else
+ if ($arg0) == ($arg1)
+ printf "\\%c", ($arg0)
+ else
+ etp-ct-printable-1 ($arg0)
+ if $etp_ct_printable
+ if $etp_ct_printable < 0
+ printf "%c", ($arg0)
+ else
+ printf "\\%c", $etp_ct_printable
+ end
+ else
+ printf "\\%03o", ($arg0)
+ end
+ end
+ end
+end
+
+define etp-ct-printable-1
+# Args: int
+#
+# Determines if integer is a printable character
+#
+# Non-reentrant
+# Returns: $etp_ct_printable
+# escape alias char, or -1 if no escape alias
+ if ($arg0) == 010
+ set $etp_ct_printable = 'b'
+ else
+ if ($arg0) == 011
+ set $etp_ct_printable = 't'
+ else
+ if ($arg0) == 012
+ set $etp_ct_printable = 'n'
+ else
+ if ($arg0) == 013
+ set $etp_ct_printable = 'v'
+ else
+ if ($arg0) == 014
+ set $etp_ct_printable = 'f'
+ else
+ if ($arg0) == 033
+ set $etp_ct_printable = 'e'
+ else
+ if ((040 <= ($arg0)) && (($arg0) <= 0176)) || \
+ ((0240 <= ($arg0)) && (($arg0) <= 0377))
+ # Other printable character
+ set $etp_ct_printable = -1
+ else
+ set $etp_ct_printable = 0
+ end
+ end
+ end
+ end
+ end
+ end
+ end
+end
+
+define etp-ct-atom-1
+# Args: int
+#
+# Determines if integer is a atom first character
+#
+# Non-reentrant
+# Returns: $etp_ct_atom
+ if ((0141 <= ($arg0)) && (($arg0) <= 0172)) || \
+ ((0337 <= ($arg0)) && (($arg0) != 0367) && (($arg0) <= 0377))
+ # Atom start character
+ set $etp_ct_atom = 1
+ else
+ set $etp_ct_atom = 0
+ end
+end
+
+define etp-ct-variable-1
+# Args: int
+#
+# Determines if integer is a variable first character
+#
+# Non-reentrant
+# Returns: $etp_ct_variable
+ if ((056 == ($arg0)) || \
+ (0101 <= ($arg0)) && (($arg0) <= 0132)) || \
+ (0137 == ($arg0)) || \
+ ((0300 <= ($arg0)) && (($arg0) != 0327) && (($arg0) <= 0336))
+ # Variable start character
+ set $etp_ct_variable = 1
+ else
+ set $etp_ct_variable = 0
+ end
+end
+
+define etp-ct-name-1
+# Args: int
+#
+# Determines if integer is a name character,
+# i.e non-first atom or variable character.
+#
+# Non-reentrant
+# Returns: $etp_ct_variable
+ if (($arg0) == 0100 || \
+ (060 <= ($arg0)) && (($arg0) <= 071))
+ set $etp_ct_name = 1
+ else
+ etp-ct-atom-1 ($arg0)
+ if $etp_ct_atom
+ set $etp_ct_name = 1
+ else
+ etp-ct-variable-1 ($arg0)
+ set $etp_ct_name = $etp_ct_variable
+ end
+ end
+end
+
+
+
+define etp-pid-1
+# Args: Eterm pid
+#
+# Non-reentrant
+#
+ set $etp_pid_1 = (Eterm)($arg0)
+ if ($etp_pid_1 & 0xF) == 0x3
+ # Internal pid
+ printf "<0.%u.%u>", (unsigned) ($etp_pid_1>>4)&0x7fff, \
+ (unsigned) ($etp_pid_1>>19)&0x1fff
+ else
+ printf "#NotPid<%#x>", ($arg0)
+ end
+end
+
+define etp-extpid-1
+# Args: Eterm extpid
+#
+# Non-reentrant
+#
+ if ((Eterm)($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", (Eterm)($arg0)
+ else
+ set $etp_extpid_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3)
+ if ($etp_extpid_1_p->header & 0x3f) != 0x30
+ printf "#NotExternalPid<%#x>", $etp_extpid_1_p->header
+ else
+ ## External pid
+ set $etp_extpid_1_number = $etp_extpid_1_p->data.ui[0]&0x7fff
+ set $etp_extpid_1_serial = ($etp_extpid_1_p->data.ui[0]>>15)&0x1fff
+ set $etp_extpid_1_np = $etp_extpid_1_p->node
+ set $etp_extpid_1_creation = $etp_extpid_1_np->creation
+ set $etp_extpid_1_dep = $etp_extpid_1_np->dist_entry
+ set $etp_extpid_1_node = $etp_extpid_1_np->sysname
+ if ($etp_extpid_1_node & 0x3f) != 0xb
+ # Should be an atom
+ printf "#ExternalPidError<%#x>", ($arg0)
+ else
+ if $etp_extpid_1_dep == erts_this_dist_entry
+ printf "<0:"
+ else
+ printf "<%u:", $etp_extpid_1_node>>6
+ end
+ etp-atom-1 ($etp_extpid_1_node)
+ printf "/%u.%u.%u>", $etp_extpid_1_creation, \
+ $etp_extpid_1_number, $etp_extpid_1_serial
+ end
+ end
+ end
+end
+
+
+
+define etp-port-1
+# Args: Eterm port
+#
+# Non-reentrant
+#
+ set $etp_port_1 = (Eterm)($arg0)
+ if ($etp_port_1 & 0xF) == 0x7
+ # Internal port
+ printf "#Port<0.%u>", (unsigned) ($etp_port_1>>4)&0x3ffff
+ else
+ printf "#NotPort<%#x>", ($arg0)
+ end
+end
+
+define etp-extport-1
+# Args: Eterm extport
+#
+# Non-reentrant
+#
+ if ((Eterm)($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", (Eterm)($arg0)
+ else
+ set $etp_extport_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3)
+ if ($etp_extport_1_p->header & 0x3F) != 0x34
+ printf "#NotExternalPort<%#x>", $etp_extport_1->header
+ else
+ ## External port
+ set $etp_extport_1_number = $etp_extport_1_p->data.ui[0]&0x3ffff
+ set $etp_extport_1_np = $etp_extport_1_p->node
+ set $etp_extport_1_creation = $etp_extport_1_np->creation
+ set $etp_extport_1_dep = $etp_extport_1_np->dist_entry
+ set $etp_extport_1_node = $etp_extport_1_np->sysname
+ if ($etp_extport_1_node & 0x3f) != 0xb
+ # Should be an atom
+ printf "#ExternalPortError<%#x>", ($arg0)
+ else
+ if $etp_extport_1_dep == erts_this_dist_entry
+ printf "#Port<0:"
+ else
+ printf "#Port<%u:", $etp_extport_1_node>>6
+ end
+ etp-atom-1 ($etp_extport_1_node)
+ printf "/%u.%u>", $etp_extport_1_creation, $etp_extport_1_number
+ end
+ end
+ end
+end
+
+
+
+define etp-bignum-1
+# Args: Eterm bignum
+#
+# Non-reentrant
+#
+ if ((Eterm)($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", (Eterm)($arg0)
+ else
+ set $etp_bignum_1_p = (Eterm*)((Eterm)($arg0) & ~0x3)
+ if ($etp_bignum_1_p[0] & 0x3b) != 0x08
+ printf "#NotBignum<%#x>", $etp_bignum_1_p[0]
+ else
+ set $etp_bignum_1_i = ($etp_bignum_1_p[0] >> 6)
+ if $etp_bignum_1_i < 1
+ printf "#BignumError<%#x>", (Eterm)($arg0)
+ else
+ if $etp_bignum_1_p[0] & 0x04
+ printf "-"
+ end
+ set $etp_bignum_1_p = (ErtsDigit *)($etp_bignum_1_p + 1)
+ printf "16#"
+ if $etp_arch64
+ while $etp_bignum_1_i > 0
+ set $etp_bignum_1_i--
+ printf "%016lx", $etp_bignum_1_p[$etp_bignum_1_i]
+ end
+ else
+ while $etp_bignum_1_i > 0
+ set $etp_bignum_1_i--
+ printf "%08x", $etp_bignum_1_p[$etp_bignum_1_i]
+ end
+ end
+ end
+ end
+ end
+end
+
+
+
+define etp-float-1
+# Args: Eterm float
+#
+# Non-reentrant
+#
+ if ((Eterm)($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", (Eterm)($arg0)
+ else
+ set $etp_float_1_p = (Eterm*)((Eterm)($arg0) & ~0x3)
+ if ($etp_float_1_p[0] & 0x3f) != 0x18
+ printf "#NotFloat<%#x>", $etp_float_1_p[0]
+ else
+ printf "%f", *(double*)($etp_float_1_p+1)
+ end
+ end
+end
+
+
+
+define etp-ref-1
+# Args: Eterm ref
+#
+# Non-reentrant
+#
+ if ((Eterm)($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", (Eterm)($arg0)
+ else
+ set $etp_ref_1_p = (RefThing *)((Eterm)($arg0) & ~0x3)
+ if ($etp_ref_1_p->header & 0x3b) != 0x10
+ printf "#NotRef<%#x>", $etp_ref_1_p->header
+ else
+ set $etp_ref_1_nump = (Uint32 *) 0
+ set $etp_ref_1_error = 0
+ if ($etp_ref_1_p->header >> 6) == 0
+ set $etp_ref_1_error = 1
+ else
+ if $etp_arch64
+ set $etp_ref_1_i = (int) $etp_ref_1_p->data.ui32[0]
+ if (($etp_ref_1_i + 1) > (2 * ($etp_ref_1_p->header >> 6)))
+ set $etp_ref_1_error = 1
+ else
+ set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[1]
+ end
+ else
+ set $etp_ref_1_i = (int) ($etp_ref_1_p->header >> 6)
+ set $etp_ref_1_nump = &$etp_ref_1_p->data.ui32[0]
+ end
+ end
+ if $etp_ref_1_error
+ printf "#InternalRefError<%#x>", ($arg0)
+ else
+ printf "#Ref<0"
+ set $etp_ref_1_i--
+ while $etp_ref_1_i >= 0
+ printf ".%u", (unsigned) $etp_ref_1_nump[$etp_ref_1_i]
+ set $etp_ref_1_i--
+ end
+ printf ">"
+ end
+ end
+ end
+end
+
+
+
+define etp-extref-1
+# Args: Eterm extref
+#
+# Non-reentrant
+#
+ if ((Eterm)($arg0) & 0x3) != 0x2
+ printf "#NotBoxed<%#x>", (Eterm)($arg0)
+ else
+ set $etp_extref_1_p = (ExternalThing*)((Eterm)($arg0) & ~0x3)
+ if ($etp_extref_1_p->header & 0x3F) != 0x38
+ printf "#NotExternalRef<%#x>", $etp_extref_1->header
+ else
+ ## External ref
+ set $etp_extref_1_nump = (Uint32 *) 0
+ set $etp_extref_1_error = 0
+ set $etp_extref_1_i = (int) ($etp_extref_1_p->header >> 6)
+ set $etp_extref_1_np = $etp_extref_1_p->node
+ set $etp_extref_1_creation = $etp_extref_1_np->creation
+ set $etp_extref_1_dep = $etp_extref_1_np->dist_entry
+ set $etp_extref_1_node = $etp_extref_1_np->sysname
+ if ($etp_extref_1_node & 0x3f) != 0xb || $etp_extref_1_i < 3
+ # Node should be an atom
+ set $etp_extref_1_error = 1
+ else
+ ## $etp_extref_1_i now equals data (Uint) words
+ set $etp_extref_1_i -= 2
+ if $etp_arch64
+ if ((((int) $etp_extref_1_p->data.ui32[0]) + 1) \
+ > (2 * $etp_extref_1_i))
+ set $etp_extref_1_error = 1
+ else
+ set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[1]
+ set $etp_extref_1_i = (int) $etp_extref_1_p->data.ui32[0]
+ end
+ else
+ set $etp_extref_1_nump = &$etp_extref_1_p->data.ui32[0]
+ end
+ ## $etp_extref_1_i now equals no of ref num (Uint32) words
+ if !$etp_extref_1_error
+ if $etp_extref_1_dep == erts_this_dist_entry
+ printf "#Ref<0:"
+ else
+ printf "#Ref<%u:", $etp_extref_1_node>>6
+ end
+ etp-atom-1 ($etp_extref_1_node)
+ printf "/%u", $etp_extref_1_creation
+ end
+ end
+ if $etp_extref_1_error
+ printf "#ExternalRefError<%#x>", ($arg0)
+ else
+ set $etp_extref_1_i--
+ while $etp_extref_1_i >= 0
+ printf ".%u", (unsigned) $etp_extref_1_nump[$etp_extref_1_i]
+ set $etp_extref_1_i--
+ end
+ printf ">"
+ end
+ end
+ end
+end
+
+
+
+define etp-mfa-1
+# Args: Eterm*, int offset
+#
+# Reentrant
+#
+ printf "<"
+ etp-atom-1 (((Eterm*)($arg0))[0])
+ printf ":"
+ etp-atom-1 (((Eterm*)($arg0))[1])
+ printf "/%d", ((Eterm*)($arg0))[2]
+ if ($arg1) > 0
+ printf "+%#x>", ($arg1)
+ else
+ printf ">"
+ end
+end
+
+define etp-mfa
+# Args: Eterm*
+#
+# Reentrant capable
+#
+ etp-mfa-1 ($arg0) 0
+ printf ".\n"
+end
+
+document etp-mfa
+%---------------------------------------------------------------------------
+% etp-mfa Eterm*
+%
+% Take an Eterm* to an MFA function name entry and print it.
+% These can be found e.g in the process structure;
+% process_tab[i]->current and process_tab[i]->initial.
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-cp-1
+# Args: Eterm cp
+#
+# Non-reentrant
+#
+ set $etp_cp = (Eterm)($arg0)
+ set $etp_cp_low = modules
+ set $etp_cp_high = $etp_cp_low + num_loaded_modules
+ set $etp_cp_mid = mid_module
+ set $etp_cp_p = 0
+ #
+ while $etp_cp_low < $etp_cp_high
+ if $etp_cp < $etp_cp_mid->start
+ set $etp_cp_high = $etp_cp_mid
+ else
+ if $etp_cp > $etp_cp_mid->end
+ set $etp_cp_low = $etp_cp_mid + 1
+ else
+ set $etp_cp_p = $etp_cp_low = $etp_cp_high = $etp_cp_mid
+ end
+ end
+ set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2
+ end
+ if $etp_cp_p
+ set $etp_cp_low = (Eterm**)($etp_cp_p->start + 8)
+ set $etp_cp_high = $etp_cp_low +$etp_cp_p->start[0]
+ set $etp_cp_p = 0
+ while $etp_cp_low < $etp_cp_high
+ set $etp_cp_mid = $etp_cp_low + ($etp_cp_high-$etp_cp_low)/2
+ if $etp_cp < $etp_cp_mid[0]
+ set $etp_cp_high = $etp_cp_mid
+ else
+ if $etp_cp < $etp_cp_mid[1]
+ set $etp_cp_p = $etp_cp_mid[0]+2
+ set $etp_cp_low = $etp_cp_high = $etp_cp_mid
+ else
+ set $etp_cp_low = $etp_cp_mid + 1
+ end
+ end
+ end
+ end
+ if $etp_cp_p
+ printf "#Cp"
+ etp-mfa-1 ($etp_cp_p) ($etp_cp-((Eterm)($etp_cp_p-2)))
+ else
+ if $etp_cp == beam_apply+1
+ printf "#Cp<terminate process normally>"
+ else
+ if *(Eterm*)($etp_cp) == beam_return_trace[0]
+ if ($etp_cp) == beam_exception_trace
+ printf "#Cp<exception trace>"
+ else
+ printf "#Cp<return trace>"
+ end
+ else
+ if *(Eterm*)($etp_cp) == beam_return_to_trace[0]
+ printf "#Cp<return to trace>"
+ else
+ printf "#Cp<%#x>", $etp_cp
+ end
+ end
+ end
+ end
+end
+
+define etp-cp
+# Args: Eterm cp
+#
+# Reentrant capable
+#
+ etp-cp-1 ($arg0)
+ printf ".\n"
+end
+
+document etp-cp
+%---------------------------------------------------------------------------
+% etp-cp Eterm
+%
+% Take a code continuation pointer and print
+% module, function, arity and offset.
+%
+% Code continuation pointers can be found in the process structure e.g
+% process_tab[i]->cp and process_tab[i]->i, the second is the
+% program counter, which is the same thing as a continuation pointer.
+%---------------------------------------------------------------------------
+end
+
+############################################################################
+# Commands for special term bunches.
+#
+
+define etp-msgq
+# Args: ErlMessageQueue*
+#
+# Non-reentrant
+#
+ set $etp_msgq = ($arg0)
+ set $etp_msgq_p = $etp_msgq->first
+ set $etp_msgq_i = $etp_msgq->len
+ set $etp_msgq_prev = $etp_msgq->last
+ printf "%% Message queue (%d):", $etp_msgq_i
+ if ($etp_msgq_i > 0) && $etp_msgq_p
+ printf "\n["
+ else
+ printf "\n"
+ end
+ while ($etp_msgq_i > 0) && $etp_msgq_p
+ set $etp_msgq_i--
+ set $etp_msgq_next = $etp_msgq_p->next
+ # Msg
+ etp-1 ($etp_msgq_p->m[0]) 0
+ if ($etp_msgq_i > 0) && $etp_msgq_next
+ printf ", %% "
+ else
+ printf "]. %% "
+ end
+ # Seq_trace token
+ etp-1 ($etp_msgq_p->m[1]) 0
+ if $etp_msgq_p == $etp_msgq->save
+ printf ", <=\n"
+ else
+ printf "\n"
+ end
+ if ($etp_msgq_i > 0) && $etp_msgq_next
+ printf " "
+ end
+ #
+ set $etp_msgq_prev = $etp_msgq_p
+ set $etp_msgq_p = $etp_msgq_next
+ end
+ if $etp_msgq_i != 0
+ printf "#MsgQShort<%d>\n", $etp_msgq_i
+ end
+ if $etp_msgq_p != 0
+ printf "#MsgQLong<%#lx%p>\n", (unsigned long)$etp_msgq_p
+ end
+ if $etp_msgq_prev != $etp_msgq->last
+ printf "#MsgQEndError<%#lx%p>\n", (unsigned long)$etp_msgq_prev
+ end
+end
+
+document etp-msgq
+%---------------------------------------------------------------------------
+% etp-msgq ErlMessageQueue*
+%
+% Take an ErlMessageQueue* and print the contents of the message queue.
+% Sequential trace tokens are included in comments and
+% the current match position in the queue is marked '<='.
+%
+% A process's message queue is process_tab[i]->msg.
+%---------------------------------------------------------------------------
+end
+
+
+
+define etpf-msgq
+# Args: Process*
+#
+# Non-reentrant
+#
+ set $etp_flat = 1
+ etp-msgq ($arg0)
+ set $etp_flat = 0
+end
+
+document etpf-msgq
+%---------------------------------------------------------------------------
+% etpf-msgq ErlMessageQueue*
+%
+% Same as 'etp-msgq' but print the messages using etpf (flat).
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-stacktrace
+# Args: Process*
+#
+# Non-reentrant
+#
+ set $etp_stacktrace_p = ($arg0)->stop
+ set $etp_stacktrace_end = ($arg0)->hend
+ printf "%% Stacktrace (%u): ", $etp_stacktrace_end-$etp_stacktrace_p
+ etp ($arg0)->cp
+ while $etp_stacktrace_p < $etp_stacktrace_end
+ if ($etp_stacktrace_p[0] & 0x3) == 0x0
+ # Continuation pointer
+ etp $etp_stacktrace_p[0]
+ end
+ set $etp_stacktrace_p++
+ end
+end
+
+document etp-stacktrace
+%---------------------------------------------------------------------------
+% etp-stacktrace Process*
+%
+% Take an Process* and print a stactrace for the process.
+% The stacktrace consists just of the pushed code continuation
+% pointers on the stack, the most recently pushed first.
+%---------------------------------------------------------------------------
+end
+
+define etp-stackdump
+# Args: Process*
+#
+# Non-reentrant
+#
+ set $etp_stackdump_p = ($arg0)->stop
+ set $etp_stackdump_end = ($arg0)->hend
+ printf "%% Stackdump (%u): ", $etp_stackdump_end-$etp_stackdump_p
+ etp ($arg0)->cp
+ while $etp_stackdump_p < $etp_stackdump_end
+ etp $etp_stackdump_p[0]
+ set $etp_stackdump_p++
+ end
+end
+
+document etp-stackdump
+%---------------------------------------------------------------------------
+% etp-stackdump Process*
+%
+% Take an Process* and print a stackdump for the process.
+% The stackdump consists of all pushed values on the stack.
+% All code continuation pointers are preceeded with a line
+% of dashes to make the stack frames more visible.
+%---------------------------------------------------------------------------
+end
+
+define etpf-stackdump
+# Args: Process*
+#
+# Non-reentrant
+#
+ set $etp_flat = 1
+ etp-stackdump ($arg0)
+ set $etp_flat = 0
+end
+
+document etpf-stackdump
+%---------------------------------------------------------------------------
+% etpf-stackdump Process*
+%
+% Same as etp-stackdump but print the values using etpf (flat).
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-dictdump
+# Args: ProcDict*
+#
+# Non-reentrant
+#
+ set $etp_dictdump = ($arg0)
+ if $etp_dictdump
+ set $etp_dictdump_n = \
+ $etp_dictdump->homeSize + $etp_dictdump->splitPosition
+ set $etp_dictdump_i = 0
+ set $etp_dictdump_written = 0
+ if $etp_dictdump_n > $etp_dictdump->size
+ set $etp_dictdump_n = $etp_dictdump->size
+ end
+ set $etp_dictdump_cnt = $etp_dictdump->numElements
+ printf "%% Dictionary (%d):\n[", $etp_dictdump_cnt
+ while $etp_dictdump_i < $etp_dictdump_n && \
+ $etp_dictdump_cnt > 0
+ set $etp_dictdump_p = $etp_dictdump->data[$etp_dictdump_i]
+ if $etp_dictdump_p != $etp_nil
+ if ((Eterm)$etp_dictdump_p & 0x3) == 0x2
+ # Boxed
+ if $etp_dictdump_written
+ printf ",\n "
+ else
+ set $etp_dictdump_written = 1
+ end
+ etp-1 $etp_dictdump_p 0
+ set $etp_dictdump_cnt--
+ else
+ while ((Eterm)$etp_dictdump_p & 0x3) == 0x1 && \
+ $etp_dictdump_cnt > 0
+ # Cons ptr
+ if $etp_dictdump_written
+ printf ",\n "
+ else
+ set $etp_dictdump_written = 1
+ end
+ etp-1 (((Eterm*)((Eterm)$etp_dictdump_p&~0x3))[0]) 0
+ set $etp_dictdump_cnt--
+ set $etp_dictdump_p = ((Eterm*)((Eterm)$etp_dictdump_p & ~0x3))[1]
+ end
+ if $etp_dictdump_p != $etp_nil
+ printf "#DictSlotError<%d>:", $etp_dictdump_i
+ set $etp_dictdump_flat = $etp_flat
+ set $etp_flat = 1
+ etp-1 ((Eterm)$etp_dictdump_p) 0
+ set $etp_flat = $etp_dictdump_flat
+ end
+ end
+ end
+ set $etp_dictdump_i++
+ end
+ if $etp_dictdump_cnt != 0
+ printf "#DictCntError<%d>, ", $etp_dictdump_cnt
+ end
+ else
+ printf "%% Dictionary (0):\n["
+ end
+ printf "].\n"
+end
+
+document etp-dictdump
+%---------------------------------------------------------------------------
+% etp-dictdump ErlProcDict*
+%
+% Take an ErlProcDict* and print all entries in the process dictionary.
+%---------------------------------------------------------------------------
+end
+
+define etpf-dictdump
+# Args: ErlProcDict*
+#
+# Non-reentrant
+#
+ set $etp_flat = 1
+ etp-dictdump ($arg0)
+ set $etp_flat = 0
+end
+
+document etpf-dictdump
+%---------------------------------------------------------------------------
+% etpf-dictdump ErlProcDict*
+%
+% Same as etp-dictdump but print the values using etpf (flat).
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-offheapdump
+# Args: ( ExternalThing* | ProcBin* | ErlFunThing* )
+#
+# Non-reentrant
+#
+ set $etp_offheapdump_p = ($arg0)
+ set $etp_offheapdump_i = 0
+ set $etp_offheapdump_
+ printf "%% Offheap dump:\n["
+ while ($etp_offheapdump_p != 0) && ($etp_offheapdump_i < $etp_max_depth)
+ if ((Eterm)$etp_offheapdump_p & 0x3) == 0x0
+ if $etp_offheapdump_i > 0
+ printf ",\n "
+ end
+ etp-1 ((Eterm)$etp_offheapdump_p|0x2) 0
+ set $etp_offheapdump_p = $etp_offheapdump_p->next
+ set $etp_offheapdump_i++
+ else
+ printf "#TaggedPtr<%#x>", $etp_offheapdump_p
+ set $etp_offheapdump_p = 0
+ end
+ end
+ printf "].\n"
+end
+
+document etp-offheapdump
+%---------------------------------------------------------------------------
+% etp-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* )
+%
+% Take an pointer to a linked list and print the terms in the list
+% up to the max depth.
+%---------------------------------------------------------------------------
+end
+
+define etpf-offheapdump
+# Args: ( ExternalThing* | ProcBin* | ErlFunThing* )
+#
+# Non-reentrant
+#
+ set $etp_flat = 1
+ etp-offheapdump ($arg0)
+ set $etp_flat = 0
+end
+
+document etpf-offheapdump
+%---------------------------------------------------------------------------
+% etpf-offheapdump ( ExternalThing* | ProcBin* | ErlFunThing* )
+%
+% Same as etp-offheapdump but print the values using etpf (flat).
+%---------------------------------------------------------------------------
+end
+
+define etp-print-procs
+# Args: Eterm
+#
+# Non-reentrant
+#
+ etp-print-procs-1
+end
+
+define etp-print-procs-1
+# Args: Eterm*
+#
+# Non-reentrant
+#
+ set $etp_print_procs_q = erts_max_processes / 10
+ set $etp_print_procs_r = erts_max_processes % 10
+ set $etp_print_procs_t = 10
+ set $etp_print_procs_m = $etp_print_procs_q
+ if $etp_print_procs_r > 0
+ set $etp_print_procs_m++
+ set $etp_print_procs_r--
+ end
+ set $etp_print_procs_i = 0
+ set $etp_print_procs_found = 0
+ while $etp_print_procs_i < erts_max_processes
+ if process_tab[$etp_print_procs_i]
+ printf "%d: ", $etp_print_procs_i
+ etp-1 process_tab[$etp_print_procs_i]->id
+ printf " "
+ etp-1 ((Eterm)(process_tab[$etp_print_procs_i]->i))
+ printf " heap=%d/%d(%d)", process_tab[$etp_print_procs_i]->htop - process_tab[$etp_print_procs_i]->heap, \
+ process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->heap, \
+ process_tab[$etp_print_procs_i]->hend - process_tab[$etp_print_procs_i]->stop
+ printf " old=%d/%d ", process_tab[$etp_print_procs_i]->old_htop - process_tab[$etp_print_procs_i]->old_heap, \
+ process_tab[$etp_print_procs_i]->old_hend - process_tab[$etp_print_procs_i]->old_heap
+ printf " mbuf_sz=%d ", process_tab[$etp_print_procs_i]->mbuf_sz
+ printf " min=%d ", process_tab[$etp_print_procs_i]->min_heap_size
+ printf " flags=%x ", process_tab[$etp_print_procs_i]->flags
+ printf " msgs=%d ", process_tab[$etp_print_procs_i]->msg.len
+ printf "\n"
+ end
+ set $etp_print_procs_i++
+ if $etp_print_procs_i > $etp_print_procs_m
+ printf "%% %d%%...\n", $etp_print_procs_t
+ set $etp_print_procs_t += 10
+ set $etp_print_procs_m += $etp_print_procs_q
+ if $etp_print_procs_r > 0
+ set $etp_print_procs_m++
+ set $etp_print_procs_r--
+ end
+ end
+ end
+ printf "%% 100%%.\n"
+end
+
+document etp-print-procs
+%---------------------------------------------------------------------------
+% etp-print-procs Eterm
+%
+% Print some information about ALL processes.
+%---------------------------------------------------------------------------
+end
+
+
+define etp-search-heaps
+# Args: Eterm
+#
+# Non-reentrant
+#
+ printf "%% Search all (<%u) process heaps for ", erts_max_processes
+ set $etp_flat = 1
+ etp-1 ($arg0) 0
+ set $etp_flat = 0
+ printf ":...\n"
+ etp-search-heaps-1 ((Eterm*)((Eterm)($arg0)&~3))
+end
+
+define etp-search-heaps-1
+# Args: Eterm*
+#
+# Non-reentrant
+#
+ set $etp_search_heaps_q = erts_max_processes / 10
+ set $etp_search_heaps_r = erts_max_processes % 10
+ set $etp_search_heaps_t = 10
+ set $etp_search_heaps_m = $etp_search_heaps_q
+ if $etp_search_heaps_r > 0
+ set $etp_search_heaps_m++
+ set $etp_search_heaps_r--
+ end
+ set $etp_search_heaps_i = 0
+ set $etp_search_heaps_found = 0
+ while $etp_search_heaps_i < erts_max_processes
+ if process_tab[$etp_search_heaps_i]
+ if (process_tab[$etp_search_heaps_i]->heap <= ($arg0)) && \
+ (($arg0) < process_tab[$etp_search_heaps_i]->hend)
+ printf "process_tab[%d]->heap+%d\n", $etp_search_heaps_i, \
+ ($arg0)-process_tab[$etp_search_heaps_i]->heap
+ end
+ if (process_tab[$etp_search_heaps_i]->old_heap <= ($arg0)) && \
+ (($arg0) <= process_tab[$etp_search_heaps_i]->old_hend)
+ printf "process_tab[%d]->old_heap+%d\n", $etp_search_heaps_i, \
+ ($arg0)-process_tab[$etp_search_heaps_i]->old_heap
+ end
+ set $etp_search_heaps_cnt = 0
+ set $etp_search_heaps_p = process_tab[$etp_search_heaps_i]->mbuf
+ while $etp_search_heaps_p && ($etp_search_heaps_cnt < $etp_max_depth)
+ set $etp_search_heaps_cnt++
+ if (&($etp_search_heaps_p->mem) <= ($arg0)) && \
+ (($arg0) < &($etp_search_heaps_p->mem)+$etp_search_heaps_p->size)
+ printf "process_tab[%d]->mbuf(%d)+%d\n", \
+ $etp_search_heaps_i, $etp_search_heaps_cnt, \
+ ($arg0)-&($etp_search_heaps_p->mem)
+ end
+ set $etp_search_heaps_p = $etp_search_heaps_p->next
+ end
+ if $etp_search_heaps_p
+ printf "process_tab[%d] %% Too many HeapFragments\n", \
+ $etp_search_heaps_i
+ end
+ end
+ set $etp_search_heaps_i++
+ if $etp_search_heaps_i > $etp_search_heaps_m
+ printf "%% %d%%...\n", $etp_search_heaps_t
+ set $etp_search_heaps_t += 10
+ set $etp_search_heaps_m += $etp_search_heaps_q
+ if $etp_search_heaps_r > 0
+ set $etp_search_heaps_m++
+ set $etp_search_heaps_r--
+ end
+ end
+ end
+ printf "%% 100%%.\n"
+end
+
+document etp-search-heaps
+%---------------------------------------------------------------------------
+% etp-search-heaps Eterm
+%
+% Search all process heaps in process_tab[], including the heap fragments
+% (process_tab[]->mbuf) for the specified Eterm.
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-search-alloc
+# Args: Eterm
+#
+# Non-reentrant
+#
+ printf "%% Search allocated memory blocks for "
+ set $etp_flat = 1
+ etp-1 ($arg0) 0
+ set $etp_flat = 0
+ printf ":...\n"
+ set $etp_search_alloc_n = sizeof(erts_allctrs) / sizeof(*erts_allctrs)
+ set $etp_search_alloc_i = 0
+ while $etp_search_alloc_i < $etp_search_alloc_n
+ if erts_allctrs[$etp_search_alloc_i].alloc
+ set $etp_search_alloc_f = (erts_allctrs+$etp_search_alloc_i)
+ while ($etp_search_alloc_f->alloc == debug_alloc) || \
+ ($etp_search_alloc_f->alloc == stat_alloc) || \
+ ($etp_search_alloc_f->alloc == map_stat_alloc)
+ set $etp_search_alloc_f = \
+ (ErtsAllocatorFunctions_t*)$etp_search_alloc_f->extra
+ end
+ if ($etp_search_alloc_f->alloc != erts_sys_alloc) && \
+ ($etp_search_alloc_f->alloc != erts_fix_alloc)
+ if ($etp_search_alloc_f->alloc == erts_alcu_alloc) || \
+ ($etp_search_alloc_f->alloc == erts_alcu_alloc_ts)
+ # alcu alloc
+ set $etp_search_alloc_e = (Allctr_t*)$etp_search_alloc_f->extra
+ # mbc_list
+ set $etp_search_alloc_p = $etp_search_alloc_e->mbc_list.first
+ set $etp_search_alloc_cnt = 0
+ while $etp_search_alloc_p && \
+ ($etp_search_alloc_cnt < $etp_max_depth)
+ set $etp_search_alloc_cnt++
+ if $etp_search_alloc_p <= ($arg0) && \
+ ($arg0) < (char*)$etp_search_alloc_p + \
+ ($etp_search_alloc_p->chdr & (Uint)~7)
+ printf "erts_allctrs[%d] %% %salloc: mbc_list: %d\n", \
+ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \
+ $etp_search_alloc_cnt
+ end
+ if $etp_search_alloc_p == $etp_search_alloc_e->mbc_list.last
+ if $etp_search_alloc_p->next
+ printf \
+ "erts_allctrs[%d] %% %salloc: mbc_list.last error %p\n",\
+ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\
+ $etp_search_alloc_p
+ end
+ set $etp_search_alloc_p = 0
+ else
+ set $etp_search_alloc_p = $etp_search_alloc_p->next
+ end
+ end
+ if $etp_search_alloc_p
+ printf "erts_allctrs[%d] %% %salloc: too large mbc_list %p\n", \
+ $ept_search_alloc_i, $etp_search_alloc_e->name_prefix,
+ $ept_search_alloc_p
+ end
+ # sbc_list
+ set $etp_search_alloc_p = $etp_search_alloc_e->sbc_list.first
+ set $etp_search_alloc_cnt = 0
+ while $etp_search_alloc_p && \
+ ($etp_search_alloc_cnt < $etp_max_depth)
+ set $etp_search_alloc_cnt++
+ if $etp_search_alloc_p <= ($arg0) && \
+ ($arg0) < (char*)$etp_search_alloc_p + \
+ ($etp_search_alloc_p->chdr & (Uint)~7)
+ printf "erts_allctrs[%d] %% %salloc: sbc_list: %d\n", \
+ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix, \
+ $etp_search_alloc_cnt
+ end
+ if $etp_search_alloc_p == $etp_search_alloc_e->sbc_list.last
+ if $etp_search_alloc_p->next
+ printf \
+ "erts_allctrs[%d] %% %salloc: sbc_list.last error %p",\
+ $etp_search_alloc_i, $etp_search_alloc_e->name_prefix,\
+ $etp_search_alloc_p
+ end
+ set $etp_search_alloc_p = 0
+ else
+ set $etp_search_alloc_p = $etp_search_alloc_p->next
+ end
+ end
+ if $etp_search_alloc_p
+ printf "erts_allctrs[%d] %% %salloc: too large sbc_list %p\n", \
+ $ept_search_alloc_i, $etp_search_alloc_e->name_prefix,
+ $ept_search_alloc_p
+ end
+ else
+ printf "erts_allctrs[%d] %% %s: unknown allocator\n", \
+ $etp_search_alloc_i, erts_alc_a2ad[$etp_search_alloc_i]
+ end
+ end
+ end
+ set $etp_search_alloc_i++
+ end
+end
+
+document etp-search-alloc
+%---------------------------------------------------------------------------
+% etp-search-heaps Eterm
+%
+% Search all internal allocator memory blocks for for the specified Eterm.
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-overlapped-heaps
+# Args:
+#
+# Non-reentrant
+#
+ printf "%% Dumping heap addresses to \"etp-commands.bin\"\n"
+ set $etp_overlapped_heaps_q = erts_max_processes / 10
+ set $etp_overlapped_heaps_r = erts_max_processes % 10
+ set $etp_overlapped_heaps_t = 10
+ set $etp_overlapped_heaps_m = $etp_overlapped_heaps_q
+ if $etp_overlapped_heaps_r > 0
+ set $etp_overlapped_heaps_m++
+ set $etp_overlapped_heaps_r--
+ end
+ set $etp_overlapped_heaps_i = 0
+ set $etp_overlapped_heaps_found = 0
+ dump binary value etp-commands.bin 'o'
+ append binary value etp-commands.bin 'v'
+ append binary value etp-commands.bin 'e'
+ append binary value etp-commands.bin 'r'
+ append binary value etp-commands.bin 'l'
+ append binary value etp-commands.bin 'a'
+ append binary value etp-commands.bin 'p'
+ append binary value etp-commands.bin 'p'
+ append binary value etp-commands.bin 'e'
+ append binary value etp-commands.bin 'd'
+ append binary value etp-commands.bin '-'
+ append binary value etp-commands.bin 'h'
+ append binary value etp-commands.bin 'e'
+ append binary value etp-commands.bin 'a'
+ append binary value etp-commands.bin 'p'
+ append binary value etp-commands.bin 's'
+ append binary value etp-commands.bin '\0'
+ while $etp_overlapped_heaps_i < erts_max_processes
+ if process_tab[$etp_overlapped_heaps_i]
+ append binary value etp-commands.bin \
+ (Eterm)$etp_overlapped_heaps_i
+ append binary value etp-commands.bin \
+ (Eterm)process_tab[$etp_overlapped_heaps_i]->heap
+ append binary value etp-commands.bin \
+ (Eterm)process_tab[$etp_overlapped_heaps_i]->hend
+ append binary value etp-commands.bin \
+ (Eterm)process_tab[$etp_overlapped_heaps_i]->old_heap
+ append binary value etp-commands.bin \
+ (Eterm)process_tab[$etp_overlapped_heaps_i]->old_hend
+ set $etp_overlapped_heaps_p = process_tab[$etp_overlapped_heaps_i]->mbuf
+ set $etp_overlapped_heaps_cnt = 0
+ while $etp_overlapped_heaps_p && \
+ ($etp_overlapped_heaps_cnt < $etp_max_depth)
+ set $etp_overlapped_heaps_cnt++
+ append binary value etp-commands.bin \
+ (Eterm)$etp_overlapped_heaps_p
+ append binary value etp-commands.bin \
+(Eterm)(&($etp_overlapped_heaps_p->mem)+$etp_overlapped_heaps_p->size)
+ set $etp_overlapped_heaps_p = $etp_overlapped_heaps_p->next
+ end
+ if $etp_overlapped_heaps_p
+ printf "process_tab[%d] %% Too many HeapFragments\n", \
+ $etp_overlapped_heaps_i
+ end
+ append binary value etp-commands.bin (Eterm)0x0
+ append binary value etp-commands.bin (Eterm)0x0
+ end
+ set $etp_overlapped_heaps_i++
+ if $etp_overlapped_heaps_i > $etp_overlapped_heaps_m
+ printf "%% %d%%...\n", $etp_overlapped_heaps_t
+ set $etp_overlapped_heaps_t += 10
+ set $etp_overlapped_heaps_m += $etp_overlapped_heaps_q
+ if $etp_overlapped_heaps_r > 0
+ set $etp_overlapped_heaps_m++
+ set $etp_overlapped_heaps_r--
+ end
+ end
+ end
+ etp-run
+end
+
+document etp-overlapped-heaps
+%---------------------------------------------------------------------------
+% etp-overlapped-heaps
+%
+% Dump all process heap addresses in process_tab[], including
+% the heap fragments in binary format on the file etp-commands.bin.
+% Then call etp_commands:file/1 to analyze if any heaps overlap.
+%
+% Requires 'erl' in the path and 'etp_commands.beam' in 'erl's search path.
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-chart
+# Args: Process*
+#
+# Non-reentrant
+ etp-chart-start ($arg0)
+ set ($arg0) = ($arg0)
+ etp-msgq (($arg0)->msg)
+ etp-stackdump ($arg0)
+ etp-dictdump (($arg0)->dictionary)
+ etp-dictdump (($arg0)->debug_dictionary)
+ printf "%% Dumping other process data...\n"
+ etp ($arg0)->seq_trace_token
+ etp ($arg0)->fvalue
+ printf "%% Dumping done.\n"
+ etp-chart-print
+end
+
+document etp-chart
+%---------------------------------------------------------------------------
+% etp-chart Process*
+%
+% Dump all process data to the file "etp-commands.bin" and then use
+% the Erlang support module to print a memory chart of all terms.
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-chart-start
+# Args: Process*
+#
+# Non-reentrant
+ set $etp_chart = 1
+ set $etp_chart_id = 0
+ set $etp_chart_start_p = ($arg0)
+ dump binary value etp-commands.bin 'c'
+ append binary value etp-commands.bin 'h'
+ append binary value etp-commands.bin 'a'
+ append binary value etp-commands.bin 'r'
+ append binary value etp-commands.bin 't'
+ append binary value etp-commands.bin '\0'
+ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->heap)
+ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->high_water)
+ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->hend)
+ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_heap)
+ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->old_hend)
+ set $etp_chart_start_cnt = 0
+ set $etp_chart_start_p = $etp_chart_start_p->mbuf
+ while $etp_chart_start_p && ($etp_chart_start_cnt < $etp_max_depth)
+ set $etp_chart_start_cnt++
+ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->mem)
+ append binary value etp-commands.bin (Eterm)($etp_chart_start_p->size)
+ set $etp_chart_start_p = $etp_chart_start_p->next
+ end
+ append binary value etp-commands.bin (Eterm)(0)
+ append binary value etp-commands.bin (Eterm)(0)
+ if $etp_chart_start_p
+ printf "%% Too many HeapFragments\n"
+ end
+end
+
+document etp-chart-start
+%---------------------------------------------------------------------------
+% etp-chart-start Process*
+%
+% Dump a chart head to the file "etp-commands.bin".
+%---------------------------------------------------------------------------
+end
+
+
+
+define etp-chart-entry-1
+# Args: Eterm, int depth, int words
+#
+# Reentrant capable
+ if ($arg1) == 0
+ set $etp_chart_id++
+ printf "#%d:", $etp_chart_id
+ end
+ append binary value etp-commands.bin ($arg0)&~0x3
+ append binary value etp-commands.bin (Eterm)(($arg2)*sizeof(Eterm))
+ append binary value etp-commands.bin (Eterm)$etp_chart_id
+ append binary value etp-commands.bin (Eterm)($arg1)
+# printf "<dumped %#x %lu %lu %lu>", ($arg0)&~0x3, \
+# (Eterm)(($arg2)*sizeof(Eterm)), (Eterm)$etp_chart_id, (Eterm)($arg1)
+end
+
+
+
+define etp-chart-print
+ set $etp_chart = 0
+ etp-run
+end
+
+document etp-chart-print
+%---------------------------------------------------------------------------
+% etp-chart-print Process*
+%
+% Print a memory chart of the dumped data in "etp-commands.bin", and stop
+% chart recording.
+%---------------------------------------------------------------------------
+end
+
+############################################################################
+# ETS table debug
+#
+
+define etp-ets-tables
+# Args:
+#
+# Non-reentrant
+ printf "%% Dumping < %lu ETS tables\n", (unsigned long)db_max_tabs
+ while $etp_ets_tables_i < db_max_tabs
+ if (meta_main_tab[$etp_ets_tables_i].u.next_free & 3) == 0
+ printf "%% %d:", $etp_ets_tables_i
+ etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.id)) 0
+ printf " "
+ etp-1 ((Eterm)(meta_main_tab[$etp_ets_tables_i].u.tb->common.owner)) 0
+ printf "\n"
+ end
+ set $etp_ets_tables_i++
+ end
+ set $etp_ets_tables_i = 0
+end
+
+document etp-ets-tables
+%---------------------------------------------------------------------------
+% etp-ets-tables
+%
+% Dump all ETS table names and their indexies.
+%---------------------------------------------------------------------------
+end
+
+define etp-ets-tabledump
+# Args: int tableindex
+#
+# Non-reentrant
+ printf "%% Dumping ETS table %d:", ($arg0)
+ set $etp_ets_tabledump_n = 0
+ set $etp_ets_tabledump_t = meta_main_tab[($arg0)].u.tb
+ set $etp_ets_tabledump_i = 0
+ etp-1 ($etp_ets_tabledump_t->common.the_name) 0
+ printf " status=%#x\n", $etp_ets_tabledump_t->common.status
+ if $etp_ets_tabledump_t->common.status & 0x130
+ # Hash table
+ set $etp_ets_tabledump_h = $etp_ets_tabledump_t->hash
+ printf "%% nitems=%d\n", $etp_ets_tabledump_t->common.nitems
+ while $etp_ets_tabledump_i < $etp_ets_tabledump_h->nactive
+ set $etp_ets_tabledump_l = $etp_ets_tabledump_h->seg \
+ [$etp_ets_tabledump_i>>8][$etp_ets_tabledump_i&0xFF]
+ if $etp_ets_tabledump_l
+ printf "%% Slot %d:\n", $etp_ets_tabledump_i
+ while $etp_ets_tabledump_l
+ if $etp_ets_tabledump_n
+ printf ","
+ else
+ printf "["
+ end
+ set $etp_ets_tabledump_n++
+ etp-1 ((Eterm)($etp_ets_tabledump_l->dbterm.tpl)|0x2) 0
+ if $etp_ets_tabledump_l->hvalue == ((unsigned long)-1)
+ printf "% *\n"
+ else
+ printf "\n"
+ end
+ set $etp_ets_tabledump_l = $etp_ets_tabledump_l->next
+ if $etp_ets_tabledump_n >= $etp_max_depth
+ set $etp_ets_tabledump_l = 0
+ end
+ end
+ end
+ set $etp_ets_tabledump_i++
+ end
+ if $etp_ets_tabledump_n
+ printf "].\n"
+ end
+ else
+ printf "%% Not a hash table\n"
+ end
+end
+
+document etp-ets-tabledump
+%---------------------------------------------------------------------------
+% etp-ets-tabledump Slot
+%
+% Dump an ETS table with a specified slot index.
+%---------------------------------------------------------------------------
+end
+
+############################################################################
+# Erlang support module handling
+#
+
+define etp-run
+ shell make -f "${ROOTDIR:?}/erts/etc/unix/etp_commands.mk" \
+ ROOTDIR="${ROOTDIR:?}" ETP_DATA="etp-commands.bin"
+end
+
+document etp-run
+%---------------------------------------------------------------------------
+% etp-run
+%
+% Make and run the Erlang support module on the input file
+% "erl-commands.bin". The environment variable ROOTDIR must
+% be set to find $ROOTDIR/erts/etc/unix/etp_commands.mk.
+%
+% Also, erl and erlc must be in the path.
+%---------------------------------------------------------------------------
+end
+
+############################################################################
+# Toolbox parameter handling
+#
+
+define etp-set-max-depth
+ if ($arg0) > 0
+ set $etp_max_depth = ($arg0)
+ else
+ echo %%%Error: max-depth <= 0 %%%\n
+ end
+end
+
+document etp-set-max-depth
+%---------------------------------------------------------------------------
+% etp-set-max-depth Depth
+%
+% Set the max term depth to use for etp. The term dept limit
+% works in both depth and width, so if you set the max depth to 10,
+% an 11 element flat tuple will be truncated.
+%---------------------------------------------------------------------------
+end
+
+define etp-set-max-string-length
+ if ($arg0) > 0
+ set $etp_max_string_length = ($arg0)
+ else
+ echo %%%Error: max-string-length <= 0 %%%\n
+ end
+end
+
+document etp-set-max-string-length
+%---------------------------------------------------------------------------
+% etp-set-max-strint-length Length
+%
+% Set the max string length to use for ept when printing lists
+% that can be shown as printable strings. Printable strings
+% that are longer will be truncated, and not even checked if
+% they really are printable all the way to the end.
+%---------------------------------------------------------------------------
+end
+
+define etp-show
+ printf "etp-set-max-depth %d\n", $etp_max_depth
+ printf "etp-set-max-string-length %d\n", $etp_max_string_length
+end
+
+document etp-show
+%---------------------------------------------------------------------------
+% etp-show
+%
+% Show the commands needed to set all etp parameters
+% to their current value.
+%---------------------------------------------------------------------------
+end
+
+############################################################################
+# Init
+#
+
+define etp-init
+ set $etp_arch64 = (sizeof(void *) == 8)
+ if $etp_arch64
+ set $etp_nil = 0xfffffffffffffffb
+ else
+ set $etp_nil = 0xfffffffb
+ end
+ set $etp_flat = 0
+ set $etp_chart_id = 0
+ set $etp_chart = 0
+
+ set $etp_max_depth = 20
+ set $etp_max_string_length = 100
+
+ set $etp_ets_tables_i = 0
+end
+
+document etp-init
+%---------------------------------------------------------------------------
+% Use etp-help for a command overview and general help.
+%
+% To use the Erlang support module, the environment variable ROOTDIR
+% must be set to the toplevel installation directory of Erlang/OTP,
+% so the etp-commands file becomes:
+% $ROOTDIR/erts/etc/unix/etp-commands
+% Also, erl and erlc must be in the path.
+%---------------------------------------------------------------------------
+end
+
+
+etp-init
+help etp-init
+etp-show
diff --git a/erts/etc/unix/etp_commands.erl b/erts/etc/unix/etp_commands.erl
new file mode 100644
index 0000000000..66cb76edbc
--- /dev/null
+++ b/erts/etc/unix/etp_commands.erl
@@ -0,0 +1,173 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2005-2009. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+
+-module(etp_commands).
+
+-export([file/1]).
+
+file([Fname]) ->
+ Result = (catch file_1(Fname)),
+ io:format("% ~p~n", [Result]),
+ init:stop().
+
+file_1(Fname) ->
+ io:format("% Reading ~p...~n", [Fname]),
+ {ok,Fd} = file:open(Fname, [read,binary]),
+ case read_op(Fd, 128) of
+ "chart" ->
+ io:format("% Reading heap chart data...~n"),
+ chart_scan(Fd);
+ "overlapped-heaps" ->
+ io:format("% Reading overlapped-heaps data...~n"),
+ overlapped_scan(Fd)
+ end.
+
+read_op(_Fd, 0) ->
+ [];
+read_op(Fd, N) ->
+ case file:read(Fd, 1) of
+ {ok,<<0>>} -> [];
+ {ok,<<C>>} -> [C|read_op(Fd, N-1)]
+ end.
+
+
+
+overlapped_scan(Fd) ->
+ overlapped_scan_1(Fd, []).
+
+overlapped_scan_1(Fd, R) ->
+ case file:read(Fd, 4*5) of
+ eof ->
+ io:format("% Analyzing overlaps...~n"),
+ overlapped_analyze(lists:sort(R));
+ {ok,<<Id:32/native,Heap:32/native,Hend:32/native,
+ 0:32/native,0:32/native>>}
+ when Heap < Hend ->
+ overlapped_scan_to_0(Fd, [{{Heap,Hend},{Id,heap}}|R], Id, 1);
+ {ok,<<Id:32/native,Heap:32/native,Hend:32/native,
+ OldHeap:32/native,OldHend:32/native>>}
+ when Heap < Hend, OldHeap < OldHend->
+ overlapped_scan_to_0(Fd, [{{Heap,Hend},{Id,heap}},
+ {{OldHeap,OldHend},{Id,old_heap}}|R],
+ Id, 1)
+ end.
+
+overlapped_scan_to_0(Fd, R, Id, Cnt) ->
+ case file:read(Fd, 4*2) of
+ {ok,<<0:32/native,0:32/native>>} ->
+ overlapped_scan_1(Fd, R);
+ {ok,<<Heap:32/native,Hend:32/native>>}
+ when Heap < Hend ->
+ overlapped_scan_to_0(Fd,
+ [{{Heap,Hend},{Id,{heap_fragment,Cnt}}}|R],
+ Id, Cnt+1);
+ eof ->
+ io:format("% Premature end of dump: ~p~n", [Id,Cnt|R])
+ end.
+
+overlapped_analyze([]) ->
+ io:format("% Oops! was that file empty?~n");
+overlapped_analyze([{{_,Hend1},_}|[{{Heap2,_},_}|_]=R])
+ when Hend1 =< Heap2 ->
+ overlapped_analyze(R);
+overlapped_analyze([{Addrs1,Tag1}|[{Addrs2,Tag2}|_]=R]) ->
+ io:format("% ~p overlaps ~p (~p,~p)~n", [Tag1,Tag2,Addrs1,Addrs2]),
+ overlapped_analyze(R);
+overlapped_analyze([_]) ->
+ io:format("% End of overlaps~n").
+
+
+chart_scan(Fd) ->
+ {ok,<<Heap:32/native,HighWater:32/native,Hend:32/native,
+ OldHeap:32/native,OldHend:32/native>>} = file:read(Fd, 4*5),
+ chart_scan_1(Fd,
+ [{Heap,Heap,heap,0},
+ {HighWater,HighWater,high_water,0},
+ {Hend,Hend,hend,0},
+ {OldHeap,OldHeap,old_heap,0},
+ {OldHend,OldHend,old_hend,0}|chart_scan_hdr(Fd)]).
+
+chart_scan_hdr(Fd) ->
+ chart_scan_hdr_2(0, chart_scan_hdr_1(Fd)).
+
+chart_scan_hdr_1(Fd) ->
+ case file:read(Fd, 4*2) of
+ eof -> [];
+ {ok,<<0:32/native,0:32/native>>} -> [];
+ {ok,<<Start:32/native,Size:32/native>>} ->
+ [{Start,Size}|chart_scan_hdr_1(Fd)]
+ end.
+
+chart_scan_hdr_2(_N, []) -> [];
+chart_scan_hdr_2(N, [{Start,End}|T]) when Start =< End ->
+ [{Start,Start,{heap_frag,N},0},{End,End,{heap_frag_end,N},0}
+ |chart_scan_hdr_2(N+1, T)].
+
+chart_scan_1(Fd, R) ->
+ case file:read(Fd, 4*4) of
+ eof ->
+ io:format("% Analyzing heap chart...~n"),
+ chart_analyze(lists:sort(R));
+ {ok,
+ <<Addr:32/native,Size:32/native,Id:32/native,Depth:32/native>>} ->
+ chart_scan_1(Fd, [{Addr,Addr+Size,Id,Depth}|R])
+ end.
+
+%-define(raw_chart_dump, 1).
+-ifdef(raw_chart_dump).
+
+chart_analyze([]) ->
+ io:format("% End of chart~n");
+chart_analyze([{S,E,Id,D}|R]) ->
+ io:format("% ~.16x-~.16x: ~w[~w]~n",
+ [S,"0x",E,"0x",Id,D]),
+ chart_analyze(R).
+
+-else.
+
+chart_analyze([]) ->
+ io:format("% ***Oops, was chart empty?***~n");
+chart_analyze([{S,_,Id,D}=X|R]) ->
+ io:format("% ~.16x: ~w[~w", [S,"0x",Id,D]),
+ chart_analyze_1(R, X).
+
+chart_analyze_1([{S,E,Id,D}=X|R], {S,E,Id,_}) ->
+ io:format(",~w", [D]),
+ chart_analyze_1(R, X);
+chart_analyze_1([{S,E,Id,D}=X|R], {S,E,_,_}) ->
+ io:format("],~w[~w", [Id,D]),
+ chart_analyze_1(R, X);
+chart_analyze_1(R, X) ->
+ io:format("]~n"),
+ chart_analyze_2(R, X).
+
+chart_analyze_2([], {_,E,_,_}) ->
+ io:format("% ~.16x: End of chart~n", [E,"0x"]);
+chart_analyze_2([{S,_,_,_}|_]=R, {_,E,_,_}) ->
+ if E == S ->
+ chart_analyze(R);
+ E < S ->
+ io:format("% ~.16x:~n", [E,"0x"]),
+ chart_analyze(R);
+ true ->
+ io:format("% ~.16x: ***Overlap***~n", [E,"0x"]),
+ chart_analyze(R)
+ end.
+
+-endif.
diff --git a/erts/etc/unix/etp_commands.mk b/erts/etc/unix/etp_commands.mk
new file mode 100644
index 0000000000..1d9a269b68
--- /dev/null
+++ b/erts/etc/unix/etp_commands.mk
@@ -0,0 +1,27 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2005-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+MAKE_AND_EXECUTE_ETP_COMMANDS : $(ETP_DATA) etp_commands.beam
+ erl -noshell -run etp_commands file "$(ETP_DATA)"
+
+.PHONY : MAKE_AND_EXECUTE_ETP_COMMANDS
+
+etp_commands.beam : $(ROOTDIR)/erts/etc/unix/etp_commands.erl $(ROOTDIR)/erts/etc/unix/etp_commands.mk
+ erlc $(ROOTDIR)/erts/etc/unix/etp_commands.erl
+
diff --git a/erts/etc/unix/format_man_pages b/erts/etc/unix/format_man_pages
new file mode 100644
index 0000000000..2c4f6eee4f
--- /dev/null
+++ b/erts/etc/unix/format_man_pages
@@ -0,0 +1,149 @@
+#!/bin/sh
+#
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Format man_pages
+#
+
+ERL_ROOT=$1
+
+echo "Formatting manual pages (this may take a while...)"
+
+if [ -z "$ERL_ROOT" -o ! -d "$ERL_ROOT" ]
+then
+ echo "Install: need ERL_ROOT directory as argument"
+ exit 1
+fi
+
+if [ `echo $ERL_ROOT | awk '{ print substr($1,1,1) }'` != "/" ]
+then
+ echo "Install: need an absolute path to ERL_ROOT"
+ exit 1
+fi
+
+#
+# Fetch target system.
+#
+SYS=`(uname -s) 2>/dev/null` || SYS=unknown
+REL=`(uname -r) 2>/dev/null` || REL=unknown
+case $SYS:$REL in
+ SunOS:5.*)
+ TARGET=sunos5 ;;
+ Linux:*)
+ TARGET=linux ;;
+ Darwin:9.*)
+ TARGET=darwin ;;
+ OpenBSD:3.*)
+ TARGET=openbsd ;;
+ *)
+ TARGET="" ;;
+esac
+
+#
+# Create the 'cat' directories (probably not needed)
+#
+
+cd $ERL_ROOT
+
+if [ ! -d man/cat1 ]
+then
+ mkdir man/cat1
+fi
+
+if [ ! -d man/cat3 ]
+then
+ mkdir man/cat3
+fi
+
+if [ ! -d man/cat4 ]
+then
+ mkdir man/cat4
+fi
+
+if [ ! -d man/cat6 ]
+then
+ mkdir man/cat6
+fi
+
+#
+# Cleanup old formatting
+#
+
+cd $ERL_ROOT/man
+
+rm -f whatis windex
+
+# Remove old cat files
+rm -f cat*/*.[0-9]* *.txt
+
+#
+# Create new formatted pages
+#
+
+case :"$TARGET" in
+:linux|:darwin)
+ # Do not build whatis database, since makewhatis can only run by root
+ # echo "whatis database not created, since makewhatis can only be run by root."
+ ## We would have run
+ ## /usr/sbin/makewhatis -v $ERL_ROOT/man -c $ERL_ROOT/man > /dev/null 2>&1
+
+ if [ ! -x /usr/bin/groff ]; then
+ echo "Cannot find groff - no formating of manual pages"
+ exit
+ fi
+
+ echo "Creating cat files ..."
+
+ # Create cat files
+ for dir in man*
+ do
+ cd $dir
+ for file in *.[0-9]*
+ do
+ if [ -f $file ]; then
+ name=`echo $file | sed 's/\.[^.]*$//'`
+ sec=`echo $file | sed 's/.*\.//'`
+ /usr/bin/groff -Tascii -mandoc $ERL_ROOT/man/man$sec/$file \
+ > $ERL_ROOT/man/cat$sec/$file
+ fi
+ done
+ cd ..
+ done
+ ;;
+:*)
+ if [ -f "/vmunix" ]; then
+ CATMAN=/usr/etc/catman
+ elif [ "$TARGET" = "openbsd" ]; then
+ CATMAN=/usr/sbin/catman
+ else
+ CATMAN=/usr/bin/catman
+ fi
+
+ if [ "$TARGET" = "sunos5" ]
+ then
+ # Special processing of footer
+ rm -f /tmp/erltmac_an
+ sed 's/Last change://g' /usr/share/lib/tmac/an > /tmp/erltmac_an
+ $CATMAN -M $ERL_ROOT/man -T /tmp/erltmac_an > /dev/null 2>&1
+ rm -f /tmp/erltmac_an
+ fi
+
+ $CATMAN -M $ERL_ROOT/man > /dev/null 2>&1
+ ;;
+esac
diff --git a/erts/etc/unix/makewhatis b/erts/etc/unix/makewhatis
new file mode 100644
index 0000000000..047c6efdfa
--- /dev/null
+++ b/erts/etc/unix/makewhatis
@@ -0,0 +1,327 @@
+#!/bin/sh
+# makewhatis: create the whatis database
+# Created: Sun Jun 14 10:49:37 1992
+# Revised: Sat Jan 8 14:12:37 1994 by [email protected]
+# Revised: Sat Mar 23 17:56:18 1996 by [email protected]
+# Copyright 1992, 1993, 1994 Rickard E. Faith ([email protected])
+# May be freely distributed and modified as long as copyright is retained.
+#
+# Wed Dec 23 13:27:50 1992: Rik Faith ([email protected]) applied changes
+# based on Mitchum DSouza ([email protected]) cat patches.
+# Also, cleaned up code and make it work with NET-2 doc pages.
+#
+# makewhatis-1.4: aeb 940802, 941007, 950417
+# Fixed so that the -c option works correctly for the cat pages
+# on my machine. Fix for -u by Nan Zou ([email protected]).
+# Many minor changes.
+# The -s option is undocumented, and may well disappear again.
+#
+# Sat Mar 23 1996: Michael Hamilton ([email protected]).
+# I changed the script to invoke gawk only once for each directory tree.
+# This speeds things up considerably (from 30 minutes down to 1.5 minutes
+# on my 486DX66).
+# 960401 - aeb: slight adaptation to work correctly with cat pages.
+# 960510 - added fixes by [email protected], author of mawk.
+# 971012 - replaced "test -z" - it doesnt work on SunOS 4.1.3_U1.
+# 980710 - be more careful with TMPFILE
+#
+# Note for Slackware users: "makewhatis -v -w -c" will work.
+
+# %ExternalCopyright%
+PATH=/usr/bin:/bin
+
+DEFMANPATH=/usr/man
+DEFCATPATH=/usr/man/preformat:/usr/man
+
+# Find a place for our temporary files. If security is not a concern, use
+# TMPFILE=/tmp/whatis$$; TMPFILEDIR=none
+# Of course makewhatis should only have the required permissions
+# (for reading and writing directories like /usr/man).
+# We try here to be careful (and avoid preconstructed symlinks)
+# in case makewhatis is run as root, by creating a subdirectory of /tmp.
+# If that fails we use $HOME.
+# The code below uses test -O which doesnt work on all systems.
+TMPFILE=$HOME/whatis$$
+TMPFILEDIR=/tmp/whatis$$
+if [ ! -d $TMPFILEDIR ]; then
+ mkdir $TMPFILEDIR
+ chmod 0700 $TMPFILEDIR
+ if [ -O $TMPFILEDIR ]; then
+ TMPFILE=$TMPFILEDIR/w
+ fi
+fi
+
+topath=manpath
+
+defmanpath=$DEFMANPATH
+defcatpath=
+
+sections="1 2 3 4 5 6 7 8 9 n l"
+
+for name in $*
+do
+if [ -n "$setsections" ]; then
+ setsections=
+ sections=$name
+ continue
+fi
+case $name in
+ -c) topath=catpath
+ defmanpath=
+ defcatpath=$DEFCATPATH
+ continue;;
+ -s) setsections=1
+ continue;;
+ -u) findarg="-ctime 0"
+ update=1
+ continue;;
+ -v) verbose=1
+ continue;;
+ -w) manpath=`man --path`
+ continue;;
+ -*) echo "Usage: makewhatis [-u] [-v] [-w] [manpath] [-c [catpath]]"
+ echo " This will build the whatis database for the man pages"
+ echo " found in manpath and the cat pages found in catpath."
+ echo " -u: update database with new pages"
+ echo " -v: verbose"
+ echo " -w: use manpath obtained from \`man --path\`"
+ echo " [manpath]: man directories (default: $DEFMANPATH)"
+ echo " [catpath]: cat directories (default: the first existing"
+ echo " directory in $DEFCATPATH)"
+ exit;;
+ *) if [ -d $name ]
+ then
+ eval $topath="\$$topath":$name
+ else
+ echo "No such directory $name"
+ exit
+ fi;;
+esac
+done
+
+manpath=`echo ${manpath-$defmanpath} | tr : ' '`
+if [ x"$catpath" = x ]; then
+ for d in `echo $defcatpath | tr : ' '`
+ do
+ if [ -d $d ]; then catpath=$d; break; fi
+ done
+fi
+catpath=`echo ${catpath} | tr : ' '`
+
+# first truncate all the whatis files that will be created new,
+# then only update - we might visit the same directory twice
+if [ x$update = x ]; then
+ for pages in man cat
+ do
+ eval path="\$$pages"path
+ for mandir in $path
+ do
+ cp /dev/null $mandir/whatis
+ done
+ done
+fi
+
+for pages in man cat
+do
+ export pages
+ eval path="\$$pages"path
+ for mandir in $path
+ do
+ if [ x$verbose != x ]; then
+ echo "about to enter $mandir" > /dev/tty
+ fi
+ if [ -s ${mandir}/whatis -a $pages = man ]; then
+ if [ x$verbose != x ]; then
+ echo skipping $mandir - we did it already > /dev/tty
+ fi
+ else
+ here=`pwd`
+ cd $mandir
+ for i in $sections
+ do
+ if [ -d ${pages}$i ]
+ then
+ cd ${pages}$i
+ section=$i
+ export section verbose
+ find . -name '*' $findarg -print | /usr/bin/gawk '
+
+ function readline() {
+ if (use_zcat) {
+ result = (pipe_cmd | getline);
+ if (result < 0) {
+ print "Pipe error: " pipe_cmd " " ERRNO > "/dev/stderr";
+ }
+ } else {
+ result = (getline < filename);
+ if (result < 0) {
+ print "Read file error: " filename " " ERRNO > "/dev/stderr";
+ }
+ }
+ return result;
+ }
+
+ function closeline() {
+ if (use_zcat) {
+ return close(pipe_cmd);
+ } else {
+ return close(filename);
+ }
+ }
+
+ function do_one() {
+ after = 0; insh = 0; thisjoin = 1; charct = 0;
+
+ if (verbose) {
+ print "adding " filename > "/dev/tty"
+ }
+
+ use_zcat = (filename ~ /\.Z$/ || filename ~ /\.z$/ ||
+ filename ~ /\.gz$/);
+ match(filename, "/[^/]+$");
+ progname = substr(filename, RSTART + 1, RLENGTH - 1);
+ if (match(progname, "\\." section "[A-Za-z]+")) {
+ actual_section = substr(progname, RSTART + 1, RLENGTH - 1);
+ } else {
+ actual_section = section;
+ }
+ sub(/\..*/, "", progname);
+ if (use_zcat) {
+ pipe_cmd = "zcat " filename;
+ }
+
+ while (readline() > 0) {
+ gsub(/.\b/, "");
+ if (($1 ~ /^\.[Ss][Hh]/ && $2 ~ /[Nn][Aa][Mm][Ee]/) ||
+ (pages == "cat" && $1 ~ /^NAME/)) {
+ if (!insh)
+ insh = 1;
+ else {
+ printf "\n";
+ closeline();
+ return;
+ }
+ } else if (insh) {
+ if ($1 ~ /^\.[Ss][HhYS]/ ||
+ (pages == "cat" &&
+ ($1 ~ /^S[yYeE]/ || $1 ~ /^DESCRIPTION/ ||
+ $1 ~ /^COMMAND/ || $1 ~ /^OVERVIEW/ ||
+ $1 ~ /^STRUCTURES/ || $1 ~ /^INTRODUCTION/))) {
+ # end insh for Synopsis, Syntax, but also for
+ # DESCRIPTION (e.g., XFree86.1x),
+ # COMMAND (e.g., xspread.1)
+ # OVERVIEW (e.g., TclCommandWriting.3)
+ # STRUCTURES (e.g., XEvent.3x)
+ # INTRODUCTION (e.g., TclX.n)
+ printf "\n";
+ closeline();
+ return;
+ } else { # derived from Tom Christiansen perl script
+ if (!after && $0 ~ progname"-") { # Fix old cat pages
+ sub(progname"-", progname" - ");
+ }
+ gsub(/ /, " "); # Translate tabs to spaces
+ gsub(/ +/, " "); # Collapse spaces
+ gsub(/ *, */, ", "); # Fix comma spacings
+ sub(/^ /, ""); # Kill initial spaces
+ sub(/ $/, ""); # Kill trailing spaces
+ sub(/__+/, "_"); # Collapse underscores
+ if ($0 ~ /[^ ]-$/) {
+ sub(/-$/, ""); # Handle Hyphenations
+ nextjoin = 1;
+ } else
+ nextjoin = 0;
+ sub(/^.[IB] /, ""); # Kill bold and italics
+ sub(/^.Nm /, ""); # Kill bold
+ sub(/^.Tn /, ""); # Kill normal
+ sub(/^.Li /, ""); # Kill .Li
+ sub(/^.Dq /, ""); # Kill .Dq
+ sub(/^.Nd */, "- "); # Convert .Nd to dash
+ gsub(/\\f[PRIB0123]/, ""); # Kill font changes
+ gsub(/\\s[-+0-9]*/, ""); # Kill size changes
+ gsub(/\\&/, ""); # Kill \&
+ gsub(/\\\((ru|ul)/, "_"); # Translate
+ gsub(/\\\((mi|hy|em)/, "-"); # Translate
+ gsub(/\\\*\(../, ""); # Kill troff strings
+ sub(/^\.\\\".*/, ""); # Kill comments
+ gsub(/\\/, ""); # Kill all backslashes
+ if ($1 ~ /^\.../ || $1 == "") {
+ if (after && !needmore) {
+ printf "\n";
+ thisjoin = 1;
+ charct = 0;
+ after = 0;
+ }
+ } else {
+ if ($0 ~ /^- /) {
+ sub("- ", " - ");
+ } else if (!thisjoin && $0 !~ /^- /) {
+ printf " ";
+ charct += 1;
+ }
+ thisjoin = nextjoin;
+ if ($0 !~ / - / && $0 !~ / -$/ && $0 !~ /^- /) {
+ printf "%s", $0;
+ charct += length();
+ needmore = 0;
+ } else {
+ after = 1
+ if ($0 ~ / - /) {
+ where = match( $0 , / - /);
+ } else if ($0 ~ / -$/) {
+ where = match( $0, / -$/);
+ } else {
+ where = 1;
+ }
+ if ((width = 20-charct) < 0) width=0
+ printf "%-*s", width, sprintf( "%s (%s)",
+ substr( $0, 1, where-1 ), actual_section );
+ printf "%s", substr( $0, where )
+ if ($0 ~ /- *$/) {
+ needmore = 1;
+ } else {
+ needmore = 0;
+ }
+ }
+ }
+ }
+ }
+ }
+ closeline();
+ }
+
+ { # Main action - process each filename read in.
+ filename = $0;
+ do_one();
+ }
+ ' pages=$pages section=$section verbose=$verbose
+ cd ..
+ fi
+ done > $TMPFILE
+
+ cd $here
+
+ # kludge for Slackware's /usr/man/preformat
+ if [ $mandir = /usr/man/preformat ]
+ then
+ mandir1=/usr/man
+ else
+ mandir1=$mandir
+ fi
+
+ if [ -f ${mandir1}/whatis ]
+ then
+ cat ${mandir1}/whatis >> $TMPFILE
+ fi
+ sed '/^$/d' < $TMPFILE | sort | uniq > ${mandir1}/whatis
+
+ chmod 644 ${mandir1}/whatis
+ rm $TMPFILE
+ fi
+ done
+done
+
+# remove the dir if we created it
+if [ $TMPFILE = $TMPFILEDIR/w ]; then
+ rmdir $TMPFILEDIR
+fi
diff --git a/erts/etc/unix/run_erl.c b/erts/etc/unix/run_erl.c
new file mode 100644
index 0000000000..4bb148df98
--- /dev/null
+++ b/erts/etc/unix/run_erl.c
@@ -0,0 +1,1298 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Module: run_erl.c
+ *
+ * This module implements a reader/writer process that opens two specified
+ * FIFOs, one for reading and one for writing; reads from the read FIFO
+ * and writes to stdout and the write FIFO.
+ *
+ ________ _________
+ | |--<-- pipe.r (fifo1) --<--| |
+ | to_erl | | run_erl | (parent)
+ |________|-->-- pipe.w (fifo2) -->--|_________|
+ ^ master pty
+ |
+ | slave pty
+ ____V____
+ | |
+ | "erl" | (child)
+ |_________|
+*/
+
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#ifdef HAVE_WORKING_POSIX_OPENPT
+#define _XOPEN_SOURCE 600
+#endif
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/select.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <dirent.h>
+#include <termios.h>
+#include <time.h>
+#ifndef NO_SYSLOG
+# include <syslog.h>
+#endif
+#ifdef HAVE_PTY_H
+# include <pty.h>
+#endif
+#ifdef HAVE_UTMP_H
+# include <utmp.h>
+#endif
+#ifdef HAVE_UTIL_H
+# include <util.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#include "run_erl.h"
+#include "safe_string.h" /* sn_printf, strn_cpy, strn_cat, etc */
+
+#ifdef O_NONBLOCK
+# define DONT_BLOCK_PLEASE O_NONBLOCK
+#else
+# define DONT_BLOCK_PLEASE O_NDELAY
+# ifndef EAGAIN
+# define EAGAIN -3898734
+# endif
+#endif
+
+#define noDEBUG
+
+#define DEFAULT_LOG_GENERATIONS 5
+#define LOG_MAX_GENERATIONS 1000 /* No more than 1000 log files */
+#define LOG_MIN_GENERATIONS 2 /* At least two to switch between */
+#define DEFAULT_LOG_MAXSIZE 100000
+#define LOG_MIN_MAXSIZE 1000 /* Smallast value for changing log file */
+#define LOG_STUBNAME "erlang.log."
+#define LOG_PERM 0664
+#define DEFAULT_LOG_ACTIVITY_MINUTES 5
+#define DEFAULT_LOG_ALIVE_MINUTES 15
+#define DEFAULT_LOG_ALIVE_FORMAT "%a %b %e %T %Z %Y"
+#define ALIVE_BUFFSIZ 256
+
+#define PERM 0600
+#define STATUSFILENAME "/run_erl.log"
+#define PIPE_STUBNAME "erlang.pipe"
+#define PIPE_STUBLEN strlen(PIPE_STUBNAME)
+
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 250
+#endif
+
+#ifndef O_SYNC
+#define O_SYNC 0
+#define USE_FSYNC 1
+#endif
+
+#define MAX(x,y) ((x) > (y) ? (x) : (y))
+
+#define FILENAME_BUFSIZ FILENAME_MAX
+
+/* prototypes */
+static void usage(char *);
+static int create_fifo(char *name, int perm);
+static int open_pty_master(char **name);
+static int open_pty_slave(char *name);
+static void pass_on(pid_t);
+static void exec_shell(char **);
+static void status(const char *format,...);
+static void error_logf(int priority, int line, const char *format,...);
+static void catch_sigchild(int);
+static int next_log(int log_num);
+static int prev_log(int log_num);
+static int find_next_log_num(void);
+static int open_log(int log_num, int flags);
+static void write_to_log(int* lfd, int* log_num, char* buf, int len);
+static void daemon_init(void);
+static char *simple_basename(char *path);
+static void init_outbuf(void);
+static int outbuf_size(void);
+static void clear_outbuf(void);
+static char* outbuf_first(void);
+static void outbuf_delete(int bytes);
+static void outbuf_append(const char* bytes, int n);
+static int write_all(int fd, const char* buf, int len);
+static int extract_ctrl_seq(char* buf, int len);
+static void set_window_size(unsigned col, unsigned row);
+
+
+#ifdef DEBUG
+static void show_terminal_settings(struct termios *t);
+#endif
+
+/* static data */
+static char fifo1[FILENAME_BUFSIZ], fifo2[FILENAME_BUFSIZ];
+static char statusfile[FILENAME_BUFSIZ];
+static char log_dir[FILENAME_BUFSIZ];
+static char pipename[FILENAME_BUFSIZ];
+static FILE *stdstatus = NULL;
+static int log_generations = DEFAULT_LOG_GENERATIONS;
+static int log_maxsize = DEFAULT_LOG_MAXSIZE;
+static int log_alive_minutes = DEFAULT_LOG_ALIVE_MINUTES;
+static int log_activity_minutes = DEFAULT_LOG_ACTIVITY_MINUTES;
+static int log_alive_in_gmt = 0;
+static char log_alive_format[ALIVE_BUFFSIZ+1];
+static int run_daemon = 0;
+static char *program_name;
+static int mfd; /* master pty fd */
+static unsigned protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */
+
+/*
+ * Output buffer.
+ *
+ * outbuf_base <= outbuf_out <= outbuf_in <= outbuf_base+outbuf_total
+ */
+static char* outbuf_base;
+static int outbuf_total;
+static char* outbuf_out;
+static char* outbuf_in;
+
+#if defined(NO_SYSCONF) || !defined(_SC_OPEN_MAX)
+# if defined(OPEN_MAX)
+# define HIGHEST_FILENO() OPEN_MAX
+# else
+# define HIGHEST_FILENO() 64 /* arbitrary value */
+# endif
+#else
+# define HIGHEST_FILENO() sysconf(_SC_OPEN_MAX)
+#endif
+
+
+#ifdef NO_SYSLOG
+# define OPEN_SYSLOG() ((void) 0)
+#else
+# define OPEN_SYSLOG() openlog(simple_basename(program_name), \
+ LOG_PID|LOG_CONS|LOG_NOWAIT,LOG_USER)
+#endif
+
+#define ERROR0(Prio,Format) error_logf(Prio,__LINE__,Format"\n")
+#define ERROR1(Prio,Format,A1) error_logf(Prio,__LINE__,Format"\n",A1)
+#define ERROR2(Prio,Format,A1,A2) error_logf(Prio,__LINE__,Format"\n",A1,A2)
+
+#ifdef HAVE_STRERROR
+# define ADD_ERRNO(Format) "errno=%d '%s'\n"Format"\n",errno,strerror(errno)
+#else
+# define ADD_ERRNO(Format) "errno=%d\n"Format"\n",errno
+#endif
+#define ERRNO_ERR0(Prio,Format) error_logf(Prio,__LINE__,ADD_ERRNO(Format))
+#define ERRNO_ERR1(Prio,Format,A1) error_logf(Prio,__LINE__,ADD_ERRNO(Format),A1)
+
+
+int main(int argc, char **argv)
+{
+ int childpid;
+ int sfd;
+ int fd;
+ char *p, *ptyslave=NULL;
+ int i = 1;
+ int off_argv;
+
+ program_name = argv[0];
+
+ if(argc<4) {
+ usage(argv[0]);
+ exit(1);
+ }
+
+ init_outbuf();
+
+ if (!strcmp(argv[1],"-daemon")) {
+ daemon_init();
+ ++i;
+ }
+
+ off_argv = i;
+ strn_cpy(pipename, sizeof(pipename), argv[i++]);
+ strn_cpy(log_dir, sizeof(log_dir), argv[i]);
+ strn_cpy(statusfile, sizeof(statusfile), log_dir);
+ strn_cat(statusfile, sizeof(statusfile), STATUSFILENAME);
+
+#ifdef DEBUG
+ status("%s: pid is : %d\n", argv[0], getpid());
+#endif
+
+ /* Get values for LOG file handling from the environment */
+ if ((p = getenv("RUN_ERL_LOG_ALIVE_MINUTES"))) {
+ log_alive_minutes = atoi(p);
+ if (!log_alive_minutes) {
+ ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ALIVE_MINUTES is 1 "
+ "(current value is %s)",p);
+ }
+ log_activity_minutes = log_alive_minutes / 3;
+ if (!log_activity_minutes) {
+ ++log_activity_minutes;
+ }
+ }
+ if ((p = getenv("RUN_ERL_LOG_ACTIVITY_MINUTES"))) {
+ log_activity_minutes = atoi(p);
+ if (!log_activity_minutes) {
+ ERROR1(LOG_ERR,"Minimum value for RUN_ERL_LOG_ACTIVITY_MINUTES is 1 "
+ "(current value is %s)",p);
+ }
+ }
+ if ((p = getenv("RUN_ERL_LOG_ALIVE_FORMAT"))) {
+ if (strlen(p) > ALIVE_BUFFSIZ) {
+ ERROR1(LOG_ERR, "RUN_ERL_LOG_ALIVE_FORMAT can contain a maximum of "
+ "%d characters", ALIVE_BUFFSIZ);
+ }
+ strn_cpy(log_alive_format, sizeof(log_alive_format), p);
+ } else {
+ strn_cpy(log_alive_format, sizeof(log_alive_format), DEFAULT_LOG_ALIVE_FORMAT);
+ }
+ if ((p = getenv("RUN_ERL_LOG_ALIVE_IN_UTC")) && strcmp(p,"0")) {
+ ++log_alive_in_gmt;
+ }
+ if ((p = getenv("RUN_ERL_LOG_GENERATIONS"))) {
+ log_generations = atoi(p);
+ if (log_generations < LOG_MIN_GENERATIONS)
+ ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_GENERATIONS is %d", LOG_MIN_GENERATIONS);
+ if (log_generations > LOG_MAX_GENERATIONS)
+ ERROR1(LOG_ERR,"Maximum RUN_ERL_LOG_GENERATIONS is %d", LOG_MAX_GENERATIONS);
+ }
+
+ if ((p = getenv("RUN_ERL_LOG_MAXSIZE"))) {
+ log_maxsize = atoi(p);
+ if (log_maxsize < LOG_MIN_MAXSIZE)
+ ERROR1(LOG_ERR,"Minimum RUN_ERL_LOG_MAXSIZE is %d", LOG_MIN_MAXSIZE);
+ }
+
+ /*
+ * Create FIFOs and open them
+ */
+
+ if(*pipename && pipename[strlen(pipename)-1] == '/') {
+ /* The user wishes us to find a unique pipe name in the specified */
+ /* directory */
+ int highest_pipe_num = 0;
+ DIR *dirp;
+ struct dirent *direntp;
+
+ dirp = opendir(pipename);
+ if(!dirp) {
+ ERRNO_ERR1(LOG_ERR,"Can't access pipe directory '%s'.", pipename);
+ exit(1);
+ }
+
+ /* Check the directory for existing pipes */
+
+ while((direntp=readdir(dirp)) != NULL) {
+ if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) {
+ int num = atoi(direntp->d_name+PIPE_STUBLEN+1);
+ if(num > highest_pipe_num)
+ highest_pipe_num = num;
+ }
+ }
+ closedir(dirp);
+ strn_catf(pipename, sizeof(pipename), "%s.%d",
+ PIPE_STUBNAME, highest_pipe_num+1);
+ } /* if */
+
+ /* write FIFO - is read FIFO for `to_erl' program */
+ strn_cpy(fifo1, sizeof(fifo1), pipename);
+ strn_cat(fifo1, sizeof(fifo1), ".r");
+ if (create_fifo(fifo1, PERM) < 0) {
+ ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for writing.", fifo1);
+ exit(1);
+ }
+
+ /* read FIFO - is write FIFO for `to_erl' program */
+ strn_cpy(fifo2, sizeof(fifo2), pipename);
+ strn_cat(fifo2, sizeof(fifo2), ".w");
+
+ /* Check that nobody is running run_erl already */
+ if ((fd = open (fifo2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) {
+ /* Open as client succeeded -- run_erl is already running! */
+ fprintf(stderr, "Erlang already running on pipe %s.\n", pipename);
+ close(fd);
+ exit(1);
+ }
+ if (create_fifo(fifo2, PERM) < 0) {
+ ERRNO_ERR1(LOG_ERR,"Cannot create FIFO %s for reading.", fifo2);
+ exit(1);
+ }
+
+ /*
+ * Open master pseudo-terminal
+ */
+
+ if ((mfd = open_pty_master(&ptyslave)) < 0) {
+ ERRNO_ERR0(LOG_ERR,"Could not open pty master");
+ exit(1);
+ }
+
+ /*
+ * Now create a child process
+ */
+
+ if ((childpid = fork()) < 0) {
+ ERRNO_ERR0(LOG_ERR,"Cannot fork");
+ exit(1);
+ }
+ if (childpid == 0) {
+ /* Child */
+ close(mfd);
+ /* disassociate from control terminal */
+#ifdef USE_SETPGRP_NOARGS /* SysV */
+ setpgrp();
+#elif defined(USE_SETPGRP) /* BSD */
+ setpgrp(0,getpid());
+#else /* POSIX */
+ setsid();
+#endif
+ /* Open the slave pty */
+ if ((sfd = open_pty_slave(ptyslave)) < 0) {
+ ERRNO_ERR1(LOG_ERR,"Could not open pty slave '%s'", ptyslave);
+ exit(1);
+ }
+ /* But sfd may be one of the stdio fd's now, and we should be unmodern and not use dup2... */
+ /* easiest to dup it up... */
+ while (sfd < 3) {
+ sfd = dup(sfd);
+ }
+
+#ifndef NO_SYSLOG
+ /* Before fiddling with file descriptors we make sure syslog is turned off
+ or "closed". In the single case where we might want it again,
+ we will open it again instead. Would not want syslog to
+ go to some other fd... */
+ if (run_daemon) {
+ closelog();
+ }
+#endif
+
+ /* Close stdio */
+ close(0);
+ close(1);
+ close(2);
+
+ if (dup(sfd) != 0 || dup(sfd) != 1 || dup(sfd) != 2) {
+ status("Cannot dup\n");
+ }
+ close(sfd);
+ exec_shell(argv+off_argv); /* exec_shell expects argv[2] to be */
+ /* the command name, so we have to */
+ /* adjust. */
+ } else {
+ /* Parent */
+ /* Ignore the SIGPIPE signal, write() will return errno=EPIPE */
+ struct sigaction sig_act;
+ sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = 0;
+ sig_act.sa_handler = SIG_IGN;
+ sigaction(SIGPIPE, &sig_act, (struct sigaction *)NULL);
+
+ sigemptyset(&sig_act.sa_mask);
+ sig_act.sa_flags = SA_NOCLDSTOP;
+ sig_act.sa_handler = catch_sigchild;
+ sigaction(SIGCHLD, &sig_act, (struct sigaction *)NULL);
+
+ /*
+ * read and write: enter the workloop
+ */
+
+ pass_on(childpid);
+ }
+ return 0;
+} /* main() */
+
+/* pass_on()
+ * Is the work loop of the logger. Selects on the pipe to the to_erl
+ * program erlang. If input arrives from to_erl it is passed on to
+ * erlang.
+ */
+static void pass_on(pid_t childpid)
+{
+ int len;
+ fd_set readfds;
+ fd_set writefds;
+ fd_set* writefds_ptr;
+ struct timeval timeout;
+ time_t last_activity;
+ char buf[BUFSIZ];
+ char log_alive_buffer[ALIVE_BUFFSIZ+1];
+ int lognum;
+ int rfd, wfd=0, lfd=0;
+ int maxfd;
+ int ready;
+ int got_some = 0; /* from to_erl */
+
+ /* Open the to_erl pipe for reading.
+ * We can't open the writing side because nobody is reading and
+ * we'd either hang or get an error.
+ */
+ if ((rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
+ ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2);
+ exit(1);
+ }
+
+#ifdef DEBUG
+ status("run_erl: %s opened for reading\n", fifo2);
+#endif
+
+ /* Open the log file */
+
+ lognum = find_next_log_num();
+ lfd = open_log(lognum, O_RDWR|O_APPEND|O_CREAT|O_SYNC);
+
+ /* Enter the work loop */
+
+ while (1) {
+ int exit_status;
+ maxfd = MAX(rfd, mfd);
+ maxfd = MAX(wfd, maxfd);
+ FD_ZERO(&readfds);
+ FD_SET(rfd, &readfds);
+ FD_SET(mfd, &readfds);
+ FD_ZERO(&writefds);
+ if (outbuf_size() == 0) {
+ writefds_ptr = NULL;
+ } else {
+ FD_SET(wfd, &writefds);
+ writefds_ptr = &writefds;
+ }
+ time(&last_activity);
+ timeout.tv_sec = log_alive_minutes*60; /* don't assume old BSD bug */
+ timeout.tv_usec = 0;
+ ready = select(maxfd + 1, &readfds, writefds_ptr, NULL, &timeout);
+ if (ready < 0) {
+ if (errno == EINTR) {
+ if (waitpid(childpid, &exit_status, WNOHANG) == childpid) {
+ /*
+ * The Erlang emulator has terminated. Give us some more
+ * time to write out any pending data before we terminate too.
+ */
+ alarm(5);
+ }
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ } else {
+ /* Some error occured */
+ ERRNO_ERR0(LOG_ERR,"Error in select.");
+ exit(1);
+ }
+ } else {
+ time_t now;
+
+ if (waitpid(childpid, &exit_status, WNOHANG) == childpid) {
+ alarm(5);
+ FD_ZERO(&readfds);
+ FD_ZERO(&writefds);
+ }
+
+ /* Check how long time we've been inactive */
+ time(&now);
+ if(!ready || now - last_activity > log_activity_minutes*60) {
+ /* Either a time out: 15 minutes without action, */
+ /* or something is coming in right now, but it's a long time */
+ /* since last time, so let's write a time stamp this message */
+ struct tm *tmptr;
+ if (log_alive_in_gmt) {
+ tmptr = gmtime(&now);
+ } else {
+ tmptr = localtime(&now);
+ }
+ if (!strftime(log_alive_buffer, ALIVE_BUFFSIZ, log_alive_format,
+ tmptr)) {
+ strn_cpy(log_alive_buffer, sizeof(log_alive_buffer),
+ "(could not format time in 256 positions "
+ "with current format string.)");
+ }
+ log_alive_buffer[ALIVE_BUFFSIZ] = '\0';
+
+ sn_printf(buf, sizeof(buf), "\n===== %s%s\n",
+ ready?"":"ALIVE ", log_alive_buffer);
+ write_to_log(&lfd, &lognum, buf, strlen(buf));
+ }
+ }
+
+ /*
+ * Write any pending output first.
+ */
+ if (FD_ISSET(wfd, &writefds)) {
+ int written;
+ char* buf = outbuf_first();
+
+ len = outbuf_size();
+ written = write(wfd, buf, len);
+ if (written < 0 && errno == EAGAIN) {
+ /*
+ * Nothing was written - this is really strange because
+ * select() told us we could write. Ignore.
+ */
+ } else if (written < 0) {
+ /*
+ * A write error. Assume that to_erl has terminated.
+ */
+ clear_outbuf();
+ close(wfd);
+ wfd = 0;
+ } else {
+ /* Delete the written part (or all) from the buffer. */
+ outbuf_delete(written);
+ }
+ }
+
+ /*
+ * Read master pty and write to FIFO.
+ */
+ if (FD_ISSET(mfd, &readfds)) {
+#ifdef DEBUG
+ status("Pty master read; ");
+#endif
+ if ((len = read(mfd, buf, BUFSIZ)) <= 0) {
+ close(rfd);
+ if(wfd) close(wfd);
+ close(mfd);
+ unlink(fifo1);
+ unlink(fifo2);
+ if (len < 0) {
+ if(errno == EIO)
+ ERROR0(LOG_ERR,"Erlang closed the connection.");
+ else
+ ERRNO_ERR0(LOG_ERR,"Error in reading from terminal");
+ exit(1);
+ }
+ exit(0);
+ }
+
+ write_to_log(&lfd, &lognum, buf, len);
+
+ /*
+ * Save in the output queue.
+ */
+
+ if (wfd) {
+ outbuf_append(buf, len);
+ }
+ }
+
+ /*
+ * Read from FIFO, write to master pty
+ */
+ if (FD_ISSET(rfd, &readfds)) {
+#ifdef DEBUG
+ status("FIFO read; ");
+#endif
+ if ((len = read(rfd, buf, BUFSIZ)) < 0) {
+ close(rfd);
+ if(wfd) close(wfd);
+ close(mfd);
+ unlink(fifo1);
+ unlink(fifo2);
+ ERRNO_ERR0(LOG_ERR,"Error in reading from FIFO.");
+ exit(1);
+ }
+
+ if(!len) {
+ /* to_erl closed its end of the pipe */
+ close(rfd);
+ rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0);
+ if (rfd < 0) {
+ ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2);
+ exit(1);
+ }
+ got_some = 0; /* reset for next session */
+ }
+ else {
+ if(!wfd) {
+ /* Try to open the write pipe to to_erl. Now that we got some data
+ * from to_erl, to_erl should already be reading this pipe - open
+ * should succeed. But in case of error, we just ignore it.
+ */
+ if ((wfd = open(fifo1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
+ status("Client expected on FIFO %s, but can't open (len=%d)\n",
+ fifo1, len);
+ close(rfd);
+ rfd = open(fifo2, O_RDONLY|DONT_BLOCK_PLEASE, 0);
+ if (rfd < 0) {
+ ERRNO_ERR1(LOG_ERR,"Could not open FIFO '%s' for reading.", fifo2);
+ exit(1);
+ }
+ wfd = 0;
+ }
+ else {
+#ifdef DEBUG
+ status("run_erl: %s opened for writing\n", fifo1);
+#endif
+ }
+ }
+
+ if (!got_some && wfd && buf[0] == '\022') {
+ char wbuf[30];
+ int wlen = sn_printf(wbuf,sizeof(wbuf),"[run_erl v%u-%u]\n",
+ RUN_ERL_HI_VER, RUN_ERL_LO_VER);
+ outbuf_append(wbuf,wlen);
+ }
+ got_some = 1;
+
+
+ /* Write the message */
+#ifdef DEBUG
+ status("Pty master write; ");
+#endif
+ len = extract_ctrl_seq(buf, len);
+
+ if(len==1 && buf[0] == '\003') {
+ kill(childpid,SIGINT);
+ }
+ else if (len>0 && write_all(mfd, buf, len) != len) {
+ ERRNO_ERR0(LOG_ERR,"Error in writing to terminal.");
+ close(rfd);
+ if(wfd) close(wfd);
+ close(mfd);
+ exit(1);
+ }
+ }
+#ifdef DEBUG
+ status("OK\n");
+#endif
+ }
+ }
+} /* pass_on() */
+
+static void catch_sigchild(int sig)
+{
+}
+
+/*
+ * next_log:
+ * Returns the index number that follows the given index number.
+ * (Wrapping after log_generations)
+ */
+static int next_log(int log_num) {
+ return log_num>=log_generations?1:log_num+1;
+}
+
+/*
+ * prev_log:
+ * Returns the index number that precedes the given index number.
+ * (Wrapping after log_generations)
+ */
+static int prev_log(int log_num) {
+ return log_num<=1?log_generations:log_num-1;
+}
+
+/*
+ * find_next_log_num()
+ * Searches through the log directory to check which logs that already
+ * exist. It finds the "hole" in the sequence, and returns the index
+ * number for the last log in the log sequence. If there is no hole, index
+ * 1 is returned.
+ */
+static int find_next_log_num(void) {
+ int i, next_gen, log_gen;
+ DIR *dirp;
+ struct dirent *direntp;
+ int log_exists[LOG_MAX_GENERATIONS+1];
+ int stub_len = strlen(LOG_STUBNAME);
+
+ /* Initialize exiting log table */
+
+ for(i=log_generations; i>=0; i--)
+ log_exists[i] = 0;
+ dirp = opendir(log_dir);
+ if(!dirp) {
+ ERRNO_ERR1(LOG_ERR,"Can't access log directory '%s'", log_dir);
+ exit(1);
+ }
+
+ /* Check the directory for existing logs */
+
+ while((direntp=readdir(dirp)) != NULL) {
+ if(strncmp(direntp->d_name,LOG_STUBNAME,stub_len)==0) {
+ int num = atoi(direntp->d_name+stub_len);
+ if(num < 1 || num > log_generations)
+ continue;
+ log_exists[num] = 1;
+ }
+ }
+ closedir(dirp);
+
+ /* Find out the next available log file number */
+
+ next_gen = 0;
+ for(i=log_generations; i>=0; i--) {
+ if(log_exists[i])
+ if(next_gen)
+ break;
+ else
+ ;
+ else
+ next_gen = i;
+ }
+
+ /* Find out the current log file number */
+
+ if(next_gen)
+ log_gen = prev_log(next_gen);
+ else
+ log_gen = 1;
+
+ return log_gen;
+} /* find_next_log_num() */
+
+/* open_log()
+ * Opens a log file (with given index) for writing. Writing may be
+ * at the end or a trucnating write, according to flags.
+ * A LOGGING STARTED and time stamp message is inserted into the log file
+ */
+static int open_log(int log_num, int flags)
+{
+ char buf[FILENAME_MAX];
+ time_t now;
+ struct tm *tmptr;
+ char log_buffer[ALIVE_BUFFSIZ+1];
+ int lfd;
+
+ /* Remove the next log (to keep a "hole" in the log sequence) */
+ sn_printf(buf, sizeof(buf), "%s/%s%d",
+ log_dir, LOG_STUBNAME, next_log(log_num));
+ unlink(buf);
+
+ /* Create or continue on the current log file */
+ sn_printf(buf, sizeof(buf), "%s/%s%d", log_dir, LOG_STUBNAME, log_num);
+ if((lfd = open(buf, flags, LOG_PERM))<0){
+ ERRNO_ERR1(LOG_ERR,"Can't open log file '%s'.", buf);
+ exit(1);
+ }
+
+ /* Write a LOGGING STARTED and time stamp into the log file */
+ time(&now);
+ if (log_alive_in_gmt) {
+ tmptr = gmtime(&now);
+ } else {
+ tmptr = localtime(&now);
+ }
+ if (!strftime(log_buffer, ALIVE_BUFFSIZ, log_alive_format,
+ tmptr)) {
+ strn_cpy(log_buffer, sizeof(log_buffer),
+ "(could not format time in 256 positions "
+ "with current format string.)");
+ }
+ log_buffer[ALIVE_BUFFSIZ] = '\0';
+
+ sn_printf(buf, sizeof(buf), "\n=====\n===== LOGGING STARTED %s\n=====\n",
+ log_buffer);
+ if (write_all(lfd, buf, strlen(buf)) < 0)
+ status("Error in writing to log.\n");
+
+#if USE_FSYNC
+ fsync(lfd);
+#endif
+
+ return lfd;
+}
+
+/* write_to_log()
+ * Writes a message to a log file. If the current log file is full,
+ * a new log file is opened.
+ */
+static void write_to_log(int* lfd, int* log_num, char* buf, int len)
+{
+ int size;
+
+ /* Decide if new logfile needed, and open if so */
+
+ size = lseek(*lfd,0,SEEK_END);
+ if(size+len > log_maxsize) {
+ close(*lfd);
+ *log_num = next_log(*log_num);
+ *lfd = open_log(*log_num, O_RDWR|O_CREAT|O_TRUNC|O_SYNC);
+ }
+
+ /* Write to log file */
+
+ if (write_all(*lfd, buf, len) < 0) {
+ status("Error in writing to log.\n");
+ }
+
+#if USE_FSYNC
+ fsync(*lfd);
+#endif
+}
+
+/* create_fifo()
+ * Creates a new fifo with the given name and permission.
+ */
+static int create_fifo(char *name, int perm)
+{
+ if ((mkfifo(name, perm) < 0) && (errno != EEXIST))
+ return -1;
+ return 0;
+}
+
+
+/* open_pty_master()
+ * Find a master device, open and return fd and slave device name.
+ */
+
+static int open_pty_master(char **ptyslave)
+{
+ int mfd;
+
+/* Use the posix_openpt if working, as this guarantees creation of the
+ slave device properly. */
+#ifdef HAVE_WORKING_POSIX_OPENPT
+ if ((mfd = posix_openpt(O_RDWR)) >= 0) {
+ if ((*ptyslave = ptsname(mfd)) != NULL &&
+ grantpt(mfd) == 0 &&
+ unlockpt(mfd) == 0) {
+
+ return mfd;
+ }
+ close(mfd);
+ }
+ /* fallback to openpty if it exist */
+#endif
+
+#ifdef HAVE_OPENPTY
+# ifdef PATH_MAX
+# define SLAVE_SIZE PATH_MAX
+# else
+# define SLAVE_SIZE 1024
+# endif
+ {
+ static char slave[SLAVE_SIZE];
+ int sfd;
+# undef SLAVE_SIZE
+
+ if (openpty(&mfd, &sfd, slave, NULL, NULL) == 0) {
+ close(sfd);
+ *ptyslave = slave;
+ return mfd;
+ }
+ }
+
+#elif !defined(HAVE_WORKING_POSIX_OPENPT)
+ /*
+ * The traditional way to find ptys. We only try it if neither
+ * posix_openpt or openpty() are available.
+ */
+ char *major, *minor;
+
+ static char majorchars[] = "pqrstuvwxyzabcdePQRSTUVWXYZABCDE";
+ static char minorchars[] = "0123456789abcdefghijklmnopqrstuv"
+ "wxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_+";
+
+ /* In the old time the names where /dex/ptyXY where */
+ /* X is in "pqrs" and Y in "0123456789abcdef" but FreeBSD */
+ /* and some Linux version has extended this. */
+
+ /* This code could probebly be improved alot. For example look at */
+ /* http://www.xcf.berkeley.edu/~ali/K0D/UNIX/PTY/code/pty.c.html */
+ /* http://www.xcf.berkeley.edu/~ali/K0D/UNIX/PTY/code/upty.h.html */
+
+ {
+ /* New style devpts or devfs /dev/pty/{m,s}{0,1....} */
+
+ static char ptyname[] = "/dev/pty/mX";
+
+ for (minor = minorchars; *minor; minor++) {
+ ptyname[10] = *minor;
+ if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) {
+ ptyname[9] = 's';
+ *ptyslave = ptyname;
+ return mfd;
+ }
+ }
+ }
+
+ {
+ /* Unix98 style /dev/ptym/ptyXY and /dev/pty/ttyXY */
+
+ static char ptyname[] = "/dev/ptym/ptyXY";
+ static char ttyname[] = "/dev/pty/ttyXY";
+
+ for (major = majorchars; *major; major++) {
+ ptyname[13] = *major;
+ for (minor = minorchars; *minor; minor++) {
+ ptyname[14] = *minor;
+ if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) {
+ ttyname[12] = *major;
+ ttyname[13] = *minor;
+ *ptyslave = ttyname;
+ return mfd;
+ }
+ }
+ }
+ }
+
+ {
+ /* Old style /dev/ptyXY */
+
+ static char ptyname[] = "/dev/ptyXY";
+
+ for (major = majorchars; *major; major++) {
+ ptyname[8] = *major;
+ for (minor = minorchars; *minor; minor++) {
+ ptyname[9] = *minor;
+ if ((mfd = open(ptyname, O_RDWR, 0)) >= 0) {
+ ptyname[5] = 't';
+ *ptyslave = ptyname;
+ return mfd;
+ }
+ }
+ }
+ }
+#endif /* !HAVE_OPENPTY */
+ return -1;
+}
+
+static int open_pty_slave(char *name)
+{
+ int sfd;
+#ifdef DEBUG
+ struct termios tty_rmode;
+#endif
+
+ if ((sfd = open(name, O_RDWR, 0)) < 0) {
+ return -1;
+ }
+
+#ifdef DEBUG
+ if (tcgetattr(sfd, &tty_rmode) , 0) {
+ fprintf(stderr, "Cannot get terminals current mode\n");
+ exit(-1);
+ }
+ show_terminal_settings(&tty_rmode);
+#endif
+
+ return sfd;
+}
+
+/* exec_shell()
+ * Executes the named command (in argv format) in a /bin/sh. IO redirection
+ * should already have been taken care of, and this process should be the
+ * child of a fork.
+ */
+static void exec_shell(char **argv)
+{
+ char *sh, **vp;
+ int i;
+
+ sh = "/bin/sh";
+ if ((argv[0] = strrchr(sh, '/')) != NULL)
+ argv[0]++;
+ else
+ argv[0] = sh;
+ argv[1] = "-c";
+ status("Args before exec of shell:\n");
+ for (vp = argv, i = 0; *vp; vp++, i++)
+ status("argv[%d] = %s\n", i, *vp);
+ if (stdstatus) {
+ fclose(stdstatus);
+ }
+ execv(sh, argv);
+ if (run_daemon) {
+ OPEN_SYSLOG();
+ }
+ ERRNO_ERR0(LOG_ERR,"Could not execv");
+}
+
+/* status()
+ * Prints the arguments to a status file
+ * Works like printf (see vfrpintf)
+ */
+static void status(const char *format,...)
+{
+ va_list args;
+ time_t now;
+
+ if (stdstatus == NULL)
+ stdstatus = fopen(statusfile, "w");
+ if (stdstatus == NULL)
+ return;
+ now = time(NULL);
+ fprintf(stdstatus, "run_erl [%d] %s", (int)getpid(), ctime(&now));
+ va_start(args, format);
+ vfprintf(stdstatus, format, args);
+ va_end(args);
+ fflush(stdstatus);
+}
+
+static void daemon_init(void)
+ /* As R Stevens wants it, to a certain extent anyway... */
+{
+ pid_t pid;
+ int i, maxfd = HIGHEST_FILENO();
+
+ if ((pid = fork()) != 0)
+ exit(0);
+#if defined(USE_SETPGRP_NOARGS)
+ setpgrp();
+#elif defined(USE_SETPGRP)
+ setpgrp(0,getpid());
+#else
+ setsid(); /* Seems to be the case on all current platforms */
+#endif
+ signal(SIGHUP, SIG_IGN);
+ if ((pid = fork()) != 0)
+ exit(0);
+
+ /* Should change working directory to "/" and change umask now, but that
+ would be backward incompatible */
+
+ for (i = 0; i < maxfd; ++i ) {
+ close(i);
+ }
+
+ OPEN_SYSLOG();
+ run_daemon = 1;
+}
+
+/* error_logf()
+ * Prints the arguments to stderr or syslog
+ * Works like printf (see vfprintf)
+ */
+static void error_logf(int priority, int line, const char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+
+#ifndef NO_SYSLOG
+ if (run_daemon) {
+ vsyslog(priority,format,args);
+ }
+ else
+#endif
+ {
+ time_t now = time(NULL);
+ fprintf(stderr, "run_erl:%d [%d] %s", line, (int)getpid(), ctime(&now));
+ vfprintf(stderr, format, args);
+ }
+ va_end(args);
+}
+
+static void usage(char *pname)
+{
+ fprintf(stderr, "Usage: %s (pipe_name|pipe_dir/) log_dir \"command [parameters ...]\"\n", pname);
+ fprintf(stderr, "\nYou may also set the environment variables RUN_ERL_LOG_GENERATIONS\n");
+ fprintf(stderr, "and RUN_ERL_LOG_MAXSIZE to the number of log files to use and the\n");
+ fprintf(stderr, "size of the log file when to switch to the next log file\n");
+}
+
+/* Instead of making sure basename exists, we do our own */
+static char *simple_basename(char *path)
+{
+ char *ptr;
+ for (ptr = path; *ptr != '\0'; ++ptr) {
+ if (*ptr == '/') {
+ path = ptr + 1;
+ }
+ }
+ return path;
+}
+
+static void init_outbuf(void)
+{
+ outbuf_total = 1;
+ outbuf_base = malloc(BUFSIZ);
+ clear_outbuf();
+}
+
+static void clear_outbuf(void)
+{
+ outbuf_in = outbuf_out = outbuf_base;
+}
+
+static int outbuf_size(void)
+{
+ return outbuf_in - outbuf_out;
+}
+
+static char* outbuf_first(void)
+{
+ return outbuf_out;
+}
+
+static void outbuf_delete(int bytes)
+{
+ outbuf_out += bytes;
+ if (outbuf_out >= outbuf_in) {
+ outbuf_in = outbuf_out = outbuf_base;
+ }
+}
+
+static void outbuf_append(const char* buf, int n)
+{
+ if (outbuf_base+outbuf_total < outbuf_in+n) {
+ /*
+ * The new data does not fit at the end of the buffer.
+ * Slide down the data to the beginning of the buffer.
+ */
+ if (outbuf_out > outbuf_base) {
+ int size = outbuf_in - outbuf_out;
+ char* p;
+
+ outbuf_in -= outbuf_out - outbuf_base;
+ p = outbuf_base;
+ while (size-- > 0) {
+ *p++ = *outbuf_out++;
+ }
+ outbuf_out = outbuf_base;
+ }
+
+ /*
+ * Allocate a larger buffer if we still cannot fit the data.
+ */
+ if (outbuf_base+outbuf_total < outbuf_in+n) {
+ int size = outbuf_in - outbuf_out;
+ outbuf_total = size+n;
+ outbuf_base = realloc(outbuf_base, outbuf_total);
+ outbuf_out = outbuf_base;
+ outbuf_in = outbuf_base + size;
+ }
+ }
+
+ /*
+ * Copy data to the end of the buffer.
+ */
+ memcpy(outbuf_in, buf, n);
+ outbuf_in += n;
+}
+
+/* Call write() until entire buffer has been written or error.
+ * Return len or -1.
+ */
+static int write_all(int fd, const char* buf, int len)
+{
+ int left = len;
+ int written;
+ for (;;) {
+ written = write(fd,buf,left);
+ if (written == left) {
+ return len;
+ }
+ if (written < 0) {
+ return -1;
+ }
+ left -= written;
+ buf += written;
+ }
+}
+
+/* Extract any control sequences that are ment only for run_erl
+ * and should not be forwarded to the pty.
+ */
+static int extract_ctrl_seq(char* buf, int len)
+{
+ static const char prefix[] = "\033_";
+ static const char suffix[] = "\033\\";
+ char* bufend = buf + len;
+ char* start = buf;
+ char* command;
+ char* end;
+
+ for (;;) {
+ start = find_str(start, bufend-start, prefix);
+ if (!start) break;
+
+ command = start + strlen(prefix);
+ end = find_str(command, bufend-command, suffix);
+ if (end) {
+ unsigned col, row;
+ if (sscanf(command,"version=%u", &protocol_ver)==1) {
+ /*fprintf(stderr,"to_erl v%u\n", protocol_ver);*/
+ }
+ else if (sscanf(command,"winsize=%u,%u", &col, &row)==2) {
+ set_window_size(col,row);
+ }
+ else {
+ ERROR2(LOG_ERR, "Ignoring unknown ctrl command '%.*s'\n",
+ (int)(end-command), command);
+ }
+
+ /* Remove ctrl sequence from buf */
+ end += strlen(suffix);
+ memmove(start, end, bufend-end);
+ bufend -= end - start;
+ }
+ else {
+ ERROR2(LOG_ERR, "Missing suffix in ctrl sequence '%.*s'\n",
+ (int)(bufend-start), start);
+ break;
+ }
+ }
+ return bufend - buf;
+}
+
+static void set_window_size(unsigned col, unsigned row)
+{
+#ifdef TIOCSWINSZ
+ struct winsize ws;
+ ws.ws_col = col;
+ ws.ws_row = row;
+ if (ioctl(mfd, TIOCSWINSZ, &ws) < 0) {
+ ERRNO_ERR0(LOG_ERR,"Failed to set window size");
+ }
+#endif
+}
+
+
+#ifdef DEBUG
+
+#define S(x) ((x) > 0 ? 1 : 0)
+
+static void show_terminal_settings(struct termios *t)
+{
+ printf("c_iflag:\n");
+ printf("Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT));
+ printf("Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL));
+ printf("Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK));
+ printf("Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR));
+ printf("Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR));
+ printf("Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR));
+ printf("Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK));
+ printf("Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP));
+ printf("Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF));
+ printf("ditto output ctrl IXON %d\n", S(t->c_iflag & IXON));
+ printf("Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK));
+ printf("\n");
+ printf("c_oflag:\n");
+ printf("Perform output processing OPOST %d\n", S(t->c_oflag & OPOST));
+ printf("\n");
+ printf("c_cflag:\n");
+ printf("Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL));
+ printf("\n");
+ printf("c_local:\n");
+ printf("Enable echo ECHO %d\n", S(t->c_lflag & ECHO));
+ printf("\n");
+ printf("c_cc:\n");
+ printf("c_cc[VEOF] %d\n", t->c_cc[VEOF]);
+}
+
+#endif /* DEBUG */
+
+
diff --git a/erts/etc/unix/run_erl.h b/erts/etc/unix/run_erl.h
new file mode 100644
index 0000000000..843cda680c
--- /dev/null
+++ b/erts/etc/unix/run_erl.h
@@ -0,0 +1,30 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+/*
+ * The protocol version number used between to_erl and run_erl.
+ */
+#define RUN_ERL_HI_VER 1 /* My preferred protocol version */
+#define RUN_ERL_LO_VER 0 /* The lowest version I accept to talk with */
+
+/* Version history:
+ * 0: Older, without version handshake
+ * 1: R12B-3, version handshake + window size ctrl
+ */
+
diff --git a/erts/etc/unix/safe_string.c b/erts/etc/unix/safe_string.c
new file mode 100644
index 0000000000..a77d9c5456
--- /dev/null
+++ b/erts/etc/unix/safe_string.c
@@ -0,0 +1,123 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Module: safe_string.c
+ *
+ * This is a bunch of generic string operation
+ * that are safe regarding buffer overflow.
+ *
+ * All string functions terminate the process with an error message
+ * on buffer overflow.
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include "safe_string.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+
+
+static void string_overflow_handler(const char* format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vfprintf(stderr,format,args);
+ va_end(args);
+ exit(1);
+}
+
+int vsn_printf(char* dst, size_t size, const char* format, va_list args)
+{
+ int ret = vsnprintf(dst, size, format, args);
+ if (ret >= size || ret < 0) {
+ string_overflow_handler("Buffer truncated '%s'\n",dst);
+ }
+ return ret;
+}
+
+int sn_printf(char* dst, size_t size, const char* format, ...)
+{
+ va_list args;
+ int ret;
+ va_start(args, format);
+ ret = vsn_printf(dst,size,format,args);
+ va_end(args);
+ return ret;
+}
+
+int strn_cpy(char* dst, size_t size, const char* src)
+{
+ return sn_printf(dst,size,"%s",src);
+}
+
+int strn_cat(char* dst, size_t size, const char* src)
+{
+ return strn_catf(dst,size,"%s",src);
+}
+
+int strn_catf(char* dst, size_t size, const char* format, ...)
+{
+ int ret;
+ va_list args;
+#ifdef _GNU_SOURCE
+ int len = strnlen(dst,size);
+#else
+ int len = strlen(dst);
+#endif
+
+ if (len >= size) {
+ string_overflow_handler("Buffer already overflowed '%.*s'\n",
+ size, dst);
+ }
+ va_start(args, format);
+ ret = vsn_printf(dst+len, size-len, format, args);
+ va_end(args);
+ return len+ret;
+}
+
+char* find_str(const char* haystack, int hsize, const char* needle)
+{
+ int i = 0;
+ int nsize = strlen(needle);
+ hsize -= nsize - 1;
+ for (i=0; i<hsize; i++) {
+ if (haystack[i]==needle[0] && strncmp(haystack+i,needle,nsize)==0) {
+ return (char*)(haystack+i);
+ }
+ }
+ return NULL;
+}
+
+#ifndef HAVE_MEMMOVE
+void* memmove(void *dest, const void *src, size_t n)
+{
+ int i;
+ if (src > dest) {
+ for (i=0; i<n; i++) ((char*)dest)[i] = ((char*)src)[i];
+ }
+ else {
+ for (i=(int)(n-1); i>=0; i--) ((char*)dest)[i] = ((char*)src)[i];
+ }
+ return dest;
+}
+#endif /* HAVE_MEMMOVE */
+
diff --git a/erts/etc/unix/safe_string.h b/erts/etc/unix/safe_string.h
new file mode 100644
index 0000000000..c70e528814
--- /dev/null
+++ b/erts/etc/unix/safe_string.h
@@ -0,0 +1,65 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Module: safe_string.h
+ *
+ * This is an interface to a bunch of generic string operation
+ * that are safe regarding buffer overflow.
+ *
+ * All string functions terminate the process with an error message
+ * on buffer overflow.
+ */
+
+#include <stdio.h>
+#include <stdarg.h>
+
+/* Like vsnprintf()
+ */
+int vsn_printf(char* dst, size_t size, const char* format, va_list args);
+
+/* Like snprintf()
+ */
+int sn_printf(char* dst, size_t size, const char* format, ...);
+
+/* Like strncpy()
+ * Returns length of copied string.
+ */
+int strn_cpy(char* dst, size_t size, const char* src);
+
+/* Almost like strncat()
+ * size is sizeof entire dst buffer.
+ * Returns length of resulting string.
+ */
+int strn_cat(char* dst, size_t size, const char* src);
+
+/* Combination of strncat() and snprintf()
+ * size is sizeof entire dst buffer.
+ * Returns length of resulting string.
+ */
+int strn_catf(char* dst, size_t size, const char* format, ...);
+
+/* Simular to strstr() but search size bytes of haystack
+ * without regard to '\0' characters.
+ */
+char* find_str(const char* haystack, int size, const char* needle);
+
+#ifndef HAVE_MEMMOVE
+void* memmove(void *dest, const void *src, size_t n);
+#endif
+
diff --git a/erts/etc/unix/setuid_socket_wrap.c b/erts/etc/unix/setuid_socket_wrap.c
new file mode 100644
index 0000000000..3f0657770c
--- /dev/null
+++ b/erts/etc/unix/setuid_socket_wrap.c
@@ -0,0 +1,259 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * setuid_socket_wrap.c
+ *
+ * ./a.out [-s [tag,][addr]:[port]]* [-d [tag,][addr]:[port]]*
+ * [-r [tag,]proto]* -- program args
+ *
+ * Where: -s = stream socket, -d datagram socket and -r means raw socket.
+ *
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#ifndef EXEC_PROGRAM
+# define EXEC_PROGRAM "/bin/echo"
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#ifndef INADDR_NONE
+#define INADDR_NONE 0xffffffff
+#endif
+
+struct sock_list {
+ struct sock_list *next;
+ int fd;
+ int type;
+ int protocol;
+ struct sockaddr_in addr;
+ char *arg;
+};
+
+int parse_addr(addr, str)
+ struct sockaddr_in *addr;
+ char *str;
+{
+ int port = 0;
+ char *cp;
+ struct hostent *hp;
+ struct servent *se;
+
+ if ((cp = strrchr(str, (int)':')) != NULL)
+ *cp++ = '\0';
+ if (cp) {
+ if (!isdigit((int)cp[0])) {
+ if ((se = getservbyname(cp, "tcp")) != NULL) {
+ port = ntohs(se->s_port);
+ } else {
+ fprintf(stderr, "unknown port %s\n", cp);
+ return -1;
+ }
+ } else {
+ port = atoi(cp);
+ }
+ }
+ if (port < 0 || port > 0xffff) {
+ fprintf(stderr, "bad port number %d\n", port);
+ return -1;
+ }
+
+ bzero(addr, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(port);
+ if (*str == '\000') {
+ addr->sin_addr.s_addr = INADDR_ANY;
+ } else {
+ if ((addr->sin_addr.s_addr = inet_addr(str)) == INADDR_NONE) {
+ if ((hp = gethostbyname(str)) == NULL) {
+ fprintf(stderr, "\"%s\" unknown host or address!\n", str);
+ return -1;
+ } else {
+ bcopy(hp->h_addr_list[0], &addr->sin_addr.s_addr,hp->h_length);
+ }
+ }
+ }
+ return 0;
+}
+
+struct sock_list *new_entry(type, argstr)
+ int type;
+ char *argstr;
+{
+ struct sock_list *sle;
+ char *cp;
+
+ sle = (struct sock_list *)malloc(sizeof(struct sock_list));
+ if (!sle)
+ return NULL;
+ sle->next = NULL;
+ sle->fd = -1;
+
+ if ((cp = strchr(argstr, (int)',')) != NULL) {
+ *cp++ = '\0';
+ sle->arg = argstr;
+ argstr = cp;
+ } else {
+ sle->arg = "-fd";
+ }
+ sle->type = type;
+ switch (type) {
+ case SOCK_RAW: {
+ struct protoent *pe;
+ pe = getprotobyname(argstr);
+ if (!pe) {
+ fprintf(stderr, "Unknown protocol: %s\n", argstr);
+ free(sle);
+ return NULL;
+ }
+ sle->protocol = pe->p_proto;
+ break;
+ }
+ case SOCK_STREAM:
+ case SOCK_DGRAM:
+ sle->protocol = 0;
+ if (parse_addr(&sle->addr, argstr) < 0) {
+ free(sle);
+ return NULL;
+ }
+ break;
+ }
+ return sle;
+}
+
+int open_socket(sle)
+ struct sock_list *sle;
+{
+ sle->fd = socket(AF_INET, sle->type, sle->protocol);
+ if (sle->fd < 0) {
+ perror("socket");
+ return -1;
+ }
+ if (sle->type != SOCK_RAW) {
+#if 0
+ printf("binding fd %d to %s:%d\n", sle->fd,
+ inet_ntoa(sle->addr.sin_addr), ntohs(sle->addr.sin_port));
+#endif
+ if (bind(sle->fd, (struct sockaddr *)&sle->addr, sizeof(sle->addr))<0){
+ perror("bind");
+ close(sle->fd);
+ return -1;
+ }
+ }
+ return sle->fd;
+}
+
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct sock_list *sl = NULL, *sltmp = NULL;
+ int count = 0;
+ int c;
+
+ while ((c = getopt(argc, argv, "s:d:r:")) != EOF)
+ switch (c) {
+ case 's':
+ sltmp = new_entry(SOCK_STREAM, optarg);
+ if (!sltmp) {
+ exit(1);
+ }
+ sltmp->next = sl;
+ sl = sltmp;
+ count++;
+ break;
+ case 'd':
+ sltmp = new_entry(SOCK_DGRAM, optarg);
+ if (!sltmp) {
+ exit(1);
+ }
+ sltmp->next = sl;
+ sl = sltmp;
+ count++;
+ break;
+ case 'r':
+ sltmp = new_entry(SOCK_RAW, optarg);
+ if (!sltmp) {
+ exit(1);
+ }
+ sltmp->next = sl;
+ sl = sltmp;
+ count++;
+ break;
+ default:
+ exit(1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ for(sltmp = sl; sltmp != NULL; sltmp = sltmp->next)
+ if (open_socket(sltmp) < 0) {
+ fprintf(stderr, "failed to create socket!\n");
+ exit(1);
+ }
+
+ setuid(getuid());
+
+ {
+ int i;
+ char **newargv;
+ char *run_prog = EXEC_PROGRAM;
+ char *run_prog_name;
+
+ newargv = (char **)malloc((1 + 2*count + argc + 1) * sizeof(char*));
+
+ if ((run_prog_name = strrchr(run_prog, (int)'/')) == NULL)
+ run_prog_name = run_prog;
+ else
+ run_prog_name++;
+
+ i = 0;
+ newargv[i++] = run_prog_name;
+
+ for (; argc; argc--, argv++, i++)
+ newargv[i] = *argv;
+ for(sltmp = sl; sltmp != NULL; ) {
+ char *fd_str = (char *)malloc(8);
+ if (!fd_str) exit(1);
+ sprintf(fd_str, "%d", sltmp->fd);
+ if (sltmp->arg && *(sltmp->arg))
+ newargv[i++] = sltmp->arg;
+ newargv[i++] = fd_str;
+ sl = sltmp;
+ sltmp = sltmp->next;
+ free(sl);
+ }
+ newargv[i] = (char *)NULL;
+ execv(run_prog, newargv);
+ perror("exec");
+ exit(1);
+ }
+ exit(0);
+}
diff --git a/erts/etc/unix/start.src b/erts/etc/unix/start.src
new file mode 100644
index 0000000000..8479be0987
--- /dev/null
+++ b/erts/etc/unix/start.src
@@ -0,0 +1,36 @@
+#!/bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# This program invokes the erlang emulator by calling run_erl.
+# It should only be used at an embedded target system.
+# It should be modified to give the correct flags to erl (via start_erl),
+# e.g -mode embedded -sname XXX
+#
+# Usage: start [Data]
+#
+ROOTDIR=%FINAL_ROOTDIR%
+
+if [ -z "$RELDIR" ]
+then
+ RELDIR=$ROOTDIR/releases
+fi
+
+START_ERL_DATA=${1:-$RELDIR/start_erl.data}
+
+$ROOTDIR/bin/run_erl -daemon /tmp/ $ROOTDIR/log "exec $ROOTDIR/bin/start_erl $ROOTDIR $RELDIR $START_ERL_DATA"
diff --git a/erts/etc/unix/start_erl.src b/erts/etc/unix/start_erl.src
new file mode 100644
index 0000000000..ea8022c449
--- /dev/null
+++ b/erts/etc/unix/start_erl.src
@@ -0,0 +1,47 @@
+#!/bin/sh
+#
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# This program is called by run_erl. It starts
+# the erlang emulator and sets -boot and -config parameters.
+# It should only be used at an embedded target system.
+#
+# Usage: start_erl RootDir RelDir DataFile [ErlFlags ...]
+#
+ROOTDIR=$1
+shift
+RELDIR=$1
+shift
+DataFile=$1
+shift
+
+ERTS_VSN=`awk '{print $1}' $DataFile`
+VSN=`awk '{print $2}' $DataFile`
+
+BINDIR=$ROOTDIR/erts-$ERTS_VSN/bin
+EMU=beam
+PROGNAME=`echo $0 | sed 's/.*\///'`
+export EMU
+export ROOTDIR
+export BINDIR
+export PROGNAME
+export RELDIR
+
+exec $BINDIR/erlexec -boot $RELDIR/$VSN/start -config $RELDIR/$VSN/sys ${1+"$@"}
+
diff --git a/erts/etc/unix/to_erl.c b/erts/etc/unix/to_erl.c
new file mode 100644
index 0000000000..588d127445
--- /dev/null
+++ b/erts/etc/unix/to_erl.c
@@ -0,0 +1,610 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Module: to_erl.c
+ *
+ * This module implements a process that opens two specified FIFOs, one
+ * for reading and one for writing; reads from its stdin, and writes what
+ * it has read to the write FIF0; reads from the read FIFO, and writes to
+ * its stdout.
+ *
+ ________ _________
+ | |--<-- pipe.r (fifo1) --<--| |
+ | to_erl | | run_erl | (parent)
+ |________|-->-- pipe.w (fifo2) -->--|_________|
+ ^ master pty
+ |
+ | slave pty
+ ____V____
+ | |
+ | "erl" | (child)
+ |_________|
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <dirent.h>
+#include <signal.h>
+#include <errno.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#include "run_erl.h"
+#include "safe_string.h" /* strn_cpy, strn_catf, sn_printf, etc. */
+
+#if defined(O_NONBLOCK)
+# define DONT_BLOCK_PLEASE O_NONBLOCK
+#else
+# define DONT_BLOCK_PLEASE O_NDELAY
+# if !defined(EAGAIN)
+# define EAGAIN -3898734
+# endif
+#endif
+
+#ifdef HAVE_STRERROR
+# define STRERROR(x) strerror(x)
+#else
+# define STRERROR(x) ""
+#endif
+
+#define noDEBUG
+
+#define PIPE_DIR "/tmp/"
+#define PIPE_STUBNAME "erlang.pipe"
+#define PIPE_STUBLEN strlen(PIPE_STUBNAME)
+
+#ifdef DEBUG
+#define STATUS(s) { fprintf(stderr, (s)); fflush(stderr); }
+#else
+#define STATUS(s)
+#endif
+
+#ifndef FILENAME_MAX
+#define FILENAME_MAX 250
+#endif
+
+static struct termios tty_smode, tty_rmode;
+static int tty_eof = 0;
+static int recv_sig = 0;
+static int protocol_ver = RUN_ERL_LO_VER; /* assume lowest to begin with */
+
+static int write_all(int fd, const char* buf, int len);
+static int window_size_seq(char* buf, size_t bufsz);
+static int version_handshake(char* buf, int len, int wfd);
+#ifdef DEBUG
+static void show_terminal_settings(struct termios *);
+#endif
+
+static void handle_ctrlc(int sig)
+{
+ /* Reinstall the handler, and signal break flag */
+ signal(SIGINT,handle_ctrlc);
+ recv_sig = SIGINT;
+}
+
+static void handle_sigwinch(int sig)
+{
+ recv_sig = SIGWINCH;
+}
+
+static void usage(char *pname)
+{
+ fprintf(stderr, "Usage: %s [-h|-F] [pipe_name|pipe_dir/]\n", pname);
+ fprintf(stderr, "\t-h\tThis help text.\n");
+ fprintf(stderr, "\t-F\tForce connection even though pipe is locked by other to_erl process.\n");
+}
+
+int main(int argc, char **argv)
+{
+ char FIFO1[FILENAME_MAX], FIFO2[FILENAME_MAX];
+ int i, len, wfd, rfd, result = 0;
+ fd_set readfds;
+ char buf[BUFSIZ];
+ char pipename[FILENAME_MAX];
+ int pipeIx = 1;
+ int force_lock = 0;
+ int got_some = 0;
+
+ if (argc >= 2 && argv[1][0]=='-') {
+ switch (argv[1][1]) {
+ case 'h':
+ usage(argv[0]);
+ exit(1);
+ case 'F':
+ force_lock = 1;
+ break;
+ default:
+ fprintf(stderr,"Invalid option '%s'\n",argv[1]);
+ exit(1);
+ }
+ pipeIx = 2;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "%s: pid is : %d\n", argv[0], (int)getpid());
+#endif
+
+ strn_cpy(pipename, sizeof(pipename),
+ (argv[pipeIx] ? argv[pipeIx] : PIPE_DIR));
+
+ if(*pipename && pipename[strlen(pipename)-1] == '/') {
+ /* The user wishes us to find a pipe name in the specified */
+ /* directory */
+ int highest_pipe_num = 0;
+ DIR *dirp;
+ struct dirent *direntp;
+
+ dirp = opendir(pipename);
+ if(!dirp) {
+ fprintf(stderr, "Can't access pipe directory %s.\n", pipename);
+ exit(1);
+ }
+
+ /* Check the directory for existing pipes */
+
+ while((direntp=readdir(dirp)) != NULL) {
+ if(strncmp(direntp->d_name,PIPE_STUBNAME,PIPE_STUBLEN)==0) {
+ int num = atoi(direntp->d_name+PIPE_STUBLEN+1);
+ if(num > highest_pipe_num)
+ highest_pipe_num = num;
+ }
+ }
+ closedir(dirp);
+ strn_catf(pipename, sizeof(pipename), (highest_pipe_num?"%s.%d":"%s"),
+ PIPE_STUBNAME, highest_pipe_num);
+ } /* if */
+
+ /* read FIFO */
+ sn_printf(FIFO1,sizeof(FIFO1),"%s.r",pipename);
+ /* write FIFO */
+ sn_printf(FIFO2,sizeof(FIFO2),"%s.w",pipename);
+
+ /* Check that nobody is running to_erl on this pipe already */
+ if ((wfd = open (FIFO1, O_WRONLY|DONT_BLOCK_PLEASE, 0)) >= 0) {
+ /* Open as server succeeded -- to_erl is already running! */
+ close(wfd);
+ fprintf(stderr, "Another to_erl process already attached to pipe "
+ "%s.\n", pipename);
+ if (force_lock) {
+ fprintf(stderr, "But we proceed anyway by force (-F).\n");
+ }
+ else {
+ exit(1);
+ }
+ }
+
+ if ((rfd = open (FIFO1, O_RDONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Could not open FIFO %s for reading.\n", FIFO1);
+#endif
+ fprintf(stderr, "No running Erlang on pipe %s.\n", pipename);
+ exit(1);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "to_erl: %s opened for reading\n", FIFO1);
+#endif
+
+ if ((wfd = open (FIFO2, O_WRONLY|DONT_BLOCK_PLEASE, 0)) < 0) {
+#ifdef DEBUG
+ fprintf(stderr, "Could not open FIFO %s for writing.\n", FIFO2);
+#endif
+ fprintf(stderr, "No running Erlang on pipe %s.\n", pipename);
+ close(rfd);
+ exit(1);
+ }
+#ifdef DEBUG
+ fprintf(stderr, "to_erl: %s opened for writing\n", FIFO2);
+#endif
+
+ fprintf(stderr, "Attaching to %s (^D to exit)\n\n", pipename);
+
+ /* Set break handler to our handler */
+ signal(SIGINT,handle_ctrlc);
+
+ /*
+ * Save the current state of the terminal, and set raw mode.
+ */
+ if (tcgetattr(0, &tty_rmode) , 0) {
+ fprintf(stderr, "Cannot get terminals current mode\n");
+ exit(-1);
+ }
+ tty_smode = tty_rmode;
+ tty_eof = '\004'; /* Ctrl+D to exit */
+#ifdef DEBUG
+ show_terminal_settings(&tty_rmode);
+#endif
+ tty_smode.c_iflag =
+ 1*BRKINT |/*Signal interrupt on break.*/
+ 1*IGNPAR |/*Ignore characters with parity errors.*/
+ 1*ISTRIP |/*Strip character.*/
+ 0;
+
+#if 0
+0*IGNBRK |/*Ignore break condition.*/
+0*PARMRK |/*Mark parity errors.*/
+0*INPCK |/*Enable input parity check.*/
+0*INLCR |/*Map NL to CR on input.*/
+0*IGNCR |/*Ignore CR.*/
+0*ICRNL |/*Map CR to NL on input.*/
+0*IUCLC |/*Map upper-case to lower-case on input.*/
+0*IXON |/*Enable start/stop output control.*/
+0*IXANY |/*Enable any character to restart output.*/
+0*IXOFF |/*Enable start/stop input control.*/
+0*IMAXBEL|/*Echo BEL on input line too long.*/
+#endif
+
+ tty_smode.c_oflag =
+ 1*OPOST |/*Post-process output.*/
+ 1*ONLCR |/*Map NL to CR-NL on output.*/
+#ifdef XTABS
+ 1*XTABS |/*Expand tabs to spaces. (Linux)*/
+#endif
+#ifdef OXTABS
+ 1*OXTABS |/*Expand tabs to spaces. (FreeBSD)*/
+#endif
+#ifdef NL0
+ 1*NL0 |/*Select newline delays*/
+#endif
+#ifdef CR0
+ 1*CR0 |/*Select carriage-return delays*/
+#endif
+#ifdef TAB0
+ 1*TAB0 |/*Select horizontal tab delays*/
+#endif
+#ifdef BS0
+ 1*BS0 |/*Select backspace delays*/
+#endif
+#ifdef VT0
+ 1*VT0 |/*Select vertical tab delays*/
+#endif
+#ifdef FF0
+ 1*FF0 |/*Select form feed delays*/
+#endif
+ 0;
+
+#if 0
+0*OLCUC |/*Map lower case to upper on output.*/
+0*OCRNL |/*Map CR to NL on output.*/
+0*ONOCR |/*No CR output at column 0.*/
+0*ONLRET |/*NL performs CR function.*/
+0*OFILL |/*Use fill characters for delay.*/
+0*OFDEL |/*Fill is DEL, else NULL.*/
+0*NL1 |
+0*CR1 |
+0*CR2 |
+0*CR3 |
+0*TAB1 |
+0*TAB2 |
+0*TAB3 |/*Expand tabs to spaces.*/
+0*BS1 |
+0*VT1 |
+0*FF1 |
+#endif
+
+ /* JALI: removed setting the tty_smode.c_cflag flags, since this is not */
+ /* advisable if this is a *real* terminal, such as the console. In fact */
+ /* this may hang the entire machine, deep, deep down (signalling break */
+ /* or toggling the abort switch doesn't help) */
+
+ tty_smode.c_lflag =
+ 0;
+
+#if 0
+0*ISIG |/*Enable signals.*/
+0*ICANON |/*Canonical input (erase and kill processing).*/
+0*XCASE |/*Canonical upper/lower presentation.*/
+0*ECHO |/*Enable echo.*/
+0*ECHOE |/*Echo erase character as BS-SP-BS.*/
+0*ECHOK |/*Echo NL after kill character.*/
+0*ECHONL |/*Echo NL.*/
+0*NOFLSH |/*Disable flush after interrupt or quit.*/
+0*TOSTOP |/*Send SIGTTOU for background output.*/
+0*ECHOCTL|/*Echo control characters as ^char, delete as ^?.*/
+0*ECHOPRT|/*Echo erase character as character erased.*/
+0*ECHOKE |/*BS-SP-BS erase entire line on line kill.*/
+0*FLUSHO |/*Output is being flushed.*/
+0*PENDIN |/*Retype pending input at next read or input character.*/
+0*IEXTEN |/*Enable extended (implementation-defined) functions.*/
+#endif
+
+ tty_smode.c_cc[VMIN] =0;/* Note that VMIN is the same as VEOF! */
+ tty_smode.c_cc[VTIME] =0;/* Note that VTIME is the same as VEOL! */
+ tty_smode.c_cc[VINTR] =3;
+
+ tcsetattr(0, TCSANOW, &tty_smode);
+
+#ifdef DEBUG
+ show_terminal_settings(&tty_smode);
+#endif
+ /*
+ * "Write a ^R to the FIFO which causes the other end to redisplay
+ * the input line."
+ * This does not seem to work as was intended in old comment above.
+ * However, this control character is now (R12B-3) used by run_erl
+ * to trigger the version handshaking between to_erl and run_erl
+ * at the start of every new to_erl-session.
+ */
+ write(wfd, "\022", 1);
+
+ /*
+ * read and write
+ */
+ while (1) {
+ FD_ZERO(&readfds);
+ FD_SET(0, &readfds);
+ FD_SET(rfd, &readfds);
+ if (select(rfd + 1, &readfds, NULL, NULL, NULL) < 0) {
+ if (recv_sig) {
+ FD_ZERO(&readfds);
+ }
+ else {
+ fprintf(stderr, "Error in select.\n");
+ result = -1;
+ break;
+ }
+ }
+ len = 0;
+
+ /*
+ * Read from terminal and write to FIFO
+ */
+ if (recv_sig) {
+ switch (recv_sig) {
+ case SIGINT:
+ fprintf(stderr, "[Break]\n\r");
+ buf[0] = '\003';
+ len = 1;
+ break;
+ case SIGWINCH:
+ len = window_size_seq(buf,sizeof(buf));
+ break;
+ default:
+ fprintf(stderr,"Unexpected signal: %u\n",recv_sig);
+ }
+ recv_sig = 0;
+ }
+ else if (FD_ISSET(0, &readfds)) {
+ len = read(0, buf, sizeof(buf));
+ if (len <= 0) {
+ close(rfd);
+ close(wfd);
+ if (len < 0) {
+ fprintf(stderr, "Error in reading from stdin.\n");
+ result = -1;
+ } else {
+ fprintf(stderr, "[EOF]\n\r");
+ }
+ break;
+ }
+ /* check if there is an eof character in input */
+ for (i = 0; i < len && buf[i] != tty_eof; i++);
+ if (buf[i] == tty_eof) {
+ fprintf(stderr, "[Quit]\n\r");
+ break;
+ }
+ }
+
+ if (len) {
+#ifdef DEBUG
+ write(1, buf, len);
+#endif
+ if (write_all(wfd, buf, len) != len) {
+ fprintf(stderr, "Error in writing to FIFO.\n");
+ close(rfd);
+ close(wfd);
+ result = -1;
+ break;
+ }
+ STATUS("\" OK\r\n");
+ }
+
+ /*
+ * Read from FIFO, write to terminal.
+ */
+ if (FD_ISSET(rfd, &readfds)) {
+ STATUS("FIFO read: ");
+ len = read(rfd, buf, BUFSIZ);
+ if (len < 0 && errno == EAGAIN) {
+ /*
+ * No data this time, but the writing end of the FIFO is still open.
+ * Do nothing.
+ */
+ ;
+ } else if (len <= 0) {
+ /*
+ * Either an error or end of file. In either case, break out
+ * of the loop.
+ */
+ close(rfd);
+ close(wfd);
+ if (len < 0) {
+ fprintf(stderr, "Error in reading from FIFO.\n");
+ result = -1;
+ } else
+ fprintf(stderr, "[End]\n\r");
+ break;
+ } else {
+ if (!got_some) {
+ if ((len=version_handshake(buf,len,wfd)) < 0) {
+ close(rfd);
+ close(wfd);
+ result = -1;
+ break;
+ }
+ if (protocol_ver >= 1) {
+ /* Tell run_erl size of terminal window */
+ signal(SIGWINCH, handle_sigwinch);
+ raise(SIGWINCH);
+ }
+ got_some = 1;
+ }
+
+ /*
+ * We successfully read at least one character. Write what we got.
+ */
+ STATUS("Terminal write: \"");
+ if (write_all(1, buf, len) != len) {
+ fprintf(stderr, "Error in writing to terminal.\n");
+ close(rfd);
+ close(wfd);
+ result = -1;
+ break;
+ }
+ STATUS("\" OK\r\n");
+ }
+ }
+ }
+
+ /*
+ * Reset terminal characterstics
+ * XXX
+ */
+ tcsetattr(0, TCSANOW, &tty_rmode);
+ return 0;
+}
+
+/* Call write() until entire buffer has been written or error.
+ * Return len or -1.
+ */
+static int write_all(int fd, const char* buf, int len)
+{
+ int left = len;
+ int written;
+ while (left) {
+ written = write(fd,buf,left);
+ if (written < 0) {
+ return -1;
+ }
+ left -= written;
+ buf += written;
+ }
+ return len;
+}
+
+static int window_size_seq(char* buf, size_t bufsz)
+{
+#ifdef TIOCGWINSZ
+ struct winsize ws;
+ static const char prefix[] = "\033_";
+ static const char suffix[] = "\033\\";
+ /* This Esc sequence is called "Application Program Command"
+ and seems suitable to use for our own customized stuff. */
+
+ if (ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == 0) {
+ int len = sn_printf(buf, bufsz, "%swinsize=%u,%u%s",
+ prefix, ws.ws_col, ws.ws_row, suffix);
+ return len;
+ }
+#endif /* TIOCGWINSZ */
+ return 0;
+}
+
+/* to_erl run_erl
+ * | |
+ * |---------- '\022' -------->| (session start)
+ * | |
+ * |<---- "[run_erl v1-0]" ----| (version interval)
+ * | |
+ * |--- Esc_"version=1"Esc\ -->| (common version)
+ * | |
+ */
+static int version_handshake(char* buf, int len, int wfd)
+{
+ unsigned re_high=0, re_low;
+ char *end = find_str(buf,len,"]\n");
+
+ if (end && sscanf(buf,"[run_erl v%u-%u",&re_high,&re_low)==2) {
+ char wbuf[30];
+ int wlen;
+
+ if (re_low > RUN_ERL_HI_VER || re_high < RUN_ERL_LO_VER) {
+ fprintf(stderr,"Incompatible versions: to_erl=v%u-%u run_erl=v%u-%u\n",
+ RUN_ERL_HI_VER, RUN_ERL_LO_VER, re_high, re_low);
+ return -1;
+ }
+ /* Choose highest common version */
+ protocol_ver = re_high < RUN_ERL_HI_VER ? re_high : RUN_ERL_HI_VER;
+
+ wlen = sn_printf(wbuf, sizeof(wbuf), "\033_version=%u\033\\",
+ protocol_ver);
+ if (write_all(wfd, wbuf, wlen) < 0) {
+ fprintf(stderr,"Failed to send version handshake\n");
+ return -1;
+ }
+ end += 2;
+ len -= (end-buf);
+ memmove(buf,end,len);
+
+ }
+ else { /* we assume old run_erl without version handshake */
+ protocol_ver = 0;
+ }
+
+ if (re_high != RUN_ERL_HI_VER) {
+ fprintf(stderr,"run_erl has different version, "
+ "using common protocol level %u\n", protocol_ver);
+ }
+
+ return len;
+}
+
+
+#ifdef DEBUG
+#define S(x) ((x) > 0 ? 1 : 0)
+
+static void show_terminal_settings(struct termios *t)
+{
+ fprintf(stderr,"c_iflag:\n");
+ fprintf(stderr,"Signal interrupt on break: BRKINT %d\n", S(t->c_iflag & BRKINT));
+ fprintf(stderr,"Map CR to NL on input: ICRNL %d\n", S(t->c_iflag & ICRNL));
+ fprintf(stderr,"Ignore break condition: IGNBRK %d\n", S(t->c_iflag & IGNBRK));
+ fprintf(stderr,"Ignore CR: IGNCR %d\n", S(t->c_iflag & IGNCR));
+ fprintf(stderr,"Ignore char with par. err's: IGNPAR %d\n", S(t->c_iflag & IGNPAR));
+ fprintf(stderr,"Map NL to CR on input: INLCR %d\n", S(t->c_iflag & INLCR));
+ fprintf(stderr,"Enable input parity check: INPCK %d\n", S(t->c_iflag & INPCK));
+ fprintf(stderr,"Strip character ISTRIP %d\n", S(t->c_iflag & ISTRIP));
+ fprintf(stderr,"Enable start/stop input ctrl IXOFF %d\n", S(t->c_iflag & IXOFF));
+ fprintf(stderr,"ditto output ctrl IXON %d\n", S(t->c_iflag & IXON));
+ fprintf(stderr,"Mark parity errors PARMRK %d\n", S(t->c_iflag & PARMRK));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_oflag:\n");
+ fprintf(stderr,"Perform output processing OPOST %d\n", S(t->c_oflag & OPOST));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_cflag:\n");
+ fprintf(stderr,"Ignore modem status lines CLOCAL %d\n", S(t->c_cflag & CLOCAL));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_local:\n");
+ fprintf(stderr,"Enable echo ECHO %d\n", S(t->c_lflag & ECHO));
+ fprintf(stderr,"\n");
+ fprintf(stderr,"c_cc:\n");
+ fprintf(stderr,"c_cc[VEOF] %d\n", t->c_cc[VEOF]);
+}
+#endif
diff --git a/erts/etc/vxworks/README.VxWorks b/erts/etc/vxworks/README.VxWorks
new file mode 100644
index 0000000000..299e35b513
--- /dev/null
+++ b/erts/etc/vxworks/README.VxWorks
@@ -0,0 +1,350 @@
+
+ %CopyrightBegin%
+
+ Copyright Ericsson AB 1997-2009. All Rights Reserved.
+
+ The contents of this file are subject to the Erlang Public License,
+ Version 1.1, (the "License"); you may not use this file except in
+ compliance with the License. You should have received a copy of the
+ Erlang Public License along with this software. If not, it can be
+ retrieved online at http://www.erlang.org/.
+
+ Software distributed under the License is distributed on an "AS IS"
+ basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ the License for the specific language governing rights and limitations
+ under the License.
+
+ %CopyrightEnd%
+
+-----------------------------------------------------------------------
+README, Erlang/OTP R11B for VxWorks on PPC860 and PPC603
+-----------------------------------------------------------------------
+20060515 -- Patrik Nyblom, [email protected]
+
+R11B is a libraries only release for VxWorks. Only the libraries of
+erl_interface (ei+erl_inteface) and ic are expected to be used. Still
+the whole erlang system is distributed, although no support will be
+given for anything else but the libraries. The information in this
+file still applies to the full erlang distribution and parts of it are
+therefore somewhat irrelevant to commercial users.
+
+
+Included OTP applications
+-------------------------
+
+appmon
+asn1
+compiler
+cosEvent
+cosNotification
+cosTime
+cosTransaction
+debugger
+erl_interface
+erts
+eva [1]
+ic
+inets [2]
+jinterface
+kernel
+mesh
+mnemosyne
+mnesia [1]
+mnesia_session
+orber
+os_mon
+pman
+runtime_tools
+sasl
+snmp
+stdlib
+tools
+tv
+
+[1] Only ram_copies work, The VxWorks filesystems are not
+ reliable enough for disk_copies to be fully supported.
+[2] CGI scripts do not work on VxWorks.
+
+Omitted applications
+--------------------
+
+crypto
+emacs
+etk
+gs
+odbc
+parsetools
+toolbar
+ssl
+megaco
+webtools
+
+As `crypto' and `ssl' provides cryptographic functionality to `inets'
+and `snmp', the latter applications will not handle cryptography on
+VxWorks.
+
+Graphical interfaces
+--------------------
+
+For applications using graphical interfaces, only the backend part works.
+
+Compilers
+---------
+
+All compilers are expected to be run on a cross host. The VxWorks
+systems memory capabilities are too restricting to allow native
+compiling. The expected host system is a Sun Solaris machine, although
+Erlang compilation may be done on most platforms.
+
+Supported boards and configuration (only libraries supported)
+----------------------------------
+The following boards and configurations are supported:
+
+* Force PowerCore 603 with Force pcore603 BSP and VxWorks 3.5.1 (no
+ SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory.
+
+* Force Powercore 750 with Force pcore750 BSP and VxWorks 3.5.1 (no
+ SENS or SENS 1.1 + SPR23938) and a minimum of 32 Mb memory.
+
+* PSS Core PPC860 processors, only erl_interface (too small main memory).
+
+Most PowerPC boards with FPU are expected to work, but will need to be
+tested by OTP to be fully supported.
+
+The PPC603 build has been compiled with Wind River's `-mlongcall'
+flag (SPR25893) to support arbitrary function calls across more
+than 32 MB of memory.
+
+The PPC860 (PowerQuicc) has no FPU and requires a separate build.
+
+For Erlang to run, the Wind kernel has to be configured with a minimum
+of these variables defined in config.h (or by the Tornado
+configuration tool):
+
+ INCLUDE_ANSI_ALL
+ INCLUDE_ENV_VARS
+ INCLUDE_EXC_HANDLING
+ INCLUDE_EXC_TASK
+ INCLUDE_FLOATING_POINT
+ INCLUDE_FORMATTED_IO
+ INCLUDE_IO_SYSTEM
+ INCLUDE_LOADER
+ INCLUDE_NETWORK
+ INCLUDE_NET_INIT
+ INCLUDE_NET_SHOW
+ INCLUDE_NET_SYM_TBL or INCLUDE_STANDALONE_SYM_TBL
+ INCLUDE_PIPES
+ INCLUDE_POSIX_FTRUNC
+ INCLUDE_RLOGIN or INCLUDE_TELNET (for pty's only)
+ INCLUDE_SELECT
+ INCLUDE_SEM_BINARY
+ INCLUDE_SEM_COUNTING
+ INCLUDE_SEM_MUTEX
+ INCLUDE_SHELL (*)
+ INCLUDE_SHOW_ROUTINES
+ INCLUDE_SIGNALS
+ INCLUDE_STARTUP_SCRIPT (*)
+ INCLUDE_STDIO
+ INCLUDE_SYM_TBL
+ INCLUDE_TASK_HOOKS
+ INCLUDE_TASK_VARS
+ INCLUDE_TTY_DEV
+ INCLUDE_UNLOADER
+ INCLUDE_NFS or INCLUDE_RAMDRV or INCLUDE_DOSFS (i.e. a file system,
+ possibly read-only) (**)
+
+(*) Needed for the example startup script, not actually needed in production if
+ erlang is set up by a c routine called from usrConfig.c.
+(**) INCLUDE_NFS usually requires the NFS_USER_ID and NFS_GROUP_ID variables
+ to be set in config.h
+
+As an erlang system may open a lot of files, it is recommended to raise the
+default NUM_FILES variable to something like 256 in config.h like this:
+ #ifdef NUM_FILES
+ #undef NUM_FILES
+ #endif
+ #define NUM_FILES 256
+
+The SENS stack *has* to be of version 1.1 or higher, 1.0 is *not*
+supported and will not work reliably. Upgrades as well as the patch
+for SPR23938 can be found at www.wrs.com (i.e. WindSurf). Also, the
+following constants in $WIND_BASE/target/h/netBufLib.h has to be
+raised to a value of at least four times the default:
+
+ NUM_NET_MBLKS
+ NUM_64
+ NUM_128
+ NUM_256
+ NUM_512
+ NUM_1024
+ NUM_2048
+
+ NUM_SYS_64
+ NUM_SYS_128
+ NUM_SYS_256
+ NUM_SYS_512
+
+Use the show routines mbufShow and netStackSysPoolShow to verify that
+these pools are not exhausted.
+
+Installation
+------------
+
+To install Erlang on a VxWorks card, the following knowledge is
+expected:
+
+* VxWorks installation and configuration.
+
+* Network (TCP/IP) configuration.
+
+* Erlang basic operation and configuration.
+
+There is no specific install script for erlang on the VxWorks
+platform. There is however an example VxWorks startup file named
+erts-5.0.1/bin/erl_script.sam under the root of an unpacked
+release. There may of course be other things to do in the start
+script, like using the rdate program in the erlang distribution to get
+a correct date and set the TIMEZONE variable.
+
+Please consult the "Embedded System" documentation for further
+information on installation.
+
+Known Bugs and problems
+-----------------------
+
+We have found the VxWorks/NFS client file system to be unreliable.
+Important file operations like rename, ftruncate, cd and unlink
+doesn't always work as expected. Therefore more complex file using
+parts of OTP, like DETS and disk based mnesia tables cannot be used
+reliably with NFS. Lowering the NFS cache size (global variable
+nfsCacheSize) to 512 gives a more reliable NFS client, but to disk
+base the mnesia tables over NFS is still not a good idea, especially
+as disk based mnesia tables are not supported on VxWorks. Another
+problem with VxWorks file systems is that the error codes they produce
+are not consistent. We have worked around this problem by mapping the
+codes to POSIX ones, by doing this we make the VxWorks Erlang platform
+behave similarly to the UNIX and Windows implementations.
+
+The rename and ftruncate operations over NFS are emulated using
+copying of files. This is mainly for our own test suites and it is not
+recommended to use file:rename and/or file:ftruncate on NFS file
+systems in production.
+
+Floating point operations is somewhat faulty. For instance, testing
+floating point numbers for equality should be done with care. This is
+actually not a bug, IEEE specifies no equality among floating point
+numbers.
+
+Memory handling
+---------------
+
+Please read the erl_set_memory_block(3) manual page in the ERTS
+documentation for information concerning memory handling in the erlang
+emulator. Also please observe that reclaim.o has to be loaded and
+reclaim_init() called before any other erlang routines are loaded and
+started. If one wish to use the resource reclamation routines in other
+programs, refer to the header file in `erts-5.0.1/include/reclaim.h'.
+Including that file in your C source makes malloc/realloc/free and
+open/fopen/socket/close etc be redefined to routines that makes the
+memory and files be free'd/closed when the task exits. Still,
+reclaim_init() *has* to be called before any program that uses this is
+started.
+
+Using heart
+-----------
+
+The default behavior of the heart object file that is part of the
+distribution is that it reboots the target when the Erlang process
+hasn't given it a heart beat in 60 seconds. The normal heart beat rate
+is one beat per five seconds. This makes an effective "software
+watchdog" but there is really no substitute for the real thing --- a
+hardware watchdog. If you want to add a hardware watchdog to the
+system please contact us for directions. If you want to disable the
+reboot you may set the environment variable HEART_DONT_REBOOT (see the
+example erlang start script, erl). Please note that if you DO want the
+card to reboot you mustn't define HEART_DONT_REBOOT at all. E.g. to
+disable heart reboot you may include the following line in the start
+script (as is indeed the case with the example start script).
+
+ putenv "HEART_DONT_REBOOT=1"
+
+A few words on writing port program and dynamically loaded drivers for VxWorks
+------------------------------------------------------------------------------
+
+VxWorks has one name-space for all symbols. This makes it harder to
+write C programs whose global symbols doesn't interfere with each
+other. It is a good rule to avoid all globally visible symbols that
+are not absolutely necessary. Due to these facts we use the following
+naming rules that are crucial to follow. (there are more issues
+involved but the issues described here is a good beginning).
+
+Port programs must have a function with the same name as the object
+file. E.g. if you have an object file named `port_test.o' it must
+contain a globally visible function named `port_test'. This is the
+function that will be called when you output data from Erlang to the
+port. (The object file, in this example, should be named
+`port_test.o', but `port_test' will also do).
+
+Also, in an embedded system, it is recommended to load the port
+program into the system before the port program is used. This is to
+avoid the real time degradation dynamical linking in runtime would
+introduce. Use VxWorks command ld < "port_prg" to accomplish this.
+
+Dynamically linked drivers must have a function with the same name as
+the object file with the added suffix `_init'. We recommend the use of
+the macro DRIVER_INIT in `driver.h'. E.g. if you have an object file
+named `echo_drv.eld' it must contain a globally visible function
+`echo_drv_init'. (The object file, in this example, should be named
+`echo_drv.eld' (`eld' is short for Erlang Loadable Driver), but
+`echo_drv.o' and `echo_drv' will both also do). It is also very
+important to initialize all unused function pointer in the
+`driver_entry' struct to NULL (see example below).
+
+Example of dynamically linked driver
+------------------------------------
+
+#include <stdio.h>
+#include "driver.h"
+
+static int erlang_port;
+static long echo_start();
+static int echo_stop(), echo_read();
+
+static struct driver_entry echo_driver_entry = {
+ null_func,
+ echo_start,
+ echo_stop,
+ echo_read,
+ null_func,
+ null_func,
+ "echo_drv",
+ null_func
+};
+
+int DRIVER_INIT(echo_drv)(void *handle)
+{
+ erlang_port = -1;
+
+ echo_driver_entry.handle = handle;
+ return (int) &echo_driver_entry;
+}
+
+static long echo_start(long port,char *buf)
+{
+ if (erlang_port != -1) {
+ return -1;
+ }
+
+ erlang_port = port;
+ return port;
+}
+
+static int echo_read(long port, char *buf, int count)
+{
+ return driver_output(erlang_port, buf, count);
+}
+
+static int echo_stop()
+{
+ return erlang_port = -1;
+}
diff --git a/erts/etc/vxworks/erl.exec.c b/erts/etc/vxworks/erl.exec.c
new file mode 100644
index 0000000000..6b45ebaa39
--- /dev/null
+++ b/erts/etc/vxworks/erl.exec.c
@@ -0,0 +1,129 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ A simpified version of the 'erl.exec' "startup script".
+ Called (e.g. from VxWorks shell) with all arguments in a
+ single string, e.g.: erl "-name thisnode -s mymod myfunc".
+ These arguments are handled as in 'erl.exec':
+ -name
+ -sname
+ -noshell
+ -noinput
+ anything else is just passed on to the emulator. Note that there
+ is no automatic start of epmd, that -oldshell is implicit, and
+ that you need to set current directory appropriately if you want
+ auto-load of port programs
+*/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#ifndef DEFAULT_HOMEDIR /* used if environment HOME isn't set */
+#define DEFAULT_HOMEDIR "/"
+#endif
+
+#define ARGLEN 2048 /* Total length of args passed to erl_main */
+#define ARGMAX 64 /* Max no of "extra" args */
+
+static char *erl_cmd = "erl_main -n ";
+
+static toomuch()
+{
+ fprintf(stderr, "erl: Too many arguments\n");
+ return(-1);
+}
+
+static toolittle(arg)
+char *arg;
+{
+ fprintf(stderr, "erl.exec: Missing argument for %s\n", arg);
+ return(-1);
+}
+
+erl_exec(arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10)
+int arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, arg10;
+{
+ char *shell = "-oldshell ", *noshell = "",
+ *home, *rootdir, *bindir, *progname;
+ char cmd[ARGLEN], eargs[ARGLEN], iargs[ARGLEN];
+ char *args[ARGMAX], *arglast = NULL, *argp;
+ int nargs = 0, len, i;
+
+ if ((rootdir = getenv("ROOTDIR")) == NULL ||
+ (bindir = getenv("BINDIR")) == NULL ||
+ (progname = getenv("PROGNAME")) == NULL) {
+ fprintf(stderr, "erl.exec: ROOTDIR, BINDIR, and PROGNAME must be set.");
+ return -1;
+ }
+ eargs[0] = '\0';
+ iargs[0] = '\0';
+ if ((home = getenv("HOME")) == NULL)
+ home = DEFAULT_HOMEDIR;
+ argp = strtok_r((char *)arg1, " \t", &arglast);
+ while (argp != NULL) {
+ if (strcmp(argp, "-name") == 0) {
+ if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL)
+ return(toolittle("-name"));
+ strcat(iargs, "-name ");
+ strcat(iargs, argp);
+ strcat(iargs, " ");
+ } else if (strcmp(argp, "-sname") == 0) {
+ if ((argp = strtok_r((char *)NULL, " \t", &arglast)) == NULL)
+ return(toolittle("-sname"));
+ strcat(iargs, "-sname ");
+ strcat(iargs, argp);
+ strcat(iargs, " ");
+ } else if (strcmp(argp, "-noshell") == 0) {
+ strcat(iargs, "-noshell -noinp_shell ");
+ } else if (strcmp(argp, "-noinput") == 0) {
+ strcat(iargs, "-noshell -noinput ");
+ } else {
+ if (nargs > ARGMAX - 1)
+ return(toomuch());
+ args[nargs++] = argp;
+ }
+ argp = strtok_r((char *)NULL, " \t", &arglast);
+ }
+ strcpy(cmd, erl_cmd);
+ strcat(cmd, eargs);
+ strcat(cmd, " -- -root ");
+ strcat(cmd, rootdir);
+ strcat(cmd, " -progname ");
+ strcat(cmd, progname);
+ strcat(cmd, " -- ");
+ strcat(cmd, "-home ");
+ strcat(cmd, home);
+ strcat(cmd, " ");
+ strcat(cmd, iargs);
+
+ len = strlen(cmd);
+ for (i = 0; i < nargs; i++) {
+ if (len + strlen(args[i]) + 2 >= ARGLEN)
+ return(toomuch());
+ cmd[len++] = ' ';
+ strcpy(&cmd[len], args[i]);
+ len += strlen(args[i]);
+ }
+ argcall(cmd);
+}
+
diff --git a/erts/etc/vxworks/erl_io.c b/erts/etc/vxworks/erl_io.c
new file mode 100644
index 0000000000..0032b77079
--- /dev/null
+++ b/erts/etc/vxworks/erl_io.c
@@ -0,0 +1,108 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/* Some stuff to let the Erlang and VxWorks shells coexist peacefully.
+ Basically, run Erlang as a spawned task with input redirected to
+ the slave side of a pseudo-tty, and connect explicitly to the master
+ side of the pseudo-tty to send input to Erlang when desired. */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <stdio.h>
+#include <ioLib.h>
+#include <taskLib.h>
+#include <ptyDrv.h>
+
+extern int spTaskPriority, spTaskOptions;
+
+#define TBUFSIZ 512
+
+#define DEFAULT_STACK_SIZE 100000
+
+static int slavefd = -1, masterfd = -1;
+static run_erl();
+
+/* Frontend to the Erlang startup function - callable from VxWorks shell
+ or script. 'arg' is actually a string passed to the real startup. */
+start_erl(arg)
+int arg;
+{
+ int stacksize;
+ char *stackenv;
+
+ /* create and open the pty - we want the master side to be open
+ all the time, since closing it probably generates EOF on the
+ slave side */
+ (void)ptyDevCreate("/pty/erlang.", TBUFSIZ, TBUFSIZ);
+ if (slavefd != -1)
+ (void)close(slavefd);
+ slavefd = open("/pty/erlang.S", O_RDONLY, 0);
+ if (masterfd != -1)
+ (void)close(masterfd);
+ masterfd = open("/pty/erlang.M", O_WRONLY, 0);
+
+ /* flush any old leftover garbage */
+ (void) ioctl(masterfd, FIOFLUSH, 0);
+ if ((stackenv = getenv("ERLSTACKSIZE")) == NULL)
+ stacksize = DEFAULT_STACK_SIZE;
+ else
+ stacksize = atoi(stackenv);
+ /* spawn Erlang, via stub below */
+ return(taskSpawn("erlang", spTaskPriority, spTaskOptions, stacksize,
+ run_erl, arg, 0,0,0,0,0,0,0,0,0));
+}
+
+/* Little stub that runs in the spawned task - we need this to redirect
+ stdin reliably (redirections aren't "inherited" in VxWorks) */
+static
+run_erl(arg)
+int arg;
+{
+ ioTaskStdSet(0, 0, slavefd); /* redirect stdin to slave side of pty */
+
+ /* We don't want to redirect stdout/err since no one will be reading
+ from the master side (to_erl - and the open()s above - could be
+ made bidirectional, but still the master side would only be read
+ when to_erl was running), and output can eventually fill the pty
+ buffer and cause the Erlang system to block. Not redirecting
+ stdout/err will have the effect that output from Erlang, e.g. the
+ shell prompt, will appear on console/rlogin/whatever even when
+ to_erl isn't running, which may be confusing - can't win 'em all... */
+
+ erl_exec(arg, 0,0,0,0,0,0,0,0); /* call the real startup */
+}
+
+/* Function callable from VxWorks shell to talk to Erlang - stop talking
+ and return to VxWorks shell through ^D (EOF) */
+to_erl()
+{
+ char buf[TBUFSIZ];
+ int cc;
+
+ if (masterfd == -1) { /* sanity check */
+ fprintf(stderr, "Must start_erl first!\n");
+ return(-1);
+ }
+ while ((cc = read(0, buf, TBUFSIZ)) > 0) /* just pass everything through */
+ if (write(masterfd, buf, cc) != cc) {
+ fprintf(stderr, "Write to Erlang failed!\n");
+ return(-1);
+ }
+ return(cc);
+}
diff --git a/erts/etc/vxworks/erl_script.sam.in b/erts/etc/vxworks/erl_script.sam.in
new file mode 100644
index 0000000000..81c2b0128d
--- /dev/null
+++ b/erts/etc/vxworks/erl_script.sam.in
@@ -0,0 +1,100 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1997-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+#
+# erl_script.sam
+# Sample VxWorks script to start Erlang
+#
+# Note! This is not a complete or ready to use VxWorks startup script,
+# rather an example of what You should add to Your existing startupscript
+# to execute the erlang emulator at boot.
+#
+# When writing Your own script to start erlang, the paths to
+# the binaries have to be changed to reflect your system.
+#
+# The ROOTDIR variable should not point to a ftp or rcp filesystem unless
+# the erlang machine is run in embedded mode. Loading of modules
+# is far to slow if the erlang binaries are not placed on a real filesystem
+# like NFS or any type of local filesystem.
+#
+
+#
+# Load modules
+#
+
+#
+# First load and initiate the reclaim facility
+#
+ld </home/tornado/erlvxworks/erts-%VSN%/bin/reclaim.o
+reclaim_init()
+
+#
+# Now load the runtime system
+#
+ld </home/tornado/erlvxworks/erts-%VSN%/bin/jam
+ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl.exec
+ld </home/tornado/erlvxworks/erts-%VSN%/bin/erl_io
+ld </home/tornado/erlvxworks/erts-%VSN%/bin/vxcall
+ld </home/tornado/erlvxworks/erts-%VSN%/bin/heart
+ld </home/tornado/erlvxworks/erts-%VSN%/bin/epmd
+
+#
+# Stack sizes
+#
+putenv "ERLSTACKSIZE=100000"
+putenv "ERLPORTSTACKSIZE=100000"
+
+#
+# Activate Round robin scheduling
+#
+kernelTimeSlice 1
+
+#
+# Distribution
+# The VxWorks machines host name
+sethostname "sb001", 5
+# Erlangs internal resolver
+putenv "ERLRESCONF=/home/tornado/erlvxworks/erts-%VSN%/bin/resolv.conf"
+
+#
+# Start epmd (for distribution)
+#
+start_epmd "-daemon"
+
+#
+# Misc environment variables
+#
+putenv "ROOTDIR=/home/tornado/erlvxworks"
+putenv "BINDIR=/home/tornado/erlvxworks/erts-%VSN%/bin"
+putenv "PROGNAME=erl"
+putenv "HOME=/home/tornado/erlvxworks"
+
+#
+# Set heart no reboot mode (to make heart reboot -
+# don't define HEART_DONT_REBOOT at all)
+#
+putenv "HEART_DONT_REBOOT=1"
+
+# To get fullsweep garbage collection on systems with
+# very limited memory, set ERL_FULLSWEEP_AFTER to "0":
+# putenv "ERL_FULLSWEEP_AFTER=0"
+
+#
+# Start Erlang/OTP
+#
+start_erl "-oldshell -heart -sname vxnode -setcookie switch -boot start_sasl"
diff --git a/erts/etc/vxworks/heart_config.c b/erts/etc/vxworks/heart_config.c
new file mode 100644
index 0000000000..7e60e61fbb
--- /dev/null
+++ b/erts/etc/vxworks/heart_config.c
@@ -0,0 +1,60 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * A basic heart configure module for VxWorks.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <vxWorks.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rebootLib.h>
+#include <sysLib.h>
+
+/* wd_init is executed to initialize a watchdog (if one is used). */
+int wd_init(timeout, prio)
+ int timeout, prio;
+{
+
+}
+
+/* wd_reset should be called every 5th second from heart */
+void wd_reset()
+{
+
+}
+
+/* This routine is called when erlang has closed */
+void heart_reboot()
+{
+ if (getenv("HEART_DONT_REBOOT") != NULL) {
+ fprintf(stderr, "heart_config: HEART_DONT_REBOOT set, no reboot ...\n");
+ } else {
+ fprintf(stderr, "heart_config: rebooting ...\n");
+ taskDelay(sysClkRateGet() * 5);
+ reboot(BOOT_CLEAR);
+ }
+}
+
+
+
+
diff --git a/erts/etc/vxworks/heart_config.h b/erts/etc/vxworks/heart_config.h
new file mode 100644
index 0000000000..5ffaaa8c3f
--- /dev/null
+++ b/erts/etc/vxworks/heart_config.h
@@ -0,0 +1,35 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * This is heart's watchdog interface for VxWorks.
+ */
+
+#ifndef _HW_WATCHDOG_H
+#define _HW_WATCHDOG_H
+
+extern void wd_init(int timeout, int prio); /* wd_init initializes the
+ watchdog, if one is used. */
+extern void wd_reset(void); /* wd_reset is used by heart to kick
+ the watchdog, if one is used. */
+extern void heart_reboot(void); /* reboot is called if heart discovers
+ that the Erlang task has stopped sending
+ heart beats. It can log system status
+ and should reboot VxWorks. */
+
+#endif
diff --git a/erts/etc/vxworks/rdate.c b/erts/etc/vxworks/rdate.c
new file mode 100644
index 0000000000..3e8cc644d0
--- /dev/null
+++ b/erts/etc/vxworks/rdate.c
@@ -0,0 +1,87 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vxWorks.h>
+#include <timers.h>
+#ifdef NETDB
+#include <netdb.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+/*
+ rdate("host") - Set the time from "host".
+*/
+
+/* No getservbyname() available... */
+#define TIME_PORT 37
+
+rdate(host)
+char *host;
+{
+ u_long haddr;
+#ifdef NETDB
+ struct hostent *hp;
+#endif
+ struct sockaddr_in saddr;
+ int sock;
+ u_long net_time;
+ struct timespec t;
+
+ if ((haddr = inet_addr(host)) == ERROR) {
+#ifdef NETDB
+ if ((hp = gethostbyname(host)) == NULL) {
+#else
+ if ((haddr = hostGetByName(host)) == ERROR) {
+#endif
+ printf("Host not found.\n");
+ return(-1);
+ }
+#ifdef NETDB
+ memcpy(&haddr, hp->h_addr, sizeof(haddr));
+#endif
+ }
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ memcpy(&saddr.sin_addr, &haddr, sizeof(haddr));
+ saddr.sin_port = htons(TIME_PORT);
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ perror("socket");
+ return(-1);
+ }
+ if (connect(sock, (struct sockaddr *)&saddr, sizeof(saddr)) < 0) {
+ perror("connect");
+ close(sock);
+ return(-1);
+ }
+ if (read(sock, &net_time, 4) != 4) {
+ perror("read");
+ close(sock);
+ return(-1);
+ }
+ t.tv_sec = ntohl(net_time);
+ t.tv_sec -= 2208988800; /* seconds 1900-01-01 -- 1970-01-01 */
+ t.tv_nsec = 0;
+ clock_settime(CLOCK_REALTIME, &t);
+ close(sock);
+ return(0);
+}
diff --git a/erts/etc/vxworks/reclaim.c b/erts/etc/vxworks/reclaim.c
new file mode 100644
index 0000000000..d8676b3750
--- /dev/null
+++ b/erts/etc/vxworks/reclaim.c
@@ -0,0 +1,551 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vxWorks.h>
+#include <version.h>
+#include <string.h>
+#include <types.h>
+#include <sigLib.h>
+#include <ioLib.h>
+#include <iosLib.h>
+#include <fioLib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <errno.h>
+#include <symLib.h>
+#include <sysLib.h>
+#include <sysSymTbl.h>
+#include <loadLib.h>
+#include <taskLib.h>
+#include <taskVarLib.h>
+#include <taskHookLib.h>
+#include <tickLib.h>
+#include <time.h>
+#include <rngLib.h>
+#include <semLib.h>
+#include <selectLib.h>
+#include <sockLib.h>
+#include <a_out.h>
+#include <wdLib.h>
+#include <timers.h>
+#include <ctype.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <stdarg.h>
+
+#include <stdio.h>
+#include <math.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <string.h>
+
+
+
+#define RECLAIM_NO_ALIAS /* No #defines for open/malloc/fopen etc */
+#include "reclaim.h"
+#include "reclaim_private.h"
+
+#undef open
+#undef creat
+#undef socket
+#undef accept
+#undef close
+#undef fopen
+#undef fdopen
+#undef freopen
+#undef fclose
+/* XXX Should do opendir/closedir too... */
+#undef malloc
+#undef calloc
+#undef realloc
+#undef free
+#undef cfree
+
+#ifdef _ARCH_PPC
+#define MAX_FILES_SYM_NAME "maxFiles"
+#else
+#define MAX_FILES_SYM_NAME "_maxFiles"
+#endif
+
+
+/*
+ * Use another free() function upon task deletion?
+ * Note! When changing free function, the new free function will have to
+ * be able to cope with ALL previously used malloced areas, that is
+ * it has to be able to find out how things are malloced
+ * to free them in the right way!
+ */
+static FreeFunction reclaim_free_function = NULL;
+
+/* delete hook handling (see below) */
+static int hook_added = 0; /* Initated at first reclaim_init, an extra
+ non MT-safe check that we only get
+ initialized once */
+
+/* Forward... */
+static void save_reclaim(WIND_TCB *tcbp);
+
+struct mall_data {
+ struct mall_data *next;
+ char *self;
+};
+
+struct task_data {
+ FUNCPTR version; /* To recognize when we have reloaded */
+ int max_files; /* It may change... */
+ struct fd_set open_fds;
+ struct mall_data *mall_data;
+ FUNCPTR delete_hook;
+ caddr_t hook_data;
+ FILE *open_fps[1]; /* Will be max_files long */
+} *task_data = NULL;
+
+static int max_files = 50; /* default configAll.h */
+
+int reclaim_max_files(void)
+{
+ return max_files;
+}
+
+#ifdef DEBUG
+#define check_hook() \
+((task_data != NULL || \
+ fdprintf(2,"check_hook() TID = 0x%08x, Called from line %d\n", \
+ (unsigned int)taskIdSelf(),\
+ __LINE__)) && \
+(task_data != NULL || \
+ (taskVarAdd(0, (int *)&task_data) == OK && \
+ (task_data = (struct task_data *)\
+ calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
+ (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
+ (task_data->max_files = max_files) != 0 && \
+ fdprintf(2,"taskVar Added for 0x%08x\n",(unsigned int)taskIdSelf()))))
+#else
+#define check_hook() \
+(task_data != NULL || \
+ (taskVarAdd(0, (int *)&task_data) == OK && \
+ (task_data = (struct task_data *)\
+ calloc(1, sizeof(struct task_data) + max_files*sizeof(FILE *))) != NULL && \
+ (task_data->version = (FUNCPTR)save_reclaim) != NULL && \
+ (task_data->max_files = max_files) != 0))
+#endif
+
+/*
+ * Global initialization of the reclaim data structures, mainly
+ * the max_files variable. This HAS to be called by some task before
+ * the first task that utilizes this exit's, preferrably before any
+ * task makes the first use of this library.
+ */
+STATUS reclaim_init(void)
+{
+ int *mp;
+ SYM_TYPE type;
+ struct task_data *tdp;
+ int i;
+
+ if (!hook_added) {
+ /* race condition */
+ ++hook_added;
+ /* Try to find the maxFiles value */
+ if (symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
+ &type,
+ N_EXT | N_BSS, N_EXT | N_BSS) == OK ||
+ symFindByNameAndType(sysSymTbl, MAX_FILES_SYM_NAME, (char **)&mp,
+ &type,
+ N_EXT | N_DATA, N_EXT | N_DATA) == OK) {
+
+#ifdef DEBUG
+ fdprintf(2, "Found maxFiles=%d\n", *mp);
+#endif
+ if (*mp <= FD_SETSIZE)
+ max_files = *mp;
+ else
+ max_files = FD_SETSIZE;
+ }
+ if (task_data != NULL && task_data->max_files != max_files) {
+ /* fix our own iff we have one */
+ if ((tdp = (struct task_data *)
+ realloc(task_data, sizeof(struct task_data) +
+ max_files*sizeof(FILE *))) != NULL) {
+ task_data = tdp;
+ for (i = task_data->max_files; i < max_files; i++)
+ task_data->open_fps[i] = NULL;
+ task_data->max_files = max_files;
+ }
+ }
+ /* Make sure taskVariables are deleted AFTER our hook is run. */
+ taskVarInit();
+ if(taskDeleteHookAdd((FUNCPTR)save_reclaim) != OK) {
+ fprintf(stderr,
+ "Panic: taskDeleteHook cannot be added for reclaim.\n");
+ return ERROR;
+ }
+ return OK;
+ } else
+ return ERROR;
+}
+
+/* N.B.!! We do *not* execute in the context of the dying task here,
+ but rather that of tExcTask - we do get a pointer to the task's
+ TCB though - this pointer is in fact also the task's ID. */
+static void save_reclaim(WIND_TCB *tcbp)
+{
+ int i, var, oldfd;
+ struct task_data *tdp;
+ struct mall_data *mdp, *mdnextp;
+
+ if ((var = taskVarGet((int)tcbp, (int *)&task_data)) != ERROR &&
+ var != 0) {
+ tdp = (struct task_data *)var;
+ if (tdp->version == (FUNCPTR)save_reclaim) { /* Only handle our own */
+#ifdef DEBUG
+ fdprintf(2, "Reclaiming for task id 0x%x:\nFiles: ", (int)tcbp);
+#endif
+ /* Ugh! VxWorks doesn't even flush stdout/err - we need to
+ get at those (which are task-private of course, i.e. we
+ can't just do fflush(stdout) here) - we could be really
+ pedantic and try to redefine stdout/err (which "are"
+ function calls) too, snarfing the values when they are
+ used - but besides the overhead this is problematic since
+ they are actually #defines already... We'll peek in the
+ TCB instead (no documentation of course). And of course,
+ we must also meddle with the *file descriptor* indirections,
+ or we'll just flush out on tExcTask's descriptors... */
+ for (i = 1; i <= 2; i++) {
+ if (tcbp->taskStdFp[i] != NULL) {
+#ifdef DEBUG
+ fdprintf(2, "fflush(%s) ", i == 1 ? "stdout" : "stderr");
+#endif
+ oldfd = ioTaskStdGet(0, i);
+ ioTaskStdSet(0, i, tcbp->taskStd[i]);
+ fflush(tcbp->taskStdFp[i]);
+ ioTaskStdSet(0, i, oldfd);
+ }
+ }
+ for (i = 3; i < tdp->max_files; i++) {
+ if (FD_ISSET(i, &tdp->open_fds)) {
+#ifdef DEBUG
+ fdprintf(2, "close(%d) ", i);
+#endif
+ (void) close(i);
+ }
+ if (tdp->open_fps[i] != NULL) {
+#ifdef DEBUG
+ fdprintf(2, "fclose(%0x%x) ", (int)tdp->open_fps[i]);
+#endif
+ (void) fclose(tdp->open_fps[i]);
+ }
+ }
+ i = 0;
+ mdp = tdp->mall_data;
+ while (mdp != NULL) {
+ mdnextp = mdp->next;
+ if(reclaim_free_function != NULL)
+ (*reclaim_free_function)(mdp->self);
+ else
+ free(mdp->self);
+ i++;
+ mdp = mdnextp;
+ }
+#ifdef DEBUG
+ fdprintf(2, "\nFreeing memory: total %d mallocs\n", i);
+#endif
+
+ if (tdp->delete_hook != NULL) {
+#ifdef DEBUG
+ fdprintf(2, "Calling delete hook at 0x%08x\n", tdp->delete_hook);
+#endif
+ (*tdp->delete_hook)(tdp->hook_data);
+#ifdef DEBUG
+ fdprintf(2, "Called delete hook at 0x%08x\n", tdp->delete_hook);
+#endif
+ }
+#ifdef DEBUG
+ fdprintf(2, "Freeing own mem at 0x%08x\n", tdp);
+#endif
+ (void) free((char *)tdp);
+#ifdef DEBUG
+ fdprintf(2, "Freed own mem at 0x%08x, done (0x%08x)\n**********\n", tdp,
+ taskIdSelf());
+ checkStack(0);
+#endif
+ }
+ }
+#ifdef DEBUG
+ else
+ fdprintf(2, "No task data found for id 0x%x, var = %d\n", (int)tcbp, var);
+#endif
+}
+
+/*
+ * This sets another free function to be used by the task deletion hook.
+ * The free function HAS to be able to free ANY type of dynamically allocated
+ * memory that can be in the task data list of allocated memory, that is
+ * also memory that's allocated before the free function was set.
+ * A "user-supplied" free function is GLOBAL to the system!!!
+ * A race condition is present, a task delete hook may be running when this
+ * function is called, that may not be especially funny...
+ */
+void set_reclaim_free_function(FreeFunction f){
+ reclaim_free_function = f;
+}
+
+void save_delete_hook(FUNCPTR func, caddr_t parm)
+{
+ if (check_hook()) {
+ task_data->delete_hook = func;
+ task_data->hook_data = parm;
+ }
+}
+
+/*
+ * plain_malloc is only used by spawn_start; plain_free by call_proc;
+ * save_fd is used by both.
+ */
+void *plain_malloc(size_t size){
+ return(malloc(size));
+}
+
+void *plain_realloc(void *ptr, size_t size){
+ return(realloc(ptr, size));
+}
+
+void plain_free(void *ptr){
+ free(ptr);
+}
+
+void save_fd(int fd){
+ if (fd >= 0 && check_hook() && fd < task_data->max_files)
+ FD_SET(fd, &task_data->open_fds);
+}
+
+int save_open(char *path, int flags, /*mode_t mode*/ ...){
+ int fd;
+ mode_t mode = 0;
+ if(flags & O_CREAT){
+ va_list pvar;
+ va_start(pvar,flags);
+#ifdef __GNUC__
+#warning save_open() gives three known alignment warnings.
+#endif
+ mode = va_arg(pvar, mode_t);
+ va_end(pvar);
+ }
+ if ((fd = open(path, flags, mode)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_creat(char *path, int mode){
+ int fd;
+ if ((fd = creat(path, mode)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_socket(int domain, int type, int protocol){
+ int fd;
+ if ((fd = socket(domain, type, protocol)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_accept(int s, struct sockaddr *addr, int *addrlen){
+ int fd;
+ if ((fd = accept(s, addr, addrlen)) >= 0 && check_hook())
+ FD_SET(fd, &task_data->open_fds);
+ return(fd);
+}
+
+int save_close(int fd){
+ if (fd >= 0 && fd <= FD_SETSIZE && check_hook())
+ FD_CLR(fd, &task_data->open_fds);
+ return(close(fd));
+}
+
+/* The dealing with FILE *'s below isn't strictly correct, we assume
+ that one never has several pointing to the same fd - in the unlikely
+ event that one does, all but the last one opened is forgotten */
+FILE *save_fopen(const char *filename, char *type){
+ FILE *fp;
+
+ if ((fp = fopen(filename, type)) != NULL &&
+ check_hook() && fileno(fp) < task_data->max_files)
+ task_data->open_fps[fileno(fp)] = fp;
+ return(fp);
+}
+
+FILE *save_fdopen(int fd, char *type){
+ FILE *fp;
+
+ if ((fp = fdopen(fd, type)) != NULL &&
+ check_hook() && fileno(fp) < task_data->max_files) {
+ task_data->open_fps[fileno(fp)] = fp;
+ FD_CLR(fd, &task_data->open_fds);
+ }
+ return(fp);
+}
+
+FILE *save_freopen(char *filename, char *type, FILE *stream){
+ FILE *fp;
+
+ if (check_hook()) {
+ if(fileno(stream) < task_data->max_files &&
+ task_data->open_fps[fileno(stream)] == stream)
+ task_data->open_fps[fileno(stream)] = NULL;
+ if ((fp = freopen(filename, type, stream)) != NULL &&
+ fileno(fp) < task_data->max_files)
+ task_data->open_fps[fileno(fp)] = fp;
+ } else
+ fp = freopen(filename, type, stream);
+ return(fp);
+}
+
+int save_fclose(FILE *stream){
+ if (check_hook() && fileno(stream) < task_data->max_files &&
+ task_data->open_fps[fileno(stream)] == stream)
+ task_data->open_fps[fileno(stream)] = NULL;
+ return(fclose(stream));
+}
+
+/* We link all malloc'ed segments by adding a couple of pointers
+ at the *end* - that way we can return the address malloc gave
+ (need to make sure we align those pointers) */
+
+/*
+ #define MALL_MARGIN 32
+ #define save_mall_size(size) save_mall_size1((size) + 2 * MALL_MARGIN)
+ #define save_mall_size1(size) \
+ (((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
+
+ #define save_mall_enq(ptr, mdp) save_mall_enq1((ptr), (mdp) - MALL_MARGIN)
+ #define save_mall_enq1(ptr, mdp) \
+ (((struct mall_data *)(mdp))->self = (ptr), \
+ ((struct mall_data *)(mdp))->next = task_data->mall_data, \
+ task_data->mall_data = (struct mall_data *)(mdp))
+*/
+#define save_mall_size(size) \
+(((size) + sizeof(char *) - 1) & (unsigned long)(-sizeof(char*)))
+#define save_mall_enq(ptr, mdp) \
+(((struct mall_data *)(mdp))->self = (ptr), \
+ ((struct mall_data *)(mdp))->next = task_data->mall_data, \
+ task_data->mall_data = (struct mall_data *)(mdp))
+
+
+#define save_mall_deq(ptr) { \
+ struct mall_data *mdp = task_data->mall_data, \
+ **prevnext = &task_data->mall_data; \
+ while (mdp != NULL && mdp->self != (ptr)) { \
+ prevnext = &mdp->next; \
+ mdp = mdp->next; \
+ } \
+ if (mdp != NULL) *prevnext = mdp->next; \
+}
+
+void *save_malloc2(size_t size, MallocFunction mf){
+ unsigned msize = save_mall_size(size);
+ char *ptr;
+
+ if ((ptr = (*mf)(msize + sizeof(struct mall_data))) != NULL &&
+ check_hook())
+ save_mall_enq((void *) ptr, (void *) (ptr + msize));
+ return((void *) ptr);
+}
+
+void *save_malloc(size_t size){
+ return save_malloc2(size, &malloc);
+}
+
+void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf){
+ unsigned msize = save_mall_size(nelem * elsize);
+ char *ptr;
+
+ if ((ptr = (*cf)(1, msize + sizeof(struct mall_data))) != NULL &&
+ check_hook())
+ save_mall_enq((void *) ptr, (void *) (ptr + msize));
+ return((void *) ptr);
+}
+
+void *save_calloc(size_t nelem, size_t elsize){
+ return save_calloc2(nelem,elsize,&calloc);
+}
+
+void *save_realloc2(void *optr, size_t size, ReallocFunction rf){
+ unsigned msize = save_mall_size(size);
+ char *ptr;
+
+ /* First we must dequeue the old save block, after that
+ we try to realloc, if that succeeds we enqueue the new
+ block, if it fails we have to enqueue the old one anew
+ so we must deduce the size of that old block first. */
+
+ struct mall_data *mdp0 = task_data->mall_data,
+ **prevnext0 = &task_data->mall_data;
+ while (mdp0 != NULL && mdp0->self != (((char *) optr))) {
+ prevnext0 = &mdp0->next;
+ mdp0 = mdp0->next;
+ }
+ /* mdp0 == NULL (can) mean that the block that is realloced
+ have been malloced with an (for example) ordinary malloc
+ (that is not a save_malloc). This is handled like: no dequeing
+ is done of that block, the new block is enqueued */
+ if (mdp0 != NULL)
+ save_mall_deq(((char *) optr));
+
+ if ((ptr = (*rf)(optr, msize + sizeof(struct mall_data))) != NULL &&
+ check_hook())
+ save_mall_enq((void *) ptr, (void *) (ptr + msize));
+ else if (mdp0 != NULL)
+ /* re-enqueue the old block that has just been dequeued */
+ save_mall_enq(((char *) optr), mdp0);
+
+ return((void *) ptr);
+}
+
+void *save_realloc(void *optr, size_t size){
+ return save_realloc2(optr,size,&realloc);
+}
+
+void save_free2(void *ptr, FreeFunction ff)
+{
+ if (check_hook())
+ save_mall_deq(((char *) ptr));
+ (*ff)(ptr);
+}
+
+void save_free(void *ptr){
+ save_free2(ptr,&free);
+}
+
+void save_cfree2(void *ptr, CfreeFunction cf)
+{
+ if (check_hook())
+ save_mall_deq(((char *)ptr));
+ (*cf)(ptr);
+}
+
+void save_cfree(void *ptr){
+ save_cfree2(ptr,&cfree);
+}
+
diff --git a/erts/etc/vxworks/reclaim.h b/erts/etc/vxworks/reclaim.h
new file mode 100644
index 0000000000..ca9aa8f6be
--- /dev/null
+++ b/erts/etc/vxworks/reclaim.h
@@ -0,0 +1,150 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifndef _RECLAIM_H
+#define _RECLAIM_H
+
+
+/* The Erlang release for VxWorks includes a simple mechanism for
+ "resource reclamation" at task exit - it allows replacement of the
+ functions that open/close "files" and malloc/free memory with versions
+ that keep track, to be able to "reclaim" file descriptors and memory
+ when a task exits (regardless of *how* it exits).
+
+ The interface to this mechanism is made available via this file,
+ with the following caveats:
+
+ - The interface may change (or perhaps even be removed, though that
+ isn't likely until VxWorks itself provides similar functionality)
+ in future releases - i.e. you must always use the version of this
+ file that comes with the Erlang release you are using.
+
+ - Disaster is guaranteed if you use the mechanism incorrectly (see
+ below for the correct way), e.g. allocate memory with the "tracking"
+ version of malloc() and free it with the "standard" version of free().
+
+ - The mechanism (of course) incurs some performance penalty - thus
+ for a simple program you may be better off with careful programming,
+ making sure that you do whatever close()/free()/etc calls that are
+ appropriate at all exit points (though if you need to guard against
+ taskDelete() etc, things get messy...).
+
+ To use the mechanism, simply program your application normally, i.e.
+ use open()/close()/malloc()/free() etc as usual, but #include this
+ file before any usage of the relevant functions. NOTE: To avoid the
+ "disaster" mentioned above, you *must* #include it in *all* (or none)
+ of the files that manipulate a particular file descriptor, allocated
+ memory area, etc.
+
+ Before any task that uses this utility is loaded (which includes the
+ erlang emulator), the reclaim.o object file has to be loaded and
+ the function reclaim_init() has to be called. reclaim_init should be called
+ only _ONCE_ in a systems lifetime and has only a primitive guard
+ against multiple calls (i.e. a global variable is checked). Therefore
+ the initialization should occur either in the start script of the system
+ or (even better) in the usrInit() part of system initialization. The
+ object file itself should be loaded only once, so linking it with the
+ kernel is a good idea, linking with each application is an extremely bad
+ dito. Make really sure that it's loaded _before_ any application that
+ uses it if You want to load it in the startup script.
+
+ If You dont want to have #define's for the posix/stdio names
+ of the file/memory operations (i.e. no #define malloc save_malloc etc),
+ #define RECLAIM_NO_ALIAS in Your source before reclaim.h is included.
+*/
+
+#include <vxWorks.h> /* STATUS, size_t */
+#include <sockLib.h> /* struct sockaddr */
+#include <memLib.h>
+#include <stdio.h> /* FILE */
+
+#if defined(__STDC__)
+#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \
+extern RetType FunName ParamList
+#define _RECLAIM_VOID_PTR void *
+#define _RECLAIM_VOID_PARAM void
+#define _RECLAIM_VOID_RETURN void
+#elif defined(__cplusplus)
+#define _RECLAIM_DECL_FUN(RetType, FunName, ParamList) \
+extern "C" RetType FunName ParamList
+#define _RECLAIM_VOID_PTR void *
+#define _RECLAIM_VOID_PARAM
+#define _RECLAIM_VOID_RETURN void
+#else
+#define _RECLAIM_DECL_FUN(RetType, FunName, Ignore) extern RetType FunName()
+#define DECLARE_FUNCTION_TYPE(RetType, Type, PList) typedef RetType (* Type)()
+#define _RECLAIM_VOID_PTR char *
+#define _RECLAIM_VOID_PARAM
+#define _RECLAIM_VOID_RETURN
+#endif /* __STDC__ / __cplusplus */
+
+/* Initialize the facility, on a per system basis. */
+_RECLAIM_DECL_FUN(STATUS, reclaim_init, (_RECLAIM_VOID_PARAM));
+
+/* File descriptor operations */
+_RECLAIM_DECL_FUN(int,save_open,(char *, int, ...));
+_RECLAIM_DECL_FUN(int,save_creat,(char *, int));
+_RECLAIM_DECL_FUN(int,save_socket,(int, int, int));
+_RECLAIM_DECL_FUN(int,save_accept,(int, struct sockaddr *, int *));
+_RECLAIM_DECL_FUN(int,save_close,(int));
+/* Interface to add an fd to what's reclaimed even though it's not open with
+ one of the above functions */
+_RECLAIM_DECL_FUN(_RECLAIM_VOID_RETURN, save_fd, (int fd));
+#ifndef RECLAIM_NO_ALIAS
+#define open save_open
+#define creat save_creat
+#define socket save_socket
+#define accept save_accept
+#define close save_close
+#endif
+/* Stdio file operations */
+_RECLAIM_DECL_FUN(FILE *, save_fopen, (const char *, char *));
+_RECLAIM_DECL_FUN(FILE *, save_fdopen, (int, char *));
+_RECLAIM_DECL_FUN(FILE *, save_freopen, (char *, char *, FILE *));
+_RECLAIM_DECL_FUN(int, save_fclose, (FILE *));
+/* XXX Should do opendir/closedir too... */
+#ifndef RECLAIM_NO_ALIAS
+#define fopen save_fopen
+#define fdopen save_fdopen
+#define freopen save_freopen
+#define fclose save_fclose
+#endif
+/* Memory allocation */
+_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_malloc, (size_t));
+_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_calloc, (size_t, size_t));
+_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, save_realloc,
+ (_RECLAIM_VOID_PTR, size_t));
+_RECLAIM_DECL_FUN(void, save_free, (_RECLAIM_VOID_PTR));
+_RECLAIM_DECL_FUN(void, save_cfree, (_RECLAIM_VOID_PTR));
+#ifndef RECLAIM_NO_ALIAS
+#define malloc save_malloc
+#define calloc save_calloc
+#define realloc save_realloc
+#define free save_free
+#define cfree save_cfree
+#endif
+/* Generic interfaces to malloc etc... */
+_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_malloc, (size_t));
+_RECLAIM_DECL_FUN(_RECLAIM_VOID_PTR, plain_realloc,
+ (_RECLAIM_VOID_PTR, size_t));
+_RECLAIM_DECL_FUN(void, plain_free, (_RECLAIM_VOID_PTR));
+#endif /* _RECLAIM_H */
+
+
+
+
diff --git a/erts/etc/vxworks/reclaim_private.h b/erts/etc/vxworks/reclaim_private.h
new file mode 100644
index 0000000000..4ed935bee2
--- /dev/null
+++ b/erts/etc/vxworks/reclaim_private.h
@@ -0,0 +1,44 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifndef _RECLAIM_PRIVATE_H
+#define _RECLAIM_PRIVATE_H
+/*
+ * Private header for the reclaim facility, also included in the emulator.
+ */
+
+#include "reclaim.h"
+
+/* Typedefs for ANSI memory allocation function pointers */
+typedef void *(*MallocFunction)(size_t);
+typedef void *(*ReallocFunction)(void *, size_t);
+typedef void *(*CallocFunction)(size_t, size_t);
+typedef void (*FreeFunction)(void *);
+typedef STATUS (*CfreeFunction)(char *);
+
+/* Functions for internal use and use by the emulator */
+extern int reclaim_max_files(void);
+extern void set_reclaim_free_function(FreeFunction f);
+extern void save_delete_hook(FUNCPTR func, caddr_t parm);
+extern void *save_malloc2(size_t size, MallocFunction mf);
+extern void *save_calloc2(size_t nelem, size_t elsize, CallocFunction cf);
+extern void *save_realloc2(void *optr, size_t size, ReallocFunction rf);
+extern void save_free2(void *ptr, FreeFunction ff);
+extern void save_cfree2(void *ptr, CfreeFunction ff);
+
+#endif /* _RECLAIM_PRIVATE_H */
diff --git a/erts/etc/vxworks/resolv.conf b/erts/etc/vxworks/resolv.conf
new file mode 100644
index 0000000000..85c89d64c4
--- /dev/null
+++ b/erts/etc/vxworks/resolv.conf
@@ -0,0 +1,6 @@
+domain du.uab.ericsson.se
+nameserver 134.138.176.16
+nameserver 136.225.254.224
+nameserver 134.138.128.25
+search du.uab.ericsson.se uab.ericsson.se ericsson.se
+lookup bind file
diff --git a/erts/etc/vxworks/vxcall.c b/erts/etc/vxworks/vxcall.c
new file mode 100644
index 0000000000..3362d05fc5
--- /dev/null
+++ b/erts/etc/vxworks/vxcall.c
@@ -0,0 +1,145 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vxWorks.h>
+#include <symLib.h>
+#include <sysSymTbl.h>
+#include <a_out.h>
+
+extern char *malloc();
+static STATUS lookup();
+
+/*
+ Little utility to convert from Unix' argv,argv calling conventions to
+ VxWorks' arg0,arg1,arg2,...
+ Will do limited argument parsing - no parenthesis around nor commas
+ between the args, which may be "-enclosed strings (without \ escapes),
+ '-enclosed characters (also no \ escapes), integers, or symbols.
+*/
+
+int vxcall(argc, argv)
+int argc;
+char **argv;
+{
+ int vxarg[10]; /* Max 10 args can be passed */
+ FUNCPTR entry;
+ SYM_TYPE type;
+ int i, l;
+
+#ifdef DEBUG
+ fdprintf(2, "vxcall:");
+ for (i = 1; i < argc; i++)
+ fdprintf(2, " %s", argv[i]);
+ fdprintf(2, "\n");
+#endif
+ if (lookup(argv[1], N_EXT | N_TEXT, (char **)&entry) != OK)
+ return(ERROR);
+ /* Do limited "C" parsing of the args */
+ for (i = 0; i < 10; i++) {
+ if (i < argc - 2) {
+ switch (argv[i+2][0]) {
+ case '"':
+ l = strlen(argv[i+2]) - 1;
+ if (argv[i+2][l] != '"')
+ return(ERROR);
+ /* just strip the quotes - should do \escapes within... */
+ vxarg[i] = (int)&argv[i+2][1];
+ argv[i+2][l] = '\0';
+ break;
+ case '\'':
+ if (argv[i+2][2] != '\'')
+ return(ERROR);
+ vxarg[i] = argv[i+2][1]; /* should do \escapes... */
+ break;
+ case '1': case '2': case '3': case '4':
+ case '5': case '6': case '7': case '8': case '9':
+ vxarg[i] = atoi(argv[i+2]); /* should do octal, hex, float.. */
+ break;
+ default:
+ if (lookup(argv[i+2], 0, (char **)&vxarg[i]) != OK)
+ return(ERROR);
+ }
+ } else
+ vxarg[i] = 0;
+ }
+#ifdef DEBUG
+ fdprintf(2, "calling 0x%x(0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x,0x%x)\n",
+ entry, vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4],
+ vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]);
+#endif
+ return((*entry)(vxarg[0], vxarg[1], vxarg[2], vxarg[3], vxarg[4],
+ vxarg[5], vxarg[6], vxarg[7], vxarg[8], vxarg[9]));
+}
+
+/* Entry point for unix:cmd in post-4.1 erlang - uses "sh -c 'cmd...'" */
+int sh(argc, argv)
+int argc;
+char **argv;
+{
+ int ll = strlen(argv[argc-1]) - 1;
+
+#ifdef DEBUG
+ int i;
+ fdprintf(2, "sh:");
+ for (i = 1; i < argc; i++)
+ fdprintf(2, " %s", argv[i]);
+ fdprintf(2, "\n");
+#endif
+ if (strcmp(argv[1], "-c") != 0 ||
+ argv[2][0] != '\'' || argv[argc-1][ll] != '\'')
+ return(ERROR);
+ argv[argc-1][ll] = '\0'; /* delete trailing ' */
+ argv[2]++; /* skip leading ' (*after* the above!) */
+ return(vxcall(argc-1, argv+1));
+}
+
+/* Lookup symbol; get address for text symbols, value (assuming int)
+ otherwise; return OK or ERROR on failure
+ Symbol name is null-terminated and without the leading '_' */
+STATUS
+lookup(sym, stype, value)
+char *sym, **value;
+int stype;
+{
+ char buf[256];
+ char *symname = buf;
+ int len, ret;
+ SYM_TYPE type;
+
+ len = strlen(sym);
+ if (len > 254 && (symname = malloc(len+2)) == NULL)
+ return(ERROR);
+#if defined _ARCH_PPC || defined SIMSPARCSOLARIS
+ /* GCC for PPC or SIMSPARC doesn't add a leading _ to symbols */
+ strcpy(symname, sym);
+#else
+ sprintf(symname, "_%s", sym);
+#endif
+ ret = (stype != 0) ?
+ symFindByNameAndType(sysSymTbl, symname, value, &type, stype, stype) :
+ symFindByName(sysSymTbl, symname, value, &type);
+ if (symname != buf)
+ free(symname);
+ if (ret == OK && (type & N_TEXT) == 0) /* get value */
+ *value = (char *)*((int *)*value);
+ return(ret);
+}
diff --git a/erts/etc/vxworks/wd_example.c b/erts/etc/vxworks/wd_example.c
new file mode 100644
index 0000000000..0e3a6a1cb2
--- /dev/null
+++ b/erts/etc/vxworks/wd_example.c
@@ -0,0 +1,141 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * File: frc5te_wd.c
+ * Purpose: Watchdog NMI handling for FORCE 5TE
+ *
+ * Description:
+ * The watchdog handler routines are system specific. A program that
+ * wants to utilize a hardware watchdog should call wd_init and test
+ * the return value. If wd_init returns true (!0); there is a hardware
+ * watchdog, and that watchdog has been activated. If no watchdog exists,
+ * wd_init returns false (0).
+ *
+ * To keep the watchdog happy, call wd_reset at least every X seconds,
+ * where X is the number of seconds specified in the call to wd_init.
+ *
+ * The watchdog can be disarmed by setting the variable wd_disarmed to 1,
+ * and armed again by setting the same variable to 0. Watchdog status
+ * information can be retrieved using the function wd_status.
+ *
+ */
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+#include <frc5e.h>
+#include <logLib.h>
+#include <taskLib.h>
+#include <sysLib.h>
+#include <stdio.h>
+#include "hw_watchdog.h"
+
+/* prototypes */
+extern sysNMIConnect();
+#ifdef __STDC__
+void wd_keeper(int);
+void wd_nmi_int(UINT8);
+void wd_status(void);
+#else
+void wd_keeper();
+void wd_nmi_int();
+void wd_status();
+#endif
+
+#define WD_NMI_MIN_DELAY 0.830 /* Min time before watchdog NMI (in seconds) */
+#define WD_RESET_FREQUENCY (WD_NMI_MIN_DELAY / 2) /* how often the timer is reset */
+
+#define WD_KEEPER_STACK_SIZE 10000
+
+/* global variables */
+extern int spTaskOptions;
+static volatile int wd_count_startval; /* start value set by wd_init */
+static volatile int wd_count; /* counter for wd_keeper */
+volatile int wd_disarmed = 0; /* debug feature */
+
+/* wd_init is executed to initialize the watchdog. It spawns the task */
+/* wd_keeper and returns true (non-zero) if a hardware watchdog exists, */
+/* or returns false (zero) otherwise. */
+int wd_init(timeout, prio)
+ int timeout, prio;
+{
+ taskSpawn("wd_keeper", prio, spTaskOptions, WD_KEEPER_STACK_SIZE,
+ (FUNCPTR)wd_keeper, timeout,0,0,0,0,0,0,0,0,0);
+ return !0; /* watchdog exists */
+}
+
+
+/* wd_reset is called as an alive-signal from the supervisor process. */
+/* If there is no call to this function within a certain time, the */
+/* watchdog will reboot the system. */
+void wd_reset()
+{
+ wd_count = wd_count_startval;
+}
+
+
+/* wd_keeper runs as a separate task and resets the watchdog timer */
+/* before an NMI is generated. This task uses the counter wd_count to */
+/* decide if it should exit or keep resetting the timer. */
+/* Note! This task must run with higher priority than the application! */
+void wd_keeper(timeout)
+ int timeout;
+{
+ int wd_delay = sysClkRateGet() * WD_RESET_FREQUENCY;
+ wd_count_startval = (int)(timeout / WD_RESET_FREQUENCY);
+ wd_count = wd_count_startval;
+
+ /* Connect and enable level 15 interrupts */
+ sysNMIConnect((VOIDFUNCPTR) wd_nmi_int, WD_NMI, WD_NMI);
+ *(char *)FRC5CE_GEN_PURPOSE2_REG |= FRC5CE_NMI_ENABLE;
+
+ while ((wd_count > 0) || wd_disarmed) {
+ *(char *)FRC5CE_VME_A32MAP_REG |= FRC5CE_WATCHDOG_ENABLE;
+ taskDelay(wd_delay);
+ if (!wd_disarmed) wd_count--;
+ else wd_count = wd_count_startval;
+ }
+ logMsg("Watchdog keeper exits. No alive signal from application in %d seconds.\n",wd_count_startval * WD_RESET_FREQUENCY,0,0,0,0,0);
+}
+
+
+/* wd_nmi_int is the function connected to the watchdog interrupt. */
+/* It will report the failure to reset the watchdog timer. */
+void wd_nmi_int(type)
+ UINT8 type;
+{
+ switch(type) {
+ case WD_NMI:
+ logMsg("Watchdog interrupt! System will reboot.\n",0,0,0,0,0,0);
+ break;
+ default:
+ logMsg("Bad type (%d) in call to watchdog interrupt handler.\n",type,0,0,0,0,0);
+ break;
+ }
+}
+
+
+/* wd_status displays the current value of the counter. */
+void wd_status()
+{
+ fprintf(stderr, "Watchdog is %sarmed.\n", wd_disarmed ? "dis" : "");
+ fprintf(stderr, "Counter value: %d\n", wd_count);
+ fprintf(stderr, "Start value is: %d (%d seconds)\n",
+ wd_count_startval, (int)(wd_count_startval * WD_RESET_FREQUENCY));
+}
diff --git a/erts/etc/win32/Install.c b/erts/etc/win32/Install.c
new file mode 100644
index 0000000000..4a559cd8a2
--- /dev/null
+++ b/erts/etc/win32/Install.c
@@ -0,0 +1,229 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Some code just simply does not deserve functions :-)
+ * Dead simple installation program to set up init files etc after erlang is
+ * copied to its destination. Also to be used after a patch is applied.
+ */
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "init_file.h"
+
+int main(int argc, char **argv)
+{
+ int silent = 0;
+ int start_sasl = 0;
+ char *root = NULL;
+ int i;
+ char buffer[MAX_PATH];
+ char erts_dir[MAX_PATH];
+ char release_dir[MAX_PATH];
+ char bin_dir[MAX_PATH];
+ char *tmp;
+ char my_ini_filename[MAX_PATH];
+ InitFile *my_ini_file;
+ InitSection *my_ini_section;
+ char version_string[MAX_PATH];
+ InitFile *ini_file;
+ InitSection *ini_section;
+ HANDLE module = GetModuleHandle(NULL);
+ char *binaries[] = { "erl.exe", "werl.exe", "erlc.exe",
+ "dialyzer.exe", "typer.exe",
+ "escript.exe", NULL };
+ char *scripts[] = { "start_clean.boot", "start_sasl.boot", NULL };
+ char fromname[MAX_PATH];
+ char toname[MAX_PATH];
+
+
+ for (i = 1; i < argc; ++i) {
+ switch(argv[i][0]) {
+ case '-' :
+ switch(argv[i][1]) {
+ case 's' :
+ silent = 1;
+ break;
+ default:
+ fprintf(stderr, "Unknown command switch %s\n",
+ argv[i]);
+ exit(1);
+ }
+ break;
+ default:
+ if (root != NULL) {
+ fprintf(stderr, "Only one root directory can be specified, "
+ "parameter %s is illegal\n",
+ argv[i]);
+ exit(1);
+ }
+ root = argv[i];
+ break;
+ }
+ }
+ if (root == NULL) {
+ if (module = NULL) {
+ fprintf(stderr, "Cannot GetModuleHandle()\n");
+ exit(1);
+ }
+
+ if (GetModuleFileName(module,buffer,MAX_PATH) == 0) {
+ fprintf(stderr,"Could not GetModuleFileName()\n");
+ exit(1);
+ }
+ i = strlen(buffer) - 1;
+ while ( i >= 0 && buffer[i] != '\\') {
+ --i;
+ }
+ if (i < 0) {
+ fprintf(stderr,"GetModuleFileName returned broken path\n");
+ exit(1);
+ }
+ buffer[i] = '\0';
+ root = buffer;
+ }
+
+ if (!silent) {
+ char answer[100];
+ char *eol;
+ start_sasl = 1;
+ printf("Do you want a minimal startup instead of sasl [No]: ");
+ fflush(stdout);
+ if (fgets(answer,100,stdin) == NULL) {
+ fprintf(stderr, "Could not read answer from user.\n");
+ exit(1);
+ }
+ eol = strchr(answer,'\n');
+ if (eol == NULL) {
+ while (getchar() != '\n')
+ ;
+ } else {
+ *eol = '\0';
+ }
+ if ((eol = strchr(answer, '\r')) != NULL) {
+ *eol = '\0';
+ }
+ if (_stricmp(answer,"yes") == 0 || _stricmp(answer,"y") == 0) {
+ start_sasl = 0;
+ }
+ }
+ sprintf(my_ini_filename,"%s\\Install.ini",root);
+ my_ini_file = load_init_file(my_ini_filename);
+ if (my_ini_file == NULL) {
+ fprintf(stderr,"Cannot open init file %s\n",my_ini_filename);
+ exit(1);
+ }
+
+ if ((my_ini_section = lookup_init_section(my_ini_file,"Install"))
+ == NULL) {
+ fprintf(stderr,"No [Install] section in init file %s\n",
+ my_ini_filename);
+ exit(1);
+ }
+
+ if ((tmp = lookup_init_entry(my_ini_section, "VSN")) == NULL) {
+ fprintf(stderr,"No key VSN in init file %s\n",
+ my_ini_filename);
+ exit(1);
+ }
+
+ strcpy(version_string,tmp);
+
+ sprintf(erts_dir,"%s\\erts-%s\\bin",root,tmp);
+ if ((tmp = lookup_init_entry(my_ini_section, "SYSTEM_VSN")) == NULL) {
+ fprintf(stderr,"No key SYSTEM_VSN in init file %s\n",
+ my_ini_filename);
+ exit(1);
+ }
+ sprintf(release_dir,"%s\\releases\\%s",root,tmp);
+
+ sprintf(bin_dir,"%s\\bin",root);
+ CreateDirectory(bin_dir,NULL);
+
+ free_init_file(my_ini_file);
+
+ for (i = 0; binaries[i] != NULL; ++i) {
+ sprintf(fromname,"%s\\%s",erts_dir,binaries[i]);
+ sprintf(toname,"%s\\%s",bin_dir,binaries[i]);
+ if (GetFileAttributes(fromname) == 0xFFFFFFFF) {
+ fprintf(stderr,"Could not find file %s\n",
+ fromname);
+ exit(1);
+ }
+ if (!CopyFile(fromname,toname,FALSE)) {
+ fprintf(stderr,"Could not copy file %s to %s\n",
+ fromname,toname);
+ fprintf(stderr,"Continuing installation anyway...\n");
+ }
+ }
+
+ for (i = 0; scripts[i] != NULL; ++i) {
+ sprintf(fromname,"%s\\%s",release_dir,scripts[i]);
+ sprintf(toname,"%s\\%s",bin_dir,scripts[i]);
+ if (GetFileAttributes(fromname) == 0xFFFFFFFF) {
+ fprintf(stderr,"Could not find file %s\n",
+ fromname);
+ exit(1);
+ }
+ if (!CopyFile(fromname,toname,FALSE)) {
+ fprintf(stderr,"Could not copy file %s to %s\n",
+ fromname,toname);
+ fprintf(stderr,"Cannot continue installation, bailing out.\n");
+ exit(1);
+ }
+ }
+ if (start_sasl) {
+ sprintf(fromname,"%s\\start_sasl.boot",bin_dir);
+ } else {
+ sprintf(fromname,"%s\\start_clean.boot",bin_dir);
+ }
+ sprintf(toname,"%s\\start.boot",bin_dir);
+ if (!CopyFile(fromname,toname,FALSE)) {
+ fprintf(stderr,"Could not copy file %s to %s\n",
+ fromname,toname);
+ fprintf(stderr,"Cannot continue installation, bailing out.\n");
+ exit(1);
+ }
+ ini_file = create_init_file();
+ ini_section = create_init_section("erlang");
+ add_init_section(ini_file,ini_section);
+ add_init_entry(ini_section,"Bindir",erts_dir);
+ add_init_entry(ini_section,"Progname","erl");
+ add_init_entry(ini_section,"Rootdir",root);
+ sprintf(fromname,"%s\\erl.ini",erts_dir);
+ sprintf(toname,"%s\\erl.ini",bin_dir);
+ if (store_init_file(ini_file,fromname) != 0) {
+ fprintf(stderr,"Could not create file %s\n",
+ fromname);
+ fprintf(stderr,"Cannot continue installation, bailing out.\n");
+ exit(1);
+ }
+ if (!CopyFile(fromname,toname,FALSE)) {
+ fprintf(stderr,"Could not copy file %s to %s\n",
+ fromname,toname);
+ fprintf(stderr,"Cannot continue installation, bailing out.\n");
+ exit(1);
+ }
+ if (!silent) {
+ printf("Erlang %s installed successfully\n", version_string);
+ }
+ return 0;
+}
+
+
+
diff --git a/erts/etc/win32/Install.src b/erts/etc/win32/Install.src
new file mode 100644
index 0000000000..4aaa171ce0
--- /dev/null
+++ b/erts/etc/win32/Install.src
@@ -0,0 +1,4 @@
+[Install]
+VSN=%I_VSN%
+SYSTEM_VSN=%I_SYSTEM_VSN%
+
diff --git a/erts/etc/win32/Makefile b/erts/etc/win32/Makefile
new file mode 100644
index 0000000000..400e5c5bba
--- /dev/null
+++ b/erts/etc/win32/Makefile
@@ -0,0 +1,72 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1996-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+
+include ../../vsn.mk
+
+# ----------------------------------------------------
+# Release directory specification
+# ----------------------------------------------------
+RELEASE_PATH= ../../release/$(TARGET)
+RELSYSDIR = $(RELEASE_PATH)/erts-$(VSN)
+ROOTSYSDIR = $(RELEASE_PATH)
+
+BINDIR = $(ERL_TOP)/bin/$(TARGET)
+RUNTIME = $(ERL_TOP)/erts/emulator/beam
+SYS = $(ERL_TOP)/erts/emulator/sys/win32
+OBJ = $(ERL_TOP)/erts/obj/$(TARGET)
+ROOTDIR = $(ERL_TOP)/erts
+
+INSTALL_PROGS = \
+ $(BINDIR)/inet_gethost.exe \
+ $(BINDIR)/erl.exe \
+ $(BINDIR)/werl.exe \
+ $(BINDIR)/heart.exe \
+ $(BINDIR)/erlc.exe \
+ $(BINDIR)/erlsrv.exe \
+ $(BINDIR)/start_erl.exe
+
+INSTALL_SRC = ./start_erl.c ./Nmakefile.start_erl
+
+INSTALL_LIBS = $(BINDIR)/erl_dll.lib
+
+INSTALL_ICONS = ./beam_icon.ico ./erl_icon.ico ./hrl_icon.ico
+
+opt debug all clean depend:
+ @echo Nothing to do for "'"$@"'" on $(TARGET)
+
+# ----------------------------------------------------
+# Release Target
+# ----------------------------------------------------
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+release_spec:
+ $(INSTALL_DIR) $(RELSYSDIR)/bin
+ $(INSTALL_DIR) $(RELSYSDIR)/src
+ $(INSTALL_DIR) $(ROOTSYSDIR)/usr/include
+ $(INSTALL_DIR) $(ROOTSYSDIR)/usr/lib
+ $(INSTALL_DIR) $(ROOTSYSDIR)/usr/lib/icons
+ $(INSTALL_PROGRAM) $(INSTALL_PROGS) $(RELSYSDIR)/bin
+ $(INSTALL_DATA) $(INSTALL_SRC) $(RELSYSDIR)/src
+ $(INSTALL_DATA) $(INSTALL_ICONS) $(ROOTSYSDIR)/usr/lib/icons
+
+release_docs release_docs_spec docs:
+
diff --git a/erts/etc/win32/Nmakefile.start_erl b/erts/etc/win32/Nmakefile.start_erl
new file mode 100644
index 0000000000..5bf9fd78d5
--- /dev/null
+++ b/erts/etc/win32/Nmakefile.start_erl
@@ -0,0 +1,33 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 1998-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Example nmakefile to build start_erl.exe from start_erl.c
+# Microsoft Visual C++ is expected to be installed
+# and the PATH, INCLUDE and LIB environment valiables set up correctly.
+# Invoke with nmake -f NMakefile.start_erl.
+
+CC=cl
+CFLAGS=-W3
+LDFLAGS=user32.lib advapi32.lib
+
+start_erl.exe: start_erl.c
+ $(CC) $(CFLAGS) $? -Festart_erl.exe $(LDFLAGS)
+
+clean:
+ -del start_erl.obj start_erl.exe
+
diff --git a/erts/etc/win32/beam.rc b/erts/etc/win32/beam.rc
new file mode 100644
index 0000000000..cd7db67d4d
--- /dev/null
+++ b/erts/etc/win32/beam.rc
@@ -0,0 +1,102 @@
+//
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1997-2009. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+#include <windows.h>
+#include "resource.h"
+
+1 ICON DISCARDABLE "erlang.ico"
+2 ICON DISCARDABLE "erl_icon.ico"
+3 ICON DISCARDABLE "hrl_icon.ico"
+4 ICON DISCARDABLE "beam_icon.ico"
+1 BITMAP MOVEABLE PURE "toolbar.bmp"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+1 MENU DISCARDABLE
+BEGIN
+ POPUP "&File"
+ BEGIN
+ MENUITEM "&Open Logfile...", IDMENU_STARTLOG
+ MENUITEM "&Close Logfile", IDMENU_STOPLOG
+ MENUITEM SEPARATOR
+ MENUITEM "&Exit\tAlt+F4", IDMENU_EXIT
+ END
+ POPUP "&Edit"
+ BEGIN
+ MENUITEM "&Copy\tCtrl+C", IDMENU_COPY
+ MENUITEM "&Paste\tCtrl+V", IDMENU_PASTE
+ MENUITEM SEPARATOR
+ MENUITEM "Select A&ll", IDMENU_SELALL
+ END
+ POPUP "&Options"
+ BEGIN
+ MENUITEM "&Select Font...", IDMENU_FONT
+ MENUITEM "Select &Background...", IDMENU_SELECTBKG
+ END
+ POPUP "&View"
+ BEGIN
+ MENUITEM "&Toolbar", IDMENU_TOOLBAR
+ END
+ POPUP "&Help"
+ BEGIN
+ MENUITEM "&About", IDMENU_ABOUT
+ END
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+AboutBox DIALOG DISCARDABLE 0, 0, 217, 55
+STYLE DS_MODALFRAME | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "About Erlang Shell"
+FONT 8, "MS Sans Serif"
+BEGIN
+ ICON 1,-1,11,17,18,20
+ LTEXT "Erlang Shell Version 1.0",ID_VERSIONSTRING,40,10,119,8,
+ SS_NOPREFIX
+ LTEXT "Copyright � Ericsson Telecom AB",-1,40,25,
+ 119,8
+ DEFPUSHBUTTON "OK",IDOK,178,7,32,14,WS_GROUP
+END
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerators
+//
+
+1 ACCELERATORS
+{
+ VK_CANCEL, ID_BREAK, VIRTKEY, CONTROL
+ "^C", IDMENU_COPY
+ VK_INSERT, IDMENU_COPY, VIRTKEY, CONTROL
+ "^V", IDMENU_PASTE
+ VK_INSERT, IDMENU_PASTE, VIRTKEY, SHIFT
+ VK_F1, IDMENU_ABOUT, VIRTKEY
+}
+
+
+
+
+
+
+
+
+
diff --git a/erts/etc/win32/beam_icon.ico b/erts/etc/win32/beam_icon.ico
new file mode 100644
index 0000000000..fb22afda62
--- /dev/null
+++ b/erts/etc/win32/beam_icon.ico
Binary files differ
diff --git a/erts/etc/win32/cygwin_tools/erl b/erts/etc/win32/cygwin_tools/erl
new file mode 100755
index 0000000000..576825c4be
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/erl
@@ -0,0 +1,48 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Note! This shellscript expects to be run in a cygwin environment,
+# it converts erlc command lines to native windows erlc commands, which
+# basically means running the command cygpath on whatever is a path...
+
+CMD=""
+for x in "$@"; do
+ case "$x" in
+ -I/*|-o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ #echo "Foooo:$z"
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ /*)
+ #echo "absolute:"$x;
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+# +{*);;
+ *)
+# y=`echo $x | sed 's,",\\\\\\\\\\\",g'`;
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+done
+#echo "$@"
+#eval echo erlc.exe $CMD
+ERL_TOP=`cygpath -m $ERL_TOP`
+export ERL_TOP
+eval erl.exe $CMD
diff --git a/erts/etc/win32/cygwin_tools/erlc b/erts/etc/win32/cygwin_tools/erlc
new file mode 100755
index 0000000000..a18ec27bf4
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/erlc
@@ -0,0 +1,61 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Note! This shellscript expects to be run in a cygwin environment,
+# it converts erlc command lines to native windows erlc commands, which
+# basically means running the command cygpath on whatever is a path...
+
+CMD=""
+ECHO_ONLY=false
+for x in "$@"; do
+ case "$x" in
+ --echo_only)
+ ECHO_ONLY=true;;
+ -I/*|-o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -$z$MPATH";;
+ -pa/*)
+ y=`echo $x | sed 's,^-pa\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -pa $MPATH";;
+ /*)
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+# Needed for +'{preproc_flags,whatever}'
+ +{preproc_flags,*})
+ y=`echo $x | sed 's,^+{preproc_flags\,"\(.*\)"},\1,g'`;
+ z=`eval $0 --echo_only $y`;
+ case "$z" in # Dont "doubledoublequote"
+ \"*\")
+ CMD="$CMD +'{preproc_flags,$z}'";;
+ *)
+ CMD="$CMD +'{preproc_flags,\"$z\"}'";;
+ esac;;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+done
+if [ $ECHO_ONLY = true ]; then
+ echo $CMD
+else
+ eval erlc.exe $CMD
+fi
diff --git a/erts/etc/win32/cygwin_tools/javac.sh b/erts/etc/win32/cygwin_tools/javac.sh
new file mode 100755
index 0000000000..f9ee24593f
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/javac.sh
@@ -0,0 +1,53 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Note! This shellscript expects to be run in a cygwin environment,
+# it converts erlc command lines to native windows erlc commands, which
+# basically means running the command cygpath on whatever is a path...
+
+CMD=""
+CLASSPATH=`cygpath -m -p $CLASSPATH`
+export CLASSPATH
+#echo "CLASSPATH=$CLASSPATH"
+SAVE="$@"
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -I/*|-o/*|-d/*)
+ y=`echo $x | sed 's,^-[Iod]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Iod]\)\(/.*\),\1,g'`;
+ #echo "Foooo:$z"
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -d|-I|-o)
+ shift;
+ MPATH=`cygpath -m $1`;
+ CMD="$CMD $x $MPATH";;
+ /*)
+ #echo "absolute:"$x;
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+#echo javac.exe $CMD
+eval javac.exe $CMD
diff --git a/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh
new file mode 100755
index 0000000000..20fe143890
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/make_bootstrap_ini.sh
@@ -0,0 +1,44 @@
+#! /bin/bash
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Create a local init-file for erlang in the build environment.
+if [ -z "$1" ]; then
+ echo "error: $0: No rootdir given"
+ exit 1
+else
+ RDIR=$1
+fi
+if [ -z "$2" ]; then
+ echo "error: $0: No bindir given"
+ exit 1
+else
+ BDIR=$2
+fi
+
+DRDIR=`(cygpath -d $RDIR 2>/dev/null || cygpath -w $RDIR) | sed 's,\\\,\\\\\\\\,g'`
+DBDIR=`(cygpath -d $BDIR 2>/dev/null || cygpath -w $BDIR) | sed 's,\\\,\\\\\\\\,g'`
+
+
+cat > $RDIR/bin/erl.ini <<EOF
+[erlang]
+Bindir=$DBDIR
+Progname=erl
+Rootdir=$DRDIR
+EOF
+
diff --git a/erts/etc/win32/cygwin_tools/make_local_ini.sh b/erts/etc/win32/cygwin_tools/make_local_ini.sh
new file mode 100755
index 0000000000..8e29573dc4
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/make_local_ini.sh
@@ -0,0 +1,41 @@
+#! /bin/bash
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Create a local init-file for erlang in the build environment.
+if [ -z "$1" ]; then
+ if [ -z $ERL_TOP ]; then
+ echo "error: $0: No rootdir available"
+ exit 1
+ else
+ RDIR=$ERL_TOP
+ fi
+else
+ RDIR=$1
+fi
+
+DDIR=`(cygpath -d $RDIR 2>/dev/null || cygpath -w $RDIR) | sed 's,\\\,\\\\\\\\,g'`
+
+
+cat > $RDIR/bin/erl.ini <<EOF
+[erlang]
+Bindir=$DDIR\\\\bin\\\\win32
+Progname=erl
+Rootdir=$DDIR
+EOF
+
diff --git a/erts/etc/win32/cygwin_tools/mingw/ar.sh b/erts/etc/win32/cygwin_tools/mingw/ar.sh
new file mode 100755
index 0000000000..5b8f58e5de
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/mingw/ar.sh
@@ -0,0 +1,55 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+CMD=""
+if [ -z "$MINGW_EXE_PATH" ]; then
+ echo "You have to set MINGW_EXE_PATH to run ar.sh" >&2
+ exit 1
+fi
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o|-out:)
+ shift
+ case "$1" in
+ /*)
+ MPATH=`cygpath -m $1`;;
+ *)
+ MPATH=$1;;
+ esac
+ CMD="rcv \"$MPATH\" $CMD";;
+ -out:*)
+ y=`echo $x | sed 's,^-out:\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="rcv \"$MPATH\" $CMD";;
+ -o*)
+ y=`echo $x | sed 's,^-o\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="rcv \"$MPATH\" $CMD";;
+ /*)
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+
+eval $MINGW_EXE_PATH/ar.exe $CMD
diff --git a/erts/etc/win32/cygwin_tools/mingw/cc.sh b/erts/etc/win32/cygwin_tools/mingw/cc.sh
new file mode 100755
index 0000000000..ae284893fa
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/mingw/cc.sh
@@ -0,0 +1,293 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Icky cl wrapper that does it's best to behave like a Unixish cc.
+# Made to work for Erlang builds and to make configure happy, not really
+# general I suspect.
+# set -x
+# Save the command line for debug outputs
+SAVE="$@"
+
+# Constants
+COMMON_CFLAGS="-mwindows -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -DWIN32_MINGW"
+
+# Variables
+# The stdout and stderr for the compiler
+MSG_FILE=/tmp/gcc.exe.$$.1
+ERR_FILE=/tmp/gcc.exe.$$.2
+
+# "Booleans" determined during "command line parsing"
+# If the stdlib option is explicitly passed to this program
+MD_FORCED=false
+# If we're preprocession (only) i.e. -E
+PREPROCESSING=false
+# If this is supposed to be a debug build
+DEBUG_BUILD=false
+# If this is supposed to be an optimized build (there can only be one...)
+OPTIMIZED_BUILD=false
+# If we're linking or only compiling
+LINKING=true
+
+# This data is accumulated during command line "parsing"
+# The stdlibrary option, default multithreaded dynamic
+# NOTE! The standard library options are actually ignored by the linker later
+# on, I've retained parsing of them from the VC++ version as they may be
+# needed in the future...
+MD=-MD
+# Flags for debug compilation
+DEBUG_FLAGS=""
+# Flags for optimization
+OPTIMIZE_FLAGS=""
+# The specified output filename (if any), may be either object or exe.
+OUTFILE=""
+# Unspe3cified command line options for the compiler
+CMD=""
+# All the c source files, in unix style
+SOURCES=""
+# All the options to pass to the linker, kept in Unix style
+LINKCMD=""
+LINKSOURCES=""
+DEPENDING=false
+
+if [ -z "$MINGW_EXE_PATH" ]; then
+ echo "You have to set MINGW_EXE_PATH to run cc.sh" >&2
+ exit 1
+fi
+
+
+# Loop through the parameters and set the above variables accordingly
+# Also convert some cygwin filenames to "mixed style" dito (understood by the
+# compiler very well), except for anything passed to the linker, that script
+# handles those and the sources, which are also kept unixish for now
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -Wall)
+ ;;
+ -c)
+ LINKING=false;;
+ -E)
+ PREPROCESSING=true;
+ LINKING=false;; # Obviously...
+ -MM|-M)
+ DEPENDING=true;
+ LINKING=false;; # Obviously...
+ -O*)
+ # Optimization hardcoded
+ OPTIMIZE_FLAGS="$x";
+ DEBUG_FLAGS="-ggdb";
+ DEBUG_BUILD=false;
+ if [ $MD_FORCED = false ]; then
+ MD=-MD;
+ fi
+ OPTIMIZED_BUILD=true;;
+ -g|-ggdb)
+ if [ $OPTIMIZED_BUILD = false ];then
+ # Hardcoded windows debug flags
+ DEBUG_FLAGS="-ggdb";
+ if [ $MD_FORCED = false ]; then
+ MD=-MDd;
+ fi
+ DEBUG_BUILD=true;
+ fi;;
+ # Allow forcing of stdlib
+ -mt|-MT)
+ MD="-MT";
+ MD_FORCED=true;;
+ -md|-MD)
+ MD="-MD";
+ MD_FORCED=true;;
+ -ml|-ML)
+ MD="-ML";
+ MD_FORCED=true;;
+ -mdd|-MDD|-MDd)
+ MD="-MDd";
+ MD_FORCED=true;;
+ -mtd|-MTD|-MTd)
+ MD="-MTd";
+ MD_FORCED=true;;
+ -mld|-MLD|-MLd)
+ MD="-MLd";
+ MD_FORCED=true;;
+ -o)
+ shift;
+ OUTFILE="$1";;
+ -o*)
+ y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`;
+ OUTFILE="$y";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -I*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -D*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ LINKCMD="$LINKCMD $x";;
+ /*.c)
+ SOURCES="$SOURCES $x";;
+ *.c)
+ SOURCES="$SOURCES $x";;
+ /*.o)
+ LINKCMD="$LINKCMD $x";;
+ *.o)
+ LINKCMD="$LINKCMD $x";;
+ *)
+ # Try to quote uninterpreted options
+ y=`echo $x | sed 's,",\\\",g'`;
+ LINKCMD="$LINKCMD $y";;
+ esac
+ shift
+done
+
+#Return code from compiler, linker.sh and finally this script...
+RES=0
+
+# Accumulated object names
+ACCUM_OBJECTS=""
+
+# A temporary object file location
+TMPOBJDIR=/tmp/tmpobj$$
+rm -rf $TMPOBJDIR
+mkdir $TMPOBJDIR
+
+
+for x in $SOURCES; do
+ # Compile each source
+ if [ $LINKING = false ]; then
+ # We should have an output defined, which is a directory
+ # or an object file
+ case $OUTFILE in
+ /*.o)
+ # Simple output, SOURCES should be one single
+ n=`echo $SOURCES | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ else
+ output_filename=`cygpath -m $OUTFILE`;
+ fi;;
+ *.o)
+ # Relative path needs no translation
+ n=`echo $SOURCES | wc -w`
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output."
+ exit 1
+ else
+ output_filename=$OUTFILE
+ fi;;
+ /*)
+ # Absolute directory
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=`cygpath -m $OUTFILE`
+ output_filename="$output_filename/${o}";;
+ *)
+ # Relative_directory or empty string (.//x.o is valid)
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename="./${OUTFILE}/${o}";;
+ esac
+ else
+ # We are linking, which means we build objects in a temporary
+ # directory and link from there. We should retain the basename
+ # of each source to make examining the exe easier...
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=$TMPOBJDIR/$o
+ ACCUM_OBJECTS="$ACCUM_OBJECTS $output_filename"
+ fi
+ # Now we know enough, lets try a compilation...
+ MPATH=`cygpath -m $x`
+ if [ $DEPENDING = true ]; then
+ output_flag="-MM"
+ elif [ $PREPROCESSING = true ]; then
+ output_flag="-E"
+ else
+ output_flag="-c -o `cygpath -m ${output_filename}`"
+ fi
+ params="$COMMON_CFLAGS $DEBUG_FLAGS $OPTIMIZE_FLAGS \
+ $CMD ${output_flag} $MPATH"
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo cc.sh "$SAVE" >>$CC_SH_DEBUG_LOG
+ echo $MINGW_EXE_PATH/gcc $params >>$CC_SH_DEBUG_LOG
+ fi
+ eval $MINGW_EXE_PATH/gcc $params >$MSG_FILE 2>$ERR_FILE
+ RES=$?
+ if [ $PREPROCESSING = false -a $DEPENDING = false ]; then
+ cat $ERR_FILE >&2
+ cat $MSG_FILE
+ else
+ cat $ERR_FILE >&2
+ if [ $DEPENDING = true ]; then
+ cat $MSG_FILE | sed 's|\([a-z]\):/|/cygdrive/\1/|g'
+ else
+ cat $MSG_FILE
+ fi
+ fi
+ rm -f $ERR_FILE $MSG_FILE
+ if [ $RES != 0 ]; then
+ rm -rf $TMPOBJDIR
+ exit $RES
+ fi
+done
+
+#If we got here, we succeeded in compiling (if there were anything to compile)
+#The output filename should name an executable if we're linking
+if [ $LINKING = true ]; then
+ case $OUTFILE in
+ "")
+ # Use the first source name to name the executable
+ first_source=""
+ for x in $SOURCES; do first_source=$x; break; done;
+ if [ -n "$first_source" ]; then
+ e=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.exe,'`;
+ out_spec="-o $e";
+ else
+ out_spec="";
+ fi;;
+ *)
+ out_spec="-o $OUTFILE";;
+ esac
+ # Descide which standard library to link against
+ case $MD in
+ -ML)
+ stdlib="-lLIBC";;
+ -MLd)
+ stdlib="-lLIBCD";;
+ -MD)
+ stdlib="-lMSVCRT";;
+ -MDd)
+ stdlib="-lMSVCRTD";;
+ -MT)
+ stdlib="-lLIBCMT";;
+ -MTd)
+ stdlib="-lLIBMTD";;
+ esac
+ # And finally call the next script to do the linking...
+ params="$out_spec $LINKCMD $stdlib"
+ eval ld.sh $ACCUM_OBJECTS $params
+ RES=$?
+fi
+rm -rf $TMPOBJDIR
+
+exit $RES
diff --git a/erts/etc/win32/cygwin_tools/mingw/coffix.c b/erts/etc/win32/cygwin_tools/mingw/coffix.c
new file mode 100644
index 0000000000..5dff030a69
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/mingw/coffix.c
@@ -0,0 +1,161 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2006-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+** This mini tool fixes an incompatibility between
+** Microsoft's tools, who dont like the virtual size being put in
+** the physical address field, but rely on the raw size field for
+** sizing the ".bss" section.
+** This fixes some of the problems with linking gcc compiled objects
+** together with MSVC dito.
+**
+** Courtesy DJ Delorie for describing the COFF file format on
+** http://www.delorie.com/djgpp/doc/coff/
+** The coff structures are fetched from Microsofts headers though.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <windows.h>
+#include <winnt.h> /* Structure definitions for PE (COFF) */
+
+static int dump_edit(char *filename, int edit);
+static int v_printf(char *format, ...);
+
+
+char *progname;
+int verbouse = 0;
+
+int main(int argc, char **argv)
+{
+ int findex = 1;
+ int edit = 0;
+ int ret;
+
+ progname = argv[0];
+ if (argc == 1) {
+ fprintf(stderr,"Format : %s [-e] [-v] <filename>\n", progname);
+ return 1;
+ }
+ for (findex = 1;
+ findex < argc && (*argv[findex] == '-' || *argv[findex] == '/');
+ ++findex)
+ switch (argv[findex][1]) {
+ case 'e':
+ case 'E':
+ edit = 1;
+ break;
+ case 'v':
+ case 'V':
+ verbouse = 1;
+ default:
+ fprintf(stderr, "%s: unknown option %s\n", progname, argv[findex]);
+ break;
+ }
+ if (findex == argc) {
+ fprintf(stderr,"%s: No filenames given.\n", progname);
+ return 1;
+ }
+ for(; findex < argc; ++findex)
+ if ((ret = dump_edit(argv[findex],edit)) != 0)
+ return ret;
+ return 0;
+}
+
+int dump_edit(char *filename, int edit)
+{
+ FILE *f = fopen(filename, (edit) ? "r+b" : "rb");
+ IMAGE_FILE_HEADER filhdr;
+ IMAGE_SECTION_HEADER scnhdr;
+ int i;
+
+ if (f == NULL) {
+ fprintf(stderr, "%s: cannot open %s.\n", progname, filename);
+ return 1;
+ }
+
+ if (fread(&filhdr, sizeof(filhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read COFF header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("File: %s\n", filename);
+ v_printf("Magic number: 0x%08x\n", filhdr.Machine);
+ v_printf("Number of sections: %d\n",filhdr.NumberOfSections);
+
+ if (fseek(f, (long) filhdr.SizeOfOptionalHeader, SEEK_CUR) != 0) {
+ fprintf(stderr,"%s: Could not read COFF optional header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+
+ for (i = 0; i < filhdr.NumberOfSections; ++i) {
+ if (fread(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read section header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("Section %s:\n", scnhdr.Name);
+ v_printf("Physical address: 0x%08x\n", scnhdr.Misc.PhysicalAddress);
+ v_printf("Size: 0x%08x\n", scnhdr.SizeOfRawData);
+ if (scnhdr.Misc.PhysicalAddress != 0 &&
+ scnhdr.SizeOfRawData == 0) {
+ printf("Section header %s in file %s will confuse MSC linker, "
+ "virtual size is 0x%08x and raw size is 0\n",
+ scnhdr.Name, filename, scnhdr.Misc.PhysicalAddress,
+ scnhdr.SizeOfRawData);
+ if (edit) {
+ scnhdr.SizeOfRawData = scnhdr.Misc.PhysicalAddress;
+ scnhdr.Misc.PhysicalAddress = 0;
+ if (fseek(f, (long) -((long)sizeof(scnhdr)), SEEK_CUR) != 0 ||
+ fwrite(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: could not edit file %s.\n",
+ progname, filename);
+ fclose(f);
+ return 1;
+ }
+ printf("Edited object, virtual size is now 0, and "
+ "raw size is 0x%08x.\n", scnhdr.SizeOfRawData);
+ } else {
+ printf("Specify option '-e' to correct the problem.\n");
+ }
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+
+static int v_printf(char *format, ...)
+{
+ va_list ap;
+ int ret = 0;
+ if (verbouse) {
+ va_start(ap, format);
+ ret = vfprintf(stdout, format, ap);
+ va_end(ap);
+ }
+ return ret;
+}
+
diff --git a/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh
new file mode 100755
index 0000000000..f3865c8cae
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/mingw/emu_cc.sh
@@ -0,0 +1,90 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+TOOLDIR=$ERL_TOP/erts/etc/win32/cygwin_tools/mingw
+COFFIX=$TOOLDIR/coffix
+WTOOLDIR=`(cygpath -d $TOOLDIR 2>/dev/null || cygpath -w $TOOLDIR)`
+
+# Do primitive 'make'
+newer_exe=`find $TOOLDIR -newer $COFFIX.c -name coffix.exe -print`
+if [ -z $newer_exe ]; then
+ echo recompiling $COFFIX.exe
+ cl.exe -Fe${WTOOLDIR}\\coffix.exe ${WTOOLDIR}\\coffix.c
+ rm -f $COFFIX.obj coffix.obj $COFFIX.pdb coffix.pdb
+fi
+
+# Try to find out the output filename and remove it from command line
+CMD=""
+OUTFILE=""
+INFILE=""
+SKIP_COFFIX=false
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o/*)
+ OUTFILE=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;;
+ -o)
+ shift
+ OUTFILE=$1;;
+ -MM)
+ SKIP_COFFIX=true
+ CMD="$CMD \"$x\"";;
+ *.c)
+ INFILE="$INFILE $x";
+ CMD="$CMD \"$x\"";;
+ *)
+ CMD="$CMD \"$x\"";;
+ esac
+ shift
+done
+if [ -z "$INFILE" ]; then
+ echo 'emu_cc.sh: please give an input filename for the compiler' >&2
+ exit 1
+fi
+if [ -z "$OUTFILE" ]; then
+ OUTFILE=`echo $INFILE | sed 's,\.c$,.o,'`
+fi
+
+if [ $SKIP_COFFIX = false ]; then
+ n=`echo $INFILE | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "emu_cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ fi
+ TEMPFILE=/tmp/tmp_emu_cc$$.o
+ if [ "X$EMU_CC_SH_DEBUG_LOG" != "X" ]; then
+ echo "gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD" >> $EMU_CC_SH_DEBUG_LOG 2>&1
+ fi
+ eval gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD
+ RES=$?
+ if [ $RES = 0 ]; then
+ $COFFIX.exe -e `(cygpath -d $TEMPFILE 2>/dev/null || cygpath -w $TEMPFILE)`
+ RES=$?
+ if [ $RES = 0 ]; then
+ cp $TEMPFILE $OUTFILE
+ else
+ echo "emu_cc.sh: fatal: coffix failed!" >&2
+ fi
+ fi
+ rm -f $TEMPFILE
+ exit $RES
+else
+ eval gcc -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD 2>/dev/null
+ exit $?
+fi
diff --git a/erts/etc/win32/cygwin_tools/mingw/ld.sh b/erts/etc/win32/cygwin_tools/mingw/ld.sh
new file mode 100755
index 0000000000..145bd2fad9
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/mingw/ld.sh
@@ -0,0 +1,147 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+kernel_libs="-lkernel32 -ladvapi32"
+gdi_libs="-lgdi32 -luser32 -lcomctl32 -lcomdlg32 -lshell32"
+DEFAULT_LIBRARIES="$kernel_libs $gdi_libs"
+
+CMD=""
+STDLIB=-lmsvcrt
+DEBUG_BUILD=false
+STDLIB_FORCED=false
+BUILD_DLL=false
+OUTPUT_FILENAME=""
+
+if [ -z "$MINGW_EXE_PATH" ]; then
+ echo "You have to set MINGW_EXE_PATH to run cc.sh" >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -dll| -DLL | -shared)
+ BUILD_DLL=true;;
+ -L/*|-L.*)
+ y=`echo $x | sed 's,^-L\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -L \"$MPATH\"";;
+ -lMSVCRT|-lmsvcrt)
+ STDLIB_FORCED=true;
+ STDLIB=-lmsvcrt;;
+ -lMSVCRTD|-lmsvcrtd)
+ STDLIB_FORCED=true;
+ STDLIB=-lmsvcrtd;;
+ -lLIBCMT|-llibcmt)
+ STDLIB_FORCED=true;
+ STDLIB=-llibcmt;;
+ -lLIBCMTD|-llibcmtd)
+ STDLIB_FORCED=true;
+ STDLIB=-llibcmtd;;
+ -lsocket)
+ DEFAULT_LIBRARIES="$DEFAULT_LIBRARIES -lws2_32";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -l\"${MPATH}\"";;
+ -g)
+ DEBUG_BUILD=true;;
+ -pdb:none|-incremental:no)
+ ;;
+ -implib:*)
+ y=`echo $x | sed 's,^-implib:\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -Xlinker --out-implib -Xlinker \"${MPATH}\"";;
+ -entry:*)
+ y=`echo $x | sed 's,^-entry:\(.*\),\1,g'`;
+ CMD="$CMD -Xlinker --entry -Xlinker _$y";;
+ -def:*)
+ ;;
+ ## Ignore -def: for now as ld.sh core dumps...
+ # y=`echo $x | sed 's,^-def:\(.*\),\1,g'`;
+ # MPATH=`cygpath -m $y`;
+ # CMD="$CMD -Xlinker --output-def -Xlinker \"${MPATH}\"";;
+ -o)
+ shift
+ MPATH=`cygpath -m $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ /*)
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+if [ $DEBUG_BUILD = true ]; then
+ linktype="-g"
+ if [ $STDLIB_FORCED = false ]; then
+ STDLIB=-lmsvcrt #d?
+ fi
+else
+ linktype=
+fi
+
+if [ $BUILD_DLL = true ];then
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ echo "Warning, output set to .exe when building DLL" >&2
+ CMD="-shared -o \"$OUTPUT_FILENAME\" $CMD";;
+ *.dll|*.DLL)
+ CMD="-shared -o \"$OUTPUT_FILENAME\" $CMD";;
+ "")
+ CMD="-shared -o \"a.dll\" $CMD";;
+ *)
+ CMD="-shared -o \"${OUTPUT_FILENAME}.dll\" $CMD";;
+ esac
+else
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ CMD="-o \"$OUTPUT_FILENAME\" $CMD";;
+ *.dll|*.DLL)
+ echo "Warning, output set to .dll when building EXE" >&2
+ CMD="-o \"$OUTPUT_FILENAME\" $CMD";;
+ "")
+ CMD="-o \"a.exe\" $CMD";;
+ *)
+ CMD="-o \"${OUTPUT_FILENAME}.exe\" $CMD";;
+ esac
+fi
+
+p=$$
+CMD="$linktype $CMD $STDLIB $DEFAULT_LIBRARIES"
+if [ "X$LD_SH_DEBUG_LOG" != "X" ]; then
+ echo ld.sh "$SAVE" >>$LD_SH_DEBUG_LOG
+ echo $MINGW_EXE_PATH/gcc.exe $CMD >>$LD_SH_DEBUG_LOG
+fi
+eval $MINGW_EXE_PATH/gcc "$CMD" >/tmp/link.exe.${p}.1 2>/tmp/link.exe.${p}.2
+RES=$?
+#tail +2 /tmp/link.exe.${p}.2 >&2
+cat /tmp/link.exe.${p}.2 >&2
+cat /tmp/link.exe.${p}.1
+rm -f /tmp/link.exe.${p}.2 /tmp/link.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/cygwin_tools/mingw/mc.sh b/erts/etc/win32/cygwin_tools/mingw/mc.sh
new file mode 100755
index 0000000000..873149172a
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/mingw/mc.sh
@@ -0,0 +1,89 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_DIRNAME=""
+
+# Find the correct mc.exe. This could be done by the configure script,
+# But as we seldom use the resource compiler, it might as well be done here...
+if [ -z "$WINE_EXE_PATH" ]; then
+ echo "You have to set MINGW_EXE_PATH to run cc.sh" >&2
+ exit 1
+fi
+
+MCC="$WINE_EXE_PATH/wmc.exe -i"
+RCC="$WINE_EXE_PATH/wrc.exe -i"
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ OUTPUT_DIRNAME="$1";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ OUTPUT_DIRNAME="$y";;
+ -I)
+ shift
+ MPATH=`cygpath -m $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ *)
+ MPATH=`cygpath -m -a $x`;
+ INFILE=$MPATH;;
+ #CMD="$CMD \"$MPATH\"";;
+ esac
+ shift
+done
+p=$$
+if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then
+ echo mc.sh "$SAVE" >>$MC_SH_DEBUG_LOG
+ echo $MCC $INFILE $CMD >>$MC_SH_DEBUG_LOG
+fi
+if [ -n "$OUTPUT_DIRNAME" ]; then
+ cd $OUTPUT_DIRNAME
+ RES=$?
+ if [ "$RES" != "0" ]; then
+ echo "mc.sh: Error: could not cd to $OUTPUT_DIRNAME">&2
+ exit $RES
+ fi
+fi
+eval $MCC "$INFILE" "$CMD" >/tmp/mc.exe.${p}.1 2>/tmp/mc.exe.${p}.2
+RES=$?
+cat /tmp/mc.exe.${p}.2 >&2
+cat /tmp/mc.exe.${p}.1
+rm -f /tmp/mc.exe.${p}.2 /tmp/mc.exe.${p}.1
+if [ $RES -eq 0 ]; then
+ XINFILE=`echo $INFILE | sed 's,.*/\([^/]*\),\1,' | sed 's,.mc$,.rc,'`
+ if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then
+ echo $RCC $XINFILE $CMD >>$MC_SH_DEBUG_LOG
+ fi
+ eval $RCC $XINFILE "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2
+ RES=$?
+ cat /tmp/rc.exe.${p}.2 >&2
+ cat /tmp/rc.exe.${p}.1
+ rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1
+fi
+exit $RES
diff --git a/erts/etc/win32/cygwin_tools/mingw/rc.sh b/erts/etc/win32/cygwin_tools/mingw/rc.sh
new file mode 100755
index 0000000000..37296f9e9f
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/mingw/rc.sh
@@ -0,0 +1,94 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2006-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_FILENAME=""
+
+if [ -z "$MINGW_EXE_PATH" ]; then
+ echo "You have to set MINGW_EXE_PATH to run rc.sh" >&2
+ exit 1
+fi
+
+
+# # Find the correct rc.exe. This could be done by the configure script,
+# # But as we seldom use the resource compiler, it might as well be done here...
+# RCC=""
+# save_ifs=$IFS
+# IFS=:
+# for p in $PATH; do
+# if [ -f $p/windres.exe ]; then
+# if [ -n "`$p/windres.exe --version 2>&1 | grep -i "GNU windres"`" ]; then
+# RCC=$p/windres.exe
+# fi
+# fi
+# done
+# IFS=$save_ifs
+
+RCC=$MINGW_EXE_PATH/windres.exe
+
+if [ -z "$RCC" ]; then
+ echo 'windres.exe not found!' >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ MPATH=`cygpath -m $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ -I)
+ shift
+ MPATH=`cygpath -m $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ /*)
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+p=$$
+if [ -n "$OUTPUT_FILENAME" ]; then
+ CMD="-o $OUTPUT_FILENAME $CMD"
+fi
+if [ "X$RC_SH_DEBUG_LOG" != "X" ]; then
+ echo rc.sh "$SAVE" >>$RC_SH_DEBUG_LOG
+ echo windres.exe $CMD >>$RC_SH_DEBUG_LOG
+fi
+eval $RCC "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2
+RES=$?
+cat /tmp/rc.exe.${p}.2 >&2
+cat /tmp/rc.exe.${p}.1
+rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/cygwin_tools/vc/ar.sh b/erts/etc/win32/cygwin_tools/vc/ar.sh
new file mode 100755
index 0000000000..24d275b01a
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/ar.sh
@@ -0,0 +1,47 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+CMD=""
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -out:)
+ shift
+ case "$1" in
+ /*)
+ MPATH=`cygpath -m $1`;;
+ *)
+ MPATH=$1;;
+ esac
+ CMD="$CMD -out:\"$MPATH\"";;
+ -out:/*)
+ y=`echo $x | sed 's,^-out:\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -out:\"$MPATH\"";;
+ /*)
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+
+eval lib.exe $CMD
diff --git a/erts/etc/win32/cygwin_tools/vc/cc.sh b/erts/etc/win32/cygwin_tools/vc/cc.sh
new file mode 100755
index 0000000000..4939465d08
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/cc.sh
@@ -0,0 +1,321 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Icky cl wrapper that does it's best to behave like a Unixish cc.
+# Made to work for Erlang builds and to make configure happy, not really
+# general I suspect.
+# set -x
+# Save the command line for debug outputs
+SAVE="$@"
+
+# Constants
+COMMON_CFLAGS="-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -D_CRT_SECURE_NO_DEPRECATE"
+
+# Variables
+# The stdout and stderr for the compiler
+MSG_FILE=/tmp/cl.exe.$$.1
+ERR_FILE=/tmp/cl.exe.$$.2
+
+# "Booleans" determined during "command line parsing"
+# If the stdlib option is explicitly passed to this program
+MD_FORCED=false
+# If we're preprocession (only) i.e. -E
+PREPROCESSING=false
+# If we're generating dependencies (implies preprocesing)
+DEPENDENCIES=false
+# If this is supposed to be a debug build
+DEBUG_BUILD=false
+# If this is supposed to be an optimized build (there can only be one...)
+OPTIMIZED_BUILD=false
+# If we're linking or only compiling
+LINKING=true
+
+# This data is accumulated during command line "parsing"
+# The stdlibrary option, default multithreaded dynamic
+MD=-MD
+# Flags for debug compilation
+DEBUG_FLAGS=""
+# Flags for optimization
+OPTIMIZE_FLAGS=""
+# The specified output filename (if any), may be either object or exe.
+OUTFILE=""
+# Unspecified command line options for the compiler
+CMD=""
+# All the c source files, in unix style
+SOURCES=""
+# All the options to pass to the linker, kept in Unix style
+LINKCMD=""
+
+
+# Loop through the parameters and set the above variables accordingly
+# Also convert some cygwin filenames to "mixed style" dito (understood by the
+# compiler very well), except for anything passed to the linker, that script
+# handles those and the sources, which are also kept unixish for now
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -Wall)
+ ;;
+ -c)
+ LINKING=false;;
+ #CMD="$CMD -c";;
+ -MM)
+ PREPROCESSING=true;
+ LINKING=false;
+ DEPENDENCIES=true;;
+ -E)
+ PREPROCESSING=true;
+ LINKING=false;; # Obviously...
+ #CMD="$CMD -E";;
+ -Owx)
+ # Optimization hardcoded of wxErlang, needs to disable debugging too
+ OPTIMIZE_FLAGS="-Ob2ity -Gs -Zi";
+ DEBUG_FLAGS="";
+ DEBUG_BUILD=false;
+ if [ $MD_FORCED = false ]; then
+ MD=-MD;
+ fi
+ OPTIMIZED_BUILD=true;;
+ -O*)
+ # Optimization hardcoded, needs to disable debugging too
+ OPTIMIZE_FLAGS="-Ox -Zi";
+ DEBUG_FLAGS="";
+ DEBUG_BUILD=false;
+ if [ $MD_FORCED = false ]; then
+ MD=-MD;
+ fi
+ OPTIMIZED_BUILD=true;;
+ -g|-ggdb)
+ if [ $OPTIMIZED_BUILD = false ];then
+ # Hardcoded windows debug flags
+ DEBUG_FLAGS="-Z7";
+ if [ $MD_FORCED = false ]; then
+ MD=-MDd;
+ fi
+ LINKCMD="$LINKCMD -g";
+ DEBUG_BUILD=true;
+ fi;;
+ # Allow forcing of stdlib
+ -mt|-MT)
+ MD="-MT";
+ MD_FORCED=true;;
+ -md|-MD)
+ MD="-MD";
+ MD_FORCED=true;;
+ -ml|-ML)
+ MD="-ML";
+ MD_FORCED=true;;
+ -mdd|-MDD|-MDd)
+ MD="-MDd";
+ MD_FORCED=true;;
+ -mtd|-MTD|-MTd)
+ MD="-MTd";
+ MD_FORCED=true;;
+ -mld|-MLD|-MLd)
+ MD="-MLd";
+ MD_FORCED=true;;
+ -o)
+ shift;
+ OUTFILE="$1";;
+ -o*)
+ y=`echo $x | sed 's,^-[Io]\(.*\),\1,g'`;
+ OUTFILE="$y";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ z=`echo $x | sed 's,^-\([Io]\)\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -$z\"$MPATH\"";;
+ -I*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -D*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -EH*)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD $y";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ LINKCMD="$LINKCMD $x";;
+ /*.c)
+ SOURCES="$SOURCES $x";;
+ *.c)
+ SOURCES="$SOURCES $x";;
+ /*.cc)
+ SOURCES="$SOURCES $x";;
+ *.cc)
+ SOURCES="$SOURCES $x";;
+ /*.cpp)
+ SOURCES="$SOURCES $x";;
+ *.cpp)
+ SOURCES="$SOURCES $x";;
+ /*.o)
+ LINKCMD="$LINKCMD $x";;
+ *.o)
+ LINKCMD="$LINKCMD $x";;
+ *)
+ # Try to quote uninterpreted options
+ y=`echo $x | sed 's,",\\\",g'`;
+ LINKCMD="$LINKCMD $y";;
+ esac
+ shift
+done
+
+#Return code from compiler, linker.sh and finally this script...
+RES=0
+
+# Accumulated object names
+ACCUM_OBJECTS=""
+
+# A temporary object file location
+TMPOBJDIR=/tmp/tmpobj$$
+mkdir $TMPOBJDIR
+
+# Compile
+for x in $SOURCES; do
+ start_time=`date '+%s'`
+ # Compile each source
+ if [ $LINKING = false ]; then
+ # We should have an output defined, which is a directory
+ # or an object file
+ case $OUTFILE in
+ /*.o)
+ # Simple output, SOURCES should be one single
+ n=`echo $SOURCES | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ else
+ output_filename=`cygpath -m $OUTFILE`;
+ fi;;
+ *.o)
+ # Relative path needs no translation
+ n=`echo $SOURCES | wc -w`
+ if [ $n -gt 1 ]; then
+ echo "cc.sh:Error, multiple sources, one object output."
+ exit 1
+ else
+ output_filename=$OUTFILE
+ fi;;
+ /*)
+ # Absolute directory
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=`cygpath -m $OUTFILE`
+ output_filename="$output_filename/${o}";;
+ *)
+ # Relative_directory or empty string (.//x.o is valid)
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.cp*$,.o,'`
+ output_filename="./${OUTFILE}/${o}";;
+ esac
+ else
+ # We are linking, which means we build objects in a temporary
+ # directory and link from there. We should retain the basename
+ # of each source to make examining the exe easier...
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.o,'`
+ output_filename=$TMPOBJDIR/$o
+ ACCUM_OBJECTS="$ACCUM_OBJECTS $output_filename"
+ fi
+ # Now we know enough, lets try a compilation...
+ MPATH=`cygpath -m $x`
+ if [ $PREPROCESSING = true ]; then
+ output_flag="-E"
+ else
+ output_flag="-c -Fo`cygpath -m ${output_filename}`"
+ fi
+ params="$COMMON_CFLAGS $MD $DEBUG_FLAGS $OPTIMIZE_FLAGS \
+ $CMD ${output_flag} $MPATH"
+ if [ "X$CC_SH_DEBUG_LOG" != "X" ]; then
+ echo cc.sh "$SAVE" >>$CC_SH_DEBUG_LOG
+ echo cl.exe $params >>$CC_SH_DEBUG_LOG
+ fi
+ eval cl.exe $params >$MSG_FILE 2>$ERR_FILE
+ RES=$?
+ if test $PREPROCESSING = false; then
+ cat $ERR_FILE >&2
+ tail -n +2 $MSG_FILE
+ else
+ tail -n +2 $ERR_FILE >&2
+ if test $DEPENDENCIES = true; then
+ if test `grep -v $x $MSG_FILE | grep -c '#line'` != "0"; then
+ o=`echo $x | sed 's,.*/,,' | sed 's,\.cp*$,.o,'`
+ echo -n $o':'
+ # Some versions of cygpath does not read paths linewise
+ # but uses space as separator, why pathnames containing
+ # spaces need to be removed. To avoid different
+ # behaviours in different versions of cygwin, we would need to
+ # write our own cygpath replacement, but this will have to do
+ # for now...
+ cat $MSG_FILE | grep '#line' | grep -v $x | awk -F\" '{printf("%s\n",$2)}' | sort -u | grep -v " " | cygpath -f - -m -s | cygpath -f - | awk '{printf("\\\n %s ",$0)}'
+ echo
+ echo
+ after_sed=`date '+%s'`
+ echo Made dependencises for $x':' `expr $after_sed '-' $start_time` 's' >&2
+ fi
+ else
+ cat $MSG_FILE
+ fi
+ fi
+ rm -f $ERR_FILE $MSG_FILE
+ if [ $RES != 0 ]; then
+ rm -rf $TMPOBJDIR
+ exit $RES
+ fi
+done
+
+# If we got here, we succeeded in compiling (if there were anything to compile)
+# The output filename should name an executable if we're linking
+if [ $LINKING = true ]; then
+ case $OUTFILE in
+ "")
+ # Use the first source name to name the executable
+ first_source=""
+ for x in $SOURCES; do first_source=$x; break; done;
+ if [ -n "$first_source" ]; then
+ e=`echo $x | sed 's,.*/,,' | sed 's,\.c$,.exe,'`;
+ out_spec="-o $e";
+ else
+ out_spec="";
+ fi;;
+ *)
+ out_spec="-o $OUTFILE";;
+ esac
+ # Descide which standard library to link against
+ case $MD in
+ -ML)
+ stdlib="-lLIBC";;
+ -MLd)
+ stdlib="-lLIBCD";;
+ -MD)
+ stdlib="-lMSVCRT";;
+ -MDd)
+ stdlib="-lMSVCRTD";;
+ -MT)
+ stdlib="-lLIBCMT";;
+ -MTd)
+ stdlib="-lLIBMTD";;
+ esac
+ # And finally call the next script to do the linking...
+ params="$out_spec $LINKCMD $stdlib"
+ eval ld.sh $ACCUM_OBJECTS $params
+ RES=$?
+fi
+rm -rf $TMPOBJDIR
+
+exit $RES
diff --git a/erts/etc/win32/cygwin_tools/vc/cc_wrap.c b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c
new file mode 100644
index 0000000000..18ecc31c17
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/cc_wrap.c
@@ -0,0 +1,864 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/cygwin.h>
+
+
+
+#ifdef CCP_POSIX_TO_WIN_A
+#define NEW_CYGPATH_INTERFACE
+#endif
+
+#ifdef NEW_CYGPATH_INTERFACE
+#define GET_WIN32_SIZE(Posix) \
+cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), NULL, 0)
+#define CONVERT_TO_WIN32(Posix,Win32,Size) \
+cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), \
+ (Win32), (Size))
+#else
+#define GET_WIN32_SIZE(Posix) PATH_MAX
+#define CONVERT_TO_WIN32(Posix,Win32,Size) \
+((cygwin32_conv_to_full_win32_path((Posix),(Win32)) >= 0) ? 0 : -1)
+#endif
+
+/*#define HARDDEBUG 1*/
+
+#ifdef HARDDEBUG
+#define DEBUGF(X) printf X
+#else
+#define DEBUGF(X) /* noop */
+#endif
+char *tmpobjdir = "";
+
+char *add_to(char *src,char *add) {
+ int len = strlen(src)+strlen(add)+1;
+ char *n;
+
+ if (strlen(src) == 0) {
+ n = malloc(len);
+ strcpy(n,add);
+ return n;
+ }
+ n = realloc(src,len);
+ strcat(n,add);
+ return n;
+}
+
+void maybe_cleanup(void)
+{
+ DIR *dir;
+ struct dirent *dent;
+ if (*tmpobjdir == '\0') {
+ return;
+ }
+ if (!(dir = opendir(tmpobjdir))) {
+ return;
+ }
+ while((dent = readdir(dir)) != NULL) {
+ char *fullname = add_to("",tmpobjdir);
+ fullname = add_to(fullname,"/");
+ fullname = add_to(fullname,dent->d_name);
+ unlink(fullname);
+ free(fullname);
+ }
+ closedir(dir);
+ rmdir(tmpobjdir);
+}
+
+
+
+
+void error(char *str)
+{
+ fprintf(stderr,"%s\n",str);
+ maybe_cleanup();
+ exit(1);
+}
+
+
+char **add_to_src(char **srcarr, char *add)
+{
+ int num;
+ if (srcarr == NULL) {
+ srcarr = malloc(sizeof(char *)*2);
+ srcarr[0]=malloc(strlen(add)+1);
+ strcpy(srcarr[0],add);
+ srcarr[1] = NULL;
+ } else {
+ for(num = 0; srcarr[num] != NULL; ++num)
+ ;
+ num +=1;
+ srcarr = realloc(srcarr,sizeof(char *)*(num+1));
+ srcarr[num-1] = malloc(strlen(add)+1);
+ strcpy(srcarr[num-1],add);
+ srcarr[num] = NULL;
+ }
+ return srcarr;
+}
+
+
+
+char *object_name(char *source) {
+ char *tmp = add_to("",source);
+ int j = strlen(tmp)-2;
+ if (j < 0) {
+ j = 0;
+ }
+ while(j>0 && tmp[j] != '.') {
+ --j;
+ }
+ if (tmp[j] == '.') {
+ ++j;
+ }
+ tmp[j++] = 'o';
+ tmp[j] = '\0';
+ return tmp;
+}
+
+char *exe_name(char *source) {
+ char *tmp = add_to("",source);
+ int j = strlen(tmp)-2;
+ if (j < 0) {
+ j = 0;
+ }
+ while(j>0 && tmp[j] != '.') {
+ --j;
+ }
+ if (tmp[j] == '.') {
+ ++j;
+ }
+ tmp[j] = '\0';
+ return add_to(tmp,"exe");
+}
+
+char *dyn_get_short(char *longp)
+{
+ int size;
+ char *shortp;
+ size = GetShortPathName(longp,NULL,0);
+ if (size <= 0) {
+ return NULL;
+ }
+ shortp = malloc(size);
+ if (GetShortPathName(longp,shortp,size) != size - 1) {
+ free(shortp);
+ return NULL;
+ }
+ return shortp;
+}
+
+char *do_cyp(char *posix)
+{
+ ssize_t size;
+ char *win32;
+ size = GET_WIN32_SIZE(posix);
+ char *ret = NULL;
+ if (size < 0) {
+ fprintf(stderr,"Could not cygpath %s, errno = %d\n",
+ posix,errno);
+ } else {
+ win32 = (char *) malloc (size);
+ if (CONVERT_TO_WIN32(posix,
+ win32, size)) {
+ fprintf(stderr,"Could not cygpath %s, errno = %d\n",
+ posix,errno);
+ } else {
+ char *w32_short = dyn_get_short(win32);
+ DEBUGF(("win32 = %s, w32_short = %s\n",win32, (w32_short == NULL) ? "NULL" : w32_short));
+ if (w32_short == NULL) {
+ char *rest = malloc(size);
+ char *first = malloc(size);
+ int x = 0;
+ int y = strlen(win32) - 1;
+ strcpy(first,win32);
+ while (w32_short == NULL) {
+ while ( y > 0 && first[y] != '\\') {
+ rest[x++] = first[y--];
+ }
+ if (y > 0) {
+ rest[x++] = first[y];
+ first[y--] = '\0';
+ } else {
+ break;
+ }
+ w32_short = dyn_get_short(first);
+ DEBUGF(("first = %s, w32_short = %s\n",first, (w32_short == NULL) ? "NULL" : w32_short));
+ }
+ if (w32_short != NULL) {
+ y = strlen(w32_short);
+ w32_short = realloc(w32_short,y+1+x);
+ /* spool back */
+ while ( x > 0) {
+ w32_short[y++] = rest[--x];
+ }
+ w32_short[y] = '\0';
+ } else {
+ w32_short = malloc(strlen(win32)+1);
+ strcpy(w32_short,win32); /* last resort */
+ }
+ free(first);
+ free(rest);
+ }
+ ret = w32_short;
+ while (*ret) {
+ if (*ret == '\\') {
+ *ret = '/';
+ }
+ ++ret;
+ }
+ ret = w32_short;
+ }
+ free(win32);
+ }
+ return ret;
+}
+
+
+
+char *save = "";
+
+void save_args(int argc, char **argv)
+{
+ int i;
+ for(i = 0; i < argc; ++i) {
+ save = add_to(save,argv[i]);
+ save = add_to(save," ");
+ }
+}
+
+char *progname="cc_wrap";
+
+int my_create_pipe(HANDLE *read_p, HANDLE *write_p)
+{
+ char name_buff[1000];
+ SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
+ static int counter = 0;
+
+ ++counter;
+
+ sprintf(name_buff,"\\\\.\\pipe\\%s_%d_%d",progname,getpid(),counter);
+ sa.bInheritHandle = FALSE;
+ if ((*read_p = CreateNamedPipe(name_buff,
+ PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1,
+ 0,
+ 0,
+ 2000,
+ &sa)) == INVALID_HANDLE_VALUE ||
+ *read_p == NULL) {
+ return 0;
+ }
+ sa.bInheritHandle = TRUE;
+ if ((*write_p = CreateFile(name_buff,
+ GENERIC_WRITE,
+ 0, /* No sharing */
+ &sa,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL)) == INVALID_HANDLE_VALUE ||
+ *write_p == NULL) {
+ CloseHandle(*read_p);
+ return 0;
+ }
+ return 1;
+}
+
+void forwardenv(void)
+{
+ char *(envs[]) = {"LIB","INCLUDE","LIBPATH", "LD_SH_DEBUG_LOG", NULL};
+ char **p = envs;
+ while (*p != NULL) {
+ char *val = getenv(*p);
+ if (val != NULL) {
+ SetEnvironmentVariable(*p,val);
+ }
+ ++p;
+ }
+}
+
+HANDLE do_run(char *commandline, HANDLE *out, HANDLE *err)
+{
+ STARTUPINFO start;
+ HANDLE write_pipe_stdout = NULL, read_pipe_stdout = NULL;
+ HANDLE write_pipe_stderr = NULL, read_pipe_stderr = NULL;
+ SECURITY_ATTRIBUTES pipe_security;
+ SECURITY_ATTRIBUTES attr;
+ PROCESS_INFORMATION info;
+
+ memset(&start,0,sizeof(start));
+ memset(&pipe_security,0,sizeof(pipe_security));
+ memset(&attr,0,sizeof(attr));
+ memset(&info,0,sizeof(info));
+
+ pipe_security.nLength = sizeof(pipe_security);
+ pipe_security.lpSecurityDescriptor = NULL;
+ pipe_security.bInheritHandle = TRUE;
+
+ if(!my_create_pipe(&read_pipe_stdout,&write_pipe_stdout)){
+ error("Could not create stdout pipes!");
+ }
+ if(!my_create_pipe(&read_pipe_stderr,&write_pipe_stderr)){
+ error("Could not create stderr pipes!");
+ }
+ start.cb = sizeof (start);
+ start.dwFlags = STARTF_USESHOWWINDOW;
+ start.wShowWindow = SW_HIDE;
+ start.hStdOutput = write_pipe_stdout;
+ start.hStdError = write_pipe_stderr;
+ start.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start.dwFlags |= STARTF_USESTDHANDLES;
+
+ attr.nLength = sizeof(attr);
+ attr.lpSecurityDescriptor = NULL;
+ attr.bInheritHandle = TRUE;
+ forwardenv(); /* Cygwin and windows environment variables... sigh... */
+ if(!CreateProcess(NULL,
+ commandline,
+ &attr,
+ NULL,
+ TRUE,
+ CREATE_DEFAULT_ERROR_MODE,
+ NULL,
+ NULL,
+ &start,
+ &info)){
+ fprintf(stderr,"Could not run %s, last error: %d\n",commandline,GetLastError());
+ error("Could not create process");
+ }
+ *out = read_pipe_stdout;
+ *err = read_pipe_stderr;
+ CloseHandle(write_pipe_stdout);
+ CloseHandle(write_pipe_stderr);
+ return info.hProcess;
+}
+#define HANDLE_STDOUT 0
+#define HANDLE_STDERR 1
+#define HANDLE_PROC 2
+
+#ifdef HARDDEBUG
+char *prefix = "";
+#endif
+
+int handle_overlapped(HANDLE fd, OVERLAPPED *ovp, char *buffer,
+ int bufflen, int get_old, FILE *whereto, int *skip)
+{
+ DWORD res,read,err;
+ char *ptr;
+
+ DEBUGF(("In handle_overlapped(%d,0x%08x,0x%08x,%d,%d), prefix = %s\n",
+ fd,ovp,buffer,bufflen,get_old,prefix));
+ /* h�mta resultat av gamla f�rst */
+ if (get_old) {
+ res = GetOverlappedResult(fd,ovp,&read,TRUE);
+ DEBUGF(("read = %d, res = %d, GetLastError() = %d\n",read,res,GetLastError()));
+ if (!res) {
+ return 0;
+ }
+ buffer[read] = '\0';
+ ptr = buffer;
+ while(*skip && *ptr != '\0') {
+ if (*ptr == '\n') {
+ --(*skip);
+ }
+ ++ptr;
+ }
+ if(*ptr != '\0') {
+ fprintf(whereto,"%s",ptr);
+ }
+ }
+
+ ResetEvent(ovp->hEvent);
+
+ for(;;) {
+ res = ReadFile(fd,buffer,bufflen-1,&read,ovp);
+
+ if (!res) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ DEBUGF(("Error I/O Pending\n"));
+ return 1;
+ }
+ DEBUGF(("ReadFileFailed for %s, %d\n",prefix,err));
+ return 0;
+ }
+ buffer[read] = '\0';
+ ptr = buffer;
+ while(*skip && *ptr != '\0') {
+ if (*ptr == '\n') {
+ --(*skip);
+ }
+ ++ptr;
+ }
+ if(*ptr != '\0') {
+ fprintf(whereto,"%s",ptr);
+ }
+ }
+}
+
+
+int run(char *commandline,int skipout,int skiperr)
+{
+ HANDLE harr[3];
+ HANDLE real_stdout,real_stderr;
+ OVERLAPPED ov_out,ov_err;
+ char outbuff[1024],errbuff[1024];
+ DWORD ret,exitcode;
+ HANDLE wait[3];
+ int map[3];
+ DWORD nwait = 3;
+ int i,j;
+ unsigned living_handles = 0x7;
+
+ harr[HANDLE_STDOUT] = CreateEvent(NULL,
+ TRUE,
+ FALSE, /*not signalled */
+ NULL);
+ harr[HANDLE_STDERR] = CreateEvent(NULL,
+ TRUE,
+ FALSE,/*not signalled */
+ NULL);
+
+ memset(&ov_out,0,sizeof(ov_out));
+ memset(&ov_err,0,sizeof(ov_err));
+
+ ov_out.hEvent = harr[HANDLE_STDOUT];
+ ov_err.hEvent = harr[HANDLE_STDERR];
+
+ harr[HANDLE_PROC] = do_run(commandline,&real_stdout,&real_stderr);
+
+#ifdef HARDDEBUG
+ prefix = "STDOUT";
+#endif
+ handle_overlapped(real_stdout,&ov_out,outbuff,1024,0,stdout,&skipout);
+#ifdef HARDDEBUG
+ prefix = "STDERR";
+#endif
+ handle_overlapped(real_stderr,&ov_err,errbuff,1024,0,stderr,&skiperr);
+
+ for(;;) {
+ nwait = 0;
+ for(i=0;i<3;++i) {
+ if ((living_handles & (1U << i))) {
+ map[nwait] = i;
+ wait[nwait++] = harr[i];
+ }
+ }
+
+ ret = WaitForMultipleObjects(nwait,
+ wait,
+ FALSE,
+ INFINITE);
+ DEBUGF(("Wait returned %d\n",ret));
+
+ if (ret == WAIT_FAILED) {
+ error("Wait failed");
+ }
+
+ ret -= WAIT_OBJECT_0;
+
+ switch (map[ret]) {
+ case HANDLE_PROC:
+
+ DEBUGF(("Process died!\n"));
+ GetExitCodeProcess(harr[HANDLE_PROC],&exitcode);
+ if ((living_handles &= (~(1U<<HANDLE_PROC))) == 0) {
+ goto done;
+ }
+ --nwait;
+ break;
+ case HANDLE_STDOUT:
+#ifdef HARDDEBUG
+ prefix = "STDOUT";
+#endif
+ if (!handle_overlapped(real_stdout,&ov_out, outbuff,1024,1,stdout,&skipout)) {
+ if ((living_handles &= (~(1U<<HANDLE_STDOUT))) == 0) {
+ goto done;
+ }
+ }
+ break;
+ case HANDLE_STDERR:
+#ifdef HARDDEBUG
+ prefix = "STDERR";
+#endif
+ if (!handle_overlapped(real_stderr,&ov_err, errbuff,1024,1,stderr,&skiperr)){
+ if ((living_handles &= (~(1U<<HANDLE_STDERR))) == 0) {
+ goto done;
+ }
+ }
+ break;
+ default:
+ error("Unexpected wait result");
+ }
+ }
+ done:
+ CloseHandle(harr[HANDLE_PROC]);
+ CloseHandle(harr[HANDLE_STDOUT]);
+ CloseHandle(harr[HANDLE_STDERR]);
+ CloseHandle(real_stdout);
+ CloseHandle(real_stderr);
+ return (int) exitcode;
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ int x;
+ char *s;
+ char *mpath;
+ char *debuglog;
+ FILE *debugfile;
+
+ char *common_cflags="-nologo -D__WIN32__ -DWIN32 -DWINDOWS -D_WIN32 -DNT -D_CRT_SECURE_NO_DEPRECATE";
+ char *md = "-MD";
+ char *debug_flags = "";
+ char *optimize_flags = "";
+ char *outfile = "";
+ char *cmd = "";
+ char **sources = NULL;
+ char *accum_objects = "";
+ char *linkcmd = "";
+
+ int md_forced = 0;
+ int preprocessing = 0;
+ int debug_build = 0;
+ int optimized_build = 0;
+ int linking = 1;
+ int retval;
+
+ save_args(argc,argv);
+
+ for(i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-') {
+ char *opt = argv[i]+1;
+ switch(*opt) {
+ case 'W':
+ if(strcmp(opt,"Wall")) {
+ goto filename;
+ }
+ break;
+ case 'c':
+ if(strlen(opt) > 1) {
+ goto filename;
+ }
+ linking = 0;
+ break;
+ case 'E':
+ if(strlen(opt) > 1) {
+ if (opt[1] == 'H') {
+ cmd = add_to(cmd," ");
+ cmd = add_to(cmd,opt);
+ } else {
+ goto filename;
+ }
+ }
+ preprocessing = 1;
+ linking = 0;
+ break;
+ case 'O':
+ /* ignore what opt is requested, set hard */
+ optimize_flags = "-Ox -Zi";
+ debug_flags = "";
+ debug_build = 0;
+ if (!md_forced) {
+ md = "-MD";
+ }
+ optimized_build = 1;
+ break;
+ case 'g':
+ if (strcmp(opt,"g") && strcmp(opt,"ggdb")) {
+ goto filename;
+ }
+ if (!optimized_build) {
+ debug_flags = "-Z7";
+ if (!md_forced) {
+ md = "-MDd";
+ }
+ linkcmd = add_to(linkcmd," -g");
+ debug_build = 1;
+ }
+ break;
+ case 'm':
+ case 'M':
+ if(!strcmp(opt,"mt") || !strcmp(opt,"MT")) {
+ md = "-MT";
+ } else if (!strcmp(opt,"md") || !strcmp(opt,"MD")) {
+ md = "-MD";
+ } else if (!strcmp(opt,"ml") || !strcmp(opt,"ML")) {
+ md = "-ML";
+ } else if (!strcmp(opt,"mdd") || !strcmp(opt,"MDd") ||
+ !strcmp(opt,"MDD")) {
+ md = "-MDd";
+ } else if (!strcmp(opt,"mtd") || !strcmp(opt,"MTd") ||
+ !strcmp(opt,"MTD")) {
+ md = "-MTd";
+ } else if (!strcmp(opt,"mld") || !strcmp(opt,"MLd") ||
+ !strcmp(opt,"MLD")) {
+ md = "-MLd";
+ } else {
+ goto filename;
+ }
+ md_forced = 1;
+ break;
+ case 'o':
+ if (!strcmp(opt,"o")) {
+ ++i;
+ if (i >= argc) {
+ error("-o without filename");
+ }
+ outfile = argv[i];
+ } else {
+ outfile = opt+1;
+ }
+ break;
+ case 'I':
+ if(opt[1] == '/') {
+ mpath = do_cyp(opt+1);
+ cmd = add_to(cmd," -I\"");
+ cmd = add_to(cmd,mpath);
+ cmd = add_to(cmd,"\"");
+ free(mpath);
+ } else {
+ cmd = add_to(cmd," ");
+ cmd = add_to(cmd,opt);
+ }
+ break;
+ case 'D':
+ cmd = add_to(cmd," -");
+ cmd = add_to(cmd,opt);
+ case 'l':
+ linkcmd = add_to(linkcmd," -");
+ linkcmd = add_to(linkcmd,opt);
+ break;
+ default:
+ goto filename;
+ }
+ continue;
+ }
+ filename:
+ s = argv[i];
+ x = strlen(s);
+ if (x > 1 && s[x-1] == 'c' && s[x-2] == '.') {
+ /* C source */
+ sources = add_to_src(sources,s);
+ } else if (x > 3 && !strcmp(s + (x - 4),".cpp")) {
+ /* C++ */
+ sources = add_to_src(sources,s);
+ } else if (x > 1 && s[x-1] == 'o' && s[x-2] == '.') {
+ linkcmd = add_to(linkcmd," ");
+ linkcmd = add_to(linkcmd,s);
+ } else {
+ /* Pass rest to linker */
+ linkcmd = add_to(linkcmd," ");
+ linkcmd = add_to(linkcmd,s);
+ }
+ }
+ if ((debuglog = getenv("CC_SH_DEBUG_LOG")) != NULL) {
+ debugfile = fopen(debuglog,"wb+");
+ if (debugfile) {
+ fprintf(debugfile,"----------------\n");
+ }
+ } else {
+ debugfile = NULL;
+ }
+
+ tmpobjdir = add_to("","/tmp/tmpobj");
+ {
+ char pidstr[100];
+ pid_t pid = getpid();
+ sprintf(pidstr,"%d",pid);
+ tmpobjdir = add_to(tmpobjdir,pidstr);
+ }
+ mkdir(tmpobjdir,0777);
+ if (sources != NULL) {
+ char *output_filename;
+ char *output_flag;
+ char *params;
+ for (i=0;sources[i] != NULL; ++i) {
+ if (!linking) {
+ int x = strlen(outfile);
+ if (x > 1 && outfile[x-1] == 'o' && outfile[x-2] == '.') {
+ if (*outfile != '/') {
+ /* non absolute object */
+ if (i > 0) {
+ error("Single object multiple sources");
+ }
+ output_filename = add_to("",outfile);
+ } else {
+ if (i > 0) {
+ error("Single object multiple sources");
+ }
+ output_filename = do_cyp(outfile);
+ }
+ } else {
+ char *tmp = object_name(sources[i]);
+
+ /*fprintf(stderr,"sources[i] = %s\ntmp = %s\n",
+ sources[i],tmp);*/
+
+ if (!x || outfile[0] != '/') {
+ /* non absolute directory */
+ output_filename = add_to("",outfile);
+ } else {
+ output_filename = do_cyp(outfile);
+ }
+ /*fprintf(stderr,"output_filename = %s\n",output_filename);*/
+ if (*output_filename != '\0') {
+ output_filename = add_to(output_filename,"/");
+ }
+ output_filename = add_to(output_filename,tmp);
+ free(tmp);
+ }
+ } else {
+ char *tmp = object_name(sources[i]);
+ output_filename = add_to("",tmpobjdir);
+ output_filename = add_to(output_filename,"/");
+ output_filename = add_to(output_filename,tmp);
+ accum_objects = add_to(accum_objects," ");
+ accum_objects = add_to(accum_objects,output_filename);
+ /* reform to dos path */
+ free(output_filename);
+ output_filename = do_cyp(tmpobjdir);
+ output_filename = add_to(output_filename,"/");
+ output_filename = add_to(output_filename,tmp);
+ }
+ mpath = do_cyp(sources[i]);
+ if (preprocessing) {
+ output_flag = add_to("","-E");
+ } else {
+ output_flag = add_to("","-c -Fo");
+ output_flag = add_to(output_flag,output_filename);
+ }
+ params = add_to("","cl.exe ");
+ params = add_to(params,common_cflags);
+ params = add_to(params," ");
+ params = add_to(params,md);
+ params = add_to(params," ");
+ params = add_to(params,debug_flags);
+ params = add_to(params," ");
+ params = add_to(params,optimize_flags);
+ params = add_to(params," ");
+ params = add_to(params,cmd);
+ params = add_to(params," ");
+ params = add_to(params,output_flag);
+ params = add_to(params," ");
+ params = add_to(params,mpath);
+ free(output_filename);
+ free(output_flag);
+ free(mpath);
+
+ if (debugfile) {
+ fprintf(debugfile,"%s\n",save);
+ fprintf(debugfile,"%s\n",params);
+ }
+ if (preprocessing) {
+ retval = run(params,0,1);
+ } else {
+ retval = run(params,1,0);
+ }
+ if (retval != 0) {
+ maybe_cleanup();
+ return retval;
+ }
+ free(params);
+ }
+ }
+ if (linking) {
+ char *out_spec;
+ char *stdlib;
+ char *params;
+ if (strlen(outfile) == 0) {
+ if (sources != NULL && sources[0] != NULL) {
+ char *tmp = exe_name(sources[0]);
+ out_spec = add_to("","-o ");
+ out_spec = add_to(out_spec,tmp);
+ free(tmp);
+ } else {
+ out_spec = add_to("","");
+ }
+ } else {
+ out_spec = add_to("","-o ");
+ out_spec = add_to(out_spec,outfile);
+ }
+ if (!strcmp(md,"-ML")) {
+ stdlib="-lLIBC";
+ } else if (!strcmp(md,"-MLd")) {
+ stdlib="-lLIBCD";
+ } else if (!strcmp(md,"-MD")) {
+ stdlib="-lMSVCRT";
+ } else if (!strcmp(md,"-MDd")) {
+ stdlib="-lMSVCRTD";
+ } else if (!strcmp(md,"-MT")) {
+ stdlib="-lLIBCMT";
+ } else if (!strcmp(md,"-MTd")) {
+ stdlib="-lLIBMTD";
+ } else {
+ stdlib = "";
+ }
+#if 0
+ params = add_to("","bash ld.sh ");
+#else
+ params = add_to("","ld_wrap.exe ");
+#endif
+ params = add_to(params,accum_objects);
+ params = add_to(params," ");
+ params = add_to(params,out_spec);
+ params = add_to(params," ");
+ params = add_to(params,linkcmd);
+ params = add_to(params," ");
+ params = add_to(params,stdlib);
+ free(out_spec);
+ free(accum_objects);
+ if (debugfile) {
+ fprintf(debugfile,"%s\n",params);
+ }
+ if (retval = run(params,0,0) != 0) {
+ maybe_cleanup();
+ return retval;
+ }
+ free(params);
+ }
+ maybe_cleanup();
+ return 0;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/erts/etc/win32/cygwin_tools/vc/coffix.c b/erts/etc/win32/cygwin_tools/vc/coffix.c
new file mode 100644
index 0000000000..dee0132a61
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/coffix.c
@@ -0,0 +1,161 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1999-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+** This mini tool fixes an incompatibility between
+** Microsoft's tools, who dont like the virtual size being put in
+** the physical address field, but rely on the raw size field for
+** sizing the ".bss" section.
+** This fixes some of the problems with linking gcc compiled objects
+** together with MSVC dito.
+**
+** Courtesy DJ Delorie for describing the COFF file format on
+** http://www.delorie.com/djgpp/doc/coff/
+** The coff structures are fetched from Microsofts headers though.
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdarg.h>
+
+#include <windows.h>
+#include <winnt.h> /* Structure definitions for PE (COFF) */
+
+static int dump_edit(char *filename, int edit);
+static int v_printf(char *format, ...);
+
+
+char *progname;
+int verbouse = 0;
+
+int main(int argc, char **argv)
+{
+ int findex = 1;
+ int edit = 0;
+ int ret;
+
+ progname = argv[0];
+ if (argc == 1) {
+ fprintf(stderr,"Format : %s [-e] [-v] <filename>\n", progname);
+ return 1;
+ }
+ for (findex = 1;
+ findex < argc && (*argv[findex] == '-' || *argv[findex] == '/');
+ ++findex)
+ switch (argv[findex][1]) {
+ case 'e':
+ case 'E':
+ edit = 1;
+ break;
+ case 'v':
+ case 'V':
+ verbouse = 1;
+ default:
+ fprintf(stderr, "%s: unknown option %s\n", progname, argv[findex]);
+ break;
+ }
+ if (findex == argc) {
+ fprintf(stderr,"%s: No filenames given.\n", progname);
+ return 1;
+ }
+ for(; findex < argc; ++findex)
+ if ((ret = dump_edit(argv[findex],edit)) != 0)
+ return ret;
+ return 0;
+}
+
+int dump_edit(char *filename, int edit)
+{
+ FILE *f = fopen(filename, (edit) ? "r+b" : "rb");
+ IMAGE_FILE_HEADER filhdr;
+ IMAGE_SECTION_HEADER scnhdr;
+ int i;
+
+ if (f == NULL) {
+ fprintf(stderr, "%s: cannot open %s.\n", progname, filename);
+ return 1;
+ }
+
+ if (fread(&filhdr, sizeof(filhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read COFF header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("File: %s\n", filename);
+ v_printf("Magic number: 0x%08x\n", filhdr.Machine);
+ v_printf("Number of sections: %d\n",filhdr.NumberOfSections);
+
+ if (fseek(f, (long) filhdr.SizeOfOptionalHeader, SEEK_CUR) != 0) {
+ fprintf(stderr,"%s: Could not read COFF optional header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+
+ for (i = 0; i < filhdr.NumberOfSections; ++i) {
+ if (fread(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: Could not read section header from %s,"
+ " is this a PE (COFF) file?\n", progname, filename);
+ fclose(f);
+ return 1;
+ }
+ v_printf("Section %s:\n", scnhdr.Name);
+ v_printf("Physical address: 0x%08x\n", scnhdr.Misc.PhysicalAddress);
+ v_printf("Size: 0x%08x\n", scnhdr.SizeOfRawData);
+ if (scnhdr.Misc.PhysicalAddress != 0 &&
+ scnhdr.SizeOfRawData == 0) {
+ printf("Section header %s in file %s will confuse MSC linker, "
+ "virtual size is 0x%08x and raw size is 0\n",
+ scnhdr.Name, filename, scnhdr.Misc.PhysicalAddress,
+ scnhdr.SizeOfRawData);
+ if (edit) {
+ scnhdr.SizeOfRawData = scnhdr.Misc.PhysicalAddress;
+ scnhdr.Misc.PhysicalAddress = 0;
+ if (fseek(f, (long) -((long)sizeof(scnhdr)), SEEK_CUR) != 0 ||
+ fwrite(&scnhdr, sizeof(scnhdr), 1, f) == 0) {
+ fprintf(stderr,"%s: could not edit file %s.\n",
+ progname, filename);
+ fclose(f);
+ return 1;
+ }
+ printf("Edited object, virtual size is now 0, and "
+ "raw size is 0x%08x.\n", scnhdr.SizeOfRawData);
+ } else {
+ printf("Specify option '-e' to correct the problem.\n");
+ }
+ }
+ }
+ fclose(f);
+ return 0;
+}
+
+
+static int v_printf(char *format, ...)
+{
+ va_list ap;
+ int ret = 0;
+ if (verbouse) {
+ va_start(ap, format);
+ ret = vfprintf(stdout, format, ap);
+ va_end(ap);
+ }
+ return ret;
+}
+
diff --git a/erts/etc/win32/cygwin_tools/vc/emu_cc.sh b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh
new file mode 100755
index 0000000000..c74c35111b
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/emu_cc.sh
@@ -0,0 +1,90 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+TOOLDIR=$ERL_TOP/erts/etc/win32/cygwin_tools/vc
+COFFIX=$TOOLDIR/coffix
+WTOOLDIR=`(cygpath -d $TOOLDIR 2>/dev/null || cygpath -w $TOOLDIR)`
+
+# Do primitive 'make'
+newer_exe=`find $TOOLDIR -newer $COFFIX.c -name coffix.exe -print`
+if [ -z $newer_exe ]; then
+ echo recompiling $COFFIX.exe
+ cl.exe -Fe${WTOOLDIR}\\coffix.exe ${WTOOLDIR}\\coffix.c
+ rm -f $COFFIX.obj coffix.obj $COFFIX.pdb coffix.pdb
+fi
+
+# Try to find out the output filename and remove it from command line
+CMD=""
+OUTFILE=""
+INFILE=""
+SKIP_COFFIX=false
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o/*)
+ OUTFILE=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;;
+ -o)
+ shift
+ OUTFILE=$1;;
+ -MM)
+ SKIP_COFFIX=true
+ CMD="$CMD \"$x\"";;
+ *.c)
+ INFILE="$INFILE $x";
+ CMD="$CMD \"$x\"";;
+ *)
+ CMD="$CMD \"$x\"";;
+ esac
+ shift
+done
+if [ -z "$INFILE" ]; then
+ echo 'emu_cc.sh: please give an input filename for the compiler' >&2
+ exit 1
+fi
+if [ -z "$OUTFILE" ]; then
+ OUTFILE=`echo $INFILE | sed 's,\.c$,.o,'`
+fi
+
+if [ $SKIP_COFFIX = false ]; then
+ n=`echo $INFILE | wc -w`;
+ if [ $n -gt 1 ]; then
+ echo "emu_cc.sh:Error, multiple sources, one object output.";
+ exit 1;
+ fi
+ TEMPFILE=/tmp/tmp_emu_cc$$.o
+ if [ "X$EMU_CC_SH_DEBUG_LOG" != "X" ]; then
+ echo "gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD" >> $EMU_CC_SH_DEBUG_LOG 2>&1
+ fi
+ eval gcc -o $TEMPFILE -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer $CMD
+ RES=$?
+ if [ $RES = 0 ]; then
+ $COFFIX.exe -e `(cygpath -d $TEMPFILE 2>/dev/null || cygpath -w $TEMPFILE)`
+ RES=$?
+ if [ $RES = 0 ]; then
+ cp $TEMPFILE $OUTFILE
+ else
+ echo "emu_cc.sh: fatal: coffix failed!" >&2
+ fi
+ fi
+ rm -f $TEMPFILE
+ exit $RES
+else
+ eval gcc -D__WIN32__ -DWIN32 -DWINDOWS -fomit-frame-pointer -fno-tree-copyrename $CMD 2>/dev/null
+ exit $?
+fi
diff --git a/erts/etc/win32/cygwin_tools/vc/ld.sh b/erts/etc/win32/cygwin_tools/vc/ld.sh
new file mode 100755
index 0000000000..ac39bf871c
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/ld.sh
@@ -0,0 +1,192 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+kernel_libs="kernel32.lib advapi32.lib"
+gdi_libs="gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib"
+DEFAULT_LIBRARIES="$kernel_libs $gdi_libs"
+
+CMD=""
+STDLIB=MSVCRT.LIB
+DEBUG_BUILD=false
+STDLIB_FORCED=false
+BUILD_DLL=false
+OUTPUT_FILENAME=""
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -dll| -DLL)
+ BUILD_DLL=true;;
+ -L/*|-L.*)
+ y=`echo $x | sed 's,^-L\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -libpath:\"$MPATH\"";;
+ -lMSVCRT|-lmsvcrt)
+ STDLIB_FORCED=true;
+ STDLIB=MSVCRT.LIB;;
+ -lMSVCRTD|-lmsvcrtd)
+ STDLIB_FORCED=true;
+ STDLIB=MSVCRTD.LIB;;
+ -lLIBCMT|-llibcmt)
+ STDLIB_FORCED=true;
+ STDLIB=LIBCMT.LIB;;
+ -lLIBCMTD|-llibcmtd)
+ STDLIB_FORCED=true;
+ STDLIB=LIBCMTD.LIB;;
+ -lsocket)
+ DEFAULT_LIBRARIES="$DEFAULT_LIBRARIES WS2_32.LIB";;
+ -l*)
+ y=`echo $x | sed 's,^-l\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD \"${MPATH}.lib\"";;
+ -g)
+ DEBUG_BUILD=true;;
+ -pdb:none|-incremental:no)
+ ;;
+ -implib:*)
+ y=`echo $x | sed 's,^-implib:\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -implib:\"${MPATH}\"";;
+ -def:*)
+ y=`echo $x | sed 's,^-def:\(.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -def:\"${MPATH}\"";;
+ -o)
+ shift
+ MPATH=`cygpath -m $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ /*)
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+if [ $DEBUG_BUILD = true ]; then
+ linktype="-debug -pdb:none"
+ if [ $STDLIB_FORCED = false ]; then
+ STDLIB=MSVCRTD.LIB
+ fi
+fi
+# Generate a PDB
+linkadd_pdb=""
+case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ fn=`echo "$OUTPUT_FILENAME" | sed 's,[eE][xX][eE]$,,g'`;
+ linkadd_pdb="-pdb:\"${fn}pdb\"";;
+ *.dll|*.DLL)
+ fn=`echo "$OUTPUT_FILENAME" | sed 's,[dD][lL][lL]$,,g'`;
+ linkadd_pdb="-pdb:\"${fn}pdb\"";;
+ "")
+ linkadd_pdb="-pdb:\"a.pdb\"";;
+ *)
+ linkadd_pdb="-pdb:\"${OUTPUT_FILENAME}.pdb\"";;
+esac
+
+ linktype="-debug $linkadd_pdb"
+
+CHMOD_FILE=""
+
+if [ $BUILD_DLL = true ];then
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ echo "Warning, output set to .exe when building DLL" >&2
+ CHMOD_FILE="$OUTPUT_FILENAME";
+ CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;2";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ *.dll|*.DLL)
+ CMD="-dll -out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;2";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ "")
+ CMD="-dll -out:\"a.dll\" $CMD";
+ OUTPUTRES="a.dll\;2";
+ MANIFEST="a.dll.manifest";;
+ *)
+ CMD="-dll -out:\"${OUTPUT_FILENAME}.dll\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}.dll\;2";
+ MANIFEST="${OUTPUT_FILENAME}.dll.manifest";;
+ esac
+else
+ case "$OUTPUT_FILENAME" in
+ *.exe|*.EXE)
+ CHMOD_FILE="$OUTPUT_FILENAME";
+ CMD="-out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;1"
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ *.dll|*.DLL)
+ echo "Warning, output set to .dll when building EXE" >&2
+ CMD="-out:\"$OUTPUT_FILENAME\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}\;1";
+ MANIFEST="${OUTPUT_FILENAME}.manifest";;
+ "")
+ CHMOD_FILE="a.exe";
+ CMD="-out:\"a.exe\" $CMD";
+ OUTPUTRES="a.exe\;1";
+ MANIFEST="a.exe.manifest";;
+ *)
+ CMD="-out:\"${OUTPUT_FILENAME}.exe\" $CMD";
+ OUTPUTRES="${OUTPUT_FILENAME}.exe\;1";
+ MANIFEST="${OUTPUT_FILENAME}.exe.manifest";;
+ esac
+fi
+
+p=$$
+CMD="$linktype -nologo -incremental:no $CMD $STDLIB $DEFAULT_LIBRARIES"
+if [ "X$LD_SH_DEBUG_LOG" != "X" ]; then
+ echo ld.sh "$SAVE" >>$LD_SH_DEBUG_LOG
+ echo link.exe $CMD >>$LD_SH_DEBUG_LOG
+fi
+eval link.exe "$CMD" >/tmp/link.exe.${p}.1 2>/tmp/link.exe.${p}.2
+RES=$?
+CMANIFEST=`cygpath $MANIFEST`
+if [ "$RES" = "0" -a -f "$CMANIFEST" ]; then
+ eval mt.exe -nologo -manifest "$MANIFEST" -outputresource:"$OUTPUTRES" >>/tmp/link.exe.${p}.1 2>>/tmp/link.exe.${p}.2
+ RES=$?
+ if [ "$RES" != "0" ]; then
+ REMOVE=`echo "$OUTPUTRES" | sed 's,\\\;[12]$,,g'`
+ CREMOVE=`cygpath $REMOVE`
+ rm -f "$CREMOVE"
+ fi
+ rm -f "$CMANIFEST"
+fi
+
+# This works around some strange behaviour
+# in cygwin 1.7 Beta on Windows 7 with samba drive.
+# Configure will think the compiler failed if test -x fails,
+# which it might do as we might not be the owner of the
+# file.
+if [ '!' -z "$CHMOD_FILE" -a -s "$CHMOD_FILE" -a '!' -x "$CHMOD_FILE" ]; then
+ chmod +x $CHMOD_FILE
+fi
+
+tail -n +2 /tmp/link.exe.${p}.2 >&2
+cat /tmp/link.exe.${p}.1
+rm -f /tmp/link.exe.${p}.2 /tmp/link.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/cygwin_tools/vc/ld_wrap.c b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c
new file mode 100644
index 0000000000..7fb3c145ee
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/ld_wrap.c
@@ -0,0 +1,796 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2008-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <limits.h>
+#include <dirent.h>
+#include <sys/cygwin.h>
+
+
+
+#ifdef CCP_POSIX_TO_WIN_A
+#define NEW_CYGPATH_INTERFACE
+#endif
+
+#ifdef NEW_CYGPATH_INTERFACE
+#define GET_WIN32_SIZE(Posix) \
+cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), NULL, 0)
+#define CONVERT_TO_WIN32(Posix,Win32,Size) \
+cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE, (Posix), \
+ (Win32), (Size))
+#else
+#define GET_WIN32_SIZE(Posix) PATH_MAX
+#define CONVERT_TO_WIN32(Posix,Win32,Size) \
+((cygwin32_conv_to_full_win32_path((Posix),(Win32)) >= 0) ? 0 : -1)
+#endif
+
+/*#define HARDDEBUG 1*/
+
+#ifdef HARDDEBUG
+#define DEBUGF(X) printf X
+#else
+#define DEBUGF(X) /* noop */
+#endif
+char *tmpobjdir = "";
+
+char *add_to(char *src,char *add) {
+ int len = strlen(src)+strlen(add)+1;
+ char *n;
+
+ if (strlen(src) == 0) {
+ n = malloc(len);
+ strcpy(n,add);
+ return n;
+ }
+ n = realloc(src,len);
+ strcat(n,add);
+ return n;
+}
+
+void error(char *str)
+{
+ fprintf(stderr,"%s\n",str);
+ exit(1);
+}
+
+
+char *dyn_get_short(char *longp)
+{
+ int size;
+ char *shortp;
+ size = GetShortPathName(longp,NULL,0);
+ if (size <= 0) {
+ return NULL;
+ }
+ shortp = malloc(size);
+ if (GetShortPathName(longp,shortp,size) != size - 1) {
+ free(shortp);
+ return NULL;
+ }
+ return shortp;
+}
+
+char *do_cyp(char *posix)
+{
+ ssize_t size;
+ char *win32;
+ size = GET_WIN32_SIZE(posix);
+ char *ret = NULL;
+ if (size < 0) {
+ fprintf(stderr,"Could not cygpath %s, errno = %d\n",
+ posix,errno);
+ } else {
+ win32 = (char *) malloc (size);
+ if (CONVERT_TO_WIN32(posix,
+ win32, size)) {
+ fprintf(stderr,"Could not cygpath %s, errno = %d\n",
+ posix,errno);
+ } else {
+ char *w32_short = dyn_get_short(win32);
+ DEBUGF(("win32 = %s, w32_short = %s\n",win32, (w32_short == NULL) ? "NULL" : w32_short));
+ if (w32_short == NULL) {
+ char *rest = malloc(size);
+ char *first = malloc(size);
+ int x = 0;
+ int y = strlen(win32) - 1;
+ strcpy(first,win32);
+ while (w32_short == NULL) {
+ while ( y > 0 && first[y] != '\\') {
+ rest[x++] = first[y--];
+ }
+ if (y > 0) {
+ rest[x++] = first[y];
+ first[y--] = '\0';
+ } else {
+ break;
+ }
+ w32_short = dyn_get_short(first);
+ DEBUGF(("first = %s, w32_short = %s\n",first, (w32_short == NULL) ? "NULL" : w32_short));
+ }
+ if (w32_short != NULL) {
+ y = strlen(w32_short);
+ w32_short = realloc(w32_short,y+1+x);
+ /* spool back */
+ while ( x > 0) {
+ w32_short[y++] = rest[--x];
+ }
+ w32_short[y] = '\0';
+ } else {
+ w32_short = malloc(strlen(win32)+1);
+ strcpy(w32_short,win32); /* last resort */
+ }
+ free(first);
+ free(rest);
+ }
+ ret = w32_short;
+ while (*ret) {
+ if (*ret == '\\') {
+ *ret = '/';
+ }
+ ++ret;
+ }
+ ret = w32_short;
+ }
+ free(win32);
+ }
+ return ret;
+}
+
+
+
+char *save = "";
+
+void save_args(int argc, char **argv)
+{
+ int i;
+ for(i = 0; i < argc; ++i) {
+ save = add_to(save,argv[i]);
+ save = add_to(save," ");
+ }
+}
+
+char *progname="ld_wrap";
+
+int my_create_pipe(HANDLE *read_p, HANDLE *write_p)
+{
+ char name_buff[1000];
+ SECURITY_ATTRIBUTES sa = {sizeof(SECURITY_ATTRIBUTES), NULL, TRUE};
+ static int counter = 0;
+
+ ++counter;
+
+ sprintf(name_buff,"\\\\.\\pipe\\%s_%d_%d",progname,getpid(),counter);
+ sa.bInheritHandle = FALSE;
+ if ((*read_p = CreateNamedPipe(name_buff,
+ PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED,
+ PIPE_TYPE_BYTE | PIPE_READMODE_BYTE,
+ 1,
+ 0,
+ 0,
+ 2000,
+ &sa)) == INVALID_HANDLE_VALUE ||
+ *read_p == NULL) {
+ return 0;
+ }
+ sa.bInheritHandle = TRUE;
+ if ((*write_p = CreateFile(name_buff,
+ GENERIC_WRITE,
+ 0, /* No sharing */
+ &sa,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL)) == INVALID_HANDLE_VALUE ||
+ *write_p == NULL) {
+ CloseHandle(*read_p);
+ return 0;
+ }
+ return 1;
+}
+
+void forwardenv(void)
+{
+ char *(envs[]) = {"LIB","INCLUDE","LIBPATH", NULL};
+ char **p = envs;
+ while (*p != NULL) {
+ char *val = getenv(*p);
+ if (val != NULL) {
+ SetEnvironmentVariable(*p,val);
+ }
+ ++p;
+ }
+}
+
+HANDLE do_run(char *commandline, HANDLE *out, HANDLE *err)
+{
+ STARTUPINFO start;
+ HANDLE write_pipe_stdout = NULL, read_pipe_stdout = NULL;
+ HANDLE write_pipe_stderr = NULL, read_pipe_stderr = NULL;
+ SECURITY_ATTRIBUTES pipe_security;
+ SECURITY_ATTRIBUTES attr;
+ PROCESS_INFORMATION info;
+
+
+ memset(&start,0,sizeof(start));
+ memset(&pipe_security,0,sizeof(pipe_security));
+ memset(&attr,0,sizeof(attr));
+ memset(&info,0,sizeof(info));
+
+
+ pipe_security.nLength = sizeof(pipe_security);
+ pipe_security.lpSecurityDescriptor = NULL;
+ pipe_security.bInheritHandle = TRUE;
+
+ if(!my_create_pipe(&read_pipe_stdout,&write_pipe_stdout)){
+ error("Could not create stdout pipes!");
+ }
+ if(!my_create_pipe(&read_pipe_stderr,&write_pipe_stderr)){
+ error("Could not create stderr pipes!");
+ }
+ start.cb = sizeof (start);
+ start.dwFlags = STARTF_USESHOWWINDOW;
+ start.wShowWindow = SW_HIDE;
+ start.hStdOutput = write_pipe_stdout;
+ start.hStdError = write_pipe_stderr;
+ start.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ start.dwFlags |= STARTF_USESTDHANDLES;
+
+ attr.nLength = sizeof(attr);
+ attr.lpSecurityDescriptor = NULL;
+ attr.bInheritHandle = TRUE;
+ forwardenv(); /* Cygwin and windows environment variables... sigh... */
+ if(!CreateProcess(NULL,
+ commandline,
+ &attr,
+ NULL,
+ TRUE,
+ CREATE_DEFAULT_ERROR_MODE,
+ NULL,
+ NULL,
+ &start,
+ &info)){
+ error("Could not create process");
+ }
+ *out = read_pipe_stdout;
+ *err = read_pipe_stderr;
+ CloseHandle(write_pipe_stdout);
+ CloseHandle(write_pipe_stderr);
+ return info.hProcess;
+}
+#define HANDLE_STDOUT 0
+#define HANDLE_STDERR 1
+#define HANDLE_PROC 2
+
+#ifdef HARDDEBUG
+char *prefix = "";
+#endif
+
+int handle_overlapped(HANDLE fd, OVERLAPPED *ovp, char *buffer,
+ int bufflen, int get_old, FILE *whereto, int *skip)
+{
+ DWORD res,read,err;
+ char *ptr;
+
+ DEBUGF(("In handle_overlapped(%d,0x%08x,0x%08x,%d,%d), prefix = %s\n",
+ fd,ovp,buffer,bufflen,get_old,prefix));
+ /* h�mta resultat av gamla f�rst */
+ if (get_old) {
+ res = GetOverlappedResult(fd,ovp,&read,TRUE);
+ DEBUGF(("read = %d, res = %d, GetLastError() = %d\n",read,res,GetLastError()));
+ if (!res) {
+ return 0;
+ }
+ buffer[read] = '\0';
+ ptr = buffer;
+ while(*skip && *ptr != '\0') {
+ if (*ptr == '\n') {
+ --(*skip);
+ }
+ ++ptr;
+ }
+ if(*ptr != '\0') {
+ fprintf(whereto,"%s",ptr);
+ }
+ }
+
+ ResetEvent(ovp->hEvent);
+
+ for(;;) {
+ res = ReadFile(fd,buffer,bufflen-1,&read,ovp);
+
+ if (!res) {
+ err = GetLastError();
+ if (err == ERROR_IO_PENDING) {
+ DEBUGF(("Error I/O Pending\n"));
+ return 1;
+ }
+ DEBUGF(("ReadFileFailed for %s, %d\n",prefix,err));
+ return 0;
+ }
+ buffer[read] = '\0';
+ ptr = buffer;
+ while(*skip && *ptr != '\0') {
+ if (*ptr == '\n') {
+ --(*skip);
+ }
+ ++ptr;
+ }
+ if(*ptr != '\0') {
+ fprintf(whereto,"%s",ptr);
+ }
+ }
+}
+
+
+int run(char *commandline,int skipout,int skiperr)
+{
+ HANDLE harr[3];
+ HANDLE real_stdout,real_stderr;
+ OVERLAPPED ov_out,ov_err;
+ char outbuff[1024],errbuff[1024];
+ DWORD ret,exitcode;
+ HANDLE wait[3];
+ int map[3];
+ DWORD nwait = 3;
+ int i,j;
+ unsigned living_handles = 0x7;
+
+ harr[HANDLE_STDOUT] = CreateEvent(NULL,
+ TRUE,
+ FALSE, /*not signalled */
+ NULL);
+ harr[HANDLE_STDERR] = CreateEvent(NULL,
+ TRUE,
+ FALSE,/*not signalled */
+ NULL);
+
+ memset(&ov_out,0,sizeof(ov_out));
+ memset(&ov_err,0,sizeof(ov_err));
+
+ ov_out.hEvent = harr[HANDLE_STDOUT];
+ ov_err.hEvent = harr[HANDLE_STDERR];
+
+ harr[HANDLE_PROC] = do_run(commandline,&real_stdout,&real_stderr);
+
+#ifdef HARDDEBUG
+ prefix = "STDOUT";
+#endif
+ handle_overlapped(real_stdout,&ov_out,outbuff,1024,0,stdout,&skipout);
+#ifdef HARDDEBUG
+ prefix = "STDERR";
+#endif
+ handle_overlapped(real_stderr,&ov_err,errbuff,1024,0,stderr,&skiperr);
+
+ for(;;) {
+ nwait = 0;
+ for(i=0;i<3;++i) {
+ if ((living_handles & (1U << i))) {
+ map[nwait] = i;
+ wait[nwait++] = harr[i];
+ }
+ }
+
+ ret = WaitForMultipleObjects(nwait,
+ wait,
+ FALSE,
+ INFINITE);
+ DEBUGF(("Wait returned %d\n",ret));
+
+ if (ret == WAIT_FAILED) {
+ error("Wait failed");
+ }
+
+ ret -= WAIT_OBJECT_0;
+
+ switch (map[ret]) {
+ case HANDLE_PROC:
+
+ DEBUGF(("Process died!\n"));
+ GetExitCodeProcess(harr[HANDLE_PROC],&exitcode);
+ if ((living_handles &= (~(1U<<HANDLE_PROC))) == 0) {
+ goto done;
+ }
+ --nwait;
+ break;
+ case HANDLE_STDOUT:
+#ifdef HARDDEBUG
+ prefix = "STDOUT";
+#endif
+ if (!handle_overlapped(real_stdout,&ov_out, outbuff,1024,1,stdout,&skipout)) {
+ if ((living_handles &= (~(1U<<HANDLE_STDOUT))) == 0) {
+ goto done;
+ }
+ }
+ break;
+ case HANDLE_STDERR:
+#ifdef HARDDEBUG
+ prefix = "STDERR";
+#endif
+ if (!handle_overlapped(real_stderr,&ov_err, errbuff,1024,1,stderr,&skiperr)){
+ if ((living_handles &= (~(1U<<HANDLE_STDERR))) == 0) {
+ goto done;
+ }
+ }
+ break;
+ default:
+ error("Unexpected wait result");
+ }
+ }
+ done:
+ CloseHandle(harr[HANDLE_PROC]);
+ CloseHandle(harr[HANDLE_STDOUT]);
+ CloseHandle(harr[HANDLE_STDERR]);
+ CloseHandle(real_stdout);
+ CloseHandle(real_stderr);
+ return (int) exitcode;
+}
+
+int main(int argc, char **argv)
+{
+ int i;
+ int x;
+ char *s;
+ char *mpath;
+ char *debuglog;
+ char *remove;
+ FILE *debugfile;
+ FILE *tmpfile;
+ int filefound;
+ int retval = 0;
+
+ char *kernel_libs="kernel32.lib advapi32.lib";
+ char *gdi_libs="gdi32.lib user32.lib comctl32.lib comdlg32.lib shell32.lib";
+ char *default_libraries = "";
+ char *cmd = "";
+ char *stdlib = "MSVCRT.LIB";
+ int debug_build = 0;
+ int stdlib_forced = 0;
+ int build_dll = 0;
+ char *output_filename = "";
+ char *linkadd_pdb = "";
+ char *linktype = "";
+ char *manifest = "";
+ char *outputres = "";
+
+ save_args(argc,argv);
+ //fprintf(stderr,"ld_wrap!\n");
+
+ default_libraries = add_to(default_libraries,kernel_libs);
+ default_libraries = add_to(default_libraries," ");
+ default_libraries = add_to(default_libraries,gdi_libs);
+
+ for(i = 1; i < argc; ++i) {
+ if (argv[i][0] == '-') {
+ char *opt = argv[i]+1;
+ switch(*opt) {
+ case 'D':
+ if(strcmp(opt,"DLL")) {
+ goto filename;
+ }
+ build_dll = 1;
+ break;
+ case 'd':
+ if(!strncmp(opt,"def:",4)) {
+ mpath = do_cyp(opt+4);
+ cmd = add_to(cmd," -def:\"");
+ cmd = add_to(cmd,mpath);
+ cmd = add_to(cmd,"\"");
+ free(mpath);
+ } else if(strcmp(opt,"dll")) {
+ goto filename;
+ } else {
+ build_dll = 1;
+ }
+ break;
+ case 'L':
+ mpath = do_cyp(opt+1);
+ cmd = add_to(cmd," -libpath:\"");
+ cmd = add_to(cmd,mpath);
+ cmd = add_to(cmd,"\"");
+ free(mpath);
+ break;
+ case 'l':
+ if(!strcmp(opt,"lMSVCRT") || !strcmp(opt,"lmsvcrt")) {
+ stdlib = "MSVCRT.LIB";
+ stdlib_forced = 1;
+ } else if(!strcmp(opt,"lMSVCRTD") || !strcmp(opt,"lmsvcrtd")) {
+ stdlib = "MSVCRTD.LIB";
+ stdlib_forced = 1;
+ } else if(!strcmp(opt,"lLIBCMT") || !strcmp(opt,"llibcmt")) {
+ stdlib = "LIBCMT.LIB";
+ stdlib_forced = 1;
+ } else if(!strcmp(opt,"lLIBCMTD") || !strcmp(opt,"llibcmtd")) {
+ stdlib = "LIBCMTD.LIB";
+ stdlib_forced = 1;
+ } else if(!strcmp(opt,"lsocket")) {
+ default_libraries = add_to(default_libraries," ");
+ default_libraries = add_to(default_libraries,"WS2_32.LIB");
+ } else {
+ mpath = do_cyp(opt+1);
+ cmd = add_to(cmd," \"");
+ cmd = add_to(cmd,mpath);
+ cmd = add_to(cmd,"\"");
+ free(mpath);
+ }
+ break;
+ case 'g':
+ debug_build = 1;
+ break;
+ case 'p':
+ if (strcmp(opt,"pdb:none")) {
+ goto filename;
+ }
+ break;
+ case 'i':
+ if (!strncmp(opt,"implib:",7)) {
+ mpath = do_cyp(opt+7);
+ cmd = add_to(cmd," -implib:\"");
+ cmd = add_to(cmd,mpath);
+ cmd = add_to(cmd,"\"");
+ free(mpath);
+ } else if (strcmp(opt,"incremental:no")) {
+ goto filename;
+ }
+ break;
+ case 'o':
+ if (!strcmp(opt,"o")) {
+ ++i;
+ if (i >= argc) {
+ error("-o without filename");
+ }
+ output_filename = do_cyp(argv[i]);
+ } else {
+ output_filename = do_cyp(opt+1);
+ }
+ break;
+ default:
+ goto filename;
+ }
+ continue;
+ }
+ filename:
+ s = argv[i];
+ if (*s == '/') {
+ mpath = do_cyp(s);
+ cmd = add_to(cmd," \"");
+ cmd = add_to(cmd,mpath);
+ cmd = add_to(cmd,"\"");
+ free(mpath);
+ } else {
+ cmd = add_to(cmd," \"");
+ cmd = add_to(cmd,s);
+ cmd = add_to(cmd,"\"");
+ }
+ }
+ if ((debuglog = getenv("LD_SH_DEBUG_LOG")) != NULL) {
+ debugfile = fopen(debuglog,"wb+");
+ if (debugfile) {
+ fprintf(debugfile,"----------------\n");
+ }
+ } else {
+ debugfile = NULL;
+ }
+
+ if (debug_build) {
+ if (!stdlib_forced) {
+ stdlib = "MSVCRTD.LIB";
+ }
+ }
+
+ s = add_to("",output_filename);
+ x = strlen(s);
+
+ if (x >= 4 && (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE") ||
+ !strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL"))) {
+ *(s+x-3) = '\0';
+ linkadd_pdb = add_to(linkadd_pdb,"-pdb:\"");
+ linkadd_pdb = add_to(linkadd_pdb,s);
+ linkadd_pdb = add_to(linkadd_pdb,"pdb\"");
+ } else if (!x) {
+ linkadd_pdb = add_to(linkadd_pdb,"-pdb:\"a.pdb\"");
+ } else {
+ linkadd_pdb = add_to(linkadd_pdb,"-pdb:\"");
+ linkadd_pdb = add_to(linkadd_pdb,s);
+ linkadd_pdb = add_to(linkadd_pdb,".pdb\"");
+ }
+ free(s);
+
+
+ linktype = add_to(linktype,"-debug ");
+ linktype = add_to(linktype,linkadd_pdb);
+
+ free(linkadd_pdb);
+
+ s = add_to("",output_filename);
+ x = strlen(s);
+
+ if (build_dll) {
+ if (x >= 4 && (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE") ||
+ !strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL"))) {
+
+ if (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE")) {
+ fprintf(stderr,"Warning, output set to .exe when building DLL");
+ }
+ mpath = cmd;
+ cmd = add_to("","-dll -out:\"");
+ cmd = add_to(cmd,s);
+ cmd = add_to(cmd,"\" ");
+ cmd = add_to(cmd,mpath);
+ if (*mpath) {
+ free(mpath);
+ }
+
+ outputres = add_to(outputres,output_filename);
+ outputres = add_to(outputres,";2");
+ manifest = add_to(manifest,output_filename);
+ manifest = add_to(manifest,".manifest");
+ } else if (x == 0) {
+ mpath = cmd;
+ cmd = add_to("","-dll -out:\"a.dll\" ");
+ cmd = add_to(cmd,mpath);
+ if (*mpath) {
+ free(mpath);
+ }
+
+ outputres = add_to(outputres,"a.dll;2");
+ manifest = add_to(manifest,"a.dll.manifest");
+ } else {
+ mpath = cmd;
+ cmd = add_to("","-dll -out:\"");
+ cmd = add_to(cmd,s);
+ cmd = add_to(cmd,".dll\" ");
+ cmd = add_to(cmd,mpath);
+ if (*mpath) {
+ free(mpath);
+ }
+
+ outputres = add_to(outputres,output_filename);
+ outputres = add_to(outputres,".dll;2");
+ manifest = add_to(manifest,output_filename);
+ manifest = add_to(manifest,".dll.manifest");
+ }
+ } else {
+ if (x >= 4 && (!strcmp(s+x-4,".exe") || !strcmp(s+x-4,".EXE") ||
+ !strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL"))) {
+
+ if (!strcmp(s+x-4,".dll") || !strcmp(s+x-4,".DLL")) {
+ fprintf(stderr,"Warning, output set to .exe when building DLL");
+ }
+ mpath = cmd;
+ cmd = add_to("","-out:\"");
+ cmd = add_to(cmd,s);
+ cmd = add_to(cmd,"\" ");
+ cmd = add_to(cmd,mpath);
+ if (*mpath) {
+ free(mpath);
+ }
+
+ outputres = add_to(outputres,output_filename);
+ outputres = add_to(outputres,";1");
+ manifest = add_to(manifest,output_filename);
+ manifest = add_to(manifest,".manifest");
+ } else if (x == 0) {
+ mpath = cmd;
+ cmd = add_to("","-out:\"a.exe\" ");
+ cmd = add_to(cmd,mpath);
+ if (*mpath) {
+ free(mpath);
+ }
+
+ outputres = add_to(outputres,"a.exe;1");
+ manifest = add_to(manifest,"a.exe.manifest");
+ } else {
+ mpath = cmd;
+ cmd = add_to("","-out:\"");
+ cmd = add_to(cmd,s);
+ cmd = add_to(cmd,".exe\" ");
+ cmd = add_to(cmd,mpath);
+ if (*mpath) {
+ free(mpath);
+ }
+
+ outputres = add_to(outputres,output_filename);
+ outputres = add_to(outputres,".exe;1");
+ manifest = add_to(manifest,output_filename);
+ manifest = add_to(manifest,".exe.manifest");
+ }
+ }
+
+ s = cmd;
+ cmd = add_to("","link.exe ");
+ cmd = add_to(cmd,linktype);
+ cmd = add_to(cmd," -nologo -incremental:no ");
+ cmd = add_to(cmd,s);
+ cmd = add_to(cmd," ");
+ cmd = add_to(cmd,stdlib);
+ cmd = add_to(cmd," ");
+ cmd = add_to(cmd,default_libraries);
+
+ if (*s) {
+ free(s);
+ }
+
+
+ if (debugfile) {
+ fprintf(debugfile,"%s\n",save);
+ fprintf(debugfile,"%s\n",cmd);
+ }
+
+ retval = run(cmd,0,0);
+
+
+ mpath = do_cyp(manifest);
+ filefound = 0;
+ tmpfile = fopen(mpath,"rb");
+ if (tmpfile != NULL) {
+ filefound = 1;
+ fclose(tmpfile);
+ }
+ if (retval == 0 && filefound) {
+ s = add_to("","mt.exe -nologo -manifest \"");
+ s = add_to(s,manifest);
+ s = add_to(s,"\" -outputresource:\"");
+ s = add_to(s,outputres);
+ s = add_to(s,"\"");
+ if (debugfile) {
+ fprintf(debugfile,"%s\n",s);
+ }
+ retval = run(s,0,0);
+ if (*s) {
+ free(s);
+ }
+ if (retval) {
+ /* cleanup needed */
+ remove = add_to("",outputres);
+ x = strlen(remove);
+ remove[x-2] = '\0';
+ if (debugfile) {
+ fprintf(debugfile,"remove %s\n",remove);
+ }
+ DeleteFile(remove);
+ free(remove);
+ }
+ if (debugfile) {
+ fprintf(debugfile,"remove %s\n",manifest);
+ }
+ DeleteFile(manifest);
+ }
+ return retval;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/erts/etc/win32/cygwin_tools/vc/mc.sh b/erts/etc/win32/cygwin_tools/vc/mc.sh
new file mode 100755
index 0000000000..813b59947b
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/mc.sh
@@ -0,0 +1,87 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_DIRNAME=""
+
+# Find the correct mc.exe. This could be done by the configure script,
+# But as we seldom use the resource compiler, it might as well be done here...
+MCC=""
+save_ifs=$IFS
+IFS=:
+for p in $PATH; do
+ if [ -f $p/mc.exe ]; then
+ if [ -n "`$p/mc.exe -? 2>&1 >/dev/null </dev/null \
+ | grep -i \"message compiler\"`" ]; then
+ MCC=$p/mc.exe
+ fi
+ fi
+done
+IFS=$save_ifs
+
+if [ -z "$MCC" ]; then
+ echo 'mc.exe not found!' >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ OUTPUT_DIRNAME="$1";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ OUTPUT_DIRNAME="$y";;
+ -I)
+ shift
+ MPATH=`cygpath -m $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ *)
+ MPATH=`cygpath -m -a $x`;
+ CMD="$CMD \"$MPATH\"";;
+ esac
+ shift
+done
+p=$$
+if [ "X$MC_SH_DEBUG_LOG" != "X" ]; then
+ echo rc.sh "$SAVE" >>$MC_SH_DEBUG_LOG
+ echo rc.exe $CMD >>$MC_SH_DEBUG_LOG
+fi
+if [ -n "$OUTPUT_DIRNAME" ]; then
+ cd $OUTPUT_DIRNAME
+ RES=$?
+ if [ "$RES" != "0" ]; then
+ echo "mc.sh: Error: could not cd to $OUTPUT_DIRNAME">&2
+ exit $RES
+ fi
+fi
+eval $MCC "$CMD" >/tmp/mc.exe.${p}.1 2>/tmp/mc.exe.${p}.2
+RES=$?
+tail +2 /tmp/mc.exe.${p}.2 >&2
+cat /tmp/mc.exe.${p}.1
+rm -f /tmp/mc.exe.${p}.2 /tmp/mc.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/cygwin_tools/vc/rc.sh b/erts/etc/win32/cygwin_tools/vc/rc.sh
new file mode 100755
index 0000000000..748de48890
--- /dev/null
+++ b/erts/etc/win32/cygwin_tools/vc/rc.sh
@@ -0,0 +1,86 @@
+#! /bin/sh
+# set -x
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2002-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# Save the command line for debug outputs
+SAVE="$@"
+CMD=""
+OUTPUT_FILENAME=""
+
+# Find the correct rc.exe. This could be done by the configure script,
+# But as we seldom use the resource compiler, it might as well be done here...
+RCC=""
+save_ifs=$IFS
+IFS=:
+for p in $PATH; do
+ if [ -f $p/rc.exe ]; then
+ if [ -n "`$p/rc.exe -? 2>&1 | grep -i "resource compiler"`" ]; then
+ RCC=$p/rc.exe
+ fi
+ fi
+done
+IFS=$save_ifs
+
+if [ -z "$RCC" ]; then
+ echo 'rc.exe not found!' >&2
+ exit 1
+fi
+
+while test -n "$1" ; do
+ x="$1"
+ case "$x" in
+ -o)
+ shift
+ MPATH=`cygpath -m $1`;
+ OUTPUT_FILENAME="$MPATH";;
+ -o/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ OUTPUT_FILENAME="$MPATH";;
+ -I)
+ shift
+ MPATH=`cygpath -m $1`;
+ CMD="$CMD -I\"$MPATH\"";;
+ -I/*)
+ y=`echo $x | sed 's,^-[Io]\(/.*\),\1,g'`;
+ MPATH=`cygpath -m $y`;
+ CMD="$CMD -I\"$MPATH\"";;
+ /*)
+ MPATH=`cygpath -m $x`;
+ CMD="$CMD \"$MPATH\"";;
+ *)
+ y=`echo $x | sed 's,",\\\",g'`;
+ CMD="$CMD \"$y\"";;
+ esac
+ shift
+done
+p=$$
+if [ -n "$OUTPUT_FILENAME" ]; then
+ CMD="-Fo$OUTPUT_FILENAME $CMD"
+fi
+if [ "X$RC_SH_DEBUG_LOG" != "X" ]; then
+ echo rc.sh "$SAVE" >>$RC_SH_DEBUG_LOG
+ echo rc.exe $CMD >>$RC_SH_DEBUG_LOG
+fi
+eval $RCC "$CMD" >/tmp/rc.exe.${p}.1 2>/tmp/rc.exe.${p}.2
+RES=$?
+tail +2 /tmp/rc.exe.${p}.2 >&2
+cat /tmp/rc.exe.${p}.1
+rm -f /tmp/rc.exe.${p}.2 /tmp/rc.exe.${p}.1
+exit $RES
diff --git a/erts/etc/win32/erl.c b/erts/etc/win32/erl.c
new file mode 100644
index 0000000000..d341153966
--- /dev/null
+++ b/erts/etc/win32/erl.c
@@ -0,0 +1,283 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#pragma comment(linker,"/manifestdependency:\"type='win32' "\
+ "name='Microsoft.Windows.Common-Controls' "\
+ "version='6.0.0.0' processorArchitecture='*' "\
+ "publicKeyToken='6595b64144ccf1df' language='*'\"")
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "init_file.h"
+
+typedef int ErlexecFunction(int, char **, HANDLE, int);
+
+#define INI_FILENAME "erl.ini"
+#define INI_SECTION "erlang"
+#define ERLEXEC_BASENAME "erlexec.dll"
+
+static void get_parameters(void);
+static void error(char* format, ...);
+
+static char *erlexec_name;
+static char *erlexec_dir;
+
+#ifdef WIN32_WERL
+#define WERL 1
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
+ PSTR szCmdLine, int iCmdShow)
+{
+ int argc = __argc;
+ char **argv = __argv;
+#else
+#define WERL 0
+int main(int argc, char **argv)
+{
+#endif
+ HANDLE erlexec_handle; /* Instance */
+ ErlexecFunction *win_erlexec;
+ char *path = malloc(100);
+ char *npath;
+ int pathlen;
+
+ get_parameters();
+
+ if ((pathlen = GetEnvironmentVariable("PATH",path,100)) == 0) {
+ error("No PATH variable (!)");
+ } else if (pathlen > 100) {
+ path = realloc(path,pathlen);
+ GetEnvironmentVariable("PATH",path,pathlen);
+ }
+ npath = malloc(strlen(path) + strlen(erlexec_dir) + 2);
+ sprintf(npath,"%s;%s",erlexec_dir,path);
+ SetEnvironmentVariable("PATH",npath);
+
+ if ((erlexec_handle = LoadLibrary(erlexec_name)) == NULL) {
+ error("Could not load module %s.",erlexec_name);
+ }
+
+ if ((win_erlexec = (ErlexecFunction *)
+ GetProcAddress(erlexec_handle,"win_erlexec")) == NULL) {
+ error("Could not find entry point \"win_erlexec\" in %s.", erlexec_name);
+ }
+
+ return (*win_erlexec)(argc,argv,erlexec_handle,WERL);
+
+}
+
+
+static char *replace_filename(char *path, char *new_base)
+{
+ int plen = strlen(path);
+ char *res = malloc((plen+strlen(new_base)+1)*sizeof(char));
+ char *p;
+
+ strcpy(res,path);
+ for (p = res+plen-1 ;p >= res && *p != '\\'; --p)
+ ;
+ *(p+1) ='\0';
+ strcat(res,new_base);
+ return res;
+}
+
+static char *do_lookup_in_section(InitSection *inis, char *name,
+ char *section, char *filename)
+{
+ char *p = lookup_init_entry(inis, name);
+
+ if (p == NULL) {
+ error("Could not find key %s in section %s of file %s",
+ name,section,filename);
+ }
+ return _strdup(p);
+}
+
+static void copy_latest_vsn(char *latest_vsn, char *next_vsn)
+{
+ /* Copy */
+ char *lp;
+ char *np;
+ /* Find vsn */
+ for (lp = next_vsn+strlen(next_vsn)-1 ;lp >= next_vsn && *lp != '\\'; --lp)
+ ;
+ /* lp =+ length("erts-"); */
+ for (np = next_vsn+strlen(next_vsn)-1 ;np >= next_vsn && *np != '\\'; --np)
+ ;
+ /* np =+ length("erts-"); */
+
+ for (; lp && np; ++lp, ++np) {
+ if (*lp == *np) {
+ continue;
+ }
+ if (*np == '.' || *np == '\0' || *np <= *lp) {
+ /* */
+ return;
+ }
+ if (*lp == '.' || *lp == '\0') {
+ strcpy(latest_vsn, next_vsn);
+ return;
+ }
+ }
+ return;
+}
+
+static char *find_erlexec_dir2(char *install_dir)
+{
+ /* List install dir and look for latest erts-vsn */
+
+ HANDLE dir_handle; /* Handle to directory. */
+ char wildcard[MAX_PATH]; /* Wildcard to search for. */
+ WIN32_FIND_DATA find_data; /* Data found by FindFirstFile() or FindNext(). */
+ char latest_vsn[MAX_PATH];
+
+ /* Setup wildcard */
+ int length = strlen(install_dir);
+ char *p;
+
+ if (length+3 >= MAX_PATH) {
+ error("Cannot find erlexec.exe");
+ }
+
+ strcpy(wildcard, install_dir);
+ p = wildcard+length-1;
+ if (*p != '/' && *p != '\\')
+ *++p = '\\';
+ strcpy(++p, "erts-*");
+
+ /* Find first dir */
+ dir_handle = FindFirstFile(wildcard, &find_data);
+ if (dir_handle == INVALID_HANDLE_VALUE) {
+ /* No erts-vsn found*/
+ return NULL;
+ }
+ strcpy(latest_vsn, find_data.cFileName);
+
+ /* Find the rest */
+ while(FindNextFile(dir_handle, &find_data)) {
+ copy_latest_vsn(latest_vsn, find_data.cFileName);
+ }
+
+ FindClose(dir_handle);
+
+ p = malloc((strlen(install_dir)+1+strlen(latest_vsn)+4+1)*sizeof(char));
+
+ strcpy(p,install_dir);
+ strcat(p,"\\");
+ strcat(p,latest_vsn);
+ strcat(p,"\\bin");
+ return p;
+}
+
+static char *find_erlexec_dir(char *erlpath)
+{
+ /* Assume that the path to erl is absolute and
+ * that it is not a symbolic link*/
+
+ char *dir =_strdup(erlpath);
+ char *p;
+ char *p2;
+
+ /* Chop of base name*/
+ for (p = dir+strlen(dir)-1 ;p >= dir && *p != '\\'; --p)
+ ;
+ *p ='\0';
+ p--;
+
+ /* Check if dir path is like ...\install_dir\erts-vsn\bin */
+ for (;p >= dir && *p != '\\'; --p)
+ ;
+ p--;
+ for (p2 = p;p2 >= dir && *p2 != '\\'; --p2)
+ ;
+ p2++;
+ if (strncmp(p2, "erts-", strlen("erts-")) == 0) {
+ p = _strdup(dir);
+ free(dir);
+ return p;
+ }
+
+ /* Assume that dir path is like ...\install_dir\bin */
+ *++p ='\0'; /* chop off bin dir */
+
+ p = find_erlexec_dir2(dir);
+ free(dir);
+ if (p == NULL) {
+ error("Cannot find erlexec.exe");
+ } else {
+ return p;
+ }
+}
+
+static void get_parameters(void)
+{
+ char buffer[MAX_PATH];
+ char *ini_filename;
+ HANDLE module = GetModuleHandle(NULL);
+ InitFile *inif;
+ InitSection *inis;
+ char *bindir;
+
+ if (module = NULL) {
+ error("Cannot GetModuleHandle()");
+ }
+
+ if (GetModuleFileName(module,buffer,MAX_PATH) == 0) {
+ error("Could not GetModuleFileName");
+ }
+
+ ini_filename = replace_filename(buffer,INI_FILENAME);
+
+ if ((inif = load_init_file(ini_filename)) == NULL) {
+ erlexec_dir = find_erlexec_dir(ini_filename);
+ SetEnvironmentVariable("ERLEXEC_DIR", erlexec_dir);
+ } else {
+
+ if ((inis = lookup_init_section(inif,INI_SECTION)) == NULL) {
+ error("Could not find section %s in init file %s",
+ INI_SECTION, ini_filename);
+ }
+
+ erlexec_dir = do_lookup_in_section(inis, "Bindir", INI_SECTION, ini_filename);
+ free_init_file(inif);
+ }
+
+ erlexec_name = malloc(strlen(erlexec_dir) + strlen(ERLEXEC_BASENAME) + 2);
+ strcpy(erlexec_name,erlexec_dir);
+ strcat(erlexec_name, "\\" ERLEXEC_BASENAME);
+
+ free(ini_filename);
+}
+
+
+static void error(char* format, ...)
+{
+ char sbuf[2048];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+
+#ifndef WIN32_WERL
+ fprintf(stderr, "%s\n", sbuf);
+#else
+ MessageBox(NULL, sbuf, "Werl", MB_OK|MB_ICONERROR);
+#endif
+ exit(1);
+}
+
diff --git a/erts/etc/win32/erl.rc b/erts/etc/win32/erl.rc
new file mode 100644
index 0000000000..88213d48f2
--- /dev/null
+++ b/erts/etc/win32/erl.rc
@@ -0,0 +1,31 @@
+//
+// %CopyrightBegin%
+//
+// Copyright Ericsson AB 1998-2009. All Rights Reserved.
+//
+// The contents of this file are subject to the Erlang Public License,
+// Version 1.1, (the "License"); you may not use this file except in
+// compliance with the License. You should have received a copy of the
+// Erlang Public License along with this software. If not, it can be
+// retrieved online at http://www.erlang.org/.
+//
+// Software distributed under the License is distributed on an "AS IS"
+// basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+// the License for the specific language governing rights and limitations
+// under the License.
+//
+// %CopyrightEnd%
+//
+#include <windows.h>
+#include "resource.h"
+
+1 ICON DISCARDABLE "erlang.ico"
+
+
+
+
+
+
+
+
+
diff --git a/erts/etc/win32/erl_icon.ico b/erts/etc/win32/erl_icon.ico
new file mode 100644
index 0000000000..3e228317cd
--- /dev/null
+++ b/erts/etc/win32/erl_icon.ico
Binary files differ
diff --git a/erts/etc/win32/erl_log.c b/erts/etc/win32/erl_log.c
new file mode 100644
index 0000000000..85cc49e0e3
--- /dev/null
+++ b/erts/etc/win32/erl_log.c
@@ -0,0 +1,73 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1996-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/* -----------------------------------------------------------------
+ * erl_log:
+ *
+ * Provides a simple debug log for the Erlang emulator.
+ * It simples echoes its standard intput to the console.
+ *
+ * Author: Bjorn Gustavsson
+ * Created: 1996-12-06
+ * -----------------------------------------------------------------
+ */
+
+#include <windows.h>
+#include <stdio.h>
+
+static void print_last_error(char* message);
+
+main()
+{
+ HANDLE in;
+ HANDLE out;
+ char sbuf[256];
+ DWORD written;
+ DWORD numChars;
+
+ in = GetStdHandle(STD_INPUT_HANDLE);
+ out = CreateFile("CONOUT$", GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL, NULL);
+ if (out == INVALID_HANDLE_VALUE) {
+ print_last_error("CreateFile");
+ exit(1);
+ }
+
+ while (ReadFile(in, sbuf, sizeof(sbuf), &numChars, NULL) && numChars) {
+ WriteFile(out, sbuf, numChars, &written, NULL);
+ }
+ return 0;
+}
+
+static void print_last_error(char* message)
+{
+ LPTSTR* lpBufPtr;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpBufPtr,
+ 0,
+ NULL);
+ if (message == NULL)
+ printf("%s", lpBufPtr);
+ else
+ printf("%s: %s\n", message, lpBufPtr);
+}
diff --git a/erts/etc/win32/erlang.ico b/erts/etc/win32/erlang.ico
new file mode 100644
index 0000000000..cee8b58af9
--- /dev/null
+++ b/erts/etc/win32/erlang.ico
Binary files differ
diff --git a/erts/etc/win32/erlsrv/erlsrv_global.h b/erts/etc/win32/erlsrv/erlsrv_global.h
new file mode 100644
index 0000000000..d3922dc1e3
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_global.h
@@ -0,0 +1,37 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifndef _ERLSRV_GLOBAL_H
+#define _ERLSRV_GLOBAL_H
+
+#define APP_NAME "ErlSrv"
+
+#define ERLANG_MACHINE "erl.exe"
+
+#define SERVICE_ENV "ERLSRV_SERVICE_NAME"
+#define EXECUTABLE_ENV "ERLSRV_EXECUTABLE"
+#define DEBUG_ENV "ERLSRV_DEBUG"
+
+#ifdef _DEBUG
+#define HARDDEBUG 1
+#define DEBUG 1
+#else
+#define NDEBUG 1
+#endif
+
+#endif /* _ERLSRV_GLOBAL_H */
diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.c b/erts/etc/win32/erlsrv/erlsrv_interactive.c
new file mode 100644
index 0000000000..13e029b364
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_interactive.c
@@ -0,0 +1,1163 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <windows.h>
+#include <winsvc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "erlsrv_global.h"
+#include "erlsrv_registry.h"
+#include "erlsrv_interactive.h"
+#include "erlsrv_util.h" /* service_name */
+
+#define DBG fprintf(stderr,"argv[0]:%s line %d\n",argv[0],__LINE__)
+
+/* Really HAS to correcpond to the enum in erlsrv_registry.h */
+static char *arg_tab[] = {
+ "stopaction", "st",
+ "onfail", "on",
+ "machine", "m",
+ "env", "e",
+ "workdir", "w",
+ "priority", "p",
+ "sname", "sn",
+ "name", "n",
+ "args", "ar",
+ "debugtype", "d",
+ "internalservicename","i",
+ "comment","c",
+ NULL, NULL
+};
+
+static char *generate_real_service_name(char *display_name){
+ SYSTEMTIME systime;
+ FILETIME ftime;
+ char *buff = malloc(strlen(display_name)+
+ (8*2)+1);
+ char *tmp = _strdup(display_name);
+ int i;
+ /* 2 Hex chars for each byte in a DWORD */
+ GetSystemTime(&systime);
+ SystemTimeToFileTime(&systime,&ftime);
+ /* Remove trailing version info to avoid user confusion */
+ for(i = (strlen(tmp)-1);i > 0; --i)
+ if(tmp[i] == '_'){
+ tmp[i] = '\0';
+ break;
+ }
+ sprintf(buff,"%s%08x%08x",tmp,ftime.dwHighDateTime,
+ ftime.dwLowDateTime);
+ free(tmp);
+ return buff;
+}
+
+static int lookup_arg(char *arg){
+ int i;
+ if(*arg != '-' && *arg != '/')
+ return -1;
+ for(i=0; arg_tab[i] != NULL; i += 2){
+ if(!_strnicmp(arg_tab[i],arg+1,strlen(arg+1)) &&
+ !_strnicmp(arg_tab[i+1],arg+1,strlen(arg_tab[i+1])))
+ return (i / 2);
+ }
+ return -1;
+}
+
+
+
+char *edit_env(char *edit, char *oldenv){
+ char **arg;
+ char *value;
+ char *name = strdup(edit);
+ int i;
+ char *tmp;
+ arg = env_to_arg(oldenv);
+ value = strchr(name,'=');
+ if(value){
+ *(value++) = '\0';
+ if(*value == '\0')
+ value = NULL;
+ }
+ for(i=0;arg[i] != NULL; ++i){
+ tmp = strchr(arg[i],'=');
+ if(((int) strlen(name)) == (tmp - arg[i]) &&
+ !_strnicmp(name,arg[i], tmp - arg[i]))
+ break;
+ }
+ if(arg[i] != NULL){
+ free(arg[i]);
+ if(value){
+ arg[i] = strdup(edit);
+ } else {
+ do {
+ arg[i] = arg[i+1];
+ ++i;
+ } while(arg[i] != NULL);
+ }
+ } else if(value){ /* add to arg, which is always allocated
+ to hold one extra environment variable*/
+ arg[i] = strdup(edit);
+ arg[i+1] = NULL;
+ }
+ free(name);
+ return arg_to_env(arg);
+}
+
+int last_error = 0;
+
+void print_last_error(void){
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ (last_error) ? last_error : GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ fprintf(stderr,"Error: %s",mes);
+ LocalFree(mes);
+}
+
+static BOOL install_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ char filename[MAX_PATH + 3];
+ DWORD fnsiz=MAX_PATH;
+ char dependant[] = { 'L','a','n','m','a','n',
+ 'W','o','r','k','s','t',
+ 'a','t','i','o','n','\0','\0'};
+
+ if(!(fnsiz = GetModuleFileName(NULL, filename, fnsiz)))
+ return FALSE;
+ if(strchr(filename,' ')){
+ memmove(filename+1,filename,fnsiz);
+ filename[0] ='\"'; /* " */
+ filename[fnsiz+1] = '\"'; /* " */
+ filename[fnsiz+2] = '\0';
+ }
+ if((scm = OpenSCManager(NULL,
+ NULL,
+ SC_MANAGER_CONNECT |
+ SC_MANAGER_CREATE_SERVICE))
+ == NULL){
+ last_error = GetLastError();
+ return FALSE;
+ }
+ service = CreateService(scm,
+ real_service_name,
+ service_name,
+ SERVICE_ALL_ACCESS &
+ ~(SERVICE_PAUSE_CONTINUE),
+ SERVICE_WIN32_OWN_PROCESS,
+ SERVICE_AUTO_START,
+ SERVICE_ERROR_NORMAL,
+ filename,
+ NULL,
+ NULL,
+ dependant,
+ NULL,
+ NULL);
+ if(service == NULL){
+ CloseServiceHandle(scm);
+ last_error = GetLastError();
+ return FALSE;
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return TRUE;
+}
+
+static BOOL remove_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ if((scm = OpenSCManager(NULL,
+ NULL,
+ GENERIC_WRITE))
+ == NULL)
+ return FALSE;
+ service = OpenService(scm,
+ real_service_name,
+ SERVICE_ALL_ACCESS);
+ if(service == NULL){
+ CloseServiceHandle(scm);
+ return FALSE;
+ }
+ if(!DeleteService(service)){
+ last_error = GetLastError();
+ return FALSE;
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return TRUE;
+}
+
+static BOOL open_service_control(SC_HANDLE *scm, SC_HANDLE *service){
+ if((*scm = OpenSCManager(NULL,
+ NULL,
+ SC_MANAGER_ALL_ACCESS))
+ == NULL)
+ return FALSE;
+ *service = OpenService(*scm,
+ real_service_name,
+ SERVICE_ALL_ACCESS);
+ if(service == NULL){
+ CloseServiceHandle(*scm);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL open_service_config(SC_HANDLE *scm, SC_HANDLE *service){
+ if((*scm = OpenSCManager(NULL,
+ NULL,
+ /*GENERIC_WRITE | GENERIC_EXECUTE*/
+ SC_MANAGER_ALL_ACCESS))
+ == NULL){
+ last_error = GetLastError();
+ return FALSE;
+ }
+ *service = OpenService(*scm,
+ real_service_name,
+ /*GENERIC_WRITE*/
+ SERVICE_ALL_ACCESS);
+ if(service == NULL){
+ last_error = GetLastError();
+ CloseServiceHandle(*scm);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static BOOL set_service_comment(char *comment) {
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ SERVICE_DESCRIPTION sd;
+ BOOL ret = TRUE;
+ sd.lpDescription = comment;
+ if (!open_service_config(&scm,&service)) {
+ return FALSE;
+ }
+ if (!ChangeServiceConfig2(service,SERVICE_CONFIG_DESCRIPTION,&sd)) {
+ last_error = GetLastError();
+ ret = FALSE;
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL wait_service_trans(DWORD initial, DWORD passes, DWORD goal,
+ int timeout)
+{
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ int moved = 0;
+ BOOL ret;
+ int i;
+ SERVICE_STATUS stat;
+
+ if(! open_service_config(&scm,&service))
+ return FALSE;
+ for(i = 0; i < timeout; ++i){
+ if(!QueryServiceStatus(service,&stat)){
+ last_error = GetLastError();
+ ret = FALSE;
+ goto out;
+ }
+ if(stat.dwCurrentState == initial){
+ if(moved){
+ ret = FALSE;
+
+ /*
+ * The exitcode is usually strange when we tried to stop and failed,
+ * to report a timeout is more appropriate.
+ */
+ if(goal == SERVICE_STOPPED)
+ last_error = ERROR_SERVICE_REQUEST_TIMEOUT;
+ else
+ last_error = stat.dwWin32ExitCode;
+ goto out;
+ }
+ } else if(stat.dwCurrentState == passes){
+ moved = 1;
+ } else if(stat.dwCurrentState == goal){
+ ret = TRUE;
+ goto out;
+ }
+ Sleep(1000);
+ }
+ ret = FALSE;
+ last_error = ERROR_SERVICE_REQUEST_TIMEOUT;
+out:
+ CloseServiceHandle(scm);
+ CloseServiceHandle(service);
+ return ret;
+}
+
+static BOOL stop_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+ SERVICE_STATUS ss;
+
+ if(!open_service_control(&scm,&service)){
+#ifdef HARDDEBUG
+ fprintf(stderr,"Failed to open service.\n");
+#endif
+ return FALSE;
+ }
+ ret = ControlService(service,SERVICE_CONTROL_STOP,&ss);
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+#ifdef HARDDEBUG
+ if(!ret)
+ {
+ fprintf(stderr,"Failed to control service.\n");
+ print_last_error();
+ }
+#endif
+ return ret;
+}
+
+static BOOL start_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+ if(!open_service_control(&scm,&service))
+ return FALSE;
+
+ ret = StartService(service,0,NULL);
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL disable_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+ if(!open_service_config(&scm,&service))
+ return FALSE;
+
+ ret = ChangeServiceConfig(service,
+ SERVICE_NO_CHANGE,
+ SERVICE_DISABLED,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+ if(!ret){
+ last_error = GetLastError();
+ }
+
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL enable_service(void){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+if(!open_service_config(&scm,&service))
+ return FALSE;
+
+ ret = ChangeServiceConfig(service,
+ SERVICE_NO_CHANGE,
+ SERVICE_AUTO_START,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+static BOOL set_interactive(BOOL interactive){
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ BOOL ret;
+
+ if(!open_service_config(&scm,&service))
+ return FALSE;
+
+ ret = ChangeServiceConfig(service,
+ SERVICE_WIN32_OWN_PROCESS | ((interactive) ?
+ SERVICE_INTERACTIVE_PROCESS : 0),
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL);
+
+ if(!ret){
+ last_error = GetLastError();
+ }
+ CloseServiceHandle(service);
+ CloseServiceHandle(scm);
+ return ret;
+}
+
+
+RegEntry *old_entries = NULL;
+
+BOOL fetch_current(RegEntry *new){
+ int i;
+
+ if(!(old_entries = get_keys(service_name)))
+ return FALSE;
+ for(i=0;i<num_reg_entries;++i)
+ new[i] = old_entries[i];
+ return TRUE;
+}
+
+void cleanup_old(){
+ if(old_entries != NULL)
+ free_keys(old_entries);
+}
+
+BOOL fill_in_defaults(RegEntry *new){
+ char filename[MAX_PATH];
+ char *ptr;
+
+
+ if(!GetModuleFileName(NULL, filename, MAX_PATH))
+ return FALSE;
+ for(ptr = filename + strlen(filename) - 1;
+ ptr > filename && *ptr != '\\';
+ --ptr)
+ ;
+ if(*ptr == '\\')
+ ++ptr;
+ *ptr = '\0';
+
+ ptr = malloc(strlen(filename)+strlen(ERLANG_MACHINE)+1);
+ strcpy(ptr,filename);
+ strcat(ptr,ERLANG_MACHINE);
+
+ new[StopAction].data.bytes = "";
+ new[OnFail].data.value = ON_FAIL_IGNORE;
+ new[Machine].data.bytes = ptr;
+ new[Machine].data.expand.unexpanded = ptr;
+ new[Env].data.bytes = "\0";
+ new[WorkDir].data.bytes = new[WorkDir].data.expand.unexpanded =
+ "";
+ new[Priority].data.value = NORMAL_PRIORITY_CLASS;
+ new[SName].data.bytes = service_name;
+ new[Name].data.bytes = "";
+ new[Args].data.bytes = new[Args].data.expand.unexpanded = "";
+ new[DebugType].data.value = DEBUG_TYPE_NO_DEBUG;
+ new[InternalServiceName].data.bytes = real_service_name;
+ new[Comment].data.bytes = "";
+ return TRUE;
+}
+
+int do_usage(char *arg0){
+ printf("Usage:\n");
+ printf("%s {set | add} <servicename>\n"
+ "\t[-st[opaction] [<erlang shell command>]]\n"
+ "\t[-on[fail] [{reboot | restart | restart_always}]]\n"
+ "\t[-m[achine] [<erl-command>]]\n"
+ "\t[-e[nv] [<variable>[=<value>]]]\n"
+ "\t[-w[orkdir] [<directory>]]\n"
+ "\t[-p[riority] [{low|high|realtime}]]\n"
+ "\t[{-sn[ame] | -n[ame]} [<nodename>]]\n"
+ "\t[-d[ebugtype] [{new|reuse|console}]]\n"
+ "\t[-ar[gs] [<limited erl arguments>]]\n\n"
+ "%s {start | stop | disable | enable} <servicename>\n\n"
+ "%s remove <servicename>\n\n"
+ "%s rename <servicename> <servicename>\n\n"
+ "%s list [<servicename>]\n\n"
+ "%s help\n\n",
+ arg0,arg0,arg0,arg0,arg0,arg0);
+ printf("Manipulates Erlang system services on Windows NT.\n\n");
+ printf("When no parameter to an option is specified, the option\n"
+ "is reset to it's default value. To set an empty argument\n"
+ "list, give option -args as last option on command line "
+ "with\n"
+ "no arguments.\n\n");
+ printf("Se Erlang documentation for full description.\n");
+ return 0;
+}
+
+int do_manage(int argc,char **argv){
+ char *action = argv[1];
+ RegEntry *current = empty_reg_tab();
+
+ if(argc < 3){
+ fprintf(stderr,"%s: No servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[2];
+ if(!fetch_current(current)){
+ fprintf(stderr,"%s: The service %s is not an erlsrv controlled service.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ real_service_name = _strdup(current[InternalServiceName].data.bytes);
+ free_keys(current);
+
+ if(!_stricmp(action,"start")){
+ if(!start_service()){
+ fprintf(stderr,"%s: Failed to start service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ if(!wait_service_trans(SERVICE_STOPPED, SERVICE_START_PENDING,
+ SERVICE_RUNNING, 60)){
+ fprintf(stderr,"%s: Failed to start service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ }
+ printf("%s: Service %s started.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ if(!_stricmp(action,"stop")){
+ if(!stop_service()){
+ fprintf(stderr,"%s: Failed to stop service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ if(!wait_service_trans(SERVICE_RUNNING, SERVICE_STOP_PENDING,
+ SERVICE_STOPPED, 60)){
+ fprintf(stderr,"%s: Failed to stop service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ }
+ printf("%s: Service %s stopped.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ if(!_stricmp(action,"disable")){
+#if 0
+ if(stop_service()){
+ printf("%s: Service %s stopped.\n",
+ argv[0],service_name);
+ }
+#endif
+ if(!disable_service()){
+ fprintf(stderr,"%s: Failed to disable service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ printf("%s: Service %s disabled.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ if(!_stricmp(action,"enable")){
+ if(!enable_service()){
+ fprintf(stderr,"%s: Failed to enable service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ } else {
+ printf("%s: Service %s enabled.\n",
+ argv[0],service_name);
+ return 0;
+ }
+ }
+ fprintf(stderr,"%s: Unrecignized argument %s.\n",
+ argv[0],action);
+ return 1;
+}
+
+int do_add_or_set(int argc, char **argv){
+ RegEntry *new_entries;
+ RegEntry *default_entries;
+ int add = 0;
+ int i;
+ int current;
+ int set_comment = 0;
+ new_entries = empty_reg_tab();
+ default_entries = empty_reg_tab();
+ if(argc < 3){
+ fprintf(stderr,"%s: No servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[2];
+ if(!_stricmp(argv[1],"add")){
+ if(fetch_current(default_entries)){
+ fprintf(stderr,"%s: A service with the name %s already "
+ "exists.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ real_service_name = generate_real_service_name(service_name);
+ if(!fill_in_defaults(new_entries)){
+ fprintf(stderr,"%s: Internal error.\n", argv[0]);
+ return 1;
+ }
+ add = 1;
+ } else {
+ if(!fetch_current(new_entries)){
+ fprintf(stderr,"%s: No service with the name %s exists.\n",
+ argv[0], service_name);
+ return 1;
+ }
+ real_service_name = new_entries[InternalServiceName].data.bytes;
+ }
+
+ if(!fill_in_defaults(default_entries)){
+ fprintf(stderr,"%s: Internal error.\n", argv[0]);
+ return 1;
+ }
+
+ /* make sure env is malloced... */
+ new_entries[Env].data.bytes = envdup(new_entries[Env].data.bytes);
+
+ for(i = 3; i < argc; ++i){
+ switch((current = lookup_arg(argv[i]))){
+ case Comment:
+ set_comment = 1;
+ case Machine:
+ case WorkDir:
+ case Args:
+ if(i+1 >= argc){
+ new_entries[current].data.bytes =
+ default_entries[current].data.bytes;
+ new_entries[current].data.expand.unexpanded =
+ default_entries[current].data.expand.unexpanded;
+ } else {
+ new_entries[current].data.expand.unexpanded =
+ new_entries[current].data.bytes = argv[i+1];
+ ++i;
+ }
+ break;
+ case SName:
+ new_entries[Name].data.bytes = "";
+ case StopAction:
+ case Name:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.bytes =
+ default_entries[current].data.bytes;
+ } else {
+ new_entries[current].data.bytes = argv[i+1];
+ ++i;
+ }
+ break;
+ case OnFail:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.value =
+ default_entries[current].data.value;
+ } else {
+ if(!_stricmp(argv[i+1],"reboot"))
+ new_entries[current].data.value = ON_FAIL_REBOOT;
+ else if(!_stricmp(argv[i+1],"restart"))
+ new_entries[current].data.value = ON_FAIL_RESTART;
+ else if(!_stricmp(argv[i+1],"restart_always"))
+ new_entries[current].data.value = ON_FAIL_RESTART_ALWAYS;
+ else {
+ fprintf(stderr,"%s: Unrecognized keyword value %s.\n",
+ argv[0],argv[i+1]);
+ return 1;
+ }
+ ++i;
+ }
+ break;
+ case DebugType:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.value =
+ default_entries[current].data.value;
+ } else {
+ if(!_stricmp(argv[i+1],"new"))
+ new_entries[current].data.value = DEBUG_TYPE_NEW;
+ else if(!_stricmp(argv[i+1],"reuse"))
+ new_entries[current].data.value = DEBUG_TYPE_REUSE;
+ else if(!_stricmp(argv[i+1],"console"))
+ new_entries[current].data.value = DEBUG_TYPE_CONSOLE;
+ else {
+ fprintf(stderr,"%s: Unrecognized keyword value %s.\n",
+ argv[0],argv[i+1]);
+ return 1;
+ }
+ ++i;
+ }
+ break;
+ case Priority:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ new_entries[current].data.value =
+ default_entries[current].data.value;
+ } else {
+ if(!_stricmp(argv[i+1],"high"))
+ new_entries[current].data.value = HIGH_PRIORITY_CLASS;
+ else if(!_stricmp(argv[i+1],"low"))
+ new_entries[current].data.value = IDLE_PRIORITY_CLASS;
+ else if(!_stricmp(argv[i+1],"realtime"))
+ new_entries[current].data.value = REALTIME_PRIORITY_CLASS;
+ else {
+ fprintf(stderr,"%s: Unrecognized keyword value %s.\n",
+ argv[0],argv[i+1]);
+ return 1;
+ }
+ ++i;
+ }
+ break;
+
+ case Env:
+ if(i+1 >= argc ||
+ *argv[i+1] == '-' || *argv[i+1] == '/'){
+ fprintf(stderr,"%s: %s requires a parameter.\n",
+ argv[0],argv[i]);
+ return 1;
+ }
+ new_entries[current].data.bytes =
+ edit_env(argv[i+1],
+ new_entries[current].data.bytes);
+ ++i;
+ break;
+ case InternalServiceName:
+ if (!add) {
+ fprintf(stderr,"%s: %s only allowed when adding a new service.\n",
+ argv[0],argv[i]);
+ return 1;
+ }
+ if(i+1 >= argc){
+ fprintf(stderr,"%s: %s requires a parameter.\n",
+ argv[0],argv[i]);
+ return 1;
+ }
+ new_entries[InternalServiceName].data.expand.unexpanded =
+ new_entries[InternalServiceName].data.bytes = argv[i+1];
+ ++i;
+ /* Discard old, should maybe be fred' but we'll exit anyway */
+ real_service_name = new_entries[InternalServiceName].data.bytes;
+ break;
+ default:
+ fprintf(stderr,"%s: Unrecognized option %s.\n", argv[0],
+ argv[i]);
+ return 1;
+ }
+ }
+ if(*new_entries[SName].data.bytes &&
+ *new_entries[Name].data.bytes){
+#if 0
+ fprintf(stderr,"%s: Both -sname and -name specified.\n",
+ argv[0]);
+ return 1;
+#else
+ new_entries[SName].data.bytes = "";
+#endif
+ }
+ if(add && !(*new_entries[SName].data.bytes) &&
+ !(*new_entries[Name].data.bytes)){
+ fprintf(stderr,"%s: Neither -sname nor -name specified.\n",
+ argv[0]);
+ return 1;
+ }
+ if(add && !install_service()){
+ fprintf(stderr,"%s: Unable to register service with service manager.\n",
+ argv[0], service_name);
+ print_last_error();
+ return 1;
+ }
+ if(!set_interactive(new_entries[DebugType].data.value ==
+ DEBUG_TYPE_CONSOLE)){
+ fprintf(stderr,"%s: Warning, could not set correct interactive mode.\n",
+ argv[0], service_name);
+ print_last_error();
+ /* Not severe or??? */
+ }
+ /* Update registry */
+ register_logkeys();
+ set_keys(service_name, new_entries);
+ /* Update service comment if needed */
+ if(set_comment) {
+ if (!set_service_comment(new_entries[Comment].data.bytes)) {
+ fprintf(stderr,"%s: Warning, could not set correct "
+ "service description (comment)",
+ argv[0], service_name);
+ print_last_error();
+ }
+ }
+
+ /* As I do this, I should also clean up the new entries, which is
+ somewhat harder as I really dont know what is and what is not
+ malloced, but we'll exit anyway, so... */
+ cleanup_old();
+ if(add)
+ printf("%s: Service %s added to system.\n",
+ argv[0], service_name);
+ else
+ printf("%s: Service %s updated.\n",
+ argv[0], service_name);
+ return 0;
+}
+int do_rename(int argc, char **argv){
+ RegEntry *current = empty_reg_tab();
+ RegEntry *dummy = empty_reg_tab();
+ SC_HANDLE scm;
+ SC_HANDLE service;
+ if(argc < 3){
+ fprintf(stderr,"%s: No old servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ if(argc < 4){
+ fprintf(stderr,"%s: No new servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[3];
+ if(fetch_current(dummy)){
+ fprintf(stderr,"%s: A service with the name %s already "
+ "exists.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ service_name = argv[2];
+
+ if(!fetch_current(current)){
+ fprintf(stderr,"%s: Error, old service name %s does not exist.\n",
+ argv[0],service_name);
+ return 1;
+ }
+ real_service_name = _strdup(current[InternalServiceName].data.bytes);
+ if(!open_service_config(&scm,&service)){
+ fprintf(stderr,"%s: Error, unable to communicate with service control"
+ " manager.\n",
+ argv[0]);
+ print_last_error();
+ return 1;
+ }
+ if(!ChangeServiceConfig(service,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ SERVICE_NO_CHANGE,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ argv[3])){
+ fprintf(stderr,"%s: Error, unable to communicate with service control"
+ " manager.\n",
+ argv[0]);
+ print_last_error();
+ CloseServiceHandle(scm);
+ CloseServiceHandle(service);
+ return 1;
+ }
+ CloseServiceHandle(scm);
+ CloseServiceHandle(service);
+
+ if(remove_keys(service_name) != 0)
+ fprintf(stderr,"%s: Warning, old service parameter keys could not "
+ "be removed, continuing.\n", argv[0]);
+ /* Update registry */
+ register_logkeys();
+ set_keys(argv[3], current);
+
+ printf("%s: Service %s renamed to %s.\n",
+ argv[0], service_name, argv[3]);
+ return 0;
+}
+
+int do_remove(int argc, char **argv){
+ RegEntry *current = empty_reg_tab();
+ int rem_res;
+ BOOL found;
+
+ if(argc < 3){
+ fprintf(stderr,"%s: No servicename given!\n",argv[0]);
+ do_usage(argv[0]);
+ return 1;
+ }
+ service_name = argv[2];
+ found = fetch_current(current);
+ if(found){
+ real_service_name = _strdup(current[InternalServiceName].data.bytes);
+ } else {
+ real_service_name = _strdup(service_name);
+ }
+ if(found)
+ free_keys(current);
+ if(stop_service() && !wait_service_trans(SERVICE_RUNNING,
+ SERVICE_STOP_PENDING,
+ SERVICE_STOPPED, 60)){
+ fprintf(stderr,"%s: Failed to stop running service %s.\n",
+ argv[0],service_name);
+ print_last_error();
+ return 1;
+ }
+ if(!remove_service()){
+ fprintf(stderr,"%s: Unable to remove service (not enough "
+ "privileges?)\n",argv[0]);
+ print_last_error();
+ return 1;
+ }
+
+ if((rem_res = remove_keys(service_name)) > 0){
+ fprintf(stderr,"%s: Warning, service parameter keys belonged to old "
+ "erlsrv version.\n", argv[0]);
+ /* Backward compatibility... */
+ } else if(rem_res < 0) {
+ fprintf(stderr,"%s: Error, service parameter keys nonexistent.\n",
+ argv[0]);
+ return 1;
+ }
+ printf("%s: Service %s removed from system.\n",
+ argv[0], service_name);
+ return 0;
+}
+
+BOOL list_one(char *servicename, RegEntry *keys, BOOL longlist){
+ char *onfail;
+ char *prio;
+ char *debugtype;
+ switch(keys[OnFail].data.value){
+ case ON_FAIL_RESTART:
+ onfail = "restart";
+ break;
+ case ON_FAIL_RESTART_ALWAYS:
+ onfail = "restart_always";
+ break;
+ case ON_FAIL_REBOOT:
+ onfail = "reboot";
+ break;
+ default:
+ onfail = "ignore";
+ }
+ switch(keys[DebugType].data.value){
+ case DEBUG_TYPE_NEW:
+ debugtype = "new";
+ break;
+ case DEBUG_TYPE_REUSE:
+ debugtype = "reuse";
+ break;
+ case DEBUG_TYPE_CONSOLE:
+ debugtype = "console";
+ break;
+ default:
+ debugtype = "none";
+ }
+ switch(keys[Priority].data.value){
+ case HIGH_PRIORITY_CLASS:
+ prio = "high";
+ break;
+ case IDLE_PRIORITY_CLASS:
+ prio = "low";
+ break;
+ case REALTIME_PRIORITY_CLASS:
+ prio = "realtime";
+ break;
+ case NORMAL_PRIORITY_CLASS:
+ prio = "default";
+ break;
+ default:
+ prio = "unknown/faulty";
+ }
+
+
+ if(longlist){
+ char *env = envdup(keys[Env].data.bytes);
+ char **arg = env_to_arg(env);
+ char **pek = arg;
+ printf("Service name: %s\n",
+ servicename);
+ printf("StopAction: %s\n",
+ keys[StopAction].data.bytes);
+ printf("OnFail: %s\n",onfail);
+ printf("Machine: %s\n",
+ keys[Machine].data.expand.unexpanded);
+ printf("WorkDir: %s\n",
+ keys[WorkDir].data.expand.unexpanded);
+ if(*keys[SName].data.bytes)
+ printf("SName: %s\n",
+ keys[SName].data.bytes);
+ else
+ printf("Name: %s\n",
+ keys[Name].data.bytes);
+ printf("Priority: %s\n",prio);
+ printf("DebugType: %s\n",debugtype);
+ printf("Args: %s\n",
+ keys[Args].data.expand.unexpanded);
+ printf("InternalServiceName: %s\n",
+ keys[InternalServiceName].data.bytes);
+ printf("Comment: %s\n",
+ keys[Comment].data.bytes);
+ printf("Env:\n");
+ while(*pek){
+ printf("\t%s\n",*pek);
+ ++pek;
+ }
+ /* env is easier to free...*/
+ env = arg_to_env(arg);
+ free(env);
+ } else {
+ printf("%s\t%s\t%s\t%s\t%s\n",
+ servicename,
+ (*keys[Name].data.bytes) ?
+ keys[Name].data.bytes :
+ keys[SName].data.bytes,
+ prio,
+ onfail,
+ keys[Args].data.expand.unexpanded);
+ }
+ return TRUE;
+}
+
+
+int do_list(int argc, char **argv){
+ if(argc < 3){
+ RegEntryDesc *all_keys = get_all_keys();
+ if(!all_keys){
+ fprintf(stderr,"%s: No services found in registry.\n",
+ argv[0]);
+ return 0;
+ }
+ printf("Service\t(S)Name\tPrio\tOnFail\tArgs\n");
+ while(all_keys->servicename){
+ list_one(all_keys->servicename,all_keys->entries,FALSE);
+ ++all_keys;
+ }
+ return 0;
+ } else {
+ RegEntry *keys;
+ service_name = argv[2];
+ keys = get_keys(service_name);
+ if(!keys){
+ fprintf(stderr,"%s: Could not retrieve any "
+ "registered data for %s.\n",argv[0],service_name);
+ return 1;
+ }
+ list_one(service_name, keys, TRUE);
+ }
+ return 0;
+}
+
+#define READ_CHUNK 100
+#define ARGV_CHUNK 20
+
+char *safe_get_line(void){
+ int lsize = READ_CHUNK;
+ char *line = malloc(READ_CHUNK);
+ int pos = 0;
+ int ch;
+
+ while((ch = getchar()) != EOF && ch != '\n'){
+ if(pos + 1 >= lsize){
+ line = realloc(line,(lsize += READ_CHUNK));
+ assert(line);
+ }
+ line[pos++] = ch;
+ }
+ if(ch == EOF || !pos){
+ free(line);
+ return NULL;
+ }
+ line[pos] = '\0';
+ return line;
+}
+
+
+void read_arguments(int *pargc, char ***pargv){
+ int argc = 0;
+ int asize = ARGV_CHUNK;
+ char **argv = malloc(ARGV_CHUNK*sizeof(char *));
+ char *tmp;
+
+ argv[0] = (*pargv)[0];
+ argc = 1;
+ while((tmp = safe_get_line()) != NULL){
+ if(argc + 1 >= asize){
+ argv = realloc(argv,(asize += ARGV_CHUNK)*sizeof(char *));
+ assert(argv);
+ }
+ argv[argc++] = tmp;
+ }
+ argv[argc] = NULL;
+ *pargc = argc;
+ *pargv = argv;
+}
+
+
+int interactive_main(int argc, char **argv){
+ char *action = argv[1];
+
+ if(!_stricmp(action,"readargs")){
+ read_arguments(&argc,&argv);
+ action = argv[1];
+ }
+ if(!_stricmp(action,"set") || !_stricmp(action,"add"))
+ return do_add_or_set(argc,argv);
+ if(!_stricmp(action,"rename"))
+ return do_rename(argc,argv);
+ if(!_stricmp(action,"remove"))
+ return do_remove(argc,argv);
+ if(!_stricmp(action,"list"))
+ return do_list(argc,argv);
+ if(!_stricmp(action,"start") ||
+ !_stricmp(action,"stop") ||
+ !_stricmp(action,"enable") ||
+ !_stricmp(action,"disable"))
+ return do_manage(argc,argv);
+ if(_stricmp(action,"?") &&
+ _stricmp(action,"/?") &&
+ _stricmp(action,"-?") &&
+ *action != 'h' &&
+ *action != 'H')
+ fprintf(stderr,"%s: action %s not implemented.\n",argv[0],action);
+ do_usage(argv[0]);
+ return 1;
+}
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_interactive.h b/erts/etc/win32/erlsrv/erlsrv_interactive.h
new file mode 100644
index 0000000000..deacf81899
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_interactive.h
@@ -0,0 +1,24 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifndef _ERLSRV_INTERACTIVE_H
+#define _ERLSRV_INTERACTIVE_H
+
+int interactive_main(int argc, char **argv);
+
+#endif /* _ERLSRV_INTERACTIVE_H */
diff --git a/erts/etc/win32/erlsrv/erlsrv_logmess.mc b/erts/etc/win32/erlsrv/erlsrv_logmess.mc
new file mode 100644
index 0000000000..354ac14c9f
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_logmess.mc
@@ -0,0 +1,33 @@
+;/*MessageIDTypedef=WORD*/
+;
+;/*MessageID=0x1*/
+;/*SymbolicName=CAT_GENERIC*/
+;/*Language=English*/
+;/*Generic Category*/
+;/*.*/
+;
+MessageIDTypedef=DWORD
+
+MessageID=0x10
+Severity=Warning
+Facility=Application
+SymbolicName=MSG_WARNING
+Language=English
+%1: %2
+.
+MessageID=0x11
+Severity=Error
+Facility=Application
+SymbolicName=MSG_ERROR
+Language=English
+%1: %2
+.
+MessageID=0x12
+Severity=Informational
+Facility=Application
+SymbolicName=MSG_INFO
+Language=English
+%1: %2
+.
+
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_main.c b/erts/etc/win32/erlsrv/erlsrv_main.c
new file mode 100644
index 0000000000..920a4a1827
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_main.c
@@ -0,0 +1,44 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <windows.h>
+#include <winsvc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "erlsrv_global.h"
+#include "erlsrv_interactive.h"
+#include "erlsrv_service.h"
+
+int main(int argc, char **argv){
+ if(argc > 1)
+ return interactive_main(argc,argv);
+ else
+ return service_main(argc,argv);
+}
+
+
+
+
+
+
+
+
+
+
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.c b/erts/etc/win32/erlsrv/erlsrv_registry.c
new file mode 100644
index 0000000000..c1aa9f2b67
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_registry.c
@@ -0,0 +1,404 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <windows.h>
+#include <winsvc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "erlsrv_global.h"
+#include "erlsrv_registry.h"
+
+#define LOG_TYPE "System"
+#define LOG_ROOT \
+"SYSTEM\\CurrentControlSet\\Services\\EventLog\\" LOG_TYPE "\\"
+#define LOG_APP_KEY APP_NAME
+
+
+#define BASE_KEY HKEY_LOCAL_MACHINE
+#define PRODUCT_NAME APP_NAME
+#define OLD_PRODUCT_VERSION "1.0"
+#define PRODUCT_VERSION "1.1"
+#define PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" PRODUCT_VERSION
+#define OLD_PROG_KEY "SOFTWARE\\Ericsson\\Erlang\\" PRODUCT_NAME "\\" OLD_PRODUCT_VERSION
+
+#define MAX_KEY_LEN MAX_PATH
+
+static const char * const noString = "\0";
+
+#define MAX_MANDATORY_REG_ENTRY 10 /* InternalServiceName == reg_entries[10] */
+static RegEntry reg_entries[] = {
+ {"StopAction",REG_SZ,NULL},
+ {"OnFail",REG_DWORD,NULL},
+ {"Machine",REG_EXPAND_SZ,NULL},
+ {"Env", REG_MULTI_SZ,NULL},
+ {"WorkDir", REG_EXPAND_SZ,NULL},
+ {"Priority",REG_DWORD,NULL},
+ {"SName",REG_SZ,NULL},
+ {"Name",REG_SZ,NULL},
+ {"Args",REG_EXPAND_SZ,NULL},
+ {"DebugType",REG_DWORD,NULL},
+ {"InternalServiceName",REG_SZ,NULL},
+ /* Non mandatory follows */
+ {"Comment",REG_SZ,NULL}
+};
+
+
+int num_reg_entries = sizeof(reg_entries)/sizeof(RegEntry);
+
+RegEntry *empty_reg_tab(void){
+ RegEntry *ret = malloc(num_reg_entries * sizeof(RegEntry));
+ memcpy(ret,reg_entries,num_reg_entries * sizeof(RegEntry));
+ return ret;
+}
+
+void free_keys(RegEntry *keys){
+ int i;
+
+ for(i=0;i<num_reg_entries && keys[i].name != NULL;++i){
+ if((keys[i].type == REG_SZ || keys[i].type == REG_EXPAND_SZ ||
+ keys[i].type == REG_MULTI_SZ) &&
+ keys[i].data.bytes != noString){
+ free(keys[i].data.bytes);
+ if(keys[i].type == REG_EXPAND_SZ &&
+ keys[i].data.expand.unexpanded != noString)
+ free(keys[i].data.expand.unexpanded);
+ }
+ }
+ free(keys);
+}
+
+void free_all_keys(RegEntryDesc *descs){
+ RegEntryDesc *tmp = descs;
+ for(;tmp->servicename != NULL; ++tmp){
+ free_keys(tmp->entries);
+ free(tmp->servicename);
+ }
+ free(descs);
+}
+
+RegEntry *get_keys(char *servicename){
+ RegEntry *res = NULL;
+ HKEY prog_key;
+ int key_opened = 0;
+ int i;
+ DWORD ret;
+ char *copy;
+ char *tmpbuf;
+ DWORD tmpbuflen;
+
+ char key_to_open[MAX_KEY_LEN];
+
+ DWORD val_type;
+ char *val_data = malloc(MAX_KEY_LEN);
+ DWORD val_datalen;
+ DWORD val_datasiz = MAX_KEY_LEN;
+
+ if(strlen(PROG_KEY) + strlen(servicename) + 2 > MAX_KEY_LEN)
+ goto error;
+ sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename);
+
+ if(RegOpenKeyEx(BASE_KEY,
+ key_to_open,
+ 0,
+ KEY_QUERY_VALUE,
+ &prog_key) != ERROR_SUCCESS)
+ goto error;
+ key_opened = 1;
+
+ res = malloc(num_reg_entries*sizeof(RegEntry));
+ for(i=0;i<num_reg_entries;++i)
+ res[i].name = NULL;
+
+ for(i=0;i<num_reg_entries;++i){
+ for(;;){
+ val_datalen = val_datasiz;
+ ret = RegQueryValueEx(prog_key,
+ reg_entries[i].name,
+ NULL,
+ &val_type,
+ (BYTE *) val_data,
+ &val_datalen);
+ if(ret == ERROR_SUCCESS){
+ if(reg_entries[i].type == val_type)
+ break;
+ else
+ goto error;
+ } else if(ret == ERROR_MORE_DATA){
+ val_data = realloc(val_data,val_datasiz = val_datalen);
+ } else if (i > MAX_MANDATORY_REG_ENTRY && ret == ERROR_FILE_NOT_FOUND) {
+ /* Non mandatory entries, look at the type... */
+ switch (reg_entries[i].type){
+ case REG_EXPAND_SZ:
+ case REG_SZ:
+ case REG_MULTI_SZ:
+ val_datalen = 0;
+ break;
+ case REG_DWORD:
+ {
+ DWORD dummy = 0;
+ memcpy(val_data,&dummy,(val_datalen = sizeof(DWORD)));
+ }
+ break;
+ default:
+ goto error;
+ }
+ break; /* for(;;) */
+ } else {
+ goto error;
+ }
+ }
+ res[i] = reg_entries[i];
+ copy = NULL;
+ switch(reg_entries[i].type){
+ case REG_EXPAND_SZ:
+ if(!val_datalen || val_data[0] == '\0'){
+ copy = (char *) noString;
+ res[i].data.expand.unexpanded = (char *) noString;
+ } else {
+ tmpbuf = malloc(MAX_KEY_LEN);
+ tmpbuflen = (DWORD) MAX_KEY_LEN;
+ for(;;){
+ ret = ExpandEnvironmentStrings(val_data,tmpbuf,tmpbuflen);
+ if(!ret){
+ free(tmpbuf);
+ goto error;
+ }else if(ret > tmpbuflen){
+ tmpbuf=realloc(tmpbuf,tmpbuflen=ret);
+ } else {
+ copy = strdup(tmpbuf);
+ free(tmpbuf);
+ break;
+ }
+ }
+ res[i].data.expand.unexpanded = strdup(val_data);
+ }
+ case REG_MULTI_SZ:
+ case REG_SZ:
+ if(!copy){
+ if(!val_datalen ||
+ ((val_datalen == 1 && val_data[0] == '\0') ||
+ (val_datalen == 2 && val_data[0] == '\0' &&
+ val_data[1] == '\0'))){
+ copy = (char *) noString;
+ } else {
+ copy = malloc(val_datalen);
+ memcpy(copy,val_data,val_datalen);
+ }
+ }
+ res[i].data.bytes = copy;
+ break;
+ case REG_DWORD:
+ memcpy(&res[i].data.value,val_data,sizeof(DWORD));
+ break;
+ default:
+ goto error;
+ }
+ }
+ RegCloseKey(prog_key);
+ free(val_data);
+ return res;
+error:
+ free(val_data);
+ if(res != NULL)
+ free_keys(res);
+ if(key_opened)
+ RegCloseKey(prog_key);
+ return NULL;
+}
+
+int set_keys(char *servicename, RegEntry *keys){
+ HKEY prog_key;
+ int key_opened = 0;
+ int i;
+ char key_to_open[MAX_KEY_LEN];
+ DWORD disposition;
+
+ if(strlen(PROG_KEY) + strlen(servicename) + 2 > MAX_KEY_LEN)
+ goto error;
+ sprintf(key_to_open,"%s\\%s",PROG_KEY,servicename);
+
+ if(RegOpenKeyEx(BASE_KEY,
+ key_to_open,
+ 0,
+ KEY_SET_VALUE,
+ &prog_key) != ERROR_SUCCESS){
+ if(RegCreateKeyEx(BASE_KEY,
+ key_to_open,
+ 0,
+ NULL,
+ REG_OPTION_NON_VOLATILE,
+ KEY_SET_VALUE,
+ NULL,
+ &prog_key,
+ &disposition) != ERROR_SUCCESS)
+ goto error;
+ }
+ key_opened = 1;
+
+
+ for(i=0;i<num_reg_entries;++i){
+ void *ptr;
+ DWORD siz;
+ int j;
+ switch(keys[i].type){
+ case REG_SZ:
+ ptr = keys[i].data.bytes;
+ siz = strlen(ptr)+1;
+ break;
+ case REG_EXPAND_SZ:
+ ptr = keys[i].data.expand.unexpanded;
+ siz = strlen(ptr)+1;
+ break;
+ case REG_MULTI_SZ:
+ ptr = keys[i].data.bytes;
+ for(j=0;!(((char *)ptr)[j] == '\0' &&
+ ((char *)ptr)[j+1] == '\0');++j)
+ ;
+ siz=(DWORD)j+2;
+ break;
+ case REG_DWORD:
+ ptr = &keys[i].data.value;
+ siz = sizeof(DWORD);
+ break;
+ default:
+ goto error;
+ }
+#ifdef HARDDEBUG
+ fprintf(stderr,"%s %s:%d\n",keys[i].name,
+ (keys[i].type == REG_DWORD) ? "(dword)" : ptr,siz);
+#endif
+ if(RegSetValueEx(prog_key,
+ keys[i].name,
+ 0,
+ keys[i].type,
+ ptr,
+ siz) != ERROR_SUCCESS)
+ goto error;
+ }
+ RegCloseKey(prog_key);
+ return 0;
+error:
+ if(key_opened)
+ RegCloseKey(prog_key);
+ return 1;
+}
+
+static int do_remove_keys(char *servicename, const char *prog_key_name){
+ HKEY prog_key;
+ if(RegOpenKeyEx(BASE_KEY,
+ prog_key_name,
+ 0,
+ KEY_ALL_ACCESS,
+ &prog_key) != ERROR_SUCCESS)
+ return -1;
+ if(RegDeleteKey(prog_key,servicename) != ERROR_SUCCESS){
+ RegCloseKey(prog_key);
+ return -1;
+ }
+ RegCloseKey(prog_key);
+ return 0;
+}
+
+int remove_keys(char *servicename){
+ int ret;
+
+ if((ret = do_remove_keys(servicename, PROG_KEY)) < 0){
+ if(!do_remove_keys(servicename, OLD_PROG_KEY))
+ return 1;
+ else
+ return -1;
+ }
+ return ret;
+}
+
+
+RegEntryDesc *get_all_keys(void){
+ RegEntryDesc *res = malloc(10*sizeof(RegEntryDesc));
+ int res_siz = 10;
+ int ndx = 0;
+ HKEY prog_key;
+ int key_opened = 0;
+ DWORD enum_index;
+ char name[MAX_KEY_LEN];
+ DWORD namelen;
+ char class[MAX_KEY_LEN];
+ DWORD classlen;
+ FILETIME ft;
+
+ res[ndx].servicename = NULL;
+ if(RegOpenKeyEx(BASE_KEY, PROG_KEY, 0,
+ KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS,
+ &prog_key) != ERROR_SUCCESS)
+ goto error;
+ key_opened = 1;
+ for(enum_index = 0, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN;
+ ERROR_SUCCESS == RegEnumKeyEx(prog_key,
+ enum_index,
+ name,
+ &namelen,
+ NULL,
+ class,
+ &classlen,
+ &ft);
+ ++enum_index, namelen = MAX_KEY_LEN, classlen = MAX_KEY_LEN){
+ if(ndx >= res_siz - 1)
+ res = realloc(res, (res_siz += 10)*sizeof(RegEntryDesc));
+ if(!(res[ndx].entries = get_keys(name)))
+ goto error;
+ res[ndx].servicename = strdup(name);
+ res[++ndx].servicename = NULL;
+ }
+ RegCloseKey(prog_key);
+ return res;
+error:
+ if(key_opened)
+ RegCloseKey(prog_key);
+ free_all_keys(res);
+ return NULL;
+}
+
+int register_logkeys(void){
+ HKEY key;
+ DWORD disposition;
+ DWORD types = EVENTLOG_ERROR_TYPE |
+ EVENTLOG_WARNING_TYPE |
+ EVENTLOG_INFORMATION_TYPE;
+ DWORD catcount=1;
+ char filename[2048];
+ DWORD fnsiz=2048;
+
+ if(RegCreateKeyEx(HKEY_LOCAL_MACHINE,
+ LOG_ROOT LOG_APP_KEY, 0,
+ NULL, REG_OPTION_NON_VOLATILE,
+ KEY_SET_VALUE, NULL,
+ &key, &disposition) != ERROR_SUCCESS)
+ return -1;
+ if(!GetModuleFileName(NULL, filename, fnsiz))
+ return -1;
+ if(RegSetValueEx(key, "EventMessageFile",
+ 0, REG_EXPAND_SZ, (LPBYTE) filename,
+ strlen(filename)+1) != ERROR_SUCCESS)
+ return -1;
+ if(RegSetValueEx(key, "TypesSupported",
+ 0, REG_DWORD, (LPBYTE) &types,
+ sizeof(DWORD)) != ERROR_SUCCESS)
+ return -1;
+ return 0;
+}
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_registry.h b/erts/etc/win32/erlsrv/erlsrv_registry.h
new file mode 100644
index 0000000000..fbccc5416a
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_registry.h
@@ -0,0 +1,76 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifndef _ERLSRV_REGISTRY_H
+#define _ERLSRV_REGISTRY_H
+
+typedef struct _reg_entry {
+ char *name;
+ DWORD type;
+ union {
+ char *bytes;
+ DWORD value;
+ struct {
+ char *bytes;
+ char *unexpanded;
+ } expand;
+ } data;
+} RegEntry;
+
+typedef struct _reg_entry_desc {
+ char *servicename;
+ RegEntry *entries;
+} RegEntryDesc;
+
+enum {
+ StopAction,
+ OnFail,
+ Machine,
+ Env,
+ WorkDir,
+ Priority,
+ SName,
+ Name,
+ Args,
+ DebugType,
+ InternalServiceName,
+ Comment
+};
+
+#define ON_FAIL_IGNORE 0
+#define ON_FAIL_RESTART 1
+#define ON_FAIL_REBOOT 2
+#define ON_FAIL_RESTART_ALWAYS 3
+
+#define DEBUG_TYPE_NO_DEBUG 0
+#define DEBUG_TYPE_NEW 1
+#define DEBUG_TYPE_REUSE 2
+#define DEBUG_TYPE_CONSOLE 3
+
+extern int num_reg_entries;
+
+RegEntry *empty_reg_tab(void);
+void free_keys(RegEntry *keys);
+void free_all_keys(RegEntryDesc *descs);
+RegEntry *get_keys(char *servicename);
+int set_keys(char *servicename, RegEntry *keys);
+RegEntryDesc *get_all_keys(void);
+int remove_keys(char *servicename);
+int register_logkeys(void);
+#endif /* _ERLSRV_REGISTRY_H */
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.c b/erts/etc/win32/erlsrv/erlsrv_service.c
new file mode 100644
index 0000000000..a58ee862c5
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_service.c
@@ -0,0 +1,966 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <windows.h>
+#include <winsvc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "erlsrv_global.h"
+#include "erlsrv_registry.h"
+#include "erlsrv_util.h"
+#include "erlsrv_service.h"
+
+static HANDLE eventStop;
+
+static HANDLE eventKillErlang;
+
+static CRITICAL_SECTION crit;
+
+static SERVICE_STATUS_HANDLE statusHandle;
+
+static DWORD currentState;
+
+static void fill_status(SERVICE_STATUS *status){
+ status->dwServiceType = SERVICE_WIN32_OWN_PROCESS;
+ status->dwCurrentState = 0;
+ status->dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN;
+ status->dwWin32ExitCode = NO_ERROR;
+ status->dwServiceSpecificExitCode = 0;
+ status->dwCheckPoint = 0;
+ status->dwWaitHint = 0;
+}
+
+static BOOL set_start_pending(int waithint, int checkpoint){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_START_PENDING;
+ LeaveCriticalSection(&crit);
+ stat.dwControlsAccepted = 0;
+ stat.dwCheckPoint = checkpoint;
+ stat.dwWaitHint = waithint;
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL set_stop_pending(int waithint, int checkpoint){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_STOP_PENDING;
+ LeaveCriticalSection(&crit);
+ stat.dwControlsAccepted = 0;
+ stat.dwCheckPoint = checkpoint;
+ stat.dwWaitHint = waithint;
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL set_running(){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_RUNNING;
+ LeaveCriticalSection(&crit);
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL set_stopped(int error){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ currentState = stat.dwCurrentState = SERVICE_STOPPED;
+ LeaveCriticalSection(&crit);
+ stat.dwWin32ExitCode = error;
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static BOOL reset_current(){
+ SERVICE_STATUS stat;
+ fill_status(&stat);
+ EnterCriticalSection(&crit);
+ stat.dwCurrentState = currentState;
+ LeaveCriticalSection(&crit);
+ return SetServiceStatus(statusHandle, &stat);
+}
+
+static VOID WINAPI handler(DWORD control){
+ char buffer[1024];
+ sprintf(buffer,"handler called with control = %d.",(int) control);
+ log_debug(buffer);
+ switch(control){
+ case SERVICE_CONTROL_STOP:
+ set_stop_pending(30000,1);
+ SetEvent(eventStop);
+ return;
+ case SERVICE_CONTROL_SHUTDOWN:
+ return;
+ default:
+ reset_current();
+ break;
+ }
+ return;
+}
+
+typedef struct _server_info {
+ RegEntry *keys;
+ PROCESS_INFORMATION info;
+ HANDLE erl_stdin;
+ char *event_name;
+} ServerInfo;
+
+
+typedef struct {
+ BOOL initialized;
+ TOKEN_DEFAULT_DACL *defdacl;
+ PACL newacl;
+ PSID adminsid;
+} SaveAclStruct;
+
+
+static BOOL reset_acl(SaveAclStruct *save_acl){
+ HANDLE tokenh;
+
+ if(!save_acl->initialized)
+ return FALSE;
+ if(!OpenProcessToken(GetCurrentProcess(),
+ TOKEN_READ|TOKEN_WRITE,&tokenh)){
+ log_warning("Failed to open access token.");
+ return FALSE;
+ }
+ save_acl->initialized = FALSE;
+ if(!SetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ save_acl->defdacl,
+ sizeof(TOKEN_DEFAULT_DACL))){
+ log_warning("Failed to get default ACL from token.");
+ CloseHandle(tokenh);
+ LocalFree(save_acl->defdacl);
+ LocalFree(save_acl->newacl);
+ FreeSid(save_acl->adminsid);
+ return FALSE;
+ }
+ CloseHandle(tokenh);
+ LocalFree(save_acl->defdacl);
+ LocalFree(save_acl->newacl);
+ FreeSid(save_acl->adminsid);
+ return TRUE;
+}
+
+
+static BOOL new_acl(SaveAclStruct *save_acl){
+ HANDLE tokenh;
+ TOKEN_DEFAULT_DACL newdacl;
+ DWORD required;
+ PACL oldacl;
+ PACL newacl;
+ int i;
+ ACL_SIZE_INFORMATION si;
+ size_t newsize;
+ PSID extra_sid;
+ SID_IDENTIFIER_AUTHORITY nt_auth = SECURITY_NT_AUTHORITY;
+ TOKEN_DEFAULT_DACL dummy;
+
+ save_acl->initialized = FALSE;
+ if(!OpenProcessToken(GetCurrentProcess(),
+ TOKEN_READ|TOKEN_WRITE,&tokenh)){
+ log_warning("Failed to open access token.");
+ return FALSE;
+ }
+ save_acl->defdacl = &dummy;
+ required = sizeof(TOKEN_DEFAULT_DACL);
+ GetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ &(save_acl->defdacl),
+ sizeof(TOKEN_DEFAULT_DACL),
+ &required);
+ if(required == 0){
+ log_warning("Failed to get any ACL info from token.");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ save_acl->defdacl = LocalAlloc(LPTR,required);
+ if(!GetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ save_acl->defdacl,
+ required,
+ &required)){
+#ifdef HARDDEBUG
+ {
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ log_info(mes);
+ LocalFree(mes);
+ }
+#endif
+ log_warning("Failed to get default ACL from token.");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ oldacl = save_acl->defdacl->DefaultDacl;
+ if(!GetAclInformation(oldacl, &si, sizeof(si),
+ AclSizeInformation)){
+ log_warning("Failed to get size information for ACL");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ if(!AllocateAndInitializeSid(&nt_auth,
+ 2,
+ SECURITY_BUILTIN_DOMAIN_RID,
+ DOMAIN_ALIAS_RID_ADMINS,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ 0,
+ &extra_sid)){
+ log_warning("Failed to initialize administrator SID.");
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ newsize = si.AclBytesInUse + sizeof(ACL) +
+ sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(extra_sid);
+
+ newacl = LocalAlloc(LPTR,newsize);
+
+ if(!InitializeAcl(newacl, newsize, ACL_REVISION)){
+ log_warning("Failed to initialize new ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+
+ for(i=0;i<((int)si.AceCount);++i){
+ ACE_HEADER *ace_header;
+ if (!GetAce (oldacl, i, &ace_header)){
+ log_warning("Failed to get ACE from old ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ if(!AddAce(newacl,ACL_REVISION,0xffffffff,ace_header,
+ ace_header->AceSize)){
+ log_warning("Failed to set ACE in new ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ }
+ if(!AddAccessAllowedAce(newacl,
+ ACL_REVISION2,
+ PROCESS_ALL_ACCESS,
+ extra_sid)){
+ log_warning("Failed to add system ACE to new ACL.");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ return FALSE;
+ }
+
+ newdacl.DefaultDacl = newacl;
+ if(!SetTokenInformation(tokenh,
+ TokenDefaultDacl,
+ &newdacl,
+ sizeof(newdacl))){
+ log_warning("Failed to set token information");
+ LocalFree(newacl);
+ FreeSid(extra_sid);
+ CloseHandle(tokenh);
+ return FALSE;
+ }
+ save_acl->initialized = TRUE;
+ save_acl->newacl = newacl;
+ save_acl->adminsid = extra_sid;
+ CloseHandle(tokenh);
+
+ return TRUE;
+}
+
+static char **find_arg(char **arg, char *str){
+ char *tmp;
+ int len;
+
+ str = strdup(str);
+ if((tmp = strchr(str,'=')) == NULL)
+ goto fail;
+ tmp++;
+ *tmp = '\0';
+ len = tmp - str;
+ while(*arg != NULL){
+ if(!_strnicmp(*arg,str,len)){
+ free(str);
+ return arg;
+ }
+ ++arg;
+ }
+fail:
+ free(str);
+ return NULL;
+}
+
+static char **merge_environment(char *current, char *add){
+ char **c_arg = env_to_arg(envdup(current));
+ char **a_arg = env_to_arg(envdup(add));
+ char **new;
+ char **tmp;
+ int i,j;
+
+ for(i=0;c_arg[i] != NULL;++i)
+ ;
+ for(j=0;a_arg[j] != NULL;++j)
+ ;
+
+ new = malloc(sizeof(char *)*(i + j + 3));
+
+ for(i = 0; c_arg[i] != NULL; ++i)
+ new[i] = strdup(c_arg[i]);
+
+ new[i] = NULL;
+
+ for(j = 0; a_arg[j] != NULL; ++j){
+ if((tmp = find_arg(new,a_arg[j])) != NULL){
+ free(*tmp);
+ *tmp = strdup(a_arg[j]);
+ } else {
+ new[i++] = strdup(a_arg[j]);
+ new[i] = NULL;
+ }
+ }
+ free(arg_to_env(c_arg));
+ free(arg_to_env(a_arg));
+ return new;
+}
+
+
+static char *get_next_debug_file(char *prefix){
+ char *buffer = malloc(strlen(prefix)+12);
+ int i;
+ for(i=1;i<100;++i){
+ sprintf(buffer,"%s.%d",prefix,i);
+ if(GetFileAttributes(buffer) == 0xFFFFFFFF)
+ return buffer;
+ }
+ return NULL;
+}
+
+
+
+static BOOL start_a_service(ServerInfo *srvi){
+ STARTUPINFO start;
+ char execbuff[MAX_PATH*4]; /* FIXME: Can get overflow! */
+ char namebuff[MAX_PATH];
+ char errbuff[MAX_PATH*4]; /* hmmm.... */
+ HANDLE write_pipe = NULL, read_pipe = NULL;
+ SECURITY_ATTRIBUTES pipe_security;
+ SECURITY_ATTRIBUTES attr;
+ HANDLE nul;
+ SaveAclStruct save_acl;
+ char *my_environ;
+ BOOL console_allocated = FALSE;
+
+ if(!(*(srvi->keys[Env].data.bytes))){
+ my_environ = NULL;
+ } else {
+ char *tmp;
+ char **merged = merge_environment((tmp = GetEnvironmentStrings()),
+ srvi->keys[Env].data.bytes);
+ FreeEnvironmentStrings(tmp);
+ my_environ = arg_to_env(merged);
+ }
+
+ if(!*(srvi->keys[Machine].data.bytes) ||
+ (!*(srvi->keys[SName].data.bytes) &&
+ !*(srvi->keys[Name].data.bytes))){
+ log_error("Not enough parameters for erlang service.");
+ if(my_environ)
+ free(my_environ);
+ return FALSE;
+ }
+
+ if(*(srvi->keys[SName].data.bytes))
+ sprintf(namebuff,"-nohup -sname %s",srvi->keys[SName].data.bytes);
+ else
+ sprintf(namebuff,"-nohup -name %s",srvi->keys[Name].data.bytes);
+
+ if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE)
+ strcat(namebuff," -keep_window");
+
+ if (srvi->event_name != NULL) {
+ sprintf(execbuff,"\"%s\" -service_event %s %s %s",
+ srvi->keys[Machine].data.bytes,
+ srvi->event_name,
+ namebuff,
+ srvi->keys[Args].data.bytes);
+ } else {
+ sprintf(execbuff,"\"%s\" %s %s",
+ srvi->keys[Machine].data.bytes,
+ namebuff,
+ srvi->keys[Args].data.bytes);
+ }
+
+ memset (&start, 0, sizeof (start));
+ start.cb = sizeof (start);
+ start.dwFlags = STARTF_USESHOWWINDOW;
+ start.wShowWindow = SW_HIDE;
+
+ /* Console debugging implies no working StopAction */
+ if(srvi->keys[DebugType].data.value == DEBUG_TYPE_CONSOLE) {
+ COORD coord = {80,999};
+ if(console_allocated = AllocConsole())
+ SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE),coord);
+ else
+ log_warning("Unable to allocate debugging console!");
+ } else if(*(srvi->keys[StopAction].data.bytes) ||
+ srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){
+ pipe_security.nLength = sizeof(pipe_security);
+ pipe_security.lpSecurityDescriptor = NULL;
+ pipe_security.bInheritHandle = TRUE;
+ if(!CreatePipe(&read_pipe,&write_pipe,&pipe_security,0)){
+ log_error("Could not create pipe for erlang service.");
+ if(my_environ)
+ free(my_environ);
+ return FALSE;
+ }
+ if(srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG){
+ char *filename;
+ if(*(srvi->keys[WorkDir].data.bytes)){
+ filename = malloc(strlen(srvi->keys[WorkDir].data.bytes) + 1 +
+ strlen(service_name)+strlen(".debug")+1);
+ sprintf(filename,"%s\\%s.debug",
+ srvi->keys[WorkDir].data.bytes,
+ service_name);
+ } else {
+ filename = malloc(strlen(service_name)+strlen(".debug")+1);
+ sprintf(filename,"%s.debug",service_name);
+ }
+ log_debug(filename);
+
+ if(srvi->keys[DebugType].data.value == DEBUG_TYPE_NEW){
+ char *tmpfn = get_next_debug_file(filename);
+ if(tmpfn){
+ free(filename);
+ filename = tmpfn;
+ } else {
+ log_warning("Number of debug files exceeds system defined "
+ "limit, reverting to DebugType: reuse. ");
+ }
+ }
+
+
+ nul = CreateFile(filename,
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &pipe_security,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ free(filename);
+ } else { /* Not debugging */
+ nul = CreateFile("NUL",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_READ | FILE_SHARE_WRITE,
+ &pipe_security,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL);
+ }
+ if(nul == NULL){
+ log_error((srvi->keys[DebugType].data.value != DEBUG_TYPE_NO_DEBUG)
+ ? "Could not create debug file. "
+ "(Working directory not valid?)"
+ : "Cold not open NUL!");
+ start.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ start.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+ }
+ start.hStdOutput = nul;
+ start.hStdError = nul;
+ start.hStdInput = read_pipe;
+ start.dwFlags |= STARTF_USESTDHANDLES;
+ }
+
+ attr.nLength = sizeof(attr);
+ attr.lpSecurityDescriptor = NULL;
+ attr.bInheritHandle = TRUE;
+
+ new_acl(&save_acl);
+
+ if(!CreateProcess(NULL,
+ execbuff,
+ &attr,
+ NULL,
+ (read_pipe != NULL),
+ CREATE_DEFAULT_ERROR_MODE |
+ (srvi->keys[Priority].data.value),
+ my_environ,
+ (*(srvi->keys[WorkDir].data.bytes)) ?
+ srvi->keys[WorkDir].data.bytes : NULL,
+ &start,
+ &(srvi->info))){
+ sprintf(errbuff,"Could not start erlang service"
+ "with commandline \"%s\".",
+ service_name,
+ execbuff
+ );
+ log_error(errbuff);
+ if(read_pipe != NULL){
+ CloseHandle(read_pipe);
+ CloseHandle(write_pipe);
+ if(nul != NULL)
+ CloseHandle(nul);
+ }
+ if(console_allocated)
+ FreeConsole();
+ reset_acl(&save_acl);
+ if(my_environ)
+ free(my_environ);
+ return FALSE;
+ }
+ if(console_allocated)
+ FreeConsole();
+#ifdef HARDDEBUG
+ sprintf(errbuff,
+ "Started %s with the following commandline: "
+ "%s",service_name,execbuff);
+ log_debug(errbuff);
+#endif
+ if(read_pipe != NULL){
+ CloseHandle(read_pipe);
+ if(nul != NULL)
+ CloseHandle(nul);
+ srvi->erl_stdin = write_pipe;
+ }
+
+ reset_acl(&save_acl);
+ if(my_environ)
+ free(my_environ);
+ return TRUE;
+}
+
+static HANDLE create_erlang_event(char *event_name)
+{
+ HANDLE e;
+ if ((e = OpenEvent(EVENT_ALL_ACCESS,FALSE,event_name)) == NULL) {
+ if ((e = CreateEvent(NULL, TRUE, FALSE, event_name)) == NULL) {
+ log_warning("Could not create or access erlang termination event");
+ }
+ } else {
+ if (!ResetEvent(e)) {
+ log_warning("Could not reset erlang termination event.");
+ }
+ }
+ return e;
+}
+
+static BOOL stop_erlang(ServerInfo *srvi, int waithint,
+ int *checkpoint){
+ DWORD written = 0;
+ char *action = srvi->keys[StopAction].data.bytes;
+ DWORD towrite = strlen(action)+1;
+ char *toerl;
+ DWORD exitcode;
+ int i;
+ int kill;
+
+ if(towrite > 2 && srvi->erl_stdin != NULL){
+ toerl = malloc(towrite+1);
+ strcpy(toerl,action);
+ strcat(toerl,"\n");
+ WriteFile(srvi->erl_stdin, toerl, towrite, &written,0);
+ free(toerl);
+ /* Give it 45 seconds to terminate */
+ for(i=0;i<45;++i){
+ if(WaitForSingleObject(srvi->info.hProcess, 1000) ==
+ WAIT_OBJECT_0){
+ GetExitCodeProcess(srvi->info.hProcess,&exitcode);
+ CloseHandle(srvi->info.hProcess);
+ CloseHandle(srvi->info.hThread);
+ return TRUE;
+ }
+ ++(*checkpoint);
+ set_stop_pending(waithint,*checkpoint);
+ }
+ log_warning("StopAction did not terminate erlang. Trying forced kill.");
+ }
+ log_debug("Terminating erlang...");
+ kill = 1;
+ if(eventKillErlang != NULL && SetEvent(eventKillErlang) != 0){
+ for(i=0;i<10;++i){
+ if(WaitForSingleObject(srvi->info.hProcess, 1000) == WAIT_OBJECT_0){
+ kill = 0;
+ break;
+ }
+ ++(*checkpoint);
+ set_stop_pending(waithint,*checkpoint);
+ }
+ } else {
+#ifdef HARDDEBUG
+ {
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ log_info(mes);
+ LocalFree(mes);
+ }
+#endif
+ log_debug("Could not send control event to Erlang process");
+ }
+ if(kill){
+ log_warning("Using TerminateProcess to kill erlang.");
+ if(!TerminateProcess(srvi->info.hProcess,NO_ERROR))
+ log_error("TerminateProcess failed");
+ }
+ GetExitCodeProcess(srvi->info.hProcess,&exitcode);
+ CloseHandle(srvi->info.hProcess);
+ CloseHandle(srvi->info.hThread);
+ if (eventKillErlang != NULL) {
+ ResetEvent(eventKillErlang);
+ }
+ return TRUE;
+}
+
+static BOOL enable_privilege(void) {
+ HANDLE ProcessHandle;
+ DWORD DesiredAccess = TOKEN_ADJUST_PRIVILEGES;
+ HANDLE TokenHandle;
+ TOKEN_PRIVILEGES Tpriv;
+ LUID luid;
+ ProcessHandle = GetCurrentProcess();
+ OpenProcessToken(ProcessHandle, DesiredAccess, &TokenHandle);
+ LookupPrivilegeValue(0,SE_SHUTDOWN_NAME,&luid);
+ Tpriv.PrivilegeCount = 1;
+ Tpriv.Privileges[0].Luid = luid;
+ Tpriv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
+ return AdjustTokenPrivileges(TokenHandle,FALSE,&Tpriv,0,0,0);
+}
+
+static BOOL pull_service_name(void){
+ SC_HANDLE scm;
+ DWORD sz = 1024;
+ static char service_name_buff[1024];
+ if((scm = OpenSCManager(NULL,
+ NULL,
+ GENERIC_READ))
+ == NULL){
+ return FALSE;
+ }
+ if(!GetServiceDisplayName(scm,real_service_name,service_name_buff,&sz))
+ return FALSE;
+ CloseServiceHandle(scm);
+ service_name = service_name_buff;
+ return TRUE;
+}
+
+
+static VOID WINAPI service_main_loop(DWORD argc, char **argv){
+ int waithint = 30000;
+ int checkpoint = 1;
+ RegEntry *keys;
+ RegEntry *save_keys;
+ ServerInfo srvi;
+ HANDLE harr[2];
+ FILETIME creationt,exitt,kernelt,usert;
+ LONGLONG creationl,exitl,diffl;
+ char event_name[MAX_PATH] = "ErlSrv_";
+ char executable_name[MAX_PATH];
+#ifdef DEBUG
+ char errorbuff[2048]; /* FIXME... */
+#endif
+ int success_wait = NO_SUCCESS_WAIT;
+
+ real_service_name = argv[0];
+ if(!pull_service_name()){
+ log_error("Could not get Display name of erlang service.");
+ set_stopped(ERROR_CANTREAD);
+ return;
+ }
+
+ SetEnvironmentVariable((LPCTSTR) SERVICE_ENV, (LPCTSTR) service_name);
+
+ strncat(event_name, service_name, MAX_PATH - strlen(event_name));
+ event_name[MAX_PATH - 1] = '\0';
+
+ if(!GetModuleFileName(NULL, executable_name, MAX_PATH)){
+ log_error("Unable to retrieve module file name, " EXECUTABLE_ENV
+ " will not be set.");
+ } else {
+ char quoted_exe_name[MAX_PATH+4];
+ sprintf(quoted_exe_name, "\"%s\"", executable_name);
+ SetEnvironmentVariable((LPCTSTR) EXECUTABLE_ENV,
+ (LPCTSTR) quoted_exe_name);
+ }
+
+ log_debug("Here we go, service_main_loop...");
+ currentState = SERVICE_START_PENDING;
+ InitializeCriticalSection(&crit);
+ eventStop = CreateEvent(NULL,FALSE,FALSE,NULL);
+ if ((eventKillErlang = create_erlang_event(event_name)) != NULL) {
+ srvi.event_name = event_name;
+ } else {
+ srvi.event_name = NULL;
+ }
+ statusHandle = RegisterServiceCtrlHandler(real_service_name, &handler);
+ if(!statusHandle)
+ return;
+ set_start_pending(waithint,checkpoint);
+ keys = get_keys(service_name);
+ if(!keys){
+ log_error("Could not get registry keys for erlang service.");
+ set_stopped(ERROR_CANTREAD);
+ return;
+ }
+ srvi.keys = keys;
+ srvi.erl_stdin = NULL;
+
+ ++checkpoint;
+ if(!start_a_service(&srvi)){
+ log_error("Could not start erlang machine");
+ set_stopped(ERROR_PROCESS_ABORTED);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ set_start_pending(waithint,checkpoint);
+ set_running();
+ success_wait = INITIAL_SUCCESS_WAIT;
+ harr[0] = srvi.info.hProcess;
+ harr[1] = eventStop;
+ for(;;){
+ DWORD ret;
+ ret = WaitForMultipleObjects((DWORD) 2,
+ harr,
+ FALSE,
+ (success_wait == NO_SUCCESS_WAIT) ?
+ INFINITE :
+ SUCCESS_WAIT_TIME);
+ if(ret == WAIT_TIMEOUT){
+ /* Just do the "success reporting" and continue */
+ if(success_wait == INITIAL_SUCCESS_WAIT){
+ log_info("Erlang service started successfully.");
+ } else {
+ log_warning("Erlang service restarted");
+ }
+ success_wait = NO_SUCCESS_WAIT;
+ continue;
+ }
+ if(ret == WAIT_FAILED || (int)(ret-WAIT_OBJECT_0) >= 2){
+ set_stopped(WAIT_FAILED);
+ log_error("Internal error, could not wait for objects.");
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ ret -= WAIT_OBJECT_0;
+ if(((int) ret) == 1){
+ /* Stop service... */
+ checkpoint = 2; /* 1 is taken by the handler */
+ set_stop_pending(waithint,checkpoint);
+ if(stop_erlang(&srvi,waithint,&checkpoint)){
+ log_debug("Erlang machine is stopped");
+ CloseHandle(eventStop);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ set_stopped(NO_ERROR);
+ if(srvi.erl_stdin)
+ CloseHandle(srvi.erl_stdin);
+ free_keys(keys);
+ return;
+ } else {
+ log_warning("Unable to stop erlang service.");
+ set_running();
+ continue;
+ }
+ }
+ /* Reload the registry keys, they may have changed. */
+ save_keys = keys;
+ keys = get_keys(service_name);
+ if(!keys){
+ log_error("Could not reload registry keys.");
+ keys = srvi.keys = save_keys;
+ } else {
+#ifdef HARDDEBUG
+ sprintf(errorbuff,"Reloaded the registry keys because %s stopped.",
+ service_name);
+ log_debug(errorbuff);
+#endif /* HARDDEBUG */
+ free_keys(save_keys);
+ srvi.keys = keys;
+ }
+ if(srvi.keys[OnFail].data.value == ON_FAIL_RESTART ||
+ srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){
+ if(!GetProcessTimes(srvi.info.hProcess,&creationt,
+ &exitt,&kernelt,&usert)){
+ DWORD rcode = GetLastError();
+ log_error("Could not get process time of terminated process.");
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(srvi.info.hThread);
+ CloseHandle(eventStop);
+ if(srvi.erl_stdin)
+ CloseHandle(srvi.erl_stdin);
+ set_stopped(rcode);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(srvi.info.hThread);
+ if(srvi.erl_stdin)
+ CloseHandle(srvi.erl_stdin);
+ srvi.erl_stdin = NULL;
+ memcpy(&creationl,&creationt,sizeof(FILETIME));
+ memcpy(&exitl,&exitt,sizeof(FILETIME));
+ diffl = exitl - creationl;
+ diffl /= 10000000;
+#ifdef DEBUG
+ sprintf(errorbuff,"Process lived for %d seconds", (int) diffl);
+ log_debug(errorbuff);
+#endif
+
+ if(diffl > CYCLIC_RESTART_LIMIT ||
+ srvi.keys[OnFail].data.value == ON_FAIL_RESTART_ALWAYS){
+ if(!start_a_service(&srvi)){
+ log_error("Unable to restart failed erlang service, "
+ "aborting.");
+ CloseHandle(eventStop);
+ set_stopped(ERROR_PROCESS_ABORTED);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ log_warning("Restarted erlang machine.");
+ if(diffl <= CYCLIC_RESTART_LIMIT)
+ log_warning("Possible cyclic restarting of erlang machine.");
+ success_wait = RESTART_SUCCESS_WAIT;
+ harr[0] = srvi.info.hProcess;
+ } else {
+ if(success_wait == INITIAL_SUCCESS_WAIT){
+ log_error("Erlang machine stopped instantly "
+ "(distribution name conflict?). "
+ "The service is not restarted, ignoring OnFail option.");
+ } else {
+ log_error("Erlang machine seems to die "
+ "continously, not restarted.");
+ }
+ CloseHandle(eventStop);
+ set_stopped(ERROR_PROCESS_ABORTED);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ } else if(srvi.keys[OnFail].data.value == ON_FAIL_REBOOT){
+ log_error("Rebooting because erlang machine stopped.");
+ enable_privilege();
+ if(!InitiateSystemShutdown("",NULL,0,TRUE,TRUE)){
+ log_error("Failed to reboot!");
+#ifdef HARDDEBUG
+ {
+ char *mes;
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ GetLastError(),
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &mes,
+ 0,
+ NULL );
+ log_debug(mes);
+ LocalFree(mes);
+ }
+#endif
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(eventStop);
+ if(srvi.erl_stdin != NULL)
+ CloseHandle(srvi.erl_stdin);
+ set_stopped(NO_ERROR);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ } else {
+ DWORD ecode = NO_ERROR;
+ if(success_wait == NO_SUCCESS_WAIT){
+ log_warning("Erlang machine volountarily stopped. "
+ "The service is not restarted as OnFail "
+ "is set to ignore.");
+ } else {
+ log_error("Erlang machine stopped instantly "
+ "(distribution name conflict?). "
+ "The service is not restarted as OnFail is set to ignore.");
+ ecode = ERROR_PROCESS_ABORTED;
+ }
+ CloseHandle(srvi.info.hProcess);
+ CloseHandle(eventStop);
+ if(srvi.erl_stdin != NULL)
+ CloseHandle(srvi.erl_stdin);
+ set_stopped(ecode);
+ if (eventKillErlang != NULL) {
+ CloseHandle(eventKillErlang);
+ }
+ free_keys(keys);
+ return;
+ }
+ }
+}
+
+int service_main(int argc, char **argv){
+ char dummy_name[] = "";
+ SERVICE_TABLE_ENTRY serviceTable[] =
+ {
+ { dummy_name,
+ (LPSERVICE_MAIN_FUNCTION) service_main_loop},
+ { NULL, NULL }
+ };
+ BOOL success;
+ success =
+ StartServiceCtrlDispatcher(serviceTable);
+ if (!success)
+ log_error("Could not initiate service");
+ log_debug("service_main done its job");
+ return 0;
+}
+
diff --git a/erts/etc/win32/erlsrv/erlsrv_service.h b/erts/etc/win32/erlsrv/erlsrv_service.h
new file mode 100644
index 0000000000..3eab275836
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_service.h
@@ -0,0 +1,32 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifndef _ERLSRV_SERVICE_H
+#define _ERLSRV_SERVICE_H
+
+#define CYCLIC_RESTART_LIMIT 10 /* Seconds */
+#define SUCCESS_WAIT_TIME (10*1000) /* Wait 5 s before reporting a service
+ as really started */
+#define NO_SUCCESS_WAIT 0
+#define INITIAL_SUCCESS_WAIT 1
+#define RESTART_SUCCESS_WAIT 2
+
+
+int service_main(int argc, char **argv);
+
+#endif /* _ERLSRV_SERVICE_H */
diff --git a/erts/etc/win32/erlsrv/erlsrv_util.c b/erts/etc/win32/erlsrv/erlsrv_util.c
new file mode 100644
index 0000000000..da3c6f5ef7
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_util.c
@@ -0,0 +1,154 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <windows.h>
+#include <winsvc.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "erlsrv_global.h"
+#include "erlsrv_util.h"
+#include "erlsrv_logmess.h"
+
+char *service_name = "";
+char *real_service_name = "";
+
+void log_warning(char *mess){
+ HANDLE logh;
+ char *strings[] = {service_name, mess , NULL};
+
+ if(!(logh = RegisterEventSource(NULL,APP_NAME)))
+ return;
+ ReportEvent(logh, EVENTLOG_WARNING_TYPE, 0, MSG_WARNING,
+ NULL, 2, 0, strings, NULL);
+ DeregisterEventSource(logh);
+}
+
+void log_error(char *mess){
+ HANDLE logh;
+ char *strings[] = {service_name, mess , NULL};
+
+ if(!(logh = RegisterEventSource(NULL,APP_NAME)))
+ return;
+ ReportEvent(logh, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR,
+ NULL, 2, 0, strings, NULL);
+ DeregisterEventSource(logh);
+}
+
+void log_info(char *mess){
+ HANDLE logh;
+ char *strings[] = {service_name, mess , NULL};
+
+ if(!(logh = RegisterEventSource(NULL,APP_NAME)))
+ return;
+ ReportEvent(logh, EVENTLOG_INFORMATION_TYPE, 0, MSG_INFO,
+ NULL, 2, 0, strings, NULL);
+ DeregisterEventSource(logh);
+}
+
+#ifndef NDEBUG
+void log_debug(char *mess){
+ char *buff=malloc(strlen(mess)+100);
+ sprintf(buff,"DEBUG! %s",mess);
+ log_info(buff);
+ free(buff);
+}
+#endif
+
+char *envdup(char *env){
+ char *tmp;
+ int len;
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1)
+ ;
+ len = (tmp - env) + 1;
+ if(len == 1)
+ ++len;
+ tmp = malloc(len);
+ memcpy(tmp,env,len);
+ return tmp;
+}
+
+char **env_to_arg(char *env){
+ char **ret;
+ char *tmp;
+ int i;
+ int num_strings = 0;
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1)
+ ++num_strings;
+ /* malloc enough to insert ONE string */
+ ret = malloc(sizeof(char *) * (num_strings + 2));
+ i = 0;
+ for(tmp = env; *tmp != '\0'; tmp += strlen(tmp)+1){
+ ret[i++] = strdup(tmp);
+ }
+ ret[i] = NULL;
+ free(env);
+ return ret;
+}
+
+static int compare(const void *a, const void *b){
+ char *s1 = *((char **) a);
+ char *s2 = *((char **) b);
+ char *e1 = strchr(s1,'=');
+ char *e2 = strchr(s2,'=');
+ int ret;
+ int len;
+
+ if(!e1)
+ e1 = s1 + strlen(s1);
+ if(!e2)
+ e2 = s2 + strlen(s2);
+
+ if((e1 - s1) > (e2 - s2))
+ len = (e2 - s2);
+ else
+ len = (e1 - s1);
+
+ ret = _strnicmp(s1,s2,len);
+ if(ret == 0)
+ return ((e1 - s1) - (e2 - s2));
+ else
+ return ret;
+}
+
+char *arg_to_env(char **arg){
+ char *block;
+ char *pek;
+ int i;
+ int totlen = 1; /* extra '\0' */
+
+ for(i=0;arg[i] != NULL;++i)
+ totlen += strlen(arg[i])+1;
+ /* sort the environment vector */
+ qsort(arg,i,sizeof(char *),&compare);
+ if(totlen == 1){
+ block = malloc(2);
+ block[0] = block[1] = '\0';
+ } else {
+ block = malloc(totlen);
+ pek = block;
+ for(i=0; arg[i] != NULL; ++i){
+ strcpy(pek, arg[i]);
+ free(arg[i]);
+ pek += strlen(pek)+1;
+ }
+ *pek = '\0';
+ }
+ free(arg);
+ return block;
+}
diff --git a/erts/etc/win32/erlsrv/erlsrv_util.h b/erts/etc/win32/erlsrv/erlsrv_util.h
new file mode 100644
index 0000000000..b98a6cd3ef
--- /dev/null
+++ b/erts/etc/win32/erlsrv/erlsrv_util.h
@@ -0,0 +1,50 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#ifndef _ERLSRV_UTIL_H
+#define _ERLSRV_UTIL_H
+
+extern char *service_name;
+extern char *real_service_name;
+void log_warning(char *mess);
+void log_error(char *mess);
+void log_info(char *mess);
+
+char *envdup(char *env);
+/*
+** Call before env_to_arg to get a 'freeable' environment block.
+*/
+
+char *arg_to_env(char **arg);
+/*
+** Frees the argument list before returning!
+*/
+
+char **env_to_arg(char *env);
+/*
+** Frees the environment block before returning!
+*/
+
+
+#ifndef NDEBUG
+void log_debug(char *mess);
+#else
+#define log_debug(mess) /* Debug removed */
+#endif
+
+#endif /* _ERLSRV_UTIL_H */
diff --git a/erts/etc/win32/hrl_icon.ico b/erts/etc/win32/hrl_icon.ico
new file mode 100644
index 0000000000..d22abb396b
--- /dev/null
+++ b/erts/etc/win32/hrl_icon.ico
Binary files differ
diff --git a/erts/etc/win32/init_file.c b/erts/etc/win32/init_file.c
new file mode 100644
index 0000000000..52f6c41d1d
--- /dev/null
+++ b/erts/etc/win32/init_file.c
@@ -0,0 +1,565 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#include <windows.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include "init_file.h"
+
+#define ALLOC malloc
+#define REALLOC realloc
+#define FREE free
+
+#define CONTEXT_BUFFER_SIZE 1024
+
+typedef struct {
+ HANDLE fd;
+ int eof;
+ int num;
+ int pos;
+ char buff[CONTEXT_BUFFER_SIZE];
+} FileContext;
+
+static char *read_one_line(FileContext *fc)
+{
+ char *buff = ALLOC(10);
+ int size = 10;
+ int num = 0;
+ int skipping;
+ int escaped;
+
+#define PUSH(Char) \
+ do { \
+ if (num == size) { \
+ size += 10; \
+ buff = REALLOC(buff,size); \
+ } \
+ buff[num++] = (Char); \
+ } while(0)
+
+#define POP() (buff[--num])
+#define TOP() (buff[(num-1)])
+
+ skipping = 0;
+ escaped = 0;
+ for(;;) {
+ char current;
+ if (fc->eof) {
+ break;
+ }
+ if (fc->pos == fc->num) {
+ if (!ReadFile(fc->fd, fc->buff, CONTEXT_BUFFER_SIZE,
+ &(fc->num), NULL) || !(fc->num)) {
+ fc->eof = 1;
+ break;
+ }
+ fc->pos = 0;
+ }
+ current = fc->buff[fc->pos];
+ ++(fc->pos);
+ switch (current) {
+ case ' ':
+ if (!skipping && num) {
+ PUSH(current);
+ }
+ escaped = 0;
+ break;
+ case ';':
+ if (!skipping) {
+ if (!escaped) {
+ skipping = 1;
+ } else {
+ PUSH(current);
+ }
+ }
+ escaped = 0;
+ break;
+ case '\\':
+ if (!skipping) {
+ if (!escaped) {
+ escaped = 1;
+ } else {
+ PUSH(current);
+ escaped = 0;
+ }
+ }
+ break;
+ case '\r':
+ break;
+ case '\n':
+ if (!escaped) {
+ while (num && TOP() == ' ') {
+ POP();
+ }
+ if (num) {
+ goto done;
+ }
+ }
+ skipping = 0;
+ escaped = 0;
+ break;
+ default:
+ if (!skipping) {
+ PUSH(current);
+ }
+ escaped = 0;
+ break;
+ }
+ }
+ /* EOF comes here */
+ while (num && TOP() == ' ') {
+ POP();
+ }
+ if (!num) {
+ FREE(buff);
+ return NULL;
+ }
+ done:
+ PUSH('\0');
+ return buff;
+#undef PUSH
+#undef POP
+#undef TOP
+}
+
+static int is_section_header(char *line)
+{
+ int x = strlen(line);
+ return (x > 2 && *line == '[' && line[x-1] == ']');
+}
+
+static int is_key_value(char *line)
+{
+ char *p = strchr(line,'=');
+
+ return (p != NULL && p > line);
+}
+
+static char *digout_section_name(char *line)
+ /* Moving it because it shall later be freed. */
+{
+ int x = strlen(line);
+ memmove(line,line+1,x-1);
+ line[x-2] = '\0';
+ return line;
+}
+
+static void digout_key_value(char *line, char **key, char **value)
+{
+ char *e = strchr(line,'=');
+ *key = line;
+ *value = (e+1);
+ *e = '\0';
+ while (*(--e) == ' ') {
+ *e = '\0';
+ }
+ while (**value == ' ') {
+ ++(*value);
+ }
+}
+
+InitFile *load_init_file(char *filename)
+{
+ HANDLE infile;
+ InitFile *inif;
+ InitSection *inis;
+ InitEntry *inie;
+ FileContext fc;
+ char *line;
+ char **lines;
+ int size_lines;
+ int num_lines;
+
+ int i;
+
+ if ( (infile = CreateFile(filename,
+ GENERIC_READ,
+ FILE_SHARE_READ,
+ NULL,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL)) == INVALID_HANDLE_VALUE) {
+ return NULL;
+ }
+
+ size_lines = 10;
+ num_lines = 0;
+ lines = ALLOC(size_lines * sizeof(char *));
+
+ fc.fd = infile;
+ fc.eof = 0;
+ fc.num = 0;
+ fc.pos = 0;
+ while ((line = read_one_line(&fc)) != NULL) {
+ if (num_lines == size_lines) {
+ size_lines += 10;
+ lines = REALLOC(lines,size_lines * sizeof(char *));
+ }
+ lines[num_lines] = line;
+ ++num_lines;
+ }
+ CloseHandle(infile);
+ /* Now check the lines before doing anything else, so that
+ we don't need any error handling while creating the data
+ structures */
+ /*
+ The file should contain:
+ [section]
+ Key=Value
+ ...
+ [section]
+ ...
+ */
+ i = 0;
+ while (i < num_lines && is_section_header(lines[i])) {
+ ++i;
+ while (i < num_lines && is_key_value(lines[i])) {
+ ++i;
+ }
+ }
+ if (i < num_lines) {
+ for (i = 0; i < num_lines; ++i) {
+ FREE(lines[i]);
+ }
+ FREE(lines);
+ return NULL;
+ }
+
+ /* So, now we know it's consistent... */
+ i = 0;
+ inif = ALLOC(sizeof(InitFile));
+ inif->num_sections = 0;
+ inif->size_sections = 10;
+ inif->sections = ALLOC(sizeof(InitSection *) * 10);
+ while (i < num_lines) {
+ inis = ALLOC(sizeof(InitSection));
+ inis->num_entries = 0;
+ inis->size_entries = 10;
+ inis->section_name = digout_section_name(lines[i]);
+ inis->entries = ALLOC(sizeof(InitEntry *) * 10);
+ ++i;
+ while (i < num_lines && is_key_value(lines[i])) {
+ inie = ALLOC(sizeof(InitEntry));
+ digout_key_value(lines[i], &(inie->key), &(inie->value));
+ if (inis->num_entries == inis->size_entries) {
+ inis->size_entries += 10;
+ inis->entries =
+ REALLOC(inis->entries,
+ sizeof(InitEntry *) * inis->size_entries);
+ }
+ inis->entries[inis->num_entries] = inie;
+ ++(inis->num_entries);
+ ++i;
+ }
+ if (inif->num_sections == inif->size_sections) {
+ inif->size_sections += 10;
+ inif->sections =
+ REALLOC(inif->sections,
+ sizeof(InitSection *) * inif->size_sections);
+ }
+ inif->sections[inif->num_sections] = inis;
+ ++(inif->num_sections);
+ }
+ FREE(lines); /* Only the array of strings, not the actual strings, they
+ are kept in the data structures. */
+ return inif;
+}
+
+int store_init_file(InitFile *inif, char *filename)
+{
+ char *buff;
+ int size = 10;
+ int num = 0;
+ int i,j;
+ HANDLE outfile;
+
+#define PUSH(Char) \
+ do { \
+ if (num == size) { \
+ size += 10; \
+ buff = REALLOC(buff,size); \
+ } \
+ buff[num++] = (Char); \
+ } while(0)
+
+ if ( (outfile = CreateFile(filename,
+ GENERIC_WRITE,
+ FILE_SHARE_WRITE,
+ NULL,
+ CREATE_ALWAYS,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL)) == INVALID_HANDLE_VALUE) {
+ return INIT_FILE_OPEN_ERROR;
+ }
+ buff = ALLOC(size);
+
+ for(i = 0; i < inif->num_sections; ++i) {
+ int len;
+ int written;
+ InitSection *inis = inif->sections[i];
+
+ if (!WriteFile(outfile,"[",1,&written,NULL) || written != 1) {
+ goto write_error;
+ }
+ len = strlen(inis->section_name);
+ if (!WriteFile(outfile,inis->section_name,len,&written,NULL) ||
+ written != len) {
+ goto write_error;
+ }
+ if (!WriteFile(outfile,"]\n",2,&written,NULL) || written != 2) {
+ goto write_error;
+ }
+ for (j = 0; j < inis->num_entries; ++j) {
+ InitEntry *inie = inis->entries[j];
+ char *p = inie->key;
+ num = 0;
+ for (;*p != '\0';++p) {
+ switch (*p) {
+ case '\\':
+ case ';':
+ PUSH('\\');
+ default:
+ PUSH(*p);
+ break;
+ }
+ }
+ PUSH('=');
+ p = inie->value;
+ for (;*p != '\0';++p) {
+ switch (*p) {
+ case '\\':
+ case ';':
+ PUSH('\\');
+ default:
+ PUSH(*p);
+ break;
+ }
+ }
+ PUSH('\n');
+ if (!WriteFile(outfile,buff,num,&written,NULL) || written != num) {
+ goto write_error;
+ }
+ }
+ }
+ FREE(buff);
+ CloseHandle(outfile);
+ return INIT_FILE_NO_ERROR;
+ write_error:
+ FREE(buff);
+ CloseHandle(outfile);
+ return INIT_FILE_WRITE_ERROR;
+#undef PUSH
+}
+
+InitFile *create_init_file(void)
+{
+ InitFile *inif = ALLOC(sizeof(InitFile));
+ inif->num_sections = 0;
+ inif->size_sections = 10;
+ inif->sections = ALLOC(sizeof(InitSection *) * 10);
+ return inif;
+}
+
+InitSection *create_init_section(char *section_name)
+{
+ InitSection *inis = ALLOC(sizeof(InitSection));
+ inis->num_entries = 0;
+ inis->section_name = ALLOC(sizeof(char) * (strlen(section_name) + 1));
+ strcpy(inis->section_name, section_name);
+ inis->size_entries = 10;
+ inis->entries = ALLOC(sizeof(InitEntry *) * 10);
+ return inis;
+}
+
+static void free_init_entry(InitEntry *inie)
+{
+ FREE(inie->key);
+ /* Value is part of the same buffer */
+ FREE(inie);
+}
+
+void free_init_section(InitSection *inis)
+{
+ int i;
+ for (i = 0;i < inis->num_entries; ++i) {
+ free_init_entry(inis->entries[i]);
+ }
+ FREE(inis->entries);
+ FREE(inis->section_name);
+ FREE(inis);
+}
+
+void free_init_file(InitFile *inif)
+{
+ int i;
+ for (i = 0; i < inif->num_sections; ++i) {
+ free_init_section(inif->sections[i]);
+ }
+ FREE(inif->sections);
+ FREE(inif);
+}
+
+static int find_init_section(InitFile *inif, char *section_name)
+{
+ int i;
+ for (i = 0; i < inif->num_sections; ++i) {
+ if (!strcmp(inif->sections[i]->section_name, section_name)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int delete_init_section(InitFile *inif, char *section_name)
+{
+ int i;
+
+ if ((i = find_init_section(inif, section_name)) < 0) {
+ return INIT_FILE_NOT_PRESENT;
+ }
+
+ free_init_section(inif->sections[i]);
+ --(inif->num_sections);
+ inif->sections[i] = inif->sections[inif->num_sections];
+
+ return INIT_FILE_PRESENT;
+}
+
+int add_init_section(InitFile *inif, InitSection *inis)
+{
+ int i;
+ InitSection *oinis;
+ if ((i = find_init_section(inif, inis->section_name)) >= 0) {
+ oinis = inif->sections[i];
+ inif->sections[i] = inis;
+ free_init_section(oinis);
+ return INIT_FILE_PRESENT;
+ }
+ if (inif->num_sections == inif->size_sections) {
+ inif->size_sections += 10;
+ inif->sections = REALLOC(inif->sections,
+ sizeof(InitSection *) * inif->size_sections);
+ }
+ inif->sections[inif->num_sections] = inis;
+ ++(inif->num_sections);
+ return INIT_FILE_NOT_PRESENT;
+}
+
+InitSection *lookup_init_section(InitFile *inif, char *section_name)
+{
+ int i;
+ if ((i = find_init_section(inif,section_name)) < 0) {
+ return NULL;
+ }
+ return inif->sections[i];
+}
+
+char *nth_init_section_name(InitFile *inif, int n)
+{
+ if (n >= inif->num_sections) {
+ return NULL;
+ }
+ return inif->sections[n]->section_name;
+}
+
+/* Inefficient... */
+InitSection *copy_init_section(InitSection *inis, char *new_name)
+{
+ int i;
+ char *key;
+ InitSection *ninis = create_init_section(new_name);
+ i = 0;
+ while ((key = nth_init_entry_key(inis,i)) != NULL) {
+ add_init_entry(ninis, key, lookup_init_entry(inis, key));
+ ++i;
+ }
+ return ninis;
+}
+
+static int find_init_entry(InitSection *inis, char *key)
+{
+ int i;
+ for (i = 0; i < inis->num_entries; ++i) {
+ if (!strcmp(inis->entries[i]->key,key)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+int add_init_entry(InitSection *inis, char *key, char *value)
+{
+ int keylen = strlen(key);
+ char *buff = ALLOC(sizeof(char) * (keylen + strlen(value) + 2));
+ InitEntry *inie;
+ char *obuff;
+ int i;
+
+ strcpy(buff,key);
+ strcpy(buff+keylen+1,value);
+
+ if ((i = find_init_entry(inis,key)) >= 0) {
+ inie = inis->entries[i];
+ FREE(inie->key);
+ inie->key = buff;
+ inie->value = buff+keylen+1;
+ return INIT_FILE_PRESENT;
+ }
+ inie = ALLOC(sizeof(InitEntry));
+ inie->key = buff;
+ inie->value = buff+keylen+1;
+ if (inis->num_entries == inis->size_entries) {
+ inis->size_entries += 10;
+ inis->entries = REALLOC(inis->entries,
+ sizeof(InitEntry *) * inis->size_entries);
+ }
+ inis->entries[inis->num_entries] = inie;
+ ++(inis->num_entries);
+ return INIT_FILE_NOT_PRESENT;
+}
+
+char *lookup_init_entry(InitSection *inis, char *key)
+{
+ int i;
+ if ((i = find_init_entry(inis,key)) < 0) {
+ return NULL;
+ }
+ return inis->entries[i]->value;
+}
+
+char *nth_init_entry_key(InitSection *inis, int n)
+{
+ if (n >= inis->num_entries) {
+ return NULL;
+ }
+ return inis->entries[n]->key;
+}
+
+int delete_init_entry(InitSection *inis, char *key)
+{
+ int i;
+ InitEntry *inie;
+ if ((i = find_init_entry(inis, key)) < 0) {
+ return INIT_FILE_NOT_PRESENT;
+ }
+ free_init_entry(inis->entries[i]);
+ --(inis->num_entries);
+ inis->entries[i] = inis->entries[inis->num_entries];
+ return INIT_FILE_PRESENT;
+}
+
diff --git a/erts/etc/win32/init_file.h b/erts/etc/win32/init_file.h
new file mode 100644
index 0000000000..48d2d2df62
--- /dev/null
+++ b/erts/etc/win32/init_file.h
@@ -0,0 +1,93 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 2003-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+
+typedef struct {
+ char *key;
+ char *value; /* Key and value points into same buffer */
+} InitEntry;
+
+typedef struct {
+ int num_entries;
+ int size_entries;
+ char *section_name;
+ InitEntry **entries;
+} InitSection;
+
+typedef struct {
+ int num_sections;
+ int size_sections;
+ InitSection **sections;
+} InitFile;
+
+/* Load a file structure from a disk file */
+InitFile *load_init_file(char *filename);
+
+/* Stores a file structure into a disk file */
+int store_init_file(InitFile *inif, char *filename);
+
+/* Create an empty file structure */
+InitFile *create_init_file(void);
+
+/* Free a file structure and everything associateed (including sections,keys
+ and values and anything looked up but not copied) */
+void free_init_file(InitFile *inif);
+
+/* Create empty section */
+InitSection *create_init_section(char *section_name);
+
+/* Add section to file Overwrites and destroys old sections with same key */
+int add_init_section(InitFile *inif, InitSection *inis);
+
+/* Kills a named section from a file. Destroys so that previously looked up
+ sections (with this key) need to be copied before the delete */
+int delete_init_section(InitFile *inif, char *section_name);
+
+/* lookup returns pointer into existing data. If data is to be preserved
+ across deletes or overwrites, it has to be copied */
+InitSection *lookup_init_section(InitFile *inif, char *section_name);
+
+/* Returns the name of the nth init section, n is >= 0, ret NULL when done */
+char *nth_init_section_name(InitFile *inif, int n);
+
+/* To save an init section so that delete or overwrite does not destroy it,
+ one needs to copy it */
+InitSection *copy_init_section(InitSection *inis, char *new_name);
+
+/* Frees up everything in the section, keys and values as well. */
+void free_init_section(InitSection *inis);
+
+/* Key and value are copied in add_entry */
+int add_init_entry(InitSection *inis, char *key, char *value);
+
+/* Returns pointer into internal string, use strcpy to save across
+ updates/deletes */
+char *lookup_init_entry(InitSection *inis, char *key);
+
+/* Returns the name of the nth entry key, n is >= 0, ret NULL when done */
+char *nth_init_entry_key(InitSection *inis, int n);
+
+/* Destroys entry, prevoiusly looked up entries need be
+ copied before deleted */
+int delete_init_entry(InitSection *inis, char *key);
+
+#define INIT_FILE_NO_ERROR 0
+#define INIT_FILE_OPEN_ERROR -1
+#define INIT_FILE_WRITE_ERROR -2
+#define INIT_FILE_PRESENT 0
+#define INIT_FILE_NOT_PRESENT 1
diff --git a/erts/etc/win32/nsis/Makefile b/erts/etc/win32/nsis/Makefile
new file mode 100644
index 0000000000..ebb3ad9a96
--- /dev/null
+++ b/erts/etc/win32/nsis/Makefile
@@ -0,0 +1,88 @@
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2003-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+include $(ERL_TOP)/make/target.mk
+include $(ERL_TOP)/make/$(TARGET)/otp.mk
+include $(ERL_TOP)/erts/vsn.mk
+
+VERSION_HEADER = erlang.nsh
+MAKENSIS = makensis
+MAKENSISFLAGS = /V2
+CUSTOM_MODERN=custom_modern.exe
+
+# This is not the way we usually do in our makefiles,
+# but making release is the ONLY thing we do with this one,
+# Its not called during ordinary recursive make.
+all: release
+
+opt debug depend:
+ @echo Nothing to do for "'"$@"'" on $(TARGET)
+
+clean:
+ rm -f $(VERSION_HEADER)
+
+include $(ERL_TOP)/make/otp_release_targets.mk
+
+TARGET_DIR = $(RELEASE_PATH)
+WTESTROOT=$(shell (cygpath -d $(RELEASE_PATH) 2>/dev/null || cygpath -w $(RELEASE_PATH)))
+WTARGET_DIR=$(shell (cygpath -d $(TARGET_DIR) 2>/dev/null || cygpath -d $(TARGET_DIR)))
+
+REDIST_FILE=$(shell (sh ./find_redist.sh || echo ""))
+REDIST_DLL_VERSION=$(shell (sh ./dll_version_helper.sh || echo ""))
+
+release_spec:
+ @NSIS_VER=`makensis /hdrinfo | head -1 | awk '{print $$2}'`; \
+ case $$NSIS_VER in \
+ v2.0b*) \
+ echo '!define MUI_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\
+ echo '!define ERTS_VERSION "$(VSN)"' >> $(VERSION_HEADER);\
+ echo '!define TESTROOT "$(WTESTROOT)"' >> $(VERSION_HEADER);\
+ echo '!define OUTFILEDIR "$(WTARGET_DIR)"' >> $(VERSION_HEADER);\
+ if [ -f $(RELEASE_PATH)/docs/doc/index.html ];\
+ then\
+ echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \
+ fi;\
+ $(MAKENSIS) erlang.nsi;;\
+ v2.*) \
+ echo '!define OTP_VERSION "$(SYSTEM_VSN)"' > $(VERSION_HEADER);\
+ echo '!define ERTS_VERSION "$(VSN)"' >> $(VERSION_HEADER);\
+ echo '!define TESTROOT "$(WTESTROOT)"' >> $(VERSION_HEADER);\
+ echo '!define OUTFILEDIR "$(WTARGET_DIR)"' >> $(VERSION_HEADER);\
+ if [ -f $(CUSTOM_MODERN) ];\
+ then \
+ echo '!define HAVE_CUSTOM_MODERN 1' >> $(VERSION_HEADER); \
+ fi;\
+ if [ '!' -z "$(REDIST_FILE)" -a '!' -z "$(REDIST_DLL_VERSION)" ];\
+ then \
+ cp $(REDIST_FILE) $(RELEASE_PATH)/vcredist_x86.exe;\
+ echo '!define HAVE_REDIST_FILE 1' >> $(VERSION_HEADER); \
+ echo '!define REDIST_DLL_VERSION "$(REDIST_DLL_VERSION)"' >> $(VERSION_HEADER);\
+ fi;\
+ if [ -f $(RELEASE_PATH)/docs/doc/index.html ];\
+ then \
+ echo '!define HAVE_DOCS 1' >> $(VERSION_HEADER); \
+ fi;\
+ echo "Running $(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi";\
+ $(MAKENSIS) $(MAKENSISFLAGS) erlang20.nsi;;\
+ *) \
+ echo 'Unsupported NSIS version';;\
+ esac
+
+release_docs release_docs_spec docs:
+
diff --git a/erts/etc/win32/nsis/custom_modern.exe b/erts/etc/win32/nsis/custom_modern.exe
new file mode 100755
index 0000000000..0f56b8b239
--- /dev/null
+++ b/erts/etc/win32/nsis/custom_modern.exe
Binary files differ
diff --git a/erts/etc/win32/nsis/dll_version_helper.sh b/erts/etc/win32/nsis/dll_version_helper.sh
new file mode 100755
index 0000000000..e0047dea8b
--- /dev/null
+++ b/erts/etc/win32/nsis/dll_version_helper.sh
@@ -0,0 +1,49 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+# This little helper digs out the current version of microsoft CRT
+# by compiling hello world and "parsing" the manifest file...
+
+# To debug using a fake version:
+
+# echo "8.0.50727.763"
+# exit 0
+
+cat > hello.c <<EOF
+#include <stdio.h>
+
+int main(void)
+{
+ printf("Hello world\n");
+ return 0;
+}
+
+EOF
+cl /MD hello.c > /dev/null 2>&1
+if [ '!' -f hello.exe.manifest ]; then
+ echo "This compiler does not generate manifest files - OK if using mingw" >&2
+ exit 0
+fi
+VERSION=`grep '<assemblyIdentity' hello.exe.manifest | sed 's,.*version=.\([0-9\.]*\).*,\1,g' | grep -v '<'`
+rm -f hello.c hello.obj hello.exe hello.exe.manifest
+if [ -z "$VERSION" ]; then
+ exit 1
+fi
+echo $VERSION
+exit 0
diff --git a/erts/etc/win32/nsis/erlang.nsi b/erts/etc/win32/nsis/erlang.nsi
new file mode 100644
index 0000000000..f4fd2b4cdb
--- /dev/null
+++ b/erts/etc/win32/nsis/erlang.nsi
@@ -0,0 +1,386 @@
+; NSIS Modern User Interface version 1.63
+; Erlang OTP installation script based on "Start Menu Folder Selection
+; Example Script"
+; Original example written by Joost Verburg
+; Modified for Erlang by Patrik
+
+; Verbosity does not come naturally with MUI, have to set it back now and then.
+ !verbose 1
+ !define MUI_MANUALVERBOSE 1
+
+ !define MUI_PRODUCT "Erlang OTP"
+
+ !include "erlang.nsh" ; All release specific parameters come from this
+
+
+ !include "MUI.nsh"
+
+;--------------------------------
+;Configuration
+
+ ;SetCompressor bzip2
+
+;General
+ OutFile "${OUTFILEDIR}\otp_win32_${MUI_VERSION}.exe"
+
+;Folder selection page
+ InstallDir "$PROGRAMFILES\erl${ERTS_VERSION}"
+
+;Remember install folder
+ InstallDirRegKey HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" ""
+
+;$9 is being used to store the Start Menu Folder.
+;Do not use this variable in your script (or Push/Pop it)!
+
+;To change this variable, use MUI_STARTMENUPAGE_VARIABLE.
+;Have a look at the Readme for info about other options (default folder,
+;registry).
+
+; Set the default start menu folder
+
+ !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${MUI_PRODUCT} ${MUI_VERSION}"
+
+; Registry keys where start menu folder is stored
+ !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
+ !define MUI_STARTMENUPAGE_REGISTRY_KEY \
+ "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+ !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
+
+; Temporary variable used here and there...
+ !define TEMP $R0
+
+;--------------------------------
+;Modern UI Configuration
+ !define MUI_ICON "erlang_inst.ico"
+ !define MUI_UNICON "erlang_uninst.ico"
+ !define MUI_WELCOMEPAGE
+ !define MUI_COMPONENTSPAGE
+ !define MUI_DIRECTORYPAGE
+ !define MUI_STARTMENUPAGE
+
+ !define MUI_ABORTWARNING
+
+ !define MUI_UNINSTALLER
+ !define MUI_UNCONFIRMPAGE
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+;--------------------------------
+;Language Strings
+
+;Description
+ LangString DESC_SecErlang ${LANG_ENGLISH} "Erlang OTP."
+ LangString DESC_SecErlangDev ${LANG_ENGLISH} \
+ "Erlang OTP development environment (required)."
+ LangString DESC_SecErlangAssoc ${LANG_ENGLISH} \
+ "Erlang filetype associations (.erl, .hrl, .beam)."
+!ifdef HAVE_DOCS
+ LangString DESC_SecErlangDoc ${LANG_ENGLISH} "Documentation."
+!endif
+;--------------------------------
+;Installer Sections
+
+SubSection /e "Erlang" SecErlang
+Section "Development" SecErlangDev
+SectionIn 1 RO
+
+ StrCmp ${MUI_STARTMENUPAGE_VARIABLE} "" 0 skip_silent_mode
+ StrCpy ${MUI_STARTMENUPAGE_VARIABLE} \
+ "${MUI_STARTMENUPAGE_DEFAULTFOLDER}"
+skip_silent_mode:
+
+ SetOutPath "$INSTDIR"
+ File "${TESTROOT}\Install.ini"
+ File "${TESTROOT}\Install.exe"
+ File /r "${TESTROOT}\releases"
+ File /r "${TESTROOT}\lib"
+ File /r "${TESTROOT}\erts-${ERTS_VERSION}"
+ File /r "${TESTROOT}\usr"
+
+;Store install folder
+ WriteRegStr HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" "" $INSTDIR
+
+; Run the setup program
+ ExecWait '"$INSTDIR\Install.exe" -s'
+
+; The startmenu stuff
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN
+; Set back verbosity...
+ !verbose 1
+; Try to use the Common startmenu...
+ SetShellVarContext All
+ ClearErrors
+ CreateDirectory "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}"
+ IfErrors 0 continue_create
+ ;MessageBox MB_OK "Error creating file"
+ SetShellVarContext current
+ CreateDirectory "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}"
+continue_create:
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+ CreateShortCut "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Erlang.lnk" \
+ "$INSTDIR\bin\werl.exe"
+ CreateShortCut \
+ "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Uninstall.lnk" \
+ "$INSTDIR\Uninstall.exe" "" "$INSTDIR\Uninstall.exe" 0
+
+ !insertmacro MUI_STARTMENU_WRITE_END
+; And once again, the verbosity...
+ !verbose 1
+;Create uninstaller
+; WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+ WriteRegStr HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "DisplayName" "Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})"
+ WriteRegStr HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegDWORD HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "NoModify" 1
+ WriteRegDWORD HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "NoRepair" 1
+
+; Check that the registry could be written, we only check one key,
+; but it should be sufficient...
+ ReadRegStr ${TEMP} "${MUI_STARTMENUPAGE_REGISTRY_ROOT}" \
+ "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}"
+
+ StrCmp ${TEMP} "" 0 done
+
+; Now we're done if we are a superuser. If the registry stuff failed, we
+; do the things below...
+
+ WriteRegStr HKCU "Software\Ericsson\Erlang\${ERTS_VERSION}" \
+ "" $INSTDIR
+ WriteRegStr HKCU "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}" \
+ "${MUI_STARTMENUPAGE_VARIABLE}"
+ WriteRegStr HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "DisplayName" "Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})"
+ WriteRegStr HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegDWORD HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "NoModify" 1
+ WriteRegDWORD HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})" \
+ "NoRepair" 1
+
+done:
+SectionEnd ; SecErlangDev
+
+Section "Associations" SecErlangAssoc
+
+ ;File /r "${TESTROOT}\usr\lib\icons"
+
+; .erl
+ ; back up old value of .erl
+ ReadRegStr $1 HKCR ".erl" ""
+ StrCmp $1 "" OwnErl
+ StrCmp $1 "ErlangSource" OwnErl
+ WriteRegStr HKCR ".erl" "backup_val" $1
+OwnErl:
+ WriteRegStr HKCR ".erl" "" "ErlangSource"
+ ReadRegStr $0 HKCR "ErlangSource" ""
+ StrCmp $0 "" 0 skipErlAssoc
+ WriteRegStr HKCR "ErlangSource" "" "Erlang source file"
+ WriteRegStr HKCR "ErlangSource\shell" "" "open"
+ WriteRegStr HKCR "ErlangSource\shell\compile" "" "Compile"
+ WriteRegStr HKCR "ErlangSource\shell\compile\command" "" \
+ '"$INSTDIR\bin\erlc.exe" "%1"'
+ WriteRegStr HKCR "ErlangSource\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\erl_icon.ico
+ WriteRegStr HKCR "ErlangSource\shell\open\command" \
+ "" 'write.exe "%1"'
+skipErlAssoc:
+
+; .hrl
+ ; back up old value of .hrl
+ ReadRegStr $1 HKCR ".hrl" ""
+ StrCmp $1 "" OwnHrl
+ StrCmp $1 "ErlangHeader" OwnHrl
+ WriteRegStr HKCR ".hrl" "backup_val" $1
+OwnHrl:
+ WriteRegStr HKCR ".hrl" "" "ErlangHeader"
+ ReadRegStr $0 HKCR "ErlangHeader" ""
+ StrCmp $0 "" 0 skipHrlAssoc
+ WriteRegStr HKCR "ErlangHeader" "" "Erlang header file"
+ WriteRegStr HKCR "ErlangHeader\shell" "" "open"
+ WriteRegStr HKCR "ErlangHeader\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\hrl_icon.ico
+ WriteRegStr HKCR "ErlangHeader\shell\open\command" \
+ "" 'write.exe "%1"'
+skipHrlAssoc:
+
+; .beam
+ ; back up old value of .beam
+ ReadRegStr $1 HKCR ".beam" ""
+ StrCmp $1 "" OwnBeam
+ StrCmp $1 "ErlangBeam" OwnBeam
+ WriteRegStr HKCR ".beam" "backup_val" $1
+OwnBeam:
+ WriteRegStr HKCR ".beam" "" "ErlangBeam"
+ ReadRegStr $0 HKCR "ErlangBeam" ""
+ StrCmp $0 "" 0 skipBeamAssoc
+ WriteRegStr HKCR "ErlangBeam" "" "Erlang beam code"
+ WriteRegStr HKCR "ErlangBeam\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\beam_icon.ico
+skipBeamAssoc:
+
+SectionEnd ; SecErlangAssoc
+SubSectionEnd
+
+!ifdef HAVE_DOCS
+Section "Erlang Documentation" SecErlangDoc
+
+ SetOutPath "$INSTDIR"
+ File /r "${TESTROOT}\docs\*"
+
+; The startmenu stuff
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN
+; Set back verbosity...
+ !verbose 1
+; Try to use the Common startmenu...
+ SetShellVarContext All
+ ClearErrors
+ CreateShortCut "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Erlang Documentation.lnk" \
+ "$INSTDIR\doc\index.html"
+ IfErrors 0 continue_create
+ ;MessageBox MB_OK "Error creating file"
+ SetShellVarContext current
+ CreateShortCut \
+ "$SMPROGRAMS\${MUI_STARTMENUPAGE_VARIABLE}\Erlang Documentation.lnk" \
+ "$INSTDIR\doc\index.html"
+continue_create:
+
+ !insertmacro MUI_STARTMENU_WRITE_END
+; And once again, the verbosity...
+ !verbose 1
+SectionEnd ; ErlangDoc
+!endif
+
+
+;Display the Finish header
+;Insert this macro after the sections if you are not using a finish page
+ !insertmacro MUI_SECTIONS_FINISHHEADER
+
+;--------------------------------
+;Descriptions
+
+ !insertmacro MUI_FUNCTIONS_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlang} $(DESC_SecErlang)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDev} $(DESC_SecErlangDev)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangAssoc} \
+ $(DESC_SecErlangAssoc)
+!ifdef HAVE_DOCS
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDoc} $(DESC_SecErlangDoc)
+!endif
+ !insertmacro MUI_FUNCTIONS_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Section "Uninstall"
+
+ RMDir /r "$INSTDIR"
+
+;Remove shortcut
+ ReadRegStr ${TEMP} "${MUI_STARTMENUPAGE_REGISTRY_ROOT}" \
+ "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}"
+ StrCmp ${TEMP} "" 0 end_try
+; Try HKCU instead...
+ ReadRegStr ${TEMP} HKCU \
+ "${MUI_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MUI_STARTMENUPAGE_REGISTRY_VALUENAME}"
+; If this failed to, we have no shortcuts (eh?)
+ StrCmp ${TEMP} "" noshortcuts
+end_try:
+ SetShellVarContext All
+ ClearErrors
+; If we cannot find the shortcut, switch to current user context
+ GetFileTime "$SMPROGRAMS\${TEMP}\Erlang.lnk" $R1 $R2
+ IfErrors 0 continue_delete
+ ;MessageBox MB_OK "Error removing file"
+ SetShellVarContext current
+continue_delete:
+ Delete "$SMPROGRAMS\${TEMP}\Erlang.lnk"
+ Delete "$SMPROGRAMS\${TEMP}\Uninstall.lnk"
+ Delete "$SMPROGRAMS\${TEMP}\Erlang Documentation.lnk"
+ RMDir "$SMPROGRAMS\${TEMP}" ;Only if empty
+
+noshortcuts:
+; We delete both in HKCU and HKLM, we don't really know were they might be...
+ DeleteRegKey /ifempty HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+ DeleteRegKey /ifempty HKCU "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+ DeleteRegKey HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})"
+ DeleteRegKey HKCU \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${MUI_VERSION} (${ERTS_VERSION})"
+
+
+; Now remove shell/file associations we'we made...
+; .erl
+ ReadRegStr $1 HKCR ".erl" ""
+ StrCmp $1 "ErlangSource" 0 NoOwnSourceExt
+ ReadRegStr $1 HKCR ".erl" "backup_val"
+ StrCmp $1 "" 0 RestoreBackupSource
+ DeleteRegKey HKCR ".erl"
+ Goto NoOwnSourceExt
+RestoreBackupSource:
+ WriteRegStr HKCR ".erl" "" $1
+ DeleteRegValue HKCR ".erl" "backup_val"
+NoOwnSourceExt:
+
+ ReadRegStr $1 HKCR "ErlangSource\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\erl_icon.ico" 0 NoOwnSource
+ DeleteRegKey HKCR "ErlangSource"
+NoOwnSource:
+
+;.hrl
+ ReadRegStr $1 HKCR ".hrl" ""
+ StrCmp $1 "ErlangHeader" 0 NoOwnHeaderExt
+ ReadRegStr $1 HKCR ".hrl" "backup_val"
+ StrCmp $1 "" 0 RestoreBackupHeader
+ DeleteRegKey HKCR ".hrl"
+ Goto NoOwnHeaderExt
+RestoreBackupHeader:
+ WriteRegStr HKCR ".hrl" "" $1
+ DeleteRegValue HKCR ".hrl" "backup_val"
+NoOwnHeaderExt:
+
+ ReadRegStr $1 HKCR "ErlangHeader\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\hrl_icon.ico" 0 NoOwnHeader
+ DeleteRegKey HKCR "ErlangHeader"
+NoOwnHeader:
+
+;.beam
+ ReadRegStr $1 HKCR ".beam" ""
+ StrCmp $1 "ErlangBeam" 0 NoOwnBeamExt
+ ReadRegStr $1 HKCR ".beam" "backup_val"
+ StrCmp $1 "" 0 RestoreBackupBeam
+ DeleteRegKey HKCR ".beam"
+ Goto NoOwnBeamExt
+RestoreBackupBeam:
+ WriteRegStr HKCR ".beam" "" $1
+ DeleteRegValue HKCR ".beam" "backup_val"
+NoOwnBeamExt:
+
+ ReadRegStr $1 HKCR "ErlangBeam\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\beam_icon.ico" 0 NoOwnBeam
+ DeleteRegKey HKCR "ErlangBeam"
+NoOwnBeam:
+
+;Display the Finish header
+ !insertmacro MUI_UNFINISHHEADER
+
+SectionEnd
+ !verbose 3
diff --git a/erts/etc/win32/nsis/erlang20.nsi b/erts/etc/win32/nsis/erlang20.nsi
new file mode 100644
index 0000000000..43e5d91604
--- /dev/null
+++ b/erts/etc/win32/nsis/erlang20.nsi
@@ -0,0 +1,440 @@
+; NSIS Modern User Interface version 1.63
+; Erlang OTP installation script based on "Start Menu Folder Selection
+; Example Script"
+; Original example written by Joost Verburg
+; Modified for Erlang by Patrik
+
+; Verbosity does not come naturally with MUI, have to set it back now and then.
+ !verbose 1
+ !define MUI_MANUALVERBOSE 1
+
+ !define OTP_PRODUCT "Erlang OTP"
+
+ !include "erlang.nsh" ; All release specific parameters come from this
+
+ Name "${OTP_PRODUCT} ${OTP_VERSION}"
+
+ !include "MUI.nsh"
+ !include "WordFunc.nsh"
+;--------------------------------
+;Configuration
+
+ SetCompressor bzip2
+
+Var MYTEMP
+;Var MUI_TEMP
+Var STARTMENU_FOLDER
+
+
+!define MY_STARTMENUPAGE_REGISTRY_ROOT HKLM
+!define MY_STARTMENUPAGE_REGISTRY_KEY "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+!define MY_STARTMENUPAGE_REGISTRY_VALUENAME "Start Menu Folder"
+
+;General
+ OutFile "${OUTFILEDIR}\otp_win32_${OTP_VERSION}.exe"
+
+;Folder selection page
+ InstallDir "$PROGRAMFILES\erl${ERTS_VERSION}"
+
+;Remember install folder
+ InstallDirRegKey HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" ""
+
+; Set the default start menu folder
+
+ !define MUI_STARTMENUPAGE_DEFAULTFOLDER "${OTP_PRODUCT} ${OTP_VERSION}"
+
+;--------------------------------
+;Modern UI Configuration
+!ifdef HAVE_CUSTOM_MODERN
+ !define MUI_UI "custom_modern.exe"
+!endif
+ !define MUI_ICON "erlang_inst.ico"
+ !define MUI_UNICON "erlang_uninst.ico"
+
+ !insertmacro MUI_PAGE_COMPONENTS
+ !insertmacro MUI_PAGE_DIRECTORY
+; Registry keys where start menu folder is stored
+
+ !define MUI_STARTMENUPAGE_REGISTRY_ROOT ${MY_STARTMENUPAGE_REGISTRY_ROOT}
+ !define MUI_STARTMENUPAGE_REGISTRY_KEY "${MY_STARTMENUPAGE_REGISTRY_KEY}"
+ !define MUI_STARTMENUPAGE_REGISTRY_VALUENAME "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+
+ !insertmacro MUI_PAGE_STARTMENU Application $STARTMENU_FOLDER
+
+ !insertmacro MUI_PAGE_INSTFILES
+
+ !insertmacro MUI_UNPAGE_CONFIRM
+ !insertmacro MUI_UNPAGE_INSTFILES
+
+;--------------------------------
+;Languages
+
+ !insertmacro MUI_LANGUAGE "English"
+
+;--------------------------------
+;Language Strings
+
+;Description
+ LangString DESC_SecErlang ${LANG_ENGLISH} "Erlang OTP."
+ LangString DESC_SecErlangDev ${LANG_ENGLISH} \
+ "Erlang OTP development environment (required)."
+ LangString DESC_SecErlangAssoc ${LANG_ENGLISH} \
+ "Erlang filetype associations (.erl, .hrl, .beam)."
+!ifdef HAVE_DOCS
+ LangString DESC_SecErlangDoc ${LANG_ENGLISH} "Documentation."
+!endif
+!ifdef HAVE_REDIST_FILE
+ LangString DESC_SecMSRedist ${LANG_ENGLISH} "Microsoft redistributable C runtime libraries, these are mandatory for Erlang runtime and development. Always installed if not already present."
+!endif
+;--------------------------------
+; WordFunc
+!ifdef HAVE_REDIST_FILE
+ !insertmacro VersionCompare
+!endif
+;--------------------------------
+;Installer Sections
+
+!ifdef HAVE_REDIST_FILE
+Section "Microsoft redistributable libraries." SecMSRedist
+
+ SetOutPath "$INSTDIR"
+ File "${TESTROOT}\vcredist_x86.exe"
+
+; Set back verbosity...
+ !verbose 1
+; Run the setup program
+ ExecWait '"$INSTDIR\vcredist_x86.exe"'
+
+ !verbose 1
+SectionEnd ; MSRedist
+!endif
+
+SubSection /e "Erlang" SecErlang
+Section "Development" SecErlangDev
+SectionIn 1 RO
+
+ SetOutPath "$INSTDIR"
+ File "${TESTROOT}\Install.ini"
+ File "${TESTROOT}\Install.exe"
+ SetOutPath "$INSTDIR\releases"
+ File /r "${TESTROOT}\releases\"
+ SetOutPath "$INSTDIR\lib"
+ File /r "${TESTROOT}\lib\"
+ SetOutPath "$INSTDIR\erts-${ERTS_VERSION}"
+ File /r "${TESTROOT}\erts-${ERTS_VERSION}\"
+ SetOutPath "$INSTDIR\usr"
+ File /r "${TESTROOT}\usr\"
+
+;Store install folder
+ WriteRegStr HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}" "" $INSTDIR
+
+; Run the setup program
+ Exec '"$INSTDIR\Install.exe" -s'
+
+; The startmenu stuff
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+; Set back verbosity...
+ !verbose 1
+; Try to use the Common startmenu...
+ SetShellVarContext All
+ ClearErrors
+ CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
+ IfErrors 0 continue_create
+ ;MessageBox MB_OK "Error creating file"
+ SetShellVarContext current
+ CreateDirectory "$SMPROGRAMS\$STARTMENU_FOLDER"
+continue_create:
+ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Erlang.lnk" \
+ "$INSTDIR\bin\werl.exe"
+
+ !insertmacro MUI_STARTMENU_WRITE_END
+; And once again, the verbosity...
+ !verbose 1
+; Check that the registry could be written, we only check one key,
+; but it should be sufficient...
+ ReadRegStr $MYTEMP ${MY_STARTMENUPAGE_REGISTRY_ROOT} "${MY_STARTMENUPAGE_REGISTRY_KEY}" "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+
+ StrCmp $MYTEMP "" 0 done_startmenu
+
+; If startmenu was skipped, this might be unnecessary, but wont hurt...
+ WriteRegStr HKCU "Software\Ericsson\Erlang\${ERTS_VERSION}" \
+ "" $INSTDIR
+ WriteRegStr HKCU "${MY_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}" \
+ "$STARTMENU_FOLDER"
+
+
+done_startmenu:
+;Create uninstaller
+ WriteUninstaller "$INSTDIR\Uninstall.exe"
+
+ WriteRegStr HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "DisplayName" "Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+ WriteRegStr HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegDWORD HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoModify" 1
+ WriteRegDWORD HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoRepair" 1
+
+; Check that the registry could be written, we only check one key,
+; but it should be sufficient...
+ ReadRegStr $MYTEMP HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoRepair"
+
+ StrCmp $MYTEMP "" 0 done
+
+; Now we're done if we are a superuser. If the registry stuff failed, we
+; do the things below...
+
+ WriteRegStr HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "DisplayName" "Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+ WriteRegStr HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "UninstallString" "$INSTDIR\Uninstall.exe"
+ WriteRegDWORD HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoModify" 1
+ WriteRegDWORD HKCU \
+ "Software\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})" \
+ "NoRepair" 1
+
+done:
+SectionEnd ; SecErlangDev
+
+Section "Associations" SecErlangAssoc
+
+ ;File /r "${TESTROOT}\usr\lib\icons"
+
+; .erl
+ DeleteRegKey HKCR ".erl"
+ DeleteRegKey HKCR "ErlangSource"
+ WriteRegStr HKCR ".erl" "" "ErlangSource"
+ WriteRegStr HKCR "ErlangSource" "" "Erlang source file"
+ WriteRegStr HKCR "ErlangSource\shell\compile" "" "Compile"
+ WriteRegStr HKCR "ErlangSource\shell\compile\command" "" \
+ '"$INSTDIR\bin\erlc.exe" "%1"'
+ WriteRegStr HKCR "ErlangSource\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\erl_icon.ico
+; .hrl
+ DeleteRegKey HKCR ".hrl"
+ DeleteRegKey HKCR "ErlangHeader"
+ WriteRegStr HKCR ".hrl" "" "ErlangHeader"
+ WriteRegStr HKCR "ErlangHeader" "" "Erlang header file"
+ WriteRegStr HKCR "ErlangHeader\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\hrl_icon.ico
+
+; .beam
+ DeleteRegKey HKCR ".beam"
+ DeleteRegKey HKCR "ErlangBeam"
+ WriteRegStr HKCR ".beam" "" "ErlangBeam"
+ WriteRegStr HKCR "ErlangBeam" "" "Erlang beam code"
+ WriteRegStr HKCR "ErlangBeam\DefaultIcon" "" \
+ $INSTDIR\usr\lib\icons\beam_icon.ico
+
+
+ SearchPath $1 "write.exe"
+ StrCmp $1 "" writeNotFound
+ WriteRegStr HKCR "ErlangSource\shell" "" "Open"
+ WriteRegStr HKCR "ErlangSource\shell\open\command" "" \
+ '"$1" "%1"'
+ WriteRegStr HKCR "ErlangHeader\shell" "" "Open"
+ WriteRegStr HKCR "ErlangHeader\shell\open\command" "" \
+ '"$1" "%1"'
+
+
+writeNotFound:
+SectionEnd ; SecErlangAssoc
+SubSectionEnd
+
+!ifdef HAVE_DOCS
+Section "Erlang Documentation" SecErlangDoc
+
+ SetOutPath "$INSTDIR"
+ File /r "${TESTROOT}\docs\*"
+
+; The startmenu stuff
+ !insertmacro MUI_STARTMENU_WRITE_BEGIN Application
+; Set back verbosity...
+ !verbose 1
+; Try to use the Common startmenu...
+ SetShellVarContext All
+ ClearErrors
+ CreateShortCut "$SMPROGRAMS\$STARTMENU_FOLDER\Erlang Documentation.lnk" \
+ "$INSTDIR\doc\index.html"
+ IfErrors 0 continue_create
+ ;MessageBox MB_OK "Error creating file"
+ SetShellVarContext current
+ CreateShortCut \
+ "$SMPROGRAMS\$STARTMENU_FOLDER\Erlang Documentation.lnk" \
+ "$INSTDIR\doc\index.html"
+continue_create:
+
+ !insertmacro MUI_STARTMENU_WRITE_END
+; And once again, the verbosity...
+ !verbose 1
+SectionEnd ; ErlangDoc
+!endif
+
+!ifdef HAVE_REDIST_FILE
+Function DllVersionGoodEnough
+ IntCmp 0 $R0 normal0 normal0 negative0
+ normal0:
+ IntOp $R2 $R0 >> 16
+ Goto continue0
+ negative0:
+ IntOp $R2 $R0 & 0x7FFF0000
+ IntOp $R2 $R2 >> 16
+ IntOp $R2 $R2 | 0x8000
+ continue0:
+ IntOp $R3 $R0 & 0x0000FFFF
+ IntCmp 0 $R1 normal1 normal1 negative1
+ normal1:
+ IntOp $R4 $R1 >> 16
+ Goto continue1
+ negative1:
+ IntOp $R4 $R1 & 0x7FFF0000
+ IntOp $R4 $R4 >> 16
+ IntOp $R4 $R4 | 0x8000
+ continue1:
+ IntOp $R5 $R1 & 0x0000FFFF
+ StrCpy $2 "$R2.$R3.$R4.$R5"
+ ${VersionCompare} $2 ${REDIST_DLL_VERSION} $R0
+ Return
+FunctionEnd
+
+Function .onInit
+ SectionGetFlags 0 $MYTEMP
+; MessageBox MB_YESNO "Found $SYSDIR\msvcr80.dll" IDYES FoundLbl
+ IfFileExists $SYSDIR\msvcr80.dll MaybeFoundInSystemLbl
+ SearchSxsLbl:
+ FindFirst $0 $1 $WINDIR\WinSxS\x86*
+ LoopLbl:
+ StrCmp $1 "" NotFoundLbl
+ IfFileExists $WINDIR\WinSxS\$1\msvcr80.dll MaybeFoundInSxsLbl
+ FindNext $0 $1
+ Goto LoopLbl
+ MaybeFoundInSxsLbl:
+ GetDllVersion $WINDIR\WinSxS\$1\msvcr80.dll $R0 $R1
+ Call DllVersionGoodEnough
+ FindNext $0 $1
+ IntCmp 2 $R0 LoopLbl
+ Goto FoundLbl
+ MaybeFoundInSystemLbl:
+ GetDllVersion $SYSDIR\msvcr80.dll $R0 $R1
+ Call DllVersionGoodEnough
+ IntCmp 2 $R0 SearchSxSLbl
+ FoundLbl:
+ IntOp $MYTEMP $MYTEMP & 4294967294
+ SectionSetFlags 0 $MYTEMP
+ SectionSetText 0 "Microsoft DLL's (present)"
+ Return
+ NotFoundLbl:
+ IntOp $MYTEMP $MYTEMP | 16
+ SectionSetFlags 0 $MYTEMP
+ SectionSetText 0 "Microsoft DLL's (needed)"
+ Return
+FunctionEnd
+!endif
+
+
+;Display the Finish header
+;Insert this macro after the sections if you are not using a finish page
+; !insertmacro MUI_SECTIONS_FINISHHEADER
+
+;--------------------------------
+;Descriptions
+
+ !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlang} $(DESC_SecErlang)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDev} $(DESC_SecErlangDev)
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangAssoc} \
+ $(DESC_SecErlangAssoc)
+!ifdef HAVE_DOCS
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecErlangDoc} $(DESC_SecErlangDoc)
+!endif
+!ifdef HAVE_REDIST_FILE
+ !insertmacro MUI_DESCRIPTION_TEXT ${SecMSRedist} $(DESC_SecMSRedist)
+!endif
+ !insertmacro MUI_FUNCTION_DESCRIPTION_END
+
+;--------------------------------
+;Uninstaller Section
+
+Section "Uninstall"
+
+ RMDir /r "$INSTDIR"
+
+;Remove shortcut
+ ReadRegStr $MYTEMP "${MY_STARTMENUPAGE_REGISTRY_ROOT}" \
+ "${MY_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+ StrCmp $MYTEMP "" 0 end_try
+; Try HKCU instead...
+ ReadRegStr $MYTEMP "${MY_STARTMENUPAGE_REGISTRY_ROOT}" \
+ "${MY_STARTMENUPAGE_REGISTRY_KEY}" \
+ "${MY_STARTMENUPAGE_REGISTRY_VALUENAME}"
+; If this failed to, we have no shortcuts (eh?)
+ StrCmp $MYTEMP "" noshortcuts
+end_try:
+ SetShellVarContext All
+ ClearErrors
+; If we cannot find the shortcut, switch to current user context
+ GetFileTime "$SMPROGRAMS\$MYTEMP\Erlang.lnk" $R1 $R2
+ IfErrors 0 continue_delete
+ ;MessageBox MB_OK "Error removing file"
+ SetShellVarContext current
+continue_delete:
+ Delete "$SMPROGRAMS\$MYTEMP\Erlang.lnk"
+ Delete "$SMPROGRAMS\$MYTEMP\Uninstall.lnk"
+ Delete "$SMPROGRAMS\$MYTEMP\Erlang Documentation.lnk"
+ RMDir "$SMPROGRAMS\$MYTEMP" ;Only if empty
+
+noshortcuts:
+; We delete both in HKCU and HKLM, we don't really know were they might be...
+ DeleteRegKey /ifempty HKLM "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+ DeleteRegKey /ifempty HKCU "SOFTWARE\Ericsson\Erlang\${ERTS_VERSION}"
+ DeleteRegKey HKLM \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+ DeleteRegKey HKCU \
+ "SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall\Erlang OTP ${OTP_VERSION} (${ERTS_VERSION})"
+
+
+; Now remove shell/file associations we'we made...
+; .erl
+ ReadRegStr $1 HKCR ".erl" ""
+ StrCmp $1 "ErlangSource" 0 NoOwnSource
+ ReadRegStr $1 HKCR "ErlangSource\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\erl_icon.ico" 0 NoOwnSource
+ DeleteRegKey HKCR ".erl"
+ DeleteRegKey HKCR "ErlangSource"
+NoOwnSource:
+; .hrl
+ ReadRegStr $1 HKCR ".hrl" ""
+ StrCmp $1 "ErlangHeader" 0 NoOwnHeader
+ ReadRegStr $1 HKCR "ErlangHeader\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\hrl_icon.ico" 0 NoOwnHeader
+ DeleteRegKey HKCR ".hrl"
+ DeleteRegKey HKCR "ErlangHeader"
+NoOwnHeader:
+
+; .beam
+ ReadRegStr $1 HKCR ".beam" ""
+ StrCmp $1 "ErlangBeam" 0 NoOwnBeam
+ ReadRegStr $1 HKCR "ErlangBeam\DefaultIcon" ""
+ StrCmp $1 "$INSTDIR\usr\lib\icons\beam_icon.ico" 0 NoOwnBeam
+ DeleteRegKey HKCR ".beam"
+ DeleteRegKey HKCR "ErlangBeam"
+NoOwnBeam:
+
+;Display the Finish header
+; !insertmacro MUI_UNFINISHHEADER
+
+SectionEnd
+ !verbose 3
diff --git a/erts/etc/win32/nsis/erlang_inst.ico b/erts/etc/win32/nsis/erlang_inst.ico
new file mode 100644
index 0000000000..edbd8a6f2c
--- /dev/null
+++ b/erts/etc/win32/nsis/erlang_inst.ico
Binary files differ
diff --git a/erts/etc/win32/nsis/erlang_uninst.ico b/erts/etc/win32/nsis/erlang_uninst.ico
new file mode 100755
index 0000000000..edbd8a6f2c
--- /dev/null
+++ b/erts/etc/win32/nsis/erlang_uninst.ico
Binary files differ
diff --git a/erts/etc/win32/nsis/find_redist.sh b/erts/etc/win32/nsis/find_redist.sh
new file mode 100755
index 0000000000..c5572839c5
--- /dev/null
+++ b/erts/etc/win32/nsis/find_redist.sh
@@ -0,0 +1,122 @@
+#! /bin/sh
+#
+# %CopyrightBegin%
+#
+# Copyright Ericsson AB 2007-2009. All Rights Reserved.
+#
+# The contents of this file are subject to the Erlang Public License,
+# Version 1.1, (the "License"); you may not use this file except in
+# compliance with the License. You should have received a copy of the
+# Erlang Public License along with this software. If not, it can be
+# retrieved online at http://www.erlang.org/.
+#
+# Software distributed under the License is distributed on an "AS IS"
+# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+# the License for the specific language governing rights and limitations
+# under the License.
+#
+# %CopyrightEnd%
+#
+
+# first find some tool we know exists, i.e. cl.exe
+lookup_prog_in_path ()
+{
+ PROG=$1
+ save_ifs=$IFS
+ IFS=:
+ for p in $PATH; do
+ # In cygwin the programs are not always executable and have .exe suffix...
+ if [ "X$TARGET" = "Xwin32" ]; then
+ if [ -f $p/$PROG.exe ]; then
+ echo $p/$PROG
+ break;
+ fi
+ else
+ if [ -x $p/$PROG ]; then
+ echo $p/$PROG
+ break;
+ fi
+ fi
+ done
+ IFS=$save_ifs
+}
+
+remove_path_element()
+{
+ EL=$1
+ PA=$2
+ ACC=""
+ save_ifs=$IFS
+ IFS=/
+ set $PA
+ N=$#
+ while [ $N -gt 1 ]; do
+ if [ '!' -z "$1" ]; then
+ ACC="${ACC}/$1"
+ fi
+ N=`expr $N - 1`
+ shift
+ done
+ UP=`echo $1 | tr [:lower:] [:upper:]`
+ ELUP=`echo $EL | tr [:lower:] [:upper:]`
+ IFS=$save_ifs
+ if [ "$UP" = "$ELUP" ]; then
+ echo "$ACC"
+ else
+ echo "${ACC}/$1"
+ fi
+
+ #echo "ACC=$ACC" >&2
+ #echo "1=$1" >&2
+}
+
+add_path_element()
+{
+ EL=$1
+ PA=$2
+
+ ELUP=`echo $EL | tr [:lower:] [:upper:]`
+ #echo "PA=$PA" >&2
+ for x in ${PA}/*; do
+ #echo "X=$x" >&2
+ UP=`basename "$x" | tr [:lower:] [:upper:]`
+ #echo "UP=$UP" >&2
+ if [ "$UP" = "$ELUP" ]; then
+ echo "$x"
+ return 0;
+ fi
+ done
+ echo "$PA"
+}
+
+CLPATH=`lookup_prog_in_path cl`
+
+if [ -z "$CLPATH" ]; then
+ echo "Can not locate cl.exe and vcredist_x86.exe - OK if using mingw" >&2
+ exit 1
+fi
+
+#echo $CLPATH
+BPATH=$CLPATH
+for x in cl bin vc; do
+ #echo $x
+ NBPATH=`remove_path_element $x "$BPATH"`
+ if [ "$NBPATH" = "$BPATH" ]; then
+ echo "Failed to locate vcredist_x86.exe because cl.exe was in an unexpected location" >&2
+ exit 2
+ fi
+ BPATH="$NBPATH"
+done
+#echo $BPATH
+for x in sdk v2.0 bootstrapper packages vcredist_x86 vcredist_x86.exe; do
+ #echo "x=$x"
+ #echo "BPATH=$BPATH"
+ NBPATH=`add_path_element $x "$BPATH"`
+ if [ "$NBPATH" = "$BPATH" ]; then
+ echo "Failed to locate vcredist_x86.exe because directory structure was unexpected" >&2
+ exit 3
+ fi
+ BPATH="$NBPATH"
+done
+echo $BPATH
+exit 0 \ No newline at end of file
diff --git a/erts/etc/win32/port_entry.c b/erts/etc/win32/port_entry.c
new file mode 100644
index 0000000000..49b5ad2f34
--- /dev/null
+++ b/erts/etc/win32/port_entry.c
@@ -0,0 +1,75 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+** This is an entry point for port programs,
+** it is used to set the console control handler of the process when
+** erlang process is run as a service.
+** Note that this entry point is only for
+** Console programs, Windowing programs can just route the WM_QUERYENDSESSION
+** and WM_ENDSESSION to the default window procedure to aquire the same
+** functionality.
+**
+** Creator Patrik Nyblom
+**
+** Notes:
+** You would really not want to use ANY of the standard library in this
+** routine, the standard library is not yet initiated...
+*/
+#include <windows.h>
+
+/*
+** The runtime libraries startup routine in the Microsoft Visual C CRT
+*/
+extern void mainCRTStartup(void);
+
+/*
+** A Console control handler that ignores the logoff events,
+** and lets the default handler take care of other events.
+*/
+BOOL WINAPI erl_port_default_handler(DWORD ctrl){
+ if(ctrl == CTRL_LOGOFF_EVENT)
+ return TRUE;
+ return FALSE;
+}
+
+/*
+** This is the entry point, it takes no parameters and never returns.
+*/
+void erl_port_entry(void){
+ char buffer[2];
+ /*
+ * We assume we're running as a service if this environment variable
+ * is defined
+ */
+ if(GetEnvironmentVariable("ERLSRV_SERVICE_NAME",buffer,(DWORD) 2)){
+#ifdef HARDDEBUG
+ DWORD dummy;
+ WriteFile(GetStdHandle(STD_OUTPUT_HANDLE),
+ "Setting handler\r\n",17,&dummy, NULL);
+#endif /* HARDDEBUG */
+ /*
+ ** Actually set the control handler
+ */
+ SetConsoleCtrlHandler(&erl_port_default_handler, TRUE);
+ }
+ /*
+ ** Call the CRT's real startup routine.
+ */
+ mainCRTStartup();
+}
diff --git a/erts/etc/win32/resource.h b/erts/etc/win32/resource.h
new file mode 100644
index 0000000000..697931952a
--- /dev/null
+++ b/erts/etc/win32/resource.h
@@ -0,0 +1,33 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+#define IDR_CONMENU 101
+#define IDMENU_STARTLOG 40001
+#define IDMENU_STOPLOG 40002
+#define IDMENU_EXIT 40003
+#define IDMENU_COPY 40004
+#define IDMENU_PASTE 40005
+#define IDMENU_FONT 40006
+#define IDMENU_TOOLBAR 40007
+#define IDMENU_ABOUT 40008
+#define IDMENU_SELALL 40009
+#define IDMENU_SELECTBKG 40010
+#define ID_BREAK 40011
+#define ID_VERSIONSTRING 40000
+#define ID_COMBOBOX 3
+
diff --git a/erts/etc/win32/start_erl.c b/erts/etc/win32/start_erl.c
new file mode 100644
index 0000000000..dcf8c8b281
--- /dev/null
+++ b/erts/etc/win32/start_erl.c
@@ -0,0 +1,677 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1998-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * start_erl.c - Windows NT start_erl
+ *
+ * Author: Mattias Nilsson
+ */
+
+#define WIN32_LEAN_AND_MEAN
+#define STRICT
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <windows.h>
+#include <assert.h>
+
+char *progname;
+
+/*
+ * If CASE_SENSITIVE_OPTIONS is specified, options are case sensitive
+ * (lower case).
+ * The reason for this switch is that _strnicmp is Microsoft specific.
+ */
+#ifndef CASE_SENSITIVE_OPTIONS
+#define strnicmp strncmp
+#else
+#define strnicmp _strnicmp
+#endif
+
+#define RELEASE_SUBDIR "\\releases"
+#define REGISTRY_BASE "Software\\Ericsson\\Erlang\\"
+#define DEFAULT_DATAFILE "start_erl.data"
+
+/* Global variables holding option values and command lines */
+char *CommandLineStart = NULL;
+char *ErlCommandLine = NULL;
+char *MyCommandLine = NULL;
+char *DataFileName = NULL;
+char *RelDir = NULL;
+char *BootFlagsFile = NULL;
+char *BootFlags = NULL;
+char *RegistryKey = NULL;
+char *BinDir = NULL;
+char *RootDir = NULL;
+char *VsnDir = NULL;
+char *Version = NULL;
+char *Release = NULL;
+BOOL NoConfig=FALSE;
+PROCESS_INFORMATION ErlProcessInfo;
+
+/*
+ * Error reason printout 'the microsoft way'
+ */
+void ShowLastError(void)
+{
+ LPVOID lpMsgBuf;
+ DWORD dwErr;
+
+ dwErr = GetLastError();
+ if( dwErr == ERROR_SUCCESS )
+ return;
+
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ dwErr,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpMsgBuf,
+ 0,
+ NULL
+ );
+ fprintf(stderr, lpMsgBuf);
+ LocalFree( lpMsgBuf );
+}
+
+/*
+ * Exit the program and give a nice and firm explanation of why
+ * and how you can avoid it.
+ */
+void exit_help(char *err)
+{
+ ShowLastError();
+ fprintf(stderr, "** Error: %s\n", err);
+
+ printf("Usage:\n%s\n"
+ " [<erlang options>] ++\n"
+ " [-data <datafile>]\n"
+ " [-reldir <releasedir>]\n"
+ " [-bootflags <bootflagsfile>]\n"
+ " [-noconfig]\n", progname);
+
+ exit(0);
+}
+
+
+/*
+ * Splits the command line into two strings:
+ * 1. Options to the Erlang node (ErlCommandLine)
+ * 2. Options to this program (MyCommandLine)
+ */
+void split_commandline(void)
+{
+ char *cmdline = CommandLineStart;
+
+ progname=cmdline;
+
+ /* Remove the first (quoted) string (our program name) */
+ if(*cmdline == '"') {
+ cmdline++; /* Skip " */
+ while( (*cmdline != '\0') && (*cmdline++) != '"' )
+ ;
+ } else {
+ while( (*cmdline != '\0') && (*cmdline++) != ' ' )
+ ;
+ }
+
+ while( (*cmdline) == ' ' )
+ cmdline++;
+
+ if( *cmdline == '\0') {
+ ErlCommandLine = "";
+ MyCommandLine = "";
+ return;
+ }
+
+ cmdline[-1] = '\0';
+
+ /* Start from the end of the string and search for "++ "
+ (PLUS PLUS SPACE) */
+ ErlCommandLine = cmdline;
+ if(strncmp(cmdline,"++ ",3))
+ cmdline = strstr(cmdline," ++ ");
+ if( cmdline == NULL ) {
+ MyCommandLine = "";
+ return;
+ }
+ /* Terminate the ErlCommandLine where MyCommandLine starts */
+ *cmdline++ = '\0';
+
+ /* Skip 'whitespace--whitespace' (WHITESPACE MINUS MINUS WHITESPACE) */
+ while( (*cmdline) == ' ' )
+ cmdline++;
+ while( (*cmdline) == '+' )
+ cmdline++;
+ while( (*cmdline) == ' ' )
+ cmdline++;
+
+ MyCommandLine = cmdline;
+
+#ifdef _DEBUG
+ fprintf(stderr, "ErlCommandLine: '%s'\n", ErlCommandLine);
+ fprintf(stderr, "MyCommandLine: '%s'\n", MyCommandLine);
+#endif
+}
+
+
+/*
+ * 'Smart' unquoting of a string: \" becomes " and " becomes (nothing)
+ * Skips any leading spaces and parses up to NULL or end of quoted string.
+ * Calls exit_help() if an unterminated quote is detected.
+ */
+char * unquote_optionarg(char *str, char **strp)
+{
+ char *newstr = (char *)malloc(strlen(str)+1); /* This one is realloc:ed later */
+ int i=0, inquote=0;
+
+ assert(newstr);
+ assert(str);
+
+ /* Skip leading spaces */
+ while( *str == ' ' )
+ str++;
+
+ /* Loop while in quote or until EOS or unquoted space
+ */
+ while( (inquote==1) || ( (*str!=0) && (*str!=' ') ) ) {
+ switch( *str ) {
+ case '\\':
+ /* If we are inside a quoted string we should convert \c to c */
+ if( inquote && str[1] == '"' )
+ str++;
+ newstr[i++]=*str++;
+ break;
+ case '"':
+ inquote = 1-inquote;
+ *str++;
+ break;
+ default:
+ newstr[i++]=*str++;
+ break;
+ }
+
+ if( (*str == 0) && (inquote==1) ) {
+ exit_help("Unterminated quote.");
+ }
+ }
+ newstr[i++] = 0x00;
+
+ /* Update the supplied pointer (used for continued parsing of options) */
+ *strp = str;
+
+ /* Adjust memblock of newstr */
+ newstr = (char *)realloc(newstr, i);
+ assert(newstr);
+ return(newstr);
+}
+
+
+/*
+ * Parses MyCommandLine and tries to fill in all the required option variables
+ * (one way or another).
+ */
+void parse_commandline(void)
+{
+ char *cmdline = MyCommandLine;
+
+ while( *cmdline != '\0' ) {
+ switch( *cmdline ) {
+ case '-': /* Handle both -arg and /arg */
+ case '/':
+ *cmdline++;
+ if( strnicmp(cmdline, "data", 4) == 0) {
+ DataFileName = unquote_optionarg(cmdline+4, &cmdline);
+ } else if( strnicmp(cmdline, "reldir", 6) == 0) {
+ RelDir = unquote_optionarg(cmdline+6, &cmdline);
+#ifdef _DEBUG
+ fprintf(stderr, "RelDir: '%s'\n", RelDir);
+#endif
+ } else if( strnicmp(cmdline, "bootflags", 9) == 0) {
+ BootFlagsFile = unquote_optionarg(cmdline+9, &cmdline);
+ } else if( strnicmp(cmdline, "noconfig", 8) == 0) {
+ NoConfig=TRUE;
+#ifdef _DEBUG
+ fprintf(stderr, "NoConfig=TRUE\n");
+#endif
+ } else {
+ fprintf(stderr, "Unkown option: '%s'\n", cmdline);
+ exit_help("Unknown command line option");
+ }
+ break;
+ default:
+ cmdline++;
+ break;
+ }
+ }
+}
+
+
+/*
+ * Read the data file specified and get the version and release number
+ * from it.
+ *
+ * This function also construct the correct RegistryKey from the version information
+ * retrieved.
+ */
+void read_datafile(void)
+{
+ FILE *fp;
+ char *newname;
+ long size;
+
+ if(!DataFileName){
+ DataFileName = malloc(strlen(DEFAULT_DATAFILE) + 1);
+ strcpy(DataFileName,DEFAULT_DATAFILE);
+ }
+ /* Is DataFileName relative or absolute ? */
+ if( (DataFileName[0] != '\\') && (strncmp(DataFileName+1, ":\\", 2)!=0) ) {
+ /* Relative name, we have to prepend RelDir to it. */
+ if( !RelDir ) {
+ exit_help("Need -reldir when -data filename has relative path.");
+ } else {
+ newname = (char *)malloc(strlen(DataFileName)+strlen(RelDir)+2);
+ assert(newname);
+ sprintf(newname, "%s\\%s", RelDir, DataFileName);
+ free(DataFileName);
+ DataFileName=newname;
+ }
+ }
+
+#ifdef _DEBUG
+ fprintf(stderr, "DataFileName: '%s'\n", DataFileName);
+#endif
+
+ if( (fp=fopen(DataFileName, "rb")) == NULL) {
+ exit_help("Cannot find the datafile.");
+ }
+
+ fseek(fp, 0, SEEK_END);
+ size=ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ Version = (char *)malloc(size+1);
+ Release = (char *)malloc(size+1);
+ assert(Version);
+ assert(Release);
+
+ if( (fscanf(fp, "%s %s", Version, Release)) == 0) {
+ fclose(fp);
+ exit_help("Format error in datafile.");
+ }
+
+ fclose(fp);
+
+#ifdef _DEBUG
+ fprintf(stderr, "DataFile version: '%s'\n", Version);
+ fprintf(stderr, "DataFile release: '%s'\n", Release);
+#endif
+}
+
+
+/*
+ * Read the registry keys we need
+ */
+void read_registry_keys(void)
+{
+ HKEY hReg;
+ ULONG lLen;
+
+ /* Create the RegistryKey name */
+ RegistryKey = (char *) malloc(strlen(REGISTRY_BASE) +
+ strlen(Version) + 1);
+ assert(RegistryKey);
+ sprintf(RegistryKey, REGISTRY_BASE "%s", Version);
+
+ /* We always need to find BinDir */
+ if( (RegOpenKeyEx(HKEY_LOCAL_MACHINE,
+ RegistryKey,
+ 0,
+ KEY_READ,
+ &hReg)) != ERROR_SUCCESS ) {
+ exit_help("Could not open registry key.");
+ }
+
+ /* First query size of data */
+ if( (RegQueryValueEx(hReg,
+ "Bindir",
+ NULL,
+ NULL,
+ NULL,
+ &lLen)) != ERROR_SUCCESS) {
+ exit_help("Failed to query BinDir of release.\n");
+ }
+
+ /* Allocate enough space */
+ BinDir = (char *)malloc(lLen+1);
+ assert(BinDir);
+ /* Retrieve the value */
+ if( (RegQueryValueEx(hReg,
+ "Bindir",
+ NULL,
+ NULL,
+ (unsigned char *) BinDir,
+ &lLen)) != ERROR_SUCCESS) {
+ exit_help("Failed to query BinDir of release (2).\n");
+ }
+
+#ifdef _DEBUG
+ fprintf(stderr, "Bindir: '%s'\n", BinDir);
+#endif
+
+ /* We also need the rootdir, in case we need to build RelDir later */
+
+ /* First query size of data */
+ if( (RegQueryValueEx(hReg,
+ "Rootdir",
+ NULL,
+ NULL,
+ NULL,
+ &lLen)) != ERROR_SUCCESS) {
+ exit_help("Failed to query RootDir of release.\n");
+ }
+
+ /* Allocate enough space */
+ RootDir = (char *) malloc(lLen+1);
+ assert(RootDir);
+ /* Retrieve the value */
+ if( (RegQueryValueEx(hReg,
+ "Rootdir",
+ NULL,
+ NULL,
+ (unsigned char *) RootDir,
+ &lLen)) != ERROR_SUCCESS) {
+ exit_help("Failed to query RootDir of release (2).\n");
+ }
+
+#ifdef _DEBUG
+ fprintf(stderr, "Rootdir: '%s'\n", RootDir);
+#endif
+
+ RegCloseKey(hReg);
+}
+
+/*
+ * Read the bootflags. This file contains extra command line options to erl.exe
+ */
+void read_bootflags(void)
+{
+ FILE *fp;
+ long fsize;
+ char *newname;
+
+ if(BootFlagsFile) {
+ /* Is BootFlagsFile relative or absolute ? */
+ if( (BootFlagsFile[0] != '\\') &&
+ (strncmp(BootFlagsFile+1, ":\\", 2)!=0) ) {
+ /* Relative name, we have to prepend RelDir\\Version to it. */
+ if( !RelDir ) {
+ exit_help("Need -reldir when -bootflags "
+ "filename has relative path.");
+ } else {
+ newname = (char *)malloc(strlen(BootFlagsFile)+strlen(RelDir)+strlen(Release)+3);
+ assert(newname);
+ sprintf(newname, "%s\\%s\\%s", RelDir, Release, BootFlagsFile);
+ free(BootFlagsFile);
+ BootFlagsFile=newname;
+ }
+ }
+
+#ifdef _DEBUG
+ fprintf(stderr, "BootFlagsFile: '%s'\n", BootFlagsFile);
+#endif
+
+
+
+ if( (fp=fopen(BootFlagsFile, "rb")) == NULL) {
+ exit_help("Could not open BootFlags file.");
+ }
+
+ fseek(fp, 0, SEEK_END);
+ fsize=ftell(fp);
+ fseek(fp, 0, SEEK_SET);
+
+ BootFlags = (char *)malloc(fsize+1);
+ assert(BootFlags);
+ if( (fgets(BootFlags, fsize+1, fp)) == NULL) {
+ exit_help("Error while reading BootFlags file");
+ }
+ fclose(fp);
+
+ /* Adjust buffer size */
+ BootFlags = (char *)realloc(BootFlags, strlen(BootFlags)+1);
+ assert(BootFlags);
+
+ /* Strip \r\n from BootFlags */
+ fsize = strlen(BootFlags);
+ while( fsize > 0 &&
+ ( (BootFlags[fsize-1] == '\r') ||
+ (BootFlags[fsize-1] == '\n') ) ) {
+ BootFlags[--fsize]=0;
+ }
+
+ } else {
+ /* Set empty BootFlags */
+ BootFlags = "";
+ }
+
+#ifdef _DEBUG
+ fprintf(stderr, "BootFlags: '%s'\n", BootFlags);
+#endif
+}
+
+
+long start_new_node(void)
+{
+ char *CommandLine;
+ unsigned long i;
+ STARTUPINFO si;
+ DWORD dwExitCode;
+
+ i = strlen(RelDir) + strlen(Release) + 4;
+ VsnDir = (char *)malloc(i);
+ assert(VsnDir);
+ sprintf(VsnDir, "%s\\%s", RelDir, Release);
+
+ if( NoConfig ) {
+ i = strlen(BinDir) + strlen(ErlCommandLine) +
+ strlen(BootFlags) + 64;
+ CommandLine = (char *)malloc(i);
+ assert(CommandLine);
+ sprintf(CommandLine,
+ "\"%s\\erl.exe\" -boot \"%s\\start\" %s %s",
+ BinDir,
+ VsnDir,
+ ErlCommandLine,
+ BootFlags);
+ } else {
+ i = strlen(BinDir) + strlen(ErlCommandLine)
+ + strlen(BootFlags) + strlen(VsnDir)*2 + 64;
+ CommandLine = (char *)malloc(i);
+ assert(CommandLine);
+ sprintf(CommandLine,
+ "\"%s\\erl.exe\" -boot \"%s\\start\" -config \"%s\\sys\" %s %s",
+ BinDir,
+ VsnDir,
+ VsnDir,
+ ErlCommandLine,
+ BootFlags);
+ }
+
+#ifdef _DEBUG
+ fprintf(stderr, "CommandLine: '%s'\n", CommandLine);
+#endif
+
+ /* Initialize the STARTUPINFO structure. */
+ memset(&si, 0, sizeof(STARTUPINFO));
+ si.cb = sizeof(STARTUPINFO);
+ si.lpTitle = NULL;
+ si.dwFlags = STARTF_USESTDHANDLES;
+ si.hStdInput = GetStdHandle(STD_INPUT_HANDLE);
+ si.hStdOutput = GetStdHandle(STD_OUTPUT_HANDLE);
+ si.hStdError = GetStdHandle(STD_ERROR_HANDLE);
+
+ /* Create the new Erlang process */
+ if( (CreateProcess(
+ NULL, /* pointer to name of executable module */
+ CommandLine, /* pointer to command line string */
+ NULL, /* pointer to process security attributes */
+ NULL, /* pointer to thread security attributes */
+ TRUE, /* handle inheritance flag */
+ GetPriorityClass(GetCurrentProcess()),
+ /* creation flags */
+ NULL, /* pointer to new environment block */
+ BinDir,/* pointer to current directory name */
+ &si, /* pointer to STARTUPINFO */
+ &ErlProcessInfo /* pointer to PROCESS_INFORMATION */
+ )) == FALSE) {
+ ShowLastError();
+ exit_help("Failed to start new node");
+ }
+
+#ifdef _DEBUG
+ fprintf(stderr, "Waiting for Erlang to terminate.\n");
+#endif
+ if(MsgWaitForMultipleObjects(1,&ErlProcessInfo.hProcess, FALSE,
+ INFINITE, QS_POSTMESSAGE) == WAIT_OBJECT_0+1){
+ if(PostThreadMessage(ErlProcessInfo.dwThreadId,
+ WM_USER,
+ (WPARAM) 0,
+ (LPARAM) 0)){
+ /* Wait 10 seconds for erl process to die, elsee terminate it. */
+ if(WaitForSingleObject(ErlProcessInfo.hProcess, 10000)
+ != WAIT_OBJECT_0){
+ TerminateProcess(ErlProcessInfo.hProcess,0);
+ }
+ } else {
+ TerminateProcess(ErlProcessInfo.hProcess,0);
+ }
+ }
+ GetExitCodeProcess(ErlProcessInfo.hProcess, &dwExitCode);
+#ifdef _DEBUG
+ fprintf(stderr, "Erlang terminated.\n");
+#endif
+
+ free(CommandLine);
+ return(dwExitCode);
+}
+
+
+/*
+ * Try to make the needed options complete by looking through the data file,
+ * environment variables and registry entries.
+ */
+void complete_options(void)
+{
+ /* Try to find a descent RelDir */
+ if( !RelDir ) {
+ DWORD sz = 32;
+ while (1) {
+ DWORD nsz;
+ if (RelDir)
+ free(RelDir);
+ RelDir = malloc(sz);
+ if (!RelDir) {
+ fprintf(stderr, "** Error : failed to allocate memory\n");
+ exit(1);
+ }
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) "RELDIR",
+ (LPTSTR) RelDir,
+ sz);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(RelDir);
+ RelDir = NULL;
+ break;
+ }
+ else if (nsz <= sz)
+ break;
+ else
+ sz = nsz;
+ }
+ if (RelDir == NULL) {
+ if(DataFileName){
+ /* Needs to be absolute for this to work, but we
+ can try... */
+ read_datafile();
+ read_registry_keys();
+ } else {
+ /* Impossible to find all data... */
+ exit_help("Need either Release directory or an absolute "
+ "datafile name.");
+ }
+ /* Ok, construct our own RelDir from RootDir */
+ RelDir = (char *) malloc(strlen(RootDir)+strlen(RELEASE_SUBDIR)+1);
+ assert(RelDir);
+ sprintf(RelDir, "%s" RELEASE_SUBDIR, RootDir);
+ } else {
+ read_datafile();
+ read_registry_keys();
+ }
+ } else {
+ read_datafile();
+ read_registry_keys();
+ }
+ read_bootflags();
+
+#ifdef _DEBUG
+ fprintf(stderr, "RelDir: '%s'\n", RelDir);
+#endif
+}
+
+
+
+
+BOOL WINAPI LogoffHandlerRoutine( DWORD dwCtrlType )
+{
+ if(dwCtrlType == CTRL_LOGOFF_EVENT) {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+
+
+int main(void)
+{
+ DWORD dwExitCode;
+ char *cmdline;
+
+ /* Make sure a logoff does not distrurb us. */
+ SetConsoleCtrlHandler(LogoffHandlerRoutine, TRUE);
+
+ cmdline = GetCommandLine();
+ assert(cmdline);
+
+ CommandLineStart = (char *) malloc(strlen(cmdline) + 1);
+ assert(CommandLineStart);
+ strcpy(CommandLineStart,cmdline);
+
+ split_commandline();
+ parse_commandline();
+ complete_options();
+
+ /* We now have all the options we need in order to fire up a new node.. */
+ dwExitCode = start_new_node();
+
+ return( (int) dwExitCode );
+}
+
+
diff --git a/erts/etc/win32/toolbar.bmp b/erts/etc/win32/toolbar.bmp
new file mode 100644
index 0000000000..e0df8454fd
--- /dev/null
+++ b/erts/etc/win32/toolbar.bmp
Binary files differ
diff --git a/erts/etc/win32/win_erlexec.c b/erts/etc/win32/win_erlexec.c
new file mode 100644
index 0000000000..0eed8e28b9
--- /dev/null
+++ b/erts/etc/win32/win_erlexec.c
@@ -0,0 +1,405 @@
+/*
+ * %CopyrightBegin%
+ *
+ * Copyright Ericsson AB 1997-2009. All Rights Reserved.
+ *
+ * The contents of this file are subject to the Erlang Public License,
+ * Version 1.1, (the "License"); you may not use this file except in
+ * compliance with the License. You should have received a copy of the
+ * Erlang Public License along with this software. If not, it can be
+ * retrieved online at http://www.erlang.org/.
+ *
+ * Software distributed under the License is distributed on an "AS IS"
+ * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+ * the License for the specific language governing rights and limitations
+ * under the License.
+ *
+ * %CopyrightEnd%
+ */
+/*
+ * Extra support for running the emulator on Windows.
+ * Most of this only used when beam is run as a separate process.
+ */
+
+#pragma comment(linker,"/manifestdependency:\"type='win32' "\
+ "name='Microsoft.Windows.Common-Controls' "\
+ "version='6.0.0.0' processorArchitecture='*' "\
+ "publicKeyToken='6595b64144ccf1df' language='*'\"")
+
+#include <windows.h>
+#include <winuser.h>
+#include <wincon.h>
+#include <process.h>
+#include "sys.h"
+#include "erl_driver.h"
+
+extern int nohup;
+extern int keep_window;
+void error(char* format, ...);
+
+/*
+ * Local functions.
+ */
+#define LOAD_BEAM_DYNAMICALLY 1
+static int start(char* emu, char** argv);
+static void start_winsock(void);
+static char* last_error(void);
+static char* last_wsa_error(void);
+static char* win32_errorstr(int error);
+static int has_console(void);
+static char** fnuttify_argv(char **argv);
+static void free_fnuttified(char **v);
+static int windowed = 0;
+
+#ifdef LOAD_BEAM_DYNAMICALLY
+typedef int SysGetKeyFunction(int);
+typedef void ErlStartFunction(int, char **);
+typedef void SysPrimitiveInitFunction(HMODULE);
+static SysGetKeyFunction *sys_get_key_p;
+static ErlStartFunction *erl_start_p;
+static SysPrimitiveInitFunction *sys_primitive_init_p;
+
+static HMODULE load_win_beam_dll(char *name)
+{
+ HMODULE beam_module;
+ beam_module=LoadLibrary(name);
+ if (beam_module == INVALID_HANDLE_VALUE || beam_module == NULL) {
+ error("Unable to load emulator DLL\n(%s)",name);
+ return NULL;
+ }
+ sys_get_key_p = (SysGetKeyFunction *)
+ GetProcAddress(beam_module, "sys_get_key");
+ erl_start_p = (ErlStartFunction *)
+ GetProcAddress(beam_module, "erl_start");
+ sys_primitive_init_p = (SysPrimitiveInitFunction *)
+ GetProcAddress(beam_module, "sys_primitive_init");
+ return beam_module;
+}
+#endif
+
+#define DLL_ENV "ERL_EMULATOR_DLL"
+
+static void
+set_env(char *key, char *value)
+{
+ if (!SetEnvironmentVariable((LPCTSTR) key, (LPCTSTR) value))
+ error("SetEnvironmentVariable(\"%s\", \"%s\") failed!", key, value);
+}
+
+static char *
+get_env(char *key)
+{
+ DWORD size = 32;
+ char *value = NULL;
+ while (1) {
+ DWORD nsz;
+ if (value)
+ free(value);
+ value = malloc(size);
+ if (!value)
+ error("GetEnvironmentVariable(\"%s\") failed", key);
+ SetLastError(0);
+ nsz = GetEnvironmentVariable((LPCTSTR) key, (LPTSTR) value, size);
+ if (nsz == 0 && GetLastError() == ERROR_ENVVAR_NOT_FOUND) {
+ free(value);
+ return NULL;
+ }
+ if (nsz <= size)
+ return value;
+ size = nsz;
+ }
+}
+
+free_env_val(char *value)
+{
+ if (value)
+ free(value);
+}
+
+
+int
+start_win_emulator(char* emu, char *start_prog, char** argv, int start_detached)
+{
+ int result;
+
+ windowed = 1;
+ if (start_detached) {
+ char *buff;
+ close(0);
+ close(1);
+ close(2);
+
+ set_env("ERL_CONSOLE_MODE", "detached");
+ set_env(DLL_ENV, emu);
+
+ argv[0] = start_prog;
+ argv = fnuttify_argv(argv);
+ result = spawnv(_P_DETACH, start_prog, argv);
+ free_fnuttified(argv);
+ } else {
+ int argc = 0;
+#ifdef LOAD_BEAM_DYNAMICALLY
+ HMODULE beam_module = load_win_beam_dll(emu);
+#endif
+ set_env("ERL_CONSOLE_MODE", "window");
+ while (argv[argc] != NULL) {
+ ++argc;
+ }
+#ifdef ARGS_HARDDEBUG
+ {
+ char sbuf[2048] = "";
+ int i;
+ for (i = 0; i < argc; ++i) {
+ strcat(sbuf,"|");
+ strcat(sbuf, argv[i]);
+ strcat(sbuf,"| ");
+ }
+ MessageBox(NULL, sbuf, "Werl", MB_OK|MB_ICONERROR);
+ }
+#endif
+#ifdef LOAD_BEAM_DYNAMICALLY
+ (*sys_primitive_init_p)(beam_module);
+ (*erl_start_p)(argc,argv);
+#else
+ erl_start(argc, argv);
+#endif
+ result = 0;
+ }
+ if (result == -1) {
+ error("Failed to execute %s: %s", emu, win32_errorstr(_doserrno));
+ }
+ return 0;
+}
+
+void __cdecl
+do_keep_window(void)
+{
+ printf("\nPress any key to close window.\n");
+#ifdef LOAD_BEAM_DYNAMICALLY
+ (*sys_get_key_p)(0);
+#else
+ sys_get_key(0);
+#endif
+}
+
+int
+start_emulator(char* emu, char *start_prog, char** argv, int start_detached)
+{
+ int result;
+ static char console_mode[] = "tty:ccc";
+ char* fd_type;
+ char* title;
+
+#ifdef HARDDEBUG
+ fprintf(stderr,"emu = %s, start_prog = %s\n",emu, start_prog);
+#endif
+
+ fd_type = strchr(console_mode, ':');
+ fd_type++;
+ _flushall();
+
+ /*
+ * If no console, we will spawn the emulator detached.
+ */
+
+ if (start_detached) {
+ char *buff;
+ close(0);
+ close(1);
+ close(2);
+ set_env("ERL_CONSOLE_MODE", "detached");
+ set_env(DLL_ENV, emu);
+
+ argv[0] = start_prog;
+ argv = fnuttify_argv(argv);
+#ifdef ARGS_HARDDEBUG
+ {
+ char buffer[2048];
+ int i;
+ sprintf(buffer,"Start detached [%s]\n",start_prog);
+ for(i=0;argv[i] != NULL;++i) {
+ strcat(buffer,"|");
+ strcat(buffer,argv[i]);
+ strcat(buffer,"|\n");
+ }
+ MessageBox(NULL, buffer,"Start detached",MB_OK);
+ }
+#endif
+ result = spawnv(_P_DETACH, start_prog, argv);
+ free_fnuttified(argv);
+ if (result == -1) {
+#ifdef ARGS_HARDDEBUG
+ MessageBox(NULL, "_spawnv failed","Start detached",MB_OK);
+#endif
+ return 1;
+ }
+ SetPriorityClass((HANDLE) result, GetPriorityClass(GetCurrentProcess()));
+ } else {
+ int argc = 0;
+#ifdef LOAD_BEAM_DYNAMICALLY
+ HMODULE beam_module = load_win_beam_dll(emu);
+#endif
+
+ /*
+ * Start the emulator.
+ */
+
+ title = get_env("ERL_WINDOW_TITLE");
+ if (title) {
+ SetConsoleTitle(title);
+ }
+ free_env_val(title);
+
+ set_env("ERL_CONSOLE_MODE", console_mode);
+ while (argv[argc] != NULL) {
+ ++argc;
+ }
+ if (keep_window) {
+ atexit(do_keep_window);
+ }
+#ifdef ARGS_HARDDEBUG
+ {
+ char sbuf[2048] = "";
+ int i;
+ for (i = 0; i < argc; ++i) {
+ strcat(sbuf,"|");
+ strcat(sbuf, argv[i]);
+ strcat(sbuf,"|\n");
+ }
+ MessageBox(NULL, sbuf, "erl", MB_OK);
+ }
+#endif
+#ifdef LOAD_BEAM_DYNAMICALLY
+ (*sys_primitive_init_p)(beam_module);
+ (*erl_start_p)(argc,argv);
+#else
+ erl_start(argc, argv);
+#endif
+ }
+ return 0;
+}
+
+void
+error(char* format, ...)
+{
+ char sbuf[2048];
+ va_list ap;
+
+ va_start(ap, format);
+ vsprintf(sbuf, format, ap);
+ va_end(ap);
+
+ if (!windowed && has_console()) {
+ fprintf(stderr, "%s\n", sbuf);
+ } else {
+ MessageBox(NULL, sbuf, "Werl", MB_OK|MB_ICONERROR);
+ }
+ exit(1);
+}
+
+static char*
+last_error(void)
+{
+ return win32_errorstr(GetLastError());
+}
+
+/*
+ * Returns a human-readable description of the last error.
+ * The returned pointer will be valid only as long as last-error()
+ * isn't called again.
+ */
+
+static char*
+win32_errorstr(int error)
+{
+ static LPTSTR lpBufPtr = NULL;
+
+ if (lpBufPtr)
+ LocalFree(lpBufPtr);
+ FormatMessage(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
+ NULL,
+ error,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ (LPTSTR) &lpBufPtr,
+ 0,
+ NULL);
+ SetLastError(error);
+ return lpBufPtr;
+}
+
+static int
+has_console(void)
+{
+ HANDLE handle = CreateFile("CONOUT$", GENERIC_WRITE, FILE_SHARE_WRITE,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+
+ if (handle != INVALID_HANDLE_VALUE) {
+ CloseHandle(handle);
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+static char** fnuttify_argv(char **argv)
+{
+ char **v;
+ char *p;
+ char *q;
+ int c;
+ int i;
+ int n;
+ int m;
+
+ for (c = 0; argv[c]; ++c)
+ ;
+
+ v = malloc(sizeof(char *) * (c+1));
+ v[c] = NULL;
+ for (i = 0; i < c; ++i) {
+ p = argv[i];
+ n = m = 0;
+ while (*p) {
+ if (*p == ' ') {
+ m = 2;
+ } else if (*p == '"') {
+ m = 2;
+ ++n;
+ }
+ ++p;
+ }
+ v[i] = malloc((p - argv[i]) + 1 + n + m);
+ p = argv[i];
+ q = v[i];
+ if (n || m) {
+ if (m) {
+ *q++ = '"';
+ }
+ while (*p) {
+ if (*p == '"') {
+ *q++ = '\\';
+ }
+ *q++ = *p++;
+ }
+ if (m) {
+ *q++ = '"';
+ }
+ *q = '\0';
+ } else {
+ strcpy(q,p);
+ }
+ }
+ return v;
+}
+
+static void free_fnuttified(char **v)
+{
+ char **t = v;
+
+ while(*t) {
+ free(*t);
+ ++t;
+ }
+ free(v);
+}