Add new function to spawn child 0.2
authorJ. Ali Harlow <ali@juiblex.co.uk>
Tue, 18 Aug 2009 13:11:42 +0000 (14:11 +0100)
committerJ. Ali Harlow <ali@juiblex.co.uk>
Tue, 18 Aug 2009 13:11:42 +0000 (14:11 +0100)
configure.ac
whelk/Makefile.am
whelk/_whelk.h
whelk/spawn.c [new file with mode: 0644]
whelk/string.c [new file with mode: 0644]
whelk/wait.c [new file with mode: 0644]
whelk/whelk.c

index 1f09a84..feb7fa0 100644 (file)
@@ -22,6 +22,17 @@ case $host_os in
 esac
 AM_CONDITIONAL(WHELK_MINGW,[test -n "$host_mingw"])
 
+# libtool versioning - this applies to all libraries in this package
+#
+# See http://sources.redhat.com/autobook/autobook/autobook_91.html#SEC91 for details
+#
+LT_CURRENT=1
+LT_REVISION=0
+LT_AGE=1
+AC_SUBST(LT_CURRENT)
+AC_SUBST(LT_REVISION)
+AC_SUBST(LT_AGE)
+
 ##################################################
 # Checks for programs.
 ##################################################
index c643597..301a63e 100644 (file)
@@ -6,11 +6,12 @@ INCLUDES=-I$(top_srcdir)
 
 lib_LTLIBRARIES=libwhelk.la
 
-libwhelk_la_SOURCES=whelk.c whelk.h _whelk.h
+libwhelk_la_SOURCES=whelk.c whelk.h _whelk.h string.c
 if WHELK_MINGW
 libwhelk_la_SOURCES+=unicode.c get_folder_path.c registry.c reg_keys.c \
-       shelllink.c
+       shelllink.c spawn.c wait.c
 endif
+libwhelk_la_LDFLAGS=-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE)
 
 pkginclude_HEADERS=whelk.h
 
index 9d0e503..5d018d7 100644 (file)
@@ -1,5 +1,6 @@
 #include <lua.h>
 #include <lualib.h>
+#include <unistd.h>                    /* For ssize_t */
 #ifdef __WIN32__
 #include <windows.h>
 #endif
        lua_settable(L,-3); \
     } while(0)
 
+struct whelk_string {
+    size_t len,alloc;
+    char *buffer;
+};
+
+#ifdef __WIN32__
+struct whelk_wait {
+    int no_objects;
+    HANDLE *objects;
+};
+#endif
+
 int whelk_perror(lua_State *L,const char *s);
 int whelk_get_folder_path(lua_State *L);
 void whelk_open_get_folder_path(lua_State *L);
 void whelk_open_reg_keys(lua_State *L);
 int whelk_create_short_cut(lua_State *L);
+int whelk_spawn(lua_State *L);
+
+char *whelk_string_prealloc(struct whelk_string *string,size_t len);
+void whelk_string_seek(struct whelk_string *string,ssize_t offset);
+char *whelk_string_finalize(struct whelk_string *string);
+void whelk_string_free(struct whelk_string *string);
 
 #ifdef __WIN32__
 char *whelk_utf16_to_utf8(const WCHAR *ucs2,int len);
@@ -29,4 +48,9 @@ HRESULT whelk_reg_get_value(HKEY key,const char *subkey,const char *value,
 HRESULT whelk_reg_set_value(HKEY key,const char *subkey,const char *value,
   DWORD type,const void *data,int nb);
 HRESULT whelk_reg_delete_key(HKEY key,const char *subkey);
+
+int whelk_wait_add_object(struct whelk_wait *wait,HANDLE object);
+void whelk_wait_remove_object(struct whelk_wait *wait,HANDLE object);
+HANDLE whelk_wait_poll(struct whelk_wait *wait);
+void whelk_wait_free(struct whelk_wait *wait);
 #endif
diff --git a/whelk/spawn.c b/whelk/spawn.c
new file mode 100644 (file)
index 0000000..ee30c6b
--- /dev/null
@@ -0,0 +1,175 @@
+#include <stdlib.h>
+#include <errno.h>
+#include <windows.h>
+#include <lua.h>
+#include <lualib.h>
+#include <lauxlib.h>
+#include "_whelk.h"
+
+static void whelk_close_pipe(HANDLE pipe[2])
+{
+    CloseHandle(pipe[0]);
+    CloseHandle(pipe[1]);
+}
+
+/* Create a pipe with one end inheritable */
+
+static BOOL whelk_child_pipe(HANDLE pipe[2],BOOL to_child)
+{
+    SECURITY_ATTRIBUTES sa={0};
+    HANDLE h;
+    sa.nLength=sizeof(sa);
+    sa.bInheritHandle=TRUE;
+    if (!CreatePipe(pipe+0,pipe+1,&sa,0))
+       return FALSE;
+    if (!DuplicateHandle(GetCurrentProcess(),pipe[to_child?0:1],
+      GetCurrentProcess(),&h,0,TRUE,DUPLICATE_SAME_ACCESS))
+    {
+       whelk_close_pipe(pipe);
+       return FALSE;
+    }
+    CloseHandle(pipe[to_child?0:1]);
+    pipe[to_child?0:1]=h;
+    return TRUE;
+}
+
+/*
+ * spawn(application_name,command-line,working-directory,standard-input)
+ *
+ * Returns two strings (the data written to standard output and standard error
+ * respectively) and the exit code.
+ */
+
+int whelk_spawn(lua_State *L)
+{
+    STARTUPINFOW si={0};
+    PROCESS_INFORMATION pi={0};
+    WCHAR *application_name=whelk_utf8_to_utf16(luaL_checkstring(L,1),-1);
+    WCHAR *command_line=whelk_utf8_to_utf16(luaL_checkstring(L,2),-1);
+    WCHAR *working_directory=whelk_utf8_to_utf16(luaL_checkstring(L,3),-1);
+    size_t stdin_len;
+    const char *standard_input=luaL_checklstring(L,4,&stdin_len);
+    struct whelk_string standard_output={0};
+    struct whelk_string standard_error={0};
+    char *buffer;
+    HANDLE stdin_pipe[2],stdout_pipe[2],stderr_pipe[2],h;
+    struct whelk_wait wait={0};
+    DWORD nb;
+    int exitcode;
+    if (!whelk_child_pipe(stdin_pipe,TRUE))
+    {
+       free(application_name);
+       free(command_line);
+       free(working_directory);
+       errno=EMFILE;
+       return whelk_perror(L,NULL);
+    }
+    if (!whelk_child_pipe(stdout_pipe,FALSE))
+    {
+       free(application_name);
+       free(command_line);
+       free(working_directory);
+       whelk_close_pipe(stdin_pipe);
+       errno=EMFILE;
+       return whelk_perror(L,NULL);
+    }
+    if (!whelk_child_pipe(stderr_pipe,FALSE))
+    {
+       free(application_name);
+       free(command_line);
+       free(working_directory);
+       whelk_close_pipe(stdin_pipe);
+       whelk_close_pipe(stdout_pipe);
+       errno=EMFILE;
+       return whelk_perror(L,NULL);
+    }
+    si.cb=sizeof(si);
+    si.dwFlags=STARTF_USESTDHANDLES;
+    si.hStdInput=stdin_pipe[0];
+    si.hStdOutput=stdout_pipe[1];
+    si.hStdError=stderr_pipe[1];
+    if (!CreateProcessW(application_name,command_line,NULL,NULL,TRUE,
+      CREATE_DEFAULT_ERROR_MODE|NORMAL_PRIORITY_CLASS,NULL,working_directory,
+      &si,&pi))
+    {
+       free(application_name);
+       free(command_line);
+       free(working_directory);
+       whelk_close_pipe(stdin_pipe);
+       whelk_close_pipe(stdout_pipe);
+       whelk_close_pipe(stderr_pipe);
+       errno=EACCES;
+       return whelk_perror(L,NULL);
+    }
+    free(application_name);
+    free(command_line);
+    free(working_directory);
+    CloseHandle(stdout_pipe[1]);
+    CloseHandle(stderr_pipe[1]);
+    whelk_wait_add_object(&wait,stdout_pipe[0]);
+    whelk_wait_add_object(&wait,stderr_pipe[0]);
+    whelk_wait_add_object(&wait,pi.hProcess);
+    if (standard_input)
+       whelk_wait_add_object(&wait,stdin_pipe[1]);
+    else
+    {
+       whelk_close_pipe(stdin_pipe);
+       stdin_pipe[1]=INVALID_HANDLE_VALUE;
+    }
+    for(;;)
+    {
+       h=whelk_wait_poll(&wait);
+       if (h==INVALID_HANDLE_VALUE)
+           break;
+       else if (h==stdout_pipe[0])
+       {
+           buffer=whelk_string_prealloc(&standard_output,1024);
+           if (ReadFile(stdout_pipe[0],buffer,1024,&nb,NULL))
+               whelk_string_seek(&standard_output,nb);
+           else
+               whelk_wait_remove_object(&wait,stdout_pipe[0]);
+       }
+       else if (h==stderr_pipe[0])
+       {
+           buffer=whelk_string_prealloc(&standard_error,1024);
+           if (ReadFile(stderr_pipe[0],buffer,1024,&nb,NULL))
+               whelk_string_seek(&standard_error,nb);
+           else
+               whelk_wait_remove_object(&wait,stderr_pipe[0]);
+       }
+       else if (h==pi.hProcess)
+           break;
+       else if (h==stdin_pipe[1])
+       {
+           nb=max(stdin_len,512);
+           WriteFile(stdin_pipe[1],standard_input,nb,NULL,NULL);
+           standard_input+=nb;
+           stdin_len-=nb;
+           if (!stdin_len)
+           {
+               whelk_wait_remove_object(&wait,stdin_pipe[1]);
+               whelk_close_pipe(stdin_pipe);
+               standard_input=NULL;
+           }
+       }
+    }
+    whelk_wait_free(&wait);
+    if (standard_input)
+       whelk_close_pipe(stdin_pipe);
+    if (GetExitCodeProcess(pi.hProcess,&nb))
+       exitcode=(int)nb;
+    else
+       exitcode=-1;
+    CloseHandle(pi.hThread);
+    CloseHandle(pi.hProcess);
+    CloseHandle(stdout_pipe[0]);
+    CloseHandle(stderr_pipe[0]);
+    whelk_string_finalize(&standard_output);
+    whelk_string_finalize(&standard_error);
+    lua_pushlstring(L,standard_output.buffer,standard_output.len);
+    lua_pushlstring(L,standard_error.buffer,standard_error.len);
+    whelk_string_free(&standard_output);
+    whelk_string_free(&standard_error);
+    lua_pushnumber(L,exitcode);
+    return 3;
+}
diff --git a/whelk/string.c b/whelk/string.c
new file mode 100644 (file)
index 0000000..6b33ea0
--- /dev/null
@@ -0,0 +1,37 @@
+#include <stdlib.h>
+#include "_whelk.h"
+
+char *whelk_string_prealloc(struct whelk_string *string,size_t len)
+{
+    char *new;
+    if (string->alloc<string->len+len)
+    {
+       new=realloc(string->buffer,string->len+len);
+       if (!new)
+           return NULL;
+       string->buffer=new;
+       string->alloc=string->len+len;
+    }
+    return string->buffer+string->len;
+}
+
+void whelk_string_seek(struct whelk_string *string,ssize_t offset)
+{
+    if (offset>=0 || string->len>=(size_t)-offset)
+       string->len+=offset;
+    else
+       string->len=0;
+}
+
+char *whelk_string_finalize(struct whelk_string *string)
+{
+    whelk_string_prealloc(string,1);
+    string->buffer[string->len]='\0';
+    return string->buffer;
+}
+
+void whelk_string_free(struct whelk_string *string)
+{
+    free(string->buffer);
+    memset(string,0,sizeof(*string));
+}
diff --git a/whelk/wait.c b/whelk/wait.c
new file mode 100644 (file)
index 0000000..ed0f4b9
--- /dev/null
@@ -0,0 +1,47 @@
+#include <stdlib.h>
+#include <windows.h>
+#include "_whelk.h"
+
+int whelk_wait_add_object(struct whelk_wait *wait,HANDLE object)
+{
+    HANDLE *new;
+    new=realloc(wait->objects,wait->no_objects+1);
+    if (!new)
+       return -1;
+    wait->objects=new;
+    wait->objects[wait->no_objects++]=object;
+    return 0;
+}
+
+void whelk_wait_remove_object(struct whelk_wait *wait,HANDLE object)
+{
+    int i;
+    for(i=0;i<wait->no_objects;i++)
+       if (wait->objects[i]==object)
+           break;
+    if (i!=wait->no_objects-1)
+       memcpy(wait->objects+i,wait->objects+i+1,
+         (wait->no_objects-i-1)*sizeof(*wait->objects));
+    wait->no_objects--;
+}
+
+/*
+ * Where two or more objects are ready,
+ * the object added first is returned.
+ */
+
+HANDLE whelk_wait_poll(struct whelk_wait *wait)
+{
+    DWORD retval;
+    retval=WaitForMultipleObjects(wait->no_objects,wait->objects,FALSE,
+      INFINITE);
+    if (retval>=WAIT_OBJECT_0 && retval-WAIT_OBJECT_0<wait->no_objects)
+       return wait->objects[retval-WAIT_OBJECT_0];
+    else
+       return INVALID_HANDLE_VALUE;
+}
+
+void whelk_wait_free(struct whelk_wait *wait)
+{
+    free(wait->objects);
+}
index dc3c155..ab32cf4 100644 (file)
@@ -33,6 +33,7 @@ static int whelk_unsupported(lua_State *L)
 static const luaL_reg whelk_functions[]={
     { "GetFolderPath",WIN32_ONLY(whelk_get_folder_path) },
     { "CreateShortCut",WIN32_ONLY(whelk_create_short_cut) },
+    { "Spawn",WIN32_ONLY(whelk_spawn) },
     { NULL }
 };