diff options
author | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
---|---|---|
committer | Erlang/OTP <[email protected]> | 2009-11-20 14:54:40 +0000 |
commit | 84adefa331c4159d432d22840663c38f155cd4c1 (patch) | |
tree | bff9a9c66adda4df2106dfd0e5c053ab182a12bd /erts/etc/win32 | |
download | otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.gz otp-84adefa331c4159d432d22840663c38f155cd4c1.tar.bz2 otp-84adefa331c4159d432d22840663c38f155cd4c1.zip |
The R13B03 release.OTP_R13B03
Diffstat (limited to 'erts/etc/win32')
59 files changed, 10563 insertions, 0 deletions
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 Binary files differnew file mode 100644 index 0000000000..fb22afda62 --- /dev/null +++ b/erts/etc/win32/beam_icon.ico 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 Binary files differnew file mode 100644 index 0000000000..3e228317cd --- /dev/null +++ b/erts/etc/win32/erl_icon.ico 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 Binary files differnew file mode 100644 index 0000000000..cee8b58af9 --- /dev/null +++ b/erts/etc/win32/erlang.ico 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 Binary files differnew file mode 100644 index 0000000000..d22abb396b --- /dev/null +++ b/erts/etc/win32/hrl_icon.ico 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 Binary files differnew file mode 100755 index 0000000000..0f56b8b239 --- /dev/null +++ b/erts/etc/win32/nsis/custom_modern.exe 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 Binary files differnew file mode 100644 index 0000000000..edbd8a6f2c --- /dev/null +++ b/erts/etc/win32/nsis/erlang_inst.ico diff --git a/erts/etc/win32/nsis/erlang_uninst.ico b/erts/etc/win32/nsis/erlang_uninst.ico Binary files differnew file mode 100755 index 0000000000..edbd8a6f2c --- /dev/null +++ b/erts/etc/win32/nsis/erlang_uninst.ico 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 Binary files differnew file mode 100644 index 0000000000..e0df8454fd --- /dev/null +++ b/erts/etc/win32/toolbar.bmp 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); +} |